From 5d0ad01d7a525173d1f279c3950b919cfc0a311d Mon Sep 17 00:00:00 2001 From: Nicholas Addison Date: Wed, 12 Nov 2025 11:54:09 +1100 Subject: [PATCH 01/13] Removed unused event from Ether.fi ARM Added more Natspec --- src/contracts/EtherFiARM.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/contracts/EtherFiARM.sol b/src/contracts/EtherFiARM.sol index 364bc989..5b3d8c8d 100644 --- a/src/contracts/EtherFiARM.sol +++ b/src/contracts/EtherFiARM.sol @@ -35,7 +35,6 @@ contract EtherFiARM is Initializable, AbstractARM, IERC721Receiver { event RequestEtherFiWithdrawal(uint256 amount, uint256 requestId); event ClaimEtherFiWithdrawals(uint256[] requestIds); - event RegisterEtherFiWithdrawalRequests(uint256[] requestIds, uint256 totalAmountRequested); /// @param _eeth The address of the eETH token /// @param _weth The address of the WETH token @@ -90,6 +89,7 @@ contract EtherFiARM is Initializable, AbstractARM, IERC721Receiver { /** * @notice Request an eETH for ETH withdrawal. * Reference: https://etherfi.gitbook.io/etherfi/contracts-and-integrations/how-to + * @param amount The amount of eETH to withdraw. */ function requestEtherFiWithdrawal(uint256 amount) external onlyOperatorOrOwner returns (uint256 requestId) { // Request the withdrawal from the EtherFi Withdrawal Queue. From 03e6acdc319e6c8c51063e4b3095e6a217b81da1 Mon Sep 17 00:00:00 2001 From: Nicholas Addison Date: Wed, 12 Nov 2025 13:14:04 +1100 Subject: [PATCH 02/13] WIP Ethena ARM --- src/contracts/EthenaARM.sol | 101 +++++++++++++++++++++++++++++++++++ src/contracts/Interfaces.sol | 25 +++++++++ 2 files changed, 126 insertions(+) create mode 100644 src/contracts/EthenaARM.sol diff --git a/src/contracts/EthenaARM.sol b/src/contracts/EthenaARM.sol new file mode 100644 index 00000000..52cf6157 --- /dev/null +++ b/src/contracts/EthenaARM.sol @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.23; + +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; + +import {AbstractARM} from "./AbstractARM.sol"; +import {IERC20, IStakedUSDe, UserCooldown} from "./Interfaces.sol"; + +/** + * @title Ethena sUSDe/USDe Automated Redemption Manager (ARM) + * @author Origin Protocol Inc + */ +contract EthenaARM is Initializable, AbstractARM { + /// @notice The address of Ethena's synthetic dollar token (USDe) + IERC20 public immutable usde; + /// @notice The address of Ethena's staked synthetic dollar token (sUSDe) + IStakedUSDe public immutable susde; + + event RequestBaseWithdrawal(uint256 amount); + event ClaimBaseWithdrawals(uint256 liquidityAmountClaimed); + + /// @param _usde The address of Ethena's synthetic dollar token (USDe) + /// @param _susde The address of Ethena's staked synthetic dollar token (sUSDe) + /// @param _claimDelay The delay in seconds before a user can claim a redeem from the request + /// @param _minSharesToRedeem The minimum amount of shares to redeem from the active lending market + /// @param _allocateThreshold The minimum amount of liquidity assets in excess of the ARM buffer before + /// the ARM can allocate to a active lending market. + constructor( + address _usde, + address _susde, + uint256 _claimDelay, + uint256 _minSharesToRedeem, + int256 _allocateThreshold + ) AbstractARM(_usde, _susde, _usde, _claimDelay, _minSharesToRedeem, _allocateThreshold) { + usde = IERC20(_usde); + susde = IStakedUSDe(_susde); + + _disableInitializers(); + } + + /// @notice Initialize the storage variables stored in the proxy contract. + /// The deployer that calls initialize has to approve the ARM's proxy contract to transfer 1e12 USDe. + /// @param _name The name of the liquidity provider (LP) token. + /// @param _symbol The symbol of the liquidity provider (LP) token. + /// @param _operator The address of the account that can request and claim withdrawals. + /// @param _fee The performance fee that is collected by the feeCollector measured in basis points (1/100th of a percent). + /// 10,000 = 100% performance fee + /// 1,500 = 15% performance fee + /// @param _feeCollector The account that can collect the performance fee + /// @param _capManager The address of the CapManager contract + function initialize( + string calldata _name, + string calldata _symbol, + address _operator, + uint256 _fee, + address _feeCollector, + address _capManager + ) external initializer { + _initARM(_operator, _name, _symbol, _fee, _feeCollector, _capManager); + } + + /** + * @notice Request a cooldown of USDe from Ethena's Staked USDe (sUSDe) contract. + * @param baseAmount The amount of base assets (sUSDe) to withdraw. + */ + function requestBaseWithdrawal(uint256 baseAmount) external onlyOperatorOrOwner { + // For now put in a restriction that only one request can be outstanding at a time. + // TODO replace this with a pool of helper contracts that can manage multiple cooldowns in parallel. + UserCooldown memory cooldown = susde.cooldowns(address(this)); + require(cooldown.underlyingAmount == 0, "EthenaARM: Existing cooldown"); + + // Request an unstaked from Ethena's staked USDe (sUSDe) contract + susde.cooldownShares(baseAmount); + + // Emit event for the request + emit RequestBaseWithdrawal(baseAmount); + } + + /** + * @notice Claim all the USDe that is now claimable from the Staked USDe contract. + * Reverts with `InvalidCooldown` from the Staked USDe contract if the cooldown period has not yet passed. + */ + function claimBaseWithdrawals() external { + UserCooldown memory cooldown = susde.cooldowns(address(this)); + + susde.unstake(address(this)); + + emit ClaimBaseWithdrawals(cooldown.underlyingAmount); + } + + /** + * @dev Gets the amount of USDe waiting to be claimed from the Staked USDe contract. + * This can be either in the cooldown period or ready to be claimed. + */ + function _externalWithdrawQueue() internal view override returns (uint256) { + UserCooldown memory cooldown = susde.cooldowns(address(this)); + + return cooldown.underlyingAmount; + } +} diff --git a/src/contracts/Interfaces.sol b/src/contracts/Interfaces.sol index 04d3d997..d013642c 100644 --- a/src/contracts/Interfaces.sol +++ b/src/contracts/Interfaces.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.23; +import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol"; + interface IERC20 { function totalSupply() external view returns (uint256); function balanceOf(address account) external view returns (uint256); @@ -333,3 +335,26 @@ interface IDistributor { bytes32[][] calldata proofs ) external; } + +// Ethena Interfaces + +struct UserCooldown { + uint104 cooldownEnd; + uint152 underlyingAmount; +} + +interface IStakedUSDe is IERC4626 { + // Errors // + /// @notice Error emitted when the shares amount to redeem is greater than the shares balance of the owner + error ExcessiveRedeemAmount(); + /// @notice Error emitted when the shares amount to withdraw is greater than the shares balance of the owner + error ExcessiveWithdrawAmount(); + + function cooldownAssets(uint256 assets) external returns (uint256 shares); + + function cooldownShares(uint256 shares) external returns (uint256 assets); + + function unstake(address receiver) external; + + function cooldowns(address receiver) external view returns (UserCooldown memory); +} From 967fade3a17cf91f5976dd600e6280d00814bc7f Mon Sep 17 00:00:00 2001 From: Nicholas Addison Date: Wed, 12 Nov 2025 14:46:18 +1100 Subject: [PATCH 03/13] Ethena ARM processes --- docs/plantuml/ethenaProcesses.png | Bin 0 -> 102807 bytes docs/plantuml/ethenaProcesses.puml | 132 +++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 docs/plantuml/ethenaProcesses.png create mode 100644 docs/plantuml/ethenaProcesses.puml diff --git a/docs/plantuml/ethenaProcesses.png b/docs/plantuml/ethenaProcesses.png new file mode 100644 index 0000000000000000000000000000000000000000..f9a39dcc2baf7a60a7dc7c0407cfabed7841f861 GIT binary patch literal 102807 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 + +box Blockchain +participant "Ethena\nARM" as arm <> +participant "Ethena\nCooldown\nFactory" as factory <> +participant "Ethena\nCooldown\nHelper" as helper <> +participant "Ethena\nMinting" as mint <> +participant "sUSDe\nToken" as susde <> +participant "USDe\nToken" as usde <> +' participant "USDT\nToken" as usdt <> +end box + +group unstake sUSDe + +op -> arm : requestUnstake(\n amount) +activate arm + +arm -> factory : assignHelper() +activate factory + +note over factory: increment helper index + +factory -> mint : cooldowns(\n helper address) +activate mint +return + +alt cooldown exits and ended + factory -> helper : claimUnstake() + activate helper + helper -> susde : unstake(arm) + activate susde + note right: check cooldown passed + susde -> usde : transfer(\n arm,\n amount) + activate usde + note right: transfer all\nunderlying USDe\nto ARM + return + return + return +else cooling exits and not ended + note over factory: revert and wait for cooldown to end +end +return helper address + +arm -> susde : transfer(\n helper address,\n amount) +activate susde +note right: transfer sUSDe\nfrom ARM\nto Helper +return + +arm -> helper : requestUnstake(\n amount) +activate helper +helper -> susde : cooldownShares(\n sUSDe amount) +activate susde +note right +burn sUSDe from helper +increment underlying USDe +restart 7 day cooldown +end note +return USDe amount +return USDe amount +return +note right: emit USDe amount and helper address + +... 7 day cooldown ... + +any -> arm : claimUnstake(\n helper address) +activate arm +' arm -> factory : claimUnstake(\n helper address) +' activate factory +' factory -> helper : claimUnstake() +arm -> helper : claimUnstake() +activate helper +helper -> susde : unstake(arm) +activate susde +note right: check cooldown passed +susde -> usde : transfer(\n arm,\n amount) +activate usde +note right: transfer all\nunderlying USDe\nto ARM +return +return +return +return + +end group + +' group redeem USDe for USDT + +' op -> api : GET Request For Quote\npayload: {\n pair,\n side,\n size,\n benefactor} +' activate api +' return order details: {\n rfq_id,\n collateral_amount,\n amount...} + +' op -> op : sign(hash(order details)) +' note right: sign order hash using Operator's private key + +' op -> arm : redeemEthenaRequest (\n order details,\n signature) +' activate arm +' note right +' verify redeem price >= cross price +' verify collateral asset = USDT +' store map of order hash to signature +' end note + +' arm -> usde : approve(\n amount,\n Ethena Minting) +' activate usde +' return +' return + +' op -> api : POST Submit Order\nparams:\n signature,\n signature_type=EIP1271\npayload: order details +' activate api +' api -> mint : redeem (\n order details,\n signature: {\n bytes, type: 1}) +' activate mint +' mint -> arm : isValidSignature(\n order hash,\n signature bytes) +' activate arm +' return +' mint -> usde : burnFrom(\n arm,\n amount) +' activate usde +' note right: burn USDe from ARM +' return +' mint -> usdt : transfer(\n arm,\n amount) +' activate usdt +' note right: transfer USDT to ARM +' return +' return +' return tx hash + +' end group From 279b1ce30b0969c0d022a57cfb747409966aad4e Mon Sep 17 00:00:00 2001 From: Nicholas Addison Date: Wed, 12 Nov 2025 16:25:39 +1100 Subject: [PATCH 04/13] Add support for base assets that appreciate against the liquidity asset --- src/contracts/AbstractARM.sol | 34 ++++++++++++++++++++++++++++------ src/contracts/EthenaARM.sol | 19 +++++++++++++++++++ 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/src/contracts/AbstractARM.sol b/src/contracts/AbstractARM.sol index 6fb52cca..b3712557 100644 --- a/src/contracts/AbstractARM.sol +++ b/src/contracts/AbstractARM.sol @@ -377,6 +377,9 @@ abstract contract AbstractARM is OwnableOperable, ERC20Upgradeable { virtual returns (uint256 amountOut) { + // Convert base asset to liquid asset or vice versa if needed + uint256 convertedAmountIn = _convert(address(inToken), amountIn); + uint256 price; if (inToken == token0) { require(outToken == token1, "ARM: Invalid out token"); @@ -387,7 +390,7 @@ abstract contract AbstractARM is OwnableOperable, ERC20Upgradeable { } else { revert("ARM: Invalid in token"); } - amountOut = amountIn * price / PRICE_SCALE; + amountOut = convertedAmountIn * price / PRICE_SCALE; // Transfer the input tokens from the caller to this ARM contract _transferAssetFrom(address(inToken), msg.sender, address(this), amountIn); @@ -401,6 +404,9 @@ abstract contract AbstractARM is OwnableOperable, ERC20Upgradeable { virtual returns (uint256 amountIn) { + // Convert base asset to liquid asset or vice versa if needed + uint256 convertedAmountOut = _convert(address(outToken), amountOut); + uint256 price; if (inToken == token0) { require(outToken == token1, "ARM: Invalid out token"); @@ -414,7 +420,7 @@ abstract contract AbstractARM is OwnableOperable, ERC20Upgradeable { // always round in our favor // +1 for truncation when dividing integers // +2 to cover stETH transfers being up to 2 wei short of the requested transfer amount - amountIn = ((amountOut * PRICE_SCALE) / price) + 3; + amountIn = ((convertedAmountOut * PRICE_SCALE) / price) + 3; // Transfer the input tokens from the caller to this ARM contract _transferAssetFrom(address(inToken), msg.sender, address(this), amountIn); @@ -423,6 +429,15 @@ abstract contract AbstractARM is OwnableOperable, ERC20Upgradeable { _transferAsset(address(outToken), to, amountOut); } + /// @dev Convert between base asset and liquidity asset if needed. + /// @param token The address of the token to convert from. + /// @param amount The amount of the token to convert from. + /// @return The converted to amount. + /// Defaults to 1:1 conversion. + function _convert(address token, uint256 amount) internal view virtual returns (uint256) { + return amount; + } + /// @notice Get the available liquidity for a each token in the ARM. /// @return reserve0 The available liquidity for token0 /// @return reserve1 The available liquidity for token1 @@ -442,6 +457,10 @@ abstract contract AbstractARM is OwnableOperable, ERC20Upgradeable { if (address(token0) == baseAsset) (reserve0, reserve1) = (reserve1, reserve0); } + //////////////////////////////////////////////////// + /// Swap Admin Functions + //////////////////////////////////////////////////// + /** * @notice Set exchange rates from an operator account from the ARM's perspective. * If token 0 is WETH and token 1 is stETH, then both prices will be set using the stETH/WETH price. @@ -696,11 +715,14 @@ abstract contract AbstractARM is OwnableOperable, ERC20Upgradeable { /// and active lending market, less liquidity assets reserved for the ARM's withdrawal queue. /// This does not exclude any accrued performance fees. function _availableAssets() internal view returns (uint256 availableAssets, uint256 outstandingWithdrawals) { - // Liquidity assets, eg WETH, in the ARM and lending markets are priced at 1.0 - // Base assets, eg stETH, in the withdrawal queue are also priced at 1.0 - // Base assets, eg stETH, in the ARM are priced at the cross price which is a discounted price + // Convert the base assets in the ARM to the amount of liquidity assets + uint256 baseConvertedToLiquid = _convert(baseAsset, IERC20(baseAsset).balanceOf(address(this))); + + // Liquidity assets, eg WETH, in the ARM and lending markets are valued at 1.0 + // Base assets, eg stETH, in the withdrawal queue are valued at the amount of liquidity assets that will be returned + // Base assets, eg stETH, in the ARM are valued the liquidity asset amount and then the cross price which is a discounted price uint256 assets = IERC20(liquidityAsset).balanceOf(address(this)) + _externalWithdrawQueue() - + IERC20(baseAsset).balanceOf(address(this)) * crossPrice / PRICE_SCALE; + + baseConvertedToLiquid * crossPrice / PRICE_SCALE; address activeMarketMem = activeMarket; if (activeMarketMem != address(0)) { diff --git a/src/contracts/EthenaARM.sol b/src/contracts/EthenaARM.sol index 52cf6157..62d57f3e 100644 --- a/src/contracts/EthenaARM.sol +++ b/src/contracts/EthenaARM.sol @@ -98,4 +98,23 @@ contract EthenaARM is Initializable, AbstractARM { return cooldown.underlyingAmount; } + + /// @dev Convert between base asset (sUSDe) and liquidity asset (USDe). + /// ERC-4626 convert functions are used as the preview functions can return a + /// smaller amount if the contract is paused or has high utilization. + /// Although that is not the case the the sUSDe implementation. + /// @param token The address of the token to convert from. sUSDe or USDe. + /// @param amount The amount of the token to convert from. + /// @return The converted to amount. + function _convert(address token, uint256 amount) internal view override returns (uint256) { + if (token == baseAsset) { + // Convert base asset (sUSDe) to liquidity asset (USDe) + return susde.convertToAssets(amount); + } else if (token == liquidityAsset) { + // Convert liquidity asset (USDe) to base asset (sUSDe) + return susde.convertToShares(amount); + } else { + revert("EthenaARM: Invalid token"); + } + } } From 5507ad84de5a45dcd53e4437b35f10b838e4269a Mon Sep 17 00:00:00 2001 From: Nicholas Addison Date: Wed, 12 Nov 2025 16:44:24 +1100 Subject: [PATCH 05/13] Deploy script for Ethena ARM --- script/deploy/DeployManager.sol | 2 + .../mainnet/014_DeployEthenaARMScript.sol | 127 ++++++++++++++++++ src/contracts/utils/Addresses.sol | 4 + 3 files changed, 133 insertions(+) create mode 100644 script/deploy/mainnet/014_DeployEthenaARMScript.sol diff --git a/script/deploy/DeployManager.sol b/script/deploy/DeployManager.sol index c0f02f45..f36ca58e 100644 --- a/script/deploy/DeployManager.sol +++ b/script/deploy/DeployManager.sol @@ -25,6 +25,7 @@ import {UpgradeOriginARMSetBufferScript} from "./sonic/005_UpgradeOriginARMSetBu import {UpgradeLidoARMAssetScript} from "./mainnet/010_UpgradeLidoARMAssetScript.sol"; import {DeployEtherFiARMScript} from "./mainnet/011_DeployEtherFiARMScript.sol"; import {UpgradeEtherFiARMScript} from "./mainnet/012_UpgradeEtherFiARMScript.sol"; +import {DeployEthenaARMScript} from "./mainnet/014_DeployEthenaARMScript.sol"; contract DeployManager is Script { using stdJson for string; @@ -86,6 +87,7 @@ contract DeployManager is Script { _runDeployFile(new UpgradeLidoARMAssetScript()); _runDeployFile(new DeployEtherFiARMScript()); _runDeployFile(new UpgradeEtherFiARMScript()); + _runDeployFile(new DeployEthenaARMScript()); } else if (block.chainid == 17000) { // Holesky _runDeployFile(new DeployCoreHoleskyScript()); diff --git a/script/deploy/mainnet/014_DeployEthenaARMScript.sol b/script/deploy/mainnet/014_DeployEthenaARMScript.sol new file mode 100644 index 00000000..5e87c93c --- /dev/null +++ b/script/deploy/mainnet/014_DeployEthenaARMScript.sol @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.23; + +// Foundry imports +import {console} from "forge-std/console.sol"; + +// Contract imports +import {IWETH} from "contracts/Interfaces.sol"; +import {Proxy} from "contracts/Proxy.sol"; +import {EthenaARM} from "contracts/EthenaARM.sol"; +import {CapManager} from "contracts/CapManager.sol"; +import {Mainnet} from "contracts/utils/Addresses.sol"; +import {MorphoMarket} from "contracts/markets/MorphoMarket.sol"; +import {ZapperARM} from "contracts/ZapperARM.sol"; +import {Abstract4626MarketWrapper} from "contracts/markets/Abstract4626MarketWrapper.sol"; + +// Deployment imports +import {GovProposal, GovSixHelper} from "contracts/utils/GovSixHelper.sol"; +import {AbstractDeployScript} from "../AbstractDeployScript.sol"; + +contract DeployEtherFiARMScript is AbstractDeployScript { + using GovSixHelper for GovProposal; + + GovProposal public govProposal; + + string public constant override DEPLOY_NAME = "014_DeployEthenaARMScript"; + bool public constant override proposalExecuted = true; + + Proxy morphoMarketProxy; + EthenaARM armImpl; + MorphoMarket morphoMarket; + + function _execute() internal override { + console.log("Deploy:", DEPLOY_NAME); + console.log("------------"); + + // 1. Deploy new ARM proxy contract + Proxy armProxy = new Proxy(); + _recordDeploy("ETHENA_ARM", address(armProxy)); + + // 2. Deploy proxy for the CapManager + Proxy capManProxy = new Proxy(); + _recordDeploy("ETHENA_ARM_CAP_MAN", address(capManProxy)); + + // 3. Deploy CapManager implementation + CapManager capManagerImpl = new CapManager(address(armProxy)); + _recordDeploy("ETHENA_ARM_CAP_IMPL", address(capManagerImpl)); + + // 4. Initialize Proxy with CapManager implementation and set the owner to the deployer for now + bytes memory capManData = abi.encodeWithSignature("initialize(address)", Mainnet.ARM_RELAYER); + capManProxy.initialize(address(capManagerImpl), deployer, capManData); + CapManager capManager = CapManager(address(capManProxy)); + + // 4. Set total assets and liquidity provider caps + capManager.setTotalAssetsCap(250 ether); + capManager.setAccountCapEnabled(true); + address[] memory lpAccounts = new address[](1); + lpAccounts[0] = Mainnet.TREASURY_LP; + capManager.setLiquidityProviderCaps(lpAccounts, 250 ether); + + // 5. Transfer ownership of CapManager to the mainnet 5/8 multisig + capManProxy.setOwner(Mainnet.GOV_MULTISIG); + + // 6. Deploy new Ethena implementation + uint256 claimDelay = tenderlyTestnet ? 1 minutes : 10 minutes; + armImpl = new EthenaARM( + Mainnet.USDE, + Mainnet.SUSDE, + claimDelay, + 1e7, // minSharesToRedeem + 1e18 // allocateThreshold + ); + _recordDeploy("ETHENA_ARM_IMPL", address(armImpl)); + + // 7. Give the deployer a tiny amount of USDe for the initialization + // This can be skipped if the deployer already has USDe + IWETH(Mainnet.USDE).approve(address(armProxy), 1e13); + + // 8. Initialize proxy, set the owner to deployer, set the operator to the ARM Relayer + bytes memory armData = abi.encodeWithSignature( + "initialize(string,string,address,uint256,address,address)", + "Ethena Staked USDe ARM", // name + "ARM-sUSDe-USDe", // symbol + Mainnet.ARM_RELAYER, // Operator + 2000, // 20% performance fee + Mainnet.BUYBACK_OPERATOR, // Fee collector + address(capManager) + ); + armProxy.initialize(address(armImpl), deployer, armData); + + console.log("Initialized Ethena ARM"); + + // 10. Deploy MorphoMarket proxy + morphoMarketProxy = new Proxy(); + _recordDeploy("MORPHO_MARKET_ETHENA", address(morphoMarketProxy)); + + // 11. Deploy MorphoMarket + morphoMarket = new MorphoMarket(address(armProxy), Mainnet.MORPHO_MARKET_ETHENA); + _recordDeploy("MORPHO_MARKET_ETHENA_IMPL", address(morphoMarket)); + + // 12. Initialize MorphoMarket proxy with the implementation, Timelock as owner + bytes memory data = abi.encodeWithSelector( + Abstract4626MarketWrapper.initialize.selector, Mainnet.STRATEGIST, Mainnet.MERKLE_DISTRIBUTOR + ); + morphoMarketProxy.initialize(address(morphoMarket), Mainnet.TIMELOCK, data); + + // 13. Set crossPrice to 0.9998 USDe + uint256 crossPrice = 0.9998 * 1e36; + EthenaARM(payable(address(armProxy))).setCrossPrice(crossPrice); + + // 14. Add Morpho Market as an active market + address[] memory markets = new address[](1); + markets[0] = address(morphoMarketProxy); + EthenaARM(payable(address(armProxy))).addMarkets(markets); + + // 15. Set Morpho Market as the active market + EthenaARM(payable(address(armProxy))).setActiveMarket(address(morphoMarketProxy)); + + // 16. Set ARM buffer to 10% + EthenaARM(payable(address(armProxy))).setARMBuffer(0.1e18); // 10% buffer + + // 17. Transfer ownership of ARM to the 5/8 multisig + armProxy.setOwner(Mainnet.GOV_MULTISIG); + + console.log("Finished deploying", DEPLOY_NAME); + } +} diff --git a/src/contracts/utils/Addresses.sol b/src/contracts/utils/Addresses.sol index cd67ec18..394855cd 100644 --- a/src/contracts/utils/Addresses.sol +++ b/src/contracts/utils/Addresses.sol @@ -32,6 +32,8 @@ library Mainnet { address public constant STETH = 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84; address public constant WSTETH = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0; address public constant MORPHO = 0x58D97B57BB95320F9a05dC918Aef65434969c2B2; + address public constant USDE = 0x4c9EDD5852cd905f086C759E8383e09bff1E68B3; + address public constant SUSDE = 0x9D39A5DE30e57443BfF2A8307A4256c8797A3497; // Contracts address public constant OETH_VAULT = 0x39254033945AA2E4809Cc2977E7087BEE48bd7Ab; @@ -54,6 +56,8 @@ library Mainnet { // Morpho Market address public constant MORPHO_MARKET_MEVCAPITAL = 0x9a8bC3B04b7f3D87cfC09ba407dCED575f2d61D8; address public constant MORPHO_MARKET_ETHERFI = 0x4881Ef0BF6d2365D3dd6499ccd7532bcdBCE0658; + // Apostro Ethena USDe is currently the only curated Morpho market that takes USDe + address public constant MORPHO_MARKET_ETHENA = 0x4EDfaB296F8Eb15aC0907CF9eCb7079b1679Da57; // Merkle Distributor address public constant MERKLE_DISTRIBUTOR = 0x3Ef3D8bA38EBe18DB133cEc108f4D14CE00Dd9Ae; From 9ced90ef5ce5849c396ced056105a0ae33db386f Mon Sep 17 00:00:00 2001 From: Nicholas Addison Date: Wed, 12 Nov 2025 16:48:06 +1100 Subject: [PATCH 06/13] Fix build --- script/deploy/mainnet/014_DeployEthenaARMScript.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/deploy/mainnet/014_DeployEthenaARMScript.sol b/script/deploy/mainnet/014_DeployEthenaARMScript.sol index 5e87c93c..dcd2002b 100644 --- a/script/deploy/mainnet/014_DeployEthenaARMScript.sol +++ b/script/deploy/mainnet/014_DeployEthenaARMScript.sol @@ -18,7 +18,7 @@ import {Abstract4626MarketWrapper} from "contracts/markets/Abstract4626MarketWra import {GovProposal, GovSixHelper} from "contracts/utils/GovSixHelper.sol"; import {AbstractDeployScript} from "../AbstractDeployScript.sol"; -contract DeployEtherFiARMScript is AbstractDeployScript { +contract DeployEthenaARMScript is AbstractDeployScript { using GovSixHelper for GovProposal; GovProposal public govProposal; From 292a867735662b7ca4e559bccab55fb059feba3f Mon Sep 17 00:00:00 2001 From: Nicholas Addison Date: Wed, 12 Nov 2025 20:36:10 +1100 Subject: [PATCH 07/13] Refactored EthenaARM so there can be multiple cooldown requests --- src/contracts/AbstractARM.sol | 3 ++- src/contracts/EthenaARM.sol | 46 ++++++++++++++++++++------------ src/contracts/EthenaUnstaker.sol | 45 +++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 18 deletions(-) create mode 100644 src/contracts/EthenaUnstaker.sol diff --git a/src/contracts/AbstractARM.sol b/src/contracts/AbstractARM.sol index b3712557..bc87ff09 100644 --- a/src/contracts/AbstractARM.sol +++ b/src/contracts/AbstractARM.sol @@ -748,7 +748,8 @@ abstract contract AbstractARM is OwnableOperable, ERC20Upgradeable { } /// @dev Hook for calculating the amount of assets in an external withdrawal queue like Lido or OETH - /// This is not the ARM's withdrawal queue + /// @return assets The amount of liquidity assets in the external withdrawal queue. eg WETH or wS + /// This is not the ARM's withdrawal queue. function _externalWithdrawQueue() internal view virtual returns (uint256 assets); /// @notice Calculates the amount of shares for a given amount of liquidity assets diff --git a/src/contracts/EthenaARM.sol b/src/contracts/EthenaARM.sol index 62d57f3e..373a5f8c 100644 --- a/src/contracts/EthenaARM.sol +++ b/src/contracts/EthenaARM.sol @@ -5,6 +5,7 @@ import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Ini import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; import {AbstractARM} from "./AbstractARM.sol"; +import {EthenaUnstaker} from "./EthenaUnstaker.sol"; import {IERC20, IStakedUSDe, UserCooldown} from "./Interfaces.sol"; /** @@ -17,8 +18,10 @@ contract EthenaARM is Initializable, AbstractARM { /// @notice The address of Ethena's staked synthetic dollar token (sUSDe) IStakedUSDe public immutable susde; - event RequestBaseWithdrawal(uint256 amount); - event ClaimBaseWithdrawals(uint256 liquidityAmountClaimed); + uint256 internal _liquidityAmountInCooldown; + + event RequestBaseWithdrawal(address indexed unstaker, uint256 baseAmount, uint256 liquidityAmount); + event ClaimBaseWithdrawals(address indexed unstaker, uint256 liquidityAmount); /// @param _usde The address of Ethena's synthetic dollar token (USDe) /// @param _susde The address of Ethena's staked synthetic dollar token (sUSDe) @@ -65,38 +68,47 @@ contract EthenaARM is Initializable, AbstractARM { * @param baseAmount The amount of base assets (sUSDe) to withdraw. */ function requestBaseWithdrawal(uint256 baseAmount) external onlyOperatorOrOwner { - // For now put in a restriction that only one request can be outstanding at a time. - // TODO replace this with a pool of helper contracts that can manage multiple cooldowns in parallel. - UserCooldown memory cooldown = susde.cooldowns(address(this)); - require(cooldown.underlyingAmount == 0, "EthenaARM: Existing cooldown"); + // Deploy a new EthenaUnstaker helper contract + EthenaUnstaker unstaker = new EthenaUnstaker(address(this), susde); + + // Transfer sUSDe to the helper contract + susde.transfer(address(unstaker), baseAmount); - // Request an unstaked from Ethena's staked USDe (sUSDe) contract - susde.cooldownShares(baseAmount); + uint256 liquidityAmount = unstaker.requestUnstake(baseAmount); + + _liquidityAmountInCooldown += liquidityAmount; // Emit event for the request - emit RequestBaseWithdrawal(baseAmount); + emit RequestBaseWithdrawal(address(unstaker), baseAmount, liquidityAmount); } /** * @notice Claim all the USDe that is now claimable from the Staked USDe contract. * Reverts with `InvalidCooldown` from the Staked USDe contract if the cooldown period has not yet passed. */ - function claimBaseWithdrawals() external { - UserCooldown memory cooldown = susde.cooldowns(address(this)); + function claimBaseWithdrawals(address unstaker) external { + uint256 cooldownAmount = EthenaUnstaker(unstaker).cooldownAmount(); + require(cooldownAmount > 0, "EthenaARM: No cooldown amount"); - susde.unstake(address(this)); + if (_liquidityAmountInCooldown < cooldownAmount) { + _liquidityAmountInCooldown = 0; + } else { + _liquidityAmountInCooldown -= cooldownAmount; + } + + // Claim all the underlying USDe that has cooled down for the unstaker and send to the ARM + EthenaUnstaker(unstaker).claimUnstake(); - emit ClaimBaseWithdrawals(cooldown.underlyingAmount); + emit ClaimBaseWithdrawals(unstaker, cooldownAmount); } /** - * @dev Gets the amount of USDe waiting to be claimed from the Staked USDe contract. + * @dev Gets the total amount of USDe waiting to be claimed from the Staked USDe contract. + * This can be for many different cooldowns. * This can be either in the cooldown period or ready to be claimed. */ function _externalWithdrawQueue() internal view override returns (uint256) { - UserCooldown memory cooldown = susde.cooldowns(address(this)); - - return cooldown.underlyingAmount; + return _liquidityAmountInCooldown; } /// @dev Convert between base asset (sUSDe) and liquidity asset (USDe). diff --git a/src/contracts/EthenaUnstaker.sol b/src/contracts/EthenaUnstaker.sol new file mode 100644 index 00000000..f94712dc --- /dev/null +++ b/src/contracts/EthenaUnstaker.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.23; + +import {IStakedUSDe, UserCooldown} from "./Interfaces.sol"; + +/** + * @title A helper contract that allows the ARM to have multiple sUSDe cooldowns in parallel. + * @author Origin Protocol Inc + */ +contract EthenaUnstaker { + /// The parent Ethena ARM contract + address public immutable arm; + /// @notice The address of Ethena's staked synthetic dollar token (sUSDe) + IStakedUSDe public immutable susde; + + constructor(address _arm, IStakedUSDe _susde) { + arm = _arm; + susde = _susde; + } + + /** + * @notice Request a cooldown of USDe from Ethena's Staked USDe (sUSDe) contract. + * @param sUSDeAmount The amount of staked USDe (sUSDe) to withdraw. + * @return usde The amount of underlying USDe that will be withdrawable after the cooldown period. + */ + function requestUnstake(uint256 sUSDeAmount) external returns (uint256 usde) { + require(msg.sender == arm, "Only ARM can request unstake"); + usde = susde.cooldownShares(sUSDeAmount); + } + + /** + * @notice Claim the underlying USDe after the cooldown period has ended and send to the ARM. + * Reverts with `InvalidCooldown` from the Staked USDe contract if the cooldown period has not yet passed. + */ + function claimUnstake() external { + require(msg.sender == arm, "Only ARM can request unstake"); + susde.unstake(arm); + } + + function cooldownAmount() external view returns (uint256) { + UserCooldown memory cooldown = susde.cooldowns(address(this)); + + return cooldown.underlyingAmount; + } +} From 642e38871fefecab0dcc1cdc5db846b8f150f366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment?= <55331875+clement-ux@users.noreply.github.com> Date: Thu, 13 Nov 2025 05:08:17 +0100 Subject: [PATCH 08/13] Add Test for Ethena ARM (#149) * Add EthenaARM integration tests and shared setup * Rename test file * Add integration tests for swapTokensForExactTokens functionality in EthenaARM * Refactor swapExactTokensForTokens tests for clarity and correctness * Remove unused IERC721Receiver import from EthenaARM contract --- src/contracts/EthenaARM.sol | 1 - test/Base.sol | 11 ++ .../EthenaARM/SwapExactTokensForTokens.t.sol | 171 +++++++++++++++++ .../EthenaARM/SwapTokensForExactTokens.t.sol | 174 ++++++++++++++++++ test/fork/EthenaARM/shared/Shared.sol | 118 ++++++++++++ 5 files changed, 474 insertions(+), 1 deletion(-) create mode 100644 test/fork/EthenaARM/SwapExactTokensForTokens.t.sol create mode 100644 test/fork/EthenaARM/SwapTokensForExactTokens.t.sol create mode 100644 test/fork/EthenaARM/shared/Shared.sol diff --git a/src/contracts/EthenaARM.sol b/src/contracts/EthenaARM.sol index 373a5f8c..1cdd3313 100644 --- a/src/contracts/EthenaARM.sol +++ b/src/contracts/EthenaARM.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.23; import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; -import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; import {AbstractARM} from "./AbstractARM.sol"; import {EthenaUnstaker} from "./EthenaUnstaker.sol"; diff --git a/test/Base.sol b/test/Base.sol index 234a59f3..0cd1e45d 100644 --- a/test/Base.sol +++ b/test/Base.sol @@ -8,6 +8,7 @@ import {Test} from "forge-std/Test.sol"; import {Proxy} from "contracts/Proxy.sol"; import {OethARM} from "contracts/OethARM.sol"; import {LidoARM} from "contracts/LidoARM.sol"; +import {EthenaARM} from "contracts/EthenaARM.sol"; import {EtherFiARM} from "contracts/EtherFiARM.sol"; import {OriginARM} from "contracts/OriginARM.sol"; import {SonicHarvester} from "contracts/SonicHarvester.sol"; @@ -40,12 +41,14 @@ abstract contract Base_Test_ is Test { Proxy public proxy; Proxy public lpcProxy; Proxy public lidoProxy; + Proxy public ethenaProxy; Proxy public etherfiProxy; Proxy public originARMProxy; Proxy public harvesterProxy; Proxy public morphoMarketProxy; OethARM public oethARM; LidoARM public lidoARM; + EthenaARM public ethenaARM; EtherFiARM public etherfiARM; SonicHarvester public harvester; OriginARM public originARM; @@ -57,6 +60,7 @@ abstract contract Base_Test_ is Test { IERC20 public ws; IERC20 public os; IERC20 public wos; + IERC20 public usde; IERC20 public oeth; IERC20 public weth; IERC20 public eeth; @@ -65,6 +69,7 @@ abstract contract Base_Test_ is Test { IERC20 public wsteth; IERC20 public morpho; IERC20 public badToken; + IERC4626 public susde; IERC4626 public market; IERC4626 public market2; IOriginVault public vault; @@ -108,6 +113,7 @@ abstract contract Base_Test_ is Test { _labelNotNull(address(proxy), "DEFAULT PROXY"); _labelNotNull(address(lpcProxy), "LPC PROXY"); _labelNotNull(address(lidoProxy), "LIDO ARM PROXY"); + _labelNotNull(address(ethenaProxy), "ETHENA ARM PROXY"); _labelNotNull(address(etherfiProxy), "ETHERFI ARM PROXY"); _labelNotNull(address(oethARM), "OETH ARM"); _labelNotNull(address(lidoARM), "LIDO ARM"); @@ -118,6 +124,8 @@ abstract contract Base_Test_ is Test { _labelNotNull(address(ws), "WS"); _labelNotNull(address(os), "OS"); + _labelNotNull(address(wos), "WOS"); + _labelNotNull(address(usde), "USDE"); _labelNotNull(address(oeth), "OETH"); _labelNotNull(address(weth), "WETH"); _labelNotNull(address(eeth), "EETH"); @@ -127,6 +135,9 @@ abstract contract Base_Test_ is Test { _labelNotNull(address(wsteth), " WRAPPED STETH"); _labelNotNull(address(badToken), "BAD TOKEN"); _labelNotNull(address(vault), "OETH VAULT"); + _labelNotNull(address(market), "MARKET"); + _labelNotNull(address(market2), "MARKET 2"); + _labelNotNull(address(susde), "SUSDE"); // Governance, multisig and EOAs _labelNotNull(alice, "Alice"); diff --git a/test/fork/EthenaARM/SwapExactTokensForTokens.t.sol b/test/fork/EthenaARM/SwapExactTokensForTokens.t.sol new file mode 100644 index 00000000..18fd27b6 --- /dev/null +++ b/test/fork/EthenaARM/SwapExactTokensForTokens.t.sol @@ -0,0 +1,171 @@ +/// SPDX-License-Identifier: MIT +pragma solidity 0.8.23; + +// Test +import {Fork_Shared_Test} from "test/fork/EthenaARM/shared/Shared.sol"; + +// Contracts +import {EthenaARM} from "contracts/EthenaARM.sol"; + +// Interfaces +import {IERC20} from "contracts/Interfaces.sol"; + +contract Fork_Concrete_EthenaARM_swapExactTokensForTokens_Test_ is Fork_Shared_Test { + uint256 public AMOUNT_IN = 100 ether; + + ////////////////////////////////////////////////////// + /// --- TESTS + ////////////////////////////////////////////////////// + function test_swapExactTokensForTokens_USDE_To_SUSDE_Sig1() public { + // Record balances before swap + uint256 usdeBalanceBefore = usde.balanceOf(address(this)); + uint256 susdeBalanceBefore = susde.balanceOf(address(this)); + + // Precompute expected amount out + uint256 traderate = ethenaARM.traderate0(); + uint256 expectedAmountOut = (susde.convertToShares(AMOUNT_IN) * 1e36) / traderate; + + // Expected events + vm.expectEmit({emitter: address(usde)}); + emit IERC20.Transfer(address(this), address(ethenaARM), AMOUNT_IN); + vm.expectEmit({emitter: address(susde)}); + emit IERC20.Transfer(address(ethenaARM), address(this), expectedAmountOut); + + // Perform the swap + uint256[] memory obtained = + ethenaARM.swapExactTokensForTokens(usde, IERC20(address(susde)), AMOUNT_IN, 0, address(this)); + + // Record balances after swap + uint256 usdeBalanceAfter = usde.balanceOf(address(this)); + uint256 susdeBalanceAfter = susde.balanceOf(address(this)); + + // Assertions + assertEq(obtained[0], AMOUNT_IN, "Obtained USDe amount should match input"); + assertEq(obtained[1], expectedAmountOut, "Obtained SUSDe amount should match expected output"); + assertEq(usdeBalanceBefore, usdeBalanceAfter + AMOUNT_IN, "USDe balance should have decreased"); + assertEq(susdeBalanceAfter, susdeBalanceBefore + expectedAmountOut, "SUSDe balance should have increased"); + } + + function test_swapExactTokensForTokens_USDE_To_SUSDE_Sig2() public { + // Record balances before swap + uint256 usdeBalanceBefore = usde.balanceOf(address(this)); + uint256 susdeBalanceBefore = susde.balanceOf(address(this)); + + // Precompute expected amount out + uint256 traderate = ethenaARM.traderate0(); + uint256 expectedAmountOut = (susde.convertToShares(AMOUNT_IN) * 1e36) / traderate; + + // Expected events + vm.expectEmit({emitter: address(usde)}); + emit IERC20.Transfer(address(this), address(ethenaARM), AMOUNT_IN); + vm.expectEmit({emitter: address(susde)}); + emit IERC20.Transfer(address(ethenaARM), address(this), expectedAmountOut); + + // Perform the swap + address[] memory path = new address[](2); + path[0] = address(usde); + path[1] = address(susde); + + uint256[] memory obtained = + ethenaARM.swapExactTokensForTokens(AMOUNT_IN, 0, path, address(this), block.timestamp + 1 hours); + + // Record balances after swap + uint256 usdeBalanceAfter = usde.balanceOf(address(this)); + uint256 susdeBalanceAfter = susde.balanceOf(address(this)); + + // Assertions + assertEq(obtained[0], AMOUNT_IN, "Obtained USDe amount should match input"); + assertEq(obtained[1], expectedAmountOut, "Obtained SUSDe amount should match expected output"); + assertEq(usdeBalanceBefore, usdeBalanceAfter + AMOUNT_IN, "USDe balance should have decreased"); + assertEq(susdeBalanceAfter, susdeBalanceBefore + expectedAmountOut, "SUSDe balance should have increased"); + } + + function test_swapExactTokensForTokens_SUSDE_To_USDE_NoOutstandingWithdrawals_Sig1() public { + // Record balances before swap + uint256 usdeBalanceBefore = usde.balanceOf(address(this)); + uint256 susdeBalanceBefore = susde.balanceOf(address(this)); + + // Precompute expected amount out + uint256 traderate = ethenaARM.traderate1(); + uint256 expectedAmountOut = (susde.convertToAssets(AMOUNT_IN) * traderate) / 1e36; + + // Expected events + vm.expectEmit({emitter: address(susde)}); + emit IERC20.Transfer(address(this), address(ethenaARM), AMOUNT_IN); + vm.expectEmit({emitter: address(usde)}); + emit IERC20.Transfer(address(ethenaARM), address(this), expectedAmountOut); + + // Perform the swap + uint256[] memory obtained = + ethenaARM.swapExactTokensForTokens(IERC20(address(susde)), usde, AMOUNT_IN, 0, address(this)); + + // Record balances after swap + uint256 usdeBalanceAfter = usde.balanceOf(address(this)); + uint256 susdeBalanceAfter = susde.balanceOf(address(this)); + + // Assertions + assertEq(obtained[0], AMOUNT_IN, "Obtained SUSDe amount should match input"); + assertEq(obtained[1], expectedAmountOut, "Obtained USDe amount should match expected output"); + assertEq(usdeBalanceAfter, usdeBalanceBefore + expectedAmountOut, "USDe balance should have increased"); + assertEq(susdeBalanceBefore, susdeBalanceAfter + AMOUNT_IN, "SUSDe balance should have decreased"); + } + + ////////////////////////////////////////////////////// + /// --- REVERTING TESTS + ////////////////////////////////////////////////////// + function test_RevertWhen_swapExactTokensForTokens_Because_InvalidInToken() public { + vm.expectRevert(bytes("EthenaARM: Invalid token")); + ethenaARM.swapExactTokensForTokens(badToken, usde, AMOUNT_IN, 0, address(this)); + } + + function test_RevertWhen_swapExactTokensForTokens_Because_InvalidOutToken() public { + vm.expectRevert(bytes("ARM: Invalid out token")); + ethenaARM.swapExactTokensForTokens(usde, badToken, AMOUNT_IN, 0, address(this)); + + vm.expectRevert(bytes("ARM: Invalid out token")); + ethenaARM.swapExactTokensForTokens(IERC20(address(susde)), badToken, AMOUNT_IN, 0, address(this)); + } + + function test_RevertWhen_swapExactTokensForTokens_Because_InsufficientOutputAmount() public { + uint256 highMinAmountOut = 1_000_000 ether; + + vm.expectRevert(bytes("ARM: Insufficient output amount")); + ethenaARM.swapExactTokensForTokens(IERC20(address(susde)), usde, AMOUNT_IN, highMinAmountOut, address(this)); + + vm.expectRevert(bytes("ARM: Insufficient output amount")); + ethenaARM.swapExactTokensForTokens(usde, IERC20(address(susde)), AMOUNT_IN, highMinAmountOut, address(this)); + + address[] memory path = new address[](2); + path[0] = address(usde); + path[1] = address(susde); + + vm.expectRevert(bytes("ARM: Insufficient output amount")); + ethenaARM.swapExactTokensForTokens(AMOUNT_IN, highMinAmountOut, path, address(this), block.timestamp + 1 hours); + } + + function test_RevertWhen_swapExactTokensForTokens_Because_DeadlineExpired() public { + uint256 pastDeadline = block.timestamp - 1; + address[] memory path = new address[](2); + path[0] = address(susde); + path[1] = address(usde); + + vm.expectRevert(bytes("ARM: Deadline expired")); + ethenaARM.swapExactTokensForTokens(AMOUNT_IN, 0, path, address(this), pastDeadline); + } + + function test_RevertWhen_swapExactTokensForTokens_Because_InvalidePathLength() public { + address[] memory shortPath = new address[](1); + shortPath[0] = address(susde); + + vm.expectRevert(bytes("ARM: Invalid path length")); + ethenaARM.swapExactTokensForTokens(AMOUNT_IN, 0, shortPath, address(this), block.timestamp + 1 hours); + + address[] memory longPath = new address[](3); + longPath[0] = address(susde); + longPath[1] = address(usde); + longPath[2] = address(susde); + + vm.expectRevert(bytes("ARM: Invalid path length")); + ethenaARM.swapExactTokensForTokens(AMOUNT_IN, 0, longPath, address(this), block.timestamp + 1 hours); + } +} diff --git a/test/fork/EthenaARM/SwapTokensForExactTokens.t.sol b/test/fork/EthenaARM/SwapTokensForExactTokens.t.sol new file mode 100644 index 00000000..1923988d --- /dev/null +++ b/test/fork/EthenaARM/SwapTokensForExactTokens.t.sol @@ -0,0 +1,174 @@ +/// SPDX-License-Identifier: MIT +pragma solidity 0.8.23; + +// Test +import {Fork_Shared_Test} from "test/fork/EthenaARM/shared/Shared.sol"; + +// Contracts +import {EthenaARM} from "contracts/EthenaARM.sol"; + +// Interfaces +import {IERC20} from "contracts/Interfaces.sol"; + +contract Fork_Concrete_EthenaARM_swapTokensForExactTokens_Test_ is Fork_Shared_Test { + uint256 public AMOUNT_OUT = 100 ether; + + ////////////////////////////////////////////////////// + /// --- TESTS + ////////////////////////////////////////////////////// + function test_swapTokensForExactTokens_USDE_To_SUSDE_Sig1() public { + // Record balances before swap + uint256 usdeBalanceBefore = usde.balanceOf(address(this)); + uint256 susdeBalanceBefore = susde.balanceOf(address(this)); + + // Precompute expected amount out + uint256 traderate = ethenaARM.traderate0(); + uint256 expectedAmountIn = ((susde.convertToAssets(AMOUNT_OUT) * 1e36) / traderate) + 3; + + // Expected events + vm.expectEmit({emitter: address(usde)}); + emit IERC20.Transfer(address(this), address(ethenaARM), expectedAmountIn); + vm.expectEmit({emitter: address(susde)}); + emit IERC20.Transfer(address(ethenaARM), address(this), AMOUNT_OUT); + + // Perform the swap + uint256[] memory obtained = ethenaARM.swapTokensForExactTokens( + usde, IERC20(address(susde)), AMOUNT_OUT, type(uint256).max, address(this) + ); + + // Record balances after swap + uint256 usdeBalanceAfter = usde.balanceOf(address(this)); + uint256 susdeBalanceAfter = susde.balanceOf(address(this)); + + // Assertions + assertEq(obtained[0], expectedAmountIn, "Obtained USDe amount should match expected input"); + assertEq(obtained[1], AMOUNT_OUT, "Obtained SUSDe amount should match expected output"); + assertEq(usdeBalanceBefore, usdeBalanceAfter + expectedAmountIn, "USDe balance should have decreased"); + assertEq(susdeBalanceAfter, susdeBalanceBefore + AMOUNT_OUT, "SUSDe balance should have increased"); + } + + function test_swapTokensForExactTokens_USDE_To_SUSDE_Sig2() public { + // Record balances before swap + uint256 usdeBalanceBefore = usde.balanceOf(address(this)); + uint256 susdeBalanceBefore = susde.balanceOf(address(this)); + + // Precompute expected amount out + uint256 traderate = ethenaARM.traderate0(); + uint256 expectedAmountIn = ((susde.convertToAssets(AMOUNT_OUT) * 1e36) / traderate) + 3; + + address[] memory path = new address[](2); + path[0] = address(usde); + path[1] = address(susde); + + // Expected events + vm.expectEmit({emitter: address(usde)}); + emit IERC20.Transfer(address(this), address(ethenaARM), expectedAmountIn); + vm.expectEmit({emitter: address(susde)}); + emit IERC20.Transfer(address(ethenaARM), address(this), AMOUNT_OUT); + + // Perform the swap + uint256[] memory obtained = ethenaARM.swapTokensForExactTokens( + AMOUNT_OUT, type(uint256).max, path, address(this), block.timestamp + 1 hours + ); + + // Record balances after swap + uint256 usdeBalanceAfter = usde.balanceOf(address(this)); + uint256 susdeBalanceAfter = susde.balanceOf(address(this)); + + // Assertions + assertEq(obtained[0], expectedAmountIn, "Obtained USDe amount should match expected input"); + assertEq(obtained[1], AMOUNT_OUT, "Obtained SUSDe amount should match expected output"); + assertEq(usdeBalanceBefore, usdeBalanceAfter + expectedAmountIn, "USDe balance should have decreased"); + assertEq(susdeBalanceAfter, susdeBalanceBefore + AMOUNT_OUT, "SUSDe balance should have increased"); + } + + function test_swapTokensForExactTokens_SUSDE_To_USDE_NoOutstandingWithdrawals_Sig1() public { + // Record balances before swap + uint256 usdeBalanceBefore = usde.balanceOf(address(this)); + uint256 susdeBalanceBefore = susde.balanceOf(address(this)); + + // Precompute expected amount out + uint256 traderate = ethenaARM.traderate1(); + uint256 expectedAmountIn = (susde.convertToShares(AMOUNT_OUT) * 1e36) / traderate + 3; + + // Expected events + vm.expectEmit({emitter: address(susde)}); + emit IERC20.Transfer(address(this), address(ethenaARM), expectedAmountIn); + vm.expectEmit({emitter: address(usde)}); + emit IERC20.Transfer(address(ethenaARM), address(this), AMOUNT_OUT); + + // Perform the swap + uint256[] memory obtained = ethenaARM.swapTokensForExactTokens( + IERC20(address(susde)), usde, AMOUNT_OUT, type(uint256).max, address(this) + ); + + // Record balances after swap + uint256 usdeBalanceAfter = usde.balanceOf(address(this)); + uint256 susdeBalanceAfter = susde.balanceOf(address(this)); + + // Assertions + assertEq(obtained[0], expectedAmountIn, "Obtained USDe amount should match expected input"); + assertEq(obtained[1], AMOUNT_OUT, "Obtained SUSDe amount should match expected output"); + assertEq(susdeBalanceBefore, susdeBalanceAfter + expectedAmountIn, "SUSDe balance should have decreased"); + assertEq(usdeBalanceAfter, usdeBalanceBefore + AMOUNT_OUT, "USDe balance should have increased"); + } + + ////////////////////////////////////////////////////// + /// --- REVERTING TESTS + ////////////////////////////////////////////////////// + function test_RevertWhen_swapTokensForExactTokens_Because_InvalidInToken() public { + vm.expectRevert(bytes("ARM: Invalid in token")); + ethenaARM.swapTokensForExactTokens(badToken, usde, AMOUNT_OUT, 0, address(this)); + } + + function test_RevertWhen_swapTokensForExactTokens_Because_InvalidOutToken() public { + vm.expectRevert(bytes("EthenaARM: Invalid token")); + ethenaARM.swapTokensForExactTokens(usde, badToken, AMOUNT_OUT, 0, address(this)); + + vm.expectRevert(bytes("EthenaARM: Invalid token")); + ethenaARM.swapTokensForExactTokens(IERC20(address(susde)), badToken, AMOUNT_OUT, 0, address(this)); + } + + function test_RevertWhen_swapTokensForExactTokens_Because_InsufficientOutputAmount() public { + uint256 lowMaxAmountIn = 10 ether; + + vm.expectRevert(bytes("ARM: Excess input amount")); + ethenaARM.swapTokensForExactTokens(IERC20(address(susde)), usde, AMOUNT_OUT, lowMaxAmountIn, address(this)); + + vm.expectRevert(bytes("ARM: Excess input amount")); + ethenaARM.swapTokensForExactTokens(usde, IERC20(address(susde)), AMOUNT_OUT, lowMaxAmountIn, address(this)); + + address[] memory path = new address[](2); + path[0] = address(usde); + path[1] = address(susde); + + vm.expectRevert(bytes("ARM: Excess input amount")); + ethenaARM.swapTokensForExactTokens(AMOUNT_OUT, lowMaxAmountIn, path, address(this), block.timestamp + 1 hours); + } + + function test_RevertWhen_swapTokensForExactTokens_Because_DeadlineExpired() public { + uint256 pastDeadline = block.timestamp - 1; + address[] memory path = new address[](2); + path[0] = address(susde); + path[1] = address(usde); + + vm.expectRevert(bytes("ARM: Deadline expired")); + ethenaARM.swapTokensForExactTokens(AMOUNT_OUT, type(uint256).max, path, address(this), pastDeadline); + } + + function test_RevertWhen_swapTokensForExactTokens_Because_InvalidePathLength() public { + address[] memory shortPath = new address[](1); + shortPath[0] = address(susde); + + vm.expectRevert(bytes("ARM: Invalid path length")); + ethenaARM.swapTokensForExactTokens(AMOUNT_OUT, 0, shortPath, address(this), block.timestamp + 1 hours); + + address[] memory longPath = new address[](3); + longPath[0] = address(susde); + longPath[1] = address(usde); + longPath[2] = address(susde); + + vm.expectRevert(bytes("ARM: Invalid path length")); + ethenaARM.swapTokensForExactTokens(AMOUNT_OUT, 0, longPath, address(this), block.timestamp + 1 hours); + } +} diff --git a/test/fork/EthenaARM/shared/Shared.sol b/test/fork/EthenaARM/shared/Shared.sol new file mode 100644 index 00000000..9ce3de24 --- /dev/null +++ b/test/fork/EthenaARM/shared/Shared.sol @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.23; + +// Test +import {Base_Test_} from "test/Base.sol"; + +// Contracts +import {Proxy} from "contracts/Proxy.sol"; +import {EthenaARM} from "contracts/EthenaARM.sol"; + +// Interfaces +import {Mainnet} from "src/contracts/utils/Addresses.sol"; +import {IERC20, IERC4626} from "contracts/Interfaces.sol"; + +abstract contract Fork_Shared_Test is Base_Test_ { + ////////////////////////////////////////////////////// + /// --- SETUP + ////////////////////////////////////////////////////// + function setUp() public virtual override { + super.setUp(); + + // Generate a fork + _createAndSelectFork(); + + // Deploy Mock contracts + _deployMockContracts(); + + // Generate addresses + _generateAddresses(); + + // Deploy contracts + _deployContracts(); + + // Ignite test contract + _ignite(); + + // Label contracts + labelAll(); + } + + function _createAndSelectFork() internal { + // Check if the PROVIDER_URL is set. + require(vm.envExists("PROVIDER_URL"), "PROVIDER_URL not set"); + + // Create and select a fork. + if (vm.envExists("FORK_BLOCK_NUMBER_MAINNET")) { + vm.createSelectFork("mainnet", vm.envUint("FORK_BLOCK_NUMBER_MAINNET")); + } else { + vm.createSelectFork("mainnet"); + } + } + + function _deployMockContracts() internal { + usde = IERC20(Mainnet.USDE); + susde = IERC4626(Mainnet.SUSDE); + badToken = IERC20(address(0xDEADBEEF)); + } + + function _generateAddresses() internal { + // Generate addresses + governor = makeAddr("governor"); + deployer = makeAddr("deployer"); + operator = makeAddr("operator"); + feeCollector = makeAddr("feeCollector"); + } + + function _deployContracts() internal { + vm.startPrank(deployer); + // 1. Deploy Ethena ARM + ethenaARM = new EthenaARM({ + _usde: address(usde), + _susde: address(susde), + _claimDelay: 10 minutes, + _minSharesToRedeem: 1e7, + _allocateThreshold: 1 ether + }); + + // 2. Deploy Ethena ARM Proxy + ethenaProxy = new Proxy(); + + // Fund deployer with USDe and approve proxy to pull USDe for initialization + deal(address(usde), deployer, 1e12); + usde.approve(address(ethenaProxy), 1e12); + + // 3. Initialize Ethena ARM Proxy + bytes memory data = abi.encodeWithSelector( + EthenaARM.initialize.selector, + "Ethena Staked USDe ARM", + "ARM-sUSDe-USDe", + operator, // operator + 2000, // 20% fee + feeCollector, // feeCollector + address(0) // capManager + ); + + ethenaProxy.initialize(address(ethenaARM), governor, data); + vm.stopPrank(); + + // Assign Ethena ARM instance + ethenaARM = EthenaARM(address(ethenaProxy)); + } + + function _ignite() internal virtual { + // Assign contract instances + deal(address(usde), address(this), 1_000_000 ether); + deal(address(susde), address(this), 1_000_000 ether); + + // Approve USDe and SUSDe to Ethena ARM + usde.approve(address(ethenaARM), type(uint256).max); + susde.approve(address(ethenaARM), type(uint256).max); + + // Deposit some usde in the ARM + ethenaARM.deposit(10_000 ether); + + // Swap usde to susde using ARM to have some susde balance + ethenaARM.swapExactTokensForTokens(IERC20(address(susde)), usde, 5_000 ether, 0, address(this)); + } +} From 4c11c78e62a83c4ccf50f7c65fd056c081f23cc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment?= <55331875+clement-ux@users.noreply.github.com> Date: Thu, 13 Nov 2025 11:08:54 +0100 Subject: [PATCH 09/13] Ethena ARM: Unstake request v1 (#150) * Enhance EthenaARM to support multiple unstaker contracts for parallel cooldown requests * Fix type casting for last used unstaker index to ensure proper indexing * Simplify array management * Implement request delay for unstake requests and update visibility of state variables * Refactor comments in EthenaARM contract for clarity and consistency * Remove unused imports and clean up comments in EthenaARM contract * Refactor EthenaARM contract to use constant for maximum unstakers and update related code for clarity --- src/contracts/EthenaARM.sol | 63 ++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/src/contracts/EthenaARM.sol b/src/contracts/EthenaARM.sol index 1cdd3313..f440c298 100644 --- a/src/contracts/EthenaARM.sol +++ b/src/contracts/EthenaARM.sol @@ -5,19 +5,30 @@ import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Ini import {AbstractARM} from "./AbstractARM.sol"; import {EthenaUnstaker} from "./EthenaUnstaker.sol"; -import {IERC20, IStakedUSDe, UserCooldown} from "./Interfaces.sol"; +import {IERC20, IStakedUSDe} from "./Interfaces.sol"; /** * @title Ethena sUSDe/USDe Automated Redemption Manager (ARM) * @author Origin Protocol Inc */ contract EthenaARM is Initializable, AbstractARM { + /// @notice The delay before a new unstake request can be made + uint256 public constant DELAY_REQUEST = 3 hours; + /// @notice The maximum number of unstaker helper contracts + uint8 public constant MAX_UNSTAKERS = 42; /// @notice The address of Ethena's synthetic dollar token (USDe) IERC20 public immutable usde; /// @notice The address of Ethena's staked synthetic dollar token (sUSDe) IStakedUSDe public immutable susde; + /// @notice The total amount of liquidity asset (USDe) currently in cooldown uint256 internal _liquidityAmountInCooldown; + /// @notice Array of unstaker helper contracts + address[MAX_UNSTAKERS] internal unstakers; + /// @notice The index of the next unstaker to use in the round robin + uint8 public nextUnstakerIndex; + /// @notice The timestamp of the last request made + uint32 public lastRequestTimestamp; event RequestBaseWithdrawal(address indexed unstaker, uint256 baseAmount, uint256 liquidityAmount); event ClaimBaseWithdrawals(address indexed unstaker, uint256 liquidityAmount); @@ -62,29 +73,38 @@ contract EthenaARM is Initializable, AbstractARM { _initARM(_operator, _name, _symbol, _fee, _feeCollector, _capManager); } - /** - * @notice Request a cooldown of USDe from Ethena's Staked USDe (sUSDe) contract. - * @param baseAmount The amount of base assets (sUSDe) to withdraw. - */ + /// @notice Request a cooldown of USDe from Ethena's Staked USDe (sUSDe) contract. + /// @dev Uses a round robin to select the next unstaker helper contract. + /// @param baseAmount The amount of staked USDe (sUSDe) to withdraw. function requestBaseWithdrawal(uint256 baseAmount) external onlyOperatorOrOwner { - // Deploy a new EthenaUnstaker helper contract - EthenaUnstaker unstaker = new EthenaUnstaker(address(this), susde); + require(block.timestamp >= lastRequestTimestamp + DELAY_REQUEST, "EthenaARM: Request delay not passed"); + lastRequestTimestamp = uint32(block.timestamp); + + // Get the next unstaker contract in the round robin + address unstaker = unstakers[nextUnstakerIndex]; + // Ensure unstaker is valid + require(unstaker != address(0), "EthenaARM: Invalid unstaker"); + + // Ensure unstaker isn't used during last 7 days + uint256 amount = EthenaUnstaker(unstaker).cooldownAmount(); + require(amount == 0, "EthenaARM: Unstaker in cooldown"); + + // Update last used unstaker for the day. Safe to cast as there is a maximum of MAX_UNSTAKERS + nextUnstakerIndex = uint8((nextUnstakerIndex + 1) % MAX_UNSTAKERS); // Transfer sUSDe to the helper contract - susde.transfer(address(unstaker), baseAmount); + susde.transfer(unstaker, baseAmount); - uint256 liquidityAmount = unstaker.requestUnstake(baseAmount); + uint256 liquidityAmount = EthenaUnstaker(unstaker).requestUnstake(baseAmount); _liquidityAmountInCooldown += liquidityAmount; // Emit event for the request - emit RequestBaseWithdrawal(address(unstaker), baseAmount, liquidityAmount); + emit RequestBaseWithdrawal(unstaker, baseAmount, liquidityAmount); } - /** - * @notice Claim all the USDe that is now claimable from the Staked USDe contract. - * Reverts with `InvalidCooldown` from the Staked USDe contract if the cooldown period has not yet passed. - */ + /// @notice Claim all the USDe that is now claimable from the Staked USDe contract. + /// Reverts with `InvalidCooldown` from the Staked USDe contract if the cooldown period has not yet passed. function claimBaseWithdrawals(address unstaker) external { uint256 cooldownAmount = EthenaUnstaker(unstaker).cooldownAmount(); require(cooldownAmount > 0, "EthenaARM: No cooldown amount"); @@ -101,11 +121,9 @@ contract EthenaARM is Initializable, AbstractARM { emit ClaimBaseWithdrawals(unstaker, cooldownAmount); } - /** - * @dev Gets the total amount of USDe waiting to be claimed from the Staked USDe contract. - * This can be for many different cooldowns. - * This can be either in the cooldown period or ready to be claimed. - */ + /// @dev Gets the total amount of USDe waiting to be claimed from the Staked USDe contract. + /// This can be for many different cooldowns. + /// This can be either in the cooldown period or ready to be claimed. function _externalWithdrawQueue() internal view override returns (uint256) { return _liquidityAmountInCooldown; } @@ -128,4 +146,11 @@ contract EthenaARM is Initializable, AbstractARM { revert("EthenaARM: Invalid token"); } } + + /// @notice Set the unstaker helper contracts. + /// @param _unstakers The array of unstaker contract addresses. + function setUnstaker(address[MAX_UNSTAKERS] calldata _unstakers) external onlyOwner { + require(_unstakers.length == MAX_UNSTAKERS, "EthenaARM: Invalid unstakers length"); + unstakers = _unstakers; + } } From d7fe8e87eab0b18539057f66c91c34048ffbdfc4 Mon Sep 17 00:00:00 2001 From: Nicholas Addison Date: Fri, 14 Nov 2025 14:00:48 +1100 Subject: [PATCH 10/13] Added snapMarket Hardhat task --- src/js/tasks/liquidity.js | 16 ++- src/js/tasks/markets.js | 199 ++++++++++++++++++++++++++++++-------- src/js/tasks/tasks.js | 34 ++++++- 3 files changed, 195 insertions(+), 54 deletions(-) diff --git a/src/js/tasks/liquidity.js b/src/js/tasks/liquidity.js index b9e50724..fddb93be 100644 --- a/src/js/tasks/liquidity.js +++ b/src/js/tasks/liquidity.js @@ -72,7 +72,8 @@ const snap = async ({ arm, block, gas, amount, oneInch, kyber }) => { if (arm !== "Oeth") { await logWithdrawalQueue(armContract, blockTag, liquidityBalance); - const armPrices = await logArmPrices({ block, gas }, armContract); + const days = arm === "EtherFi" ? 5 : undefined; + const armPrices = await logArmPrices({ block, gas, days }, armContract); const pair = arm === "Lido" @@ -82,12 +83,12 @@ const snap = async ({ arm, block, gas, amount, oneInch, kyber }) => { : arm == "Origin" ? "OS/wS" : "Unknown"; + const assets = { + liquid: await armContract.liquidityAsset(), + base: await armContract.baseAsset(), + }; if (oneInch) { - const assets = { - liquid: await armContract.liquidityAsset(), - base: await armContract.baseAsset(), - }; const fee = arm === "Lido" ? 10n : 30n; const chainId = await (await ethers.provider.getNetwork()).chainId; @@ -100,11 +101,6 @@ const snap = async ({ arm, block, gas, amount, oneInch, kyber }) => { if (kyber && arm !== "Origin") { // Kyber does not support Sonic - const assets = { - liquid: await armContract.liquidityAsset(), - base: await armContract.baseAsset(), - }; - await logKyberPrices({ amount, assets, pair }, armPrices); } } diff --git a/src/js/tasks/markets.js b/src/js/tasks/markets.js index a50f3805..087dca86 100644 --- a/src/js/tasks/markets.js +++ b/src/js/tasks/markets.js @@ -8,10 +8,113 @@ const { getUniswapV3SpotPrices } = require("../utils/uniswap"); const { getSigner } = require("../utils/signers"); const { getFluidSpotPrices } = require("../utils/fluid"); const { mainnet } = require("../utils/addresses"); +const { resolveAddress } = require("../utils/assets"); const log = require("../utils/logger")("task:markets"); -const logArmPrices = async ({ blockTag, gas }, arm) => { +const snapMarket = async ({ + amount, + base, + liquid, + wrapped, + days, + fee1Inch, + oneInch, + kyber, +}) => { + const baseAddress = await resolveAddress(base.toUpperCase()); + const liquidAddress = await resolveAddress(liquid.toUpperCase()); + const assets = { + liquid: liquidAddress, + base: baseAddress, + }; + + // Assume the wrapped base asset is ERC-4626 + let wrapPrice; + if (wrapped) { + const vault = await ethers.getContractAt("IERC4626", baseAddress); + const assetAmount = await vault.convertToAssets( + parseUnits(amount.toString(), 18), + ); + wrapPrice = + (assetAmount * parseUnits("1")) / parseUnits(amount.toString(), 18); + + console.log( + `\nWrapped price: ${formatUnits(wrapPrice, 18)} ${base}/${liquid}`, + ); + } + + const pair = wrapped ? `unwrapped ${base}->${liquid}` : `${base}/${liquid}`; + if (oneInch) { + const fee = BigInt(fee1Inch); + + const chainId = await (await ethers.provider.getNetwork()).chainId; + const marketPrices = await log1InchPrices({ + amount, + assets, + fee, + pair, + chainId, + wrapPrice, + }); + + if (days) { + logDiscount(marketPrices.sellPrice, days); + } + } + + if (kyber) { + const marketPrices = await logKyberPrices({ + amount, + days, + assets, + pair, + wrapPrice, + }); + + if (days) { + logDiscount(marketPrices.sellPrice, days); + } + } +}; + +const logDiscountsOverDays = (marketPrice, daysArray) => { + // take 80% of the discount to cover the 20% fee + const discount = BigInt(1e18) - marketPrice; + const discountPostFee = (discount * 8n) / 10n; + console.log( + `\nYield on ${formatUnits( + discountPostFee * 10000n, + 18, + )} bps discount after 20% fee`, + ); + + let output = ""; + for (const days of daysArray) { + const discountAPY = + (discountPostFee * 36500n) / BigInt(days) / parseUnits("1", 16); + output += `${days} days ${formatUnits(discountAPY, 2)}%, `; + } + output = output.slice(0, -2); // remove trailing comma and space + output += " APY"; + + console.log(output); +}; + +const logDiscount = (marketPrice, days) => { + const discount = BigInt(1e18) - marketPrice; + // take 80% of the discount to cover the 20% fee + const discountPostFee = (discount * 8n) / 10n; + const discountAPY = (discountPostFee * 365n) / BigInt(days); + console.log( + `Discount over ${days} days after 20% fee: ${formatUnits( + discountPostFee * 10000n, + 18, + )} bps, ${formatUnits(discountAPY * 100n, 18)}% APY`, + ); +}; + +const logArmPrices = async ({ blockTag, gas, days }, arm) => { console.log(`\nARM Prices`); // The rate of 1 WETH for stETH to 36 decimals from the perspective of the AMM. ie WETH/stETH // from the trader's perspective, this is the stETH/WETH buy price @@ -80,31 +183,11 @@ const logArmPrices = async ({ blockTag, gas }, arm) => { // Origin rates are to 36 decimals console.log(`spread : ${formatUnits(spread, 14)} bps`); - // take 80% of the discount to cover the 20% fee - const buyDiscount = BigInt(1e18) - buyPrice; - const buyDiscountPostFee = (buyDiscount * 8n) / 10n; - - console.log( - `\nYield on ${formatUnits( - buyDiscount * 10000n, - 18, - )} bps buy discount after fee`, - ); - console.log( - `1 day ${formatUnits( - buyDiscountPostFee * 36500n, - 18, - )}%, 2 days ${formatUnits( - (buyDiscountPostFee * 36500n) / 2n, - 18, - )}%, 3 days ${formatUnits( - (buyDiscountPostFee * 36500n) / 3n, - 18, - )}%, 4 days ${formatUnits( - (buyDiscountPostFee * 36500n) / 4n, - 18, - )}%, 5 days ${formatUnits((buyDiscountPostFee * 36500n) / 5n, 18)}% APY`, - ); + if (days) { + logDiscount(buyPrice, days); + } else { + logDiscountsOverDays(buyPrice, [1, 2, 3, 4, 5, 7]); + } return { buyPrice, @@ -130,16 +213,20 @@ const logMarketPrices = async ({ ); console.log(`\n${marketName} prices for swap size ${amount}`); - // Note market sell is from the trader's perspective while the ARM sell price is from the AMM's perspective - const buyRateDiff = marketPrices.buyPrice - armPrices.sellPrice; - const armBuyToMarketSellDiff = marketPrices.buyPrice - armPrices.buyPrice; - const buyGasCosts = gas - ? `, ${marketPrices.buyGas.toLocaleString()} gas` - : ""; + let armDiff = ""; + if (armPrices) { + // Note market sell is from the trader's perspective while the ARM sell price is from the AMM's perspective + const buyRateDiff = marketPrices.buyPrice - armPrices.sellPrice; + const armBuyToMarketSellDiff = marketPrices.buyPrice - armPrices.buyPrice; + const buyGasCosts = gas + ? `, ${marketPrices.buyGas.toLocaleString()} gas` + : ""; + armDiff = `, ${formatUnits(armBuyToMarketSellDiff, 14)} bps from ARM buy, ${formatUnits(buyRateDiff, 14)} bps from ARM sell${buyGasCosts}`; + } console.log( `buy : ${formatUnits(marketPrices.buyPrice, 18).padEnd( 20, - )} ${pair}, ${formatUnits(armBuyToMarketSellDiff, 14)} bps from ARM buy, ${formatUnits(buyRateDiff, 14)} bps from ARM sell${buyGasCosts}`, + )} ${pair}${armDiff}`, ); console.log( @@ -147,25 +234,32 @@ const logMarketPrices = async ({ ); // Note market buy is from the trader's perspective while the ARM buy price is from the AMM's perspective - const sellRateDiff = marketPrices.sellPrice - armPrices.buyPrice; - const sellGasCosts = gas - ? `, ${marketPrices.sellGas.toLocaleString()} gas` - : ""; + if (armPrices) { + const sellRateDiff = marketPrices.sellPrice - armPrices.buyPrice; + const sellGasCosts = gas + ? `, ${marketPrices.sellGas.toLocaleString()} gas` + : ""; + armDiff = `, ${formatUnits(sellRateDiff, 14).padEnd( + 17, + )} bps from ARM buy${sellGasCosts}`; + } console.log( `sell : ${formatUnits(marketPrices.sellPrice, 18).padEnd( 20, - )} ${pair}, ${formatUnits(sellRateDiff, 14).padEnd( - 17, - )} bps from ARM buy${sellGasCosts}`, + )} ${pair}${armDiff}`, ); console.log(`spread : ${formatUnits(marketPrices.spread, 14)} bps`); }; const log1InchPrices = async (options, armPrices) => { - const { amount, assets, fee, chainId } = options; + const { amount, assets, fee, chainId, wrapPrice } = options; const marketPrices = await get1InchPrices(amount, assets, fee, chainId); + if (wrapPrice) { + unwrapPrices(marketPrices, wrapPrice); + } + await logMarketPrices({ ...options, marketPrices, @@ -173,6 +267,8 @@ const log1InchPrices = async (options, armPrices) => { marketName: "1Inch", }); + if (armPrices === undefined) return marketPrices; + console.log( `\nBest buy : ${ armPrices.sellPrice < marketPrices.buyPrice ? "Origin" : "1Inch" @@ -186,10 +282,14 @@ const log1InchPrices = async (options, armPrices) => { }; const logKyberPrices = async (options, armPrices) => { - const { amount, assets } = options; + const { amount, assets, wrapPrice } = options; const marketPrices = await getKyberPrices(amount, assets); + if (wrapPrice) { + unwrapPrices(marketPrices, wrapPrice); + } + await logMarketPrices({ ...options, marketPrices, @@ -197,6 +297,8 @@ const logKyberPrices = async (options, armPrices) => { marketName: "Kyber", }); + if (armPrices === undefined) return marketPrices; + console.log( `\nBest buy : ${ armPrices.sellPrice < marketPrices.buyPrice ? "Origin" : "Kyber" @@ -209,6 +311,18 @@ const logKyberPrices = async (options, armPrices) => { return marketPrices; }; +const unwrapPrices = (marketPrices, wrapPrice) => { + // Adjust prices back to unwrapped base asset + marketPrices.buyPrice = (marketPrices.buyPrice * parseUnits("1")) / wrapPrice; + marketPrices.sellPrice = + (marketPrices.sellPrice * parseUnits("1")) / wrapPrice; + marketPrices.midPrice = (marketPrices.midPrice * parseUnits("1")) / wrapPrice; + marketPrices.buyToAmount = + (marketPrices.buyToAmount * parseUnits("1")) / wrapPrice; + marketPrices.sellToAmount = + (marketPrices.sellToAmount * parseUnits("1")) / wrapPrice; +}; + const logCurvePrices = async (options, armPrices) => { const marketPrices = await getCurvePrices(options); @@ -297,6 +411,7 @@ const logWrappedEtherFiPrices = async ({ amount, armPrices }) => { }; module.exports = { + snapMarket, log1InchPrices, logKyberPrices, logArmPrices, diff --git a/src/js/tasks/tasks.js b/src/js/tasks/tasks.js index 63e712a3..3467e494 100644 --- a/src/js/tasks/tasks.js +++ b/src/js/tasks/tasks.js @@ -33,6 +33,7 @@ const { snap, withdrawRequestStatus, } = require("./liquidity"); +const { snapMarket } = require("./markets"); const { autoRequestWithdraw, autoClaimWithdraw, @@ -613,7 +614,12 @@ task("claimRedeemARM").setAction(async (_, __, runSuper) => { // Capital Management subtask("setLiquidityProviderCaps", "Set deposit cap for liquidity providers") - .addParam("arm", "Name of the ARM. eg Lido, Origin or EtherFi", "Lido", types.string) + .addParam( + "arm", + "Name of the ARM. eg Lido, Origin or EtherFi", + "Lido", + types.string, + ) .addParam( "cap", "Amount of WETH not scaled to 18 decimals", @@ -632,7 +638,12 @@ task("setLiquidityProviderCaps").setAction(async (_, __, runSuper) => { }); subtask("setTotalAssetsCap", "Set total assets cap") - .addParam("arm", "Name of the ARM. eg Lido, Origin or EtherFi", "Lido", types.string) + .addParam( + "arm", + "Name of the ARM. eg Lido, Origin or EtherFi", + "Lido", + types.string, + ) .addParam( "cap", "Amount of WETH not scaled to 18 decimals", @@ -1094,6 +1105,25 @@ task("setOperator").setAction(async (_, __, runSuper) => { // ARM Snapshots +subtask("snapMarket", "Take a market snapshot of prices") + .addParam("base", "Symbol of base asset", undefined, types.string) + .addParam("wrapped", "Is the base asset wrapped?", false, types.boolean) + .addParam("liquid", "Symbol of liquid asset", undefined, types.string) + .addOptionalParam("amount", "Swap quantity", 100, types.int) + .addOptionalParam( + "days", + "Days to unwrap the base asset", + undefined, + types.float, + ) + .addOptionalParam("oneInch", "Include 1Inch prices", true, types.boolean) + .addOptionalParam("fee1Inch", "1Inch infrastructure fee", 10, types.int) + .addOptionalParam("kyber", "Include Kyber prices", true, types.boolean) + .setAction(snapMarket); +task("snapMarket").setAction(async (_, __, runSuper) => { + return runSuper(); +}); + subtask("snap", "Take a snapshot of the an ARM") .addOptionalParam( "arm", From 7c821be8812e09943b2402b9166ee1dfa1b187ac Mon Sep 17 00:00:00 2001 From: Nicholas Addison Date: Fri, 14 Nov 2025 16:05:42 +1100 Subject: [PATCH 11/13] Updated Ethena process diagram --- docs/plantuml/ethenaProcesses.png | Bin 102807 -> 80346 bytes docs/plantuml/ethenaProcesses.puml | 67 +++++++++++------------------ 2 files changed, 26 insertions(+), 41 deletions(-) diff --git a/docs/plantuml/ethenaProcesses.png b/docs/plantuml/ethenaProcesses.png index f9a39dcc2baf7a60a7dc7c0407cfabed7841f861..8876882507dfa9eda2a0b05e5085a0bcec03e5db 100644 GIT binary patch literal 80346 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%0DvRp6Ws5ljKzSt~OWDA{K+r_B{&9L%@*foSo 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 literal 102807 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 -participant "Ethena\nCooldown\nFactory" as factory <> -participant "Ethena\nCooldown\nHelper" as helper <> +participant "Ethena\nUnstaker" as unstaker <> participant "Ethena\nMinting" as mint <> participant "sUSDe\nToken" as susde <> participant "USDe\nToken" as usde <> @@ -19,64 +18,49 @@ end box group unstake sUSDe -op -> arm : requestUnstake(\n amount) +op -> arm : requestBaseWithdrawal(\n base amount) activate arm -arm -> factory : assignHelper() -activate factory +' arm -> factory : assignHelper() +' activate factory -note over factory: increment helper index +note over arm: increment unstaker index -factory -> mint : cooldowns(\n helper address) +arm -> mint : cooldowns(\n unstaker) activate mint -return +return liquid amount -alt cooldown exits and ended - factory -> helper : claimUnstake() - activate helper - helper -> susde : unstake(arm) - activate susde - note right: check cooldown passed - susde -> usde : transfer(\n arm,\n amount) - activate usde - note right: transfer all\nunderlying USDe\nto ARM - return - return - return -else cooling exits and not ended - note over factory: revert and wait for cooldown to end -end -return helper address - -arm -> susde : transfer(\n helper address,\n amount) +arm -> susde : transfer(\n unstaker,\n base amount) activate susde -note right: transfer sUSDe\nfrom ARM\nto Helper +note right: transfer sUSDe\nfrom ARM\nto unstaker return -arm -> helper : requestUnstake(\n amount) -activate helper -helper -> susde : cooldownShares(\n sUSDe amount) +arm -> unstaker : requestUnstake(\n base amount) +activate unstaker +unstaker -> susde : cooldownShares(\n base amount) activate susde note right -burn sUSDe from helper +burn sUSDe from unstaker increment underlying USDe restart 7 day cooldown end note -return USDe amount -return USDe amount +return liquid amount +return liquid amount return -note right: emit USDe amount and helper address +note right: emit unstaker, base amount, liquid amount ... 7 day cooldown ... -any -> arm : claimUnstake(\n helper address) +any -> arm : claimBaseWithdrawals(\n unstaker) activate arm -' arm -> factory : claimUnstake(\n helper address) -' activate factory -' factory -> helper : claimUnstake() -arm -> helper : claimUnstake() -activate helper -helper -> susde : unstake(arm) + +arm -> mint : cooldowns(\n unstaker) +activate mint +return liquid amount + +arm -> unstaker : claimUnstake() +activate unstaker +unstaker -> susde : unstake(arm) activate susde note right: check cooldown passed susde -> usde : transfer(\n arm,\n amount) @@ -85,6 +69,7 @@ note right: transfer all\nunderlying USDe\nto ARM return return return +note right: emit unstaker, liquid amount return end group From f1ed26332bdad16039c1cf1bd6699b9ef337c79a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment?= <55331875+clement-ux@users.noreply.github.com> Date: Mon, 17 Nov 2025 14:18:10 +0100 Subject: [PATCH 12/13] EthenaARM Tests (#153) * wip add unstaker deployment script * Refactor deployUnstakers function to use constant for max unstakers * Rename setUnstaker function to setUnstakers for clarity * Deploy unstakers and set ownership in DeployEthenaARMScript * Make unstakers array public for improved accessibility * Add tests for request/claim withdraw * Add more test for request/claim * Fix error message for request delay in requestBaseWithdrawal function * Simplify cooldown amount handling in EthenaARM contract * Add tests for swapping SUSDE to USDE with outstanding withdrawals and insufficient liquidity * Refactor cooldown handling in EthenaARM and EthenaUnstaker contracts to use UserCooldown struct * Refactor import statements in DeployEthenaARMScript to consolidate IWETH and IStakedUSDe imports --- .../mainnet/014_DeployEthenaARMScript.sol | 28 +++- src/contracts/EthenaARM.sol | 24 ++-- src/contracts/EthenaUnstaker.sol | 22 +--- .../fork/EthenaARM/ClaimBaseWithdrawals.t.sol | 48 +++++++ test/fork/EthenaARM/RequestWithdraw.t.sol | 121 ++++++++++++++++++ .../EthenaARM/SwapExactTokensForTokens.t.sol | 23 ++++ test/fork/EthenaARM/shared/Shared.sol | 16 ++- 7 files changed, 246 insertions(+), 36 deletions(-) create mode 100644 test/fork/EthenaARM/ClaimBaseWithdrawals.t.sol create mode 100644 test/fork/EthenaARM/RequestWithdraw.t.sol diff --git a/script/deploy/mainnet/014_DeployEthenaARMScript.sol b/script/deploy/mainnet/014_DeployEthenaARMScript.sol index dcd2002b..9de78f2e 100644 --- a/script/deploy/mainnet/014_DeployEthenaARMScript.sol +++ b/script/deploy/mainnet/014_DeployEthenaARMScript.sol @@ -5,13 +5,13 @@ pragma solidity 0.8.23; import {console} from "forge-std/console.sol"; // Contract imports -import {IWETH} from "contracts/Interfaces.sol"; import {Proxy} from "contracts/Proxy.sol"; +import {Mainnet} from "contracts/utils/Addresses.sol"; import {EthenaARM} from "contracts/EthenaARM.sol"; import {CapManager} from "contracts/CapManager.sol"; -import {Mainnet} from "contracts/utils/Addresses.sol"; import {MorphoMarket} from "contracts/markets/MorphoMarket.sol"; -import {ZapperARM} from "contracts/ZapperARM.sol"; +import {EthenaUnstaker} from "contracts/EthenaARM.sol"; +import {IWETH, IStakedUSDe} from "contracts/Interfaces.sol"; import {Abstract4626MarketWrapper} from "contracts/markets/Abstract4626MarketWrapper.sol"; // Deployment imports @@ -29,13 +29,16 @@ contract DeployEthenaARMScript is AbstractDeployScript { Proxy morphoMarketProxy; EthenaARM armImpl; MorphoMarket morphoMarket; + Proxy armProxy; + + uint256 public constant MAX_UNSTAKERS = 42; function _execute() internal override { console.log("Deploy:", DEPLOY_NAME); console.log("------------"); // 1. Deploy new ARM proxy contract - Proxy armProxy = new Proxy(); + armProxy = new Proxy(); _recordDeploy("ETHENA_ARM", address(armProxy)); // 2. Deploy proxy for the CapManager @@ -119,9 +122,24 @@ contract DeployEthenaARMScript is AbstractDeployScript { // 16. Set ARM buffer to 10% EthenaARM(payable(address(armProxy))).setARMBuffer(0.1e18); // 10% buffer - // 17. Transfer ownership of ARM to the 5/8 multisig + // 17. Deploy Unstakers + address[MAX_UNSTAKERS] memory unstakers = _deployUnstakers(); + + // 18. Set Unstakers in the ARM + EthenaARM(payable(address(armProxy))).setUnstakers(unstakers); + + // 19. Transfer ownership of ARM to the 5/8 multisig armProxy.setOwner(Mainnet.GOV_MULTISIG); console.log("Finished deploying", DEPLOY_NAME); } + + function _deployUnstakers() internal returns (address[MAX_UNSTAKERS] memory unstakers) { + for (uint256 i = 0; i < MAX_UNSTAKERS; i++) { + address unstaker = address(new EthenaUnstaker(payable(armProxy), IStakedUSDe(Mainnet.SUSDE))); + unstakers[i] = address(unstaker); + console.log("Deployed unstaker", i, address(unstaker)); + } + return unstakers; + } } diff --git a/src/contracts/EthenaARM.sol b/src/contracts/EthenaARM.sol index f440c298..e7a2e22e 100644 --- a/src/contracts/EthenaARM.sol +++ b/src/contracts/EthenaARM.sol @@ -5,7 +5,7 @@ import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Ini import {AbstractARM} from "./AbstractARM.sol"; import {EthenaUnstaker} from "./EthenaUnstaker.sol"; -import {IERC20, IStakedUSDe} from "./Interfaces.sol"; +import {IERC20, IStakedUSDe, UserCooldown} from "./Interfaces.sol"; /** * @title Ethena sUSDe/USDe Automated Redemption Manager (ARM) @@ -24,7 +24,7 @@ contract EthenaARM is Initializable, AbstractARM { /// @notice The total amount of liquidity asset (USDe) currently in cooldown uint256 internal _liquidityAmountInCooldown; /// @notice Array of unstaker helper contracts - address[MAX_UNSTAKERS] internal unstakers; + address[MAX_UNSTAKERS] public unstakers; /// @notice The index of the next unstaker to use in the round robin uint8 public nextUnstakerIndex; /// @notice The timestamp of the last request made @@ -77,7 +77,7 @@ contract EthenaARM is Initializable, AbstractARM { /// @dev Uses a round robin to select the next unstaker helper contract. /// @param baseAmount The amount of staked USDe (sUSDe) to withdraw. function requestBaseWithdrawal(uint256 baseAmount) external onlyOperatorOrOwner { - require(block.timestamp >= lastRequestTimestamp + DELAY_REQUEST, "EthenaARM: Request delay not passed"); + require(block.timestamp >= lastRequestTimestamp + DELAY_REQUEST, "EthenaARM: Delay not passed"); lastRequestTimestamp = uint32(block.timestamp); // Get the next unstaker contract in the round robin @@ -86,8 +86,8 @@ contract EthenaARM is Initializable, AbstractARM { require(unstaker != address(0), "EthenaARM: Invalid unstaker"); // Ensure unstaker isn't used during last 7 days - uint256 amount = EthenaUnstaker(unstaker).cooldownAmount(); - require(amount == 0, "EthenaARM: Unstaker in cooldown"); + UserCooldown memory cooldown = susde.cooldowns(address(unstaker)); + require(cooldown.underlyingAmount == 0, "EthenaARM: Unstaker in cooldown"); // Update last used unstaker for the day. Safe to cast as there is a maximum of MAX_UNSTAKERS nextUnstakerIndex = uint8((nextUnstakerIndex + 1) % MAX_UNSTAKERS); @@ -106,19 +106,15 @@ contract EthenaARM is Initializable, AbstractARM { /// @notice Claim all the USDe that is now claimable from the Staked USDe contract. /// Reverts with `InvalidCooldown` from the Staked USDe contract if the cooldown period has not yet passed. function claimBaseWithdrawals(address unstaker) external { - uint256 cooldownAmount = EthenaUnstaker(unstaker).cooldownAmount(); - require(cooldownAmount > 0, "EthenaARM: No cooldown amount"); + UserCooldown memory cooldown = susde.cooldowns(address(unstaker)); + require(cooldown.underlyingAmount > 0, "EthenaARM: No cooldown amount"); - if (_liquidityAmountInCooldown < cooldownAmount) { - _liquidityAmountInCooldown = 0; - } else { - _liquidityAmountInCooldown -= cooldownAmount; - } + _liquidityAmountInCooldown -= cooldown.underlyingAmount; // Claim all the underlying USDe that has cooled down for the unstaker and send to the ARM EthenaUnstaker(unstaker).claimUnstake(); - emit ClaimBaseWithdrawals(unstaker, cooldownAmount); + emit ClaimBaseWithdrawals(unstaker, cooldown.underlyingAmount); } /// @dev Gets the total amount of USDe waiting to be claimed from the Staked USDe contract. @@ -149,7 +145,7 @@ contract EthenaARM is Initializable, AbstractARM { /// @notice Set the unstaker helper contracts. /// @param _unstakers The array of unstaker contract addresses. - function setUnstaker(address[MAX_UNSTAKERS] calldata _unstakers) external onlyOwner { + function setUnstakers(address[MAX_UNSTAKERS] calldata _unstakers) external onlyOwner { require(_unstakers.length == MAX_UNSTAKERS, "EthenaARM: Invalid unstakers length"); unstakers = _unstakers; } diff --git a/src/contracts/EthenaUnstaker.sol b/src/contracts/EthenaUnstaker.sol index f94712dc..1fd66dc6 100644 --- a/src/contracts/EthenaUnstaker.sol +++ b/src/contracts/EthenaUnstaker.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.23; -import {IStakedUSDe, UserCooldown} from "./Interfaces.sol"; +import {IStakedUSDe} from "./Interfaces.sol"; /** * @title A helper contract that allows the ARM to have multiple sUSDe cooldowns in parallel. @@ -18,28 +18,18 @@ contract EthenaUnstaker { susde = _susde; } - /** - * @notice Request a cooldown of USDe from Ethena's Staked USDe (sUSDe) contract. - * @param sUSDeAmount The amount of staked USDe (sUSDe) to withdraw. - * @return usde The amount of underlying USDe that will be withdrawable after the cooldown period. - */ + /// @notice Request a cooldown of USDe from Ethena's Staked USDe (sUSDe) contract. + /// @param sUSDeAmount The amount of staked USDe (sUSDe) to withdraw. + /// @return usde The amount of underlying USDe that will be withdrawable after the cooldown period. function requestUnstake(uint256 sUSDeAmount) external returns (uint256 usde) { require(msg.sender == arm, "Only ARM can request unstake"); usde = susde.cooldownShares(sUSDeAmount); } - /** - * @notice Claim the underlying USDe after the cooldown period has ended and send to the ARM. - * Reverts with `InvalidCooldown` from the Staked USDe contract if the cooldown period has not yet passed. - */ + /// @notice Claim the underlying USDe after the cooldown period has ended and send to the ARM. + /// Reverts with `InvalidCooldown` from the Staked USDe contract if the cooldown period has not yet passed. function claimUnstake() external { require(msg.sender == arm, "Only ARM can request unstake"); susde.unstake(arm); } - - function cooldownAmount() external view returns (uint256) { - UserCooldown memory cooldown = susde.cooldowns(address(this)); - - return cooldown.underlyingAmount; - } } diff --git a/test/fork/EthenaARM/ClaimBaseWithdrawals.t.sol b/test/fork/EthenaARM/ClaimBaseWithdrawals.t.sol new file mode 100644 index 00000000..0c5a9e4a --- /dev/null +++ b/test/fork/EthenaARM/ClaimBaseWithdrawals.t.sol @@ -0,0 +1,48 @@ +/// SPDX-License-Identifier: MIT +pragma solidity 0.8.23; + +// Test +import {Fork_Shared_Test} from "test/fork/EthenaARM/shared/Shared.sol"; + +// Contracts +import {EthenaARM} from "contracts/EthenaARM.sol"; +import {EthenaUnstaker} from "contracts/EthenaUnstaker.sol"; + +contract Fork_Concrete_EthenaARM_ClaimBaseWithdrawals_Test_ is Fork_Shared_Test { + uint256 public AMOUNT_IN = 100 ether; + + ////////////////////////////////////////////////////// + /// --- TESTS + ////////////////////////////////////////////////////// + function test_ClaimBaseWithdrawals_FirstRequest() public { + vm.prank(operator); + ethenaARM.requestBaseWithdrawal(AMOUNT_IN); + + uint256 amountOut = susde.convertToAssets(AMOUNT_IN); + address unstakerAddress = ethenaARM.unstakers(ethenaARM.nextUnstakerIndex() - 1); + skip(7 days + 1); + + vm.expectEmit({emitter: address(ethenaARM)}); + emit EthenaARM.ClaimBaseWithdrawals(unstakerAddress, amountOut); + ethenaARM.claimBaseWithdrawals(unstakerAddress); + } + + ////////////////////////////////////////////////////// + /// --- REVERT TESTS + ////////////////////////////////////////////////////// + function test_RevertWhen_ClaimBaseWithdrawals_NoCooldownAmount() public { + address unstakerAddress = ethenaARM.unstakers(0); + + vm.expectRevert("EthenaARM: No cooldown amount"); + ethenaARM.claimBaseWithdrawals(unstakerAddress); + } + + function test_RevertWhen_ClaimBaseWithdrawals_InvalidUnstaker() public { + vm.prank(operator); + ethenaARM.requestBaseWithdrawal(AMOUNT_IN); + address unstaker = ethenaARM.unstakers(0); + skip(7 days + 1); + vm.expectRevert("Only ARM can request unstake"); + EthenaUnstaker(unstaker).claimUnstake(); + } +} diff --git a/test/fork/EthenaARM/RequestWithdraw.t.sol b/test/fork/EthenaARM/RequestWithdraw.t.sol new file mode 100644 index 00000000..3bc16434 --- /dev/null +++ b/test/fork/EthenaARM/RequestWithdraw.t.sol @@ -0,0 +1,121 @@ +/// SPDX-License-Identifier: MIT +pragma solidity 0.8.23; + +// Test +import {Fork_Shared_Test} from "test/fork/EthenaARM/shared/Shared.sol"; + +// Contracts +import {EthenaARM} from "contracts/EthenaARM.sol"; +import {IStakedUSDe, UserCooldown} from "contracts/Interfaces.sol"; +import {EthenaUnstaker} from "contracts/EthenaUnstaker.sol"; + +contract Fork_Concrete_EthenaARM_RequestWithdraw_Test_ is Fork_Shared_Test { + uint256 public AMOUNT_IN = 100 ether; + + ////////////////////////////////////////////////////// + /// --- TESTS + ////////////////////////////////////////////////////// + function test_RequestWithdraw_FirstRequest() public { + uint256 susdeBalanceBefore = susde.balanceOf(address(ethenaARM)); + uint256 nextUnstakerIndex = ethenaARM.nextUnstakerIndex(); + + vm.expectEmit({emitter: address(ethenaARM)}); + emit EthenaARM.RequestBaseWithdrawal( + ethenaARM.unstakers(nextUnstakerIndex), AMOUNT_IN, susde.convertToAssets(AMOUNT_IN) + ); + + vm.prank(operator); + ethenaARM.requestBaseWithdrawal(AMOUNT_IN); + + EthenaUnstaker unstaker = EthenaUnstaker(ethenaARM.unstakers(nextUnstakerIndex)); + UserCooldown memory cooldown = IStakedUSDe(address(susde)).cooldowns(address(unstaker)); + uint256 susdeBalanceAfter = susde.balanceOf(address(ethenaARM)); + assertEq(susdeBalanceAfter, susdeBalanceBefore - AMOUNT_IN, "sUSDe balance after request incorrect"); + assertEq(ethenaARM.nextUnstakerIndex(), nextUnstakerIndex + 1, "nextUnstakerIndex not incremented"); + assertEq(cooldown.underlyingAmount, susde.convertToAssets(AMOUNT_IN), "unstaker cooldown amount incorrect"); + } + + function test_RequestWithdraw_SecondRequest() public { + // First request + vm.prank(operator); + ethenaARM.requestBaseWithdrawal(AMOUNT_IN); + skip(ethenaARM.DELAY_REQUEST()); + + // Second request + uint256 susdeBalanceBefore = susde.balanceOf(address(ethenaARM)); + uint256 nextUnstakerIndex = ethenaARM.nextUnstakerIndex(); + vm.prank(operator); + ethenaARM.requestBaseWithdrawal(AMOUNT_IN * 2); + + UserCooldown memory cooldown = IStakedUSDe(address(susde)).cooldowns(ethenaARM.unstakers(nextUnstakerIndex)); + uint256 susdeBalanceAfter = susde.balanceOf(address(ethenaARM)); + assertEq(ethenaARM.nextUnstakerIndex(), 2, "nextUnstakerIndex not incremented"); + assertEq(susdeBalanceAfter, susdeBalanceBefore - (2 * AMOUNT_IN), "sUSDe balance after requests incorrect"); + assertEq(cooldown.underlyingAmount, susde.convertToAssets(AMOUNT_IN * 2), "second unstaker cooldown incorrect"); + } + + function test_RequestWithdraw_MaxRequest() public { + uint256 balanceBefore = susde.balanceOf(address(ethenaARM)); + uint256 delay = ethenaARM.DELAY_REQUEST(); + + // Make MAX_UNSTAKERS requests + for (uint256 i; i < MAX_UNSTAKERS; i++) { + vm.prank(operator); + ethenaARM.requestBaseWithdrawal(AMOUNT_IN); + skip(delay); + } + + uint256 balanceAfter = susde.balanceOf(address(ethenaARM)); + assertEq(ethenaARM.nextUnstakerIndex(), 0, "nextUnstakerIndex not wrapped around"); + assertEq(balanceBefore - balanceAfter, AMOUNT_IN * MAX_UNSTAKERS, "sUSDe balance after max requests incorrect"); + } + + ////////////////////////////////////////////////////// + /// --- REVERT TESTS + ////////////////////////////////////////////////////// + function test_RevertWhen_RequestWithdraw_RequestDelayNotPassed() public { + vm.prank(operator); + ethenaARM.requestBaseWithdrawal(AMOUNT_IN); + + vm.expectRevert("EthenaARM: Delay not passed"); + vm.prank(operator); + ethenaARM.requestBaseWithdrawal(AMOUNT_IN); + } + + function test_RevertWhen_RequestWithdraw_InvalidUnstaker() public { + address[42] memory emptyUnstakers; + vm.prank(governor); + ethenaARM.setUnstakers(emptyUnstakers); + + vm.expectRevert("EthenaARM: Invalid unstaker"); + vm.prank(operator); + ethenaARM.requestBaseWithdrawal(AMOUNT_IN); + } + + function test_RevertWhen_RequestWithdraw_UnstakerInCooldown() public { + uint256 delay = ethenaARM.DELAY_REQUEST(); + + // Make MAX_UNSTAKERS requests + for (uint256 i; i < MAX_UNSTAKERS; i++) { + vm.prank(operator); + ethenaARM.requestBaseWithdrawal(AMOUNT_IN); + skip(delay); + } + + vm.prank(operator); + vm.expectRevert("EthenaARM: Unstaker in cooldown"); + ethenaARM.requestBaseWithdrawal(AMOUNT_IN); + } + + function test_RevertWhen_RequestWithdraw_NotOperatorOrOwner() public { + vm.expectRevert("ARM: Only operator or owner can call this function."); + ethenaARM.requestBaseWithdrawal(AMOUNT_IN); + } + + function test_RevertWhen_RequestWithdraw_UnauthorizedCaller() public { + address unstakerAddress = ethenaARM.unstakers(0); + + vm.expectRevert("Only ARM can request unstake"); + EthenaUnstaker(unstakerAddress).requestUnstake(AMOUNT_IN); + } +} diff --git a/test/fork/EthenaARM/SwapExactTokensForTokens.t.sol b/test/fork/EthenaARM/SwapExactTokensForTokens.t.sol index 18fd27b6..b184abfb 100644 --- a/test/fork/EthenaARM/SwapExactTokensForTokens.t.sol +++ b/test/fork/EthenaARM/SwapExactTokensForTokens.t.sol @@ -110,6 +110,22 @@ contract Fork_Concrete_EthenaARM_swapExactTokensForTokens_Test_ is Fork_Shared_T assertEq(susdeBalanceBefore, susdeBalanceAfter + AMOUNT_IN, "SUSDe balance should have decreased"); } + function test_swapExactTokensForTokens_SUSDE_To_USDE_WithOutstandingWithdrawals_Sig1() public { + ethenaARM.requestRedeem(AMOUNT_IN); + + // Precompute expected amount out + uint256 traderate = ethenaARM.traderate1(); + uint256 expectedAmountOut = (susde.convertToAssets(AMOUNT_IN) * traderate) / 1e36; + + // Perform the swap + uint256[] memory obtained = + ethenaARM.swapExactTokensForTokens(IERC20(address(susde)), usde, AMOUNT_IN, 0, address(this)); + + // Assertions + assertEq(obtained[0], AMOUNT_IN, "Obtained SUSDe amount should match input"); + assertEq(obtained[1], expectedAmountOut, "Obtained USDe amount should match expected output"); + } + ////////////////////////////////////////////////////// /// --- REVERTING TESTS ////////////////////////////////////////////////////// @@ -168,4 +184,11 @@ contract Fork_Concrete_EthenaARM_swapExactTokensForTokens_Test_ is Fork_Shared_T vm.expectRevert(bytes("ARM: Invalid path length")); ethenaARM.swapExactTokensForTokens(AMOUNT_IN, 0, longPath, address(this), block.timestamp + 1 hours); } + + function test_RevertWhen_swapExactTokensForTokens_Because_InsufficientLiquidity() public { + ethenaARM.requestRedeem(ethenaARM.balanceOf(address(this))); + + vm.expectRevert(bytes("ARM: Insufficient liquidity")); + ethenaARM.swapExactTokensForTokens(IERC20(address(susde)), usde, AMOUNT_IN, 0, address(this)); + } } diff --git a/test/fork/EthenaARM/shared/Shared.sol b/test/fork/EthenaARM/shared/Shared.sol index 9ce3de24..bd9502ae 100644 --- a/test/fork/EthenaARM/shared/Shared.sol +++ b/test/fork/EthenaARM/shared/Shared.sol @@ -7,12 +7,15 @@ import {Base_Test_} from "test/Base.sol"; // Contracts import {Proxy} from "contracts/Proxy.sol"; import {EthenaARM} from "contracts/EthenaARM.sol"; +import {EthenaUnstaker} from "contracts/EthenaUnstaker.sol"; // Interfaces import {Mainnet} from "src/contracts/utils/Addresses.sol"; -import {IERC20, IERC4626} from "contracts/Interfaces.sol"; +import {IERC20, IERC4626, IStakedUSDe} from "contracts/Interfaces.sol"; abstract contract Fork_Shared_Test is Base_Test_ { + uint256 public constant MAX_UNSTAKERS = 42; + ////////////////////////////////////////////////////// /// --- SETUP ////////////////////////////////////////////////////// @@ -114,5 +117,16 @@ abstract contract Fork_Shared_Test is Base_Test_ { // Swap usde to susde using ARM to have some susde balance ethenaARM.swapExactTokensForTokens(IERC20(address(susde)), usde, 5_000 ether, 0, address(this)); + + vm.startPrank(ethenaARM.owner()); + ethenaARM.setUnstakers(_deployUnstakers()); + vm.stopPrank(); + } + + function _deployUnstakers() internal returns (address[MAX_UNSTAKERS] memory unstakers) { + for (uint256 i; i < MAX_UNSTAKERS; i++) { + address unstaker = address(new EthenaUnstaker(payable(ethenaProxy), IStakedUSDe(Mainnet.SUSDE))); + unstakers[i] = address(unstaker); + } } } From ac59272be0018623288413571cb46af0bbea1dc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment?= <55331875+clement-ux@users.noreply.github.com> Date: Tue, 25 Nov 2025 23:15:29 +0100 Subject: [PATCH 13/13] [Ethena ARM] - Invariant Testing (#156) * Add mock for sUSDe contract. * Add Medusa VM interface. * Add base contracts and setup for EthenaARM invariant tests * Simplify MockSUSDE * Add unstakers * Prepare list for target functions * Fix rewards calculation * Add label and assume functions to Vm interface * Improve invariant setup * wip add targets for susde contract * add logs * Update SUSDE target functions to reflect completed implementations * Add Morpho target functions * Add Find library for user request retrieval in testing * Add ARM deposit/request/claim target functions * Add MockMorpho contract and update tests for utilization rate functionality * Fix claimable request check in Find library by updating variable usage * Make MockMorpho more realistic * Add target functions for allocate liquidity on ARM. * Add target function for setting prices in TargetFunctions contract * add target functions for swaps * Fix ClaimRequest checks * Add target for fees management * Add target functions for base request/claim withdraw * Add Math library with utility functions for absolute values, min/max, and comparisons * Add ghost values * Add initial swaps invariant * implement properties * Add invariant functions for swap and liquidity properties * Add after-invariant checks and unit tests for ARM functionality * Add test target for Ethena invariant checks * Refactor CI workflow to include Ethena invariant tests and adjust Foundry profiles for improved testing parameters * Reduce invariant test runs in CI profile from 1000 to 500 for improved performance * Remove redundant invariant checks for USDe balance and ARM total assets in Properties contract * Add exchange rate checks to target functions for improved invariants * fmt * Update Foundry version to 1.4.4 and enforce fail on revert in CI profile * try to fix CI * try again * externalize fetching user with ARM shares * fmt * [Ethena ARM] Support Medusa Fuzzer (#160) * wip * Add crytic-export directory to .gitignore for improved build cleanliness * Disable assertion testing and update property test prefix in medusa.json * Refactor code structure for improved readability and maintainability * Update .gitignore to ignore the crytic-export directory instead of its contents * Rename FuzzerFoundry_EthenaARM file * Refactor EthenaARM contracts to use state variables for label availability and update medusa.json for improved assertion testing configurations * Update invariant profile settings for reduced runs and depth * restore default values * remove crytic export * improve tests * try something * try again * add medusa ci * skip fuzzer during contract compilation * try again * add previous work * Update invariant properties in Properties.sol to reflect current state * Update USDe balance requirement and remove dead address redemption logic * bump to latest forge version * Update Foundry version specification from "latest" to "stable" in CI workflow * small adjustment * increase ci runs * remove one invariant * adjust comment * update contract titles and descriptions for FuzzerFoundry and FuzzerMedusa * add ethena invariant test to test-invariants target --- .github/workflows/main.yml | 92 +- .gitignore | 6 +- Makefile | 15 +- foundry.toml | 12 +- src/contracts/Interfaces.sol | 6 + test/invariants/EthenaARM/Base.sol | 105 +++ .../EthenaARM/FuzzerFoundry_EthenaARM.sol | 97 ++ .../EthenaARM/FuzzerMedusa_EthenaARM.sol | 19 + test/invariants/EthenaARM/Properties.sol | 228 +++++ test/invariants/EthenaARM/Setup.sol | 317 +++++++ test/invariants/EthenaARM/TargetFunctions.sol | 877 ++++++++++++++++++ test/invariants/EthenaARM/helpers/Find.sol | 58 ++ test/invariants/EthenaARM/helpers/Math.sol | 127 +++ test/invariants/EthenaARM/helpers/Test.sol | 24 + test/invariants/EthenaARM/helpers/Vm.sol | 96 ++ test/invariants/EthenaARM/medusa.json | 108 +++ .../invariants/EthenaARM/mocks/MockMorpho.sol | 58 ++ test/invariants/EthenaARM/mocks/MockSUSDE.sol | 148 +++ 18 files changed, 2379 insertions(+), 14 deletions(-) create mode 100644 test/invariants/EthenaARM/Base.sol create mode 100644 test/invariants/EthenaARM/FuzzerFoundry_EthenaARM.sol create mode 100644 test/invariants/EthenaARM/FuzzerMedusa_EthenaARM.sol create mode 100644 test/invariants/EthenaARM/Properties.sol create mode 100644 test/invariants/EthenaARM/Setup.sol create mode 100644 test/invariants/EthenaARM/TargetFunctions.sol create mode 100644 test/invariants/EthenaARM/helpers/Find.sol create mode 100644 test/invariants/EthenaARM/helpers/Math.sol create mode 100644 test/invariants/EthenaARM/helpers/Test.sol create mode 100644 test/invariants/EthenaARM/helpers/Vm.sol create mode 100644 test/invariants/EthenaARM/medusa.json create mode 100644 test/invariants/EthenaARM/mocks/MockMorpho.sol create mode 100644 test/invariants/EthenaARM/mocks/MockSUSDE.sol diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0b02934b..7c6c8e02 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -26,6 +26,8 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 + with: + version: "stable" - name: Show Forge version run: forge --version @@ -46,6 +48,8 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 + with: + version: "stable" - name: Show Forge version run: forge --version @@ -54,13 +58,13 @@ jobs: run: forge soldeer install && yarn install - name: Compile contracts with Forge - run: forge build --sizes + run: forge build --sizes --skip Fuzzer - - name: Compile contracts with Hardhat - run: npx hardhat compile + #- name: Compile contracts with Hardhat + # run: npx hardhat compile #################################################### - ### TESTS + ### CONCRETE TESTS #################################################### foundry-unit-tests: name: Foundry Unit-Tests @@ -73,6 +77,8 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 + with: + version: "stable" - name: Install Dependencies run: forge soldeer install && yarn install @@ -95,6 +101,8 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 + with: + version: "stable" - name: Install Dependencies run: forge soldeer install && yarn install @@ -117,6 +125,8 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 + with: + version: "stable" - name: Install Dependencies run: forge soldeer install && yarn install @@ -124,8 +134,11 @@ jobs: - name: Run non-invariant tests run: forge test --summary --fail-fast --show-progress --mp 'test/smoke/**' - foundry-tests-invariants: - name: Foundry Invariants Tests + #################################################### + ### FUZZ TESTS + #################################################### + foundry-tests-invariants-OethARM: + name: Foundry Invariants Tests - OethARM runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -134,18 +147,83 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 + with: + version: "stable" - name: Install Dependencies run: forge soldeer install - name: Run invariant tests (OethARM) run: | + FOUNDRY_PROFILE=ci \ FOUNDRY_INVARIANT_FAIL_ON_REVERT=false \ FOUNDRY_MATCH_CONTRACT=FuzzerFoundry_OethARM \ forge test --summary --fail-fast --show-progress + + foundry-tests-invariants-OriginARM: + name: Foundry Invariants Tests - OriginARM + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: "stable" + + - name: Install Dependencies + run: forge soldeer install - name: Run invariant tests (OriginARM) run: | + FOUNDRY_PROFILE=ci \ FOUNDRY_INVARIANT_FAIL_ON_REVERT=true \ FOUNDRY_MATCH_CONTRACT=FuzzerFoundry_OriginARM \ - forge test --summary --fail-fast --show-progress \ No newline at end of file + forge test --summary --fail-fast --show-progress + + foundry-tests-invariants-EthenaARM: + name: Foundry Invariants Tests - EthenaARM + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: "stable" + + - name: Install Dependencies + run: forge soldeer install + + - name: Run invariant tests (EthenaARM) + run: | + FOUNDRY_PROFILE=ci \ + FOUNDRY_INVARIANT_FAIL_ON_REVERT=true \ + FOUNDRY_MATCH_CONTRACT=FuzzerFoundry_EthenaARM \ + forge test --summary --fail-fast --show-progress + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: '1.21' + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.10' + + - name: Install Crytic Compile + run: pip install crytic-compile + + - name: Install Medusa from Source + run: go install github.com/crytic/medusa@latest + + - name: Run Medusa + working-directory: test/invariants/EthenaARM/ + run: medusa fuzz --timeout 1800 --seq-len 500 --fail-fast + + \ No newline at end of file diff --git a/.gitignore b/.gitignore index 7b4c4786..3f3e0acc 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ soldeer.lock # Coverage lcov.info* +crytic-export # Defender Actions dist @@ -37,4 +38,7 @@ cache_hardhat build/deployments-fork*.json # Reports. eg stats.html -*.html \ No newline at end of file +*.html + +# Other +.DS_Store \ No newline at end of file diff --git a/Makefile b/Makefile index 93f12561..695c6844 100644 --- a/Makefile +++ b/Makefile @@ -15,8 +15,14 @@ install: clean: @rm -rf broadcast cache out -clean-all: - @rm -rf broadcast cache out dependencies node_modules soldeer.lock yarn.lock lcov.info lcov.info.pruned artifacts cache_hardhat +# Remove every "crytic-export" directory anywhere in the project +clean-crytic: + @find . -type d -name crytic-export -exec rm -rf '{}' + + +clean-all: + @rm -rf broadcast cache out dependencies node_modules soldeer.lock yarn.lock .lcov.info lcov.info pruned artifacts cache hardhat-node_modules + @$(MAKE) clean-crytic + gas: @forge test --gas-report @@ -47,8 +53,11 @@ test-invariant-lido: test-invariant-origin: @FOUNDRY_INVARIANT_FAIL_ON_REVERT=true FOUNDRY_MATCH_CONTRACT=FuzzerFoundry_OriginARM $(MAKE) test-std +test-invariant-ethena: + @FOUNDRY_INVARIANT_FAIL_ON_REVERT=true FOUNDRY_MATCH_CONTRACT=FuzzerFoundry_EthenaARM $(MAKE) test-std + test-invariants: - @$(MAKE) test-invariant-lido && $(MAKE) test-invariant-origin + @$(MAKE) test-invariant-lido && $(MAKE) test-invariant-origin && $(MAKE) test-invariant-ethena test-unit: @FOUNDRY_MATCH_PATH='test/unit/**' $(MAKE) test-std diff --git a/foundry.toml b/foundry.toml index 5803d379..b3fbed98 100644 --- a/foundry.toml +++ b/foundry.toml @@ -55,12 +55,18 @@ lint_on_build = false [fuzz] runs = 1_000 -[invariant] +[profile.default.invariant] runs = 256 depth = 500 shrink_run_limit = 5_000 show_metrics = true -fail_on_revert = false +fail_on_revert = true + +[profile.ci.invariant] +runs = 1_000 +depth = 1_000 +shrink_run_limit = 5_000 +show_metrics = true [dependencies] solmate = "6.7.0" @@ -83,4 +89,4 @@ remappings_location = "config" mainnet = "${PROVIDER_URL}" sonic = "${SONIC_URL}" -# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options \ No newline at end of file +# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/src/contracts/Interfaces.sol b/src/contracts/Interfaces.sol index d013642c..13c75b7a 100644 --- a/src/contracts/Interfaces.sol +++ b/src/contracts/Interfaces.sol @@ -357,4 +357,10 @@ interface IStakedUSDe is IERC4626 { function unstake(address receiver) external; function cooldowns(address receiver) external view returns (UserCooldown memory); + + function getUnvestedAmount() external view returns (uint256); + + function lastDistributionTimestamp() external view returns (uint256); + + function transferInRewards(uint256 amount) external; } diff --git a/test/invariants/EthenaARM/Base.sol b/test/invariants/EthenaARM/Base.sol new file mode 100644 index 00000000..538b08ae --- /dev/null +++ b/test/invariants/EthenaARM/Base.sol @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.23; + +// Contracts +import {Proxy} from "contracts/Proxy.sol"; +import {EthenaARM} from "contracts/EthenaARM.sol"; +import {MockMorpho} from "test/invariants/EthenaARM/mocks/MockMorpho.sol"; +import {MorphoMarket} from "src/contracts/markets/MorphoMarket.sol"; +import {EthenaUnstaker} from "contracts/EthenaUnstaker.sol"; + +// Interfaces +import {IERC20} from "contracts/Interfaces.sol"; +import {IStakedUSDe} from "contracts/Interfaces.sol"; + +// Tests +import {Vm} from "./helpers/Vm.sol"; + +/// @notice This contract should be the common parent for all test contracts. +/// It should be used to define common variables and that will be +/// used across all test contracts. This pattern is used to allow different +/// test contracts to share common variables, and ensure a consistent setup. +/// @dev This contract should be inherited by "Shared" contracts. +/// @dev This contract should only be used as storage for common variables. +/// @dev Helpers and other functions should be defined in a separate contract. +abstract contract Base_Test_ { + ////////////////////////////////////////////////////// + /// --- CONTRACTS + ////////////////////////////////////////////////////// + // --- Main contracts --- + Proxy internal armProxy; + Proxy internal morphoMarketProxy; + EthenaARM internal arm; + MockMorpho internal morpho; + MorphoMarket internal market; + EthenaUnstaker[] internal unstakers; + uint256[] internal unstakerIndices; + + // --- Tokens --- + IERC20 internal usde; + IStakedUSDe internal susde; + + // --- Utils --- + Vm internal vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + + ////////////////////////////////////////////////////// + /// --- USERS + ////////////////////////////////////////////////////// + // --- Users with roles --- + address internal deployer; + address internal governor; + address internal operator; + address internal treasury; + + // --- Regular users --- + address internal alice; + address internal bobby; + address internal carol; + address internal david; + address internal elise; + address internal frank; + address internal grace; + address internal harry; + address internal dead; + + // --- Group of users --- + address[] internal makers; + address[] internal traders; + mapping(address => uint256[]) internal pendingRequests; + + ////////////////////////////////////////////////////// + /// --- DEFAULT VALUES + ////////////////////////////////////////////////////// + uint256 internal constant MAKERS_COUNT = 3; + uint256 internal constant TRADERS_COUNT = 3; + uint256 internal constant UNSTAKERS_COUNT = 42; + uint256 internal constant DEFAULT_CLAIM_DELAY = 10 minutes; + uint256 internal constant DEFAULT_MIN_TOTAL_SUPPLY = 1e12; + uint256 internal constant DEFAULT_ALLOCATE_THRESHOLD = 1e18; + uint256 internal constant DEFAULT_MIN_SHARES_TO_REDEEM = 1e7; + + /// @notice Indicates if labels have been set in the Vm. + bool public isLabelAvailable; + bool public isAssumeAvailable; + bool public isConsoleAvailable; + + ////////////////////////////////////////////////////// + /// --- GHOST VALUES + ////////////////////////////////////////////////////// + // --- USDe values --- + uint256 internal sumUSDeSwapIn; + uint256 internal sumUSDeSwapOut; + uint256 internal sumUSDeUserDeposit; + uint256 internal sumUSDeUserRedeem; + uint256 internal sumUSDeUserRequest; + uint256 internal sumUSDeBaseRedeem; + uint256 internal sumUSDeFeesCollected; + uint256 internal sumUSDeMarketDeposit; + uint256 internal sumUSDeMarketWithdraw; + mapping(address => uint256) internal mintedUSDe; + // --- sUSDe values --- + uint256 internal sumSUSDeSwapIn; + uint256 internal sumSUSDeSwapOut; + uint256 internal sumSUSDeBaseRedeem; +} + diff --git a/test/invariants/EthenaARM/FuzzerFoundry_EthenaARM.sol b/test/invariants/EthenaARM/FuzzerFoundry_EthenaARM.sol new file mode 100644 index 00000000..7c3208a8 --- /dev/null +++ b/test/invariants/EthenaARM/FuzzerFoundry_EthenaARM.sol @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.23; + +// Test imports +import {Properties} from "./Properties.sol"; +import {StdInvariant} from "forge-std/StdInvariant.sol"; +import {StdAssertions} from "forge-std/StdAssertions.sol"; + +/// @title FuzzerFoundry_EthenaARM +/// @notice Concrete fuzzing contract implementing Foundry's invariant testing framework. +/// @dev This contract configures and executes property-based testing: +/// - Inherits from Properties to access handler functions and properties +/// - Configures fuzzer targeting (contracts, selectors, senders) +/// - Implements invariant test functions that call property validators +/// - Each invariant function represents a critical system property to maintain +/// - Fuzzer will call targeted handlers randomly and check invariants after each call +contract FuzzerFoundry_EthenaARM is Properties, StdInvariant, StdAssertions { + ////////////////////////////////////////////////////// + /// --- SETUP + ////////////////////////////////////////////////////// + constructor() { + // --- Fuzzer configuration --- + isLabelAvailable = true; + isAssumeAvailable = true; + isConsoleAvailable = true; + } + + function setUp() public { + // --- Common setup --- + _setup(); + + // --- Setup Fuzzer target --- + // Setup target + targetContract(address(this)); + + // Add selectors + bytes4[] memory selectors = new bytes4[](22); + // --- sUSDe --- + selectors[0] = this.targetSUSDeDeposit.selector; + selectors[1] = this.targetSUSDeCooldownShares.selector; + selectors[2] = this.targetSUSDeUnstake.selector; + selectors[3] = this.targetSUSDeTransferInRewards.selector; + // --- Morpho --- + selectors[4] = this.targetMorphoDeposit.selector; + selectors[5] = this.targetMorphoWithdraw.selector; + selectors[6] = this.targetMorphoTransferInRewards.selector; + selectors[7] = this.targetMorphoSetUtilizationRate.selector; + // --- ARM --- + selectors[8] = this.targetARMDeposit.selector; + selectors[9] = this.targetARMRequestRedeem.selector; + selectors[10] = this.targetARMClaimRedeem.selector; + selectors[11] = this.targetARMSetARMBuffer.selector; + selectors[12] = this.targetARMSetActiveMarket.selector; + selectors[13] = this.targetARMAllocate.selector; + selectors[14] = this.targetARMSetPrices.selector; + selectors[15] = this.targetARMSetCrossPrice.selector; + selectors[16] = this.targetARMSwapExactTokensForTokens.selector; + selectors[17] = this.targetARMSwapTokensForExactTokens.selector; + selectors[18] = this.targetARMCollectFees.selector; + selectors[19] = this.targetARMSetFees.selector; + selectors[20] = this.targetARMRequestBaseWithdrawal.selector; + selectors[21] = this.targetARMClaimBaseWithdrawals.selector; + // Target selectors + targetSelector(FuzzSelector({addr: address(this), selectors: selectors})); + } + + ////////////////////////////////////////////////////// + /// --- INVARIANTS + ////////////////////////////////////////////////////// + function invariantSwap() public view { + assertTrue(propertyA(), "Property A failed"); + assertTrue(propertyB(), "Property B failed"); + } + + function invariantLP() public { + assertTrue(propertyC(), "Property C failed"); + assertTrue(propertyD(), "Property D failed"); + assertTrue(propertyE(), "Property E failed"); + assertTrue(propertyF(), "Property F failed"); + assertTrue(propertyG(), "Property G failed"); + assertTrue(propertyH(), "Property H failed"); + assertTrue(propertyI(), "Property I failed"); + assertTrue(propertyJ(), "Property J failed"); + assertTrue(propertyK(), "Property K failed"); + } + + function invariantLiquidity() public { + assertTrue(propertyL(), "Property L failed"); + assertTrue(propertyM(), "Property M failed"); + assertTrue(propertyN(), "Property N failed"); + } + + function afterInvariant() public { + _targetAfterAll(); + assertTrue(_propertyAfterAll(), "Property After All failed"); + } +} diff --git a/test/invariants/EthenaARM/FuzzerMedusa_EthenaARM.sol b/test/invariants/EthenaARM/FuzzerMedusa_EthenaARM.sol new file mode 100644 index 00000000..5af87193 --- /dev/null +++ b/test/invariants/EthenaARM/FuzzerMedusa_EthenaARM.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.23; + +// Test imports +import {Properties} from "./Properties.sol"; + +/// @title FuzzerMedusa_EthenaARM +/// @notice Concrete fuzzing contract implementing Medusa's invariant testing framework. +/// @dev This contract configures and executes property-based testing: +/// - Inherits from Properties to access handler functions and properties +/// - All configuration is done in medusa.json. +contract FuzzerMedusa_EthenaARM is Properties { + ////////////////////////////////////////////////////// + /// --- SETUP + ////////////////////////////////////////////////////// + constructor() { + _setup(); + } +} diff --git a/test/invariants/EthenaARM/Properties.sol b/test/invariants/EthenaARM/Properties.sol new file mode 100644 index 00000000..ad5d9da7 --- /dev/null +++ b/test/invariants/EthenaARM/Properties.sol @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.23; + +// Foundry +import {console} from "forge-std/console.sol"; + +// Test imports +import {TargetFunctions} from "./TargetFunctions.sol"; + +// Helpers +import {Math} from "./helpers/Math.sol"; + +// Interfaces +import {UserCooldown} from "contracts/Interfaces.sol"; + +/// @title Properties +/// @notice Abstract contract defining invariant properties for formal verification and fuzzing. +/// @dev This contract contains pure property functions that express system invariants: +/// - Properties must be implemented as view/pure functions returning bool +/// - Each property should represent a mathematical invariant of the system +/// - Properties should be stateless and deterministic +/// - Property names should clearly indicate what invariant they check +/// Usage: Properties are called by fuzzing contracts to validate system state +abstract contract Properties is TargetFunctions { + // ╔══════════════════════════════════════════════════════════════════════════════╗ + // ║ ✦✦✦ SWAP PROPERTIES ✦✦✦ ║ + // ╚══════════════════════════════════════════════════════════════════════════════╝ + // [x] Invariant A: USDe balance == ∑swapIn - ∑swapOut + // + ∑userDeposit - ∑userWithdraw + // + ∑marketWithdraw - ∑marketDeposit + // + ∑baseRedeem - ∑feesCollected + // [x] Invariant B: sUSDe balance == (∑swapIn - ∑swapOut) - ∑baseRedeem + // + // ╔══════════════════════════════════════════════════════════════════════════════╗ + // ║ ✦✦✦ LP PROPERTIES ✦✦✦ ║ + // ╚══════════════════════════════════════════════════════════════════════════════╝ + // [x] Invariant C: ∑shares > 0 due to initial deposit + // [x] Invariant D: totalShares == ∑userShares + deadShares + // [x] Invariant E: previewRedeem(∑shares) == totalAssets + // [x] Invariant F: withdrawsQueued == ∑requestRedeem.amount + // [x] Invariant G: withdrawsQueued >= withdrawsClaimed + // [x] Invariant H: withdrawsQueued == ∑request.assets + // [x] Invariant I: withdrawsClaimed == ∑claimRedeem.amount + // [x] Invariant J: ∀ requestId, request.queued >= request.assets + // [x] Invariant K: ∑feesCollected == feeCollector.balance + // + // ╔══════════════════════════════════════════════════════════════════════════════╗ + // ║ ✦✦✦ LIQUIDITY MANAGEMENT ✦✦✦ ║ + // ╚══════════════════════════════════════════════════════════════════════════════╝ + // [x] Invariant L: liquidityAmountInCooldown == ∑unstaker.underlyingAmount + // [x] Invariant M: nextUnstakerIndex < MAX_UNSTAKERS + // [x] Invariant N: ∀ unstaker, usde.balanceOf(unstaker) == 0 && susde.balanceOf(unstaker) == 0 + // + // ╔══════════════════════════════════════════════════════════════════════════════╗ + // ║ ✦✦✦ AFTER ALL ✦✦✦ ║ + // ╚══════════════════════════════════════════════════════════════════════════════╝ + // [x] sUSDe in ARM == 0 + // [x] Morpho shares in ARM == 0 + // [x] ∀ user, usde.balanceOf(user) >= totalMinted - 1e1 + // + // ╔══════════════════════════════════════════════════════════════════════════════╗ + // ║ ✦✦✦ ✦✦✦ ║ + // ╚══════════════════════════════════════════════════════════════════════════════╝ + + // ╔══════════════════════════════════════════════════════════════════════════════╗ + // ║ ✦✦✦ SWAP PROPERTIES ✦✦✦ ║ + // ╚══════════════════════════════════════════════════════════════════════════════╝ + function propertyA() public view returns (bool) { + uint256 usdeBalance = usde.balanceOf(address(arm)); + uint256 inflow = 1e12 + sumUSDeSwapIn + sumUSDeUserDeposit + sumUSDeMarketWithdraw + sumUSDeBaseRedeem; + uint256 outflow = sumUSDeSwapOut + sumUSDeUserRedeem + sumUSDeMarketDeposit + sumUSDeFeesCollected; + // console.log(">>> Property A:"); + // console.log(" - USDe balance: %18e", usdeBalance); + // console.log(" - Inflow breakdown:"); + // console.log(" o Initial buffer: %18e", uint256(1e12)); + // console.log(" o Swap In: %18e", sumUSDeSwapIn); + // console.log(" o User Deposit: %18e", sumUSDeUserDeposit); + // console.log(" o Market Withdraw: %18e", sumUSDeMarketWithdraw); + // console.log(" o Base Redeem: %18e", sumUSDeBaseRedeem); + // console.log(" - USDe inflow sum: %18e", inflow); + // console.log(" - Outflow breakdown:"); + // console.log(" o Swap Out: %18e", sumUSDeSwapOut); + // console.log(" o User Redeem: %18e", sumUSDeUserRedeem); + // console.log(" o Market Deposit: %18e", sumUSDeMarketDeposit); + // console.log(" o Fees Collected: %18e", sumUSDeFeesCollected); + // console.log(" - USDe outflow sum: %18e", outflow); + // console.log(" - Diff: %18e", Math.absDiff(inflow, outflow)); + return Math.eq(usdeBalance, Math.absDiff(inflow, outflow)); + } + + function propertyB() public view returns (bool) { + uint256 susdeBalance = susde.balanceOf(address(arm)); + uint256 inflow = sumSUSDeSwapIn; + uint256 outflow = sumSUSDeSwapOut + sumSUSDeBaseRedeem; + // console.log(">>> Property B:"); + // console.log(" - sUSDe balance: %18e", susdeBalance); + // console.log(" - Inflow breakdown:"); + // console.log(" o Swap In: %18e", sumSUSDeSwapIn); + // console.log(" - sUSDe inflow sum: %18e", inflow); + // console.log(" - Outflow breakdown:"); + // console.log(" o Swap Out: %18e", sumSUSDeSwapOut); + // console.log(" o Base Redeem: %18e", sumSUSDeBaseRedeem); + // console.log(" - sUSDe outflow sum: %18e", outflow); + // console.log(" - Diff: %18e", Math.absDiff(inflow, outflow)); + return Math.eq(susdeBalance, Math.absDiff(inflow, outflow)); + } + + // ╔══════════════════════════════════════════════════════════════════════════════╗ + // ║ ✦✦✦ LP PROPERTIES ✦✦✦ ║ + // ╚══════════════════════════════════════════════════════════════════════════════╝ + function propertyC() public view returns (bool) { + return Math.gt(arm.totalSupply(), 0); + } + + function propertyD() public view returns (bool) { + uint256 totalUserShares = 0; + for (uint256 i = 0; i < MAKERS_COUNT; i++) { + totalUserShares += arm.balanceOf(makers[i]); + } + uint256 deadShares = 1e12; + return Math.eq(arm.totalSupply(), totalUserShares + deadShares); + } + + function propertyE() public view returns (bool) { + return Math.eq(arm.previewRedeem(arm.totalSupply()), arm.totalAssets()); + } + + function propertyF() public view returns (bool) { + return Math.eq(arm.withdrawsQueued(), sumUSDeUserRequest); + } + + function propertyG() public view returns (bool) { + return Math.gte(arm.withdrawsQueued(), arm.withdrawsClaimed()); + } + + function propertyH() public view returns (bool) { + uint256 sum = 0; + uint256 len = arm.nextWithdrawalIndex(); + for (uint256 i; i < len; i++) { + (,,, uint128 amount,) = arm.withdrawalRequests(i); + sum += amount; + } + return Math.eq(arm.withdrawsQueued(), sum); + } + + function propertyI() public view returns (bool) { + return Math.eq(arm.withdrawsClaimed(), sumUSDeUserRedeem); + } + + function propertyJ() public view returns (bool) { + uint256 len = arm.nextWithdrawalIndex(); + for (uint256 i; i < len; i++) { + (,,, uint128 amount, uint128 queued) = arm.withdrawalRequests(i); + if (queued < amount) { + return false; + } + } + return true; + } + + function propertyK() public view returns (bool) { + uint256 feeCollectorBalance = usde.balanceOf(treasury); + return Math.eq(sumUSDeFeesCollected, feeCollectorBalance); + } + + // ╔══════════════════════════════════════════════════════════════════════════════╗ + // ║ ✦✦✦ LIQUIDITY MANAGEMENT ✦✦✦ ║ + // ╚══════════════════════════════════════════════════════════════════════════════╝ + function propertyL() public returns (bool) { + uint256 liquidityAmountInCooldown; + uint256 len = unstakers.length; + for (uint256 i; i < len; i++) { + UserCooldown memory cooldown = susde.cooldowns(address(unstakers[i])); + liquidityAmountInCooldown += cooldown.underlyingAmount; + } + return Math.eq(liquidityAmountInCooldown, uint256(vm.load(address(arm), bytes32(uint256(100))))); + } + + function propertyM() public view returns (bool) { + uint256 nextUnstakerIndex = arm.nextUnstakerIndex(); + return Math.lt(nextUnstakerIndex, arm.MAX_UNSTAKERS()); + } + + function propertyN() public view returns (bool) { + uint256 len = unstakers.length; + for (uint256 i; i < len; i++) { + address unstaker = address(unstakers[i]); + if (usde.balanceOf(unstaker) != 0 || susde.balanceOf(unstaker) != 0) { + return false; + } + } + return true; + } + + // ╔══════════════════════════════════════════════════════════════════════════════╗ + // ║ ✦✦✦ AFTER ALL ✦✦✦ ║ + // ╚══════════════════════════════════════════════════════════════════════════════╝ + function _propertyAfterAll() internal returns (bool) { + uint256 usdeBalance = usde.balanceOf(address(arm)); + uint256 susdeBalance = susde.balanceOf(address(arm)); + uint256 morphoBalance = morpho.balanceOf(address(arm)); + uint256 armTotalAssets = arm.totalAssets(); + if (isConsoleAvailable) { + console.log("--- Final Balances ---"); + console.log("ARM USDe balance:\t %18e", usdeBalance); + console.log("ARM sUSDe balance:\t %18e", susdeBalance); + console.log("ARM Morpho shares:\t %18e", morphoBalance); + console.log("ARM total assets:\t %18e", armTotalAssets); + } + require(susdeBalance == 0, "sUSDe balance not zero"); + require(morphoBalance == 0, "Morpho shares not zero"); + for (uint256 i; i < MAKERS_COUNT; i++) { + address user = makers[i]; + uint256 totalMinted = mintedUSDe[user]; + uint256 userBalance = usde.balanceOf(user); + if (!Math.approxGteAbs(userBalance, totalMinted, 1e1)) { + if (isConsoleAvailable) { + console.log(">>> Property After All failed for user %s:", vm.getLabel(user)); + console.log(" - User USDe balance: %18e", userBalance); + console.log(" - Total minted USDe: %18e", totalMinted); + console.log(" - Difference: %18e", Math.absDiff(userBalance, totalMinted)); + } + return false; + } + } + return true; + } +} diff --git a/test/invariants/EthenaARM/Setup.sol b/test/invariants/EthenaARM/Setup.sol new file mode 100644 index 00000000..ca1f12a8 --- /dev/null +++ b/test/invariants/EthenaARM/Setup.sol @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.23; + +// Test imports +import {Base_Test_} from "./Base.sol"; + +// Contracts +import {Proxy} from "contracts/Proxy.sol"; +import {EthenaARM} from "contracts/EthenaARM.sol"; +import {MorphoMarket} from "src/contracts/markets/MorphoMarket.sol"; +import {EthenaUnstaker} from "contracts/EthenaUnstaker.sol"; +import {Abstract4626MarketWrapper} from "contracts/markets/Abstract4626MarketWrapper.sol"; + +// Mocks +import {MockERC20} from "@solmate/test/utils/mocks/MockERC20.sol"; +import {MockSUSDE} from "test/invariants/EthenaARM/mocks/MockSUSDE.sol"; +import {MockMorpho} from "test/invariants/EthenaARM/mocks/MockMorpho.sol"; + +// Interfaces +import {IERC20} from "contracts/Interfaces.sol"; +import {IStakedUSDe} from "contracts/Interfaces.sol"; + +/// @notice Shared invariant test contract. +/// @dev This contract should be used for deploying all contracts and mocks needed for the test. +abstract contract Setup is Base_Test_ { + ////////////////////////////////////////////////////// + /// --- SETUP + ////////////////////////////////////////////////////// + function _setup() internal virtual { + // 1. Setup a realistic test environnement. + _setUpRealisticEnvironnement(); + + // 2. Create user. + _createUsers(); + + // 3. Deploy mocks. + _deployMocks(); + + // 4. Deploy contracts. + _deployContracts(); + + // 5. Label addresses + _labelAll(); + + // 6. Ignite contracts + _ignite(); + } + + function _setUpRealisticEnvironnement() internal virtual { + vm.warp(1_800_000_000); // Warp to a future timestamp + vm.roll(24_000_000); // Warp to a future block number + } + + function _createUsers() internal virtual { + // --- Users with roles --- + deployer = generateAddr("deployer"); + governor = generateAddr("governor"); + operator = generateAddr("operator"); + treasury = generateAddr("treasury"); + + // --- Regular users --- + alice = generateAddr("alice"); + bobby = generateAddr("bobby"); + carol = generateAddr("carol"); + david = generateAddr("david"); + elise = generateAddr("elise"); + frank = generateAddr("frank"); + grace = generateAddr("grace"); + harry = generateAddr("harry"); + dead = generateAddr("dead"); + + // --- Group of users --- + makers = new address[](MAKERS_COUNT); + makers[0] = alice; + makers[1] = bobby; + makers[2] = carol; + + traders = new address[](TRADERS_COUNT); + traders[0] = david; + traders[1] = elise; + traders[2] = frank; + } + + function _deployMocks() internal virtual { + // Deploy mock USDe. + usde = IERC20(address(new MockERC20("USDe", "USDe", 18))); + + // Deploy mock sUSDe. + susde = IStakedUSDe(address(new MockSUSDE(address(usde), governor))); + + // Deploy mock Morpho Market. + morpho = new MockMorpho(address(usde)); + } + + function _deployContracts() internal virtual { + vm.startPrank(deployer); + + // --- Ethena ARM --- + // Deploy Ethena ARM proxy. + armProxy = new Proxy(); + + // Deploy Ethena ARM implementation. + arm = new EthenaARM({ + _usde: address(usde), + _susde: address(susde), + _claimDelay: DEFAULT_CLAIM_DELAY, + _minSharesToRedeem: DEFAULT_MIN_SHARES_TO_REDEEM, + _allocateThreshold: int256(DEFAULT_ALLOCATE_THRESHOLD) + }); + + // Initialization requires to transfer some USDe to the proxy from the deployer. + MockERC20(address(usde)).mint(deployer, DEFAULT_MIN_TOTAL_SUPPLY); + usde.approve(address(armProxy), DEFAULT_MIN_TOTAL_SUPPLY); + + // Initialize Ethena ARM proxy. + bytes memory data = abi.encodeWithSelector( + EthenaARM.initialize.selector, + "Ethena ARM", + "ARM-USDe-sUSDe", + operator, + 2000, // 20% performance fee + treasury, + address(0) // CapManager address + ); + armProxy.initialize(address(arm), deployer, data); + + // Cast proxy address to EthenaARM type for easier interaction. + arm = EthenaARM(address(armProxy)); + + // --- Ethena Unstakers --- + // Deploy 42 Ethena Unstaker contracts + address[UNSTAKERS_COUNT] memory _unstakers; + for (uint256 i; i < UNSTAKERS_COUNT; i++) { + unstakers.push(new EthenaUnstaker(address(arm), susde)); + _unstakers[i] = address(unstakers[i]); + } + // Set unstakers in the ARM + arm.setUnstakers(_unstakers); + + // Transfer ownership of the ARM to the governor. + arm.setOwner(governor); + + // --- Morpho Market --- + // Deploy Morpho Market Proxy. + morphoMarketProxy = new Proxy(); + + // Deploy Morpho Market implementation. + market = new MorphoMarket(address(arm), address(morpho)); + + // Initialize Morpho Market proxy. + data = abi.encodeWithSelector(Abstract4626MarketWrapper.initialize.selector, address(0x1), address(0x1)); + morphoMarketProxy.initialize(address(market), governor, data); + + // Cast proxy address to MorphoMarket type for easier interaction. + market = MorphoMarket(address(morphoMarketProxy)); + + vm.stopPrank(); + } + + function _labelAll() internal virtual { + // This only works with Foundry's Vm.label feature. + if (!isLabelAvailable) return; + + // --- Proxies --- + vm.label(address(armProxy), "Proxy EthenaARM"); + vm.label(address(morphoMarketProxy), "Proxy MorphoMarket"); + + // --- Implementations --- + vm.label(address(arm), "Ethena ARM"); + vm.label(address(market), "Morpho Market"); + vm.label(address(morpho), "Morpho Blue"); + vm.label(address(unstakers[0]), "Ethena Unstaker 0"); + vm.label(address(unstakers[1]), "Ethena Unstaker 1"); + vm.label(address(unstakers[2]), "Ethena Unstaker 2"); + vm.label(address(unstakers[3]), "Ethena Unstaker 3"); + vm.label(address(unstakers[4]), "Ethena Unstaker 4"); + vm.label(address(unstakers[5]), "Ethena Unstaker 5"); + vm.label(address(unstakers[6]), "Ethena Unstaker 6"); + vm.label(address(unstakers[7]), "Ethena Unstaker 7"); + vm.label(address(unstakers[8]), "Ethena Unstaker 8"); + vm.label(address(unstakers[9]), "Ethena Unstaker 9"); + vm.label(address(unstakers[10]), "Ethena Unstaker 10"); + vm.label(address(unstakers[11]), "Ethena Unstaker 11"); + vm.label(address(unstakers[12]), "Ethena Unstaker 12"); + vm.label(address(unstakers[13]), "Ethena Unstaker 13"); + vm.label(address(unstakers[14]), "Ethena Unstaker 14"); + vm.label(address(unstakers[15]), "Ethena Unstaker 15"); + vm.label(address(unstakers[16]), "Ethena Unstaker 16"); + vm.label(address(unstakers[17]), "Ethena Unstaker 17"); + vm.label(address(unstakers[18]), "Ethena Unstaker 18"); + vm.label(address(unstakers[19]), "Ethena Unstaker 19"); + vm.label(address(unstakers[20]), "Ethena Unstaker 20"); + vm.label(address(unstakers[21]), "Ethena Unstaker 21"); + vm.label(address(unstakers[22]), "Ethena Unstaker 22"); + vm.label(address(unstakers[23]), "Ethena Unstaker 23"); + vm.label(address(unstakers[24]), "Ethena Unstaker 24"); + vm.label(address(unstakers[25]), "Ethena Unstaker 25"); + vm.label(address(unstakers[26]), "Ethena Unstaker 26"); + vm.label(address(unstakers[27]), "Ethena Unstaker 27"); + vm.label(address(unstakers[28]), "Ethena Unstaker 28"); + vm.label(address(unstakers[29]), "Ethena Unstaker 29"); + vm.label(address(unstakers[30]), "Ethena Unstaker 30"); + vm.label(address(unstakers[31]), "Ethena Unstaker 31"); + vm.label(address(unstakers[32]), "Ethena Unstaker 32"); + vm.label(address(unstakers[33]), "Ethena Unstaker 33"); + vm.label(address(unstakers[34]), "Ethena Unstaker 34"); + vm.label(address(unstakers[35]), "Ethena Unstaker 35"); + vm.label(address(unstakers[36]), "Ethena Unstaker 36"); + vm.label(address(unstakers[37]), "Ethena Unstaker 37"); + vm.label(address(unstakers[38]), "Ethena Unstaker 38"); + vm.label(address(unstakers[39]), "Ethena Unstaker 39"); + vm.label(address(unstakers[40]), "Ethena Unstaker 40"); + vm.label(address(unstakers[41]), "Ethena Unstaker 41"); + // Using a loop here would be cleaner, but Vm.label doesn't support dynamic strings. + + // --- Tokens --- + vm.label(address(usde), "USDe"); + vm.label(address(susde), "sUSDe"); + + // --- Users with roles --- + vm.label(deployer, "Deployer"); + vm.label(governor, "Governor"); + vm.label(operator, "Operator"); + vm.label(treasury, "Treasury"); + + // --- Regular users --- + vm.label(alice, "Alice"); + vm.label(bobby, "Bobby"); + vm.label(carol, "Carol"); + vm.label(david, "David"); + vm.label(elise, "Elise"); + vm.label(frank, "Frank"); + vm.label(grace, "Grace"); + vm.label(harry, "Harry"); + vm.label(dead, "Dead"); + } + + function _ignite() internal virtual { + // As sUSDe is an ERC4626, we want to avoid small total supply issues. + // So we mint some sUSDe to the dead address, to replicate a realistic scenario. + MockERC20(address(usde)).mint(address(dead), 2_000_000 ether); + + vm.startPrank(dead); + usde.approve(address(susde), 1_000_000 ether); + susde.deposit(1_000_000 ether, dead); + + // Same for morpho contract. + usde.approve(address(morpho), 1_000_000 ether); + morpho.deposit(1_000_000 ether, dead); + vm.stopPrank(); + + // Set initial prices in the ARM. + vm.prank(governor); + arm.setCrossPrice(0.9998e36); + vm.prank(operator); + arm.setPrices(0.9992e36, 0.9999e36); + address[] memory markets = new address[](1); + markets[0] = address(market); + vm.prank(governor); + arm.addMarkets(markets); + + // Grace will only deposit/withdraw USDe from/to sUSDe. + vm.prank(grace); + usde.approve(address(susde), type(uint256).max); + + // Harry will only deposit/withdraw USDe from/to Morpho. + vm.prank(harry); + usde.approve(address(morpho), type(uint256).max); + + // Governor will deposit usde rewards into sUSDe. + vm.prank(governor); + usde.approve(address(susde), type(uint256).max); + + // Makers and traders approve ARM to spend their USDe. + for (uint256 i; i < MAKERS_COUNT; i++) { + vm.prank(makers[i]); + usde.approve(address(arm), type(uint256).max); + } + + for (uint256 i; i < TRADERS_COUNT; i++) { + vm.startPrank(traders[i]); + usde.approve(address(arm), type(uint256).max); + usde.approve(address(susde), type(uint256).max); + susde.approve(address(arm), type(uint256).max); + vm.stopPrank(); + } + } + + function generateAddr(string memory name) internal returns (address) { + return vm.addr(uint256(keccak256(abi.encodePacked(name)))); + } + + function assume(bool condition) internal returns (bool returnEarly) { + if (!condition) { + if (isAssumeAvailable) vm.assume(false); + else returnEarly = true; + } + } + + function getExchangeRate() internal view returns (uint256) { + uint256 totalAssets = susde.totalAssets(); + uint256 totalSupply = susde.totalSupply(); + return (totalAssets * 1e18) / totalSupply; + } + + modifier ensureTimeIncrease() { + uint256 oldTimestamp = block.timestamp; + _; + require(block.timestamp >= oldTimestamp, "TIME_DECREASED"); + } + + modifier ensureExchangeRateIncrease() { + uint256 oldExchangeRate = getExchangeRate(); + _; + require(getExchangeRate() >= oldExchangeRate, "EXCHANGE_RATE_DECREASED"); + } +} diff --git a/test/invariants/EthenaARM/TargetFunctions.sol b/test/invariants/EthenaARM/TargetFunctions.sol new file mode 100644 index 00000000..e11bb331 --- /dev/null +++ b/test/invariants/EthenaARM/TargetFunctions.sol @@ -0,0 +1,877 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.23; + +// Test imports +import {Setup} from "./Setup.sol"; +import {console} from "forge-std/console.sol"; +import {StdUtils} from "forge-std/StdUtils.sol"; +import {StdStyle} from "forge-std/StdStyle.sol"; + +// Solmate +import {MockERC20} from "@solmate/test/utils/mocks/MockERC20.sol"; + +// Contracts +import {IERC20} from "contracts/Interfaces.sol"; +import {IERC4626} from "contracts/Interfaces.sol"; +import {UserCooldown} from "contracts/Interfaces.sol"; + +// Helpers +import {Find} from "./helpers/Find.sol"; +import {Math} from "./helpers/Math.sol"; + +/// @title TargetFunctions +/// @notice TargetFunctions contract for tests, containing the target functions that should be tested. +/// This is the entry point with the contract we are testing. Ideally, it should never revert. +abstract contract TargetFunctions is Setup, StdUtils { + // ╔══════════════════════════════════════════════════════════════════════════════╗ + // ║ ✦✦✦ ETHENA ARM ✦✦✦ ║ + // ╚══════════════════════════════════════════════════════════════════════════════╝ + // [x] SwapExactTokensForTokens + // [x] SwapTokensForExactTokens + // [x] Deposit + // [x] Allocate + // [x] CollectFees + // [x] RequestRedeem + // [x] ClaimRedeem + // [x] RequestBaseWithdrawal + // [x] ClaimBaseWithdrawals + // --- Admin functions + // [x] SetPrices + // [x] SetCrossPrice + // [x] SetFee + // [x] SetActiveMarket + // [x] SetARMBuffer + // + // ╔══════════════════════════════════════════════════════════════════════════════╗ + // ║ ✦✦✦ SUSDE ✦✦✦ ║ + // ╚══════════════════════════════════════════════════════════════════════════════╝ + // [x] Deposit + // [x] CoolDownShares + // [x] Unstake + // --- Admin functions + // [x] TransferInRewards + // + // ╔══════════════════════════════════════════════════════════════════════════════╗ + // ║ ✦✦✦ MORPHO ✦✦✦ ║ + // ╚══════════════════════════════════════════════════════════════════════════════╝ + // [x] Deposit + // [x] Withdraw + // [x] TransferInRewards + // [x] SetUtilizationRate + // + // ╔══════════════════════════════════════════════════════════════════════════════╗ + // ║ ✦✦✦ ✦✦✦ ║ + // ╚══════════════════════════════════════════════════════════════════════════════╝ + + // ╔══════════════════════════════════════════════════════════════════════════════╗ + // ║ ✦✦✦ ETHENA ARM ✦✦✦ ║ + // ╚══════════════════════════════════════════════════════════════════════════════╝ + function targetARMDeposit(uint88 amount, uint256 randomAddressIndex) external ensureExchangeRateIncrease { + // Select a random user from makers + address user = makers[randomAddressIndex % MAKERS_COUNT]; + + uint256 totalSupply = arm.totalSupply(); + uint256 totalAssets = arm.totalAssets(); + // Min amount to avoid 0 shares minting + uint256 minAmount = totalAssets / totalSupply + 1; + amount = uint88(_bound(amount, minAmount, type(uint88).max)); + + // Mint amount to user + MockERC20(address(usde)).mint(user, amount); + // Deposit as user + vm.prank(user); + uint256 shares = arm.deposit(amount, user); + + if (isConsoleAvailable) { + console.log( + ">>> ARM Deposit:\t %s deposited %18e USDe\t and received %18e ARM shares", + vm.getLabel(user), + amount, + shares + ); + } + + sumUSDeUserDeposit += amount; + mintedUSDe[user] += amount; + } + + function targetARMRequestRedeem(uint88 shareAmount, uint248 randomAddressIndex) + external + ensureExchangeRateIncrease + { + address user; + uint256 balance; + (user, balance) = Find.getUserWithARMShares(makers, address(arm)); + if (assume(user != address(0))) return; + // Bound shareAmount to [1, balance] + shareAmount = uint88(_bound(shareAmount, 1, balance)); + + // Request redeem as user + vm.prank(user); + (uint256 requestId, uint256 amount) = arm.requestRedeem(shareAmount); + pendingRequests[user].push(requestId); + + if (isConsoleAvailable) { + console.log( + string( + abi.encodePacked( + ">>> ARM Request:\t ", + vm.getLabel(user), + " requested redeem of %18e ARM shares\t for %18e USDe underlying\t Request ID: %d" + ) + ), + shareAmount, + amount, + requestId + ); + } + + sumUSDeUserRequest += amount; + } + + function targetARMClaimRedeem(uint248 randomAddressIndex, uint248 randomArrayIndex) + external + ensureExchangeRateIncrease + ensureTimeIncrease + { + address user; + uint256 requestId; + uint256 claimTimestamp; + uint256 claimable = arm.claimable(); + uint256 availableLiquidity = usde.balanceOf(address(arm)); + address market = arm.activeMarket(); + if (market != address(0)) { + availableLiquidity += IERC4626(market).maxWithdraw(address(arm)); + } + if (assume(claimable != 0)) return; + // Find a user with a pending request, where the amount is <= claimable + { + (user, requestId, claimTimestamp) = Find.getUserRequestWithAmount( + Find.GetUserRequestWithAmountStruct({ + arm: address(arm), + randomAddressIndex: randomAddressIndex, + randomArrayIndex: randomArrayIndex, + users: makers, + claimable: uint128(claimable), + availableLiquidity: uint128(availableLiquidity) + }), + pendingRequests + ); + if (assume(user != address(0))) return; + } + + // Fast forward time if needed + if (block.timestamp < claimTimestamp) { + if (isConsoleAvailable) { + console.log( + StdStyle.yellow( + string( + abi.encodePacked( + ">>> Time jump:\t Fast forwarded to: ", + vm.toString(claimTimestamp), + " (+ ", + vm.toString(claimTimestamp - block.timestamp), + "s)" + ) + ) + ) + ); + } + vm.warp(claimTimestamp); + } + + // Claim redeem as user + uint256 balanceBefore = usde.balanceOf(address(arm)); + vm.prank(user); + uint256 amount = arm.claimRedeem(requestId); + + if (isConsoleAvailable) { + console.log( + string( + abi.encodePacked( + ">>> ARM Claim:\t ", + vm.getLabel(user), + " claimed redeem request ID %d\t and received %18e USDe underlying" + ) + ), + requestId, + amount + ); + } + + sumUSDeUserRedeem += amount; + if (balanceBefore < amount) { + // This means we had to withdraw from market + sumUSDeMarketWithdraw += amount - balanceBefore; + } + } + + function targetARMSetARMBuffer(uint256 pct) external ensureExchangeRateIncrease { + pct = _bound(pct, 0, 100); + + vm.prank(operator); + arm.setARMBuffer(pct * 1e16); + + if (isConsoleAvailable) { + console.log(">>> ARM Buffer:\t Governor set ARM buffer to %s%", pct); + } + } + + function targetARMSetActiveMarket(bool isActive) external ensureExchangeRateIncrease { + // If isActive is true it will `setActiveMarket` with MorphoMarket + // else it will set it to address(0) + address currentMarket = arm.activeMarket(); + address targetMarket = isActive ? address(market) : address(0); + + // If the current market is the morpho market and we want to deactivate it + // ensure the is enough liquidity in Morpho to cover the ARM's assets withdrawals + if (currentMarket == address(market) && !isActive) { + uint256 shares = market.balanceOf(address(arm)); + uint256 assets = market.convertToAssets(shares); + uint256 availableLiquidity = morpho.availableLiquidity(); + if (assume(assets < availableLiquidity)) return; + } + + uint256 balanceBefore = usde.balanceOf(address(arm)); + vm.prank(operator); + arm.setActiveMarket(targetMarket); + uint256 balanceAfter = usde.balanceOf(address(arm)); + + if (isConsoleAvailable) { + console.log( + ">>> ARM SetMarket:\t Governor set active market to %s", isActive ? "Morpho Market" : "No active market" + ); + } + + int256 diff = int256(balanceAfter) - int256(balanceBefore); + if (diff > 0) { + sumUSDeMarketWithdraw += uint256(diff); + } else { + sumUSDeMarketDeposit += uint256(-diff); + } + } + + function targetARMAllocate() external ensureExchangeRateIncrease { + address currentMarket = arm.activeMarket(); + if (assume(currentMarket != address(0))) return; + + (int256 targetLiquidityDelta, int256 actualLiquidityDelta) = arm.allocate(); + + if (isConsoleAvailable) { + console.log( + string( + abi.encodePacked( + ">>> ARM Allocate:\t ARM allocated liquidity to active market. Target delta: ", + targetLiquidityDelta < 0 ? "-" : "", + "%18e USDe\t Actual delta: ", + actualLiquidityDelta < 0 ? "-" : "", + "%18e USDe" + ) + ), + Math.abs(targetLiquidityDelta), + Math.abs(actualLiquidityDelta) + ); + } + + if (actualLiquidityDelta > 0) { + sumUSDeMarketDeposit += uint256(actualLiquidityDelta); + } else { + sumUSDeMarketWithdraw += uint256(-actualLiquidityDelta); + } + } + + function targetARMSetPrices(uint256 buyPrice, uint256 sellPrice) external ensureExchangeRateIncrease { + uint256 crossPrice = arm.crossPrice(); + // Bound sellPrice + sellPrice = uint120(_bound(sellPrice, crossPrice, (1e37 - 1) / 9)); // -> min traderate0 -> 0.9e36 + // Bound buyPrice + buyPrice = uint120(_bound(buyPrice, 0.9e36, crossPrice - 1)); // -> min traderate1 -> 0.9e36 + + vm.prank(operator); + arm.setPrices(buyPrice, sellPrice); + + if (isConsoleAvailable) { + console.log( + ">>> ARM SetPrices:\t Governor set buy price to %36e\t sell price to %36e\t cross price to %36e", + buyPrice, + 1e72 / sellPrice, + arm.crossPrice() + ); + } + } + + function targetARMSetCrossPrice(uint256 crossPrice) external ensureExchangeRateIncrease { + uint256 maxCrossPrice = 1e36; + uint256 minCrossPrice = 1e36 - 20e32; + uint256 sellT1 = 1e72 / (arm.traderate0()); + uint256 buyT1 = arm.traderate1() + 1; + minCrossPrice = Math.max(minCrossPrice, buyT1); + maxCrossPrice = Math.min(maxCrossPrice, sellT1); + if (assume(maxCrossPrice >= minCrossPrice)) return; + crossPrice = _bound(crossPrice, minCrossPrice, maxCrossPrice); + + if (arm.crossPrice() > crossPrice) { + if (assume(susde.balanceOf(address(arm)) < 1e12)) return; + } + + vm.prank(governor); + arm.setCrossPrice(crossPrice); + + if (isConsoleAvailable) { + console.log(">>> ARM SetCPrice:\t Governor set cross price to %36e", crossPrice); + } + } + + function targetARMSwapExactTokensForTokens(bool token0ForToken1, uint88 amountIn, uint256 randomAddressIndex) + external + ensureExchangeRateIncrease + { + (IERC20 tokenIn, IERC20 tokenOut) = token0ForToken1 + ? (IERC20(address(usde)), IERC20(address(susde))) + : (IERC20(address(susde)), IERC20(address(usde))); + + // What's the maximum amountOut we can obtain? + uint256 maxAmountOut; + if (address(tokenOut) == address(usde)) { + uint256 balance = usde.balanceOf(address(arm)); + uint256 outstandingWithdrawals = arm.withdrawsQueued() - arm.withdrawsClaimed(); + maxAmountOut = outstandingWithdrawals >= balance ? 0 : balance - outstandingWithdrawals; + } else { + maxAmountOut = susde.balanceOf(address(arm)); + } + // Ensure there is liquidity available in ARM + if (assume(maxAmountOut > 1)) return; + + // What's the maximum amountIn we can provide to not exceed maxAmountOut? + uint256 maxAmountIn = token0ForToken1 + ? (maxAmountOut * 1e36 / arm.traderate0()) * susde.totalAssets() / susde.totalSupply() + : (maxAmountOut * 1e36 / arm.traderate1()) * susde.totalSupply() / susde.totalAssets(); + if (assume(maxAmountIn > 0)) return; + + // Bound amountIn + amountIn = uint88(_bound(amountIn, 1, maxAmountIn)); + // Select a random user from makers + address user = traders[randomAddressIndex % TRADERS_COUNT]; + + vm.startPrank(user); + // Mint amountIn to user + if (token0ForToken1) { + MockERC20(address(usde)).mint(user, amountIn); + } else { + // Mint too much USDe to user to be able to mint enough sUSDe + MockERC20(address(usde)).mint(user, uint256(amountIn) * 10); + // Mint sUSDe to user + susde.mint(amountIn, user); + // Burn excess USDe + MockERC20(address(usde)).burn(user, usde.balanceOf(user)); + } + // Perform swap + uint256[] memory obtained = arm.swapExactTokensForTokens(tokenIn, tokenOut, amountIn, 0, user); + vm.stopPrank(); + + if (isConsoleAvailable) { + console.log( + string( + abi.encodePacked( + ">>> ARM SwapEF:\t ", + vm.getLabel(user), + " swapped %18e ", + token0ForToken1 ? "USDe" : "sUSDe", + "\t for %18e ", + token0ForToken1 ? "sUSDe" : "USDe" + ) + ), + amountIn, + obtained[1] + ); + } + + require(obtained[0] == amountIn, "Amount in mismatch"); + if (token0ForToken1) { + sumUSDeSwapIn += obtained[0]; + sumSUSDeSwapOut += obtained[1]; + } else { + sumSUSDeSwapIn += obtained[0]; + sumUSDeSwapOut += obtained[1]; + } + } + + function targetARMSwapTokensForExactTokens(bool token0ForToken1, uint88 amountOut, uint256 randomAddressIndex) + external + ensureExchangeRateIncrease + { + (IERC20 tokenIn, IERC20 tokenOut) = token0ForToken1 + ? (IERC20(address(usde)), IERC20(address(susde))) + : (IERC20(address(susde)), IERC20(address(usde))); + + // What's the maximum amountOut we can obtain? + uint256 maxAmountOut; + if (address(tokenOut) == address(usde)) { + uint256 balance = usde.balanceOf(address(arm)); + uint256 outstandingWithdrawals = arm.withdrawsQueued() - arm.withdrawsClaimed(); + maxAmountOut = outstandingWithdrawals >= balance ? 0 : balance - outstandingWithdrawals; + } else { + maxAmountOut = susde.balanceOf(address(arm)); + } + // Ensure there is liquidity available in ARM + if (assume(maxAmountOut > 1)) return; + + amountOut = uint88(_bound(amountOut, 1, maxAmountOut)); + + // What's the maximum amountIn we can provide to not exceed maxAmountOut? + uint256 convertedAmountOut; + if (token0ForToken1) { + convertedAmountOut = (amountOut * susde.totalAssets()) / susde.totalSupply(); + } else { + convertedAmountOut = (amountOut * susde.totalSupply()) / susde.totalAssets(); + } + uint256 price = token0ForToken1 ? arm.traderate0() : arm.traderate1(); + uint256 amountIn = ((uint256(convertedAmountOut) * 1e36) / price) + 3 + 10; // slippage + rounding buffer + + // Select a random user from makers + address user = traders[randomAddressIndex % TRADERS_COUNT]; + vm.startPrank(user); + // Mint amountIn to user + if (token0ForToken1) { + MockERC20(address(usde)).mint(user, amountIn); + } else { + // Mint too much USDe to user to be able to mint enough sUSDe + MockERC20(address(usde)).mint(user, amountIn * 2); + // Mint sUSDe to user + susde.mint(amountIn, user); + // Burn excess USDe + MockERC20(address(usde)).burn(user, usde.balanceOf(user)); + } + // Perform swap + uint256[] memory obtained = arm.swapTokensForExactTokens(tokenIn, tokenOut, amountOut, type(uint256).max, user); + vm.stopPrank(); + + if (isConsoleAvailable) { + console.log( + string( + abi.encodePacked( + ">>> ARM SwapFT:\t ", + vm.getLabel(user), + " swapped %18e ", + token0ForToken1 ? "USDe" : "sUSDe", + "\t for %18e ", + token0ForToken1 ? "sUSDe" : "USDe" + ) + ), + obtained[0], + amountOut + ); + } + + require(obtained[1] == amountOut, "Amount out mismatch"); + if (token0ForToken1) { + sumUSDeSwapIn += obtained[0]; + sumSUSDeSwapOut += obtained[1]; + } else { + sumSUSDeSwapIn += obtained[0]; + sumUSDeSwapOut += obtained[1]; + } + } + + function targetARMCollectFees() external ensureExchangeRateIncrease { + uint256 feesAccrued = arm.feesAccrued(); + uint256 balance = usde.balanceOf(address(arm)); + uint256 outstandingWithdrawals = arm.withdrawsQueued() - arm.withdrawsClaimed(); + if (assume(balance >= feesAccrued + outstandingWithdrawals)) return; + + uint256 feesCollected = arm.collectFees(); + + if (isConsoleAvailable) { + console.log(">>> ARM Collect:\t Governor collected %18e USDe in fees", feesCollected); + } + require(feesCollected == feesAccrued, "Fees collected mismatch"); + + sumUSDeFeesCollected += feesCollected; + } + + function targetARMSetFees(uint256 fee) external ensureExchangeRateIncrease { + // Ensure current fee can be collected + uint256 feesAccrued = arm.feesAccrued(); + if (feesAccrued != 0) { + uint256 balance = usde.balanceOf(address(arm)); + uint256 outstandingWithdrawals = arm.withdrawsQueued() - arm.withdrawsClaimed(); + if (assume(balance >= feesAccrued + outstandingWithdrawals)) return; + } + + uint256 oldFee = arm.fee(); + // Bound fee to [0, 50%] + fee = _bound(fee, 0, 50); + vm.prank(governor); + arm.setFee(fee * 100); + + if (isConsoleAvailable) { + console.log(">>> ARM SetFees:\t Governor set ARM fee from %s% to %s%", oldFee / 100, fee); + } + + sumUSDeFeesCollected += feesAccrued; + } + + function targetARMRequestBaseWithdrawal(uint88 amount) external ensureExchangeRateIncrease { + uint256 balance = susde.balanceOf(address(arm)); + if (assume(balance > 1)) return; + amount = uint88(_bound(amount, 1, balance)); + + // Ensure there is an unstaker available + uint256 nextIndex = arm.nextUnstakerIndex(); + address unstaker = arm.unstakers(nextIndex); + UserCooldown memory cooldown = susde.cooldowns(unstaker); + // If next unstaker has an active cooldown, this means all unstakers are in cooldown + // -> no unstaker available + if (assume(cooldown.underlyingAmount == 0)) return; + + // Ensure time delay has passed + uint32 lastRequestTimestamp = arm.lastRequestTimestamp(); + if (block.timestamp < lastRequestTimestamp + 3 hours) { + if (isConsoleAvailable) { + console.log( + StdStyle.yellow( + string( + abi.encodePacked( + ">>> Time jump:\t Fast forwarded to: ", + vm.toString(lastRequestTimestamp + 3 hours), + " (+ ", + vm.toString((lastRequestTimestamp + 3 hours) - block.timestamp), + "s)" + ) + ) + ) + ); + } + vm.warp(lastRequestTimestamp + 3 hours); + } + + vm.prank(operator); + arm.requestBaseWithdrawal(amount); + + unstakerIndices.push(nextIndex); + + if (isConsoleAvailable) { + console.log( + ">>> ARM ReqBaseW:\t Operator requested base withdrawal of %18e sUSDe underlying, using unstakers #%s", + amount, + nextIndex + ); + } + + sumSUSDeBaseRedeem += amount; + } + + function targetARMClaimBaseWithdrawals(uint256 randomAddressIndex) + external + ensureExchangeRateIncrease + ensureTimeIncrease + { + if (assume(unstakerIndices.length != 0)) return; + // Select a random unstaker index from used unstakers + uint256 selectedIndex = unstakerIndices[randomAddressIndex % unstakerIndices.length]; + address unstaker = arm.unstakers(uint8(selectedIndex)); + UserCooldown memory cooldown = susde.cooldowns(address(unstaker)); + uint256 endTimestamp = cooldown.cooldownEnd; + + // Fast forward time if needed + if (block.timestamp < endTimestamp) { + if (isConsoleAvailable) { + console.log( + StdStyle.yellow( + string( + abi.encodePacked( + ">>> Time jump:\t Fast forwarded to: ", + vm.toString(endTimestamp), + " (+ ", + vm.toString(endTimestamp - block.timestamp), + "s)" + ) + ) + ) + ); + } + vm.warp(endTimestamp); + } + + vm.prank(operator); + arm.claimBaseWithdrawals(unstaker); + + // Remove selectedIndex from unstakerIndices, without preserving order + unstakerIndices[randomAddressIndex % unstakerIndices.length] = unstakerIndices[unstakerIndices.length - 1]; + unstakerIndices.pop(); + + if (isConsoleAvailable) { + console.log( + string( + abi.encodePacked( + ">>> ARM ClaimBaseW:\t Operator claimed base withdrawals using %s\t ", "who unstaked %18e USDe" + ) + ), + vm.getLabel(unstaker), + cooldown.underlyingAmount + ); + } + + sumUSDeBaseRedeem += cooldown.underlyingAmount; + } + + // ╔══════════════════════════════════════════════════════════════════════════════╗ + // ║ ✦✦✦ SUSDE ✦✦✦ ║ + // ╚══════════════════════════════════════════════════════════════════════════════╝ + function targetSUSDeDeposit(uint88 amount) external ensureExchangeRateIncrease { + // Ensure we don't mint 0 shares. + uint256 totalAssets = susde.totalAssets(); + uint256 totalSupply = susde.totalSupply(); + uint256 minAmount = totalAssets / totalSupply + 1; + // Prevent zero deposits + amount = uint88(_bound(amount, minAmount, type(uint88).max)); + + // Mint amount to grace + MockERC20(address(usde)).mint(grace, amount); + + // Deposit as grace + vm.prank(grace); + uint256 shares = susde.deposit(amount, grace); + + if (isConsoleAvailable) { + console.log( + ">>> sUSDe Deposit:\t Grace deposited %18e USDe\t and received %18e sUSDe shares", amount, shares + ); + } + } + + function targetSUSDeCooldownShares(uint88 shareAmount) external ensureExchangeRateIncrease { + // Cache balance + uint256 balance = susde.balanceOf(grace); + + // Assume balance not zero + if (assume(balance > 1)) return; + + // Bound shareAmount to [1, balance] + shareAmount = uint88(_bound(shareAmount, 1, balance)); + + // Cooldown shares as grace + vm.prank(grace); + uint256 amount = susde.cooldownShares(shareAmount); + if (isConsoleAvailable) { + console.log( + ">>> sUSDe Cooldown:\t Grace cooled down %18e sUSDe shares\t for %18e USDe underlying", + shareAmount, + amount + ); + } + } + + function targetSUSDeUnstake() external ensureExchangeRateIncrease ensureTimeIncrease { + // Check grace's cooldown + UserCooldown memory cooldown = susde.cooldowns(grace); + + // Ensure grace has a valid cooldown + if (assume(cooldown.cooldownEnd != 0)) return; + + // Fast forward to after cooldown end if needed + if (block.timestamp < cooldown.cooldownEnd) { + if (isConsoleAvailable) { + console.log( + StdStyle.yellow( + string( + abi.encodePacked( + ">>> Time jump:\t Fast forwarded to: ", + vm.toString(cooldown.cooldownEnd), + " (+ ", + vm.toString(cooldown.cooldownEnd - block.timestamp), + "s)" + ) + ) + ) + ); + } + vm.warp(cooldown.cooldownEnd); + } + + // Unstake as grace + vm.prank(grace); + susde.unstake(grace); + + if (isConsoleAvailable) { + console.log( + ">>> sUSDe Unstake:\t Grace unstaked %18e USDe underlying after cooldown", cooldown.underlyingAmount + ); + } + MockERC20(address(usde)).burn(grace, cooldown.underlyingAmount); + } + + function targetSUSDeTransferInRewards(uint8 bps) external ensureExchangeRateIncrease ensureTimeIncrease { + // Ensure enough time has passed since last distribution + uint256 lastDistribution = susde.lastDistributionTimestamp(); + if (block.timestamp < 8 hours + lastDistribution) { + // Fast forward time to allow rewards distribution + if (isConsoleAvailable) { + console.log( + StdStyle.yellow( + string( + abi.encodePacked( + ">>> Time jump:\t Fast forwarded to: ", + vm.toString(lastDistribution + 8 hours), + " (+ ", + vm.toString((lastDistribution + 8 hours) - block.timestamp), + "s)" + ) + ) + ) + ); + vm.warp(lastDistribution + 8 hours); + } + } + + uint256 balance = usde.balanceOf(address(susde)); + // Rewards can be distributed 3/days max. 1bps at every distribution -> 10 APR. + bps = uint8(_bound(bps, 1, 10)); + uint256 rewards = (balance * bps) / 10_000; + MockERC20(address(usde)).mint(governor, rewards); + vm.prank(governor); + susde.transferInRewards(rewards); + + if (isConsoleAvailable) { + console.log(">>> sUSDe Rewards:\t Governor transferred in %18e USDe as rewards, bps: %d", rewards, bps); + } + } + + // ╔══════════════════════════════════════════════════════════════════════════════╗ + // ║ ✦✦✦ MORPHO ✦✦✦ ║ + // ╚══════════════════════════════════════════════════════════════════════════════╝ + function targetMorphoDeposit(uint88 amount) external ensureExchangeRateIncrease { + // Ensure we don't mint 0 shares. + uint256 totalAssets = morpho.totalAssets(); + uint256 totalSupply = morpho.totalSupply(); + uint256 minAmount = totalAssets / totalSupply + 1; + // Prevent zero deposits + amount = uint88(_bound(amount, minAmount, type(uint88).max)); + + // Mint amount to harry + MockERC20(address(usde)).mint(harry, amount); + + // Deposit as harry + vm.prank(harry); + uint256 shares = morpho.deposit(amount, harry); + + if (isConsoleAvailable) { + console.log( + ">>> Morpho Deposit:\t Harry deposited %18e USDe\t and received %18e Morpho shares", amount, shares + ); + } + } + + function targetMorphoWithdraw(uint88 amount) external ensureExchangeRateIncrease { + // Check harry's balance + uint256 balance = morpho.balanceOf(harry); + + // Assume balance not zero + if (assume(balance > 1)) return; + + // Bound shareAmount to [1, balance] + amount = uint88(_bound(amount, 1, balance)); + + // Ensure there is enough liquidity to withdraw the amount + uint256 maxWithdrawable = morpho.maxWithdraw(harry); + if (assume(amount <= maxWithdrawable)) return; + + // Withdraw as harry + vm.prank(harry); + uint256 shares = morpho.withdraw(amount, harry, harry); + if (isConsoleAvailable) { + console.log( + ">>> Morpho Withdraw:\t Harry withdrew %18e Morpho shares\t for %18e USDe underlying", shares, amount + ); + } + + MockERC20(address(usde)).burn(harry, amount); + } + + function targetMorphoTransferInRewards(uint8 bps) external ensureExchangeRateIncrease { + uint256 balance = usde.balanceOf(address(morpho)); + bps = uint8(_bound(bps, 1, 10)); + uint256 rewards = (balance * bps) / 10_000; + MockERC20(address(usde)).mint(address(morpho), rewards); + + if (isConsoleAvailable) { + console.log(">>> Morpho Rewards:\t Transferred in %18e USDe as rewards, bps: %d", rewards, bps); + } + } + + function targetMorphoSetUtilizationRate(uint256 pct) external ensureExchangeRateIncrease { + pct = _bound(pct, 0, 100); + + morpho.setUtilizationRate(pct * 1e16); + + if (isConsoleAvailable) { + console.log(">>> Morpho UseRate:\t Governor set utilization rate to %s%", pct); + } + } + + function _targetAfterAll() internal { + // In this function, we will simulate shutting down the ARM. This involves letting all users redeem their funds. + // This is important to ensure that the ARM can handle a complete withdrawal scenario without issues. + // This involves: + // 1. Claim all sUSDe base withdrawals + // 2. Request base withdrawal of the remaining sUSDe + // 3. Claim previous base withdrawals. At this point we shouldn't have any sUSDe left in the ARM. + // 4. Remove position from Morpho if any. + // 5. Let all ARM users (including dead address) redeem their shares. + // 6. Claim fees accrued. + + // 1. Claim all sUSDe base withdrawals + // Fast forward time to allow claiming all previous base withdrawals + vm.warp(block.timestamp + 7 days); + for (uint256 i; i < unstakerIndices.length; i++) { + arm.claimBaseWithdrawals(arm.unstakers(uint8(unstakerIndices[i]))); + } + + // 2. Request base withdrawal of the remaining sUSDe + uint256 susdeBalance = susde.balanceOf(address(arm)); + uint256 nextIndex = arm.nextUnstakerIndex(); + if (susdeBalance > 0) { + vm.prank(operator); + arm.requestBaseWithdrawal(susdeBalance); + } + + // 3. Claim previous base withdrawals. At this point we shouldn't have any sUSDe left in the ARM. + if (susdeBalance > 0) { + // Fast forward time to allow claiming the last base withdrawal + vm.warp(block.timestamp + 7 days); + arm.claimBaseWithdrawals(arm.unstakers(uint8(nextIndex))); + } + require(susde.balanceOf(address(arm)) == 0, "ARM still has sUSDe balance"); + + // 4. Remove position from Morpho if any. + address activeMarket = arm.activeMarket(); + if (activeMarket != address(0)) { + morpho.setUtilizationRate(0); + vm.prank(operator); + arm.setActiveMarket(address(0)); + } + + // 5. Let all ARM users redeem their shares. + for (uint256 i; i < MAKERS_COUNT; i++) { + address user = makers[i]; + uint256 balance = arm.balanceOf(user); + if (balance > 0) { + vm.prank(user); + arm.requestRedeem(balance); + } + } + + // Fast forward time to allow claiming all redemptions + vm.warp(block.timestamp + DEFAULT_CLAIM_DELAY); + uint256 nextWithdrawalIndex = arm.nextWithdrawalIndex(); + for (uint256 i; i < nextWithdrawalIndex; i++) { + (address user, bool claimed,,,) = arm.withdrawalRequests(i); + if (claimed) continue; + vm.prank(user); + arm.claimRedeem(i); + } + + // 6. Claim fees accrued. + arm.collectFees(); + } +} diff --git a/test/invariants/EthenaARM/helpers/Find.sol b/test/invariants/EthenaARM/helpers/Find.sol new file mode 100644 index 00000000..f56c53cf --- /dev/null +++ b/test/invariants/EthenaARM/helpers/Find.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.23; + +import {AbstractARM} from "contracts/AbstractARM.sol"; + +/// @notice Library used to find specific data in storage for testing purposes. +/// Most of the time to find a specific user/request that meets certain criteria. +library Find { + struct GetUserRequestWithAmountStruct { + address arm; + uint248 randomAddressIndex; + uint248 randomArrayIndex; + address[] users; + uint128 claimable; + uint128 availableLiquidity; + } + + function getUserRequestWithAmount( + GetUserRequestWithAmountStruct memory $, + mapping(address => uint256[]) storage pendingRequests + ) internal returns (address user, uint256 requestId, uint40 claimTimestamp) { + for (uint256 i; i < $.users.length; i++) { + // Take a random user + address _user = $.users[($.randomAddressIndex + i) % $.users.length]; + // Find a request that can be claimed + for (uint256 j; j < pendingRequests[_user].length; j++) { + // Take a random request from that user + uint256 _requestId = pendingRequests[_user][($.randomArrayIndex + j) % pendingRequests[_user].length]; + // Check request data + (,, uint40 _claimTimestamp, uint128 _amount, uint128 _queued) = + AbstractARM($.arm).withdrawalRequests(_requestId); + // Check if this is claimable + if (_queued <= $.claimable && _amount <= $.availableLiquidity) { + (user, requestId, claimTimestamp) = (_user, _requestId, _claimTimestamp); + // Remove pendingRequests + pendingRequests[_user][($.randomArrayIndex + j) % pendingRequests[_user].length] = + pendingRequests[_user][pendingRequests[_user].length - 1]; + pendingRequests[_user].pop(); + break; + } + } + } + } + + function getUserWithARMShares(address[] memory users, address arm) + internal + view + returns (address user, uint256 balance) + { + for (uint256 i; i < users.length; i++) { + balance = AbstractARM(arm).balanceOf(users[i]); + if (balance > 1) { + user = users[i]; + break; + } + } + } +} diff --git a/test/invariants/EthenaARM/helpers/Math.sol b/test/invariants/EthenaARM/helpers/Math.sol new file mode 100644 index 00000000..18c1676d --- /dev/null +++ b/test/invariants/EthenaARM/helpers/Math.sol @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.23; + +library Math { + ////////////////////////////////////////////////////// + /// --- ABS + ////////////////////////////////////////////////////// + /// @notice Returns the absolute value of an int256 as uint256 + /// @param a The integer to get the absolute value of + /// @return The absolute value as uint256 + function abs(int256 a) internal pure returns (uint256) { + return uint256(a >= 0 ? a : -a); + } + + /// @notice Returns the absolute difference between two uint256 values + /// @param a The first value + /// @param b The second value + /// @return The absolute difference + function absDiff(uint256 a, uint256 b) internal pure returns (uint256) { + return a >= b ? a - b : b - a; + } + + ////////////////////////////////////////////////////// + /// --- MIN & MAX + ////////////////////////////////////////////////////// + /// @notice Returns the maximum of two uint256 values + /// @param a The first value + /// @param b The second value + /// @return The maximum value + function max(uint256 a, uint256 b) internal pure returns (uint256) { + return a >= b ? a : b; + } + + /// @notice Returns the minimum of two uint256 values + /// @param a The first value + /// @param b The second value + /// @return The minimum value + function min(uint256 a, uint256 b) internal pure returns (uint256) { + return a <= b ? a : b; + } + + ////////////////////////////////////////////////////// + /// --- EQUALITY STRICT AND APPROXIMATE + ////////////////////////////////////////////////////// + /// @notice Checks if two uint256 values are equal + /// @param a The first value + /// @param b The second value + /// @return True if equal, false otherwise + function eq(uint256 a, uint256 b) internal pure returns (bool) { + return a == b; + } + + /// @notice Checks if two uint256 values are approximately equal within a maximum absolute difference + /// @param a The first value + /// @param b The second value + /// @param maxDelta The maximum allowed absolute difference + /// @return True if approximately equal, false otherwise + function approxEqAbs(uint256 a, uint256 b, uint256 maxDelta) internal pure returns (bool) { + if (a >= b) { + return (a - b) <= maxDelta; + } else { + return (b - a) <= maxDelta; + } + } + + /// @notice Checks if two uint256 values are approximately equal within a maximum relative difference (in WAD) + /// @param a The first value + /// @param b The second value + /// @param maxRelDeltaWAD The maximum allowed relative difference in WAD (1e18 = 100%) + /// @return True if approximately equal, false otherwise + function approxEqRel(uint256 a, uint256 b, uint256 maxRelDeltaWAD) internal pure returns (bool) { + if (a == b) { + return true; + } + uint256 _absDiff = a >= b ? a - b : b - a; + uint256 relDiffWAD = (_absDiff * 1 ether) / Math.max(a, b); + return relDiffWAD <= maxRelDeltaWAD; + } + + ////////////////////////////////////////////////////// + /// --- GREATER THAN + ////////////////////////////////////////////////////// + /// @notice Checks if a is greater than b + /// @param a The first value + /// @param b The second value + /// @return True if a > b, false otherwise + function gt(uint256 a, uint256 b) internal pure returns (bool) { + return a > b; + } + + /// @notice Checks if a is greater than or equal to b + function gte(uint256 a, uint256 b) internal pure returns (bool) { + return a >= b; + } + + /// @notice Checks if a is approximately greater than or equal to b within a maximum absolute difference + /// @param a The first value + /// @param b The second value + /// @param maxDelta The maximum allowed absolute difference + /// @return True if a is approximately greater than or equal to b, false otherwise + function approxGteAbs(uint256 a, uint256 b, uint256 maxDelta) internal pure returns (bool) { + if (a >= b) { + return true; + } else { + return (b - a) <= maxDelta; + } + } + + ////////////////////////////////////////////////////// + /// --- LESS THAN + ////////////////////////////////////////////////////// + /// @notice Checks if a is less than b + /// @param a The first value + /// @param b The second value + /// @return True if a < b, false otherwise + function lt(uint256 a, uint256 b) internal pure returns (bool) { + return a < b; + } + + /// @notice Checks if a is less than or equal to b + /// @param a The first value + /// @param b The second value + /// @return True if a <= b, false otherwise + function lte(uint256 a, uint256 b) internal pure returns (bool) { + return a <= b; + } +} diff --git a/test/invariants/EthenaARM/helpers/Test.sol b/test/invariants/EthenaARM/helpers/Test.sol new file mode 100644 index 00000000..8886c4ab --- /dev/null +++ b/test/invariants/EthenaARM/helpers/Test.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.23; + +import {Test} from "forge-std/Test.sol"; + +import {FuzzerFoundry_EthenaARM} from "test/invariants/EthenaARM/FuzzerFoundry_EthenaARM.sol"; + +contract Unit_Ethena_replay is Test { + FuzzerFoundry_EthenaARM f; + + function setUp() public { + f = new FuzzerFoundry_EthenaARM(); + f.setUp(); + } + + function test_ethena_replay_unit() public { + //f.targetARMDeposit(309485009821345068724781054, 814939); + //f.targetARMSetActiveMarket(true); + //f.targetARMRequestRedeem(309485009821345068724781053, 52648352319298637938247915450656049947404); + //f.targetMorphoTransferInRewards(0); + //f._targetAfterAll(); + //assertTrue(f._propertyAfterAll(), "Property After All failed"); + } +} diff --git a/test/invariants/EthenaARM/helpers/Vm.sol b/test/invariants/EthenaARM/helpers/Vm.sol new file mode 100644 index 00000000..ba1ddc99 --- /dev/null +++ b/test/invariants/EthenaARM/helpers/Vm.sol @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.23; + +/// @notice Medusa StdCheats interface +interface Vm { + // Set block.timestamp + function warp(uint256) external; + + // Set block.number + function roll(uint256) external; + + // Set block.basefee + function fee(uint256) external; + + // Set block.difficulty (deprecated in `medusa`) + function difficulty(uint256) external; + + // Set block.prevrandao + function prevrandao(bytes32) external; + + // Set block.chainid + function chainId(uint256) external; + + // Sets the block.coinbase + function coinbase(address) external; + + // Loads a storage slot from an address + function load(address account, bytes32 slot) external returns (bytes32); + + // Stores a value to an address' storage slot + function store(address account, bytes32 slot, bytes32 value) external; + + // Sets the *next* call's msg.sender to be the input address + function prank(address) external; + + // Sets all subsequent call's msg.sender (until stopPrank is called) to be the input address + function startPrank(address) external; + + // Stops a previously called startPrank + function stopPrank() external; + + // Set msg.sender to the input address until the current call exits + function prankHere(address) external; + + // Sets an address' balance + function deal(address who, uint256 newBalance) external; + + // Sets an address' code + function etch(address who, bytes calldata code) external; + + // Signs data + function sign(uint256 privateKey, bytes32 digest) external returns (uint8 v, bytes32 r, bytes32 s); + + // Computes address for a given private key + function addr(uint256 privateKey) external returns (address); + + // Gets the creation bytecode of a contract + function getCode(string calldata) external returns (bytes memory); + + // Gets the nonce of an account + function getNonce(address account) external returns (uint64); + + // Sets the nonce of an account + // The new nonce must be higher than the current nonce of the account + function setNonce(address account, uint64 nonce) external; + + // Performs a foreign function call via terminal + function ffi(string[] calldata) external returns (bytes memory); + + // Take a snapshot of the current state of the EVM + function snapshot() external returns (uint256); + + // Revert state back to a snapshot + function revertTo(uint256) external returns (bool); + + // Convert Solidity types to strings + function toString(address) external returns (string memory); + function toString(bytes calldata) external returns (string memory); + function toString(bytes32) external returns (string memory); + function toString(bool) external returns (string memory); + function toString(uint256) external returns (string memory); + function toString(int256) external returns (string memory); + + // Convert strings into Solidity types + function parseBytes(string memory) external returns (bytes memory); + function parseBytes32(string memory) external returns (bytes32); + function parseAddress(string memory) external returns (address); + function parseUint(string memory) external returns (uint256); + function parseInt(string memory) external returns (int256); + function parseBool(string memory) external returns (bool); + + // Only works with Foundry + function label(address account, string calldata newLabel) external; + function getLabel(address account) external returns (string memory); + function assume(bool condition) external; +} diff --git a/test/invariants/EthenaARM/medusa.json b/test/invariants/EthenaARM/medusa.json new file mode 100644 index 00000000..f939beb8 --- /dev/null +++ b/test/invariants/EthenaARM/medusa.json @@ -0,0 +1,108 @@ +{ + "fuzzing": { + "workers": 10, + "workerResetLimit": 50, + "timeout": 0, + "testLimit": 0, + "shrinkLimit": 5000, + "callSequenceLength": 200, + "pruneFrequency": 5, + "corpusDirectory": "", + "coverageEnabled": true, + "coverageFormats": [ + "html", + "lcov" + ], + "coverageExclusions": [], + "revertReporterEnabled": false, + "targetContracts": [ + "FuzzerMedusa_EthenaARM" + ], + "predeployedContracts": {}, + "targetContractsBalances": [], + "constructorArgs": {}, + "deployerAddress": "0x30000", + "senderAddresses": [ + "0x10000", + "0x20000", + "0x30000" + ], + "blockNumberDelayMax": 60480, + "blockTimestampDelayMax": 604800, + "transactionGasLimit": 12500000, + "testing": { + "stopOnFailedTest": true, + "stopOnFailedContractMatching": false, + "stopOnNoTests": true, + "testAllContracts": false, + "testViewMethods": true, + "verbosity": 2, + "assertionTesting": { + "enabled": false, + "panicCodeConfig": { + "failOnCompilerInsertedPanic": true, + "failOnAssertion": true, + "failOnArithmeticUnderflow": true, + "failOnDivideByZero": true, + "failOnEnumTypeConversionOutOfBounds": true, + "failOnIncorrectStorageAccess": true, + "failOnPopEmptyArray": true, + "failOnOutOfBoundsArrayAccess": true, + "failOnAllocateTooMuchMemory": true, + "failOnCallUninitializedVariable": true + } + }, + "propertyTesting": { + "enabled": true, + "testPrefixes": [ + "property" + ] + }, + "optimizationTesting": { + "enabled": false, + "testPrefixes": [ + "optimize_" + ] + }, + "targetFunctionSignatures": [], + "excludeFunctionSignatures": [ + "FuzzerMedusa_EthenaARM.isLabelAvailable()", + "FuzzerMedusa_EthenaARM.isAssumeAvailable()", + "FuzzerMedusa_EthenaARM.isConsoleAvailable()" + ] + }, + "chainConfig": { + "codeSizeCheckDisabled": true, + "cheatCodes": { + "cheatCodesEnabled": true, + "enableFFI": false + }, + "skipAccountChecks": true, + "forkConfig": { + "forkModeEnabled": false, + "rpcUrl": "", + "rpcBlock": 1, + "poolSize": 20 + } + } + }, + "compilation": { + "platform": "crytic-compile", + "platformConfig": { + "target": ".", + "solcVersion": "", + "exportDirectory": "", + "args": [] + } + }, + "slither": { + "useSlither": true, + "cachePath": "", + "args": [] + }, + "logging": { + "level": "trace", + "logDirectory": "", + "noColor": false + } +} \ No newline at end of file diff --git a/test/invariants/EthenaARM/mocks/MockMorpho.sol b/test/invariants/EthenaARM/mocks/MockMorpho.sol new file mode 100644 index 00000000..32a5dc17 --- /dev/null +++ b/test/invariants/EthenaARM/mocks/MockMorpho.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.23; + +// Solmate +import {ERC20} from "@solmate/tokens/ERC20.sol"; +import {ERC4626} from "@solmate/mixins/ERC4626.sol"; + +contract MockMorpho is ERC4626 { + ////////////////////////////////////////////////////// + /// --- STATE VARIABLES + ////////////////////////////////////////////////////// + uint256 public utilizationRate; + + ////////////////////////////////////////////////////// + /// --- EVENTS + ////////////////////////////////////////////////////// + event UtilizationRateChanged(uint256 oldUtilizationRate, uint256 newUtilizationRate); + + ////////////////////////////////////////////////////// + /// --- CONSTRUCTOR + ////////////////////////////////////////////////////// + constructor(address _underlying) ERC4626(ERC20(_underlying), "Mock Morpho Blue", "Mock Morpho Blue") {} + + ////////////////////////////////////////////////////// + /// --- VIEW FUNCTIONS + ////////////////////////////////////////////////////// + function totalAssets() public view override returns (uint256) { + return asset.balanceOf(address(this)); + } + + function maxWithdraw(address owner) public view override returns (uint256) { + uint256 remainingLiquidity = availableLiquidity(); + uint256 userLiquidity = convertToAssets(balanceOf[owner]); + return userLiquidity > remainingLiquidity ? remainingLiquidity : userLiquidity; + } + + function maxRedeem(address owner) public view override returns (uint256) { + uint256 maxRedeemableShares = convertToShares(availableLiquidity()); + uint256 userShares = balanceOf[owner]; + return userShares > maxRedeemableShares ? maxRedeemableShares : userShares; + } + + function beforeWithdraw(uint256 assets, uint256) internal view override { + require(assets <= availableLiquidity(), "INSUFFICIENT_LIQUIDITY"); + } + + function availableLiquidity() public view returns (uint256) { + return totalAssets() * (1e18 - utilizationRate) / 1e18; + } + + ////////////////////////////////////////////////////// + /// --- MUTATIVE FUNCTIONS + ////////////////////////////////////////////////////// + function setUtilizationRate(uint256 _utilizationRate) external { + emit UtilizationRateChanged(utilizationRate, _utilizationRate); + utilizationRate = _utilizationRate; + } +} diff --git a/test/invariants/EthenaARM/mocks/MockSUSDE.sol b/test/invariants/EthenaARM/mocks/MockSUSDE.sol new file mode 100644 index 00000000..b71cd1f5 --- /dev/null +++ b/test/invariants/EthenaARM/mocks/MockSUSDE.sol @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.23; + +// Solmate +import {Owned} from "@solmate/auth/Owned.sol"; +import {ERC20} from "@solmate/tokens/ERC20.sol"; +import {ERC4626} from "@solmate/mixins/ERC4626.sol"; + +// Interfaces +import {UserCooldown} from "contracts/Interfaces.sol"; + +contract MockSUSDE is ERC4626, Owned { + ////////////////////////////////////////////////////// + /// --- CONSTANTS & IMMUTABLES + ////////////////////////////////////////////////////// + address public immutable SILO; + uint256 public immutable VESTING_DURATION; + uint256 public immutable COOLDOWN_DURATION; + + ////////////////////////////////////////////////////// + /// --- STATE VARIABLES + ////////////////////////////////////////////////////// + uint256 public vestingAmount; + uint256 public lastDistributionTimestamp; + mapping(address => UserCooldown) public cooldowns; + + ////////////////////////////////////////////////////// + /// --- EVENTS + ////////////////////////////////////////////////////// + event CooldownSet(uint256 oldDuration, uint256 newDuration); + event RewardReceived(uint256 amount); + + ////////////////////////////////////////////////////// + /// --- CONSTRUCTOR + ////////////////////////////////////////////////////// + constructor(address _underlying, address _governor) + ERC4626(ERC20(_underlying), "Staked USDe", "sUSDe") + Owned(_governor) + { + SILO = address(new MockSilo(asset)); + VESTING_DURATION = 8 hours; + COOLDOWN_DURATION = 7 days; + } + + ////////////////////////////////////////////////////// + /// --- VIEWS + ////////////////////////////////////////////////////// + function totalAssets() public view override returns (uint256) { + return asset.balanceOf(address(this)) - getUnvestedAmount(); + } + + function getUnvestedAmount() public view returns (uint256) { + uint256 timeSinceLastDistribution = block.timestamp - lastDistributionTimestamp; + + if (timeSinceLastDistribution >= VESTING_DURATION) { + return 0; + } + + uint256 deltaT; + unchecked { + deltaT = VESTING_DURATION - timeSinceLastDistribution; + } + return (vestingAmount * deltaT) / VESTING_DURATION; + } + + ////////////////////////////////////////////////////// + /// --- MUTATIVE FUNCTIONS + ////////////////////////////////////////////////////// + function unstake(address receiver) external { + UserCooldown storage cooldown = cooldowns[msg.sender]; + uint256 assets = cooldown.underlyingAmount; + + if (block.timestamp >= cooldown.cooldownEnd) { + delete cooldowns[msg.sender]; + + MockSilo(SILO).withdraw(receiver, assets); + } else { + revert("SUSDE: Invalid cooldown"); + } + } + + function cooldownAssets(uint256 assets) external returns (uint256 shares) { + if (assets > maxWithdraw(msg.sender)) revert("SUSDE: Excessive withdraw amount"); + + shares = previewWithdraw(assets); + + cooldowns[msg.sender].cooldownEnd = uint104(block.timestamp + COOLDOWN_DURATION); + cooldowns[msg.sender].underlyingAmount += uint152(assets); + + super.withdraw(assets, SILO, msg.sender); + } + + function cooldownShares(uint256 shares) external returns (uint256 assets) { + if (shares > maxRedeem(msg.sender)) revert("SUSDE: Excessive redeem amount"); + + assets = previewRedeem(shares); + + cooldowns[msg.sender].cooldownEnd = uint104(block.timestamp + COOLDOWN_DURATION); + cooldowns[msg.sender].underlyingAmount += uint152(assets); + + super.withdraw(assets, SILO, msg.sender); + } + + function withdraw(uint256, address, address) public pure override returns (uint256) { + revert("SUSDE: Use cooldown functions"); + } + + function redeem(uint256, address, address) public pure override returns (uint256) { + revert("SUSDE: Use cooldown functions"); + } + + ////////////////////////////////////////////////////// + /// --- ADMIN FUNCTIONS + ////////////////////////////////////////////////////// + function transferInRewards(uint256 amount) external onlyOwner { + require(amount != 0, "SUSDE: amount zero"); + + // Ensure previous vesting period is complete before starting a new one + // _updateVestingAmount(amount) in original contract + require(getUnvestedAmount() == 0, "SUSDE: previous vesting not complete"); + vestingAmount = amount; + lastDistributionTimestamp = block.timestamp; + + asset.transferFrom(msg.sender, address(this), amount); + emit RewardReceived(amount); + } +} + +contract MockSilo is Owned { + ////////////////////////////////////////////////////// + /// --- IMMUTABLES + ////////////////////////////////////////////////////// + ERC20 public immutable _USDE; + + ///////////////////////////////////////////////////// + /// --- CONSTRUCTOR + ////////////////////////////////////////////////////// + constructor(ERC20 _usde) Owned(msg.sender) { + _USDE = _usde; + } + + ////////////////////////////////////////////////////// + /// --- MUTATIVE FUNCTIONS + ////////////////////////////////////////////////////// + function withdraw(address to, uint256 amount) external onlyOwner { + _USDE.transfer(to, amount); + } +}