diff --git a/.gitmodules b/.gitmodules index 24a15a56..b8f5af89 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "lib/morpho-blue-irm"] path = lib/morpho-blue-irm url = https://github.com/morpho-org/morpho-blue-irm +[submodule "lib/aave-v3-origin"] + path = lib/aave-v3-origin + url = https://github.com/aave-dao/aave-v3-origin diff --git a/foundry.lock b/foundry.lock index a519ca8d..7bee6763 100644 --- a/foundry.lock +++ b/foundry.lock @@ -1,4 +1,10 @@ { + "lib/aave-v3-origin": { + "tag": { + "name": "v3.6.0", + "rev": "5a230ec82fcb10afc7fe7cffa8978752fb17aa2b" + } + }, "lib/forge-std": { "rev": "1eea5bae12ae557d589f9f0f0edae2faa47cb262" }, diff --git a/lib/aave-v3-origin b/lib/aave-v3-origin new file mode 160000 index 00000000..5a230ec8 --- /dev/null +++ b/lib/aave-v3-origin @@ -0,0 +1 @@ +Subproject commit 5a230ec82fcb10afc7fe7cffa8978752fb17aa2b diff --git a/remappings.txt b/remappings.txt index 63569623..996c36c3 100644 --- a/remappings.txt +++ b/remappings.txt @@ -8,6 +8,4 @@ solidity-stringutils/=lib/openzeppelin-foundry-upgrades/lib/solidity-stringutils @openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/ @openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ @foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/ - - - +@aave-v3-origin/=lib/aave-v3-origin/src/ diff --git a/script/1/CreateLeverageToken.PT-RLP-4DEC2025-USDC-2x.s.sol b/script/1/CreateLeverageToken.PT-RLP-4DEC2025-USDC-2x.s.sol index e293fc40..df64830e 100644 --- a/script/1/CreateLeverageToken.PT-RLP-4DEC2025-USDC-2x.s.sol +++ b/script/1/CreateLeverageToken.PT-RLP-4DEC2025-USDC-2x.s.sol @@ -24,7 +24,7 @@ contract CreateLeverageToken is Script { ILeverageManager public leverageManager = ILeverageManager(DeployConstants.LEVERAGE_MANAGER); IMorphoLendingAdapterFactory public lendingAdapterFactory = - IMorphoLendingAdapterFactory(DeployConstants.LENDING_ADAPTER_FACTORY); + IMorphoLendingAdapterFactory(DeployConstants.MORPHO_LENDING_ADAPTER_FACTORY); ILeverageTokenDeploymentBatcher public leverageTokenDeploymentBatcher = ILeverageTokenDeploymentBatcher(DeployConstants.LEVERAGE_TOKEN_DEPLOYMENT_BATCHER); diff --git a/script/1/CreateLeverageToken.RLP-USDC-6.75x.s.sol b/script/1/CreateLeverageToken.RLP-USDC-6.75x.s.sol index 4503e72f..6190835f 100644 --- a/script/1/CreateLeverageToken.RLP-USDC-6.75x.s.sol +++ b/script/1/CreateLeverageToken.RLP-USDC-6.75x.s.sol @@ -24,7 +24,7 @@ contract CreateLeverageToken is Script { ILeverageManager public leverageManager = ILeverageManager(DeployConstants.LEVERAGE_MANAGER); IMorphoLendingAdapterFactory public lendingAdapterFactory = - IMorphoLendingAdapterFactory(DeployConstants.LENDING_ADAPTER_FACTORY); + IMorphoLendingAdapterFactory(DeployConstants.MORPHO_LENDING_ADAPTER_FACTORY); ILeverageTokenDeploymentBatcher public leverageTokenDeploymentBatcher = ILeverageTokenDeploymentBatcher(DeployConstants.LEVERAGE_TOKEN_DEPLOYMENT_BATCHER); diff --git a/script/1/CreateLeverageToken.sUSDS-USDT-25x.s.sol b/script/1/CreateLeverageToken.sUSDS-USDT-25x.s.sol index a692fa87..c8ef9791 100644 --- a/script/1/CreateLeverageToken.sUSDS-USDT-25x.s.sol +++ b/script/1/CreateLeverageToken.sUSDS-USDT-25x.s.sol @@ -24,7 +24,7 @@ contract CreateLeverageToken is Script { ILeverageManager public leverageManager = ILeverageManager(DeployConstants.LEVERAGE_MANAGER); IMorphoLendingAdapterFactory public lendingAdapterFactory = - IMorphoLendingAdapterFactory(DeployConstants.LENDING_ADAPTER_FACTORY); + IMorphoLendingAdapterFactory(DeployConstants.MORPHO_LENDING_ADAPTER_FACTORY); ILeverageTokenDeploymentBatcher public leverageTokenDeploymentBatcher = ILeverageTokenDeploymentBatcher(DeployConstants.LEVERAGE_TOKEN_DEPLOYMENT_BATCHER); diff --git a/script/1/CreateLeverageToken.siUSD-USDC-11x.s.sol b/script/1/CreateLeverageToken.siUSD-USDC-11x.s.sol index 7ffc573b..a4e448ad 100644 --- a/script/1/CreateLeverageToken.siUSD-USDC-11x.s.sol +++ b/script/1/CreateLeverageToken.siUSD-USDC-11x.s.sol @@ -24,7 +24,7 @@ contract CreateLeverageToken is Script { ILeverageManager public leverageManager = ILeverageManager(DeployConstants.LEVERAGE_MANAGER); IMorphoLendingAdapterFactory public lendingAdapterFactory = - IMorphoLendingAdapterFactory(DeployConstants.LENDING_ADAPTER_FACTORY); + IMorphoLendingAdapterFactory(DeployConstants.MORPHO_LENDING_ADAPTER_FACTORY); ILeverageTokenDeploymentBatcher public leverageTokenDeploymentBatcher = ILeverageTokenDeploymentBatcher(DeployConstants.LEVERAGE_TOKEN_DEPLOYMENT_BATCHER); diff --git a/script/1/CreateLeverageToken.wstETH-ETH-25x.s.sol b/script/1/CreateLeverageToken.wstETH-ETH-25x.s.sol index 50dc2f7d..72c48d07 100644 --- a/script/1/CreateLeverageToken.wstETH-ETH-25x.s.sol +++ b/script/1/CreateLeverageToken.wstETH-ETH-25x.s.sol @@ -24,7 +24,7 @@ contract CreateLeverageToken is Script { ILeverageManager public leverageManager = ILeverageManager(DeployConstants.LEVERAGE_MANAGER); IMorphoLendingAdapterFactory public lendingAdapterFactory = - IMorphoLendingAdapterFactory(DeployConstants.LENDING_ADAPTER_FACTORY); + IMorphoLendingAdapterFactory(DeployConstants.MORPHO_LENDING_ADAPTER_FACTORY); ILeverageTokenDeploymentBatcher public leverageTokenDeploymentBatcher = ILeverageTokenDeploymentBatcher(DeployConstants.LEVERAGE_TOKEN_DEPLOYMENT_BATCHER); diff --git a/script/1/CreateLeverageToken.wstETH-WETH-2x.s.sol b/script/1/CreateLeverageToken.wstETH-WETH-2x.s.sol index 43f80a84..e8358922 100644 --- a/script/1/CreateLeverageToken.wstETH-WETH-2x.s.sol +++ b/script/1/CreateLeverageToken.wstETH-WETH-2x.s.sol @@ -24,7 +24,7 @@ contract CreateLeverageToken is Script { ILeverageManager public leverageManager = ILeverageManager(DeployConstants.LEVERAGE_MANAGER); IMorphoLendingAdapterFactory public lendingAdapterFactory = - IMorphoLendingAdapterFactory(DeployConstants.LENDING_ADAPTER_FACTORY); + IMorphoLendingAdapterFactory(DeployConstants.MORPHO_LENDING_ADAPTER_FACTORY); ILeverageTokenDeploymentBatcher public leverageTokenDeploymentBatcher = ILeverageTokenDeploymentBatcher(DeployConstants.LEVERAGE_TOKEN_DEPLOYMENT_BATCHER); diff --git a/script/1/DeployConstants.sol b/script/1/DeployConstants.sol index 424aaefc..05d5686b 100644 --- a/script/1/DeployConstants.sol +++ b/script/1/DeployConstants.sol @@ -7,12 +7,18 @@ library DeployConstants { address public constant MORPHO = 0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb; + address public constant AAVE_POOL = 0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2; + address public constant LEVERAGE_MANAGER = 0x5C37EB148D4a261ACD101e2B997A0F163Fb3E351; - address public constant LENDING_ADAPTER_FACTORY = 0xce05FbEd9260810Bdded179ADfdaf737BE7ded71; + address public constant MORPHO_LENDING_ADAPTER_FACTORY = 0xce05FbEd9260810Bdded179ADfdaf737BE7ded71; + address public constant AAVE_LENDING_ADAPTER_FACTORY = address(0); // TODO: Deploy and set address address public constant LEVERAGE_TOKEN_FACTORY = 0x603Da735780e6bC7D04f3FB85C26dccCd4Ff0a82; address public constant LEVERAGE_ROUTER = 0xb0764dE7eeF0aC69855C431334B7BC51A96E6DbA; + address public constant LEVERAGE_ROUTER_V2 = address(0); // TODO: Deploy and set address address public constant MULTICALL_EXECUTOR = 0x16D02Ebd89988cAd1Ce945807b963aB7A9Fd22E1; address public constant LEVERAGE_TOKEN_DEPLOYMENT_BATCHER = 0x4466D52b714Ef32657db89ec61FAB1b7E30A0352; + address public constant LEVERAGE_TOKEN_DEPLOYMENT_BATCHER_V2 = address(0); // TODO: Deploy and set address + address public constant DUTCH_AUCTION_PRE_LIQUIDATION_COLLATERAL_RATIOS_REBALANCE_ADAPTER_IMPLEMENTATION = 0x1d0c191a0fe2917e244826D3a8d0a64503efAec8; diff --git a/script/1/DeployLeverageTokenDeploymentBatcher.s.sol b/script/1/DeployLeverageTokenDeploymentBatcher.s.sol index 242ccd96..5503c2c4 100644 --- a/script/1/DeployLeverageTokenDeploymentBatcher.s.sol +++ b/script/1/DeployLeverageTokenDeploymentBatcher.s.sol @@ -5,9 +5,10 @@ pragma solidity ^0.8.26; import {Script, console} from "forge-std/Script.sol"; /// Internal imports -import {LeverageTokenDeploymentBatcher} from "src/periphery/LeverageTokenDeploymentBatcher.sol"; +import {LeverageTokenDeploymentBatcherV2} from "src/periphery/LeverageTokenDeploymentBatcherV2.sol"; import {ILeverageManager} from "src/interfaces/ILeverageManager.sol"; import {IMorphoLendingAdapterFactory} from "src/interfaces/IMorphoLendingAdapterFactory.sol"; +import {IAaveLendingAdapterFactory} from "src/interfaces/IAaveLendingAdapterFactory.sol"; import {DeployConstants} from "./DeployConstants.sol"; contract DeployLeverageTokenDeploymentBatcher is Script { @@ -22,11 +23,12 @@ contract DeployLeverageTokenDeploymentBatcher is Script { address deployerAddress = msg.sender; console.log("DeployerAddress: ", deployerAddress); - LeverageTokenDeploymentBatcher leverageTokenDeploymentBatcher = new LeverageTokenDeploymentBatcher( + LeverageTokenDeploymentBatcherV2 leverageTokenDeploymentBatcherV2 = new LeverageTokenDeploymentBatcherV2( ILeverageManager(DeployConstants.LEVERAGE_MANAGER), - IMorphoLendingAdapterFactory(DeployConstants.LENDING_ADAPTER_FACTORY) + IMorphoLendingAdapterFactory(DeployConstants.MORPHO_LENDING_ADAPTER_FACTORY), + IAaveLendingAdapterFactory(DeployConstants.AAVE_LENDING_ADAPTER_FACTORY) ); - console.log("LeverageTokenDeploymentBatcher deployed at: ", address(leverageTokenDeploymentBatcher)); + console.log("LeverageTokenDeploymentBatcherV2 deployed at: ", address(leverageTokenDeploymentBatcherV2)); vm.stopBroadcast(); } diff --git a/script/1/Periphery.s.sol b/script/1/Periphery.s.sol index 5f895712..43d53c30 100644 --- a/script/1/Periphery.s.sol +++ b/script/1/Periphery.s.sol @@ -6,14 +6,16 @@ import {Script, console} from "forge-std/Script.sol"; /// Dependency imports import {IMorpho} from "@morpho-blue/interfaces/IMorpho.sol"; +import {IPool} from "@aave-v3-origin/contracts/interfaces/IPool.sol"; /// Internal imports import {MulticallExecutor} from "src/periphery/MulticallExecutor.sol"; import {VeloraAdapter} from "src/periphery/VeloraAdapter.sol"; -import {LeverageRouter} from "src/periphery/LeverageRouter.sol"; -import {LeverageTokenDeploymentBatcher} from "src/periphery/LeverageTokenDeploymentBatcher.sol"; +import {LeverageRouterV2} from "src/periphery/LeverageRouterV2.sol"; +import {LeverageTokenDeploymentBatcherV2} from "src/periphery/LeverageTokenDeploymentBatcherV2.sol"; import {ILeverageManager} from "src/interfaces/ILeverageManager.sol"; import {IMorphoLendingAdapterFactory} from "src/interfaces/IMorphoLendingAdapterFactory.sol"; +import {IAaveLendingAdapterFactory} from "src/interfaces/IAaveLendingAdapterFactory.sol"; import {DeployConstants} from "./DeployConstants.sol"; contract PeripheryDeploy is Script { @@ -34,15 +36,19 @@ contract PeripheryDeploy is Script { VeloraAdapter veloraAdapter = new VeloraAdapter(DeployConstants.AUGUSTUS_REGISTRY); console.log("VeloraAdapter deployed at: ", address(veloraAdapter)); - LeverageRouter leverageRouter = - new LeverageRouter(ILeverageManager(DeployConstants.LEVERAGE_MANAGER), IMorpho(DeployConstants.MORPHO)); - console.log("LeverageRouter deployed at: ", address(leverageRouter)); + LeverageRouterV2 leverageRouterV2 = new LeverageRouterV2( + ILeverageManager(DeployConstants.LEVERAGE_MANAGER), + IMorpho(DeployConstants.MORPHO), + IPool(DeployConstants.AAVE_POOL) + ); + console.log("LeverageRouterV2 deployed at: ", address(leverageRouterV2)); - LeverageTokenDeploymentBatcher leverageTokenDeploymentBatcher = new LeverageTokenDeploymentBatcher( + LeverageTokenDeploymentBatcherV2 leverageTokenDeploymentBatcherV2 = new LeverageTokenDeploymentBatcherV2( ILeverageManager(DeployConstants.LEVERAGE_MANAGER), - IMorphoLendingAdapterFactory(DeployConstants.LENDING_ADAPTER_FACTORY) + IMorphoLendingAdapterFactory(DeployConstants.MORPHO_LENDING_ADAPTER_FACTORY), + IAaveLendingAdapterFactory(DeployConstants.AAVE_LENDING_ADAPTER_FACTORY) ); - console.log("LeverageTokenDeploymentBatcher deployed at: ", address(leverageTokenDeploymentBatcher)); + console.log("LeverageTokenDeploymentBatcherV2 deployed at: ", address(leverageTokenDeploymentBatcherV2)); vm.stopBroadcast(); } diff --git a/script/8453/CreateLeverageToken.WEETH-WETH-17x.s.sol b/script/8453/CreateLeverageToken.WEETH-WETH-17x.s.sol index abd2c5c9..a4b491f1 100644 --- a/script/8453/CreateLeverageToken.WEETH-WETH-17x.s.sol +++ b/script/8453/CreateLeverageToken.WEETH-WETH-17x.s.sol @@ -31,7 +31,7 @@ contract CreateLeverageToken is Script { ILeverageManager public leverageManager = ILeverageManager(DeployConstants.LEVERAGE_MANAGER); IMorphoLendingAdapterFactory public lendingAdapterFactory = - IMorphoLendingAdapterFactory(DeployConstants.LENDING_ADAPTER_FACTORY); + IMorphoLendingAdapterFactory(DeployConstants.MORPHO_LENDING_ADAPTER_FACTORY); ILeverageRouter public leverageRouter = ILeverageRouter(DeployConstants.LEVERAGE_ROUTER); IMulticallExecutor public multicallExecutor = IMulticallExecutor(DeployConstants.MULTICALL_EXECUTOR); diff --git a/script/8453/CreateLeverageToken.s.sol b/script/8453/CreateLeverageToken.s.sol index c59ee904..a249c3ae 100644 --- a/script/8453/CreateLeverageToken.s.sol +++ b/script/8453/CreateLeverageToken.s.sol @@ -28,7 +28,7 @@ contract CreateLeverageToken is Script { ILeverageManager public leverageManager = ILeverageManager(DeployConstants.LEVERAGE_MANAGER); IMorphoLendingAdapterFactory public lendingAdapterFactory = - IMorphoLendingAdapterFactory(DeployConstants.LENDING_ADAPTER_FACTORY); + IMorphoLendingAdapterFactory(DeployConstants.MORPHO_LENDING_ADAPTER_FACTORY); /// @dev Market ID for Morpho market that LT will be created on top of Id public MORPHO_MARKET_ID = Id.wrap(0x8793cf302b8ffd655ab97bd1c695dbd967807e8367a65cb2f4edaf1380ba1bda); diff --git a/script/8453/DeployConstants.sol b/script/8453/DeployConstants.sol index c5cd9476..8d98bbb2 100644 --- a/script/8453/DeployConstants.sol +++ b/script/8453/DeployConstants.sol @@ -3,6 +3,8 @@ pragma solidity ^0.8.26; library DeployConstants { address public constant MORPHO = 0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb; + address public constant AAVE_POOL = 0xA238Dd80C259a72e81d7e4664a9801593F98d1c5; + address public constant SEAMLESS_TIMELOCK_SHORT = 0x639d2dD24304aC2e6A691d8c1cFf4a2665925fee; address public constant SEAMLESS_TREASURY = 0x639d2dD24304aC2e6A691d8c1cFf4a2665925fee; address public constant ETHERFI_L2_MODE_SYNC_POOL = 0xc38e046dFDAdf15f7F56853674242888301208a5; @@ -12,10 +14,13 @@ library DeployConstants { address public constant WEETH = 0x04C0599Ae5A44757c0af6F9eC3b93da8976c150A; address public constant LEVERAGE_MANAGER = 0x38Ba21C6Bf31dF1b1798FCEd07B4e9b07C5ec3a8; - address public constant LENDING_ADAPTER_FACTORY = 0xDd33419F0c01879a23051edbcdA997A0f9E68e61; + address public constant MORPHO_LENDING_ADAPTER_FACTORY = 0xDd33419F0c01879a23051edbcdA997A0f9E68e61; + address public constant AAVE_LENDING_ADAPTER_FACTORY = address(0); // TODO: Deploy and set address address public constant LEVERAGE_ROUTER = 0xDbA92fC3dc10a17b96b6E807a908155C389A887C; + address public constant LEVERAGE_ROUTER_V2 = address(0); // TODO: Deploy and set address address public constant MULTICALL_EXECUTOR = 0x0000000000000000000000000000000000000000; address public constant LEVERAGE_TOKEN_DEPLOYMENT_BATCHER = 0x0000000000000000000000000000000000000000; + address public constant LEVERAGE_TOKEN_DEPLOYMENT_BATCHER_V2 = address(0); // TODO: Deploy and set address address public constant DUTCH_AUCTION_PRE_LIQUIDATION_COLLATERAL_RATIOS_REBALANCE_ADAPTER_IMPLEMENTATION = 0x0000000000000000000000000000000000000000; diff --git a/script/8453/DeployLeverageTokenDeploymentBatcher.s.sol b/script/8453/DeployLeverageTokenDeploymentBatcher.s.sol index 242ccd96..5503c2c4 100644 --- a/script/8453/DeployLeverageTokenDeploymentBatcher.s.sol +++ b/script/8453/DeployLeverageTokenDeploymentBatcher.s.sol @@ -5,9 +5,10 @@ pragma solidity ^0.8.26; import {Script, console} from "forge-std/Script.sol"; /// Internal imports -import {LeverageTokenDeploymentBatcher} from "src/periphery/LeverageTokenDeploymentBatcher.sol"; +import {LeverageTokenDeploymentBatcherV2} from "src/periphery/LeverageTokenDeploymentBatcherV2.sol"; import {ILeverageManager} from "src/interfaces/ILeverageManager.sol"; import {IMorphoLendingAdapterFactory} from "src/interfaces/IMorphoLendingAdapterFactory.sol"; +import {IAaveLendingAdapterFactory} from "src/interfaces/IAaveLendingAdapterFactory.sol"; import {DeployConstants} from "./DeployConstants.sol"; contract DeployLeverageTokenDeploymentBatcher is Script { @@ -22,11 +23,12 @@ contract DeployLeverageTokenDeploymentBatcher is Script { address deployerAddress = msg.sender; console.log("DeployerAddress: ", deployerAddress); - LeverageTokenDeploymentBatcher leverageTokenDeploymentBatcher = new LeverageTokenDeploymentBatcher( + LeverageTokenDeploymentBatcherV2 leverageTokenDeploymentBatcherV2 = new LeverageTokenDeploymentBatcherV2( ILeverageManager(DeployConstants.LEVERAGE_MANAGER), - IMorphoLendingAdapterFactory(DeployConstants.LENDING_ADAPTER_FACTORY) + IMorphoLendingAdapterFactory(DeployConstants.MORPHO_LENDING_ADAPTER_FACTORY), + IAaveLendingAdapterFactory(DeployConstants.AAVE_LENDING_ADAPTER_FACTORY) ); - console.log("LeverageTokenDeploymentBatcher deployed at: ", address(leverageTokenDeploymentBatcher)); + console.log("LeverageTokenDeploymentBatcherV2 deployed at: ", address(leverageTokenDeploymentBatcherV2)); vm.stopBroadcast(); } diff --git a/script/8453/Periphery.s.sol b/script/8453/Periphery.s.sol index 5f895712..43d53c30 100644 --- a/script/8453/Periphery.s.sol +++ b/script/8453/Periphery.s.sol @@ -6,14 +6,16 @@ import {Script, console} from "forge-std/Script.sol"; /// Dependency imports import {IMorpho} from "@morpho-blue/interfaces/IMorpho.sol"; +import {IPool} from "@aave-v3-origin/contracts/interfaces/IPool.sol"; /// Internal imports import {MulticallExecutor} from "src/periphery/MulticallExecutor.sol"; import {VeloraAdapter} from "src/periphery/VeloraAdapter.sol"; -import {LeverageRouter} from "src/periphery/LeverageRouter.sol"; -import {LeverageTokenDeploymentBatcher} from "src/periphery/LeverageTokenDeploymentBatcher.sol"; +import {LeverageRouterV2} from "src/periphery/LeverageRouterV2.sol"; +import {LeverageTokenDeploymentBatcherV2} from "src/periphery/LeverageTokenDeploymentBatcherV2.sol"; import {ILeverageManager} from "src/interfaces/ILeverageManager.sol"; import {IMorphoLendingAdapterFactory} from "src/interfaces/IMorphoLendingAdapterFactory.sol"; +import {IAaveLendingAdapterFactory} from "src/interfaces/IAaveLendingAdapterFactory.sol"; import {DeployConstants} from "./DeployConstants.sol"; contract PeripheryDeploy is Script { @@ -34,15 +36,19 @@ contract PeripheryDeploy is Script { VeloraAdapter veloraAdapter = new VeloraAdapter(DeployConstants.AUGUSTUS_REGISTRY); console.log("VeloraAdapter deployed at: ", address(veloraAdapter)); - LeverageRouter leverageRouter = - new LeverageRouter(ILeverageManager(DeployConstants.LEVERAGE_MANAGER), IMorpho(DeployConstants.MORPHO)); - console.log("LeverageRouter deployed at: ", address(leverageRouter)); + LeverageRouterV2 leverageRouterV2 = new LeverageRouterV2( + ILeverageManager(DeployConstants.LEVERAGE_MANAGER), + IMorpho(DeployConstants.MORPHO), + IPool(DeployConstants.AAVE_POOL) + ); + console.log("LeverageRouterV2 deployed at: ", address(leverageRouterV2)); - LeverageTokenDeploymentBatcher leverageTokenDeploymentBatcher = new LeverageTokenDeploymentBatcher( + LeverageTokenDeploymentBatcherV2 leverageTokenDeploymentBatcherV2 = new LeverageTokenDeploymentBatcherV2( ILeverageManager(DeployConstants.LEVERAGE_MANAGER), - IMorphoLendingAdapterFactory(DeployConstants.LENDING_ADAPTER_FACTORY) + IMorphoLendingAdapterFactory(DeployConstants.MORPHO_LENDING_ADAPTER_FACTORY), + IAaveLendingAdapterFactory(DeployConstants.AAVE_LENDING_ADAPTER_FACTORY) ); - console.log("LeverageTokenDeploymentBatcher deployed at: ", address(leverageTokenDeploymentBatcher)); + console.log("LeverageTokenDeploymentBatcherV2 deployed at: ", address(leverageTokenDeploymentBatcherV2)); vm.stopBroadcast(); } diff --git a/src/interfaces/IAaveLendingAdapter.sol b/src/interfaces/IAaveLendingAdapter.sol new file mode 100644 index 00000000..95765df8 --- /dev/null +++ b/src/interfaces/IAaveLendingAdapter.sol @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +// Dependency imports +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +// Internal imports +import {IPreLiquidationLendingAdapter} from "./IPreLiquidationLendingAdapter.sol"; +import {ILeverageManager} from "./ILeverageManager.sol"; + +// Aave v3 imports +import {IPool} from "@aave-v3-origin/contracts/interfaces/IPool.sol"; +import {IPoolAddressesProvider} from "@aave-v3-origin/contracts/interfaces/IPoolAddressesProvider.sol"; + +/// @title IAaveLendingAdapter +/// @notice Interface for the Aave v3 Lending Adapter +/// @dev IMPORTANT: This adapter deliberately does NOT use Aave's aggregated position functions like +/// `getUserAccountData()` to determine position state. Instead, it tracks collateral and debt using +/// the specific aToken and variableDebtToken balances for the configured assets only. +/// +/// This design protects against manipulation via "foreign collateral donations" - where someone +/// supplies a different asset on behalf of this adapter in Aave. Such donations would affect +/// Aave's aggregated position calculations but do NOT affect this adapter's view of its position. +interface IAaveLendingAdapter is IPreLiquidationLendingAdapter { + /// @notice Event emitted when the AaveLendingAdapter is initialized + /// @param collateralAsset The address of the collateral asset + /// @param debtAsset The address of the debt asset + /// @param authorizedCreator The authorized creator of the AaveLendingAdapter + event AaveLendingAdapterInitialized( + address indexed collateralAsset, address indexed debtAsset, address indexed authorizedCreator + ); + + /// @notice Event emitted when the AaveLendingAdapter is flagged as used + event AaveLendingAdapterUsed(); + + /// @notice Event emitted when the eMode is updated + /// @param previousEModeCategory The previous eMode category id + /// @param newEModeCategory The new eMode category id + event EModeUpdated(uint8 previousEModeCategory, uint8 newEModeCategory); + + /// @notice Thrown when someone tries to create a LeverageToken with this AaveLendingAdapter but it is already in use + error LendingAdapterAlreadyInUse(); + + /// @notice The authorized creator of the AaveLendingAdapter + /// @return _authorizedCreator The authorized creator of the AaveLendingAdapter + /// @dev Only the authorized creator can create a new LeverageToken using this adapter on the LeverageManager + function authorizedCreator() external view returns (address _authorizedCreator); + + /// @notice Whether the AaveLendingAdapter is in use + /// @return _isUsed Whether the AaveLendingAdapter is in use + /// @dev If this is true, the AaveLendingAdapter cannot be used to create a new LeverageToken + function isUsed() external view returns (bool _isUsed); + + /// @notice The LeverageManager contract + /// @return _leverageManager The LeverageManager contract + function leverageManager() external view returns (ILeverageManager _leverageManager); + + /// @notice The Aave v3 Pool contract + /// @return _pool The Aave v3 Pool contract + function pool() external view returns (IPool _pool); + + /// @notice The Aave v3 PoolAddressesProvider contract + /// @return _addressesProvider The Aave v3 PoolAddressesProvider contract + function addressesProvider() external view returns (IPoolAddressesProvider _addressesProvider); + + /// @notice The collateral asset address + /// @return _collateralAsset The collateral asset address + function collateralAsset() external view returns (address _collateralAsset); + + /// @notice The debt asset address + /// @return _debtAsset The debt asset address + function debtAsset() external view returns (address _debtAsset); + + /// @notice The current eMode category id from the Aave pool + /// @return _eModeCategory The eMode category id (0 = no eMode) + function eModeCategory() external view returns (uint8 _eModeCategory); + + /// @notice The aToken address for the collateral asset + /// @return _aToken The aToken address + function aToken() external view returns (IERC20 _aToken); + + /// @notice The variable debt token address for the debt asset + /// @return _variableDebtToken The variable debt token address + function variableDebtToken() external view returns (IERC20 _variableDebtToken); + + /// @notice The decimals of the collateral asset + /// @return _collateralDecimals The decimals of the collateral asset + function collateralDecimals() external view returns (uint8 _collateralDecimals); + + /// @notice The decimals of the debt asset + /// @return _debtDecimals The decimals of the debt asset + function debtDecimals() external view returns (uint8 _debtDecimals); + + /// @notice Error thrown when trying to set an invalid eMode + /// @param newEModeCategory The invalid eMode category that was attempted + error InvalidEMode(uint8 newEModeCategory); + + /// @notice Sets a new eMode for the lending adapter + /// @dev Only allows setting an eMode that is valid and better than (or equal to) the current eMode. + /// This is permissionless since it only benefits the leverage token holders. + /// @param newEModeCategory The eMode category to set (0 to disable eMode) + function setEMode(uint8 newEModeCategory) external; + + /// @notice Validates that a new eMode category is valid and better than (or equal to) the current eMode + /// @dev Checks that: + /// 1. The collateral asset is valid collateral in the new eMode + /// 2. The debt asset is borrowable in the new eMode + /// 3. The collateral has non-zero LTV in the new eMode + /// 4. The new eMode's LTV is >= current eMode's LTV + /// @param newEModeCategory The eMode category to validate (0 to disable eMode) + /// @return isValid True if the new eMode is valid and beneficial + function validateEMode(uint8 newEModeCategory) external view returns (bool isValid); +} diff --git a/src/interfaces/IAaveLendingAdapterFactory.sol b/src/interfaces/IAaveLendingAdapterFactory.sol new file mode 100644 index 00000000..85f12353 --- /dev/null +++ b/src/interfaces/IAaveLendingAdapterFactory.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.26; + +// Internal imports +import {IAaveLendingAdapter} from "src/interfaces/IAaveLendingAdapter.sol"; + +interface IAaveLendingAdapterFactory { + /// @notice Emitted when a new AaveLendingAdapter is deployed. + /// @param lendingAdapter The deployed AaveLendingAdapter + event AaveLendingAdapterDeployed(IAaveLendingAdapter lendingAdapter); + + /// @notice Given the `sender` and `baseSalt` compute and return the address that AaveLendingAdapter will be deployed to + /// using the `IAaveLendingAdapterFactory.deployAdapter` function. + /// @param sender The address of the sender of the `IAaveLendingAdapterFactory.deployAdapter` call. + /// @param baseSalt The user-provided salt. + /// @dev AaveLendingAdapter addresses are uniquely determined by their salt because the deployer is always the factory, + /// and the use of minimal proxies means they all have identical bytecode and therefore an identical bytecode hash. + /// @dev The `baseSalt` is the user-provided salt, not the final salt after hashing with the sender's address. + function computeAddress(address sender, bytes32 baseSalt) external view returns (address); + + /// @notice Returns the address of the AaveLendingAdapter logic contract used to deploy minimal proxies. + function lendingAdapterLogic() external view returns (IAaveLendingAdapter); + + /// @notice Deploys a new AaveLendingAdapter contract with the specified configuration. + /// @param collateralAsset The address of the collateral asset + /// @param debtAsset The address of the debt asset + /// @param authorizedCreator The authorized creator of the deployed AaveLendingAdapter. The authorized creator can create a + /// new LeverageToken using this adapter on the LeverageManager + /// @param baseSalt Used to compute the resulting address of the AaveLendingAdapter. + /// @dev AaveLendingAdapters deployed by this factory are minimal proxies. + /// @dev The optimal eMode is automatically set during initialization. + function deployAdapter( + address collateralAsset, + address debtAsset, + address authorizedCreator, + bytes32 baseSalt + ) external returns (IAaveLendingAdapter lendingAdapter); +} diff --git a/src/interfaces/periphery/ILeverageRouterV2.sol b/src/interfaces/periphery/ILeverageRouterV2.sol new file mode 100644 index 00000000..4b23fa13 --- /dev/null +++ b/src/interfaces/periphery/ILeverageRouterV2.sol @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +// Dependency imports +import {IMorpho} from "@morpho-blue/interfaces/IMorpho.sol"; + +// Internal imports +import {ILeverageManager} from "../ILeverageManager.sol"; +import {ILeverageToken} from "../ILeverageToken.sol"; +import {IMulticallExecutor} from "./IMulticallExecutor.sol"; +import {IVeloraAdapter} from "./IVeloraAdapter.sol"; +import {ActionData} from "src/types/DataTypes.sol"; + +/// @title ILeverageRouterV2 +/// @notice Interface for LeverageRouterV2 - supports both Morpho and Aave v3 flash loans +interface ILeverageRouterV2 { + /// @notice Flash loan source to use for the operation + enum FlashLoanSource { + Morpho, + Aave + } + + /// @notice Internal action type for flash loan callbacks + enum LeverageRouterAction { + Deposit, + Redeem, + RedeemWithVelora + } + + /// @notice Deposit related parameters to pass to flash loan callback handlers for deposits + struct DepositParams { + // Address of the sender of the deposit (collateral is pulled from this address) + address sender; + // Address to receive the minted shares and any surplus debt + address receiver; + // LeverageToken to deposit into + ILeverageToken leverageToken; + // Amount of collateral from the sender to deposit + uint256 collateralFromSender; + // Minimum amount of shares (LeverageTokens) to receive + uint256 minShares; + // multicall executor to use for the swap + IMulticallExecutor multicallExecutor; + // External calls to execute for the swap of flash loaned debt to collateral + IMulticallExecutor.Call[] swapCalls; + } + + /// @notice Flash loan callback data passed to both Morpho and Aave flash loan callbacks + struct FlashLoanCallbackData { + LeverageRouterAction action; + bytes data; + } + + /// @notice Redeem related parameters to pass to flash loan callback handlers for redeems + struct RedeemParams { + // Address of the sender of the redeem (shares are pulled from this address) + address sender; + // Address to receive the collateral and any surplus debt + address receiver; + // LeverageToken to redeem from + ILeverageToken leverageToken; + // Amount of shares to redeem + uint256 shares; + // Minimum amount of collateral for the receiver + uint256 minCollateralForReceiver; + // multicall executor to use for the swap + IMulticallExecutor multicallExecutor; + // External calls to execute for the swap of flash loaned debt to collateral + IMulticallExecutor.Call[] swapCalls; + } + + /// @notice Redeem related parameters to pass to flash loan callback handlers for redeems using Velora + struct RedeemWithVeloraParams { + // Address of the sender of the redeem (shares are pulled from this address) + address sender; + // Address to receive the collateral + address receiver; + // LeverageToken to redeem from + ILeverageToken leverageToken; + // Amount of shares to redeem + uint256 shares; + // Minimum amount of collateral for the receiver + uint256 minCollateralForReceiver; + // Velora adapter to use for the swap + IVeloraAdapter veloraAdapter; + // Velora Augustus contract to use for the swap + address augustus; + // Offsets for the Velora swap + IVeloraAdapter.Offsets offsets; + // Calldata for the Velora swap + bytes swapData; + } + + /// @notice Error thrown when the remaining collateral is less than the minimum collateral for the receiver + /// @param remainingCollateral The remaining collateral after the swap + /// @param minCollateralForReceiver The minimum collateral for the receiver + error CollateralSlippageTooHigh(uint256 remainingCollateral, uint256 minCollateralForReceiver); + + /// @notice Error thrown when the collateral from the swap + the collateral from the sender is less than the collateral required for the deposit + /// @param available The collateral from the swap + the collateral from the sender, available for the deposit + /// @param required The collateral required for the deposit + error InsufficientCollateralForDeposit(uint256 available, uint256 required); + + /// @notice Error thrown when the cost of a swap exceeds the maximum allowed cost + /// @param actualCost The actual cost of the swap + /// @param maxCost The maximum allowed cost of the swap + error MaxSwapCostExceeded(uint256 actualCost, uint256 maxCost); + + /// @notice Error thrown when the caller is not authorized to execute a function + error Unauthorized(); + + /// @notice Converts an amount of equity to an amount of collateral for a LeverageToken, based on the current + /// collateral ratio of the LeverageToken + /// @param token LeverageToken to convert equity to collateral for + /// @param equityInCollateralAsset Amount of equity to convert to collateral, denominated in the collateral asset of the LeverageToken + /// @return collateral Amount of collateral that correspond to the equity amount + function convertEquityToCollateral(ILeverageToken token, uint256 equityInCollateralAsset) + external + view + returns (uint256 collateral); + + /// @notice The LeverageManager contract + /// @return _leverageManager The LeverageManager contract + function leverageManager() external view returns (ILeverageManager _leverageManager); + + /// @notice The Morpho core protocol contract + /// @return _morpho The Morpho core protocol contract + function morpho() external view returns (IMorpho _morpho); + + /// @notice Previews the deposit function call for an amount of equity and returns all required data + /// @param token LeverageToken to preview deposit for + /// @param collateralFromSender The amount of collateral from the sender to deposit + /// @return previewData Preview data for deposit + /// - collateral Total amount of collateral that will be added to the LeverageToken (including collateral from swapping flash loaned debt) + /// - debt Amount of debt that will be borrowed + /// - shares Amount of shares that will be minted + /// - tokenFee Amount of shares that will be charged for the deposit that are given to the LeverageToken + /// - treasuryFee Amount of shares that will be charged for the deposit that are given to the treasury + function previewDeposit(ILeverageToken token, uint256 collateralFromSender) + external + view + returns (ActionData memory); + + /// @notice Deposits collateral into a LeverageToken and mints shares to the receiver. Any surplus debt received from + /// the deposit of (collateralFromSender + debt swapped to collateral) is given to the receiver. + /// @param leverageToken LeverageToken to deposit into + /// @param collateralFromSender Collateral asset amount from the sender to deposit + /// @param flashLoanAmount Amount of debt to flash loan, which is swapped to collateral and used to deposit into the LeverageToken + /// @param minShares Minimum number of shares expected to be received by the receiver + /// @param receiver Address to receive the minted shares and any surplus debt + /// @param multicallExecutor multicall executor to use for the swap + /// @param swapCalls External calls to execute for the swap of flash loaned debt to collateral for the LeverageToken deposit. + /// The calls are executed by the `multicallExecutor` contract after receiving the flash loaned debt. Thus, for any encoded approvals and + /// swaps that require the `from` address to be encoded,`from` must be set to the `multicallExecutor` contract address. The receiver of the swap + /// must either be the `multicallExecutor` or this `LeverageRouter` contract - any leftover collateral and debt assets after the execution + /// of the calls are swept to this `LeverageRouter` contract. + /// @param flashLoanSource Source of the flash loan (Morpho or Aave) + function deposit( + ILeverageToken leverageToken, + uint256 collateralFromSender, + uint256 flashLoanAmount, + uint256 minShares, + address receiver, + IMulticallExecutor multicallExecutor, + IMulticallExecutor.Call[] calldata swapCalls, + FlashLoanSource flashLoanSource + ) external; + + /// @notice Redeems an amount of shares of a LeverageToken and transfers collateral asset to the receiver, using arbitrary + /// calldata for the swap of collateral from the redemption to debt to repay the flash loan. Any surplus debt assets + /// after repaying the flash loan are given to the receiver along with the remaining collateral asset. + /// @param token LeverageToken to redeem from + /// @param shares Amount of shares to redeem + /// @param minCollateralForReceiver Minimum amount of collateral for the receiver + /// @param receiver Address to receive the collateral and any surplus debt + /// @param multicallExecutor multicall executor to use for the swap + /// @param swapCalls External calls to execute for the swap of collateral from the redemption to debt to repay the flash loan. + /// The calls are executed by the `multicallExecutor` contract after receiving the collateral from the redemption. Thus, for + /// any encoded approvals and swaps that require the `from` address to be encoded, `from` must be set to the `multicallExecutor` + /// contract address. The receiver of the swap must either be the `multicallExecutor` or this `LeverageRouter` contract - any leftover + /// collateral and debt assets after the execution of the calls are swept to this `LeverageRouter` contract. + /// @param flashLoanSource Source of the flash loan (Morpho or Aave) + function redeem( + ILeverageToken token, + uint256 shares, + uint256 minCollateralForReceiver, + address receiver, + IMulticallExecutor multicallExecutor, + IMulticallExecutor.Call[] calldata swapCalls, + FlashLoanSource flashLoanSource + ) external; + + /// @notice Redeems an amount of shares of a LeverageToken and transfers collateral asset to the receiver, using Velora + /// for the required swap of collateral from the redemption to debt to repay the flash loan + /// @param token LeverageToken to redeem from + /// @param shares Amount of shares to redeem + /// @param minCollateralForReceiver Minimum amount of collateral for the receiver + /// @param receiver Address to receive the collateral + /// @param veloraAdapter Velora adapter to use for the swap + /// @param augustus Velora Augustus address to use for the swap + /// @param offsets Offsets to use for updating the Velora Augustus calldata + /// @param swapData Velora swap calldata to use for the swap + /// @param flashLoanSource Source of the flash loan (Morpho or Aave) + /// @dev The calldata should be for using Velora for an exact output swap of the collateral asset to the debt asset + /// for the debt amount flash loaned, which is equal to the amount of debt removed from the LeverageToken for the + /// redemption of shares. The exact output amount in the calldata is updated on chain to match the up to date debt + /// amount for the redemption of shares, which typically occurs due to borrow interest accrual and price changes + /// between off chain and on chain execution + function redeemWithVelora( + ILeverageToken token, + uint256 shares, + uint256 minCollateralForReceiver, + address receiver, + IVeloraAdapter veloraAdapter, + address augustus, + IVeloraAdapter.Offsets calldata offsets, + bytes calldata swapData, + FlashLoanSource flashLoanSource + ) external; +} diff --git a/src/interfaces/periphery/ILeverageTokenDeploymentBatcherV2.sol b/src/interfaces/periphery/ILeverageTokenDeploymentBatcherV2.sol new file mode 100644 index 00000000..be7ab66e --- /dev/null +++ b/src/interfaces/periphery/ILeverageTokenDeploymentBatcherV2.sol @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +import {Id} from "@morpho-blue/interfaces/IMorpho.sol"; + +import {ILeverageManager} from "../ILeverageManager.sol"; +import {ILeverageToken} from "../ILeverageToken.sol"; +import {IMorphoLendingAdapter} from "../IMorphoLendingAdapter.sol"; +import {IMorphoLendingAdapterFactory} from "../IMorphoLendingAdapterFactory.sol"; +import {IAaveLendingAdapter} from "../IAaveLendingAdapter.sol"; +import {IAaveLendingAdapterFactory} from "../IAaveLendingAdapterFactory.sol"; +import {IRebalanceAdapter} from "../IRebalanceAdapter.sol"; +import {ActionData} from "../../types/DataTypes.sol"; + +/// @title ILeverageTokenDeploymentBatcherV2 +/// @notice Interface for LeverageTokenDeploymentBatcherV2 - batches deployment and initial deposit for LeverageTokens +interface ILeverageTokenDeploymentBatcherV2 { + struct LeverageTokenDeploymentParams { + /// @notice The name of the leverage token + string leverageTokenName; + /// @notice The symbol of the leverage token + string leverageTokenSymbol; + /// @notice The mint token action fee for the leverage token + uint256 mintTokenFee; + /// @notice The redeem token action fee for the leverage token + uint256 redeemTokenFee; + } + + struct MorphoLendingAdapterDeploymentParams { + /// @notice The Morpho market ID for the lending adapter + Id morphoMarketId; + /// @notice The base salt for the lending adapter deployment + bytes32 baseSalt; + } + + struct AaveLendingAdapterDeploymentParams { + /// @notice The collateral asset address + address collateralAsset; + /// @notice The debt asset address + address debtAsset; + /// @notice The base salt for the lending adapter deployment + bytes32 baseSalt; + } + + struct RebalanceAdapterDeploymentParams { + /// @notice The implementation address of the rebalance adapter + address implementation; + /// @notice The owner of the rebalance adapter + address owner; + /// @notice The minimum collateral ratio for the rebalance adapter + uint256 minCollateralRatio; + /// @notice The target collateral ratio for the rebalance adapter. Must be > `LeverageManager.BASE_RATIO()` + uint256 targetCollateralRatio; + /// @notice The maximum collateral ratio for the rebalance adapter + uint256 maxCollateralRatio; + /// @notice The duration of the auction for the rebalance adapter + uint120 auctionDuration; + /// @notice The initial price multiplier for the rebalance adapter + uint256 initialPriceMultiplier; + /// @notice The minimum price multiplier for the rebalance adapter + uint256 minPriceMultiplier; + /// @notice The collateral ratio threshold for the pre-liquidation rebalance adapter + uint256 preLiquidationCollateralRatioThreshold; + /// @notice The rebalance reward for the rebalance adapter + uint256 rebalanceReward; + } + + /// @notice The LeverageManager contract + function leverageManager() external view returns (ILeverageManager); + + /// @notice The MorphoLendingAdapterFactory contract + function morphoLendingAdapterFactory() external view returns (IMorphoLendingAdapterFactory); + + /// @notice The AaveLendingAdapterFactory contract + function aaveLendingAdapterFactory() external view returns (IAaveLendingAdapterFactory); + + /// @notice Deploys a LeverageToken with a Morpho lending adapter and deposits collateral into it. + /// The sender receives the shares and debt from the deposit. + /// @param leverageTokenDeploymentParams The parameters for the leverage token deployment + /// @param lendingAdapterDeploymentParams The parameters for the Morpho lending adapter deployment + /// @param rebalanceAdapterDeploymentParams The parameters for the rebalance adapter deployment + /// @param collateral The collateral to deposit into the leverage token + /// @param minShares The minimum number of shares to receive from the deposit + /// @return The leverage token and the action data for the deposit + /// @dev The lending adapter deployed is a `MorphoLendingAdapter`. The `morphoLendingAdapterFactory` is used to deploy it + /// as a ERC-1167 minimal proxy. + /// @dev The rebalance adapter is deployed from the `rebalanceAdapterDeploymentParams.implementation` address as a + /// UUPS proxy. + function deployLeverageTokenAndDepositWithMorpho( + LeverageTokenDeploymentParams memory leverageTokenDeploymentParams, + MorphoLendingAdapterDeploymentParams memory lendingAdapterDeploymentParams, + RebalanceAdapterDeploymentParams memory rebalanceAdapterDeploymentParams, + uint256 collateral, + uint256 minShares + ) external returns (ILeverageToken, ActionData memory); + + /// @notice Deploys a LeverageToken with an Aave lending adapter and deposits collateral into it. + /// The sender receives the shares and debt from the deposit. + /// @param leverageTokenDeploymentParams The parameters for the leverage token deployment + /// @param lendingAdapterDeploymentParams The parameters for the Aave lending adapter deployment + /// @param rebalanceAdapterDeploymentParams The parameters for the rebalance adapter deployment + /// @param collateral The collateral to deposit into the leverage token + /// @param minShares The minimum number of shares to receive from the deposit + /// @return The leverage token and the action data for the deposit + /// @dev The lending adapter deployed is an `AaveLendingAdapter`. The `aaveLendingAdapterFactory` is used to deploy it + /// as a ERC-1167 minimal proxy. + /// @dev The rebalance adapter is deployed from the `rebalanceAdapterDeploymentParams.implementation` address as a + /// UUPS proxy. + function deployLeverageTokenAndDepositWithAave( + LeverageTokenDeploymentParams memory leverageTokenDeploymentParams, + AaveLendingAdapterDeploymentParams memory lendingAdapterDeploymentParams, + RebalanceAdapterDeploymentParams memory rebalanceAdapterDeploymentParams, + uint256 collateral, + uint256 minShares + ) external returns (ILeverageToken, ActionData memory); +} diff --git a/src/lending/AaveLendingAdapter.sol b/src/lending/AaveLendingAdapter.sol new file mode 100644 index 00000000..c0ffa6a6 --- /dev/null +++ b/src/lending/AaveLendingAdapter.sol @@ -0,0 +1,424 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +// Dependency imports +import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol"; + +// Internal imports +import {ILendingAdapter} from "src/interfaces/ILendingAdapter.sol"; +import {ILeverageManager} from "src/interfaces/ILeverageManager.sol"; +import {IAaveLendingAdapter} from "src/interfaces/IAaveLendingAdapter.sol"; +import {IPreLiquidationLendingAdapter} from "src/interfaces/IPreLiquidationLendingAdapter.sol"; + +// Aave v3 imports +import {IPool} from "@aave-v3-origin/contracts/interfaces/IPool.sol"; +import {IPoolAddressesProvider} from "@aave-v3-origin/contracts/interfaces/IPoolAddressesProvider.sol"; +import {IAaveOracle} from "@aave-v3-origin/contracts/interfaces/IAaveOracle.sol"; +import {DataTypes} from "@aave-v3-origin/contracts/protocol/libraries/types/DataTypes.sol"; +import {ReserveConfiguration} from "@aave-v3-origin/contracts/protocol/libraries/configuration/ReserveConfiguration.sol"; + +/** + * @title AaveLendingAdapter + * @notice Adapter to interface with Aave v3 markets for LeverageTokens + * @dev LeverageToken creators can configure their LeverageToken to use an AaveLendingAdapter + * to use Aave as the lending protocol for their LeverageToken. + * + * COMPATIBILITY: This adapter requires Aave v3.1+ (aave-v3-origin). It uses functions like + * `getEModeCategoryLtvzeroBitmap()` that are not available in earlier Aave v3.0.x deployments. + * + * The AaveLendingAdapter uses Aave's oracle to convert between the collateral and debt asset. + * It supports Aave's E-Mode for correlated assets like ETH/stETH pairs, which enables higher + * capital efficiency. + * + * SECURITY NOTE: This adapter deliberately does NOT use Aave's aggregated position functions + * (like `getUserAccountData()`) to determine position state. Instead, it tracks collateral and + * debt using the specific aToken and variableDebtToken balances for the configured assets only. + * + * This design protects against manipulation via "foreign collateral donations" - where someone + * supplies a different asset on behalf of this adapter in Aave. Such donations would affect + * Aave's aggregated position calculations but do NOT affect this adapter's view of its position. + * Foreign collateral can be rescued using `rescueForeignCollateral()`. + * + * Note: `getDebt` returns the current variable debt balance which includes accrued interest. + * + * @custom:contact security@seamlessprotocol.com + */ +contract AaveLendingAdapter is IAaveLendingAdapter, Initializable { + /// @dev Variable interest rate mode in Aave + uint256 internal constant VARIABLE_INTEREST_RATE_MODE = 2; + + /// @dev Base for percentage calculations (100% = 10000) + uint256 internal constant PERCENTAGE_FACTOR = 1e4; + + /// @dev WAD for 1e18 calculations + uint256 internal constant WAD = 1e18; + + /// @inheritdoc IAaveLendingAdapter + ILeverageManager public immutable leverageManager; + + /// @inheritdoc IAaveLendingAdapter + IPoolAddressesProvider public immutable addressesProvider; + + /// @inheritdoc IAaveLendingAdapter + IPool public immutable pool; + + /// @inheritdoc IAaveLendingAdapter + address public collateralAsset; + + /// @inheritdoc IAaveLendingAdapter + address public debtAsset; + + /// @inheritdoc IAaveLendingAdapter + IERC20 public aToken; + + /// @inheritdoc IAaveLendingAdapter + IERC20 public variableDebtToken; + + /// @inheritdoc IAaveLendingAdapter + address public authorizedCreator; + + /// @inheritdoc IAaveLendingAdapter + bool public isUsed; + + /// @inheritdoc IAaveLendingAdapter + uint8 public collateralDecimals; + + /// @inheritdoc IAaveLendingAdapter + uint8 public debtDecimals; + + /// @dev Reverts if the caller is not the stored LeverageManager address + modifier onlyLeverageManager() { + if (msg.sender != address(leverageManager)) revert Unauthorized(); + _; + } + + /// @notice Creates a new AaveLendingAdapter + /// @param _leverageManager The LeverageManager contract + /// @param _addressesProvider The Aave v3 PoolAddressesProvider contract + constructor(ILeverageManager _leverageManager, IPoolAddressesProvider _addressesProvider) { + leverageManager = _leverageManager; + addressesProvider = _addressesProvider; + pool = IPool(_addressesProvider.getPool()); + } + + /// @notice Initializes the AaveLendingAdapter + /// @param _collateralAsset The address of the collateral asset + /// @param _debtAsset The address of the debt asset + /// @param _authorizedCreator The authorized creator of this AaveLendingAdapter + function initialize(address _collateralAsset, address _debtAsset, address _authorizedCreator) + external + initializer + { + collateralAsset = _collateralAsset; + debtAsset = _debtAsset; + + aToken = IERC20(pool.getReserveAToken(_collateralAsset)); + variableDebtToken = IERC20(pool.getReserveVariableDebtToken(_debtAsset)); + + collateralDecimals = IERC20Metadata(_collateralAsset).decimals(); + debtDecimals = IERC20Metadata(_debtAsset).decimals(); + + authorizedCreator = _authorizedCreator; + + emit AaveLendingAdapterInitialized(_collateralAsset, _debtAsset, _authorizedCreator); + } + + /// @inheritdoc ILendingAdapter + function postLeverageTokenCreation(address creator, address) external onlyLeverageManager { + if (creator != authorizedCreator) revert Unauthorized(); + if (isUsed) revert LendingAdapterAlreadyInUse(); + isUsed = true; + + emit AaveLendingAdapterUsed(); + } + + /// @inheritdoc ILendingAdapter + function getCollateralAsset() external view returns (IERC20) { + return IERC20(collateralAsset); + } + + /// @inheritdoc ILendingAdapter + function getDebtAsset() external view returns (IERC20) { + return IERC20(debtAsset); + } + + /// @inheritdoc ILendingAdapter + function convertCollateralToDebtAsset(uint256 collateral) public view returns (uint256) { + IAaveOracle oracle = IAaveOracle(addressesProvider.getPriceOracle()); + + uint256 collateralPriceInBase = oracle.getAssetPrice(collateralAsset); + uint256 debtPriceInBase = oracle.getAssetPrice(debtAsset); + + // Calculate: (collateral * collateralPrice * 10^debtDecimals) / (debtPrice * 10^collateralDecimals) + // This properly handles decimal differences between assets + // Rounds down the value of collateral + // Assumes collateralPriceInBase and debtPriceInBase are in the same base (e.g., 1e8) + return Math.mulDiv( + collateral * collateralPriceInBase, + 10 ** debtDecimals, + debtPriceInBase * (10 ** collateralDecimals), + Math.Rounding.Floor + ); + } + + /// @inheritdoc ILendingAdapter + function convertDebtToCollateralAsset(uint256 debt) public view returns (uint256) { + IAaveOracle oracle = IAaveOracle(addressesProvider.getPriceOracle()); + + uint256 collateralPriceInBase = oracle.getAssetPrice(collateralAsset); + uint256 debtPriceInBase = oracle.getAssetPrice(debtAsset); + + // Calculate: (debt * debtPrice * 10^collateralDecimals) / (collateralPrice * 10^debtDecimals) + // Rounds up the value of debt + // Assumes collateralPriceInBase and debtPriceInBase are in the same base (e.g., 1e8) + return Math.mulDiv( + debt * debtPriceInBase, + 10 ** collateralDecimals, + collateralPriceInBase * (10 ** debtDecimals), + Math.Rounding.Ceil + ); + } + + /// @inheritdoc ILendingAdapter + function getCollateral() public view returns (uint256) { + return aToken.balanceOf(address(this)); + } + + /// @inheritdoc ILendingAdapter + function getCollateralInDebtAsset() public view returns (uint256) { + return convertCollateralToDebtAsset(getCollateral()); + } + + /// @inheritdoc ILendingAdapter + function getDebt() public view returns (uint256) { + return variableDebtToken.balanceOf(address(this)); + } + + /// @inheritdoc ILendingAdapter + function getEquityInCollateralAsset() external view returns (uint256) { + uint256 collateral = getCollateral(); + uint256 debtInCollateralAsset = convertDebtToCollateralAsset(getDebt()); + + return collateral > debtInCollateralAsset ? collateral - debtInCollateralAsset : 0; + } + + /// @inheritdoc ILendingAdapter + function getEquityInDebtAsset() external view returns (uint256) { + uint256 collateralInDebtAsset = getCollateralInDebtAsset(); + uint256 debt = getDebt(); + + return collateralInDebtAsset > debt ? collateralInDebtAsset - debt : 0; + } + + /// @inheritdoc IPreLiquidationLendingAdapter + function getLiquidationPenalty() external view returns (uint256) { + uint256 liquidationBonus = _getLiquidationBonus(); + + // Aave's liquidationBonus is in basis points (e.g., 10500 = 5% bonus = 105%) + // We need to return the penalty as a WAD value where 1e18 = 100% + // So 5% penalty = 0.05e18 + if (liquidationBonus <= PERCENTAGE_FACTOR) { + return 0; + } + return Math.mulDiv(liquidationBonus - PERCENTAGE_FACTOR, WAD, PERCENTAGE_FACTOR); + } + + /// @inheritdoc IAaveLendingAdapter + function eModeCategory() public view returns (uint8) { + return uint8(pool.getUserEMode(address(this))); + } + + /// @dev Returns the liquidation bonus for the collateral asset, accounting for e-mode + /// @return The liquidation bonus in basis points (e.g., 10500 = 105%) + function _getLiquidationBonus() internal view returns (uint256) { + uint8 currentEModeCategory = eModeCategory(); + + // If in e-mode, check if collateral is part of the e-mode category + if (currentEModeCategory != 0) { + uint16 collateralReserveId = pool.getReserveData(collateralAsset).id; + uint128 collateralBitmap = pool.getEModeCategoryCollateralBitmap(currentEModeCategory); + + // If collateral is part of e-mode, use e-mode's liquidation bonus + if ((collateralBitmap >> collateralReserveId) & 1 != 0) { + DataTypes.CollateralConfig memory eModeConfig = + pool.getEModeCategoryCollateralConfig(currentEModeCategory); + return eModeConfig.liquidationBonus; + } + } + + // Otherwise, use the reserve's default liquidation bonus + DataTypes.ReserveConfigurationMap memory reserveConfig = pool.getConfiguration(collateralAsset); + return ReserveConfiguration.getLiquidationBonus(reserveConfig); + } + + /// @inheritdoc ILendingAdapter + function addCollateral(uint256 amount) external { + if (amount == 0) return; + + // Transfer the collateral from msg.sender to this contract + SafeERC20.safeTransferFrom(IERC20(collateralAsset), msg.sender, address(this), amount); + + // Supply the collateral to Aave + SafeERC20.forceApprove(IERC20(collateralAsset), address(pool), amount); + pool.supply(collateralAsset, amount, address(this), 0); + } + + /// @inheritdoc ILendingAdapter + function removeCollateral(uint256 amount) external onlyLeverageManager { + if (amount == 0) return; + // Withdraw the collateral from Aave and send it to msg.sender + pool.withdraw(collateralAsset, amount, msg.sender); + } + + /// @inheritdoc ILendingAdapter + function borrow(uint256 amount) external onlyLeverageManager { + if (amount == 0) return; + + // Borrow the debt asset from Aave and send it to the caller + pool.borrow(debtAsset, amount, VARIABLE_INTEREST_RATE_MODE, 0, address(this)); + + // Transfer the borrowed amount to the caller + SafeERC20.safeTransfer(IERC20(debtAsset), msg.sender, amount); + } + + /// @inheritdoc ILendingAdapter + function repay(uint256 amount) external { + if (amount == 0) return; + + // Transfer the debt asset from msg.sender to this contract + SafeERC20.safeTransferFrom(IERC20(debtAsset), msg.sender, address(this), amount); + + // Get current debt to cap repayment. + // This is duplication of Aave repay logic, but since it's not part of the interface definition we do it here in case this logic changes in a future Aave update. + uint256 currentDebt = getDebt(); + uint256 repayAmount = amount > currentDebt ? currentDebt : amount; + + // Repay the debt to Aave + SafeERC20.forceApprove(IERC20(debtAsset), address(pool), repayAmount); + pool.repay(debtAsset, repayAmount, VARIABLE_INTEREST_RATE_MODE, address(this)); + } + + /// @inheritdoc IAaveLendingAdapter + function setEMode(uint8 newEModeCategory) external { + if (!validateEMode(newEModeCategory)) { + revert InvalidEMode(newEModeCategory); + } + + uint8 previousEModeCategory = eModeCategory(); + + // Only update if the eMode is different from current + if (newEModeCategory != previousEModeCategory) { + pool.setUserEMode(newEModeCategory); + emit EModeUpdated(previousEModeCategory, newEModeCategory); + } + } + + /// @inheritdoc IAaveLendingAdapter + function validateEMode(uint8 newEModeCategory) public view returns (bool) { + uint8 currentEModeCategory = eModeCategory(); + + // Same category is always valid (no change) + if (newEModeCategory == currentEModeCategory) { + return true; + } + + // Get the config to compare against (either current eMode config or default reserve config) + DataTypes.CollateralConfig memory currentConfig; + if (currentEModeCategory != 0) { + currentConfig = pool.getEModeCategoryCollateralConfig(currentEModeCategory); + } else { + currentConfig = _getDefaultReserveConfig(); + } + + // Get the new config to validate (either new eMode config or default reserve config) + DataTypes.CollateralConfig memory newConfig; + if (newEModeCategory != 0) { + // Validate the new eMode category is valid for our assets + if (!_isEModeValidForAssets(newEModeCategory)) { + return false; + } + newConfig = pool.getEModeCategoryCollateralConfig(newEModeCategory); + } else { + newConfig = _getDefaultReserveConfig(); + } + + // New config must have LTV >= current config's LTV (higher LTV is better) + if (newConfig.ltv < currentConfig.ltv) { + return false; + } + + // New config must have liquidationThreshold >= current (higher threshold is better) + if (newConfig.liquidationThreshold < currentConfig.liquidationThreshold) { + return false; + } + + // New config must have liquidationBonus <= current (lower bonus means less penalty, which is better) + if (newConfig.liquidationBonus > currentConfig.liquidationBonus) { + return false; + } + + return true; + } + + // ============ Internal Functions ============ + + /// @dev Validates that an eMode category is valid for the adapter's collateral and debt assets + /// @param eModeId The eMode category ID to validate + /// @return True if the eMode is valid for the assets, false otherwise + function _isEModeValidForAssets(uint8 eModeId) internal view returns (bool) { + // Get reserve IDs for collateral and debt assets + uint16 collateralReserveId = pool.getReserveData(collateralAsset).id; + uint16 debtReserveId = pool.getReserveData(debtAsset).id; + + // Create bitmasks for checking if assets are in the eMode + uint128 collateralMask = uint128(1) << collateralReserveId; + uint128 debtMask = uint128(1) << debtReserveId; + + // Get eMode configuration + DataTypes.CollateralConfig memory config = pool.getEModeCategoryCollateralConfig(eModeId); + + // Check if this eMode exists (liquidationThreshold != 0) + if (config.liquidationThreshold == 0) { + return false; + } + + // Get the bitmaps for this eMode + uint128 collateralBitmap = pool.getEModeCategoryCollateralBitmap(eModeId); + uint128 borrowableBitmap = pool.getEModeCategoryBorrowableBitmap(eModeId); + + // Check if collateral asset is valid collateral in this eMode + if ((collateralBitmap & collateralMask) == 0) { + return false; + } + + // Check if debt asset is borrowable in this eMode + if ((borrowableBitmap & debtMask) == 0) { + return false; + } + + // Check ltvzero bitmap - if collateral is in ltvzero, it can't be used for borrowing power + uint128 ltvzeroBitmap = pool.getEModeCategoryLtvzeroBitmap(eModeId); + if ((ltvzeroBitmap & collateralMask) != 0) { + return false; + } + + return true; + } + + /// @dev Gets the default reserve configuration for the collateral asset as a CollateralConfig + /// @return config The collateral configuration from the reserve's default settings + function _getDefaultReserveConfig() internal view returns (DataTypes.CollateralConfig memory config) { + DataTypes.ReserveConfigurationMap memory reserveConfig = pool.getConfiguration(collateralAsset); + + config.ltv = uint16(ReserveConfiguration.getLtv(reserveConfig)); + config.liquidationThreshold = uint16(ReserveConfiguration.getLiquidationThreshold(reserveConfig)); + config.liquidationBonus = uint16(ReserveConfiguration.getLiquidationBonus(reserveConfig)); + + return config; + } +} diff --git a/src/lending/AaveLendingAdapterFactory.sol b/src/lending/AaveLendingAdapterFactory.sol new file mode 100644 index 00000000..198ee493 --- /dev/null +++ b/src/lending/AaveLendingAdapterFactory.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.26; + +// Dependency imports +import {Clones} from "openzeppelin-contracts/contracts/proxy/Clones.sol"; + +// Internal imports +import {IAaveLendingAdapter} from "src/interfaces/IAaveLendingAdapter.sol"; +import {IAaveLendingAdapterFactory} from "src/interfaces/IAaveLendingAdapterFactory.sol"; +import {AaveLendingAdapter} from "src/lending/AaveLendingAdapter.sol"; + +/** + * @dev The AaveLendingAdapterFactory is a factory contract for deploying ERC-1167 minimal proxies of the + * AaveLendingAdapter contract using OpenZeppelin's Clones library. + * + * @custom:contact security@seamlessprotocol.com + */ +contract AaveLendingAdapterFactory is IAaveLendingAdapterFactory { + using Clones for address; + + /// @inheritdoc IAaveLendingAdapterFactory + IAaveLendingAdapter public immutable lendingAdapterLogic; + + /// @param _lendingAdapterLogic Logic contract for deploying new AaveLendingAdapters. + constructor(IAaveLendingAdapter _lendingAdapterLogic) { + lendingAdapterLogic = _lendingAdapterLogic; + } + + /// @inheritdoc IAaveLendingAdapterFactory + function computeAddress(address sender, bytes32 baseSalt) external view returns (address) { + return Clones.predictDeterministicAddress(address(lendingAdapterLogic), salt(sender, baseSalt), address(this)); + } + + /// @inheritdoc IAaveLendingAdapterFactory + function deployAdapter( + address collateralAsset, + address debtAsset, + address authorizedCreator, + bytes32 baseSalt + ) public returns (IAaveLendingAdapter) { + IAaveLendingAdapter lendingAdapter = + IAaveLendingAdapter(address(lendingAdapterLogic).cloneDeterministic(salt(msg.sender, baseSalt))); + emit AaveLendingAdapterDeployed(lendingAdapter); + + AaveLendingAdapter(address(lendingAdapter)).initialize( + collateralAsset, debtAsset, authorizedCreator + ); + + return lendingAdapter; + } + + /// @notice Given the `sender` and `baseSalt`, return the salt that will be used for deployment. + /// @param sender The address of the sender of the `deployAdapter` call. + /// @param baseSalt The user-provided base salt. + function salt(address sender, bytes32 baseSalt) internal pure returns (bytes32) { + return keccak256(abi.encode(sender, baseSalt)); + } +} diff --git a/src/periphery/LeverageRouter.sol b/src/periphery/LeverageRouter.sol index 7922c7ea..a179da93 100644 --- a/src/periphery/LeverageRouter.sol +++ b/src/periphery/LeverageRouter.sol @@ -10,12 +10,11 @@ import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol // Internal imports import {ILendingAdapter} from "../interfaces/ILendingAdapter.sol"; -import {ILeverageManager} from "../interfaces/ILeverageManager.sol"; +import {ILeverageManager, ActionData} from "../interfaces/ILeverageManager.sol"; import {ILeverageToken} from "../interfaces/ILeverageToken.sol"; import {ILeverageRouter} from "../interfaces/periphery/ILeverageRouter.sol"; import {IVeloraAdapter} from "../interfaces/periphery/IVeloraAdapter.sol"; import {IMulticallExecutor} from "../interfaces/periphery/IMulticallExecutor.sol"; -import {ActionData} from "../types/DataTypes.sol"; /** * @dev The LeverageRouter contract is an immutable periphery contract that facilitates the use of flash loans and swaps diff --git a/src/periphery/LeverageRouterV2.sol b/src/periphery/LeverageRouterV2.sol new file mode 100644 index 00000000..e9eb55e0 --- /dev/null +++ b/src/periphery/LeverageRouterV2.sol @@ -0,0 +1,460 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +// Dependency imports +import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; +import {IMorpho} from "@morpho-blue/interfaces/IMorpho.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {ReentrancyGuardTransient} from "@openzeppelin/contracts/utils/ReentrancyGuardTransient.sol"; +import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + +// Aave v3 imports +import {IPool} from "@aave-v3-origin/contracts/interfaces/IPool.sol"; +import {IFlashLoanSimpleReceiver} from + "@aave-v3-origin/contracts/misc/flashloan/interfaces/IFlashLoanSimpleReceiver.sol"; +import {IPoolAddressesProvider} from "@aave-v3-origin/contracts/interfaces/IPoolAddressesProvider.sol"; + +// Internal imports +import {ILendingAdapter} from "../interfaces/ILendingAdapter.sol"; +import {ILeverageManager, ActionData} from "../interfaces/ILeverageManager.sol"; +import {ILeverageToken} from "../interfaces/ILeverageToken.sol"; +import {ILeverageRouterV2} from "../interfaces/periphery/ILeverageRouterV2.sol"; +import {IVeloraAdapter} from "../interfaces/periphery/IVeloraAdapter.sol"; +import {IMulticallExecutor} from "../interfaces/periphery/IMulticallExecutor.sol"; + +/** + * @title LeverageRouterV2 + * @dev The LeverageRouterV2 contract is an immutable periphery contract that facilitates the use of flash loans and swaps + * to deposit and redeem equity from LeverageTokens. Supports both Morpho and Aave v3 flash loans. + * + * The high-level deposit flow is as follows: + * 1. The sender calls `deposit` with the amount of collateral from the sender to deposit, the amount of debt to flash loan + * (which will be swapped to collateral), the minimum amount of shares to receive, and the calldata to execute for + * the swap of the flash loaned debt to collateral + * 2. The LeverageRouter will flash loan the debt asset amount and execute the calldata to swap it to collateral + * 3. The LeverageRouter will use the collateral from the swapped debt and the collateral from the sender for the deposit + * into the LeverageToken, receiving LeverageToken shares and debt in return + * 4. The LeverageRouter will use the debt received from the deposit to repay the flash loan + * 5. The LeverageRouter will transfer the LeverageToken shares and any surplus debt assets to the receiver + * + * The high-level redeem flow is the same as the deposit flow, but in reverse. + * + * Note: The flash loan source (Morpho or Aave) does not need to match the underlying lending adapter type + * of the LeverageToken. For example, you can use Aave flash loans to deposit/redeem from a LeverageToken + * that uses a MorphoLendingAdapter, and vice versa. The flash loan is simply a source of temporary liquidity. + * + * @custom:contact security@seamlessprotocol.com + */ +contract LeverageRouterV2 is ILeverageRouterV2, IFlashLoanSimpleReceiver, ReentrancyGuardTransient { + /// @inheritdoc ILeverageRouterV2 + ILeverageManager public immutable leverageManager; + + /// @inheritdoc ILeverageRouterV2 + IMorpho public immutable morpho; + + /// @inheritdoc IFlashLoanSimpleReceiver + IPoolAddressesProvider public immutable ADDRESSES_PROVIDER; + + /// @notice The Aave v3 Pool contract used for flash loans + /// @dev Required by IFlashLoanSimpleReceiver interface + /// @inheritdoc IFlashLoanSimpleReceiver + IPool public immutable POOL; + + /// @notice Creates a new LeverageRouterV2 + /// @param _leverageManager The LeverageManager contract + /// @param _morpho The Morpho core protocol contract + /// @param _aavePool The Aave v3 Pool contract + constructor(ILeverageManager _leverageManager, IMorpho _morpho, IPool _aavePool) { + leverageManager = _leverageManager; + morpho = _morpho; + POOL = _aavePool; + ADDRESSES_PROVIDER = _aavePool.ADDRESSES_PROVIDER(); + } + + /// @inheritdoc ILeverageRouterV2 + function convertEquityToCollateral(ILeverageToken token, uint256 equityInCollateralAsset) + public + view + returns (uint256 collateral) + { + uint256 collateralRatio = leverageManager.getLeverageTokenState(token).collateralRatio; + ILendingAdapter lendingAdapter = leverageManager.getLeverageTokenLendingAdapter(token); + uint256 baseRatio = leverageManager.BASE_RATIO(); + + if (lendingAdapter.getCollateral() == 0 && lendingAdapter.getDebt() == 0) { + uint256 initialCollateralRatio = leverageManager.getLeverageTokenInitialCollateralRatio(token); + collateral = Math.mulDiv( + equityInCollateralAsset, initialCollateralRatio, initialCollateralRatio - baseRatio, Math.Rounding.Ceil + ); + } else if (collateralRatio == type(uint256).max) { + collateral = equityInCollateralAsset; + } else { + collateral = + Math.mulDiv(equityInCollateralAsset, collateralRatio, collateralRatio - baseRatio, Math.Rounding.Ceil); + } + + return collateral; + } + + /// @inheritdoc ILeverageRouterV2 + function previewDeposit(ILeverageToken token, uint256 collateralFromSender) + external + view + returns (ActionData memory previewData) + { + uint256 collateral = convertEquityToCollateral(token, collateralFromSender); + return leverageManager.previewDeposit(token, collateral); + } + + // ==================== Public Entry Points ==================== + + /// @inheritdoc ILeverageRouterV2 + function deposit( + ILeverageToken leverageToken, + uint256 collateralFromSender, + uint256 flashLoanAmount, + uint256 minShares, + address receiver, + IMulticallExecutor multicallExecutor, + IMulticallExecutor.Call[] calldata swapCalls, + FlashLoanSource flashLoanSource + ) external nonReentrant { + bytes memory depositData = abi.encode( + DepositParams({ + sender: msg.sender, + receiver: receiver, + leverageToken: leverageToken, + collateralFromSender: collateralFromSender, + minShares: minShares, + multicallExecutor: multicallExecutor, + swapCalls: swapCalls + }) + ); + + address debtAsset = address(leverageManager.getLeverageTokenDebtAsset(leverageToken)); + + if (flashLoanSource == FlashLoanSource.Morpho) { + morpho.flashLoan( + debtAsset, + flashLoanAmount, + abi.encode(FlashLoanCallbackData({action: LeverageRouterAction.Deposit, data: depositData})) + ); + } else { + POOL.flashLoanSimple( + address(this), + debtAsset, + flashLoanAmount, + abi.encode(FlashLoanCallbackData({action: LeverageRouterAction.Deposit, data: depositData})), + 0 // referralCode + ); + } + } + + /// @inheritdoc ILeverageRouterV2 + function redeem( + ILeverageToken token, + uint256 shares, + uint256 minCollateralForReceiver, + address receiver, + IMulticallExecutor multicallExecutor, + IMulticallExecutor.Call[] calldata swapCalls, + FlashLoanSource flashLoanSource + ) external nonReentrant { + uint256 debtRequired = leverageManager.previewRedeem(token, shares).debt; + + bytes memory redeemData = abi.encode( + RedeemParams({ + sender: msg.sender, + receiver: receiver, + leverageToken: token, + shares: shares, + minCollateralForReceiver: minCollateralForReceiver, + multicallExecutor: multicallExecutor, + swapCalls: swapCalls + }) + ); + + address debtAsset = address(leverageManager.getLeverageTokenDebtAsset(token)); + + if (flashLoanSource == FlashLoanSource.Morpho) { + morpho.flashLoan( + debtAsset, + debtRequired, + abi.encode(FlashLoanCallbackData({action: LeverageRouterAction.Redeem, data: redeemData})) + ); + } else { + POOL.flashLoanSimple( + address(this), + debtAsset, + debtRequired, + abi.encode(FlashLoanCallbackData({action: LeverageRouterAction.Redeem, data: redeemData})), + 0 // referralCode + ); + } + } + + /// @inheritdoc ILeverageRouterV2 + function redeemWithVelora( + ILeverageToken token, + uint256 shares, + uint256 minCollateralForReceiver, + address receiver, + IVeloraAdapter veloraAdapter, + address augustus, + IVeloraAdapter.Offsets calldata offsets, + bytes calldata swapData, + FlashLoanSource flashLoanSource + ) external nonReentrant { + uint256 debtRequired = leverageManager.previewRedeem(token, shares).debt; + + bytes memory redeemData = abi.encode( + RedeemWithVeloraParams({ + sender: msg.sender, + receiver: receiver, + leverageToken: token, + shares: shares, + minCollateralForReceiver: minCollateralForReceiver, + veloraAdapter: veloraAdapter, + augustus: augustus, + offsets: offsets, + swapData: swapData + }) + ); + + address debtAsset = address(leverageManager.getLeverageTokenDebtAsset(token)); + + if (flashLoanSource == FlashLoanSource.Morpho) { + morpho.flashLoan( + debtAsset, + debtRequired, + abi.encode(FlashLoanCallbackData({action: LeverageRouterAction.RedeemWithVelora, data: redeemData})) + ); + } else { + POOL.flashLoanSimple( + address(this), + debtAsset, + debtRequired, + abi.encode(FlashLoanCallbackData({action: LeverageRouterAction.RedeemWithVelora, data: redeemData})), + 0 // referralCode + ); + } + } + + // ==================== Flash Loan Callbacks ==================== + + /// @notice Morpho flash loan callback function + /// @param loanAmount Amount of asset flash loaned + /// @param data Encoded data passed to `morpho.flashLoan` + function onMorphoFlashLoan(uint256 loanAmount, bytes calldata data) external { + if (msg.sender != address(morpho)) revert Unauthorized(); + + FlashLoanCallbackData memory callbackData = abi.decode(data, (FlashLoanCallbackData)); + + // Morpho has no premium, so totalRepayAmount equals loanAmount + _handleFlashLoanCallback(callbackData.action, callbackData.data, loanAmount, loanAmount, address(morpho)); + } + + /// @inheritdoc IFlashLoanSimpleReceiver + /// @notice Aave flash loan callback function + function executeOperation(address, uint256 amount, uint256 premium, address initiator, bytes calldata params) + external + returns (bool) + { + if (msg.sender != address(POOL)) revert Unauthorized(); + if (initiator != address(this)) revert Unauthorized(); + + FlashLoanCallbackData memory callbackData = abi.decode(params, (FlashLoanCallbackData)); + + uint256 totalRepayAmount = amount + premium; + + _handleFlashLoanCallback(callbackData.action, callbackData.data, amount, totalRepayAmount, address(POOL)); + + return true; + } + + // ==================== Internal Flash Loan Handler ==================== + + /// @notice Routes flash loan callback to the appropriate handler + /// @param action The action type (Deposit, Redeem, RedeemWithVelora) + /// @param data Encoded parameters for the action + /// @param loanAmount Amount flash loaned (excluding premium) + /// @param totalRepayAmount Total amount to repay (loan + premium for Aave, loan for Morpho) + /// @param flashLoanProvider Address to approve for repayment + function _handleFlashLoanCallback( + LeverageRouterAction action, + bytes memory data, + uint256 loanAmount, + uint256 totalRepayAmount, + address flashLoanProvider + ) internal { + if (action == LeverageRouterAction.Deposit) { + DepositParams memory params = abi.decode(data, (DepositParams)); + _executeDeposit(params, loanAmount, totalRepayAmount, flashLoanProvider); + } else if (action == LeverageRouterAction.Redeem) { + RedeemParams memory params = abi.decode(data, (RedeemParams)); + _executeRedeem(params, loanAmount, totalRepayAmount, flashLoanProvider); + } else if (action == LeverageRouterAction.RedeemWithVelora) { + RedeemWithVeloraParams memory params = abi.decode(data, (RedeemWithVeloraParams)); + _executeRedeemWithVelora(params, loanAmount, totalRepayAmount, flashLoanProvider); + } + } + + // ==================== Internal Core Logic ==================== + + /// @notice Executes the deposit into a LeverageToken and repays the flash loan + /// @param params Params for the deposit into a LeverageToken + /// @param loanAmount Amount of debt asset flash loaned (excluding premium) + /// @param totalRepayAmount Total amount to repay (loan + premium for Aave, loan for Morpho) + /// @param flashLoanProvider Address to approve for repayment + function _executeDeposit( + DepositParams memory params, + uint256 loanAmount, + uint256 totalRepayAmount, + address flashLoanProvider + ) internal { + IERC20 collateralAsset = leverageManager.getLeverageTokenCollateralAsset(params.leverageToken); + IERC20 debtAsset = leverageManager.getLeverageTokenDebtAsset(params.leverageToken); + + // Transfer the collateral from the sender for the deposit + // slither-disable-next-line arbitrary-send-erc20 + SafeERC20.safeTransferFrom(collateralAsset, params.sender, address(this), params.collateralFromSender); + + // Swap the debt asset received from the flash loan to the collateral asset, used to deposit into the LeverageToken + SafeERC20.safeTransfer(debtAsset, address(params.multicallExecutor), loanAmount); + + IERC20[] memory tokens = new IERC20[](2); + tokens[0] = collateralAsset; + tokens[1] = debtAsset; + params.multicallExecutor.multicallAndSweep(params.swapCalls, tokens); + + // The sum of the collateral from the swap and the collateral from the sender + uint256 totalCollateral = IERC20(collateralAsset).balanceOf(address(this)); + + // Use the collateral from the swap and the collateral from the sender for the deposit into the LeverageToken + SafeERC20.forceApprove(collateralAsset, address(leverageManager), totalCollateral); + + uint256 shares = leverageManager.deposit(params.leverageToken, totalCollateral, params.minShares).shares; + + // Transfer any surplus debt assets to the receiver (after accounting for flash loan repayment) + uint256 debtBalance = debtAsset.balanceOf(address(this)); + if (totalRepayAmount < debtBalance) { + SafeERC20.safeTransfer(debtAsset, params.receiver, debtBalance - totalRepayAmount); + } + + // Transfer shares received from the deposit to the receiver + SafeERC20.safeTransfer(params.leverageToken, params.receiver, shares); + + // Approve flash loan provider to transfer debt assets to repay the flash loan + SafeERC20.forceApprove(debtAsset, flashLoanProvider, totalRepayAmount); + } + + /// @notice Executes the redeem from a LeverageToken and repays the flash loan + /// @param params Params for the redeem from a LeverageToken + /// @param loanAmount Amount of debt asset flash loaned (excluding premium) + /// @param totalRepayAmount Total amount to repay (loan + premium for Aave, loan for Morpho) + /// @param flashLoanProvider Address to approve for repayment + function _executeRedeem( + RedeemParams memory params, + uint256 loanAmount, + uint256 totalRepayAmount, + address flashLoanProvider + ) internal { + IERC20 collateralAsset = leverageManager.getLeverageTokenCollateralAsset(params.leverageToken); + IERC20 debtAsset = leverageManager.getLeverageTokenDebtAsset(params.leverageToken); + + // Transfer the shares from the sender + // slither-disable-next-line arbitrary-send-erc20 + SafeERC20.safeTransferFrom(params.leverageToken, params.sender, address(this), params.shares); + + // Use the debt from the flash loan to redeem the shares from the sender + SafeERC20.forceApprove(debtAsset, address(leverageManager), loanAmount); + // slither-disable-next-line unused-return + uint256 collateralWithdrawn = + leverageManager.redeem(params.leverageToken, params.shares, params.minCollateralForReceiver).collateral; + + // Swap the collateral asset received from the redeem to the debt asset, used to repay the flash loan. + SafeERC20.safeTransfer(collateralAsset, address(params.multicallExecutor), collateralWithdrawn); + + IERC20[] memory tokens = new IERC20[](2); + tokens[0] = collateralAsset; + tokens[1] = debtAsset; + params.multicallExecutor.multicallAndSweep(params.swapCalls, tokens); + + // The remaining collateral after the arbitrary swap calls is available for the receiver + uint256 collateralForReceiver = collateralAsset.balanceOf(address(this)); + + // The remaining debt after the arbitrary swap calls is available for the receiver, minus + // the amount of debt for repaying the flash loan (including premium if Aave) + uint256 debtBalance = debtAsset.balanceOf(address(this)); + uint256 debtForReceiver = debtBalance > totalRepayAmount ? debtBalance - totalRepayAmount : 0; + + // Check slippage on collateral the receiver gets + if (collateralForReceiver < params.minCollateralForReceiver) { + revert CollateralSlippageTooHigh(collateralForReceiver, params.minCollateralForReceiver); + } + + // Transfer remaining collateral to the receiver + if (collateralForReceiver > 0) { + SafeERC20.safeTransfer(collateralAsset, params.receiver, collateralForReceiver); + } + + // Transfer any remaining debt assets to the receiver + if (debtForReceiver > 0) { + SafeERC20.safeTransfer(debtAsset, params.receiver, debtForReceiver); + } + + // Approve flash loan provider to spend the debt asset to repay the flash loan + SafeERC20.forceApprove(debtAsset, flashLoanProvider, totalRepayAmount); + } + + /// @notice Executes the redeem from a LeverageToken using Velora and repays the flash loan + /// @param params Params for the redeem from a LeverageToken using Velora + /// @param loanAmount Amount of debt asset flash loaned (excluding premium) + /// @param totalRepayAmount Total amount to repay (loan + premium for Aave, loan for Morpho) + /// @param flashLoanProvider Address to approve for repayment + function _executeRedeemWithVelora( + RedeemWithVeloraParams memory params, + uint256 loanAmount, + uint256 totalRepayAmount, + address flashLoanProvider + ) internal { + IERC20 collateralAsset = leverageManager.getLeverageTokenCollateralAsset(params.leverageToken); + IERC20 debtAsset = leverageManager.getLeverageTokenDebtAsset(params.leverageToken); + + // Transfer the shares from the sender + // slither-disable-next-line arbitrary-send-erc20 + SafeERC20.safeTransferFrom(params.leverageToken, params.sender, address(this), params.shares); + + // Use the debt from the flash loan to redeem the shares from the sender + SafeERC20.forceApprove(debtAsset, address(leverageManager), loanAmount); + uint256 collateralWithdrawn = + leverageManager.redeem(params.leverageToken, params.shares, params.minCollateralForReceiver).collateral; + + // Use the VeloraAdapter to swap the collateral asset received from the redeem to the debt asset. + // For Aave, totalRepayAmount includes premium; for Morpho it equals loanAmount + // slither-disable-next-line arbitrary-send-erc20 + SafeERC20.safeTransfer(collateralAsset, address(params.veloraAdapter), collateralWithdrawn); + uint256 collateralForReceiver = params.veloraAdapter.buy( + params.augustus, + params.swapData, + address(collateralAsset), + address(debtAsset), + totalRepayAmount, + params.offsets, + address(this) + ); + + // Check slippage + if (collateralForReceiver < params.minCollateralForReceiver) { + revert CollateralSlippageTooHigh(collateralForReceiver, params.minCollateralForReceiver); + } + + // Transfer remaining collateral to the receiver + if (collateralForReceiver > 0) { + SafeERC20.safeTransfer(collateralAsset, params.receiver, collateralForReceiver); + } + + // Approve flash loan provider to spend the debt asset to repay the flash loan + SafeERC20.forceApprove(debtAsset, flashLoanProvider, totalRepayAmount); + } +} diff --git a/src/periphery/LeverageTokenDeploymentBatcherV2.sol b/src/periphery/LeverageTokenDeploymentBatcherV2.sol new file mode 100644 index 00000000..fc9b489b --- /dev/null +++ b/src/periphery/LeverageTokenDeploymentBatcherV2.sol @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +// Dependency imports +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import {UnsafeUpgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol"; + +// Internal imports +import {ILendingAdapter} from "../interfaces/ILendingAdapter.sol"; +import {ILeverageManager} from "../interfaces/ILeverageManager.sol"; +import {ILeverageToken} from "../interfaces/ILeverageToken.sol"; +import {ILeverageTokenDeploymentBatcherV2} from "../interfaces/periphery/ILeverageTokenDeploymentBatcherV2.sol"; +import {IMorphoLendingAdapter} from "../interfaces/IMorphoLendingAdapter.sol"; +import {IMorphoLendingAdapterFactory} from "../interfaces/IMorphoLendingAdapterFactory.sol"; +import {IAaveLendingAdapter} from "../interfaces/IAaveLendingAdapter.sol"; +import {IAaveLendingAdapterFactory} from "../interfaces/IAaveLendingAdapterFactory.sol"; +import {IRebalanceAdapter} from "../interfaces/IRebalanceAdapter.sol"; +import {RebalanceAdapter} from "../rebalance/RebalanceAdapter.sol"; +import {ActionData, LeverageTokenConfig} from "../types/DataTypes.sol"; + +/** + * @title LeverageTokenDeploymentBatcherV2 + * @dev The LeverageTokenDeploymentBatcherV2 is a periphery contract that can be used to batch deployment of a LeverageToken + * with a deposit of collateral into the LeverageToken. This is highly recommended to avoid inflation attacks from front-running + * the initial deposit into the LeverageToken. + * + * @custom:contact security@seamlessprotocol.com + */ +contract LeverageTokenDeploymentBatcherV2 is ILeverageTokenDeploymentBatcherV2 { + /// @inheritdoc ILeverageTokenDeploymentBatcherV2 + ILeverageManager public immutable leverageManager; + + /// @inheritdoc ILeverageTokenDeploymentBatcherV2 + IMorphoLendingAdapterFactory public immutable morphoLendingAdapterFactory; + + /// @inheritdoc ILeverageTokenDeploymentBatcherV2 + IAaveLendingAdapterFactory public immutable aaveLendingAdapterFactory; + + /// @notice Constructor + /// @param _leverageManager The LeverageManager contract + /// @param _morphoLendingAdapterFactory The MorphoLendingAdapterFactory contract + /// @param _aaveLendingAdapterFactory The AaveLendingAdapterFactory contract + constructor( + ILeverageManager _leverageManager, + IMorphoLendingAdapterFactory _morphoLendingAdapterFactory, + IAaveLendingAdapterFactory _aaveLendingAdapterFactory + ) { + leverageManager = _leverageManager; + morphoLendingAdapterFactory = _morphoLendingAdapterFactory; + aaveLendingAdapterFactory = _aaveLendingAdapterFactory; + } + + /// @inheritdoc ILeverageTokenDeploymentBatcherV2 + function deployLeverageTokenAndDepositWithMorpho( + LeverageTokenDeploymentParams memory leverageTokenDeploymentParams, + MorphoLendingAdapterDeploymentParams memory lendingAdapterDeploymentParams, + RebalanceAdapterDeploymentParams memory rebalanceAdapterDeploymentParams, + uint256 collateral, + uint256 minShares + ) public returns (ILeverageToken, ActionData memory) { + IMorphoLendingAdapter lendingAdapter = morphoLendingAdapterFactory.deployAdapter( + lendingAdapterDeploymentParams.morphoMarketId, + address(this), + salt(msg.sender, lendingAdapterDeploymentParams.baseSalt) + ); + + return _deployLeverageTokenAndDeposit( + leverageTokenDeploymentParams, + ILendingAdapter(address(lendingAdapter)), + rebalanceAdapterDeploymentParams, + collateral, + minShares + ); + } + + /// @inheritdoc ILeverageTokenDeploymentBatcherV2 + function deployLeverageTokenAndDepositWithAave( + LeverageTokenDeploymentParams memory leverageTokenDeploymentParams, + AaveLendingAdapterDeploymentParams memory lendingAdapterDeploymentParams, + RebalanceAdapterDeploymentParams memory rebalanceAdapterDeploymentParams, + uint256 collateral, + uint256 minShares + ) public returns (ILeverageToken, ActionData memory) { + IAaveLendingAdapter lendingAdapter = aaveLendingAdapterFactory.deployAdapter( + lendingAdapterDeploymentParams.collateralAsset, + lendingAdapterDeploymentParams.debtAsset, + address(this), + salt(msg.sender, lendingAdapterDeploymentParams.baseSalt) + ); + + return _deployLeverageTokenAndDeposit( + leverageTokenDeploymentParams, + ILendingAdapter(address(lendingAdapter)), + rebalanceAdapterDeploymentParams, + collateral, + minShares + ); + } + + /// @notice Internal function to deploy a leverage token with a given lending adapter and deposit collateral + /// @param leverageTokenDeploymentParams The parameters for the leverage token deployment + /// @param lendingAdapter The lending adapter to use for the leverage token + /// @param rebalanceAdapterDeploymentParams The parameters for the rebalance adapter deployment + /// @param collateral The collateral to deposit into the leverage token + /// @param minShares The minimum number of shares to receive from the deposit + /// @return The leverage token and the action data for the deposit + function _deployLeverageTokenAndDeposit( + LeverageTokenDeploymentParams memory leverageTokenDeploymentParams, + ILendingAdapter lendingAdapter, + RebalanceAdapterDeploymentParams memory rebalanceAdapterDeploymentParams, + uint256 collateral, + uint256 minShares + ) internal returns (ILeverageToken, ActionData memory) { + RebalanceAdapter.RebalanceAdapterInitParams memory rebalanceAdapterInitParams = RebalanceAdapter + .RebalanceAdapterInitParams({ + owner: rebalanceAdapterDeploymentParams.owner, + authorizedCreator: address(this), + leverageManager: leverageManager, + minCollateralRatio: rebalanceAdapterDeploymentParams.minCollateralRatio, + targetCollateralRatio: rebalanceAdapterDeploymentParams.targetCollateralRatio, + maxCollateralRatio: rebalanceAdapterDeploymentParams.maxCollateralRatio, + auctionDuration: rebalanceAdapterDeploymentParams.auctionDuration, + initialPriceMultiplier: rebalanceAdapterDeploymentParams.initialPriceMultiplier, + minPriceMultiplier: rebalanceAdapterDeploymentParams.minPriceMultiplier, + preLiquidationCollateralRatioThreshold: rebalanceAdapterDeploymentParams.preLiquidationCollateralRatioThreshold, + rebalanceReward: rebalanceAdapterDeploymentParams.rebalanceReward + }); + + IRebalanceAdapter rebalanceAdapter = IRebalanceAdapter( + UnsafeUpgrades.deployUUPSProxy( + rebalanceAdapterDeploymentParams.implementation, + abi.encodeCall(RebalanceAdapter.initialize, (rebalanceAdapterInitParams)) + ) + ); + + LeverageTokenConfig memory leverageTokenConfig = LeverageTokenConfig({ + lendingAdapter: lendingAdapter, + rebalanceAdapter: IRebalanceAdapter(rebalanceAdapter), + mintTokenFee: leverageTokenDeploymentParams.mintTokenFee, + redeemTokenFee: leverageTokenDeploymentParams.redeemTokenFee + }); + + ILeverageToken leverageToken = leverageManager.createNewLeverageToken( + leverageTokenConfig, + leverageTokenDeploymentParams.leverageTokenName, + leverageTokenDeploymentParams.leverageTokenSymbol + ); + + IERC20 collateralAsset = leverageTokenConfig.lendingAdapter.getCollateralAsset(); + SafeERC20.safeTransferFrom(collateralAsset, msg.sender, address(this), collateral); + + SafeERC20.forceApprove(collateralAsset, address(leverageManager), collateral); + ActionData memory depositData = leverageManager.deposit(leverageToken, collateral, minShares); + + // Transfer shares and debt received from the deposit to the sender + SafeERC20.safeTransfer(leverageToken, msg.sender, depositData.shares); + SafeERC20.safeTransfer(leverageTokenConfig.lendingAdapter.getDebtAsset(), msg.sender, depositData.debt); + + return (leverageToken, depositData); + } + + /// @notice Given the `sender` and `baseSalt`, return the salt that will be used for deployment. + /// @param sender The address of the sender. + /// @param baseSalt The user-provided base salt. + function salt(address sender, bytes32 baseSalt) internal pure returns (bytes32) { + return keccak256(abi.encode(sender, baseSalt)); + } +} diff --git a/test/unit/LendingAdapter/morpho/AddCollateral.t.sol b/test/unit/LendingAdapter/MorphoLendingAdapter/AddCollateral.t.sol similarity index 100% rename from test/unit/LendingAdapter/morpho/AddCollateral.t.sol rename to test/unit/LendingAdapter/MorphoLendingAdapter/AddCollateral.t.sol diff --git a/test/unit/LendingAdapter/morpho/Borrow.t.sol b/test/unit/LendingAdapter/MorphoLendingAdapter/Borrow.t.sol similarity index 100% rename from test/unit/LendingAdapter/morpho/Borrow.t.sol rename to test/unit/LendingAdapter/MorphoLendingAdapter/Borrow.t.sol diff --git a/test/unit/LendingAdapter/morpho/ConvertCollateralToDebtAsset.t.sol b/test/unit/LendingAdapter/MorphoLendingAdapter/ConvertCollateralToDebtAsset.t.sol similarity index 100% rename from test/unit/LendingAdapter/morpho/ConvertCollateralToDebtAsset.t.sol rename to test/unit/LendingAdapter/MorphoLendingAdapter/ConvertCollateralToDebtAsset.t.sol diff --git a/test/unit/LendingAdapter/morpho/ConvertDebtToCollateralAsset.t.sol b/test/unit/LendingAdapter/MorphoLendingAdapter/ConvertDebtToCollateralAsset.t.sol similarity index 100% rename from test/unit/LendingAdapter/morpho/ConvertDebtToCollateralAsset.t.sol rename to test/unit/LendingAdapter/MorphoLendingAdapter/ConvertDebtToCollateralAsset.t.sol diff --git a/test/unit/LendingAdapter/morpho/GetCollateral.t.sol b/test/unit/LendingAdapter/MorphoLendingAdapter/GetCollateral.t.sol similarity index 100% rename from test/unit/LendingAdapter/morpho/GetCollateral.t.sol rename to test/unit/LendingAdapter/MorphoLendingAdapter/GetCollateral.t.sol diff --git a/test/unit/LendingAdapter/morpho/GetCollateralInDebtAsset.t.sol b/test/unit/LendingAdapter/MorphoLendingAdapter/GetCollateralInDebtAsset.t.sol similarity index 100% rename from test/unit/LendingAdapter/morpho/GetCollateralInDebtAsset.t.sol rename to test/unit/LendingAdapter/MorphoLendingAdapter/GetCollateralInDebtAsset.t.sol diff --git a/test/unit/LendingAdapter/morpho/GetDebt.t.sol b/test/unit/LendingAdapter/MorphoLendingAdapter/GetDebt.t.sol similarity index 100% rename from test/unit/LendingAdapter/morpho/GetDebt.t.sol rename to test/unit/LendingAdapter/MorphoLendingAdapter/GetDebt.t.sol diff --git a/test/unit/LendingAdapter/morpho/GetEquityInCollateralAsset.t.sol b/test/unit/LendingAdapter/MorphoLendingAdapter/GetEquityInCollateralAsset.t.sol similarity index 100% rename from test/unit/LendingAdapter/morpho/GetEquityInCollateralAsset.t.sol rename to test/unit/LendingAdapter/MorphoLendingAdapter/GetEquityInCollateralAsset.t.sol diff --git a/test/unit/LendingAdapter/morpho/GetEquityInDebtAsset.t.sol b/test/unit/LendingAdapter/MorphoLendingAdapter/GetEquityInDebtAsset.t.sol similarity index 100% rename from test/unit/LendingAdapter/morpho/GetEquityInDebtAsset.t.sol rename to test/unit/LendingAdapter/MorphoLendingAdapter/GetEquityInDebtAsset.t.sol diff --git a/test/unit/LendingAdapter/morpho/GetLiquidationPenalty.t.sol b/test/unit/LendingAdapter/MorphoLendingAdapter/GetLiquidationPenalty.t.sol similarity index 100% rename from test/unit/LendingAdapter/morpho/GetLiquidationPenalty.t.sol rename to test/unit/LendingAdapter/MorphoLendingAdapter/GetLiquidationPenalty.t.sol diff --git a/test/unit/LendingAdapter/morpho/Initialize.t.sol b/test/unit/LendingAdapter/MorphoLendingAdapter/Initialize.t.sol similarity index 100% rename from test/unit/LendingAdapter/morpho/Initialize.t.sol rename to test/unit/LendingAdapter/MorphoLendingAdapter/Initialize.t.sol diff --git a/test/unit/LendingAdapter/morpho/MorphoLendingAdapter.t.sol b/test/unit/LendingAdapter/MorphoLendingAdapter/MorphoLendingAdapter.t.sol similarity index 100% rename from test/unit/LendingAdapter/morpho/MorphoLendingAdapter.t.sol rename to test/unit/LendingAdapter/MorphoLendingAdapter/MorphoLendingAdapter.t.sol diff --git a/test/unit/LendingAdapter/morpho/PostLeverageTokenCreation.t.sol b/test/unit/LendingAdapter/MorphoLendingAdapter/PostLeverageTokenCreation.t.sol similarity index 100% rename from test/unit/LendingAdapter/morpho/PostLeverageTokenCreation.t.sol rename to test/unit/LendingAdapter/MorphoLendingAdapter/PostLeverageTokenCreation.t.sol diff --git a/test/unit/LendingAdapter/morpho/RemoveCollateral.t.sol b/test/unit/LendingAdapter/MorphoLendingAdapter/RemoveCollateral.t.sol similarity index 100% rename from test/unit/LendingAdapter/morpho/RemoveCollateral.t.sol rename to test/unit/LendingAdapter/MorphoLendingAdapter/RemoveCollateral.t.sol diff --git a/test/unit/LendingAdapter/morpho/Repay.t.sol b/test/unit/LendingAdapter/MorphoLendingAdapter/Repay.t.sol similarity index 100% rename from test/unit/LendingAdapter/morpho/Repay.t.sol rename to test/unit/LendingAdapter/MorphoLendingAdapter/Repay.t.sol