From 7cd6ed66e09558b69ca5d22fdd9afeeecfd3eccf Mon Sep 17 00:00:00 2001 From: Priyam Anand Date: Wed, 29 Oct 2025 17:41:45 +0530 Subject: [PATCH 01/21] chore: updated migration tests for btc vault --- script/migration.s.sol | 6 +- test/core/TestBase.sol | 36 ++- test/core/integration/IntegrationBase.sol | 42 ++- test/core/integration/Migration.t.sol | 304 +++++++++++++++++----- test/core/unit/DepositManager.t.sol | 2 +- test/core/unit/Superloop.t.sol | 2 +- test/core/unit/WithdrawManager.t.sol | 2 +- 7 files changed, 304 insertions(+), 90 deletions(-) diff --git a/script/migration.s.sol b/script/migration.s.sol index 7e42f3d..948e900 100644 --- a/script/migration.s.sol +++ b/script/migration.s.sol @@ -111,11 +111,7 @@ contract Migration is Script { } function _preMigrationSetup() internal { - ISuperloop(oldVault).setVaultAdmin(address(migrationHelper)); - // ISuperloop(oldVault).setPrivilegedAddress( - // address(migrationHelper), - // true - // ); + ISuperloop(oldVault).setPrivilegedAddress(address(migrationHelper), true); ISuperloop(newVault).setRegisteredModule(REPAY_MODULE, true); ISuperloop(newVault).setRegisteredModule(WITHDRAW_MODULE, true); diff --git a/test/core/TestBase.sol b/test/core/TestBase.sol index 0f56ef9..8797a83 100644 --- a/test/core/TestBase.sol +++ b/test/core/TestBase.sol @@ -34,6 +34,8 @@ import {AaveV3PreliquidationFallbackHandler} from "../../src/modules/fallback/Aa contract TestBase is Test { address public constant ST_XTZ = 0x01F07f4d78d47A64F4C3B2b65f513f15Be6E1854; address public constant XTZ = 0xc9B53AB2679f573e480d01e0f49e2B5CFB7a3EAb; + address public constant LBTC = 0xecAc9C5F704e954931349Da37F60E39f515c11c1; + address public constant WBTC = 0xbFc94CD2B1E55999Cfc7347a9313e88702B83d0F; address public constant AAVE_V3_POOL_ADDRESSES_PROVIDER = 0x5ccF60c7E10547c5389E9cBFf543E5D0Db9F4feC; address public constant AAVE_V3_POOL_DATA_PROVIDER = 0x99e8269dDD5c7Af0F1B3973A591b47E8E001BCac; address public constant AAVE_V3_PRICE_ORACLE = 0xeCF313dE38aA85EF618D06D1A602bAa917D62525; @@ -45,7 +47,6 @@ contract TestBase is Test { address public constant USDT = 0x2C03058C8AFC06713be23e58D2febC8337dbfE6A; address public constant USDC = 0x796Ea11Fa2dD751eD01b53C372fFDB4AAa8f00F9; address public constant WXTZ = 0xc9B53AB2679f573e480d01e0f49e2B5CFB7a3EAb; - address public constant WBTC = 0xbFc94CD2B1E55999Cfc7347a9313e88702B83d0F; address public constant ROUTER = 0xbfe9C246A5EdB4F021C8910155EC93e7CfDaB7a0; address public constant USDC_WHALE = 0xd03bfdF9B26DB1e6764724d914d7c3d18106a9Fb; address public constant POOL_CONFIGURATOR = 0x30F6880Bb1cF780a49eB4Ef64E64585780AAe060; @@ -81,9 +82,14 @@ contract TestBase is Test { WithdrawManager public withdrawManager; UnwrapModule public unwrapModule; WrapModule public wrapModule; - DepositManager public depositManager; + Superloop public superloopBtc; + DepositManager public depositManagerBtc; + WithdrawManager public withdrawManagerBtc; + UniversalAccountant public accountantBtc; + AccountantAaveV3 public accountantAaveV3Btc; + address public mockModule; AaveV3EmodeModule public emodeModule; IPoolDataProvider public poolDataProvider; @@ -156,12 +162,10 @@ contract TestBase is Test { vm.label(address(withdrawManagerCallbackHandler), "withdrawManagerCallbackHandler"); } - function _deployAccountant(address vault) internal { - address[] memory lendAssets = new address[](1); - lendAssets[0] = ST_XTZ; - address[] memory borrowAssets = new address[](1); - borrowAssets[0] = XTZ; - + function _deployAccountant(address vault, address[] memory lendAssets, address[] memory borrowAssets) + internal + returns (UniversalAccountant) + { DataTypes.AaveV3AccountantPluginModuleInitData memory accountantPluginInitData = DataTypes .AaveV3AccountantPluginModuleInitData({ poolAddressesProvider: AAVE_V3_POOL_ADDRESSES_PROVIDER, @@ -187,10 +191,10 @@ contract TestBase is Test { abi.encodeWithSelector(UniversalAccountant.initialize.selector, initData) ); - accountant = UniversalAccountant(address(proxy)); + return UniversalAccountant(address(proxy)); } - function _deployDepositManager(address vault) internal { + function _deployDepositManager(address vault) internal returns (DepositManager) { DepositManager depositManagerImplementation = new DepositManager(); TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( address(depositManagerImplementation), @@ -198,17 +202,17 @@ contract TestBase is Test { abi.encodeWithSelector(DepositManager.initialize.selector, vault) ); - depositManager = DepositManager(address(proxy)); + return DepositManager(address(proxy)); } - function _deployWithdrawManager(address vault) internal { + function _deployWithdrawManager(address vault) internal returns (WithdrawManager) { WithdrawManager withdrawManagerImplementation = new WithdrawManager(); TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( address(withdrawManagerImplementation), address(this), abi.encodeWithSelector(WithdrawManager.initialize.selector, vault) ); - withdrawManager = WithdrawManager(address(proxy)); + return WithdrawManager(address(proxy)); } function _deployPreliquidationFallbackHandler(address vault) internal { @@ -231,4 +235,10 @@ contract TestBase is Test { moduleRegistry.setModule("AaveV3PreliquidationFallbackHandler", address(preliquidationFallbackHandler)); vm.label(address(preliquidationFallbackHandler), "preliquidationFallbackHandler"); } + + function _singleAddressArray(address a) internal pure returns (address[] memory) { + address[] memory array = new address[](1); + array[0] = a; + return array; + } } diff --git a/test/core/integration/IntegrationBase.sol b/test/core/integration/IntegrationBase.sol index 0dc4aa0..6dd9706 100644 --- a/test/core/integration/IntegrationBase.sol +++ b/test/core/integration/IntegrationBase.sol @@ -78,6 +78,23 @@ abstract contract IntegrationBase is TestBase { treasury: treasury, vaultOperator: admin }); + DataTypes.VaultInitData memory initDataBtc = DataTypes.VaultInitData({ + asset: WBTC, + name: "BTC Vault", + symbol: "BTC", + supplyCap: 100000 * 10 ** 18, + minimumDepositAmount: 100, + instantWithdrawFee: INSTANT_WITHDRAW_FEE, + superloopModuleRegistry: address(moduleRegistry), + modules: modules, + accountant: address(accountantBtc), + withdrawManager: address(withdrawManagerBtc), + depositManager: address(0), + cashReserve: 1000, + vaultAdmin: admin, + treasury: treasury, + vaultOperator: admin + }); superloopImplementation = new Superloop(); proxyAdmin = new ProxyAdmin(address(this)); @@ -86,11 +103,22 @@ abstract contract IntegrationBase is TestBase { address(proxyAdmin), abi.encodeWithSelector(Superloop.initialize.selector, initData) ); + TransparentUpgradeableProxy proxyBtc = new TransparentUpgradeableProxy( + address(superloopImplementation), + address(proxyAdmin), + abi.encodeWithSelector(Superloop.initialize.selector, initDataBtc) + ); superloop = Superloop(payable(address(proxy))); + superloopBtc = Superloop(payable(address(proxyBtc))); - _deployAccountant(address(superloop)); - _deployWithdrawManager(address(superloop)); - _deployDepositManager(address(superloop)); + accountant = _deployAccountant(address(superloop), _singleAddressArray(ST_XTZ), _singleAddressArray(XTZ)); + accountantBtc = _deployAccountant(address(superloopBtc), _singleAddressArray(LBTC), _singleAddressArray(WBTC)); + + withdrawManager = _deployWithdrawManager(address(superloop)); + withdrawManagerBtc = _deployWithdrawManager(address(superloopBtc)); + + depositManager = _deployDepositManager(address(superloop)); + depositManagerBtc = _deployDepositManager(address(superloopBtc)); bytes32 key = keccak256(abi.encodePacked(POOL, IFlashLoanSimpleReceiver.executeOperation.selector)); bytes32 depositKey = @@ -110,10 +138,18 @@ abstract contract IntegrationBase is TestBase { superloop.setRegisteredModule(address(accountant), true); superloop.setRegisteredModule(address(depositManager), true); + superloopBtc.setRegisteredModule(address(withdrawManagerBtc), true); + superloopBtc.setRegisteredModule(address(accountantBtc), true); + superloopBtc.setRegisteredModule(address(depositManagerBtc), true); + superloop.setAccountantModule(address(accountant)); superloop.setWithdrawManagerModule(address(withdrawManager)); superloop.setDepositManagerModule(address(depositManager)); + superloopBtc.setAccountantModule(address(accountantBtc)); + superloopBtc.setWithdrawManagerModule(address(withdrawManagerBtc)); + superloopBtc.setDepositManagerModule(address(depositManagerBtc)); + vm.stopPrank(); vm.label(address(superloop), "superloop"); diff --git a/test/core/integration/Migration.t.sol b/test/core/integration/Migration.t.sol index c35f0c7..7609166 100644 --- a/test/core/integration/Migration.t.sol +++ b/test/core/integration/Migration.t.sol @@ -35,61 +35,57 @@ contract MigrationTest is IntegrationBase { address DEX_MODULE = 0x38F5efC1267F6103c9b0FE802E1731E245f09Cd0; address oldVault = 0xe24e5DEbA01Ab0B5D78A0093442De0864832803E; + address oldVaultBtc = 0xC557529dd252e5a02E6C653b0B88984aFa3c8199; - // currently there are 115 holder who need to be migrated to the new vault - address[] public ALL_HOLDERS = [ - 0x8fb2FB82fF44da85E43d0A71CA992DbBC3243c5a, + // currently there are 119 holder who need to be migrated to the new vault + address[] public ALL_HOLDERS_XTZ = [ + 0x2B38f0De645E05Ec410AE86Ef479a1e42E45Bd12, 0xb3d1aCA9bF004d0da560930Cf9277c604eDf2283, 0x11fC7853944570C1F9D9EBE7Ac24e2FeFddf0314, 0xced306BfCB4a057aE3905063fD601774F9F18730, + 0x8fb2FB82fF44da85E43d0A71CA992DbBC3243c5a, 0x5d8809340760b1bB54642BE91Bb5A2871C0d7a10, 0x40F832B71D2C525A9aa4b4908Ec511Ed93c8a308, - 0x52f86e43dAbdDee24A35227598bF4569FE59034D, + 0xde5017f32C3AB32dd0286891A11F5d5D54C13728, 0x4f2421553E571627C6801521316732693016d9cF, - 0x953D1668BC03e0EE9145A7c4F79956b73db90B67, 0x6EA314366871459Dbc3e1A2Eb422e23B538c7ADC, + 0x953D1668BC03e0EE9145A7c4F79956b73db90B67, 0x46C9CCC6857E33a8209706BB5043700b4608Dda8, 0x67Efd0FfD493Fa5C55f2a19033557557e3A5197C, 0xbE00FA424243C56c7CF5792aFBd6b8Dc4d6CBE4E, 0xEC23a0C9D4107cAD33271c4F1955a21f4c041208, - 0x07ABc6225724a3f29e09173f585AacDE7A701dB3, + 0x3d72971b117EE8EF445D138557151f66CB0D408F, + 0xc2E97Ae6Ca9aAFB19CE0B8bCd1F4C50285db2377, 0x5EdF9DeC5b003AF80B65e129Ab17DBE64106a8e3, + 0x669bd328f6C494949Ed9fB2dc8021557A6Dd005f, 0x664324F8B7430b4C22Dc036234Dd752A4755Fda7, - 0x3d72971b117EE8EF445D138557151f66CB0D408F, + 0x07ABc6225724a3f29e09173f585AacDE7A701dB3, 0xB35722e595F2B974c2B1D14A80Fab5d0c60c2fa3, - 0x93e3A2a50e7A6091B877Fc939e0a3f43954811d8, 0x37b14bF02B1D01E196E3439d1689fEf845E9314A, + 0x93e3A2a50e7A6091B877Fc939e0a3f43954811d8, 0xd9aeb979cbe3a85F2d6fca3723f846Af8CA56E05, 0x4A2cFAa5B24850493298DCb8969fe11FEeFfc366, - 0xc2E97Ae6Ca9aAFB19CE0B8bCd1F4C50285db2377, - 0x68f6609d45A9dA001A88f7A3b9ec236Acf27e1f5, 0x1C6Aed70006d8d65c6407Fa9b27C9359C1572f40, 0x92c34B736B832d33209497f3904C4423d11E2a8a, 0xCAf9131ADF841cC8A3f8C14cBc94Aa16cF0a2faA, 0x9214835f82752E0574F3828A4400C36Ba386Df82, 0xF09E6d5EE9b5B7FC84412260FD6E6D70dCadcd9C, 0x8b529eF78046008f9d1FbC91c7407030De96EE32, - 0x669bd328f6C494949Ed9fB2dc8021557A6Dd005f, 0xa77E705d7166750F53F60ca7e246BAFBE40f5c42, 0x4fb30f8CcE1F80FC9CC45F7F626069be7549aF59, 0xc210a4D56D99E095b126664fDCE545b369ED3FD0, 0x03adFaA573aC1a9b19D2b8F79a5aAFFb9c2A0532, - 0xb6c82dCC296b055cEB6DBF1440d1379Bb4Dc1131, 0x79bc970B69CC8fb31406C409692eD19612E39731, - 0x96b29b8dD6240144e203F9f45D55F8FE6fC466aB, 0xb9b4800282BDC0DD2284fD321cf6800a24a7B8B0, 0x06e477F88ae55721B72f0F2c545328eDe998803e, 0xc25404122C1776689A6fD620d0F816Cb9e36e5dD, 0x2192013589F926637cB288004db2f1dadb71D390, 0xb0DebcB643eE79f19Ef659Bd01D0FAC12d058604, - 0x2E6Ff365367c9DB7C4a6140d37394aB8CF25C8A2, + 0x09e63186b4F94FafcCC60a42D3f77b4A10995674, 0x521e6f07bDfa2fF8D072887F0ef3bB908a3D9e2a, - 0x432d741Ae42175a2515121BcB917e7FE6d21Cf2e, + 0x341979e95a3e2969EaDa286b635d6e29F7a76a71, 0xAD89C778C7dBC0C7546cE7Fdf6A1d87C369A7892, - 0x8Fb2B9010780c418fbf84733ca3F0F5623EC2e7a, - 0x8B714cB45683733fD4Dc423FE6BF9A1d9F8DDD2C, 0x4987f13D50AC3246E589433349095492e820f8fd, - 0xa4F2F6D9BAB9532d2A5B9B41504274E7FD0fEf4f, 0x22472e7cA126b25fA031DeA6A7E0a7fbD9B2cdfd, 0x595e187831CB1ebAf3e4c09423eef3E74DfDaD1E, 0xaD96ACD2e6574682AEBf8f25774FA643bcaa3B5a, @@ -97,17 +93,12 @@ contract MigrationTest is IntegrationBase { 0xc51101BAbC9Cc75118fBCe5187914e45841A7f01, 0x09505A42D693D828B78CC87ed64DB9A0348Ca5e6, 0xdD543DC0efeA305A4c595305C784166fbe87897B, - 0xb1f607F3B753bEf4756a30cfC235629A0c2d1E57, - 0x0E9B063789909565CEdA1Fba162474405A151E66, - 0x341979e95a3e2969EaDa286b635d6e29F7a76a71, - 0x117AD17Bf596D6337931f41141F34275Eab16129, 0xbb85bFF98F145af30BA935Ae4DeD97C7A5Ce9bF2, 0xfd005e01FAa45d3CD3fD4641FBCd9FFa1d26C703, 0xaB14C744e5316475dd2384E6Bb95F9eCF4480bD5, 0x595f5e9cF36dF0AAf12Ae27f1b3dC18622Fc5748, 0xF5AC5943D16fC865824910033B756519DC396682, 0xa0c507E52359160a334d9f1AeAA459b8BD39568a, - 0x007EA857fdbE8D19997b507a21B4c377e3B21D82, 0x0E16cAA0E000cD3B68b4128C6aE62E846cb5b431, 0x603c081e1d72063Cd691CAD3Bd2FEd68eBE8922a, 0x5c53414E1f15D7668c2b9EC0A92482A64845f5f6, @@ -123,43 +114,97 @@ contract MigrationTest is IntegrationBase { 0xE3c3cD192Bdf289558A0bA645bc2c2c822F7EB06, 0x18514a31077b33f32BDbeA52443f2C0EEb094Be1, 0x0E9852b16AE49C99B84b0241E3C6F4a5692C6b05, + 0x2E6Ff365367c9DB7C4a6140d37394aB8CF25C8A2, 0x49400E8A2d270669ED7cfc4Fd5A2804c0A87e1BB, 0xDDc91d503Eeb7C3CB78559fB998C0a23354821ee, 0x0C5Cd230e9EBF5A707CF1f822Dfc49664304DfD2, 0x92751712B2F6ADE4AD1F35a837591A66394C6799, - 0x00ab2b9D924aaDCb60126A4050B68e8531A6A5a6, 0x57Fe7D8A10F5C4aC2Ca953a5B0a6b4BBfEC187C9, 0x5413E315A8c242132aC5DD626B0eceE616e5023D, + 0xf458B8FB16D661bC1E260991EBa9b1FFBC8bA39b, 0xC6Ad105483F89de96A3E423980fB550465A05D9b, 0x26C43439031301a85e19868647DF46177869c4C8, 0x9cB4a586bbdE7528DB256c3FCE08845255d892D9, + 0xb1f607F3B753bEf4756a30cfC235629A0c2d1E57, 0x1CbB1E8274E44287517A34De6AF3CF97dfD9E0Cb, 0x2b3F539dAf59126F6F566adD0dEDc19959305e9d, 0x2eCe359621497F4C9053c30912e9ecB7e74718c6, 0x1A50D1AeD031eE6B22b37c84eD8f3f3487C5Ca47, + 0x117AD17Bf596D6337931f41141F34275Eab16129, 0x5BD39fcEE33D45C5c5f615e09D2AA785Aa0c36C6, 0x698230D8445C21E99f4CC81F8F733d205a661cD3, 0x10b453C5379877d6b55B71D73Af7D8Fbc69eeF91, 0x13deFCe8Be050f902f8eE06CB7f0E743e3e8b705, 0xFF5A5bBC1b0f124974165b2190f460438d7CD220, 0x7F5B2c355bFC0C6F3aE2D394534C29D3609FF542, + 0x007EA857fdbE8D19997b507a21B4c377e3B21D82, 0x97F4aa12F637BAe9c07cA492eE281534b4F3BB50, 0x23b3511474c960960E4C030e572dADE633Cfc0F6, 0x7cA50563448C1f87cF131D96dEF7124f995cf3aC, 0xA8617c6BdE242B213bA283c93aCf034dfcbE66fA, + 0x68f6609d45A9dA001A88f7A3b9ec236Acf27e1f5, 0x37ae94b377B6D6522BCED1a448D144C69B6bfa9a, - 0xC44125EA298770DFA6B4a27A599CF014f19A573d, + 0x8Fb2B9010780c418fbf84733ca3F0F5623EC2e7a, 0x5F4BD8107FF5EB8fD25480C938662bBC11da27B8, 0x57453361422BfBE5332eF75CBE1C38242c5deEEA, + 0x96b29b8dD6240144e203F9f45D55F8FE6fC466aB, + 0x00ab2b9D924aaDCb60126A4050B68e8531A6A5a6, 0x431588Aff8ea1BEcB1d8188D87195Aa95678BA0A, + 0x0E9B063789909565CEdA1Fba162474405A151E66, 0x5952f70FEF1CbC26856d149646D4A8F97E923eE7, 0x894d222eCeC91B9dFFf8bcBD164B7a72DF7469aD, + 0x8B714cB45683733fD4Dc423FE6BF9A1d9F8DDD2C, 0x5E9b31FA592913C2aD2356ed843F66057c81455D, 0x58F1eFd2fBf415CFC0Ce30A13c36A2642690eDA0, 0x84e47C7E20Ee8e8d6195A99788226EB488f5Fad7, 0xCce7AcFb0d4760df15a1f87C92324B166e9C0638 ]; + address[] public ALL_HOLDERS_BTC = [ + 0x9D5132Dd45CdaCd1De3a7b7f1f13da7F025fF726, + 0xa7CBb758849979CEc43FD146fe01EB2BF560202C, + 0x6bA9c7d86eBD6A20817d482C42C9dE2806EeFB7e, + 0x9D0214C3dB28875fe2b9eb9cD9d4F71E7817890A, + 0x2B38f0De645E05Ec410AE86Ef479a1e42E45Bd12, + 0x8fb2FB82fF44da85E43d0A71CA992DbBC3243c5a, + 0xced306BfCB4a057aE3905063fD601774F9F18730, + 0x0C1856Cb7444cc8596c706185b54535F45C2623B, + 0x11fC7853944570C1F9D9EBE7Ac24e2FeFddf0314, + 0xb3d1aCA9bF004d0da560930Cf9277c604eDf2283, + 0xB438Dbaae78225eEfcDBB04E207A8eFB06036a2c, + 0x997b96BAD648c39226281Bc002f9857274E42A01, + 0xd52929B69680A6f74D2eB9c8F1ef482f37b1b32B, + 0x706FC1a8e457De0cf52e7679C2922aEF7F7a397e, + 0x3497818F50f79A11236B941dDAd35Da68A57864E, + 0xB35722e595F2B974c2B1D14A80Fab5d0c60c2fa3, + 0xA9f7b86FCC86EA1b913dDcaB8c9514a8e677666E, + 0x7077e395C9FA4E480366A5DC5792d2504d78dffF, + 0x664324F8B7430b4C22Dc036234Dd752A4755Fda7, + 0xD22ADba687aAd7DD9CEC3b18D970C581734783bA, + 0x5BC81274740A73D33ec8539182c326b8b58004C2, + 0x02F73B8e29dD6Ec38Bc5D8B3826051DC562c3060, + 0x4e90Da1dDCD5B0Fb295b6A69251b5D04D5bCCA7a, + 0x150a0D516C0ceFa39f980Da57d5422E91A94238b, + 0x4fb30f8CcE1F80FC9CC45F7F626069be7549aF59, + 0xF382ce5457Bba6113e082DD638b6671Cb2277B1f, + 0xb95f08B2eda7B23Ce412A6dE82eA5f5100335A3A, + 0x5b6Da8BC8696Aa4D20030151345ec652cf1eC727, + 0x0396816A361e2353a91Fde8438600a9353b34ce7, + 0xA1ebf2043e76446eFA2724bD1Ec18321776096FF, + 0x9214835f82752E0574F3828A4400C36Ba386Df82, + 0xb0DebcB643eE79f19Ef659Bd01D0FAC12d058604, + 0x3d72971b117EE8EF445D138557151f66CB0D408F, + 0x09e63186b4F94FafcCC60a42D3f77b4A10995674, + 0xc210a4D56D99E095b126664fDCE545b369ED3FD0, + 0xEC23a0C9D4107cAD33271c4F1955a21f4c041208, + 0x03adFaA573aC1a9b19D2b8F79a5aAFFb9c2A0532, + 0x6a58EBB35664A171A1B070DE48ee93278A63c168, + 0x521e6f07bDfa2fF8D072887F0ef3bB908a3D9e2a, + 0xF5AC5943D16fC865824910033B756519DC396682, + 0xBD73cF5baf12F120Ee3f6C4ad82df9a12649e578, + 0x44119A62EA645242234cAF408c4c20513E660EBb + ]; + function setUp() public override { super.setUp(); @@ -172,8 +217,17 @@ contract MigrationTest is IntegrationBase { data: abi.encodeWithSelector(emodeModule.execute.selector, params) }); + DataTypes.AaveV3EmodeParams memory paramsBtc = DataTypes.AaveV3EmodeParams({emodeCategory: 2}); + DataTypes.ModuleExecutionData[] memory moduleExecutionDataBtc = new DataTypes.ModuleExecutionData[](1); + moduleExecutionDataBtc[0] = DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.DELEGATECALL, + module: address(emodeModule), + data: abi.encodeWithSelector(emodeModule.execute.selector, paramsBtc) + }); + vm.startPrank(admin); superloop.operate(moduleExecutionData); + superloopBtc.operate(moduleExecutionDataBtc); // register the above modules moduleRegistry.setModule("REPAY_MODULE", REPAY_MODULE); @@ -187,18 +241,136 @@ contract MigrationTest is IntegrationBase { superloop.setRegisteredModule(DEPOSIT_MODULE, true); superloop.setRegisteredModule(BORROW_MODULE, true); superloop.setRegisteredModule(DEX_MODULE, true); + + superloopBtc.setRegisteredModule(REPAY_MODULE, true); + superloopBtc.setRegisteredModule(WITHDRAW_MODULE, true); + superloopBtc.setRegisteredModule(DEPOSIT_MODULE, true); + superloopBtc.setRegisteredModule(BORROW_MODULE, true); + superloopBtc.setRegisteredModule(DEX_MODULE, true); vm.stopPrank(); vm.label(oldVault, "oldVault"); + vm.label(oldVaultBtc, "oldVaultBtc"); vm.label(address(superloop), "newVault"); + vm.label(address(superloopBtc), "newVaultBtc"); vm.label(REPAY_MODULE, "REPAY_MODULE_OLD"); vm.label(WITHDRAW_MODULE, "WITHDRAW_MODULE_OLD"); vm.label(DEPOSIT_MODULE, "DEPOSIT_MODULE_OLD"); vm.label(BORROW_MODULE, "BORROW_MODULE_OLD"); vm.label(DEX_MODULE, "DEX_MODULE_OLD"); + + // TODO: do deposit and withdraw for both } - function test_migration() public { + // function test_migration() public { + // // deploy migration helper + // MigrationHelper migrationHelper = new MigrationHelper( + // AAVE_V3_POOL_ADDRESSES_PROVIDER, + // REPAY_MODULE, + // WITHDRAW_MODULE, + // DEPOSIT_MODULE, + // BORROW_MODULE, + // DEX_MODULE, + // USDC + // ); + // vm.label(address(migrationHelper), "migrationHelper"); + + // // set migration helper contract as priveledged address in old vault + // address oldVaultAdmin = ISuperloop(oldVault).vaultAdmin(); + // vm.prank(oldVaultAdmin); + // ISuperloop(oldVault).setPrivilegedAddress(address(migrationHelper), true); + + // // set migration helper contract as deposit manager and vault operator in new vault + // vm.startPrank(admin); + // superloop.setDepositManagerModule(address(migrationHelper)); + // superloop.setVaultOperator(address(migrationHelper)); + + // // set migration helper as vault in new accountant module + // accountant.setVault(address(migrationHelper)); + // accountant.transferOwnership(address(migrationHelper)); + // vm.stopPrank(); + + // address[] memory users = ALL_HOLDERS_XTZ; + + // // old balances + // uint256 oldVaultXTZBalance = IERC20(XTZ).balanceOf(oldVault); + // uint256 oldVaultSTXTZBalance = IERC20(ST_XTZ).balanceOf(oldVault); + // (uint256 oldVaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(ST_XTZ, oldVault); + // (,, uint256 oldVaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(XTZ, oldVault); + + // console.log("OLD VAULT STATE INITIAL"); + // console.log("oldVaultXTZBalance", oldVaultXTZBalance); + // console.log("oldVaultSTXTZBalance", oldVaultSTXTZBalance); + // console.log("oldVaultLendBalance", oldVaultLendBalance); + // console.log("oldVaultBorrowBalance", oldVaultBorrowBalance); + // console.log("old vault total supply", ISuperloop(oldVault).totalSupply()); + // console.log("old vault total assets", ISuperloop(oldVault).totalAssets()); + + // assertEq(Ownable(address(accountant)).owner(), address(migrationHelper)); + // assertEq(accountant.vault(), address(migrationHelper)); + + // uint256 maxSharesDelta = 100; + + // // start recording gas + // uint256 startGas = gasleft(); + // uint256 batches = 4; + // migrationHelper.migrate(oldVault, address(superloop), users, ST_XTZ, XTZ, batches, maxSharesDelta, 5); + + // console.log("migration complete"); + + // uint256 endGas = gasleft(); + // uint256 gasUsed = startGas - endGas; + // console.log("gas used", gasUsed); + // uint256 newVaultXTZBalance = IERC20(XTZ).balanceOf(address(superloop)); + // uint256 newVaultSTXTZBalance = IERC20(ST_XTZ).balanceOf(address(superloop)); + // (uint256 newVaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(ST_XTZ, address(superloop)); + // (,, uint256 newVaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(XTZ, address(superloop)); + // uint256 newVaultLastRealizedFeeExchangeRate = + // IAccountantModule(ISuperloop(address(superloop)).accountant()).lastRealizedFeeExchangeRate(); + // uint256 oldVaultLastRealizedFeeExchangeRate = + // IAccountantModule(ISuperloopLegacy(oldVault).accountantModule()).lastRealizedFeeExchangeRate(); + + // console.log("NEW VAULT STATE AFTER MIGRATION"); + // console.log("newVaultXTZBalance", newVaultXTZBalance); + // console.log("newVaultSTXTZBalance", newVaultSTXTZBalance); + // console.log("newVaultLendBalance", newVaultLendBalance); + // console.log("newVaultBorrowBalance", newVaultBorrowBalance); + // console.log("new vault total supply", ISuperloop(address(superloop)).totalSupply()); + // console.log("new vault total assets", ISuperloop(address(superloop)).totalAssets()); + // console.log( + // "new vault last realized fee exchange rate", + // IAccountantModule(ISuperloop(address(superloop)).accountant()).lastRealizedFeeExchangeRate() + // ); + + // // assert balances of each of the users are the same + + // for (uint256 i = 0; i < users.length; i++) { + // assertEq(ISuperloop(address(superloop)).balanceOf(users[i]), ISuperloop(oldVault).balanceOf(users[i])); + // } + + // assertEq(newVaultLastRealizedFeeExchangeRate, oldVaultLastRealizedFeeExchangeRate); + // assertEq(Ownable(address(accountant)).owner(), address(admin)); + // assertEq(accountant.vault(), address(superloop)); + + // oldVaultXTZBalance = IERC20(XTZ).balanceOf(oldVault); + // oldVaultSTXTZBalance = IERC20(ST_XTZ).balanceOf(oldVault); + // (oldVaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(ST_XTZ, oldVault); + // (,, oldVaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(XTZ, oldVault); + + // console.log("OLD VAULT STATE AFTER MIGRATION"); + // console.log("oldVaultXTZBalance", oldVaultXTZBalance); + // console.log("oldVaultSTXTZBalance", oldVaultSTXTZBalance); + // console.log("oldVaultLendBalance", oldVaultLendBalance); + // console.log("oldVaultBorrowBalance", oldVaultBorrowBalance); + // console.log("old vault total supply", ISuperloop(oldVault).totalSupply()); + // console.log("old vault total assets", ISuperloop(oldVault).totalAssets()); + // console.log( + // "old vault last realized fee exchange rate", + // IAccountantModule(ISuperloopLegacy(oldVault).accountantModule()).lastRealizedFeeExchangeRate() + // ); + // } + + function test_migrationBtc() public { // deploy migration helper MigrationHelper migrationHelper = new MigrationHelper( AAVE_V3_POOL_ADDRESSES_PROVIDER, @@ -212,67 +384,67 @@ contract MigrationTest is IntegrationBase { vm.label(address(migrationHelper), "migrationHelper"); // set migration helper contract as priveledged address in old vault - address oldVaultAdmin = ISuperloop(oldVault).vaultAdmin(); + address oldVaultAdmin = ISuperloop(oldVaultBtc).vaultAdmin(); vm.prank(oldVaultAdmin); - ISuperloop(oldVault).setPrivilegedAddress(address(migrationHelper), true); + ISuperloop(oldVaultBtc).setPrivilegedAddress(address(migrationHelper), true); // set migration helper contract as deposit manager and vault operator in new vault vm.startPrank(admin); - superloop.setDepositManagerModule(address(migrationHelper)); - superloop.setVaultOperator(address(migrationHelper)); + superloopBtc.setDepositManagerModule(address(migrationHelper)); + superloopBtc.setVaultOperator(address(migrationHelper)); // set migration helper as vault in new accountant module - accountant.setVault(address(migrationHelper)); - accountant.transferOwnership(address(migrationHelper)); + accountantBtc.setVault(address(migrationHelper)); + accountantBtc.transferOwnership(address(migrationHelper)); vm.stopPrank(); - address[] memory users = ALL_HOLDERS; + address[] memory users = ALL_HOLDERS_BTC; // old balances - uint256 oldVaultXTZBalance = IERC20(XTZ).balanceOf(oldVault); - uint256 oldVaultSTXTZBalance = IERC20(ST_XTZ).balanceOf(oldVault); - (uint256 oldVaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(ST_XTZ, oldVault); - (,, uint256 oldVaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(XTZ, oldVault); + uint256 oldVaultBTCBalance = IERC20(WBTC).balanceOf(oldVaultBtc); + uint256 oldVaultLBTCBalance = IERC20(LBTC).balanceOf(oldVaultBtc); + (uint256 oldVaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(LBTC, oldVaultBtc); + (,, uint256 oldVaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(WBTC, oldVaultBtc); console.log("OLD VAULT STATE INITIAL"); - console.log("oldVaultXTZBalance", oldVaultXTZBalance); - console.log("oldVaultSTXTZBalance", oldVaultSTXTZBalance); + console.log("oldVaultBTCBalance", oldVaultBTCBalance); + console.log("oldVaultLBTCBalance", oldVaultLBTCBalance); console.log("oldVaultLendBalance", oldVaultLendBalance); console.log("oldVaultBorrowBalance", oldVaultBorrowBalance); - console.log("old vault total supply", ISuperloop(oldVault).totalSupply()); - console.log("old vault total assets", ISuperloop(oldVault).totalAssets()); + console.log("old vault total supply", ISuperloop(oldVaultBtc).totalSupply()); + console.log("old vault total assets", ISuperloop(oldVaultBtc).totalAssets()); - assertEq(Ownable(address(accountant)).owner(), address(migrationHelper)); - assertEq(accountant.vault(), address(migrationHelper)); + assertEq(Ownable(address(accountantBtc)).owner(), address(migrationHelper)); + assertEq(accountantBtc.vault(), address(migrationHelper)); uint256 maxSharesDelta = 100; // start recording gas uint256 startGas = gasleft(); uint256 batches = 4; - migrationHelper.migrate(oldVault, address(superloop), users, ST_XTZ, XTZ, batches, maxSharesDelta); + migrationHelper.migrate(oldVaultBtc, address(superloopBtc), users, LBTC, WBTC, batches, maxSharesDelta, 5); console.log("migration complete"); uint256 endGas = gasleft(); uint256 gasUsed = startGas - endGas; console.log("gas used", gasUsed); - uint256 newVaultXTZBalance = IERC20(XTZ).balanceOf(address(superloop)); - uint256 newVaultSTXTZBalance = IERC20(ST_XTZ).balanceOf(address(superloop)); - (uint256 newVaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(ST_XTZ, address(superloop)); - (,, uint256 newVaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(XTZ, address(superloop)); + uint256 newVaultBTCBalance = IERC20(WBTC).balanceOf(address(superloopBtc)); + uint256 newVaultLBTCBalance = IERC20(LBTC).balanceOf(address(superloopBtc)); + (uint256 newVaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(LBTC, address(superloopBtc)); + (,, uint256 newVaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(WBTC, address(superloopBtc)); uint256 newVaultLastRealizedFeeExchangeRate = - IAccountantModule(ISuperloop(address(superloop)).accountant()).lastRealizedFeeExchangeRate(); + IAccountantModule(ISuperloop(address(superloopBtc)).accountant()).lastRealizedFeeExchangeRate(); uint256 oldVaultLastRealizedFeeExchangeRate = - IAccountantModule(ISuperloopLegacy(oldVault).accountantModule()).lastRealizedFeeExchangeRate(); + IAccountantModule(ISuperloopLegacy(oldVaultBtc).accountantModule()).lastRealizedFeeExchangeRate(); console.log("NEW VAULT STATE AFTER MIGRATION"); - console.log("newVaultXTZBalance", newVaultXTZBalance); - console.log("newVaultSTXTZBalance", newVaultSTXTZBalance); + console.log("newVaultXTZBalance", newVaultBTCBalance); + console.log("newVaultLBTCBalance", newVaultLBTCBalance); console.log("newVaultLendBalance", newVaultLendBalance); console.log("newVaultBorrowBalance", newVaultBorrowBalance); - console.log("new vault total supply", ISuperloop(address(superloop)).totalSupply()); - console.log("new vault total assets", ISuperloop(address(superloop)).totalAssets()); + console.log("new vault total supply", ISuperloop(address(superloopBtc)).totalSupply()); + console.log("new vault total assets", ISuperloop(address(superloopBtc)).totalAssets()); console.log( "new vault last realized fee exchange rate", IAccountantModule(ISuperloop(address(superloop)).accountant()).lastRealizedFeeExchangeRate() @@ -281,30 +453,30 @@ contract MigrationTest is IntegrationBase { // assert balances of each of the users are the same for (uint256 i = 0; i < users.length; i++) { - assertEq(ISuperloop(address(superloop)).balanceOf(users[i]), ISuperloop(oldVault).balanceOf(users[i])); + assertEq(ISuperloop(address(superloopBtc)).balanceOf(users[i]), ISuperloop(oldVaultBtc).balanceOf(users[i])); } assertEq(newVaultLastRealizedFeeExchangeRate, oldVaultLastRealizedFeeExchangeRate); - assertEq(Ownable(address(accountant)).owner(), address(admin)); - assertEq(accountant.vault(), address(superloop)); + assertEq(Ownable(address(accountantBtc)).owner(), address(admin)); + assertEq(accountantBtc.vault(), address(superloopBtc)); console.log("gas used", gasUsed); - oldVaultXTZBalance = IERC20(XTZ).balanceOf(oldVault); - oldVaultSTXTZBalance = IERC20(ST_XTZ).balanceOf(oldVault); - (oldVaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(ST_XTZ, oldVault); - (,, oldVaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(XTZ, oldVault); + oldVaultBTCBalance = IERC20(WBTC).balanceOf(oldVaultBtc); + oldVaultLBTCBalance = IERC20(LBTC).balanceOf(oldVaultBtc); + (oldVaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(LBTC, oldVaultBtc); + (,, oldVaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(WBTC, oldVaultBtc); console.log("OLD VAULT STATE AFTER MIGRATION"); - console.log("oldVaultXTZBalance", oldVaultXTZBalance); - console.log("oldVaultSTXTZBalance", oldVaultSTXTZBalance); + console.log("oldVaultBTCBalance", oldVaultBTCBalance); + console.log("oldVaultLBTCBalance", oldVaultLBTCBalance); console.log("oldVaultLendBalance", oldVaultLendBalance); console.log("oldVaultBorrowBalance", oldVaultBorrowBalance); - console.log("old vault total supply", ISuperloop(oldVault).totalSupply()); - console.log("old vault total assets", ISuperloop(oldVault).totalAssets()); + console.log("old vault total supply", ISuperloop(oldVaultBtc).totalSupply()); + console.log("old vault total assets", ISuperloop(oldVaultBtc).totalAssets()); console.log( "old vault last realized fee exchange rate", - IAccountantModule(ISuperloopLegacy(oldVault).accountantModule()).lastRealizedFeeExchangeRate() + IAccountantModule(ISuperloopLegacy(oldVaultBtc).accountantModule()).lastRealizedFeeExchangeRate() ); } } diff --git a/test/core/unit/DepositManager.t.sol b/test/core/unit/DepositManager.t.sol index acc4b57..66807a6 100644 --- a/test/core/unit/DepositManager.t.sol +++ b/test/core/unit/DepositManager.t.sol @@ -62,7 +62,7 @@ contract DepositManagerTest is TestBase { ); superloop = Superloop(payable(address(proxy))); - _deployAccountant(address(superloop)); + _deployAccountant(address(superloop), _singleAddressArray(ST_XTZ), _singleAddressArray(XTZ)); _deployWithdrawManager(address(superloop)); _deployDepositManager(address(superloop)); diff --git a/test/core/unit/Superloop.t.sol b/test/core/unit/Superloop.t.sol index 9038243..1eee229 100644 --- a/test/core/unit/Superloop.t.sol +++ b/test/core/unit/Superloop.t.sol @@ -60,7 +60,7 @@ contract SuperloopTest is TestBase { ); superloop = Superloop(payable(address(proxy))); - _deployAccountant(address(superloop)); + _deployAccountant(address(superloop), _singleAddressArray(ST_XTZ), _singleAddressArray(XTZ)); _deployWithdrawManager(address(superloop)); superloop.setAccountantModule(address(accountantAaveV3)); diff --git a/test/core/unit/WithdrawManager.t.sol b/test/core/unit/WithdrawManager.t.sol index d030caa..c5a1810 100644 --- a/test/core/unit/WithdrawManager.t.sol +++ b/test/core/unit/WithdrawManager.t.sol @@ -62,7 +62,7 @@ contract WithdrawManagerTest is TestBase { ); superloop = Superloop(payable(address(proxy))); - _deployAccountant(address(superloop)); + _deployAccountant(address(superloop), _singleAddressArray(ST_XTZ), _singleAddressArray(XTZ)); _deployWithdrawManager(address(superloop)); _deployDepositManager(address(superloop)); From 451034abea66a564a0e9b5bff2f5d10019dafcbf Mon Sep 17 00:00:00 2001 From: Priyam Anand Date: Wed, 29 Oct 2025 18:17:08 +0530 Subject: [PATCH 02/21] chore: separate scripts for xtz and btc migration --- script/deploy-migration.s.sol | 42 ++ script/deploy-v2-for-migration.s.sol | 469 ++++++++++++++++++ ...ration.s.sol => migration-btc-vault.s.sol} | 104 ++-- script/migration-xtz-vault.s.sol | 302 +++++++++++ test/core/TestBase.sol | 11 + 5 files changed, 894 insertions(+), 34 deletions(-) create mode 100644 script/deploy-migration.s.sol create mode 100644 script/deploy-v2-for-migration.s.sol rename script/{migration.s.sol => migration-btc-vault.s.sol} (63%) create mode 100644 script/migration-xtz-vault.s.sol diff --git a/script/deploy-migration.s.sol b/script/deploy-migration.s.sol new file mode 100644 index 0000000..a2021b9 --- /dev/null +++ b/script/deploy-migration.s.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import {Script} from "forge-std/Script.sol"; +import {MigrationHelper} from "../src/helpers/MigrationHelper.sol"; + +contract DeployMigration is Script { + address public deployer; + uint256 public deployerPvtKey; + + address public AAVE_V3_POOL_ADDRESSES_PROVIDER = 0x5ccF60c7E10547c5389E9cBFf543E5D0Db9F4feC; + address public REPAY_MODULE = 0x9AF8cCabC21ff594dA237f9694C4A9C6123480c6; + address public WITHDRAW_MODULE = 0x1f5Ba080B9E5705DA47212167cA44611F78DB130; + address public DEPOSIT_MODULE = 0x66e82124412C61D7fF90ACFBa82936DD037D737E; + address public BORROW_MODULE = 0x3de57294989d12066a94a8A16E977992F3cF8433; + address public DEX_MODULE = 0x38F5efC1267F6103c9b0FE802E1731E245f09Cd0; + address public constant USDC = 0x796Ea11Fa2dD751eD01b53C372fFDB4AAa8f00F9; + + function setUp() public { + vm.createSelectFork("etherlink"); + deployerPvtKey = vm.envUint("PRIVATE_KEY"); + deployer = vm.addr(deployerPvtKey); + } + + function run() public returns (address) { + vm.startBroadcast(deployerPvtKey); + + MigrationHelper migrationHelper = new MigrationHelper( + AAVE_V3_POOL_ADDRESSES_PROVIDER, + REPAY_MODULE, + WITHDRAW_MODULE, + DEPOSIT_MODULE, + BORROW_MODULE, + DEX_MODULE, + USDC + ); + + vm.stopBroadcast(); + + return address(migrationHelper); + } +} diff --git a/script/deploy-v2-for-migration.s.sol b/script/deploy-v2-for-migration.s.sol new file mode 100644 index 0000000..76dd0c7 --- /dev/null +++ b/script/deploy-v2-for-migration.s.sol @@ -0,0 +1,469 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.13; + +import {Script} from "forge-std/Script.sol"; +import {console} from "forge-std/console.sol"; +import {UniversalDexModule} from "../src/modules/dex/UniversalDexModule.sol"; +import {AaveV3FlashloanModule} from "../src/modules/aave/AaveV3FlashloanModule.sol"; +import {AaveV3CallbackHandler} from "../src/modules/callback/AaveV3CallbackHandler.sol"; +import {AaveV3EmodeModule} from "../src/modules/aave/AaveV3EmodeModule.sol"; +import {AaveV3SupplyModule} from "../src/modules/aave/AaveV3SupplyModule.sol"; +import {AaveV3WithdrawModule} from "../src/modules/aave/AaveV3WithdrawModule.sol"; +import {AaveV3BorrowModule} from "../src/modules/aave/AaveV3BorrowModule.sol"; +import {AaveV3RepayModule} from "../src/modules/aave/AaveV3RepayModule.sol"; +import {SuperloopModuleRegistry} from "../src/core/ModuleRegistry/ModuleRegistry.sol"; +import {TransparentUpgradeableProxy} from + "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {DataTypes} from "../src/common/DataTypes.sol"; +import {Superloop} from "../src/core/Superloop/Superloop.sol"; +import {IFlashLoanSimpleReceiver} from "aave-v3-core/contracts/flashloan/interfaces/IFlashLoanSimpleReceiver.sol"; +import {DepositManagerCallbackHandler} from "../src/modules/callback/DepositManagerCallbackHandler.sol"; +import {WithdrawManagerCallbackHandler} from "../src/modules/callback/WithdrawManagerCallbackHandler.sol"; +import {DepositManager} from "../src/core/DepositManager/DepositManager.sol"; +import {WithdrawManager} from "../src/core/WithdrawManager/WithdrawManager.sol"; +import {UniversalAccountant} from "../src/core/Accountant/universalAccountant/UniversalAccountant.sol"; +import {AaveV3AccountantPlugin} from "../src/plugins/accountant/AaveV3AccountantPlugin.sol"; +import {AaveV3PreliquidationFallbackHandler} from "../src/modules/fallback/AaveV3PreliquidationFallbackHandler.sol"; +import {VaultRouter} from "../src/helpers/VaultRouter.sol"; +import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; + +contract Deploy is Script { + address public deployer; + uint256 public deployerPvtKey; + address public vaultAdmin; + address public rebalanceAdmin; + address public treasury; + address public vaultOperator; + + address public asset; + address public lendAsset; + address public borrowAsset; + string public name; + string public symbol; + uint256 public supplyCap; + uint256 public minimumDepositAmount; + uint256 public instantWithdrawFee; + uint256 public cashReserve; + uint256 public performanceFee; + uint256 public seedAmount; + uint8 public emodeCategory; + + address public AAVE_V3_POOL_ADDRESSES_PROVIDER = 0x5ccF60c7E10547c5389E9cBFf543E5D0Db9F4feC; + address public constant ST_XTZ = 0x01F07f4d78d47A64F4C3B2b65f513f15Be6E1854; + address public constant XTZ = 0xc9B53AB2679f573e480d01e0f49e2B5CFB7a3EAb; + address public constant LBTC = 0xecAc9C5F704e954931349Da37F60E39f515c11c1; + address public constant WBTC = 0xbFc94CD2B1E55999Cfc7347a9313e88702B83d0F; + address public constant POOL = 0x3bD16D195786fb2F509f2E2D7F69920262EF114D; + address public constant VAULT_ADMIN = 0x81b833Df09A7ce39C00ecE916EC54166d2a6B193; + address public constant TREASURY = 0x669bd328f6C494949Ed9fB2dc8021557A6Dd005f; + uint256 public constant XTZ_SCALE = 10 ** 18; + uint256 public constant BTC_SCALE = 10 ** 8; + + SuperloopModuleRegistry public moduleRegistry; + + // aave modules + AaveV3FlashloanModule public flashloanModule; + AaveV3CallbackHandler public aaveFlashloanCallbackHandler; + AaveV3EmodeModule public emodeModule; + AaveV3SupplyModule public supplyModule; + AaveV3WithdrawModule public withdrawModule; + AaveV3BorrowModule public borrowModule; + AaveV3RepayModule public repayModule; + + // dex module + UniversalDexModule public dexModule; + + DepositManagerCallbackHandler public depositManagerCallbackHandler; + WithdrawManagerCallbackHandler public withdrawManagerCallbackHandler; + + // TODO: Add later + AaveV3PreliquidationFallbackHandler public preliquidationFallbackHandler; + + address public accountantImplementation; + address public withdrawManagerImplementation; + address public depositManagerImplementation; + address public vaultImplementation; + + address public accountantAaveV3Plugin; + + UniversalAccountant public accountant; + WithdrawManager public withdrawManager; + DepositManager public depositManager; + + Superloop public superloop; + VaultRouter public vaultRouter; + + function setUp() public { + asset = XTZ; // WBTC + name = "Superloop XTZ"; // "Superloop WBTC" + symbol = "sloopXTZ"; // "sloopWBTC" + supplyCap = 760_000 * XTZ_SCALE; // 12.5 * BTC_SCALE + minimumDepositAmount = 100; // in token terms + instantWithdrawFee = 10; // 0.1% in BPS + cashReserve = 100; // 1% in BPS + performanceFee = 1500; // 10% in bps + lendAsset = ST_XTZ; // LBTC + borrowAsset = XTZ; // WBTC + seedAmount = 0; + emodeCategory = 3; // 2; + + // TODO: Check this config every time before deploying + console.log("--------------------------------"); + console.log("Using configs: "); + console.log("ASSET", asset); + console.log("NAME", name); + console.log("SYMBOL", symbol); + console.log("PERFORMANCE_FEE", performanceFee); + console.log("SUPPLY_CAP", supplyCap); + console.log("MINIMUM_DEPOSIT_AMOUNT", minimumDepositAmount); + console.log("INSTANT_WITHDRAW_FEE", instantWithdrawFee); + console.log("CASH_RESERVE", cashReserve); + console.log("SEED_AMOUNT", seedAmount); + console.log("--------------------------------"); + + vm.createSelectFork("etherlink"); + + deployerPvtKey = vm.envUint("PRIVATE_KEY"); + deployer = vm.addr(deployerPvtKey); + + vaultAdmin = deployer; + treasury = TREASURY; + vaultOperator = deployer; + + console.log("--------------------------------"); + console.log("Depolyer config and roles: "); + console.log("DEPLOYER", deployer); + console.log("VAULT_ADMIN", vaultAdmin); + console.log("TREASURY", treasury); + console.log("VAULT_OPERATOR", vaultOperator); + console.log("--------------------------------"); + } + + function run() public { + vm.startBroadcast(deployerPvtKey); + + // deploy module registry + moduleRegistry = new SuperloopModuleRegistry(); + + // deploy all the modules + deployModules(); + + address[] memory modules = new address[](10); + modules[0] = address(flashloanModule); + modules[1] = address(aaveFlashloanCallbackHandler); + modules[2] = address(emodeModule); + modules[3] = address(supplyModule); + modules[4] = address(withdrawModule); + modules[5] = address(borrowModule); + modules[6] = address(repayModule); + modules[7] = address(dexModule); + modules[8] = address(depositManagerCallbackHandler); + modules[9] = address(withdrawManagerCallbackHandler); + + // deploy vault + DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ + asset: asset, + name: name, + symbol: symbol, + supplyCap: supplyCap, + superloopModuleRegistry: address(moduleRegistry), + modules: modules, + accountant: address(accountant), + withdrawManager: address(withdrawManager), + minimumDepositAmount: minimumDepositAmount, + instantWithdrawFee: instantWithdrawFee, + cashReserve: cashReserve, + depositManager: address(depositManager), + vaultAdmin: vaultAdmin, + treasury: treasury, + vaultOperator: vaultOperator + }); + vaultImplementation = address(new Superloop()); + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(vaultImplementation), + address(vaultAdmin), + abi.encodeWithSelector(Superloop.initialize.selector, initData) + ); + superloop = Superloop(payable(address(proxy))); + + // deploy accountant + _deployAccountant(address(superloop)); + moduleRegistry.setModule("universalAccountant", address(accountant)); + superloop.setRegisteredModule(address(accountant), true); + superloop.setAccountantModule(address(accountant)); + + // deploy withdraw manager + _deployWithdrawManager(address(superloop)); + moduleRegistry.setModule("withdrawManager", address(withdrawManager)); + superloop.setRegisteredModule(address(withdrawManager), true); + superloop.setWithdrawManagerModule(address(withdrawManager)); + + // deploy deposit manager + _deployDepositManager(address(superloop)); + moduleRegistry.setModule("depositManager", address(depositManager)); + superloop.setRegisteredModule(address(depositManager), true); + superloop.setDepositManagerModule(address(depositManager)); + + // set callback handler + _setupCallbackHandlers(); + + // call emode module and setup emode + _setupEmode(); + + // setup vault router + _setupVaultRouter(); + + /** + * ROLE TRANSFERS : + * 1. Vault admin => safe + * 2. Vault proxy admin => safe + * 3. Accountant plugin owner => safe + * 4. Accountant => safe + * 5. Accountant proxy admin => safe + * 6. Deposit manager proxy admin => safe + * 7. Withdraw manager proxy admin => safe + * 8. Module registry owner => safe + * 9. Vault operator => rebalance admin + * 10. Vault router owner => vault admin + */ + _logAddresses(); + + vm.stopBroadcast(); + } + + function deployModules() internal { + flashloanModule = new AaveV3FlashloanModule(AAVE_V3_POOL_ADDRESSES_PROVIDER); + moduleRegistry.setModule("AaveV3FlashloanModule", address(flashloanModule)); + + aaveFlashloanCallbackHandler = new AaveV3CallbackHandler(); + moduleRegistry.setModule("AaveV3CallbackHandler", address(aaveFlashloanCallbackHandler)); + + depositManagerCallbackHandler = new DepositManagerCallbackHandler(); + moduleRegistry.setModule("DepositManagerCallbackHandler", address(depositManagerCallbackHandler)); + + withdrawManagerCallbackHandler = new WithdrawManagerCallbackHandler(); + moduleRegistry.setModule("WithdrawManagerCallbackHandler", address(withdrawManagerCallbackHandler)); + + // TODO: add later + // preliquidationFallbackHandler = new AaveV3PreliquidationFallbackHandler( + // AAVE_V3_POOL_ADDRESSES_PROVIDER, + // address(superloop), + // 2, + // 8, + // DataTypes.AaveV3PreliquidationParamsInit({}) + // ); + // moduleRegistry.setModule("AaveV3PreliquidationFallbackHandler", address(preliquidationFallbackHandler)); + + emodeModule = new AaveV3EmodeModule(AAVE_V3_POOL_ADDRESSES_PROVIDER); + moduleRegistry.setModule("AaveV3EmodeModule", address(emodeModule)); + + supplyModule = new AaveV3SupplyModule(AAVE_V3_POOL_ADDRESSES_PROVIDER); + moduleRegistry.setModule("AaveV3SupplyModule", address(supplyModule)); + + withdrawModule = new AaveV3WithdrawModule(AAVE_V3_POOL_ADDRESSES_PROVIDER); + moduleRegistry.setModule("AaveV3WithdrawModule", address(withdrawModule)); + + borrowModule = new AaveV3BorrowModule(AAVE_V3_POOL_ADDRESSES_PROVIDER); + moduleRegistry.setModule("AaveV3BorrowModule", address(borrowModule)); + + repayModule = new AaveV3RepayModule(AAVE_V3_POOL_ADDRESSES_PROVIDER); + moduleRegistry.setModule("AaveV3RepayModule", address(repayModule)); + + dexModule = UniversalDexModule(0x38F5efC1267F6103c9b0FE802E1731E245f09Cd0); + // moduleRegistry.setModule("UniversalDexModule", address(dexModule)); + } + + function _deployAccountant(address vault) internal { + address[] memory lendAssets = new address[](1); + lendAssets[0] = lendAsset; + address[] memory borrowAssets = new address[](1); + borrowAssets[0] = borrowAsset; + + DataTypes.AaveV3AccountantPluginModuleInitData memory accountantPluginInitData = DataTypes + .AaveV3AccountantPluginModuleInitData({ + poolAddressesProvider: AAVE_V3_POOL_ADDRESSES_PROVIDER, + lendAssets: lendAssets, + borrowAssets: borrowAssets + }); + accountantAaveV3Plugin = address(new AaveV3AccountantPlugin(accountantPluginInitData)); + + address[] memory registeredAccountants = new address[](1); + registeredAccountants[0] = accountantAaveV3Plugin; + + // deploy accountant + DataTypes.UniversalAccountantModuleInitData memory initData = DataTypes.UniversalAccountantModuleInitData({ + registeredAccountants: registeredAccountants, + performanceFee: uint16(performanceFee), + vault: address(vault) + }); + + accountantImplementation = address(new UniversalAccountant()); + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + accountantImplementation, + address(vaultAdmin), + abi.encodeWithSelector(UniversalAccountant.initialize.selector, initData) + ); + + accountant = UniversalAccountant(address(proxy)); + } + + function _deployWithdrawManager(address vault) internal { + withdrawManagerImplementation = address(new WithdrawManager()); + + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(withdrawManagerImplementation), + address(vaultAdmin), + abi.encodeWithSelector(WithdrawManager.initialize.selector, vault) + ); + + withdrawManager = WithdrawManager(address(proxy)); + } + + function _deployDepositManager(address vault) internal { + depositManagerImplementation = address(new DepositManager()); + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(depositManagerImplementation), + address(vaultAdmin), + abi.encodeWithSelector(DepositManager.initialize.selector, vault) + ); + depositManager = DepositManager(address(proxy)); + } + + function _setupEmode() internal { + DataTypes.AaveV3EmodeParams memory params = DataTypes.AaveV3EmodeParams({emodeCategory: emodeCategory}); + + DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); + moduleExecutionData[0] = DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.DELEGATECALL, + module: address(emodeModule), + data: abi.encodeWithSelector(emodeModule.execute.selector, params) + }); + + superloop.operate(moduleExecutionData); + } + + function _setupCallbackHandlers() internal { + bytes32 key = keccak256(abi.encodePacked(POOL, IFlashLoanSimpleReceiver.executeOperation.selector)); + bytes32 depositKey = + keccak256(abi.encodePacked(address(depositManager), depositManagerCallbackHandler.executeDeposit.selector)); + bytes32 withdrawKey = keccak256( + abi.encodePacked(address(withdrawManager), withdrawManagerCallbackHandler.executeWithdraw.selector) + ); + superloop.setCallbackHandler(key, address(aaveFlashloanCallbackHandler)); + superloop.setCallbackHandler(depositKey, address(depositManagerCallbackHandler)); + superloop.setCallbackHandler(withdrawKey, address(withdrawManagerCallbackHandler)); + } + + function _seedVault() internal { + IERC20(asset).approve(address(superloop), seedAmount); + superloop.seed(seedAmount); + } + + function _logAddresses() internal view { + // log module registry address + console.log("--------------------------------"); + console.log("Module Registry: %s", address(moduleRegistry)); + console.log("--------------------------------"); + + // log all aave module addresses + console.log("--------------------------------"); + console.log("AaveV3 Modules: %s", "--------------------------------"); + console.log("--------------------------------"); + console.log("AaveV3FlashloanModule: %s", address(flashloanModule)); + console.log("--------------------------------"); + console.log("AaveV3EmodeModule: %s", address(emodeModule)); + console.log("--------------------------------"); + console.log("AaveV3SupplyModule: %s", address(supplyModule)); + console.log("--------------------------------"); + console.log("AaveV3WithdrawModule: %s", address(withdrawModule)); + console.log("--------------------------------"); + console.log("AaveV3BorrowModule: %s", address(borrowModule)); + console.log("--------------------------------"); + console.log("AaveV3RepayModule: %s", address(repayModule)); + + // log all the dex modules + console.log("--------------------------------"); + console.log("Dex Modules: %s", "--------------------------------"); + console.log("--------------------------------"); + console.log("Universal Dex Module: %s", address(dexModule)); + console.log("--------------------------------"); + + // log all the callback handler modules + console.log("--------------------------------"); + console.log("Callback Handler Modules: %s", "--------------------------------"); + console.log("--------------------------------"); + console.log("Deposit Manager Callback Handler: %s", address(depositManagerCallbackHandler)); + console.log("--------------------------------"); + console.log("Withdraw Manager Callback Handler: %s", address(withdrawManagerCallbackHandler)); + console.log("--------------------------------"); + console.log("AaveV3CallbackHandler: %s", address(aaveFlashloanCallbackHandler)); + console.log("--------------------------------"); + + // log accountant along with accountant plugin + console.log("--------------------------------"); + console.log("Accountant: %s", "--------------------------------"); + console.log("--------------------------------"); + console.log("Accountant: %s", address(accountant)); + console.log("--------------------------------"); + console.log("Accountant Implementation: %s", address(accountantImplementation)); + console.log("--------------------------------"); + console.log("Accountant Plugin: %s", address(accountantAaveV3Plugin)); + console.log("--------------------------------"); + + // log withdraw manager along with withdraw manager implementation + console.log("--------------------------------"); + console.log("Withdraw Manager: %s", "--------------------------------"); + console.log("--------------------------------"); + console.log("Withdraw Manager: %s", address(withdrawManager)); + console.log("--------------------------------"); + console.log("Withdraw Manager Implementation: %s", address(withdrawManagerImplementation)); + console.log("--------------------------------"); + + // log deposit manager along with deposit manager implementation + console.log("--------------------------------"); + console.log("Deposit Manager: %s", "--------------------------------"); + console.log("--------------------------------"); + console.log("Deposit Manager: %s", address(depositManager)); + console.log("--------------------------------"); + console.log("Deposit Manager Implementation: %s", address(depositManagerImplementation)); + console.log("--------------------------------"); + + // log superloop along with implementation and proxy admin + console.log("--------------------------------"); + console.log("Superloop: %s", "--------------------------------"); + console.log("--------------------------------"); + console.log("Superloop: %s", address(superloop)); + console.log("--------------------------------"); + console.log("Superloop Implementation: %s", address(vaultImplementation)); + console.log("--------------------------------"); + + console.log("--------------------------------"); + console.log("Vault Router: %s", "--------------------------------"); + console.log("--------------------------------"); + console.log("Vault Router: %s", address(vaultRouter)); + } + + function _setupVaultRouter() internal { + address[] memory supportedVaults = new address[](1); + supportedVaults[0] = address(superloop); + + address[] memory supportedDepositManagers = new address[](1); + supportedDepositManagers[0] = address(depositManager); + + address[] memory supportedTokens = new address[](9); + supportedTokens[0] = XTZ; + supportedTokens[1] = 0x796Ea11Fa2dD751eD01b53C372fFDB4AAa8f00F9; // USDC + supportedTokens[2] = 0x01F07f4d78d47A64F4C3B2b65f513f15Be6E1854; // ST_XTZ + supportedTokens[3] = 0x2C03058C8AFC06713be23e58D2febC8337dbfE6A; // USDT + supportedTokens[4] = 0xDD629E5241CbC5919847783e6C96B2De4754e438; // mtbill + supportedTokens[5] = 0x2247B5A46BB79421a314aB0f0b67fFd11dd37Ee4; // mbasis + supportedTokens[6] = 0xbFc94CD2B1E55999Cfc7347a9313e88702B83d0F; // wbtc + supportedTokens[7] = 0xfc24f770F94edBca6D6f885E12d4317320BcB401; // weth + supportedTokens[8] = 0xecAc9C5F704e954931349Da37F60E39f515c11c1; // lbtc + + vaultRouter = new VaultRouter(supportedVaults, supportedTokens, address(dexModule), supportedDepositManagers); + + vaultRouter.transferOwnership(vaultAdmin); + } +} diff --git a/script/migration.s.sol b/script/migration-btc-vault.s.sol similarity index 63% rename from script/migration.s.sol rename to script/migration-btc-vault.s.sol index 948e900..d3ec4b5 100644 --- a/script/migration.s.sol +++ b/script/migration-btc-vault.s.sol @@ -12,50 +12,86 @@ import {Ownable} from "openzeppelin-contracts/contracts/access/Ownable.sol"; import {console2} from "forge-std/console2.sol"; import {IPoolDataProvider} from "aave-v3-core/contracts/interfaces/IPoolDataProvider.sol"; +// TODO: update address(0) to actual addresses contract Migration is Script { - uint256 public adminPvtKey; - address public admin; + uint256 public migratorPvtKey; + address public migrator; address public oldVault; address public newVault; MigrationHelper public migrationHelper; IPoolDataProvider public poolDataProvider = IPoolDataProvider(0x99e8269dDD5c7Af0F1B3973A591b47E8E001BCac); address public AAVE_V3_POOL_ADDRESSES_PROVIDER = 0x5ccF60c7E10547c5389E9cBFf543E5D0Db9F4feC; - address REPAY_MODULE = 0xB8A8415B20b119aDEaff3Ed078980B0585190E5B; - address WITHDRAW_MODULE = 0x545D45227Ec1dE0dae88C5B576A234931fa2e428; - address DEPOSIT_MODULE = 0x193EcEA94c424dF41d466b6bcA597ADb5B788999; - address BORROW_MODULE = 0x23fd2A315e6F30552278849AF467e978EF76c5C4; - address DEX_MODULE = 0x2871677D649019A4e901C8b0f5a3B6Fa88900a91; + address public REPAY_MODULE = 0x9AF8cCabC21ff594dA237f9694C4A9C6123480c6; + address public WITHDRAW_MODULE = 0x1f5Ba080B9E5705DA47212167cA44611F78DB130; + address public DEPOSIT_MODULE = 0x66e82124412C61D7fF90ACFBa82936DD037D737E; + address public BORROW_MODULE = 0x3de57294989d12066a94a8A16E977992F3cF8433; + address public DEX_MODULE = 0x38F5efC1267F6103c9b0FE802E1731E245f09Cd0; address public constant USDC = 0x796Ea11Fa2dD751eD01b53C372fFDB4AAa8f00F9; - address public constant ST_XTZ = 0x01F07f4d78d47A64F4C3B2b65f513f15Be6E1854; - address public constant XTZ = 0xc9B53AB2679f573e480d01e0f49e2B5CFB7a3EAb; address public constant LBTC = 0xecAc9C5F704e954931349Da37F60E39f515c11c1; address public constant WBTC = 0xbFc94CD2B1E55999Cfc7347a9313e88702B83d0F; - address newVaultDepositManager = 0xC2d2325DF20bB36E5859068dEBBbeefd239D6192; - uint256 maxSharesDelta = 100; + address newVaultDepositManager = address(0); + uint256 maxSharesDelta = 0; uint256 batches = 1; - uint256 maxPercentageChangeInExchangeRate = 5; // 0.05% ? + uint256 maxPercentageChangeInExchangeRate = 5; // 0.05% - // TODO: Add read time users to the list + // TODO: 42 holders, check this before running address[] public ALL_USERS = [ - 0x5c53414E1f15D7668c2b9EC0A92482A64845f5f6, - 0x52f86e43dAbdDee24A35227598bF4569FE59034D, + 0x9D5132Dd45CdaCd1De3a7b7f1f13da7F025fF726, + 0xa7CBb758849979CEc43FD146fe01EB2BF560202C, + 0x6bA9c7d86eBD6A20817d482C42C9dE2806EeFB7e, + 0x9D0214C3dB28875fe2b9eb9cD9d4F71E7817890A, + 0x2B38f0De645E05Ec410AE86Ef479a1e42E45Bd12, + 0x8fb2FB82fF44da85E43d0A71CA992DbBC3243c5a, + 0xced306BfCB4a057aE3905063fD601774F9F18730, + 0x0C1856Cb7444cc8596c706185b54535F45C2623B, + 0x11fC7853944570C1F9D9EBE7Ac24e2FeFddf0314, + 0xb3d1aCA9bF004d0da560930Cf9277c604eDf2283, + 0xB438Dbaae78225eEfcDBB04E207A8eFB06036a2c, + 0x997b96BAD648c39226281Bc002f9857274E42A01, + 0xd52929B69680A6f74D2eB9c8F1ef482f37b1b32B, + 0x706FC1a8e457De0cf52e7679C2922aEF7F7a397e, + 0x3497818F50f79A11236B941dDAd35Da68A57864E, + 0xB35722e595F2B974c2B1D14A80Fab5d0c60c2fa3, + 0xA9f7b86FCC86EA1b913dDcaB8c9514a8e677666E, + 0x7077e395C9FA4E480366A5DC5792d2504d78dffF, + 0x664324F8B7430b4C22Dc036234Dd752A4755Fda7, + 0xD22ADba687aAd7DD9CEC3b18D970C581734783bA, + 0x5BC81274740A73D33ec8539182c326b8b58004C2, + 0x02F73B8e29dD6Ec38Bc5D8B3826051DC562c3060, + 0x4e90Da1dDCD5B0Fb295b6A69251b5D04D5bCCA7a, + 0x150a0D516C0ceFa39f980Da57d5422E91A94238b, + 0x4fb30f8CcE1F80FC9CC45F7F626069be7549aF59, + 0xF382ce5457Bba6113e082DD638b6671Cb2277B1f, + 0xb95f08B2eda7B23Ce412A6dE82eA5f5100335A3A, + 0x5b6Da8BC8696Aa4D20030151345ec652cf1eC727, + 0x0396816A361e2353a91Fde8438600a9353b34ce7, + 0xA1ebf2043e76446eFA2724bD1Ec18321776096FF, + 0x9214835f82752E0574F3828A4400C36Ba386Df82, + 0xb0DebcB643eE79f19Ef659Bd01D0FAC12d058604, + 0x3d72971b117EE8EF445D138557151f66CB0D408F, + 0x09e63186b4F94FafcCC60a42D3f77b4A10995674, + 0xc210a4D56D99E095b126664fDCE545b369ED3FD0, + 0xEC23a0C9D4107cAD33271c4F1955a21f4c041208, 0x03adFaA573aC1a9b19D2b8F79a5aAFFb9c2A0532, - 0xF5AC5943D16fC865824910033B756519DC396682 + 0x6a58EBB35664A171A1B070DE48ee93278A63c168, + 0x521e6f07bDfa2fF8D072887F0ef3bB908a3D9e2a, + 0xF5AC5943D16fC865824910033B756519DC396682, + 0xBD73cF5baf12F120Ee3f6C4ad82df9a12649e578, + 0x44119A62EA645242234cAF408c4c20513E660EBb ]; function setUp() public { vm.createSelectFork("etherlink"); - adminPvtKey = vm.envUint("PRIVATE_KEY"); - admin = vm.addr(adminPvtKey); + migratorPvtKey = vm.envUint("PRIVATE_KEY"); + migrator = vm.addr(migratorPvtKey); - // TODO: Add old vault address - oldVault = 0x9a3f9C4d3B5A40fc064f4f9dB11dE617CDCBD3eF; - newVault = 0x93fcCf4b8dDE650c98f6F1c18831B5c8D2966210; + oldVault = 0xC557529dd252e5a02E6C653b0B88984aFa3c8199; + newVault = address(0); - migrationHelper = _deployMigrationHelper(); + migrationHelper = MigrationHelper(address(0)); vm.label(oldVault, "oldVault"); vm.label(newVault, "newVault"); @@ -63,7 +99,7 @@ contract Migration is Script { } function run() public { - vm.startBroadcast(adminPvtKey); + vm.startBroadcast(migratorPvtKey); console.log("OLD VAULT STATE BEFORE MIGRATION"); _logVaultState(oldVault); @@ -127,6 +163,8 @@ contract Migration is Script { ); // assert these roles + address withdrawManager = ISuperloopLegacy(oldVault).withdrawManagerModule(); + assert(IERC20(WBTC).balanceOf(withdrawManager) == 0 && IERC20(oldVault).balanceOf(withdrawManager) == 0); assert(ISuperloop(newVault).depositManagerModule() == address(migrationHelper)); assert(ISuperloop(newVault).vaultOperator() == address(migrationHelper)); assert(IAccountantModule(ISuperloop(newVault).accountant()).vault() == address(migrationHelper)); @@ -137,7 +175,7 @@ contract Migration is Script { function _performMigration() internal { migrationHelper.migrate( - oldVault, newVault, ALL_USERS, ST_XTZ, XTZ, batches, maxSharesDelta, maxPercentageChangeInExchangeRate + oldVault, newVault, ALL_USERS, LBTC, WBTC, batches, maxSharesDelta, maxPercentageChangeInExchangeRate ); for (uint256 i = 0; i < ALL_USERS.length; i++) { @@ -150,16 +188,14 @@ contract Migration is Script { IAccountantModule(ISuperloopLegacy(oldVault).accountantModule()).lastRealizedFeeExchangeRate(); assert(newVaultLastRealizedFeeExchangeRate == oldVaultLastRealizedFeeExchangeRate); - assert(Ownable(address(accountant)).owner() == address(admin)); + assert(Ownable(address(accountant)).owner() == address(migrator)); assert(accountant.vault() == address(newVault)); } function _postMigrationSetup() internal { - ISuperloop(newVault).setVaultOperator(admin); + ISuperloop(newVault).setVaultOperator(migrator); ISuperloop(newVault).setDepositManagerModule(newVaultDepositManager); - // ISuperloop(oldVault).setPrivilegedAddress(address(migrationHelper), false); - ISuperloop(newVault).setRegisteredModule(REPAY_MODULE, false); ISuperloop(newVault).setRegisteredModule(WITHDRAW_MODULE, false); ISuperloop(newVault).setRegisteredModule(DEPOSIT_MODULE, false); @@ -167,17 +203,17 @@ contract Migration is Script { ISuperloop(newVault).setRegisteredModule(DEX_MODULE, false); // assert these roles - assert(ISuperloop(newVault).vaultOperator() == admin); + assert(ISuperloop(newVault).vaultOperator() == migrator); assert(ISuperloop(newVault).depositManagerModule() == newVaultDepositManager); - assert(Ownable(address(IAccountantModule(ISuperloop(newVault).accountant()))).owner() == address(admin)); + assert(Ownable(address(IAccountantModule(ISuperloop(newVault).accountant()))).owner() == address(migrator)); assert(IAccountantModule(ISuperloop(newVault).accountant()).vault() == newVault); } function _logVaultState(address vault) internal view { - uint256 vaultXTZBalance = IERC20(XTZ).balanceOf(vault); - uint256 vaultSTXTZBalance = IERC20(ST_XTZ).balanceOf(vault); - (uint256 vaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(ST_XTZ, vault); - (,, uint256 vaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(XTZ, vault); + uint256 vaultXTZBalance = IERC20(WBTC).balanceOf(vault); + uint256 vaultSTXTZBalance = IERC20(LBTC).balanceOf(vault); + (uint256 vaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(LBTC, vault); + (,, uint256 vaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(WBTC, vault); console.log("vaultXTZBalance", vaultXTZBalance); console.log("vaultSTXTZBalance", vaultSTXTZBalance); diff --git a/script/migration-xtz-vault.s.sol b/script/migration-xtz-vault.s.sol new file mode 100644 index 0000000..8ae6aad --- /dev/null +++ b/script/migration-xtz-vault.s.sol @@ -0,0 +1,302 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import {Script} from "forge-std/Script.sol"; +import {MigrationHelper} from "../src/helpers/MigrationHelper.sol"; +import {ISuperloop} from "../src/interfaces/ISuperloop.sol"; +import {IAccountantModule} from "../src/interfaces/IAccountantModule.sol"; +import {console} from "forge-std/console.sol"; +import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +import {ISuperloopLegacy} from "../src/helpers/ISuperloopLegacy.sol"; +import {Ownable} from "openzeppelin-contracts/contracts/access/Ownable.sol"; +import {console2} from "forge-std/console2.sol"; +import {IPoolDataProvider} from "aave-v3-core/contracts/interfaces/IPoolDataProvider.sol"; + +// TODO: update address(0) to actual addresses +contract Migration is Script { + uint256 public migratorPvtKey; + address public migrator; + address public oldVault; + address public newVault; + MigrationHelper public migrationHelper; + + IPoolDataProvider public poolDataProvider = IPoolDataProvider(0x99e8269dDD5c7Af0F1B3973A591b47E8E001BCac); + address public AAVE_V3_POOL_ADDRESSES_PROVIDER = 0x5ccF60c7E10547c5389E9cBFf543E5D0Db9F4feC; + address REPAY_MODULE = 0xB8A8415B20b119aDEaff3Ed078980B0585190E5B; + address WITHDRAW_MODULE = 0x545D45227Ec1dE0dae88C5B576A234931fa2e428; + address DEPOSIT_MODULE = 0x193EcEA94c424dF41d466b6bcA597ADb5B788999; + address BORROW_MODULE = 0x23fd2A315e6F30552278849AF467e978EF76c5C4; + address DEX_MODULE = 0x2871677D649019A4e901C8b0f5a3B6Fa88900a91; + address public constant USDC = 0x796Ea11Fa2dD751eD01b53C372fFDB4AAa8f00F9; + + address public constant ST_XTZ = 0x01F07f4d78d47A64F4C3B2b65f513f15Be6E1854; + address public constant XTZ = 0xc9B53AB2679f573e480d01e0f49e2B5CFB7a3EAb; + + address newVaultDepositManager = address(0); + uint256 maxSharesDelta = 0; + uint256 batches = 4; + uint256 maxPercentageChangeInExchangeRate = 5; // 0.05% + + // TODO: 119 holders, check this before running + address[] public ALL_USERS = [ + 0x2B38f0De645E05Ec410AE86Ef479a1e42E45Bd12, + 0xb3d1aCA9bF004d0da560930Cf9277c604eDf2283, + 0x11fC7853944570C1F9D9EBE7Ac24e2FeFddf0314, + 0xced306BfCB4a057aE3905063fD601774F9F18730, + 0x8fb2FB82fF44da85E43d0A71CA992DbBC3243c5a, + 0x5d8809340760b1bB54642BE91Bb5A2871C0d7a10, + 0x40F832B71D2C525A9aa4b4908Ec511Ed93c8a308, + 0xde5017f32C3AB32dd0286891A11F5d5D54C13728, + 0x4f2421553E571627C6801521316732693016d9cF, + 0x6EA314366871459Dbc3e1A2Eb422e23B538c7ADC, + 0x953D1668BC03e0EE9145A7c4F79956b73db90B67, + 0x46C9CCC6857E33a8209706BB5043700b4608Dda8, + 0x67Efd0FfD493Fa5C55f2a19033557557e3A5197C, + 0xbE00FA424243C56c7CF5792aFBd6b8Dc4d6CBE4E, + 0xEC23a0C9D4107cAD33271c4F1955a21f4c041208, + 0x3d72971b117EE8EF445D138557151f66CB0D408F, + 0xc2E97Ae6Ca9aAFB19CE0B8bCd1F4C50285db2377, + 0x5EdF9DeC5b003AF80B65e129Ab17DBE64106a8e3, + 0x669bd328f6C494949Ed9fB2dc8021557A6Dd005f, + 0x664324F8B7430b4C22Dc036234Dd752A4755Fda7, + 0x07ABc6225724a3f29e09173f585AacDE7A701dB3, + 0xB35722e595F2B974c2B1D14A80Fab5d0c60c2fa3, + 0x37b14bF02B1D01E196E3439d1689fEf845E9314A, + 0x93e3A2a50e7A6091B877Fc939e0a3f43954811d8, + 0xd9aeb979cbe3a85F2d6fca3723f846Af8CA56E05, + 0x4A2cFAa5B24850493298DCb8969fe11FEeFfc366, + 0x1C6Aed70006d8d65c6407Fa9b27C9359C1572f40, + 0x92c34B736B832d33209497f3904C4423d11E2a8a, + 0xCAf9131ADF841cC8A3f8C14cBc94Aa16cF0a2faA, + 0x9214835f82752E0574F3828A4400C36Ba386Df82, + 0xF09E6d5EE9b5B7FC84412260FD6E6D70dCadcd9C, + 0x8b529eF78046008f9d1FbC91c7407030De96EE32, + 0xa77E705d7166750F53F60ca7e246BAFBE40f5c42, + 0x4fb30f8CcE1F80FC9CC45F7F626069be7549aF59, + 0xc210a4D56D99E095b126664fDCE545b369ED3FD0, + 0x03adFaA573aC1a9b19D2b8F79a5aAFFb9c2A0532, + 0x79bc970B69CC8fb31406C409692eD19612E39731, + 0xb9b4800282BDC0DD2284fD321cf6800a24a7B8B0, + 0x06e477F88ae55721B72f0F2c545328eDe998803e, + 0xc25404122C1776689A6fD620d0F816Cb9e36e5dD, + 0x2192013589F926637cB288004db2f1dadb71D390, + 0xb0DebcB643eE79f19Ef659Bd01D0FAC12d058604, + 0x09e63186b4F94FafcCC60a42D3f77b4A10995674, + 0x521e6f07bDfa2fF8D072887F0ef3bB908a3D9e2a, + 0x341979e95a3e2969EaDa286b635d6e29F7a76a71, + 0xAD89C778C7dBC0C7546cE7Fdf6A1d87C369A7892, + 0x4987f13D50AC3246E589433349095492e820f8fd, + 0x22472e7cA126b25fA031DeA6A7E0a7fbD9B2cdfd, + 0x595e187831CB1ebAf3e4c09423eef3E74DfDaD1E, + 0xaD96ACD2e6574682AEBf8f25774FA643bcaa3B5a, + 0x82C421023dBE34CC526F4c027577363E6C7Af9b6, + 0xc51101BAbC9Cc75118fBCe5187914e45841A7f01, + 0x09505A42D693D828B78CC87ed64DB9A0348Ca5e6, + 0xdD543DC0efeA305A4c595305C784166fbe87897B, + 0xbb85bFF98F145af30BA935Ae4DeD97C7A5Ce9bF2, + 0xfd005e01FAa45d3CD3fD4641FBCd9FFa1d26C703, + 0xaB14C744e5316475dd2384E6Bb95F9eCF4480bD5, + 0x595f5e9cF36dF0AAf12Ae27f1b3dC18622Fc5748, + 0xF5AC5943D16fC865824910033B756519DC396682, + 0xa0c507E52359160a334d9f1AeAA459b8BD39568a, + 0x0E16cAA0E000cD3B68b4128C6aE62E846cb5b431, + 0x603c081e1d72063Cd691CAD3Bd2FEd68eBE8922a, + 0x5c53414E1f15D7668c2b9EC0A92482A64845f5f6, + 0xa87D56273CBE184927D92eEF1613ab2a72eacAC8, + 0xCc9757171B3cf4Dc86D1a86370D5B10662519096, + 0xeB92f0f58b218ad22a22741D5aa5bb7250E70045, + 0xa2608e76d835D0b3DB2Bbcbd4Bd1F78D47208353, + 0x9fD8Ef7E3867E962B401bC7289272653FEC11245, + 0x73cF6fCbEb5a5f56e550Cce5e0eeAC47f622EA2e, + 0x9b4A7002a086d4F8dFf04Af524987DdCa9b6ddCd, + 0x94Ae5310ba47b42D6D8DF3F20cD55650Ad43Be37, + 0xFd01eF34D30E6fA1169CBa769a22eEE454906f38, + 0xE3c3cD192Bdf289558A0bA645bc2c2c822F7EB06, + 0x18514a31077b33f32BDbeA52443f2C0EEb094Be1, + 0x0E9852b16AE49C99B84b0241E3C6F4a5692C6b05, + 0x2E6Ff365367c9DB7C4a6140d37394aB8CF25C8A2, + 0x49400E8A2d270669ED7cfc4Fd5A2804c0A87e1BB, + 0xDDc91d503Eeb7C3CB78559fB998C0a23354821ee, + 0x0C5Cd230e9EBF5A707CF1f822Dfc49664304DfD2, + 0x92751712B2F6ADE4AD1F35a837591A66394C6799, + 0x57Fe7D8A10F5C4aC2Ca953a5B0a6b4BBfEC187C9, + 0x5413E315A8c242132aC5DD626B0eceE616e5023D, + 0xf458B8FB16D661bC1E260991EBa9b1FFBC8bA39b, + 0xC6Ad105483F89de96A3E423980fB550465A05D9b, + 0x26C43439031301a85e19868647DF46177869c4C8, + 0x9cB4a586bbdE7528DB256c3FCE08845255d892D9, + 0xb1f607F3B753bEf4756a30cfC235629A0c2d1E57, + 0x1CbB1E8274E44287517A34De6AF3CF97dfD9E0Cb, + 0x2b3F539dAf59126F6F566adD0dEDc19959305e9d, + 0x2eCe359621497F4C9053c30912e9ecB7e74718c6, + 0x1A50D1AeD031eE6B22b37c84eD8f3f3487C5Ca47, + 0x117AD17Bf596D6337931f41141F34275Eab16129, + 0x5BD39fcEE33D45C5c5f615e09D2AA785Aa0c36C6, + 0x698230D8445C21E99f4CC81F8F733d205a661cD3, + 0x10b453C5379877d6b55B71D73Af7D8Fbc69eeF91, + 0x13deFCe8Be050f902f8eE06CB7f0E743e3e8b705, + 0xFF5A5bBC1b0f124974165b2190f460438d7CD220, + 0x7F5B2c355bFC0C6F3aE2D394534C29D3609FF542, + 0x007EA857fdbE8D19997b507a21B4c377e3B21D82, + 0x97F4aa12F637BAe9c07cA492eE281534b4F3BB50, + 0x23b3511474c960960E4C030e572dADE633Cfc0F6, + 0x7cA50563448C1f87cF131D96dEF7124f995cf3aC, + 0xA8617c6BdE242B213bA283c93aCf034dfcbE66fA, + 0x68f6609d45A9dA001A88f7A3b9ec236Acf27e1f5, + 0x37ae94b377B6D6522BCED1a448D144C69B6bfa9a, + 0x8Fb2B9010780c418fbf84733ca3F0F5623EC2e7a, + 0x5F4BD8107FF5EB8fD25480C938662bBC11da27B8, + 0x57453361422BfBE5332eF75CBE1C38242c5deEEA, + 0x96b29b8dD6240144e203F9f45D55F8FE6fC466aB, + 0x00ab2b9D924aaDCb60126A4050B68e8531A6A5a6, + 0x431588Aff8ea1BEcB1d8188D87195Aa95678BA0A, + 0x0E9B063789909565CEdA1Fba162474405A151E66, + 0x5952f70FEF1CbC26856d149646D4A8F97E923eE7, + 0x894d222eCeC91B9dFFf8bcBD164B7a72DF7469aD, + 0x8B714cB45683733fD4Dc423FE6BF9A1d9F8DDD2C, + 0x5E9b31FA592913C2aD2356ed843F66057c81455D, + 0x58F1eFd2fBf415CFC0Ce30A13c36A2642690eDA0, + 0x84e47C7E20Ee8e8d6195A99788226EB488f5Fad7, + 0xCce7AcFb0d4760df15a1f87C92324B166e9C0638 + ]; + + function setUp() public { + vm.createSelectFork("etherlink"); + migratorPvtKey = vm.envUint("PRIVATE_KEY"); + migrator = vm.addr(migratorPvtKey); + + oldVault = 0xe24e5DEbA01Ab0B5D78A0093442De0864832803E; + newVault = address(0); + + migrationHelper = MigrationHelper(address(0)); + + vm.label(oldVault, "oldVault"); + vm.label(newVault, "newVault"); + vm.label(address(migrationHelper), "migrationHelper"); + } + + function run() public { + vm.startBroadcast(migratorPvtKey); + + console.log("OLD VAULT STATE BEFORE MIGRATION"); + _logVaultState(oldVault); + console.log( + "old vault last realized fee exchange rate", + IAccountantModule(ISuperloopLegacy(oldVault).accountantModule()).lastRealizedFeeExchangeRate() + ); + + console.log("================================================"); + + _preMigrationSetup(); + _performMigration(); + _postMigrationSetup(); + + console.log("NEW VAULT STATE AFTER MIGRATION"); + _logVaultState(newVault); + console.log( + "new vault last realized fee exchange rate", + IAccountantModule(ISuperloop(newVault).accountant()).lastRealizedFeeExchangeRate() + ); + console.log("================================================"); + + console.log("OLD VAULT STATE AFTER MIGRATION"); + _logVaultState(oldVault); + console.log( + "old vault last realized fee exchange rate", + IAccountantModule(ISuperloopLegacy(oldVault).accountantModule()).lastRealizedFeeExchangeRate() + ); + + console.log("================================================"); + + vm.stopBroadcast(); + } + + function _deployMigrationHelper() internal returns (MigrationHelper) { + return new MigrationHelper( + AAVE_V3_POOL_ADDRESSES_PROVIDER, + REPAY_MODULE, + WITHDRAW_MODULE, + DEPOSIT_MODULE, + BORROW_MODULE, + DEX_MODULE, + USDC + ); + } + + function _preMigrationSetup() internal { + ISuperloop(oldVault).setPrivilegedAddress(address(migrationHelper), true); + + ISuperloop(newVault).setRegisteredModule(REPAY_MODULE, true); + ISuperloop(newVault).setRegisteredModule(WITHDRAW_MODULE, true); + ISuperloop(newVault).setRegisteredModule(DEPOSIT_MODULE, true); + ISuperloop(newVault).setRegisteredModule(BORROW_MODULE, true); + ISuperloop(newVault).setRegisteredModule(DEX_MODULE, true); + + ISuperloop(newVault).setDepositManagerModule(address(migrationHelper)); + ISuperloop(newVault).setVaultOperator(address(migrationHelper)); + IAccountantModule(ISuperloop(newVault).accountant()).setVault(address(migrationHelper)); + Ownable(address(IAccountantModule(ISuperloop(newVault).accountant()))).transferOwnership( + address(migrationHelper) + ); + + // assert these roles + address withdrawManager = ISuperloopLegacy(oldVault).withdrawManagerModule(); + assert(IERC20(XTZ).balanceOf(withdrawManager) == 0 && IERC20(oldVault).balanceOf(withdrawManager) == 0); + assert(ISuperloop(newVault).depositManagerModule() == address(migrationHelper)); + assert(ISuperloop(newVault).vaultOperator() == address(migrationHelper)); + assert(IAccountantModule(ISuperloop(newVault).accountant()).vault() == address(migrationHelper)); + assert( + Ownable(address(IAccountantModule(ISuperloop(newVault).accountant()))).owner() == address(migrationHelper) + ); + } + + function _performMigration() internal { + migrationHelper.migrate( + oldVault, newVault, ALL_USERS, ST_XTZ, XTZ, batches, maxSharesDelta, maxPercentageChangeInExchangeRate + ); + + for (uint256 i = 0; i < ALL_USERS.length; i++) { + assert(ISuperloop(newVault).balanceOf(ALL_USERS[i]) == ISuperloop(oldVault).balanceOf(ALL_USERS[i])); + } + IAccountantModule accountant = IAccountantModule(ISuperloop(newVault).accountant()); + uint256 newVaultLastRealizedFeeExchangeRate = + IAccountantModule(ISuperloop(newVault).accountant()).lastRealizedFeeExchangeRate(); + uint256 oldVaultLastRealizedFeeExchangeRate = + IAccountantModule(ISuperloopLegacy(oldVault).accountantModule()).lastRealizedFeeExchangeRate(); + + assert(newVaultLastRealizedFeeExchangeRate == oldVaultLastRealizedFeeExchangeRate); + assert(Ownable(address(accountant)).owner() == address(migrator)); + assert(accountant.vault() == address(newVault)); + } + + function _postMigrationSetup() internal { + ISuperloop(newVault).setVaultOperator(migrator); + ISuperloop(newVault).setDepositManagerModule(newVaultDepositManager); + + ISuperloop(newVault).setRegisteredModule(REPAY_MODULE, false); + ISuperloop(newVault).setRegisteredModule(WITHDRAW_MODULE, false); + ISuperloop(newVault).setRegisteredModule(DEPOSIT_MODULE, false); + ISuperloop(newVault).setRegisteredModule(BORROW_MODULE, false); + ISuperloop(newVault).setRegisteredModule(DEX_MODULE, false); + + // assert these roles + assert(ISuperloop(newVault).vaultOperator() == migrator); + assert(ISuperloop(newVault).depositManagerModule() == newVaultDepositManager); + assert(Ownable(address(IAccountantModule(ISuperloop(newVault).accountant()))).owner() == address(migrator)); + assert(IAccountantModule(ISuperloop(newVault).accountant()).vault() == newVault); + } + + function _logVaultState(address vault) internal view { + uint256 vaultXTZBalance = IERC20(XTZ).balanceOf(vault); + uint256 vaultSTXTZBalance = IERC20(ST_XTZ).balanceOf(vault); + (uint256 vaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(ST_XTZ, vault); + (,, uint256 vaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(XTZ, vault); + + console.log("vaultXTZBalance", vaultXTZBalance); + console.log("vaultSTXTZBalance", vaultSTXTZBalance); + console.log("vaultLendBalance", vaultLendBalance); + console.log("vaultBorrowBalance", vaultBorrowBalance); + console.log("vault total supply", ISuperloop(vault).totalSupply()); + console.log("vault total assets", ISuperloop(vault).totalAssets()); + } +} diff --git a/test/core/TestBase.sol b/test/core/TestBase.sol index 8797a83..62497bc 100644 --- a/test/core/TestBase.sol +++ b/test/core/TestBase.sol @@ -127,26 +127,37 @@ contract TestBase is Test { function _deployModules() internal { flashloanModule = new AaveV3FlashloanModule(AAVE_V3_POOL_ADDRESSES_PROVIDER); moduleRegistry.setModule("AaveV3FlashloanModule", address(flashloanModule)); + callbackHandler = new AaveV3CallbackHandler(); moduleRegistry.setModule("AaveV3CallbackHandler", address(callbackHandler)); + emodeModule = new AaveV3EmodeModule(AAVE_V3_POOL_ADDRESSES_PROVIDER); moduleRegistry.setModule("AaveV3EmodeModule", address(emodeModule)); + supplyModule = new AaveV3SupplyModule(AAVE_V3_POOL_ADDRESSES_PROVIDER); moduleRegistry.setModule("AaveV3SupplyModule", address(supplyModule)); + withdrawModule = new AaveV3WithdrawModule(AAVE_V3_POOL_ADDRESSES_PROVIDER); moduleRegistry.setModule("AaveV3WithdrawModule", address(withdrawModule)); + borrowModule = new AaveV3BorrowModule(AAVE_V3_POOL_ADDRESSES_PROVIDER); moduleRegistry.setModule("AaveV3BorrowModule", address(borrowModule)); + repayModule = new AaveV3RepayModule(AAVE_V3_POOL_ADDRESSES_PROVIDER); moduleRegistry.setModule("AaveV3RepayModule", address(repayModule)); + dexModule = new UniversalDexModule(); moduleRegistry.setModule("UniversalDexModule", address(dexModule)); + depositManagerCallbackHandler = new DepositManagerCallbackHandler(); moduleRegistry.setModule("DepositManagerCallbackHandler", address(depositManagerCallbackHandler)); + withdrawManagerCallbackHandler = new WithdrawManagerCallbackHandler(); moduleRegistry.setModule("WithdrawManagerCallbackHandler", address(withdrawManagerCallbackHandler)); + unwrapModule = new UnwrapModule(XTZ); moduleRegistry.setModule("UnwrapModule", address(unwrapModule)); + wrapModule = new WrapModule(XTZ); moduleRegistry.setModule("WrapModule", address(wrapModule)); From 39e047836a574738374422b239a67f86dc231656 Mon Sep 17 00:00:00 2001 From: Priyam Anand Date: Thu, 22 Jan 2026 18:21:36 +0530 Subject: [PATCH 03/21] feat: add Merkl and Vault modules for reward claiming --- src/common/DataTypes.sol | 24 +++++++++++++ src/modules/merkl/IDistributor.sol | 11 ++++++ src/modules/merkl/MerklModule.sol | 44 +++++++++++++++++++++++ src/modules/vault/VaultSupplyModule.sol | 41 +++++++++++++++++++++ src/modules/vault/VaultWithdrawModule.sol | 38 ++++++++++++++++++++ 5 files changed, 158 insertions(+) create mode 100644 src/modules/merkl/IDistributor.sol create mode 100644 src/modules/merkl/MerklModule.sol create mode 100644 src/modules/vault/VaultSupplyModule.sol create mode 100644 src/modules/vault/VaultWithdrawModule.sol diff --git a/src/common/DataTypes.sol b/src/common/DataTypes.sol index 3df3384..632b70a 100644 --- a/src/common/DataTypes.sol +++ b/src/common/DataTypes.sol @@ -363,4 +363,28 @@ library DataTypes { uint256 preIF1; uint256 preIF2; } + + /** + * @notice Structure for Merkl claim parameters + * @param users The addresses of the users to claim rewards for + * @param tokens The addresses of the tokens to claim rewards for + * @param amounts The amounts of the tokens to claim rewards for + * @param proofs The proofs of the Merkle tree + */ + struct MerklClaimParams { + address[] users; + address[] tokens; + uint256[] amounts; + bytes32[][] proofs; + } + + /** + * @notice Structure for vault action parameters + * @param vault The address of the vault + * @param amount The amount of the vault to supply + */ + struct VaultActionParams { + address vault; + uint256 amount; + } } diff --git a/src/modules/merkl/IDistributor.sol b/src/modules/merkl/IDistributor.sol new file mode 100644 index 0000000..c6295a5 --- /dev/null +++ b/src/modules/merkl/IDistributor.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +interface IDistributor { + function claim( + address[] calldata users, + address[] calldata tokens, + uint256[] calldata amounts, + bytes32[][] calldata proofs + ) external; +} diff --git a/src/modules/merkl/MerklModule.sol b/src/modules/merkl/MerklModule.sol new file mode 100644 index 0000000..2fbdae2 --- /dev/null +++ b/src/modules/merkl/MerklModule.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import {IDistributor} from "./IDistributor.sol"; +import {DataTypes} from "../../common/DataTypes.sol"; + +/** + * @title MerklModule + * @author Superlend + * @notice Module for claiming rewards from Merkl + * @dev Extends IDistributor to provide claiming functionality + */ +contract MerklModule { + /** + * @notice The distributor contract + */ + IDistributor public immutable distributor; + + /** + * @notice Emitted when rewards are claimed + * @param users The addresses of the users who claimed rewards + * @param tokens The addresses of the tokens claimed + * @param amounts The amounts of the tokens claimed + */ + event MerklRewardClaimed(address[] users, address[] tokens, uint256[] amounts); + + /** + * @notice Constructor + * @param _distributor The address of the distributor contract + */ + constructor(address _distributor) { + distributor = IDistributor(_distributor); + } + + /** + * @notice Executes the claim operation + * @param params The parameters for the claim operation + */ + function execute(DataTypes.MerklClaimParams memory params) external { + distributor.claim(params.users, params.tokens, params.amounts, params.proofs); + + emit MerklRewardClaimed(params.users, params.tokens, params.amounts); + } +} diff --git a/src/modules/vault/VaultSupplyModule.sol b/src/modules/vault/VaultSupplyModule.sol new file mode 100644 index 0000000..0d62a88 --- /dev/null +++ b/src/modules/vault/VaultSupplyModule.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import {IERC4626} from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; +import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {DataTypes} from "../../common/DataTypes.sol"; + +/** + * @title VaultSupplyModule + * @author Superlend + * @notice Module for supplying assets to a vault + * @dev Extends IERC4626 to provide vault supply functionality + */ +contract VaultSupplyModule { + /** + * @notice Emitted when assets are supplied to a vault + * @param vault The address of the vault + * @param amount The amount of the underlying asset supplied + * @param shares The amount of shares minted + */ + event VaultSupplied(address indexed vault, uint256 amount, uint256 shares); + + /** + * @notice Executes the supply operation + * @param params The parameters for the supply operation + */ + function execute(DataTypes.VaultActionParams memory params) external { + address underlyingAsset = IERC4626(params.vault).asset(); + uint256 amount = + params.amount == type(uint256).max ? IERC20(underlyingAsset).balanceOf(address(this)) : params.amount; + + if (amount == 0) return; + + SafeERC20.forceApprove(IERC20(underlyingAsset), address(IERC4626(params.vault)), amount); + + uint256 shares = IERC4626(params.vault).deposit(amount, address(this)); + + emit VaultSupplied(params.vault, amount, shares); + } +} diff --git a/src/modules/vault/VaultWithdrawModule.sol b/src/modules/vault/VaultWithdrawModule.sol new file mode 100644 index 0000000..3833a5c --- /dev/null +++ b/src/modules/vault/VaultWithdrawModule.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import {IERC4626} from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; +import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {DataTypes} from "../../common/DataTypes.sol"; + +/** + * @title VaultWithdrawModule + * @author Superlend + * @notice Module for withdrawing assets from a vault + * @dev Extends IERC4626 to provide vault withdraw functionality + */ +contract VaultWithdrawModule { + /** + * @notice Emitted when assets are withdrawn from a vault + * @param vault The address of the vault + * @param amount The amount of the underlying asset withdrawn + * @param shares The amount of shares withdrawn + */ + event VaultWithdrawn(address indexed vault, uint256 amount, uint256 shares); + + /** + * @notice Executes the withdraw operation + * @param params The parameters for the withdraw operation + */ + function execute(DataTypes.VaultActionParams memory params) external { + uint256 amount = + params.amount == type(uint256).max ? IERC4626(params.vault).maxWithdraw(address(this)) : params.amount; + + if (amount == 0) return; + + uint256 shares = IERC4626(params.vault).withdraw(amount, address(this), address(this)); + + emit VaultWithdrawn(params.vault, amount, shares); + } +} From a72f0c1930b537cc5208b9a346859a11327df495 Mon Sep 17 00:00:00 2001 From: Priyam Anand Date: Fri, 23 Jan 2026 14:33:22 +0530 Subject: [PATCH 04/21] test: refactored aave module tests --- foundry.toml | 1 + test/core/TestBase.sol | 120 +- test/core/TestEnv.sol | 124 ++ .../core/integration/AavePreliquidation.t.sol | 236 ++-- test/core/integration/DepositManager.t.sol | 498 +++---- test/core/integration/FallbackHandler.t.sol | 58 +- test/core/integration/InstantWithdraw.t.sol | 148 +- test/core/integration/IntegrationBase.sol | 1142 ++++++++-------- test/core/integration/Migration.t.sol | 964 ++++++------- test/core/integration/Seed.t.sol | 126 +- test/core/integration/WithdrawManager.t.sol | 744 +++++----- test/core/unit/AccountantAaveV3.t.sol | 678 +++++----- test/core/unit/DepositManager.t.sol | 502 +++---- test/core/unit/ModuleRegistry.t.sol | 512 +++---- test/core/unit/Superloop.t.sol | 840 ++++++------ test/core/unit/UniversalAccountant.t.sol | 534 ++++---- test/core/unit/WithdrawManager.t.sol | 720 +++++----- test/helpers/VaultRouter.t.sol | 1204 ++++++++--------- test/helpers/integration/VaultRouter.t.sol | 436 +++--- test/modules/aave/AaveV3BorrowModule.t.sol | 36 +- test/modules/aave/AaveV3EmodeModule.t.sol | 10 +- test/modules/aave/AaveV3FlashloanModule.t.sol | 28 +- test/modules/aave/AaveV3RepayModule.t.sol | 40 +- test/modules/aave/AaveV3SupplyModule.t.sol | 29 +- test/modules/aave/AaveV3WithdrawModule.t.sol | 29 +- test/modules/dex/UniversalDexModule.t.sol | 342 ++--- .../AaveV3PreliquidationFallbackHandler.t.sol | 776 +++++------ test/modules/helpers/UnwrapModule.t.sol | 208 +-- test/modules/helpers/WrapModule.t.sol | 246 ++-- .../hyperliquid/HyperbeatStakingModule.t.sol | 158 +-- .../HyperliquidStakingModule.t.sol | 164 +-- .../hyperliquid/KinetiqStakingModule.t.sol | 158 +-- test/modules/stake/hyperliquid/TestBase.sol | 248 ++-- 33 files changed, 6099 insertions(+), 5960 deletions(-) create mode 100644 test/core/TestEnv.sol diff --git a/foundry.toml b/foundry.toml index 65ccabb..b10e7e5 100644 --- a/foundry.toml +++ b/foundry.toml @@ -10,6 +10,7 @@ viaIR = true [rpc_endpoints] etherlink="https://node.mainnet.etherlink.com" hyperevm="https://rpc.hyperlend.finance" +mainnet="https://ethereum-rpc.publicnode.com" [etherscan] etherlink={ key = "NO_KEY", chain = 42793, url= "https://explorer.etherlink.com/api/" } diff --git a/test/core/TestBase.sol b/test/core/TestBase.sol index 62497bc..63af07c 100644 --- a/test/core/TestBase.sol +++ b/test/core/TestBase.sol @@ -2,7 +2,8 @@ pragma solidity ^0.8.13; -import {Test, console} from "forge-std/Test.sol"; +import {console} from "forge-std/Test.sol"; +import {TestEnv} from "./TestEnv.sol"; import {Vm} from "forge-std/Vm.sol"; import {Superloop} from "../../src/core/Superloop/Superloop.sol"; import {DataTypes} from "../../src/common/DataTypes.sol"; @@ -30,27 +31,31 @@ import {WithdrawManagerCallbackHandler} from "../../src/modules/callback/Withdra import {UnwrapModule} from "../../src/modules/helper/UnwrapModule.sol"; import {WrapModule} from "../../src/modules/helper/WrapModule.sol"; import {AaveV3PreliquidationFallbackHandler} from "../../src/modules/fallback/AaveV3PreliquidationFallbackHandler.sol"; - -contract TestBase is Test { - address public constant ST_XTZ = 0x01F07f4d78d47A64F4C3B2b65f513f15Be6E1854; - address public constant XTZ = 0xc9B53AB2679f573e480d01e0f49e2B5CFB7a3EAb; - address public constant LBTC = 0xecAc9C5F704e954931349Da37F60E39f515c11c1; - address public constant WBTC = 0xbFc94CD2B1E55999Cfc7347a9313e88702B83d0F; - address public constant AAVE_V3_POOL_ADDRESSES_PROVIDER = 0x5ccF60c7E10547c5389E9cBFf543E5D0Db9F4feC; - address public constant AAVE_V3_POOL_DATA_PROVIDER = 0x99e8269dDD5c7Af0F1B3973A591b47E8E001BCac; - address public constant AAVE_V3_PRICE_ORACLE = 0xeCF313dE38aA85EF618D06D1A602bAa917D62525; - address public constant POOL = 0x3bD16D195786fb2F509f2E2D7F69920262EF114D; - address public constant XTZ_WHALE = 0x008ae222661B6A42e3A097bd7AAC15412829106b; - address public constant STXTZ_WHALE = 0x65142dEC2969f1a3083Ad31541Ef4B73871C8C9B; - address public constant USDT_WHALE = 0x998098A1B2E95e2b8f15360676428EdFd976861f; - uint256 public constant PERFORMANCE_FEE = 2000; // 20% - address public constant USDT = 0x2C03058C8AFC06713be23e58D2febC8337dbfE6A; - address public constant USDC = 0x796Ea11Fa2dD751eD01b53C372fFDB4AAa8f00F9; - address public constant WXTZ = 0xc9B53AB2679f573e480d01e0f49e2B5CFB7a3EAb; - address public constant ROUTER = 0xbfe9C246A5EdB4F021C8910155EC93e7CfDaB7a0; - address public constant USDC_WHALE = 0xd03bfdF9B26DB1e6764724d914d7c3d18106a9Fb; - address public constant POOL_CONFIGURATOR = 0x30F6880Bb1cF780a49eB4Ef64E64585780AAe060; - address public constant POOL_ADMIN = 0x669bd328f6C494949Ed9fB2dc8021557A6Dd005f; +import {IERC20Metadata} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol"; + +abstract contract TestBase is TestEnv { + // address public constant AAVE_V3_POOL_ADDRESSES_PROVIDER = + // 0x5ccF60c7E10547c5389E9cBFf543E5D0Db9F4feC; + // address public constant AAVE_V3_POOL_DATA_PROVIDER = + // 0x99e8269dDD5c7Af0F1B3973A591b47E8E001BCac; + // address public constant AAVE_V3_PRICE_ORACLE = + // 0xeCF313dE38aA85EF618D06D1A602bAa917D62525; + // address public constant POOL = 0x3bD16D195786fb2F509f2E2D7F69920262EF114D; + // address public constant XTZ_WHALE = + // 0x008ae222661B6A42e3A097bd7AAC15412829106b; + // address public constant STXTZ_WHALE = + // 0x65142dEC2969f1a3083Ad31541Ef4B73871C8C9B; + // address public constant USDT_WHALE = + // 0x998098A1B2E95e2b8f15360676428EdFd976861f; + // address public constant USDT = 0x2C03058C8AFC06713be23e58D2febC8337dbfE6A; + // address public constant USDC = 0x796Ea11Fa2dD751eD01b53C372fFDB4AAa8f00F9; + // address public constant ROUTER = 0xbfe9C246A5EdB4F021C8910155EC93e7CfDaB7a0; + // address public constant USDC_WHALE = + // 0xd03bfdF9B26DB1e6764724d914d7c3d18106a9Fb; + // address public constant POOL_CONFIGURATOR = + // 0x30F6880Bb1cF780a49eB4Ef64E64585780AAe060; + // address public constant POOL_ADMIN = + // 0x669bd328f6C494949Ed9fB2dc8021557A6Dd005f; uint256 public constant WAD = 10 ** 18; uint256 public constant BPS = 10000; @@ -62,6 +67,8 @@ contract TestBase is Test { uint256 public constant PRE_IF2 = ((80 + BPS) * WAD) / BPS; uint256 public LLTV = (9600 * WAD) / BPS; + TestEnvironment public environment; + address public admin; address public treasury; @@ -95,8 +102,13 @@ contract TestBase is Test { IPoolDataProvider public poolDataProvider; IPool public pool; - function setUp() public virtual { - vm.createSelectFork("etherlink"); + function setUp() public virtual override { + super.setUp(); + + uint256 envIndex = 2; // TODO: move this to config + environment = testEnvironments[envIndex]; + + vm.createSelectFork(environment.chainName); admin = makeAddr("admin"); treasury = makeAddr("treasury"); @@ -104,46 +116,52 @@ contract TestBase is Test { moduleRegistry = new SuperloopModuleRegistry(); mockModule = makeAddr("mockModule"); moduleRegistry.setModule("MockModule", mockModule); - poolDataProvider = IPoolDataProvider(AAVE_V3_POOL_DATA_PROVIDER); - pool = IPool(POOL); + poolDataProvider = IPoolDataProvider(environment.poolDataProvider); + pool = IPool(environment.pool); vm.stopPrank(); vm.label(admin, "admin"); vm.label(treasury, "treasury"); vm.label(mockModule, "mockModule"); vm.label(address(moduleRegistry), "moduleRegistry"); - vm.label(XTZ, "XTZ"); - vm.label(ST_XTZ, "ST_XTZ"); - vm.label(AAVE_V3_POOL_ADDRESSES_PROVIDER, "AAVE_V3_POOL_ADDRESSES_PROVIDER"); - vm.label(AAVE_V3_POOL_DATA_PROVIDER, "AAVE_V3_POOL_DATA_PROVIDER"); - vm.label(AAVE_V3_PRICE_ORACLE, "AAVE_V3_PRICE_ORACLE"); - vm.label(POOL, "POOL"); - vm.label(XTZ_WHALE, "XTZ_WHALE"); - vm.label(STXTZ_WHALE, "STXTZ_WHALE"); - vm.label(address(poolDataProvider), "poolDataProvider"); - vm.label(address(pool), "pool"); + for (uint256 i = 0; i < environment.lendAssets.length; i++) { + string memory symbol = IERC20Metadata(environment.lendAssets[i]).symbol(); + vm.label(environment.lendAssets[i], symbol); + } + for (uint256 i = 0; i < environment.borrowAssets.length; i++) { + string memory symbol = IERC20Metadata(environment.borrowAssets[i]).symbol(); + vm.label(environment.borrowAssets[i], symbol); + } + vm.label(environment.poolAddressesProvider, "AAVE_V3_POOL_ADDRESSES_PROVIDER"); + vm.label(environment.poolDataProvider, "AAVE_V3_POOL_DATA_PROVIDER"); + vm.label(environment.priceOracle, "AAVE_V3_PRICE_ORACLE"); + vm.label(environment.pool, "POOL"); + vm.label(environment.vaultAssetWhale, "VAULT_ASSET_WHALE"); + vm.label(environment.stablecoin, "STABLECOIN"); + vm.label(environment.stablecoinWhale, "STABLECOIN_WHALE"); + // vm.label(STXTZ_WHALE, "STXTZ_WHALE"); } function _deployModules() internal { - flashloanModule = new AaveV3FlashloanModule(AAVE_V3_POOL_ADDRESSES_PROVIDER); + flashloanModule = new AaveV3FlashloanModule(environment.poolAddressesProvider); moduleRegistry.setModule("AaveV3FlashloanModule", address(flashloanModule)); callbackHandler = new AaveV3CallbackHandler(); moduleRegistry.setModule("AaveV3CallbackHandler", address(callbackHandler)); - emodeModule = new AaveV3EmodeModule(AAVE_V3_POOL_ADDRESSES_PROVIDER); + emodeModule = new AaveV3EmodeModule(environment.poolAddressesProvider); moduleRegistry.setModule("AaveV3EmodeModule", address(emodeModule)); - supplyModule = new AaveV3SupplyModule(AAVE_V3_POOL_ADDRESSES_PROVIDER); + supplyModule = new AaveV3SupplyModule(environment.poolAddressesProvider); moduleRegistry.setModule("AaveV3SupplyModule", address(supplyModule)); - withdrawModule = new AaveV3WithdrawModule(AAVE_V3_POOL_ADDRESSES_PROVIDER); + withdrawModule = new AaveV3WithdrawModule(environment.poolAddressesProvider); moduleRegistry.setModule("AaveV3WithdrawModule", address(withdrawModule)); - borrowModule = new AaveV3BorrowModule(AAVE_V3_POOL_ADDRESSES_PROVIDER); + borrowModule = new AaveV3BorrowModule(environment.poolAddressesProvider); moduleRegistry.setModule("AaveV3BorrowModule", address(borrowModule)); - repayModule = new AaveV3RepayModule(AAVE_V3_POOL_ADDRESSES_PROVIDER); + repayModule = new AaveV3RepayModule(environment.poolAddressesProvider); moduleRegistry.setModule("AaveV3RepayModule", address(repayModule)); dexModule = new UniversalDexModule(); @@ -155,10 +173,10 @@ contract TestBase is Test { withdrawManagerCallbackHandler = new WithdrawManagerCallbackHandler(); moduleRegistry.setModule("WithdrawManagerCallbackHandler", address(withdrawManagerCallbackHandler)); - unwrapModule = new UnwrapModule(XTZ); + unwrapModule = new UnwrapModule(environment.vaultAsset); moduleRegistry.setModule("UnwrapModule", address(unwrapModule)); - wrapModule = new WrapModule(XTZ); + wrapModule = new WrapModule(environment.vaultAsset); moduleRegistry.setModule("WrapModule", address(wrapModule)); vm.label(address(flashloanModule), "flashloanModule"); @@ -179,7 +197,7 @@ contract TestBase is Test { { DataTypes.AaveV3AccountantPluginModuleInitData memory accountantPluginInitData = DataTypes .AaveV3AccountantPluginModuleInitData({ - poolAddressesProvider: AAVE_V3_POOL_ADDRESSES_PROVIDER, + poolAddressesProvider: environment.poolAddressesProvider, lendAssets: lendAssets, borrowAssets: borrowAssets }); @@ -228,14 +246,14 @@ contract TestBase is Test { function _deployPreliquidationFallbackHandler(address vault) internal { preliquidationFallbackHandler = new AaveV3PreliquidationFallbackHandler( - AAVE_V3_POOL_ADDRESSES_PROVIDER, + environment.poolAddressesProvider, vault, 2, 8, DataTypes.AaveV3PreliquidationParamsInit({ id: id, - lendReserve: ST_XTZ, - borrowReserve: XTZ, + lendReserve: environment.lendAssets[0], + borrowReserve: environment.borrowAssets[0], preLltv: PRE_LLTV, preCF1: PRE_CF1, preCF2: PRE_CF2, @@ -246,10 +264,4 @@ contract TestBase is Test { moduleRegistry.setModule("AaveV3PreliquidationFallbackHandler", address(preliquidationFallbackHandler)); vm.label(address(preliquidationFallbackHandler), "preliquidationFallbackHandler"); } - - function _singleAddressArray(address a) internal pure returns (address[] memory) { - address[] memory array = new address[](1); - array[0] = a; - return array; - } } diff --git a/test/core/TestEnv.sol b/test/core/TestEnv.sol new file mode 100644 index 0000000..9f655cc --- /dev/null +++ b/test/core/TestEnv.sol @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import {Test, console} from "forge-std/Test.sol"; + +abstract contract TestEnv is Test { + struct TestEnvironment { + uint256 chainId; + string chainName; + address vaultAsset; + uint8 vaultAssetDecimals; + address[] lendAssets; + address[] borrowAssets; + address poolAddressesProvider; + address poolDataProvider; + address priceOracle; + address pool; + address vaultAssetWhale; + address poolConfigurator; + address poolAdmin; + address router; + address stablecoin; + address stablecoinWhale; + } + + // etlk chain + address public constant ST_XTZ = 0x01F07f4d78d47A64F4C3B2b65f513f15Be6E1854; + address public constant WXTZ = 0xc9B53AB2679f573e480d01e0f49e2B5CFB7a3EAb; + address public constant LBTC = 0xecAc9C5F704e954931349Da37F60E39f515c11c1; + address public constant WBTC = 0xbFc94CD2B1E55999Cfc7347a9313e88702B83d0F; + + // eth mainnet chain + address public constant USDe = 0x4c9EDD5852cd905f086C759E8383e09bff1E68B3; + address public constant USDeWhale = 0x4F5923Fc5FD4a93352581b38B7cD26943012DECF; + address public constant sUSDe = 0x9D39A5DE30e57443BfF2A8307A4256c8797A3497; + address public constant USDC_ETH = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; + address public constant USDC_ETH_Whale = 0x98C23E9d8f34FEFb1B7BD6a91B7FF122F4e16F5c; + address USDT_ETH = 0xdAC17F958D2ee523a2206206994597C13D831ec7; + + // address public constant + uint256 public constant PERFORMANCE_FEE = 2000; // 20% + TestEnvironment[] internal testEnvironments; + + function setUp() public virtual { + // etherlink xtz + testEnvironments.push( + TestEnvironment({ + chainId: 42793, + chainName: "etherlink", + vaultAsset: WXTZ, + vaultAssetDecimals: 18, + lendAssets: _singleAddressArray(ST_XTZ), + borrowAssets: _singleAddressArray(WXTZ), + poolAddressesProvider: 0x5ccF60c7E10547c5389E9cBFf543E5D0Db9F4feC, + poolDataProvider: 0x99e8269dDD5c7Af0F1B3973A591b47E8E001BCac, + priceOracle: 0xeCF313dE38aA85EF618D06D1A602bAa917D62525, + pool: 0x3bD16D195786fb2F509f2E2D7F69920262EF114D, + vaultAssetWhale: 0x008ae222661B6A42e3A097bd7AAC15412829106b, + poolConfigurator: 0x30F6880Bb1cF780a49eB4Ef64E64585780AAe060, + poolAdmin: 0x669bd328f6C494949Ed9fB2dc8021557A6Dd005f, + router: 0xbfe9C246A5EdB4F021C8910155EC93e7CfDaB7a0, + stablecoin: 0x2C03058C8AFC06713be23e58D2febC8337dbfE6A, + stablecoinWhale: 0x998098A1B2E95e2b8f15360676428EdFd976861f + }) + ); + + // etherlink btc + testEnvironments.push( + TestEnvironment({ + chainId: 42793, + chainName: "etherlink", + vaultAsset: WBTC, + vaultAssetDecimals: 8, + lendAssets: _singleAddressArray(LBTC), + borrowAssets: _singleAddressArray(WBTC), + poolAddressesProvider: 0x5ccF60c7E10547c5389E9cBFf543E5D0Db9F4feC, + poolDataProvider: 0x99e8269dDD5c7Af0F1B3973A591b47E8E001BCac, + priceOracle: 0xeCF313dE38aA85EF618D06D1A602bAa917D62525, + pool: 0x3bD16D195786fb2F509f2E2D7F69920262EF114D, + vaultAssetWhale: 0xfCA0802cb10b3b134a91e07f03965f63eF4B23eA, + poolConfigurator: 0x30F6880Bb1cF780a49eB4Ef64E64585780AAe060, + poolAdmin: 0x669bd328f6C494949Ed9fB2dc8021557A6Dd005f, + router: 0xbfe9C246A5EdB4F021C8910155EC93e7CfDaB7a0, + stablecoin: 0x2C03058C8AFC06713be23e58D2febC8337dbfE6A, + stablecoinWhale: 0x998098A1B2E95e2b8f15360676428EdFd976861f + }) + ); + + // eth mainnet ethena + testEnvironments.push( + TestEnvironment({ + chainId: 1, + chainName: "mainnet", + vaultAsset: USDe, + vaultAssetDecimals: 18, + lendAssets: _twoAddressArray(USDe, sUSDe), + borrowAssets: _twoAddressArray(USDT_ETH, USDC_ETH), + poolAddressesProvider: 0x2f39d218133AFaB8F2B819B1066c7E434Ad94E9e, + poolDataProvider: 0x0a16f2FCC0D44FaE41cc54e079281D84A363bECD, + priceOracle: 0x54586bE62E3c3580375aE3723C145253060Ca0C2, + pool: 0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2, + vaultAssetWhale: USDeWhale, + poolConfigurator: 0x64b761D848206f447Fe2dd461b0c635Ec39EbB27, + poolAdmin: 0x72B8fD3eb0c08275b8B60F96aAb0C8a50Cb80EcA, + router: address(0), + stablecoin: USDC_ETH, + stablecoinWhale: USDC_ETH_Whale + }) + ); + } + + function _singleAddressArray(address a) internal pure returns (address[] memory) { + address[] memory array = new address[](1); + array[0] = a; + return array; + } + + function _twoAddressArray(address a, address b) internal pure returns (address[] memory) { + address[] memory array = new address[](2); + array[0] = a; + array[1] = b; + return array; + } +} diff --git a/test/core/integration/AavePreliquidation.t.sol b/test/core/integration/AavePreliquidation.t.sol index 71cec45..83576ed 100644 --- a/test/core/integration/AavePreliquidation.t.sol +++ b/test/core/integration/AavePreliquidation.t.sol @@ -1,118 +1,118 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.13; - -import {IntegrationBase} from "./IntegrationBase.sol"; -import {console} from "forge-std/console.sol"; -import {DataTypes} from "../../../src/common/DataTypes.sol"; -import {AaveV3PreliquidationFallbackHandler} from - "../../../src/modules/fallback/AaveV3PreliquidationFallbackHandler.sol"; -import {console} from "forge-std/console.sol"; -import {IPoolDataProvider} from "aave-v3-core/contracts/interfaces/IPoolDataProvider.sol"; -import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -import {IAaveOracle} from "aave-v3-core/contracts/interfaces/IAaveOracle.sol"; -import {IPoolAddressesProvider} from "aave-v3-core/contracts/interfaces/IPoolAddressesProvider.sol"; - -contract AavePreliquidationTest is IntegrationBase { - function setUp() public override { - super.setUp(); - - DataTypes.AaveV3EmodeParams memory params = DataTypes.AaveV3EmodeParams({emodeCategory: 3}); - - DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); - moduleExecutionData[0] = DataTypes.ModuleExecutionData({ - executionType: DataTypes.CallType.DELEGATECALL, - module: address(emodeModule), - data: abi.encodeWithSelector(emodeModule.execute.selector, params) - }); - - vm.startPrank(admin); - superloop.operate(moduleExecutionData); - - _deployPreliquidationFallbackHandler(address(superloop)); - bytes32 key1 = keccak256( - abi.encodePacked( - abi.encodeWithSelector(AaveV3PreliquidationFallbackHandler.preliquidate.selector), - id, - DataTypes.CallType.DELEGATECALL - ) - ); - bytes32 key2 = keccak256( - abi.encodePacked( - abi.encodeWithSelector(AaveV3PreliquidationFallbackHandler.preliquidationParams.selector), - id, - DataTypes.CallType.DELEGATECALL - ) - ); - - superloop.setFallbackHandler(key1, address(preliquidationFallbackHandler)); - superloop.setFallbackHandler(key2, address(preliquidationFallbackHandler)); - vm.stopPrank(); - } - - function test_aavePreliquidationParams() public view { - DataTypes.AaveV3PreliquidationParams memory params = AaveV3PreliquidationFallbackHandler(address(superloop)) - .preliquidationParams(id, DataTypes.CallType.DELEGATECALL); - - assertEq(params.lendReserve, ST_XTZ); - assertEq(params.borrowReserve, XTZ); - assertEq(params.Lltv, LLTV); - assertEq(params.preLltv, PRE_LLTV); - assertEq(params.preCF1, PRE_CF1); - assertEq(params.preCF2, PRE_CF2); - assertEq(params.preIF1, PRE_IF1); - assertEq(params.preIF2, PRE_IF2); - } - - function test_aavePreliquidation() public { - _createPartialDepositWithResolution(true); - - (,, uint256 currentVariableDebt,,,,,,) = - IPoolDataProvider(AAVE_V3_POOL_DATA_PROVIDER).getUserReserveData(XTZ, address(superloop)); - (uint256 currentATokenBalance,,,,,,,,) = - IPoolDataProvider(AAVE_V3_POOL_DATA_PROVIDER).getUserReserveData(ST_XTZ, address(superloop)); - - address liquidator = makeAddr("liquidator"); - deal(XTZ, liquidator, 1000 * XTZ_SCALE); - - uint256 userBalanceXTZBefore = IERC20(XTZ).balanceOf(liquidator); - uint256 userBalanceSTXTZBefore = IERC20(ST_XTZ).balanceOf(liquidator); - - vm.startPrank(liquidator); - IERC20(XTZ).approve(address(superloop), 1000 * XTZ_SCALE); - - AaveV3PreliquidationFallbackHandler(address(superloop)).preliquidate( - id, - DataTypes.CallType.DELEGATECALL, - DataTypes.AaveV3ExecutePreliquidationParams({user: address(superloop), debtToCover: currentVariableDebt}) - ); - vm.stopPrank(); - - (,, uint256 currentVariableDebtAfter,,,,,,) = - IPoolDataProvider(AAVE_V3_POOL_DATA_PROVIDER).getUserReserveData(XTZ, address(superloop)); - (uint256 currentATokenBalanceAfter,,,,,,,,) = - IPoolDataProvider(AAVE_V3_POOL_DATA_PROVIDER).getUserReserveData(ST_XTZ, address(superloop)); - - uint256 userBalanceXTZAfter = IERC20(XTZ).balanceOf(liquidator); - uint256 userBalanceSTXTZAfter = IERC20(ST_XTZ).balanceOf(liquidator); - uint256 debtRepaid = currentVariableDebt - currentVariableDebtAfter; - uint256 collateralWithdrawn = currentATokenBalance - currentATokenBalanceAfter; - - uint256 userBalanceXTZDiff = userBalanceXTZBefore - userBalanceXTZAfter; - uint256 userBalanceSTXTZDiff = userBalanceSTXTZAfter - userBalanceSTXTZBefore; - - assertApproxEqRel(debtRepaid, userBalanceXTZDiff, 1e18); - assertApproxEqRel(collateralWithdrawn, userBalanceSTXTZDiff, 1e18); - - IAaveOracle oracle = IAaveOracle(IPoolAddressesProvider(AAVE_V3_POOL_ADDRESSES_PROVIDER).getPriceOracle()); - uint256 stXtzPrice = oracle.getAssetPrice(ST_XTZ); - uint256 xtzPrice = oracle.getAssetPrice(XTZ); - - uint256 debtRepaidUsd = (userBalanceXTZDiff * xtzPrice) / (10 ** 18); - uint256 collateralWithdrawnUsd = (userBalanceSTXTZDiff * stXtzPrice) / (10 ** 6); - - uint256 incentiveWAD = (collateralWithdrawnUsd * WAD) / debtRepaidUsd; - assertGe(incentiveWAD, PRE_IF1); - assertLe(incentiveWAD, PRE_IF2); - } -} +// // SPDX-License-Identifier: MIT + +// pragma solidity ^0.8.13; + +// import {IntegrationBase} from "./IntegrationBase.sol"; +// import {console} from "forge-std/console.sol"; +// import {DataTypes} from "../../../src/common/DataTypes.sol"; +// import {AaveV3PreliquidationFallbackHandler} from +// "../../../src/modules/fallback/AaveV3PreliquidationFallbackHandler.sol"; +// import {console} from "forge-std/console.sol"; +// import {IPoolDataProvider} from "aave-v3-core/contracts/interfaces/IPoolDataProvider.sol"; +// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +// import {IAaveOracle} from "aave-v3-core/contracts/interfaces/IAaveOracle.sol"; +// import {IPoolAddressesProvider} from "aave-v3-core/contracts/interfaces/IPoolAddressesProvider.sol"; + +// contract AavePreliquidationTest is IntegrationBase { +// function setUp() public override { +// super.setUp(); + +// DataTypes.AaveV3EmodeParams memory params = DataTypes.AaveV3EmodeParams({emodeCategory: 3}); + +// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); +// moduleExecutionData[0] = DataTypes.ModuleExecutionData({ +// executionType: DataTypes.CallType.DELEGATECALL, +// module: address(emodeModule), +// data: abi.encodeWithSelector(emodeModule.execute.selector, params) +// }); + +// vm.startPrank(admin); +// superloop.operate(moduleExecutionData); + +// _deployPreliquidationFallbackHandler(address(superloop)); +// bytes32 key1 = keccak256( +// abi.encodePacked( +// abi.encodeWithSelector(AaveV3PreliquidationFallbackHandler.preliquidate.selector), +// id, +// DataTypes.CallType.DELEGATECALL +// ) +// ); +// bytes32 key2 = keccak256( +// abi.encodePacked( +// abi.encodeWithSelector(AaveV3PreliquidationFallbackHandler.preliquidationParams.selector), +// id, +// DataTypes.CallType.DELEGATECALL +// ) +// ); + +// superloop.setFallbackHandler(key1, address(preliquidationFallbackHandler)); +// superloop.setFallbackHandler(key2, address(preliquidationFallbackHandler)); +// vm.stopPrank(); +// } + +// function test_aavePreliquidationParams() public view { +// DataTypes.AaveV3PreliquidationParams memory params = AaveV3PreliquidationFallbackHandler(address(superloop)) +// .preliquidationParams(id, DataTypes.CallType.DELEGATECALL); + +// assertEq(params.lendReserve, ST_XTZ); +// assertEq(params.borrowReserve, XTZ); +// assertEq(params.Lltv, LLTV); +// assertEq(params.preLltv, PRE_LLTV); +// assertEq(params.preCF1, PRE_CF1); +// assertEq(params.preCF2, PRE_CF2); +// assertEq(params.preIF1, PRE_IF1); +// assertEq(params.preIF2, PRE_IF2); +// } + +// function test_aavePreliquidation() public { +// _createPartialDepositWithResolution(true); + +// (,, uint256 currentVariableDebt,,,,,,) = +// IPoolDataProvider(AAVE_V3_POOL_DATA_PROVIDER).getUserReserveData(XTZ, address(superloop)); +// (uint256 currentATokenBalance,,,,,,,,) = +// IPoolDataProvider(AAVE_V3_POOL_DATA_PROVIDER).getUserReserveData(ST_XTZ, address(superloop)); + +// address liquidator = makeAddr("liquidator"); +// deal(XTZ, liquidator, 1000 * XTZ_SCALE); + +// uint256 userBalanceXTZBefore = IERC20(XTZ).balanceOf(liquidator); +// uint256 userBalanceSTXTZBefore = IERC20(ST_XTZ).balanceOf(liquidator); + +// vm.startPrank(liquidator); +// IERC20(XTZ).approve(address(superloop), 1000 * XTZ_SCALE); + +// AaveV3PreliquidationFallbackHandler(address(superloop)).preliquidate( +// id, +// DataTypes.CallType.DELEGATECALL, +// DataTypes.AaveV3ExecutePreliquidationParams({user: address(superloop), debtToCover: currentVariableDebt}) +// ); +// vm.stopPrank(); + +// (,, uint256 currentVariableDebtAfter,,,,,,) = +// IPoolDataProvider(AAVE_V3_POOL_DATA_PROVIDER).getUserReserveData(XTZ, address(superloop)); +// (uint256 currentATokenBalanceAfter,,,,,,,,) = +// IPoolDataProvider(AAVE_V3_POOL_DATA_PROVIDER).getUserReserveData(ST_XTZ, address(superloop)); + +// uint256 userBalanceXTZAfter = IERC20(XTZ).balanceOf(liquidator); +// uint256 userBalanceSTXTZAfter = IERC20(ST_XTZ).balanceOf(liquidator); +// uint256 debtRepaid = currentVariableDebt - currentVariableDebtAfter; +// uint256 collateralWithdrawn = currentATokenBalance - currentATokenBalanceAfter; + +// uint256 userBalanceXTZDiff = userBalanceXTZBefore - userBalanceXTZAfter; +// uint256 userBalanceSTXTZDiff = userBalanceSTXTZAfter - userBalanceSTXTZBefore; + +// assertApproxEqRel(debtRepaid, userBalanceXTZDiff, 1e18); +// assertApproxEqRel(collateralWithdrawn, userBalanceSTXTZDiff, 1e18); + +// IAaveOracle oracle = IAaveOracle(IPoolAddressesProvider(AAVE_V3_POOL_ADDRESSES_PROVIDER).getPriceOracle()); +// uint256 stXtzPrice = oracle.getAssetPrice(ST_XTZ); +// uint256 xtzPrice = oracle.getAssetPrice(XTZ); + +// uint256 debtRepaidUsd = (userBalanceXTZDiff * xtzPrice) / (10 ** 18); +// uint256 collateralWithdrawnUsd = (userBalanceSTXTZDiff * stXtzPrice) / (10 ** 6); + +// uint256 incentiveWAD = (collateralWithdrawnUsd * WAD) / debtRepaidUsd; +// assertGe(incentiveWAD, PRE_IF1); +// assertLe(incentiveWAD, PRE_IF2); +// } +// } diff --git a/test/core/integration/DepositManager.t.sol b/test/core/integration/DepositManager.t.sol index 4340426..95129b0 100644 --- a/test/core/integration/DepositManager.t.sol +++ b/test/core/integration/DepositManager.t.sol @@ -1,290 +1,290 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.13; - -import {IntegrationBase} from "./IntegrationBase.sol"; -import {DataTypes} from "../../../src/common/DataTypes.sol"; -import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -import {DepositManager} from "../../../src/core/DepositManager/DepositManager.sol"; -import {console} from "forge-std/Test.sol"; -import {Errors} from "../../../src/common/Errors.sol"; - -contract DepositManagerTest is IntegrationBase { - bool depositAll = false; - DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; - - function setUp() public override { - super.setUp(); - - DataTypes.AaveV3EmodeParams memory params = DataTypes.AaveV3EmodeParams({emodeCategory: 3}); - - DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); - moduleExecutionData[0] = DataTypes.ModuleExecutionData({ - executionType: DataTypes.CallType.DELEGATECALL, - module: address(emodeModule), - data: abi.encodeWithSelector(emodeModule.execute.selector, params) - }); - - vm.prank(admin); - superloop.operate(moduleExecutionData); - } - - function test_initialize() public view { - assertEq(depositManager.vault(), address(superloop)); - assertEq(depositManager.asset(), XTZ); - assertEq(depositManager.nextDepositRequestId(), 1); - } - - function test_requestDeposit_MinimumDepositAmountNotMet() public { - uint256 depositAmount = 99; - - vm.startPrank(user1); - vm.expectRevert(bytes(Errors.INVALID_AMOUNT)); - depositManager.requestDeposit(depositAmount, address(0)); - vm.stopPrank(); - } - - function test_resolveDepositRequestResolution() public { - uint256 exchangeRateBefore = superloop.convertToAssets(ONE_SHARE); - uint256 depositAmount = 100 * XTZ_SCALE; - _makeDepositRequest(depositAmount, user1, true); - - // build the operate call - uint256 supplyAmount = 150 * STXTZ_SCALE; - uint256 borrowAmount = 60 * XTZ_SCALE; - uint256 swapAmount = borrowAmount + depositAmount; - uint256 supplyAmountWithPremium = supplyAmount + (supplyAmount * 1) / 10000; - - DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](3); - moduleExecutionData[0] = _supplyCall(ST_XTZ, supplyAmount); - moduleExecutionData[1] = _borrowCall(XTZ, borrowAmount); - - moduleExecutionData[2] = - _swapCallExactOutCurve(XTZ, ST_XTZ, XTZ_STXTZ_POOL, swapAmount, supplyAmountWithPremium, XTZ_STXTZ_SWAP); - - DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); - intermediateExecutionData[0] = _flashloanCall(ST_XTZ, supplyAmount, abi.encode(moduleExecutionData)); +// // SPDX-License-Identifier: MIT + +// pragma solidity ^0.8.13; + +// import {IntegrationBase} from "./IntegrationBase.sol"; +// import {DataTypes} from "../../../src/common/DataTypes.sol"; +// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +// import {DepositManager} from "../../../src/core/DepositManager/DepositManager.sol"; +// import {console} from "forge-std/Test.sol"; +// import {Errors} from "../../../src/common/Errors.sol"; + +// contract DepositManagerTest is IntegrationBase { +// bool depositAll = false; +// DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; + +// function setUp() public override { +// super.setUp(); + +// DataTypes.AaveV3EmodeParams memory params = DataTypes.AaveV3EmodeParams({emodeCategory: 3}); + +// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); +// moduleExecutionData[0] = DataTypes.ModuleExecutionData({ +// executionType: DataTypes.CallType.DELEGATECALL, +// module: address(emodeModule), +// data: abi.encodeWithSelector(emodeModule.execute.selector, params) +// }); + +// vm.prank(admin); +// superloop.operate(moduleExecutionData); +// } + +// function test_initialize() public view { +// assertEq(depositManager.vault(), address(superloop)); +// assertEq(depositManager.asset(), XTZ); +// assertEq(depositManager.nextDepositRequestId(), 1); +// } + +// function test_requestDeposit_MinimumDepositAmountNotMet() public { +// uint256 depositAmount = 99; + +// vm.startPrank(user1); +// vm.expectRevert(bytes(Errors.INVALID_AMOUNT)); +// depositManager.requestDeposit(depositAmount, address(0)); +// vm.stopPrank(); +// } + +// function test_resolveDepositRequestResolution() public { +// uint256 exchangeRateBefore = superloop.convertToAssets(ONE_SHARE); +// uint256 depositAmount = 100 * XTZ_SCALE; +// _makeDepositRequest(depositAmount, user1, true); + +// // build the operate call +// uint256 supplyAmount = 150 * STXTZ_SCALE; +// uint256 borrowAmount = 60 * XTZ_SCALE; +// uint256 swapAmount = borrowAmount + depositAmount; +// uint256 supplyAmountWithPremium = supplyAmount + (supplyAmount * 1) / 10000; + +// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](3); +// moduleExecutionData[0] = _supplyCall(ST_XTZ, supplyAmount); +// moduleExecutionData[1] = _borrowCall(XTZ, borrowAmount); + +// moduleExecutionData[2] = +// _swapCallExactOutCurve(XTZ, ST_XTZ, XTZ_STXTZ_POOL, swapAmount, supplyAmountWithPremium, XTZ_STXTZ_SWAP); + +// DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); +// intermediateExecutionData[0] = _flashloanCall(ST_XTZ, supplyAmount, abi.encode(moduleExecutionData)); - DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); - finalExecutionData[0] = _resolveDepositRequestsCall(XTZ, depositAmount, abi.encode(intermediateExecutionData)); - - vm.prank(admin); - superloop.operate(finalExecutionData); - - // user 1 should get shares - DataTypes.DepositRequestData memory depositRequest1 = depositManager.depositRequest(1); - uint256 user1Shares = superloop.balanceOf(user1); - assertEq(user1Shares, depositRequest1.sharesMinted); - - // deposit maanger should not have any xtz now, resolution ptr must have increased, pendingDepsotAmout should be 0 - assertEq(IERC20(XTZ).balanceOf(address(depositManager)), 0); - assertEq(depositManager.totalPendingDeposits(), 0); - assertEq(depositManager.resolutionIdPointer(), 2); - - // exchange rate before should equal to exchange rate after - uint256 exchangeRateAfter = superloop.convertToAssets(ONE_SHARE); - assertTrue( - exchangeRateAfter > exchangeRateBefore - ? exchangeRateAfter - exchangeRateBefore < 100 - : exchangeRateBefore - exchangeRateAfter < 100 - ); - } - - function test_instantDepositWithLimit() public { - _createPartialDepositWithResolution(depositAll); +// DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); +// finalExecutionData[0] = _resolveDepositRequestsCall(XTZ, depositAmount, abi.encode(intermediateExecutionData)); + +// vm.prank(admin); +// superloop.operate(finalExecutionData); + +// // user 1 should get shares +// DataTypes.DepositRequestData memory depositRequest1 = depositManager.depositRequest(1); +// uint256 user1Shares = superloop.balanceOf(user1); +// assertEq(user1Shares, depositRequest1.sharesMinted); + +// // deposit maanger should not have any xtz now, resolution ptr must have increased, pendingDepsotAmout should be 0 +// assertEq(IERC20(XTZ).balanceOf(address(depositManager)), 0); +// assertEq(depositManager.totalPendingDeposits(), 0); +// assertEq(depositManager.resolutionIdPointer(), 2); + +// // exchange rate before should equal to exchange rate after +// uint256 exchangeRateAfter = superloop.convertToAssets(ONE_SHARE); +// assertTrue( +// exchangeRateAfter > exchangeRateBefore +// ? exchangeRateAfter - exchangeRateBefore < 100 +// : exchangeRateBefore - exchangeRateAfter < 100 +// ); +// } + +// function test_instantDepositWithLimit() public { +// _createPartialDepositWithResolution(depositAll); - // i should be able to do an instant deposit of 0.001 xtz - uint256 user1SharesBalanceBefore = superloop.balanceOf(user1); - deal(XTZ, user1, XTZ_SCALE); - vm.startPrank(user1); - IERC20(XTZ).approve(address(superloop), XTZ_SCALE); - superloop.deposit(XTZ_SCALE / 1000, user1); - uint256 user1SharesBalanceAfter = superloop.balanceOf(user1); +// // i should be able to do an instant deposit of 0.001 xtz +// uint256 user1SharesBalanceBefore = superloop.balanceOf(user1); +// deal(XTZ, user1, XTZ_SCALE); +// vm.startPrank(user1); +// IERC20(XTZ).approve(address(superloop), XTZ_SCALE); +// superloop.deposit(XTZ_SCALE / 1000, user1); +// uint256 user1SharesBalanceAfter = superloop.balanceOf(user1); - assertTrue(user1SharesBalanceAfter > user1SharesBalanceBefore); +// assertTrue(user1SharesBalanceAfter > user1SharesBalanceBefore); - // i should not be able to do an instant deposit of 100 xtz due to cash reserve - deal(XTZ, user1, 100 * XTZ_SCALE); - vm.startPrank(user1); - IERC20(XTZ).approve(address(superloop), 100 * XTZ_SCALE); - vm.expectRevert(bytes(Errors.INSUFFICIENT_CASH_SHORTFALL)); - superloop.deposit(100 * XTZ_SCALE, user1); - vm.stopPrank(); - } +// // i should not be able to do an instant deposit of 100 xtz due to cash reserve +// deal(XTZ, user1, 100 * XTZ_SCALE); +// vm.startPrank(user1); +// IERC20(XTZ).approve(address(superloop), 100 * XTZ_SCALE); +// vm.expectRevert(bytes(Errors.INSUFFICIENT_CASH_SHORTFALL)); +// superloop.deposit(100 * XTZ_SCALE, user1); +// vm.stopPrank(); +// } - function test_resolveDepositRequestWithPartials() public { - _createPartialDepositWithResolution(depositAll); // 300 worth of depostits, 150 pending +// function test_resolveDepositRequestWithPartials() public { +// _createPartialDepositWithResolution(depositAll); // 300 worth of depostits, 150 pending - // try to operate again +// // try to operate again - uint256 depositAmount_secondBatch = 90 * XTZ_SCALE; - uint256 supplyAmount = 180 * STXTZ_SCALE; - uint256 borrowAmount = 110 * XTZ_SCALE; - uint256 swapAmount = borrowAmount + depositAmount_secondBatch; - uint256 supplyAmountWithPremium = supplyAmount + (supplyAmount * 1) / 10000; - - DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](3); - moduleExecutionData[0] = _supplyCall(ST_XTZ, supplyAmount); - moduleExecutionData[1] = _borrowCall(XTZ, borrowAmount); - - moduleExecutionData[2] = - _swapCallExactOutCurve(XTZ, ST_XTZ, XTZ_STXTZ_POOL, swapAmount, supplyAmountWithPremium, XTZ_STXTZ_SWAP); - - DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); - intermediateExecutionData[0] = _flashloanCall(ST_XTZ, supplyAmount, abi.encode(moduleExecutionData)); - - DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); - finalExecutionData[0] = - _resolveDepositRequestsCall(XTZ, depositAmount_secondBatch, abi.encode(intermediateExecutionData)); - - uint256 exchangeRateBefore = superloop.convertToAssets(ONE_SHARE); - DataTypes.DepositRequestData memory depositRequest2_before = depositManager.depositRequest(2); - - vm.prank(admin); - superloop.operate(finalExecutionData); - - uint256 exchangeRateAfter = superloop.convertToAssets(ONE_SHARE); - DataTypes.DepositRequestData memory depositRequest2_after = depositManager.depositRequest(2); - - assertApproxEqAbs(exchangeRateBefore, exchangeRateAfter, 1000); // 1000 is the precision - - assertTrue(depositRequest2_after.sharesMinted > depositRequest2_before.sharesMinted); - // deposit request 2 should be fully processed, deposit request 3 should be partially processed - assertTrue(superloop.balanceOf(user3) > 0); - - // pending deposits should be 150 xtz - assertEq(depositManager.totalPendingDeposits(), 60 * XTZ_SCALE); +// uint256 depositAmount_secondBatch = 90 * XTZ_SCALE; +// uint256 supplyAmount = 180 * STXTZ_SCALE; +// uint256 borrowAmount = 110 * XTZ_SCALE; +// uint256 swapAmount = borrowAmount + depositAmount_secondBatch; +// uint256 supplyAmountWithPremium = supplyAmount + (supplyAmount * 1) / 10000; + +// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](3); +// moduleExecutionData[0] = _supplyCall(ST_XTZ, supplyAmount); +// moduleExecutionData[1] = _borrowCall(XTZ, borrowAmount); + +// moduleExecutionData[2] = +// _swapCallExactOutCurve(XTZ, ST_XTZ, XTZ_STXTZ_POOL, swapAmount, supplyAmountWithPremium, XTZ_STXTZ_SWAP); + +// DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); +// intermediateExecutionData[0] = _flashloanCall(ST_XTZ, supplyAmount, abi.encode(moduleExecutionData)); + +// DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); +// finalExecutionData[0] = +// _resolveDepositRequestsCall(XTZ, depositAmount_secondBatch, abi.encode(intermediateExecutionData)); + +// uint256 exchangeRateBefore = superloop.convertToAssets(ONE_SHARE); +// DataTypes.DepositRequestData memory depositRequest2_before = depositManager.depositRequest(2); + +// vm.prank(admin); +// superloop.operate(finalExecutionData); + +// uint256 exchangeRateAfter = superloop.convertToAssets(ONE_SHARE); +// DataTypes.DepositRequestData memory depositRequest2_after = depositManager.depositRequest(2); + +// assertApproxEqAbs(exchangeRateBefore, exchangeRateAfter, 1000); // 1000 is the precision + +// assertTrue(depositRequest2_after.sharesMinted > depositRequest2_before.sharesMinted); +// // deposit request 2 should be fully processed, deposit request 3 should be partially processed +// assertTrue(superloop.balanceOf(user3) > 0); + +// // pending deposits should be 150 xtz +// assertEq(depositManager.totalPendingDeposits(), 60 * XTZ_SCALE); - // resolution id pointer should be 2 - assertEq(depositManager.resolutionIdPointer(), 3); +// // resolution id pointer should be 2 +// assertEq(depositManager.resolutionIdPointer(), 3); - // deposit request 2 should be partially processed - DataTypes.DepositRequestData memory depositRequest3 = depositManager.depositRequest(3); - assertEq(uint256(depositRequest3.state), uint256(DataTypes.RequestProcessingState.PARTIALLY_PROCESSED)); - assertEq(depositRequest3.amountProcessed, 40 * XTZ_SCALE); +// // deposit request 2 should be partially processed +// DataTypes.DepositRequestData memory depositRequest3 = depositManager.depositRequest(3); +// assertEq(uint256(depositRequest3.state), uint256(DataTypes.RequestProcessingState.PARTIALLY_PROCESSED)); +// assertEq(depositRequest3.amountProcessed, 40 * XTZ_SCALE); - // deposit another batch, with 60 xtz resolving a request partially +// // deposit another batch, with 60 xtz resolving a request partially - uint256 depositAmount_thirdBatch = 60 * XTZ_SCALE; - uint256 supplyAmountSecondBatch = 120 * STXTZ_SCALE; - uint256 borrowAmountSecondBatch = 75 * XTZ_SCALE; - uint256 swapAmountSecondBatch = borrowAmountSecondBatch + depositAmount_thirdBatch; - uint256 supplyAmountWithPremiumSecondBatch = supplyAmountSecondBatch + (supplyAmountSecondBatch * 1) / 10000; +// uint256 depositAmount_thirdBatch = 60 * XTZ_SCALE; +// uint256 supplyAmountSecondBatch = 120 * STXTZ_SCALE; +// uint256 borrowAmountSecondBatch = 75 * XTZ_SCALE; +// uint256 swapAmountSecondBatch = borrowAmountSecondBatch + depositAmount_thirdBatch; +// uint256 supplyAmountWithPremiumSecondBatch = supplyAmountSecondBatch + (supplyAmountSecondBatch * 1) / 10000; - DataTypes.ModuleExecutionData[] memory moduleExecutionDataSecondBatch = new DataTypes.ModuleExecutionData[](3); - moduleExecutionDataSecondBatch[0] = _supplyCall(ST_XTZ, supplyAmountSecondBatch); - moduleExecutionDataSecondBatch[1] = _borrowCall(XTZ, borrowAmountSecondBatch); +// DataTypes.ModuleExecutionData[] memory moduleExecutionDataSecondBatch = new DataTypes.ModuleExecutionData[](3); +// moduleExecutionDataSecondBatch[0] = _supplyCall(ST_XTZ, supplyAmountSecondBatch); +// moduleExecutionDataSecondBatch[1] = _borrowCall(XTZ, borrowAmountSecondBatch); - moduleExecutionDataSecondBatch[2] = _swapCallExactOutCurve( - XTZ, ST_XTZ, XTZ_STXTZ_POOL, swapAmountSecondBatch, supplyAmountWithPremiumSecondBatch, XTZ_STXTZ_SWAP - ); +// moduleExecutionDataSecondBatch[2] = _swapCallExactOutCurve( +// XTZ, ST_XTZ, XTZ_STXTZ_POOL, swapAmountSecondBatch, supplyAmountWithPremiumSecondBatch, XTZ_STXTZ_SWAP +// ); - DataTypes.ModuleExecutionData[] memory intermediateExecutionDataSecondBatch = - new DataTypes.ModuleExecutionData[](1); - intermediateExecutionDataSecondBatch[0] = - _flashloanCall(ST_XTZ, supplyAmountSecondBatch, abi.encode(moduleExecutionDataSecondBatch)); +// DataTypes.ModuleExecutionData[] memory intermediateExecutionDataSecondBatch = +// new DataTypes.ModuleExecutionData[](1); +// intermediateExecutionDataSecondBatch[0] = +// _flashloanCall(ST_XTZ, supplyAmountSecondBatch, abi.encode(moduleExecutionDataSecondBatch)); - DataTypes.ModuleExecutionData[] memory finalExecutionDataSecondBatch = new DataTypes.ModuleExecutionData[](1); - finalExecutionDataSecondBatch[0] = - _resolveDepositRequestsCall(XTZ, depositAmount_thirdBatch, abi.encode(intermediateExecutionDataSecondBatch)); +// DataTypes.ModuleExecutionData[] memory finalExecutionDataSecondBatch = new DataTypes.ModuleExecutionData[](1); +// finalExecutionDataSecondBatch[0] = +// _resolveDepositRequestsCall(XTZ, depositAmount_thirdBatch, abi.encode(intermediateExecutionDataSecondBatch)); - uint256 exchangeRateBeforeSecondBatch = superloop.convertToAssets(ONE_SHARE); +// uint256 exchangeRateBeforeSecondBatch = superloop.convertToAssets(ONE_SHARE); - vm.prank(admin); - superloop.operate(finalExecutionDataSecondBatch); +// vm.prank(admin); +// superloop.operate(finalExecutionDataSecondBatch); - uint256 exchangeRateAfterSecondBatch = superloop.convertToAssets(ONE_SHARE); +// uint256 exchangeRateAfterSecondBatch = superloop.convertToAssets(ONE_SHARE); - assertApproxEqAbs(exchangeRateBeforeSecondBatch, exchangeRateAfterSecondBatch, 1000); +// assertApproxEqAbs(exchangeRateBeforeSecondBatch, exchangeRateAfterSecondBatch, 1000); - // pending deposits should be 150 xtz - assertEq(depositManager.totalPendingDeposits(), 0); +// // pending deposits should be 150 xtz +// assertEq(depositManager.totalPendingDeposits(), 0); - // resolution id pointer should be 2 - assertEq(depositManager.resolutionIdPointer(), 4); +// // resolution id pointer should be 2 +// assertEq(depositManager.resolutionIdPointer(), 4); - depositRequest3 = depositManager.depositRequest(3); - assertEq(uint256(depositRequest3.state), uint256(DataTypes.RequestProcessingState.FULLY_PROCESSED)); - } +// depositRequest3 = depositManager.depositRequest(3); +// assertEq(uint256(depositRequest3.state), uint256(DataTypes.RequestProcessingState.FULLY_PROCESSED)); +// } - function test_resolveDepositRequestWithCancellation() public { - _createPartialDepositWithResolution(depositAll); // 300 worth of depostits, 150 pending +// function test_resolveDepositRequestWithCancellation() public { +// _createPartialDepositWithResolution(depositAll); // 300 worth of depostits, 150 pending - // user1 should not be able to cancel deposit request 1 because it's already processed - vm.startPrank(user1); - vm.expectRevert(bytes(Errors.DEPOSIT_REQUEST_ALREADY_PROCESSED)); - depositManager.cancelDepositRequest(1); - vm.stopPrank(); +// // user1 should not be able to cancel deposit request 1 because it's already processed +// vm.startPrank(user1); +// vm.expectRevert(bytes(Errors.DEPOSIT_REQUEST_ALREADY_PROCESSED)); +// depositManager.cancelDepositRequest(1); +// vm.stopPrank(); - // cancel deposit request 2 => it should be partially cancelled and user 2 should get back the remaining amount - uint256 user2BalanceBefore = IERC20(XTZ).balanceOf(user2); - vm.startPrank(user2); - depositManager.cancelDepositRequest(2); - vm.stopPrank(); - // user 2 should get back the remaining amount - uint256 user2BalanceAfter = IERC20(XTZ).balanceOf(user2); - assertEq(user2BalanceAfter - user2BalanceBefore, 50 * XTZ_SCALE); - // deposit request 2 should be partially cancelled - DataTypes.DepositRequestData memory depositRequest = depositManager.depositRequest(2); - assertEq(uint256(depositRequest.state), uint256(DataTypes.RequestProcessingState.PARTIALLY_CANCELLED)); - assertEq(depositRequest.amountProcessed, 50 * XTZ_SCALE); +// // cancel deposit request 2 => it should be partially cancelled and user 2 should get back the remaining amount +// uint256 user2BalanceBefore = IERC20(XTZ).balanceOf(user2); +// vm.startPrank(user2); +// depositManager.cancelDepositRequest(2); +// vm.stopPrank(); +// // user 2 should get back the remaining amount +// uint256 user2BalanceAfter = IERC20(XTZ).balanceOf(user2); +// assertEq(user2BalanceAfter - user2BalanceBefore, 50 * XTZ_SCALE); +// // deposit request 2 should be partially cancelled +// DataTypes.DepositRequestData memory depositRequest = depositManager.depositRequest(2); +// assertEq(uint256(depositRequest.state), uint256(DataTypes.RequestProcessingState.PARTIALLY_CANCELLED)); +// assertEq(depositRequest.amountProcessed, 50 * XTZ_SCALE); - // pending deposits should be 100 xtz - assertEq(depositManager.totalPendingDeposits(), 100 * XTZ_SCALE); +// // pending deposits should be 100 xtz +// assertEq(depositManager.totalPendingDeposits(), 100 * XTZ_SCALE); - // try to operate again with 75 xtz => request 3 should be partially processed - uint256 depositAmount_secondBatch = 75 * XTZ_SCALE; - uint256 supplyAmount = 150 * STXTZ_SCALE; - uint256 borrowAmount = 85 * XTZ_SCALE; - uint256 swapAmount = borrowAmount + depositAmount_secondBatch; - uint256 supplyAmountWithPremium = supplyAmount + (supplyAmount * 1) / 10000; +// // try to operate again with 75 xtz => request 3 should be partially processed +// uint256 depositAmount_secondBatch = 75 * XTZ_SCALE; +// uint256 supplyAmount = 150 * STXTZ_SCALE; +// uint256 borrowAmount = 85 * XTZ_SCALE; +// uint256 swapAmount = borrowAmount + depositAmount_secondBatch; +// uint256 supplyAmountWithPremium = supplyAmount + (supplyAmount * 1) / 10000; - DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](3); - moduleExecutionData[0] = _supplyCall(ST_XTZ, supplyAmount); - moduleExecutionData[1] = _borrowCall(XTZ, borrowAmount); +// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](3); +// moduleExecutionData[0] = _supplyCall(ST_XTZ, supplyAmount); +// moduleExecutionData[1] = _borrowCall(XTZ, borrowAmount); - moduleExecutionData[2] = - _swapCallExactOutCurve(XTZ, ST_XTZ, XTZ_STXTZ_POOL, swapAmount, supplyAmountWithPremium, XTZ_STXTZ_SWAP); +// moduleExecutionData[2] = +// _swapCallExactOutCurve(XTZ, ST_XTZ, XTZ_STXTZ_POOL, swapAmount, supplyAmountWithPremium, XTZ_STXTZ_SWAP); - DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); - intermediateExecutionData[0] = _flashloanCall(ST_XTZ, supplyAmount, abi.encode(moduleExecutionData)); +// DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); +// intermediateExecutionData[0] = _flashloanCall(ST_XTZ, supplyAmount, abi.encode(moduleExecutionData)); - DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); - finalExecutionData[0] = - _resolveDepositRequestsCall(XTZ, depositAmount_secondBatch, abi.encode(intermediateExecutionData)); +// DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); +// finalExecutionData[0] = +// _resolveDepositRequestsCall(XTZ, depositAmount_secondBatch, abi.encode(intermediateExecutionData)); - uint256 exchangeRateBefore = superloop.convertToAssets(ONE_SHARE); +// uint256 exchangeRateBefore = superloop.convertToAssets(ONE_SHARE); - vm.prank(admin); - superloop.operate(finalExecutionData); +// vm.prank(admin); +// superloop.operate(finalExecutionData); - uint256 exchangeRateAfter = superloop.convertToAssets(ONE_SHARE); +// uint256 exchangeRateAfter = superloop.convertToAssets(ONE_SHARE); - assertApproxEqAbs(exchangeRateBefore, exchangeRateAfter, 1000); // 1000 is the precision +// assertApproxEqAbs(exchangeRateBefore, exchangeRateAfter, 1000); // 1000 is the precision - // pending deposit should be 25 - assertEq(depositManager.totalPendingDeposits(), 25 * XTZ_SCALE); - // resolution id pointer should be 3 - assertEq(depositManager.resolutionIdPointer(), 3); +// // pending deposit should be 25 +// assertEq(depositManager.totalPendingDeposits(), 25 * XTZ_SCALE); +// // resolution id pointer should be 3 +// assertEq(depositManager.resolutionIdPointer(), 3); - // deposit request 3 should be partially processed - DataTypes.DepositRequestData memory depositRequest3 = depositManager.depositRequest(3); - assertEq(uint256(depositRequest3.state), uint256(DataTypes.RequestProcessingState.PARTIALLY_PROCESSED)); - assertEq(depositRequest3.amountProcessed, 75 * XTZ_SCALE); +// // deposit request 3 should be partially processed +// DataTypes.DepositRequestData memory depositRequest3 = depositManager.depositRequest(3); +// assertEq(uint256(depositRequest3.state), uint256(DataTypes.RequestProcessingState.PARTIALLY_PROCESSED)); +// assertEq(depositRequest3.amountProcessed, 75 * XTZ_SCALE); - // user3 should get shares - assertTrue(superloop.balanceOf(user3) > 0); +// // user3 should get shares +// assertTrue(superloop.balanceOf(user3) > 0); - //////////////// Make sure new deposits are working as expected //////////////// +// //////////////// Make sure new deposits are working as expected //////////////// - // user1 and user2 should be able to request new deposits - _makeDepositRequest(100 * XTZ_SCALE, user1, true); - _makeDepositRequest(100 * XTZ_SCALE, user2, true); +// // user1 and user2 should be able to request new deposits +// _makeDepositRequest(100 * XTZ_SCALE, user1, true); +// _makeDepositRequest(100 * XTZ_SCALE, user2, true); - // user3 should not be able to request new deposits because one request is still under process - vm.expectRevert(bytes(Errors.DEPOSIT_REQUEST_ACTIVE)); - vm.startPrank(user3); - depositManager.requestDeposit(100 * XTZ_SCALE, address(0)); - vm.stopPrank(); - } -} +// // user3 should not be able to request new deposits because one request is still under process +// vm.expectRevert(bytes(Errors.DEPOSIT_REQUEST_ACTIVE)); +// vm.startPrank(user3); +// depositManager.requestDeposit(100 * XTZ_SCALE, address(0)); +// vm.stopPrank(); +// } +// } diff --git a/test/core/integration/FallbackHandler.t.sol b/test/core/integration/FallbackHandler.t.sol index 1cfaf93..dc5a3a5 100644 --- a/test/core/integration/FallbackHandler.t.sol +++ b/test/core/integration/FallbackHandler.t.sol @@ -1,38 +1,38 @@ -// SPDX-License-Identifier: MIT +// // SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; +// pragma solidity ^0.8.13; -import {IntegrationBase} from "./IntegrationBase.sol"; -import {DataTypes} from "../../../src/common/DataTypes.sol"; -import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -import {DepositManager} from "../../../src/core/DepositManager/DepositManager.sol"; -import {Errors} from "../../../src/common/Errors.sol"; -import {MockPreliquidation} from "../../../src/mock/MockPreliquidation.sol"; +// import {IntegrationBase} from "./IntegrationBase.sol"; +// import {DataTypes} from "../../../src/common/DataTypes.sol"; +// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +// import {DepositManager} from "../../../src/core/DepositManager/DepositManager.sol"; +// import {Errors} from "../../../src/common/Errors.sol"; +// import {MockPreliquidation} from "../../../src/mock/MockPreliquidation.sol"; -contract FallbackHandlerTest is IntegrationBase { - address public preliquidation; - DataTypes.CallType public callType; - bytes32 public key; +// contract FallbackHandlerTest is IntegrationBase { +// address public preliquidation; +// DataTypes.CallType public callType; +// bytes32 public key; - function setUp() public override { - super.setUp(); - callType = DataTypes.CallType.DELEGATECALL; +// function setUp() public override { +// super.setUp(); +// callType = DataTypes.CallType.DELEGATECALL; - preliquidation = address(new MockPreliquidation()); +// preliquidation = address(new MockPreliquidation()); - key = - keccak256(abi.encodePacked(abi.encodeWithSelector(MockPreliquidation.preliquidate.selector), id, callType)); - vm.startPrank(admin); - superloop.setFallbackHandler(key, preliquidation); - vm.stopPrank(); - } +// key = +// keccak256(abi.encodePacked(abi.encodeWithSelector(MockPreliquidation.preliquidate.selector), id, callType)); +// vm.startPrank(admin); +// superloop.setFallbackHandler(key, preliquidation); +// vm.stopPrank(); +// } - function test_fallbackHandler() public { - assertEq(superloop.fallbackHandler(key), preliquidation); +// function test_fallbackHandler() public { +// assertEq(superloop.fallbackHandler(key), preliquidation); - // make a call to superloop with context of IPreliquidation.preliquidate - MockPreliquidation(address(superloop)).preliquidate(id, callType, ""); +// // make a call to superloop with context of IPreliquidation.preliquidate +// MockPreliquidation(address(superloop)).preliquidate(id, callType, ""); - // this should not revert, not reverting means the decoding in fallback handler is working correctly - } -} +// // this should not revert, not reverting means the decoding in fallback handler is working correctly +// } +// } diff --git a/test/core/integration/InstantWithdraw.t.sol b/test/core/integration/InstantWithdraw.t.sol index bb49f6c..f76b32c 100644 --- a/test/core/integration/InstantWithdraw.t.sol +++ b/test/core/integration/InstantWithdraw.t.sol @@ -1,74 +1,74 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.13; - -import {IntegrationBase} from "./IntegrationBase.sol"; -import {DataTypes} from "../../../src/common/DataTypes.sol"; -import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -import {DepositManager} from "../../../src/core/DepositManager/DepositManager.sol"; -import {console} from "forge-std/Test.sol"; -import {Errors} from "../../../src/common/Errors.sol"; - -contract InstantWithdrawTest is IntegrationBase { - function setUp() public override { - super.setUp(); - } - - function test_instantWithdraw() public { - _seed(); - - uint256 userSharesBalanceBefore = superloop.balanceOf(admin); - uint256 userTokenBalanceBefore = IERC20(XTZ).balanceOf(admin); - uint256 treasurySharesBalanceBefore = superloop.balanceOf(treasury); - - vm.startPrank(admin); - uint256 instantWithdrawAmount = 10 * XTZ_SCALE; - superloop.withdraw(instantWithdrawAmount, admin, admin); - - uint256 userSharesBalanceAfter = superloop.balanceOf(admin); - uint256 userTokenBalanceAfter = IERC20(XTZ).balanceOf(admin); - uint256 treasurySharesBalanceAfter = superloop.balanceOf(treasury); - - uint256 instantWithdrawFee = (instantWithdrawAmount * INSTANT_WITHDRAW_FEE) / 10_000; - assertEq(userSharesBalanceBefore - userSharesBalanceAfter, instantWithdrawAmount * 100); - assertEq(userTokenBalanceAfter - userTokenBalanceBefore, instantWithdrawAmount - instantWithdrawFee); - assertEq(treasurySharesBalanceAfter - treasurySharesBalanceBefore, instantWithdrawFee * 100); - } - - function test_instantRedeem() public { - _seed(); - - uint256 userSharesBalanceBefore = superloop.balanceOf(admin); - uint256 userTokenBalanceBefore = IERC20(XTZ).balanceOf(admin); - uint256 treasurySharesBalanceBefore = superloop.balanceOf(treasury); - - vm.startPrank(admin); - uint256 instantRedeemAmount = 10 * ONE_SHARE; - superloop.redeem(instantRedeemAmount, admin, admin); - - uint256 userSharesBalanceAfter = superloop.balanceOf(admin); - uint256 userTokenBalanceAfter = IERC20(XTZ).balanceOf(admin); - uint256 treasurySharesBalanceAfter = superloop.balanceOf(treasury); - - uint256 instantRedeemFee = (instantRedeemAmount * INSTANT_WITHDRAW_FEE) / 10_000; - assertEq(userSharesBalanceBefore - userSharesBalanceAfter, instantRedeemAmount); - assertEq(userTokenBalanceAfter - userTokenBalanceBefore, (instantRedeemAmount - instantRedeemFee) / 100); - assertEq(treasurySharesBalanceAfter - treasurySharesBalanceBefore, instantRedeemFee); - } - - function _seed() internal { - uint256 seedAmount = 100 * XTZ_SCALE; - deal(XTZ, admin, seedAmount); - - vm.startPrank(admin); - IERC20(XTZ).approve(address(superloop), seedAmount); - superloop.seed(seedAmount); - vm.stopPrank(); - - uint256 totalSupply = superloop.totalSupply(); - - assertApproxEqAbs(totalSupply, seedAmount * 100, 100); - assertEq(superloop.totalAssets(), seedAmount); - assertEq(superloop.balanceOf(admin), totalSupply); - } -} +// // SPDX-License-Identifier: MIT + +// pragma solidity ^0.8.13; + +// import {IntegrationBase} from "./IntegrationBase.sol"; +// import {DataTypes} from "../../../src/common/DataTypes.sol"; +// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +// import {DepositManager} from "../../../src/core/DepositManager/DepositManager.sol"; +// import {console} from "forge-std/Test.sol"; +// import {Errors} from "../../../src/common/Errors.sol"; + +// contract InstantWithdrawTest is IntegrationBase { +// function setUp() public override { +// super.setUp(); +// } + +// function test_instantWithdraw() public { +// _seed(); + +// uint256 userSharesBalanceBefore = superloop.balanceOf(admin); +// uint256 userTokenBalanceBefore = IERC20(XTZ).balanceOf(admin); +// uint256 treasurySharesBalanceBefore = superloop.balanceOf(treasury); + +// vm.startPrank(admin); +// uint256 instantWithdrawAmount = 10 * XTZ_SCALE; +// superloop.withdraw(instantWithdrawAmount, admin, admin); + +// uint256 userSharesBalanceAfter = superloop.balanceOf(admin); +// uint256 userTokenBalanceAfter = IERC20(XTZ).balanceOf(admin); +// uint256 treasurySharesBalanceAfter = superloop.balanceOf(treasury); + +// uint256 instantWithdrawFee = (instantWithdrawAmount * INSTANT_WITHDRAW_FEE) / 10_000; +// assertEq(userSharesBalanceBefore - userSharesBalanceAfter, instantWithdrawAmount * 100); +// assertEq(userTokenBalanceAfter - userTokenBalanceBefore, instantWithdrawAmount - instantWithdrawFee); +// assertEq(treasurySharesBalanceAfter - treasurySharesBalanceBefore, instantWithdrawFee * 100); +// } + +// function test_instantRedeem() public { +// _seed(); + +// uint256 userSharesBalanceBefore = superloop.balanceOf(admin); +// uint256 userTokenBalanceBefore = IERC20(XTZ).balanceOf(admin); +// uint256 treasurySharesBalanceBefore = superloop.balanceOf(treasury); + +// vm.startPrank(admin); +// uint256 instantRedeemAmount = 10 * ONE_SHARE; +// superloop.redeem(instantRedeemAmount, admin, admin); + +// uint256 userSharesBalanceAfter = superloop.balanceOf(admin); +// uint256 userTokenBalanceAfter = IERC20(XTZ).balanceOf(admin); +// uint256 treasurySharesBalanceAfter = superloop.balanceOf(treasury); + +// uint256 instantRedeemFee = (instantRedeemAmount * INSTANT_WITHDRAW_FEE) / 10_000; +// assertEq(userSharesBalanceBefore - userSharesBalanceAfter, instantRedeemAmount); +// assertEq(userTokenBalanceAfter - userTokenBalanceBefore, (instantRedeemAmount - instantRedeemFee) / 100); +// assertEq(treasurySharesBalanceAfter - treasurySharesBalanceBefore, instantRedeemFee); +// } + +// function _seed() internal { +// uint256 seedAmount = 100 * XTZ_SCALE; +// deal(XTZ, admin, seedAmount); + +// vm.startPrank(admin); +// IERC20(XTZ).approve(address(superloop), seedAmount); +// superloop.seed(seedAmount); +// vm.stopPrank(); + +// uint256 totalSupply = superloop.totalSupply(); + +// assertApproxEqAbs(totalSupply, seedAmount * 100, 100); +// assertEq(superloop.totalAssets(), seedAmount); +// assertEq(superloop.balanceOf(admin), totalSupply); +// } +// } diff --git a/test/core/integration/IntegrationBase.sol b/test/core/integration/IntegrationBase.sol index 6dd9706..6b62ebb 100644 --- a/test/core/integration/IntegrationBase.sol +++ b/test/core/integration/IntegrationBase.sol @@ -1,571 +1,571 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.13; - -import {TestBase} from "../TestBase.sol"; -import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; -import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; -import {TransparentUpgradeableProxy} from - "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -import {DataTypes} from "../../../src/common/DataTypes.sol"; -import {IFlashLoanSimpleReceiver} from "aave-v3-core/contracts/flashloan/interfaces/IFlashLoanSimpleReceiver.sol"; -import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -import {IPoolConfigurator} from "aave-v3-core/contracts/interfaces/IPoolConfigurator.sol"; -import {IRouter} from "../../../src/mock/MockIRouter.sol"; -import {ICurvePool} from "../../../src/mock/ICurvePool.sol"; -import {console} from "forge-std/console.sol"; - -abstract contract IntegrationBase is TestBase { - struct CURVE_IJ { - int128 i; - int128 j; - } - - Superloop public superloopImplementation; - ProxyAdmin public proxyAdmin; - - address public user1; - address public user2; - address public user3; - - uint24 public constant XTZ_STXTZ_POOL_FEE = 100; // 0.01% - address public constant XTZ_STXTZ_POOL = 0x74d80eE400D3026FDd2520265cC98300710b25D4; - - uint256 public constant ONE_SHARE = 10 ** 20; - - uint256 public constant INSTANT_WITHDRAW_FEE = 100; - - uint256 public constant XTZ_SCALE = 10 ** 18; - uint256 public constant STXTZ_SCALE = 10 ** 6; - - CURVE_IJ public XTZ_STXTZ_SWAP; - CURVE_IJ public STXTZ_XTZ_SWAP; - - function setUp() public virtual override { - super.setUp(); - - XTZ_STXTZ_SWAP = CURVE_IJ({i: 1, j: 0}); - STXTZ_XTZ_SWAP = CURVE_IJ({i: 0, j: 1}); - - vm.startPrank(admin); - _deployModules(); - - address[] memory modules = new address[](9); - modules[0] = address(dexModule); - modules[1] = address(flashloanModule); - modules[2] = address(callbackHandler); - modules[3] = address(emodeModule); - modules[4] = address(supplyModule); - modules[5] = address(withdrawModule); - modules[6] = address(borrowModule); - modules[7] = address(repayModule); - modules[8] = address(depositManagerCallbackHandler); - - DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ - asset: XTZ, - name: "XTZ Vault", - symbol: "XTZV", - supplyCap: 100000 * 10 ** 18, - minimumDepositAmount: 100, - instantWithdrawFee: INSTANT_WITHDRAW_FEE, - superloopModuleRegistry: address(moduleRegistry), - modules: modules, - accountant: address(accountant), - withdrawManager: address(withdrawManager), - depositManager: address(0), - cashReserve: 1000, - vaultAdmin: admin, - treasury: treasury, - vaultOperator: admin - }); - DataTypes.VaultInitData memory initDataBtc = DataTypes.VaultInitData({ - asset: WBTC, - name: "BTC Vault", - symbol: "BTC", - supplyCap: 100000 * 10 ** 18, - minimumDepositAmount: 100, - instantWithdrawFee: INSTANT_WITHDRAW_FEE, - superloopModuleRegistry: address(moduleRegistry), - modules: modules, - accountant: address(accountantBtc), - withdrawManager: address(withdrawManagerBtc), - depositManager: address(0), - cashReserve: 1000, - vaultAdmin: admin, - treasury: treasury, - vaultOperator: admin - }); - superloopImplementation = new Superloop(); - proxyAdmin = new ProxyAdmin(address(this)); - - TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( - address(superloopImplementation), - address(proxyAdmin), - abi.encodeWithSelector(Superloop.initialize.selector, initData) - ); - TransparentUpgradeableProxy proxyBtc = new TransparentUpgradeableProxy( - address(superloopImplementation), - address(proxyAdmin), - abi.encodeWithSelector(Superloop.initialize.selector, initDataBtc) - ); - superloop = Superloop(payable(address(proxy))); - superloopBtc = Superloop(payable(address(proxyBtc))); - - accountant = _deployAccountant(address(superloop), _singleAddressArray(ST_XTZ), _singleAddressArray(XTZ)); - accountantBtc = _deployAccountant(address(superloopBtc), _singleAddressArray(LBTC), _singleAddressArray(WBTC)); - - withdrawManager = _deployWithdrawManager(address(superloop)); - withdrawManagerBtc = _deployWithdrawManager(address(superloopBtc)); - - depositManager = _deployDepositManager(address(superloop)); - depositManagerBtc = _deployDepositManager(address(superloopBtc)); - - bytes32 key = keccak256(abi.encodePacked(POOL, IFlashLoanSimpleReceiver.executeOperation.selector)); - bytes32 depositKey = - keccak256(abi.encodePacked(address(depositManager), depositManagerCallbackHandler.executeDeposit.selector)); - bytes32 withdrawKey = keccak256( - abi.encodePacked(address(withdrawManager), withdrawManagerCallbackHandler.executeWithdraw.selector) - ); - superloop.setCallbackHandler(key, address(callbackHandler)); - superloop.setCallbackHandler(depositKey, address(depositManagerCallbackHandler)); - superloop.setCallbackHandler(withdrawKey, address(withdrawManagerCallbackHandler)); - - moduleRegistry.setModule("withdrawManager", address(withdrawManager)); - moduleRegistry.setModule("universalAccountant", address(accountant)); - moduleRegistry.setModule("depositManager", address(depositManager)); - - superloop.setRegisteredModule(address(withdrawManager), true); - superloop.setRegisteredModule(address(accountant), true); - superloop.setRegisteredModule(address(depositManager), true); - - superloopBtc.setRegisteredModule(address(withdrawManagerBtc), true); - superloopBtc.setRegisteredModule(address(accountantBtc), true); - superloopBtc.setRegisteredModule(address(depositManagerBtc), true); - - superloop.setAccountantModule(address(accountant)); - superloop.setWithdrawManagerModule(address(withdrawManager)); - superloop.setDepositManagerModule(address(depositManager)); - - superloopBtc.setAccountantModule(address(accountantBtc)); - superloopBtc.setWithdrawManagerModule(address(withdrawManagerBtc)); - superloopBtc.setDepositManagerModule(address(depositManagerBtc)); - - vm.stopPrank(); - vm.label(address(superloop), "superloop"); - - user1 = makeAddr("user1"); - user2 = makeAddr("user2"); - user3 = makeAddr("user3"); - - vm.label(user1, "user1"); - vm.label(user2, "user2"); - vm.label(user3, "user3"); - - vm.startPrank(XTZ_WHALE); - IERC20(XTZ).transfer(user1, 100 * 10 ** 18); - IERC20(XTZ).transfer(user2, 100 * 10 ** 18); - IERC20(XTZ).transfer(user3, 100 * 10 ** 18); - vm.stopPrank(); - - vm.startPrank(POOL_ADMIN); - IPoolConfigurator(POOL_CONFIGURATOR).setReserveFlashLoaning(ST_XTZ, true); - IPoolConfigurator(POOL_CONFIGURATOR).setSupplyCap(ST_XTZ, 10000000); - vm.stopPrank(); - } - - function _resolveDepositRequestsCall(address asset, uint256 amount, bytes memory data) - internal - view - returns (DataTypes.ModuleExecutionData memory) - { - DataTypes.ResolveDepositRequestsData memory resolveDepositRequestsData = - DataTypes.ResolveDepositRequestsData({asset: asset, amount: amount, callbackExecutionData: data}); - return DataTypes.ModuleExecutionData({ - executionType: DataTypes.CallType.CALL, - module: address(depositManager), - data: abi.encodeWithSelector(depositManager.resolveDepositRequests.selector, resolveDepositRequestsData) - }); - } - - function _resolveWithdrawRequestsCall(uint256 shares, DataTypes.WithdrawRequestType requestType, bytes memory data) - internal - view - returns (DataTypes.ModuleExecutionData memory) - { - DataTypes.ResolveWithdrawRequestsData memory resolveWithdrawRequestsData = DataTypes.ResolveWithdrawRequestsData({ - shares: shares, - requestType: requestType, - callbackExecutionData: data - }); - return DataTypes.ModuleExecutionData({ - executionType: DataTypes.CallType.CALL, - module: address(withdrawManager), - data: abi.encodeWithSelector(withdrawManager.resolveWithdrawRequests.selector, resolveWithdrawRequestsData) - }); - } - - function _flashloanCall(address asset, uint256 amount, bytes memory data) - internal - view - returns (DataTypes.ModuleExecutionData memory) - { - DataTypes.AaveV3FlashloanParams memory flashloanParams = DataTypes.AaveV3FlashloanParams({ - asset: asset, - amount: amount, - referralCode: 0, - callbackExecutionData: data - }); - return DataTypes.ModuleExecutionData({ - executionType: DataTypes.CallType.DELEGATECALL, - module: address(flashloanModule), - data: abi.encodeWithSelector(flashloanModule.execute.selector, flashloanParams) - }); - } - - function _supplyCall(address asset, uint256 amount) internal view returns (DataTypes.ModuleExecutionData memory) { - DataTypes.AaveV3ActionParams memory supplyParams = DataTypes.AaveV3ActionParams({asset: asset, amount: amount}); - return DataTypes.ModuleExecutionData({ - executionType: DataTypes.CallType.DELEGATECALL, - module: address(supplyModule), - data: abi.encodeWithSelector(supplyModule.execute.selector, supplyParams) - }); - } - - function _borrowCall(address asset, uint256 amount) internal view returns (DataTypes.ModuleExecutionData memory) { - DataTypes.AaveV3ActionParams memory borrowParams = DataTypes.AaveV3ActionParams({asset: asset, amount: amount}); - return DataTypes.ModuleExecutionData({ - executionType: DataTypes.CallType.DELEGATECALL, - module: address(borrowModule), - data: abi.encodeWithSelector(borrowModule.execute.selector, borrowParams) - }); - } - - function _swapCallExactOut( - address tokenIn, - address tokenOut, - uint256 swapAmount, - uint256 amountOut, - address router, - uint24 fee - ) internal view returns (DataTypes.ModuleExecutionData memory) { - DataTypes.ExecuteSwapParamsData[] memory swapParamsData = new DataTypes.ExecuteSwapParamsData[](2); - swapParamsData[0] = DataTypes.ExecuteSwapParamsData({ - target: tokenIn, - data: abi.encodeWithSelector(IERC20.approve.selector, router, swapAmount) - }); - swapParamsData[1] = DataTypes.ExecuteSwapParamsData({ - target: router, - data: abi.encodeWithSelector( - IRouter.exactOutputSingle.selector, - IRouter.ExactOutputSingleParams({ - tokenIn: tokenIn, - tokenOut: tokenOut, - fee: fee, - recipient: address(superloop), - amountOut: amountOut, - amountInMaximum: swapAmount, - sqrtPriceLimitX96: 0 - }) - ) - }); - DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ - tokenIn: tokenIn, - tokenOut: tokenOut, - amountIn: swapAmount, - maxAmountIn: swapAmount, - minAmountOut: amountOut, - data: swapParamsData - }); - - return DataTypes.ModuleExecutionData({ - executionType: DataTypes.CallType.DELEGATECALL, - module: address(dexModule), - data: abi.encodeWithSelector(dexModule.execute.selector, swapParams) - }); - } - - function _swapCallExactIn( - address tokenIn, - address tokenOut, - uint256 withdrawAmount, - uint256 amountOut, - address router, - uint24 fee - ) internal view returns (DataTypes.ModuleExecutionData memory) { - DataTypes.ExecuteSwapParamsData[] memory swapParamsData = new DataTypes.ExecuteSwapParamsData[](2); - swapParamsData[0] = DataTypes.ExecuteSwapParamsData({ - target: tokenIn, - data: abi.encodeWithSelector(IERC20.approve.selector, router, withdrawAmount) - }); - swapParamsData[1] = DataTypes.ExecuteSwapParamsData({ - target: router, - data: abi.encodeWithSelector( - IRouter.exactInputSingle.selector, - IRouter.ExactInputSingleParams({ - tokenIn: tokenIn, - tokenOut: tokenOut, - fee: fee, - recipient: address(superloop), - amountIn: withdrawAmount, - amountOutMinimum: amountOut, - sqrtPriceLimitX96: 0 - }) - ) - }); - - DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ - tokenIn: tokenIn, - tokenOut: tokenOut, - amountIn: withdrawAmount, - maxAmountIn: withdrawAmount, - minAmountOut: amountOut, - data: swapParamsData - }); - - return DataTypes.ModuleExecutionData({ - executionType: DataTypes.CallType.DELEGATECALL, - module: address(dexModule), - data: abi.encodeWithSelector(dexModule.execute.selector, swapParams) - }); - } - - function _swapCallExactOutCurve( - address tokenIn, - address tokenOut, - address pool, - uint256 amountIn, - uint256 amountOut, - CURVE_IJ memory swap - ) internal view returns (DataTypes.ModuleExecutionData memory) { - DataTypes.ExecuteSwapParamsData[] memory swapParamsData = new DataTypes.ExecuteSwapParamsData[](2); - swapParamsData[0] = DataTypes.ExecuteSwapParamsData({ - target: tokenIn, - data: abi.encodeWithSelector(IERC20.approve.selector, pool, amountIn) - }); - swapParamsData[1] = DataTypes.ExecuteSwapParamsData({ - target: pool, - data: abi.encodeWithSelector(ICurvePool.exchange.selector, swap.i, swap.j, amountIn, amountOut) - }); - - DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ - tokenIn: tokenIn, - tokenOut: tokenOut, - amountIn: amountIn, - maxAmountIn: amountIn, - minAmountOut: amountOut, - data: swapParamsData - }); - - return DataTypes.ModuleExecutionData({ - executionType: DataTypes.CallType.DELEGATECALL, - module: address(dexModule), - data: abi.encodeWithSelector(dexModule.execute.selector, swapParams) - }); - } - - function _repayCall(address asset, uint256 amount) internal view returns (DataTypes.ModuleExecutionData memory) { - DataTypes.AaveV3ActionParams memory repayParams = DataTypes.AaveV3ActionParams({asset: asset, amount: amount}); - return DataTypes.ModuleExecutionData({ - executionType: DataTypes.CallType.DELEGATECALL, - module: address(repayModule), - data: abi.encodeWithSelector(repayModule.execute.selector, repayParams) - }); - } - - function _withdrawCall(address asset, uint256 amount) - internal - view - returns (DataTypes.ModuleExecutionData memory) - { - DataTypes.AaveV3ActionParams memory withdrawParams = - DataTypes.AaveV3ActionParams({asset: asset, amount: amount}); - return DataTypes.ModuleExecutionData({ - executionType: DataTypes.CallType.DELEGATECALL, - module: address(withdrawModule), - data: abi.encodeWithSelector(withdrawModule.execute.selector, withdrawParams) - }); - } - - /** - * @dev Creates a partial deposit with resolution. - * @notice Creates 3 deposit requests with 100 xtz each. - * @notice request 1 is fully processed, request 2 is partially processed, request 3 is unprocessed. - */ - function _createPartialDepositWithResolution(bool depositAll) internal returns (uint256, uint256) { - uint256 depositAmountUnscaled = 100; - uint256 depositAmount = depositAmountUnscaled * XTZ_SCALE; - _makeDepositRequest(depositAmount, user1, true); - _makeDepositRequest(depositAmount, user2, true); - _makeDepositRequest(depositAmount, user3, true); - - uint256 depositAmountUnscaledBatch = depositAll ? depositAmountUnscaled * 3 : (3 * depositAmountUnscaled) / 2; - uint256 supplyAmountUnscaled = (3 * depositAmountUnscaledBatch); // 3x - uint256 borrowAmountUnscaled = (3 * supplyAmountUnscaled) / 4; - - uint256 depositAmountBatch = depositAmountUnscaledBatch * XTZ_SCALE; - // build the operate call - uint256 supplyAmount = supplyAmountUnscaled * STXTZ_SCALE; - uint256 borrowAmount = borrowAmountUnscaled * XTZ_SCALE; - uint256 swapAmount = borrowAmount + depositAmountBatch; - uint256 supplyAmountWithPremium = supplyAmount + (supplyAmount * 1) / 10000; - - DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](3); - moduleExecutionData[0] = _supplyCall(ST_XTZ, supplyAmount); - moduleExecutionData[1] = _borrowCall(XTZ, borrowAmount); - - moduleExecutionData[2] = - _swapCallExactOutCurve(XTZ, ST_XTZ, XTZ_STXTZ_POOL, swapAmount, supplyAmountWithPremium, XTZ_STXTZ_SWAP); - - DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); - intermediateExecutionData[0] = _flashloanCall(ST_XTZ, supplyAmount, abi.encode(moduleExecutionData)); - - DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); - finalExecutionData[0] = - _resolveDepositRequestsCall(XTZ, depositAmountBatch, abi.encode(intermediateExecutionData)); - - uint256 exchangeRateBefore = superloop.convertToAssets(ONE_SHARE); - - uint256 user1SharesBefore = superloop.balanceOf(user1); - uint256 user2SharesBefore = superloop.balanceOf(user2); - uint256 user3SharesBefore = superloop.balanceOf(user3); - - vm.prank(admin); - superloop.operate(finalExecutionData); - - if (!depositAll) { - // user 1 and user user 2 should get shares - // user 3 should not get shares - - uint256 user1SharesAfter = superloop.balanceOf(user1); - uint256 user2SharesAfter = superloop.balanceOf(user2); - uint256 user3SharesAfter = superloop.balanceOf(user3); - - // deposit manager should have 150 xtz now - assertEq(IERC20(XTZ).balanceOf(address(depositManager)), 150 * XTZ_SCALE); - // pending deposits should be 150 xtz - assertEq(depositManager.totalPendingDeposits(), 150 * XTZ_SCALE); - - // resolution id pointer should be 2 - assertEq(depositManager.resolutionIdPointer(), 2); - - // deposit request 1 should be fully processed - DataTypes.DepositRequestData memory depositRequest1 = depositManager.depositRequest(1); - assertEq(uint256(depositRequest1.state), uint256(DataTypes.RequestProcessingState.FULLY_PROCESSED)); - assertEq(depositRequest1.amountProcessed, 100 * XTZ_SCALE); - - // deposit request 2 should be partially processed - DataTypes.DepositRequestData memory depositRequest2 = depositManager.depositRequest(2); - assertEq(uint256(depositRequest2.state), uint256(DataTypes.RequestProcessingState.PARTIALLY_PROCESSED)); - assertEq(depositRequest2.amountProcessed, 50 * XTZ_SCALE); - - // deposit request 3 should be unprocessed - DataTypes.DepositRequestData memory depositRequest3 = depositManager.depositRequest(3); - assertEq(uint256(depositRequest3.state), uint256(DataTypes.RequestProcessingState.UNPROCESSED)); - - assertEq(user1SharesAfter - user1SharesBefore, depositRequest1.sharesMinted); - assertEq(user2SharesAfter - user2SharesBefore, depositRequest2.sharesMinted); - assertEq(user3SharesAfter - user3SharesBefore, depositRequest3.sharesMinted); - - // exchange rate before should equal to exchange rate after - uint256 exchangeRateAfter = superloop.convertToAssets(ONE_SHARE); - assertApproxEqAbs(exchangeRateBefore, exchangeRateAfter, 1000); - } - - return (supplyAmountUnscaled, borrowAmountUnscaled); - } - - function _makeDepositRequest(uint256 depositAmount, address user, bool _deal) internal { - if (_deal) { - deal(XTZ, user, depositAmount); - } - - vm.startPrank(user); - IERC20(XTZ).approve(address(depositManager), depositAmount); - depositManager.requestDeposit(depositAmount, address(0)); - vm.stopPrank(); - } - - function _makeWithdrawRequest( - address share, - uint256 shareAmount, - address user, - DataTypes.WithdrawRequestType requestType - ) internal { - vm.startPrank(user); - IERC20(share).approve(address(withdrawManager), shareAmount); - withdrawManager.requestWithdraw(shareAmount, requestType); - vm.stopPrank(); - } - - /** - * @dev Creates a partial withdraw with resolution. - * @notice Creates 3 withdraw requests with 100 shares each. - * @notice request 1 is fully processed, request 2 is partially processed, request 3 is unprocessed. - */ - function _createPartialWithdrawWithResolution(DataTypes.WithdrawRequestType requestType) - internal - returns (uint256, uint256, uint256) - { - (uint256 supplyAmountUnscaled, uint256 borrowAmountUnscaled) = _createPartialDepositWithResolution(true); - - // make 3 withdraw requests - uint256 user1ShareBalance = superloop.balanceOf(user1); - uint256 user2ShareBalance = superloop.balanceOf(user2); - uint256 user3ShareBalance = superloop.balanceOf(user3); - _makeWithdrawRequest(address(superloop), user1ShareBalance, user1, requestType); - _makeWithdrawRequest(address(superloop), user2ShareBalance, user2, requestType); - _makeWithdrawRequest(address(superloop), user3ShareBalance, user3, requestType); - - // resolve 1st fully, and 2nd partially - // repay half of borrow amount - uint256 repayAmount = (borrowAmountUnscaled * XTZ_SCALE) / 2; - uint256 withdrawAmount = ((supplyAmountUnscaled + 10) * STXTZ_SCALE) / 2; - uint256 repayAmountWithPremium = repayAmount + (repayAmount * 1) / 10000; - uint256 totalShares = superloop.totalSupply(); - uint256 sharesToResolve = totalShares / 2; - - DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](3); - moduleExecutionData[0] = _repayCall(XTZ, repayAmount); - moduleExecutionData[1] = _withdrawCall(ST_XTZ, withdrawAmount); - moduleExecutionData[2] = - _swapCallExactOutCurve(ST_XTZ, XTZ, XTZ_STXTZ_POOL, withdrawAmount, repayAmountWithPremium, STXTZ_XTZ_SWAP); - - DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); - intermediateExecutionData[0] = _flashloanCall(XTZ, repayAmount, abi.encode(moduleExecutionData)); - - DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); - finalExecutionData[0] = - _resolveWithdrawRequestsCall(sharesToResolve, requestType, abi.encode(intermediateExecutionData)); - - uint256 exchangeRateBefore = superloop.convertToAssets(ONE_SHARE); - - vm.prank(admin); - superloop.operate(finalExecutionData); - - // withdraw requests should have claimable > 0 and sharesProcessed > 0, exchange rate remains the same - DataTypes.WithdrawRequestData memory withdrawRequest1 = withdrawManager.withdrawRequest(1, requestType); - DataTypes.WithdrawRequestData memory withdrawRequest2 = withdrawManager.withdrawRequest(2, requestType); - DataTypes.WithdrawRequestData memory withdrawRequest3 = withdrawManager.withdrawRequest(3, requestType); - uint256 resolutionIdPointer = withdrawManager.resolutionIdPointer(requestType); - uint256 totalPendingWithdraws = withdrawManager.totalPendingWithdraws(requestType); - - assertTrue(withdrawRequest1.amountClaimable > 0); - assertTrue(withdrawRequest1.sharesProcessed == withdrawRequest1.shares); - assertTrue(withdrawRequest2.amountClaimable > 0); - assertTrue(withdrawRequest2.sharesProcessed > 0 && withdrawRequest2.sharesProcessed < withdrawRequest2.shares); - assertTrue(withdrawRequest3.amountClaimable == 0); - assertTrue(withdrawRequest3.sharesProcessed == 0); - - assertTrue(withdrawRequest1.state == DataTypes.RequestProcessingState.FULLY_PROCESSED); - assertTrue(withdrawRequest2.state == DataTypes.RequestProcessingState.PARTIALLY_PROCESSED); - assertTrue(withdrawRequest3.state == DataTypes.RequestProcessingState.UNPROCESSED); - - uint256 exchangeRateAfter = superloop.convertToAssets(ONE_SHARE); - - assertApproxEqAbs(exchangeRateAfter, exchangeRateBefore, 1000); - assertEq(totalPendingWithdraws, totalShares - sharesToResolve); - assertEq(resolutionIdPointer, 2); - - return (totalShares - sharesToResolve, repayAmount, withdrawAmount); - } -} +// // SPDX-License-Identifier: MIT + +// pragma solidity ^0.8.13; + +// import {TestBase} from "../TestBase.sol"; +// import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; +// import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; +// import {TransparentUpgradeableProxy} from +// "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +// import {DataTypes} from "../../../src/common/DataTypes.sol"; +// import {IFlashLoanSimpleReceiver} from "aave-v3-core/contracts/flashloan/interfaces/IFlashLoanSimpleReceiver.sol"; +// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +// import {IPoolConfigurator} from "aave-v3-core/contracts/interfaces/IPoolConfigurator.sol"; +// import {IRouter} from "../../../src/mock/MockIRouter.sol"; +// import {ICurvePool} from "../../../src/mock/ICurvePool.sol"; +// import {console} from "forge-std/console.sol"; + +// abstract contract IntegrationBase is TestBase { +// struct CURVE_IJ { +// int128 i; +// int128 j; +// } + +// Superloop public superloopImplementation; +// ProxyAdmin public proxyAdmin; + +// address public user1; +// address public user2; +// address public user3; + +// uint24 public constant XTZ_STXTZ_POOL_FEE = 100; // 0.01% +// address public constant XTZ_STXTZ_POOL = 0x74d80eE400D3026FDd2520265cC98300710b25D4; + +// uint256 public constant ONE_SHARE = 10 ** 20; + +// uint256 public constant INSTANT_WITHDRAW_FEE = 100; + +// uint256 public constant XTZ_SCALE = 10 ** 18; +// uint256 public constant STXTZ_SCALE = 10 ** 6; + +// CURVE_IJ public XTZ_STXTZ_SWAP; +// CURVE_IJ public STXTZ_XTZ_SWAP; + +// function setUp() public virtual override { +// super.setUp(); + +// XTZ_STXTZ_SWAP = CURVE_IJ({i: 1, j: 0}); +// STXTZ_XTZ_SWAP = CURVE_IJ({i: 0, j: 1}); + +// vm.startPrank(admin); +// _deployModules(); + +// address[] memory modules = new address[](9); +// modules[0] = address(dexModule); +// modules[1] = address(flashloanModule); +// modules[2] = address(callbackHandler); +// modules[3] = address(emodeModule); +// modules[4] = address(supplyModule); +// modules[5] = address(withdrawModule); +// modules[6] = address(borrowModule); +// modules[7] = address(repayModule); +// modules[8] = address(depositManagerCallbackHandler); + +// DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ +// asset: XTZ, +// name: "XTZ Vault", +// symbol: "XTZV", +// supplyCap: 100000 * 10 ** 18, +// minimumDepositAmount: 100, +// instantWithdrawFee: INSTANT_WITHDRAW_FEE, +// superloopModuleRegistry: address(moduleRegistry), +// modules: modules, +// accountant: address(accountant), +// withdrawManager: address(withdrawManager), +// depositManager: address(0), +// cashReserve: 1000, +// vaultAdmin: admin, +// treasury: treasury, +// vaultOperator: admin +// }); +// DataTypes.VaultInitData memory initDataBtc = DataTypes.VaultInitData({ +// asset: WBTC, +// name: "BTC Vault", +// symbol: "BTC", +// supplyCap: 100000 * 10 ** 18, +// minimumDepositAmount: 100, +// instantWithdrawFee: INSTANT_WITHDRAW_FEE, +// superloopModuleRegistry: address(moduleRegistry), +// modules: modules, +// accountant: address(accountantBtc), +// withdrawManager: address(withdrawManagerBtc), +// depositManager: address(0), +// cashReserve: 1000, +// vaultAdmin: admin, +// treasury: treasury, +// vaultOperator: admin +// }); +// superloopImplementation = new Superloop(); +// proxyAdmin = new ProxyAdmin(address(this)); + +// TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( +// address(superloopImplementation), +// address(proxyAdmin), +// abi.encodeWithSelector(Superloop.initialize.selector, initData) +// ); +// TransparentUpgradeableProxy proxyBtc = new TransparentUpgradeableProxy( +// address(superloopImplementation), +// address(proxyAdmin), +// abi.encodeWithSelector(Superloop.initialize.selector, initDataBtc) +// ); +// superloop = Superloop(payable(address(proxy))); +// superloopBtc = Superloop(payable(address(proxyBtc))); + +// accountant = _deployAccountant(address(superloop), _singleAddressArray(ST_XTZ), _singleAddressArray(XTZ)); +// accountantBtc = _deployAccountant(address(superloopBtc), _singleAddressArray(LBTC), _singleAddressArray(WBTC)); + +// withdrawManager = _deployWithdrawManager(address(superloop)); +// withdrawManagerBtc = _deployWithdrawManager(address(superloopBtc)); + +// depositManager = _deployDepositManager(address(superloop)); +// depositManagerBtc = _deployDepositManager(address(superloopBtc)); + +// bytes32 key = keccak256(abi.encodePacked(POOL, IFlashLoanSimpleReceiver.executeOperation.selector)); +// bytes32 depositKey = +// keccak256(abi.encodePacked(address(depositManager), depositManagerCallbackHandler.executeDeposit.selector)); +// bytes32 withdrawKey = keccak256( +// abi.encodePacked(address(withdrawManager), withdrawManagerCallbackHandler.executeWithdraw.selector) +// ); +// superloop.setCallbackHandler(key, address(callbackHandler)); +// superloop.setCallbackHandler(depositKey, address(depositManagerCallbackHandler)); +// superloop.setCallbackHandler(withdrawKey, address(withdrawManagerCallbackHandler)); + +// moduleRegistry.setModule("withdrawManager", address(withdrawManager)); +// moduleRegistry.setModule("universalAccountant", address(accountant)); +// moduleRegistry.setModule("depositManager", address(depositManager)); + +// superloop.setRegisteredModule(address(withdrawManager), true); +// superloop.setRegisteredModule(address(accountant), true); +// superloop.setRegisteredModule(address(depositManager), true); + +// superloopBtc.setRegisteredModule(address(withdrawManagerBtc), true); +// superloopBtc.setRegisteredModule(address(accountantBtc), true); +// superloopBtc.setRegisteredModule(address(depositManagerBtc), true); + +// superloop.setAccountantModule(address(accountant)); +// superloop.setWithdrawManagerModule(address(withdrawManager)); +// superloop.setDepositManagerModule(address(depositManager)); + +// superloopBtc.setAccountantModule(address(accountantBtc)); +// superloopBtc.setWithdrawManagerModule(address(withdrawManagerBtc)); +// superloopBtc.setDepositManagerModule(address(depositManagerBtc)); + +// vm.stopPrank(); +// vm.label(address(superloop), "superloop"); + +// user1 = makeAddr("user1"); +// user2 = makeAddr("user2"); +// user3 = makeAddr("user3"); + +// vm.label(user1, "user1"); +// vm.label(user2, "user2"); +// vm.label(user3, "user3"); + +// vm.startPrank(XTZ_WHALE); +// IERC20(XTZ).transfer(user1, 100 * 10 ** 18); +// IERC20(XTZ).transfer(user2, 100 * 10 ** 18); +// IERC20(XTZ).transfer(user3, 100 * 10 ** 18); +// vm.stopPrank(); + +// vm.startPrank(POOL_ADMIN); +// IPoolConfigurator(POOL_CONFIGURATOR).setReserveFlashLoaning(ST_XTZ, true); +// IPoolConfigurator(POOL_CONFIGURATOR).setSupplyCap(ST_XTZ, 10000000); +// vm.stopPrank(); +// } + +// function _resolveDepositRequestsCall(address asset, uint256 amount, bytes memory data) +// internal +// view +// returns (DataTypes.ModuleExecutionData memory) +// { +// DataTypes.ResolveDepositRequestsData memory resolveDepositRequestsData = +// DataTypes.ResolveDepositRequestsData({asset: asset, amount: amount, callbackExecutionData: data}); +// return DataTypes.ModuleExecutionData({ +// executionType: DataTypes.CallType.CALL, +// module: address(depositManager), +// data: abi.encodeWithSelector(depositManager.resolveDepositRequests.selector, resolveDepositRequestsData) +// }); +// } + +// function _resolveWithdrawRequestsCall(uint256 shares, DataTypes.WithdrawRequestType requestType, bytes memory data) +// internal +// view +// returns (DataTypes.ModuleExecutionData memory) +// { +// DataTypes.ResolveWithdrawRequestsData memory resolveWithdrawRequestsData = DataTypes.ResolveWithdrawRequestsData({ +// shares: shares, +// requestType: requestType, +// callbackExecutionData: data +// }); +// return DataTypes.ModuleExecutionData({ +// executionType: DataTypes.CallType.CALL, +// module: address(withdrawManager), +// data: abi.encodeWithSelector(withdrawManager.resolveWithdrawRequests.selector, resolveWithdrawRequestsData) +// }); +// } + +// function _flashloanCall(address asset, uint256 amount, bytes memory data) +// internal +// view +// returns (DataTypes.ModuleExecutionData memory) +// { +// DataTypes.AaveV3FlashloanParams memory flashloanParams = DataTypes.AaveV3FlashloanParams({ +// asset: asset, +// amount: amount, +// referralCode: 0, +// callbackExecutionData: data +// }); +// return DataTypes.ModuleExecutionData({ +// executionType: DataTypes.CallType.DELEGATECALL, +// module: address(flashloanModule), +// data: abi.encodeWithSelector(flashloanModule.execute.selector, flashloanParams) +// }); +// } + +// function _supplyCall(address asset, uint256 amount) internal view returns (DataTypes.ModuleExecutionData memory) { +// DataTypes.AaveV3ActionParams memory supplyParams = DataTypes.AaveV3ActionParams({asset: asset, amount: amount}); +// return DataTypes.ModuleExecutionData({ +// executionType: DataTypes.CallType.DELEGATECALL, +// module: address(supplyModule), +// data: abi.encodeWithSelector(supplyModule.execute.selector, supplyParams) +// }); +// } + +// function _borrowCall(address asset, uint256 amount) internal view returns (DataTypes.ModuleExecutionData memory) { +// DataTypes.AaveV3ActionParams memory borrowParams = DataTypes.AaveV3ActionParams({asset: asset, amount: amount}); +// return DataTypes.ModuleExecutionData({ +// executionType: DataTypes.CallType.DELEGATECALL, +// module: address(borrowModule), +// data: abi.encodeWithSelector(borrowModule.execute.selector, borrowParams) +// }); +// } + +// function _swapCallExactOut( +// address tokenIn, +// address tokenOut, +// uint256 swapAmount, +// uint256 amountOut, +// address router, +// uint24 fee +// ) internal view returns (DataTypes.ModuleExecutionData memory) { +// DataTypes.ExecuteSwapParamsData[] memory swapParamsData = new DataTypes.ExecuteSwapParamsData[](2); +// swapParamsData[0] = DataTypes.ExecuteSwapParamsData({ +// target: tokenIn, +// data: abi.encodeWithSelector(IERC20.approve.selector, router, swapAmount) +// }); +// swapParamsData[1] = DataTypes.ExecuteSwapParamsData({ +// target: router, +// data: abi.encodeWithSelector( +// IRouter.exactOutputSingle.selector, +// IRouter.ExactOutputSingleParams({ +// tokenIn: tokenIn, +// tokenOut: tokenOut, +// fee: fee, +// recipient: address(superloop), +// amountOut: amountOut, +// amountInMaximum: swapAmount, +// sqrtPriceLimitX96: 0 +// }) +// ) +// }); +// DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ +// tokenIn: tokenIn, +// tokenOut: tokenOut, +// amountIn: swapAmount, +// maxAmountIn: swapAmount, +// minAmountOut: amountOut, +// data: swapParamsData +// }); + +// return DataTypes.ModuleExecutionData({ +// executionType: DataTypes.CallType.DELEGATECALL, +// module: address(dexModule), +// data: abi.encodeWithSelector(dexModule.execute.selector, swapParams) +// }); +// } + +// function _swapCallExactIn( +// address tokenIn, +// address tokenOut, +// uint256 withdrawAmount, +// uint256 amountOut, +// address router, +// uint24 fee +// ) internal view returns (DataTypes.ModuleExecutionData memory) { +// DataTypes.ExecuteSwapParamsData[] memory swapParamsData = new DataTypes.ExecuteSwapParamsData[](2); +// swapParamsData[0] = DataTypes.ExecuteSwapParamsData({ +// target: tokenIn, +// data: abi.encodeWithSelector(IERC20.approve.selector, router, withdrawAmount) +// }); +// swapParamsData[1] = DataTypes.ExecuteSwapParamsData({ +// target: router, +// data: abi.encodeWithSelector( +// IRouter.exactInputSingle.selector, +// IRouter.ExactInputSingleParams({ +// tokenIn: tokenIn, +// tokenOut: tokenOut, +// fee: fee, +// recipient: address(superloop), +// amountIn: withdrawAmount, +// amountOutMinimum: amountOut, +// sqrtPriceLimitX96: 0 +// }) +// ) +// }); + +// DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ +// tokenIn: tokenIn, +// tokenOut: tokenOut, +// amountIn: withdrawAmount, +// maxAmountIn: withdrawAmount, +// minAmountOut: amountOut, +// data: swapParamsData +// }); + +// return DataTypes.ModuleExecutionData({ +// executionType: DataTypes.CallType.DELEGATECALL, +// module: address(dexModule), +// data: abi.encodeWithSelector(dexModule.execute.selector, swapParams) +// }); +// } + +// function _swapCallExactOutCurve( +// address tokenIn, +// address tokenOut, +// address pool, +// uint256 amountIn, +// uint256 amountOut, +// CURVE_IJ memory swap +// ) internal view returns (DataTypes.ModuleExecutionData memory) { +// DataTypes.ExecuteSwapParamsData[] memory swapParamsData = new DataTypes.ExecuteSwapParamsData[](2); +// swapParamsData[0] = DataTypes.ExecuteSwapParamsData({ +// target: tokenIn, +// data: abi.encodeWithSelector(IERC20.approve.selector, pool, amountIn) +// }); +// swapParamsData[1] = DataTypes.ExecuteSwapParamsData({ +// target: pool, +// data: abi.encodeWithSelector(ICurvePool.exchange.selector, swap.i, swap.j, amountIn, amountOut) +// }); + +// DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ +// tokenIn: tokenIn, +// tokenOut: tokenOut, +// amountIn: amountIn, +// maxAmountIn: amountIn, +// minAmountOut: amountOut, +// data: swapParamsData +// }); + +// return DataTypes.ModuleExecutionData({ +// executionType: DataTypes.CallType.DELEGATECALL, +// module: address(dexModule), +// data: abi.encodeWithSelector(dexModule.execute.selector, swapParams) +// }); +// } + +// function _repayCall(address asset, uint256 amount) internal view returns (DataTypes.ModuleExecutionData memory) { +// DataTypes.AaveV3ActionParams memory repayParams = DataTypes.AaveV3ActionParams({asset: asset, amount: amount}); +// return DataTypes.ModuleExecutionData({ +// executionType: DataTypes.CallType.DELEGATECALL, +// module: address(repayModule), +// data: abi.encodeWithSelector(repayModule.execute.selector, repayParams) +// }); +// } + +// function _withdrawCall(address asset, uint256 amount) +// internal +// view +// returns (DataTypes.ModuleExecutionData memory) +// { +// DataTypes.AaveV3ActionParams memory withdrawParams = +// DataTypes.AaveV3ActionParams({asset: asset, amount: amount}); +// return DataTypes.ModuleExecutionData({ +// executionType: DataTypes.CallType.DELEGATECALL, +// module: address(withdrawModule), +// data: abi.encodeWithSelector(withdrawModule.execute.selector, withdrawParams) +// }); +// } + +// /** +// * @dev Creates a partial deposit with resolution. +// * @notice Creates 3 deposit requests with 100 xtz each. +// * @notice request 1 is fully processed, request 2 is partially processed, request 3 is unprocessed. +// */ +// function _createPartialDepositWithResolution(bool depositAll) internal returns (uint256, uint256) { +// uint256 depositAmountUnscaled = 100; +// uint256 depositAmount = depositAmountUnscaled * XTZ_SCALE; +// _makeDepositRequest(depositAmount, user1, true); +// _makeDepositRequest(depositAmount, user2, true); +// _makeDepositRequest(depositAmount, user3, true); + +// uint256 depositAmountUnscaledBatch = depositAll ? depositAmountUnscaled * 3 : (3 * depositAmountUnscaled) / 2; +// uint256 supplyAmountUnscaled = (3 * depositAmountUnscaledBatch); // 3x +// uint256 borrowAmountUnscaled = (3 * supplyAmountUnscaled) / 4; + +// uint256 depositAmountBatch = depositAmountUnscaledBatch * XTZ_SCALE; +// // build the operate call +// uint256 supplyAmount = supplyAmountUnscaled * STXTZ_SCALE; +// uint256 borrowAmount = borrowAmountUnscaled * XTZ_SCALE; +// uint256 swapAmount = borrowAmount + depositAmountBatch; +// uint256 supplyAmountWithPremium = supplyAmount + (supplyAmount * 1) / 10000; + +// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](3); +// moduleExecutionData[0] = _supplyCall(ST_XTZ, supplyAmount); +// moduleExecutionData[1] = _borrowCall(XTZ, borrowAmount); + +// moduleExecutionData[2] = +// _swapCallExactOutCurve(XTZ, ST_XTZ, XTZ_STXTZ_POOL, swapAmount, supplyAmountWithPremium, XTZ_STXTZ_SWAP); + +// DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); +// intermediateExecutionData[0] = _flashloanCall(ST_XTZ, supplyAmount, abi.encode(moduleExecutionData)); + +// DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); +// finalExecutionData[0] = +// _resolveDepositRequestsCall(XTZ, depositAmountBatch, abi.encode(intermediateExecutionData)); + +// uint256 exchangeRateBefore = superloop.convertToAssets(ONE_SHARE); + +// uint256 user1SharesBefore = superloop.balanceOf(user1); +// uint256 user2SharesBefore = superloop.balanceOf(user2); +// uint256 user3SharesBefore = superloop.balanceOf(user3); + +// vm.prank(admin); +// superloop.operate(finalExecutionData); + +// if (!depositAll) { +// // user 1 and user user 2 should get shares +// // user 3 should not get shares + +// uint256 user1SharesAfter = superloop.balanceOf(user1); +// uint256 user2SharesAfter = superloop.balanceOf(user2); +// uint256 user3SharesAfter = superloop.balanceOf(user3); + +// // deposit manager should have 150 xtz now +// assertEq(IERC20(XTZ).balanceOf(address(depositManager)), 150 * XTZ_SCALE); +// // pending deposits should be 150 xtz +// assertEq(depositManager.totalPendingDeposits(), 150 * XTZ_SCALE); + +// // resolution id pointer should be 2 +// assertEq(depositManager.resolutionIdPointer(), 2); + +// // deposit request 1 should be fully processed +// DataTypes.DepositRequestData memory depositRequest1 = depositManager.depositRequest(1); +// assertEq(uint256(depositRequest1.state), uint256(DataTypes.RequestProcessingState.FULLY_PROCESSED)); +// assertEq(depositRequest1.amountProcessed, 100 * XTZ_SCALE); + +// // deposit request 2 should be partially processed +// DataTypes.DepositRequestData memory depositRequest2 = depositManager.depositRequest(2); +// assertEq(uint256(depositRequest2.state), uint256(DataTypes.RequestProcessingState.PARTIALLY_PROCESSED)); +// assertEq(depositRequest2.amountProcessed, 50 * XTZ_SCALE); + +// // deposit request 3 should be unprocessed +// DataTypes.DepositRequestData memory depositRequest3 = depositManager.depositRequest(3); +// assertEq(uint256(depositRequest3.state), uint256(DataTypes.RequestProcessingState.UNPROCESSED)); + +// assertEq(user1SharesAfter - user1SharesBefore, depositRequest1.sharesMinted); +// assertEq(user2SharesAfter - user2SharesBefore, depositRequest2.sharesMinted); +// assertEq(user3SharesAfter - user3SharesBefore, depositRequest3.sharesMinted); + +// // exchange rate before should equal to exchange rate after +// uint256 exchangeRateAfter = superloop.convertToAssets(ONE_SHARE); +// assertApproxEqAbs(exchangeRateBefore, exchangeRateAfter, 1000); +// } + +// return (supplyAmountUnscaled, borrowAmountUnscaled); +// } + +// function _makeDepositRequest(uint256 depositAmount, address user, bool _deal) internal { +// if (_deal) { +// deal(XTZ, user, depositAmount); +// } + +// vm.startPrank(user); +// IERC20(XTZ).approve(address(depositManager), depositAmount); +// depositManager.requestDeposit(depositAmount, address(0)); +// vm.stopPrank(); +// } + +// function _makeWithdrawRequest( +// address share, +// uint256 shareAmount, +// address user, +// DataTypes.WithdrawRequestType requestType +// ) internal { +// vm.startPrank(user); +// IERC20(share).approve(address(withdrawManager), shareAmount); +// withdrawManager.requestWithdraw(shareAmount, requestType); +// vm.stopPrank(); +// } + +// /** +// * @dev Creates a partial withdraw with resolution. +// * @notice Creates 3 withdraw requests with 100 shares each. +// * @notice request 1 is fully processed, request 2 is partially processed, request 3 is unprocessed. +// */ +// function _createPartialWithdrawWithResolution(DataTypes.WithdrawRequestType requestType) +// internal +// returns (uint256, uint256, uint256) +// { +// (uint256 supplyAmountUnscaled, uint256 borrowAmountUnscaled) = _createPartialDepositWithResolution(true); + +// // make 3 withdraw requests +// uint256 user1ShareBalance = superloop.balanceOf(user1); +// uint256 user2ShareBalance = superloop.balanceOf(user2); +// uint256 user3ShareBalance = superloop.balanceOf(user3); +// _makeWithdrawRequest(address(superloop), user1ShareBalance, user1, requestType); +// _makeWithdrawRequest(address(superloop), user2ShareBalance, user2, requestType); +// _makeWithdrawRequest(address(superloop), user3ShareBalance, user3, requestType); + +// // resolve 1st fully, and 2nd partially +// // repay half of borrow amount +// uint256 repayAmount = (borrowAmountUnscaled * XTZ_SCALE) / 2; +// uint256 withdrawAmount = ((supplyAmountUnscaled + 10) * STXTZ_SCALE) / 2; +// uint256 repayAmountWithPremium = repayAmount + (repayAmount * 1) / 10000; +// uint256 totalShares = superloop.totalSupply(); +// uint256 sharesToResolve = totalShares / 2; + +// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](3); +// moduleExecutionData[0] = _repayCall(XTZ, repayAmount); +// moduleExecutionData[1] = _withdrawCall(ST_XTZ, withdrawAmount); +// moduleExecutionData[2] = +// _swapCallExactOutCurve(ST_XTZ, XTZ, XTZ_STXTZ_POOL, withdrawAmount, repayAmountWithPremium, STXTZ_XTZ_SWAP); + +// DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); +// intermediateExecutionData[0] = _flashloanCall(XTZ, repayAmount, abi.encode(moduleExecutionData)); + +// DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); +// finalExecutionData[0] = +// _resolveWithdrawRequestsCall(sharesToResolve, requestType, abi.encode(intermediateExecutionData)); + +// uint256 exchangeRateBefore = superloop.convertToAssets(ONE_SHARE); + +// vm.prank(admin); +// superloop.operate(finalExecutionData); + +// // withdraw requests should have claimable > 0 and sharesProcessed > 0, exchange rate remains the same +// DataTypes.WithdrawRequestData memory withdrawRequest1 = withdrawManager.withdrawRequest(1, requestType); +// DataTypes.WithdrawRequestData memory withdrawRequest2 = withdrawManager.withdrawRequest(2, requestType); +// DataTypes.WithdrawRequestData memory withdrawRequest3 = withdrawManager.withdrawRequest(3, requestType); +// uint256 resolutionIdPointer = withdrawManager.resolutionIdPointer(requestType); +// uint256 totalPendingWithdraws = withdrawManager.totalPendingWithdraws(requestType); + +// assertTrue(withdrawRequest1.amountClaimable > 0); +// assertTrue(withdrawRequest1.sharesProcessed == withdrawRequest1.shares); +// assertTrue(withdrawRequest2.amountClaimable > 0); +// assertTrue(withdrawRequest2.sharesProcessed > 0 && withdrawRequest2.sharesProcessed < withdrawRequest2.shares); +// assertTrue(withdrawRequest3.amountClaimable == 0); +// assertTrue(withdrawRequest3.sharesProcessed == 0); + +// assertTrue(withdrawRequest1.state == DataTypes.RequestProcessingState.FULLY_PROCESSED); +// assertTrue(withdrawRequest2.state == DataTypes.RequestProcessingState.PARTIALLY_PROCESSED); +// assertTrue(withdrawRequest3.state == DataTypes.RequestProcessingState.UNPROCESSED); + +// uint256 exchangeRateAfter = superloop.convertToAssets(ONE_SHARE); + +// assertApproxEqAbs(exchangeRateAfter, exchangeRateBefore, 1000); +// assertEq(totalPendingWithdraws, totalShares - sharesToResolve); +// assertEq(resolutionIdPointer, 2); + +// return (totalShares - sharesToResolve, repayAmount, withdrawAmount); +// } +// } diff --git a/test/core/integration/Migration.t.sol b/test/core/integration/Migration.t.sol index 7609166..e4a95f9 100644 --- a/test/core/integration/Migration.t.sol +++ b/test/core/integration/Migration.t.sol @@ -1,482 +1,482 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.13; - -import {IntegrationBase} from "../integration/IntegrationBase.sol"; -import {DataTypes} from "../../../src/common/DataTypes.sol"; -import {MigrationHelper} from "../../../src/helpers/MigrationHelper.sol"; -import {ISuperloop} from "../../../src/interfaces/ISuperloop.sol"; -import {IAccountantModule} from "../../../src/interfaces/IAccountantModule.sol"; -import {console} from "forge-std/console.sol"; -import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -import {ISuperloopLegacy} from "../../../src/helpers/ISuperloopLegacy.sol"; -import {Ownable} from "openzeppelin-contracts/contracts/access/Ownable.sol"; - -/** - * Prerequisites for the migration to work - * 1. The old vault's withdraw manager must be empty - * 2. Both the vaults should share common aave action modules and dex module - * 2. Migration helper contract must be set as priveledged address in the old vault - * 3. Migration helper contract must be set as these in the new vault - * 1. vault operator - * 2. deposit manager in the new vault - * 3. vault in the new accountant module - * 4. Post migration - * 1. Update vault operator on the new vault - * 2. Update vault in the new accountant module - * 3. Update deposit manager on the new vault - * 4. Remove migration helper as priveledged address from the old vault - */ -contract MigrationTest is IntegrationBase { - address REPAY_MODULE = 0x9AF8cCabC21ff594dA237f9694C4A9C6123480c6; - address WITHDRAW_MODULE = 0x1f5Ba080B9E5705DA47212167cA44611F78DB130; - address DEPOSIT_MODULE = 0x66e82124412C61D7fF90ACFBa82936DD037D737E; - address BORROW_MODULE = 0x3de57294989d12066a94a8A16E977992F3cF8433; - address DEX_MODULE = 0x38F5efC1267F6103c9b0FE802E1731E245f09Cd0; - - address oldVault = 0xe24e5DEbA01Ab0B5D78A0093442De0864832803E; - address oldVaultBtc = 0xC557529dd252e5a02E6C653b0B88984aFa3c8199; - - // currently there are 119 holder who need to be migrated to the new vault - address[] public ALL_HOLDERS_XTZ = [ - 0x2B38f0De645E05Ec410AE86Ef479a1e42E45Bd12, - 0xb3d1aCA9bF004d0da560930Cf9277c604eDf2283, - 0x11fC7853944570C1F9D9EBE7Ac24e2FeFddf0314, - 0xced306BfCB4a057aE3905063fD601774F9F18730, - 0x8fb2FB82fF44da85E43d0A71CA992DbBC3243c5a, - 0x5d8809340760b1bB54642BE91Bb5A2871C0d7a10, - 0x40F832B71D2C525A9aa4b4908Ec511Ed93c8a308, - 0xde5017f32C3AB32dd0286891A11F5d5D54C13728, - 0x4f2421553E571627C6801521316732693016d9cF, - 0x6EA314366871459Dbc3e1A2Eb422e23B538c7ADC, - 0x953D1668BC03e0EE9145A7c4F79956b73db90B67, - 0x46C9CCC6857E33a8209706BB5043700b4608Dda8, - 0x67Efd0FfD493Fa5C55f2a19033557557e3A5197C, - 0xbE00FA424243C56c7CF5792aFBd6b8Dc4d6CBE4E, - 0xEC23a0C9D4107cAD33271c4F1955a21f4c041208, - 0x3d72971b117EE8EF445D138557151f66CB0D408F, - 0xc2E97Ae6Ca9aAFB19CE0B8bCd1F4C50285db2377, - 0x5EdF9DeC5b003AF80B65e129Ab17DBE64106a8e3, - 0x669bd328f6C494949Ed9fB2dc8021557A6Dd005f, - 0x664324F8B7430b4C22Dc036234Dd752A4755Fda7, - 0x07ABc6225724a3f29e09173f585AacDE7A701dB3, - 0xB35722e595F2B974c2B1D14A80Fab5d0c60c2fa3, - 0x37b14bF02B1D01E196E3439d1689fEf845E9314A, - 0x93e3A2a50e7A6091B877Fc939e0a3f43954811d8, - 0xd9aeb979cbe3a85F2d6fca3723f846Af8CA56E05, - 0x4A2cFAa5B24850493298DCb8969fe11FEeFfc366, - 0x1C6Aed70006d8d65c6407Fa9b27C9359C1572f40, - 0x92c34B736B832d33209497f3904C4423d11E2a8a, - 0xCAf9131ADF841cC8A3f8C14cBc94Aa16cF0a2faA, - 0x9214835f82752E0574F3828A4400C36Ba386Df82, - 0xF09E6d5EE9b5B7FC84412260FD6E6D70dCadcd9C, - 0x8b529eF78046008f9d1FbC91c7407030De96EE32, - 0xa77E705d7166750F53F60ca7e246BAFBE40f5c42, - 0x4fb30f8CcE1F80FC9CC45F7F626069be7549aF59, - 0xc210a4D56D99E095b126664fDCE545b369ED3FD0, - 0x03adFaA573aC1a9b19D2b8F79a5aAFFb9c2A0532, - 0x79bc970B69CC8fb31406C409692eD19612E39731, - 0xb9b4800282BDC0DD2284fD321cf6800a24a7B8B0, - 0x06e477F88ae55721B72f0F2c545328eDe998803e, - 0xc25404122C1776689A6fD620d0F816Cb9e36e5dD, - 0x2192013589F926637cB288004db2f1dadb71D390, - 0xb0DebcB643eE79f19Ef659Bd01D0FAC12d058604, - 0x09e63186b4F94FafcCC60a42D3f77b4A10995674, - 0x521e6f07bDfa2fF8D072887F0ef3bB908a3D9e2a, - 0x341979e95a3e2969EaDa286b635d6e29F7a76a71, - 0xAD89C778C7dBC0C7546cE7Fdf6A1d87C369A7892, - 0x4987f13D50AC3246E589433349095492e820f8fd, - 0x22472e7cA126b25fA031DeA6A7E0a7fbD9B2cdfd, - 0x595e187831CB1ebAf3e4c09423eef3E74DfDaD1E, - 0xaD96ACD2e6574682AEBf8f25774FA643bcaa3B5a, - 0x82C421023dBE34CC526F4c027577363E6C7Af9b6, - 0xc51101BAbC9Cc75118fBCe5187914e45841A7f01, - 0x09505A42D693D828B78CC87ed64DB9A0348Ca5e6, - 0xdD543DC0efeA305A4c595305C784166fbe87897B, - 0xbb85bFF98F145af30BA935Ae4DeD97C7A5Ce9bF2, - 0xfd005e01FAa45d3CD3fD4641FBCd9FFa1d26C703, - 0xaB14C744e5316475dd2384E6Bb95F9eCF4480bD5, - 0x595f5e9cF36dF0AAf12Ae27f1b3dC18622Fc5748, - 0xF5AC5943D16fC865824910033B756519DC396682, - 0xa0c507E52359160a334d9f1AeAA459b8BD39568a, - 0x0E16cAA0E000cD3B68b4128C6aE62E846cb5b431, - 0x603c081e1d72063Cd691CAD3Bd2FEd68eBE8922a, - 0x5c53414E1f15D7668c2b9EC0A92482A64845f5f6, - 0xa87D56273CBE184927D92eEF1613ab2a72eacAC8, - 0xCc9757171B3cf4Dc86D1a86370D5B10662519096, - 0xeB92f0f58b218ad22a22741D5aa5bb7250E70045, - 0xa2608e76d835D0b3DB2Bbcbd4Bd1F78D47208353, - 0x9fD8Ef7E3867E962B401bC7289272653FEC11245, - 0x73cF6fCbEb5a5f56e550Cce5e0eeAC47f622EA2e, - 0x9b4A7002a086d4F8dFf04Af524987DdCa9b6ddCd, - 0x94Ae5310ba47b42D6D8DF3F20cD55650Ad43Be37, - 0xFd01eF34D30E6fA1169CBa769a22eEE454906f38, - 0xE3c3cD192Bdf289558A0bA645bc2c2c822F7EB06, - 0x18514a31077b33f32BDbeA52443f2C0EEb094Be1, - 0x0E9852b16AE49C99B84b0241E3C6F4a5692C6b05, - 0x2E6Ff365367c9DB7C4a6140d37394aB8CF25C8A2, - 0x49400E8A2d270669ED7cfc4Fd5A2804c0A87e1BB, - 0xDDc91d503Eeb7C3CB78559fB998C0a23354821ee, - 0x0C5Cd230e9EBF5A707CF1f822Dfc49664304DfD2, - 0x92751712B2F6ADE4AD1F35a837591A66394C6799, - 0x57Fe7D8A10F5C4aC2Ca953a5B0a6b4BBfEC187C9, - 0x5413E315A8c242132aC5DD626B0eceE616e5023D, - 0xf458B8FB16D661bC1E260991EBa9b1FFBC8bA39b, - 0xC6Ad105483F89de96A3E423980fB550465A05D9b, - 0x26C43439031301a85e19868647DF46177869c4C8, - 0x9cB4a586bbdE7528DB256c3FCE08845255d892D9, - 0xb1f607F3B753bEf4756a30cfC235629A0c2d1E57, - 0x1CbB1E8274E44287517A34De6AF3CF97dfD9E0Cb, - 0x2b3F539dAf59126F6F566adD0dEDc19959305e9d, - 0x2eCe359621497F4C9053c30912e9ecB7e74718c6, - 0x1A50D1AeD031eE6B22b37c84eD8f3f3487C5Ca47, - 0x117AD17Bf596D6337931f41141F34275Eab16129, - 0x5BD39fcEE33D45C5c5f615e09D2AA785Aa0c36C6, - 0x698230D8445C21E99f4CC81F8F733d205a661cD3, - 0x10b453C5379877d6b55B71D73Af7D8Fbc69eeF91, - 0x13deFCe8Be050f902f8eE06CB7f0E743e3e8b705, - 0xFF5A5bBC1b0f124974165b2190f460438d7CD220, - 0x7F5B2c355bFC0C6F3aE2D394534C29D3609FF542, - 0x007EA857fdbE8D19997b507a21B4c377e3B21D82, - 0x97F4aa12F637BAe9c07cA492eE281534b4F3BB50, - 0x23b3511474c960960E4C030e572dADE633Cfc0F6, - 0x7cA50563448C1f87cF131D96dEF7124f995cf3aC, - 0xA8617c6BdE242B213bA283c93aCf034dfcbE66fA, - 0x68f6609d45A9dA001A88f7A3b9ec236Acf27e1f5, - 0x37ae94b377B6D6522BCED1a448D144C69B6bfa9a, - 0x8Fb2B9010780c418fbf84733ca3F0F5623EC2e7a, - 0x5F4BD8107FF5EB8fD25480C938662bBC11da27B8, - 0x57453361422BfBE5332eF75CBE1C38242c5deEEA, - 0x96b29b8dD6240144e203F9f45D55F8FE6fC466aB, - 0x00ab2b9D924aaDCb60126A4050B68e8531A6A5a6, - 0x431588Aff8ea1BEcB1d8188D87195Aa95678BA0A, - 0x0E9B063789909565CEdA1Fba162474405A151E66, - 0x5952f70FEF1CbC26856d149646D4A8F97E923eE7, - 0x894d222eCeC91B9dFFf8bcBD164B7a72DF7469aD, - 0x8B714cB45683733fD4Dc423FE6BF9A1d9F8DDD2C, - 0x5E9b31FA592913C2aD2356ed843F66057c81455D, - 0x58F1eFd2fBf415CFC0Ce30A13c36A2642690eDA0, - 0x84e47C7E20Ee8e8d6195A99788226EB488f5Fad7, - 0xCce7AcFb0d4760df15a1f87C92324B166e9C0638 - ]; - - address[] public ALL_HOLDERS_BTC = [ - 0x9D5132Dd45CdaCd1De3a7b7f1f13da7F025fF726, - 0xa7CBb758849979CEc43FD146fe01EB2BF560202C, - 0x6bA9c7d86eBD6A20817d482C42C9dE2806EeFB7e, - 0x9D0214C3dB28875fe2b9eb9cD9d4F71E7817890A, - 0x2B38f0De645E05Ec410AE86Ef479a1e42E45Bd12, - 0x8fb2FB82fF44da85E43d0A71CA992DbBC3243c5a, - 0xced306BfCB4a057aE3905063fD601774F9F18730, - 0x0C1856Cb7444cc8596c706185b54535F45C2623B, - 0x11fC7853944570C1F9D9EBE7Ac24e2FeFddf0314, - 0xb3d1aCA9bF004d0da560930Cf9277c604eDf2283, - 0xB438Dbaae78225eEfcDBB04E207A8eFB06036a2c, - 0x997b96BAD648c39226281Bc002f9857274E42A01, - 0xd52929B69680A6f74D2eB9c8F1ef482f37b1b32B, - 0x706FC1a8e457De0cf52e7679C2922aEF7F7a397e, - 0x3497818F50f79A11236B941dDAd35Da68A57864E, - 0xB35722e595F2B974c2B1D14A80Fab5d0c60c2fa3, - 0xA9f7b86FCC86EA1b913dDcaB8c9514a8e677666E, - 0x7077e395C9FA4E480366A5DC5792d2504d78dffF, - 0x664324F8B7430b4C22Dc036234Dd752A4755Fda7, - 0xD22ADba687aAd7DD9CEC3b18D970C581734783bA, - 0x5BC81274740A73D33ec8539182c326b8b58004C2, - 0x02F73B8e29dD6Ec38Bc5D8B3826051DC562c3060, - 0x4e90Da1dDCD5B0Fb295b6A69251b5D04D5bCCA7a, - 0x150a0D516C0ceFa39f980Da57d5422E91A94238b, - 0x4fb30f8CcE1F80FC9CC45F7F626069be7549aF59, - 0xF382ce5457Bba6113e082DD638b6671Cb2277B1f, - 0xb95f08B2eda7B23Ce412A6dE82eA5f5100335A3A, - 0x5b6Da8BC8696Aa4D20030151345ec652cf1eC727, - 0x0396816A361e2353a91Fde8438600a9353b34ce7, - 0xA1ebf2043e76446eFA2724bD1Ec18321776096FF, - 0x9214835f82752E0574F3828A4400C36Ba386Df82, - 0xb0DebcB643eE79f19Ef659Bd01D0FAC12d058604, - 0x3d72971b117EE8EF445D138557151f66CB0D408F, - 0x09e63186b4F94FafcCC60a42D3f77b4A10995674, - 0xc210a4D56D99E095b126664fDCE545b369ED3FD0, - 0xEC23a0C9D4107cAD33271c4F1955a21f4c041208, - 0x03adFaA573aC1a9b19D2b8F79a5aAFFb9c2A0532, - 0x6a58EBB35664A171A1B070DE48ee93278A63c168, - 0x521e6f07bDfa2fF8D072887F0ef3bB908a3D9e2a, - 0xF5AC5943D16fC865824910033B756519DC396682, - 0xBD73cF5baf12F120Ee3f6C4ad82df9a12649e578, - 0x44119A62EA645242234cAF408c4c20513E660EBb - ]; - - function setUp() public override { - super.setUp(); - - DataTypes.AaveV3EmodeParams memory params = DataTypes.AaveV3EmodeParams({emodeCategory: 3}); - - DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); - moduleExecutionData[0] = DataTypes.ModuleExecutionData({ - executionType: DataTypes.CallType.DELEGATECALL, - module: address(emodeModule), - data: abi.encodeWithSelector(emodeModule.execute.selector, params) - }); - - DataTypes.AaveV3EmodeParams memory paramsBtc = DataTypes.AaveV3EmodeParams({emodeCategory: 2}); - DataTypes.ModuleExecutionData[] memory moduleExecutionDataBtc = new DataTypes.ModuleExecutionData[](1); - moduleExecutionDataBtc[0] = DataTypes.ModuleExecutionData({ - executionType: DataTypes.CallType.DELEGATECALL, - module: address(emodeModule), - data: abi.encodeWithSelector(emodeModule.execute.selector, paramsBtc) - }); - - vm.startPrank(admin); - superloop.operate(moduleExecutionData); - superloopBtc.operate(moduleExecutionDataBtc); - - // register the above modules - moduleRegistry.setModule("REPAY_MODULE", REPAY_MODULE); - moduleRegistry.setModule("WITHDRAW_MODULE", WITHDRAW_MODULE); - moduleRegistry.setModule("DEPOSIT_MODULE", DEPOSIT_MODULE); - moduleRegistry.setModule("BORROW_MODULE", BORROW_MODULE); - moduleRegistry.setModule("DEX_MODULE", DEX_MODULE); - - superloop.setRegisteredModule(REPAY_MODULE, true); - superloop.setRegisteredModule(WITHDRAW_MODULE, true); - superloop.setRegisteredModule(DEPOSIT_MODULE, true); - superloop.setRegisteredModule(BORROW_MODULE, true); - superloop.setRegisteredModule(DEX_MODULE, true); - - superloopBtc.setRegisteredModule(REPAY_MODULE, true); - superloopBtc.setRegisteredModule(WITHDRAW_MODULE, true); - superloopBtc.setRegisteredModule(DEPOSIT_MODULE, true); - superloopBtc.setRegisteredModule(BORROW_MODULE, true); - superloopBtc.setRegisteredModule(DEX_MODULE, true); - vm.stopPrank(); - - vm.label(oldVault, "oldVault"); - vm.label(oldVaultBtc, "oldVaultBtc"); - vm.label(address(superloop), "newVault"); - vm.label(address(superloopBtc), "newVaultBtc"); - vm.label(REPAY_MODULE, "REPAY_MODULE_OLD"); - vm.label(WITHDRAW_MODULE, "WITHDRAW_MODULE_OLD"); - vm.label(DEPOSIT_MODULE, "DEPOSIT_MODULE_OLD"); - vm.label(BORROW_MODULE, "BORROW_MODULE_OLD"); - vm.label(DEX_MODULE, "DEX_MODULE_OLD"); - - // TODO: do deposit and withdraw for both - } - - // function test_migration() public { - // // deploy migration helper - // MigrationHelper migrationHelper = new MigrationHelper( - // AAVE_V3_POOL_ADDRESSES_PROVIDER, - // REPAY_MODULE, - // WITHDRAW_MODULE, - // DEPOSIT_MODULE, - // BORROW_MODULE, - // DEX_MODULE, - // USDC - // ); - // vm.label(address(migrationHelper), "migrationHelper"); - - // // set migration helper contract as priveledged address in old vault - // address oldVaultAdmin = ISuperloop(oldVault).vaultAdmin(); - // vm.prank(oldVaultAdmin); - // ISuperloop(oldVault).setPrivilegedAddress(address(migrationHelper), true); - - // // set migration helper contract as deposit manager and vault operator in new vault - // vm.startPrank(admin); - // superloop.setDepositManagerModule(address(migrationHelper)); - // superloop.setVaultOperator(address(migrationHelper)); - - // // set migration helper as vault in new accountant module - // accountant.setVault(address(migrationHelper)); - // accountant.transferOwnership(address(migrationHelper)); - // vm.stopPrank(); - - // address[] memory users = ALL_HOLDERS_XTZ; - - // // old balances - // uint256 oldVaultXTZBalance = IERC20(XTZ).balanceOf(oldVault); - // uint256 oldVaultSTXTZBalance = IERC20(ST_XTZ).balanceOf(oldVault); - // (uint256 oldVaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(ST_XTZ, oldVault); - // (,, uint256 oldVaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(XTZ, oldVault); - - // console.log("OLD VAULT STATE INITIAL"); - // console.log("oldVaultXTZBalance", oldVaultXTZBalance); - // console.log("oldVaultSTXTZBalance", oldVaultSTXTZBalance); - // console.log("oldVaultLendBalance", oldVaultLendBalance); - // console.log("oldVaultBorrowBalance", oldVaultBorrowBalance); - // console.log("old vault total supply", ISuperloop(oldVault).totalSupply()); - // console.log("old vault total assets", ISuperloop(oldVault).totalAssets()); - - // assertEq(Ownable(address(accountant)).owner(), address(migrationHelper)); - // assertEq(accountant.vault(), address(migrationHelper)); - - // uint256 maxSharesDelta = 100; - - // // start recording gas - // uint256 startGas = gasleft(); - // uint256 batches = 4; - // migrationHelper.migrate(oldVault, address(superloop), users, ST_XTZ, XTZ, batches, maxSharesDelta, 5); - - // console.log("migration complete"); - - // uint256 endGas = gasleft(); - // uint256 gasUsed = startGas - endGas; - // console.log("gas used", gasUsed); - // uint256 newVaultXTZBalance = IERC20(XTZ).balanceOf(address(superloop)); - // uint256 newVaultSTXTZBalance = IERC20(ST_XTZ).balanceOf(address(superloop)); - // (uint256 newVaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(ST_XTZ, address(superloop)); - // (,, uint256 newVaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(XTZ, address(superloop)); - // uint256 newVaultLastRealizedFeeExchangeRate = - // IAccountantModule(ISuperloop(address(superloop)).accountant()).lastRealizedFeeExchangeRate(); - // uint256 oldVaultLastRealizedFeeExchangeRate = - // IAccountantModule(ISuperloopLegacy(oldVault).accountantModule()).lastRealizedFeeExchangeRate(); - - // console.log("NEW VAULT STATE AFTER MIGRATION"); - // console.log("newVaultXTZBalance", newVaultXTZBalance); - // console.log("newVaultSTXTZBalance", newVaultSTXTZBalance); - // console.log("newVaultLendBalance", newVaultLendBalance); - // console.log("newVaultBorrowBalance", newVaultBorrowBalance); - // console.log("new vault total supply", ISuperloop(address(superloop)).totalSupply()); - // console.log("new vault total assets", ISuperloop(address(superloop)).totalAssets()); - // console.log( - // "new vault last realized fee exchange rate", - // IAccountantModule(ISuperloop(address(superloop)).accountant()).lastRealizedFeeExchangeRate() - // ); - - // // assert balances of each of the users are the same - - // for (uint256 i = 0; i < users.length; i++) { - // assertEq(ISuperloop(address(superloop)).balanceOf(users[i]), ISuperloop(oldVault).balanceOf(users[i])); - // } - - // assertEq(newVaultLastRealizedFeeExchangeRate, oldVaultLastRealizedFeeExchangeRate); - // assertEq(Ownable(address(accountant)).owner(), address(admin)); - // assertEq(accountant.vault(), address(superloop)); - - // oldVaultXTZBalance = IERC20(XTZ).balanceOf(oldVault); - // oldVaultSTXTZBalance = IERC20(ST_XTZ).balanceOf(oldVault); - // (oldVaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(ST_XTZ, oldVault); - // (,, oldVaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(XTZ, oldVault); - - // console.log("OLD VAULT STATE AFTER MIGRATION"); - // console.log("oldVaultXTZBalance", oldVaultXTZBalance); - // console.log("oldVaultSTXTZBalance", oldVaultSTXTZBalance); - // console.log("oldVaultLendBalance", oldVaultLendBalance); - // console.log("oldVaultBorrowBalance", oldVaultBorrowBalance); - // console.log("old vault total supply", ISuperloop(oldVault).totalSupply()); - // console.log("old vault total assets", ISuperloop(oldVault).totalAssets()); - // console.log( - // "old vault last realized fee exchange rate", - // IAccountantModule(ISuperloopLegacy(oldVault).accountantModule()).lastRealizedFeeExchangeRate() - // ); - // } - - function test_migrationBtc() public { - // deploy migration helper - MigrationHelper migrationHelper = new MigrationHelper( - AAVE_V3_POOL_ADDRESSES_PROVIDER, - REPAY_MODULE, - WITHDRAW_MODULE, - DEPOSIT_MODULE, - BORROW_MODULE, - DEX_MODULE, - USDC - ); - vm.label(address(migrationHelper), "migrationHelper"); - - // set migration helper contract as priveledged address in old vault - address oldVaultAdmin = ISuperloop(oldVaultBtc).vaultAdmin(); - vm.prank(oldVaultAdmin); - ISuperloop(oldVaultBtc).setPrivilegedAddress(address(migrationHelper), true); - - // set migration helper contract as deposit manager and vault operator in new vault - vm.startPrank(admin); - superloopBtc.setDepositManagerModule(address(migrationHelper)); - superloopBtc.setVaultOperator(address(migrationHelper)); - - // set migration helper as vault in new accountant module - accountantBtc.setVault(address(migrationHelper)); - accountantBtc.transferOwnership(address(migrationHelper)); - vm.stopPrank(); - - address[] memory users = ALL_HOLDERS_BTC; - - // old balances - uint256 oldVaultBTCBalance = IERC20(WBTC).balanceOf(oldVaultBtc); - uint256 oldVaultLBTCBalance = IERC20(LBTC).balanceOf(oldVaultBtc); - (uint256 oldVaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(LBTC, oldVaultBtc); - (,, uint256 oldVaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(WBTC, oldVaultBtc); - - console.log("OLD VAULT STATE INITIAL"); - console.log("oldVaultBTCBalance", oldVaultBTCBalance); - console.log("oldVaultLBTCBalance", oldVaultLBTCBalance); - console.log("oldVaultLendBalance", oldVaultLendBalance); - console.log("oldVaultBorrowBalance", oldVaultBorrowBalance); - console.log("old vault total supply", ISuperloop(oldVaultBtc).totalSupply()); - console.log("old vault total assets", ISuperloop(oldVaultBtc).totalAssets()); - - assertEq(Ownable(address(accountantBtc)).owner(), address(migrationHelper)); - assertEq(accountantBtc.vault(), address(migrationHelper)); - - uint256 maxSharesDelta = 100; - - // start recording gas - uint256 startGas = gasleft(); - uint256 batches = 4; - migrationHelper.migrate(oldVaultBtc, address(superloopBtc), users, LBTC, WBTC, batches, maxSharesDelta, 5); - - console.log("migration complete"); - - uint256 endGas = gasleft(); - uint256 gasUsed = startGas - endGas; - console.log("gas used", gasUsed); - uint256 newVaultBTCBalance = IERC20(WBTC).balanceOf(address(superloopBtc)); - uint256 newVaultLBTCBalance = IERC20(LBTC).balanceOf(address(superloopBtc)); - (uint256 newVaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(LBTC, address(superloopBtc)); - (,, uint256 newVaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(WBTC, address(superloopBtc)); - uint256 newVaultLastRealizedFeeExchangeRate = - IAccountantModule(ISuperloop(address(superloopBtc)).accountant()).lastRealizedFeeExchangeRate(); - uint256 oldVaultLastRealizedFeeExchangeRate = - IAccountantModule(ISuperloopLegacy(oldVaultBtc).accountantModule()).lastRealizedFeeExchangeRate(); - - console.log("NEW VAULT STATE AFTER MIGRATION"); - console.log("newVaultXTZBalance", newVaultBTCBalance); - console.log("newVaultLBTCBalance", newVaultLBTCBalance); - console.log("newVaultLendBalance", newVaultLendBalance); - console.log("newVaultBorrowBalance", newVaultBorrowBalance); - console.log("new vault total supply", ISuperloop(address(superloopBtc)).totalSupply()); - console.log("new vault total assets", ISuperloop(address(superloopBtc)).totalAssets()); - console.log( - "new vault last realized fee exchange rate", - IAccountantModule(ISuperloop(address(superloop)).accountant()).lastRealizedFeeExchangeRate() - ); - - // assert balances of each of the users are the same - - for (uint256 i = 0; i < users.length; i++) { - assertEq(ISuperloop(address(superloopBtc)).balanceOf(users[i]), ISuperloop(oldVaultBtc).balanceOf(users[i])); - } - - assertEq(newVaultLastRealizedFeeExchangeRate, oldVaultLastRealizedFeeExchangeRate); - assertEq(Ownable(address(accountantBtc)).owner(), address(admin)); - assertEq(accountantBtc.vault(), address(superloopBtc)); - - console.log("gas used", gasUsed); - - oldVaultBTCBalance = IERC20(WBTC).balanceOf(oldVaultBtc); - oldVaultLBTCBalance = IERC20(LBTC).balanceOf(oldVaultBtc); - (oldVaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(LBTC, oldVaultBtc); - (,, oldVaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(WBTC, oldVaultBtc); - - console.log("OLD VAULT STATE AFTER MIGRATION"); - console.log("oldVaultBTCBalance", oldVaultBTCBalance); - console.log("oldVaultLBTCBalance", oldVaultLBTCBalance); - console.log("oldVaultLendBalance", oldVaultLendBalance); - console.log("oldVaultBorrowBalance", oldVaultBorrowBalance); - console.log("old vault total supply", ISuperloop(oldVaultBtc).totalSupply()); - console.log("old vault total assets", ISuperloop(oldVaultBtc).totalAssets()); - console.log( - "old vault last realized fee exchange rate", - IAccountantModule(ISuperloopLegacy(oldVaultBtc).accountantModule()).lastRealizedFeeExchangeRate() - ); - } -} +// // SPDX-License-Identifier: MIT + +// pragma solidity ^0.8.13; + +// import {IntegrationBase} from "../integration/IntegrationBase.sol"; +// import {DataTypes} from "../../../src/common/DataTypes.sol"; +// import {MigrationHelper} from "../../../src/helpers/MigrationHelper.sol"; +// import {ISuperloop} from "../../../src/interfaces/ISuperloop.sol"; +// import {IAccountantModule} from "../../../src/interfaces/IAccountantModule.sol"; +// import {console} from "forge-std/console.sol"; +// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +// import {ISuperloopLegacy} from "../../../src/helpers/ISuperloopLegacy.sol"; +// import {Ownable} from "openzeppelin-contracts/contracts/access/Ownable.sol"; + +// /** +// * Prerequisites for the migration to work +// * 1. The old vault's withdraw manager must be empty +// * 2. Both the vaults should share common aave action modules and dex module +// * 2. Migration helper contract must be set as priveledged address in the old vault +// * 3. Migration helper contract must be set as these in the new vault +// * 1. vault operator +// * 2. deposit manager in the new vault +// * 3. vault in the new accountant module +// * 4. Post migration +// * 1. Update vault operator on the new vault +// * 2. Update vault in the new accountant module +// * 3. Update deposit manager on the new vault +// * 4. Remove migration helper as priveledged address from the old vault +// */ +// contract MigrationTest is IntegrationBase { +// address REPAY_MODULE = 0x9AF8cCabC21ff594dA237f9694C4A9C6123480c6; +// address WITHDRAW_MODULE = 0x1f5Ba080B9E5705DA47212167cA44611F78DB130; +// address DEPOSIT_MODULE = 0x66e82124412C61D7fF90ACFBa82936DD037D737E; +// address BORROW_MODULE = 0x3de57294989d12066a94a8A16E977992F3cF8433; +// address DEX_MODULE = 0x38F5efC1267F6103c9b0FE802E1731E245f09Cd0; + +// address oldVault = 0xe24e5DEbA01Ab0B5D78A0093442De0864832803E; +// address oldVaultBtc = 0xC557529dd252e5a02E6C653b0B88984aFa3c8199; + +// // currently there are 119 holder who need to be migrated to the new vault +// address[] public ALL_HOLDERS_XTZ = [ +// 0x2B38f0De645E05Ec410AE86Ef479a1e42E45Bd12, +// 0xb3d1aCA9bF004d0da560930Cf9277c604eDf2283, +// 0x11fC7853944570C1F9D9EBE7Ac24e2FeFddf0314, +// 0xced306BfCB4a057aE3905063fD601774F9F18730, +// 0x8fb2FB82fF44da85E43d0A71CA992DbBC3243c5a, +// 0x5d8809340760b1bB54642BE91Bb5A2871C0d7a10, +// 0x40F832B71D2C525A9aa4b4908Ec511Ed93c8a308, +// 0xde5017f32C3AB32dd0286891A11F5d5D54C13728, +// 0x4f2421553E571627C6801521316732693016d9cF, +// 0x6EA314366871459Dbc3e1A2Eb422e23B538c7ADC, +// 0x953D1668BC03e0EE9145A7c4F79956b73db90B67, +// 0x46C9CCC6857E33a8209706BB5043700b4608Dda8, +// 0x67Efd0FfD493Fa5C55f2a19033557557e3A5197C, +// 0xbE00FA424243C56c7CF5792aFBd6b8Dc4d6CBE4E, +// 0xEC23a0C9D4107cAD33271c4F1955a21f4c041208, +// 0x3d72971b117EE8EF445D138557151f66CB0D408F, +// 0xc2E97Ae6Ca9aAFB19CE0B8bCd1F4C50285db2377, +// 0x5EdF9DeC5b003AF80B65e129Ab17DBE64106a8e3, +// 0x669bd328f6C494949Ed9fB2dc8021557A6Dd005f, +// 0x664324F8B7430b4C22Dc036234Dd752A4755Fda7, +// 0x07ABc6225724a3f29e09173f585AacDE7A701dB3, +// 0xB35722e595F2B974c2B1D14A80Fab5d0c60c2fa3, +// 0x37b14bF02B1D01E196E3439d1689fEf845E9314A, +// 0x93e3A2a50e7A6091B877Fc939e0a3f43954811d8, +// 0xd9aeb979cbe3a85F2d6fca3723f846Af8CA56E05, +// 0x4A2cFAa5B24850493298DCb8969fe11FEeFfc366, +// 0x1C6Aed70006d8d65c6407Fa9b27C9359C1572f40, +// 0x92c34B736B832d33209497f3904C4423d11E2a8a, +// 0xCAf9131ADF841cC8A3f8C14cBc94Aa16cF0a2faA, +// 0x9214835f82752E0574F3828A4400C36Ba386Df82, +// 0xF09E6d5EE9b5B7FC84412260FD6E6D70dCadcd9C, +// 0x8b529eF78046008f9d1FbC91c7407030De96EE32, +// 0xa77E705d7166750F53F60ca7e246BAFBE40f5c42, +// 0x4fb30f8CcE1F80FC9CC45F7F626069be7549aF59, +// 0xc210a4D56D99E095b126664fDCE545b369ED3FD0, +// 0x03adFaA573aC1a9b19D2b8F79a5aAFFb9c2A0532, +// 0x79bc970B69CC8fb31406C409692eD19612E39731, +// 0xb9b4800282BDC0DD2284fD321cf6800a24a7B8B0, +// 0x06e477F88ae55721B72f0F2c545328eDe998803e, +// 0xc25404122C1776689A6fD620d0F816Cb9e36e5dD, +// 0x2192013589F926637cB288004db2f1dadb71D390, +// 0xb0DebcB643eE79f19Ef659Bd01D0FAC12d058604, +// 0x09e63186b4F94FafcCC60a42D3f77b4A10995674, +// 0x521e6f07bDfa2fF8D072887F0ef3bB908a3D9e2a, +// 0x341979e95a3e2969EaDa286b635d6e29F7a76a71, +// 0xAD89C778C7dBC0C7546cE7Fdf6A1d87C369A7892, +// 0x4987f13D50AC3246E589433349095492e820f8fd, +// 0x22472e7cA126b25fA031DeA6A7E0a7fbD9B2cdfd, +// 0x595e187831CB1ebAf3e4c09423eef3E74DfDaD1E, +// 0xaD96ACD2e6574682AEBf8f25774FA643bcaa3B5a, +// 0x82C421023dBE34CC526F4c027577363E6C7Af9b6, +// 0xc51101BAbC9Cc75118fBCe5187914e45841A7f01, +// 0x09505A42D693D828B78CC87ed64DB9A0348Ca5e6, +// 0xdD543DC0efeA305A4c595305C784166fbe87897B, +// 0xbb85bFF98F145af30BA935Ae4DeD97C7A5Ce9bF2, +// 0xfd005e01FAa45d3CD3fD4641FBCd9FFa1d26C703, +// 0xaB14C744e5316475dd2384E6Bb95F9eCF4480bD5, +// 0x595f5e9cF36dF0AAf12Ae27f1b3dC18622Fc5748, +// 0xF5AC5943D16fC865824910033B756519DC396682, +// 0xa0c507E52359160a334d9f1AeAA459b8BD39568a, +// 0x0E16cAA0E000cD3B68b4128C6aE62E846cb5b431, +// 0x603c081e1d72063Cd691CAD3Bd2FEd68eBE8922a, +// 0x5c53414E1f15D7668c2b9EC0A92482A64845f5f6, +// 0xa87D56273CBE184927D92eEF1613ab2a72eacAC8, +// 0xCc9757171B3cf4Dc86D1a86370D5B10662519096, +// 0xeB92f0f58b218ad22a22741D5aa5bb7250E70045, +// 0xa2608e76d835D0b3DB2Bbcbd4Bd1F78D47208353, +// 0x9fD8Ef7E3867E962B401bC7289272653FEC11245, +// 0x73cF6fCbEb5a5f56e550Cce5e0eeAC47f622EA2e, +// 0x9b4A7002a086d4F8dFf04Af524987DdCa9b6ddCd, +// 0x94Ae5310ba47b42D6D8DF3F20cD55650Ad43Be37, +// 0xFd01eF34D30E6fA1169CBa769a22eEE454906f38, +// 0xE3c3cD192Bdf289558A0bA645bc2c2c822F7EB06, +// 0x18514a31077b33f32BDbeA52443f2C0EEb094Be1, +// 0x0E9852b16AE49C99B84b0241E3C6F4a5692C6b05, +// 0x2E6Ff365367c9DB7C4a6140d37394aB8CF25C8A2, +// 0x49400E8A2d270669ED7cfc4Fd5A2804c0A87e1BB, +// 0xDDc91d503Eeb7C3CB78559fB998C0a23354821ee, +// 0x0C5Cd230e9EBF5A707CF1f822Dfc49664304DfD2, +// 0x92751712B2F6ADE4AD1F35a837591A66394C6799, +// 0x57Fe7D8A10F5C4aC2Ca953a5B0a6b4BBfEC187C9, +// 0x5413E315A8c242132aC5DD626B0eceE616e5023D, +// 0xf458B8FB16D661bC1E260991EBa9b1FFBC8bA39b, +// 0xC6Ad105483F89de96A3E423980fB550465A05D9b, +// 0x26C43439031301a85e19868647DF46177869c4C8, +// 0x9cB4a586bbdE7528DB256c3FCE08845255d892D9, +// 0xb1f607F3B753bEf4756a30cfC235629A0c2d1E57, +// 0x1CbB1E8274E44287517A34De6AF3CF97dfD9E0Cb, +// 0x2b3F539dAf59126F6F566adD0dEDc19959305e9d, +// 0x2eCe359621497F4C9053c30912e9ecB7e74718c6, +// 0x1A50D1AeD031eE6B22b37c84eD8f3f3487C5Ca47, +// 0x117AD17Bf596D6337931f41141F34275Eab16129, +// 0x5BD39fcEE33D45C5c5f615e09D2AA785Aa0c36C6, +// 0x698230D8445C21E99f4CC81F8F733d205a661cD3, +// 0x10b453C5379877d6b55B71D73Af7D8Fbc69eeF91, +// 0x13deFCe8Be050f902f8eE06CB7f0E743e3e8b705, +// 0xFF5A5bBC1b0f124974165b2190f460438d7CD220, +// 0x7F5B2c355bFC0C6F3aE2D394534C29D3609FF542, +// 0x007EA857fdbE8D19997b507a21B4c377e3B21D82, +// 0x97F4aa12F637BAe9c07cA492eE281534b4F3BB50, +// 0x23b3511474c960960E4C030e572dADE633Cfc0F6, +// 0x7cA50563448C1f87cF131D96dEF7124f995cf3aC, +// 0xA8617c6BdE242B213bA283c93aCf034dfcbE66fA, +// 0x68f6609d45A9dA001A88f7A3b9ec236Acf27e1f5, +// 0x37ae94b377B6D6522BCED1a448D144C69B6bfa9a, +// 0x8Fb2B9010780c418fbf84733ca3F0F5623EC2e7a, +// 0x5F4BD8107FF5EB8fD25480C938662bBC11da27B8, +// 0x57453361422BfBE5332eF75CBE1C38242c5deEEA, +// 0x96b29b8dD6240144e203F9f45D55F8FE6fC466aB, +// 0x00ab2b9D924aaDCb60126A4050B68e8531A6A5a6, +// 0x431588Aff8ea1BEcB1d8188D87195Aa95678BA0A, +// 0x0E9B063789909565CEdA1Fba162474405A151E66, +// 0x5952f70FEF1CbC26856d149646D4A8F97E923eE7, +// 0x894d222eCeC91B9dFFf8bcBD164B7a72DF7469aD, +// 0x8B714cB45683733fD4Dc423FE6BF9A1d9F8DDD2C, +// 0x5E9b31FA592913C2aD2356ed843F66057c81455D, +// 0x58F1eFd2fBf415CFC0Ce30A13c36A2642690eDA0, +// 0x84e47C7E20Ee8e8d6195A99788226EB488f5Fad7, +// 0xCce7AcFb0d4760df15a1f87C92324B166e9C0638 +// ]; + +// address[] public ALL_HOLDERS_BTC = [ +// 0x9D5132Dd45CdaCd1De3a7b7f1f13da7F025fF726, +// 0xa7CBb758849979CEc43FD146fe01EB2BF560202C, +// 0x6bA9c7d86eBD6A20817d482C42C9dE2806EeFB7e, +// 0x9D0214C3dB28875fe2b9eb9cD9d4F71E7817890A, +// 0x2B38f0De645E05Ec410AE86Ef479a1e42E45Bd12, +// 0x8fb2FB82fF44da85E43d0A71CA992DbBC3243c5a, +// 0xced306BfCB4a057aE3905063fD601774F9F18730, +// 0x0C1856Cb7444cc8596c706185b54535F45C2623B, +// 0x11fC7853944570C1F9D9EBE7Ac24e2FeFddf0314, +// 0xb3d1aCA9bF004d0da560930Cf9277c604eDf2283, +// 0xB438Dbaae78225eEfcDBB04E207A8eFB06036a2c, +// 0x997b96BAD648c39226281Bc002f9857274E42A01, +// 0xd52929B69680A6f74D2eB9c8F1ef482f37b1b32B, +// 0x706FC1a8e457De0cf52e7679C2922aEF7F7a397e, +// 0x3497818F50f79A11236B941dDAd35Da68A57864E, +// 0xB35722e595F2B974c2B1D14A80Fab5d0c60c2fa3, +// 0xA9f7b86FCC86EA1b913dDcaB8c9514a8e677666E, +// 0x7077e395C9FA4E480366A5DC5792d2504d78dffF, +// 0x664324F8B7430b4C22Dc036234Dd752A4755Fda7, +// 0xD22ADba687aAd7DD9CEC3b18D970C581734783bA, +// 0x5BC81274740A73D33ec8539182c326b8b58004C2, +// 0x02F73B8e29dD6Ec38Bc5D8B3826051DC562c3060, +// 0x4e90Da1dDCD5B0Fb295b6A69251b5D04D5bCCA7a, +// 0x150a0D516C0ceFa39f980Da57d5422E91A94238b, +// 0x4fb30f8CcE1F80FC9CC45F7F626069be7549aF59, +// 0xF382ce5457Bba6113e082DD638b6671Cb2277B1f, +// 0xb95f08B2eda7B23Ce412A6dE82eA5f5100335A3A, +// 0x5b6Da8BC8696Aa4D20030151345ec652cf1eC727, +// 0x0396816A361e2353a91Fde8438600a9353b34ce7, +// 0xA1ebf2043e76446eFA2724bD1Ec18321776096FF, +// 0x9214835f82752E0574F3828A4400C36Ba386Df82, +// 0xb0DebcB643eE79f19Ef659Bd01D0FAC12d058604, +// 0x3d72971b117EE8EF445D138557151f66CB0D408F, +// 0x09e63186b4F94FafcCC60a42D3f77b4A10995674, +// 0xc210a4D56D99E095b126664fDCE545b369ED3FD0, +// 0xEC23a0C9D4107cAD33271c4F1955a21f4c041208, +// 0x03adFaA573aC1a9b19D2b8F79a5aAFFb9c2A0532, +// 0x6a58EBB35664A171A1B070DE48ee93278A63c168, +// 0x521e6f07bDfa2fF8D072887F0ef3bB908a3D9e2a, +// 0xF5AC5943D16fC865824910033B756519DC396682, +// 0xBD73cF5baf12F120Ee3f6C4ad82df9a12649e578, +// 0x44119A62EA645242234cAF408c4c20513E660EBb +// ]; + +// function setUp() public override { +// super.setUp(); + +// DataTypes.AaveV3EmodeParams memory params = DataTypes.AaveV3EmodeParams({emodeCategory: 3}); + +// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); +// moduleExecutionData[0] = DataTypes.ModuleExecutionData({ +// executionType: DataTypes.CallType.DELEGATECALL, +// module: address(emodeModule), +// data: abi.encodeWithSelector(emodeModule.execute.selector, params) +// }); + +// DataTypes.AaveV3EmodeParams memory paramsBtc = DataTypes.AaveV3EmodeParams({emodeCategory: 2}); +// DataTypes.ModuleExecutionData[] memory moduleExecutionDataBtc = new DataTypes.ModuleExecutionData[](1); +// moduleExecutionDataBtc[0] = DataTypes.ModuleExecutionData({ +// executionType: DataTypes.CallType.DELEGATECALL, +// module: address(emodeModule), +// data: abi.encodeWithSelector(emodeModule.execute.selector, paramsBtc) +// }); + +// vm.startPrank(admin); +// superloop.operate(moduleExecutionData); +// superloopBtc.operate(moduleExecutionDataBtc); + +// // register the above modules +// moduleRegistry.setModule("REPAY_MODULE", REPAY_MODULE); +// moduleRegistry.setModule("WITHDRAW_MODULE", WITHDRAW_MODULE); +// moduleRegistry.setModule("DEPOSIT_MODULE", DEPOSIT_MODULE); +// moduleRegistry.setModule("BORROW_MODULE", BORROW_MODULE); +// moduleRegistry.setModule("DEX_MODULE", DEX_MODULE); + +// superloop.setRegisteredModule(REPAY_MODULE, true); +// superloop.setRegisteredModule(WITHDRAW_MODULE, true); +// superloop.setRegisteredModule(DEPOSIT_MODULE, true); +// superloop.setRegisteredModule(BORROW_MODULE, true); +// superloop.setRegisteredModule(DEX_MODULE, true); + +// superloopBtc.setRegisteredModule(REPAY_MODULE, true); +// superloopBtc.setRegisteredModule(WITHDRAW_MODULE, true); +// superloopBtc.setRegisteredModule(DEPOSIT_MODULE, true); +// superloopBtc.setRegisteredModule(BORROW_MODULE, true); +// superloopBtc.setRegisteredModule(DEX_MODULE, true); +// vm.stopPrank(); + +// vm.label(oldVault, "oldVault"); +// vm.label(oldVaultBtc, "oldVaultBtc"); +// vm.label(address(superloop), "newVault"); +// vm.label(address(superloopBtc), "newVaultBtc"); +// vm.label(REPAY_MODULE, "REPAY_MODULE_OLD"); +// vm.label(WITHDRAW_MODULE, "WITHDRAW_MODULE_OLD"); +// vm.label(DEPOSIT_MODULE, "DEPOSIT_MODULE_OLD"); +// vm.label(BORROW_MODULE, "BORROW_MODULE_OLD"); +// vm.label(DEX_MODULE, "DEX_MODULE_OLD"); + +// // TODO: do deposit and withdraw for both +// } + +// // function test_migration() public { +// // // deploy migration helper +// // MigrationHelper migrationHelper = new MigrationHelper( +// // AAVE_V3_POOL_ADDRESSES_PROVIDER, +// // REPAY_MODULE, +// // WITHDRAW_MODULE, +// // DEPOSIT_MODULE, +// // BORROW_MODULE, +// // DEX_MODULE, +// // USDC +// // ); +// // vm.label(address(migrationHelper), "migrationHelper"); + +// // // set migration helper contract as priveledged address in old vault +// // address oldVaultAdmin = ISuperloop(oldVault).vaultAdmin(); +// // vm.prank(oldVaultAdmin); +// // ISuperloop(oldVault).setPrivilegedAddress(address(migrationHelper), true); + +// // // set migration helper contract as deposit manager and vault operator in new vault +// // vm.startPrank(admin); +// // superloop.setDepositManagerModule(address(migrationHelper)); +// // superloop.setVaultOperator(address(migrationHelper)); + +// // // set migration helper as vault in new accountant module +// // accountant.setVault(address(migrationHelper)); +// // accountant.transferOwnership(address(migrationHelper)); +// // vm.stopPrank(); + +// // address[] memory users = ALL_HOLDERS_XTZ; + +// // // old balances +// // uint256 oldVaultXTZBalance = IERC20(XTZ).balanceOf(oldVault); +// // uint256 oldVaultSTXTZBalance = IERC20(ST_XTZ).balanceOf(oldVault); +// // (uint256 oldVaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(ST_XTZ, oldVault); +// // (,, uint256 oldVaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(XTZ, oldVault); + +// // console.log("OLD VAULT STATE INITIAL"); +// // console.log("oldVaultXTZBalance", oldVaultXTZBalance); +// // console.log("oldVaultSTXTZBalance", oldVaultSTXTZBalance); +// // console.log("oldVaultLendBalance", oldVaultLendBalance); +// // console.log("oldVaultBorrowBalance", oldVaultBorrowBalance); +// // console.log("old vault total supply", ISuperloop(oldVault).totalSupply()); +// // console.log("old vault total assets", ISuperloop(oldVault).totalAssets()); + +// // assertEq(Ownable(address(accountant)).owner(), address(migrationHelper)); +// // assertEq(accountant.vault(), address(migrationHelper)); + +// // uint256 maxSharesDelta = 100; + +// // // start recording gas +// // uint256 startGas = gasleft(); +// // uint256 batches = 4; +// // migrationHelper.migrate(oldVault, address(superloop), users, ST_XTZ, XTZ, batches, maxSharesDelta, 5); + +// // console.log("migration complete"); + +// // uint256 endGas = gasleft(); +// // uint256 gasUsed = startGas - endGas; +// // console.log("gas used", gasUsed); +// // uint256 newVaultXTZBalance = IERC20(XTZ).balanceOf(address(superloop)); +// // uint256 newVaultSTXTZBalance = IERC20(ST_XTZ).balanceOf(address(superloop)); +// // (uint256 newVaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(ST_XTZ, address(superloop)); +// // (,, uint256 newVaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(XTZ, address(superloop)); +// // uint256 newVaultLastRealizedFeeExchangeRate = +// // IAccountantModule(ISuperloop(address(superloop)).accountant()).lastRealizedFeeExchangeRate(); +// // uint256 oldVaultLastRealizedFeeExchangeRate = +// // IAccountantModule(ISuperloopLegacy(oldVault).accountantModule()).lastRealizedFeeExchangeRate(); + +// // console.log("NEW VAULT STATE AFTER MIGRATION"); +// // console.log("newVaultXTZBalance", newVaultXTZBalance); +// // console.log("newVaultSTXTZBalance", newVaultSTXTZBalance); +// // console.log("newVaultLendBalance", newVaultLendBalance); +// // console.log("newVaultBorrowBalance", newVaultBorrowBalance); +// // console.log("new vault total supply", ISuperloop(address(superloop)).totalSupply()); +// // console.log("new vault total assets", ISuperloop(address(superloop)).totalAssets()); +// // console.log( +// // "new vault last realized fee exchange rate", +// // IAccountantModule(ISuperloop(address(superloop)).accountant()).lastRealizedFeeExchangeRate() +// // ); + +// // // assert balances of each of the users are the same + +// // for (uint256 i = 0; i < users.length; i++) { +// // assertEq(ISuperloop(address(superloop)).balanceOf(users[i]), ISuperloop(oldVault).balanceOf(users[i])); +// // } + +// // assertEq(newVaultLastRealizedFeeExchangeRate, oldVaultLastRealizedFeeExchangeRate); +// // assertEq(Ownable(address(accountant)).owner(), address(admin)); +// // assertEq(accountant.vault(), address(superloop)); + +// // oldVaultXTZBalance = IERC20(XTZ).balanceOf(oldVault); +// // oldVaultSTXTZBalance = IERC20(ST_XTZ).balanceOf(oldVault); +// // (oldVaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(ST_XTZ, oldVault); +// // (,, oldVaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(XTZ, oldVault); + +// // console.log("OLD VAULT STATE AFTER MIGRATION"); +// // console.log("oldVaultXTZBalance", oldVaultXTZBalance); +// // console.log("oldVaultSTXTZBalance", oldVaultSTXTZBalance); +// // console.log("oldVaultLendBalance", oldVaultLendBalance); +// // console.log("oldVaultBorrowBalance", oldVaultBorrowBalance); +// // console.log("old vault total supply", ISuperloop(oldVault).totalSupply()); +// // console.log("old vault total assets", ISuperloop(oldVault).totalAssets()); +// // console.log( +// // "old vault last realized fee exchange rate", +// // IAccountantModule(ISuperloopLegacy(oldVault).accountantModule()).lastRealizedFeeExchangeRate() +// // ); +// // } + +// function test_migrationBtc() public { +// // deploy migration helper +// MigrationHelper migrationHelper = new MigrationHelper( +// AAVE_V3_POOL_ADDRESSES_PROVIDER, +// REPAY_MODULE, +// WITHDRAW_MODULE, +// DEPOSIT_MODULE, +// BORROW_MODULE, +// DEX_MODULE, +// USDC +// ); +// vm.label(address(migrationHelper), "migrationHelper"); + +// // set migration helper contract as priveledged address in old vault +// address oldVaultAdmin = ISuperloop(oldVaultBtc).vaultAdmin(); +// vm.prank(oldVaultAdmin); +// ISuperloop(oldVaultBtc).setPrivilegedAddress(address(migrationHelper), true); + +// // set migration helper contract as deposit manager and vault operator in new vault +// vm.startPrank(admin); +// superloopBtc.setDepositManagerModule(address(migrationHelper)); +// superloopBtc.setVaultOperator(address(migrationHelper)); + +// // set migration helper as vault in new accountant module +// accountantBtc.setVault(address(migrationHelper)); +// accountantBtc.transferOwnership(address(migrationHelper)); +// vm.stopPrank(); + +// address[] memory users = ALL_HOLDERS_BTC; + +// // old balances +// uint256 oldVaultBTCBalance = IERC20(WBTC).balanceOf(oldVaultBtc); +// uint256 oldVaultLBTCBalance = IERC20(LBTC).balanceOf(oldVaultBtc); +// (uint256 oldVaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(LBTC, oldVaultBtc); +// (,, uint256 oldVaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(WBTC, oldVaultBtc); + +// console.log("OLD VAULT STATE INITIAL"); +// console.log("oldVaultBTCBalance", oldVaultBTCBalance); +// console.log("oldVaultLBTCBalance", oldVaultLBTCBalance); +// console.log("oldVaultLendBalance", oldVaultLendBalance); +// console.log("oldVaultBorrowBalance", oldVaultBorrowBalance); +// console.log("old vault total supply", ISuperloop(oldVaultBtc).totalSupply()); +// console.log("old vault total assets", ISuperloop(oldVaultBtc).totalAssets()); + +// assertEq(Ownable(address(accountantBtc)).owner(), address(migrationHelper)); +// assertEq(accountantBtc.vault(), address(migrationHelper)); + +// uint256 maxSharesDelta = 100; + +// // start recording gas +// uint256 startGas = gasleft(); +// uint256 batches = 4; +// migrationHelper.migrate(oldVaultBtc, address(superloopBtc), users, LBTC, WBTC, batches, maxSharesDelta, 5); + +// console.log("migration complete"); + +// uint256 endGas = gasleft(); +// uint256 gasUsed = startGas - endGas; +// console.log("gas used", gasUsed); +// uint256 newVaultBTCBalance = IERC20(WBTC).balanceOf(address(superloopBtc)); +// uint256 newVaultLBTCBalance = IERC20(LBTC).balanceOf(address(superloopBtc)); +// (uint256 newVaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(LBTC, address(superloopBtc)); +// (,, uint256 newVaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(WBTC, address(superloopBtc)); +// uint256 newVaultLastRealizedFeeExchangeRate = +// IAccountantModule(ISuperloop(address(superloopBtc)).accountant()).lastRealizedFeeExchangeRate(); +// uint256 oldVaultLastRealizedFeeExchangeRate = +// IAccountantModule(ISuperloopLegacy(oldVaultBtc).accountantModule()).lastRealizedFeeExchangeRate(); + +// console.log("NEW VAULT STATE AFTER MIGRATION"); +// console.log("newVaultXTZBalance", newVaultBTCBalance); +// console.log("newVaultLBTCBalance", newVaultLBTCBalance); +// console.log("newVaultLendBalance", newVaultLendBalance); +// console.log("newVaultBorrowBalance", newVaultBorrowBalance); +// console.log("new vault total supply", ISuperloop(address(superloopBtc)).totalSupply()); +// console.log("new vault total assets", ISuperloop(address(superloopBtc)).totalAssets()); +// console.log( +// "new vault last realized fee exchange rate", +// IAccountantModule(ISuperloop(address(superloop)).accountant()).lastRealizedFeeExchangeRate() +// ); + +// // assert balances of each of the users are the same + +// for (uint256 i = 0; i < users.length; i++) { +// assertEq(ISuperloop(address(superloopBtc)).balanceOf(users[i]), ISuperloop(oldVaultBtc).balanceOf(users[i])); +// } + +// assertEq(newVaultLastRealizedFeeExchangeRate, oldVaultLastRealizedFeeExchangeRate); +// assertEq(Ownable(address(accountantBtc)).owner(), address(admin)); +// assertEq(accountantBtc.vault(), address(superloopBtc)); + +// console.log("gas used", gasUsed); + +// oldVaultBTCBalance = IERC20(WBTC).balanceOf(oldVaultBtc); +// oldVaultLBTCBalance = IERC20(LBTC).balanceOf(oldVaultBtc); +// (oldVaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(LBTC, oldVaultBtc); +// (,, oldVaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(WBTC, oldVaultBtc); + +// console.log("OLD VAULT STATE AFTER MIGRATION"); +// console.log("oldVaultBTCBalance", oldVaultBTCBalance); +// console.log("oldVaultLBTCBalance", oldVaultLBTCBalance); +// console.log("oldVaultLendBalance", oldVaultLendBalance); +// console.log("oldVaultBorrowBalance", oldVaultBorrowBalance); +// console.log("old vault total supply", ISuperloop(oldVaultBtc).totalSupply()); +// console.log("old vault total assets", ISuperloop(oldVaultBtc).totalAssets()); +// console.log( +// "old vault last realized fee exchange rate", +// IAccountantModule(ISuperloopLegacy(oldVaultBtc).accountantModule()).lastRealizedFeeExchangeRate() +// ); +// } +// } diff --git a/test/core/integration/Seed.t.sol b/test/core/integration/Seed.t.sol index e56bf44..30f9ff9 100644 --- a/test/core/integration/Seed.t.sol +++ b/test/core/integration/Seed.t.sol @@ -1,63 +1,63 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.13; - -import {IntegrationBase} from "./IntegrationBase.sol"; -import {DataTypes} from "../../../src/common/DataTypes.sol"; -import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -import {DepositManager} from "../../../src/core/DepositManager/DepositManager.sol"; -import {console} from "forge-std/Test.sol"; -import {Errors} from "../../../src/common/Errors.sol"; - -contract SeedTest is IntegrationBase { - function setUp() public override { - super.setUp(); - } - - function test_seed() public { - uint256 seedAmount = 10 * XTZ_SCALE; - deal(XTZ, admin, seedAmount); - - vm.startPrank(admin); - IERC20(XTZ).approve(address(superloop), seedAmount); - superloop.seed(seedAmount); - vm.stopPrank(); - - uint256 totalSupply = superloop.totalSupply(); - - assertApproxEqAbs(totalSupply, 10 * (XTZ_SCALE) * 100, 100); - assertEq(superloop.totalAssets(), seedAmount); - assertEq(superloop.balanceOf(admin), totalSupply); - } - - function test_seed_failing_cases() public { - uint256 seedAmount = 10 * XTZ_SCALE; - deal(XTZ, user1, seedAmount); - deal(XTZ, admin, seedAmount); - - // should rever if not called by admin - vm.startPrank(user1); - IERC20(XTZ).approve(address(superloop), seedAmount); - vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); - superloop.seed(seedAmount); - vm.stopPrank(); - - // should not seed with 0 assets - vm.startPrank(admin); - vm.expectRevert(bytes(Errors.INVALID_AMOUNT)); - superloop.seed(0); - vm.stopPrank(); - - // should seed - vm.startPrank(admin); - IERC20(XTZ).approve(address(superloop), seedAmount); - superloop.seed(seedAmount); - vm.stopPrank(); - - // should revert if already seeded - vm.startPrank(admin); - vm.expectRevert(bytes(Errors.VAULT_ALREADY_SEEDED)); - superloop.seed(seedAmount); - vm.stopPrank(); - } -} +// // SPDX-License-Identifier: MIT + +// pragma solidity ^0.8.13; + +// import {IntegrationBase} from "./IntegrationBase.sol"; +// import {DataTypes} from "../../../src/common/DataTypes.sol"; +// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +// import {DepositManager} from "../../../src/core/DepositManager/DepositManager.sol"; +// import {console} from "forge-std/Test.sol"; +// import {Errors} from "../../../src/common/Errors.sol"; + +// contract SeedTest is IntegrationBase { +// function setUp() public override { +// super.setUp(); +// } + +// function test_seed() public { +// uint256 seedAmount = 10 * XTZ_SCALE; +// deal(XTZ, admin, seedAmount); + +// vm.startPrank(admin); +// IERC20(XTZ).approve(address(superloop), seedAmount); +// superloop.seed(seedAmount); +// vm.stopPrank(); + +// uint256 totalSupply = superloop.totalSupply(); + +// assertApproxEqAbs(totalSupply, 10 * (XTZ_SCALE) * 100, 100); +// assertEq(superloop.totalAssets(), seedAmount); +// assertEq(superloop.balanceOf(admin), totalSupply); +// } + +// function test_seed_failing_cases() public { +// uint256 seedAmount = 10 * XTZ_SCALE; +// deal(XTZ, user1, seedAmount); +// deal(XTZ, admin, seedAmount); + +// // should rever if not called by admin +// vm.startPrank(user1); +// IERC20(XTZ).approve(address(superloop), seedAmount); +// vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); +// superloop.seed(seedAmount); +// vm.stopPrank(); + +// // should not seed with 0 assets +// vm.startPrank(admin); +// vm.expectRevert(bytes(Errors.INVALID_AMOUNT)); +// superloop.seed(0); +// vm.stopPrank(); + +// // should seed +// vm.startPrank(admin); +// IERC20(XTZ).approve(address(superloop), seedAmount); +// superloop.seed(seedAmount); +// vm.stopPrank(); + +// // should revert if already seeded +// vm.startPrank(admin); +// vm.expectRevert(bytes(Errors.VAULT_ALREADY_SEEDED)); +// superloop.seed(seedAmount); +// vm.stopPrank(); +// } +// } diff --git a/test/core/integration/WithdrawManager.t.sol b/test/core/integration/WithdrawManager.t.sol index 4303ce2..6b28c84 100644 --- a/test/core/integration/WithdrawManager.t.sol +++ b/test/core/integration/WithdrawManager.t.sol @@ -1,372 +1,372 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.13; - -import {IntegrationBase} from "./IntegrationBase.sol"; -import {DataTypes} from "../../../src/common/DataTypes.sol"; -import {console} from "forge-std/console.sol"; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {Errors} from "../../../src/common/Errors.sol"; - -contract WithdrawManagerTest is IntegrationBase { - bool depositAll = true; - DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.INSTANT; - - function setUp() public override { - super.setUp(); - - DataTypes.AaveV3EmodeParams memory params = DataTypes.AaveV3EmodeParams({emodeCategory: 3}); - - DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); - moduleExecutionData[0] = DataTypes.ModuleExecutionData({ - executionType: DataTypes.CallType.DELEGATECALL, - module: address(emodeModule), - data: abi.encodeWithSelector(emodeModule.execute.selector, params) - }); - - vm.prank(admin); - superloop.operate(moduleExecutionData); - } - - function test_initialize() public view { - assertEq(withdrawManager.vault(), address(superloop)); - assertEq(withdrawManager.asset(), XTZ); - assertEq(withdrawManager.nextWithdrawRequestId(requestType), 1); - assertEq(withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.INSTANT), 1); - assertEq(withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.PRIORITY), 1); - assertEq(withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.DEFERRED), 1); - } - - function test_resolveWithdrawRequests() public { - _createPartialDepositWithResolution(depositAll); - - // make 3 withdraw requests - uint256 user1ShareBalance = superloop.balanceOf(user1); - uint256 user2ShareBalance = superloop.balanceOf(user2); - uint256 user3ShareBalance = superloop.balanceOf(user3); - _makeWithdrawRequest(address(superloop), user1ShareBalance, user1, requestType); - _makeWithdrawRequest(address(superloop), user2ShareBalance, user2, requestType); - _makeWithdrawRequest(address(superloop), user3ShareBalance, user3, requestType); - - _resolveAllRequests(user1ShareBalance + user2ShareBalance + user3ShareBalance, requestType); - } - - function test_resolveWithdrawRequestsWithPartials() public { - // make 3 withdraw requests - // resolve 1st fully, and 2nd partially - // assert claimable and sharesProcessed for 1st and 2nd requests - (uint256 sharesLeftToResolve,,) = _createPartialWithdrawWithResolution(requestType); - - (,, uint256 repayAmount,,,,,,) = poolDataProvider.getUserReserveData(XTZ, address(superloop)); - (uint256 withdrawAmount,,,,,,,,) = poolDataProvider.getUserReserveData(ST_XTZ, address(superloop)); - - // resolve 2nd fuly and 3rd partially. I will unwind the entire amount but claim only partial amount - uint256 repayAmountWithPremium = repayAmount + (repayAmount * 1) / 10000; - uint256 sharesToResolve = sharesLeftToResolve / 2; - - DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](3); - moduleExecutionData[0] = _repayCall(XTZ, repayAmount); - moduleExecutionData[1] = _withdrawCall(ST_XTZ, withdrawAmount); - moduleExecutionData[2] = - _swapCallExactOutCurve(ST_XTZ, XTZ, XTZ_STXTZ_POOL, withdrawAmount, repayAmountWithPremium, STXTZ_XTZ_SWAP); - - DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); - intermediateExecutionData[0] = _flashloanCall(XTZ, repayAmount, abi.encode(moduleExecutionData)); - - DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); - finalExecutionData[0] = - _resolveWithdrawRequestsCall(sharesToResolve, requestType, abi.encode(intermediateExecutionData)); - - uint256 exchangeRateBefore = superloop.convertToAssets(ONE_SHARE); - - vm.prank(admin); - superloop.operate(finalExecutionData); - - DataTypes.WithdrawRequestData memory withdrawRequest2 = withdrawManager.withdrawRequest(2, requestType); - DataTypes.WithdrawRequestData memory withdrawRequest3 = withdrawManager.withdrawRequest(3, requestType); - uint256 resolutionIdPointer = withdrawManager.resolutionIdPointer(requestType); - uint256 totalPendingWithdraws = withdrawManager.totalPendingWithdraws(requestType); - - assertTrue(withdrawRequest2.amountClaimable > 0); - assertTrue(withdrawRequest2.sharesProcessed == withdrawRequest2.shares); - assertTrue(withdrawRequest3.amountClaimable > 0); - assertTrue(withdrawRequest3.sharesProcessed > 0 && withdrawRequest3.sharesProcessed < withdrawRequest3.shares); - - assertTrue(withdrawRequest2.state == DataTypes.RequestProcessingState.FULLY_PROCESSED); - assertTrue(withdrawRequest3.state == DataTypes.RequestProcessingState.PARTIALLY_PROCESSED); - - uint256 exchangeRateAfter = superloop.convertToAssets(ONE_SHARE); - assertApproxEqAbs(exchangeRateAfter, exchangeRateBefore, 1000); - assertEq(totalPendingWithdraws, sharesLeftToResolve - sharesToResolve); - assertEq(resolutionIdPointer, 3); - } - - function test_resolveWithdrawRequestsWithCancellation() public { - (uint256 sharesLeftToResolve,,) = _createPartialWithdrawWithResolution(requestType); - - // shoud not be able to cancel withdraw request if it's already processed - vm.startPrank(user1); - vm.expectRevert(bytes(Errors.INVALID_WITHDRAW_REQUEST_STATE)); - withdrawManager.cancelWithdrawRequest(1, requestType); - vm.stopPrank(); - - // req 2 is partially processed, hence it should be partially cancelled and user 2 should get back the remaining shares + claimable amount - uint256 user2BalanceBefore = IERC20(XTZ).balanceOf(user2); - uint256 user2ShareBalanceBefore = superloop.balanceOf(user2); - vm.startPrank(user2); - withdrawManager.cancelWithdrawRequest(2, requestType); - vm.stopPrank(); - uint256 user2BalanceAfter = IERC20(XTZ).balanceOf(user2); - uint256 user2ShareBalanceAfter = superloop.balanceOf(user2); - - DataTypes.WithdrawRequestData memory withdrawRequest2 = withdrawManager.withdrawRequest(2, requestType); - assertApproxEqRel( - uint256(withdrawRequest2.state), uint256(DataTypes.RequestProcessingState.PARTIALLY_CANCELLED), 1e18 - ); - assertApproxEqRel( - user2ShareBalanceAfter - user2ShareBalanceBefore, - withdrawRequest2.shares - withdrawRequest2.sharesProcessed, - 1e18 - ); - assertApproxEqRel(withdrawRequest2.amountClaimable, 0, 1e18); - assertApproxEqRel(withdrawRequest2.amountClaimed, user2BalanceAfter - user2BalanceBefore, 1e18); - uint256 totalPendingWithdraws = withdrawManager.totalPendingWithdraws(requestType); - assertApproxEqRel(totalPendingWithdraws, sharesLeftToResolve - withdrawRequest2.sharesProcessed, 1e18); - - (,, uint256 repayAmount,,,,,,) = poolDataProvider.getUserReserveData(XTZ, address(superloop)); - (uint256 withdrawAmount,,,,,,,,) = poolDataProvider.getUserReserveData(ST_XTZ, address(superloop)); - - // unwind the remaining position - uint256 repayAmountWithPremium = repayAmount + (repayAmount * 1) / 10000; - uint256 sharesToResolve = totalPendingWithdraws; - - DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](3); - moduleExecutionData[0] = _repayCall(XTZ, repayAmount); - moduleExecutionData[1] = _withdrawCall(ST_XTZ, withdrawAmount); - moduleExecutionData[2] = - _swapCallExactOutCurve(ST_XTZ, XTZ, XTZ_STXTZ_POOL, withdrawAmount, repayAmountWithPremium, STXTZ_XTZ_SWAP); - - DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); - intermediateExecutionData[0] = _flashloanCall(XTZ, repayAmount, abi.encode(moduleExecutionData)); - - DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); - finalExecutionData[0] = - _resolveWithdrawRequestsCall(sharesToResolve, requestType, abi.encode(intermediateExecutionData)); - - uint256 exchangeRateBefore = superloop.convertToAssets(ONE_SHARE); - - vm.prank(admin); - superloop.operate(finalExecutionData); - - uint256 exchangeRateAfter = superloop.convertToAssets(ONE_SHARE); - assertApproxEqAbs(exchangeRateAfter, exchangeRateBefore, 1000); - - // pending withdraws should be only worth 3rd request - // 3rd request shoudl be resolved based on amount, ie. 2nd should get skipped - uint256 resolutionIdPointer = withdrawManager.resolutionIdPointer(requestType); - DataTypes.WithdrawRequestData memory withdrawRequest3 = withdrawManager.withdrawRequest(3, requestType); - assertApproxEqRel( - uint256(withdrawRequest3.state), uint256(DataTypes.RequestProcessingState.FULLY_PROCESSED), 1e18 - ); - assertApproxEqRel(withdrawRequest3.sharesProcessed, withdrawRequest3.shares, 1e18); - assertTrue(withdrawRequest3.amountClaimable > 0); - totalPendingWithdraws = withdrawManager.totalPendingWithdraws(requestType); - assertApproxEqRel(totalPendingWithdraws, 0, 1e18); - assertApproxEqRel(resolutionIdPointer, 4, 1e18); - - // new withdraw request should be able to be made, ie not blocked because of cancellation - // user 2 shoudl be able to make a new withdraw request - vm.startPrank(user2); - superloop.approve(address(withdrawManager), 10 * ONE_SHARE); - withdrawManager.requestWithdraw(10 * ONE_SHARE, requestType); - vm.stopPrank(); - - vm.startPrank(user3); - vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_UNCLAIMED)); - withdrawManager.requestWithdraw(10 * ONE_SHARE, requestType); - vm.stopPrank(); - - vm.startPrank(user1); - vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_UNCLAIMED)); - withdrawManager.requestWithdraw(10 * ONE_SHARE, requestType); - vm.stopPrank(); - } - - function test_withdraw() public { - _createPartialWithdrawWithResolution(requestType); - - // should be able to withdraw if fully processed - uint256 user1BalanceBefore = IERC20(XTZ).balanceOf(user1); - uint256 user1ShareBalanceBefore = superloop.balanceOf(user1); - vm.startPrank(user1); - withdrawManager.withdraw(requestType); - vm.stopPrank(); - uint256 user1BalanceAfter = IERC20(XTZ).balanceOf(user1); - uint256 user1ShareBalanceAfter = superloop.balanceOf(user1); - DataTypes.WithdrawRequestData memory withdrawRequest1 = withdrawManager.withdrawRequest(1, requestType); - assertEq(uint256(withdrawRequest1.state), uint256(DataTypes.RequestProcessingState.FULLY_PROCESSED)); - assertEq(withdrawRequest1.sharesProcessed, withdrawRequest1.shares); - assertTrue(withdrawRequest1.amountClaimable == 0); - assertEq(withdrawRequest1.amountClaimed, user1BalanceAfter - user1BalanceBefore); - assertEq(user1ShareBalanceAfter - user1ShareBalanceBefore, 0); - - // should not be able to claim if already claimed - vm.startPrank(user1); - vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_ALREADY_CLAIMED)); - withdrawManager.withdraw(requestType); - vm.stopPrank(); - - // should be able to withdraw partially processed - uint256 user2BalanceBefore = IERC20(XTZ).balanceOf(user2); - uint256 user2ShareBalanceBefore = superloop.balanceOf(user2); - vm.startPrank(user2); - withdrawManager.withdraw(requestType); - vm.stopPrank(); - uint256 user2BalanceAfter = IERC20(XTZ).balanceOf(user2); - uint256 user2ShareBalanceAfter = superloop.balanceOf(user2); - DataTypes.WithdrawRequestData memory withdrawRequest2 = withdrawManager.withdrawRequest(2, requestType); - assertEq(uint256(withdrawRequest2.state), uint256(DataTypes.RequestProcessingState.PARTIALLY_PROCESSED)); - assertTrue(withdrawRequest2.amountClaimable == 0); - assertEq(withdrawRequest2.amountClaimed, user2BalanceAfter - user2BalanceBefore); - assertEq(user2ShareBalanceAfter - user2ShareBalanceBefore, 0); - - // should not be able to claim if nothing left to claim - vm.startPrank(user2); - vm.expectRevert(bytes(Errors.CANNOT_CLAIM_ZERO_AMOUNT)); - withdrawManager.withdraw(requestType); - vm.stopPrank(); - - // should not be able to claim if cancelled - user2BalanceBefore = IERC20(XTZ).balanceOf(user2); - user2ShareBalanceBefore = superloop.balanceOf(user2); - vm.startPrank(user2); - withdrawManager.cancelWithdrawRequest(2, requestType); - withdrawRequest2 = withdrawManager.withdrawRequest(2, requestType); - assertEq(uint256(withdrawRequest2.state), uint256(DataTypes.RequestProcessingState.PARTIALLY_CANCELLED)); - assertEq(withdrawRequest2.amountClaimable, 0); - vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_NOT_FOUND)); - withdrawManager.withdraw(requestType); - vm.stopPrank(); - user2BalanceAfter = IERC20(XTZ).balanceOf(user2); - user2ShareBalanceAfter = superloop.balanceOf(user2); - assertEq(user2BalanceAfter - user2BalanceBefore, 0); - assertEq( - user2ShareBalanceAfter - user2ShareBalanceBefore, withdrawRequest2.shares - withdrawRequest2.sharesProcessed - ); - - // should not be able to claim if unprocessed - vm.startPrank(user3); - vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_ACTIVE)); - withdrawManager.withdraw(requestType); - vm.stopPrank(); - } - - function test_isolationOfQueues() public { - if (requestType != DataTypes.WithdrawRequestType.GENERAL) return; - - _createPartialDepositWithResolution(true); - - // make a withdraw request by every user on every queue - _makeWithdrawRequest(address(superloop), ONE_SHARE, user1, DataTypes.WithdrawRequestType.GENERAL); - _makeWithdrawRequest(address(superloop), ONE_SHARE, user2, DataTypes.WithdrawRequestType.GENERAL); - _makeWithdrawRequest(address(superloop), ONE_SHARE, user3, DataTypes.WithdrawRequestType.GENERAL); - - _makeWithdrawRequest(address(superloop), ONE_SHARE, user1, DataTypes.WithdrawRequestType.DEFERRED); - _makeWithdrawRequest(address(superloop), ONE_SHARE, user2, DataTypes.WithdrawRequestType.DEFERRED); - _makeWithdrawRequest(address(superloop), ONE_SHARE, user3, DataTypes.WithdrawRequestType.DEFERRED); - - _makeWithdrawRequest(address(superloop), ONE_SHARE, user1, DataTypes.WithdrawRequestType.PRIORITY); - _makeWithdrawRequest(address(superloop), ONE_SHARE, user2, DataTypes.WithdrawRequestType.PRIORITY); - _makeWithdrawRequest(address(superloop), ONE_SHARE, user3, DataTypes.WithdrawRequestType.PRIORITY); - - _makeWithdrawRequest(address(superloop), ONE_SHARE, user1, DataTypes.WithdrawRequestType.INSTANT); - _makeWithdrawRequest(address(superloop), ONE_SHARE, user2, DataTypes.WithdrawRequestType.INSTANT); - _makeWithdrawRequest(address(superloop), ONE_SHARE, user3, DataTypes.WithdrawRequestType.INSTANT); - - uint256[] memory ids = new uint256[](3); - ids[0] = 1; - ids[1] = 2; - ids[2] = 3; - - address[] memory users = new address[](3); - users[0] = user1; - users[1] = user2; - users[2] = user3; - - DataTypes.WithdrawRequestData[] memory withdrawRequestsGeneral = - withdrawManager.withdrawRequests(ids, DataTypes.WithdrawRequestType.GENERAL); - DataTypes.WithdrawRequestData[] memory withdrawRequestsDeferred = - withdrawManager.withdrawRequests(ids, DataTypes.WithdrawRequestType.DEFERRED); - DataTypes.WithdrawRequestData[] memory withdrawRequestsPriority = - withdrawManager.withdrawRequests(ids, DataTypes.WithdrawRequestType.PRIORITY); - DataTypes.WithdrawRequestData[] memory withdrawRequestsInstant = - withdrawManager.withdrawRequests(ids, DataTypes.WithdrawRequestType.INSTANT); - - assertEq(withdrawRequestsGeneral.length, 3); - assertEq(withdrawRequestsDeferred.length, 3); - assertEq(withdrawRequestsPriority.length, 3); - assertEq(withdrawRequestsInstant.length, 3); - - for (uint256 i = 0; i < 3; i++) { - assertEq(withdrawRequestsGeneral[i].user, users[i]); - assertEq(withdrawRequestsDeferred[i].user, users[i]); - assertEq(withdrawRequestsPriority[i].user, users[i]); - - assertEq(withdrawRequestsInstant[i].shares, ONE_SHARE); - assertEq(withdrawRequestsDeferred[i].shares, ONE_SHARE); - assertEq(withdrawRequestsPriority[i].shares, ONE_SHARE); - assertEq(withdrawRequestsGeneral[i].shares, ONE_SHARE); - } - - uint256 totalPendingWithdraws = withdrawManager.totalPendingWithdraws(DataTypes.WithdrawRequestType.GENERAL); - assertEq(totalPendingWithdraws, 3 * ONE_SHARE); - totalPendingWithdraws = withdrawManager.totalPendingWithdraws(DataTypes.WithdrawRequestType.DEFERRED); - assertEq(totalPendingWithdraws, 3 * ONE_SHARE); - totalPendingWithdraws = withdrawManager.totalPendingWithdraws(DataTypes.WithdrawRequestType.PRIORITY); - assertEq(totalPendingWithdraws, 3 * ONE_SHARE); - totalPendingWithdraws = withdrawManager.totalPendingWithdraws(DataTypes.WithdrawRequestType.INSTANT); - assertEq(totalPendingWithdraws, 3 * ONE_SHARE); - } - - function _resolveAllRequests(uint256 sharesToResolve, DataTypes.WithdrawRequestType _requestType) internal { - (,, uint256 repayAmount,,,,,,) = poolDataProvider.getUserReserveData(XTZ, address(superloop)); - (uint256 withdrawAmount,,,,,,,,) = poolDataProvider.getUserReserveData(ST_XTZ, address(superloop)); - uint256 repayAmountWithPremium = repayAmount + (repayAmount * 1) / 10000; - - DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](3); - moduleExecutionData[0] = _repayCall(XTZ, repayAmount); - moduleExecutionData[1] = _withdrawCall(ST_XTZ, withdrawAmount); - moduleExecutionData[2] = - _swapCallExactOutCurve(ST_XTZ, XTZ, XTZ_STXTZ_POOL, withdrawAmount, repayAmountWithPremium, STXTZ_XTZ_SWAP); - - DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); - intermediateExecutionData[0] = _flashloanCall(XTZ, repayAmount, abi.encode(moduleExecutionData)); - - DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); - finalExecutionData[0] = - _resolveWithdrawRequestsCall(sharesToResolve, _requestType, abi.encode(intermediateExecutionData)); - - uint256 exchangeRateBefore = superloop.convertToAssets(ONE_SHARE); - - vm.prank(admin); - superloop.operate(finalExecutionData); - - // withdraw requests should have claimable > 0 and sharesProcessed > 0, exchange rate remains the same - DataTypes.WithdrawRequestData memory withdrawRequest1 = withdrawManager.withdrawRequest(1, _requestType); - DataTypes.WithdrawRequestData memory withdrawRequest2 = withdrawManager.withdrawRequest(2, _requestType); - DataTypes.WithdrawRequestData memory withdrawRequest3 = withdrawManager.withdrawRequest(3, _requestType); - uint256 resolutionIdPointer = withdrawManager.resolutionIdPointer(_requestType); - uint256 totalPendingWithdraws = withdrawManager.totalPendingWithdraws(_requestType); - - assertTrue(withdrawRequest1.amountClaimable > 0); - assertTrue(withdrawRequest1.sharesProcessed == withdrawRequest1.shares); - assertTrue(withdrawRequest2.amountClaimable > 0); - assertTrue(withdrawRequest2.sharesProcessed == withdrawRequest2.shares); - assertTrue(withdrawRequest3.amountClaimable > 0); - assertTrue(withdrawRequest3.sharesProcessed == withdrawRequest3.shares); - uint256 exchangeRateAfter = superloop.convertToAssets(ONE_SHARE); - assertApproxEqAbs(exchangeRateAfter, exchangeRateBefore, 1000); - assertEq(totalPendingWithdraws, 0); - assertEq(resolutionIdPointer, 4); - } -} +// // SPDX-License-Identifier: MIT + +// pragma solidity ^0.8.13; + +// import {IntegrationBase} from "./IntegrationBase.sol"; +// import {DataTypes} from "../../../src/common/DataTypes.sol"; +// import {console} from "forge-std/console.sol"; +// import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +// import {Errors} from "../../../src/common/Errors.sol"; + +// contract WithdrawManagerTest is IntegrationBase { +// bool depositAll = true; +// DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.INSTANT; + +// function setUp() public override { +// super.setUp(); + +// DataTypes.AaveV3EmodeParams memory params = DataTypes.AaveV3EmodeParams({emodeCategory: 3}); + +// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); +// moduleExecutionData[0] = DataTypes.ModuleExecutionData({ +// executionType: DataTypes.CallType.DELEGATECALL, +// module: address(emodeModule), +// data: abi.encodeWithSelector(emodeModule.execute.selector, params) +// }); + +// vm.prank(admin); +// superloop.operate(moduleExecutionData); +// } + +// function test_initialize() public view { +// assertEq(withdrawManager.vault(), address(superloop)); +// assertEq(withdrawManager.asset(), XTZ); +// assertEq(withdrawManager.nextWithdrawRequestId(requestType), 1); +// assertEq(withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.INSTANT), 1); +// assertEq(withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.PRIORITY), 1); +// assertEq(withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.DEFERRED), 1); +// } + +// function test_resolveWithdrawRequests() public { +// _createPartialDepositWithResolution(depositAll); + +// // make 3 withdraw requests +// uint256 user1ShareBalance = superloop.balanceOf(user1); +// uint256 user2ShareBalance = superloop.balanceOf(user2); +// uint256 user3ShareBalance = superloop.balanceOf(user3); +// _makeWithdrawRequest(address(superloop), user1ShareBalance, user1, requestType); +// _makeWithdrawRequest(address(superloop), user2ShareBalance, user2, requestType); +// _makeWithdrawRequest(address(superloop), user3ShareBalance, user3, requestType); + +// _resolveAllRequests(user1ShareBalance + user2ShareBalance + user3ShareBalance, requestType); +// } + +// function test_resolveWithdrawRequestsWithPartials() public { +// // make 3 withdraw requests +// // resolve 1st fully, and 2nd partially +// // assert claimable and sharesProcessed for 1st and 2nd requests +// (uint256 sharesLeftToResolve,,) = _createPartialWithdrawWithResolution(requestType); + +// (,, uint256 repayAmount,,,,,,) = poolDataProvider.getUserReserveData(XTZ, address(superloop)); +// (uint256 withdrawAmount,,,,,,,,) = poolDataProvider.getUserReserveData(ST_XTZ, address(superloop)); + +// // resolve 2nd fuly and 3rd partially. I will unwind the entire amount but claim only partial amount +// uint256 repayAmountWithPremium = repayAmount + (repayAmount * 1) / 10000; +// uint256 sharesToResolve = sharesLeftToResolve / 2; + +// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](3); +// moduleExecutionData[0] = _repayCall(XTZ, repayAmount); +// moduleExecutionData[1] = _withdrawCall(ST_XTZ, withdrawAmount); +// moduleExecutionData[2] = +// _swapCallExactOutCurve(ST_XTZ, XTZ, XTZ_STXTZ_POOL, withdrawAmount, repayAmountWithPremium, STXTZ_XTZ_SWAP); + +// DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); +// intermediateExecutionData[0] = _flashloanCall(XTZ, repayAmount, abi.encode(moduleExecutionData)); + +// DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); +// finalExecutionData[0] = +// _resolveWithdrawRequestsCall(sharesToResolve, requestType, abi.encode(intermediateExecutionData)); + +// uint256 exchangeRateBefore = superloop.convertToAssets(ONE_SHARE); + +// vm.prank(admin); +// superloop.operate(finalExecutionData); + +// DataTypes.WithdrawRequestData memory withdrawRequest2 = withdrawManager.withdrawRequest(2, requestType); +// DataTypes.WithdrawRequestData memory withdrawRequest3 = withdrawManager.withdrawRequest(3, requestType); +// uint256 resolutionIdPointer = withdrawManager.resolutionIdPointer(requestType); +// uint256 totalPendingWithdraws = withdrawManager.totalPendingWithdraws(requestType); + +// assertTrue(withdrawRequest2.amountClaimable > 0); +// assertTrue(withdrawRequest2.sharesProcessed == withdrawRequest2.shares); +// assertTrue(withdrawRequest3.amountClaimable > 0); +// assertTrue(withdrawRequest3.sharesProcessed > 0 && withdrawRequest3.sharesProcessed < withdrawRequest3.shares); + +// assertTrue(withdrawRequest2.state == DataTypes.RequestProcessingState.FULLY_PROCESSED); +// assertTrue(withdrawRequest3.state == DataTypes.RequestProcessingState.PARTIALLY_PROCESSED); + +// uint256 exchangeRateAfter = superloop.convertToAssets(ONE_SHARE); +// assertApproxEqAbs(exchangeRateAfter, exchangeRateBefore, 1000); +// assertEq(totalPendingWithdraws, sharesLeftToResolve - sharesToResolve); +// assertEq(resolutionIdPointer, 3); +// } + +// function test_resolveWithdrawRequestsWithCancellation() public { +// (uint256 sharesLeftToResolve,,) = _createPartialWithdrawWithResolution(requestType); + +// // shoud not be able to cancel withdraw request if it's already processed +// vm.startPrank(user1); +// vm.expectRevert(bytes(Errors.INVALID_WITHDRAW_REQUEST_STATE)); +// withdrawManager.cancelWithdrawRequest(1, requestType); +// vm.stopPrank(); + +// // req 2 is partially processed, hence it should be partially cancelled and user 2 should get back the remaining shares + claimable amount +// uint256 user2BalanceBefore = IERC20(XTZ).balanceOf(user2); +// uint256 user2ShareBalanceBefore = superloop.balanceOf(user2); +// vm.startPrank(user2); +// withdrawManager.cancelWithdrawRequest(2, requestType); +// vm.stopPrank(); +// uint256 user2BalanceAfter = IERC20(XTZ).balanceOf(user2); +// uint256 user2ShareBalanceAfter = superloop.balanceOf(user2); + +// DataTypes.WithdrawRequestData memory withdrawRequest2 = withdrawManager.withdrawRequest(2, requestType); +// assertApproxEqRel( +// uint256(withdrawRequest2.state), uint256(DataTypes.RequestProcessingState.PARTIALLY_CANCELLED), 1e18 +// ); +// assertApproxEqRel( +// user2ShareBalanceAfter - user2ShareBalanceBefore, +// withdrawRequest2.shares - withdrawRequest2.sharesProcessed, +// 1e18 +// ); +// assertApproxEqRel(withdrawRequest2.amountClaimable, 0, 1e18); +// assertApproxEqRel(withdrawRequest2.amountClaimed, user2BalanceAfter - user2BalanceBefore, 1e18); +// uint256 totalPendingWithdraws = withdrawManager.totalPendingWithdraws(requestType); +// assertApproxEqRel(totalPendingWithdraws, sharesLeftToResolve - withdrawRequest2.sharesProcessed, 1e18); + +// (,, uint256 repayAmount,,,,,,) = poolDataProvider.getUserReserveData(XTZ, address(superloop)); +// (uint256 withdrawAmount,,,,,,,,) = poolDataProvider.getUserReserveData(ST_XTZ, address(superloop)); + +// // unwind the remaining position +// uint256 repayAmountWithPremium = repayAmount + (repayAmount * 1) / 10000; +// uint256 sharesToResolve = totalPendingWithdraws; + +// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](3); +// moduleExecutionData[0] = _repayCall(XTZ, repayAmount); +// moduleExecutionData[1] = _withdrawCall(ST_XTZ, withdrawAmount); +// moduleExecutionData[2] = +// _swapCallExactOutCurve(ST_XTZ, XTZ, XTZ_STXTZ_POOL, withdrawAmount, repayAmountWithPremium, STXTZ_XTZ_SWAP); + +// DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); +// intermediateExecutionData[0] = _flashloanCall(XTZ, repayAmount, abi.encode(moduleExecutionData)); + +// DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); +// finalExecutionData[0] = +// _resolveWithdrawRequestsCall(sharesToResolve, requestType, abi.encode(intermediateExecutionData)); + +// uint256 exchangeRateBefore = superloop.convertToAssets(ONE_SHARE); + +// vm.prank(admin); +// superloop.operate(finalExecutionData); + +// uint256 exchangeRateAfter = superloop.convertToAssets(ONE_SHARE); +// assertApproxEqAbs(exchangeRateAfter, exchangeRateBefore, 1000); + +// // pending withdraws should be only worth 3rd request +// // 3rd request shoudl be resolved based on amount, ie. 2nd should get skipped +// uint256 resolutionIdPointer = withdrawManager.resolutionIdPointer(requestType); +// DataTypes.WithdrawRequestData memory withdrawRequest3 = withdrawManager.withdrawRequest(3, requestType); +// assertApproxEqRel( +// uint256(withdrawRequest3.state), uint256(DataTypes.RequestProcessingState.FULLY_PROCESSED), 1e18 +// ); +// assertApproxEqRel(withdrawRequest3.sharesProcessed, withdrawRequest3.shares, 1e18); +// assertTrue(withdrawRequest3.amountClaimable > 0); +// totalPendingWithdraws = withdrawManager.totalPendingWithdraws(requestType); +// assertApproxEqRel(totalPendingWithdraws, 0, 1e18); +// assertApproxEqRel(resolutionIdPointer, 4, 1e18); + +// // new withdraw request should be able to be made, ie not blocked because of cancellation +// // user 2 shoudl be able to make a new withdraw request +// vm.startPrank(user2); +// superloop.approve(address(withdrawManager), 10 * ONE_SHARE); +// withdrawManager.requestWithdraw(10 * ONE_SHARE, requestType); +// vm.stopPrank(); + +// vm.startPrank(user3); +// vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_UNCLAIMED)); +// withdrawManager.requestWithdraw(10 * ONE_SHARE, requestType); +// vm.stopPrank(); + +// vm.startPrank(user1); +// vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_UNCLAIMED)); +// withdrawManager.requestWithdraw(10 * ONE_SHARE, requestType); +// vm.stopPrank(); +// } + +// function test_withdraw() public { +// _createPartialWithdrawWithResolution(requestType); + +// // should be able to withdraw if fully processed +// uint256 user1BalanceBefore = IERC20(XTZ).balanceOf(user1); +// uint256 user1ShareBalanceBefore = superloop.balanceOf(user1); +// vm.startPrank(user1); +// withdrawManager.withdraw(requestType); +// vm.stopPrank(); +// uint256 user1BalanceAfter = IERC20(XTZ).balanceOf(user1); +// uint256 user1ShareBalanceAfter = superloop.balanceOf(user1); +// DataTypes.WithdrawRequestData memory withdrawRequest1 = withdrawManager.withdrawRequest(1, requestType); +// assertEq(uint256(withdrawRequest1.state), uint256(DataTypes.RequestProcessingState.FULLY_PROCESSED)); +// assertEq(withdrawRequest1.sharesProcessed, withdrawRequest1.shares); +// assertTrue(withdrawRequest1.amountClaimable == 0); +// assertEq(withdrawRequest1.amountClaimed, user1BalanceAfter - user1BalanceBefore); +// assertEq(user1ShareBalanceAfter - user1ShareBalanceBefore, 0); + +// // should not be able to claim if already claimed +// vm.startPrank(user1); +// vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_ALREADY_CLAIMED)); +// withdrawManager.withdraw(requestType); +// vm.stopPrank(); + +// // should be able to withdraw partially processed +// uint256 user2BalanceBefore = IERC20(XTZ).balanceOf(user2); +// uint256 user2ShareBalanceBefore = superloop.balanceOf(user2); +// vm.startPrank(user2); +// withdrawManager.withdraw(requestType); +// vm.stopPrank(); +// uint256 user2BalanceAfter = IERC20(XTZ).balanceOf(user2); +// uint256 user2ShareBalanceAfter = superloop.balanceOf(user2); +// DataTypes.WithdrawRequestData memory withdrawRequest2 = withdrawManager.withdrawRequest(2, requestType); +// assertEq(uint256(withdrawRequest2.state), uint256(DataTypes.RequestProcessingState.PARTIALLY_PROCESSED)); +// assertTrue(withdrawRequest2.amountClaimable == 0); +// assertEq(withdrawRequest2.amountClaimed, user2BalanceAfter - user2BalanceBefore); +// assertEq(user2ShareBalanceAfter - user2ShareBalanceBefore, 0); + +// // should not be able to claim if nothing left to claim +// vm.startPrank(user2); +// vm.expectRevert(bytes(Errors.CANNOT_CLAIM_ZERO_AMOUNT)); +// withdrawManager.withdraw(requestType); +// vm.stopPrank(); + +// // should not be able to claim if cancelled +// user2BalanceBefore = IERC20(XTZ).balanceOf(user2); +// user2ShareBalanceBefore = superloop.balanceOf(user2); +// vm.startPrank(user2); +// withdrawManager.cancelWithdrawRequest(2, requestType); +// withdrawRequest2 = withdrawManager.withdrawRequest(2, requestType); +// assertEq(uint256(withdrawRequest2.state), uint256(DataTypes.RequestProcessingState.PARTIALLY_CANCELLED)); +// assertEq(withdrawRequest2.amountClaimable, 0); +// vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_NOT_FOUND)); +// withdrawManager.withdraw(requestType); +// vm.stopPrank(); +// user2BalanceAfter = IERC20(XTZ).balanceOf(user2); +// user2ShareBalanceAfter = superloop.balanceOf(user2); +// assertEq(user2BalanceAfter - user2BalanceBefore, 0); +// assertEq( +// user2ShareBalanceAfter - user2ShareBalanceBefore, withdrawRequest2.shares - withdrawRequest2.sharesProcessed +// ); + +// // should not be able to claim if unprocessed +// vm.startPrank(user3); +// vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_ACTIVE)); +// withdrawManager.withdraw(requestType); +// vm.stopPrank(); +// } + +// function test_isolationOfQueues() public { +// if (requestType != DataTypes.WithdrawRequestType.GENERAL) return; + +// _createPartialDepositWithResolution(true); + +// // make a withdraw request by every user on every queue +// _makeWithdrawRequest(address(superloop), ONE_SHARE, user1, DataTypes.WithdrawRequestType.GENERAL); +// _makeWithdrawRequest(address(superloop), ONE_SHARE, user2, DataTypes.WithdrawRequestType.GENERAL); +// _makeWithdrawRequest(address(superloop), ONE_SHARE, user3, DataTypes.WithdrawRequestType.GENERAL); + +// _makeWithdrawRequest(address(superloop), ONE_SHARE, user1, DataTypes.WithdrawRequestType.DEFERRED); +// _makeWithdrawRequest(address(superloop), ONE_SHARE, user2, DataTypes.WithdrawRequestType.DEFERRED); +// _makeWithdrawRequest(address(superloop), ONE_SHARE, user3, DataTypes.WithdrawRequestType.DEFERRED); + +// _makeWithdrawRequest(address(superloop), ONE_SHARE, user1, DataTypes.WithdrawRequestType.PRIORITY); +// _makeWithdrawRequest(address(superloop), ONE_SHARE, user2, DataTypes.WithdrawRequestType.PRIORITY); +// _makeWithdrawRequest(address(superloop), ONE_SHARE, user3, DataTypes.WithdrawRequestType.PRIORITY); + +// _makeWithdrawRequest(address(superloop), ONE_SHARE, user1, DataTypes.WithdrawRequestType.INSTANT); +// _makeWithdrawRequest(address(superloop), ONE_SHARE, user2, DataTypes.WithdrawRequestType.INSTANT); +// _makeWithdrawRequest(address(superloop), ONE_SHARE, user3, DataTypes.WithdrawRequestType.INSTANT); + +// uint256[] memory ids = new uint256[](3); +// ids[0] = 1; +// ids[1] = 2; +// ids[2] = 3; + +// address[] memory users = new address[](3); +// users[0] = user1; +// users[1] = user2; +// users[2] = user3; + +// DataTypes.WithdrawRequestData[] memory withdrawRequestsGeneral = +// withdrawManager.withdrawRequests(ids, DataTypes.WithdrawRequestType.GENERAL); +// DataTypes.WithdrawRequestData[] memory withdrawRequestsDeferred = +// withdrawManager.withdrawRequests(ids, DataTypes.WithdrawRequestType.DEFERRED); +// DataTypes.WithdrawRequestData[] memory withdrawRequestsPriority = +// withdrawManager.withdrawRequests(ids, DataTypes.WithdrawRequestType.PRIORITY); +// DataTypes.WithdrawRequestData[] memory withdrawRequestsInstant = +// withdrawManager.withdrawRequests(ids, DataTypes.WithdrawRequestType.INSTANT); + +// assertEq(withdrawRequestsGeneral.length, 3); +// assertEq(withdrawRequestsDeferred.length, 3); +// assertEq(withdrawRequestsPriority.length, 3); +// assertEq(withdrawRequestsInstant.length, 3); + +// for (uint256 i = 0; i < 3; i++) { +// assertEq(withdrawRequestsGeneral[i].user, users[i]); +// assertEq(withdrawRequestsDeferred[i].user, users[i]); +// assertEq(withdrawRequestsPriority[i].user, users[i]); + +// assertEq(withdrawRequestsInstant[i].shares, ONE_SHARE); +// assertEq(withdrawRequestsDeferred[i].shares, ONE_SHARE); +// assertEq(withdrawRequestsPriority[i].shares, ONE_SHARE); +// assertEq(withdrawRequestsGeneral[i].shares, ONE_SHARE); +// } + +// uint256 totalPendingWithdraws = withdrawManager.totalPendingWithdraws(DataTypes.WithdrawRequestType.GENERAL); +// assertEq(totalPendingWithdraws, 3 * ONE_SHARE); +// totalPendingWithdraws = withdrawManager.totalPendingWithdraws(DataTypes.WithdrawRequestType.DEFERRED); +// assertEq(totalPendingWithdraws, 3 * ONE_SHARE); +// totalPendingWithdraws = withdrawManager.totalPendingWithdraws(DataTypes.WithdrawRequestType.PRIORITY); +// assertEq(totalPendingWithdraws, 3 * ONE_SHARE); +// totalPendingWithdraws = withdrawManager.totalPendingWithdraws(DataTypes.WithdrawRequestType.INSTANT); +// assertEq(totalPendingWithdraws, 3 * ONE_SHARE); +// } + +// function _resolveAllRequests(uint256 sharesToResolve, DataTypes.WithdrawRequestType _requestType) internal { +// (,, uint256 repayAmount,,,,,,) = poolDataProvider.getUserReserveData(XTZ, address(superloop)); +// (uint256 withdrawAmount,,,,,,,,) = poolDataProvider.getUserReserveData(ST_XTZ, address(superloop)); +// uint256 repayAmountWithPremium = repayAmount + (repayAmount * 1) / 10000; + +// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](3); +// moduleExecutionData[0] = _repayCall(XTZ, repayAmount); +// moduleExecutionData[1] = _withdrawCall(ST_XTZ, withdrawAmount); +// moduleExecutionData[2] = +// _swapCallExactOutCurve(ST_XTZ, XTZ, XTZ_STXTZ_POOL, withdrawAmount, repayAmountWithPremium, STXTZ_XTZ_SWAP); + +// DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); +// intermediateExecutionData[0] = _flashloanCall(XTZ, repayAmount, abi.encode(moduleExecutionData)); + +// DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); +// finalExecutionData[0] = +// _resolveWithdrawRequestsCall(sharesToResolve, _requestType, abi.encode(intermediateExecutionData)); + +// uint256 exchangeRateBefore = superloop.convertToAssets(ONE_SHARE); + +// vm.prank(admin); +// superloop.operate(finalExecutionData); + +// // withdraw requests should have claimable > 0 and sharesProcessed > 0, exchange rate remains the same +// DataTypes.WithdrawRequestData memory withdrawRequest1 = withdrawManager.withdrawRequest(1, _requestType); +// DataTypes.WithdrawRequestData memory withdrawRequest2 = withdrawManager.withdrawRequest(2, _requestType); +// DataTypes.WithdrawRequestData memory withdrawRequest3 = withdrawManager.withdrawRequest(3, _requestType); +// uint256 resolutionIdPointer = withdrawManager.resolutionIdPointer(_requestType); +// uint256 totalPendingWithdraws = withdrawManager.totalPendingWithdraws(_requestType); + +// assertTrue(withdrawRequest1.amountClaimable > 0); +// assertTrue(withdrawRequest1.sharesProcessed == withdrawRequest1.shares); +// assertTrue(withdrawRequest2.amountClaimable > 0); +// assertTrue(withdrawRequest2.sharesProcessed == withdrawRequest2.shares); +// assertTrue(withdrawRequest3.amountClaimable > 0); +// assertTrue(withdrawRequest3.sharesProcessed == withdrawRequest3.shares); +// uint256 exchangeRateAfter = superloop.convertToAssets(ONE_SHARE); +// assertApproxEqAbs(exchangeRateAfter, exchangeRateBefore, 1000); +// assertEq(totalPendingWithdraws, 0); +// assertEq(resolutionIdPointer, 4); +// } +// } diff --git a/test/core/unit/AccountantAaveV3.t.sol b/test/core/unit/AccountantAaveV3.t.sol index 5aa156c..0033146 100644 --- a/test/core/unit/AccountantAaveV3.t.sol +++ b/test/core/unit/AccountantAaveV3.t.sol @@ -1,339 +1,339 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.13; - -import {TestBase} from "../TestBase.sol"; -import {console} from "forge-std/console.sol"; -import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -import {MockVault} from "../../../src/mock/MockVault.sol"; -import {AccountantAaveV3} from "../../../src/core/Accountant/aaveV3Accountant/AccountantAaveV3.sol"; -import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -import {DataTypes} from "../../../src/common/DataTypes.sol"; -import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; -import {IPoolAddressesProvider} from "aave-v3-core/contracts/interfaces/IPoolAddressesProvider.sol"; -import {IPoolDataProvider} from "aave-v3-core/contracts/interfaces/IPoolDataProvider.sol"; -import {IAaveOracle} from "aave-v3-core/contracts/interfaces/IAaveOracle.sol"; -import {IPriceOracleGetter} from "aave-v3-core/contracts/interfaces/IPriceOracleGetter.sol"; - -contract AccountantAaveV3Test is TestBase { - AccountantAaveV3 public accountantAaveV3Implementation; - ProxyAdmin public proxyAdmin; - - IERC20 public asset; - MockVault public vault; - - // Test data - uint256 public constant INITIAL_WHALE_BALANCE = 1000 ether; - - function setUp() public override { - super.setUp(); - - asset = IERC20(XTZ); - vault = new MockVault(asset, "Mock Vault", "mVLT"); - - address[] memory lendAssets = new address[](1); - lendAssets[0] = ST_XTZ; - address[] memory borrowAssets = new address[](1); - borrowAssets[0] = XTZ; - - DataTypes.AaveV3AccountantModuleInitData memory initData = DataTypes.AaveV3AccountantModuleInitData({ - poolAddressesProvider: AAVE_V3_POOL_ADDRESSES_PROVIDER, - lendAssets: lendAssets, - borrowAssets: borrowAssets, - performanceFee: uint16(PERFORMANCE_FEE), - vault: address(vault) - }); - - accountantAaveV3Implementation = new AccountantAaveV3(); - proxyAdmin = new ProxyAdmin(address(this)); - - TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( - address(accountantAaveV3Implementation), - address(proxyAdmin), - abi.encodeWithSelector(AccountantAaveV3.initialize.selector, initData) - ); - - accountantAaveV3 = AccountantAaveV3(address(proxy)); - - // Fund the accountant with XTZ from whale - vm.startPrank(XTZ_WHALE); - asset.transfer(address(vault), INITIAL_WHALE_BALANCE); - vm.stopPrank(); - } - - function test_Initialize() public { - // Test that initialization works correctly - address[] memory lendAssets = new address[](1); - lendAssets[0] = ST_XTZ; - address[] memory borrowAssets = new address[](1); - borrowAssets[0] = XTZ; - - DataTypes.AaveV3AccountantModuleInitData memory initData = DataTypes.AaveV3AccountantModuleInitData({ - poolAddressesProvider: AAVE_V3_POOL_ADDRESSES_PROVIDER, - lendAssets: lendAssets, - borrowAssets: borrowAssets, - performanceFee: uint16(PERFORMANCE_FEE), - vault: address(vault) - }); - - AccountantAaveV3 newImplementation = new AccountantAaveV3(); - ProxyAdmin newProxyAdmin = new ProxyAdmin(address(this)); - - TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( - address(newImplementation), - address(newProxyAdmin), - abi.encodeWithSelector(AccountantAaveV3.initialize.selector, initData) - ); - - AccountantAaveV3 newAccountant = AccountantAaveV3(address(proxy)); - - // Test that the contract is properly initialized - assertEq(newAccountant.getTotalAssets(), INITIAL_WHALE_BALANCE); // Should work without reverting - } - - function test_InitializeRevertIfAlreadyInitialized() public { - // Test that initialize reverts if called again - address[] memory lendAssets = new address[](1); - lendAssets[0] = ST_XTZ; - address[] memory borrowAssets = new address[](1); - borrowAssets[0] = XTZ; - - DataTypes.AaveV3AccountantModuleInitData memory initData = DataTypes.AaveV3AccountantModuleInitData({ - poolAddressesProvider: AAVE_V3_POOL_ADDRESSES_PROVIDER, - lendAssets: lendAssets, - borrowAssets: borrowAssets, - performanceFee: uint16(PERFORMANCE_FEE), - vault: address(vault) - }); - - vm.expectRevert(); - accountantAaveV3.initialize(initData); - } - - function test_GetTotalAssets() public view { - // Test getTotalAssets calculation using actual fork data - uint256 totalAssets = accountantAaveV3.getTotalAssets(); - - // Should return the base asset balance since there are no lend/borrow positions yet - assertEq(totalAssets, INITIAL_WHALE_BALANCE); - - console.log("Total assets:", totalAssets); - console.log("Expected assets:", INITIAL_WHALE_BALANCE); - } - - function test_GetTotalAssetsWithMultipleAssets() public { - // mock pool data provider and price oracle - _enableMockingPoolDataProvider(); - _enableMockingPriceOracle(1.01e8, 1e8); - - uint256 totalAssets = accountantAaveV3.getTotalAssets(); - - // Calculate expected total assets based on mock data: - // Lend assets: 1000 ether * 1.01e8 (ST_XTZ price) = 1010e8 - // Borrow assets: -800 ether * 1e8 (XTZ price) = -800e8 - // Base asset balance: 1000 ether * 1e8 (XTZ price) = 1000e8 - // Total in market reference currency: 1010e8 - 800e8 + 1000e8 = 1210e8 - // Convert to base asset: 1210e18 / 1e18 = 1210 ether - uint256 expectedTotalAssets = 1210 ether; - - assertEq(totalAssets, expectedTotalAssets); - console.log("Total assets with multiple assets:", totalAssets); - console.log("Expected total assets:", expectedTotalAssets); - } - - function test_GetPerformanceFee() public { - uint256 totalShares = 1000 ether; - uint256 exchangeRate = 1.1e18; // 10% increase - uint256 lastRealizedFeeExchangeRate = 1.0e18; - uint256 totalSupply = 1000 ether; - - // Set the last realized fee exchange rate - vm.prank(address(vault)); - accountantAaveV3.setLastRealizedFeeExchangeRate(lastRealizedFeeExchangeRate, totalSupply); - - // Calculate expected performance fee - uint256 latestAssetAmount = totalShares * exchangeRate; - uint256 prevAssetAmount = totalShares * lastRealizedFeeExchangeRate; - uint256 interestGenerated = latestAssetAmount - prevAssetAmount; - uint256 expectedPerformanceFee = (interestGenerated * PERFORMANCE_FEE) / (10000 * 1e18); - - vm.prank(address(vault)); - uint256 actualPerformanceFee = accountantAaveV3.getPerformanceFee(totalShares, exchangeRate, 18); - - console.log("Performance fee:", actualPerformanceFee); - console.log("Expected fee:", expectedPerformanceFee); - - assertEq(actualPerformanceFee, expectedPerformanceFee); - } - - function test_GetPerformanceFeeNoInterest() public { - uint256 totalShares = 1000 ether; - uint256 exchangeRate = 0.9e18; // 10% decrease - uint256 lastRealizedFeeExchangeRate = 1.0e18; - uint256 totalSupply = 1000 ether; - // Set the last realized fee exchange rate - vm.prank(address(vault)); - accountantAaveV3.setLastRealizedFeeExchangeRate(lastRealizedFeeExchangeRate, totalSupply); - - vm.prank(address(vault)); - uint256 performanceFee = accountantAaveV3.getPerformanceFee(totalShares, exchangeRate, 18); - - // Should return 0 when there's no interest (exchange rate decreased) - assertEq(performanceFee, 0); - } - - function test_GetPerformanceFeeRevertIfNotVault() public { - uint256 totalShares = 1000 ether; - uint256 exchangeRate = 1.1e18; - - vm.expectRevert(); - accountantAaveV3.getPerformanceFee(totalShares, exchangeRate, 18); - } - - function test_SetLastRealizedFeeExchangeRate() public { - uint256 newExchangeRate = 1.2e18; - uint256 totalSupply = 1000 ether; - - vm.prank(address(vault)); - accountantAaveV3.setLastRealizedFeeExchangeRate(newExchangeRate, totalSupply); - - // Test that the exchange rate was set correctly by calling getPerformanceFee - vm.prank(address(vault)); - uint256 performanceFee = accountantAaveV3.getPerformanceFee(1000 ether, 1.3e18, 18); - - // Should calculate based on the new exchange rate - uint256 expectedInterest = 1000 ether * 1.3e18 - 1000 ether * newExchangeRate; - uint256 expectedFee = (expectedInterest * PERFORMANCE_FEE) / (10000 * 1e18); - - assertEq(performanceFee, expectedFee); - } - - function test_SetLastRealizedFeeExchangeRateRevertIfNotVault() public { - uint256 newExchangeRate = 1.2e18; - uint256 totalSupply = 1000 ether; - - vm.expectRevert(); - accountantAaveV3.setLastRealizedFeeExchangeRate(newExchangeRate, totalSupply); - } - - function test_OnlyVaultModifier() public { - // Test that only vault can call restricted functions - vm.expectRevert(); - accountantAaveV3.getPerformanceFee(1000 ether, 1.1e18, 18); - - vm.expectRevert(); - accountantAaveV3.setLastRealizedFeeExchangeRate(1.1e18, 1000 ether); - } - - function test_ConstructorDisablesInitializers() public { - // Test that the constructor properly disables initializers - AccountantAaveV3 newContract = new AccountantAaveV3(); - - // Should revert if we try to initialize directly - address[] memory lendAssets = new address[](1); - lendAssets[0] = ST_XTZ; - address[] memory borrowAssets = new address[](1); - borrowAssets[0] = XTZ; - - DataTypes.AaveV3AccountantModuleInitData memory initData = DataTypes.AaveV3AccountantModuleInitData({ - poolAddressesProvider: AAVE_V3_POOL_ADDRESSES_PROVIDER, - lendAssets: lendAssets, - borrowAssets: borrowAssets, - performanceFee: uint16(PERFORMANCE_FEE), - vault: address(vault) - }); - - vm.expectRevert(); - newContract.initialize(initData); - } - - function test_GetTotalAssetsWithZeroBalances() public view { - // Test getTotalAssets when there are no lend/borrow positions - // This should be the default state since we haven't interacted with Aave V3 - uint256 totalAssets = accountantAaveV3.getTotalAssets(); - - // Should only include base asset balance - assertEq(totalAssets, INITIAL_WHALE_BALANCE); - } - - function test_PerformanceFeeCalculationEdgeCases() public { - // Test performance fee calculation with edge cases - - // Case 1: Zero shares - vm.prank(address(vault)); - accountantAaveV3.setLastRealizedFeeExchangeRate(1.0e18, 1000 ether); - - vm.prank(address(vault)); - uint256 fee1 = accountantAaveV3.getPerformanceFee(0, 1.1e18, 18); - assertEq(fee1, 0); - - // Case 2: Same exchange rate (no change) - vm.prank(address(vault)); - uint256 fee2 = accountantAaveV3.getPerformanceFee(1000 ether, 1.0e18, 18); - assertEq(fee2, 0); - - // Case 3: Very small interest - vm.prank(address(vault)); - uint256 fee3 = accountantAaveV3.getPerformanceFee(1000 ether, 1.0001e18, 18); - assertGt(fee3, 0); - - console.log("Small interest fee:", fee3); - } - - function test_InitializeWithEmptyArrays() public { - // Test initialization with empty arrays - address[] memory lendAssets = new address[](0); - address[] memory borrowAssets = new address[](0); - - DataTypes.AaveV3AccountantModuleInitData memory initData = DataTypes.AaveV3AccountantModuleInitData({ - poolAddressesProvider: AAVE_V3_POOL_ADDRESSES_PROVIDER, - lendAssets: lendAssets, - borrowAssets: borrowAssets, - performanceFee: uint16(PERFORMANCE_FEE), - vault: address(vault) - }); - - AccountantAaveV3 newImplementation = new AccountantAaveV3(); - ProxyAdmin newProxyAdmin = new ProxyAdmin(address(this)); - - TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( - address(newImplementation), - address(newProxyAdmin), - abi.encodeWithSelector(AccountantAaveV3.initialize.selector, initData) - ); - - AccountantAaveV3 newAccountant = AccountantAaveV3(address(proxy)); - - // Should work without reverting - uint256 totalAssets = newAccountant.getTotalAssets(); - assertEq(totalAssets, INITIAL_WHALE_BALANCE); - } - - function _enableMockingPoolDataProvider() internal { - vm.mockCall( - AAVE_V3_POOL_DATA_PROVIDER, - abi.encodeWithSelector(IPoolDataProvider.getUserReserveData.selector, ST_XTZ, address(vault)), - abi.encode(1000 * 10 ** 6, 0, 0, 0, 0, 0, 0, 0, 0) - ); - - vm.mockCall( - AAVE_V3_POOL_DATA_PROVIDER, - abi.encodeWithSelector(IPoolDataProvider.getUserReserveData.selector, XTZ, address(vault)), - abi.encode(0, 0, 800 ether, 0, 0, 0, 0, 0, 0) - ); - } - - function _enableMockingPriceOracle(uint256 stXtzPrice, uint256 xtzPrice) internal { - vm.mockCall( - AAVE_V3_PRICE_ORACLE, - abi.encodeWithSelector(IPriceOracleGetter.getAssetPrice.selector, ST_XTZ), - abi.encode(stXtzPrice) - ); - - vm.mockCall( - AAVE_V3_PRICE_ORACLE, - abi.encodeWithSelector(IPriceOracleGetter.getAssetPrice.selector, XTZ), - abi.encode(xtzPrice) - ); - } -} +// // SPDX-License-Identifier: MIT + +// pragma solidity ^0.8.13; + +// import {TestBase} from "../TestBase.sol"; +// import {console} from "forge-std/console.sol"; +// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +// import {MockVault} from "../../../src/mock/MockVault.sol"; +// import {AccountantAaveV3} from "../../../src/core/Accountant/aaveV3Accountant/AccountantAaveV3.sol"; +// import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +// import {DataTypes} from "../../../src/common/DataTypes.sol"; +// import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +// import {IPoolAddressesProvider} from "aave-v3-core/contracts/interfaces/IPoolAddressesProvider.sol"; +// import {IPoolDataProvider} from "aave-v3-core/contracts/interfaces/IPoolDataProvider.sol"; +// import {IAaveOracle} from "aave-v3-core/contracts/interfaces/IAaveOracle.sol"; +// import {IPriceOracleGetter} from "aave-v3-core/contracts/interfaces/IPriceOracleGetter.sol"; + +// contract AccountantAaveV3Test is TestBase { +// AccountantAaveV3 public accountantAaveV3Implementation; +// ProxyAdmin public proxyAdmin; + +// IERC20 public asset; +// MockVault public vault; + +// // Test data +// uint256 public constant INITIAL_WHALE_BALANCE = 1000 ether; + +// function setUp() public override { +// super.setUp(); + +// asset = IERC20(XTZ); +// vault = new MockVault(asset, "Mock Vault", "mVLT"); + +// address[] memory lendAssets = new address[](1); +// lendAssets[0] = ST_XTZ; +// address[] memory borrowAssets = new address[](1); +// borrowAssets[0] = XTZ; + +// DataTypes.AaveV3AccountantModuleInitData memory initData = DataTypes.AaveV3AccountantModuleInitData({ +// poolAddressesProvider: AAVE_V3_POOL_ADDRESSES_PROVIDER, +// lendAssets: lendAssets, +// borrowAssets: borrowAssets, +// performanceFee: uint16(PERFORMANCE_FEE), +// vault: address(vault) +// }); + +// accountantAaveV3Implementation = new AccountantAaveV3(); +// proxyAdmin = new ProxyAdmin(address(this)); + +// TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( +// address(accountantAaveV3Implementation), +// address(proxyAdmin), +// abi.encodeWithSelector(AccountantAaveV3.initialize.selector, initData) +// ); + +// accountantAaveV3 = AccountantAaveV3(address(proxy)); + +// // Fund the accountant with XTZ from whale +// vm.startPrank(XTZ_WHALE); +// asset.transfer(address(vault), INITIAL_WHALE_BALANCE); +// vm.stopPrank(); +// } + +// function test_Initialize() public { +// // Test that initialization works correctly +// address[] memory lendAssets = new address[](1); +// lendAssets[0] = ST_XTZ; +// address[] memory borrowAssets = new address[](1); +// borrowAssets[0] = XTZ; + +// DataTypes.AaveV3AccountantModuleInitData memory initData = DataTypes.AaveV3AccountantModuleInitData({ +// poolAddressesProvider: AAVE_V3_POOL_ADDRESSES_PROVIDER, +// lendAssets: lendAssets, +// borrowAssets: borrowAssets, +// performanceFee: uint16(PERFORMANCE_FEE), +// vault: address(vault) +// }); + +// AccountantAaveV3 newImplementation = new AccountantAaveV3(); +// ProxyAdmin newProxyAdmin = new ProxyAdmin(address(this)); + +// TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( +// address(newImplementation), +// address(newProxyAdmin), +// abi.encodeWithSelector(AccountantAaveV3.initialize.selector, initData) +// ); + +// AccountantAaveV3 newAccountant = AccountantAaveV3(address(proxy)); + +// // Test that the contract is properly initialized +// assertEq(newAccountant.getTotalAssets(), INITIAL_WHALE_BALANCE); // Should work without reverting +// } + +// function test_InitializeRevertIfAlreadyInitialized() public { +// // Test that initialize reverts if called again +// address[] memory lendAssets = new address[](1); +// lendAssets[0] = ST_XTZ; +// address[] memory borrowAssets = new address[](1); +// borrowAssets[0] = XTZ; + +// DataTypes.AaveV3AccountantModuleInitData memory initData = DataTypes.AaveV3AccountantModuleInitData({ +// poolAddressesProvider: AAVE_V3_POOL_ADDRESSES_PROVIDER, +// lendAssets: lendAssets, +// borrowAssets: borrowAssets, +// performanceFee: uint16(PERFORMANCE_FEE), +// vault: address(vault) +// }); + +// vm.expectRevert(); +// accountantAaveV3.initialize(initData); +// } + +// function test_GetTotalAssets() public view { +// // Test getTotalAssets calculation using actual fork data +// uint256 totalAssets = accountantAaveV3.getTotalAssets(); + +// // Should return the base asset balance since there are no lend/borrow positions yet +// assertEq(totalAssets, INITIAL_WHALE_BALANCE); + +// console.log("Total assets:", totalAssets); +// console.log("Expected assets:", INITIAL_WHALE_BALANCE); +// } + +// function test_GetTotalAssetsWithMultipleAssets() public { +// // mock pool data provider and price oracle +// _enableMockingPoolDataProvider(); +// _enableMockingPriceOracle(1.01e8, 1e8); + +// uint256 totalAssets = accountantAaveV3.getTotalAssets(); + +// // Calculate expected total assets based on mock data: +// // Lend assets: 1000 ether * 1.01e8 (ST_XTZ price) = 1010e8 +// // Borrow assets: -800 ether * 1e8 (XTZ price) = -800e8 +// // Base asset balance: 1000 ether * 1e8 (XTZ price) = 1000e8 +// // Total in market reference currency: 1010e8 - 800e8 + 1000e8 = 1210e8 +// // Convert to base asset: 1210e18 / 1e18 = 1210 ether +// uint256 expectedTotalAssets = 1210 ether; + +// assertEq(totalAssets, expectedTotalAssets); +// console.log("Total assets with multiple assets:", totalAssets); +// console.log("Expected total assets:", expectedTotalAssets); +// } + +// function test_GetPerformanceFee() public { +// uint256 totalShares = 1000 ether; +// uint256 exchangeRate = 1.1e18; // 10% increase +// uint256 lastRealizedFeeExchangeRate = 1.0e18; +// uint256 totalSupply = 1000 ether; + +// // Set the last realized fee exchange rate +// vm.prank(address(vault)); +// accountantAaveV3.setLastRealizedFeeExchangeRate(lastRealizedFeeExchangeRate, totalSupply); + +// // Calculate expected performance fee +// uint256 latestAssetAmount = totalShares * exchangeRate; +// uint256 prevAssetAmount = totalShares * lastRealizedFeeExchangeRate; +// uint256 interestGenerated = latestAssetAmount - prevAssetAmount; +// uint256 expectedPerformanceFee = (interestGenerated * PERFORMANCE_FEE) / (10000 * 1e18); + +// vm.prank(address(vault)); +// uint256 actualPerformanceFee = accountantAaveV3.getPerformanceFee(totalShares, exchangeRate, 18); + +// console.log("Performance fee:", actualPerformanceFee); +// console.log("Expected fee:", expectedPerformanceFee); + +// assertEq(actualPerformanceFee, expectedPerformanceFee); +// } + +// function test_GetPerformanceFeeNoInterest() public { +// uint256 totalShares = 1000 ether; +// uint256 exchangeRate = 0.9e18; // 10% decrease +// uint256 lastRealizedFeeExchangeRate = 1.0e18; +// uint256 totalSupply = 1000 ether; +// // Set the last realized fee exchange rate +// vm.prank(address(vault)); +// accountantAaveV3.setLastRealizedFeeExchangeRate(lastRealizedFeeExchangeRate, totalSupply); + +// vm.prank(address(vault)); +// uint256 performanceFee = accountantAaveV3.getPerformanceFee(totalShares, exchangeRate, 18); + +// // Should return 0 when there's no interest (exchange rate decreased) +// assertEq(performanceFee, 0); +// } + +// function test_GetPerformanceFeeRevertIfNotVault() public { +// uint256 totalShares = 1000 ether; +// uint256 exchangeRate = 1.1e18; + +// vm.expectRevert(); +// accountantAaveV3.getPerformanceFee(totalShares, exchangeRate, 18); +// } + +// function test_SetLastRealizedFeeExchangeRate() public { +// uint256 newExchangeRate = 1.2e18; +// uint256 totalSupply = 1000 ether; + +// vm.prank(address(vault)); +// accountantAaveV3.setLastRealizedFeeExchangeRate(newExchangeRate, totalSupply); + +// // Test that the exchange rate was set correctly by calling getPerformanceFee +// vm.prank(address(vault)); +// uint256 performanceFee = accountantAaveV3.getPerformanceFee(1000 ether, 1.3e18, 18); + +// // Should calculate based on the new exchange rate +// uint256 expectedInterest = 1000 ether * 1.3e18 - 1000 ether * newExchangeRate; +// uint256 expectedFee = (expectedInterest * PERFORMANCE_FEE) / (10000 * 1e18); + +// assertEq(performanceFee, expectedFee); +// } + +// function test_SetLastRealizedFeeExchangeRateRevertIfNotVault() public { +// uint256 newExchangeRate = 1.2e18; +// uint256 totalSupply = 1000 ether; + +// vm.expectRevert(); +// accountantAaveV3.setLastRealizedFeeExchangeRate(newExchangeRate, totalSupply); +// } + +// function test_OnlyVaultModifier() public { +// // Test that only vault can call restricted functions +// vm.expectRevert(); +// accountantAaveV3.getPerformanceFee(1000 ether, 1.1e18, 18); + +// vm.expectRevert(); +// accountantAaveV3.setLastRealizedFeeExchangeRate(1.1e18, 1000 ether); +// } + +// function test_ConstructorDisablesInitializers() public { +// // Test that the constructor properly disables initializers +// AccountantAaveV3 newContract = new AccountantAaveV3(); + +// // Should revert if we try to initialize directly +// address[] memory lendAssets = new address[](1); +// lendAssets[0] = ST_XTZ; +// address[] memory borrowAssets = new address[](1); +// borrowAssets[0] = XTZ; + +// DataTypes.AaveV3AccountantModuleInitData memory initData = DataTypes.AaveV3AccountantModuleInitData({ +// poolAddressesProvider: AAVE_V3_POOL_ADDRESSES_PROVIDER, +// lendAssets: lendAssets, +// borrowAssets: borrowAssets, +// performanceFee: uint16(PERFORMANCE_FEE), +// vault: address(vault) +// }); + +// vm.expectRevert(); +// newContract.initialize(initData); +// } + +// function test_GetTotalAssetsWithZeroBalances() public view { +// // Test getTotalAssets when there are no lend/borrow positions +// // This should be the default state since we haven't interacted with Aave V3 +// uint256 totalAssets = accountantAaveV3.getTotalAssets(); + +// // Should only include base asset balance +// assertEq(totalAssets, INITIAL_WHALE_BALANCE); +// } + +// function test_PerformanceFeeCalculationEdgeCases() public { +// // Test performance fee calculation with edge cases + +// // Case 1: Zero shares +// vm.prank(address(vault)); +// accountantAaveV3.setLastRealizedFeeExchangeRate(1.0e18, 1000 ether); + +// vm.prank(address(vault)); +// uint256 fee1 = accountantAaveV3.getPerformanceFee(0, 1.1e18, 18); +// assertEq(fee1, 0); + +// // Case 2: Same exchange rate (no change) +// vm.prank(address(vault)); +// uint256 fee2 = accountantAaveV3.getPerformanceFee(1000 ether, 1.0e18, 18); +// assertEq(fee2, 0); + +// // Case 3: Very small interest +// vm.prank(address(vault)); +// uint256 fee3 = accountantAaveV3.getPerformanceFee(1000 ether, 1.0001e18, 18); +// assertGt(fee3, 0); + +// console.log("Small interest fee:", fee3); +// } + +// function test_InitializeWithEmptyArrays() public { +// // Test initialization with empty arrays +// address[] memory lendAssets = new address[](0); +// address[] memory borrowAssets = new address[](0); + +// DataTypes.AaveV3AccountantModuleInitData memory initData = DataTypes.AaveV3AccountantModuleInitData({ +// poolAddressesProvider: AAVE_V3_POOL_ADDRESSES_PROVIDER, +// lendAssets: lendAssets, +// borrowAssets: borrowAssets, +// performanceFee: uint16(PERFORMANCE_FEE), +// vault: address(vault) +// }); + +// AccountantAaveV3 newImplementation = new AccountantAaveV3(); +// ProxyAdmin newProxyAdmin = new ProxyAdmin(address(this)); + +// TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( +// address(newImplementation), +// address(newProxyAdmin), +// abi.encodeWithSelector(AccountantAaveV3.initialize.selector, initData) +// ); + +// AccountantAaveV3 newAccountant = AccountantAaveV3(address(proxy)); + +// // Should work without reverting +// uint256 totalAssets = newAccountant.getTotalAssets(); +// assertEq(totalAssets, INITIAL_WHALE_BALANCE); +// } + +// function _enableMockingPoolDataProvider() internal { +// vm.mockCall( +// AAVE_V3_POOL_DATA_PROVIDER, +// abi.encodeWithSelector(IPoolDataProvider.getUserReserveData.selector, ST_XTZ, address(vault)), +// abi.encode(1000 * 10 ** 6, 0, 0, 0, 0, 0, 0, 0, 0) +// ); + +// vm.mockCall( +// AAVE_V3_POOL_DATA_PROVIDER, +// abi.encodeWithSelector(IPoolDataProvider.getUserReserveData.selector, XTZ, address(vault)), +// abi.encode(0, 0, 800 ether, 0, 0, 0, 0, 0, 0) +// ); +// } + +// function _enableMockingPriceOracle(uint256 stXtzPrice, uint256 xtzPrice) internal { +// vm.mockCall( +// AAVE_V3_PRICE_ORACLE, +// abi.encodeWithSelector(IPriceOracleGetter.getAssetPrice.selector, ST_XTZ), +// abi.encode(stXtzPrice) +// ); + +// vm.mockCall( +// AAVE_V3_PRICE_ORACLE, +// abi.encodeWithSelector(IPriceOracleGetter.getAssetPrice.selector, XTZ), +// abi.encode(xtzPrice) +// ); +// } +// } diff --git a/test/core/unit/DepositManager.t.sol b/test/core/unit/DepositManager.t.sol index 66807a6..e9d5001 100644 --- a/test/core/unit/DepositManager.t.sol +++ b/test/core/unit/DepositManager.t.sol @@ -1,297 +1,297 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.13; - -import {TestBase} from "../TestBase.sol"; -import {console} from "forge-std/console.sol"; -import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; -import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; -import {TransparentUpgradeableProxy} from - "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -import {DataTypes} from "../../../src/common/DataTypes.sol"; -import {Errors} from "../../../src/common/Errors.sol"; -import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -import {DepositManager} from "../../../src/core/DepositManager/DepositManager.sol"; - -contract DepositManagerTest is TestBase { - Superloop public superloopImplementation; - ProxyAdmin public proxyAdmin; - address public user1; - address public user2; - - function setUp() public override { - super.setUp(); - - user1 = makeAddr("user1"); - user2 = makeAddr("user2"); - - vm.startPrank(admin); - _deployModules(); - - address[] memory modules = new address[](5); - modules[0] = address(supplyModule); - modules[1] = address(withdrawModule); - modules[2] = address(borrowModule); - modules[3] = address(repayModule); - modules[4] = address(dexModule); - - DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ - asset: XTZ, - name: "XTZ Vault", - symbol: "XTZV", - supplyCap: 100000 * 10 ** 18, - minimumDepositAmount: 100, - instantWithdrawFee: 0, - superloopModuleRegistry: address(moduleRegistry), - modules: modules, - accountant: mockModule, - withdrawManager: mockModule, - depositManager: mockModule, - cashReserve: 1000, - vaultAdmin: admin, - treasury: treasury, - vaultOperator: admin - }); - superloopImplementation = new Superloop(); - proxyAdmin = new ProxyAdmin(address(this)); - - TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( - address(superloopImplementation), - address(proxyAdmin), - abi.encodeWithSelector(Superloop.initialize.selector, initData) - ); - superloop = Superloop(payable(address(proxy))); - - _deployAccountant(address(superloop), _singleAddressArray(ST_XTZ), _singleAddressArray(XTZ)); - _deployWithdrawManager(address(superloop)); - _deployDepositManager(address(superloop)); - - superloop.setAccountantModule(address(accountant)); - superloop.setWithdrawManagerModule(address(withdrawManager)); - superloop.setDepositManagerModule(address(depositManager)); - - vm.stopPrank(); - - vm.label(address(superloop), "superloop"); - vm.label(user1, "user1"); - vm.label(user2, "user2"); - } - - function test_Initialize() public view { - address vault = depositManager.vault(); - address asset = depositManager.asset(); - uint256 nextDepositRequestId = depositManager.nextDepositRequestId(); - - assertEq(vault, address(superloop)); - assertEq(asset, XTZ); - assertEq(nextDepositRequestId, 1); - } - - // ============ requestDeposit Tests ============ - - function test_requestDeposit_Success() public { - uint256 depositAmount = 1000 * 10 ** 18; - - // Give user1 some XTZ tokens - deal(XTZ, user1, depositAmount); - - vm.startPrank(user1); - IERC20(XTZ).approve(address(depositManager), depositAmount); - - // Expect the DepositRequested event - vm.expectEmit(true, true, true, true); - emit DepositManager.DepositRequested(user1, depositAmount, 1); - - depositManager.requestDeposit(depositAmount, address(0)); - vm.stopPrank(); - - // Verify the deposit request was created - DataTypes.DepositRequestData memory request = depositManager.depositRequest(1); - - assertEq(request.amount, depositAmount); - assertEq(request.amountProcessed, 0); - assertEq(request.user, user1); - assertEq(uint256(request.state), uint256(DataTypes.RequestProcessingState.UNPROCESSED)); - - // Verify user's deposit request ID was set - (, uint256 userRequestId) = depositManager.userDepositRequest(user1); - assertEq(userRequestId, 1); - - // Verify total pending deposits increased - assertEq(depositManager.totalPendingDeposits(), depositAmount); - - // Verify tokens were transferred to deposit manager - assertEq(IERC20(XTZ).balanceOf(address(depositManager)), depositAmount); - assertEq(IERC20(XTZ).balanceOf(user1), 0); - } +// // SPDX-License-Identifier: MIT + +// pragma solidity ^0.8.13; + +// import {TestBase} from "../TestBase.sol"; +// import {console} from "forge-std/console.sol"; +// import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; +// import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; +// import {TransparentUpgradeableProxy} from +// "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +// import {DataTypes} from "../../../src/common/DataTypes.sol"; +// import {Errors} from "../../../src/common/Errors.sol"; +// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +// import {DepositManager} from "../../../src/core/DepositManager/DepositManager.sol"; + +// contract DepositManagerTest is TestBase { +// Superloop public superloopImplementation; +// ProxyAdmin public proxyAdmin; +// address public user1; +// address public user2; + +// function setUp() public override { +// super.setUp(); + +// user1 = makeAddr("user1"); +// user2 = makeAddr("user2"); + +// vm.startPrank(admin); +// _deployModules(); + +// address[] memory modules = new address[](5); +// modules[0] = address(supplyModule); +// modules[1] = address(withdrawModule); +// modules[2] = address(borrowModule); +// modules[3] = address(repayModule); +// modules[4] = address(dexModule); + +// DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ +// asset: XTZ, +// name: "XTZ Vault", +// symbol: "XTZV", +// supplyCap: 100000 * 10 ** 18, +// minimumDepositAmount: 100, +// instantWithdrawFee: 0, +// superloopModuleRegistry: address(moduleRegistry), +// modules: modules, +// accountant: mockModule, +// withdrawManager: mockModule, +// depositManager: mockModule, +// cashReserve: 1000, +// vaultAdmin: admin, +// treasury: treasury, +// vaultOperator: admin +// }); +// superloopImplementation = new Superloop(); +// proxyAdmin = new ProxyAdmin(address(this)); + +// TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( +// address(superloopImplementation), +// address(proxyAdmin), +// abi.encodeWithSelector(Superloop.initialize.selector, initData) +// ); +// superloop = Superloop(payable(address(proxy))); + +// _deployAccountant(address(superloop), _singleAddressArray(ST_XTZ), _singleAddressArray(XTZ)); +// _deployWithdrawManager(address(superloop)); +// _deployDepositManager(address(superloop)); + +// superloop.setAccountantModule(address(accountant)); +// superloop.setWithdrawManagerModule(address(withdrawManager)); +// superloop.setDepositManagerModule(address(depositManager)); + +// vm.stopPrank(); + +// vm.label(address(superloop), "superloop"); +// vm.label(user1, "user1"); +// vm.label(user2, "user2"); +// } + +// function test_Initialize() public view { +// address vault = depositManager.vault(); +// address asset = depositManager.asset(); +// uint256 nextDepositRequestId = depositManager.nextDepositRequestId(); + +// assertEq(vault, address(superloop)); +// assertEq(asset, XTZ); +// assertEq(nextDepositRequestId, 1); +// } + +// // ============ requestDeposit Tests ============ + +// function test_requestDeposit_Success() public { +// uint256 depositAmount = 1000 * 10 ** 18; + +// // Give user1 some XTZ tokens +// deal(XTZ, user1, depositAmount); + +// vm.startPrank(user1); +// IERC20(XTZ).approve(address(depositManager), depositAmount); + +// // Expect the DepositRequested event +// vm.expectEmit(true, true, true, true); +// emit DepositManager.DepositRequested(user1, depositAmount, 1); + +// depositManager.requestDeposit(depositAmount, address(0)); +// vm.stopPrank(); + +// // Verify the deposit request was created +// DataTypes.DepositRequestData memory request = depositManager.depositRequest(1); + +// assertEq(request.amount, depositAmount); +// assertEq(request.amountProcessed, 0); +// assertEq(request.user, user1); +// assertEq(uint256(request.state), uint256(DataTypes.RequestProcessingState.UNPROCESSED)); + +// // Verify user's deposit request ID was set +// (, uint256 userRequestId) = depositManager.userDepositRequest(user1); +// assertEq(userRequestId, 1); + +// // Verify total pending deposits increased +// assertEq(depositManager.totalPendingDeposits(), depositAmount); + +// // Verify tokens were transferred to deposit manager +// assertEq(IERC20(XTZ).balanceOf(address(depositManager)), depositAmount); +// assertEq(IERC20(XTZ).balanceOf(user1), 0); +// } - function test_requestDeposit_OnBehalfOf() public { - uint256 depositAmount = 1000 * 10 ** 18; +// function test_requestDeposit_OnBehalfOf() public { +// uint256 depositAmount = 1000 * 10 ** 18; - // Give user1 some XTZ tokens - deal(XTZ, user1, depositAmount); +// // Give user1 some XTZ tokens +// deal(XTZ, user1, depositAmount); - vm.startPrank(user1); - IERC20(XTZ).approve(address(depositManager), depositAmount); +// vm.startPrank(user1); +// IERC20(XTZ).approve(address(depositManager), depositAmount); - // Expect the DepositRequested event with user2 as the beneficiary - vm.expectEmit(true, true, true, true); - emit DepositManager.DepositRequested(user2, depositAmount, 1); +// // Expect the DepositRequested event with user2 as the beneficiary +// vm.expectEmit(true, true, true, true); +// emit DepositManager.DepositRequested(user2, depositAmount, 1); - depositManager.requestDeposit(depositAmount, user2); - vm.stopPrank(); +// depositManager.requestDeposit(depositAmount, user2); +// vm.stopPrank(); - // Verify the deposit request was created for user2 - DataTypes.DepositRequestData memory request = depositManager.depositRequest(1); +// // Verify the deposit request was created for user2 +// DataTypes.DepositRequestData memory request = depositManager.depositRequest(1); - assertEq(request.user, user2); - assertEq(request.amount, depositAmount); - } +// assertEq(request.user, user2); +// assertEq(request.amount, depositAmount); +// } - function test_requestDeposit_ZeroAmount() public { - uint256 depositAmount = 0; +// function test_requestDeposit_ZeroAmount() public { +// uint256 depositAmount = 0; - vm.startPrank(user1); - vm.expectRevert(bytes(Errors.INVALID_AMOUNT)); - depositManager.requestDeposit(depositAmount, address(0)); - vm.stopPrank(); - } +// vm.startPrank(user1); +// vm.expectRevert(bytes(Errors.INVALID_AMOUNT)); +// depositManager.requestDeposit(depositAmount, address(0)); +// vm.stopPrank(); +// } - function test_requestDeposit_MinimumDepositAmountNotMet() public { - uint256 depositAmount = 99; +// function test_requestDeposit_MinimumDepositAmountNotMet() public { +// uint256 depositAmount = 99; - vm.startPrank(user1); - vm.expectRevert(bytes(Errors.INVALID_AMOUNT)); - depositManager.requestDeposit(depositAmount, address(0)); - vm.stopPrank(); - } +// vm.startPrank(user1); +// vm.expectRevert(bytes(Errors.INVALID_AMOUNT)); +// depositManager.requestDeposit(depositAmount, address(0)); +// vm.stopPrank(); +// } - function test_requestDeposit_SupplyCapExceeded() public { - uint256 depositAmount = 200000 * 10 ** 18; // Exceeds supply cap of 100000 * 10**18 +// function test_requestDeposit_SupplyCapExceeded() public { +// uint256 depositAmount = 200000 * 10 ** 18; // Exceeds supply cap of 100000 * 10**18 - // Give user1 enough XTZ tokens - deal(XTZ, user1, depositAmount); +// // Give user1 enough XTZ tokens +// deal(XTZ, user1, depositAmount); - vm.startPrank(user1); - IERC20(XTZ).approve(address(depositManager), depositAmount); +// vm.startPrank(user1); +// IERC20(XTZ).approve(address(depositManager), depositAmount); - vm.expectRevert(bytes(Errors.SUPPLY_CAP_EXCEEDED)); - depositManager.requestDeposit(depositAmount, address(0)); - vm.stopPrank(); - } +// vm.expectRevert(bytes(Errors.SUPPLY_CAP_EXCEEDED)); +// depositManager.requestDeposit(depositAmount, address(0)); +// vm.stopPrank(); +// } - function test_requestDeposit_ActiveRequestExists() public { - uint256 depositAmount = 1000 * 10 ** 18; +// function test_requestDeposit_ActiveRequestExists() public { +// uint256 depositAmount = 1000 * 10 ** 18; - // Give user1 some XTZ tokens - deal(XTZ, user1, depositAmount * 2); +// // Give user1 some XTZ tokens +// deal(XTZ, user1, depositAmount * 2); - vm.startPrank(user1); - IERC20(XTZ).approve(address(depositManager), depositAmount * 2); +// vm.startPrank(user1); +// IERC20(XTZ).approve(address(depositManager), depositAmount * 2); - // Make first deposit request - depositManager.requestDeposit(depositAmount, address(0)); +// // Make first deposit request +// depositManager.requestDeposit(depositAmount, address(0)); - // Try to make another deposit request - should fail - vm.expectRevert(bytes(Errors.DEPOSIT_REQUEST_ACTIVE)); - depositManager.requestDeposit(depositAmount, address(0)); - vm.stopPrank(); - } +// // Try to make another deposit request - should fail +// vm.expectRevert(bytes(Errors.DEPOSIT_REQUEST_ACTIVE)); +// depositManager.requestDeposit(depositAmount, address(0)); +// vm.stopPrank(); +// } - function test_requestDeposit_InsufficientBalance() public { - uint256 depositAmount = 1000 * 10 ** 18; +// function test_requestDeposit_InsufficientBalance() public { +// uint256 depositAmount = 1000 * 10 ** 18; - // Don't give user1 any XTZ tokens - vm.startPrank(user1); - IERC20(XTZ).approve(address(depositManager), depositAmount); +// // Don't give user1 any XTZ tokens +// vm.startPrank(user1); +// IERC20(XTZ).approve(address(depositManager), depositAmount); - vm.expectRevert(); - depositManager.requestDeposit(depositAmount, address(0)); - vm.stopPrank(); - } +// vm.expectRevert(); +// depositManager.requestDeposit(depositAmount, address(0)); +// vm.stopPrank(); +// } - // // ============ cancelDepositRequest Tests ============ +// // // ============ cancelDepositRequest Tests ============ - function test_cancelDepositRequest_Success() public { - uint256 depositAmount = 1000 * 10 ** 18; +// function test_cancelDepositRequest_Success() public { +// uint256 depositAmount = 1000 * 10 ** 18; - // Give user1 some XTZ tokens and make a deposit request - deal(XTZ, user1, depositAmount); +// // Give user1 some XTZ tokens and make a deposit request +// deal(XTZ, user1, depositAmount); - vm.startPrank(user1); - IERC20(XTZ).approve(address(depositManager), depositAmount); - depositManager.requestDeposit(depositAmount, address(0)); +// vm.startPrank(user1); +// IERC20(XTZ).approve(address(depositManager), depositAmount); +// depositManager.requestDeposit(depositAmount, address(0)); - uint256 userBalanceBefore = IERC20(XTZ).balanceOf(user1); +// uint256 userBalanceBefore = IERC20(XTZ).balanceOf(user1); - // Expect the DepositRequestCancelled event - vm.expectEmit(true, true, true, true); - emit DepositManager.DepositRequestCancelled(1, user1, depositAmount); +// // Expect the DepositRequestCancelled event +// vm.expectEmit(true, true, true, true); +// emit DepositManager.DepositRequestCancelled(1, user1, depositAmount); - depositManager.cancelDepositRequest(1); - vm.stopPrank(); +// depositManager.cancelDepositRequest(1); +// vm.stopPrank(); - // Verify the deposit request was cancelled - DataTypes.DepositRequestData memory request = depositManager.depositRequest(1); +// // Verify the deposit request was cancelled +// DataTypes.DepositRequestData memory request = depositManager.depositRequest(1); - assertEq(uint256(request.state), uint256(DataTypes.RequestProcessingState.CANCELLED)); +// assertEq(uint256(request.state), uint256(DataTypes.RequestProcessingState.CANCELLED)); - // Verify user's deposit request ID was cleared - (, uint256 userRequestId) = depositManager.userDepositRequest(user1); - assertEq(userRequestId, 0); +// // Verify user's deposit request ID was cleared +// (, uint256 userRequestId) = depositManager.userDepositRequest(user1); +// assertEq(userRequestId, 0); - // Verify total pending deposits decreased - assertEq(depositManager.totalPendingDeposits(), 0); +// // Verify total pending deposits decreased +// assertEq(depositManager.totalPendingDeposits(), 0); - // Verify tokens were refunded to user - assertEq(IERC20(XTZ).balanceOf(user1), userBalanceBefore + depositAmount); - assertEq(IERC20(XTZ).balanceOf(address(depositManager)), 0); - } +// // Verify tokens were refunded to user +// assertEq(IERC20(XTZ).balanceOf(user1), userBalanceBefore + depositAmount); +// assertEq(IERC20(XTZ).balanceOf(address(depositManager)), 0); +// } - function test_cancelDepositRequest_WrongOwner() public { - uint256 depositAmount = 1000 * 10 ** 18; +// function test_cancelDepositRequest_WrongOwner() public { +// uint256 depositAmount = 1000 * 10 ** 18; - // Give user1 some XTZ tokens and make a deposit request - deal(XTZ, user1, depositAmount); +// // Give user1 some XTZ tokens and make a deposit request +// deal(XTZ, user1, depositAmount); - vm.startPrank(user1); - IERC20(XTZ).approve(address(depositManager), depositAmount); - depositManager.requestDeposit(depositAmount, address(0)); - vm.stopPrank(); +// vm.startPrank(user1); +// IERC20(XTZ).approve(address(depositManager), depositAmount); +// depositManager.requestDeposit(depositAmount, address(0)); +// vm.stopPrank(); - // Try to cancel from user2 - should fail - vm.startPrank(user2); - vm.expectRevert(bytes(Errors.CALLER_NOT_DEPOSIT_REQUEST_OWNER)); - depositManager.cancelDepositRequest(1); - vm.stopPrank(); - } +// // Try to cancel from user2 - should fail +// vm.startPrank(user2); +// vm.expectRevert(bytes(Errors.CALLER_NOT_DEPOSIT_REQUEST_OWNER)); +// depositManager.cancelDepositRequest(1); +// vm.stopPrank(); +// } - function test_cancelDepositRequest_AlreadyCancelled() public { - uint256 depositAmount = 1000 * 10 ** 18; +// function test_cancelDepositRequest_AlreadyCancelled() public { +// uint256 depositAmount = 1000 * 10 ** 18; - // Give user1 some XTZ tokens and make a deposit request - deal(XTZ, user1, depositAmount); +// // Give user1 some XTZ tokens and make a deposit request +// deal(XTZ, user1, depositAmount); - vm.startPrank(user1); - IERC20(XTZ).approve(address(depositManager), depositAmount); - depositManager.requestDeposit(depositAmount, address(0)); +// vm.startPrank(user1); +// IERC20(XTZ).approve(address(depositManager), depositAmount); +// depositManager.requestDeposit(depositAmount, address(0)); - // Cancel the request - depositManager.cancelDepositRequest(1); +// // Cancel the request +// depositManager.cancelDepositRequest(1); - // Try to cancel again - should fail - vm.expectRevert(bytes(Errors.DEPOSIT_REQUEST_ALREADY_CANCELLED)); - depositManager.cancelDepositRequest(1); - vm.stopPrank(); - } +// // Try to cancel again - should fail +// vm.expectRevert(bytes(Errors.DEPOSIT_REQUEST_ALREADY_CANCELLED)); +// depositManager.cancelDepositRequest(1); +// vm.stopPrank(); +// } - function test_cancelDepositRequest_NonExistentRequest() public { - vm.startPrank(user1); - vm.expectRevert(bytes(Errors.DEPOSIT_REQUEST_NOT_FOUND)); - depositManager.cancelDepositRequest(999); // Non-existent request ID - vm.stopPrank(); - } +// function test_cancelDepositRequest_NonExistentRequest() public { +// vm.startPrank(user1); +// vm.expectRevert(bytes(Errors.DEPOSIT_REQUEST_NOT_FOUND)); +// depositManager.cancelDepositRequest(999); // Non-existent request ID +// vm.stopPrank(); +// } - // TODO: Test partial cancellation, already processed cancellation and reslveDepostRequest functions - // NOTE: Cannot test these function without manipulating the internal state, so will be tested in integration tests -} +// // TODO: Test partial cancellation, already processed cancellation and reslveDepostRequest functions +// // NOTE: Cannot test these function without manipulating the internal state, so will be tested in integration tests +// } diff --git a/test/core/unit/ModuleRegistry.t.sol b/test/core/unit/ModuleRegistry.t.sol index 386504c..e6fbea5 100644 --- a/test/core/unit/ModuleRegistry.t.sol +++ b/test/core/unit/ModuleRegistry.t.sol @@ -1,289 +1,289 @@ -// SPDX-License-Identifier: MIT +// // SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; +// pragma solidity ^0.8.13; -import {Test, console} from "forge-std/Test.sol"; -import {SuperloopModuleRegistry} from "../../../src/core/ModuleRegistry/ModuleRegistry.sol"; -import {DataTypes} from "../../../src/common/DataTypes.sol"; -import {Errors} from "../../../src/common/Errors.sol"; +// import {Test, console} from "forge-std/Test.sol"; +// import {SuperloopModuleRegistry} from "../../../src/core/ModuleRegistry/ModuleRegistry.sol"; +// import {DataTypes} from "../../../src/common/DataTypes.sol"; +// import {Errors} from "../../../src/common/Errors.sol"; -contract ModuleRegistryTest is Test { - SuperloopModuleRegistry public moduleRegistry; +// contract ModuleRegistryTest is Test { +// SuperloopModuleRegistry public moduleRegistry; - address public owner; - address public user; - address public module1; - address public module2; - address public module3; +// address public owner; +// address public user; +// address public module1; +// address public module2; +// address public module3; - string public constant MODULE_NAME_1 = "UniversalDexModule"; - string public constant MODULE_NAME_2 = "AccountantModule"; - string public constant MODULE_NAME_3 = "WithdrawManagerModule"; +// string public constant MODULE_NAME_1 = "UniversalDexModule"; +// string public constant MODULE_NAME_2 = "AccountantModule"; +// string public constant MODULE_NAME_3 = "WithdrawManagerModule"; - event ModuleSet(string name, bytes32 indexed id, address indexed module); +// event ModuleSet(string name, bytes32 indexed id, address indexed module); - function setUp() public { - owner = makeAddr("owner"); - user = makeAddr("user"); - module1 = makeAddr("module1"); - module2 = makeAddr("module2"); - module3 = makeAddr("module3"); +// function setUp() public { +// owner = makeAddr("owner"); +// user = makeAddr("user"); +// module1 = makeAddr("module1"); +// module2 = makeAddr("module2"); +// module3 = makeAddr("module3"); - vm.startPrank(owner); - moduleRegistry = new SuperloopModuleRegistry(); - vm.stopPrank(); - } +// vm.startPrank(owner); +// moduleRegistry = new SuperloopModuleRegistry(); +// vm.stopPrank(); +// } - // ============ Constructor Tests ============ +// // ============ Constructor Tests ============ - function test_Constructor_SetsOwner() public view { - assertEq(moduleRegistry.owner(), owner); - } +// function test_Constructor_SetsOwner() public view { +// assertEq(moduleRegistry.owner(), owner); +// } - // ============ setModule Tests ============ +// // ============ setModule Tests ============ - function test_SetModule_Success() public { - vm.startPrank(owner); +// function test_SetModule_Success() public { +// vm.startPrank(owner); - vm.expectEmit(true, true, false, true); - emit ModuleSet(MODULE_NAME_1, keccak256(abi.encode(MODULE_NAME_1)), module1); +// vm.expectEmit(true, true, false, true); +// emit ModuleSet(MODULE_NAME_1, keccak256(abi.encode(MODULE_NAME_1)), module1); - moduleRegistry.setModule(MODULE_NAME_1, module1); +// moduleRegistry.setModule(MODULE_NAME_1, module1); - vm.stopPrank(); +// vm.stopPrank(); - // Verify module was set correctly - DataTypes.ModuleData memory moduleData = moduleRegistry.getModuleByName(MODULE_NAME_1); - assertEq(moduleData.moduleName, MODULE_NAME_1); - assertEq(moduleData.moduleAddress, module1); - } +// // Verify module was set correctly +// DataTypes.ModuleData memory moduleData = moduleRegistry.getModuleByName(MODULE_NAME_1); +// assertEq(moduleData.moduleName, MODULE_NAME_1); +// assertEq(moduleData.moduleAddress, module1); +// } - function test_SetModule_OnlyOwner() public { - vm.startPrank(user); +// function test_SetModule_OnlyOwner() public { +// vm.startPrank(user); - vm.expectRevert(); - moduleRegistry.setModule(MODULE_NAME_1, module1); +// vm.expectRevert(); +// moduleRegistry.setModule(MODULE_NAME_1, module1); - vm.stopPrank(); - } +// vm.stopPrank(); +// } - function test_SetModule_EmptyName() public { - vm.startPrank(owner); +// function test_SetModule_EmptyName() public { +// vm.startPrank(owner); - vm.expectRevert(); - moduleRegistry.setModule("", module1); +// vm.expectRevert(); +// moduleRegistry.setModule("", module1); - vm.stopPrank(); - } +// vm.stopPrank(); +// } - function test_SetModule_ZeroAddress() public { - vm.startPrank(owner); +// function test_SetModule_ZeroAddress() public { +// vm.startPrank(owner); - vm.expectRevert(); - moduleRegistry.setModule(MODULE_NAME_1, address(0)); +// vm.expectRevert(); +// moduleRegistry.setModule(MODULE_NAME_1, address(0)); - vm.stopPrank(); - } +// vm.stopPrank(); +// } - function test_SetModule_MultipleModules() public { - vm.startPrank(owner); +// function test_SetModule_MultipleModules() public { +// vm.startPrank(owner); - moduleRegistry.setModule(MODULE_NAME_1, module1); - moduleRegistry.setModule(MODULE_NAME_2, module2); - moduleRegistry.setModule(MODULE_NAME_3, module3); +// moduleRegistry.setModule(MODULE_NAME_1, module1); +// moduleRegistry.setModule(MODULE_NAME_2, module2); +// moduleRegistry.setModule(MODULE_NAME_3, module3); - vm.stopPrank(); +// vm.stopPrank(); - // Verify all modules were set correctly - DataTypes.ModuleData memory moduleData1 = moduleRegistry.getModuleByName(MODULE_NAME_1); - DataTypes.ModuleData memory moduleData2 = moduleRegistry.getModuleByName(MODULE_NAME_2); - DataTypes.ModuleData memory moduleData3 = moduleRegistry.getModuleByName(MODULE_NAME_3); +// // Verify all modules were set correctly +// DataTypes.ModuleData memory moduleData1 = moduleRegistry.getModuleByName(MODULE_NAME_1); +// DataTypes.ModuleData memory moduleData2 = moduleRegistry.getModuleByName(MODULE_NAME_2); +// DataTypes.ModuleData memory moduleData3 = moduleRegistry.getModuleByName(MODULE_NAME_3); - assertEq(moduleData1.moduleName, MODULE_NAME_1); - assertEq(moduleData1.moduleAddress, module1); - assertEq(moduleData2.moduleName, MODULE_NAME_2); - assertEq(moduleData2.moduleAddress, module2); - assertEq(moduleData3.moduleName, MODULE_NAME_3); - assertEq(moduleData3.moduleAddress, module3); - } +// assertEq(moduleData1.moduleName, MODULE_NAME_1); +// assertEq(moduleData1.moduleAddress, module1); +// assertEq(moduleData2.moduleName, MODULE_NAME_2); +// assertEq(moduleData2.moduleAddress, module2); +// assertEq(moduleData3.moduleName, MODULE_NAME_3); +// assertEq(moduleData3.moduleAddress, module3); +// } - function test_SetModule_OverwriteExisting() public { - vm.startPrank(owner); +// function test_SetModule_OverwriteExisting() public { +// vm.startPrank(owner); - moduleRegistry.setModule(MODULE_NAME_1, module1); - moduleRegistry.setModule(MODULE_NAME_1, module2); // Overwrite with different address - - vm.stopPrank(); - - // Verify module was overwritten - DataTypes.ModuleData memory moduleData = moduleRegistry.getModuleByName(MODULE_NAME_1); - assertEq(moduleData.moduleName, MODULE_NAME_1); - assertEq(moduleData.moduleAddress, module2); - } - - // ============ getModuleByName Tests ============ - - function test_GetModuleByName_ExistingModule() public { - vm.startPrank(owner); - moduleRegistry.setModule(MODULE_NAME_1, module1); - vm.stopPrank(); - - DataTypes.ModuleData memory moduleData = moduleRegistry.getModuleByName(MODULE_NAME_1); - assertEq(moduleData.moduleName, MODULE_NAME_1); - assertEq(moduleData.moduleAddress, module1); - } +// moduleRegistry.setModule(MODULE_NAME_1, module1); +// moduleRegistry.setModule(MODULE_NAME_1, module2); // Overwrite with different address + +// vm.stopPrank(); + +// // Verify module was overwritten +// DataTypes.ModuleData memory moduleData = moduleRegistry.getModuleByName(MODULE_NAME_1); +// assertEq(moduleData.moduleName, MODULE_NAME_1); +// assertEq(moduleData.moduleAddress, module2); +// } + +// // ============ getModuleByName Tests ============ + +// function test_GetModuleByName_ExistingModule() public { +// vm.startPrank(owner); +// moduleRegistry.setModule(MODULE_NAME_1, module1); +// vm.stopPrank(); + +// DataTypes.ModuleData memory moduleData = moduleRegistry.getModuleByName(MODULE_NAME_1); +// assertEq(moduleData.moduleName, MODULE_NAME_1); +// assertEq(moduleData.moduleAddress, module1); +// } - function test_GetModuleByName_NonExistentModule() public view { - DataTypes.ModuleData memory moduleData = moduleRegistry.getModuleByName("NonExistentModule"); - assertEq(moduleData.moduleName, ""); - assertEq(moduleData.moduleAddress, address(0)); - } - - function test_GetModuleByName_CaseSensitive() public { - vm.startPrank(owner); - moduleRegistry.setModule(MODULE_NAME_1, module1); - vm.stopPrank(); - - // Different case should return empty data - DataTypes.ModuleData memory moduleData = moduleRegistry.getModuleByName("universaldexmodule"); - assertEq(moduleData.moduleName, ""); - assertEq(moduleData.moduleAddress, address(0)); - } - - // ============ getModuleByAddress Tests ============ - - function test_GetModuleByAddress_ExistingModule() public { - vm.startPrank(owner); - moduleRegistry.setModule(MODULE_NAME_1, module1); - vm.stopPrank(); - - DataTypes.ModuleData memory moduleData = moduleRegistry.getModuleByAddress(module1); - assertEq(moduleData.moduleName, MODULE_NAME_1); - assertEq(moduleData.moduleAddress, module1); - } - - function test_GetModuleByAddress_NonExistentModule() public { - DataTypes.ModuleData memory moduleData = moduleRegistry.getModuleByAddress(makeAddr("nonexistent")); - assertEq(moduleData.moduleName, ""); - assertEq(moduleData.moduleAddress, address(0)); - } - - function test_GetModuleByAddress_ZeroAddress() public view { - DataTypes.ModuleData memory moduleData = moduleRegistry.getModuleByAddress(address(0)); - assertEq(moduleData.moduleName, ""); - assertEq(moduleData.moduleAddress, address(0)); - } - - // ============ getModules Tests ============ - - function test_GetModules_EmptyRegistry() public view { - DataTypes.ModuleData[] memory modules = moduleRegistry.getModules(); - assertEq(modules.length, 0); - } - - function test_GetModules_SingleModule() public { - vm.startPrank(owner); - moduleRegistry.setModule(MODULE_NAME_1, module1); - vm.stopPrank(); - - DataTypes.ModuleData[] memory modules = moduleRegistry.getModules(); - assertEq(modules.length, 1); - assertEq(modules[0].moduleName, MODULE_NAME_1); - assertEq(modules[0].moduleAddress, module1); - } - - function test_GetModules_MultipleModules() public { - vm.startPrank(owner); - moduleRegistry.setModule(MODULE_NAME_1, module1); - moduleRegistry.setModule(MODULE_NAME_2, module2); - moduleRegistry.setModule(MODULE_NAME_3, module3); - vm.stopPrank(); - - DataTypes.ModuleData[] memory modules = moduleRegistry.getModules(); - assertEq(modules.length, 3); - - // Verify all modules are present (order may vary) - bool found1 = false; - bool found2 = false; - bool found3 = false; - - for (uint256 i = 0; i < modules.length; i++) { - if (keccak256(abi.encode(modules[i].moduleName)) == keccak256(abi.encode(MODULE_NAME_1))) { - assertEq(modules[i].moduleAddress, module1); - found1 = true; - } else if (keccak256(abi.encode(modules[i].moduleName)) == keccak256(abi.encode(MODULE_NAME_2))) { - assertEq(modules[i].moduleAddress, module2); - found2 = true; - } else if (keccak256(abi.encode(modules[i].moduleName)) == keccak256(abi.encode(MODULE_NAME_3))) { - assertEq(modules[i].moduleAddress, module3); - found3 = true; - } - } - - assertTrue(found1 && found2 && found3, "All modules should be found"); - } - - // ============ isModuleWhitelisted Tests ============ - - function test_IsModuleWhitelisted_WhitelistedModule() public { - vm.startPrank(owner); - moduleRegistry.setModule(MODULE_NAME_1, module1); - vm.stopPrank(); - - assertTrue(moduleRegistry.isModuleWhitelisted(module1)); - } - - function test_IsModuleWhitelisted_NonWhitelistedModule() public view { - assertFalse(moduleRegistry.isModuleWhitelisted(module1)); - } - - function test_IsModuleWhitelisted_ZeroAddress() public view { - assertFalse(moduleRegistry.isModuleWhitelisted(address(0))); - } - - // ============ Integration Tests ============ - - function test_Integration_FullWorkflow() public { - vm.startPrank(owner); - - // Set multiple modules - moduleRegistry.setModule(MODULE_NAME_1, module1); - moduleRegistry.setModule(MODULE_NAME_2, module2); - moduleRegistry.setModule(MODULE_NAME_3, module3); - - vm.stopPrank(); - - // Test getModuleByName for all modules - DataTypes.ModuleData memory data1 = moduleRegistry.getModuleByName(MODULE_NAME_1); - DataTypes.ModuleData memory data2 = moduleRegistry.getModuleByName(MODULE_NAME_2); - DataTypes.ModuleData memory data3 = moduleRegistry.getModuleByName(MODULE_NAME_3); - - assertEq(data1.moduleName, MODULE_NAME_1); - assertEq(data1.moduleAddress, module1); - assertEq(data2.moduleName, MODULE_NAME_2); - assertEq(data2.moduleAddress, module2); - assertEq(data3.moduleName, MODULE_NAME_3); - assertEq(data3.moduleAddress, module3); - - // Test getModuleByAddress for all modules - DataTypes.ModuleData memory addrData1 = moduleRegistry.getModuleByAddress(module1); - DataTypes.ModuleData memory addrData2 = moduleRegistry.getModuleByAddress(module2); - DataTypes.ModuleData memory addrData3 = moduleRegistry.getModuleByAddress(module3); - - assertEq(addrData1.moduleName, MODULE_NAME_1); - assertEq(addrData1.moduleAddress, module1); - assertEq(addrData2.moduleName, MODULE_NAME_2); - assertEq(addrData2.moduleAddress, module2); - assertEq(addrData3.moduleName, MODULE_NAME_3); - assertEq(addrData3.moduleAddress, module3); - - // Test isModuleWhitelisted for all modules - assertTrue(moduleRegistry.isModuleWhitelisted(module1)); - assertTrue(moduleRegistry.isModuleWhitelisted(module2)); - assertTrue(moduleRegistry.isModuleWhitelisted(module3)); - - // Test getModules returns all modules - DataTypes.ModuleData[] memory allModules = moduleRegistry.getModules(); - assertEq(allModules.length, 3); - } -} +// function test_GetModuleByName_NonExistentModule() public view { +// DataTypes.ModuleData memory moduleData = moduleRegistry.getModuleByName("NonExistentModule"); +// assertEq(moduleData.moduleName, ""); +// assertEq(moduleData.moduleAddress, address(0)); +// } + +// function test_GetModuleByName_CaseSensitive() public { +// vm.startPrank(owner); +// moduleRegistry.setModule(MODULE_NAME_1, module1); +// vm.stopPrank(); + +// // Different case should return empty data +// DataTypes.ModuleData memory moduleData = moduleRegistry.getModuleByName("universaldexmodule"); +// assertEq(moduleData.moduleName, ""); +// assertEq(moduleData.moduleAddress, address(0)); +// } + +// // ============ getModuleByAddress Tests ============ + +// function test_GetModuleByAddress_ExistingModule() public { +// vm.startPrank(owner); +// moduleRegistry.setModule(MODULE_NAME_1, module1); +// vm.stopPrank(); + +// DataTypes.ModuleData memory moduleData = moduleRegistry.getModuleByAddress(module1); +// assertEq(moduleData.moduleName, MODULE_NAME_1); +// assertEq(moduleData.moduleAddress, module1); +// } + +// function test_GetModuleByAddress_NonExistentModule() public { +// DataTypes.ModuleData memory moduleData = moduleRegistry.getModuleByAddress(makeAddr("nonexistent")); +// assertEq(moduleData.moduleName, ""); +// assertEq(moduleData.moduleAddress, address(0)); +// } + +// function test_GetModuleByAddress_ZeroAddress() public view { +// DataTypes.ModuleData memory moduleData = moduleRegistry.getModuleByAddress(address(0)); +// assertEq(moduleData.moduleName, ""); +// assertEq(moduleData.moduleAddress, address(0)); +// } + +// // ============ getModules Tests ============ + +// function test_GetModules_EmptyRegistry() public view { +// DataTypes.ModuleData[] memory modules = moduleRegistry.getModules(); +// assertEq(modules.length, 0); +// } + +// function test_GetModules_SingleModule() public { +// vm.startPrank(owner); +// moduleRegistry.setModule(MODULE_NAME_1, module1); +// vm.stopPrank(); + +// DataTypes.ModuleData[] memory modules = moduleRegistry.getModules(); +// assertEq(modules.length, 1); +// assertEq(modules[0].moduleName, MODULE_NAME_1); +// assertEq(modules[0].moduleAddress, module1); +// } + +// function test_GetModules_MultipleModules() public { +// vm.startPrank(owner); +// moduleRegistry.setModule(MODULE_NAME_1, module1); +// moduleRegistry.setModule(MODULE_NAME_2, module2); +// moduleRegistry.setModule(MODULE_NAME_3, module3); +// vm.stopPrank(); + +// DataTypes.ModuleData[] memory modules = moduleRegistry.getModules(); +// assertEq(modules.length, 3); + +// // Verify all modules are present (order may vary) +// bool found1 = false; +// bool found2 = false; +// bool found3 = false; + +// for (uint256 i = 0; i < modules.length; i++) { +// if (keccak256(abi.encode(modules[i].moduleName)) == keccak256(abi.encode(MODULE_NAME_1))) { +// assertEq(modules[i].moduleAddress, module1); +// found1 = true; +// } else if (keccak256(abi.encode(modules[i].moduleName)) == keccak256(abi.encode(MODULE_NAME_2))) { +// assertEq(modules[i].moduleAddress, module2); +// found2 = true; +// } else if (keccak256(abi.encode(modules[i].moduleName)) == keccak256(abi.encode(MODULE_NAME_3))) { +// assertEq(modules[i].moduleAddress, module3); +// found3 = true; +// } +// } + +// assertTrue(found1 && found2 && found3, "All modules should be found"); +// } + +// // ============ isModuleWhitelisted Tests ============ + +// function test_IsModuleWhitelisted_WhitelistedModule() public { +// vm.startPrank(owner); +// moduleRegistry.setModule(MODULE_NAME_1, module1); +// vm.stopPrank(); + +// assertTrue(moduleRegistry.isModuleWhitelisted(module1)); +// } + +// function test_IsModuleWhitelisted_NonWhitelistedModule() public view { +// assertFalse(moduleRegistry.isModuleWhitelisted(module1)); +// } + +// function test_IsModuleWhitelisted_ZeroAddress() public view { +// assertFalse(moduleRegistry.isModuleWhitelisted(address(0))); +// } + +// // ============ Integration Tests ============ + +// function test_Integration_FullWorkflow() public { +// vm.startPrank(owner); + +// // Set multiple modules +// moduleRegistry.setModule(MODULE_NAME_1, module1); +// moduleRegistry.setModule(MODULE_NAME_2, module2); +// moduleRegistry.setModule(MODULE_NAME_3, module3); + +// vm.stopPrank(); + +// // Test getModuleByName for all modules +// DataTypes.ModuleData memory data1 = moduleRegistry.getModuleByName(MODULE_NAME_1); +// DataTypes.ModuleData memory data2 = moduleRegistry.getModuleByName(MODULE_NAME_2); +// DataTypes.ModuleData memory data3 = moduleRegistry.getModuleByName(MODULE_NAME_3); + +// assertEq(data1.moduleName, MODULE_NAME_1); +// assertEq(data1.moduleAddress, module1); +// assertEq(data2.moduleName, MODULE_NAME_2); +// assertEq(data2.moduleAddress, module2); +// assertEq(data3.moduleName, MODULE_NAME_3); +// assertEq(data3.moduleAddress, module3); + +// // Test getModuleByAddress for all modules +// DataTypes.ModuleData memory addrData1 = moduleRegistry.getModuleByAddress(module1); +// DataTypes.ModuleData memory addrData2 = moduleRegistry.getModuleByAddress(module2); +// DataTypes.ModuleData memory addrData3 = moduleRegistry.getModuleByAddress(module3); + +// assertEq(addrData1.moduleName, MODULE_NAME_1); +// assertEq(addrData1.moduleAddress, module1); +// assertEq(addrData2.moduleName, MODULE_NAME_2); +// assertEq(addrData2.moduleAddress, module2); +// assertEq(addrData3.moduleName, MODULE_NAME_3); +// assertEq(addrData3.moduleAddress, module3); + +// // Test isModuleWhitelisted for all modules +// assertTrue(moduleRegistry.isModuleWhitelisted(module1)); +// assertTrue(moduleRegistry.isModuleWhitelisted(module2)); +// assertTrue(moduleRegistry.isModuleWhitelisted(module3)); + +// // Test getModules returns all modules +// DataTypes.ModuleData[] memory allModules = moduleRegistry.getModules(); +// assertEq(allModules.length, 3); +// } +// } diff --git a/test/core/unit/Superloop.t.sol b/test/core/unit/Superloop.t.sol index 1eee229..5c742a5 100644 --- a/test/core/unit/Superloop.t.sol +++ b/test/core/unit/Superloop.t.sol @@ -1,455 +1,455 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.13; - -import {console} from "forge-std/console.sol"; -import {TestBase} from "../TestBase.sol"; -import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; -import {DataTypes} from "../../../src/common/DataTypes.sol"; -import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; -import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {Errors} from "../../../src/common/Errors.sol"; - -contract SuperloopTest is TestBase { - Superloop public superloopImplementation; - ProxyAdmin public proxyAdmin; - address public user1; - address public user2; - - function setUp() public override { - super.setUp(); - - user1 = makeAddr("user1"); - user2 = makeAddr("user2"); - - vm.startPrank(admin); - _deployModules(); - - address[] memory modules = new address[](5); - modules[0] = address(supplyModule); - modules[1] = address(withdrawModule); - modules[2] = address(borrowModule); - modules[3] = address(repayModule); - modules[4] = address(dexModule); - - DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ - asset: XTZ, - name: "XTZ Vault", - symbol: "XTZV", - supplyCap: 100000 * 10 ** 18, - minimumDepositAmount: 100, - instantWithdrawFee: 0, - superloopModuleRegistry: address(moduleRegistry), - modules: modules, - accountant: mockModule, - withdrawManager: mockModule, - depositManager: mockModule, - cashReserve: 1000, - vaultAdmin: admin, - treasury: treasury, - vaultOperator: admin - }); - superloopImplementation = new Superloop(); - proxyAdmin = new ProxyAdmin(address(this)); - - TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( - address(superloopImplementation), - address(proxyAdmin), - abi.encodeWithSelector(Superloop.initialize.selector, initData) - ); - superloop = Superloop(payable(address(proxy))); - - _deployAccountant(address(superloop), _singleAddressArray(ST_XTZ), _singleAddressArray(XTZ)); - _deployWithdrawManager(address(superloop)); - - superloop.setAccountantModule(address(accountantAaveV3)); - superloop.setWithdrawManagerModule(address(withdrawManager)); - - vm.stopPrank(); - - vm.label(address(superloop), "superloop"); - vm.label(user1, "user1"); - vm.label(user2, "user2"); - } - - // ============ Superloop.sol Tests ============ - - function test_Initialize() public { - // Test that initialization works correctly - address[] memory modules = new address[](1); - modules[0] = address(supplyModule); - - DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ - asset: XTZ, - name: "Test Vault", - symbol: "TEST", - supplyCap: 1000 * 10 ** 18, - minimumDepositAmount: 100, - instantWithdrawFee: 0, - superloopModuleRegistry: address(moduleRegistry), - modules: modules, - accountant: mockModule, - withdrawManager: mockModule, - depositManager: mockModule, - cashReserve: 1000, - vaultAdmin: admin, - treasury: treasury, - vaultOperator: admin - }); - - Superloop newImplementation = new Superloop(); - ProxyAdmin newProxyAdmin = new ProxyAdmin(address(this)); - - TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( - address(newImplementation), - address(newProxyAdmin), - abi.encodeWithSelector(Superloop.initialize.selector, initData) - ); - - Superloop newSuperloop = Superloop(payable(address(proxy))); - - // Test that the contract is properly initialized - assertEq(newSuperloop.name(), "Test Vault"); - assertEq(newSuperloop.symbol(), "TEST"); - assertEq(newSuperloop.asset(), XTZ); - } - - function test_InitializeRevertIfAlreadyInitialized() public { - // Test that initialize reverts if called again - address[] memory modules = new address[](1); - modules[0] = address(supplyModule); - - DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ - asset: XTZ, - name: "Test Vault", - symbol: "TEST", - supplyCap: 1000 * 10 ** 18, - minimumDepositAmount: 100, - instantWithdrawFee: 0, - superloopModuleRegistry: address(moduleRegistry), - modules: modules, - accountant: mockModule, - withdrawManager: mockModule, - depositManager: mockModule, - cashReserve: 1000, - vaultAdmin: admin, - treasury: treasury, - vaultOperator: admin - }); - - vm.expectRevert(); - superloop.initialize(initData); - } - - function test_InitializeRevertIfInvalidModule() public { - // Test that initialization reverts if module is not whitelisted - address[] memory modules = new address[](1); - modules[0] = address(0x123); // Invalid module - - DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ - asset: XTZ, - name: "Test Vault", - symbol: "TEST", - supplyCap: 1000 * 10 ** 18, - minimumDepositAmount: 100, - instantWithdrawFee: 0, - superloopModuleRegistry: address(moduleRegistry), - modules: modules, - accountant: mockModule, - withdrawManager: mockModule, - depositManager: mockModule, - cashReserve: 1000, - vaultAdmin: admin, - treasury: treasury, - vaultOperator: admin - }); - - Superloop newImplementation = new Superloop(); - ProxyAdmin newProxyAdmin = new ProxyAdmin(address(this)); - - vm.expectRevert(bytes(Errors.INVALID_MODULE)); - new TransparentUpgradeableProxy( - address(newImplementation), - address(newProxyAdmin), - abi.encodeWithSelector(Superloop.initialize.selector, initData) - ); - } - - function test_ConstructorDisablesInitializers() public { - // Test that the constructor properly disables initializers - Superloop newContract = new Superloop(); - - // Should revert if we try to initialize directly - address[] memory modules = new address[](1); - modules[0] = address(supplyModule); - - DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ - asset: XTZ, - name: "Test Vault", - symbol: "TEST", - supplyCap: 1000 * 10 ** 18, - minimumDepositAmount: 100, - instantWithdrawFee: 100, - superloopModuleRegistry: address(moduleRegistry), - modules: modules, - accountant: mockModule, - withdrawManager: mockModule, - depositManager: mockModule, - cashReserve: 1000, - vaultAdmin: admin, - treasury: treasury, - vaultOperator: admin - }); - - vm.expectRevert(); - newContract.initialize(initData); - } - - // ============ SuperloopBase.sol Tests ============ - - function test_SetSupplyCapRevertIfNotAdmin() public { - uint256 newSupplyCap = 50000 * 10 ** 18; - - vm.prank(user1); - vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); - superloop.setSupplyCap(newSupplyCap); - } - - function test_SetSuperloopModuleRegistry() public { - address newRegistry = makeAddr("newRegistry"); - - vm.prank(admin); - superloop.setSuperloopModuleRegistry(newRegistry); - - // Note: We can't directly test the storage change, but we can verify it doesn't revert - } - - function test_SetSuperloopModuleRegistryRevertIfNotAdmin() public { - address newRegistry = makeAddr("newRegistry"); - - vm.prank(user1); - vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); - superloop.setSuperloopModuleRegistry(newRegistry); - } - - function test_SetRegisteredModule() public { - address newModule = makeAddr("newModule"); - - vm.prank(admin); - superloop.setRegisteredModule(newModule, true); - - // Note: We can't directly test the storage change, but we can verify it doesn't revert - } - - function test_SetRegisteredModuleRevertIfNotAdmin() public { - address newModule = makeAddr("newModule"); - - vm.prank(user1); - vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); - superloop.setRegisteredModule(newModule, true); - } - - function test_SetCallbackHandler() public { - bytes32 key = keccak256(abi.encodePacked(address(0x123), bytes4(0x12345678))); - address handler = makeAddr("handler"); - - vm.prank(admin); - superloop.setCallbackHandler(key, handler); - - // Note: We can't directly test the storage change, but we can verify it doesn't revert - } +// // SPDX-License-Identifier: MIT + +// pragma solidity ^0.8.13; + +// import {console} from "forge-std/console.sol"; +// import {TestBase} from "../TestBase.sol"; +// import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; +// import {DataTypes} from "../../../src/common/DataTypes.sol"; +// import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +// import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +// import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +// import {Errors} from "../../../src/common/Errors.sol"; + +// contract SuperloopTest is TestBase { +// Superloop public superloopImplementation; +// ProxyAdmin public proxyAdmin; +// address public user1; +// address public user2; + +// function setUp() public override { +// super.setUp(); + +// user1 = makeAddr("user1"); +// user2 = makeAddr("user2"); + +// vm.startPrank(admin); +// _deployModules(); + +// address[] memory modules = new address[](5); +// modules[0] = address(supplyModule); +// modules[1] = address(withdrawModule); +// modules[2] = address(borrowModule); +// modules[3] = address(repayModule); +// modules[4] = address(dexModule); + +// DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ +// asset: XTZ, +// name: "XTZ Vault", +// symbol: "XTZV", +// supplyCap: 100000 * 10 ** 18, +// minimumDepositAmount: 100, +// instantWithdrawFee: 0, +// superloopModuleRegistry: address(moduleRegistry), +// modules: modules, +// accountant: mockModule, +// withdrawManager: mockModule, +// depositManager: mockModule, +// cashReserve: 1000, +// vaultAdmin: admin, +// treasury: treasury, +// vaultOperator: admin +// }); +// superloopImplementation = new Superloop(); +// proxyAdmin = new ProxyAdmin(address(this)); + +// TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( +// address(superloopImplementation), +// address(proxyAdmin), +// abi.encodeWithSelector(Superloop.initialize.selector, initData) +// ); +// superloop = Superloop(payable(address(proxy))); + +// _deployAccountant(address(superloop), _singleAddressArray(ST_XTZ), _singleAddressArray(XTZ)); +// _deployWithdrawManager(address(superloop)); + +// superloop.setAccountantModule(address(accountantAaveV3)); +// superloop.setWithdrawManagerModule(address(withdrawManager)); + +// vm.stopPrank(); + +// vm.label(address(superloop), "superloop"); +// vm.label(user1, "user1"); +// vm.label(user2, "user2"); +// } + +// // ============ Superloop.sol Tests ============ + +// function test_Initialize() public { +// // Test that initialization works correctly +// address[] memory modules = new address[](1); +// modules[0] = address(supplyModule); + +// DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ +// asset: XTZ, +// name: "Test Vault", +// symbol: "TEST", +// supplyCap: 1000 * 10 ** 18, +// minimumDepositAmount: 100, +// instantWithdrawFee: 0, +// superloopModuleRegistry: address(moduleRegistry), +// modules: modules, +// accountant: mockModule, +// withdrawManager: mockModule, +// depositManager: mockModule, +// cashReserve: 1000, +// vaultAdmin: admin, +// treasury: treasury, +// vaultOperator: admin +// }); + +// Superloop newImplementation = new Superloop(); +// ProxyAdmin newProxyAdmin = new ProxyAdmin(address(this)); + +// TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( +// address(newImplementation), +// address(newProxyAdmin), +// abi.encodeWithSelector(Superloop.initialize.selector, initData) +// ); + +// Superloop newSuperloop = Superloop(payable(address(proxy))); + +// // Test that the contract is properly initialized +// assertEq(newSuperloop.name(), "Test Vault"); +// assertEq(newSuperloop.symbol(), "TEST"); +// assertEq(newSuperloop.asset(), XTZ); +// } + +// function test_InitializeRevertIfAlreadyInitialized() public { +// // Test that initialize reverts if called again +// address[] memory modules = new address[](1); +// modules[0] = address(supplyModule); + +// DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ +// asset: XTZ, +// name: "Test Vault", +// symbol: "TEST", +// supplyCap: 1000 * 10 ** 18, +// minimumDepositAmount: 100, +// instantWithdrawFee: 0, +// superloopModuleRegistry: address(moduleRegistry), +// modules: modules, +// accountant: mockModule, +// withdrawManager: mockModule, +// depositManager: mockModule, +// cashReserve: 1000, +// vaultAdmin: admin, +// treasury: treasury, +// vaultOperator: admin +// }); + +// vm.expectRevert(); +// superloop.initialize(initData); +// } + +// function test_InitializeRevertIfInvalidModule() public { +// // Test that initialization reverts if module is not whitelisted +// address[] memory modules = new address[](1); +// modules[0] = address(0x123); // Invalid module + +// DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ +// asset: XTZ, +// name: "Test Vault", +// symbol: "TEST", +// supplyCap: 1000 * 10 ** 18, +// minimumDepositAmount: 100, +// instantWithdrawFee: 0, +// superloopModuleRegistry: address(moduleRegistry), +// modules: modules, +// accountant: mockModule, +// withdrawManager: mockModule, +// depositManager: mockModule, +// cashReserve: 1000, +// vaultAdmin: admin, +// treasury: treasury, +// vaultOperator: admin +// }); + +// Superloop newImplementation = new Superloop(); +// ProxyAdmin newProxyAdmin = new ProxyAdmin(address(this)); + +// vm.expectRevert(bytes(Errors.INVALID_MODULE)); +// new TransparentUpgradeableProxy( +// address(newImplementation), +// address(newProxyAdmin), +// abi.encodeWithSelector(Superloop.initialize.selector, initData) +// ); +// } + +// function test_ConstructorDisablesInitializers() public { +// // Test that the constructor properly disables initializers +// Superloop newContract = new Superloop(); + +// // Should revert if we try to initialize directly +// address[] memory modules = new address[](1); +// modules[0] = address(supplyModule); + +// DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ +// asset: XTZ, +// name: "Test Vault", +// symbol: "TEST", +// supplyCap: 1000 * 10 ** 18, +// minimumDepositAmount: 100, +// instantWithdrawFee: 100, +// superloopModuleRegistry: address(moduleRegistry), +// modules: modules, +// accountant: mockModule, +// withdrawManager: mockModule, +// depositManager: mockModule, +// cashReserve: 1000, +// vaultAdmin: admin, +// treasury: treasury, +// vaultOperator: admin +// }); + +// vm.expectRevert(); +// newContract.initialize(initData); +// } + +// // ============ SuperloopBase.sol Tests ============ + +// function test_SetSupplyCapRevertIfNotAdmin() public { +// uint256 newSupplyCap = 50000 * 10 ** 18; + +// vm.prank(user1); +// vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); +// superloop.setSupplyCap(newSupplyCap); +// } + +// function test_SetSuperloopModuleRegistry() public { +// address newRegistry = makeAddr("newRegistry"); + +// vm.prank(admin); +// superloop.setSuperloopModuleRegistry(newRegistry); + +// // Note: We can't directly test the storage change, but we can verify it doesn't revert +// } + +// function test_SetSuperloopModuleRegistryRevertIfNotAdmin() public { +// address newRegistry = makeAddr("newRegistry"); + +// vm.prank(user1); +// vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); +// superloop.setSuperloopModuleRegistry(newRegistry); +// } + +// function test_SetRegisteredModule() public { +// address newModule = makeAddr("newModule"); + +// vm.prank(admin); +// superloop.setRegisteredModule(newModule, true); + +// // Note: We can't directly test the storage change, but we can verify it doesn't revert +// } + +// function test_SetRegisteredModuleRevertIfNotAdmin() public { +// address newModule = makeAddr("newModule"); + +// vm.prank(user1); +// vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); +// superloop.setRegisteredModule(newModule, true); +// } + +// function test_SetCallbackHandler() public { +// bytes32 key = keccak256(abi.encodePacked(address(0x123), bytes4(0x12345678))); +// address handler = makeAddr("handler"); + +// vm.prank(admin); +// superloop.setCallbackHandler(key, handler); + +// // Note: We can't directly test the storage change, but we can verify it doesn't revert +// } - function test_SetCallbackHandlerRevertIfNotAdmin() public { - bytes32 key = keccak256(abi.encodePacked(address(0x123), bytes4(0x12345678))); - address handler = makeAddr("handler"); +// function test_SetCallbackHandlerRevertIfNotAdmin() public { +// bytes32 key = keccak256(abi.encodePacked(address(0x123), bytes4(0x12345678))); +// address handler = makeAddr("handler"); - vm.prank(user1); - vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); - superloop.setCallbackHandler(key, handler); - } +// vm.prank(user1); +// vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); +// superloop.setCallbackHandler(key, handler); +// } - function test_SetAccountantModule() public { - address newAccountant = makeAddr("newAccountant"); +// function test_SetAccountantModule() public { +// address newAccountant = makeAddr("newAccountant"); - vm.prank(admin); - superloop.setAccountantModule(newAccountant); +// vm.prank(admin); +// superloop.setAccountantModule(newAccountant); - // Note: We can't directly test the storage change, but we can verify it doesn't revert - } +// // Note: We can't directly test the storage change, but we can verify it doesn't revert +// } - function test_SetAccountantModuleRevertIfNotAdmin() public { - address newAccountant = makeAddr("newAccountant"); +// function test_SetAccountantModuleRevertIfNotAdmin() public { +// address newAccountant = makeAddr("newAccountant"); - vm.prank(user1); - vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); - superloop.setAccountantModule(newAccountant); - } +// vm.prank(user1); +// vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); +// superloop.setAccountantModule(newAccountant); +// } - function test_SetWithdrawManagerModule() public { - address newWithdrawManager = makeAddr("newWithdrawManager"); +// function test_SetWithdrawManagerModule() public { +// address newWithdrawManager = makeAddr("newWithdrawManager"); - vm.prank(admin); - superloop.setWithdrawManagerModule(newWithdrawManager); +// vm.prank(admin); +// superloop.setWithdrawManagerModule(newWithdrawManager); - // Note: We can't directly test the storage change, but we can verify it doesn't revert - } +// // Note: We can't directly test the storage change, but we can verify it doesn't revert +// } - function test_SetWithdrawManagerModuleRevertIfNotAdmin() public { - address newWithdrawManager = makeAddr("newWithdrawManager"); +// function test_SetWithdrawManagerModuleRevertIfNotAdmin() public { +// address newWithdrawManager = makeAddr("newWithdrawManager"); - vm.prank(user1); - vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); - superloop.setWithdrawManagerModule(newWithdrawManager); - } +// vm.prank(user1); +// vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); +// superloop.setWithdrawManagerModule(newWithdrawManager); +// } - function test_SetVaultAdmin() public { - address newAdmin = makeAddr("newAdmin"); +// function test_SetVaultAdmin() public { +// address newAdmin = makeAddr("newAdmin"); - vm.prank(admin); - superloop.setVaultAdmin(newAdmin); +// vm.prank(admin); +// superloop.setVaultAdmin(newAdmin); - // Test that new admin can call admin functions - vm.prank(newAdmin); - superloop.setSupplyCap(1000 * 10 ** 18); - } +// // Test that new admin can call admin functions +// vm.prank(newAdmin); +// superloop.setSupplyCap(1000 * 10 ** 18); +// } - function test_SetVaultAdminRevertIfNotAdmin() public { - address newAdmin = makeAddr("newAdmin"); +// function test_SetVaultAdminRevertIfNotAdmin() public { +// address newAdmin = makeAddr("newAdmin"); - vm.prank(user1); - vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); - superloop.setVaultAdmin(newAdmin); - } +// vm.prank(user1); +// vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); +// superloop.setVaultAdmin(newAdmin); +// } - function test_SetTreasury() public { - address newTreasury = makeAddr("newTreasury"); +// function test_SetTreasury() public { +// address newTreasury = makeAddr("newTreasury"); - vm.prank(admin); - superloop.setTreasury(newTreasury); +// vm.prank(admin); +// superloop.setTreasury(newTreasury); - // Note: We can't directly test the storage change, but we can verify it doesn't revert - } +// // Note: We can't directly test the storage change, but we can verify it doesn't revert +// } - function test_SetTreasuryRevertIfNotAdmin() public { - address newTreasury = makeAddr("newTreasury"); +// function test_SetTreasuryRevertIfNotAdmin() public { +// address newTreasury = makeAddr("newTreasury"); - vm.prank(user1); - vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); - superloop.setTreasury(newTreasury); - } +// vm.prank(user1); +// vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); +// superloop.setTreasury(newTreasury); +// } - function test_SetPrivilegedAddressRevertIfNotAdmin() public { - address privilegedUser = makeAddr("privilegedUser"); +// function test_SetPrivilegedAddressRevertIfNotAdmin() public { +// address privilegedUser = makeAddr("privilegedUser"); - vm.prank(user1); - vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); - superloop.setPrivilegedAddress(privilegedUser, true); - } +// vm.prank(user1); +// vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); +// superloop.setPrivilegedAddress(privilegedUser, true); +// } - function test_SetInstantWithdrawFeeRevertIfNotAdmin() public { - uint256 newInstantWithdrawFee = 100; +// function test_SetInstantWithdrawFeeRevertIfNotAdmin() public { +// uint256 newInstantWithdrawFee = 100; - vm.prank(user1); - vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); - superloop.setInstantWithdrawFee(newInstantWithdrawFee); - } +// vm.prank(user1); +// vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); +// superloop.setInstantWithdrawFee(newInstantWithdrawFee); +// } - function test_SetInstantWithdrawFeeRevertIfInvalidFee() public { - uint256 newInstantWithdrawFee = 1000; +// function test_SetInstantWithdrawFeeRevertIfInvalidFee() public { +// uint256 newInstantWithdrawFee = 1000; - vm.prank(admin); - vm.expectRevert(bytes(Errors.INVALID_INSTANT_WITHDRAW_FEE)); - superloop.setInstantWithdrawFee(newInstantWithdrawFee); - } +// vm.prank(admin); +// vm.expectRevert(bytes(Errors.INVALID_INSTANT_WITHDRAW_FEE)); +// superloop.setInstantWithdrawFee(newInstantWithdrawFee); +// } - function test_SetInstantWithdrawFee() public { - uint256 newInstantWithdrawFee = 100; +// function test_SetInstantWithdrawFee() public { +// uint256 newInstantWithdrawFee = 100; - vm.prank(admin); - superloop.setInstantWithdrawFee(newInstantWithdrawFee); +// vm.prank(admin); +// superloop.setInstantWithdrawFee(newInstantWithdrawFee); - assertEq(superloop.instantWithdrawFee(), newInstantWithdrawFee); - } +// assertEq(superloop.instantWithdrawFee(), newInstantWithdrawFee); +// } - // ============ SuperloopVault.sol Tests ============ +// // ============ SuperloopVault.sol Tests ============ - function test_TotalAssets() public { - // Mock the accountant module to return a specific value - vm.mockCall( - address(accountantAaveV3), - abi.encodeWithSelector(accountantAaveV3.getTotalAssets.selector), - abi.encode(1000 * 10 ** 18) - ); - - uint256 totalAssets = superloop.totalAssets(); - assertEq(totalAssets, 1000 * 10 ** 18); - } +// function test_TotalAssets() public { +// // Mock the accountant module to return a specific value +// vm.mockCall( +// address(accountantAaveV3), +// abi.encodeWithSelector(accountantAaveV3.getTotalAssets.selector), +// abi.encode(1000 * 10 ** 18) +// ); + +// uint256 totalAssets = superloop.totalAssets(); +// assertEq(totalAssets, 1000 * 10 ** 18); +// } - function test_MaxDepositWithSupplyCap() public { - // Set a supply cap - vm.prank(admin); - superloop.setSupplyCap(1000 * 10 ** 18); +// function test_MaxDepositWithSupplyCap() public { +// // Set a supply cap +// vm.prank(admin); +// superloop.setSupplyCap(1000 * 10 ** 18); - // Mock total assets to be less than supply cap - vm.mockCall( - address(accountantAaveV3), - abi.encodeWithSelector(accountantAaveV3.getTotalAssets.selector), - abi.encode(500 * 10 ** 18) - ); - - uint256 maxDeposit = superloop.maxDeposit(address(0)); - assertEq(maxDeposit, 500 * 10 ** 18); - } - - function test_MaxDepositExceedsSupplyCap() public { - // Set a supply cap - vm.prank(admin); - superloop.setSupplyCap(1000 * 10 ** 18); - - // Mock total assets to exceed supply cap - vm.mockCall( - address(accountantAaveV3), - abi.encodeWithSelector(accountantAaveV3.getTotalAssets.selector), - abi.encode(1500 * 10 ** 18) - ); - - uint256 maxDeposit = superloop.maxDeposit(address(0)); - assertEq(maxDeposit, 0); - } - - function test_DecimalsOffset() public view { - // Test that decimals offset is correctly applied - // The offset should be 2 as defined in SuperloopStorage - assertEq(superloop.decimals(), 20); // Standard ERC20 decimals - } - - // ============ Integration Tests ============ - - function test_skimRevertIfInvalidAsset() public { - deal(XTZ, address(superloop), 1000 * 10 ** 18); - - vm.expectRevert(bytes(Errors.INVALID_SKIM_ASSET)); - - vm.prank(admin); - superloop.skim(XTZ); - } - - function test_skim() public { - deal(ST_XTZ, address(superloop), 1000 * 10 ** 18); - - vm.prank(admin); - superloop.skim(ST_XTZ); - - assertEq(IERC20(ST_XTZ).balanceOf(treasury), 1000 * 10 ** 18); - assertEq(IERC20(ST_XTZ).balanceOf(address(superloop)), 0); - } - - function _seed() internal { - vm.startPrank(admin); - deal(XTZ, admin, 1000 * 10 ** 18); - IERC20(XTZ).approve(address(superloop), type(uint256).max); - superloop.deposit(100 * 10 ** 18, admin); - vm.stopPrank(); - } -} +// // Mock total assets to be less than supply cap +// vm.mockCall( +// address(accountantAaveV3), +// abi.encodeWithSelector(accountantAaveV3.getTotalAssets.selector), +// abi.encode(500 * 10 ** 18) +// ); + +// uint256 maxDeposit = superloop.maxDeposit(address(0)); +// assertEq(maxDeposit, 500 * 10 ** 18); +// } + +// function test_MaxDepositExceedsSupplyCap() public { +// // Set a supply cap +// vm.prank(admin); +// superloop.setSupplyCap(1000 * 10 ** 18); + +// // Mock total assets to exceed supply cap +// vm.mockCall( +// address(accountantAaveV3), +// abi.encodeWithSelector(accountantAaveV3.getTotalAssets.selector), +// abi.encode(1500 * 10 ** 18) +// ); + +// uint256 maxDeposit = superloop.maxDeposit(address(0)); +// assertEq(maxDeposit, 0); +// } + +// function test_DecimalsOffset() public view { +// // Test that decimals offset is correctly applied +// // The offset should be 2 as defined in SuperloopStorage +// assertEq(superloop.decimals(), 20); // Standard ERC20 decimals +// } + +// // ============ Integration Tests ============ + +// function test_skimRevertIfInvalidAsset() public { +// deal(XTZ, address(superloop), 1000 * 10 ** 18); + +// vm.expectRevert(bytes(Errors.INVALID_SKIM_ASSET)); + +// vm.prank(admin); +// superloop.skim(XTZ); +// } + +// function test_skim() public { +// deal(ST_XTZ, address(superloop), 1000 * 10 ** 18); + +// vm.prank(admin); +// superloop.skim(ST_XTZ); + +// assertEq(IERC20(ST_XTZ).balanceOf(treasury), 1000 * 10 ** 18); +// assertEq(IERC20(ST_XTZ).balanceOf(address(superloop)), 0); +// } + +// function _seed() internal { +// vm.startPrank(admin); +// deal(XTZ, admin, 1000 * 10 ** 18); +// IERC20(XTZ).approve(address(superloop), type(uint256).max); +// superloop.deposit(100 * 10 ** 18, admin); +// vm.stopPrank(); +// } +// } diff --git a/test/core/unit/UniversalAccountant.t.sol b/test/core/unit/UniversalAccountant.t.sol index fd8e70a..5e4137e 100644 --- a/test/core/unit/UniversalAccountant.t.sol +++ b/test/core/unit/UniversalAccountant.t.sol @@ -1,267 +1,267 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.13; - -import {TestBase} from "../TestBase.sol"; -import {console} from "forge-std/console.sol"; -import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -import {MockVault} from "../../../src/mock/MockVault.sol"; -import {UniversalAccountant} from "../../../src/core/Accountant/universalAccountant/UniversalAccountant.sol"; -import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -import {DataTypes} from "../../../src/common/DataTypes.sol"; -import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; -import {IPoolAddressesProvider} from "aave-v3-core/contracts/interfaces/IPoolAddressesProvider.sol"; -import {IPoolDataProvider} from "aave-v3-core/contracts/interfaces/IPoolDataProvider.sol"; -import {IAaveOracle} from "aave-v3-core/contracts/interfaces/IAaveOracle.sol"; -import {IPriceOracleGetter} from "aave-v3-core/contracts/interfaces/IPriceOracleGetter.sol"; -import {AaveV3AccountantPlugin} from "../../../src/plugins/Accountant/AaveV3AccountantPlugin.sol"; - -contract AccountantAaveV3Test is TestBase { - UniversalAccountant public accountantImplementation; - AaveV3AccountantPlugin public accountantPlugin; - - IERC20 public asset; - MockVault public vault; - - // Test data - uint256 public constant INITIAL_WHALE_BALANCE = 1000 ether; - - function setUp() public override { - super.setUp(); - - asset = IERC20(XTZ); - vault = new MockVault(asset, "Mock Vault", "mVLT"); - - address[] memory lendAssets = new address[](1); - lendAssets[0] = ST_XTZ; - address[] memory borrowAssets = new address[](1); - borrowAssets[0] = XTZ; - - // deploy accountant plugin - DataTypes.AaveV3AccountantPluginModuleInitData memory accountantPluginInitData = DataTypes - .AaveV3AccountantPluginModuleInitData({ - poolAddressesProvider: AAVE_V3_POOL_ADDRESSES_PROVIDER, - lendAssets: lendAssets, - borrowAssets: borrowAssets - }); - accountantPlugin = new AaveV3AccountantPlugin(accountantPluginInitData); - - address[] memory registeredAccountants = new address[](1); - registeredAccountants[0] = address(accountantPlugin); - - // deploy accountant - DataTypes.UniversalAccountantModuleInitData memory initData = DataTypes.UniversalAccountantModuleInitData({ - registeredAccountants: registeredAccountants, - performanceFee: uint16(PERFORMANCE_FEE), - vault: address(vault) - }); - - accountantImplementation = new UniversalAccountant(); - TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( - address(accountantImplementation), - address(this), - abi.encodeWithSelector(UniversalAccountant.initialize.selector, initData) - ); - - accountant = UniversalAccountant(address(proxy)); - - // Fund the accountant with XTZ from whale - vm.startPrank(XTZ_WHALE); - asset.transfer(address(vault), INITIAL_WHALE_BALANCE); - vm.stopPrank(); - } - - function test_Initialize() public view { - assertEq(accountant.getTotalAssets(), INITIAL_WHALE_BALANCE); - assertEq(accountant.performanceFee(), PERFORMANCE_FEE); - assertEq(accountant.vault(), address(vault)); - assertEq(accountant.registeredAccountants()[0], address(accountantPlugin)); - } - - function test_InitializeRevertIfAlreadyInitialized() public { - // Test that initialize reverts if called again - address[] memory lendAssets = new address[](1); - lendAssets[0] = ST_XTZ; - address[] memory borrowAssets = new address[](1); - borrowAssets[0] = XTZ; - - address[] memory registeredAccountants = new address[](1); - registeredAccountants[0] = address(accountantPlugin); - - DataTypes.UniversalAccountantModuleInitData memory initData = DataTypes.UniversalAccountantModuleInitData({ - registeredAccountants: registeredAccountants, - performanceFee: uint16(PERFORMANCE_FEE), - vault: address(vault) - }); - - vm.expectRevert(); - accountant.initialize(initData); - } - - function test_GetTotalAssets() public view { - // Test getTotalAssets calculation using actual fork data - uint256 totalAssets = accountant.getTotalAssets(); - - // Should return the base asset balance since there are no lend/borrow positions yet - assertEq(totalAssets, INITIAL_WHALE_BALANCE); - - console.log("Total assets:", totalAssets); - console.log("Expected assets:", INITIAL_WHALE_BALANCE); - } - - function test_GetTotalAssetsWithMultipleAssets() public { - // mock pool data provider and price oracle - _enableMockingPoolDataProvider(); - _enableMockingPriceOracle(1.01e8, 1e8); - - uint256 totalAssets = accountant.getTotalAssets(); - - // Calculate expected total assets based on mock data: - // Lend assets: 1000 ether * 1.01e8 (ST_XTZ price) = 1010e8 - // Borrow assets: -800 ether * 1e8 (XTZ price) = -800e8 - // Base asset balance: 1000 ether * 1e8 (XTZ price) = 1000e8 - // Total in market reference currency: 1010e8 - 800e8 + 1000e8 = 1210e8 - // Convert to base asset: 1210e18 / 1e18 = 1210 ether - uint256 expectedTotalAssets = 1210 ether; - - assertEq(totalAssets, expectedTotalAssets); - console.log("Total assets with multiple assets:", totalAssets); - console.log("Expected total assets:", expectedTotalAssets); - } - - function test_GetPerformanceFee() public { - uint256 totalShares = 1000 ether; - uint256 exchangeRate = 1.1e18; // 10% increase - uint256 lastRealizedFeeExchangeRate = 1.0e18; - uint256 totalSupply = 1000 ether; - - // Set the last realized fee exchange rate - vm.prank(address(vault)); - accountant.setLastRealizedFeeExchangeRate(lastRealizedFeeExchangeRate, totalSupply); - - // Calculate expected performance fee - uint256 latestAssetAmount = totalShares * exchangeRate; - uint256 prevAssetAmount = totalShares * lastRealizedFeeExchangeRate; - uint256 interestGenerated = latestAssetAmount - prevAssetAmount; - uint256 expectedPerformanceFee = (interestGenerated * PERFORMANCE_FEE) / (10000 * 1e18); - - vm.prank(address(vault)); - uint256 actualPerformanceFee = accountant.getPerformanceFee(totalShares, exchangeRate, 18); - - console.log("Performance fee:", actualPerformanceFee); - console.log("Expected fee:", expectedPerformanceFee); - - assertEq(actualPerformanceFee, expectedPerformanceFee); - } - - function test_GetPerformanceFeeNoInterest() public { - uint256 totalShares = 1000 ether; - uint256 exchangeRate = 0.9e18; // 10% decrease - uint256 lastRealizedFeeExchangeRate = 1.0e18; - uint256 totalSupply = 1000 ether; - // Set the last realized fee exchange rate - vm.prank(address(vault)); - accountant.setLastRealizedFeeExchangeRate(lastRealizedFeeExchangeRate, totalSupply); - - vm.prank(address(vault)); - uint256 performanceFee = accountant.getPerformanceFee(totalShares, exchangeRate, 18); - - // Should return 0 when there's no interest (exchange rate decreased) - assertEq(performanceFee, 0); - } - - function test_GetPerformanceFeeRevertIfNotVault() public { - uint256 totalShares = 1000 ether; - uint256 exchangeRate = 1.1e18; - - vm.expectRevert(); - accountant.getPerformanceFee(totalShares, exchangeRate, 18); - } - - function test_SetLastRealizedFeeExchangeRate() public { - uint256 newExchangeRate = 1.2e18; - uint256 totalSupply = 1000 ether; - - vm.prank(address(vault)); - accountant.setLastRealizedFeeExchangeRate(newExchangeRate, totalSupply); - - // Test that the exchange rate was set correctly by calling getPerformanceFee - vm.prank(address(vault)); - uint256 performanceFee = accountant.getPerformanceFee(1000 ether, 1.3e18, 18); - - // Should calculate based on the new exchange rate - uint256 expectedInterest = 1000 ether * 1.3e18 - 1000 ether * newExchangeRate; - uint256 expectedFee = (expectedInterest * PERFORMANCE_FEE) / (10000 * 1e18); - - assertEq(performanceFee, expectedFee); - } - - function test_SetLastRealizedFeeExchangeRateRevertIfNotVault() public { - uint256 newExchangeRate = 1.2e18; - uint256 totalSupply = 1000 ether; - - vm.expectRevert(); - accountant.setLastRealizedFeeExchangeRate(newExchangeRate, totalSupply); - } - - function test_GetTotalAssetsWithZeroBalances() public view { - // Test getTotalAssets when there are no lend/borrow positions - // This should be the default state since we haven't interacted with Aave V3 - uint256 totalAssets = accountant.getTotalAssets(); - - // Should only include base asset balance - assertEq(totalAssets, INITIAL_WHALE_BALANCE); - } - - function test_PerformanceFeeCalculationEdgeCases() public { - // Test performance fee calculation with edge cases - - // Case 1: Zero shares - vm.prank(address(vault)); - accountant.setLastRealizedFeeExchangeRate(1.0e18, 1000 ether); - - vm.prank(address(vault)); - uint256 fee1 = accountant.getPerformanceFee(0, 1.1e18, 18); - assertEq(fee1, 0); - - // Case 2: Same exchange rate (no change) - vm.prank(address(vault)); - uint256 fee2 = accountant.getPerformanceFee(1000 ether, 1.0e18, 18); - assertEq(fee2, 0); - - // Case 3: Very small interest - vm.prank(address(vault)); - uint256 fee3 = accountant.getPerformanceFee(1000 ether, 1.0001e18, 18); - assertGt(fee3, 0); - - console.log("Small interest fee:", fee3); - } - - function _enableMockingPoolDataProvider() internal { - vm.mockCall( - AAVE_V3_POOL_DATA_PROVIDER, - abi.encodeWithSelector(IPoolDataProvider.getUserReserveData.selector, ST_XTZ, address(vault)), - abi.encode(1000 * 10 ** 6, 0, 0, 0, 0, 0, 0, 0, 0) - ); - - vm.mockCall( - AAVE_V3_POOL_DATA_PROVIDER, - abi.encodeWithSelector(IPoolDataProvider.getUserReserveData.selector, XTZ, address(vault)), - abi.encode(0, 0, 800 ether, 0, 0, 0, 0, 0, 0) - ); - } - - function _enableMockingPriceOracle(uint256 stXtzPrice, uint256 xtzPrice) internal { - vm.mockCall( - AAVE_V3_PRICE_ORACLE, - abi.encodeWithSelector(IPriceOracleGetter.getAssetPrice.selector, ST_XTZ), - abi.encode(stXtzPrice) - ); - - vm.mockCall( - AAVE_V3_PRICE_ORACLE, - abi.encodeWithSelector(IPriceOracleGetter.getAssetPrice.selector, XTZ), - abi.encode(xtzPrice) - ); - } -} +// // SPDX-License-Identifier: MIT + +// pragma solidity ^0.8.13; + +// import {TestBase} from "../TestBase.sol"; +// import {console} from "forge-std/console.sol"; +// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +// import {MockVault} from "../../../src/mock/MockVault.sol"; +// import {UniversalAccountant} from "../../../src/core/Accountant/universalAccountant/UniversalAccountant.sol"; +// import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +// import {DataTypes} from "../../../src/common/DataTypes.sol"; +// import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +// import {IPoolAddressesProvider} from "aave-v3-core/contracts/interfaces/IPoolAddressesProvider.sol"; +// import {IPoolDataProvider} from "aave-v3-core/contracts/interfaces/IPoolDataProvider.sol"; +// import {IAaveOracle} from "aave-v3-core/contracts/interfaces/IAaveOracle.sol"; +// import {IPriceOracleGetter} from "aave-v3-core/contracts/interfaces/IPriceOracleGetter.sol"; +// import {AaveV3AccountantPlugin} from "../../../src/plugins/Accountant/AaveV3AccountantPlugin.sol"; + +// contract AccountantAaveV3Test is TestBase { +// UniversalAccountant public accountantImplementation; +// AaveV3AccountantPlugin public accountantPlugin; + +// IERC20 public asset; +// MockVault public vault; + +// // Test data +// uint256 public constant INITIAL_WHALE_BALANCE = 1000 ether; + +// function setUp() public override { +// super.setUp(); + +// asset = IERC20(XTZ); +// vault = new MockVault(asset, "Mock Vault", "mVLT"); + +// address[] memory lendAssets = new address[](1); +// lendAssets[0] = ST_XTZ; +// address[] memory borrowAssets = new address[](1); +// borrowAssets[0] = XTZ; + +// // deploy accountant plugin +// DataTypes.AaveV3AccountantPluginModuleInitData memory accountantPluginInitData = DataTypes +// .AaveV3AccountantPluginModuleInitData({ +// poolAddressesProvider: AAVE_V3_POOL_ADDRESSES_PROVIDER, +// lendAssets: lendAssets, +// borrowAssets: borrowAssets +// }); +// accountantPlugin = new AaveV3AccountantPlugin(accountantPluginInitData); + +// address[] memory registeredAccountants = new address[](1); +// registeredAccountants[0] = address(accountantPlugin); + +// // deploy accountant +// DataTypes.UniversalAccountantModuleInitData memory initData = DataTypes.UniversalAccountantModuleInitData({ +// registeredAccountants: registeredAccountants, +// performanceFee: uint16(PERFORMANCE_FEE), +// vault: address(vault) +// }); + +// accountantImplementation = new UniversalAccountant(); +// TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( +// address(accountantImplementation), +// address(this), +// abi.encodeWithSelector(UniversalAccountant.initialize.selector, initData) +// ); + +// accountant = UniversalAccountant(address(proxy)); + +// // Fund the accountant with XTZ from whale +// vm.startPrank(XTZ_WHALE); +// asset.transfer(address(vault), INITIAL_WHALE_BALANCE); +// vm.stopPrank(); +// } + +// function test_Initialize() public view { +// assertEq(accountant.getTotalAssets(), INITIAL_WHALE_BALANCE); +// assertEq(accountant.performanceFee(), PERFORMANCE_FEE); +// assertEq(accountant.vault(), address(vault)); +// assertEq(accountant.registeredAccountants()[0], address(accountantPlugin)); +// } + +// function test_InitializeRevertIfAlreadyInitialized() public { +// // Test that initialize reverts if called again +// address[] memory lendAssets = new address[](1); +// lendAssets[0] = ST_XTZ; +// address[] memory borrowAssets = new address[](1); +// borrowAssets[0] = XTZ; + +// address[] memory registeredAccountants = new address[](1); +// registeredAccountants[0] = address(accountantPlugin); + +// DataTypes.UniversalAccountantModuleInitData memory initData = DataTypes.UniversalAccountantModuleInitData({ +// registeredAccountants: registeredAccountants, +// performanceFee: uint16(PERFORMANCE_FEE), +// vault: address(vault) +// }); + +// vm.expectRevert(); +// accountant.initialize(initData); +// } + +// function test_GetTotalAssets() public view { +// // Test getTotalAssets calculation using actual fork data +// uint256 totalAssets = accountant.getTotalAssets(); + +// // Should return the base asset balance since there are no lend/borrow positions yet +// assertEq(totalAssets, INITIAL_WHALE_BALANCE); + +// console.log("Total assets:", totalAssets); +// console.log("Expected assets:", INITIAL_WHALE_BALANCE); +// } + +// function test_GetTotalAssetsWithMultipleAssets() public { +// // mock pool data provider and price oracle +// _enableMockingPoolDataProvider(); +// _enableMockingPriceOracle(1.01e8, 1e8); + +// uint256 totalAssets = accountant.getTotalAssets(); + +// // Calculate expected total assets based on mock data: +// // Lend assets: 1000 ether * 1.01e8 (ST_XTZ price) = 1010e8 +// // Borrow assets: -800 ether * 1e8 (XTZ price) = -800e8 +// // Base asset balance: 1000 ether * 1e8 (XTZ price) = 1000e8 +// // Total in market reference currency: 1010e8 - 800e8 + 1000e8 = 1210e8 +// // Convert to base asset: 1210e18 / 1e18 = 1210 ether +// uint256 expectedTotalAssets = 1210 ether; + +// assertEq(totalAssets, expectedTotalAssets); +// console.log("Total assets with multiple assets:", totalAssets); +// console.log("Expected total assets:", expectedTotalAssets); +// } + +// function test_GetPerformanceFee() public { +// uint256 totalShares = 1000 ether; +// uint256 exchangeRate = 1.1e18; // 10% increase +// uint256 lastRealizedFeeExchangeRate = 1.0e18; +// uint256 totalSupply = 1000 ether; + +// // Set the last realized fee exchange rate +// vm.prank(address(vault)); +// accountant.setLastRealizedFeeExchangeRate(lastRealizedFeeExchangeRate, totalSupply); + +// // Calculate expected performance fee +// uint256 latestAssetAmount = totalShares * exchangeRate; +// uint256 prevAssetAmount = totalShares * lastRealizedFeeExchangeRate; +// uint256 interestGenerated = latestAssetAmount - prevAssetAmount; +// uint256 expectedPerformanceFee = (interestGenerated * PERFORMANCE_FEE) / (10000 * 1e18); + +// vm.prank(address(vault)); +// uint256 actualPerformanceFee = accountant.getPerformanceFee(totalShares, exchangeRate, 18); + +// console.log("Performance fee:", actualPerformanceFee); +// console.log("Expected fee:", expectedPerformanceFee); + +// assertEq(actualPerformanceFee, expectedPerformanceFee); +// } + +// function test_GetPerformanceFeeNoInterest() public { +// uint256 totalShares = 1000 ether; +// uint256 exchangeRate = 0.9e18; // 10% decrease +// uint256 lastRealizedFeeExchangeRate = 1.0e18; +// uint256 totalSupply = 1000 ether; +// // Set the last realized fee exchange rate +// vm.prank(address(vault)); +// accountant.setLastRealizedFeeExchangeRate(lastRealizedFeeExchangeRate, totalSupply); + +// vm.prank(address(vault)); +// uint256 performanceFee = accountant.getPerformanceFee(totalShares, exchangeRate, 18); + +// // Should return 0 when there's no interest (exchange rate decreased) +// assertEq(performanceFee, 0); +// } + +// function test_GetPerformanceFeeRevertIfNotVault() public { +// uint256 totalShares = 1000 ether; +// uint256 exchangeRate = 1.1e18; + +// vm.expectRevert(); +// accountant.getPerformanceFee(totalShares, exchangeRate, 18); +// } + +// function test_SetLastRealizedFeeExchangeRate() public { +// uint256 newExchangeRate = 1.2e18; +// uint256 totalSupply = 1000 ether; + +// vm.prank(address(vault)); +// accountant.setLastRealizedFeeExchangeRate(newExchangeRate, totalSupply); + +// // Test that the exchange rate was set correctly by calling getPerformanceFee +// vm.prank(address(vault)); +// uint256 performanceFee = accountant.getPerformanceFee(1000 ether, 1.3e18, 18); + +// // Should calculate based on the new exchange rate +// uint256 expectedInterest = 1000 ether * 1.3e18 - 1000 ether * newExchangeRate; +// uint256 expectedFee = (expectedInterest * PERFORMANCE_FEE) / (10000 * 1e18); + +// assertEq(performanceFee, expectedFee); +// } + +// function test_SetLastRealizedFeeExchangeRateRevertIfNotVault() public { +// uint256 newExchangeRate = 1.2e18; +// uint256 totalSupply = 1000 ether; + +// vm.expectRevert(); +// accountant.setLastRealizedFeeExchangeRate(newExchangeRate, totalSupply); +// } + +// function test_GetTotalAssetsWithZeroBalances() public view { +// // Test getTotalAssets when there are no lend/borrow positions +// // This should be the default state since we haven't interacted with Aave V3 +// uint256 totalAssets = accountant.getTotalAssets(); + +// // Should only include base asset balance +// assertEq(totalAssets, INITIAL_WHALE_BALANCE); +// } + +// function test_PerformanceFeeCalculationEdgeCases() public { +// // Test performance fee calculation with edge cases + +// // Case 1: Zero shares +// vm.prank(address(vault)); +// accountant.setLastRealizedFeeExchangeRate(1.0e18, 1000 ether); + +// vm.prank(address(vault)); +// uint256 fee1 = accountant.getPerformanceFee(0, 1.1e18, 18); +// assertEq(fee1, 0); + +// // Case 2: Same exchange rate (no change) +// vm.prank(address(vault)); +// uint256 fee2 = accountant.getPerformanceFee(1000 ether, 1.0e18, 18); +// assertEq(fee2, 0); + +// // Case 3: Very small interest +// vm.prank(address(vault)); +// uint256 fee3 = accountant.getPerformanceFee(1000 ether, 1.0001e18, 18); +// assertGt(fee3, 0); + +// console.log("Small interest fee:", fee3); +// } + +// function _enableMockingPoolDataProvider() internal { +// vm.mockCall( +// AAVE_V3_POOL_DATA_PROVIDER, +// abi.encodeWithSelector(IPoolDataProvider.getUserReserveData.selector, ST_XTZ, address(vault)), +// abi.encode(1000 * 10 ** 6, 0, 0, 0, 0, 0, 0, 0, 0) +// ); + +// vm.mockCall( +// AAVE_V3_POOL_DATA_PROVIDER, +// abi.encodeWithSelector(IPoolDataProvider.getUserReserveData.selector, XTZ, address(vault)), +// abi.encode(0, 0, 800 ether, 0, 0, 0, 0, 0, 0) +// ); +// } + +// function _enableMockingPriceOracle(uint256 stXtzPrice, uint256 xtzPrice) internal { +// vm.mockCall( +// AAVE_V3_PRICE_ORACLE, +// abi.encodeWithSelector(IPriceOracleGetter.getAssetPrice.selector, ST_XTZ), +// abi.encode(stXtzPrice) +// ); + +// vm.mockCall( +// AAVE_V3_PRICE_ORACLE, +// abi.encodeWithSelector(IPriceOracleGetter.getAssetPrice.selector, XTZ), +// abi.encode(xtzPrice) +// ); +// } +// } diff --git a/test/core/unit/WithdrawManager.t.sol b/test/core/unit/WithdrawManager.t.sol index c5a1810..8e5ead0 100644 --- a/test/core/unit/WithdrawManager.t.sol +++ b/test/core/unit/WithdrawManager.t.sol @@ -1,389 +1,389 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.13; - -import {TestBase} from "../TestBase.sol"; -import {console} from "forge-std/console.sol"; -import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; -import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; -import {TransparentUpgradeableProxy} from - "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -import {DataTypes} from "../../../src/common/DataTypes.sol"; -import {Errors} from "../../../src/common/Errors.sol"; -import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -import {WithdrawManager} from "../../../src/core/WithdrawManager/WithdrawManager.sol"; - -contract WithdrawManagerTest is TestBase { - Superloop public superloopImplementation; - ProxyAdmin public proxyAdmin; - address public user1; - address public user2; - - function setUp() public override { - super.setUp(); - - user1 = makeAddr("user1"); - user2 = makeAddr("user2"); - - vm.startPrank(admin); - _deployModules(); - - address[] memory modules = new address[](5); - modules[0] = address(supplyModule); - modules[1] = address(withdrawModule); - modules[2] = address(borrowModule); - modules[3] = address(repayModule); - modules[4] = address(dexModule); - - DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ - asset: XTZ, - name: "XTZ Vault", - symbol: "XTZV", - supplyCap: 100000 * 10 ** 18, - minimumDepositAmount: 100, - instantWithdrawFee: 0, - superloopModuleRegistry: address(moduleRegistry), - modules: modules, - accountant: mockModule, - withdrawManager: mockModule, - depositManager: mockModule, - cashReserve: 1000, - vaultAdmin: admin, - treasury: treasury, - vaultOperator: admin - }); - superloopImplementation = new Superloop(); - proxyAdmin = new ProxyAdmin(address(this)); - - TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( - address(superloopImplementation), - address(proxyAdmin), - abi.encodeWithSelector(Superloop.initialize.selector, initData) - ); - superloop = Superloop(payable(address(proxy))); - - _deployAccountant(address(superloop), _singleAddressArray(ST_XTZ), _singleAddressArray(XTZ)); - _deployWithdrawManager(address(superloop)); - _deployDepositManager(address(superloop)); - - superloop.setAccountantModule(address(accountant)); - superloop.setWithdrawManagerModule(address(withdrawManager)); - superloop.setDepositManagerModule(address(depositManager)); - - vm.stopPrank(); - - vm.label(address(superloop), "superloop"); - vm.label(user1, "user1"); - vm.label(user2, "user2"); - } - - function test_Initialize() public view { - address vault = withdrawManager.vault(); - address asset = withdrawManager.asset(); - uint256 nextGeneralRequestId = withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.GENERAL); - uint256 nextInstantRequestId = withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.INSTANT); - uint256 nextPriorityRequestId = withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.PRIORITY); - uint256 nextDeferredRequestId = withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.DEFERRED); - - assertEq(vault, address(superloop)); - assertEq(asset, XTZ); - assertEq(nextGeneralRequestId, 1); - assertEq(nextInstantRequestId, 1); - assertEq(nextPriorityRequestId, 1); - assertEq(nextDeferredRequestId, 1); - } - - // ============ requestWithdraw Tests ============ - - function test_requestWithdraw_Success() public { - uint256 shares = 1000 * 10 ** 18; - DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; - - // Give user1 some vault shares - deal(address(superloop), user1, shares); - - vm.startPrank(user1); - IERC20(address(superloop)).approve(address(withdrawManager), shares); - - // Expect the WithdrawRequested event - vm.expectEmit(true, true, true, true); - emit WithdrawManager.WithdrawRequested(user1, shares, 1, requestType); - - withdrawManager.requestWithdraw(shares, requestType); - vm.stopPrank(); - - // Verify the withdraw request was created - DataTypes.WithdrawRequestData memory request = withdrawManager.withdrawRequest(1, requestType); - - assertEq(request.shares, shares); - assertEq(request.sharesProcessed, 0); - assertEq(request.amountClaimable, 0); - assertEq(request.amountClaimed, 0); - assertEq(request.user, user1); - assertEq(uint256(request.state), uint256(DataTypes.RequestProcessingState.UNPROCESSED)); - - // Verify user's withdraw request ID was set - (, uint256 userRequestId) = withdrawManager.userWithdrawRequest(user1, requestType); - assertEq(userRequestId, 1); - - // Verify shares were transferred to withdraw manager - assertEq(IERC20(address(superloop)).balanceOf(address(withdrawManager)), shares); - assertEq(IERC20(address(superloop)).balanceOf(user1), 0); - } - - function test_requestWithdraw_ZeroShares() public { - uint256 shares = 0; - DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; - - vm.startPrank(user1); - vm.expectRevert(bytes(Errors.INVALID_SHARES_AMOUNT)); - withdrawManager.requestWithdraw(shares, requestType); - vm.stopPrank(); - } - - function test_requestWithdraw_ZeroExpectedAmount() public { - uint256 shares = 1; // Very small amount that would result in 0 expected withdraw amount - DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; - - // Give user1 some vault shares - deal(address(superloop), user1, shares); - - vm.startPrank(user1); - IERC20(address(superloop)).approve(address(withdrawManager), shares); - - vm.expectRevert(bytes(Errors.INVALID_AMOUNT)); - withdrawManager.requestWithdraw(shares, requestType); - vm.stopPrank(); - } +// // SPDX-License-Identifier: MIT + +// pragma solidity ^0.8.13; + +// import {TestBase} from "../TestBase.sol"; +// import {console} from "forge-std/console.sol"; +// import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; +// import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; +// import {TransparentUpgradeableProxy} from +// "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +// import {DataTypes} from "../../../src/common/DataTypes.sol"; +// import {Errors} from "../../../src/common/Errors.sol"; +// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +// import {WithdrawManager} from "../../../src/core/WithdrawManager/WithdrawManager.sol"; + +// contract WithdrawManagerTest is TestBase { +// Superloop public superloopImplementation; +// ProxyAdmin public proxyAdmin; +// address public user1; +// address public user2; + +// function setUp() public override { +// super.setUp(); + +// user1 = makeAddr("user1"); +// user2 = makeAddr("user2"); + +// vm.startPrank(admin); +// _deployModules(); + +// address[] memory modules = new address[](5); +// modules[0] = address(supplyModule); +// modules[1] = address(withdrawModule); +// modules[2] = address(borrowModule); +// modules[3] = address(repayModule); +// modules[4] = address(dexModule); + +// DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ +// asset: XTZ, +// name: "XTZ Vault", +// symbol: "XTZV", +// supplyCap: 100000 * 10 ** 18, +// minimumDepositAmount: 100, +// instantWithdrawFee: 0, +// superloopModuleRegistry: address(moduleRegistry), +// modules: modules, +// accountant: mockModule, +// withdrawManager: mockModule, +// depositManager: mockModule, +// cashReserve: 1000, +// vaultAdmin: admin, +// treasury: treasury, +// vaultOperator: admin +// }); +// superloopImplementation = new Superloop(); +// proxyAdmin = new ProxyAdmin(address(this)); + +// TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( +// address(superloopImplementation), +// address(proxyAdmin), +// abi.encodeWithSelector(Superloop.initialize.selector, initData) +// ); +// superloop = Superloop(payable(address(proxy))); + +// _deployAccountant(address(superloop), _singleAddressArray(ST_XTZ), _singleAddressArray(XTZ)); +// _deployWithdrawManager(address(superloop)); +// _deployDepositManager(address(superloop)); + +// superloop.setAccountantModule(address(accountant)); +// superloop.setWithdrawManagerModule(address(withdrawManager)); +// superloop.setDepositManagerModule(address(depositManager)); + +// vm.stopPrank(); + +// vm.label(address(superloop), "superloop"); +// vm.label(user1, "user1"); +// vm.label(user2, "user2"); +// } + +// function test_Initialize() public view { +// address vault = withdrawManager.vault(); +// address asset = withdrawManager.asset(); +// uint256 nextGeneralRequestId = withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.GENERAL); +// uint256 nextInstantRequestId = withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.INSTANT); +// uint256 nextPriorityRequestId = withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.PRIORITY); +// uint256 nextDeferredRequestId = withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.DEFERRED); + +// assertEq(vault, address(superloop)); +// assertEq(asset, XTZ); +// assertEq(nextGeneralRequestId, 1); +// assertEq(nextInstantRequestId, 1); +// assertEq(nextPriorityRequestId, 1); +// assertEq(nextDeferredRequestId, 1); +// } + +// // ============ requestWithdraw Tests ============ + +// function test_requestWithdraw_Success() public { +// uint256 shares = 1000 * 10 ** 18; +// DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; + +// // Give user1 some vault shares +// deal(address(superloop), user1, shares); + +// vm.startPrank(user1); +// IERC20(address(superloop)).approve(address(withdrawManager), shares); + +// // Expect the WithdrawRequested event +// vm.expectEmit(true, true, true, true); +// emit WithdrawManager.WithdrawRequested(user1, shares, 1, requestType); + +// withdrawManager.requestWithdraw(shares, requestType); +// vm.stopPrank(); + +// // Verify the withdraw request was created +// DataTypes.WithdrawRequestData memory request = withdrawManager.withdrawRequest(1, requestType); + +// assertEq(request.shares, shares); +// assertEq(request.sharesProcessed, 0); +// assertEq(request.amountClaimable, 0); +// assertEq(request.amountClaimed, 0); +// assertEq(request.user, user1); +// assertEq(uint256(request.state), uint256(DataTypes.RequestProcessingState.UNPROCESSED)); + +// // Verify user's withdraw request ID was set +// (, uint256 userRequestId) = withdrawManager.userWithdrawRequest(user1, requestType); +// assertEq(userRequestId, 1); + +// // Verify shares were transferred to withdraw manager +// assertEq(IERC20(address(superloop)).balanceOf(address(withdrawManager)), shares); +// assertEq(IERC20(address(superloop)).balanceOf(user1), 0); +// } + +// function test_requestWithdraw_ZeroShares() public { +// uint256 shares = 0; +// DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; + +// vm.startPrank(user1); +// vm.expectRevert(bytes(Errors.INVALID_SHARES_AMOUNT)); +// withdrawManager.requestWithdraw(shares, requestType); +// vm.stopPrank(); +// } + +// function test_requestWithdraw_ZeroExpectedAmount() public { +// uint256 shares = 1; // Very small amount that would result in 0 expected withdraw amount +// DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; + +// // Give user1 some vault shares +// deal(address(superloop), user1, shares); + +// vm.startPrank(user1); +// IERC20(address(superloop)).approve(address(withdrawManager), shares); + +// vm.expectRevert(bytes(Errors.INVALID_AMOUNT)); +// withdrawManager.requestWithdraw(shares, requestType); +// vm.stopPrank(); +// } - function test_requestWithdraw_ActiveRequestExists() public { - uint256 shares = 1000 * 10 ** 18; - DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; +// function test_requestWithdraw_ActiveRequestExists() public { +// uint256 shares = 1000 * 10 ** 18; +// DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; - // Give user1 some vault shares - deal(address(superloop), user1, shares * 2); +// // Give user1 some vault shares +// deal(address(superloop), user1, shares * 2); - vm.startPrank(user1); - IERC20(address(superloop)).approve(address(withdrawManager), shares * 2); +// vm.startPrank(user1); +// IERC20(address(superloop)).approve(address(withdrawManager), shares * 2); - // Make first withdraw request - withdrawManager.requestWithdraw(shares, requestType); +// // Make first withdraw request +// withdrawManager.requestWithdraw(shares, requestType); - // Try to make another withdraw request - should fail - vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_ACTIVE)); - withdrawManager.requestWithdraw(shares, requestType); - vm.stopPrank(); - } +// // Try to make another withdraw request - should fail +// vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_ACTIVE)); +// withdrawManager.requestWithdraw(shares, requestType); +// vm.stopPrank(); +// } - function test_requestWithdraw_InsufficientBalance() public { - uint256 shares = 1000 * 10 ** 18; - DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; +// function test_requestWithdraw_InsufficientBalance() public { +// uint256 shares = 1000 * 10 ** 18; +// DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; - // Don't give user1 any vault shares - vm.startPrank(user1); - IERC20(address(superloop)).approve(address(withdrawManager), shares); +// // Don't give user1 any vault shares +// vm.startPrank(user1); +// IERC20(address(superloop)).approve(address(withdrawManager), shares); - vm.expectRevert(); - withdrawManager.requestWithdraw(shares, requestType); - vm.stopPrank(); - } +// vm.expectRevert(); +// withdrawManager.requestWithdraw(shares, requestType); +// vm.stopPrank(); +// } - function test_requestWithdraw_DifferentRequestTypes() public { - uint256 shares = 1000 * 10 ** 18; +// function test_requestWithdraw_DifferentRequestTypes() public { +// uint256 shares = 1000 * 10 ** 18; - // Give user1 some vault shares - deal(address(superloop), user1, shares * 4); +// // Give user1 some vault shares +// deal(address(superloop), user1, shares * 4); - vm.startPrank(user1); - IERC20(address(superloop)).approve(address(withdrawManager), shares * 4); +// vm.startPrank(user1); +// IERC20(address(superloop)).approve(address(withdrawManager), shares * 4); - // Test all request types - withdrawManager.requestWithdraw(shares, DataTypes.WithdrawRequestType.GENERAL); - withdrawManager.requestWithdraw(shares, DataTypes.WithdrawRequestType.INSTANT); - withdrawManager.requestWithdraw(shares, DataTypes.WithdrawRequestType.PRIORITY); - withdrawManager.requestWithdraw(shares, DataTypes.WithdrawRequestType.DEFERRED); +// // Test all request types +// withdrawManager.requestWithdraw(shares, DataTypes.WithdrawRequestType.GENERAL); +// withdrawManager.requestWithdraw(shares, DataTypes.WithdrawRequestType.INSTANT); +// withdrawManager.requestWithdraw(shares, DataTypes.WithdrawRequestType.PRIORITY); +// withdrawManager.requestWithdraw(shares, DataTypes.WithdrawRequestType.DEFERRED); - vm.stopPrank(); +// vm.stopPrank(); - // Verify all requests were created - assertEq(withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.GENERAL), 2); - assertEq(withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.INSTANT), 2); - assertEq(withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.PRIORITY), 2); - assertEq(withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.DEFERRED), 2); - } +// // Verify all requests were created +// assertEq(withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.GENERAL), 2); +// assertEq(withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.INSTANT), 2); +// assertEq(withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.PRIORITY), 2); +// assertEq(withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.DEFERRED), 2); +// } - // ============ cancelWithdrawRequest Tests ============ +// // ============ cancelWithdrawRequest Tests ============ - function test_cancelWithdrawRequest_Success() public { - uint256 shares = 1000 * 10 ** 18; - DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; +// function test_cancelWithdrawRequest_Success() public { +// uint256 shares = 1000 * 10 ** 18; +// DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; - // Give user1 some vault shares and make a withdraw request - deal(address(superloop), user1, shares); +// // Give user1 some vault shares and make a withdraw request +// deal(address(superloop), user1, shares); - vm.startPrank(user1); - IERC20(address(superloop)).approve(address(withdrawManager), shares); - withdrawManager.requestWithdraw(shares, requestType); +// vm.startPrank(user1); +// IERC20(address(superloop)).approve(address(withdrawManager), shares); +// withdrawManager.requestWithdraw(shares, requestType); - uint256 userBalanceBefore = IERC20(address(superloop)).balanceOf(user1); +// uint256 userBalanceBefore = IERC20(address(superloop)).balanceOf(user1); - // Expect the WithdrawRequestCancelled event - vm.expectEmit(true, true, true, true); - emit WithdrawManager.WithdrawRequestCancelled(1, user1, shares, 0, requestType); +// // Expect the WithdrawRequestCancelled event +// vm.expectEmit(true, true, true, true); +// emit WithdrawManager.WithdrawRequestCancelled(1, user1, shares, 0, requestType); - withdrawManager.cancelWithdrawRequest(1, requestType); - vm.stopPrank(); +// withdrawManager.cancelWithdrawRequest(1, requestType); +// vm.stopPrank(); - // Verify the withdraw request was cancelled - DataTypes.WithdrawRequestData memory request = withdrawManager.withdrawRequest(1, requestType); +// // Verify the withdraw request was cancelled +// DataTypes.WithdrawRequestData memory request = withdrawManager.withdrawRequest(1, requestType); - assertEq(uint256(request.state), uint256(DataTypes.RequestProcessingState.CANCELLED)); +// assertEq(uint256(request.state), uint256(DataTypes.RequestProcessingState.CANCELLED)); - // Verify user's withdraw request ID was cleared - (, uint256 userRequestId) = withdrawManager.userWithdrawRequest(user1, requestType); - assertEq(userRequestId, 0); +// // Verify user's withdraw request ID was cleared +// (, uint256 userRequestId) = withdrawManager.userWithdrawRequest(user1, requestType); +// assertEq(userRequestId, 0); - // Verify shares were refunded to user - assertEq(IERC20(address(superloop)).balanceOf(user1), userBalanceBefore + shares); - assertEq(IERC20(address(superloop)).balanceOf(address(withdrawManager)), 0); - } +// // Verify shares were refunded to user +// assertEq(IERC20(address(superloop)).balanceOf(user1), userBalanceBefore + shares); +// assertEq(IERC20(address(superloop)).balanceOf(address(withdrawManager)), 0); +// } - function test_cancelWithdrawRequest_WrongOwner() public { - uint256 shares = 1000 * 10 ** 18; - DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; +// function test_cancelWithdrawRequest_WrongOwner() public { +// uint256 shares = 1000 * 10 ** 18; +// DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; - // Give user1 some vault shares and make a withdraw request - deal(address(superloop), user1, shares); +// // Give user1 some vault shares and make a withdraw request +// deal(address(superloop), user1, shares); - vm.startPrank(user1); - IERC20(address(superloop)).approve(address(withdrawManager), shares); - withdrawManager.requestWithdraw(shares, requestType); - vm.stopPrank(); +// vm.startPrank(user1); +// IERC20(address(superloop)).approve(address(withdrawManager), shares); +// withdrawManager.requestWithdraw(shares, requestType); +// vm.stopPrank(); - // Try to cancel from user2 - should fail - vm.startPrank(user2); - vm.expectRevert(bytes(Errors.CALLER_NOT_WITHDRAW_REQUEST_OWNER)); - withdrawManager.cancelWithdrawRequest(1, requestType); - vm.stopPrank(); - } - - function test_cancelWithdrawRequest_AlreadyCancelled() public { - uint256 shares = 1000 * 10 ** 18; - DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; - - // Give user1 some vault shares and make a withdraw request - deal(address(superloop), user1, shares); - - vm.startPrank(user1); - IERC20(address(superloop)).approve(address(withdrawManager), shares); - withdrawManager.requestWithdraw(shares, requestType); - - // Cancel the request - withdrawManager.cancelWithdrawRequest(1, requestType); - - // Try to cancel again - should fail - vm.expectRevert(bytes(Errors.INVALID_WITHDRAW_REQUEST_STATE)); - withdrawManager.cancelWithdrawRequest(1, requestType); - vm.stopPrank(); - } - - function test_cancelWithdrawRequest_NonExistentRequest() public { - DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; - - vm.startPrank(user1); - vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_NOT_FOUND)); - withdrawManager.cancelWithdrawRequest(999, requestType); // Non-existent request ID - vm.stopPrank(); - } - - // ============ withdraw Tests ============ - - function test_withdraw_NoRequestFound() public { - DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; - - vm.startPrank(user1); - vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_NOT_FOUND)); - withdrawManager.withdraw(requestType); - vm.stopPrank(); - } - - function test_withdraw_WrongOwner() public { - uint256 shares = 1000 * 10 ** 18; - DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; - - // Give user1 some vault shares and make a withdraw request - deal(address(superloop), user1, shares); - - vm.startPrank(user1); - IERC20(address(superloop)).approve(address(withdrawManager), shares); - withdrawManager.requestWithdraw(shares, requestType); - vm.stopPrank(); - - // Try to withdraw from user2 - should fail - vm.startPrank(user2); - vm.expectRevert(); - withdrawManager.withdraw(requestType); - vm.stopPrank(); - } - - function test_withdraw_RequestActive() public { - uint256 shares = 1000 * 10 ** 18; - DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; - - // Give user1 some vault shares and make a withdraw request - deal(address(superloop), user1, shares); - - vm.startPrank(user1); - IERC20(address(superloop)).approve(address(withdrawManager), shares); - withdrawManager.requestWithdraw(shares, requestType); - - // Try to withdraw immediately - should fail (request is still active/unprocessed) - vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_ACTIVE)); - withdrawManager.withdraw(requestType); - vm.stopPrank(); - } - - // ============ resolveWithdrawRequests Tests ============ - - function test_resolveWithdrawRequests_OnlyVault() public { - DataTypes.ResolveWithdrawRequestsData memory data = DataTypes.ResolveWithdrawRequestsData({ - shares: 1000 * 10 ** 18, - requestType: DataTypes.WithdrawRequestType.GENERAL, - callbackExecutionData: "" - }); - - vm.startPrank(user1); - vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT)); - withdrawManager.resolveWithdrawRequests(data); - vm.stopPrank(); - } - - function test_resolveWithdrawRequests_ZeroShares() public { - DataTypes.ResolveWithdrawRequestsData memory data = DataTypes.ResolveWithdrawRequestsData({ - shares: 0, - requestType: DataTypes.WithdrawRequestType.GENERAL, - callbackExecutionData: "" - }); - - vm.startPrank(address(superloop)); - vm.expectRevert(bytes(Errors.INVALID_SHARES_AMOUNT)); - withdrawManager.resolveWithdrawRequests(data); - vm.stopPrank(); - } - - function test_resolveWithdrawRequests_ExceedsPending() public { - DataTypes.ResolveWithdrawRequestsData memory data = DataTypes.ResolveWithdrawRequestsData({ - shares: 1000 * 10 ** 18, - requestType: DataTypes.WithdrawRequestType.GENERAL, - callbackExecutionData: "" - }); - - vm.startPrank(address(superloop)); - vm.expectRevert(bytes(Errors.INVALID_SHARES_AMOUNT)); - withdrawManager.resolveWithdrawRequests(data); - vm.stopPrank(); - } - - // TODO: Test partial cancellation, partial processing, and resolveWithdrawRequests functions - // NOTE: Cannot test these functions without manipulating the internal state, so will be tested in integration tests -} +// // Try to cancel from user2 - should fail +// vm.startPrank(user2); +// vm.expectRevert(bytes(Errors.CALLER_NOT_WITHDRAW_REQUEST_OWNER)); +// withdrawManager.cancelWithdrawRequest(1, requestType); +// vm.stopPrank(); +// } + +// function test_cancelWithdrawRequest_AlreadyCancelled() public { +// uint256 shares = 1000 * 10 ** 18; +// DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; + +// // Give user1 some vault shares and make a withdraw request +// deal(address(superloop), user1, shares); + +// vm.startPrank(user1); +// IERC20(address(superloop)).approve(address(withdrawManager), shares); +// withdrawManager.requestWithdraw(shares, requestType); + +// // Cancel the request +// withdrawManager.cancelWithdrawRequest(1, requestType); + +// // Try to cancel again - should fail +// vm.expectRevert(bytes(Errors.INVALID_WITHDRAW_REQUEST_STATE)); +// withdrawManager.cancelWithdrawRequest(1, requestType); +// vm.stopPrank(); +// } + +// function test_cancelWithdrawRequest_NonExistentRequest() public { +// DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; + +// vm.startPrank(user1); +// vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_NOT_FOUND)); +// withdrawManager.cancelWithdrawRequest(999, requestType); // Non-existent request ID +// vm.stopPrank(); +// } + +// // ============ withdraw Tests ============ + +// function test_withdraw_NoRequestFound() public { +// DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; + +// vm.startPrank(user1); +// vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_NOT_FOUND)); +// withdrawManager.withdraw(requestType); +// vm.stopPrank(); +// } + +// function test_withdraw_WrongOwner() public { +// uint256 shares = 1000 * 10 ** 18; +// DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; + +// // Give user1 some vault shares and make a withdraw request +// deal(address(superloop), user1, shares); + +// vm.startPrank(user1); +// IERC20(address(superloop)).approve(address(withdrawManager), shares); +// withdrawManager.requestWithdraw(shares, requestType); +// vm.stopPrank(); + +// // Try to withdraw from user2 - should fail +// vm.startPrank(user2); +// vm.expectRevert(); +// withdrawManager.withdraw(requestType); +// vm.stopPrank(); +// } + +// function test_withdraw_RequestActive() public { +// uint256 shares = 1000 * 10 ** 18; +// DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; + +// // Give user1 some vault shares and make a withdraw request +// deal(address(superloop), user1, shares); + +// vm.startPrank(user1); +// IERC20(address(superloop)).approve(address(withdrawManager), shares); +// withdrawManager.requestWithdraw(shares, requestType); + +// // Try to withdraw immediately - should fail (request is still active/unprocessed) +// vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_ACTIVE)); +// withdrawManager.withdraw(requestType); +// vm.stopPrank(); +// } + +// // ============ resolveWithdrawRequests Tests ============ + +// function test_resolveWithdrawRequests_OnlyVault() public { +// DataTypes.ResolveWithdrawRequestsData memory data = DataTypes.ResolveWithdrawRequestsData({ +// shares: 1000 * 10 ** 18, +// requestType: DataTypes.WithdrawRequestType.GENERAL, +// callbackExecutionData: "" +// }); + +// vm.startPrank(user1); +// vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT)); +// withdrawManager.resolveWithdrawRequests(data); +// vm.stopPrank(); +// } + +// function test_resolveWithdrawRequests_ZeroShares() public { +// DataTypes.ResolveWithdrawRequestsData memory data = DataTypes.ResolveWithdrawRequestsData({ +// shares: 0, +// requestType: DataTypes.WithdrawRequestType.GENERAL, +// callbackExecutionData: "" +// }); + +// vm.startPrank(address(superloop)); +// vm.expectRevert(bytes(Errors.INVALID_SHARES_AMOUNT)); +// withdrawManager.resolveWithdrawRequests(data); +// vm.stopPrank(); +// } + +// function test_resolveWithdrawRequests_ExceedsPending() public { +// DataTypes.ResolveWithdrawRequestsData memory data = DataTypes.ResolveWithdrawRequestsData({ +// shares: 1000 * 10 ** 18, +// requestType: DataTypes.WithdrawRequestType.GENERAL, +// callbackExecutionData: "" +// }); + +// vm.startPrank(address(superloop)); +// vm.expectRevert(bytes(Errors.INVALID_SHARES_AMOUNT)); +// withdrawManager.resolveWithdrawRequests(data); +// vm.stopPrank(); +// } + +// // TODO: Test partial cancellation, partial processing, and resolveWithdrawRequests functions +// // NOTE: Cannot test these functions without manipulating the internal state, so will be tested in integration tests +// } diff --git a/test/helpers/VaultRouter.t.sol b/test/helpers/VaultRouter.t.sol index ca1fb24..c706ac1 100644 --- a/test/helpers/VaultRouter.t.sol +++ b/test/helpers/VaultRouter.t.sol @@ -1,602 +1,602 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; - -import {Test, console} from "forge-std/Test.sol"; -import {Vm} from "forge-std/Vm.sol"; -import {VaultRouter} from "../../src/helpers/VaultRouter.sol"; -import {DataTypes} from "../../src/common/DataTypes.sol"; -import {Errors} from "../../src/common/Errors.sol"; -import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -import {IERC4626} from "openzeppelin-contracts/contracts/interfaces/IERC4626.sol"; -import {MockUniversalDexModule} from "../../src/mock/MockUniversalDexModule.sol"; -import {MockAsset} from "../../src/mock/MockAsset.sol"; -import {MockVault} from "../../src/mock/MockVault.sol"; -import {MockDepositManager} from "../../src/mock/MockDepositManager.sol"; - -/** - * @title VaultRouterTest - * @author Superlend - * @notice Comprehensive unit tests for the VaultRouter contract - */ -contract VaultRouterTest is Test { - VaultRouter public vaultRouter; - MockUniversalDexModule public mockDexModule; - MockAsset public mockToken; - MockVault public mockVault; - MockDepositManager public mockDepositManager; - - address public owner; - address public user; - address public nonOwner; - - uint256 public constant INITIAL_SUPPLY = 1000000 * 10 ** 18; - uint256 public constant DEPOSIT_AMOUNT = 1000 * 10 ** 18; - uint256 public constant SWAP_OUTPUT = 950 * 10 ** 18; // 5% slippage - - event VaultWhitelisted(address indexed vault, bool isWhitelisted); - event TokenWhitelisted(address indexed token, bool isWhitelisted); - event DepositManagerWhitelisted(address indexed depositManager, bool isWhitelisted); - - function setUp() public { - owner = makeAddr("owner"); - user = makeAddr("user"); - nonOwner = makeAddr("nonOwner"); - - // Deploy mock contracts - mockToken = new MockAsset(); - mockVault = new MockVault(IERC20(address(mockToken)), "Mock Vault", "MV"); - mockDepositManager = new MockDepositManager(); - mockDexModule = new MockUniversalDexModule(SWAP_OUTPUT); - - // Setup initial arrays - address[] memory supportedVaults = new address[](1); - supportedVaults[0] = address(mockVault); - - address[] memory supportedTokens = new address[](1); - supportedTokens[0] = address(mockToken); - - address[] memory supportedDepositManagers = new address[](1); - supportedDepositManagers[0] = address(mockDepositManager); - - // Deploy VaultRouter - vm.prank(owner); - vaultRouter = - new VaultRouter(supportedVaults, supportedTokens, address(mockDexModule), supportedDepositManagers); - - // Transfer tokens to user for testing - mockToken.transfer(user, DEPOSIT_AMOUNT * 10); - - // Label addresses for better debugging - vm.label(owner, "owner"); - vm.label(user, "user"); - vm.label(nonOwner, "nonOwner"); - vm.label(address(mockToken), "mockToken"); - vm.label(address(mockVault), "mockVault"); - vm.label(address(mockDepositManager), "mockDepositManager"); - vm.label(address(mockDexModule), "mockDexModule"); - vm.label(address(vaultRouter), "vaultRouter"); - } - - // ============ Constructor Tests ============ - - function test_Constructor_InitializesCorrectly() public view { - assertTrue(vaultRouter.supportedVaults(address(mockVault))); - assertTrue(vaultRouter.supportedTokens(address(mockToken))); - assertTrue(vaultRouter.supportedDepositManagers(address(mockDepositManager))); - assertEq(address(vaultRouter.universalDexModule()), address(mockDexModule)); - assertEq(vaultRouter.owner(), owner); - } - - function test_Constructor_EmitsEvents() public { - address[] memory vaults = new address[](1); - vaults[0] = address(0x123); - address[] memory tokens = new address[](1); - tokens[0] = address(0x456); - address[] memory depositManagers = new address[](1); - depositManagers[0] = address(0x789); - - vm.expectEmit(true, false, false, true); - emit VaultWhitelisted(address(0x123), true); - - vm.expectEmit(true, false, false, true); - emit TokenWhitelisted(address(0x456), true); - - vm.expectEmit(true, false, false, true); - emit DepositManagerWhitelisted(address(0x789), true); - - vm.prank(owner); - new VaultRouter(vaults, tokens, address(mockDexModule), depositManagers); - } - - // ============ Deposit Tests ============ - - function test_DepositWithToken_InstantDeposit_Success() public { - uint256 amountIn = DEPOSIT_AMOUNT; - - DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ - tokenIn: address(mockToken), - tokenOut: address(mockToken), - amountIn: amountIn, - maxAmountIn: amountIn, - minAmountOut: 0, - data: new DataTypes.ExecuteSwapParamsData[](0) - }); - - uint256 userBalanceBefore = mockToken.balanceOf(user); - uint256 vaultBalanceBefore = mockToken.balanceOf(address(mockVault)); - - vm.startPrank(user); - mockToken.approve(address(vaultRouter), amountIn); - vaultRouter.depositWithToken( - address(mockVault), - address(mockDepositManager), - address(mockToken), - amountIn, - DataTypes.DepositType.INSTANT, - swapParams - ); - vm.stopPrank(); - - assertEq(mockToken.balanceOf(user), userBalanceBefore - amountIn); - assertEq(mockToken.balanceOf(address(mockVault)), vaultBalanceBefore + amountIn); - } - - function test_DepositWithToken_RequestedDeposit_Success() public { - uint256 amountIn = DEPOSIT_AMOUNT; - - DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ - tokenIn: address(mockToken), - tokenOut: address(mockToken), - amountIn: amountIn, - maxAmountIn: amountIn, - minAmountOut: 0, - data: new DataTypes.ExecuteSwapParamsData[](0) - }); - - uint256 userBalanceBefore = mockToken.balanceOf(user); - - vm.startPrank(user); - mockToken.approve(address(vaultRouter), amountIn); - vaultRouter.depositWithToken( - address(mockVault), - address(mockDepositManager), - address(mockToken), - amountIn, - DataTypes.DepositType.REQUESTED, - swapParams - ); - vm.stopPrank(); - - assertEq(mockToken.balanceOf(user), userBalanceBefore - amountIn); - // For requested deposits, tokens are transferred to the deposit manager - // The mock deposit manager doesn't actually hold tokens, so we just check the shares - } - - function test_DepositWithToken_WithSwap_Success() public { - // Create a different token for the vault - MockAsset vaultToken = new MockAsset(); - MockVault swapVault = new MockVault(IERC20(address(vaultToken)), "Swap Vault", "SV"); - - // Whitelist the new vault and token - vm.startPrank(owner); - vaultRouter.whitelistVault(address(swapVault), true); - vaultRouter.whitelistToken(address(vaultToken), true); - vm.stopPrank(); - - // Transfer vault tokens to the VaultRouter to simulate the swap output - vaultToken.transfer(address(vaultRouter), SWAP_OUTPUT); - - uint256 amountIn = DEPOSIT_AMOUNT; - - DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ - tokenIn: address(mockToken), - tokenOut: address(vaultToken), - amountIn: amountIn, - maxAmountIn: amountIn, - minAmountOut: 0, - data: new DataTypes.ExecuteSwapParamsData[](0) - }); - - uint256 userBalanceBefore = mockToken.balanceOf(user); - uint256 vaultBalanceBefore = vaultToken.balanceOf(address(swapVault)); - - vm.startPrank(user); - mockToken.approve(address(vaultRouter), amountIn); - vaultRouter.depositWithToken( - address(swapVault), - address(mockDepositManager), - address(mockToken), - amountIn, - DataTypes.DepositType.INSTANT, - swapParams - ); - vm.stopPrank(); - - assertEq(mockToken.balanceOf(user), userBalanceBefore); // no diff should be there because unswapped tokens are returned to the user - assertEq(vaultToken.balanceOf(address(swapVault)), vaultBalanceBefore + SWAP_OUTPUT); - } - - // ============ Access Control Tests ============ - - function test_DepositWithToken_VaultNotWhitelisted_Reverts() public { - address unwhitelistedVault = makeAddr("unwhitelistedVault"); - - DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ - tokenIn: address(mockToken), - tokenOut: address(mockToken), - amountIn: DEPOSIT_AMOUNT, - maxAmountIn: DEPOSIT_AMOUNT, - minAmountOut: 0, - data: new DataTypes.ExecuteSwapParamsData[](0) - }); - - vm.startPrank(user); - mockToken.approve(address(vaultRouter), DEPOSIT_AMOUNT); - vm.expectRevert(abi.encodePacked(Errors.VAULT_NOT_WHITELISTED)); - vaultRouter.depositWithToken( - unwhitelistedVault, - address(mockDepositManager), - address(mockToken), - DEPOSIT_AMOUNT, - DataTypes.DepositType.INSTANT, - swapParams - ); - vm.stopPrank(); - } - - function test_DepositWithToken_TokenNotWhitelisted_Reverts() public { - MockAsset unwhitelistedToken = new MockAsset(); - - DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ - tokenIn: address(unwhitelistedToken), - tokenOut: address(unwhitelistedToken), - amountIn: DEPOSIT_AMOUNT, - maxAmountIn: DEPOSIT_AMOUNT, - minAmountOut: 0, - data: new DataTypes.ExecuteSwapParamsData[](0) - }); - - vm.startPrank(user); - unwhitelistedToken.approve(address(vaultRouter), DEPOSIT_AMOUNT); - vm.expectRevert(abi.encodePacked(Errors.TOKEN_NOT_WHITELISTED)); - vaultRouter.depositWithToken( - address(mockVault), - address(mockDepositManager), - address(unwhitelistedToken), - DEPOSIT_AMOUNT, - DataTypes.DepositType.INSTANT, - swapParams - ); - vm.stopPrank(); - } - - function test_DepositWithToken_DepositManagerNotWhitelisted_Reverts() public { - address unwhitelistedDepositManager = makeAddr("unwhitelistedDepositManager"); - - DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ - tokenIn: address(mockToken), - tokenOut: address(mockToken), - amountIn: DEPOSIT_AMOUNT, - maxAmountIn: DEPOSIT_AMOUNT, - minAmountOut: 0, - data: new DataTypes.ExecuteSwapParamsData[](0) - }); - - vm.startPrank(user); - mockToken.approve(address(vaultRouter), DEPOSIT_AMOUNT); - vm.expectRevert(abi.encodePacked(Errors.DEPOSIT_MANAGER_NOT_WHITELISTED)); - vaultRouter.depositWithToken( - address(mockVault), - unwhitelistedDepositManager, - address(mockToken), - DEPOSIT_AMOUNT, - DataTypes.DepositType.INSTANT, - swapParams - ); - vm.stopPrank(); - } - - // ============ Whitelist Management Tests ============ - - function test_WhitelistVault_OnlyOwner_Success() public { - address newVault = makeAddr("newVault"); - - vm.expectEmit(true, false, false, true); - emit VaultWhitelisted(newVault, true); - - vm.prank(owner); - vaultRouter.whitelistVault(newVault, true); - - assertTrue(vaultRouter.supportedVaults(newVault)); - } - - function test_WhitelistVault_NonOwner_Reverts() public { - address newVault = makeAddr("newVault"); - - vm.prank(nonOwner); - vm.expectRevert(); - vaultRouter.whitelistVault(newVault, true); - } - - function test_WhitelistToken_OnlyOwner_Success() public { - address newToken = makeAddr("newToken"); - - vm.expectEmit(true, false, false, true); - emit TokenWhitelisted(newToken, true); - - vm.prank(owner); - vaultRouter.whitelistToken(newToken, true); - - assertTrue(vaultRouter.supportedTokens(newToken)); - } - - function test_WhitelistToken_NonOwner_Reverts() public { - address newToken = makeAddr("newToken"); - - vm.prank(nonOwner); - vm.expectRevert(); - vaultRouter.whitelistToken(newToken, true); - } - - function test_WhitelistDepositManager_OnlyOwner_Success() public { - address newDepositManager = makeAddr("newDepositManager"); - - vm.expectEmit(true, false, false, true); - emit DepositManagerWhitelisted(newDepositManager, true); - - vm.prank(owner); - vaultRouter.whitelistDepositManager(newDepositManager, true); - - assertTrue(vaultRouter.supportedDepositManagers(newDepositManager)); - } - - function test_WhitelistDepositManager_NonOwner_Reverts() public { - address newDepositManager = makeAddr("newDepositManager"); - - vm.prank(nonOwner); - vm.expectRevert(); - vaultRouter.whitelistDepositManager(newDepositManager, true); - } - - function test_WhitelistVault_RemoveFromWhitelist_Success() public { - // First add to whitelist - vm.prank(owner); - vaultRouter.whitelistVault(address(mockVault), false); - - assertFalse(vaultRouter.supportedVaults(address(mockVault))); - } - - function test_WhitelistToken_RemoveFromWhitelist_Success() public { - // First add to whitelist - vm.prank(owner); - vaultRouter.whitelistToken(address(mockToken), false); - - assertFalse(vaultRouter.supportedTokens(address(mockToken))); - } - - function test_WhitelistDepositManager_RemoveFromWhitelist_Success() public { - // First add to whitelist - vm.prank(owner); - vaultRouter.whitelistDepositManager(address(mockDepositManager), false); - - assertFalse(vaultRouter.supportedDepositManagers(address(mockDepositManager))); - } - - // ============ Universal DEX Module Tests ============ - - function test_SetUniversalDexModule_OnlyOwner_Success() public { - MockUniversalDexModule newDexModule = new MockUniversalDexModule(1000); - - vm.prank(owner); - vaultRouter.setUniversalDexModule(address(newDexModule)); - - assertEq(address(vaultRouter.universalDexModule()), address(newDexModule)); - } - - function test_SetUniversalDexModule_NonOwner_Reverts() public { - MockUniversalDexModule newDexModule = new MockUniversalDexModule(1000); - - vm.prank(nonOwner); - vm.expectRevert(); - vaultRouter.setUniversalDexModule(address(newDexModule)); - } - - // ============ Edge Cases and Error Conditions ============ - - function test_DepositWithToken_InsufficientAllowance_Reverts() public { - DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ - tokenIn: address(mockToken), - tokenOut: address(mockToken), - amountIn: DEPOSIT_AMOUNT, - maxAmountIn: DEPOSIT_AMOUNT, - minAmountOut: 0, - data: new DataTypes.ExecuteSwapParamsData[](0) - }); - - vm.startPrank(user); - // Don't approve tokens - vm.expectRevert(); - vaultRouter.depositWithToken( - address(mockVault), - address(mockDepositManager), - address(mockToken), - DEPOSIT_AMOUNT, - DataTypes.DepositType.INSTANT, - swapParams - ); - vm.stopPrank(); - } - - function test_DepositWithToken_InsufficientBalance_Reverts() public { - uint256 excessiveAmount = mockToken.balanceOf(user) + 1; - - DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ - tokenIn: address(mockToken), - tokenOut: address(mockToken), - amountIn: excessiveAmount, - maxAmountIn: excessiveAmount, - minAmountOut: 0, - data: new DataTypes.ExecuteSwapParamsData[](0) - }); - - vm.startPrank(user); - mockToken.approve(address(vaultRouter), excessiveAmount); - vm.expectRevert(); - vaultRouter.depositWithToken( - address(mockVault), - address(mockDepositManager), - address(mockToken), - excessiveAmount, - DataTypes.DepositType.INSTANT, - swapParams - ); - vm.stopPrank(); - } - - function test_DepositWithToken_SwapFails_Reverts() public { - // Create a different token for the vault - MockAsset vaultToken = new MockAsset(); - MockVault swapVault = new MockVault(IERC20(address(vaultToken)), "Swap Vault", "SV"); - - // Whitelist the new vault and token - vm.startPrank(owner); - vaultRouter.whitelistVault(address(swapVault), true); - vaultRouter.whitelistToken(address(vaultToken), true); - vm.stopPrank(); - - // Transfer vault tokens to the VaultRouter to simulate the swap output - vaultToken.transfer(address(vaultRouter), SWAP_OUTPUT); - - // Make the DEX module revert - mockDexModule.setShouldRevert(true); - - uint256 amountIn = DEPOSIT_AMOUNT; - - DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ - tokenIn: address(mockToken), - tokenOut: address(vaultToken), - amountIn: amountIn, - maxAmountIn: amountIn, - minAmountOut: 0, - data: new DataTypes.ExecuteSwapParamsData[](0) - }); - - vm.startPrank(user); - mockToken.approve(address(vaultRouter), amountIn); - vm.expectRevert("MockDexModule: execution failed"); - vaultRouter.depositWithToken( - address(swapVault), - address(mockDepositManager), - address(mockToken), - amountIn, - DataTypes.DepositType.INSTANT, - swapParams - ); - vm.stopPrank(); - } - - // ============ Integration Tests ============ - - function test_FullDepositFlow_InstantDeposit() public { - uint256 amountIn = DEPOSIT_AMOUNT; - - DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ - tokenIn: address(mockToken), - tokenOut: address(mockToken), - amountIn: amountIn, - maxAmountIn: amountIn, - minAmountOut: 0, - data: new DataTypes.ExecuteSwapParamsData[](0) - }); - - // Record initial state - uint256 userTokenBalanceBefore = mockToken.balanceOf(user); - uint256 vaultTokenBalanceBefore = mockToken.balanceOf(address(mockVault)); - - // Execute deposit - vm.startPrank(user); - mockToken.approve(address(vaultRouter), amountIn); - vaultRouter.depositWithToken( - address(mockVault), - address(mockDepositManager), - address(mockToken), - amountIn, - DataTypes.DepositType.INSTANT, - swapParams - ); - vm.stopPrank(); - - // Verify final state - assertEq(mockToken.balanceOf(user), userTokenBalanceBefore - amountIn); - assertEq(mockToken.balanceOf(address(mockVault)), vaultTokenBalanceBefore + amountIn); - } - - function test_FullDepositFlow_RequestedDeposit() public { - uint256 amountIn = DEPOSIT_AMOUNT; - - DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ - tokenIn: address(mockToken), - tokenOut: address(mockToken), - amountIn: amountIn, - maxAmountIn: amountIn, - minAmountOut: 0, - data: new DataTypes.ExecuteSwapParamsData[](0) - }); - - // Record initial state - uint256 userTokenBalanceBefore = mockToken.balanceOf(user); - - // Execute deposit - vm.startPrank(user); - mockToken.approve(address(vaultRouter), amountIn); - vaultRouter.depositWithToken( - address(mockVault), - address(mockDepositManager), - address(mockToken), - amountIn, - DataTypes.DepositType.REQUESTED, - swapParams - ); - vm.stopPrank(); - - // Verify final state - assertEq(mockToken.balanceOf(user), userTokenBalanceBefore - amountIn); - // For requested deposits, tokens are transferred to the deposit manager - // The mock deposit manager doesn't actually hold tokens, so we just check the shares - } - - // ============ Gas Optimization Tests ============ - - function test_DepositWithToken_GasUsage() public { - uint256 amountIn = DEPOSIT_AMOUNT; - - DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ - tokenIn: address(mockToken), - tokenOut: address(mockToken), - amountIn: amountIn, - maxAmountIn: amountIn, - minAmountOut: 0, - data: new DataTypes.ExecuteSwapParamsData[](0) - }); - - vm.startPrank(user); - mockToken.approve(address(vaultRouter), amountIn); - - uint256 gasStart = gasleft(); - vaultRouter.depositWithToken( - address(mockVault), - address(mockDepositManager), - address(mockToken), - amountIn, - DataTypes.DepositType.INSTANT, - swapParams - ); - uint256 gasUsed = gasStart - gasleft(); - - console.log("Gas used for instant deposit:", gasUsed); - vm.stopPrank(); - - // Gas usage should be reasonable (less than 200k gas) - assertLt(gasUsed, 200000); - } -} +// // SPDX-License-Identifier: MIT +// pragma solidity ^0.8.13; + +// import {Test, console} from "forge-std/Test.sol"; +// import {Vm} from "forge-std/Vm.sol"; +// import {VaultRouter} from "../../src/helpers/VaultRouter.sol"; +// import {DataTypes} from "../../src/common/DataTypes.sol"; +// import {Errors} from "../../src/common/Errors.sol"; +// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +// import {IERC4626} from "openzeppelin-contracts/contracts/interfaces/IERC4626.sol"; +// import {MockUniversalDexModule} from "../../src/mock/MockUniversalDexModule.sol"; +// import {MockAsset} from "../../src/mock/MockAsset.sol"; +// import {MockVault} from "../../src/mock/MockVault.sol"; +// import {MockDepositManager} from "../../src/mock/MockDepositManager.sol"; + +// /** +// * @title VaultRouterTest +// * @author Superlend +// * @notice Comprehensive unit tests for the VaultRouter contract +// */ +// contract VaultRouterTest is Test { +// VaultRouter public vaultRouter; +// MockUniversalDexModule public mockDexModule; +// MockAsset public mockToken; +// MockVault public mockVault; +// MockDepositManager public mockDepositManager; + +// address public owner; +// address public user; +// address public nonOwner; + +// uint256 public constant INITIAL_SUPPLY = 1000000 * 10 ** 18; +// uint256 public constant DEPOSIT_AMOUNT = 1000 * 10 ** 18; +// uint256 public constant SWAP_OUTPUT = 950 * 10 ** 18; // 5% slippage + +// event VaultWhitelisted(address indexed vault, bool isWhitelisted); +// event TokenWhitelisted(address indexed token, bool isWhitelisted); +// event DepositManagerWhitelisted(address indexed depositManager, bool isWhitelisted); + +// function setUp() public { +// owner = makeAddr("owner"); +// user = makeAddr("user"); +// nonOwner = makeAddr("nonOwner"); + +// // Deploy mock contracts +// mockToken = new MockAsset(); +// mockVault = new MockVault(IERC20(address(mockToken)), "Mock Vault", "MV"); +// mockDepositManager = new MockDepositManager(); +// mockDexModule = new MockUniversalDexModule(SWAP_OUTPUT); + +// // Setup initial arrays +// address[] memory supportedVaults = new address[](1); +// supportedVaults[0] = address(mockVault); + +// address[] memory supportedTokens = new address[](1); +// supportedTokens[0] = address(mockToken); + +// address[] memory supportedDepositManagers = new address[](1); +// supportedDepositManagers[0] = address(mockDepositManager); + +// // Deploy VaultRouter +// vm.prank(owner); +// vaultRouter = +// new VaultRouter(supportedVaults, supportedTokens, address(mockDexModule), supportedDepositManagers); + +// // Transfer tokens to user for testing +// mockToken.transfer(user, DEPOSIT_AMOUNT * 10); + +// // Label addresses for better debugging +// vm.label(owner, "owner"); +// vm.label(user, "user"); +// vm.label(nonOwner, "nonOwner"); +// vm.label(address(mockToken), "mockToken"); +// vm.label(address(mockVault), "mockVault"); +// vm.label(address(mockDepositManager), "mockDepositManager"); +// vm.label(address(mockDexModule), "mockDexModule"); +// vm.label(address(vaultRouter), "vaultRouter"); +// } + +// // ============ Constructor Tests ============ + +// function test_Constructor_InitializesCorrectly() public view { +// assertTrue(vaultRouter.supportedVaults(address(mockVault))); +// assertTrue(vaultRouter.supportedTokens(address(mockToken))); +// assertTrue(vaultRouter.supportedDepositManagers(address(mockDepositManager))); +// assertEq(address(vaultRouter.universalDexModule()), address(mockDexModule)); +// assertEq(vaultRouter.owner(), owner); +// } + +// function test_Constructor_EmitsEvents() public { +// address[] memory vaults = new address[](1); +// vaults[0] = address(0x123); +// address[] memory tokens = new address[](1); +// tokens[0] = address(0x456); +// address[] memory depositManagers = new address[](1); +// depositManagers[0] = address(0x789); + +// vm.expectEmit(true, false, false, true); +// emit VaultWhitelisted(address(0x123), true); + +// vm.expectEmit(true, false, false, true); +// emit TokenWhitelisted(address(0x456), true); + +// vm.expectEmit(true, false, false, true); +// emit DepositManagerWhitelisted(address(0x789), true); + +// vm.prank(owner); +// new VaultRouter(vaults, tokens, address(mockDexModule), depositManagers); +// } + +// // ============ Deposit Tests ============ + +// function test_DepositWithToken_InstantDeposit_Success() public { +// uint256 amountIn = DEPOSIT_AMOUNT; + +// DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ +// tokenIn: address(mockToken), +// tokenOut: address(mockToken), +// amountIn: amountIn, +// maxAmountIn: amountIn, +// minAmountOut: 0, +// data: new DataTypes.ExecuteSwapParamsData[](0) +// }); + +// uint256 userBalanceBefore = mockToken.balanceOf(user); +// uint256 vaultBalanceBefore = mockToken.balanceOf(address(mockVault)); + +// vm.startPrank(user); +// mockToken.approve(address(vaultRouter), amountIn); +// vaultRouter.depositWithToken( +// address(mockVault), +// address(mockDepositManager), +// address(mockToken), +// amountIn, +// DataTypes.DepositType.INSTANT, +// swapParams +// ); +// vm.stopPrank(); + +// assertEq(mockToken.balanceOf(user), userBalanceBefore - amountIn); +// assertEq(mockToken.balanceOf(address(mockVault)), vaultBalanceBefore + amountIn); +// } + +// function test_DepositWithToken_RequestedDeposit_Success() public { +// uint256 amountIn = DEPOSIT_AMOUNT; + +// DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ +// tokenIn: address(mockToken), +// tokenOut: address(mockToken), +// amountIn: amountIn, +// maxAmountIn: amountIn, +// minAmountOut: 0, +// data: new DataTypes.ExecuteSwapParamsData[](0) +// }); + +// uint256 userBalanceBefore = mockToken.balanceOf(user); + +// vm.startPrank(user); +// mockToken.approve(address(vaultRouter), amountIn); +// vaultRouter.depositWithToken( +// address(mockVault), +// address(mockDepositManager), +// address(mockToken), +// amountIn, +// DataTypes.DepositType.REQUESTED, +// swapParams +// ); +// vm.stopPrank(); + +// assertEq(mockToken.balanceOf(user), userBalanceBefore - amountIn); +// // For requested deposits, tokens are transferred to the deposit manager +// // The mock deposit manager doesn't actually hold tokens, so we just check the shares +// } + +// function test_DepositWithToken_WithSwap_Success() public { +// // Create a different token for the vault +// MockAsset vaultToken = new MockAsset(); +// MockVault swapVault = new MockVault(IERC20(address(vaultToken)), "Swap Vault", "SV"); + +// // Whitelist the new vault and token +// vm.startPrank(owner); +// vaultRouter.whitelistVault(address(swapVault), true); +// vaultRouter.whitelistToken(address(vaultToken), true); +// vm.stopPrank(); + +// // Transfer vault tokens to the VaultRouter to simulate the swap output +// vaultToken.transfer(address(vaultRouter), SWAP_OUTPUT); + +// uint256 amountIn = DEPOSIT_AMOUNT; + +// DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ +// tokenIn: address(mockToken), +// tokenOut: address(vaultToken), +// amountIn: amountIn, +// maxAmountIn: amountIn, +// minAmountOut: 0, +// data: new DataTypes.ExecuteSwapParamsData[](0) +// }); + +// uint256 userBalanceBefore = mockToken.balanceOf(user); +// uint256 vaultBalanceBefore = vaultToken.balanceOf(address(swapVault)); + +// vm.startPrank(user); +// mockToken.approve(address(vaultRouter), amountIn); +// vaultRouter.depositWithToken( +// address(swapVault), +// address(mockDepositManager), +// address(mockToken), +// amountIn, +// DataTypes.DepositType.INSTANT, +// swapParams +// ); +// vm.stopPrank(); + +// assertEq(mockToken.balanceOf(user), userBalanceBefore); // no diff should be there because unswapped tokens are returned to the user +// assertEq(vaultToken.balanceOf(address(swapVault)), vaultBalanceBefore + SWAP_OUTPUT); +// } + +// // ============ Access Control Tests ============ + +// function test_DepositWithToken_VaultNotWhitelisted_Reverts() public { +// address unwhitelistedVault = makeAddr("unwhitelistedVault"); + +// DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ +// tokenIn: address(mockToken), +// tokenOut: address(mockToken), +// amountIn: DEPOSIT_AMOUNT, +// maxAmountIn: DEPOSIT_AMOUNT, +// minAmountOut: 0, +// data: new DataTypes.ExecuteSwapParamsData[](0) +// }); + +// vm.startPrank(user); +// mockToken.approve(address(vaultRouter), DEPOSIT_AMOUNT); +// vm.expectRevert(abi.encodePacked(Errors.VAULT_NOT_WHITELISTED)); +// vaultRouter.depositWithToken( +// unwhitelistedVault, +// address(mockDepositManager), +// address(mockToken), +// DEPOSIT_AMOUNT, +// DataTypes.DepositType.INSTANT, +// swapParams +// ); +// vm.stopPrank(); +// } + +// function test_DepositWithToken_TokenNotWhitelisted_Reverts() public { +// MockAsset unwhitelistedToken = new MockAsset(); + +// DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ +// tokenIn: address(unwhitelistedToken), +// tokenOut: address(unwhitelistedToken), +// amountIn: DEPOSIT_AMOUNT, +// maxAmountIn: DEPOSIT_AMOUNT, +// minAmountOut: 0, +// data: new DataTypes.ExecuteSwapParamsData[](0) +// }); + +// vm.startPrank(user); +// unwhitelistedToken.approve(address(vaultRouter), DEPOSIT_AMOUNT); +// vm.expectRevert(abi.encodePacked(Errors.TOKEN_NOT_WHITELISTED)); +// vaultRouter.depositWithToken( +// address(mockVault), +// address(mockDepositManager), +// address(unwhitelistedToken), +// DEPOSIT_AMOUNT, +// DataTypes.DepositType.INSTANT, +// swapParams +// ); +// vm.stopPrank(); +// } + +// function test_DepositWithToken_DepositManagerNotWhitelisted_Reverts() public { +// address unwhitelistedDepositManager = makeAddr("unwhitelistedDepositManager"); + +// DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ +// tokenIn: address(mockToken), +// tokenOut: address(mockToken), +// amountIn: DEPOSIT_AMOUNT, +// maxAmountIn: DEPOSIT_AMOUNT, +// minAmountOut: 0, +// data: new DataTypes.ExecuteSwapParamsData[](0) +// }); + +// vm.startPrank(user); +// mockToken.approve(address(vaultRouter), DEPOSIT_AMOUNT); +// vm.expectRevert(abi.encodePacked(Errors.DEPOSIT_MANAGER_NOT_WHITELISTED)); +// vaultRouter.depositWithToken( +// address(mockVault), +// unwhitelistedDepositManager, +// address(mockToken), +// DEPOSIT_AMOUNT, +// DataTypes.DepositType.INSTANT, +// swapParams +// ); +// vm.stopPrank(); +// } + +// // ============ Whitelist Management Tests ============ + +// function test_WhitelistVault_OnlyOwner_Success() public { +// address newVault = makeAddr("newVault"); + +// vm.expectEmit(true, false, false, true); +// emit VaultWhitelisted(newVault, true); + +// vm.prank(owner); +// vaultRouter.whitelistVault(newVault, true); + +// assertTrue(vaultRouter.supportedVaults(newVault)); +// } + +// function test_WhitelistVault_NonOwner_Reverts() public { +// address newVault = makeAddr("newVault"); + +// vm.prank(nonOwner); +// vm.expectRevert(); +// vaultRouter.whitelistVault(newVault, true); +// } + +// function test_WhitelistToken_OnlyOwner_Success() public { +// address newToken = makeAddr("newToken"); + +// vm.expectEmit(true, false, false, true); +// emit TokenWhitelisted(newToken, true); + +// vm.prank(owner); +// vaultRouter.whitelistToken(newToken, true); + +// assertTrue(vaultRouter.supportedTokens(newToken)); +// } + +// function test_WhitelistToken_NonOwner_Reverts() public { +// address newToken = makeAddr("newToken"); + +// vm.prank(nonOwner); +// vm.expectRevert(); +// vaultRouter.whitelistToken(newToken, true); +// } + +// function test_WhitelistDepositManager_OnlyOwner_Success() public { +// address newDepositManager = makeAddr("newDepositManager"); + +// vm.expectEmit(true, false, false, true); +// emit DepositManagerWhitelisted(newDepositManager, true); + +// vm.prank(owner); +// vaultRouter.whitelistDepositManager(newDepositManager, true); + +// assertTrue(vaultRouter.supportedDepositManagers(newDepositManager)); +// } + +// function test_WhitelistDepositManager_NonOwner_Reverts() public { +// address newDepositManager = makeAddr("newDepositManager"); + +// vm.prank(nonOwner); +// vm.expectRevert(); +// vaultRouter.whitelistDepositManager(newDepositManager, true); +// } + +// function test_WhitelistVault_RemoveFromWhitelist_Success() public { +// // First add to whitelist +// vm.prank(owner); +// vaultRouter.whitelistVault(address(mockVault), false); + +// assertFalse(vaultRouter.supportedVaults(address(mockVault))); +// } + +// function test_WhitelistToken_RemoveFromWhitelist_Success() public { +// // First add to whitelist +// vm.prank(owner); +// vaultRouter.whitelistToken(address(mockToken), false); + +// assertFalse(vaultRouter.supportedTokens(address(mockToken))); +// } + +// function test_WhitelistDepositManager_RemoveFromWhitelist_Success() public { +// // First add to whitelist +// vm.prank(owner); +// vaultRouter.whitelistDepositManager(address(mockDepositManager), false); + +// assertFalse(vaultRouter.supportedDepositManagers(address(mockDepositManager))); +// } + +// // ============ Universal DEX Module Tests ============ + +// function test_SetUniversalDexModule_OnlyOwner_Success() public { +// MockUniversalDexModule newDexModule = new MockUniversalDexModule(1000); + +// vm.prank(owner); +// vaultRouter.setUniversalDexModule(address(newDexModule)); + +// assertEq(address(vaultRouter.universalDexModule()), address(newDexModule)); +// } + +// function test_SetUniversalDexModule_NonOwner_Reverts() public { +// MockUniversalDexModule newDexModule = new MockUniversalDexModule(1000); + +// vm.prank(nonOwner); +// vm.expectRevert(); +// vaultRouter.setUniversalDexModule(address(newDexModule)); +// } + +// // ============ Edge Cases and Error Conditions ============ + +// function test_DepositWithToken_InsufficientAllowance_Reverts() public { +// DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ +// tokenIn: address(mockToken), +// tokenOut: address(mockToken), +// amountIn: DEPOSIT_AMOUNT, +// maxAmountIn: DEPOSIT_AMOUNT, +// minAmountOut: 0, +// data: new DataTypes.ExecuteSwapParamsData[](0) +// }); + +// vm.startPrank(user); +// // Don't approve tokens +// vm.expectRevert(); +// vaultRouter.depositWithToken( +// address(mockVault), +// address(mockDepositManager), +// address(mockToken), +// DEPOSIT_AMOUNT, +// DataTypes.DepositType.INSTANT, +// swapParams +// ); +// vm.stopPrank(); +// } + +// function test_DepositWithToken_InsufficientBalance_Reverts() public { +// uint256 excessiveAmount = mockToken.balanceOf(user) + 1; + +// DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ +// tokenIn: address(mockToken), +// tokenOut: address(mockToken), +// amountIn: excessiveAmount, +// maxAmountIn: excessiveAmount, +// minAmountOut: 0, +// data: new DataTypes.ExecuteSwapParamsData[](0) +// }); + +// vm.startPrank(user); +// mockToken.approve(address(vaultRouter), excessiveAmount); +// vm.expectRevert(); +// vaultRouter.depositWithToken( +// address(mockVault), +// address(mockDepositManager), +// address(mockToken), +// excessiveAmount, +// DataTypes.DepositType.INSTANT, +// swapParams +// ); +// vm.stopPrank(); +// } + +// function test_DepositWithToken_SwapFails_Reverts() public { +// // Create a different token for the vault +// MockAsset vaultToken = new MockAsset(); +// MockVault swapVault = new MockVault(IERC20(address(vaultToken)), "Swap Vault", "SV"); + +// // Whitelist the new vault and token +// vm.startPrank(owner); +// vaultRouter.whitelistVault(address(swapVault), true); +// vaultRouter.whitelistToken(address(vaultToken), true); +// vm.stopPrank(); + +// // Transfer vault tokens to the VaultRouter to simulate the swap output +// vaultToken.transfer(address(vaultRouter), SWAP_OUTPUT); + +// // Make the DEX module revert +// mockDexModule.setShouldRevert(true); + +// uint256 amountIn = DEPOSIT_AMOUNT; + +// DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ +// tokenIn: address(mockToken), +// tokenOut: address(vaultToken), +// amountIn: amountIn, +// maxAmountIn: amountIn, +// minAmountOut: 0, +// data: new DataTypes.ExecuteSwapParamsData[](0) +// }); + +// vm.startPrank(user); +// mockToken.approve(address(vaultRouter), amountIn); +// vm.expectRevert("MockDexModule: execution failed"); +// vaultRouter.depositWithToken( +// address(swapVault), +// address(mockDepositManager), +// address(mockToken), +// amountIn, +// DataTypes.DepositType.INSTANT, +// swapParams +// ); +// vm.stopPrank(); +// } + +// // ============ Integration Tests ============ + +// function test_FullDepositFlow_InstantDeposit() public { +// uint256 amountIn = DEPOSIT_AMOUNT; + +// DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ +// tokenIn: address(mockToken), +// tokenOut: address(mockToken), +// amountIn: amountIn, +// maxAmountIn: amountIn, +// minAmountOut: 0, +// data: new DataTypes.ExecuteSwapParamsData[](0) +// }); + +// // Record initial state +// uint256 userTokenBalanceBefore = mockToken.balanceOf(user); +// uint256 vaultTokenBalanceBefore = mockToken.balanceOf(address(mockVault)); + +// // Execute deposit +// vm.startPrank(user); +// mockToken.approve(address(vaultRouter), amountIn); +// vaultRouter.depositWithToken( +// address(mockVault), +// address(mockDepositManager), +// address(mockToken), +// amountIn, +// DataTypes.DepositType.INSTANT, +// swapParams +// ); +// vm.stopPrank(); + +// // Verify final state +// assertEq(mockToken.balanceOf(user), userTokenBalanceBefore - amountIn); +// assertEq(mockToken.balanceOf(address(mockVault)), vaultTokenBalanceBefore + amountIn); +// } + +// function test_FullDepositFlow_RequestedDeposit() public { +// uint256 amountIn = DEPOSIT_AMOUNT; + +// DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ +// tokenIn: address(mockToken), +// tokenOut: address(mockToken), +// amountIn: amountIn, +// maxAmountIn: amountIn, +// minAmountOut: 0, +// data: new DataTypes.ExecuteSwapParamsData[](0) +// }); + +// // Record initial state +// uint256 userTokenBalanceBefore = mockToken.balanceOf(user); + +// // Execute deposit +// vm.startPrank(user); +// mockToken.approve(address(vaultRouter), amountIn); +// vaultRouter.depositWithToken( +// address(mockVault), +// address(mockDepositManager), +// address(mockToken), +// amountIn, +// DataTypes.DepositType.REQUESTED, +// swapParams +// ); +// vm.stopPrank(); + +// // Verify final state +// assertEq(mockToken.balanceOf(user), userTokenBalanceBefore - amountIn); +// // For requested deposits, tokens are transferred to the deposit manager +// // The mock deposit manager doesn't actually hold tokens, so we just check the shares +// } + +// // ============ Gas Optimization Tests ============ + +// function test_DepositWithToken_GasUsage() public { +// uint256 amountIn = DEPOSIT_AMOUNT; + +// DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ +// tokenIn: address(mockToken), +// tokenOut: address(mockToken), +// amountIn: amountIn, +// maxAmountIn: amountIn, +// minAmountOut: 0, +// data: new DataTypes.ExecuteSwapParamsData[](0) +// }); + +// vm.startPrank(user); +// mockToken.approve(address(vaultRouter), amountIn); + +// uint256 gasStart = gasleft(); +// vaultRouter.depositWithToken( +// address(mockVault), +// address(mockDepositManager), +// address(mockToken), +// amountIn, +// DataTypes.DepositType.INSTANT, +// swapParams +// ); +// uint256 gasUsed = gasStart - gasleft(); + +// console.log("Gas used for instant deposit:", gasUsed); +// vm.stopPrank(); + +// // Gas usage should be reasonable (less than 200k gas) +// assertLt(gasUsed, 200000); +// } +// } diff --git a/test/helpers/integration/VaultRouter.t.sol b/test/helpers/integration/VaultRouter.t.sol index fa4f7e3..018b034 100644 --- a/test/helpers/integration/VaultRouter.t.sol +++ b/test/helpers/integration/VaultRouter.t.sol @@ -1,218 +1,218 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.13; - -import {IntegrationBase} from "../../core/integration/IntegrationBase.sol"; -import {console} from "forge-std/console.sol"; -import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -import {VaultRouter} from "../../../src/helpers/VaultRouter.sol"; -import {IRouter} from "../../../src/mock/MockIRouter.sol"; -import {DataTypes} from "../../../src/common/DataTypes.sol"; -import {Errors} from "../../../src/common/Errors.sol"; - -// test to swap from usdc to wxtz then deposit via the vault router - -contract VaultRouterTest is IntegrationBase { - VaultRouter public vaultRouter; - - function setUp() public override { - super.setUp(); - - address[] memory supportedVaults = new address[](1); - supportedVaults[0] = address(superloop); - address[] memory supportedTokens = new address[](3); - supportedTokens[0] = XTZ; - supportedTokens[1] = ST_XTZ; - supportedTokens[2] = USDC; - - address[] memory supportedDepositManagers = new address[](1); - supportedDepositManagers[0] = address(depositManager); - - vm.startPrank(admin); - vaultRouter = new VaultRouter(supportedVaults, supportedTokens, address(dexModule), supportedDepositManagers); - vm.stopPrank(); - - vm.prank(USDC_WHALE); - IERC20(USDC).transfer(user1, 100 * 10 ** 6); - - vm.prank(XTZ_WHALE); - IERC20(XTZ).transfer(user1, 100 * 10 ** 18); - } - - function test_normalDeposit() public { - uint256 amountIn = 1 * 10 ** 18; - - DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ - tokenIn: XTZ, - tokenOut: XTZ, - amountIn: amountIn, - maxAmountIn: amountIn, - minAmountOut: 0, - data: new DataTypes.ExecuteSwapParamsData[](0) - }); - - vm.startPrank(user1); - IERC20(XTZ).approve(address(vaultRouter), amountIn); - vm.expectRevert(bytes(Errors.INSUFFICIENT_CASH_SHORTFALL)); - vaultRouter.depositWithToken( - address(superloop), address(depositManager), XTZ, amountIn, DataTypes.DepositType.INSTANT, swapParams - ); - } - - function test_swapAndDeposit() public { - uint256 amountIn = 1 * 10 ** 6; - - // approve vault router to spend USDC - DataTypes.ExecuteSwapParamsData[] memory data = new DataTypes.ExecuteSwapParamsData[](2); - - data[0] = DataTypes.ExecuteSwapParamsData({ - target: address(USDC), - data: abi.encodeWithSelector(IERC20.approve.selector, address(ROUTER), amountIn) - }); - data[1] = DataTypes.ExecuteSwapParamsData({ - target: address(ROUTER), - data: abi.encodeWithSelector( - IRouter.exactInputSingle.selector, - IRouter.ExactInputSingleParams({ - tokenIn: USDC, - tokenOut: XTZ, - fee: 500, - recipient: address(dexModule), - amountIn: amountIn, - amountOutMinimum: 0, - sqrtPriceLimitX96: 0 - }) - ) - }); - - DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ - tokenIn: USDC, - tokenOut: XTZ, - amountIn: amountIn, - maxAmountIn: amountIn, - minAmountOut: 0, - data: data - }); - - vm.startPrank(user1); - IERC20(USDC).approve(address(vaultRouter), amountIn); - vm.expectRevert(bytes(Errors.INSUFFICIENT_CASH_SHORTFALL)); - vaultRouter.depositWithToken( - address(superloop), address(depositManager), USDC, amountIn, DataTypes.DepositType.INSTANT, swapParams - ); - } - - function test_depositRequest() public { - uint256 amountIn = 1 * 10 ** 18; - - DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ - tokenIn: XTZ, - tokenOut: XTZ, - amountIn: amountIn, - maxAmountIn: amountIn, - minAmountOut: 0, - data: new DataTypes.ExecuteSwapParamsData[](0) - }); - - vm.startPrank(user1); - IERC20(XTZ).approve(address(vaultRouter), amountIn); - vaultRouter.depositWithToken( - address(superloop), address(depositManager), XTZ, amountIn, DataTypes.DepositType.REQUESTED, swapParams - ); - - DataTypes.DepositRequestData memory request = depositManager.depositRequest(1); - assertEq(request.amount, amountIn); - assertEq(request.user, user1); - } - - function test_depositRequestWithSwap() public { - uint256 amountIn = 1 * 10 ** 6; - - // approve vault router to spend USDC - DataTypes.ExecuteSwapParamsData[] memory data = new DataTypes.ExecuteSwapParamsData[](2); - - data[0] = DataTypes.ExecuteSwapParamsData({ - target: address(USDC), - data: abi.encodeWithSelector(IERC20.approve.selector, address(ROUTER), amountIn) - }); - data[1] = DataTypes.ExecuteSwapParamsData({ - target: address(ROUTER), - data: abi.encodeWithSelector( - IRouter.exactInputSingle.selector, - IRouter.ExactInputSingleParams({ - tokenIn: USDC, - tokenOut: XTZ, - fee: 500, - recipient: address(dexModule), - amountIn: amountIn, - amountOutMinimum: 0, - sqrtPriceLimitX96: 0 - }) - ) - }); - - DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ - tokenIn: USDC, - tokenOut: XTZ, - amountIn: amountIn, - maxAmountIn: amountIn, - minAmountOut: 0, - data: data - }); - - vm.startPrank(user1); - IERC20(USDC).approve(address(vaultRouter), amountIn); - vaultRouter.depositWithToken( - address(superloop), address(depositManager), USDC, amountIn, DataTypes.DepositType.REQUESTED, swapParams - ); - - DataTypes.DepositRequestData memory request = depositManager.depositRequest(1); - assertGt(request.amount, 0); - assertEq(request.user, user1); - } - - function test_frontrunningProtection() public { - uint256 amountIn = 1 * 10 ** 6; - - // approve vault router to spend USDC - DataTypes.ExecuteSwapParamsData[] memory data = new DataTypes.ExecuteSwapParamsData[](2); - - data[0] = DataTypes.ExecuteSwapParamsData({ - target: address(USDC), - data: abi.encodeWithSelector(IERC20.approve.selector, address(ROUTER), amountIn) - }); - data[1] = DataTypes.ExecuteSwapParamsData({ - target: address(ROUTER), - data: abi.encodeWithSelector( - IRouter.exactInputSingle.selector, - IRouter.ExactInputSingleParams({ - tokenIn: USDC, - tokenOut: XTZ, - fee: 500, - recipient: address(dexModule), - amountIn: amountIn, - amountOutMinimum: 0, - sqrtPriceLimitX96: 0 - }) - ) - }); - - DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ - tokenIn: USDC, - tokenOut: XTZ, - amountIn: amountIn, - maxAmountIn: amountIn, - minAmountOut: 0, - data: data - }); - - vm.prank(user1); - IERC20(USDC).approve(address(vaultRouter), amountIn); - - vm.prank(user2); - vm.expectRevert(); - vaultRouter.depositWithToken( - address(superloop), address(depositManager), USDC, amountIn, DataTypes.DepositType.INSTANT, swapParams - ); - } -} +// // SPDX-License-Identifier: MIT + +// pragma solidity ^0.8.13; + +// import {IntegrationBase} from "../../core/integration/IntegrationBase.sol"; +// import {console} from "forge-std/console.sol"; +// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +// import {VaultRouter} from "../../../src/helpers/VaultRouter.sol"; +// import {IRouter} from "../../../src/mock/MockIRouter.sol"; +// import {DataTypes} from "../../../src/common/DataTypes.sol"; +// import {Errors} from "../../../src/common/Errors.sol"; + +// // test to swap from usdc to wxtz then deposit via the vault router + +// contract VaultRouterTest is IntegrationBase { +// VaultRouter public vaultRouter; + +// function setUp() public override { +// super.setUp(); + +// address[] memory supportedVaults = new address[](1); +// supportedVaults[0] = address(superloop); +// address[] memory supportedTokens = new address[](3); +// supportedTokens[0] = XTZ; +// supportedTokens[1] = ST_XTZ; +// supportedTokens[2] = USDC; + +// address[] memory supportedDepositManagers = new address[](1); +// supportedDepositManagers[0] = address(depositManager); + +// vm.startPrank(admin); +// vaultRouter = new VaultRouter(supportedVaults, supportedTokens, address(dexModule), supportedDepositManagers); +// vm.stopPrank(); + +// vm.prank(USDC_WHALE); +// IERC20(USDC).transfer(user1, 100 * 10 ** 6); + +// vm.prank(XTZ_WHALE); +// IERC20(XTZ).transfer(user1, 100 * 10 ** 18); +// } + +// function test_normalDeposit() public { +// uint256 amountIn = 1 * 10 ** 18; + +// DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ +// tokenIn: XTZ, +// tokenOut: XTZ, +// amountIn: amountIn, +// maxAmountIn: amountIn, +// minAmountOut: 0, +// data: new DataTypes.ExecuteSwapParamsData[](0) +// }); + +// vm.startPrank(user1); +// IERC20(XTZ).approve(address(vaultRouter), amountIn); +// vm.expectRevert(bytes(Errors.INSUFFICIENT_CASH_SHORTFALL)); +// vaultRouter.depositWithToken( +// address(superloop), address(depositManager), XTZ, amountIn, DataTypes.DepositType.INSTANT, swapParams +// ); +// } + +// function test_swapAndDeposit() public { +// uint256 amountIn = 1 * 10 ** 6; + +// // approve vault router to spend USDC +// DataTypes.ExecuteSwapParamsData[] memory data = new DataTypes.ExecuteSwapParamsData[](2); + +// data[0] = DataTypes.ExecuteSwapParamsData({ +// target: address(USDC), +// data: abi.encodeWithSelector(IERC20.approve.selector, address(ROUTER), amountIn) +// }); +// data[1] = DataTypes.ExecuteSwapParamsData({ +// target: address(ROUTER), +// data: abi.encodeWithSelector( +// IRouter.exactInputSingle.selector, +// IRouter.ExactInputSingleParams({ +// tokenIn: USDC, +// tokenOut: XTZ, +// fee: 500, +// recipient: address(dexModule), +// amountIn: amountIn, +// amountOutMinimum: 0, +// sqrtPriceLimitX96: 0 +// }) +// ) +// }); + +// DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ +// tokenIn: USDC, +// tokenOut: XTZ, +// amountIn: amountIn, +// maxAmountIn: amountIn, +// minAmountOut: 0, +// data: data +// }); + +// vm.startPrank(user1); +// IERC20(USDC).approve(address(vaultRouter), amountIn); +// vm.expectRevert(bytes(Errors.INSUFFICIENT_CASH_SHORTFALL)); +// vaultRouter.depositWithToken( +// address(superloop), address(depositManager), USDC, amountIn, DataTypes.DepositType.INSTANT, swapParams +// ); +// } + +// function test_depositRequest() public { +// uint256 amountIn = 1 * 10 ** 18; + +// DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ +// tokenIn: XTZ, +// tokenOut: XTZ, +// amountIn: amountIn, +// maxAmountIn: amountIn, +// minAmountOut: 0, +// data: new DataTypes.ExecuteSwapParamsData[](0) +// }); + +// vm.startPrank(user1); +// IERC20(XTZ).approve(address(vaultRouter), amountIn); +// vaultRouter.depositWithToken( +// address(superloop), address(depositManager), XTZ, amountIn, DataTypes.DepositType.REQUESTED, swapParams +// ); + +// DataTypes.DepositRequestData memory request = depositManager.depositRequest(1); +// assertEq(request.amount, amountIn); +// assertEq(request.user, user1); +// } + +// function test_depositRequestWithSwap() public { +// uint256 amountIn = 1 * 10 ** 6; + +// // approve vault router to spend USDC +// DataTypes.ExecuteSwapParamsData[] memory data = new DataTypes.ExecuteSwapParamsData[](2); + +// data[0] = DataTypes.ExecuteSwapParamsData({ +// target: address(USDC), +// data: abi.encodeWithSelector(IERC20.approve.selector, address(ROUTER), amountIn) +// }); +// data[1] = DataTypes.ExecuteSwapParamsData({ +// target: address(ROUTER), +// data: abi.encodeWithSelector( +// IRouter.exactInputSingle.selector, +// IRouter.ExactInputSingleParams({ +// tokenIn: USDC, +// tokenOut: XTZ, +// fee: 500, +// recipient: address(dexModule), +// amountIn: amountIn, +// amountOutMinimum: 0, +// sqrtPriceLimitX96: 0 +// }) +// ) +// }); + +// DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ +// tokenIn: USDC, +// tokenOut: XTZ, +// amountIn: amountIn, +// maxAmountIn: amountIn, +// minAmountOut: 0, +// data: data +// }); + +// vm.startPrank(user1); +// IERC20(USDC).approve(address(vaultRouter), amountIn); +// vaultRouter.depositWithToken( +// address(superloop), address(depositManager), USDC, amountIn, DataTypes.DepositType.REQUESTED, swapParams +// ); + +// DataTypes.DepositRequestData memory request = depositManager.depositRequest(1); +// assertGt(request.amount, 0); +// assertEq(request.user, user1); +// } + +// function test_frontrunningProtection() public { +// uint256 amountIn = 1 * 10 ** 6; + +// // approve vault router to spend USDC +// DataTypes.ExecuteSwapParamsData[] memory data = new DataTypes.ExecuteSwapParamsData[](2); + +// data[0] = DataTypes.ExecuteSwapParamsData({ +// target: address(USDC), +// data: abi.encodeWithSelector(IERC20.approve.selector, address(ROUTER), amountIn) +// }); +// data[1] = DataTypes.ExecuteSwapParamsData({ +// target: address(ROUTER), +// data: abi.encodeWithSelector( +// IRouter.exactInputSingle.selector, +// IRouter.ExactInputSingleParams({ +// tokenIn: USDC, +// tokenOut: XTZ, +// fee: 500, +// recipient: address(dexModule), +// amountIn: amountIn, +// amountOutMinimum: 0, +// sqrtPriceLimitX96: 0 +// }) +// ) +// }); + +// DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ +// tokenIn: USDC, +// tokenOut: XTZ, +// amountIn: amountIn, +// maxAmountIn: amountIn, +// minAmountOut: 0, +// data: data +// }); + +// vm.prank(user1); +// IERC20(USDC).approve(address(vaultRouter), amountIn); + +// vm.prank(user2); +// vm.expectRevert(); +// vaultRouter.depositWithToken( +// address(superloop), address(depositManager), USDC, amountIn, DataTypes.DepositType.INSTANT, swapParams +// ); +// } +// } diff --git a/test/modules/aave/AaveV3BorrowModule.t.sol b/test/modules/aave/AaveV3BorrowModule.t.sol index 094d4b0..7b26cbc 100644 --- a/test/modules/aave/AaveV3BorrowModule.t.sol +++ b/test/modules/aave/AaveV3BorrowModule.t.sol @@ -11,6 +11,7 @@ import {TransparentUpgradeableProxy} from "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import {IPoolConfigurator} from "aave-v3-core/contracts/interfaces/IPoolConfigurator.sol"; +import {IERC20Metadata} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {console} from "forge-std/console.sol"; contract AaveV3BorrowModuleTest is TestBase { @@ -30,10 +31,10 @@ contract AaveV3BorrowModuleTest is TestBase { modules[2] = address(borrowModule); DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ - asset: XTZ, - name: "XTZ Vault", - symbol: "XTZV", - supplyCap: 100000 * 10 ** 18, + asset: environment.vaultAsset, + name: "Vault", + symbol: "VLT", + supplyCap: 100000 * 10 ** environment.vaultAssetDecimals, minimumDepositAmount: 100, instantWithdrawFee: 0, superloopModuleRegistry: address(moduleRegistry), @@ -60,22 +61,17 @@ contract AaveV3BorrowModuleTest is TestBase { user = makeAddr("user"); vm.label(user, "user"); vm.label(address(superloop), "superloop"); - - vm.startPrank(POOL_ADMIN); - IPoolConfigurator(POOL_CONFIGURATOR).setReserveFlashLoaning(ST_XTZ, true); - IPoolConfigurator(POOL_CONFIGURATOR).setSupplyCap(ST_XTZ, 10000000); - vm.stopPrank(); } function test_BorrowBasicFlow() public { _supply(); // Arrange - uint256 borrowAmount = 5 * 10 ** 18; // 5 XTZ + uint256 borrowAmount = 5 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals(); // 5 borrowAsset // Create withdraw params DataTypes.AaveV3ActionParams memory borrowParams = - DataTypes.AaveV3ActionParams({asset: XTZ, amount: borrowAmount}); + DataTypes.AaveV3ActionParams({asset: environment.borrowAssets[0], amount: borrowAmount}); DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); moduleExecutionData[0] = DataTypes.ModuleExecutionData({ @@ -84,27 +80,29 @@ contract AaveV3BorrowModuleTest is TestBase { data: abi.encodeWithSelector(borrowModule.execute.selector, borrowParams) }); - (,, uint256 currentBorrow,,,,,,) = poolDataProvider.getUserReserveData(XTZ, address(superloop)); - uint256 currentBalance = IERC20(XTZ).balanceOf(address(superloop)); + (,, uint256 currentBorrow,,,,,,) = + poolDataProvider.getUserReserveData(environment.borrowAssets[0], address(superloop)); + uint256 currentBalance = IERC20(environment.borrowAssets[0]).balanceOf(address(superloop)); vm.prank(admin); superloop.operate(moduleExecutionData); - (,, uint256 finalBorrow,,,,,,) = poolDataProvider.getUserReserveData(XTZ, address(superloop)); - uint256 finalBalance = IERC20(XTZ).balanceOf(address(superloop)); + (,, uint256 finalBorrow,,,,,,) = + poolDataProvider.getUserReserveData(environment.borrowAssets[0], address(superloop)); + uint256 finalBalance = IERC20(environment.borrowAssets[0]).balanceOf(address(superloop)); assertTrue(finalBorrow > currentBorrow); assertTrue(finalBalance > currentBalance); } function _supply() internal { - vm.startPrank(STXTZ_WHALE); - IERC20(ST_XTZ).transfer(address(superloop), 1000 * 10 ** 6); + vm.startPrank(environment.vaultAssetWhale); + IERC20(environment.vaultAsset).transfer(address(superloop), 1000 * 10 ** environment.vaultAssetDecimals); vm.stopPrank(); - uint256 supplyAmount = 10 * 10 ** 6; // 10 ST_XTZ + uint256 supplyAmount = 1000 * 10 ** environment.vaultAssetDecimals; // 1000 vaultAsset DataTypes.AaveV3ActionParams memory supplyParams = - DataTypes.AaveV3ActionParams({asset: ST_XTZ, amount: supplyAmount}); + DataTypes.AaveV3ActionParams({asset: environment.vaultAsset, amount: supplyAmount}); DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); moduleExecutionData[0] = DataTypes.ModuleExecutionData({ diff --git a/test/modules/aave/AaveV3EmodeModule.t.sol b/test/modules/aave/AaveV3EmodeModule.t.sol index a0c6721..0c183c8 100644 --- a/test/modules/aave/AaveV3EmodeModule.t.sol +++ b/test/modules/aave/AaveV3EmodeModule.t.sol @@ -26,10 +26,10 @@ contract AaveV3EmodeModuleTest is TestBase { modules[0] = address(emodeModule); DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ - asset: XTZ, - name: "XTZ Vault", - symbol: "XTZV", - supplyCap: 100000 * 10 ** 18, + asset: environment.vaultAsset, + name: "Vault", + symbol: "VLT", + supplyCap: 100000 * 10 ** environment.vaultAssetDecimals, minimumDepositAmount: 100, instantWithdrawFee: 0, superloopModuleRegistry: address(moduleRegistry), @@ -61,7 +61,7 @@ contract AaveV3EmodeModuleTest is TestBase { function test_EmodeBasicFlow() public { uint256 currentEmodeCategory = pool.getUserEMode(address(superloop)); - console.log("currentEmodeCategory", currentEmodeCategory); + assertEq(currentEmodeCategory, 0); DataTypes.AaveV3EmodeParams memory params = DataTypes.AaveV3EmodeParams({emodeCategory: 1}); diff --git a/test/modules/aave/AaveV3FlashloanModule.t.sol b/test/modules/aave/AaveV3FlashloanModule.t.sol index 7142450..0061907 100644 --- a/test/modules/aave/AaveV3FlashloanModule.t.sol +++ b/test/modules/aave/AaveV3FlashloanModule.t.sol @@ -26,10 +26,10 @@ contract AaveV3FlashloanModuleTest is TestBase { modules[0] = address(flashloanModule); DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ - asset: XTZ, - name: "XTZ Vault", - symbol: "XTZV", - supplyCap: 100000 * 10 ** 18, + asset: environment.vaultAsset, + name: "Vault", + symbol: "VLT", + supplyCap: 100000 * 10 ** environment.vaultAssetDecimals, minimumDepositAmount: 100, instantWithdrawFee: 0, superloopModuleRegistry: address(moduleRegistry), @@ -52,7 +52,7 @@ contract AaveV3FlashloanModuleTest is TestBase { ); superloop = Superloop(payable(address(proxy))); - bytes32 key = keccak256(abi.encodePacked(POOL, IFlashLoanSimpleReceiver.executeOperation.selector)); + bytes32 key = keccak256(abi.encodePacked(environment.pool, IFlashLoanSimpleReceiver.executeOperation.selector)); superloop.setCallbackHandler(key, address(callbackHandler)); vm.stopPrank(); user = makeAddr("user"); @@ -62,17 +62,17 @@ contract AaveV3FlashloanModuleTest is TestBase { } function test_FlashloanBasicFlow() public { - vm.startPrank(XTZ_WHALE); - IERC20(XTZ).transfer(address(superloop), 10 * 10 ** 18); + vm.startPrank(environment.vaultAssetWhale); + IERC20(environment.vaultAsset).transfer(address(superloop), 1000 * 10 ** environment.vaultAssetDecimals); vm.stopPrank(); // Arrange - uint256 flashloanAmount = 1000 * 10 ** 18; // 1000 XTZ + uint256 flashloanAmount = 1000 * 10 ** environment.vaultAssetDecimals; // 1000 vaultAsset uint16 referralCode = 0; // Create flashloan params DataTypes.AaveV3FlashloanParams memory flashloanParams = DataTypes.AaveV3FlashloanParams({ - asset: XTZ, + asset: environment.vaultAsset, amount: flashloanAmount, referralCode: referralCode, callbackExecutionData: "" // No additional execution data for simple return @@ -87,14 +87,18 @@ contract AaveV3FlashloanModuleTest is TestBase { }); // Record initial balances - uint256 initialXTZBalance = IERC20(XTZ).balanceOf(address(superloop)); + uint256 initialVaultAssetBalance = IERC20(environment.vaultAsset).balanceOf(address(superloop)); // Act vm.prank(admin); superloop.operate(moduleExecutionData); // Assert - uint256 finalXTZBalance = IERC20(XTZ).balanceOf(address(superloop)); - assertLt(finalXTZBalance, initialXTZBalance, "Balance should decrease slightly after flashloan due to premium"); + uint256 finalVaultAssetBalance = IERC20(environment.vaultAsset).balanceOf(address(superloop)); + assertLt( + finalVaultAssetBalance, + initialVaultAssetBalance, + "Balance should decrease slightly after flashloan due to premium" + ); } } diff --git a/test/modules/aave/AaveV3RepayModule.t.sol b/test/modules/aave/AaveV3RepayModule.t.sol index 8c44a02..847e1bc 100644 --- a/test/modules/aave/AaveV3RepayModule.t.sol +++ b/test/modules/aave/AaveV3RepayModule.t.sol @@ -11,6 +11,7 @@ import {TransparentUpgradeableProxy} from "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import {IPoolConfigurator} from "aave-v3-core/contracts/interfaces/IPoolConfigurator.sol"; +import {IERC20Metadata} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {console} from "forge-std/console.sol"; contract AaveV3RepayModuleTest is TestBase { @@ -31,10 +32,10 @@ contract AaveV3RepayModuleTest is TestBase { modules[3] = address(repayModule); DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ - asset: XTZ, - name: "XTZ Vault", - symbol: "XTZV", - supplyCap: 100000 * 10 ** 18, + asset: environment.vaultAsset, + name: "Vault", + symbol: "VLT", + supplyCap: 100000 * 10 ** environment.vaultAssetDecimals, minimumDepositAmount: 100, instantWithdrawFee: 0, superloopModuleRegistry: address(moduleRegistry), @@ -61,11 +62,6 @@ contract AaveV3RepayModuleTest is TestBase { user = makeAddr("user"); vm.label(user, "user"); vm.label(address(superloop), "superloop"); - - vm.startPrank(POOL_ADMIN); - IPoolConfigurator(POOL_CONFIGURATOR).setReserveFlashLoaning(ST_XTZ, true); - IPoolConfigurator(POOL_CONFIGURATOR).setSupplyCap(ST_XTZ, 10000000); - vm.stopPrank(); } function test_RepayBasicFlow() public { @@ -73,11 +69,11 @@ contract AaveV3RepayModuleTest is TestBase { _borrow(); // Arrange - uint256 repayAmount = 5 * 10 ** 18; // 1000 XTZ + uint256 repayAmount = 5 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals(); // 5 borrowAsset // Create repay params DataTypes.AaveV3ActionParams memory repayParams = - DataTypes.AaveV3ActionParams({asset: XTZ, amount: repayAmount}); + DataTypes.AaveV3ActionParams({asset: environment.borrowAssets[0], amount: repayAmount}); DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); moduleExecutionData[0] = DataTypes.ModuleExecutionData({ @@ -86,27 +82,29 @@ contract AaveV3RepayModuleTest is TestBase { data: abi.encodeWithSelector(repayModule.execute.selector, repayParams) }); - (,, uint256 currentBorrow,,,,,,) = poolDataProvider.getUserReserveData(XTZ, address(superloop)); - uint256 currentBalance = IERC20(XTZ).balanceOf(address(superloop)); + (,, uint256 currentBorrow,,,,,,) = + poolDataProvider.getUserReserveData(environment.borrowAssets[0], address(superloop)); + uint256 currentBalance = IERC20(environment.borrowAssets[0]).balanceOf(address(superloop)); vm.prank(admin); superloop.operate(moduleExecutionData); - (,, uint256 finalBorrow,,,,,,) = poolDataProvider.getUserReserveData(XTZ, address(superloop)); - uint256 finalBalance = IERC20(XTZ).balanceOf(address(superloop)); + (,, uint256 finalBorrow,,,,,,) = + poolDataProvider.getUserReserveData(environment.borrowAssets[0], address(superloop)); + uint256 finalBalance = IERC20(environment.borrowAssets[0]).balanceOf(address(superloop)); assertTrue(finalBorrow < currentBorrow); assertTrue(finalBalance < currentBalance); } function _supply() internal { - vm.startPrank(STXTZ_WHALE); - IERC20(ST_XTZ).transfer(address(superloop), 1000 * 10 ** 6); + vm.startPrank(environment.vaultAssetWhale); + IERC20(environment.vaultAsset).transfer(address(superloop), 1000 * 10 ** environment.vaultAssetDecimals); vm.stopPrank(); - uint256 supplyAmount = 10 * 10 ** 6; // 10 ST_XTZ + uint256 supplyAmount = 1000 * 10 ** environment.vaultAssetDecimals; // 1000 vaultAsset DataTypes.AaveV3ActionParams memory supplyParams = - DataTypes.AaveV3ActionParams({asset: ST_XTZ, amount: supplyAmount}); + DataTypes.AaveV3ActionParams({asset: environment.vaultAsset, amount: supplyAmount}); DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); moduleExecutionData[0] = DataTypes.ModuleExecutionData({ @@ -120,11 +118,11 @@ contract AaveV3RepayModuleTest is TestBase { function _borrow() internal { // Arrange - uint256 borrowAmount = 5 * 10 ** 18; // 5 XTZ + uint256 borrowAmount = 5 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals(); // 5 borrowAsset // Create withdraw params DataTypes.AaveV3ActionParams memory borrowParams = - DataTypes.AaveV3ActionParams({asset: XTZ, amount: borrowAmount}); + DataTypes.AaveV3ActionParams({asset: environment.borrowAssets[0], amount: borrowAmount}); DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); moduleExecutionData[0] = DataTypes.ModuleExecutionData({ diff --git a/test/modules/aave/AaveV3SupplyModule.t.sol b/test/modules/aave/AaveV3SupplyModule.t.sol index 80f3b72..37c2caf 100644 --- a/test/modules/aave/AaveV3SupplyModule.t.sol +++ b/test/modules/aave/AaveV3SupplyModule.t.sol @@ -27,10 +27,10 @@ contract AaveV3SupplyModuleTest is TestBase { modules[0] = address(supplyModule); DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ - asset: XTZ, - name: "XTZ Vault", - symbol: "XTZV", - supplyCap: 100000 * 10 ** 18, + asset: environment.vaultAsset, + name: "Vault", + symbol: "VLT", + supplyCap: 100000 * 10 ** environment.vaultAssetDecimals, minimumDepositAmount: 100, instantWithdrawFee: 0, superloopModuleRegistry: address(moduleRegistry), @@ -60,16 +60,16 @@ contract AaveV3SupplyModuleTest is TestBase { } function test_SupplyBasicFlow() public { - vm.startPrank(XTZ_WHALE); - IERC20(XTZ).transfer(address(superloop), 1000 * 10 ** 18); + vm.startPrank(environment.vaultAssetWhale); + IERC20(environment.vaultAsset).transfer(address(superloop), 1000 * 10 ** environment.vaultAssetDecimals); vm.stopPrank(); // Arrange - uint256 supplyAmount = 1000 * 10 ** 18; // 1000 XTZ + uint256 supplyAmount = 1000 * 10 ** environment.vaultAssetDecimals; // 1000 vaultAsset // Create supply params DataTypes.AaveV3ActionParams memory supplyParams = - DataTypes.AaveV3ActionParams({asset: XTZ, amount: supplyAmount}); + DataTypes.AaveV3ActionParams({asset: environment.vaultAsset, amount: supplyAmount}); // Create module execution data DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); @@ -80,18 +80,19 @@ contract AaveV3SupplyModuleTest is TestBase { }); // current supply - (uint256 currentSupply,,,,,,,,) = poolDataProvider.getUserReserveData(XTZ, address(superloop)); - uint256 currentBalance = IERC20(XTZ).balanceOf(address(superloop)); + (uint256 currentSupply,,,,,,,,) = + poolDataProvider.getUserReserveData(environment.vaultAsset, address(superloop)); + uint256 currentBalance = IERC20(environment.vaultAsset).balanceOf(address(superloop)); // Act vm.prank(admin); superloop.operate(moduleExecutionData); // Assert - (uint256 finalSupply,,,,,,,,) = poolDataProvider.getUserReserveData(XTZ, address(superloop)); - uint256 finalBalance = IERC20(XTZ).balanceOf(address(superloop)); + (uint256 finalSupply,,,,,,,,) = poolDataProvider.getUserReserveData(environment.vaultAsset, address(superloop)); + uint256 finalBalance = IERC20(environment.vaultAsset).balanceOf(address(superloop)); - assertEq(finalSupply, currentSupply + supplyAmount); - assertEq(finalBalance, currentBalance - supplyAmount); + assertApproxEqAbs(finalSupply, currentSupply + supplyAmount, 1); + assertApproxEqAbs(finalBalance, currentBalance - supplyAmount, 1); } } diff --git a/test/modules/aave/AaveV3WithdrawModule.t.sol b/test/modules/aave/AaveV3WithdrawModule.t.sol index dafc868..e75a9fe 100644 --- a/test/modules/aave/AaveV3WithdrawModule.t.sol +++ b/test/modules/aave/AaveV3WithdrawModule.t.sol @@ -28,10 +28,10 @@ contract AaveV3WithdrawModuleTest is TestBase { modules[1] = address(withdrawModule); DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ - asset: XTZ, - name: "XTZ Vault", - symbol: "XTZV", - supplyCap: 100000 * 10 ** 18, + asset: environment.vaultAsset, + name: "Vault", + symbol: "VLT", + supplyCap: 100000 * 10 ** environment.vaultAssetDecimals, minimumDepositAmount: 100, instantWithdrawFee: 0, superloopModuleRegistry: address(moduleRegistry), @@ -64,11 +64,11 @@ contract AaveV3WithdrawModuleTest is TestBase { _supply(); // Arrange - uint256 withdrawAmount = 500 * 10 ** 18; // 1000 XTZ + uint256 withdrawAmount = 500 * 10 ** environment.vaultAssetDecimals; // 1000 vaultAsset // Create withdraw params DataTypes.AaveV3ActionParams memory withdrawParams = - DataTypes.AaveV3ActionParams({asset: XTZ, amount: withdrawAmount}); + DataTypes.AaveV3ActionParams({asset: environment.vaultAsset, amount: withdrawAmount}); DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); moduleExecutionData[0] = DataTypes.ModuleExecutionData({ @@ -77,27 +77,28 @@ contract AaveV3WithdrawModuleTest is TestBase { data: abi.encodeWithSelector(withdrawModule.execute.selector, withdrawParams) }); - (uint256 currentSupply,,,,,,,,) = poolDataProvider.getUserReserveData(XTZ, address(superloop)); - uint256 currentBalance = IERC20(XTZ).balanceOf(address(superloop)); + (uint256 currentSupply,,,,,,,,) = + poolDataProvider.getUserReserveData(environment.vaultAsset, address(superloop)); + uint256 currentBalance = IERC20(environment.vaultAsset).balanceOf(address(superloop)); vm.prank(admin); superloop.operate(moduleExecutionData); - (uint256 finalSupply,,,,,,,,) = poolDataProvider.getUserReserveData(XTZ, address(superloop)); - uint256 finalBalance = IERC20(XTZ).balanceOf(address(superloop)); + (uint256 finalSupply,,,,,,,,) = poolDataProvider.getUserReserveData(environment.vaultAsset, address(superloop)); + uint256 finalBalance = IERC20(environment.vaultAsset).balanceOf(address(superloop)); assertTrue(finalSupply <= currentSupply); assertTrue(finalBalance > currentBalance); } function _supply() internal { - vm.startPrank(XTZ_WHALE); - IERC20(XTZ).transfer(address(superloop), 1000 * 10 ** 18); + vm.startPrank(environment.vaultAssetWhale); + IERC20(environment.vaultAsset).transfer(address(superloop), 1000 * 10 ** environment.vaultAssetDecimals); vm.stopPrank(); - uint256 supplyAmount = 1000 * 10 ** 18; // 1000 XTZ + uint256 supplyAmount = 1000 * 10 ** environment.vaultAssetDecimals; // 1000 vaultAsset DataTypes.AaveV3ActionParams memory supplyParams = - DataTypes.AaveV3ActionParams({asset: XTZ, amount: supplyAmount}); + DataTypes.AaveV3ActionParams({asset: environment.vaultAsset, amount: supplyAmount}); DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); moduleExecutionData[0] = DataTypes.ModuleExecutionData({ diff --git a/test/modules/dex/UniversalDexModule.t.sol b/test/modules/dex/UniversalDexModule.t.sol index 03cbc8c..fc12a88 100644 --- a/test/modules/dex/UniversalDexModule.t.sol +++ b/test/modules/dex/UniversalDexModule.t.sol @@ -1,171 +1,171 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.13; - -import {console} from "forge-std/Test.sol"; -import {UniversalDexModule} from "../../../src/modules/dex/UniversalDexModule.sol"; -import {DataTypes} from "../../../src/common/DataTypes.sol"; -import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -import {TestBase} from "../../core/TestBase.sol"; -import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; -import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; -import {TransparentUpgradeableProxy} from - "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -import {IRouter} from "../../../src/mock/MockIRouter.sol"; - -contract UniversalDexModuleTest is TestBase { - Superloop public superloopImplementation; - ProxyAdmin public proxyAdmin; - address public user; - - function setUp() public override { - super.setUp(); - - vm.startPrank(admin); - _deployModules(); - - address[] memory modules = new address[](1); - modules[0] = address(dexModule); - - DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ - asset: XTZ, - name: "XTZ Vault", - symbol: "XTZV", - supplyCap: 100000 * 10 ** 18, - minimumDepositAmount: 100, - instantWithdrawFee: 0, - superloopModuleRegistry: address(moduleRegistry), - modules: modules, - accountant: mockModule, - withdrawManager: mockModule, - depositManager: mockModule, - cashReserve: 1000, - vaultAdmin: admin, - treasury: treasury, - vaultOperator: admin - }); - superloopImplementation = new Superloop(); - proxyAdmin = new ProxyAdmin(address(this)); - - TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( - address(superloopImplementation), - address(proxyAdmin), - abi.encodeWithSelector(Superloop.initialize.selector, initData) - ); - superloop = Superloop(payable(address(proxy))); - vm.stopPrank(); - - user = makeAddr("user"); - vm.label(user, "user"); - vm.label(address(superloop), "superloop"); - } - - function test_executeSwap() public { - address tokenIn = USDT; - address tokenOut = USDC; - - uint256 amountIn = 1000 * 10 ** 6; - uint256 maxAmountIn = 1000 * 10 ** 6; - uint256 minAmountOut = 900 * 10 ** 6; - - DataTypes.ExecuteSwapParamsData[] memory swapParamsData = new DataTypes.ExecuteSwapParamsData[](2); - swapParamsData[0] = DataTypes.ExecuteSwapParamsData({ - target: USDT, - data: abi.encodeWithSelector(IERC20.approve.selector, ROUTER, amountIn) - }); - swapParamsData[1] = DataTypes.ExecuteSwapParamsData({ - target: ROUTER, - data: abi.encodeWithSelector( - IRouter.exactInputSingle.selector, - IRouter.ExactInputSingleParams({ - tokenIn: USDT, - tokenOut: USDC, - fee: 100, - recipient: address(superloop), - amountIn: amountIn, - amountOutMinimum: 0, - sqrtPriceLimitX96: 0 - }) - ) - }); - - DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ - tokenIn: tokenIn, - tokenOut: tokenOut, - amountIn: amountIn, - maxAmountIn: maxAmountIn, - minAmountOut: minAmountOut, - data: swapParamsData - }); - - DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); - moduleExecutionData[0] = DataTypes.ModuleExecutionData({ - executionType: DataTypes.CallType.DELEGATECALL, - module: address(dexModule), - data: abi.encodeWithSelector(dexModule.execute.selector, swapParams) - }); - - vm.startPrank(USDT_WHALE); - IERC20(USDT).transfer(address(superloop), amountIn); - vm.stopPrank(); - - vm.startPrank(admin); - superloop.operate(moduleExecutionData); - vm.stopPrank(); - - uint256 usdcBalance = IERC20(USDC).balanceOf(address(superloop)); - assertGt(usdcBalance, 0); - } - - function test_executeSwapAndExit() public { - vm.startPrank(USDT_WHALE); - IERC20(USDT).transfer(address(user), 1000 * 10 ** 6); - vm.stopPrank(); - - address tokenIn = USDT; - address tokenOut = USDC; - - uint256 amountIn = 1000 * 10 ** 6; - uint256 maxAmountIn = 1000 * 10 ** 6; - uint256 minAmountOut = 900 * 10 ** 6; - - DataTypes.ExecuteSwapParamsData[] memory data = new DataTypes.ExecuteSwapParamsData[](2); - data[0] = DataTypes.ExecuteSwapParamsData({ - target: USDT, - data: abi.encodeWithSelector(IERC20.approve.selector, ROUTER, amountIn) - }); - data[1] = DataTypes.ExecuteSwapParamsData({ - target: ROUTER, - data: abi.encodeWithSelector( - IRouter.exactInputSingle.selector, - IRouter.ExactInputSingleParams({ - tokenIn: USDT, - tokenOut: USDC, - fee: 100, - recipient: address(dexModule), - amountIn: amountIn, - amountOutMinimum: 0, - sqrtPriceLimitX96: 0 - }) - ) - }); - - DataTypes.ExecuteSwapParams memory params = DataTypes.ExecuteSwapParams({ - tokenIn: tokenIn, - tokenOut: tokenOut, - amountIn: amountIn, - maxAmountIn: maxAmountIn, - minAmountOut: minAmountOut, - data: data - }); - - vm.startPrank(user); - IERC20(USDT).approve(address(dexModule), amountIn); - uint256 amountOut = dexModule.executeAndExit(params, user); - - vm.stopPrank(); - - uint256 usdcBalanceUser = IERC20(USDC).balanceOf(address(user)); - assertEq(usdcBalanceUser, amountOut); - } -} +// // SPDX-License-Identifier: MIT + +// pragma solidity ^0.8.13; + +// import {console} from "forge-std/Test.sol"; +// import {UniversalDexModule} from "../../../src/modules/dex/UniversalDexModule.sol"; +// import {DataTypes} from "../../../src/common/DataTypes.sol"; +// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +// import {TestBase} from "../../core/TestBase.sol"; +// import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; +// import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; +// import {TransparentUpgradeableProxy} from +// "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +// import {IRouter} from "../../../src/mock/MockIRouter.sol"; + +// contract UniversalDexModuleTest is TestBase { +// Superloop public superloopImplementation; +// ProxyAdmin public proxyAdmin; +// address public user; + +// function setUp() public override { +// super.setUp(); + +// vm.startPrank(admin); +// _deployModules(); + +// address[] memory modules = new address[](1); +// modules[0] = address(dexModule); + +// DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ +// asset: XTZ, +// name: "XTZ Vault", +// symbol: "XTZV", +// supplyCap: 100000 * 10 ** 18, +// minimumDepositAmount: 100, +// instantWithdrawFee: 0, +// superloopModuleRegistry: address(moduleRegistry), +// modules: modules, +// accountant: mockModule, +// withdrawManager: mockModule, +// depositManager: mockModule, +// cashReserve: 1000, +// vaultAdmin: admin, +// treasury: treasury, +// vaultOperator: admin +// }); +// superloopImplementation = new Superloop(); +// proxyAdmin = new ProxyAdmin(address(this)); + +// TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( +// address(superloopImplementation), +// address(proxyAdmin), +// abi.encodeWithSelector(Superloop.initialize.selector, initData) +// ); +// superloop = Superloop(payable(address(proxy))); +// vm.stopPrank(); + +// user = makeAddr("user"); +// vm.label(user, "user"); +// vm.label(address(superloop), "superloop"); +// } + +// function test_executeSwap() public { +// address tokenIn = USDT; +// address tokenOut = USDC; + +// uint256 amountIn = 1000 * 10 ** 6; +// uint256 maxAmountIn = 1000 * 10 ** 6; +// uint256 minAmountOut = 900 * 10 ** 6; + +// DataTypes.ExecuteSwapParamsData[] memory swapParamsData = new DataTypes.ExecuteSwapParamsData[](2); +// swapParamsData[0] = DataTypes.ExecuteSwapParamsData({ +// target: USDT, +// data: abi.encodeWithSelector(IERC20.approve.selector, ROUTER, amountIn) +// }); +// swapParamsData[1] = DataTypes.ExecuteSwapParamsData({ +// target: ROUTER, +// data: abi.encodeWithSelector( +// IRouter.exactInputSingle.selector, +// IRouter.ExactInputSingleParams({ +// tokenIn: USDT, +// tokenOut: USDC, +// fee: 100, +// recipient: address(superloop), +// amountIn: amountIn, +// amountOutMinimum: 0, +// sqrtPriceLimitX96: 0 +// }) +// ) +// }); + +// DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ +// tokenIn: tokenIn, +// tokenOut: tokenOut, +// amountIn: amountIn, +// maxAmountIn: maxAmountIn, +// minAmountOut: minAmountOut, +// data: swapParamsData +// }); + +// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); +// moduleExecutionData[0] = DataTypes.ModuleExecutionData({ +// executionType: DataTypes.CallType.DELEGATECALL, +// module: address(dexModule), +// data: abi.encodeWithSelector(dexModule.execute.selector, swapParams) +// }); + +// vm.startPrank(USDT_WHALE); +// IERC20(USDT).transfer(address(superloop), amountIn); +// vm.stopPrank(); + +// vm.startPrank(admin); +// superloop.operate(moduleExecutionData); +// vm.stopPrank(); + +// uint256 usdcBalance = IERC20(USDC).balanceOf(address(superloop)); +// assertGt(usdcBalance, 0); +// } + +// function test_executeSwapAndExit() public { +// vm.startPrank(USDT_WHALE); +// IERC20(USDT).transfer(address(user), 1000 * 10 ** 6); +// vm.stopPrank(); + +// address tokenIn = USDT; +// address tokenOut = USDC; + +// uint256 amountIn = 1000 * 10 ** 6; +// uint256 maxAmountIn = 1000 * 10 ** 6; +// uint256 minAmountOut = 900 * 10 ** 6; + +// DataTypes.ExecuteSwapParamsData[] memory data = new DataTypes.ExecuteSwapParamsData[](2); +// data[0] = DataTypes.ExecuteSwapParamsData({ +// target: USDT, +// data: abi.encodeWithSelector(IERC20.approve.selector, ROUTER, amountIn) +// }); +// data[1] = DataTypes.ExecuteSwapParamsData({ +// target: ROUTER, +// data: abi.encodeWithSelector( +// IRouter.exactInputSingle.selector, +// IRouter.ExactInputSingleParams({ +// tokenIn: USDT, +// tokenOut: USDC, +// fee: 100, +// recipient: address(dexModule), +// amountIn: amountIn, +// amountOutMinimum: 0, +// sqrtPriceLimitX96: 0 +// }) +// ) +// }); + +// DataTypes.ExecuteSwapParams memory params = DataTypes.ExecuteSwapParams({ +// tokenIn: tokenIn, +// tokenOut: tokenOut, +// amountIn: amountIn, +// maxAmountIn: maxAmountIn, +// minAmountOut: minAmountOut, +// data: data +// }); + +// vm.startPrank(user); +// IERC20(USDT).approve(address(dexModule), amountIn); +// uint256 amountOut = dexModule.executeAndExit(params, user); + +// vm.stopPrank(); + +// uint256 usdcBalanceUser = IERC20(USDC).balanceOf(address(user)); +// assertEq(usdcBalanceUser, amountOut); +// } +// } diff --git a/test/modules/fallback/AaveV3PreliquidationFallbackHandler.t.sol b/test/modules/fallback/AaveV3PreliquidationFallbackHandler.t.sol index 6a4ae79..f13c4d5 100644 --- a/test/modules/fallback/AaveV3PreliquidationFallbackHandler.t.sol +++ b/test/modules/fallback/AaveV3PreliquidationFallbackHandler.t.sol @@ -1,388 +1,388 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; - -import {TestBase} from "../../core/TestBase.sol"; -import {AaveV3PreliquidationFallbackHandler} from - "../../../src/modules/fallback/AaveV3PreliquidationFallbackHandler.sol"; -import {DataTypes} from "../../../src/common/DataTypes.sol"; -import {Errors} from "../../../src/common/Errors.sol"; -import {console} from "forge-std/console.sol"; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {IAaveOracle} from "aave-v3-core/contracts/interfaces/IAaveOracle.sol"; -import {IPoolAddressesProvider} from "aave-v3-core/contracts/interfaces/IPoolAddressesProvider.sol"; -import {IPoolConfigurator} from "aave-v3-core/contracts/interfaces/IPoolConfigurator.sol"; - -contract AaveV3PreliquidationFallbackHandlerTest is TestBase { - AaveV3PreliquidationFallbackHandler public preliquidation; - - function setUp() public override { - super.setUp(); - id = bytes32("1"); - LLTV = (7800 * WAD) / BPS; - preliquidation = new AaveV3PreliquidationFallbackHandler( - AAVE_V3_POOL_ADDRESSES_PROVIDER, - 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f, - 2, - 8, - DataTypes.AaveV3PreliquidationParamsInit({ - id: id, - lendReserve: ST_XTZ, - borrowReserve: XTZ, - preLltv: PRE_LLTV, - preCF1: PRE_CF1, - preCF2: PRE_CF2, - preIF1: PRE_IF1, - preIF2: PRE_IF2 - }) - ); - } - - function test_preliquidationParams() public view { - DataTypes.AaveV3PreliquidationParams memory preliquidationParams = - preliquidation.preliquidationParams(id, DataTypes.CallType.DELEGATECALL); - assertEq(preliquidationParams.lendReserve, ST_XTZ); - assertEq(preliquidationParams.borrowReserve, XTZ); - assertEq(preliquidationParams.Lltv, LLTV); - assertEq(preliquidationParams.preLltv, PRE_LLTV); - assertEq(preliquidationParams.preCF1, PRE_CF1); - assertEq(preliquidationParams.preCF2, PRE_CF2); - assertEq(preliquidationParams.preIF1, PRE_IF1); - assertEq(preliquidationParams.preIF2, PRE_IF2); - } - - function test_preliquidation() public { - deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); - deal(XTZ, address(preliquidation), 70 * 10 ** 18); - - vm.startPrank(address(preliquidation)); - IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); - pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); - pool.borrow(XTZ, 70 * 10 ** 18, 2, 0, address(preliquidation)); - vm.stopPrank(); - - deal(XTZ, address(this), 12 * 10 ** 18); - - uint256 stxtzBalanceBefore = IERC20(ST_XTZ).balanceOf(address(this)); - uint256 xtzBalanceBefore = IERC20(XTZ).balanceOf(address(this)); - - IAaveOracle oracle = IAaveOracle(IPoolAddressesProvider(AAVE_V3_POOL_ADDRESSES_PROVIDER).getPriceOracle()); - - // usd value of debt repaid - uint256 debtRepaid = 10 * 10 ** 18; - uint256 debtPriceUsd = oracle.getAssetPrice(XTZ); - uint256 debtRepaidUSD = debtRepaid * debtPriceUsd / (10 ** 18); - - IERC20(XTZ).approve(address(preliquidation), 12 * 10 ** 18); - preliquidation.preliquidate( - id, - DataTypes.CallType.DELEGATECALL, - DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 10 * 10 ** 18}) - ); - - uint256 stxtzBalanceAfter = IERC20(ST_XTZ).balanceOf(address(this)); - uint256 xtzBalanceAfter = IERC20(XTZ).balanceOf(address(this)); - - uint256 collateralToSieze = stxtzBalanceAfter - stxtzBalanceBefore; - uint256 collateralPriceUsd = oracle.getAssetPrice(ST_XTZ); - uint256 collateralToSiezeUSD = collateralToSieze * collateralPriceUsd / (10 ** 6); - - // compare collateralToSiezeUSD and debtRepaidUSD - uint256 incentiveWAD = ((collateralToSiezeUSD) * WAD) / debtRepaidUSD; - assertTrue(incentiveWAD > PRE_IF1 && incentiveWAD < PRE_IF2); - assertEq(xtzBalanceAfter, xtzBalanceBefore - 10 * 10 ** 18); - } - - // ============ REVERTING CASES ============ - - function test_preliquidation_revert_invalid_lltv() public { - deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); - deal(XTZ, address(preliquidation), 70 * 10 ** 18); - - vm.startPrank(address(preliquidation)); - IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); - pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); - pool.borrow(XTZ, 70 * 10 ** 18, 2, 0, address(preliquidation)); - vm.stopPrank(); - - deal(XTZ, address(this), 12 * 10 ** 18); - - // updat emode ltv to 9900 => it should revert - vm.prank(POOL_ADMIN); - IPoolConfigurator(POOL_CONFIGURATOR).configureReserveAsCollateral(ST_XTZ, 9000, 9200, 10100); - - IERC20(XTZ).approve(address(preliquidation), 12 * 10 ** 18); - - vm.expectRevert(bytes(Errors.AAVE_V3_PRELIQUIDATION_INVALID_LLTV)); - preliquidation.preliquidate( - id, - DataTypes.CallType.DELEGATECALL, - DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 10 * 10 ** 18}) - ); - } - - function test_preliquidate_revert_invalid_id() public { - deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); - deal(XTZ, address(preliquidation), 70 * 10 ** 18); - - vm.startPrank(address(preliquidation)); - IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); - pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); - pool.borrow(XTZ, 70 * 10 ** 18, 2, 0, address(preliquidation)); - vm.stopPrank(); - - deal(XTZ, address(this), 12 * 10 ** 18); - IERC20(XTZ).approve(address(preliquidation), 12 * 10 ** 18); - - bytes32 wrongId = bytes32("wrong_id"); - vm.expectRevert(bytes(Errors.PRELIQUIDATION_INVALID_ID)); - preliquidation.preliquidate( - wrongId, - DataTypes.CallType.DELEGATECALL, - DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 10 * 10 ** 18}) - ); - } - - function test_preliquidate_revert_invalid_user() public { - deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); - deal(XTZ, address(preliquidation), 70 * 10 ** 18); - - vm.startPrank(address(preliquidation)); - IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); - pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); - pool.borrow(XTZ, 70 * 10 ** 18, 2, 0, address(preliquidation)); - vm.stopPrank(); - - deal(XTZ, address(this), 12 * 10 ** 18); - IERC20(XTZ).approve(address(preliquidation), 12 * 10 ** 18); - - address wrongUser = makeAddr("wrongUser"); - vm.expectRevert(bytes(Errors.PRELIQUIDATION_INVALID_USER)); - preliquidation.preliquidate( - id, - DataTypes.CallType.DELEGATECALL, - DataTypes.AaveV3ExecutePreliquidationParams({user: wrongUser, debtToCover: 10 * 10 ** 18}) - ); - } - - function test_preliquidate_revert_possible_bad_debt() public { - // Set up position with LTV > LLTV (bad debt scenario) - deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); - deal(XTZ, address(preliquidation), 80 * 10 ** 18); // High borrow amount - - vm.startPrank(address(preliquidation)); - IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); - pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); - vm.expectRevert(bytes(Errors.INSUFFICIENT_CASH_SHORTFALL)); - pool.borrow(XTZ, 80 * 10 ** 18, 2, 0, address(preliquidation)); - vm.stopPrank(); - } - - function test_preliquidate_revert_not_in_preliquidation_state() public { - // Set up position with LTV <= preLltv (not in preliquidation state) - deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); - deal(XTZ, address(preliquidation), 40 * 10 ** 18); // Low borrow amount - - vm.startPrank(address(preliquidation)); - IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); - pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); - pool.borrow(XTZ, 40 * 10 ** 18, 2, 0, address(preliquidation)); - vm.stopPrank(); - - deal(XTZ, address(this), 12 * 10 ** 18); - IERC20(XTZ).approve(address(preliquidation), 12 * 10 ** 18); - - vm.expectRevert(bytes(Errors.PRELIQUIDATION_NOT_IN_PRELIQUIDATION_STATE)); - preliquidation.preliquidate( - id, - DataTypes.CallType.DELEGATECALL, - DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 10 * 10 ** 18}) - ); - } - - // // ============ EDGE CASES ============ - - function test_preliquidate_edge_case_ltv_equals_prelltv() public { - // Set up position with LTV = preLltv (exactly at the boundary) - deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); - - // Calculate exact borrow amount to achieve LTV = preLltv - // We need: borrowUsdWAD = collateralUsdWAD * preLltv - // For simplicity, we'll use a value that should be close to the boundary - uint256 borrowAmount = 50 * 10 ** 18; // This should be close to preLltv boundary - - deal(XTZ, address(preliquidation), borrowAmount); - - vm.startPrank(address(preliquidation)); - IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); - pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); - pool.borrow(XTZ, borrowAmount, 2, 0, address(preliquidation)); - vm.stopPrank(); - - deal(XTZ, address(this), 12 * 10 ** 18); - IERC20(XTZ).approve(address(preliquidation), 12 * 10 ** 18); - - // This should revert because LTV <= preLltv - vm.expectRevert(bytes(Errors.PRELIQUIDATION_NOT_IN_PRELIQUIDATION_STATE)); - preliquidation.preliquidate( - id, - DataTypes.CallType.DELEGATECALL, - DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 5 * 10 ** 18}) - ); - } - - function test_preliquidate_edge_case_ltv_equals_lltv() public { - // Set up position with LTV = Lltv (exactly at the liquidation threshold) - deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); - - // Calculate exact borrow amount to achieve LTV = Lltv - uint256 borrowAmount = 74 * 10 ** 18; // This should be close to Lltv boundary - - deal(XTZ, address(preliquidation), borrowAmount); - - vm.startPrank(address(preliquidation)); - IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); - pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); - pool.borrow(XTZ, borrowAmount, 2, 0, address(preliquidation)); - vm.stopPrank(); - - deal(XTZ, address(this), 12 * 10 ** 18); - IERC20(XTZ).approve(address(preliquidation), 12 * 10 ** 18); - - // This should succeed as it's exactly at the boundary - preliquidation.preliquidate( - id, - DataTypes.CallType.DELEGATECALL, - DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 5 * 10 ** 18}) - ); - } - - function test_preliquidate_edge_case_trying_to_liquidate_max() public { - deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); - deal(XTZ, address(this), 70 * 10 ** 18); - - vm.startPrank(address(preliquidation)); - IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); - pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); - pool.borrow(XTZ, 70 * 10 ** 18, 2, 0, address(preliquidation)); - vm.stopPrank(); - - // try to liquidate 70xtz, but i should be able to do only till close factor - - uint256 xtzBalanceBefore = IERC20(XTZ).balanceOf(address(this)); - uint256 stxtzBalanceBefore = IERC20(ST_XTZ).balanceOf(address(this)); - - IERC20(XTZ).approve(address(preliquidation), 70 * 10 ** 18); - preliquidation.preliquidate( - id, - DataTypes.CallType.DELEGATECALL, - DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 70 * 10 ** 18}) - ); - - uint256 xtzBalanceAfter = IERC20(XTZ).balanceOf(address(this)); - uint256 stxtzBalanceAfter = IERC20(ST_XTZ).balanceOf(address(this)); - - uint256 collateralSeized = stxtzBalanceAfter - stxtzBalanceBefore; - - assertTrue(collateralSeized < 40 * 10 ** 6); - assertTrue(xtzBalanceBefore - xtzBalanceAfter < 40 * 10 ** 18); - } - - // ============ LTV IN RANGE TESTS ============ - - function test_preliquidate_ltv_low_range() public { - // Set up position with LTV slightly above preLltv - deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); - uint256 borrowAmount = 55 * 10 ** 18; // Between preLltv and Lltv - - deal(XTZ, address(preliquidation), borrowAmount); - - vm.startPrank(address(preliquidation)); - IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); - pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); - pool.borrow(XTZ, borrowAmount, 2, 0, address(preliquidation)); - vm.stopPrank(); - - deal(XTZ, address(this), 12 * 10 ** 18); - IERC20(XTZ).approve(address(preliquidation), 12 * 10 ** 18); - - preliquidation.preliquidate( - id, - DataTypes.CallType.DELEGATECALL, - DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 5 * 10 ** 18}) - ); - } - - function test_preliquidate_ltv_mid_range() public { - // Set up position with LTV in the middle of preLltv and Lltv - deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); - uint256 borrowAmount = 65 * 10 ** 18; // Middle of the range - - deal(XTZ, address(preliquidation), borrowAmount); - - vm.startPrank(address(preliquidation)); - IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); - pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); - pool.borrow(XTZ, borrowAmount, 2, 0, address(preliquidation)); - vm.stopPrank(); - - deal(XTZ, address(this), 12 * 10 ** 18); - IERC20(XTZ).approve(address(preliquidation), 12 * 10 ** 18); - - preliquidation.preliquidate( - id, - DataTypes.CallType.DELEGATECALL, - DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 5 * 10 ** 18}) - ); - } - - function test_preliquidate_ltv_high_range() public { - // Set up position with LTV close to Lltv - deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); - uint256 borrowAmount = 74 * 10 ** 18; // Close to Lltv - - deal(XTZ, address(preliquidation), borrowAmount); - - vm.startPrank(address(preliquidation)); - IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); - pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); - pool.borrow(XTZ, borrowAmount, 2, 0, address(preliquidation)); - vm.stopPrank(); - - deal(XTZ, address(this), 12 * 10 ** 18); - IERC20(XTZ).approve(address(preliquidation), 12 * 10 ** 18); - - preliquidation.preliquidate( - id, - DataTypes.CallType.DELEGATECALL, - DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 5 * 10 ** 18}) - ); - } - - // ============ E-MODE TESTS ============ - - function test_preliquidate_e_mode() public { - address newPreLiq = 0x2e234DAe75C793f67A35089C9d99245E1C58470b; - vm.prank(address(newPreLiq)); - pool.setUserEMode(3); - preliquidation = new AaveV3PreliquidationFallbackHandler( - AAVE_V3_POOL_ADDRESSES_PROVIDER, - newPreLiq, - 2, - 8, - DataTypes.AaveV3PreliquidationParamsInit({ - id: id, - lendReserve: ST_XTZ, - borrowReserve: XTZ, - preLltv: PRE_LLTV, - preCF1: PRE_CF1, - preCF2: PRE_CF2, - preIF1: PRE_IF1, - preIF2: PRE_IF2 - }) - ); - - DataTypes.AaveV3PreliquidationParams memory preliquidationParams = - preliquidation.preliquidationParams(id, DataTypes.CallType.DELEGATECALL); - assertEq(preliquidationParams.Lltv, (9600 * WAD) / BPS); - } -} +// // SPDX-License-Identifier: MIT +// pragma solidity ^0.8.13; + +// import {TestBase} from "../../core/TestBase.sol"; +// import {AaveV3PreliquidationFallbackHandler} from +// "../../../src/modules/fallback/AaveV3PreliquidationFallbackHandler.sol"; +// import {DataTypes} from "../../../src/common/DataTypes.sol"; +// import {Errors} from "../../../src/common/Errors.sol"; +// import {console} from "forge-std/console.sol"; +// import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +// import {IAaveOracle} from "aave-v3-core/contracts/interfaces/IAaveOracle.sol"; +// import {IPoolAddressesProvider} from "aave-v3-core/contracts/interfaces/IPoolAddressesProvider.sol"; +// import {IPoolConfigurator} from "aave-v3-core/contracts/interfaces/IPoolConfigurator.sol"; + +// contract AaveV3PreliquidationFallbackHandlerTest is TestBase { +// AaveV3PreliquidationFallbackHandler public preliquidation; + +// function setUp() public override { +// super.setUp(); +// id = bytes32("1"); +// LLTV = (7800 * WAD) / BPS; +// preliquidation = new AaveV3PreliquidationFallbackHandler( +// AAVE_V3_POOL_ADDRESSES_PROVIDER, +// 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f, +// 2, +// 8, +// DataTypes.AaveV3PreliquidationParamsInit({ +// id: id, +// lendReserve: ST_XTZ, +// borrowReserve: XTZ, +// preLltv: PRE_LLTV, +// preCF1: PRE_CF1, +// preCF2: PRE_CF2, +// preIF1: PRE_IF1, +// preIF2: PRE_IF2 +// }) +// ); +// } + +// function test_preliquidationParams() public view { +// DataTypes.AaveV3PreliquidationParams memory preliquidationParams = +// preliquidation.preliquidationParams(id, DataTypes.CallType.DELEGATECALL); +// assertEq(preliquidationParams.lendReserve, ST_XTZ); +// assertEq(preliquidationParams.borrowReserve, XTZ); +// assertEq(preliquidationParams.Lltv, LLTV); +// assertEq(preliquidationParams.preLltv, PRE_LLTV); +// assertEq(preliquidationParams.preCF1, PRE_CF1); +// assertEq(preliquidationParams.preCF2, PRE_CF2); +// assertEq(preliquidationParams.preIF1, PRE_IF1); +// assertEq(preliquidationParams.preIF2, PRE_IF2); +// } + +// function test_preliquidation() public { +// deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); +// deal(XTZ, address(preliquidation), 70 * 10 ** 18); + +// vm.startPrank(address(preliquidation)); +// IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); +// pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); +// pool.borrow(XTZ, 70 * 10 ** 18, 2, 0, address(preliquidation)); +// vm.stopPrank(); + +// deal(XTZ, address(this), 12 * 10 ** 18); + +// uint256 stxtzBalanceBefore = IERC20(ST_XTZ).balanceOf(address(this)); +// uint256 xtzBalanceBefore = IERC20(XTZ).balanceOf(address(this)); + +// IAaveOracle oracle = IAaveOracle(IPoolAddressesProvider(AAVE_V3_POOL_ADDRESSES_PROVIDER).getPriceOracle()); + +// // usd value of debt repaid +// uint256 debtRepaid = 10 * 10 ** 18; +// uint256 debtPriceUsd = oracle.getAssetPrice(XTZ); +// uint256 debtRepaidUSD = debtRepaid * debtPriceUsd / (10 ** 18); + +// IERC20(XTZ).approve(address(preliquidation), 12 * 10 ** 18); +// preliquidation.preliquidate( +// id, +// DataTypes.CallType.DELEGATECALL, +// DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 10 * 10 ** 18}) +// ); + +// uint256 stxtzBalanceAfter = IERC20(ST_XTZ).balanceOf(address(this)); +// uint256 xtzBalanceAfter = IERC20(XTZ).balanceOf(address(this)); + +// uint256 collateralToSieze = stxtzBalanceAfter - stxtzBalanceBefore; +// uint256 collateralPriceUsd = oracle.getAssetPrice(ST_XTZ); +// uint256 collateralToSiezeUSD = collateralToSieze * collateralPriceUsd / (10 ** 6); + +// // compare collateralToSiezeUSD and debtRepaidUSD +// uint256 incentiveWAD = ((collateralToSiezeUSD) * WAD) / debtRepaidUSD; +// assertTrue(incentiveWAD > PRE_IF1 && incentiveWAD < PRE_IF2); +// assertEq(xtzBalanceAfter, xtzBalanceBefore - 10 * 10 ** 18); +// } + +// // ============ REVERTING CASES ============ + +// function test_preliquidation_revert_invalid_lltv() public { +// deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); +// deal(XTZ, address(preliquidation), 70 * 10 ** 18); + +// vm.startPrank(address(preliquidation)); +// IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); +// pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); +// pool.borrow(XTZ, 70 * 10 ** 18, 2, 0, address(preliquidation)); +// vm.stopPrank(); + +// deal(XTZ, address(this), 12 * 10 ** 18); + +// // updat emode ltv to 9900 => it should revert +// vm.prank(POOL_ADMIN); +// IPoolConfigurator(POOL_CONFIGURATOR).configureReserveAsCollateral(ST_XTZ, 9000, 9200, 10100); + +// IERC20(XTZ).approve(address(preliquidation), 12 * 10 ** 18); + +// vm.expectRevert(bytes(Errors.AAVE_V3_PRELIQUIDATION_INVALID_LLTV)); +// preliquidation.preliquidate( +// id, +// DataTypes.CallType.DELEGATECALL, +// DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 10 * 10 ** 18}) +// ); +// } + +// function test_preliquidate_revert_invalid_id() public { +// deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); +// deal(XTZ, address(preliquidation), 70 * 10 ** 18); + +// vm.startPrank(address(preliquidation)); +// IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); +// pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); +// pool.borrow(XTZ, 70 * 10 ** 18, 2, 0, address(preliquidation)); +// vm.stopPrank(); + +// deal(XTZ, address(this), 12 * 10 ** 18); +// IERC20(XTZ).approve(address(preliquidation), 12 * 10 ** 18); + +// bytes32 wrongId = bytes32("wrong_id"); +// vm.expectRevert(bytes(Errors.PRELIQUIDATION_INVALID_ID)); +// preliquidation.preliquidate( +// wrongId, +// DataTypes.CallType.DELEGATECALL, +// DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 10 * 10 ** 18}) +// ); +// } + +// function test_preliquidate_revert_invalid_user() public { +// deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); +// deal(XTZ, address(preliquidation), 70 * 10 ** 18); + +// vm.startPrank(address(preliquidation)); +// IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); +// pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); +// pool.borrow(XTZ, 70 * 10 ** 18, 2, 0, address(preliquidation)); +// vm.stopPrank(); + +// deal(XTZ, address(this), 12 * 10 ** 18); +// IERC20(XTZ).approve(address(preliquidation), 12 * 10 ** 18); + +// address wrongUser = makeAddr("wrongUser"); +// vm.expectRevert(bytes(Errors.PRELIQUIDATION_INVALID_USER)); +// preliquidation.preliquidate( +// id, +// DataTypes.CallType.DELEGATECALL, +// DataTypes.AaveV3ExecutePreliquidationParams({user: wrongUser, debtToCover: 10 * 10 ** 18}) +// ); +// } + +// function test_preliquidate_revert_possible_bad_debt() public { +// // Set up position with LTV > LLTV (bad debt scenario) +// deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); +// deal(XTZ, address(preliquidation), 80 * 10 ** 18); // High borrow amount + +// vm.startPrank(address(preliquidation)); +// IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); +// pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); +// vm.expectRevert(bytes(Errors.INSUFFICIENT_CASH_SHORTFALL)); +// pool.borrow(XTZ, 80 * 10 ** 18, 2, 0, address(preliquidation)); +// vm.stopPrank(); +// } + +// function test_preliquidate_revert_not_in_preliquidation_state() public { +// // Set up position with LTV <= preLltv (not in preliquidation state) +// deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); +// deal(XTZ, address(preliquidation), 40 * 10 ** 18); // Low borrow amount + +// vm.startPrank(address(preliquidation)); +// IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); +// pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); +// pool.borrow(XTZ, 40 * 10 ** 18, 2, 0, address(preliquidation)); +// vm.stopPrank(); + +// deal(XTZ, address(this), 12 * 10 ** 18); +// IERC20(XTZ).approve(address(preliquidation), 12 * 10 ** 18); + +// vm.expectRevert(bytes(Errors.PRELIQUIDATION_NOT_IN_PRELIQUIDATION_STATE)); +// preliquidation.preliquidate( +// id, +// DataTypes.CallType.DELEGATECALL, +// DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 10 * 10 ** 18}) +// ); +// } + +// // // ============ EDGE CASES ============ + +// function test_preliquidate_edge_case_ltv_equals_prelltv() public { +// // Set up position with LTV = preLltv (exactly at the boundary) +// deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); + +// // Calculate exact borrow amount to achieve LTV = preLltv +// // We need: borrowUsdWAD = collateralUsdWAD * preLltv +// // For simplicity, we'll use a value that should be close to the boundary +// uint256 borrowAmount = 50 * 10 ** 18; // This should be close to preLltv boundary + +// deal(XTZ, address(preliquidation), borrowAmount); + +// vm.startPrank(address(preliquidation)); +// IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); +// pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); +// pool.borrow(XTZ, borrowAmount, 2, 0, address(preliquidation)); +// vm.stopPrank(); + +// deal(XTZ, address(this), 12 * 10 ** 18); +// IERC20(XTZ).approve(address(preliquidation), 12 * 10 ** 18); + +// // This should revert because LTV <= preLltv +// vm.expectRevert(bytes(Errors.PRELIQUIDATION_NOT_IN_PRELIQUIDATION_STATE)); +// preliquidation.preliquidate( +// id, +// DataTypes.CallType.DELEGATECALL, +// DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 5 * 10 ** 18}) +// ); +// } + +// function test_preliquidate_edge_case_ltv_equals_lltv() public { +// // Set up position with LTV = Lltv (exactly at the liquidation threshold) +// deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); + +// // Calculate exact borrow amount to achieve LTV = Lltv +// uint256 borrowAmount = 74 * 10 ** 18; // This should be close to Lltv boundary + +// deal(XTZ, address(preliquidation), borrowAmount); + +// vm.startPrank(address(preliquidation)); +// IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); +// pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); +// pool.borrow(XTZ, borrowAmount, 2, 0, address(preliquidation)); +// vm.stopPrank(); + +// deal(XTZ, address(this), 12 * 10 ** 18); +// IERC20(XTZ).approve(address(preliquidation), 12 * 10 ** 18); + +// // This should succeed as it's exactly at the boundary +// preliquidation.preliquidate( +// id, +// DataTypes.CallType.DELEGATECALL, +// DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 5 * 10 ** 18}) +// ); +// } + +// function test_preliquidate_edge_case_trying_to_liquidate_max() public { +// deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); +// deal(XTZ, address(this), 70 * 10 ** 18); + +// vm.startPrank(address(preliquidation)); +// IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); +// pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); +// pool.borrow(XTZ, 70 * 10 ** 18, 2, 0, address(preliquidation)); +// vm.stopPrank(); + +// // try to liquidate 70xtz, but i should be able to do only till close factor + +// uint256 xtzBalanceBefore = IERC20(XTZ).balanceOf(address(this)); +// uint256 stxtzBalanceBefore = IERC20(ST_XTZ).balanceOf(address(this)); + +// IERC20(XTZ).approve(address(preliquidation), 70 * 10 ** 18); +// preliquidation.preliquidate( +// id, +// DataTypes.CallType.DELEGATECALL, +// DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 70 * 10 ** 18}) +// ); + +// uint256 xtzBalanceAfter = IERC20(XTZ).balanceOf(address(this)); +// uint256 stxtzBalanceAfter = IERC20(ST_XTZ).balanceOf(address(this)); + +// uint256 collateralSeized = stxtzBalanceAfter - stxtzBalanceBefore; + +// assertTrue(collateralSeized < 40 * 10 ** 6); +// assertTrue(xtzBalanceBefore - xtzBalanceAfter < 40 * 10 ** 18); +// } + +// // ============ LTV IN RANGE TESTS ============ + +// function test_preliquidate_ltv_low_range() public { +// // Set up position with LTV slightly above preLltv +// deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); +// uint256 borrowAmount = 55 * 10 ** 18; // Between preLltv and Lltv + +// deal(XTZ, address(preliquidation), borrowAmount); + +// vm.startPrank(address(preliquidation)); +// IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); +// pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); +// pool.borrow(XTZ, borrowAmount, 2, 0, address(preliquidation)); +// vm.stopPrank(); + +// deal(XTZ, address(this), 12 * 10 ** 18); +// IERC20(XTZ).approve(address(preliquidation), 12 * 10 ** 18); + +// preliquidation.preliquidate( +// id, +// DataTypes.CallType.DELEGATECALL, +// DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 5 * 10 ** 18}) +// ); +// } + +// function test_preliquidate_ltv_mid_range() public { +// // Set up position with LTV in the middle of preLltv and Lltv +// deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); +// uint256 borrowAmount = 65 * 10 ** 18; // Middle of the range + +// deal(XTZ, address(preliquidation), borrowAmount); + +// vm.startPrank(address(preliquidation)); +// IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); +// pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); +// pool.borrow(XTZ, borrowAmount, 2, 0, address(preliquidation)); +// vm.stopPrank(); + +// deal(XTZ, address(this), 12 * 10 ** 18); +// IERC20(XTZ).approve(address(preliquidation), 12 * 10 ** 18); + +// preliquidation.preliquidate( +// id, +// DataTypes.CallType.DELEGATECALL, +// DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 5 * 10 ** 18}) +// ); +// } + +// function test_preliquidate_ltv_high_range() public { +// // Set up position with LTV close to Lltv +// deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); +// uint256 borrowAmount = 74 * 10 ** 18; // Close to Lltv + +// deal(XTZ, address(preliquidation), borrowAmount); + +// vm.startPrank(address(preliquidation)); +// IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); +// pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); +// pool.borrow(XTZ, borrowAmount, 2, 0, address(preliquidation)); +// vm.stopPrank(); + +// deal(XTZ, address(this), 12 * 10 ** 18); +// IERC20(XTZ).approve(address(preliquidation), 12 * 10 ** 18); + +// preliquidation.preliquidate( +// id, +// DataTypes.CallType.DELEGATECALL, +// DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 5 * 10 ** 18}) +// ); +// } + +// // ============ E-MODE TESTS ============ + +// function test_preliquidate_e_mode() public { +// address newPreLiq = 0x2e234DAe75C793f67A35089C9d99245E1C58470b; +// vm.prank(address(newPreLiq)); +// pool.setUserEMode(3); +// preliquidation = new AaveV3PreliquidationFallbackHandler( +// AAVE_V3_POOL_ADDRESSES_PROVIDER, +// newPreLiq, +// 2, +// 8, +// DataTypes.AaveV3PreliquidationParamsInit({ +// id: id, +// lendReserve: ST_XTZ, +// borrowReserve: XTZ, +// preLltv: PRE_LLTV, +// preCF1: PRE_CF1, +// preCF2: PRE_CF2, +// preIF1: PRE_IF1, +// preIF2: PRE_IF2 +// }) +// ); + +// DataTypes.AaveV3PreliquidationParams memory preliquidationParams = +// preliquidation.preliquidationParams(id, DataTypes.CallType.DELEGATECALL); +// assertEq(preliquidationParams.Lltv, (9600 * WAD) / BPS); +// } +// } diff --git a/test/modules/helpers/UnwrapModule.t.sol b/test/modules/helpers/UnwrapModule.t.sol index 4b7cd1c..6ccc6b5 100644 --- a/test/modules/helpers/UnwrapModule.t.sol +++ b/test/modules/helpers/UnwrapModule.t.sol @@ -1,104 +1,104 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.13; - -import {TestBase} from "../../core/TestBase.sol"; -import {IFlashLoanSimpleReceiver} from "aave-v3-core/contracts/flashloan/interfaces/IFlashLoanSimpleReceiver.sol"; -import {DataTypes} from "../../../src/common/DataTypes.sol"; -import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; -import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; -import {TransparentUpgradeableProxy} from - "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -import {console} from "forge-std/console.sol"; - -contract UnwrapModuleTest is TestBase { - Superloop public superloopImplementation; - ProxyAdmin public proxyAdmin; - address public user; - - function setUp() public override { - super.setUp(); - - vm.startPrank(admin); - _deployModules(); - - address[] memory modules = new address[](1); - modules[0] = address(unwrapModule); - - DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ - asset: XTZ, - name: "XTZ Vault", - symbol: "XTZV", - supplyCap: 100000 * 10 ** 18, - minimumDepositAmount: 100, - instantWithdrawFee: 0, - superloopModuleRegistry: address(moduleRegistry), - modules: modules, - accountant: mockModule, - withdrawManager: mockModule, - depositManager: mockModule, - cashReserve: 1000, - vaultAdmin: admin, - treasury: treasury, - vaultOperator: admin - }); - superloopImplementation = new Superloop(); - proxyAdmin = new ProxyAdmin(address(this)); - - TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( - address(superloopImplementation), - address(proxyAdmin), - abi.encodeWithSelector(Superloop.initialize.selector, initData) - ); - superloop = Superloop(payable(address(proxy))); - vm.stopPrank(); - - user = makeAddr("user"); - vm.label(user, "user"); - vm.label(address(superloop), "superloop"); - } - - function test_SupplyBasicFlow() public { - vm.startPrank(XTZ_WHALE); - IERC20(XTZ).transfer(address(superloop), 1000 * 10 ** 18); - vm.stopPrank(); - - // Arrange - uint256 supplyAmount = 100 * 10 ** 18; // 1000 XTZ - - // Create supply params - DataTypes.AaveV3ActionParams memory unwarpParams = - DataTypes.AaveV3ActionParams({asset: XTZ, amount: supplyAmount}); - - // Create module execution data - DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); - moduleExecutionData[0] = DataTypes.ModuleExecutionData({ - executionType: DataTypes.CallType.DELEGATECALL, - module: address(unwrapModule), - data: abi.encodeWithSelector(unwrapModule.execute.selector, unwarpParams) - }); - - // current supply - uint256 currentBalanceWrappedToken = IERC20(XTZ).balanceOf(address(superloop)); - uint256 currentBalanceUnderlyingToken = address(superloop).balance; - - // Act - vm.prank(admin); - superloop.operate(moduleExecutionData); - - uint256 finalBalanceWrappedToken = IERC20(XTZ).balanceOf(address(superloop)); - uint256 finalBalanceUnderlyingToken = address(superloop).balance; - - console.log("currentBalanceWrappedToken", currentBalanceWrappedToken); - console.log("currentBalanceUnderlyingToken", currentBalanceUnderlyingToken); - console.log("finalBalanceWrappedToken", finalBalanceWrappedToken); - console.log("finalBalanceUnderlyingToken", finalBalanceUnderlyingToken); - - console.log("supplyAmount", supplyAmount); - - // Assert - assertEq(finalBalanceWrappedToken, currentBalanceWrappedToken - supplyAmount); - assertEq(finalBalanceUnderlyingToken, currentBalanceUnderlyingToken + supplyAmount); - } -} +// // SPDX-License-Identifier: MIT + +// pragma solidity ^0.8.13; + +// import {TestBase} from "../../core/TestBase.sol"; +// import {IFlashLoanSimpleReceiver} from "aave-v3-core/contracts/flashloan/interfaces/IFlashLoanSimpleReceiver.sol"; +// import {DataTypes} from "../../../src/common/DataTypes.sol"; +// import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; +// import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; +// import {TransparentUpgradeableProxy} from +// "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +// import {console} from "forge-std/console.sol"; + +// contract UnwrapModuleTest is TestBase { +// Superloop public superloopImplementation; +// ProxyAdmin public proxyAdmin; +// address public user; + +// function setUp() public override { +// super.setUp(); + +// vm.startPrank(admin); +// _deployModules(); + +// address[] memory modules = new address[](1); +// modules[0] = address(unwrapModule); + +// DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ +// asset: XTZ, +// name: "XTZ Vault", +// symbol: "XTZV", +// supplyCap: 100000 * 10 ** 18, +// minimumDepositAmount: 100, +// instantWithdrawFee: 0, +// superloopModuleRegistry: address(moduleRegistry), +// modules: modules, +// accountant: mockModule, +// withdrawManager: mockModule, +// depositManager: mockModule, +// cashReserve: 1000, +// vaultAdmin: admin, +// treasury: treasury, +// vaultOperator: admin +// }); +// superloopImplementation = new Superloop(); +// proxyAdmin = new ProxyAdmin(address(this)); + +// TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( +// address(superloopImplementation), +// address(proxyAdmin), +// abi.encodeWithSelector(Superloop.initialize.selector, initData) +// ); +// superloop = Superloop(payable(address(proxy))); +// vm.stopPrank(); + +// user = makeAddr("user"); +// vm.label(user, "user"); +// vm.label(address(superloop), "superloop"); +// } + +// function test_SupplyBasicFlow() public { +// vm.startPrank(XTZ_WHALE); +// IERC20(XTZ).transfer(address(superloop), 1000 * 10 ** 18); +// vm.stopPrank(); + +// // Arrange +// uint256 supplyAmount = 100 * 10 ** 18; // 1000 XTZ + +// // Create supply params +// DataTypes.AaveV3ActionParams memory unwarpParams = +// DataTypes.AaveV3ActionParams({asset: XTZ, amount: supplyAmount}); + +// // Create module execution data +// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); +// moduleExecutionData[0] = DataTypes.ModuleExecutionData({ +// executionType: DataTypes.CallType.DELEGATECALL, +// module: address(unwrapModule), +// data: abi.encodeWithSelector(unwrapModule.execute.selector, unwarpParams) +// }); + +// // current supply +// uint256 currentBalanceWrappedToken = IERC20(XTZ).balanceOf(address(superloop)); +// uint256 currentBalanceUnderlyingToken = address(superloop).balance; + +// // Act +// vm.prank(admin); +// superloop.operate(moduleExecutionData); + +// uint256 finalBalanceWrappedToken = IERC20(XTZ).balanceOf(address(superloop)); +// uint256 finalBalanceUnderlyingToken = address(superloop).balance; + +// console.log("currentBalanceWrappedToken", currentBalanceWrappedToken); +// console.log("currentBalanceUnderlyingToken", currentBalanceUnderlyingToken); +// console.log("finalBalanceWrappedToken", finalBalanceWrappedToken); +// console.log("finalBalanceUnderlyingToken", finalBalanceUnderlyingToken); + +// console.log("supplyAmount", supplyAmount); + +// // Assert +// assertEq(finalBalanceWrappedToken, currentBalanceWrappedToken - supplyAmount); +// assertEq(finalBalanceUnderlyingToken, currentBalanceUnderlyingToken + supplyAmount); +// } +// } diff --git a/test/modules/helpers/WrapModule.t.sol b/test/modules/helpers/WrapModule.t.sol index 9c9261e..a7be9a0 100644 --- a/test/modules/helpers/WrapModule.t.sol +++ b/test/modules/helpers/WrapModule.t.sol @@ -1,123 +1,123 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.13; - -import {TestBase} from "../../core/TestBase.sol"; -import {IFlashLoanSimpleReceiver} from "aave-v3-core/contracts/flashloan/interfaces/IFlashLoanSimpleReceiver.sol"; -import {DataTypes} from "../../../src/common/DataTypes.sol"; -import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; -import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; -import {TransparentUpgradeableProxy} from - "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -import {console} from "forge-std/console.sol"; - -contract WrapModuleTest is TestBase { - Superloop public superloopImplementation; - ProxyAdmin public proxyAdmin; - address public user; - - function setUp() public override { - super.setUp(); - - vm.startPrank(admin); - _deployModules(); - - address[] memory modules = new address[](2); - modules[0] = address(unwrapModule); - modules[1] = address(wrapModule); - - DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ - asset: XTZ, - name: "XTZ Vault", - symbol: "XTZV", - supplyCap: 100000 * 10 ** 18, - minimumDepositAmount: 100, - instantWithdrawFee: 0, - superloopModuleRegistry: address(moduleRegistry), - modules: modules, - accountant: mockModule, - withdrawManager: mockModule, - depositManager: mockModule, - cashReserve: 1000, - vaultAdmin: admin, - treasury: treasury, - vaultOperator: admin - }); - superloopImplementation = new Superloop(); - proxyAdmin = new ProxyAdmin(address(this)); - - TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( - address(superloopImplementation), - address(proxyAdmin), - abi.encodeWithSelector(Superloop.initialize.selector, initData) - ); - superloop = Superloop(payable(address(proxy))); - vm.stopPrank(); - - user = makeAddr("user"); - vm.label(user, "user"); - vm.label(address(superloop), "superloop"); - } - - function test_SupplyBasicFlow() public { - vm.startPrank(XTZ_WHALE); - IERC20(XTZ).transfer(address(superloop), 1000 * 10 ** 18); - vm.stopPrank(); - - uint256 supplyAmount = 100 * 10 ** 18; - _executeUnwrap(supplyAmount); - - // warp - DataTypes.AaveV3ActionParams memory wrapParams = - DataTypes.AaveV3ActionParams({asset: XTZ, amount: supplyAmount}); - - DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); - moduleExecutionData[0] = DataTypes.ModuleExecutionData({ - executionType: DataTypes.CallType.DELEGATECALL, - module: address(wrapModule), - data: abi.encodeWithSelector(wrapModule.execute.selector, wrapParams) - }); - - uint256 currentBalanceWrappedToken = IERC20(XTZ).balanceOf(address(superloop)); - uint256 currentBalanceUnderlyingToken = address(superloop).balance; - - vm.prank(admin); - superloop.operate(moduleExecutionData); - - uint256 finalBalanceWrappedToken = IERC20(XTZ).balanceOf(address(superloop)); - uint256 finalBalanceUnderlyingToken = address(superloop).balance; - - assertEq(finalBalanceWrappedToken, currentBalanceWrappedToken + supplyAmount); - assertEq(finalBalanceUnderlyingToken, currentBalanceUnderlyingToken - supplyAmount); - } - - function _executeUnwrap(uint256 supplyAmount) internal { - // Create supply params - DataTypes.AaveV3ActionParams memory unwarpParams = - DataTypes.AaveV3ActionParams({asset: XTZ, amount: supplyAmount}); - - // Create module execution data - DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); - moduleExecutionData[0] = DataTypes.ModuleExecutionData({ - executionType: DataTypes.CallType.DELEGATECALL, - module: address(unwrapModule), - data: abi.encodeWithSelector(unwrapModule.execute.selector, unwarpParams) - }); - - // current supply - uint256 currentBalanceWrappedToken = IERC20(XTZ).balanceOf(address(superloop)); - uint256 currentBalanceUnderlyingToken = address(superloop).balance; - - // Act - vm.prank(admin); - superloop.operate(moduleExecutionData); - - uint256 finalBalanceWrappedToken = IERC20(XTZ).balanceOf(address(superloop)); - uint256 finalBalanceUnderlyingToken = address(superloop).balance; - - // Assert - assertEq(finalBalanceWrappedToken, currentBalanceWrappedToken - supplyAmount); - assertEq(finalBalanceUnderlyingToken, currentBalanceUnderlyingToken + supplyAmount); - } -} +// // SPDX-License-Identifier: MIT + +// pragma solidity ^0.8.13; + +// import {TestBase} from "../../core/TestBase.sol"; +// import {IFlashLoanSimpleReceiver} from "aave-v3-core/contracts/flashloan/interfaces/IFlashLoanSimpleReceiver.sol"; +// import {DataTypes} from "../../../src/common/DataTypes.sol"; +// import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; +// import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; +// import {TransparentUpgradeableProxy} from +// "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +// import {console} from "forge-std/console.sol"; + +// contract WrapModuleTest is TestBase { +// Superloop public superloopImplementation; +// ProxyAdmin public proxyAdmin; +// address public user; + +// function setUp() public override { +// super.setUp(); + +// vm.startPrank(admin); +// _deployModules(); + +// address[] memory modules = new address[](2); +// modules[0] = address(unwrapModule); +// modules[1] = address(wrapModule); + +// DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ +// asset: XTZ, +// name: "XTZ Vault", +// symbol: "XTZV", +// supplyCap: 100000 * 10 ** 18, +// minimumDepositAmount: 100, +// instantWithdrawFee: 0, +// superloopModuleRegistry: address(moduleRegistry), +// modules: modules, +// accountant: mockModule, +// withdrawManager: mockModule, +// depositManager: mockModule, +// cashReserve: 1000, +// vaultAdmin: admin, +// treasury: treasury, +// vaultOperator: admin +// }); +// superloopImplementation = new Superloop(); +// proxyAdmin = new ProxyAdmin(address(this)); + +// TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( +// address(superloopImplementation), +// address(proxyAdmin), +// abi.encodeWithSelector(Superloop.initialize.selector, initData) +// ); +// superloop = Superloop(payable(address(proxy))); +// vm.stopPrank(); + +// user = makeAddr("user"); +// vm.label(user, "user"); +// vm.label(address(superloop), "superloop"); +// } + +// function test_SupplyBasicFlow() public { +// vm.startPrank(XTZ_WHALE); +// IERC20(XTZ).transfer(address(superloop), 1000 * 10 ** 18); +// vm.stopPrank(); + +// uint256 supplyAmount = 100 * 10 ** 18; +// _executeUnwrap(supplyAmount); + +// // warp +// DataTypes.AaveV3ActionParams memory wrapParams = +// DataTypes.AaveV3ActionParams({asset: XTZ, amount: supplyAmount}); + +// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); +// moduleExecutionData[0] = DataTypes.ModuleExecutionData({ +// executionType: DataTypes.CallType.DELEGATECALL, +// module: address(wrapModule), +// data: abi.encodeWithSelector(wrapModule.execute.selector, wrapParams) +// }); + +// uint256 currentBalanceWrappedToken = IERC20(XTZ).balanceOf(address(superloop)); +// uint256 currentBalanceUnderlyingToken = address(superloop).balance; + +// vm.prank(admin); +// superloop.operate(moduleExecutionData); + +// uint256 finalBalanceWrappedToken = IERC20(XTZ).balanceOf(address(superloop)); +// uint256 finalBalanceUnderlyingToken = address(superloop).balance; + +// assertEq(finalBalanceWrappedToken, currentBalanceWrappedToken + supplyAmount); +// assertEq(finalBalanceUnderlyingToken, currentBalanceUnderlyingToken - supplyAmount); +// } + +// function _executeUnwrap(uint256 supplyAmount) internal { +// // Create supply params +// DataTypes.AaveV3ActionParams memory unwarpParams = +// DataTypes.AaveV3ActionParams({asset: XTZ, amount: supplyAmount}); + +// // Create module execution data +// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); +// moduleExecutionData[0] = DataTypes.ModuleExecutionData({ +// executionType: DataTypes.CallType.DELEGATECALL, +// module: address(unwrapModule), +// data: abi.encodeWithSelector(unwrapModule.execute.selector, unwarpParams) +// }); + +// // current supply +// uint256 currentBalanceWrappedToken = IERC20(XTZ).balanceOf(address(superloop)); +// uint256 currentBalanceUnderlyingToken = address(superloop).balance; + +// // Act +// vm.prank(admin); +// superloop.operate(moduleExecutionData); + +// uint256 finalBalanceWrappedToken = IERC20(XTZ).balanceOf(address(superloop)); +// uint256 finalBalanceUnderlyingToken = address(superloop).balance; + +// // Assert +// assertEq(finalBalanceWrappedToken, currentBalanceWrappedToken - supplyAmount); +// assertEq(finalBalanceUnderlyingToken, currentBalanceUnderlyingToken + supplyAmount); +// } +// } diff --git a/test/modules/stake/hyperliquid/HyperbeatStakingModule.t.sol b/test/modules/stake/hyperliquid/HyperbeatStakingModule.t.sol index 5ead9a5..3478ef8 100644 --- a/test/modules/stake/hyperliquid/HyperbeatStakingModule.t.sol +++ b/test/modules/stake/hyperliquid/HyperbeatStakingModule.t.sol @@ -1,94 +1,94 @@ -// SPDX-License-Identifier: MIT +// // SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; +// pragma solidity ^0.8.13; -import {TestBase} from "./TestBase.sol"; -import {DataTypes} from "../../../../src/common/DataTypes.sol"; -import {Superloop} from "../../../../src/core/Superloop/Superloop.sol"; -import {TransparentUpgradeableProxy} from - "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -import {IPoolConfigurator} from "aave-v3-core/contracts/interfaces/IPoolConfigurator.sol"; -import {console} from "forge-std/console.sol"; +// import {TestBase} from "./TestBase.sol"; +// import {DataTypes} from "../../../../src/common/DataTypes.sol"; +// import {Superloop} from "../../../../src/core/Superloop/Superloop.sol"; +// import {TransparentUpgradeableProxy} from +// "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +// import {IPoolConfigurator} from "aave-v3-core/contracts/interfaces/IPoolConfigurator.sol"; +// import {console} from "forge-std/console.sol"; -contract HyperbeatStakingModuleTest is TestBase { - Superloop public superloopImplementation; - address public user; +// contract HyperbeatStakingModuleTest is TestBase { +// Superloop public superloopImplementation; +// address public user; - function setUp() public override { - super.setUp(); +// function setUp() public override { +// super.setUp(); - vm.startPrank(admin); - _deployModules(); +// vm.startPrank(admin); +// _deployModules(); - address[] memory modules = new address[](5); - modules[0] = address(hyperliquidStakeModule); - modules[1] = address(kinetiqStakeModule); - modules[2] = address(hyperbeatStakingModule); - modules[3] = address(wrapModule); - modules[4] = address(unwrapModule); +// address[] memory modules = new address[](5); +// modules[0] = address(hyperliquidStakeModule); +// modules[1] = address(kinetiqStakeModule); +// modules[2] = address(hyperbeatStakingModule); +// modules[3] = address(wrapModule); +// modules[4] = address(unwrapModule); - DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ - asset: WHYPE, - name: "WHYPE Vault", - symbol: "WHYPEV", - supplyCap: 100000 * WHYPE_SCALE, - minimumDepositAmount: 100, - instantWithdrawFee: 0, - superloopModuleRegistry: address(moduleRegistry), - modules: modules, - accountant: mockModule, - withdrawManager: mockModule, - depositManager: mockModule, - cashReserve: 1000, - vaultAdmin: admin, - treasury: treasury, - vaultOperator: admin - }); - superloopImplementation = new Superloop(); +// DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ +// asset: WHYPE, +// name: "WHYPE Vault", +// symbol: "WHYPEV", +// supplyCap: 100000 * WHYPE_SCALE, +// minimumDepositAmount: 100, +// instantWithdrawFee: 0, +// superloopModuleRegistry: address(moduleRegistry), +// modules: modules, +// accountant: mockModule, +// withdrawManager: mockModule, +// depositManager: mockModule, +// cashReserve: 1000, +// vaultAdmin: admin, +// treasury: treasury, +// vaultOperator: admin +// }); +// superloopImplementation = new Superloop(); - TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( - address(superloopImplementation), - address(this), - abi.encodeWithSelector(Superloop.initialize.selector, initData) - ); - superloop = Superloop(payable(address(proxy))); - vm.stopPrank(); +// TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( +// address(superloopImplementation), +// address(this), +// abi.encodeWithSelector(Superloop.initialize.selector, initData) +// ); +// superloop = Superloop(payable(address(proxy))); +// vm.stopPrank(); - user = makeAddr("user"); - vm.label(user, "user"); - vm.label(address(superloop), "superloop"); - } +// user = makeAddr("user"); +// vm.label(user, "user"); +// vm.label(address(superloop), "superloop"); +// } - function test_HyperbeatStake() public { - // transfer 100 wHYPE to the vault - deal(WHYPE, address(superloop), 100 * WHYPE_SCALE); +// function test_HyperbeatStake() public { +// // transfer 100 wHYPE to the vault +// deal(WHYPE, address(superloop), 100 * WHYPE_SCALE); - // call unwrap module & kinetiq stake module - DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](2); - moduleExecutionData[0] = DataTypes.ModuleExecutionData({ - executionType: DataTypes.CallType.DELEGATECALL, - module: address(unwrapModule), - data: abi.encodeWithSelector( - unwrapModule.execute.selector, DataTypes.AaveV3ActionParams({asset: WHYPE, amount: 100 * WHYPE_SCALE}) - ) - }); - moduleExecutionData[1] = DataTypes.ModuleExecutionData({ - executionType: DataTypes.CallType.DELEGATECALL, - module: address(hyperbeatStakingModule), - data: abi.encodeWithSelector( - hyperbeatStakingModule.execute.selector, - DataTypes.StakeParams({assets: 100 * WHYPE_SCALE, data: abi.encode(string(""))}) - ) - }); +// // call unwrap module & kinetiq stake module +// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](2); +// moduleExecutionData[0] = DataTypes.ModuleExecutionData({ +// executionType: DataTypes.CallType.DELEGATECALL, +// module: address(unwrapModule), +// data: abi.encodeWithSelector( +// unwrapModule.execute.selector, DataTypes.AaveV3ActionParams({asset: WHYPE, amount: 100 * WHYPE_SCALE}) +// ) +// }); +// moduleExecutionData[1] = DataTypes.ModuleExecutionData({ +// executionType: DataTypes.CallType.DELEGATECALL, +// module: address(hyperbeatStakingModule), +// data: abi.encodeWithSelector( +// hyperbeatStakingModule.execute.selector, +// DataTypes.StakeParams({assets: 100 * WHYPE_SCALE, data: abi.encode(string(""))}) +// ) +// }); - uint256 currentBalanceKHype = IERC20(BE_HYPE).balanceOf(address(superloop)); +// uint256 currentBalanceKHype = IERC20(BE_HYPE).balanceOf(address(superloop)); - vm.prank(admin); - superloop.operate(moduleExecutionData); +// vm.prank(admin); +// superloop.operate(moduleExecutionData); - uint256 finalBalanceKHype = IERC20(BE_HYPE).balanceOf(address(superloop)); +// uint256 finalBalanceKHype = IERC20(BE_HYPE).balanceOf(address(superloop)); - assertApproxEqAbs(finalBalanceKHype, currentBalanceKHype + 100 * WHYPE_SCALE, 5 * WHYPE_SCALE); - } -} +// assertApproxEqAbs(finalBalanceKHype, currentBalanceKHype + 100 * WHYPE_SCALE, 5 * WHYPE_SCALE); +// } +// } diff --git a/test/modules/stake/hyperliquid/HyperliquidStakingModule.t.sol b/test/modules/stake/hyperliquid/HyperliquidStakingModule.t.sol index 05c772e..bd78b39 100644 --- a/test/modules/stake/hyperliquid/HyperliquidStakingModule.t.sol +++ b/test/modules/stake/hyperliquid/HyperliquidStakingModule.t.sol @@ -1,97 +1,97 @@ -// SPDX-License-Identifier: MIT +// // SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; +// pragma solidity ^0.8.13; -import {TestBase} from "./TestBase.sol"; -import {DataTypes} from "../../../../src/common/DataTypes.sol"; -import {Superloop} from "../../../../src/core/Superloop/Superloop.sol"; -import {TransparentUpgradeableProxy} from - "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -import {IPoolConfigurator} from "aave-v3-core/contracts/interfaces/IPoolConfigurator.sol"; +// import {TestBase} from "./TestBase.sol"; +// import {DataTypes} from "../../../../src/common/DataTypes.sol"; +// import {Superloop} from "../../../../src/core/Superloop/Superloop.sol"; +// import {TransparentUpgradeableProxy} from +// "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +// import {IPoolConfigurator} from "aave-v3-core/contracts/interfaces/IPoolConfigurator.sol"; -contract HyperliquidStakingModuleTest is TestBase { - Superloop public superloopImplementation; - address public user; +// contract HyperliquidStakingModuleTest is TestBase { +// Superloop public superloopImplementation; +// address public user; - function setUp() public override { - super.setUp(); +// function setUp() public override { +// super.setUp(); - vm.startPrank(admin); - _deployModules(); +// vm.startPrank(admin); +// _deployModules(); - address[] memory modules = new address[](5); - modules[0] = address(hyperliquidStakeModule); - modules[1] = address(kinetiqStakeModule); - modules[2] = address(hyperbeatStakingModule); - modules[3] = address(wrapModule); - modules[4] = address(unwrapModule); +// address[] memory modules = new address[](5); +// modules[0] = address(hyperliquidStakeModule); +// modules[1] = address(kinetiqStakeModule); +// modules[2] = address(hyperbeatStakingModule); +// modules[3] = address(wrapModule); +// modules[4] = address(unwrapModule); - DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ - asset: WHYPE, - name: "WHYPE Vault", - symbol: "WHYPEV", - supplyCap: 100000 * WHYPE_SCALE, - minimumDepositAmount: 100, - instantWithdrawFee: 0, - superloopModuleRegistry: address(moduleRegistry), - modules: modules, - accountant: mockModule, - withdrawManager: mockModule, - depositManager: mockModule, - cashReserve: 1000, - vaultAdmin: admin, - treasury: treasury, - vaultOperator: admin - }); - superloopImplementation = new Superloop(); +// DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ +// asset: WHYPE, +// name: "WHYPE Vault", +// symbol: "WHYPEV", +// supplyCap: 100000 * WHYPE_SCALE, +// minimumDepositAmount: 100, +// instantWithdrawFee: 0, +// superloopModuleRegistry: address(moduleRegistry), +// modules: modules, +// accountant: mockModule, +// withdrawManager: mockModule, +// depositManager: mockModule, +// cashReserve: 1000, +// vaultAdmin: admin, +// treasury: treasury, +// vaultOperator: admin +// }); +// superloopImplementation = new Superloop(); - TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( - address(superloopImplementation), - address(this), - abi.encodeWithSelector(Superloop.initialize.selector, initData) - ); - superloop = Superloop(payable(address(proxy))); - vm.stopPrank(); +// TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( +// address(superloopImplementation), +// address(this), +// abi.encodeWithSelector(Superloop.initialize.selector, initData) +// ); +// superloop = Superloop(payable(address(proxy))); +// vm.stopPrank(); - user = makeAddr("user"); - vm.label(user, "user"); - vm.label(address(superloop), "superloop"); - } +// user = makeAddr("user"); +// vm.label(user, "user"); +// vm.label(address(superloop), "superloop"); +// } - function test_HyperliquidStake() public { - // transfer 100 wHYPE to the vault - deal(WHYPE, address(superloop), 100 * WHYPE_SCALE); +// function test_HyperliquidStake() public { +// // transfer 100 wHYPE to the vault +// deal(WHYPE, address(superloop), 100 * WHYPE_SCALE); - // call unwrap module & hyperliquid stake module - DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](2); - moduleExecutionData[0] = DataTypes.ModuleExecutionData({ - executionType: DataTypes.CallType.DELEGATECALL, - module: address(unwrapModule), - data: abi.encodeWithSelector( - unwrapModule.execute.selector, DataTypes.AaveV3ActionParams({asset: WHYPE, amount: 100 * WHYPE_SCALE}) - ) - }); - moduleExecutionData[1] = DataTypes.ModuleExecutionData({ - executionType: DataTypes.CallType.DELEGATECALL, - module: address(hyperliquidStakeModule), - data: abi.encodeWithSelector( - hyperliquidStakeModule.execute.selector, - DataTypes.StakeParams({assets: 100 * WHYPE_SCALE, data: abi.encode(string(""))}) - ) - }); +// // call unwrap module & hyperliquid stake module +// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](2); +// moduleExecutionData[0] = DataTypes.ModuleExecutionData({ +// executionType: DataTypes.CallType.DELEGATECALL, +// module: address(unwrapModule), +// data: abi.encodeWithSelector( +// unwrapModule.execute.selector, DataTypes.AaveV3ActionParams({asset: WHYPE, amount: 100 * WHYPE_SCALE}) +// ) +// }); +// moduleExecutionData[1] = DataTypes.ModuleExecutionData({ +// executionType: DataTypes.CallType.DELEGATECALL, +// module: address(hyperliquidStakeModule), +// data: abi.encodeWithSelector( +// hyperliquidStakeModule.execute.selector, +// DataTypes.StakeParams({assets: 100 * WHYPE_SCALE, data: abi.encode(string(""))}) +// ) +// }); - uint256 currentBalanceStHype = IERC20(ST_HYPE).balanceOf(address(superloop)); - uint256 currentBalanceWstHype = IERC20(WST_HYPE).balanceOf(address(superloop)); +// uint256 currentBalanceStHype = IERC20(ST_HYPE).balanceOf(address(superloop)); +// uint256 currentBalanceWstHype = IERC20(WST_HYPE).balanceOf(address(superloop)); - vm.prank(admin); - superloop.operate(moduleExecutionData); +// vm.prank(admin); +// superloop.operate(moduleExecutionData); - uint256 finalBalanceStHype = IERC20(ST_HYPE).balanceOf(address(superloop)); - uint256 finalBalanceWstHype = IERC20(WST_HYPE).balanceOf(address(superloop)); +// uint256 finalBalanceStHype = IERC20(ST_HYPE).balanceOf(address(superloop)); +// uint256 finalBalanceWstHype = IERC20(WST_HYPE).balanceOf(address(superloop)); - // it should have minted 100stHype for the vault and ~98 wstHype for the vault - assertApproxEqAbs(finalBalanceStHype, currentBalanceStHype + 100 * WHYPE_SCALE, 100); - assertApproxEqAbs(finalBalanceWstHype, currentBalanceWstHype + 100 * WHYPE_SCALE, 5 * WHYPE_SCALE); - } -} +// // it should have minted 100stHype for the vault and ~98 wstHype for the vault +// assertApproxEqAbs(finalBalanceStHype, currentBalanceStHype + 100 * WHYPE_SCALE, 100); +// assertApproxEqAbs(finalBalanceWstHype, currentBalanceWstHype + 100 * WHYPE_SCALE, 5 * WHYPE_SCALE); +// } +// } diff --git a/test/modules/stake/hyperliquid/KinetiqStakingModule.t.sol b/test/modules/stake/hyperliquid/KinetiqStakingModule.t.sol index 5631c46..a560218 100644 --- a/test/modules/stake/hyperliquid/KinetiqStakingModule.t.sol +++ b/test/modules/stake/hyperliquid/KinetiqStakingModule.t.sol @@ -1,94 +1,94 @@ -// SPDX-License-Identifier: MIT +// // SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; +// pragma solidity ^0.8.13; -import {TestBase} from "./TestBase.sol"; -import {DataTypes} from "../../../../src/common/DataTypes.sol"; -import {Superloop} from "../../../../src/core/Superloop/Superloop.sol"; -import {TransparentUpgradeableProxy} from - "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -import {IPoolConfigurator} from "aave-v3-core/contracts/interfaces/IPoolConfigurator.sol"; -import {console} from "forge-std/console.sol"; +// import {TestBase} from "./TestBase.sol"; +// import {DataTypes} from "../../../../src/common/DataTypes.sol"; +// import {Superloop} from "../../../../src/core/Superloop/Superloop.sol"; +// import {TransparentUpgradeableProxy} from +// "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +// import {IPoolConfigurator} from "aave-v3-core/contracts/interfaces/IPoolConfigurator.sol"; +// import {console} from "forge-std/console.sol"; -contract KinetiqStakingModuleTest is TestBase { - Superloop public superloopImplementation; - address public user; +// contract KinetiqStakingModuleTest is TestBase { +// Superloop public superloopImplementation; +// address public user; - function setUp() public override { - super.setUp(); +// function setUp() public override { +// super.setUp(); - vm.startPrank(admin); - _deployModules(); +// vm.startPrank(admin); +// _deployModules(); - address[] memory modules = new address[](5); - modules[0] = address(hyperliquidStakeModule); - modules[1] = address(kinetiqStakeModule); - modules[2] = address(hyperbeatStakingModule); - modules[3] = address(wrapModule); - modules[4] = address(unwrapModule); +// address[] memory modules = new address[](5); +// modules[0] = address(hyperliquidStakeModule); +// modules[1] = address(kinetiqStakeModule); +// modules[2] = address(hyperbeatStakingModule); +// modules[3] = address(wrapModule); +// modules[4] = address(unwrapModule); - DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ - asset: WHYPE, - name: "WHYPE Vault", - symbol: "WHYPEV", - supplyCap: 100000 * WHYPE_SCALE, - minimumDepositAmount: 100, - instantWithdrawFee: 0, - superloopModuleRegistry: address(moduleRegistry), - modules: modules, - accountant: mockModule, - withdrawManager: mockModule, - depositManager: mockModule, - cashReserve: 1000, - vaultAdmin: admin, - treasury: treasury, - vaultOperator: admin - }); - superloopImplementation = new Superloop(); +// DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ +// asset: WHYPE, +// name: "WHYPE Vault", +// symbol: "WHYPEV", +// supplyCap: 100000 * WHYPE_SCALE, +// minimumDepositAmount: 100, +// instantWithdrawFee: 0, +// superloopModuleRegistry: address(moduleRegistry), +// modules: modules, +// accountant: mockModule, +// withdrawManager: mockModule, +// depositManager: mockModule, +// cashReserve: 1000, +// vaultAdmin: admin, +// treasury: treasury, +// vaultOperator: admin +// }); +// superloopImplementation = new Superloop(); - TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( - address(superloopImplementation), - address(this), - abi.encodeWithSelector(Superloop.initialize.selector, initData) - ); - superloop = Superloop(payable(address(proxy))); - vm.stopPrank(); +// TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( +// address(superloopImplementation), +// address(this), +// abi.encodeWithSelector(Superloop.initialize.selector, initData) +// ); +// superloop = Superloop(payable(address(proxy))); +// vm.stopPrank(); - user = makeAddr("user"); - vm.label(user, "user"); - vm.label(address(superloop), "superloop"); - } +// user = makeAddr("user"); +// vm.label(user, "user"); +// vm.label(address(superloop), "superloop"); +// } - function test_KinetiqStake() public { - // transfer 100 wHYPE to the vault - deal(WHYPE, address(superloop), 100 * WHYPE_SCALE); +// function test_KinetiqStake() public { +// // transfer 100 wHYPE to the vault +// deal(WHYPE, address(superloop), 100 * WHYPE_SCALE); - // call unwrap module & kinetiq stake module - DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](2); - moduleExecutionData[0] = DataTypes.ModuleExecutionData({ - executionType: DataTypes.CallType.DELEGATECALL, - module: address(unwrapModule), - data: abi.encodeWithSelector( - unwrapModule.execute.selector, DataTypes.AaveV3ActionParams({asset: WHYPE, amount: 100 * WHYPE_SCALE}) - ) - }); - moduleExecutionData[1] = DataTypes.ModuleExecutionData({ - executionType: DataTypes.CallType.DELEGATECALL, - module: address(kinetiqStakeModule), - data: abi.encodeWithSelector( - kinetiqStakeModule.execute.selector, - DataTypes.StakeParams({assets: 100 * WHYPE_SCALE, data: abi.encode(string(""))}) - ) - }); +// // call unwrap module & kinetiq stake module +// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](2); +// moduleExecutionData[0] = DataTypes.ModuleExecutionData({ +// executionType: DataTypes.CallType.DELEGATECALL, +// module: address(unwrapModule), +// data: abi.encodeWithSelector( +// unwrapModule.execute.selector, DataTypes.AaveV3ActionParams({asset: WHYPE, amount: 100 * WHYPE_SCALE}) +// ) +// }); +// moduleExecutionData[1] = DataTypes.ModuleExecutionData({ +// executionType: DataTypes.CallType.DELEGATECALL, +// module: address(kinetiqStakeModule), +// data: abi.encodeWithSelector( +// kinetiqStakeModule.execute.selector, +// DataTypes.StakeParams({assets: 100 * WHYPE_SCALE, data: abi.encode(string(""))}) +// ) +// }); - uint256 currentBalanceKHype = IERC20(K_HYPE).balanceOf(address(superloop)); +// uint256 currentBalanceKHype = IERC20(K_HYPE).balanceOf(address(superloop)); - vm.prank(admin); - superloop.operate(moduleExecutionData); +// vm.prank(admin); +// superloop.operate(moduleExecutionData); - uint256 finalBalanceKHype = IERC20(K_HYPE).balanceOf(address(superloop)); +// uint256 finalBalanceKHype = IERC20(K_HYPE).balanceOf(address(superloop)); - assertApproxEqAbs(finalBalanceKHype, currentBalanceKHype + 100 * WHYPE_SCALE, 5 * WHYPE_SCALE); - } -} +// assertApproxEqAbs(finalBalanceKHype, currentBalanceKHype + 100 * WHYPE_SCALE, 5 * WHYPE_SCALE); +// } +// } diff --git a/test/modules/stake/hyperliquid/TestBase.sol b/test/modules/stake/hyperliquid/TestBase.sol index 66dc71a..5e84907 100644 --- a/test/modules/stake/hyperliquid/TestBase.sol +++ b/test/modules/stake/hyperliquid/TestBase.sol @@ -1,124 +1,124 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.13; - -import {Test, console} from "forge-std/Test.sol"; -import {Vm} from "forge-std/Vm.sol"; -import {Superloop} from "../../../../src/core/Superloop/Superloop.sol"; -import {DataTypes} from "../../../../src/common/DataTypes.sol"; -import {SuperloopModuleRegistry} from "../../../../src/core/ModuleRegistry/ModuleRegistry.sol"; -import {AaveV3FlashloanModule} from "../../../../src/modules/aave/AaveV3FlashloanModule.sol"; -import {AaveV3CallbackHandler} from "../../../../src/modules/callback/AaveV3CallbackHandler.sol"; -import {AaveV3EmodeModule} from "../../../../src/modules/aave/AaveV3EmodeModule.sol"; -import {AaveV3SupplyModule} from "../../../../src/modules/aave/AaveV3SupplyModule.sol"; -import {AaveV3WithdrawModule} from "../../../../src/modules/aave/AaveV3WithdrawModule.sol"; -import {AaveV3BorrowModule} from "../../../../src/modules/aave/AaveV3BorrowModule.sol"; -import {AaveV3RepayModule} from "../../../../src/modules/aave/AaveV3RepayModule.sol"; -import {IPoolDataProvider} from "aave-v3-core/contracts/interfaces/IPoolDataProvider.sol"; -import {IPool} from "aave-v3-core/contracts/interfaces/IPool.sol"; -import {UniversalDexModule} from "../../../../src/modules/dex/UniversalDexModule.sol"; -import {AccountantAaveV3} from "../../../../src/core/Accountant/aaveV3Accountant/AccountantAaveV3.sol"; -import {WithdrawManager} from "../../../../src/core/WithdrawManager/WithdrawManager.sol"; -import {DepositManager} from "../../../../src/core/DepositManager/DepositManager.sol"; -import {DepositManagerCallbackHandler} from "../../../../src/modules/callback/DepositManagerCallbackHandler.sol"; -import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; -import {TransparentUpgradeableProxy} from - "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -import {UniversalAccountant} from "../../../../src/core/Accountant/universalAccountant/UniversalAccountant.sol"; -import {AaveV3AccountantPlugin} from "../../../../src/plugins/Accountant/AaveV3AccountantPlugin.sol"; -import {WithdrawManagerCallbackHandler} from "../../../../src/modules/callback/WithdrawManagerCallbackHandler.sol"; -import {UnwrapModule} from "../../../../src/modules/helper/UnwrapModule.sol"; -import {WrapModule} from "../../../../src/modules/helper/WrapModule.sol"; -import {HyperliquidStakeModule} from "../../../../src/modules/stake/hyperliquid/HyperliquidStakeModule.sol"; -import {KinetiqStakeModule} from "../../../../src/modules/stake/hyperliquid/KinetiqStakeModule.sol"; -import {HyperbeatStakingModule} from "../../../../src/modules/stake/hyperliquid/HyperbeatStakingModule.sol"; - -contract TestBase is Test { - address public AAVE_V3_POOL_ADDRESSES_PROVIDER; - address public AAVE_V3_POOL_DATA_PROVIDER; - address public AAVE_V3_PRICE_ORACLE; - address public POOL; - uint256 public PERFORMANCE_FEE; // 20% - address public POOL_CONFIGURATOR; - - address public stakingManager = 0x393D0B87Ed38fc779FD9611144aE649BA6082109; - address public stakingCore = 0xCeaD893b162D38e714D82d06a7fe0b0dc3c38E0b; - address public overseer = 0xB96f07367e69e86d6e9C3F29215885104813eeAE; - address public constant WHYPE = 0x5555555555555555555555555555555555555555; - uint256 public constant WHYPE_SCALE = 10 ** 18; - - address public constant ST_HYPE = 0xfFaa4a3D97fE9107Cef8a3F48c069F577Ff76cC1; - address public constant WST_HYPE = 0x94e8396e0869c9F2200760aF0621aFd240E1CF38; - address public constant K_HYPE = 0xfD739d4e423301CE9385c1fb8850539D657C296D; - address public constant BE_HYPE = 0xd8FC8F0b03eBA61F64D08B0bef69d80916E5DdA9; - - address public admin; - address public treasury; - - SuperloopModuleRegistry public moduleRegistry; - Superloop public superloop; - AaveV3FlashloanModule public flashloanModule; - DepositManagerCallbackHandler public depositManagerCallbackHandler; - WithdrawManagerCallbackHandler public withdrawManagerCallbackHandler; - AaveV3CallbackHandler public callbackHandler; - AaveV3SupplyModule public supplyModule; - AaveV3WithdrawModule public withdrawModule; - AaveV3BorrowModule public borrowModule; - AaveV3RepayModule public repayModule; - UniversalDexModule public dexModule; - AccountantAaveV3 public accountantAaveV3; - UniversalAccountant public accountant; - WithdrawManager public withdrawManager; - UnwrapModule public unwrapModule; - WrapModule public wrapModule; - - HyperliquidStakeModule public hyperliquidStakeModule; - KinetiqStakeModule public kinetiqStakeModule; - HyperbeatStakingModule public hyperbeatStakingModule; - - DepositManager public depositManager; - - address public mockModule; - AaveV3EmodeModule public emodeModule; - IPoolDataProvider public poolDataProvider; - IPool public pool; - - function setUp() public virtual { - vm.createSelectFork("hyperevm"); - admin = makeAddr("admin"); - treasury = makeAddr("treasury"); - - vm.startPrank(admin); - moduleRegistry = new SuperloopModuleRegistry(); - mockModule = makeAddr("mockModule"); - moduleRegistry.setModule("MockModule", mockModule); - poolDataProvider = IPoolDataProvider(AAVE_V3_POOL_DATA_PROVIDER); - pool = IPool(POOL); - vm.stopPrank(); - - vm.label(admin, "admin"); - vm.label(treasury, "treasury"); - vm.label(mockModule, "mockModule"); - vm.label(address(moduleRegistry), "moduleRegistry"); - } - - function _deployModules() internal { - unwrapModule = new UnwrapModule(WHYPE); - moduleRegistry.setModule("UnwrapModule", address(unwrapModule)); - wrapModule = new WrapModule(WHYPE); - moduleRegistry.setModule("WrapModule", address(wrapModule)); - - hyperliquidStakeModule = new HyperliquidStakeModule(overseer); - moduleRegistry.setModule("HyperliquidStakeModule", address(hyperliquidStakeModule)); - kinetiqStakeModule = new KinetiqStakeModule(stakingManager); - moduleRegistry.setModule("KinetiqStakeModule", address(kinetiqStakeModule)); - hyperbeatStakingModule = new HyperbeatStakingModule(stakingCore); - moduleRegistry.setModule("HyperbeatStakingModule", address(hyperbeatStakingModule)); - - vm.label(address(unwrapModule), "unwrapModule"); - vm.label(address(wrapModule), "wrapModule"); - vm.label(address(hyperliquidStakeModule), "hyperliquidStakeModule"); - vm.label(address(kinetiqStakeModule), "kinetiqStakeModule"); - vm.label(address(hyperbeatStakingModule), "hyperbeatStakingModule"); - } -} +// // SPDX-License-Identifier: MIT + +// pragma solidity ^0.8.13; + +// import {Test, console} from "forge-std/Test.sol"; +// import {Vm} from "forge-std/Vm.sol"; +// import {Superloop} from "../../../../src/core/Superloop/Superloop.sol"; +// import {DataTypes} from "../../../../src/common/DataTypes.sol"; +// import {SuperloopModuleRegistry} from "../../../../src/core/ModuleRegistry/ModuleRegistry.sol"; +// import {AaveV3FlashloanModule} from "../../../../src/modules/aave/AaveV3FlashloanModule.sol"; +// import {AaveV3CallbackHandler} from "../../../../src/modules/callback/AaveV3CallbackHandler.sol"; +// import {AaveV3EmodeModule} from "../../../../src/modules/aave/AaveV3EmodeModule.sol"; +// import {AaveV3SupplyModule} from "../../../../src/modules/aave/AaveV3SupplyModule.sol"; +// import {AaveV3WithdrawModule} from "../../../../src/modules/aave/AaveV3WithdrawModule.sol"; +// import {AaveV3BorrowModule} from "../../../../src/modules/aave/AaveV3BorrowModule.sol"; +// import {AaveV3RepayModule} from "../../../../src/modules/aave/AaveV3RepayModule.sol"; +// import {IPoolDataProvider} from "aave-v3-core/contracts/interfaces/IPoolDataProvider.sol"; +// import {IPool} from "aave-v3-core/contracts/interfaces/IPool.sol"; +// import {UniversalDexModule} from "../../../../src/modules/dex/UniversalDexModule.sol"; +// import {AccountantAaveV3} from "../../../../src/core/Accountant/aaveV3Accountant/AccountantAaveV3.sol"; +// import {WithdrawManager} from "../../../../src/core/WithdrawManager/WithdrawManager.sol"; +// import {DepositManager} from "../../../../src/core/DepositManager/DepositManager.sol"; +// import {DepositManagerCallbackHandler} from "../../../../src/modules/callback/DepositManagerCallbackHandler.sol"; +// import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; +// import {TransparentUpgradeableProxy} from +// "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +// import {UniversalAccountant} from "../../../../src/core/Accountant/universalAccountant/UniversalAccountant.sol"; +// import {AaveV3AccountantPlugin} from "../../../../src/plugins/Accountant/AaveV3AccountantPlugin.sol"; +// import {WithdrawManagerCallbackHandler} from "../../../../src/modules/callback/WithdrawManagerCallbackHandler.sol"; +// import {UnwrapModule} from "../../../../src/modules/helper/UnwrapModule.sol"; +// import {WrapModule} from "../../../../src/modules/helper/WrapModule.sol"; +// import {HyperliquidStakeModule} from "../../../../src/modules/stake/hyperliquid/HyperliquidStakeModule.sol"; +// import {KinetiqStakeModule} from "../../../../src/modules/stake/hyperliquid/KinetiqStakeModule.sol"; +// import {HyperbeatStakingModule} from "../../../../src/modules/stake/hyperliquid/HyperbeatStakingModule.sol"; + +// contract TestBase is Test { +// address public AAVE_V3_POOL_ADDRESSES_PROVIDER; +// address public AAVE_V3_POOL_DATA_PROVIDER; +// address public AAVE_V3_PRICE_ORACLE; +// address public POOL; +// uint256 public PERFORMANCE_FEE; // 20% +// address public POOL_CONFIGURATOR; + +// address public stakingManager = 0x393D0B87Ed38fc779FD9611144aE649BA6082109; +// address public stakingCore = 0xCeaD893b162D38e714D82d06a7fe0b0dc3c38E0b; +// address public overseer = 0xB96f07367e69e86d6e9C3F29215885104813eeAE; +// address public constant WHYPE = 0x5555555555555555555555555555555555555555; +// uint256 public constant WHYPE_SCALE = 10 ** 18; + +// address public constant ST_HYPE = 0xfFaa4a3D97fE9107Cef8a3F48c069F577Ff76cC1; +// address public constant WST_HYPE = 0x94e8396e0869c9F2200760aF0621aFd240E1CF38; +// address public constant K_HYPE = 0xfD739d4e423301CE9385c1fb8850539D657C296D; +// address public constant BE_HYPE = 0xd8FC8F0b03eBA61F64D08B0bef69d80916E5DdA9; + +// address public admin; +// address public treasury; + +// SuperloopModuleRegistry public moduleRegistry; +// Superloop public superloop; +// AaveV3FlashloanModule public flashloanModule; +// DepositManagerCallbackHandler public depositManagerCallbackHandler; +// WithdrawManagerCallbackHandler public withdrawManagerCallbackHandler; +// AaveV3CallbackHandler public callbackHandler; +// AaveV3SupplyModule public supplyModule; +// AaveV3WithdrawModule public withdrawModule; +// AaveV3BorrowModule public borrowModule; +// AaveV3RepayModule public repayModule; +// UniversalDexModule public dexModule; +// AccountantAaveV3 public accountantAaveV3; +// UniversalAccountant public accountant; +// WithdrawManager public withdrawManager; +// UnwrapModule public unwrapModule; +// WrapModule public wrapModule; + +// HyperliquidStakeModule public hyperliquidStakeModule; +// KinetiqStakeModule public kinetiqStakeModule; +// HyperbeatStakingModule public hyperbeatStakingModule; + +// DepositManager public depositManager; + +// address public mockModule; +// AaveV3EmodeModule public emodeModule; +// IPoolDataProvider public poolDataProvider; +// IPool public pool; + +// function setUp() public virtual { +// vm.createSelectFork("hyperevm"); +// admin = makeAddr("admin"); +// treasury = makeAddr("treasury"); + +// vm.startPrank(admin); +// moduleRegistry = new SuperloopModuleRegistry(); +// mockModule = makeAddr("mockModule"); +// moduleRegistry.setModule("MockModule", mockModule); +// poolDataProvider = IPoolDataProvider(AAVE_V3_POOL_DATA_PROVIDER); +// pool = IPool(POOL); +// vm.stopPrank(); + +// vm.label(admin, "admin"); +// vm.label(treasury, "treasury"); +// vm.label(mockModule, "mockModule"); +// vm.label(address(moduleRegistry), "moduleRegistry"); +// } + +// function _deployModules() internal { +// unwrapModule = new UnwrapModule(WHYPE); +// moduleRegistry.setModule("UnwrapModule", address(unwrapModule)); +// wrapModule = new WrapModule(WHYPE); +// moduleRegistry.setModule("WrapModule", address(wrapModule)); + +// hyperliquidStakeModule = new HyperliquidStakeModule(overseer); +// moduleRegistry.setModule("HyperliquidStakeModule", address(hyperliquidStakeModule)); +// kinetiqStakeModule = new KinetiqStakeModule(stakingManager); +// moduleRegistry.setModule("KinetiqStakeModule", address(kinetiqStakeModule)); +// hyperbeatStakingModule = new HyperbeatStakingModule(stakingCore); +// moduleRegistry.setModule("HyperbeatStakingModule", address(hyperbeatStakingModule)); + +// vm.label(address(unwrapModule), "unwrapModule"); +// vm.label(address(wrapModule), "wrapModule"); +// vm.label(address(hyperliquidStakeModule), "hyperliquidStakeModule"); +// vm.label(address(kinetiqStakeModule), "kinetiqStakeModule"); +// vm.label(address(hyperbeatStakingModule), "hyperbeatStakingModule"); +// } +// } From 0148183a4ca215fbaef474f743787b131778e668 Mon Sep 17 00:00:00 2001 From: Priyam Anand Date: Fri, 23 Jan 2026 15:00:40 +0530 Subject: [PATCH 05/21] test: refactored dex, fallback and helper modules --- test/core/TestBase.sol | 2 +- test/core/TestEnv.sol | 6 +- test/modules/dex/UniversalDexModule.t.sol | 343 +++--- .../AaveV3PreliquidationFallbackHandler.t.sol | 1051 +++++++++++------ test/modules/helpers/UnwrapModule.t.sol | 211 ++-- test/modules/helpers/WrapModule.t.sol | 249 ++-- 6 files changed, 1074 insertions(+), 788 deletions(-) diff --git a/test/core/TestBase.sol b/test/core/TestBase.sol index 63af07c..74fe5f1 100644 --- a/test/core/TestBase.sol +++ b/test/core/TestBase.sol @@ -105,7 +105,7 @@ abstract contract TestBase is TestEnv { function setUp() public virtual override { super.setUp(); - uint256 envIndex = 2; // TODO: move this to config + uint256 envIndex = 0; // TODO: move this to config environment = testEnvironments[envIndex]; vm.createSelectFork(environment.chainName); diff --git a/test/core/TestEnv.sol b/test/core/TestEnv.sol index 9f655cc..07f383d 100644 --- a/test/core/TestEnv.sol +++ b/test/core/TestEnv.sol @@ -28,6 +28,10 @@ abstract contract TestEnv is Test { address public constant WXTZ = 0xc9B53AB2679f573e480d01e0f49e2B5CFB7a3EAb; address public constant LBTC = 0xecAc9C5F704e954931349Da37F60E39f515c11c1; address public constant WBTC = 0xbFc94CD2B1E55999Cfc7347a9313e88702B83d0F; + address public constant USDC_ETLK = 0x796Ea11Fa2dD751eD01b53C372fFDB4AAa8f00F9; + address public constant USDT_ETLK = 0x2C03058C8AFC06713be23e58D2febC8337dbfE6A; + address public constant USDT_ETLK_WHALE = 0x998098A1B2E95e2b8f15360676428EdFd976861f; + address public constant USDC_ETLK_WHALE = 0xd03bfdF9B26DB1e6764724d914d7c3d18106a9Fb; // eth mainnet chain address public constant USDe = 0x4c9EDD5852cd905f086C759E8383e09bff1E68B3; @@ -35,7 +39,7 @@ abstract contract TestEnv is Test { address public constant sUSDe = 0x9D39A5DE30e57443BfF2A8307A4256c8797A3497; address public constant USDC_ETH = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; address public constant USDC_ETH_Whale = 0x98C23E9d8f34FEFb1B7BD6a91B7FF122F4e16F5c; - address USDT_ETH = 0xdAC17F958D2ee523a2206206994597C13D831ec7; + address public constant USDT_ETH = 0x2C03058C8AFC06713be23e58D2febC8337dbfE6A; // address public constant uint256 public constant PERFORMANCE_FEE = 2000; // 20% diff --git a/test/modules/dex/UniversalDexModule.t.sol b/test/modules/dex/UniversalDexModule.t.sol index fc12a88..2c9ec25 100644 --- a/test/modules/dex/UniversalDexModule.t.sol +++ b/test/modules/dex/UniversalDexModule.t.sol @@ -1,171 +1,172 @@ -// // SPDX-License-Identifier: MIT - -// pragma solidity ^0.8.13; - -// import {console} from "forge-std/Test.sol"; -// import {UniversalDexModule} from "../../../src/modules/dex/UniversalDexModule.sol"; -// import {DataTypes} from "../../../src/common/DataTypes.sol"; -// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -// import {TestBase} from "../../core/TestBase.sol"; -// import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; -// import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; -// import {TransparentUpgradeableProxy} from -// "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -// import {IRouter} from "../../../src/mock/MockIRouter.sol"; - -// contract UniversalDexModuleTest is TestBase { -// Superloop public superloopImplementation; -// ProxyAdmin public proxyAdmin; -// address public user; - -// function setUp() public override { -// super.setUp(); - -// vm.startPrank(admin); -// _deployModules(); - -// address[] memory modules = new address[](1); -// modules[0] = address(dexModule); - -// DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ -// asset: XTZ, -// name: "XTZ Vault", -// symbol: "XTZV", -// supplyCap: 100000 * 10 ** 18, -// minimumDepositAmount: 100, -// instantWithdrawFee: 0, -// superloopModuleRegistry: address(moduleRegistry), -// modules: modules, -// accountant: mockModule, -// withdrawManager: mockModule, -// depositManager: mockModule, -// cashReserve: 1000, -// vaultAdmin: admin, -// treasury: treasury, -// vaultOperator: admin -// }); -// superloopImplementation = new Superloop(); -// proxyAdmin = new ProxyAdmin(address(this)); - -// TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( -// address(superloopImplementation), -// address(proxyAdmin), -// abi.encodeWithSelector(Superloop.initialize.selector, initData) -// ); -// superloop = Superloop(payable(address(proxy))); -// vm.stopPrank(); - -// user = makeAddr("user"); -// vm.label(user, "user"); -// vm.label(address(superloop), "superloop"); -// } - -// function test_executeSwap() public { -// address tokenIn = USDT; -// address tokenOut = USDC; - -// uint256 amountIn = 1000 * 10 ** 6; -// uint256 maxAmountIn = 1000 * 10 ** 6; -// uint256 minAmountOut = 900 * 10 ** 6; - -// DataTypes.ExecuteSwapParamsData[] memory swapParamsData = new DataTypes.ExecuteSwapParamsData[](2); -// swapParamsData[0] = DataTypes.ExecuteSwapParamsData({ -// target: USDT, -// data: abi.encodeWithSelector(IERC20.approve.selector, ROUTER, amountIn) -// }); -// swapParamsData[1] = DataTypes.ExecuteSwapParamsData({ -// target: ROUTER, -// data: abi.encodeWithSelector( -// IRouter.exactInputSingle.selector, -// IRouter.ExactInputSingleParams({ -// tokenIn: USDT, -// tokenOut: USDC, -// fee: 100, -// recipient: address(superloop), -// amountIn: amountIn, -// amountOutMinimum: 0, -// sqrtPriceLimitX96: 0 -// }) -// ) -// }); - -// DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ -// tokenIn: tokenIn, -// tokenOut: tokenOut, -// amountIn: amountIn, -// maxAmountIn: maxAmountIn, -// minAmountOut: minAmountOut, -// data: swapParamsData -// }); - -// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); -// moduleExecutionData[0] = DataTypes.ModuleExecutionData({ -// executionType: DataTypes.CallType.DELEGATECALL, -// module: address(dexModule), -// data: abi.encodeWithSelector(dexModule.execute.selector, swapParams) -// }); - -// vm.startPrank(USDT_WHALE); -// IERC20(USDT).transfer(address(superloop), amountIn); -// vm.stopPrank(); - -// vm.startPrank(admin); -// superloop.operate(moduleExecutionData); -// vm.stopPrank(); - -// uint256 usdcBalance = IERC20(USDC).balanceOf(address(superloop)); -// assertGt(usdcBalance, 0); -// } - -// function test_executeSwapAndExit() public { -// vm.startPrank(USDT_WHALE); -// IERC20(USDT).transfer(address(user), 1000 * 10 ** 6); -// vm.stopPrank(); - -// address tokenIn = USDT; -// address tokenOut = USDC; - -// uint256 amountIn = 1000 * 10 ** 6; -// uint256 maxAmountIn = 1000 * 10 ** 6; -// uint256 minAmountOut = 900 * 10 ** 6; - -// DataTypes.ExecuteSwapParamsData[] memory data = new DataTypes.ExecuteSwapParamsData[](2); -// data[0] = DataTypes.ExecuteSwapParamsData({ -// target: USDT, -// data: abi.encodeWithSelector(IERC20.approve.selector, ROUTER, amountIn) -// }); -// data[1] = DataTypes.ExecuteSwapParamsData({ -// target: ROUTER, -// data: abi.encodeWithSelector( -// IRouter.exactInputSingle.selector, -// IRouter.ExactInputSingleParams({ -// tokenIn: USDT, -// tokenOut: USDC, -// fee: 100, -// recipient: address(dexModule), -// amountIn: amountIn, -// amountOutMinimum: 0, -// sqrtPriceLimitX96: 0 -// }) -// ) -// }); - -// DataTypes.ExecuteSwapParams memory params = DataTypes.ExecuteSwapParams({ -// tokenIn: tokenIn, -// tokenOut: tokenOut, -// amountIn: amountIn, -// maxAmountIn: maxAmountIn, -// minAmountOut: minAmountOut, -// data: data -// }); - -// vm.startPrank(user); -// IERC20(USDT).approve(address(dexModule), amountIn); -// uint256 amountOut = dexModule.executeAndExit(params, user); - -// vm.stopPrank(); - -// uint256 usdcBalanceUser = IERC20(USDC).balanceOf(address(user)); -// assertEq(usdcBalanceUser, amountOut); -// } -// } +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.13; + +import {console} from "forge-std/Test.sol"; +import {UniversalDexModule} from "../../../src/modules/dex/UniversalDexModule.sol"; +import {DataTypes} from "../../../src/common/DataTypes.sol"; +import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +import {TestBase} from "../../core/TestBase.sol"; +import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; +import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; +import {TransparentUpgradeableProxy} from + "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {IRouter} from "../../../src/mock/MockIRouter.sol"; + +// not maintained, may fail +contract UniversalDexModuleTest is TestBase { + Superloop public superloopImplementation; + ProxyAdmin public proxyAdmin; + address public user; + + function setUp() public override { + super.setUp(); + + vm.startPrank(admin); + _deployModules(); + + address[] memory modules = new address[](1); + modules[0] = address(dexModule); + + DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ + asset: environment.vaultAsset, + name: "Vault", + symbol: "VLT", + supplyCap: 100000 * 10 ** environment.vaultAssetDecimals, + minimumDepositAmount: 100, + instantWithdrawFee: 0, + superloopModuleRegistry: address(moduleRegistry), + modules: modules, + accountant: mockModule, + withdrawManager: mockModule, + depositManager: mockModule, + cashReserve: 1000, + vaultAdmin: admin, + treasury: treasury, + vaultOperator: admin + }); + superloopImplementation = new Superloop(); + proxyAdmin = new ProxyAdmin(address(this)); + + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(superloopImplementation), + address(proxyAdmin), + abi.encodeWithSelector(Superloop.initialize.selector, initData) + ); + superloop = Superloop(payable(address(proxy))); + vm.stopPrank(); + + user = makeAddr("user"); + vm.label(user, "user"); + vm.label(address(superloop), "superloop"); + } + + function test_executeSwap() public { + address tokenIn = USDT_ETLK; + address tokenOut = USDC_ETLK; + + uint256 amountIn = 1000 * 10 ** 6; + uint256 maxAmountIn = 1000 * 10 ** 6; + uint256 minAmountOut = 900 * 10 ** 6; + + DataTypes.ExecuteSwapParamsData[] memory swapParamsData = new DataTypes.ExecuteSwapParamsData[](2); + swapParamsData[0] = DataTypes.ExecuteSwapParamsData({ + target: tokenIn, + data: abi.encodeWithSelector(IERC20.approve.selector, environment.router, amountIn) + }); + swapParamsData[1] = DataTypes.ExecuteSwapParamsData({ + target: environment.router, + data: abi.encodeWithSelector( + IRouter.exactInputSingle.selector, + IRouter.ExactInputSingleParams({ + tokenIn: tokenIn, + tokenOut: tokenOut, + fee: 100, + recipient: address(superloop), + amountIn: amountIn, + amountOutMinimum: 0, + sqrtPriceLimitX96: 0 + }) + ) + }); + + DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ + tokenIn: tokenIn, + tokenOut: tokenOut, + amountIn: amountIn, + maxAmountIn: maxAmountIn, + minAmountOut: minAmountOut, + data: swapParamsData + }); + + DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); + moduleExecutionData[0] = DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.DELEGATECALL, + module: address(dexModule), + data: abi.encodeWithSelector(dexModule.execute.selector, swapParams) + }); + + vm.startPrank(USDT_ETLK_WHALE); + IERC20(tokenIn).transfer(address(superloop), amountIn); + vm.stopPrank(); + + vm.startPrank(admin); + superloop.operate(moduleExecutionData); + vm.stopPrank(); + + uint256 usdcBalance = IERC20(tokenOut).balanceOf(address(superloop)); + assertGt(usdcBalance, 0); + } + + function test_executeSwapAndExit() public { + address tokenIn = USDT_ETLK; + address tokenOut = USDC_ETLK; + + vm.startPrank(USDT_ETLK_WHALE); + IERC20(tokenIn).transfer(address(user), 1000 * 10 ** 6); + vm.stopPrank(); + + uint256 amountIn = 1000 * 10 ** 6; + uint256 maxAmountIn = 1000 * 10 ** 6; + uint256 minAmountOut = 900 * 10 ** 6; + + DataTypes.ExecuteSwapParamsData[] memory data = new DataTypes.ExecuteSwapParamsData[](2); + data[0] = DataTypes.ExecuteSwapParamsData({ + target: tokenIn, + data: abi.encodeWithSelector(IERC20.approve.selector, environment.router, amountIn) + }); + data[1] = DataTypes.ExecuteSwapParamsData({ + target: environment.router, + data: abi.encodeWithSelector( + IRouter.exactInputSingle.selector, + IRouter.ExactInputSingleParams({ + tokenIn: tokenIn, + tokenOut: tokenOut, + fee: 100, + recipient: address(dexModule), + amountIn: amountIn, + amountOutMinimum: 0, + sqrtPriceLimitX96: 0 + }) + ) + }); + + DataTypes.ExecuteSwapParams memory params = DataTypes.ExecuteSwapParams({ + tokenIn: tokenIn, + tokenOut: tokenOut, + amountIn: amountIn, + maxAmountIn: maxAmountIn, + minAmountOut: minAmountOut, + data: data + }); + + vm.startPrank(user); + IERC20(tokenIn).approve(address(dexModule), amountIn); + uint256 amountOut = dexModule.executeAndExit(params, user); + + vm.stopPrank(); + + uint256 usdcBalanceUser = IERC20(tokenOut).balanceOf(address(user)); + assertEq(usdcBalanceUser, amountOut); + } +} diff --git a/test/modules/fallback/AaveV3PreliquidationFallbackHandler.t.sol b/test/modules/fallback/AaveV3PreliquidationFallbackHandler.t.sol index f13c4d5..172e08f 100644 --- a/test/modules/fallback/AaveV3PreliquidationFallbackHandler.t.sol +++ b/test/modules/fallback/AaveV3PreliquidationFallbackHandler.t.sol @@ -1,388 +1,663 @@ -// // SPDX-License-Identifier: MIT -// pragma solidity ^0.8.13; - -// import {TestBase} from "../../core/TestBase.sol"; -// import {AaveV3PreliquidationFallbackHandler} from -// "../../../src/modules/fallback/AaveV3PreliquidationFallbackHandler.sol"; -// import {DataTypes} from "../../../src/common/DataTypes.sol"; -// import {Errors} from "../../../src/common/Errors.sol"; -// import {console} from "forge-std/console.sol"; -// import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -// import {IAaveOracle} from "aave-v3-core/contracts/interfaces/IAaveOracle.sol"; -// import {IPoolAddressesProvider} from "aave-v3-core/contracts/interfaces/IPoolAddressesProvider.sol"; -// import {IPoolConfigurator} from "aave-v3-core/contracts/interfaces/IPoolConfigurator.sol"; - -// contract AaveV3PreliquidationFallbackHandlerTest is TestBase { -// AaveV3PreliquidationFallbackHandler public preliquidation; - -// function setUp() public override { -// super.setUp(); -// id = bytes32("1"); -// LLTV = (7800 * WAD) / BPS; -// preliquidation = new AaveV3PreliquidationFallbackHandler( -// AAVE_V3_POOL_ADDRESSES_PROVIDER, -// 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f, -// 2, -// 8, -// DataTypes.AaveV3PreliquidationParamsInit({ -// id: id, -// lendReserve: ST_XTZ, -// borrowReserve: XTZ, -// preLltv: PRE_LLTV, -// preCF1: PRE_CF1, -// preCF2: PRE_CF2, -// preIF1: PRE_IF1, -// preIF2: PRE_IF2 -// }) -// ); -// } - -// function test_preliquidationParams() public view { -// DataTypes.AaveV3PreliquidationParams memory preliquidationParams = -// preliquidation.preliquidationParams(id, DataTypes.CallType.DELEGATECALL); -// assertEq(preliquidationParams.lendReserve, ST_XTZ); -// assertEq(preliquidationParams.borrowReserve, XTZ); -// assertEq(preliquidationParams.Lltv, LLTV); -// assertEq(preliquidationParams.preLltv, PRE_LLTV); -// assertEq(preliquidationParams.preCF1, PRE_CF1); -// assertEq(preliquidationParams.preCF2, PRE_CF2); -// assertEq(preliquidationParams.preIF1, PRE_IF1); -// assertEq(preliquidationParams.preIF2, PRE_IF2); -// } - -// function test_preliquidation() public { -// deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); -// deal(XTZ, address(preliquidation), 70 * 10 ** 18); - -// vm.startPrank(address(preliquidation)); -// IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); -// pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); -// pool.borrow(XTZ, 70 * 10 ** 18, 2, 0, address(preliquidation)); -// vm.stopPrank(); - -// deal(XTZ, address(this), 12 * 10 ** 18); - -// uint256 stxtzBalanceBefore = IERC20(ST_XTZ).balanceOf(address(this)); -// uint256 xtzBalanceBefore = IERC20(XTZ).balanceOf(address(this)); - -// IAaveOracle oracle = IAaveOracle(IPoolAddressesProvider(AAVE_V3_POOL_ADDRESSES_PROVIDER).getPriceOracle()); - -// // usd value of debt repaid -// uint256 debtRepaid = 10 * 10 ** 18; -// uint256 debtPriceUsd = oracle.getAssetPrice(XTZ); -// uint256 debtRepaidUSD = debtRepaid * debtPriceUsd / (10 ** 18); - -// IERC20(XTZ).approve(address(preliquidation), 12 * 10 ** 18); -// preliquidation.preliquidate( -// id, -// DataTypes.CallType.DELEGATECALL, -// DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 10 * 10 ** 18}) -// ); - -// uint256 stxtzBalanceAfter = IERC20(ST_XTZ).balanceOf(address(this)); -// uint256 xtzBalanceAfter = IERC20(XTZ).balanceOf(address(this)); - -// uint256 collateralToSieze = stxtzBalanceAfter - stxtzBalanceBefore; -// uint256 collateralPriceUsd = oracle.getAssetPrice(ST_XTZ); -// uint256 collateralToSiezeUSD = collateralToSieze * collateralPriceUsd / (10 ** 6); - -// // compare collateralToSiezeUSD and debtRepaidUSD -// uint256 incentiveWAD = ((collateralToSiezeUSD) * WAD) / debtRepaidUSD; -// assertTrue(incentiveWAD > PRE_IF1 && incentiveWAD < PRE_IF2); -// assertEq(xtzBalanceAfter, xtzBalanceBefore - 10 * 10 ** 18); -// } - -// // ============ REVERTING CASES ============ - -// function test_preliquidation_revert_invalid_lltv() public { -// deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); -// deal(XTZ, address(preliquidation), 70 * 10 ** 18); - -// vm.startPrank(address(preliquidation)); -// IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); -// pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); -// pool.borrow(XTZ, 70 * 10 ** 18, 2, 0, address(preliquidation)); -// vm.stopPrank(); - -// deal(XTZ, address(this), 12 * 10 ** 18); - -// // updat emode ltv to 9900 => it should revert -// vm.prank(POOL_ADMIN); -// IPoolConfigurator(POOL_CONFIGURATOR).configureReserveAsCollateral(ST_XTZ, 9000, 9200, 10100); - -// IERC20(XTZ).approve(address(preliquidation), 12 * 10 ** 18); - -// vm.expectRevert(bytes(Errors.AAVE_V3_PRELIQUIDATION_INVALID_LLTV)); -// preliquidation.preliquidate( -// id, -// DataTypes.CallType.DELEGATECALL, -// DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 10 * 10 ** 18}) -// ); -// } - -// function test_preliquidate_revert_invalid_id() public { -// deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); -// deal(XTZ, address(preliquidation), 70 * 10 ** 18); - -// vm.startPrank(address(preliquidation)); -// IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); -// pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); -// pool.borrow(XTZ, 70 * 10 ** 18, 2, 0, address(preliquidation)); -// vm.stopPrank(); - -// deal(XTZ, address(this), 12 * 10 ** 18); -// IERC20(XTZ).approve(address(preliquidation), 12 * 10 ** 18); - -// bytes32 wrongId = bytes32("wrong_id"); -// vm.expectRevert(bytes(Errors.PRELIQUIDATION_INVALID_ID)); -// preliquidation.preliquidate( -// wrongId, -// DataTypes.CallType.DELEGATECALL, -// DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 10 * 10 ** 18}) -// ); -// } - -// function test_preliquidate_revert_invalid_user() public { -// deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); -// deal(XTZ, address(preliquidation), 70 * 10 ** 18); - -// vm.startPrank(address(preliquidation)); -// IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); -// pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); -// pool.borrow(XTZ, 70 * 10 ** 18, 2, 0, address(preliquidation)); -// vm.stopPrank(); - -// deal(XTZ, address(this), 12 * 10 ** 18); -// IERC20(XTZ).approve(address(preliquidation), 12 * 10 ** 18); - -// address wrongUser = makeAddr("wrongUser"); -// vm.expectRevert(bytes(Errors.PRELIQUIDATION_INVALID_USER)); -// preliquidation.preliquidate( -// id, -// DataTypes.CallType.DELEGATECALL, -// DataTypes.AaveV3ExecutePreliquidationParams({user: wrongUser, debtToCover: 10 * 10 ** 18}) -// ); -// } - -// function test_preliquidate_revert_possible_bad_debt() public { -// // Set up position with LTV > LLTV (bad debt scenario) -// deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); -// deal(XTZ, address(preliquidation), 80 * 10 ** 18); // High borrow amount - -// vm.startPrank(address(preliquidation)); -// IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); -// pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); -// vm.expectRevert(bytes(Errors.INSUFFICIENT_CASH_SHORTFALL)); -// pool.borrow(XTZ, 80 * 10 ** 18, 2, 0, address(preliquidation)); -// vm.stopPrank(); -// } - -// function test_preliquidate_revert_not_in_preliquidation_state() public { -// // Set up position with LTV <= preLltv (not in preliquidation state) -// deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); -// deal(XTZ, address(preliquidation), 40 * 10 ** 18); // Low borrow amount - -// vm.startPrank(address(preliquidation)); -// IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); -// pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); -// pool.borrow(XTZ, 40 * 10 ** 18, 2, 0, address(preliquidation)); -// vm.stopPrank(); - -// deal(XTZ, address(this), 12 * 10 ** 18); -// IERC20(XTZ).approve(address(preliquidation), 12 * 10 ** 18); - -// vm.expectRevert(bytes(Errors.PRELIQUIDATION_NOT_IN_PRELIQUIDATION_STATE)); -// preliquidation.preliquidate( -// id, -// DataTypes.CallType.DELEGATECALL, -// DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 10 * 10 ** 18}) -// ); -// } - -// // // ============ EDGE CASES ============ - -// function test_preliquidate_edge_case_ltv_equals_prelltv() public { -// // Set up position with LTV = preLltv (exactly at the boundary) -// deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); - -// // Calculate exact borrow amount to achieve LTV = preLltv -// // We need: borrowUsdWAD = collateralUsdWAD * preLltv -// // For simplicity, we'll use a value that should be close to the boundary -// uint256 borrowAmount = 50 * 10 ** 18; // This should be close to preLltv boundary - -// deal(XTZ, address(preliquidation), borrowAmount); - -// vm.startPrank(address(preliquidation)); -// IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); -// pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); -// pool.borrow(XTZ, borrowAmount, 2, 0, address(preliquidation)); -// vm.stopPrank(); - -// deal(XTZ, address(this), 12 * 10 ** 18); -// IERC20(XTZ).approve(address(preliquidation), 12 * 10 ** 18); - -// // This should revert because LTV <= preLltv -// vm.expectRevert(bytes(Errors.PRELIQUIDATION_NOT_IN_PRELIQUIDATION_STATE)); -// preliquidation.preliquidate( -// id, -// DataTypes.CallType.DELEGATECALL, -// DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 5 * 10 ** 18}) -// ); -// } - -// function test_preliquidate_edge_case_ltv_equals_lltv() public { -// // Set up position with LTV = Lltv (exactly at the liquidation threshold) -// deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); - -// // Calculate exact borrow amount to achieve LTV = Lltv -// uint256 borrowAmount = 74 * 10 ** 18; // This should be close to Lltv boundary - -// deal(XTZ, address(preliquidation), borrowAmount); - -// vm.startPrank(address(preliquidation)); -// IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); -// pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); -// pool.borrow(XTZ, borrowAmount, 2, 0, address(preliquidation)); -// vm.stopPrank(); - -// deal(XTZ, address(this), 12 * 10 ** 18); -// IERC20(XTZ).approve(address(preliquidation), 12 * 10 ** 18); - -// // This should succeed as it's exactly at the boundary -// preliquidation.preliquidate( -// id, -// DataTypes.CallType.DELEGATECALL, -// DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 5 * 10 ** 18}) -// ); -// } - -// function test_preliquidate_edge_case_trying_to_liquidate_max() public { -// deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); -// deal(XTZ, address(this), 70 * 10 ** 18); - -// vm.startPrank(address(preliquidation)); -// IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); -// pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); -// pool.borrow(XTZ, 70 * 10 ** 18, 2, 0, address(preliquidation)); -// vm.stopPrank(); - -// // try to liquidate 70xtz, but i should be able to do only till close factor - -// uint256 xtzBalanceBefore = IERC20(XTZ).balanceOf(address(this)); -// uint256 stxtzBalanceBefore = IERC20(ST_XTZ).balanceOf(address(this)); - -// IERC20(XTZ).approve(address(preliquidation), 70 * 10 ** 18); -// preliquidation.preliquidate( -// id, -// DataTypes.CallType.DELEGATECALL, -// DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 70 * 10 ** 18}) -// ); - -// uint256 xtzBalanceAfter = IERC20(XTZ).balanceOf(address(this)); -// uint256 stxtzBalanceAfter = IERC20(ST_XTZ).balanceOf(address(this)); - -// uint256 collateralSeized = stxtzBalanceAfter - stxtzBalanceBefore; - -// assertTrue(collateralSeized < 40 * 10 ** 6); -// assertTrue(xtzBalanceBefore - xtzBalanceAfter < 40 * 10 ** 18); -// } - -// // ============ LTV IN RANGE TESTS ============ - -// function test_preliquidate_ltv_low_range() public { -// // Set up position with LTV slightly above preLltv -// deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); -// uint256 borrowAmount = 55 * 10 ** 18; // Between preLltv and Lltv - -// deal(XTZ, address(preliquidation), borrowAmount); - -// vm.startPrank(address(preliquidation)); -// IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); -// pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); -// pool.borrow(XTZ, borrowAmount, 2, 0, address(preliquidation)); -// vm.stopPrank(); - -// deal(XTZ, address(this), 12 * 10 ** 18); -// IERC20(XTZ).approve(address(preliquidation), 12 * 10 ** 18); - -// preliquidation.preliquidate( -// id, -// DataTypes.CallType.DELEGATECALL, -// DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 5 * 10 ** 18}) -// ); -// } - -// function test_preliquidate_ltv_mid_range() public { -// // Set up position with LTV in the middle of preLltv and Lltv -// deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); -// uint256 borrowAmount = 65 * 10 ** 18; // Middle of the range - -// deal(XTZ, address(preliquidation), borrowAmount); - -// vm.startPrank(address(preliquidation)); -// IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); -// pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); -// pool.borrow(XTZ, borrowAmount, 2, 0, address(preliquidation)); -// vm.stopPrank(); - -// deal(XTZ, address(this), 12 * 10 ** 18); -// IERC20(XTZ).approve(address(preliquidation), 12 * 10 ** 18); - -// preliquidation.preliquidate( -// id, -// DataTypes.CallType.DELEGATECALL, -// DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 5 * 10 ** 18}) -// ); -// } - -// function test_preliquidate_ltv_high_range() public { -// // Set up position with LTV close to Lltv -// deal(ST_XTZ, address(preliquidation), 100 * 10 ** 6); -// uint256 borrowAmount = 74 * 10 ** 18; // Close to Lltv - -// deal(XTZ, address(preliquidation), borrowAmount); - -// vm.startPrank(address(preliquidation)); -// IERC20(ST_XTZ).approve(address(pool), 100 * 10 ** 6); -// pool.supply(ST_XTZ, 100 * 10 ** 6, address(preliquidation), 0); -// pool.borrow(XTZ, borrowAmount, 2, 0, address(preliquidation)); -// vm.stopPrank(); - -// deal(XTZ, address(this), 12 * 10 ** 18); -// IERC20(XTZ).approve(address(preliquidation), 12 * 10 ** 18); - -// preliquidation.preliquidate( -// id, -// DataTypes.CallType.DELEGATECALL, -// DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 5 * 10 ** 18}) -// ); -// } - -// // ============ E-MODE TESTS ============ - -// function test_preliquidate_e_mode() public { -// address newPreLiq = 0x2e234DAe75C793f67A35089C9d99245E1C58470b; -// vm.prank(address(newPreLiq)); -// pool.setUserEMode(3); -// preliquidation = new AaveV3PreliquidationFallbackHandler( -// AAVE_V3_POOL_ADDRESSES_PROVIDER, -// newPreLiq, -// 2, -// 8, -// DataTypes.AaveV3PreliquidationParamsInit({ -// id: id, -// lendReserve: ST_XTZ, -// borrowReserve: XTZ, -// preLltv: PRE_LLTV, -// preCF1: PRE_CF1, -// preCF2: PRE_CF2, -// preIF1: PRE_IF1, -// preIF2: PRE_IF2 -// }) -// ); - -// DataTypes.AaveV3PreliquidationParams memory preliquidationParams = -// preliquidation.preliquidationParams(id, DataTypes.CallType.DELEGATECALL); -// assertEq(preliquidationParams.Lltv, (9600 * WAD) / BPS); -// } -// } +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import {TestBase} from "../../core/TestBase.sol"; +import {AaveV3PreliquidationFallbackHandler} from + "../../../src/modules/fallback/AaveV3PreliquidationFallbackHandler.sol"; +import {DataTypes} from "../../../src/common/DataTypes.sol"; +import {Errors} from "../../../src/common/Errors.sol"; +import {console} from "forge-std/console.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {IAaveOracle} from "aave-v3-core/contracts/interfaces/IAaveOracle.sol"; +import {IPoolAddressesProvider} from "aave-v3-core/contracts/interfaces/IPoolAddressesProvider.sol"; +import {IPoolConfigurator} from "aave-v3-core/contracts/interfaces/IPoolConfigurator.sol"; +import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; + +// not maintained right now. will be updated soon. +contract AaveV3PreliquidationFallbackHandlerTest is TestBase { + AaveV3PreliquidationFallbackHandler public preliquidation; + + function setUp() public override { + super.setUp(); + id = bytes32("1"); + LLTV = (7800 * WAD) / BPS; + preliquidation = new AaveV3PreliquidationFallbackHandler( + environment.poolAddressesProvider, + 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f, + 2, + 8, + DataTypes.AaveV3PreliquidationParamsInit({ + id: id, + lendReserve: environment.lendAssets[0], + borrowReserve: environment.borrowAssets[0], + preLltv: PRE_LLTV, + preCF1: PRE_CF1, + preCF2: PRE_CF2, + preIF1: PRE_IF1, + preIF2: PRE_IF2 + }) + ); + } + + function test_preliquidationParams() public view { + DataTypes.AaveV3PreliquidationParams memory preliquidationParams = + preliquidation.preliquidationParams(id, DataTypes.CallType.DELEGATECALL); + assertEq(preliquidationParams.lendReserve, environment.lendAssets[0]); + assertEq(preliquidationParams.borrowReserve, environment.borrowAssets[0]); + assertEq(preliquidationParams.Lltv, LLTV); + assertEq(preliquidationParams.preLltv, PRE_LLTV); + assertEq(preliquidationParams.preCF1, PRE_CF1); + assertEq(preliquidationParams.preCF2, PRE_CF2); + assertEq(preliquidationParams.preIF1, PRE_IF1); + assertEq(preliquidationParams.preIF2, PRE_IF2); + } + + function test_preliquidation() public { + deal( + environment.lendAssets[0], + address(preliquidation), + 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() + ); + deal( + environment.borrowAssets[0], + address(preliquidation), + 70 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() + ); + + vm.startPrank(address(preliquidation)); + IERC20(environment.lendAssets[0]).approve( + address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() + ); + pool.supply( + environment.lendAssets[0], + 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals(), + address(preliquidation), + 0 + ); + pool.borrow( + environment.borrowAssets[0], + 70 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals(), + 2, + 0, + address(preliquidation) + ); + vm.stopPrank(); + + deal( + environment.borrowAssets[0], + address(this), + 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() + ); + + uint256 lendAssetBalanceBefore = IERC20(environment.lendAssets[0]).balanceOf(address(this)); + uint256 borrowAssetBalanceBefore = IERC20(environment.borrowAssets[0]).balanceOf(address(this)); + + IAaveOracle oracle = IAaveOracle(IPoolAddressesProvider(environment.poolAddressesProvider).getPriceOracle()); + + // usd value of debt repaid + uint256 debtRepaid = 10 * 10 ** 18; + uint256 debtPriceUsd = oracle.getAssetPrice(environment.borrowAssets[0]); + uint256 debtRepaidUSD = + (debtRepaid * debtPriceUsd) / (10 ** IERC20Metadata(environment.borrowAssets[0]).decimals()); + + IERC20(environment.borrowAssets[0]).approve( + address(preliquidation), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() + ); + preliquidation.preliquidate( + id, + DataTypes.CallType.DELEGATECALL, + DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 10 * 10 ** 18}) + ); + + uint256 lendAssetBalanceAfter = IERC20(environment.lendAssets[0]).balanceOf(address(this)); + uint256 borrowAssetBalanceAfter = IERC20(environment.borrowAssets[0]).balanceOf(address(this)); + + uint256 collateralToSieze = lendAssetBalanceAfter - lendAssetBalanceBefore; + uint256 collateralPriceUsd = oracle.getAssetPrice(environment.lendAssets[0]); + uint256 collateralToSiezeUSD = (collateralToSieze * collateralPriceUsd) / (10 ** 6); + + // compare collateralToSiezeUSD and debtRepaidUSD + uint256 incentiveWAD = ((collateralToSiezeUSD) * WAD) / debtRepaidUSD; + assertTrue(incentiveWAD > PRE_IF1 && incentiveWAD < PRE_IF2); + assertEq( + borrowAssetBalanceAfter, + borrowAssetBalanceBefore - 10 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() + ); + } + + // ============ REVERTING CASES ============ + + function test_preliquidation_revert_invalid_lltv() public { + deal( + environment.lendAssets[0], + address(preliquidation), + 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() + ); + deal( + environment.borrowAssets[0], + address(preliquidation), + 70 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() + ); + + vm.startPrank(address(preliquidation)); + IERC20(environment.lendAssets[0]).approve( + address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() + ); + pool.supply( + environment.lendAssets[0], + 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals(), + address(preliquidation), + 0 + ); + pool.borrow( + environment.borrowAssets[0], + 70 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals(), + 2, + 0, + address(preliquidation) + ); + vm.stopPrank(); + + deal( + environment.borrowAssets[0], + address(this), + 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() + ); + + // updat emode ltv to 9900 => it should revert + vm.prank(environment.poolAdmin); + IPoolConfigurator(environment.poolConfigurator).configureReserveAsCollateral( + environment.lendAssets[0], 9000, 9200, 10100 + ); + + IERC20(environment.borrowAssets[0]).approve( + address(preliquidation), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() + ); + + vm.expectRevert(bytes(Errors.AAVE_V3_PRELIQUIDATION_INVALID_LLTV)); + preliquidation.preliquidate( + id, + DataTypes.CallType.DELEGATECALL, + DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 10 * 10 ** 18}) + ); + } + + function test_preliquidate_revert_invalid_id() public { + deal( + environment.lendAssets[0], + address(preliquidation), + 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() + ); + deal( + environment.borrowAssets[0], + address(preliquidation), + 70 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() + ); + + vm.startPrank(address(preliquidation)); + IERC20(environment.lendAssets[0]).approve( + address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() + ); + pool.supply( + environment.lendAssets[0], + 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals(), + address(preliquidation), + 0 + ); + pool.borrow( + environment.borrowAssets[0], + 70 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals(), + 2, + 0, + address(preliquidation) + ); + vm.stopPrank(); + + deal( + environment.borrowAssets[0], + address(this), + 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() + ); + IERC20(environment.borrowAssets[0]).approve( + address(preliquidation), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() + ); + + bytes32 wrongId = bytes32("wrong_id"); + vm.expectRevert(bytes(Errors.PRELIQUIDATION_INVALID_ID)); + preliquidation.preliquidate( + wrongId, + DataTypes.CallType.DELEGATECALL, + DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 10 * 10 ** 18}) + ); + } + + function test_preliquidate_revert_invalid_user() public { + deal( + environment.lendAssets[0], + address(preliquidation), + 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() + ); + deal( + environment.borrowAssets[0], + address(preliquidation), + 70 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() + ); + + vm.startPrank(address(preliquidation)); + IERC20(environment.lendAssets[0]).approve( + address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() + ); + pool.supply( + environment.lendAssets[0], + 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals(), + address(preliquidation), + 0 + ); + pool.borrow( + environment.borrowAssets[0], + 70 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals(), + 2, + 0, + address(preliquidation) + ); + vm.stopPrank(); + + deal( + environment.borrowAssets[0], + address(this), + 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() + ); + IERC20(environment.borrowAssets[0]).approve( + address(preliquidation), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() + ); + + address wrongUser = makeAddr("wrongUser"); + vm.expectRevert(bytes(Errors.PRELIQUIDATION_INVALID_USER)); + preliquidation.preliquidate( + id, + DataTypes.CallType.DELEGATECALL, + DataTypes.AaveV3ExecutePreliquidationParams({user: wrongUser, debtToCover: 10 * 10 ** 18}) + ); + } + + function test_preliquidate_revert_possible_bad_debt() public { + // Set up position with LTV > LLTV (bad debt scenario) + deal( + environment.lendAssets[0], + address(preliquidation), + 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() + ); + deal( + environment.borrowAssets[0], + address(preliquidation), + 80 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() + ); // High borrow amount + + vm.startPrank(address(preliquidation)); + IERC20(environment.lendAssets[0]).approve( + address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() + ); + pool.supply( + environment.lendAssets[0], + 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals(), + address(preliquidation), + 0 + ); + vm.expectRevert(bytes(Errors.INSUFFICIENT_CASH_SHORTFALL)); + pool.borrow( + environment.borrowAssets[0], + 80 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals(), + 2, + 0, + address(preliquidation) + ); + vm.stopPrank(); + } + + function test_preliquidate_revert_not_in_preliquidation_state() public { + // Set up position with LTV <= preLltv (not in preliquidation state) + deal( + environment.lendAssets[0], + address(preliquidation), + 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() + ); + deal( + environment.borrowAssets[0], + address(preliquidation), + 40 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() + ); // Low borrow amount + + vm.startPrank(address(preliquidation)); + IERC20(environment.lendAssets[0]).approve( + address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() + ); + pool.supply( + environment.lendAssets[0], + 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals(), + address(preliquidation), + 0 + ); + pool.borrow( + environment.borrowAssets[0], + 40 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals(), + 2, + 0, + address(preliquidation) + ); + vm.stopPrank(); + + deal( + environment.borrowAssets[0], + address(this), + 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() + ); + IERC20(environment.borrowAssets[0]).approve( + address(preliquidation), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() + ); + + vm.expectRevert(bytes(Errors.PRELIQUIDATION_NOT_IN_PRELIQUIDATION_STATE)); + preliquidation.preliquidate( + id, + DataTypes.CallType.DELEGATECALL, + DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 10 * 10 ** 18}) + ); + } + + // // ============ EDGE CASES ============ + + function test_preliquidate_edge_case_ltv_equals_prelltv() public { + // Set up position with LTV = preLltv (exactly at the boundary) + deal( + environment.lendAssets[0], + address(preliquidation), + 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() + ); + + // Calculate exact borrow amount to achieve LTV = preLltv + // We need: borrowUsdWAD = collateralUsdWAD * preLltv + // For simplicity, we'll use a value that should be close to the boundary + uint256 borrowAmount = 50 * 10 ** 18; // This should be close to preLltv boundary + + deal(environment.borrowAssets[0], address(preliquidation), borrowAmount); + + vm.startPrank(address(preliquidation)); + IERC20(environment.lendAssets[0]).approve( + address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() + ); + pool.supply( + environment.lendAssets[0], + 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals(), + address(preliquidation), + 0 + ); + pool.borrow(environment.borrowAssets[0], borrowAmount, 2, 0, address(preliquidation)); + vm.stopPrank(); + + deal( + environment.borrowAssets[0], + address(this), + 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() + ); + IERC20(environment.borrowAssets[0]).approve( + address(preliquidation), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() + ); + + // This should revert because LTV <= preLltv + vm.expectRevert(bytes(Errors.PRELIQUIDATION_NOT_IN_PRELIQUIDATION_STATE)); + preliquidation.preliquidate( + id, + DataTypes.CallType.DELEGATECALL, + DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 5 * 10 ** 18}) + ); + } + + function test_preliquidate_edge_case_ltv_equals_lltv() public { + // Set up position with LTV = Lltv (exactly at the liquidation threshold) + deal( + environment.lendAssets[0], + address(preliquidation), + 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() + ); + + // Calculate exact borrow amount to achieve LTV = Lltv + uint256 borrowAmount = 74 * 10 ** 18; // This should be close to Lltv boundary + + deal(environment.borrowAssets[0], address(preliquidation), borrowAmount); + + vm.startPrank(address(preliquidation)); + IERC20(environment.lendAssets[0]).approve( + address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() + ); + pool.supply( + environment.lendAssets[0], + 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals(), + address(preliquidation), + 0 + ); + pool.borrow(environment.borrowAssets[0], borrowAmount, 2, 0, address(preliquidation)); + vm.stopPrank(); + + deal( + environment.borrowAssets[0], + address(this), + 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() + ); + IERC20(environment.borrowAssets[0]).approve( + address(preliquidation), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() + ); + + // This should succeed as it's exactly at the boundary + preliquidation.preliquidate( + id, + DataTypes.CallType.DELEGATECALL, + DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 5 * 10 ** 18}) + ); + } + + function test_preliquidate_edge_case_trying_to_liquidate_max() public { + deal( + environment.lendAssets[0], + address(preliquidation), + 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() + ); + deal( + environment.borrowAssets[0], + address(this), + 70 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() + ); + + vm.startPrank(address(preliquidation)); + IERC20(environment.lendAssets[0]).approve( + address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() + ); + pool.supply( + environment.lendAssets[0], + 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals(), + address(preliquidation), + 0 + ); + pool.borrow( + environment.borrowAssets[0], + 70 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals(), + 2, + 0, + address(preliquidation) + ); + vm.stopPrank(); + + // try to liquidate 70xtz, but i should be able to do only till close factor + + uint256 borrowAssetBalanceBefore = IERC20(environment.borrowAssets[0]).balanceOf(address(this)); + uint256 lendAssetBalanceBefore = IERC20(environment.lendAssets[0]).balanceOf(address(this)); + + IERC20(environment.borrowAssets[0]).approve( + address(preliquidation), 70 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() + ); + preliquidation.preliquidate( + id, + DataTypes.CallType.DELEGATECALL, + DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 70 * 10 ** 18}) + ); + + uint256 borrowAssetBalanceAfter = IERC20(environment.borrowAssets[0]).balanceOf(address(this)); + uint256 lendAssetBalanceAfter = IERC20(environment.lendAssets[0]).balanceOf(address(this)); + + uint256 collateralSeized = lendAssetBalanceAfter - lendAssetBalanceBefore; + + assertTrue(collateralSeized < 40 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals()); + assertTrue( + borrowAssetBalanceBefore - borrowAssetBalanceAfter + < 40 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() + ); + } + + // ============ LTV IN RANGE TESTS ============ + + function test_preliquidate_ltv_low_range() public { + // Set up position with LTV slightly above preLltv + deal( + environment.lendAssets[0], + address(preliquidation), + 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() + ); + uint256 borrowAmount = 55 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals(); // Between preLltv and Lltv + + deal(environment.borrowAssets[0], address(preliquidation), borrowAmount); + + vm.startPrank(address(preliquidation)); + IERC20(environment.lendAssets[0]).approve( + address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() + ); + pool.supply( + environment.lendAssets[0], + 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals(), + address(preliquidation), + 0 + ); + pool.borrow(environment.borrowAssets[0], borrowAmount, 2, 0, address(preliquidation)); + vm.stopPrank(); + + deal( + environment.borrowAssets[0], + address(this), + 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() + ); + IERC20(environment.borrowAssets[0]).approve( + address(preliquidation), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() + ); + + preliquidation.preliquidate( + id, + DataTypes.CallType.DELEGATECALL, + DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 5 * 10 ** 18}) + ); + } + + function test_preliquidate_ltv_mid_range() public { + // Set up position with LTV in the middle of preLltv and Lltv + deal( + environment.lendAssets[0], + address(preliquidation), + 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() + ); + uint256 borrowAmount = 65 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals(); // Middle of the range + + deal(environment.borrowAssets[0], address(preliquidation), borrowAmount); + + vm.startPrank(address(preliquidation)); + IERC20(environment.lendAssets[0]).approve( + address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() + ); + pool.supply( + environment.lendAssets[0], + 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals(), + address(preliquidation), + 0 + ); + pool.borrow(environment.borrowAssets[0], borrowAmount, 2, 0, address(preliquidation)); + vm.stopPrank(); + + deal( + environment.borrowAssets[0], + address(this), + 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() + ); + IERC20(environment.borrowAssets[0]).approve( + address(preliquidation), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() + ); + + preliquidation.preliquidate( + id, + DataTypes.CallType.DELEGATECALL, + DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 5 * 10 ** 18}) + ); + } + + function test_preliquidate_ltv_high_range() public { + // Set up position with LTV close to Lltv + deal( + environment.lendAssets[0], + address(preliquidation), + 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() + ); + uint256 borrowAmount = 74 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals(); // Close to Lltv + + deal(environment.borrowAssets[0], address(preliquidation), borrowAmount); + + vm.startPrank(address(preliquidation)); + IERC20(environment.lendAssets[0]).approve( + address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() + ); + pool.supply( + environment.lendAssets[0], + 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals(), + address(preliquidation), + 0 + ); + pool.borrow(environment.borrowAssets[0], borrowAmount, 2, 0, address(preliquidation)); + vm.stopPrank(); + + deal( + environment.borrowAssets[0], + address(this), + 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() + ); + IERC20(environment.borrowAssets[0]).approve( + address(preliquidation), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() + ); + + preliquidation.preliquidate( + id, + DataTypes.CallType.DELEGATECALL, + DataTypes.AaveV3ExecutePreliquidationParams({user: address(preliquidation), debtToCover: 5 * 10 ** 18}) + ); + } + + // ============ E-MODE TESTS ============ + + function test_preliquidate_e_mode() public { + address newPreLiq = 0x2e234DAe75C793f67A35089C9d99245E1C58470b; + vm.prank(address(newPreLiq)); + pool.setUserEMode(3); + preliquidation = new AaveV3PreliquidationFallbackHandler( + environment.poolAddressesProvider, + newPreLiq, + 2, + 8, + DataTypes.AaveV3PreliquidationParamsInit({ + id: id, + lendReserve: environment.lendAssets[0], + borrowReserve: environment.borrowAssets[0], + preLltv: PRE_LLTV, + preCF1: PRE_CF1, + preCF2: PRE_CF2, + preIF1: PRE_IF1, + preIF2: PRE_IF2 + }) + ); + + DataTypes.AaveV3PreliquidationParams memory preliquidationParams = + preliquidation.preliquidationParams(id, DataTypes.CallType.DELEGATECALL); + assertEq(preliquidationParams.Lltv, (9600 * WAD) / BPS); + } +} diff --git a/test/modules/helpers/UnwrapModule.t.sol b/test/modules/helpers/UnwrapModule.t.sol index 6ccc6b5..6b0df59 100644 --- a/test/modules/helpers/UnwrapModule.t.sol +++ b/test/modules/helpers/UnwrapModule.t.sol @@ -1,104 +1,107 @@ -// // SPDX-License-Identifier: MIT - -// pragma solidity ^0.8.13; - -// import {TestBase} from "../../core/TestBase.sol"; -// import {IFlashLoanSimpleReceiver} from "aave-v3-core/contracts/flashloan/interfaces/IFlashLoanSimpleReceiver.sol"; -// import {DataTypes} from "../../../src/common/DataTypes.sol"; -// import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; -// import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; -// import {TransparentUpgradeableProxy} from -// "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -// import {console} from "forge-std/console.sol"; - -// contract UnwrapModuleTest is TestBase { -// Superloop public superloopImplementation; -// ProxyAdmin public proxyAdmin; -// address public user; - -// function setUp() public override { -// super.setUp(); - -// vm.startPrank(admin); -// _deployModules(); - -// address[] memory modules = new address[](1); -// modules[0] = address(unwrapModule); - -// DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ -// asset: XTZ, -// name: "XTZ Vault", -// symbol: "XTZV", -// supplyCap: 100000 * 10 ** 18, -// minimumDepositAmount: 100, -// instantWithdrawFee: 0, -// superloopModuleRegistry: address(moduleRegistry), -// modules: modules, -// accountant: mockModule, -// withdrawManager: mockModule, -// depositManager: mockModule, -// cashReserve: 1000, -// vaultAdmin: admin, -// treasury: treasury, -// vaultOperator: admin -// }); -// superloopImplementation = new Superloop(); -// proxyAdmin = new ProxyAdmin(address(this)); - -// TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( -// address(superloopImplementation), -// address(proxyAdmin), -// abi.encodeWithSelector(Superloop.initialize.selector, initData) -// ); -// superloop = Superloop(payable(address(proxy))); -// vm.stopPrank(); - -// user = makeAddr("user"); -// vm.label(user, "user"); -// vm.label(address(superloop), "superloop"); -// } - -// function test_SupplyBasicFlow() public { -// vm.startPrank(XTZ_WHALE); -// IERC20(XTZ).transfer(address(superloop), 1000 * 10 ** 18); -// vm.stopPrank(); - -// // Arrange -// uint256 supplyAmount = 100 * 10 ** 18; // 1000 XTZ - -// // Create supply params -// DataTypes.AaveV3ActionParams memory unwarpParams = -// DataTypes.AaveV3ActionParams({asset: XTZ, amount: supplyAmount}); - -// // Create module execution data -// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); -// moduleExecutionData[0] = DataTypes.ModuleExecutionData({ -// executionType: DataTypes.CallType.DELEGATECALL, -// module: address(unwrapModule), -// data: abi.encodeWithSelector(unwrapModule.execute.selector, unwarpParams) -// }); - -// // current supply -// uint256 currentBalanceWrappedToken = IERC20(XTZ).balanceOf(address(superloop)); -// uint256 currentBalanceUnderlyingToken = address(superloop).balance; - -// // Act -// vm.prank(admin); -// superloop.operate(moduleExecutionData); - -// uint256 finalBalanceWrappedToken = IERC20(XTZ).balanceOf(address(superloop)); -// uint256 finalBalanceUnderlyingToken = address(superloop).balance; - -// console.log("currentBalanceWrappedToken", currentBalanceWrappedToken); -// console.log("currentBalanceUnderlyingToken", currentBalanceUnderlyingToken); -// console.log("finalBalanceWrappedToken", finalBalanceWrappedToken); -// console.log("finalBalanceUnderlyingToken", finalBalanceUnderlyingToken); - -// console.log("supplyAmount", supplyAmount); - -// // Assert -// assertEq(finalBalanceWrappedToken, currentBalanceWrappedToken - supplyAmount); -// assertEq(finalBalanceUnderlyingToken, currentBalanceUnderlyingToken + supplyAmount); -// } -// } +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.13; + +import {TestBase} from "../../core/TestBase.sol"; +import {IFlashLoanSimpleReceiver} from "aave-v3-core/contracts/flashloan/interfaces/IFlashLoanSimpleReceiver.sol"; +import {DataTypes} from "../../../src/common/DataTypes.sol"; +import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; +import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; +import {TransparentUpgradeableProxy} from + "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +import {console} from "forge-std/console.sol"; + +contract UnwrapModuleTest is TestBase { + Superloop public superloopImplementation; + ProxyAdmin public proxyAdmin; + address public user; + + function setUp() public override { + super.setUp(); + + vm.startPrank(admin); + _deployModules(); + + address[] memory modules = new address[](1); + modules[0] = address(unwrapModule); + + DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ + asset: environment.vaultAsset, + name: "Vault", + symbol: "VLT", + supplyCap: 100000 * 10 ** environment.vaultAssetDecimals, + minimumDepositAmount: 100, + instantWithdrawFee: 0, + superloopModuleRegistry: address(moduleRegistry), + modules: modules, + accountant: mockModule, + withdrawManager: mockModule, + depositManager: mockModule, + cashReserve: 1000, + vaultAdmin: admin, + treasury: treasury, + vaultOperator: admin + }); + superloopImplementation = new Superloop(); + proxyAdmin = new ProxyAdmin(address(this)); + + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(superloopImplementation), + address(proxyAdmin), + abi.encodeWithSelector(Superloop.initialize.selector, initData) + ); + superloop = Superloop(payable(address(proxy))); + vm.stopPrank(); + + user = makeAddr("user"); + vm.label(user, "user"); + vm.label(address(superloop), "superloop"); + } + + function test_SupplyBasicFlow() public { + // works only on etherlink + if (environment.chainId != 42793) return; + + vm.startPrank(environment.vaultAssetWhale); + IERC20(environment.vaultAsset).transfer(address(superloop), 1000 * 10 ** environment.vaultAssetDecimals); + vm.stopPrank(); + + // Arrange + uint256 supplyAmount = 100 * 10 ** environment.vaultAssetDecimals; // 1000 vaultAsset + + // Create supply params + DataTypes.AaveV3ActionParams memory unwarpParams = + DataTypes.AaveV3ActionParams({asset: environment.vaultAsset, amount: supplyAmount}); + + // Create module execution data + DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); + moduleExecutionData[0] = DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.DELEGATECALL, + module: address(unwrapModule), + data: abi.encodeWithSelector(unwrapModule.execute.selector, unwarpParams) + }); + + // current supply + uint256 currentBalanceWrappedToken = IERC20(environment.vaultAsset).balanceOf(address(superloop)); + uint256 currentBalanceUnderlyingToken = address(superloop).balance; + + // Act + vm.prank(admin); + superloop.operate(moduleExecutionData); + + uint256 finalBalanceWrappedToken = IERC20(environment.vaultAsset).balanceOf(address(superloop)); + uint256 finalBalanceUnderlyingToken = address(superloop).balance; + + console.log("currentBalanceWrappedToken", currentBalanceWrappedToken); + console.log("currentBalanceUnderlyingToken", currentBalanceUnderlyingToken); + console.log("finalBalanceWrappedToken", finalBalanceWrappedToken); + console.log("finalBalanceUnderlyingToken", finalBalanceUnderlyingToken); + + console.log("supplyAmount", supplyAmount); + + // Assert + assertEq(finalBalanceWrappedToken, currentBalanceWrappedToken - supplyAmount); + assertEq(finalBalanceUnderlyingToken, currentBalanceUnderlyingToken + supplyAmount); + } +} diff --git a/test/modules/helpers/WrapModule.t.sol b/test/modules/helpers/WrapModule.t.sol index a7be9a0..8b2dc06 100644 --- a/test/modules/helpers/WrapModule.t.sol +++ b/test/modules/helpers/WrapModule.t.sol @@ -1,123 +1,126 @@ -// // SPDX-License-Identifier: MIT - -// pragma solidity ^0.8.13; - -// import {TestBase} from "../../core/TestBase.sol"; -// import {IFlashLoanSimpleReceiver} from "aave-v3-core/contracts/flashloan/interfaces/IFlashLoanSimpleReceiver.sol"; -// import {DataTypes} from "../../../src/common/DataTypes.sol"; -// import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; -// import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; -// import {TransparentUpgradeableProxy} from -// "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -// import {console} from "forge-std/console.sol"; - -// contract WrapModuleTest is TestBase { -// Superloop public superloopImplementation; -// ProxyAdmin public proxyAdmin; -// address public user; - -// function setUp() public override { -// super.setUp(); - -// vm.startPrank(admin); -// _deployModules(); - -// address[] memory modules = new address[](2); -// modules[0] = address(unwrapModule); -// modules[1] = address(wrapModule); - -// DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ -// asset: XTZ, -// name: "XTZ Vault", -// symbol: "XTZV", -// supplyCap: 100000 * 10 ** 18, -// minimumDepositAmount: 100, -// instantWithdrawFee: 0, -// superloopModuleRegistry: address(moduleRegistry), -// modules: modules, -// accountant: mockModule, -// withdrawManager: mockModule, -// depositManager: mockModule, -// cashReserve: 1000, -// vaultAdmin: admin, -// treasury: treasury, -// vaultOperator: admin -// }); -// superloopImplementation = new Superloop(); -// proxyAdmin = new ProxyAdmin(address(this)); - -// TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( -// address(superloopImplementation), -// address(proxyAdmin), -// abi.encodeWithSelector(Superloop.initialize.selector, initData) -// ); -// superloop = Superloop(payable(address(proxy))); -// vm.stopPrank(); - -// user = makeAddr("user"); -// vm.label(user, "user"); -// vm.label(address(superloop), "superloop"); -// } - -// function test_SupplyBasicFlow() public { -// vm.startPrank(XTZ_WHALE); -// IERC20(XTZ).transfer(address(superloop), 1000 * 10 ** 18); -// vm.stopPrank(); - -// uint256 supplyAmount = 100 * 10 ** 18; -// _executeUnwrap(supplyAmount); - -// // warp -// DataTypes.AaveV3ActionParams memory wrapParams = -// DataTypes.AaveV3ActionParams({asset: XTZ, amount: supplyAmount}); - -// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); -// moduleExecutionData[0] = DataTypes.ModuleExecutionData({ -// executionType: DataTypes.CallType.DELEGATECALL, -// module: address(wrapModule), -// data: abi.encodeWithSelector(wrapModule.execute.selector, wrapParams) -// }); - -// uint256 currentBalanceWrappedToken = IERC20(XTZ).balanceOf(address(superloop)); -// uint256 currentBalanceUnderlyingToken = address(superloop).balance; - -// vm.prank(admin); -// superloop.operate(moduleExecutionData); - -// uint256 finalBalanceWrappedToken = IERC20(XTZ).balanceOf(address(superloop)); -// uint256 finalBalanceUnderlyingToken = address(superloop).balance; - -// assertEq(finalBalanceWrappedToken, currentBalanceWrappedToken + supplyAmount); -// assertEq(finalBalanceUnderlyingToken, currentBalanceUnderlyingToken - supplyAmount); -// } - -// function _executeUnwrap(uint256 supplyAmount) internal { -// // Create supply params -// DataTypes.AaveV3ActionParams memory unwarpParams = -// DataTypes.AaveV3ActionParams({asset: XTZ, amount: supplyAmount}); - -// // Create module execution data -// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); -// moduleExecutionData[0] = DataTypes.ModuleExecutionData({ -// executionType: DataTypes.CallType.DELEGATECALL, -// module: address(unwrapModule), -// data: abi.encodeWithSelector(unwrapModule.execute.selector, unwarpParams) -// }); - -// // current supply -// uint256 currentBalanceWrappedToken = IERC20(XTZ).balanceOf(address(superloop)); -// uint256 currentBalanceUnderlyingToken = address(superloop).balance; - -// // Act -// vm.prank(admin); -// superloop.operate(moduleExecutionData); - -// uint256 finalBalanceWrappedToken = IERC20(XTZ).balanceOf(address(superloop)); -// uint256 finalBalanceUnderlyingToken = address(superloop).balance; - -// // Assert -// assertEq(finalBalanceWrappedToken, currentBalanceWrappedToken - supplyAmount); -// assertEq(finalBalanceUnderlyingToken, currentBalanceUnderlyingToken + supplyAmount); -// } -// } +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.13; + +import {TestBase} from "../../core/TestBase.sol"; +import {IFlashLoanSimpleReceiver} from "aave-v3-core/contracts/flashloan/interfaces/IFlashLoanSimpleReceiver.sol"; +import {DataTypes} from "../../../src/common/DataTypes.sol"; +import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; +import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; +import {TransparentUpgradeableProxy} from + "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +import {console} from "forge-std/console.sol"; + +contract WrapModuleTest is TestBase { + Superloop public superloopImplementation; + ProxyAdmin public proxyAdmin; + address public user; + + function setUp() public override { + super.setUp(); + + vm.startPrank(admin); + _deployModules(); + + address[] memory modules = new address[](2); + modules[0] = address(unwrapModule); + modules[1] = address(wrapModule); + + DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ + asset: environment.vaultAsset, + name: "Vault", + symbol: "VLT", + supplyCap: 100000 * 10 ** environment.vaultAssetDecimals, + minimumDepositAmount: 100, + instantWithdrawFee: 0, + superloopModuleRegistry: address(moduleRegistry), + modules: modules, + accountant: mockModule, + withdrawManager: mockModule, + depositManager: mockModule, + cashReserve: 1000, + vaultAdmin: admin, + treasury: treasury, + vaultOperator: admin + }); + superloopImplementation = new Superloop(); + proxyAdmin = new ProxyAdmin(address(this)); + + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(superloopImplementation), + address(proxyAdmin), + abi.encodeWithSelector(Superloop.initialize.selector, initData) + ); + superloop = Superloop(payable(address(proxy))); + vm.stopPrank(); + + user = makeAddr("user"); + vm.label(user, "user"); + vm.label(address(superloop), "superloop"); + } + + function test_SupplyBasicFlow() public { + // works only on etherlink + if (environment.chainId != 42793) return; + + vm.startPrank(environment.vaultAssetWhale); + IERC20(environment.vaultAsset).transfer(address(superloop), 1000 * 10 ** environment.vaultAssetDecimals); + vm.stopPrank(); + + uint256 supplyAmount = 100 * 10 ** environment.vaultAssetDecimals; + _executeUnwrap(supplyAmount); + + // warp + DataTypes.AaveV3ActionParams memory wrapParams = + DataTypes.AaveV3ActionParams({asset: environment.vaultAsset, amount: supplyAmount}); + + DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); + moduleExecutionData[0] = DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.DELEGATECALL, + module: address(wrapModule), + data: abi.encodeWithSelector(wrapModule.execute.selector, wrapParams) + }); + + uint256 currentBalanceWrappedToken = IERC20(environment.vaultAsset).balanceOf(address(superloop)); + uint256 currentBalanceUnderlyingToken = address(superloop).balance; + + vm.prank(admin); + superloop.operate(moduleExecutionData); + + uint256 finalBalanceWrappedToken = IERC20(environment.vaultAsset).balanceOf(address(superloop)); + uint256 finalBalanceUnderlyingToken = address(superloop).balance; + + assertEq(finalBalanceWrappedToken, currentBalanceWrappedToken + supplyAmount); + assertEq(finalBalanceUnderlyingToken, currentBalanceUnderlyingToken - supplyAmount); + } + + function _executeUnwrap(uint256 supplyAmount) internal { + // Create supply params + DataTypes.AaveV3ActionParams memory unwarpParams = + DataTypes.AaveV3ActionParams({asset: environment.vaultAsset, amount: supplyAmount}); + + // Create module execution data + DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); + moduleExecutionData[0] = DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.DELEGATECALL, + module: address(unwrapModule), + data: abi.encodeWithSelector(unwrapModule.execute.selector, unwarpParams) + }); + + // current supply + uint256 currentBalanceWrappedToken = IERC20(environment.vaultAsset).balanceOf(address(superloop)); + uint256 currentBalanceUnderlyingToken = address(superloop).balance; + + // Act + vm.prank(admin); + superloop.operate(moduleExecutionData); + + uint256 finalBalanceWrappedToken = IERC20(environment.vaultAsset).balanceOf(address(superloop)); + uint256 finalBalanceUnderlyingToken = address(superloop).balance; + + // Assert + assertEq(finalBalanceWrappedToken, currentBalanceWrappedToken - supplyAmount); + assertEq(finalBalanceUnderlyingToken, currentBalanceUnderlyingToken + supplyAmount); + } +} From 1cdec999b6e19dcae225dcf14c6cfccb860b2ddf Mon Sep 17 00:00:00 2001 From: Priyam Anand Date: Fri, 23 Jan 2026 15:24:19 +0530 Subject: [PATCH 06/21] test: hyperliquid modules restructure --- test/core/TestBase.sol | 24 ++- test/core/TestEnv.sol | 33 +++ .../hyperliquid/HyperbeatStakingModule.t.sol | 196 +++++++++--------- .../HyperliquidStakingModule.t.sol | 166 +++++++-------- .../hyperliquid/KinetiqStakingModule.t.sol | 160 +++++++------- test/modules/stake/hyperliquid/TestBase.sol | 124 ----------- 6 files changed, 323 insertions(+), 380 deletions(-) delete mode 100644 test/modules/stake/hyperliquid/TestBase.sol diff --git a/test/core/TestBase.sol b/test/core/TestBase.sol index 74fe5f1..f4a9f61 100644 --- a/test/core/TestBase.sol +++ b/test/core/TestBase.sol @@ -32,6 +32,9 @@ import {UnwrapModule} from "../../src/modules/helper/UnwrapModule.sol"; import {WrapModule} from "../../src/modules/helper/WrapModule.sol"; import {AaveV3PreliquidationFallbackHandler} from "../../src/modules/fallback/AaveV3PreliquidationFallbackHandler.sol"; import {IERC20Metadata} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import {HyperliquidStakeModule} from "../../src/modules/stake/hyperliquid/HyperliquidStakeModule.sol"; +import {KinetiqStakeModule} from "../../src/modules/stake/hyperliquid/KinetiqStakeModule.sol"; +import {HyperbeatStakingModule} from "../../src/modules/stake/hyperliquid/HyperbeatStakingModule.sol"; abstract contract TestBase is TestEnv { // address public constant AAVE_V3_POOL_ADDRESSES_PROVIDER = @@ -102,10 +105,14 @@ abstract contract TestBase is TestEnv { IPoolDataProvider public poolDataProvider; IPool public pool; + HyperliquidStakeModule public hyperliquidStakeModule; + KinetiqStakeModule public kinetiqStakeModule; + HyperbeatStakingModule public hyperbeatStakingModule; + function setUp() public virtual override { super.setUp(); - uint256 envIndex = 0; // TODO: move this to config + uint256 envIndex = 3; // TODO: move this to config environment = testEnvironments[envIndex]; vm.createSelectFork(environment.chainName); @@ -191,6 +198,21 @@ abstract contract TestBase is TestEnv { vm.label(address(withdrawManagerCallbackHandler), "withdrawManagerCallbackHandler"); } + function _deployHyperliquidStakeModule() internal { + if (environment.chainId != 999) return; + + wrapModule = new WrapModule(environment.vaultAsset); + moduleRegistry.setModule("WrapModule", address(wrapModule)); + unwrapModule = new UnwrapModule(environment.vaultAsset); + moduleRegistry.setModule("UnwrapModule", address(unwrapModule)); + hyperliquidStakeModule = new HyperliquidStakeModule(overseer_hyperevm); + moduleRegistry.setModule("HyperliquidStakeModule", address(hyperliquidStakeModule)); + kinetiqStakeModule = new KinetiqStakeModule(stakingManager_hyperevm); + moduleRegistry.setModule("KinetiqStakeModule", address(kinetiqStakeModule)); + hyperbeatStakingModule = new HyperbeatStakingModule(stakingCore_hyperevm); + moduleRegistry.setModule("HyperbeatStakingModule", address(hyperbeatStakingModule)); + } + function _deployAccountant(address vault, address[] memory lendAssets, address[] memory borrowAssets) internal returns (UniversalAccountant) diff --git a/test/core/TestEnv.sol b/test/core/TestEnv.sol index 07f383d..adcf058 100644 --- a/test/core/TestEnv.sol +++ b/test/core/TestEnv.sol @@ -41,6 +41,17 @@ abstract contract TestEnv is Test { address public constant USDC_ETH_Whale = 0x98C23E9d8f34FEFb1B7BD6a91B7FF122F4e16F5c; address public constant USDT_ETH = 0x2C03058C8AFC06713be23e58D2febC8337dbfE6A; + // hyperevm + address public constant WHYPE = 0x5555555555555555555555555555555555555555; + address public constant WHYPE_WHALE = 0x008ae222661B6A42e3A097bd7AAC15412829106b; + address public constant ST_HYPE = 0xfFaa4a3D97fE9107Cef8a3F48c069F577Ff76cC1; + address public constant WST_HYPE = 0x94e8396e0869c9F2200760aF0621aFd240E1CF38; + address public constant K_HYPE = 0xfD739d4e423301CE9385c1fb8850539D657C296D; + address public constant BE_HYPE = 0xd8FC8F0b03eBA61F64D08B0bef69d80916E5DdA9; + address public stakingManager_hyperevm = 0x393D0B87Ed38fc779FD9611144aE649BA6082109; + address public stakingCore_hyperevm = 0xCeaD893b162D38e714D82d06a7fe0b0dc3c38E0b; + address public overseer_hyperevm = 0xB96f07367e69e86d6e9C3F29215885104813eeAE; + // address public constant uint256 public constant PERFORMANCE_FEE = 2000; // 20% TestEnvironment[] internal testEnvironments; @@ -111,6 +122,28 @@ abstract contract TestEnv is Test { stablecoinWhale: USDC_ETH_Whale }) ); + + // hyperevm + testEnvironments.push( + TestEnvironment({ + chainId: 999, + chainName: "hyperevm", + vaultAsset: WHYPE, + vaultAssetDecimals: 18, + lendAssets: _singleAddressArray(ST_HYPE), + borrowAssets: _singleAddressArray(WHYPE), + poolAddressesProvider: 0x2f39d218133AFaB8F2B819B1066c7E434Ad94E9e, // dummy values as they are not used yet + poolDataProvider: 0x0a16f2FCC0D44FaE41cc54e079281D84A363bECD, + priceOracle: 0x54586bE62E3c3580375aE3723C145253060Ca0C2, + pool: 0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2, + vaultAssetWhale: WHYPE_WHALE, + poolConfigurator: 0x64b761D848206f447Fe2dd461b0c635Ec39EbB27, + poolAdmin: 0x72B8fD3eb0c08275b8B60F96aAb0C8a50Cb80EcA, + router: address(0), + stablecoin: USDC_ETH, + stablecoinWhale: USDC_ETH_Whale + }) + ); } function _singleAddressArray(address a) internal pure returns (address[] memory) { diff --git a/test/modules/stake/hyperliquid/HyperbeatStakingModule.t.sol b/test/modules/stake/hyperliquid/HyperbeatStakingModule.t.sol index 3478ef8..c860d1e 100644 --- a/test/modules/stake/hyperliquid/HyperbeatStakingModule.t.sol +++ b/test/modules/stake/hyperliquid/HyperbeatStakingModule.t.sol @@ -1,94 +1,102 @@ -// // SPDX-License-Identifier: MIT - -// pragma solidity ^0.8.13; - -// import {TestBase} from "./TestBase.sol"; -// import {DataTypes} from "../../../../src/common/DataTypes.sol"; -// import {Superloop} from "../../../../src/core/Superloop/Superloop.sol"; -// import {TransparentUpgradeableProxy} from -// "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -// import {IPoolConfigurator} from "aave-v3-core/contracts/interfaces/IPoolConfigurator.sol"; -// import {console} from "forge-std/console.sol"; - -// contract HyperbeatStakingModuleTest is TestBase { -// Superloop public superloopImplementation; -// address public user; - -// function setUp() public override { -// super.setUp(); - -// vm.startPrank(admin); -// _deployModules(); - -// address[] memory modules = new address[](5); -// modules[0] = address(hyperliquidStakeModule); -// modules[1] = address(kinetiqStakeModule); -// modules[2] = address(hyperbeatStakingModule); -// modules[3] = address(wrapModule); -// modules[4] = address(unwrapModule); - -// DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ -// asset: WHYPE, -// name: "WHYPE Vault", -// symbol: "WHYPEV", -// supplyCap: 100000 * WHYPE_SCALE, -// minimumDepositAmount: 100, -// instantWithdrawFee: 0, -// superloopModuleRegistry: address(moduleRegistry), -// modules: modules, -// accountant: mockModule, -// withdrawManager: mockModule, -// depositManager: mockModule, -// cashReserve: 1000, -// vaultAdmin: admin, -// treasury: treasury, -// vaultOperator: admin -// }); -// superloopImplementation = new Superloop(); - -// TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( -// address(superloopImplementation), -// address(this), -// abi.encodeWithSelector(Superloop.initialize.selector, initData) -// ); -// superloop = Superloop(payable(address(proxy))); -// vm.stopPrank(); - -// user = makeAddr("user"); -// vm.label(user, "user"); -// vm.label(address(superloop), "superloop"); -// } - -// function test_HyperbeatStake() public { -// // transfer 100 wHYPE to the vault -// deal(WHYPE, address(superloop), 100 * WHYPE_SCALE); - -// // call unwrap module & kinetiq stake module -// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](2); -// moduleExecutionData[0] = DataTypes.ModuleExecutionData({ -// executionType: DataTypes.CallType.DELEGATECALL, -// module: address(unwrapModule), -// data: abi.encodeWithSelector( -// unwrapModule.execute.selector, DataTypes.AaveV3ActionParams({asset: WHYPE, amount: 100 * WHYPE_SCALE}) -// ) -// }); -// moduleExecutionData[1] = DataTypes.ModuleExecutionData({ -// executionType: DataTypes.CallType.DELEGATECALL, -// module: address(hyperbeatStakingModule), -// data: abi.encodeWithSelector( -// hyperbeatStakingModule.execute.selector, -// DataTypes.StakeParams({assets: 100 * WHYPE_SCALE, data: abi.encode(string(""))}) -// ) -// }); - -// uint256 currentBalanceKHype = IERC20(BE_HYPE).balanceOf(address(superloop)); - -// vm.prank(admin); -// superloop.operate(moduleExecutionData); - -// uint256 finalBalanceKHype = IERC20(BE_HYPE).balanceOf(address(superloop)); - -// assertApproxEqAbs(finalBalanceKHype, currentBalanceKHype + 100 * WHYPE_SCALE, 5 * WHYPE_SCALE); -// } -// } +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.13; + +import {TestBase} from "../../../core/TestBase.sol"; +import {DataTypes} from "../../../../src/common/DataTypes.sol"; +import {Superloop} from "../../../../src/core/Superloop/Superloop.sol"; +import {TransparentUpgradeableProxy} from + "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +import {IPoolConfigurator} from "aave-v3-core/contracts/interfaces/IPoolConfigurator.sol"; +import {console} from "forge-std/console.sol"; + +contract HyperbeatStakingModuleTest is TestBase { + Superloop public superloopImplementation; + address public user; + + uint256 public constant WHYPE_SCALE = 10 ** 18; + + function setUp() public override { + super.setUp(); + + vm.startPrank(admin); + _deployHyperliquidStakeModule(); + + address[] memory modules = new address[](5); + modules[0] = address(hyperliquidStakeModule); + modules[1] = address(kinetiqStakeModule); + modules[2] = address(hyperbeatStakingModule); + modules[3] = address(wrapModule); + modules[4] = address(unwrapModule); + + DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ + asset: environment.vaultAsset, + name: "Vault", + symbol: "VLT", + supplyCap: 100000 * 10 ** environment.vaultAssetDecimals, + minimumDepositAmount: 100, + instantWithdrawFee: 0, + superloopModuleRegistry: address(moduleRegistry), + modules: modules, + accountant: mockModule, + withdrawManager: mockModule, + depositManager: mockModule, + cashReserve: 1000, + vaultAdmin: admin, + treasury: treasury, + vaultOperator: admin + }); + superloopImplementation = new Superloop(); + + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(superloopImplementation), + address(this), + abi.encodeWithSelector(Superloop.initialize.selector, initData) + ); + superloop = Superloop(payable(address(proxy))); + vm.stopPrank(); + + user = makeAddr("user"); + vm.label(user, "user"); + vm.label(address(superloop), "superloop"); + } + + function test_HyperbeatStake() public { + if (environment.chainId != 999) return; + + // transfer 100 wHYPE to the vault + deal(environment.vaultAsset, address(superloop), 100 * 10 ** environment.vaultAssetDecimals); + + // call unwrap module & kinetiq stake module + DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](2); + moduleExecutionData[0] = DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.DELEGATECALL, + module: address(unwrapModule), + data: abi.encodeWithSelector( + unwrapModule.execute.selector, + DataTypes.AaveV3ActionParams({ + asset: environment.vaultAsset, + amount: 100 * 10 ** environment.vaultAssetDecimals + }) + ) + }); + moduleExecutionData[1] = DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.DELEGATECALL, + module: address(hyperbeatStakingModule), + data: abi.encodeWithSelector( + hyperbeatStakingModule.execute.selector, + DataTypes.StakeParams({assets: 100 * 10 ** environment.vaultAssetDecimals, data: abi.encode(string(""))}) + ) + }); + + uint256 currentBalanceKHype = IERC20(BE_HYPE).balanceOf(address(superloop)); + + vm.prank(admin); + superloop.operate(moduleExecutionData); + + uint256 finalBalanceKHype = IERC20(BE_HYPE).balanceOf(address(superloop)); + + assertApproxEqAbs(finalBalanceKHype, currentBalanceKHype + 100 * WHYPE_SCALE, 5 * WHYPE_SCALE); + } +} diff --git a/test/modules/stake/hyperliquid/HyperliquidStakingModule.t.sol b/test/modules/stake/hyperliquid/HyperliquidStakingModule.t.sol index bd78b39..85bf3b1 100644 --- a/test/modules/stake/hyperliquid/HyperliquidStakingModule.t.sol +++ b/test/modules/stake/hyperliquid/HyperliquidStakingModule.t.sol @@ -1,97 +1,99 @@ -// // SPDX-License-Identifier: MIT +// SPDX-License-Identifier: MIT -// pragma solidity ^0.8.13; +pragma solidity ^0.8.13; -// import {TestBase} from "./TestBase.sol"; -// import {DataTypes} from "../../../../src/common/DataTypes.sol"; -// import {Superloop} from "../../../../src/core/Superloop/Superloop.sol"; -// import {TransparentUpgradeableProxy} from -// "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -// import {IPoolConfigurator} from "aave-v3-core/contracts/interfaces/IPoolConfigurator.sol"; +import {TestBase} from "../../../core/TestBase.sol"; +import {DataTypes} from "../../../../src/common/DataTypes.sol"; +import {Superloop} from "../../../../src/core/Superloop/Superloop.sol"; +import {TransparentUpgradeableProxy} from + "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +import {IPoolConfigurator} from "aave-v3-core/contracts/interfaces/IPoolConfigurator.sol"; -// contract HyperliquidStakingModuleTest is TestBase { -// Superloop public superloopImplementation; -// address public user; +contract HyperliquidStakingModuleTest is TestBase { + Superloop public superloopImplementation; + address public user; + uint256 public constant WHYPE_SCALE = 10 ** 18; -// function setUp() public override { -// super.setUp(); + function setUp() public override { + super.setUp(); -// vm.startPrank(admin); -// _deployModules(); + vm.startPrank(admin); + _deployHyperliquidStakeModule(); -// address[] memory modules = new address[](5); -// modules[0] = address(hyperliquidStakeModule); -// modules[1] = address(kinetiqStakeModule); -// modules[2] = address(hyperbeatStakingModule); -// modules[3] = address(wrapModule); -// modules[4] = address(unwrapModule); + address[] memory modules = new address[](5); + modules[0] = address(hyperliquidStakeModule); + modules[1] = address(kinetiqStakeModule); + modules[2] = address(hyperbeatStakingModule); + modules[3] = address(wrapModule); + modules[4] = address(unwrapModule); -// DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ -// asset: WHYPE, -// name: "WHYPE Vault", -// symbol: "WHYPEV", -// supplyCap: 100000 * WHYPE_SCALE, -// minimumDepositAmount: 100, -// instantWithdrawFee: 0, -// superloopModuleRegistry: address(moduleRegistry), -// modules: modules, -// accountant: mockModule, -// withdrawManager: mockModule, -// depositManager: mockModule, -// cashReserve: 1000, -// vaultAdmin: admin, -// treasury: treasury, -// vaultOperator: admin -// }); -// superloopImplementation = new Superloop(); + DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ + asset: environment.vaultAsset, + name: "WHYPE Vault", + symbol: "WHYPEV", + supplyCap: 100000 * WHYPE_SCALE, + minimumDepositAmount: 100, + instantWithdrawFee: 0, + superloopModuleRegistry: address(moduleRegistry), + modules: modules, + accountant: mockModule, + withdrawManager: mockModule, + depositManager: mockModule, + cashReserve: 1000, + vaultAdmin: admin, + treasury: treasury, + vaultOperator: admin + }); + superloopImplementation = new Superloop(); -// TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( -// address(superloopImplementation), -// address(this), -// abi.encodeWithSelector(Superloop.initialize.selector, initData) -// ); -// superloop = Superloop(payable(address(proxy))); -// vm.stopPrank(); + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(superloopImplementation), + address(this), + abi.encodeWithSelector(Superloop.initialize.selector, initData) + ); + superloop = Superloop(payable(address(proxy))); + vm.stopPrank(); -// user = makeAddr("user"); -// vm.label(user, "user"); -// vm.label(address(superloop), "superloop"); -// } + user = makeAddr("user"); + vm.label(user, "user"); + vm.label(address(superloop), "superloop"); + } -// function test_HyperliquidStake() public { -// // transfer 100 wHYPE to the vault -// deal(WHYPE, address(superloop), 100 * WHYPE_SCALE); + function test_HyperliquidStake() public { + // transfer 100 wHYPE to the vault + deal(WHYPE, address(superloop), 100 * WHYPE_SCALE); -// // call unwrap module & hyperliquid stake module -// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](2); -// moduleExecutionData[0] = DataTypes.ModuleExecutionData({ -// executionType: DataTypes.CallType.DELEGATECALL, -// module: address(unwrapModule), -// data: abi.encodeWithSelector( -// unwrapModule.execute.selector, DataTypes.AaveV3ActionParams({asset: WHYPE, amount: 100 * WHYPE_SCALE}) -// ) -// }); -// moduleExecutionData[1] = DataTypes.ModuleExecutionData({ -// executionType: DataTypes.CallType.DELEGATECALL, -// module: address(hyperliquidStakeModule), -// data: abi.encodeWithSelector( -// hyperliquidStakeModule.execute.selector, -// DataTypes.StakeParams({assets: 100 * WHYPE_SCALE, data: abi.encode(string(""))}) -// ) -// }); + // call unwrap module & hyperliquid stake module + DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](2); + moduleExecutionData[0] = DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.DELEGATECALL, + module: address(unwrapModule), + data: abi.encodeWithSelector( + unwrapModule.execute.selector, + DataTypes.AaveV3ActionParams({asset: environment.vaultAsset, amount: 100 * WHYPE_SCALE}) + ) + }); + moduleExecutionData[1] = DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.DELEGATECALL, + module: address(hyperliquidStakeModule), + data: abi.encodeWithSelector( + hyperliquidStakeModule.execute.selector, + DataTypes.StakeParams({assets: 100 * WHYPE_SCALE, data: abi.encode(string(""))}) + ) + }); -// uint256 currentBalanceStHype = IERC20(ST_HYPE).balanceOf(address(superloop)); -// uint256 currentBalanceWstHype = IERC20(WST_HYPE).balanceOf(address(superloop)); + uint256 currentBalanceStHype = IERC20(ST_HYPE).balanceOf(address(superloop)); + uint256 currentBalanceWstHype = IERC20(WST_HYPE).balanceOf(address(superloop)); -// vm.prank(admin); -// superloop.operate(moduleExecutionData); + vm.prank(admin); + superloop.operate(moduleExecutionData); -// uint256 finalBalanceStHype = IERC20(ST_HYPE).balanceOf(address(superloop)); -// uint256 finalBalanceWstHype = IERC20(WST_HYPE).balanceOf(address(superloop)); + uint256 finalBalanceStHype = IERC20(ST_HYPE).balanceOf(address(superloop)); + uint256 finalBalanceWstHype = IERC20(WST_HYPE).balanceOf(address(superloop)); -// // it should have minted 100stHype for the vault and ~98 wstHype for the vault -// assertApproxEqAbs(finalBalanceStHype, currentBalanceStHype + 100 * WHYPE_SCALE, 100); -// assertApproxEqAbs(finalBalanceWstHype, currentBalanceWstHype + 100 * WHYPE_SCALE, 5 * WHYPE_SCALE); -// } -// } + // it should have minted 100stHype for the vault and ~98 wstHype for the vault + assertApproxEqAbs(finalBalanceStHype, currentBalanceStHype + 100 * WHYPE_SCALE, 100); + assertApproxEqAbs(finalBalanceWstHype, currentBalanceWstHype + 100 * WHYPE_SCALE, 5 * WHYPE_SCALE); + } +} diff --git a/test/modules/stake/hyperliquid/KinetiqStakingModule.t.sol b/test/modules/stake/hyperliquid/KinetiqStakingModule.t.sol index a560218..0563fd0 100644 --- a/test/modules/stake/hyperliquid/KinetiqStakingModule.t.sol +++ b/test/modules/stake/hyperliquid/KinetiqStakingModule.t.sol @@ -1,94 +1,96 @@ -// // SPDX-License-Identifier: MIT +// SPDX-License-Identifier: MIT -// pragma solidity ^0.8.13; +pragma solidity ^0.8.13; -// import {TestBase} from "./TestBase.sol"; -// import {DataTypes} from "../../../../src/common/DataTypes.sol"; -// import {Superloop} from "../../../../src/core/Superloop/Superloop.sol"; -// import {TransparentUpgradeableProxy} from -// "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -// import {IPoolConfigurator} from "aave-v3-core/contracts/interfaces/IPoolConfigurator.sol"; -// import {console} from "forge-std/console.sol"; +import {TestBase} from "../../../core/TestBase.sol"; +import {DataTypes} from "../../../../src/common/DataTypes.sol"; +import {Superloop} from "../../../../src/core/Superloop/Superloop.sol"; +import {TransparentUpgradeableProxy} from + "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +import {IPoolConfigurator} from "aave-v3-core/contracts/interfaces/IPoolConfigurator.sol"; +import {console} from "forge-std/console.sol"; -// contract KinetiqStakingModuleTest is TestBase { -// Superloop public superloopImplementation; -// address public user; +contract KinetiqStakingModuleTest is TestBase { + Superloop public superloopImplementation; + address public user; + uint256 public constant WHYPE_SCALE = 10 ** 18; -// function setUp() public override { -// super.setUp(); + function setUp() public override { + super.setUp(); -// vm.startPrank(admin); -// _deployModules(); + vm.startPrank(admin); + _deployHyperliquidStakeModule(); -// address[] memory modules = new address[](5); -// modules[0] = address(hyperliquidStakeModule); -// modules[1] = address(kinetiqStakeModule); -// modules[2] = address(hyperbeatStakingModule); -// modules[3] = address(wrapModule); -// modules[4] = address(unwrapModule); + address[] memory modules = new address[](5); + modules[0] = address(hyperliquidStakeModule); + modules[1] = address(kinetiqStakeModule); + modules[2] = address(hyperbeatStakingModule); + modules[3] = address(wrapModule); + modules[4] = address(unwrapModule); -// DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ -// asset: WHYPE, -// name: "WHYPE Vault", -// symbol: "WHYPEV", -// supplyCap: 100000 * WHYPE_SCALE, -// minimumDepositAmount: 100, -// instantWithdrawFee: 0, -// superloopModuleRegistry: address(moduleRegistry), -// modules: modules, -// accountant: mockModule, -// withdrawManager: mockModule, -// depositManager: mockModule, -// cashReserve: 1000, -// vaultAdmin: admin, -// treasury: treasury, -// vaultOperator: admin -// }); -// superloopImplementation = new Superloop(); + DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ + asset: environment.vaultAsset, + name: "Vault", + symbol: "VLT", + supplyCap: 100000 * 10 ** environment.vaultAssetDecimals, + minimumDepositAmount: 100, + instantWithdrawFee: 0, + superloopModuleRegistry: address(moduleRegistry), + modules: modules, + accountant: mockModule, + withdrawManager: mockModule, + depositManager: mockModule, + cashReserve: 1000, + vaultAdmin: admin, + treasury: treasury, + vaultOperator: admin + }); + superloopImplementation = new Superloop(); -// TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( -// address(superloopImplementation), -// address(this), -// abi.encodeWithSelector(Superloop.initialize.selector, initData) -// ); -// superloop = Superloop(payable(address(proxy))); -// vm.stopPrank(); + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(superloopImplementation), + address(this), + abi.encodeWithSelector(Superloop.initialize.selector, initData) + ); + superloop = Superloop(payable(address(proxy))); + vm.stopPrank(); -// user = makeAddr("user"); -// vm.label(user, "user"); -// vm.label(address(superloop), "superloop"); -// } + user = makeAddr("user"); + vm.label(user, "user"); + vm.label(address(superloop), "superloop"); + } -// function test_KinetiqStake() public { -// // transfer 100 wHYPE to the vault -// deal(WHYPE, address(superloop), 100 * WHYPE_SCALE); + function test_KinetiqStake() public { + // transfer 100 wHYPE to the vault + deal(environment.vaultAsset, address(superloop), 100 * 10 ** environment.vaultAssetDecimals); -// // call unwrap module & kinetiq stake module -// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](2); -// moduleExecutionData[0] = DataTypes.ModuleExecutionData({ -// executionType: DataTypes.CallType.DELEGATECALL, -// module: address(unwrapModule), -// data: abi.encodeWithSelector( -// unwrapModule.execute.selector, DataTypes.AaveV3ActionParams({asset: WHYPE, amount: 100 * WHYPE_SCALE}) -// ) -// }); -// moduleExecutionData[1] = DataTypes.ModuleExecutionData({ -// executionType: DataTypes.CallType.DELEGATECALL, -// module: address(kinetiqStakeModule), -// data: abi.encodeWithSelector( -// kinetiqStakeModule.execute.selector, -// DataTypes.StakeParams({assets: 100 * WHYPE_SCALE, data: abi.encode(string(""))}) -// ) -// }); + // call unwrap module & kinetiq stake module + DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](2); + moduleExecutionData[0] = DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.DELEGATECALL, + module: address(unwrapModule), + data: abi.encodeWithSelector( + unwrapModule.execute.selector, + DataTypes.AaveV3ActionParams({asset: environment.vaultAsset, amount: 100 * WHYPE_SCALE}) + ) + }); + moduleExecutionData[1] = DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.DELEGATECALL, + module: address(kinetiqStakeModule), + data: abi.encodeWithSelector( + kinetiqStakeModule.execute.selector, + DataTypes.StakeParams({assets: 100 * 10 ** environment.vaultAssetDecimals, data: abi.encode(string(""))}) + ) + }); -// uint256 currentBalanceKHype = IERC20(K_HYPE).balanceOf(address(superloop)); + uint256 currentBalanceKHype = IERC20(K_HYPE).balanceOf(address(superloop)); -// vm.prank(admin); -// superloop.operate(moduleExecutionData); + vm.prank(admin); + superloop.operate(moduleExecutionData); -// uint256 finalBalanceKHype = IERC20(K_HYPE).balanceOf(address(superloop)); + uint256 finalBalanceKHype = IERC20(K_HYPE).balanceOf(address(superloop)); -// assertApproxEqAbs(finalBalanceKHype, currentBalanceKHype + 100 * WHYPE_SCALE, 5 * WHYPE_SCALE); -// } -// } + assertApproxEqAbs(finalBalanceKHype, currentBalanceKHype + 100 * WHYPE_SCALE, 5 * WHYPE_SCALE); + } +} diff --git a/test/modules/stake/hyperliquid/TestBase.sol b/test/modules/stake/hyperliquid/TestBase.sol deleted file mode 100644 index 5e84907..0000000 --- a/test/modules/stake/hyperliquid/TestBase.sol +++ /dev/null @@ -1,124 +0,0 @@ -// // SPDX-License-Identifier: MIT - -// pragma solidity ^0.8.13; - -// import {Test, console} from "forge-std/Test.sol"; -// import {Vm} from "forge-std/Vm.sol"; -// import {Superloop} from "../../../../src/core/Superloop/Superloop.sol"; -// import {DataTypes} from "../../../../src/common/DataTypes.sol"; -// import {SuperloopModuleRegistry} from "../../../../src/core/ModuleRegistry/ModuleRegistry.sol"; -// import {AaveV3FlashloanModule} from "../../../../src/modules/aave/AaveV3FlashloanModule.sol"; -// import {AaveV3CallbackHandler} from "../../../../src/modules/callback/AaveV3CallbackHandler.sol"; -// import {AaveV3EmodeModule} from "../../../../src/modules/aave/AaveV3EmodeModule.sol"; -// import {AaveV3SupplyModule} from "../../../../src/modules/aave/AaveV3SupplyModule.sol"; -// import {AaveV3WithdrawModule} from "../../../../src/modules/aave/AaveV3WithdrawModule.sol"; -// import {AaveV3BorrowModule} from "../../../../src/modules/aave/AaveV3BorrowModule.sol"; -// import {AaveV3RepayModule} from "../../../../src/modules/aave/AaveV3RepayModule.sol"; -// import {IPoolDataProvider} from "aave-v3-core/contracts/interfaces/IPoolDataProvider.sol"; -// import {IPool} from "aave-v3-core/contracts/interfaces/IPool.sol"; -// import {UniversalDexModule} from "../../../../src/modules/dex/UniversalDexModule.sol"; -// import {AccountantAaveV3} from "../../../../src/core/Accountant/aaveV3Accountant/AccountantAaveV3.sol"; -// import {WithdrawManager} from "../../../../src/core/WithdrawManager/WithdrawManager.sol"; -// import {DepositManager} from "../../../../src/core/DepositManager/DepositManager.sol"; -// import {DepositManagerCallbackHandler} from "../../../../src/modules/callback/DepositManagerCallbackHandler.sol"; -// import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; -// import {TransparentUpgradeableProxy} from -// "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -// import {UniversalAccountant} from "../../../../src/core/Accountant/universalAccountant/UniversalAccountant.sol"; -// import {AaveV3AccountantPlugin} from "../../../../src/plugins/Accountant/AaveV3AccountantPlugin.sol"; -// import {WithdrawManagerCallbackHandler} from "../../../../src/modules/callback/WithdrawManagerCallbackHandler.sol"; -// import {UnwrapModule} from "../../../../src/modules/helper/UnwrapModule.sol"; -// import {WrapModule} from "../../../../src/modules/helper/WrapModule.sol"; -// import {HyperliquidStakeModule} from "../../../../src/modules/stake/hyperliquid/HyperliquidStakeModule.sol"; -// import {KinetiqStakeModule} from "../../../../src/modules/stake/hyperliquid/KinetiqStakeModule.sol"; -// import {HyperbeatStakingModule} from "../../../../src/modules/stake/hyperliquid/HyperbeatStakingModule.sol"; - -// contract TestBase is Test { -// address public AAVE_V3_POOL_ADDRESSES_PROVIDER; -// address public AAVE_V3_POOL_DATA_PROVIDER; -// address public AAVE_V3_PRICE_ORACLE; -// address public POOL; -// uint256 public PERFORMANCE_FEE; // 20% -// address public POOL_CONFIGURATOR; - -// address public stakingManager = 0x393D0B87Ed38fc779FD9611144aE649BA6082109; -// address public stakingCore = 0xCeaD893b162D38e714D82d06a7fe0b0dc3c38E0b; -// address public overseer = 0xB96f07367e69e86d6e9C3F29215885104813eeAE; -// address public constant WHYPE = 0x5555555555555555555555555555555555555555; -// uint256 public constant WHYPE_SCALE = 10 ** 18; - -// address public constant ST_HYPE = 0xfFaa4a3D97fE9107Cef8a3F48c069F577Ff76cC1; -// address public constant WST_HYPE = 0x94e8396e0869c9F2200760aF0621aFd240E1CF38; -// address public constant K_HYPE = 0xfD739d4e423301CE9385c1fb8850539D657C296D; -// address public constant BE_HYPE = 0xd8FC8F0b03eBA61F64D08B0bef69d80916E5DdA9; - -// address public admin; -// address public treasury; - -// SuperloopModuleRegistry public moduleRegistry; -// Superloop public superloop; -// AaveV3FlashloanModule public flashloanModule; -// DepositManagerCallbackHandler public depositManagerCallbackHandler; -// WithdrawManagerCallbackHandler public withdrawManagerCallbackHandler; -// AaveV3CallbackHandler public callbackHandler; -// AaveV3SupplyModule public supplyModule; -// AaveV3WithdrawModule public withdrawModule; -// AaveV3BorrowModule public borrowModule; -// AaveV3RepayModule public repayModule; -// UniversalDexModule public dexModule; -// AccountantAaveV3 public accountantAaveV3; -// UniversalAccountant public accountant; -// WithdrawManager public withdrawManager; -// UnwrapModule public unwrapModule; -// WrapModule public wrapModule; - -// HyperliquidStakeModule public hyperliquidStakeModule; -// KinetiqStakeModule public kinetiqStakeModule; -// HyperbeatStakingModule public hyperbeatStakingModule; - -// DepositManager public depositManager; - -// address public mockModule; -// AaveV3EmodeModule public emodeModule; -// IPoolDataProvider public poolDataProvider; -// IPool public pool; - -// function setUp() public virtual { -// vm.createSelectFork("hyperevm"); -// admin = makeAddr("admin"); -// treasury = makeAddr("treasury"); - -// vm.startPrank(admin); -// moduleRegistry = new SuperloopModuleRegistry(); -// mockModule = makeAddr("mockModule"); -// moduleRegistry.setModule("MockModule", mockModule); -// poolDataProvider = IPoolDataProvider(AAVE_V3_POOL_DATA_PROVIDER); -// pool = IPool(POOL); -// vm.stopPrank(); - -// vm.label(admin, "admin"); -// vm.label(treasury, "treasury"); -// vm.label(mockModule, "mockModule"); -// vm.label(address(moduleRegistry), "moduleRegistry"); -// } - -// function _deployModules() internal { -// unwrapModule = new UnwrapModule(WHYPE); -// moduleRegistry.setModule("UnwrapModule", address(unwrapModule)); -// wrapModule = new WrapModule(WHYPE); -// moduleRegistry.setModule("WrapModule", address(wrapModule)); - -// hyperliquidStakeModule = new HyperliquidStakeModule(overseer); -// moduleRegistry.setModule("HyperliquidStakeModule", address(hyperliquidStakeModule)); -// kinetiqStakeModule = new KinetiqStakeModule(stakingManager); -// moduleRegistry.setModule("KinetiqStakeModule", address(kinetiqStakeModule)); -// hyperbeatStakingModule = new HyperbeatStakingModule(stakingCore); -// moduleRegistry.setModule("HyperbeatStakingModule", address(hyperbeatStakingModule)); - -// vm.label(address(unwrapModule), "unwrapModule"); -// vm.label(address(wrapModule), "wrapModule"); -// vm.label(address(hyperliquidStakeModule), "hyperliquidStakeModule"); -// vm.label(address(kinetiqStakeModule), "kinetiqStakeModule"); -// vm.label(address(hyperbeatStakingModule), "hyperbeatStakingModule"); -// } -// } From f822cda61bf683c56752b855838455ed446ca525 Mon Sep 17 00:00:00 2001 From: Priyam Anand Date: Fri, 23 Jan 2026 15:50:44 +0530 Subject: [PATCH 07/21] test: core unit tests restructured --- test/core/TestBase.sol | 11 +- test/core/TestEnv.sol | 1 + test/core/unit/AccountantAaveV3.t.sol | 663 ++++++++-------- test/core/unit/DepositManager.t.sol | 503 ++++++------ test/core/unit/ModuleRegistry.t.sol | 513 ++++++------- test/core/unit/Superloop.t.sol | 840 ++++++++++----------- test/core/unit/UniversalAccountant.t.sol | 529 +++++++------ test/core/unit/WithdrawManager.t.sol | 720 +++++++++--------- test/helpers/VaultRouter.t.sol | 3 +- test/helpers/integration/VaultRouter.t.sol | 4 +- 10 files changed, 1888 insertions(+), 1899 deletions(-) diff --git a/test/core/TestBase.sol b/test/core/TestBase.sol index f4a9f61..0fee7d7 100644 --- a/test/core/TestBase.sol +++ b/test/core/TestBase.sol @@ -112,7 +112,7 @@ abstract contract TestBase is TestEnv { function setUp() public virtual override { super.setUp(); - uint256 envIndex = 3; // TODO: move this to config + uint256 envIndex = 0; // TODO: move this to config environment = testEnvironments[envIndex]; vm.createSelectFork(environment.chainName); @@ -242,7 +242,8 @@ abstract contract TestBase is TestEnv { abi.encodeWithSelector(UniversalAccountant.initialize.selector, initData) ); - return UniversalAccountant(address(proxy)); + accountant = UniversalAccountant(address(proxy)); + return accountant; } function _deployDepositManager(address vault) internal returns (DepositManager) { @@ -253,7 +254,8 @@ abstract contract TestBase is TestEnv { abi.encodeWithSelector(DepositManager.initialize.selector, vault) ); - return DepositManager(address(proxy)); + depositManager = DepositManager(address(proxy)); + return depositManager; } function _deployWithdrawManager(address vault) internal returns (WithdrawManager) { @@ -263,7 +265,8 @@ abstract contract TestBase is TestEnv { address(this), abi.encodeWithSelector(WithdrawManager.initialize.selector, vault) ); - return WithdrawManager(address(proxy)); + withdrawManager = WithdrawManager(address(proxy)); + return withdrawManager; } function _deployPreliquidationFallbackHandler(address vault) internal { diff --git a/test/core/TestEnv.sol b/test/core/TestEnv.sol index adcf058..fde47c1 100644 --- a/test/core/TestEnv.sol +++ b/test/core/TestEnv.sol @@ -26,6 +26,7 @@ abstract contract TestEnv is Test { // etlk chain address public constant ST_XTZ = 0x01F07f4d78d47A64F4C3B2b65f513f15Be6E1854; address public constant WXTZ = 0xc9B53AB2679f573e480d01e0f49e2B5CFB7a3EAb; + address public constant XTZ = 0xc9B53AB2679f573e480d01e0f49e2B5CFB7a3EAb; address public constant LBTC = 0xecAc9C5F704e954931349Da37F60E39f515c11c1; address public constant WBTC = 0xbFc94CD2B1E55999Cfc7347a9313e88702B83d0F; address public constant USDC_ETLK = 0x796Ea11Fa2dD751eD01b53C372fFDB4AAa8f00F9; diff --git a/test/core/unit/AccountantAaveV3.t.sol b/test/core/unit/AccountantAaveV3.t.sol index 0033146..fcbd75b 100644 --- a/test/core/unit/AccountantAaveV3.t.sol +++ b/test/core/unit/AccountantAaveV3.t.sol @@ -1,339 +1,324 @@ -// // SPDX-License-Identifier: MIT - -// pragma solidity ^0.8.13; - -// import {TestBase} from "../TestBase.sol"; -// import {console} from "forge-std/console.sol"; -// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -// import {MockVault} from "../../../src/mock/MockVault.sol"; -// import {AccountantAaveV3} from "../../../src/core/Accountant/aaveV3Accountant/AccountantAaveV3.sol"; -// import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -// import {DataTypes} from "../../../src/common/DataTypes.sol"; -// import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; -// import {IPoolAddressesProvider} from "aave-v3-core/contracts/interfaces/IPoolAddressesProvider.sol"; -// import {IPoolDataProvider} from "aave-v3-core/contracts/interfaces/IPoolDataProvider.sol"; -// import {IAaveOracle} from "aave-v3-core/contracts/interfaces/IAaveOracle.sol"; -// import {IPriceOracleGetter} from "aave-v3-core/contracts/interfaces/IPriceOracleGetter.sol"; - -// contract AccountantAaveV3Test is TestBase { -// AccountantAaveV3 public accountantAaveV3Implementation; -// ProxyAdmin public proxyAdmin; - -// IERC20 public asset; -// MockVault public vault; - -// // Test data -// uint256 public constant INITIAL_WHALE_BALANCE = 1000 ether; - -// function setUp() public override { -// super.setUp(); - -// asset = IERC20(XTZ); -// vault = new MockVault(asset, "Mock Vault", "mVLT"); - -// address[] memory lendAssets = new address[](1); -// lendAssets[0] = ST_XTZ; -// address[] memory borrowAssets = new address[](1); -// borrowAssets[0] = XTZ; - -// DataTypes.AaveV3AccountantModuleInitData memory initData = DataTypes.AaveV3AccountantModuleInitData({ -// poolAddressesProvider: AAVE_V3_POOL_ADDRESSES_PROVIDER, -// lendAssets: lendAssets, -// borrowAssets: borrowAssets, -// performanceFee: uint16(PERFORMANCE_FEE), -// vault: address(vault) -// }); - -// accountantAaveV3Implementation = new AccountantAaveV3(); -// proxyAdmin = new ProxyAdmin(address(this)); - -// TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( -// address(accountantAaveV3Implementation), -// address(proxyAdmin), -// abi.encodeWithSelector(AccountantAaveV3.initialize.selector, initData) -// ); - -// accountantAaveV3 = AccountantAaveV3(address(proxy)); - -// // Fund the accountant with XTZ from whale -// vm.startPrank(XTZ_WHALE); -// asset.transfer(address(vault), INITIAL_WHALE_BALANCE); -// vm.stopPrank(); -// } - -// function test_Initialize() public { -// // Test that initialization works correctly -// address[] memory lendAssets = new address[](1); -// lendAssets[0] = ST_XTZ; -// address[] memory borrowAssets = new address[](1); -// borrowAssets[0] = XTZ; - -// DataTypes.AaveV3AccountantModuleInitData memory initData = DataTypes.AaveV3AccountantModuleInitData({ -// poolAddressesProvider: AAVE_V3_POOL_ADDRESSES_PROVIDER, -// lendAssets: lendAssets, -// borrowAssets: borrowAssets, -// performanceFee: uint16(PERFORMANCE_FEE), -// vault: address(vault) -// }); - -// AccountantAaveV3 newImplementation = new AccountantAaveV3(); -// ProxyAdmin newProxyAdmin = new ProxyAdmin(address(this)); - -// TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( -// address(newImplementation), -// address(newProxyAdmin), -// abi.encodeWithSelector(AccountantAaveV3.initialize.selector, initData) -// ); - -// AccountantAaveV3 newAccountant = AccountantAaveV3(address(proxy)); - -// // Test that the contract is properly initialized -// assertEq(newAccountant.getTotalAssets(), INITIAL_WHALE_BALANCE); // Should work without reverting -// } - -// function test_InitializeRevertIfAlreadyInitialized() public { -// // Test that initialize reverts if called again -// address[] memory lendAssets = new address[](1); -// lendAssets[0] = ST_XTZ; -// address[] memory borrowAssets = new address[](1); -// borrowAssets[0] = XTZ; - -// DataTypes.AaveV3AccountantModuleInitData memory initData = DataTypes.AaveV3AccountantModuleInitData({ -// poolAddressesProvider: AAVE_V3_POOL_ADDRESSES_PROVIDER, -// lendAssets: lendAssets, -// borrowAssets: borrowAssets, -// performanceFee: uint16(PERFORMANCE_FEE), -// vault: address(vault) -// }); - -// vm.expectRevert(); -// accountantAaveV3.initialize(initData); -// } - -// function test_GetTotalAssets() public view { -// // Test getTotalAssets calculation using actual fork data -// uint256 totalAssets = accountantAaveV3.getTotalAssets(); - -// // Should return the base asset balance since there are no lend/borrow positions yet -// assertEq(totalAssets, INITIAL_WHALE_BALANCE); - -// console.log("Total assets:", totalAssets); -// console.log("Expected assets:", INITIAL_WHALE_BALANCE); -// } - -// function test_GetTotalAssetsWithMultipleAssets() public { -// // mock pool data provider and price oracle -// _enableMockingPoolDataProvider(); -// _enableMockingPriceOracle(1.01e8, 1e8); - -// uint256 totalAssets = accountantAaveV3.getTotalAssets(); - -// // Calculate expected total assets based on mock data: -// // Lend assets: 1000 ether * 1.01e8 (ST_XTZ price) = 1010e8 -// // Borrow assets: -800 ether * 1e8 (XTZ price) = -800e8 -// // Base asset balance: 1000 ether * 1e8 (XTZ price) = 1000e8 -// // Total in market reference currency: 1010e8 - 800e8 + 1000e8 = 1210e8 -// // Convert to base asset: 1210e18 / 1e18 = 1210 ether -// uint256 expectedTotalAssets = 1210 ether; - -// assertEq(totalAssets, expectedTotalAssets); -// console.log("Total assets with multiple assets:", totalAssets); -// console.log("Expected total assets:", expectedTotalAssets); -// } - -// function test_GetPerformanceFee() public { -// uint256 totalShares = 1000 ether; -// uint256 exchangeRate = 1.1e18; // 10% increase -// uint256 lastRealizedFeeExchangeRate = 1.0e18; -// uint256 totalSupply = 1000 ether; - -// // Set the last realized fee exchange rate -// vm.prank(address(vault)); -// accountantAaveV3.setLastRealizedFeeExchangeRate(lastRealizedFeeExchangeRate, totalSupply); - -// // Calculate expected performance fee -// uint256 latestAssetAmount = totalShares * exchangeRate; -// uint256 prevAssetAmount = totalShares * lastRealizedFeeExchangeRate; -// uint256 interestGenerated = latestAssetAmount - prevAssetAmount; -// uint256 expectedPerformanceFee = (interestGenerated * PERFORMANCE_FEE) / (10000 * 1e18); - -// vm.prank(address(vault)); -// uint256 actualPerformanceFee = accountantAaveV3.getPerformanceFee(totalShares, exchangeRate, 18); - -// console.log("Performance fee:", actualPerformanceFee); -// console.log("Expected fee:", expectedPerformanceFee); - -// assertEq(actualPerformanceFee, expectedPerformanceFee); -// } - -// function test_GetPerformanceFeeNoInterest() public { -// uint256 totalShares = 1000 ether; -// uint256 exchangeRate = 0.9e18; // 10% decrease -// uint256 lastRealizedFeeExchangeRate = 1.0e18; -// uint256 totalSupply = 1000 ether; -// // Set the last realized fee exchange rate -// vm.prank(address(vault)); -// accountantAaveV3.setLastRealizedFeeExchangeRate(lastRealizedFeeExchangeRate, totalSupply); - -// vm.prank(address(vault)); -// uint256 performanceFee = accountantAaveV3.getPerformanceFee(totalShares, exchangeRate, 18); - -// // Should return 0 when there's no interest (exchange rate decreased) -// assertEq(performanceFee, 0); -// } - -// function test_GetPerformanceFeeRevertIfNotVault() public { -// uint256 totalShares = 1000 ether; -// uint256 exchangeRate = 1.1e18; - -// vm.expectRevert(); -// accountantAaveV3.getPerformanceFee(totalShares, exchangeRate, 18); -// } - -// function test_SetLastRealizedFeeExchangeRate() public { -// uint256 newExchangeRate = 1.2e18; -// uint256 totalSupply = 1000 ether; - -// vm.prank(address(vault)); -// accountantAaveV3.setLastRealizedFeeExchangeRate(newExchangeRate, totalSupply); - -// // Test that the exchange rate was set correctly by calling getPerformanceFee -// vm.prank(address(vault)); -// uint256 performanceFee = accountantAaveV3.getPerformanceFee(1000 ether, 1.3e18, 18); - -// // Should calculate based on the new exchange rate -// uint256 expectedInterest = 1000 ether * 1.3e18 - 1000 ether * newExchangeRate; -// uint256 expectedFee = (expectedInterest * PERFORMANCE_FEE) / (10000 * 1e18); - -// assertEq(performanceFee, expectedFee); -// } - -// function test_SetLastRealizedFeeExchangeRateRevertIfNotVault() public { -// uint256 newExchangeRate = 1.2e18; -// uint256 totalSupply = 1000 ether; - -// vm.expectRevert(); -// accountantAaveV3.setLastRealizedFeeExchangeRate(newExchangeRate, totalSupply); -// } - -// function test_OnlyVaultModifier() public { -// // Test that only vault can call restricted functions -// vm.expectRevert(); -// accountantAaveV3.getPerformanceFee(1000 ether, 1.1e18, 18); - -// vm.expectRevert(); -// accountantAaveV3.setLastRealizedFeeExchangeRate(1.1e18, 1000 ether); -// } - -// function test_ConstructorDisablesInitializers() public { -// // Test that the constructor properly disables initializers -// AccountantAaveV3 newContract = new AccountantAaveV3(); - -// // Should revert if we try to initialize directly -// address[] memory lendAssets = new address[](1); -// lendAssets[0] = ST_XTZ; -// address[] memory borrowAssets = new address[](1); -// borrowAssets[0] = XTZ; - -// DataTypes.AaveV3AccountantModuleInitData memory initData = DataTypes.AaveV3AccountantModuleInitData({ -// poolAddressesProvider: AAVE_V3_POOL_ADDRESSES_PROVIDER, -// lendAssets: lendAssets, -// borrowAssets: borrowAssets, -// performanceFee: uint16(PERFORMANCE_FEE), -// vault: address(vault) -// }); - -// vm.expectRevert(); -// newContract.initialize(initData); -// } - -// function test_GetTotalAssetsWithZeroBalances() public view { -// // Test getTotalAssets when there are no lend/borrow positions -// // This should be the default state since we haven't interacted with Aave V3 -// uint256 totalAssets = accountantAaveV3.getTotalAssets(); - -// // Should only include base asset balance -// assertEq(totalAssets, INITIAL_WHALE_BALANCE); -// } - -// function test_PerformanceFeeCalculationEdgeCases() public { -// // Test performance fee calculation with edge cases - -// // Case 1: Zero shares -// vm.prank(address(vault)); -// accountantAaveV3.setLastRealizedFeeExchangeRate(1.0e18, 1000 ether); - -// vm.prank(address(vault)); -// uint256 fee1 = accountantAaveV3.getPerformanceFee(0, 1.1e18, 18); -// assertEq(fee1, 0); - -// // Case 2: Same exchange rate (no change) -// vm.prank(address(vault)); -// uint256 fee2 = accountantAaveV3.getPerformanceFee(1000 ether, 1.0e18, 18); -// assertEq(fee2, 0); - -// // Case 3: Very small interest -// vm.prank(address(vault)); -// uint256 fee3 = accountantAaveV3.getPerformanceFee(1000 ether, 1.0001e18, 18); -// assertGt(fee3, 0); - -// console.log("Small interest fee:", fee3); -// } - -// function test_InitializeWithEmptyArrays() public { -// // Test initialization with empty arrays -// address[] memory lendAssets = new address[](0); -// address[] memory borrowAssets = new address[](0); - -// DataTypes.AaveV3AccountantModuleInitData memory initData = DataTypes.AaveV3AccountantModuleInitData({ -// poolAddressesProvider: AAVE_V3_POOL_ADDRESSES_PROVIDER, -// lendAssets: lendAssets, -// borrowAssets: borrowAssets, -// performanceFee: uint16(PERFORMANCE_FEE), -// vault: address(vault) -// }); - -// AccountantAaveV3 newImplementation = new AccountantAaveV3(); -// ProxyAdmin newProxyAdmin = new ProxyAdmin(address(this)); - -// TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( -// address(newImplementation), -// address(newProxyAdmin), -// abi.encodeWithSelector(AccountantAaveV3.initialize.selector, initData) -// ); - -// AccountantAaveV3 newAccountant = AccountantAaveV3(address(proxy)); - -// // Should work without reverting -// uint256 totalAssets = newAccountant.getTotalAssets(); -// assertEq(totalAssets, INITIAL_WHALE_BALANCE); -// } - -// function _enableMockingPoolDataProvider() internal { -// vm.mockCall( -// AAVE_V3_POOL_DATA_PROVIDER, -// abi.encodeWithSelector(IPoolDataProvider.getUserReserveData.selector, ST_XTZ, address(vault)), -// abi.encode(1000 * 10 ** 6, 0, 0, 0, 0, 0, 0, 0, 0) -// ); - -// vm.mockCall( -// AAVE_V3_POOL_DATA_PROVIDER, -// abi.encodeWithSelector(IPoolDataProvider.getUserReserveData.selector, XTZ, address(vault)), -// abi.encode(0, 0, 800 ether, 0, 0, 0, 0, 0, 0) -// ); -// } - -// function _enableMockingPriceOracle(uint256 stXtzPrice, uint256 xtzPrice) internal { -// vm.mockCall( -// AAVE_V3_PRICE_ORACLE, -// abi.encodeWithSelector(IPriceOracleGetter.getAssetPrice.selector, ST_XTZ), -// abi.encode(stXtzPrice) -// ); - -// vm.mockCall( -// AAVE_V3_PRICE_ORACLE, -// abi.encodeWithSelector(IPriceOracleGetter.getAssetPrice.selector, XTZ), -// abi.encode(xtzPrice) -// ); -// } -// } +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.13; + +import {TestBase} from "../TestBase.sol"; +import {console} from "forge-std/console.sol"; +import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +import {MockVault} from "../../../src/mock/MockVault.sol"; +import {AccountantAaveV3} from "../../../src/core/Accountant/aaveV3Accountant/AccountantAaveV3.sol"; +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {DataTypes} from "../../../src/common/DataTypes.sol"; +import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import {IPoolAddressesProvider} from "aave-v3-core/contracts/interfaces/IPoolAddressesProvider.sol"; +import {IPoolDataProvider} from "aave-v3-core/contracts/interfaces/IPoolDataProvider.sol"; +import {IAaveOracle} from "aave-v3-core/contracts/interfaces/IAaveOracle.sol"; +import {IPriceOracleGetter} from "aave-v3-core/contracts/interfaces/IPriceOracleGetter.sol"; + +// TODO : revisit with mutli token setup for eth mainnet +contract AccountantAaveV3Test is TestBase { + AccountantAaveV3 public accountantAaveV3Implementation; + ProxyAdmin public proxyAdmin; + + IERC20 public asset; + MockVault public vault; + + // Test data + uint256 public constant INITIAL_WHALE_BALANCE = 1000 ether; + + function setUp() public override { + super.setUp(); + + asset = IERC20(XTZ); + vault = new MockVault(asset, "Mock Vault", "mVLT"); + + DataTypes.AaveV3AccountantModuleInitData memory initData = DataTypes.AaveV3AccountantModuleInitData({ + poolAddressesProvider: environment.poolAddressesProvider, + lendAssets: environment.lendAssets, + borrowAssets: environment.borrowAssets, + performanceFee: uint16(PERFORMANCE_FEE), + vault: address(vault) + }); + + accountantAaveV3Implementation = new AccountantAaveV3(); + proxyAdmin = new ProxyAdmin(address(this)); + + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(accountantAaveV3Implementation), + address(proxyAdmin), + abi.encodeWithSelector(AccountantAaveV3.initialize.selector, initData) + ); + + accountantAaveV3 = AccountantAaveV3(address(proxy)); + + // Fund the accountant with XTZ from whale + vm.startPrank(environment.vaultAssetWhale); + asset.transfer(address(vault), INITIAL_WHALE_BALANCE); + vm.stopPrank(); + } + + function test_Initialize() public { + // Test that initialization works correctly + DataTypes.AaveV3AccountantModuleInitData memory initData = DataTypes.AaveV3AccountantModuleInitData({ + poolAddressesProvider: environment.poolAddressesProvider, + lendAssets: environment.lendAssets, + borrowAssets: environment.borrowAssets, + performanceFee: uint16(PERFORMANCE_FEE), + vault: address(vault) + }); + + AccountantAaveV3 newImplementation = new AccountantAaveV3(); + ProxyAdmin newProxyAdmin = new ProxyAdmin(address(this)); + + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(newImplementation), + address(newProxyAdmin), + abi.encodeWithSelector(AccountantAaveV3.initialize.selector, initData) + ); + + AccountantAaveV3 newAccountant = AccountantAaveV3(address(proxy)); + + // Test that the contract is properly initialized + assertEq(newAccountant.getTotalAssets(), INITIAL_WHALE_BALANCE); // Should work without reverting + } + + function test_InitializeRevertIfAlreadyInitialized() public { + // Test that initialize reverts if called again + DataTypes.AaveV3AccountantModuleInitData memory initData = DataTypes.AaveV3AccountantModuleInitData({ + poolAddressesProvider: environment.poolAddressesProvider, + lendAssets: environment.lendAssets, + borrowAssets: environment.borrowAssets, + performanceFee: uint16(PERFORMANCE_FEE), + vault: address(vault) + }); + + vm.expectRevert(); + accountantAaveV3.initialize(initData); + } + + function test_GetTotalAssets() public view { + // Test getTotalAssets calculation using actual fork data + uint256 totalAssets = accountantAaveV3.getTotalAssets(); + + // Should return the base asset balance since there are no lend/borrow positions yet + assertEq(totalAssets, INITIAL_WHALE_BALANCE); + + console.log("Total assets:", totalAssets); + console.log("Expected assets:", INITIAL_WHALE_BALANCE); + } + + function test_GetTotalAssetsWithMultipleAssets() public { + // mock pool data provider and price oracle + _enableMockingPoolDataProvider(); + _enableMockingPriceOracle(1.01e8, 1e8); + + uint256 totalAssets = accountantAaveV3.getTotalAssets(); + + // Calculate expected total assets based on mock data: + // Lend assets: 1000 ether * 1.01e8 (ST_XTZ price) = 1010e8 + // Borrow assets: -800 ether * 1e8 (XTZ price) = -800e8 + // Base asset balance: 1000 ether * 1e8 (XTZ price) = 1000e8 + // Total in market reference currency: 1010e8 - 800e8 + 1000e8 = 1210e8 + // Convert to base asset: 1210e18 / 1e18 = 1210 ether + uint256 expectedTotalAssets = 1210 ether; + + assertEq(totalAssets, expectedTotalAssets); + console.log("Total assets with multiple assets:", totalAssets); + console.log("Expected total assets:", expectedTotalAssets); + } + + function test_GetPerformanceFee() public { + uint256 totalShares = 1000 ether; + uint256 exchangeRate = 1.1e18; // 10% increase + uint256 lastRealizedFeeExchangeRate = 1.0e18; + uint256 totalSupply = 1000 ether; + + // Set the last realized fee exchange rate + vm.prank(address(vault)); + accountantAaveV3.setLastRealizedFeeExchangeRate(lastRealizedFeeExchangeRate, totalSupply); + + // Calculate expected performance fee + uint256 latestAssetAmount = totalShares * exchangeRate; + uint256 prevAssetAmount = totalShares * lastRealizedFeeExchangeRate; + uint256 interestGenerated = latestAssetAmount - prevAssetAmount; + uint256 expectedPerformanceFee = (interestGenerated * PERFORMANCE_FEE) / (10000 * 1e18); + + vm.prank(address(vault)); + uint256 actualPerformanceFee = accountantAaveV3.getPerformanceFee(totalShares, exchangeRate, 18); + + console.log("Performance fee:", actualPerformanceFee); + console.log("Expected fee:", expectedPerformanceFee); + + assertEq(actualPerformanceFee, expectedPerformanceFee); + } + + function test_GetPerformanceFeeNoInterest() public { + uint256 totalShares = 1000 ether; + uint256 exchangeRate = 0.9e18; // 10% decrease + uint256 lastRealizedFeeExchangeRate = 1.0e18; + uint256 totalSupply = 1000 ether; + // Set the last realized fee exchange rate + vm.prank(address(vault)); + accountantAaveV3.setLastRealizedFeeExchangeRate(lastRealizedFeeExchangeRate, totalSupply); + + vm.prank(address(vault)); + uint256 performanceFee = accountantAaveV3.getPerformanceFee(totalShares, exchangeRate, 18); + + // Should return 0 when there's no interest (exchange rate decreased) + assertEq(performanceFee, 0); + } + + function test_GetPerformanceFeeRevertIfNotVault() public { + uint256 totalShares = 1000 ether; + uint256 exchangeRate = 1.1e18; + + vm.expectRevert(); + accountantAaveV3.getPerformanceFee(totalShares, exchangeRate, 18); + } + + function test_SetLastRealizedFeeExchangeRate() public { + uint256 newExchangeRate = 1.2e18; + uint256 totalSupply = 1000 ether; + + vm.prank(address(vault)); + accountantAaveV3.setLastRealizedFeeExchangeRate(newExchangeRate, totalSupply); + + // Test that the exchange rate was set correctly by calling getPerformanceFee + vm.prank(address(vault)); + uint256 performanceFee = accountantAaveV3.getPerformanceFee(1000 ether, 1.3e18, 18); + + // Should calculate based on the new exchange rate + uint256 expectedInterest = 1000 ether * 1.3e18 - 1000 ether * newExchangeRate; + uint256 expectedFee = (expectedInterest * PERFORMANCE_FEE) / (10000 * 1e18); + + assertEq(performanceFee, expectedFee); + } + + function test_SetLastRealizedFeeExchangeRateRevertIfNotVault() public { + uint256 newExchangeRate = 1.2e18; + uint256 totalSupply = 1000 ether; + + vm.expectRevert(); + accountantAaveV3.setLastRealizedFeeExchangeRate(newExchangeRate, totalSupply); + } + + function test_OnlyVaultModifier() public { + // Test that only vault can call restricted functions + vm.expectRevert(); + accountantAaveV3.getPerformanceFee(1000 ether, 1.1e18, 18); + + vm.expectRevert(); + accountantAaveV3.setLastRealizedFeeExchangeRate(1.1e18, 1000 ether); + } + + function test_ConstructorDisablesInitializers() public { + // Test that the constructor properly disables initializers + AccountantAaveV3 newContract = new AccountantAaveV3(); + + // Should revert if we try to initialize directly + DataTypes.AaveV3AccountantModuleInitData memory initData = DataTypes.AaveV3AccountantModuleInitData({ + poolAddressesProvider: environment.poolAddressesProvider, + lendAssets: environment.lendAssets, + borrowAssets: environment.borrowAssets, + performanceFee: uint16(PERFORMANCE_FEE), + vault: address(vault) + }); + + vm.expectRevert(); + newContract.initialize(initData); + } + + function test_GetTotalAssetsWithZeroBalances() public view { + // Test getTotalAssets when there are no lend/borrow positions + // This should be the default state since we haven't interacted with Aave V3 + uint256 totalAssets = accountantAaveV3.getTotalAssets(); + + // Should only include base asset balance + assertEq(totalAssets, INITIAL_WHALE_BALANCE); + } + + function test_PerformanceFeeCalculationEdgeCases() public { + // Test performance fee calculation with edge cases + + // Case 1: Zero shares + vm.prank(address(vault)); + accountantAaveV3.setLastRealizedFeeExchangeRate(1.0e18, 1000 ether); + + vm.prank(address(vault)); + uint256 fee1 = accountantAaveV3.getPerformanceFee(0, 1.1e18, 18); + assertEq(fee1, 0); + + // Case 2: Same exchange rate (no change) + vm.prank(address(vault)); + uint256 fee2 = accountantAaveV3.getPerformanceFee(1000 ether, 1.0e18, 18); + assertEq(fee2, 0); + + // Case 3: Very small interest + vm.prank(address(vault)); + uint256 fee3 = accountantAaveV3.getPerformanceFee(1000 ether, 1.0001e18, 18); + assertGt(fee3, 0); + + console.log("Small interest fee:", fee3); + } + + function test_InitializeWithEmptyArrays() public { + // Test initialization with empty arrays + address[] memory lendAssets = new address[](0); + address[] memory borrowAssets = new address[](0); + + DataTypes.AaveV3AccountantModuleInitData memory initData = DataTypes.AaveV3AccountantModuleInitData({ + poolAddressesProvider: environment.poolAddressesProvider, + lendAssets: lendAssets, + borrowAssets: borrowAssets, + performanceFee: uint16(PERFORMANCE_FEE), + vault: address(vault) + }); + + AccountantAaveV3 newImplementation = new AccountantAaveV3(); + ProxyAdmin newProxyAdmin = new ProxyAdmin(address(this)); + + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(newImplementation), + address(newProxyAdmin), + abi.encodeWithSelector(AccountantAaveV3.initialize.selector, initData) + ); + + AccountantAaveV3 newAccountant = AccountantAaveV3(address(proxy)); + + // Should work without reverting + uint256 totalAssets = newAccountant.getTotalAssets(); + assertEq(totalAssets, INITIAL_WHALE_BALANCE); + } + + function _enableMockingPoolDataProvider() internal { + vm.mockCall( + environment.poolDataProvider, + abi.encodeWithSelector( + IPoolDataProvider.getUserReserveData.selector, environment.lendAssets[0], address(vault) + ), + abi.encode(1000 * 10 ** 6, 0, 0, 0, 0, 0, 0, 0, 0) + ); + + vm.mockCall( + environment.poolDataProvider, + abi.encodeWithSelector( + IPoolDataProvider.getUserReserveData.selector, environment.borrowAssets[0], address(vault) + ), + abi.encode(0, 0, 800 ether, 0, 0, 0, 0, 0, 0) + ); + } + + function _enableMockingPriceOracle(uint256 stXtzPrice, uint256 xtzPrice) internal { + vm.mockCall( + environment.priceOracle, + abi.encodeWithSelector(IPriceOracleGetter.getAssetPrice.selector, environment.lendAssets[0]), + abi.encode(stXtzPrice) + ); + + vm.mockCall( + environment.priceOracle, + abi.encodeWithSelector(IPriceOracleGetter.getAssetPrice.selector, environment.borrowAssets[0]), + abi.encode(xtzPrice) + ); + } +} diff --git a/test/core/unit/DepositManager.t.sol b/test/core/unit/DepositManager.t.sol index e9d5001..2b03229 100644 --- a/test/core/unit/DepositManager.t.sol +++ b/test/core/unit/DepositManager.t.sol @@ -1,297 +1,298 @@ -// // SPDX-License-Identifier: MIT - -// pragma solidity ^0.8.13; - -// import {TestBase} from "../TestBase.sol"; -// import {console} from "forge-std/console.sol"; -// import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; -// import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; -// import {TransparentUpgradeableProxy} from -// "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -// import {DataTypes} from "../../../src/common/DataTypes.sol"; -// import {Errors} from "../../../src/common/Errors.sol"; -// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -// import {DepositManager} from "../../../src/core/DepositManager/DepositManager.sol"; - -// contract DepositManagerTest is TestBase { -// Superloop public superloopImplementation; -// ProxyAdmin public proxyAdmin; -// address public user1; -// address public user2; - -// function setUp() public override { -// super.setUp(); - -// user1 = makeAddr("user1"); -// user2 = makeAddr("user2"); - -// vm.startPrank(admin); -// _deployModules(); - -// address[] memory modules = new address[](5); -// modules[0] = address(supplyModule); -// modules[1] = address(withdrawModule); -// modules[2] = address(borrowModule); -// modules[3] = address(repayModule); -// modules[4] = address(dexModule); - -// DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ -// asset: XTZ, -// name: "XTZ Vault", -// symbol: "XTZV", -// supplyCap: 100000 * 10 ** 18, -// minimumDepositAmount: 100, -// instantWithdrawFee: 0, -// superloopModuleRegistry: address(moduleRegistry), -// modules: modules, -// accountant: mockModule, -// withdrawManager: mockModule, -// depositManager: mockModule, -// cashReserve: 1000, -// vaultAdmin: admin, -// treasury: treasury, -// vaultOperator: admin -// }); -// superloopImplementation = new Superloop(); -// proxyAdmin = new ProxyAdmin(address(this)); - -// TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( -// address(superloopImplementation), -// address(proxyAdmin), -// abi.encodeWithSelector(Superloop.initialize.selector, initData) -// ); -// superloop = Superloop(payable(address(proxy))); - -// _deployAccountant(address(superloop), _singleAddressArray(ST_XTZ), _singleAddressArray(XTZ)); -// _deployWithdrawManager(address(superloop)); -// _deployDepositManager(address(superloop)); - -// superloop.setAccountantModule(address(accountant)); -// superloop.setWithdrawManagerModule(address(withdrawManager)); -// superloop.setDepositManagerModule(address(depositManager)); - -// vm.stopPrank(); - -// vm.label(address(superloop), "superloop"); -// vm.label(user1, "user1"); -// vm.label(user2, "user2"); -// } - -// function test_Initialize() public view { -// address vault = depositManager.vault(); -// address asset = depositManager.asset(); -// uint256 nextDepositRequestId = depositManager.nextDepositRequestId(); - -// assertEq(vault, address(superloop)); -// assertEq(asset, XTZ); -// assertEq(nextDepositRequestId, 1); -// } - -// // ============ requestDeposit Tests ============ - -// function test_requestDeposit_Success() public { -// uint256 depositAmount = 1000 * 10 ** 18; - -// // Give user1 some XTZ tokens -// deal(XTZ, user1, depositAmount); - -// vm.startPrank(user1); -// IERC20(XTZ).approve(address(depositManager), depositAmount); - -// // Expect the DepositRequested event -// vm.expectEmit(true, true, true, true); -// emit DepositManager.DepositRequested(user1, depositAmount, 1); - -// depositManager.requestDeposit(depositAmount, address(0)); -// vm.stopPrank(); - -// // Verify the deposit request was created -// DataTypes.DepositRequestData memory request = depositManager.depositRequest(1); - -// assertEq(request.amount, depositAmount); -// assertEq(request.amountProcessed, 0); -// assertEq(request.user, user1); -// assertEq(uint256(request.state), uint256(DataTypes.RequestProcessingState.UNPROCESSED)); - -// // Verify user's deposit request ID was set -// (, uint256 userRequestId) = depositManager.userDepositRequest(user1); -// assertEq(userRequestId, 1); - -// // Verify total pending deposits increased -// assertEq(depositManager.totalPendingDeposits(), depositAmount); - -// // Verify tokens were transferred to deposit manager -// assertEq(IERC20(XTZ).balanceOf(address(depositManager)), depositAmount); -// assertEq(IERC20(XTZ).balanceOf(user1), 0); -// } +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.13; + +import {TestBase} from "../TestBase.sol"; +import {console} from "forge-std/console.sol"; +import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; +import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; +import {TransparentUpgradeableProxy} from + "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {DataTypes} from "../../../src/common/DataTypes.sol"; +import {Errors} from "../../../src/common/Errors.sol"; +import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +import {DepositManager} from "../../../src/core/DepositManager/DepositManager.sol"; + +// TODO : revisit with mutli token setup for eth mainnet +contract DepositManagerTest is TestBase { + Superloop public superloopImplementation; + ProxyAdmin public proxyAdmin; + address public user1; + address public user2; + + function setUp() public override { + super.setUp(); + + user1 = makeAddr("user1"); + user2 = makeAddr("user2"); + + vm.startPrank(admin); + _deployModules(); + + address[] memory modules = new address[](5); + modules[0] = address(supplyModule); + modules[1] = address(withdrawModule); + modules[2] = address(borrowModule); + modules[3] = address(repayModule); + modules[4] = address(dexModule); + + DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ + asset: environment.vaultAsset, + name: "Vault", + symbol: "VLT", + supplyCap: 100000 * 10 ** environment.vaultAssetDecimals, + minimumDepositAmount: 100, + instantWithdrawFee: 0, + superloopModuleRegistry: address(moduleRegistry), + modules: modules, + accountant: mockModule, + withdrawManager: mockModule, + depositManager: mockModule, + cashReserve: 1000, + vaultAdmin: admin, + treasury: treasury, + vaultOperator: admin + }); + superloopImplementation = new Superloop(); + proxyAdmin = new ProxyAdmin(address(this)); + + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(superloopImplementation), + address(proxyAdmin), + abi.encodeWithSelector(Superloop.initialize.selector, initData) + ); + superloop = Superloop(payable(address(proxy))); + + _deployAccountant(address(superloop), environment.lendAssets, environment.borrowAssets); + _deployWithdrawManager(address(superloop)); + _deployDepositManager(address(superloop)); + + superloop.setAccountantModule(address(accountant)); + superloop.setWithdrawManagerModule(address(withdrawManager)); + superloop.setDepositManagerModule(address(depositManager)); + + vm.stopPrank(); + + vm.label(address(superloop), "superloop"); + vm.label(user1, "user1"); + vm.label(user2, "user2"); + } + + function test_Initialize() public view { + address vault = depositManager.vault(); + address asset = depositManager.asset(); + uint256 nextDepositRequestId = depositManager.nextDepositRequestId(); + + assertEq(vault, address(superloop)); + assertEq(asset, environment.vaultAsset); + assertEq(nextDepositRequestId, 1); + } + + // ============ requestDeposit Tests ============ + + function test_requestDeposit_Success() public { + uint256 depositAmount = 1000 * 10 ** 18; + + // Give user1 some XTZ tokens + deal(environment.vaultAsset, user1, depositAmount); + + vm.startPrank(user1); + IERC20(environment.vaultAsset).approve(address(depositManager), depositAmount); + + // Expect the DepositRequested event + vm.expectEmit(true, true, true, true); + emit DepositManager.DepositRequested(user1, depositAmount, 1); + + depositManager.requestDeposit(depositAmount, address(0)); + vm.stopPrank(); + + // Verify the deposit request was created + DataTypes.DepositRequestData memory request = depositManager.depositRequest(1); + + assertEq(request.amount, depositAmount); + assertEq(request.amountProcessed, 0); + assertEq(request.user, user1); + assertEq(uint256(request.state), uint256(DataTypes.RequestProcessingState.UNPROCESSED)); + + // Verify user's deposit request ID was set + (, uint256 userRequestId) = depositManager.userDepositRequest(user1); + assertEq(userRequestId, 1); + + // Verify total pending deposits increased + assertEq(depositManager.totalPendingDeposits(), depositAmount); + + // Verify tokens were transferred to deposit manager + assertEq(IERC20(environment.vaultAsset).balanceOf(address(depositManager)), depositAmount); + assertEq(IERC20(environment.vaultAsset).balanceOf(user1), 0); + } -// function test_requestDeposit_OnBehalfOf() public { -// uint256 depositAmount = 1000 * 10 ** 18; + function test_requestDeposit_OnBehalfOf() public { + uint256 depositAmount = 1000 * 10 ** 18; -// // Give user1 some XTZ tokens -// deal(XTZ, user1, depositAmount); + // Give user1 some XTZ tokens + deal(environment.vaultAsset, user1, depositAmount); -// vm.startPrank(user1); -// IERC20(XTZ).approve(address(depositManager), depositAmount); + vm.startPrank(user1); + IERC20(environment.vaultAsset).approve(address(depositManager), depositAmount); -// // Expect the DepositRequested event with user2 as the beneficiary -// vm.expectEmit(true, true, true, true); -// emit DepositManager.DepositRequested(user2, depositAmount, 1); + // Expect the DepositRequested event with user2 as the beneficiary + vm.expectEmit(true, true, true, true); + emit DepositManager.DepositRequested(user2, depositAmount, 1); -// depositManager.requestDeposit(depositAmount, user2); -// vm.stopPrank(); + depositManager.requestDeposit(depositAmount, user2); + vm.stopPrank(); -// // Verify the deposit request was created for user2 -// DataTypes.DepositRequestData memory request = depositManager.depositRequest(1); + // Verify the deposit request was created for user2 + DataTypes.DepositRequestData memory request = depositManager.depositRequest(1); -// assertEq(request.user, user2); -// assertEq(request.amount, depositAmount); -// } + assertEq(request.user, user2); + assertEq(request.amount, depositAmount); + } -// function test_requestDeposit_ZeroAmount() public { -// uint256 depositAmount = 0; + function test_requestDeposit_ZeroAmount() public { + uint256 depositAmount = 0; -// vm.startPrank(user1); -// vm.expectRevert(bytes(Errors.INVALID_AMOUNT)); -// depositManager.requestDeposit(depositAmount, address(0)); -// vm.stopPrank(); -// } + vm.startPrank(user1); + vm.expectRevert(bytes(Errors.INVALID_AMOUNT)); + depositManager.requestDeposit(depositAmount, address(0)); + vm.stopPrank(); + } -// function test_requestDeposit_MinimumDepositAmountNotMet() public { -// uint256 depositAmount = 99; + function test_requestDeposit_MinimumDepositAmountNotMet() public { + uint256 depositAmount = 99; -// vm.startPrank(user1); -// vm.expectRevert(bytes(Errors.INVALID_AMOUNT)); -// depositManager.requestDeposit(depositAmount, address(0)); -// vm.stopPrank(); -// } + vm.startPrank(user1); + vm.expectRevert(bytes(Errors.INVALID_AMOUNT)); + depositManager.requestDeposit(depositAmount, address(0)); + vm.stopPrank(); + } -// function test_requestDeposit_SupplyCapExceeded() public { -// uint256 depositAmount = 200000 * 10 ** 18; // Exceeds supply cap of 100000 * 10**18 + function test_requestDeposit_SupplyCapExceeded() public { + uint256 depositAmount = 200000 * 10 ** 18; // Exceeds supply cap of 100000 * 10**18 -// // Give user1 enough XTZ tokens -// deal(XTZ, user1, depositAmount); + // Give user1 enough XTZ tokens + deal(environment.vaultAsset, user1, depositAmount); -// vm.startPrank(user1); -// IERC20(XTZ).approve(address(depositManager), depositAmount); + vm.startPrank(user1); + IERC20(environment.vaultAsset).approve(address(depositManager), depositAmount); -// vm.expectRevert(bytes(Errors.SUPPLY_CAP_EXCEEDED)); -// depositManager.requestDeposit(depositAmount, address(0)); -// vm.stopPrank(); -// } + vm.expectRevert(bytes(Errors.SUPPLY_CAP_EXCEEDED)); + depositManager.requestDeposit(depositAmount, address(0)); + vm.stopPrank(); + } -// function test_requestDeposit_ActiveRequestExists() public { -// uint256 depositAmount = 1000 * 10 ** 18; + function test_requestDeposit_ActiveRequestExists() public { + uint256 depositAmount = 1000 * 10 ** 18; -// // Give user1 some XTZ tokens -// deal(XTZ, user1, depositAmount * 2); + // Give user1 some XTZ tokens + deal(environment.vaultAsset, user1, depositAmount * 2); -// vm.startPrank(user1); -// IERC20(XTZ).approve(address(depositManager), depositAmount * 2); + vm.startPrank(user1); + IERC20(environment.vaultAsset).approve(address(depositManager), depositAmount * 2); -// // Make first deposit request -// depositManager.requestDeposit(depositAmount, address(0)); + // Make first deposit request + depositManager.requestDeposit(depositAmount, address(0)); -// // Try to make another deposit request - should fail -// vm.expectRevert(bytes(Errors.DEPOSIT_REQUEST_ACTIVE)); -// depositManager.requestDeposit(depositAmount, address(0)); -// vm.stopPrank(); -// } + // Try to make another deposit request - should fail + vm.expectRevert(bytes(Errors.DEPOSIT_REQUEST_ACTIVE)); + depositManager.requestDeposit(depositAmount, address(0)); + vm.stopPrank(); + } -// function test_requestDeposit_InsufficientBalance() public { -// uint256 depositAmount = 1000 * 10 ** 18; + function test_requestDeposit_InsufficientBalance() public { + uint256 depositAmount = 1000 * 10 ** 18; -// // Don't give user1 any XTZ tokens -// vm.startPrank(user1); -// IERC20(XTZ).approve(address(depositManager), depositAmount); + // Don't give user1 any XTZ tokens + vm.startPrank(user1); + IERC20(environment.vaultAsset).approve(address(depositManager), depositAmount); -// vm.expectRevert(); -// depositManager.requestDeposit(depositAmount, address(0)); -// vm.stopPrank(); -// } + vm.expectRevert(); + depositManager.requestDeposit(depositAmount, address(0)); + vm.stopPrank(); + } -// // // ============ cancelDepositRequest Tests ============ + // // ============ cancelDepositRequest Tests ============ -// function test_cancelDepositRequest_Success() public { -// uint256 depositAmount = 1000 * 10 ** 18; + function test_cancelDepositRequest_Success() public { + uint256 depositAmount = 1000 * 10 ** 18; -// // Give user1 some XTZ tokens and make a deposit request -// deal(XTZ, user1, depositAmount); + // Give user1 some XTZ tokens and make a deposit request + deal(environment.vaultAsset, user1, depositAmount); -// vm.startPrank(user1); -// IERC20(XTZ).approve(address(depositManager), depositAmount); -// depositManager.requestDeposit(depositAmount, address(0)); + vm.startPrank(user1); + IERC20(environment.vaultAsset).approve(address(depositManager), depositAmount); + depositManager.requestDeposit(depositAmount, address(0)); -// uint256 userBalanceBefore = IERC20(XTZ).balanceOf(user1); + uint256 userBalanceBefore = IERC20(environment.vaultAsset).balanceOf(user1); -// // Expect the DepositRequestCancelled event -// vm.expectEmit(true, true, true, true); -// emit DepositManager.DepositRequestCancelled(1, user1, depositAmount); + // Expect the DepositRequestCancelled event + vm.expectEmit(true, true, true, true); + emit DepositManager.DepositRequestCancelled(1, user1, depositAmount); -// depositManager.cancelDepositRequest(1); -// vm.stopPrank(); + depositManager.cancelDepositRequest(1); + vm.stopPrank(); -// // Verify the deposit request was cancelled -// DataTypes.DepositRequestData memory request = depositManager.depositRequest(1); + // Verify the deposit request was cancelled + DataTypes.DepositRequestData memory request = depositManager.depositRequest(1); -// assertEq(uint256(request.state), uint256(DataTypes.RequestProcessingState.CANCELLED)); + assertEq(uint256(request.state), uint256(DataTypes.RequestProcessingState.CANCELLED)); -// // Verify user's deposit request ID was cleared -// (, uint256 userRequestId) = depositManager.userDepositRequest(user1); -// assertEq(userRequestId, 0); + // Verify user's deposit request ID was cleared + (, uint256 userRequestId) = depositManager.userDepositRequest(user1); + assertEq(userRequestId, 0); -// // Verify total pending deposits decreased -// assertEq(depositManager.totalPendingDeposits(), 0); + // Verify total pending deposits decreased + assertEq(depositManager.totalPendingDeposits(), 0); -// // Verify tokens were refunded to user -// assertEq(IERC20(XTZ).balanceOf(user1), userBalanceBefore + depositAmount); -// assertEq(IERC20(XTZ).balanceOf(address(depositManager)), 0); -// } + // Verify tokens were refunded to user + assertEq(IERC20(environment.vaultAsset).balanceOf(user1), userBalanceBefore + depositAmount); + assertEq(IERC20(environment.vaultAsset).balanceOf(address(depositManager)), 0); + } -// function test_cancelDepositRequest_WrongOwner() public { -// uint256 depositAmount = 1000 * 10 ** 18; + function test_cancelDepositRequest_WrongOwner() public { + uint256 depositAmount = 1000 * 10 ** 18; -// // Give user1 some XTZ tokens and make a deposit request -// deal(XTZ, user1, depositAmount); + // Give user1 some XTZ tokens and make a deposit request + deal(environment.vaultAsset, user1, depositAmount); -// vm.startPrank(user1); -// IERC20(XTZ).approve(address(depositManager), depositAmount); -// depositManager.requestDeposit(depositAmount, address(0)); -// vm.stopPrank(); + vm.startPrank(user1); + IERC20(environment.vaultAsset).approve(address(depositManager), depositAmount); + depositManager.requestDeposit(depositAmount, address(0)); + vm.stopPrank(); -// // Try to cancel from user2 - should fail -// vm.startPrank(user2); -// vm.expectRevert(bytes(Errors.CALLER_NOT_DEPOSIT_REQUEST_OWNER)); -// depositManager.cancelDepositRequest(1); -// vm.stopPrank(); -// } + // Try to cancel from user2 - should fail + vm.startPrank(user2); + vm.expectRevert(bytes(Errors.CALLER_NOT_DEPOSIT_REQUEST_OWNER)); + depositManager.cancelDepositRequest(1); + vm.stopPrank(); + } -// function test_cancelDepositRequest_AlreadyCancelled() public { -// uint256 depositAmount = 1000 * 10 ** 18; + function test_cancelDepositRequest_AlreadyCancelled() public { + uint256 depositAmount = 1000 * 10 ** 18; -// // Give user1 some XTZ tokens and make a deposit request -// deal(XTZ, user1, depositAmount); + // Give user1 some XTZ tokens and make a deposit request + deal(XTZ, user1, depositAmount); -// vm.startPrank(user1); -// IERC20(XTZ).approve(address(depositManager), depositAmount); -// depositManager.requestDeposit(depositAmount, address(0)); + vm.startPrank(user1); + IERC20(XTZ).approve(address(depositManager), depositAmount); + depositManager.requestDeposit(depositAmount, address(0)); -// // Cancel the request -// depositManager.cancelDepositRequest(1); + // Cancel the request + depositManager.cancelDepositRequest(1); -// // Try to cancel again - should fail -// vm.expectRevert(bytes(Errors.DEPOSIT_REQUEST_ALREADY_CANCELLED)); -// depositManager.cancelDepositRequest(1); -// vm.stopPrank(); -// } + // Try to cancel again - should fail + vm.expectRevert(bytes(Errors.DEPOSIT_REQUEST_ALREADY_CANCELLED)); + depositManager.cancelDepositRequest(1); + vm.stopPrank(); + } -// function test_cancelDepositRequest_NonExistentRequest() public { -// vm.startPrank(user1); -// vm.expectRevert(bytes(Errors.DEPOSIT_REQUEST_NOT_FOUND)); -// depositManager.cancelDepositRequest(999); // Non-existent request ID -// vm.stopPrank(); -// } + function test_cancelDepositRequest_NonExistentRequest() public { + vm.startPrank(user1); + vm.expectRevert(bytes(Errors.DEPOSIT_REQUEST_NOT_FOUND)); + depositManager.cancelDepositRequest(999); // Non-existent request ID + vm.stopPrank(); + } -// // TODO: Test partial cancellation, already processed cancellation and reslveDepostRequest functions -// // NOTE: Cannot test these function without manipulating the internal state, so will be tested in integration tests -// } + // TODO: Test partial cancellation, already processed cancellation and reslveDepostRequest functions + // NOTE: Cannot test these function without manipulating the internal state, so will be tested in integration tests +} diff --git a/test/core/unit/ModuleRegistry.t.sol b/test/core/unit/ModuleRegistry.t.sol index e6fbea5..e208b19 100644 --- a/test/core/unit/ModuleRegistry.t.sol +++ b/test/core/unit/ModuleRegistry.t.sol @@ -1,289 +1,290 @@ -// // SPDX-License-Identifier: MIT +// SPDX-License-Identifier: MIT -// pragma solidity ^0.8.13; +pragma solidity ^0.8.13; -// import {Test, console} from "forge-std/Test.sol"; -// import {SuperloopModuleRegistry} from "../../../src/core/ModuleRegistry/ModuleRegistry.sol"; -// import {DataTypes} from "../../../src/common/DataTypes.sol"; -// import {Errors} from "../../../src/common/Errors.sol"; +import {Test, console} from "forge-std/Test.sol"; +import {SuperloopModuleRegistry} from "../../../src/core/ModuleRegistry/ModuleRegistry.sol"; +import {DataTypes} from "../../../src/common/DataTypes.sol"; +import {Errors} from "../../../src/common/Errors.sol"; -// contract ModuleRegistryTest is Test { -// SuperloopModuleRegistry public moduleRegistry; +// TODO : revisit with mutli token setup for eth mainnet +contract ModuleRegistryTest is Test { + SuperloopModuleRegistry public moduleRegistry; -// address public owner; -// address public user; -// address public module1; -// address public module2; -// address public module3; + address public owner; + address public user; + address public module1; + address public module2; + address public module3; -// string public constant MODULE_NAME_1 = "UniversalDexModule"; -// string public constant MODULE_NAME_2 = "AccountantModule"; -// string public constant MODULE_NAME_3 = "WithdrawManagerModule"; + string public constant MODULE_NAME_1 = "UniversalDexModule"; + string public constant MODULE_NAME_2 = "AccountantModule"; + string public constant MODULE_NAME_3 = "WithdrawManagerModule"; -// event ModuleSet(string name, bytes32 indexed id, address indexed module); + event ModuleSet(string name, bytes32 indexed id, address indexed module); -// function setUp() public { -// owner = makeAddr("owner"); -// user = makeAddr("user"); -// module1 = makeAddr("module1"); -// module2 = makeAddr("module2"); -// module3 = makeAddr("module3"); + function setUp() public { + owner = makeAddr("owner"); + user = makeAddr("user"); + module1 = makeAddr("module1"); + module2 = makeAddr("module2"); + module3 = makeAddr("module3"); -// vm.startPrank(owner); -// moduleRegistry = new SuperloopModuleRegistry(); -// vm.stopPrank(); -// } + vm.startPrank(owner); + moduleRegistry = new SuperloopModuleRegistry(); + vm.stopPrank(); + } -// // ============ Constructor Tests ============ + // ============ Constructor Tests ============ -// function test_Constructor_SetsOwner() public view { -// assertEq(moduleRegistry.owner(), owner); -// } + function test_Constructor_SetsOwner() public view { + assertEq(moduleRegistry.owner(), owner); + } -// // ============ setModule Tests ============ + // ============ setModule Tests ============ -// function test_SetModule_Success() public { -// vm.startPrank(owner); + function test_SetModule_Success() public { + vm.startPrank(owner); -// vm.expectEmit(true, true, false, true); -// emit ModuleSet(MODULE_NAME_1, keccak256(abi.encode(MODULE_NAME_1)), module1); + vm.expectEmit(true, true, false, true); + emit ModuleSet(MODULE_NAME_1, keccak256(abi.encode(MODULE_NAME_1)), module1); -// moduleRegistry.setModule(MODULE_NAME_1, module1); + moduleRegistry.setModule(MODULE_NAME_1, module1); -// vm.stopPrank(); + vm.stopPrank(); -// // Verify module was set correctly -// DataTypes.ModuleData memory moduleData = moduleRegistry.getModuleByName(MODULE_NAME_1); -// assertEq(moduleData.moduleName, MODULE_NAME_1); -// assertEq(moduleData.moduleAddress, module1); -// } + // Verify module was set correctly + DataTypes.ModuleData memory moduleData = moduleRegistry.getModuleByName(MODULE_NAME_1); + assertEq(moduleData.moduleName, MODULE_NAME_1); + assertEq(moduleData.moduleAddress, module1); + } -// function test_SetModule_OnlyOwner() public { -// vm.startPrank(user); + function test_SetModule_OnlyOwner() public { + vm.startPrank(user); -// vm.expectRevert(); -// moduleRegistry.setModule(MODULE_NAME_1, module1); + vm.expectRevert(); + moduleRegistry.setModule(MODULE_NAME_1, module1); -// vm.stopPrank(); -// } + vm.stopPrank(); + } -// function test_SetModule_EmptyName() public { -// vm.startPrank(owner); + function test_SetModule_EmptyName() public { + vm.startPrank(owner); -// vm.expectRevert(); -// moduleRegistry.setModule("", module1); + vm.expectRevert(); + moduleRegistry.setModule("", module1); -// vm.stopPrank(); -// } + vm.stopPrank(); + } -// function test_SetModule_ZeroAddress() public { -// vm.startPrank(owner); + function test_SetModule_ZeroAddress() public { + vm.startPrank(owner); -// vm.expectRevert(); -// moduleRegistry.setModule(MODULE_NAME_1, address(0)); + vm.expectRevert(); + moduleRegistry.setModule(MODULE_NAME_1, address(0)); -// vm.stopPrank(); -// } + vm.stopPrank(); + } -// function test_SetModule_MultipleModules() public { -// vm.startPrank(owner); + function test_SetModule_MultipleModules() public { + vm.startPrank(owner); -// moduleRegistry.setModule(MODULE_NAME_1, module1); -// moduleRegistry.setModule(MODULE_NAME_2, module2); -// moduleRegistry.setModule(MODULE_NAME_3, module3); + moduleRegistry.setModule(MODULE_NAME_1, module1); + moduleRegistry.setModule(MODULE_NAME_2, module2); + moduleRegistry.setModule(MODULE_NAME_3, module3); -// vm.stopPrank(); + vm.stopPrank(); -// // Verify all modules were set correctly -// DataTypes.ModuleData memory moduleData1 = moduleRegistry.getModuleByName(MODULE_NAME_1); -// DataTypes.ModuleData memory moduleData2 = moduleRegistry.getModuleByName(MODULE_NAME_2); -// DataTypes.ModuleData memory moduleData3 = moduleRegistry.getModuleByName(MODULE_NAME_3); + // Verify all modules were set correctly + DataTypes.ModuleData memory moduleData1 = moduleRegistry.getModuleByName(MODULE_NAME_1); + DataTypes.ModuleData memory moduleData2 = moduleRegistry.getModuleByName(MODULE_NAME_2); + DataTypes.ModuleData memory moduleData3 = moduleRegistry.getModuleByName(MODULE_NAME_3); -// assertEq(moduleData1.moduleName, MODULE_NAME_1); -// assertEq(moduleData1.moduleAddress, module1); -// assertEq(moduleData2.moduleName, MODULE_NAME_2); -// assertEq(moduleData2.moduleAddress, module2); -// assertEq(moduleData3.moduleName, MODULE_NAME_3); -// assertEq(moduleData3.moduleAddress, module3); -// } + assertEq(moduleData1.moduleName, MODULE_NAME_1); + assertEq(moduleData1.moduleAddress, module1); + assertEq(moduleData2.moduleName, MODULE_NAME_2); + assertEq(moduleData2.moduleAddress, module2); + assertEq(moduleData3.moduleName, MODULE_NAME_3); + assertEq(moduleData3.moduleAddress, module3); + } -// function test_SetModule_OverwriteExisting() public { -// vm.startPrank(owner); + function test_SetModule_OverwriteExisting() public { + vm.startPrank(owner); -// moduleRegistry.setModule(MODULE_NAME_1, module1); -// moduleRegistry.setModule(MODULE_NAME_1, module2); // Overwrite with different address - -// vm.stopPrank(); - -// // Verify module was overwritten -// DataTypes.ModuleData memory moduleData = moduleRegistry.getModuleByName(MODULE_NAME_1); -// assertEq(moduleData.moduleName, MODULE_NAME_1); -// assertEq(moduleData.moduleAddress, module2); -// } - -// // ============ getModuleByName Tests ============ - -// function test_GetModuleByName_ExistingModule() public { -// vm.startPrank(owner); -// moduleRegistry.setModule(MODULE_NAME_1, module1); -// vm.stopPrank(); - -// DataTypes.ModuleData memory moduleData = moduleRegistry.getModuleByName(MODULE_NAME_1); -// assertEq(moduleData.moduleName, MODULE_NAME_1); -// assertEq(moduleData.moduleAddress, module1); -// } + moduleRegistry.setModule(MODULE_NAME_1, module1); + moduleRegistry.setModule(MODULE_NAME_1, module2); // Overwrite with different address + + vm.stopPrank(); + + // Verify module was overwritten + DataTypes.ModuleData memory moduleData = moduleRegistry.getModuleByName(MODULE_NAME_1); + assertEq(moduleData.moduleName, MODULE_NAME_1); + assertEq(moduleData.moduleAddress, module2); + } + + // ============ getModuleByName Tests ============ + + function test_GetModuleByName_ExistingModule() public { + vm.startPrank(owner); + moduleRegistry.setModule(MODULE_NAME_1, module1); + vm.stopPrank(); + + DataTypes.ModuleData memory moduleData = moduleRegistry.getModuleByName(MODULE_NAME_1); + assertEq(moduleData.moduleName, MODULE_NAME_1); + assertEq(moduleData.moduleAddress, module1); + } -// function test_GetModuleByName_NonExistentModule() public view { -// DataTypes.ModuleData memory moduleData = moduleRegistry.getModuleByName("NonExistentModule"); -// assertEq(moduleData.moduleName, ""); -// assertEq(moduleData.moduleAddress, address(0)); -// } - -// function test_GetModuleByName_CaseSensitive() public { -// vm.startPrank(owner); -// moduleRegistry.setModule(MODULE_NAME_1, module1); -// vm.stopPrank(); - -// // Different case should return empty data -// DataTypes.ModuleData memory moduleData = moduleRegistry.getModuleByName("universaldexmodule"); -// assertEq(moduleData.moduleName, ""); -// assertEq(moduleData.moduleAddress, address(0)); -// } - -// // ============ getModuleByAddress Tests ============ - -// function test_GetModuleByAddress_ExistingModule() public { -// vm.startPrank(owner); -// moduleRegistry.setModule(MODULE_NAME_1, module1); -// vm.stopPrank(); - -// DataTypes.ModuleData memory moduleData = moduleRegistry.getModuleByAddress(module1); -// assertEq(moduleData.moduleName, MODULE_NAME_1); -// assertEq(moduleData.moduleAddress, module1); -// } - -// function test_GetModuleByAddress_NonExistentModule() public { -// DataTypes.ModuleData memory moduleData = moduleRegistry.getModuleByAddress(makeAddr("nonexistent")); -// assertEq(moduleData.moduleName, ""); -// assertEq(moduleData.moduleAddress, address(0)); -// } - -// function test_GetModuleByAddress_ZeroAddress() public view { -// DataTypes.ModuleData memory moduleData = moduleRegistry.getModuleByAddress(address(0)); -// assertEq(moduleData.moduleName, ""); -// assertEq(moduleData.moduleAddress, address(0)); -// } - -// // ============ getModules Tests ============ - -// function test_GetModules_EmptyRegistry() public view { -// DataTypes.ModuleData[] memory modules = moduleRegistry.getModules(); -// assertEq(modules.length, 0); -// } - -// function test_GetModules_SingleModule() public { -// vm.startPrank(owner); -// moduleRegistry.setModule(MODULE_NAME_1, module1); -// vm.stopPrank(); - -// DataTypes.ModuleData[] memory modules = moduleRegistry.getModules(); -// assertEq(modules.length, 1); -// assertEq(modules[0].moduleName, MODULE_NAME_1); -// assertEq(modules[0].moduleAddress, module1); -// } - -// function test_GetModules_MultipleModules() public { -// vm.startPrank(owner); -// moduleRegistry.setModule(MODULE_NAME_1, module1); -// moduleRegistry.setModule(MODULE_NAME_2, module2); -// moduleRegistry.setModule(MODULE_NAME_3, module3); -// vm.stopPrank(); - -// DataTypes.ModuleData[] memory modules = moduleRegistry.getModules(); -// assertEq(modules.length, 3); - -// // Verify all modules are present (order may vary) -// bool found1 = false; -// bool found2 = false; -// bool found3 = false; - -// for (uint256 i = 0; i < modules.length; i++) { -// if (keccak256(abi.encode(modules[i].moduleName)) == keccak256(abi.encode(MODULE_NAME_1))) { -// assertEq(modules[i].moduleAddress, module1); -// found1 = true; -// } else if (keccak256(abi.encode(modules[i].moduleName)) == keccak256(abi.encode(MODULE_NAME_2))) { -// assertEq(modules[i].moduleAddress, module2); -// found2 = true; -// } else if (keccak256(abi.encode(modules[i].moduleName)) == keccak256(abi.encode(MODULE_NAME_3))) { -// assertEq(modules[i].moduleAddress, module3); -// found3 = true; -// } -// } - -// assertTrue(found1 && found2 && found3, "All modules should be found"); -// } - -// // ============ isModuleWhitelisted Tests ============ - -// function test_IsModuleWhitelisted_WhitelistedModule() public { -// vm.startPrank(owner); -// moduleRegistry.setModule(MODULE_NAME_1, module1); -// vm.stopPrank(); - -// assertTrue(moduleRegistry.isModuleWhitelisted(module1)); -// } - -// function test_IsModuleWhitelisted_NonWhitelistedModule() public view { -// assertFalse(moduleRegistry.isModuleWhitelisted(module1)); -// } - -// function test_IsModuleWhitelisted_ZeroAddress() public view { -// assertFalse(moduleRegistry.isModuleWhitelisted(address(0))); -// } - -// // ============ Integration Tests ============ - -// function test_Integration_FullWorkflow() public { -// vm.startPrank(owner); - -// // Set multiple modules -// moduleRegistry.setModule(MODULE_NAME_1, module1); -// moduleRegistry.setModule(MODULE_NAME_2, module2); -// moduleRegistry.setModule(MODULE_NAME_3, module3); - -// vm.stopPrank(); - -// // Test getModuleByName for all modules -// DataTypes.ModuleData memory data1 = moduleRegistry.getModuleByName(MODULE_NAME_1); -// DataTypes.ModuleData memory data2 = moduleRegistry.getModuleByName(MODULE_NAME_2); -// DataTypes.ModuleData memory data3 = moduleRegistry.getModuleByName(MODULE_NAME_3); - -// assertEq(data1.moduleName, MODULE_NAME_1); -// assertEq(data1.moduleAddress, module1); -// assertEq(data2.moduleName, MODULE_NAME_2); -// assertEq(data2.moduleAddress, module2); -// assertEq(data3.moduleName, MODULE_NAME_3); -// assertEq(data3.moduleAddress, module3); - -// // Test getModuleByAddress for all modules -// DataTypes.ModuleData memory addrData1 = moduleRegistry.getModuleByAddress(module1); -// DataTypes.ModuleData memory addrData2 = moduleRegistry.getModuleByAddress(module2); -// DataTypes.ModuleData memory addrData3 = moduleRegistry.getModuleByAddress(module3); - -// assertEq(addrData1.moduleName, MODULE_NAME_1); -// assertEq(addrData1.moduleAddress, module1); -// assertEq(addrData2.moduleName, MODULE_NAME_2); -// assertEq(addrData2.moduleAddress, module2); -// assertEq(addrData3.moduleName, MODULE_NAME_3); -// assertEq(addrData3.moduleAddress, module3); - -// // Test isModuleWhitelisted for all modules -// assertTrue(moduleRegistry.isModuleWhitelisted(module1)); -// assertTrue(moduleRegistry.isModuleWhitelisted(module2)); -// assertTrue(moduleRegistry.isModuleWhitelisted(module3)); - -// // Test getModules returns all modules -// DataTypes.ModuleData[] memory allModules = moduleRegistry.getModules(); -// assertEq(allModules.length, 3); -// } -// } + function test_GetModuleByName_NonExistentModule() public view { + DataTypes.ModuleData memory moduleData = moduleRegistry.getModuleByName("NonExistentModule"); + assertEq(moduleData.moduleName, ""); + assertEq(moduleData.moduleAddress, address(0)); + } + + function test_GetModuleByName_CaseSensitive() public { + vm.startPrank(owner); + moduleRegistry.setModule(MODULE_NAME_1, module1); + vm.stopPrank(); + + // Different case should return empty data + DataTypes.ModuleData memory moduleData = moduleRegistry.getModuleByName("universaldexmodule"); + assertEq(moduleData.moduleName, ""); + assertEq(moduleData.moduleAddress, address(0)); + } + + // ============ getModuleByAddress Tests ============ + + function test_GetModuleByAddress_ExistingModule() public { + vm.startPrank(owner); + moduleRegistry.setModule(MODULE_NAME_1, module1); + vm.stopPrank(); + + DataTypes.ModuleData memory moduleData = moduleRegistry.getModuleByAddress(module1); + assertEq(moduleData.moduleName, MODULE_NAME_1); + assertEq(moduleData.moduleAddress, module1); + } + + function test_GetModuleByAddress_NonExistentModule() public { + DataTypes.ModuleData memory moduleData = moduleRegistry.getModuleByAddress(makeAddr("nonexistent")); + assertEq(moduleData.moduleName, ""); + assertEq(moduleData.moduleAddress, address(0)); + } + + function test_GetModuleByAddress_ZeroAddress() public view { + DataTypes.ModuleData memory moduleData = moduleRegistry.getModuleByAddress(address(0)); + assertEq(moduleData.moduleName, ""); + assertEq(moduleData.moduleAddress, address(0)); + } + + // ============ getModules Tests ============ + + function test_GetModules_EmptyRegistry() public view { + DataTypes.ModuleData[] memory modules = moduleRegistry.getModules(); + assertEq(modules.length, 0); + } + + function test_GetModules_SingleModule() public { + vm.startPrank(owner); + moduleRegistry.setModule(MODULE_NAME_1, module1); + vm.stopPrank(); + + DataTypes.ModuleData[] memory modules = moduleRegistry.getModules(); + assertEq(modules.length, 1); + assertEq(modules[0].moduleName, MODULE_NAME_1); + assertEq(modules[0].moduleAddress, module1); + } + + function test_GetModules_MultipleModules() public { + vm.startPrank(owner); + moduleRegistry.setModule(MODULE_NAME_1, module1); + moduleRegistry.setModule(MODULE_NAME_2, module2); + moduleRegistry.setModule(MODULE_NAME_3, module3); + vm.stopPrank(); + + DataTypes.ModuleData[] memory modules = moduleRegistry.getModules(); + assertEq(modules.length, 3); + + // Verify all modules are present (order may vary) + bool found1 = false; + bool found2 = false; + bool found3 = false; + + for (uint256 i = 0; i < modules.length; i++) { + if (keccak256(abi.encode(modules[i].moduleName)) == keccak256(abi.encode(MODULE_NAME_1))) { + assertEq(modules[i].moduleAddress, module1); + found1 = true; + } else if (keccak256(abi.encode(modules[i].moduleName)) == keccak256(abi.encode(MODULE_NAME_2))) { + assertEq(modules[i].moduleAddress, module2); + found2 = true; + } else if (keccak256(abi.encode(modules[i].moduleName)) == keccak256(abi.encode(MODULE_NAME_3))) { + assertEq(modules[i].moduleAddress, module3); + found3 = true; + } + } + + assertTrue(found1 && found2 && found3, "All modules should be found"); + } + + // ============ isModuleWhitelisted Tests ============ + + function test_IsModuleWhitelisted_WhitelistedModule() public { + vm.startPrank(owner); + moduleRegistry.setModule(MODULE_NAME_1, module1); + vm.stopPrank(); + + assertTrue(moduleRegistry.isModuleWhitelisted(module1)); + } + + function test_IsModuleWhitelisted_NonWhitelistedModule() public view { + assertFalse(moduleRegistry.isModuleWhitelisted(module1)); + } + + function test_IsModuleWhitelisted_ZeroAddress() public view { + assertFalse(moduleRegistry.isModuleWhitelisted(address(0))); + } + + // ============ Integration Tests ============ + + function test_Integration_FullWorkflow() public { + vm.startPrank(owner); + + // Set multiple modules + moduleRegistry.setModule(MODULE_NAME_1, module1); + moduleRegistry.setModule(MODULE_NAME_2, module2); + moduleRegistry.setModule(MODULE_NAME_3, module3); + + vm.stopPrank(); + + // Test getModuleByName for all modules + DataTypes.ModuleData memory data1 = moduleRegistry.getModuleByName(MODULE_NAME_1); + DataTypes.ModuleData memory data2 = moduleRegistry.getModuleByName(MODULE_NAME_2); + DataTypes.ModuleData memory data3 = moduleRegistry.getModuleByName(MODULE_NAME_3); + + assertEq(data1.moduleName, MODULE_NAME_1); + assertEq(data1.moduleAddress, module1); + assertEq(data2.moduleName, MODULE_NAME_2); + assertEq(data2.moduleAddress, module2); + assertEq(data3.moduleName, MODULE_NAME_3); + assertEq(data3.moduleAddress, module3); + + // Test getModuleByAddress for all modules + DataTypes.ModuleData memory addrData1 = moduleRegistry.getModuleByAddress(module1); + DataTypes.ModuleData memory addrData2 = moduleRegistry.getModuleByAddress(module2); + DataTypes.ModuleData memory addrData3 = moduleRegistry.getModuleByAddress(module3); + + assertEq(addrData1.moduleName, MODULE_NAME_1); + assertEq(addrData1.moduleAddress, module1); + assertEq(addrData2.moduleName, MODULE_NAME_2); + assertEq(addrData2.moduleAddress, module2); + assertEq(addrData3.moduleName, MODULE_NAME_3); + assertEq(addrData3.moduleAddress, module3); + + // Test isModuleWhitelisted for all modules + assertTrue(moduleRegistry.isModuleWhitelisted(module1)); + assertTrue(moduleRegistry.isModuleWhitelisted(module2)); + assertTrue(moduleRegistry.isModuleWhitelisted(module3)); + + // Test getModules returns all modules + DataTypes.ModuleData[] memory allModules = moduleRegistry.getModules(); + assertEq(allModules.length, 3); + } +} diff --git a/test/core/unit/Superloop.t.sol b/test/core/unit/Superloop.t.sol index 5c742a5..d571f30 100644 --- a/test/core/unit/Superloop.t.sol +++ b/test/core/unit/Superloop.t.sol @@ -1,455 +1,455 @@ -// // SPDX-License-Identifier: MIT - -// pragma solidity ^0.8.13; - -// import {console} from "forge-std/console.sol"; -// import {TestBase} from "../TestBase.sol"; -// import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; -// import {DataTypes} from "../../../src/common/DataTypes.sol"; -// import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; -// import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -// import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -// import {Errors} from "../../../src/common/Errors.sol"; - -// contract SuperloopTest is TestBase { -// Superloop public superloopImplementation; -// ProxyAdmin public proxyAdmin; -// address public user1; -// address public user2; - -// function setUp() public override { -// super.setUp(); - -// user1 = makeAddr("user1"); -// user2 = makeAddr("user2"); - -// vm.startPrank(admin); -// _deployModules(); - -// address[] memory modules = new address[](5); -// modules[0] = address(supplyModule); -// modules[1] = address(withdrawModule); -// modules[2] = address(borrowModule); -// modules[3] = address(repayModule); -// modules[4] = address(dexModule); - -// DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ -// asset: XTZ, -// name: "XTZ Vault", -// symbol: "XTZV", -// supplyCap: 100000 * 10 ** 18, -// minimumDepositAmount: 100, -// instantWithdrawFee: 0, -// superloopModuleRegistry: address(moduleRegistry), -// modules: modules, -// accountant: mockModule, -// withdrawManager: mockModule, -// depositManager: mockModule, -// cashReserve: 1000, -// vaultAdmin: admin, -// treasury: treasury, -// vaultOperator: admin -// }); -// superloopImplementation = new Superloop(); -// proxyAdmin = new ProxyAdmin(address(this)); - -// TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( -// address(superloopImplementation), -// address(proxyAdmin), -// abi.encodeWithSelector(Superloop.initialize.selector, initData) -// ); -// superloop = Superloop(payable(address(proxy))); - -// _deployAccountant(address(superloop), _singleAddressArray(ST_XTZ), _singleAddressArray(XTZ)); -// _deployWithdrawManager(address(superloop)); - -// superloop.setAccountantModule(address(accountantAaveV3)); -// superloop.setWithdrawManagerModule(address(withdrawManager)); - -// vm.stopPrank(); - -// vm.label(address(superloop), "superloop"); -// vm.label(user1, "user1"); -// vm.label(user2, "user2"); -// } - -// // ============ Superloop.sol Tests ============ - -// function test_Initialize() public { -// // Test that initialization works correctly -// address[] memory modules = new address[](1); -// modules[0] = address(supplyModule); - -// DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ -// asset: XTZ, -// name: "Test Vault", -// symbol: "TEST", -// supplyCap: 1000 * 10 ** 18, -// minimumDepositAmount: 100, -// instantWithdrawFee: 0, -// superloopModuleRegistry: address(moduleRegistry), -// modules: modules, -// accountant: mockModule, -// withdrawManager: mockModule, -// depositManager: mockModule, -// cashReserve: 1000, -// vaultAdmin: admin, -// treasury: treasury, -// vaultOperator: admin -// }); - -// Superloop newImplementation = new Superloop(); -// ProxyAdmin newProxyAdmin = new ProxyAdmin(address(this)); - -// TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( -// address(newImplementation), -// address(newProxyAdmin), -// abi.encodeWithSelector(Superloop.initialize.selector, initData) -// ); - -// Superloop newSuperloop = Superloop(payable(address(proxy))); - -// // Test that the contract is properly initialized -// assertEq(newSuperloop.name(), "Test Vault"); -// assertEq(newSuperloop.symbol(), "TEST"); -// assertEq(newSuperloop.asset(), XTZ); -// } - -// function test_InitializeRevertIfAlreadyInitialized() public { -// // Test that initialize reverts if called again -// address[] memory modules = new address[](1); -// modules[0] = address(supplyModule); - -// DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ -// asset: XTZ, -// name: "Test Vault", -// symbol: "TEST", -// supplyCap: 1000 * 10 ** 18, -// minimumDepositAmount: 100, -// instantWithdrawFee: 0, -// superloopModuleRegistry: address(moduleRegistry), -// modules: modules, -// accountant: mockModule, -// withdrawManager: mockModule, -// depositManager: mockModule, -// cashReserve: 1000, -// vaultAdmin: admin, -// treasury: treasury, -// vaultOperator: admin -// }); - -// vm.expectRevert(); -// superloop.initialize(initData); -// } - -// function test_InitializeRevertIfInvalidModule() public { -// // Test that initialization reverts if module is not whitelisted -// address[] memory modules = new address[](1); -// modules[0] = address(0x123); // Invalid module - -// DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ -// asset: XTZ, -// name: "Test Vault", -// symbol: "TEST", -// supplyCap: 1000 * 10 ** 18, -// minimumDepositAmount: 100, -// instantWithdrawFee: 0, -// superloopModuleRegistry: address(moduleRegistry), -// modules: modules, -// accountant: mockModule, -// withdrawManager: mockModule, -// depositManager: mockModule, -// cashReserve: 1000, -// vaultAdmin: admin, -// treasury: treasury, -// vaultOperator: admin -// }); - -// Superloop newImplementation = new Superloop(); -// ProxyAdmin newProxyAdmin = new ProxyAdmin(address(this)); - -// vm.expectRevert(bytes(Errors.INVALID_MODULE)); -// new TransparentUpgradeableProxy( -// address(newImplementation), -// address(newProxyAdmin), -// abi.encodeWithSelector(Superloop.initialize.selector, initData) -// ); -// } - -// function test_ConstructorDisablesInitializers() public { -// // Test that the constructor properly disables initializers -// Superloop newContract = new Superloop(); - -// // Should revert if we try to initialize directly -// address[] memory modules = new address[](1); -// modules[0] = address(supplyModule); - -// DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ -// asset: XTZ, -// name: "Test Vault", -// symbol: "TEST", -// supplyCap: 1000 * 10 ** 18, -// minimumDepositAmount: 100, -// instantWithdrawFee: 100, -// superloopModuleRegistry: address(moduleRegistry), -// modules: modules, -// accountant: mockModule, -// withdrawManager: mockModule, -// depositManager: mockModule, -// cashReserve: 1000, -// vaultAdmin: admin, -// treasury: treasury, -// vaultOperator: admin -// }); - -// vm.expectRevert(); -// newContract.initialize(initData); -// } - -// // ============ SuperloopBase.sol Tests ============ - -// function test_SetSupplyCapRevertIfNotAdmin() public { -// uint256 newSupplyCap = 50000 * 10 ** 18; - -// vm.prank(user1); -// vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); -// superloop.setSupplyCap(newSupplyCap); -// } - -// function test_SetSuperloopModuleRegistry() public { -// address newRegistry = makeAddr("newRegistry"); - -// vm.prank(admin); -// superloop.setSuperloopModuleRegistry(newRegistry); - -// // Note: We can't directly test the storage change, but we can verify it doesn't revert -// } - -// function test_SetSuperloopModuleRegistryRevertIfNotAdmin() public { -// address newRegistry = makeAddr("newRegistry"); - -// vm.prank(user1); -// vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); -// superloop.setSuperloopModuleRegistry(newRegistry); -// } - -// function test_SetRegisteredModule() public { -// address newModule = makeAddr("newModule"); - -// vm.prank(admin); -// superloop.setRegisteredModule(newModule, true); - -// // Note: We can't directly test the storage change, but we can verify it doesn't revert -// } - -// function test_SetRegisteredModuleRevertIfNotAdmin() public { -// address newModule = makeAddr("newModule"); - -// vm.prank(user1); -// vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); -// superloop.setRegisteredModule(newModule, true); -// } - -// function test_SetCallbackHandler() public { -// bytes32 key = keccak256(abi.encodePacked(address(0x123), bytes4(0x12345678))); -// address handler = makeAddr("handler"); - -// vm.prank(admin); -// superloop.setCallbackHandler(key, handler); - -// // Note: We can't directly test the storage change, but we can verify it doesn't revert -// } +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.13; + +import {console} from "forge-std/console.sol"; +import {TestBase} from "../TestBase.sol"; +import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; +import {DataTypes} from "../../../src/common/DataTypes.sol"; +import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {Errors} from "../../../src/common/Errors.sol"; + +contract SuperloopTest is TestBase { + Superloop public superloopImplementation; + ProxyAdmin public proxyAdmin; + address public user1; + address public user2; + + function setUp() public override { + super.setUp(); + + user1 = makeAddr("user1"); + user2 = makeAddr("user2"); + + vm.startPrank(admin); + _deployModules(); + + address[] memory modules = new address[](5); + modules[0] = address(supplyModule); + modules[1] = address(withdrawModule); + modules[2] = address(borrowModule); + modules[3] = address(repayModule); + modules[4] = address(dexModule); + + DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ + asset: environment.vaultAsset, + name: "Vault", + symbol: "VLT", + supplyCap: 100000 * 10 ** environment.vaultAssetDecimals, + minimumDepositAmount: 100, + instantWithdrawFee: 0, + superloopModuleRegistry: address(moduleRegistry), + modules: modules, + accountant: mockModule, + withdrawManager: mockModule, + depositManager: mockModule, + cashReserve: 1000, + vaultAdmin: admin, + treasury: treasury, + vaultOperator: admin + }); + superloopImplementation = new Superloop(); + proxyAdmin = new ProxyAdmin(address(this)); + + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(superloopImplementation), + address(proxyAdmin), + abi.encodeWithSelector(Superloop.initialize.selector, initData) + ); + superloop = Superloop(payable(address(proxy))); + + _deployAccountant(address(superloop), environment.lendAssets, environment.borrowAssets); + _deployWithdrawManager(address(superloop)); + + superloop.setAccountantModule(address(accountantAaveV3)); + superloop.setWithdrawManagerModule(address(withdrawManager)); + + vm.stopPrank(); + + vm.label(address(superloop), "superloop"); + vm.label(user1, "user1"); + vm.label(user2, "user2"); + } + + // ============ Superloop.sol Tests ============ + + function test_Initialize() public { + // Test that initialization works correctly + address[] memory modules = new address[](1); + modules[0] = address(supplyModule); + + DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ + asset: environment.vaultAsset, + name: "Test Vault", + symbol: "TEST", + supplyCap: 1000 * 10 ** 18, + minimumDepositAmount: 100, + instantWithdrawFee: 0, + superloopModuleRegistry: address(moduleRegistry), + modules: modules, + accountant: mockModule, + withdrawManager: mockModule, + depositManager: mockModule, + cashReserve: 1000, + vaultAdmin: admin, + treasury: treasury, + vaultOperator: admin + }); + + Superloop newImplementation = new Superloop(); + ProxyAdmin newProxyAdmin = new ProxyAdmin(address(this)); + + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(newImplementation), + address(newProxyAdmin), + abi.encodeWithSelector(Superloop.initialize.selector, initData) + ); + + Superloop newSuperloop = Superloop(payable(address(proxy))); + + // Test that the contract is properly initialized + assertEq(newSuperloop.name(), "Test Vault"); + assertEq(newSuperloop.symbol(), "TEST"); + assertEq(newSuperloop.asset(), environment.vaultAsset); + } + + function test_InitializeRevertIfAlreadyInitialized() public { + // Test that initialize reverts if called again + address[] memory modules = new address[](1); + modules[0] = address(supplyModule); + + DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ + asset: environment.vaultAsset, + name: "Test Vault", + symbol: "TEST", + supplyCap: 1000 * 10 ** 18, + minimumDepositAmount: 100, + instantWithdrawFee: 0, + superloopModuleRegistry: address(moduleRegistry), + modules: modules, + accountant: mockModule, + withdrawManager: mockModule, + depositManager: mockModule, + cashReserve: 1000, + vaultAdmin: admin, + treasury: treasury, + vaultOperator: admin + }); + + vm.expectRevert(); + superloop.initialize(initData); + } + + function test_InitializeRevertIfInvalidModule() public { + // Test that initialization reverts if module is not whitelisted + address[] memory modules = new address[](1); + modules[0] = address(0x123); // Invalid module + + DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ + asset: environment.vaultAsset, + name: "Test Vault", + symbol: "TEST", + supplyCap: 1000 * 10 ** 18, + minimumDepositAmount: 100, + instantWithdrawFee: 0, + superloopModuleRegistry: address(moduleRegistry), + modules: modules, + accountant: mockModule, + withdrawManager: mockModule, + depositManager: mockModule, + cashReserve: 1000, + vaultAdmin: admin, + treasury: treasury, + vaultOperator: admin + }); + + Superloop newImplementation = new Superloop(); + ProxyAdmin newProxyAdmin = new ProxyAdmin(address(this)); + + vm.expectRevert(bytes(Errors.INVALID_MODULE)); + new TransparentUpgradeableProxy( + address(newImplementation), + address(newProxyAdmin), + abi.encodeWithSelector(Superloop.initialize.selector, initData) + ); + } + + function test_ConstructorDisablesInitializers() public { + // Test that the constructor properly disables initializers + Superloop newContract = new Superloop(); + + // Should revert if we try to initialize directly + address[] memory modules = new address[](1); + modules[0] = address(supplyModule); + + DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ + asset: environment.vaultAsset, + name: "Test Vault", + symbol: "TEST", + supplyCap: 1000 * 10 ** 18, + minimumDepositAmount: 100, + instantWithdrawFee: 100, + superloopModuleRegistry: address(moduleRegistry), + modules: modules, + accountant: mockModule, + withdrawManager: mockModule, + depositManager: mockModule, + cashReserve: 1000, + vaultAdmin: admin, + treasury: treasury, + vaultOperator: admin + }); + + vm.expectRevert(); + newContract.initialize(initData); + } + + // ============ SuperloopBase.sol Tests ============ + + function test_SetSupplyCapRevertIfNotAdmin() public { + uint256 newSupplyCap = 50000 * 10 ** 18; + + vm.prank(user1); + vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); + superloop.setSupplyCap(newSupplyCap); + } + + function test_SetSuperloopModuleRegistry() public { + address newRegistry = makeAddr("newRegistry"); + + vm.prank(admin); + superloop.setSuperloopModuleRegistry(newRegistry); + + // Note: We can't directly test the storage change, but we can verify it doesn't revert + } + + function test_SetSuperloopModuleRegistryRevertIfNotAdmin() public { + address newRegistry = makeAddr("newRegistry"); + + vm.prank(user1); + vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); + superloop.setSuperloopModuleRegistry(newRegistry); + } + + function test_SetRegisteredModule() public { + address newModule = makeAddr("newModule"); + + vm.prank(admin); + superloop.setRegisteredModule(newModule, true); + + // Note: We can't directly test the storage change, but we can verify it doesn't revert + } + + function test_SetRegisteredModuleRevertIfNotAdmin() public { + address newModule = makeAddr("newModule"); + + vm.prank(user1); + vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); + superloop.setRegisteredModule(newModule, true); + } + + function test_SetCallbackHandler() public { + bytes32 key = keccak256(abi.encodePacked(address(0x123), bytes4(0x12345678))); + address handler = makeAddr("handler"); + + vm.prank(admin); + superloop.setCallbackHandler(key, handler); + + // Note: We can't directly test the storage change, but we can verify it doesn't revert + } -// function test_SetCallbackHandlerRevertIfNotAdmin() public { -// bytes32 key = keccak256(abi.encodePacked(address(0x123), bytes4(0x12345678))); -// address handler = makeAddr("handler"); + function test_SetCallbackHandlerRevertIfNotAdmin() public { + bytes32 key = keccak256(abi.encodePacked(address(0x123), bytes4(0x12345678))); + address handler = makeAddr("handler"); -// vm.prank(user1); -// vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); -// superloop.setCallbackHandler(key, handler); -// } + vm.prank(user1); + vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); + superloop.setCallbackHandler(key, handler); + } -// function test_SetAccountantModule() public { -// address newAccountant = makeAddr("newAccountant"); + function test_SetAccountantModule() public { + address newAccountant = makeAddr("newAccountant"); -// vm.prank(admin); -// superloop.setAccountantModule(newAccountant); + vm.prank(admin); + superloop.setAccountantModule(newAccountant); -// // Note: We can't directly test the storage change, but we can verify it doesn't revert -// } + // Note: We can't directly test the storage change, but we can verify it doesn't revert + } -// function test_SetAccountantModuleRevertIfNotAdmin() public { -// address newAccountant = makeAddr("newAccountant"); + function test_SetAccountantModuleRevertIfNotAdmin() public { + address newAccountant = makeAddr("newAccountant"); -// vm.prank(user1); -// vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); -// superloop.setAccountantModule(newAccountant); -// } + vm.prank(user1); + vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); + superloop.setAccountantModule(newAccountant); + } -// function test_SetWithdrawManagerModule() public { -// address newWithdrawManager = makeAddr("newWithdrawManager"); + function test_SetWithdrawManagerModule() public { + address newWithdrawManager = makeAddr("newWithdrawManager"); -// vm.prank(admin); -// superloop.setWithdrawManagerModule(newWithdrawManager); + vm.prank(admin); + superloop.setWithdrawManagerModule(newWithdrawManager); -// // Note: We can't directly test the storage change, but we can verify it doesn't revert -// } + // Note: We can't directly test the storage change, but we can verify it doesn't revert + } -// function test_SetWithdrawManagerModuleRevertIfNotAdmin() public { -// address newWithdrawManager = makeAddr("newWithdrawManager"); + function test_SetWithdrawManagerModuleRevertIfNotAdmin() public { + address newWithdrawManager = makeAddr("newWithdrawManager"); -// vm.prank(user1); -// vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); -// superloop.setWithdrawManagerModule(newWithdrawManager); -// } + vm.prank(user1); + vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); + superloop.setWithdrawManagerModule(newWithdrawManager); + } -// function test_SetVaultAdmin() public { -// address newAdmin = makeAddr("newAdmin"); + function test_SetVaultAdmin() public { + address newAdmin = makeAddr("newAdmin"); -// vm.prank(admin); -// superloop.setVaultAdmin(newAdmin); + vm.prank(admin); + superloop.setVaultAdmin(newAdmin); -// // Test that new admin can call admin functions -// vm.prank(newAdmin); -// superloop.setSupplyCap(1000 * 10 ** 18); -// } + // Test that new admin can call admin functions + vm.prank(newAdmin); + superloop.setSupplyCap(1000 * 10 ** 18); + } -// function test_SetVaultAdminRevertIfNotAdmin() public { -// address newAdmin = makeAddr("newAdmin"); + function test_SetVaultAdminRevertIfNotAdmin() public { + address newAdmin = makeAddr("newAdmin"); -// vm.prank(user1); -// vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); -// superloop.setVaultAdmin(newAdmin); -// } + vm.prank(user1); + vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); + superloop.setVaultAdmin(newAdmin); + } -// function test_SetTreasury() public { -// address newTreasury = makeAddr("newTreasury"); + function test_SetTreasury() public { + address newTreasury = makeAddr("newTreasury"); -// vm.prank(admin); -// superloop.setTreasury(newTreasury); + vm.prank(admin); + superloop.setTreasury(newTreasury); -// // Note: We can't directly test the storage change, but we can verify it doesn't revert -// } + // Note: We can't directly test the storage change, but we can verify it doesn't revert + } -// function test_SetTreasuryRevertIfNotAdmin() public { -// address newTreasury = makeAddr("newTreasury"); + function test_SetTreasuryRevertIfNotAdmin() public { + address newTreasury = makeAddr("newTreasury"); -// vm.prank(user1); -// vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); -// superloop.setTreasury(newTreasury); -// } + vm.prank(user1); + vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); + superloop.setTreasury(newTreasury); + } -// function test_SetPrivilegedAddressRevertIfNotAdmin() public { -// address privilegedUser = makeAddr("privilegedUser"); + function test_SetPrivilegedAddressRevertIfNotAdmin() public { + address privilegedUser = makeAddr("privilegedUser"); -// vm.prank(user1); -// vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); -// superloop.setPrivilegedAddress(privilegedUser, true); -// } + vm.prank(user1); + vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); + superloop.setPrivilegedAddress(privilegedUser, true); + } -// function test_SetInstantWithdrawFeeRevertIfNotAdmin() public { -// uint256 newInstantWithdrawFee = 100; + function test_SetInstantWithdrawFeeRevertIfNotAdmin() public { + uint256 newInstantWithdrawFee = 100; -// vm.prank(user1); -// vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); -// superloop.setInstantWithdrawFee(newInstantWithdrawFee); -// } + vm.prank(user1); + vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); + superloop.setInstantWithdrawFee(newInstantWithdrawFee); + } -// function test_SetInstantWithdrawFeeRevertIfInvalidFee() public { -// uint256 newInstantWithdrawFee = 1000; + function test_SetInstantWithdrawFeeRevertIfInvalidFee() public { + uint256 newInstantWithdrawFee = 1000; -// vm.prank(admin); -// vm.expectRevert(bytes(Errors.INVALID_INSTANT_WITHDRAW_FEE)); -// superloop.setInstantWithdrawFee(newInstantWithdrawFee); -// } + vm.prank(admin); + vm.expectRevert(bytes(Errors.INVALID_INSTANT_WITHDRAW_FEE)); + superloop.setInstantWithdrawFee(newInstantWithdrawFee); + } -// function test_SetInstantWithdrawFee() public { -// uint256 newInstantWithdrawFee = 100; + function test_SetInstantWithdrawFee() public { + uint256 newInstantWithdrawFee = 100; -// vm.prank(admin); -// superloop.setInstantWithdrawFee(newInstantWithdrawFee); + vm.prank(admin); + superloop.setInstantWithdrawFee(newInstantWithdrawFee); -// assertEq(superloop.instantWithdrawFee(), newInstantWithdrawFee); -// } + assertEq(superloop.instantWithdrawFee(), newInstantWithdrawFee); + } -// // ============ SuperloopVault.sol Tests ============ + // ============ SuperloopVault.sol Tests ============ -// function test_TotalAssets() public { -// // Mock the accountant module to return a specific value -// vm.mockCall( -// address(accountantAaveV3), -// abi.encodeWithSelector(accountantAaveV3.getTotalAssets.selector), -// abi.encode(1000 * 10 ** 18) -// ); - -// uint256 totalAssets = superloop.totalAssets(); -// assertEq(totalAssets, 1000 * 10 ** 18); -// } + function test_TotalAssets() public { + // Mock the accountant module to return a specific value + vm.mockCall( + address(accountantAaveV3), + abi.encodeWithSelector(accountantAaveV3.getTotalAssets.selector), + abi.encode(1000 * 10 ** 18) + ); + + uint256 totalAssets = superloop.totalAssets(); + assertEq(totalAssets, 1000 * 10 ** 18); + } -// function test_MaxDepositWithSupplyCap() public { -// // Set a supply cap -// vm.prank(admin); -// superloop.setSupplyCap(1000 * 10 ** 18); + function test_MaxDepositWithSupplyCap() public { + // Set a supply cap + vm.prank(admin); + superloop.setSupplyCap(1000 * 10 ** 18); -// // Mock total assets to be less than supply cap -// vm.mockCall( -// address(accountantAaveV3), -// abi.encodeWithSelector(accountantAaveV3.getTotalAssets.selector), -// abi.encode(500 * 10 ** 18) -// ); - -// uint256 maxDeposit = superloop.maxDeposit(address(0)); -// assertEq(maxDeposit, 500 * 10 ** 18); -// } - -// function test_MaxDepositExceedsSupplyCap() public { -// // Set a supply cap -// vm.prank(admin); -// superloop.setSupplyCap(1000 * 10 ** 18); - -// // Mock total assets to exceed supply cap -// vm.mockCall( -// address(accountantAaveV3), -// abi.encodeWithSelector(accountantAaveV3.getTotalAssets.selector), -// abi.encode(1500 * 10 ** 18) -// ); - -// uint256 maxDeposit = superloop.maxDeposit(address(0)); -// assertEq(maxDeposit, 0); -// } - -// function test_DecimalsOffset() public view { -// // Test that decimals offset is correctly applied -// // The offset should be 2 as defined in SuperloopStorage -// assertEq(superloop.decimals(), 20); // Standard ERC20 decimals -// } - -// // ============ Integration Tests ============ - -// function test_skimRevertIfInvalidAsset() public { -// deal(XTZ, address(superloop), 1000 * 10 ** 18); - -// vm.expectRevert(bytes(Errors.INVALID_SKIM_ASSET)); - -// vm.prank(admin); -// superloop.skim(XTZ); -// } - -// function test_skim() public { -// deal(ST_XTZ, address(superloop), 1000 * 10 ** 18); - -// vm.prank(admin); -// superloop.skim(ST_XTZ); - -// assertEq(IERC20(ST_XTZ).balanceOf(treasury), 1000 * 10 ** 18); -// assertEq(IERC20(ST_XTZ).balanceOf(address(superloop)), 0); -// } - -// function _seed() internal { -// vm.startPrank(admin); -// deal(XTZ, admin, 1000 * 10 ** 18); -// IERC20(XTZ).approve(address(superloop), type(uint256).max); -// superloop.deposit(100 * 10 ** 18, admin); -// vm.stopPrank(); -// } -// } + // Mock total assets to be less than supply cap + vm.mockCall( + address(accountantAaveV3), + abi.encodeWithSelector(accountantAaveV3.getTotalAssets.selector), + abi.encode(500 * 10 ** 18) + ); + + uint256 maxDeposit = superloop.maxDeposit(address(0)); + assertEq(maxDeposit, 500 * 10 ** 18); + } + + function test_MaxDepositExceedsSupplyCap() public { + // Set a supply cap + vm.prank(admin); + superloop.setSupplyCap(1000 * 10 ** 18); + + // Mock total assets to exceed supply cap + vm.mockCall( + address(accountantAaveV3), + abi.encodeWithSelector(accountantAaveV3.getTotalAssets.selector), + abi.encode(1500 * 10 ** 18) + ); + + uint256 maxDeposit = superloop.maxDeposit(address(0)); + assertEq(maxDeposit, 0); + } + + function test_DecimalsOffset() public view { + // Test that decimals offset is correctly applied + // The offset should be 2 as defined in SuperloopStorage + assertEq(superloop.decimals(), 20); // Standard ERC20 decimals + } + + // ============ Integration Tests ============ + + function test_skimRevertIfInvalidAsset() public { + deal(environment.vaultAsset, address(superloop), 1000 * 10 ** 18); + + vm.expectRevert(bytes(Errors.INVALID_SKIM_ASSET)); + + vm.prank(admin); + superloop.skim(environment.vaultAsset); + } + + function test_skim() public { + deal(environment.borrowAssets[0], address(superloop), 1000 * 10 ** 18); + + vm.prank(admin); + superloop.skim(environment.borrowAssets[0]); + + assertEq(IERC20(environment.borrowAssets[0]).balanceOf(treasury), 1000 * 10 ** 18); + assertEq(IERC20(environment.borrowAssets[0]).balanceOf(address(superloop)), 0); + } + + function _seed() internal { + vm.startPrank(admin); + deal(environment.vaultAsset, admin, 1000 * 10 ** 18); + IERC20(environment.vaultAsset).approve(address(superloop), type(uint256).max); + superloop.deposit(100 * 10 ** 18, admin); + vm.stopPrank(); + } +} diff --git a/test/core/unit/UniversalAccountant.t.sol b/test/core/unit/UniversalAccountant.t.sol index 5e4137e..2d81e03 100644 --- a/test/core/unit/UniversalAccountant.t.sol +++ b/test/core/unit/UniversalAccountant.t.sol @@ -1,267 +1,262 @@ -// // SPDX-License-Identifier: MIT - -// pragma solidity ^0.8.13; - -// import {TestBase} from "../TestBase.sol"; -// import {console} from "forge-std/console.sol"; -// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -// import {MockVault} from "../../../src/mock/MockVault.sol"; -// import {UniversalAccountant} from "../../../src/core/Accountant/universalAccountant/UniversalAccountant.sol"; -// import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -// import {DataTypes} from "../../../src/common/DataTypes.sol"; -// import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; -// import {IPoolAddressesProvider} from "aave-v3-core/contracts/interfaces/IPoolAddressesProvider.sol"; -// import {IPoolDataProvider} from "aave-v3-core/contracts/interfaces/IPoolDataProvider.sol"; -// import {IAaveOracle} from "aave-v3-core/contracts/interfaces/IAaveOracle.sol"; -// import {IPriceOracleGetter} from "aave-v3-core/contracts/interfaces/IPriceOracleGetter.sol"; -// import {AaveV3AccountantPlugin} from "../../../src/plugins/Accountant/AaveV3AccountantPlugin.sol"; - -// contract AccountantAaveV3Test is TestBase { -// UniversalAccountant public accountantImplementation; -// AaveV3AccountantPlugin public accountantPlugin; - -// IERC20 public asset; -// MockVault public vault; - -// // Test data -// uint256 public constant INITIAL_WHALE_BALANCE = 1000 ether; - -// function setUp() public override { -// super.setUp(); - -// asset = IERC20(XTZ); -// vault = new MockVault(asset, "Mock Vault", "mVLT"); - -// address[] memory lendAssets = new address[](1); -// lendAssets[0] = ST_XTZ; -// address[] memory borrowAssets = new address[](1); -// borrowAssets[0] = XTZ; - -// // deploy accountant plugin -// DataTypes.AaveV3AccountantPluginModuleInitData memory accountantPluginInitData = DataTypes -// .AaveV3AccountantPluginModuleInitData({ -// poolAddressesProvider: AAVE_V3_POOL_ADDRESSES_PROVIDER, -// lendAssets: lendAssets, -// borrowAssets: borrowAssets -// }); -// accountantPlugin = new AaveV3AccountantPlugin(accountantPluginInitData); - -// address[] memory registeredAccountants = new address[](1); -// registeredAccountants[0] = address(accountantPlugin); - -// // deploy accountant -// DataTypes.UniversalAccountantModuleInitData memory initData = DataTypes.UniversalAccountantModuleInitData({ -// registeredAccountants: registeredAccountants, -// performanceFee: uint16(PERFORMANCE_FEE), -// vault: address(vault) -// }); - -// accountantImplementation = new UniversalAccountant(); -// TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( -// address(accountantImplementation), -// address(this), -// abi.encodeWithSelector(UniversalAccountant.initialize.selector, initData) -// ); - -// accountant = UniversalAccountant(address(proxy)); - -// // Fund the accountant with XTZ from whale -// vm.startPrank(XTZ_WHALE); -// asset.transfer(address(vault), INITIAL_WHALE_BALANCE); -// vm.stopPrank(); -// } - -// function test_Initialize() public view { -// assertEq(accountant.getTotalAssets(), INITIAL_WHALE_BALANCE); -// assertEq(accountant.performanceFee(), PERFORMANCE_FEE); -// assertEq(accountant.vault(), address(vault)); -// assertEq(accountant.registeredAccountants()[0], address(accountantPlugin)); -// } - -// function test_InitializeRevertIfAlreadyInitialized() public { -// // Test that initialize reverts if called again -// address[] memory lendAssets = new address[](1); -// lendAssets[0] = ST_XTZ; -// address[] memory borrowAssets = new address[](1); -// borrowAssets[0] = XTZ; - -// address[] memory registeredAccountants = new address[](1); -// registeredAccountants[0] = address(accountantPlugin); - -// DataTypes.UniversalAccountantModuleInitData memory initData = DataTypes.UniversalAccountantModuleInitData({ -// registeredAccountants: registeredAccountants, -// performanceFee: uint16(PERFORMANCE_FEE), -// vault: address(vault) -// }); - -// vm.expectRevert(); -// accountant.initialize(initData); -// } - -// function test_GetTotalAssets() public view { -// // Test getTotalAssets calculation using actual fork data -// uint256 totalAssets = accountant.getTotalAssets(); - -// // Should return the base asset balance since there are no lend/borrow positions yet -// assertEq(totalAssets, INITIAL_WHALE_BALANCE); - -// console.log("Total assets:", totalAssets); -// console.log("Expected assets:", INITIAL_WHALE_BALANCE); -// } - -// function test_GetTotalAssetsWithMultipleAssets() public { -// // mock pool data provider and price oracle -// _enableMockingPoolDataProvider(); -// _enableMockingPriceOracle(1.01e8, 1e8); - -// uint256 totalAssets = accountant.getTotalAssets(); - -// // Calculate expected total assets based on mock data: -// // Lend assets: 1000 ether * 1.01e8 (ST_XTZ price) = 1010e8 -// // Borrow assets: -800 ether * 1e8 (XTZ price) = -800e8 -// // Base asset balance: 1000 ether * 1e8 (XTZ price) = 1000e8 -// // Total in market reference currency: 1010e8 - 800e8 + 1000e8 = 1210e8 -// // Convert to base asset: 1210e18 / 1e18 = 1210 ether -// uint256 expectedTotalAssets = 1210 ether; - -// assertEq(totalAssets, expectedTotalAssets); -// console.log("Total assets with multiple assets:", totalAssets); -// console.log("Expected total assets:", expectedTotalAssets); -// } - -// function test_GetPerformanceFee() public { -// uint256 totalShares = 1000 ether; -// uint256 exchangeRate = 1.1e18; // 10% increase -// uint256 lastRealizedFeeExchangeRate = 1.0e18; -// uint256 totalSupply = 1000 ether; - -// // Set the last realized fee exchange rate -// vm.prank(address(vault)); -// accountant.setLastRealizedFeeExchangeRate(lastRealizedFeeExchangeRate, totalSupply); - -// // Calculate expected performance fee -// uint256 latestAssetAmount = totalShares * exchangeRate; -// uint256 prevAssetAmount = totalShares * lastRealizedFeeExchangeRate; -// uint256 interestGenerated = latestAssetAmount - prevAssetAmount; -// uint256 expectedPerformanceFee = (interestGenerated * PERFORMANCE_FEE) / (10000 * 1e18); - -// vm.prank(address(vault)); -// uint256 actualPerformanceFee = accountant.getPerformanceFee(totalShares, exchangeRate, 18); - -// console.log("Performance fee:", actualPerformanceFee); -// console.log("Expected fee:", expectedPerformanceFee); - -// assertEq(actualPerformanceFee, expectedPerformanceFee); -// } - -// function test_GetPerformanceFeeNoInterest() public { -// uint256 totalShares = 1000 ether; -// uint256 exchangeRate = 0.9e18; // 10% decrease -// uint256 lastRealizedFeeExchangeRate = 1.0e18; -// uint256 totalSupply = 1000 ether; -// // Set the last realized fee exchange rate -// vm.prank(address(vault)); -// accountant.setLastRealizedFeeExchangeRate(lastRealizedFeeExchangeRate, totalSupply); - -// vm.prank(address(vault)); -// uint256 performanceFee = accountant.getPerformanceFee(totalShares, exchangeRate, 18); - -// // Should return 0 when there's no interest (exchange rate decreased) -// assertEq(performanceFee, 0); -// } - -// function test_GetPerformanceFeeRevertIfNotVault() public { -// uint256 totalShares = 1000 ether; -// uint256 exchangeRate = 1.1e18; - -// vm.expectRevert(); -// accountant.getPerformanceFee(totalShares, exchangeRate, 18); -// } - -// function test_SetLastRealizedFeeExchangeRate() public { -// uint256 newExchangeRate = 1.2e18; -// uint256 totalSupply = 1000 ether; - -// vm.prank(address(vault)); -// accountant.setLastRealizedFeeExchangeRate(newExchangeRate, totalSupply); - -// // Test that the exchange rate was set correctly by calling getPerformanceFee -// vm.prank(address(vault)); -// uint256 performanceFee = accountant.getPerformanceFee(1000 ether, 1.3e18, 18); - -// // Should calculate based on the new exchange rate -// uint256 expectedInterest = 1000 ether * 1.3e18 - 1000 ether * newExchangeRate; -// uint256 expectedFee = (expectedInterest * PERFORMANCE_FEE) / (10000 * 1e18); - -// assertEq(performanceFee, expectedFee); -// } - -// function test_SetLastRealizedFeeExchangeRateRevertIfNotVault() public { -// uint256 newExchangeRate = 1.2e18; -// uint256 totalSupply = 1000 ether; - -// vm.expectRevert(); -// accountant.setLastRealizedFeeExchangeRate(newExchangeRate, totalSupply); -// } - -// function test_GetTotalAssetsWithZeroBalances() public view { -// // Test getTotalAssets when there are no lend/borrow positions -// // This should be the default state since we haven't interacted with Aave V3 -// uint256 totalAssets = accountant.getTotalAssets(); - -// // Should only include base asset balance -// assertEq(totalAssets, INITIAL_WHALE_BALANCE); -// } - -// function test_PerformanceFeeCalculationEdgeCases() public { -// // Test performance fee calculation with edge cases - -// // Case 1: Zero shares -// vm.prank(address(vault)); -// accountant.setLastRealizedFeeExchangeRate(1.0e18, 1000 ether); - -// vm.prank(address(vault)); -// uint256 fee1 = accountant.getPerformanceFee(0, 1.1e18, 18); -// assertEq(fee1, 0); - -// // Case 2: Same exchange rate (no change) -// vm.prank(address(vault)); -// uint256 fee2 = accountant.getPerformanceFee(1000 ether, 1.0e18, 18); -// assertEq(fee2, 0); - -// // Case 3: Very small interest -// vm.prank(address(vault)); -// uint256 fee3 = accountant.getPerformanceFee(1000 ether, 1.0001e18, 18); -// assertGt(fee3, 0); - -// console.log("Small interest fee:", fee3); -// } - -// function _enableMockingPoolDataProvider() internal { -// vm.mockCall( -// AAVE_V3_POOL_DATA_PROVIDER, -// abi.encodeWithSelector(IPoolDataProvider.getUserReserveData.selector, ST_XTZ, address(vault)), -// abi.encode(1000 * 10 ** 6, 0, 0, 0, 0, 0, 0, 0, 0) -// ); - -// vm.mockCall( -// AAVE_V3_POOL_DATA_PROVIDER, -// abi.encodeWithSelector(IPoolDataProvider.getUserReserveData.selector, XTZ, address(vault)), -// abi.encode(0, 0, 800 ether, 0, 0, 0, 0, 0, 0) -// ); -// } - -// function _enableMockingPriceOracle(uint256 stXtzPrice, uint256 xtzPrice) internal { -// vm.mockCall( -// AAVE_V3_PRICE_ORACLE, -// abi.encodeWithSelector(IPriceOracleGetter.getAssetPrice.selector, ST_XTZ), -// abi.encode(stXtzPrice) -// ); - -// vm.mockCall( -// AAVE_V3_PRICE_ORACLE, -// abi.encodeWithSelector(IPriceOracleGetter.getAssetPrice.selector, XTZ), -// abi.encode(xtzPrice) -// ); -// } -// } +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.13; + +import {TestBase} from "../TestBase.sol"; +import {console} from "forge-std/console.sol"; +import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +import {MockVault} from "../../../src/mock/MockVault.sol"; +import {UniversalAccountant} from "../../../src/core/Accountant/universalAccountant/UniversalAccountant.sol"; +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {DataTypes} from "../../../src/common/DataTypes.sol"; +import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import {IPoolAddressesProvider} from "aave-v3-core/contracts/interfaces/IPoolAddressesProvider.sol"; +import {IPoolDataProvider} from "aave-v3-core/contracts/interfaces/IPoolDataProvider.sol"; +import {IAaveOracle} from "aave-v3-core/contracts/interfaces/IAaveOracle.sol"; +import {IPriceOracleGetter} from "aave-v3-core/contracts/interfaces/IPriceOracleGetter.sol"; +import {AaveV3AccountantPlugin} from "../../../src/plugins/Accountant/AaveV3AccountantPlugin.sol"; + +// TODO : revisit with mutli token setup for eth mainnet +contract AccountantAaveV3Test is TestBase { + UniversalAccountant public accountantImplementation; + AaveV3AccountantPlugin public accountantPlugin; + + IERC20 public asset; + MockVault public vault; + + // Test data + uint256 public constant INITIAL_WHALE_BALANCE = 1000 ether; + + function setUp() public override { + super.setUp(); + + asset = IERC20(environment.vaultAsset); + vault = new MockVault(asset, "Mock Vault", "mVLT"); + + // deploy accountant plugin + DataTypes.AaveV3AccountantPluginModuleInitData memory accountantPluginInitData = DataTypes + .AaveV3AccountantPluginModuleInitData({ + poolAddressesProvider: environment.poolAddressesProvider, + lendAssets: environment.lendAssets, + borrowAssets: environment.borrowAssets + }); + accountantPlugin = new AaveV3AccountantPlugin(accountantPluginInitData); + + address[] memory registeredAccountants = new address[](1); + registeredAccountants[0] = address(accountantPlugin); + + // deploy accountant + DataTypes.UniversalAccountantModuleInitData memory initData = DataTypes.UniversalAccountantModuleInitData({ + registeredAccountants: registeredAccountants, + performanceFee: uint16(PERFORMANCE_FEE), + vault: address(vault) + }); + + accountantImplementation = new UniversalAccountant(); + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(accountantImplementation), + address(this), + abi.encodeWithSelector(UniversalAccountant.initialize.selector, initData) + ); + + accountant = UniversalAccountant(address(proxy)); + + // Fund the accountant with XTZ from whale + vm.startPrank(environment.vaultAssetWhale); + asset.transfer(address(vault), INITIAL_WHALE_BALANCE); + vm.stopPrank(); + } + + function test_Initialize() public view { + assertEq(accountant.getTotalAssets(), INITIAL_WHALE_BALANCE); + assertEq(accountant.performanceFee(), PERFORMANCE_FEE); + assertEq(accountant.vault(), address(vault)); + assertEq(accountant.registeredAccountants()[0], address(accountantPlugin)); + } + + function test_InitializeRevertIfAlreadyInitialized() public { + // Test that initialize reverts if called again + address[] memory registeredAccountants = new address[](1); + registeredAccountants[0] = address(accountantPlugin); + + DataTypes.UniversalAccountantModuleInitData memory initData = DataTypes.UniversalAccountantModuleInitData({ + registeredAccountants: registeredAccountants, + performanceFee: uint16(PERFORMANCE_FEE), + vault: address(vault) + }); + + vm.expectRevert(); + accountant.initialize(initData); + } + + function test_GetTotalAssets() public view { + // Test getTotalAssets calculation using actual fork data + uint256 totalAssets = accountant.getTotalAssets(); + + // Should return the base asset balance since there are no lend/borrow positions yet + assertEq(totalAssets, INITIAL_WHALE_BALANCE); + + console.log("Total assets:", totalAssets); + console.log("Expected assets:", INITIAL_WHALE_BALANCE); + } + + function test_GetTotalAssetsWithMultipleAssets() public { + // mock pool data provider and price oracle + _enableMockingPoolDataProvider(); + _enableMockingPriceOracle(1.01e8, 1e8); + + uint256 totalAssets = accountant.getTotalAssets(); + + // Calculate expected total assets based on mock data: + // Lend assets: 1000 ether * 1.01e8 (ST_XTZ price) = 1010e8 + // Borrow assets: -800 ether * 1e8 (XTZ price) = -800e8 + // Base asset balance: 1000 ether * 1e8 (XTZ price) = 1000e8 + // Total in market reference currency: 1010e8 - 800e8 + 1000e8 = 1210e8 + // Convert to base asset: 1210e18 / 1e18 = 1210 ether + uint256 expectedTotalAssets = 1210 ether; + + assertEq(totalAssets, expectedTotalAssets); + console.log("Total assets with multiple assets:", totalAssets); + console.log("Expected total assets:", expectedTotalAssets); + } + + function test_GetPerformanceFee() public { + uint256 totalShares = 1000 ether; + uint256 exchangeRate = 1.1e18; // 10% increase + uint256 lastRealizedFeeExchangeRate = 1.0e18; + uint256 totalSupply = 1000 ether; + + // Set the last realized fee exchange rate + vm.prank(address(vault)); + accountant.setLastRealizedFeeExchangeRate(lastRealizedFeeExchangeRate, totalSupply); + + // Calculate expected performance fee + uint256 latestAssetAmount = totalShares * exchangeRate; + uint256 prevAssetAmount = totalShares * lastRealizedFeeExchangeRate; + uint256 interestGenerated = latestAssetAmount - prevAssetAmount; + uint256 expectedPerformanceFee = (interestGenerated * PERFORMANCE_FEE) / (10000 * 1e18); + + vm.prank(address(vault)); + uint256 actualPerformanceFee = accountant.getPerformanceFee(totalShares, exchangeRate, 18); + + console.log("Performance fee:", actualPerformanceFee); + console.log("Expected fee:", expectedPerformanceFee); + + assertEq(actualPerformanceFee, expectedPerformanceFee); + } + + function test_GetPerformanceFeeNoInterest() public { + uint256 totalShares = 1000 ether; + uint256 exchangeRate = 0.9e18; // 10% decrease + uint256 lastRealizedFeeExchangeRate = 1.0e18; + uint256 totalSupply = 1000 ether; + // Set the last realized fee exchange rate + vm.prank(address(vault)); + accountant.setLastRealizedFeeExchangeRate(lastRealizedFeeExchangeRate, totalSupply); + + vm.prank(address(vault)); + uint256 performanceFee = accountant.getPerformanceFee(totalShares, exchangeRate, 18); + + // Should return 0 when there's no interest (exchange rate decreased) + assertEq(performanceFee, 0); + } + + function test_GetPerformanceFeeRevertIfNotVault() public { + uint256 totalShares = 1000 ether; + uint256 exchangeRate = 1.1e18; + + vm.expectRevert(); + accountant.getPerformanceFee(totalShares, exchangeRate, 18); + } + + function test_SetLastRealizedFeeExchangeRate() public { + uint256 newExchangeRate = 1.2e18; + uint256 totalSupply = 1000 ether; + + vm.prank(address(vault)); + accountant.setLastRealizedFeeExchangeRate(newExchangeRate, totalSupply); + + // Test that the exchange rate was set correctly by calling getPerformanceFee + vm.prank(address(vault)); + uint256 performanceFee = accountant.getPerformanceFee(1000 ether, 1.3e18, 18); + + // Should calculate based on the new exchange rate + uint256 expectedInterest = 1000 ether * 1.3e18 - 1000 ether * newExchangeRate; + uint256 expectedFee = (expectedInterest * PERFORMANCE_FEE) / (10000 * 1e18); + + assertEq(performanceFee, expectedFee); + } + + function test_SetLastRealizedFeeExchangeRateRevertIfNotVault() public { + uint256 newExchangeRate = 1.2e18; + uint256 totalSupply = 1000 ether; + + vm.expectRevert(); + accountant.setLastRealizedFeeExchangeRate(newExchangeRate, totalSupply); + } + + function test_GetTotalAssetsWithZeroBalances() public view { + // Test getTotalAssets when there are no lend/borrow positions + // This should be the default state since we haven't interacted with Aave V3 + uint256 totalAssets = accountant.getTotalAssets(); + + // Should only include base asset balance + assertEq(totalAssets, INITIAL_WHALE_BALANCE); + } + + function test_PerformanceFeeCalculationEdgeCases() public { + // Test performance fee calculation with edge cases + + // Case 1: Zero shares + vm.prank(address(vault)); + accountant.setLastRealizedFeeExchangeRate(1.0e18, 1000 ether); + + vm.prank(address(vault)); + uint256 fee1 = accountant.getPerformanceFee(0, 1.1e18, 18); + assertEq(fee1, 0); + + // Case 2: Same exchange rate (no change) + vm.prank(address(vault)); + uint256 fee2 = accountant.getPerformanceFee(1000 ether, 1.0e18, 18); + assertEq(fee2, 0); + + // Case 3: Very small interest + vm.prank(address(vault)); + uint256 fee3 = accountant.getPerformanceFee(1000 ether, 1.0001e18, 18); + assertGt(fee3, 0); + + console.log("Small interest fee:", fee3); + } + + function _enableMockingPoolDataProvider() internal { + vm.mockCall( + environment.poolDataProvider, + abi.encodeWithSelector( + IPoolDataProvider.getUserReserveData.selector, environment.lendAssets[0], address(vault) + ), + abi.encode(1000 * 10 ** 6, 0, 0, 0, 0, 0, 0, 0, 0) + ); + + vm.mockCall( + environment.poolDataProvider, + abi.encodeWithSelector( + IPoolDataProvider.getUserReserveData.selector, environment.borrowAssets[0], address(vault) + ), + abi.encode(0, 0, 800 ether, 0, 0, 0, 0, 0, 0) + ); + } + + function _enableMockingPriceOracle(uint256 stXtzPrice, uint256 xtzPrice) internal { + vm.mockCall( + environment.priceOracle, + abi.encodeWithSelector(IPriceOracleGetter.getAssetPrice.selector, environment.lendAssets[0]), + abi.encode(stXtzPrice) + ); + + vm.mockCall( + environment.priceOracle, + abi.encodeWithSelector(IPriceOracleGetter.getAssetPrice.selector, environment.borrowAssets[0]), + abi.encode(xtzPrice) + ); + } +} diff --git a/test/core/unit/WithdrawManager.t.sol b/test/core/unit/WithdrawManager.t.sol index 8e5ead0..691a896 100644 --- a/test/core/unit/WithdrawManager.t.sol +++ b/test/core/unit/WithdrawManager.t.sol @@ -1,389 +1,389 @@ -// // SPDX-License-Identifier: MIT - -// pragma solidity ^0.8.13; - -// import {TestBase} from "../TestBase.sol"; -// import {console} from "forge-std/console.sol"; -// import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; -// import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; -// import {TransparentUpgradeableProxy} from -// "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -// import {DataTypes} from "../../../src/common/DataTypes.sol"; -// import {Errors} from "../../../src/common/Errors.sol"; -// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -// import {WithdrawManager} from "../../../src/core/WithdrawManager/WithdrawManager.sol"; - -// contract WithdrawManagerTest is TestBase { -// Superloop public superloopImplementation; -// ProxyAdmin public proxyAdmin; -// address public user1; -// address public user2; - -// function setUp() public override { -// super.setUp(); - -// user1 = makeAddr("user1"); -// user2 = makeAddr("user2"); - -// vm.startPrank(admin); -// _deployModules(); - -// address[] memory modules = new address[](5); -// modules[0] = address(supplyModule); -// modules[1] = address(withdrawModule); -// modules[2] = address(borrowModule); -// modules[3] = address(repayModule); -// modules[4] = address(dexModule); - -// DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ -// asset: XTZ, -// name: "XTZ Vault", -// symbol: "XTZV", -// supplyCap: 100000 * 10 ** 18, -// minimumDepositAmount: 100, -// instantWithdrawFee: 0, -// superloopModuleRegistry: address(moduleRegistry), -// modules: modules, -// accountant: mockModule, -// withdrawManager: mockModule, -// depositManager: mockModule, -// cashReserve: 1000, -// vaultAdmin: admin, -// treasury: treasury, -// vaultOperator: admin -// }); -// superloopImplementation = new Superloop(); -// proxyAdmin = new ProxyAdmin(address(this)); - -// TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( -// address(superloopImplementation), -// address(proxyAdmin), -// abi.encodeWithSelector(Superloop.initialize.selector, initData) -// ); -// superloop = Superloop(payable(address(proxy))); - -// _deployAccountant(address(superloop), _singleAddressArray(ST_XTZ), _singleAddressArray(XTZ)); -// _deployWithdrawManager(address(superloop)); -// _deployDepositManager(address(superloop)); - -// superloop.setAccountantModule(address(accountant)); -// superloop.setWithdrawManagerModule(address(withdrawManager)); -// superloop.setDepositManagerModule(address(depositManager)); - -// vm.stopPrank(); - -// vm.label(address(superloop), "superloop"); -// vm.label(user1, "user1"); -// vm.label(user2, "user2"); -// } - -// function test_Initialize() public view { -// address vault = withdrawManager.vault(); -// address asset = withdrawManager.asset(); -// uint256 nextGeneralRequestId = withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.GENERAL); -// uint256 nextInstantRequestId = withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.INSTANT); -// uint256 nextPriorityRequestId = withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.PRIORITY); -// uint256 nextDeferredRequestId = withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.DEFERRED); - -// assertEq(vault, address(superloop)); -// assertEq(asset, XTZ); -// assertEq(nextGeneralRequestId, 1); -// assertEq(nextInstantRequestId, 1); -// assertEq(nextPriorityRequestId, 1); -// assertEq(nextDeferredRequestId, 1); -// } - -// // ============ requestWithdraw Tests ============ - -// function test_requestWithdraw_Success() public { -// uint256 shares = 1000 * 10 ** 18; -// DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; - -// // Give user1 some vault shares -// deal(address(superloop), user1, shares); - -// vm.startPrank(user1); -// IERC20(address(superloop)).approve(address(withdrawManager), shares); - -// // Expect the WithdrawRequested event -// vm.expectEmit(true, true, true, true); -// emit WithdrawManager.WithdrawRequested(user1, shares, 1, requestType); - -// withdrawManager.requestWithdraw(shares, requestType); -// vm.stopPrank(); - -// // Verify the withdraw request was created -// DataTypes.WithdrawRequestData memory request = withdrawManager.withdrawRequest(1, requestType); - -// assertEq(request.shares, shares); -// assertEq(request.sharesProcessed, 0); -// assertEq(request.amountClaimable, 0); -// assertEq(request.amountClaimed, 0); -// assertEq(request.user, user1); -// assertEq(uint256(request.state), uint256(DataTypes.RequestProcessingState.UNPROCESSED)); - -// // Verify user's withdraw request ID was set -// (, uint256 userRequestId) = withdrawManager.userWithdrawRequest(user1, requestType); -// assertEq(userRequestId, 1); - -// // Verify shares were transferred to withdraw manager -// assertEq(IERC20(address(superloop)).balanceOf(address(withdrawManager)), shares); -// assertEq(IERC20(address(superloop)).balanceOf(user1), 0); -// } - -// function test_requestWithdraw_ZeroShares() public { -// uint256 shares = 0; -// DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; - -// vm.startPrank(user1); -// vm.expectRevert(bytes(Errors.INVALID_SHARES_AMOUNT)); -// withdrawManager.requestWithdraw(shares, requestType); -// vm.stopPrank(); -// } - -// function test_requestWithdraw_ZeroExpectedAmount() public { -// uint256 shares = 1; // Very small amount that would result in 0 expected withdraw amount -// DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; - -// // Give user1 some vault shares -// deal(address(superloop), user1, shares); - -// vm.startPrank(user1); -// IERC20(address(superloop)).approve(address(withdrawManager), shares); - -// vm.expectRevert(bytes(Errors.INVALID_AMOUNT)); -// withdrawManager.requestWithdraw(shares, requestType); -// vm.stopPrank(); -// } +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.13; + +import {TestBase} from "../TestBase.sol"; +import {console} from "forge-std/console.sol"; +import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; +import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; +import {TransparentUpgradeableProxy} from + "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {DataTypes} from "../../../src/common/DataTypes.sol"; +import {Errors} from "../../../src/common/Errors.sol"; +import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +import {WithdrawManager} from "../../../src/core/WithdrawManager/WithdrawManager.sol"; + +contract WithdrawManagerTest is TestBase { + Superloop public superloopImplementation; + ProxyAdmin public proxyAdmin; + address public user1; + address public user2; + + function setUp() public override { + super.setUp(); + + user1 = makeAddr("user1"); + user2 = makeAddr("user2"); + + vm.startPrank(admin); + _deployModules(); + + address[] memory modules = new address[](5); + modules[0] = address(supplyModule); + modules[1] = address(withdrawModule); + modules[2] = address(borrowModule); + modules[3] = address(repayModule); + modules[4] = address(dexModule); + + DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ + asset: environment.vaultAsset, + name: "Vault", + symbol: "VLT", + supplyCap: 100000 * 10 ** environment.vaultAssetDecimals, + minimumDepositAmount: 100, + instantWithdrawFee: 0, + superloopModuleRegistry: address(moduleRegistry), + modules: modules, + accountant: mockModule, + withdrawManager: mockModule, + depositManager: mockModule, + cashReserve: 1000, + vaultAdmin: admin, + treasury: treasury, + vaultOperator: admin + }); + superloopImplementation = new Superloop(); + proxyAdmin = new ProxyAdmin(address(this)); + + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(superloopImplementation), + address(proxyAdmin), + abi.encodeWithSelector(Superloop.initialize.selector, initData) + ); + superloop = Superloop(payable(address(proxy))); + + _deployAccountant(address(superloop), environment.lendAssets, environment.borrowAssets); + _deployWithdrawManager(address(superloop)); + _deployDepositManager(address(superloop)); + + superloop.setAccountantModule(address(accountant)); + superloop.setWithdrawManagerModule(address(withdrawManager)); + superloop.setDepositManagerModule(address(depositManager)); + + vm.stopPrank(); + + vm.label(address(superloop), "superloop"); + vm.label(user1, "user1"); + vm.label(user2, "user2"); + } + + function test_Initialize() public view { + address vault = withdrawManager.vault(); + address asset = withdrawManager.asset(); + uint256 nextGeneralRequestId = withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.GENERAL); + uint256 nextInstantRequestId = withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.INSTANT); + uint256 nextPriorityRequestId = withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.PRIORITY); + uint256 nextDeferredRequestId = withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.DEFERRED); + + assertEq(vault, address(superloop)); + assertEq(asset, environment.vaultAsset); + assertEq(nextGeneralRequestId, 1); + assertEq(nextInstantRequestId, 1); + assertEq(nextPriorityRequestId, 1); + assertEq(nextDeferredRequestId, 1); + } + + // ============ requestWithdraw Tests ============ + + function test_requestWithdraw_Success() public { + uint256 shares = 1000 * 10 ** 18; + DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; + + // Give user1 some vault shares + deal(address(superloop), user1, shares); + + vm.startPrank(user1); + IERC20(address(superloop)).approve(address(withdrawManager), shares); + + // Expect the WithdrawRequested event + vm.expectEmit(true, true, true, true); + emit WithdrawManager.WithdrawRequested(user1, shares, 1, requestType); + + withdrawManager.requestWithdraw(shares, requestType); + vm.stopPrank(); + + // Verify the withdraw request was created + DataTypes.WithdrawRequestData memory request = withdrawManager.withdrawRequest(1, requestType); + + assertEq(request.shares, shares); + assertEq(request.sharesProcessed, 0); + assertEq(request.amountClaimable, 0); + assertEq(request.amountClaimed, 0); + assertEq(request.user, user1); + assertEq(uint256(request.state), uint256(DataTypes.RequestProcessingState.UNPROCESSED)); + + // Verify user's withdraw request ID was set + (, uint256 userRequestId) = withdrawManager.userWithdrawRequest(user1, requestType); + assertEq(userRequestId, 1); + + // Verify shares were transferred to withdraw manager + assertEq(IERC20(address(superloop)).balanceOf(address(withdrawManager)), shares); + assertEq(IERC20(address(superloop)).balanceOf(user1), 0); + } + + function test_requestWithdraw_ZeroShares() public { + uint256 shares = 0; + DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; + + vm.startPrank(user1); + vm.expectRevert(bytes(Errors.INVALID_SHARES_AMOUNT)); + withdrawManager.requestWithdraw(shares, requestType); + vm.stopPrank(); + } + + function test_requestWithdraw_ZeroExpectedAmount() public { + uint256 shares = 1; // Very small amount that would result in 0 expected withdraw amount + DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; + + // Give user1 some vault shares + deal(address(superloop), user1, shares); + + vm.startPrank(user1); + IERC20(address(superloop)).approve(address(withdrawManager), shares); + + vm.expectRevert(bytes(Errors.INVALID_AMOUNT)); + withdrawManager.requestWithdraw(shares, requestType); + vm.stopPrank(); + } -// function test_requestWithdraw_ActiveRequestExists() public { -// uint256 shares = 1000 * 10 ** 18; -// DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; + function test_requestWithdraw_ActiveRequestExists() public { + uint256 shares = 1000 * 10 ** 18; + DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; -// // Give user1 some vault shares -// deal(address(superloop), user1, shares * 2); + // Give user1 some vault shares + deal(address(superloop), user1, shares * 2); -// vm.startPrank(user1); -// IERC20(address(superloop)).approve(address(withdrawManager), shares * 2); + vm.startPrank(user1); + IERC20(address(superloop)).approve(address(withdrawManager), shares * 2); -// // Make first withdraw request -// withdrawManager.requestWithdraw(shares, requestType); + // Make first withdraw request + withdrawManager.requestWithdraw(shares, requestType); -// // Try to make another withdraw request - should fail -// vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_ACTIVE)); -// withdrawManager.requestWithdraw(shares, requestType); -// vm.stopPrank(); -// } + // Try to make another withdraw request - should fail + vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_ACTIVE)); + withdrawManager.requestWithdraw(shares, requestType); + vm.stopPrank(); + } -// function test_requestWithdraw_InsufficientBalance() public { -// uint256 shares = 1000 * 10 ** 18; -// DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; + function test_requestWithdraw_InsufficientBalance() public { + uint256 shares = 1000 * 10 ** 18; + DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; -// // Don't give user1 any vault shares -// vm.startPrank(user1); -// IERC20(address(superloop)).approve(address(withdrawManager), shares); + // Don't give user1 any vault shares + vm.startPrank(user1); + IERC20(address(superloop)).approve(address(withdrawManager), shares); -// vm.expectRevert(); -// withdrawManager.requestWithdraw(shares, requestType); -// vm.stopPrank(); -// } + vm.expectRevert(); + withdrawManager.requestWithdraw(shares, requestType); + vm.stopPrank(); + } -// function test_requestWithdraw_DifferentRequestTypes() public { -// uint256 shares = 1000 * 10 ** 18; + function test_requestWithdraw_DifferentRequestTypes() public { + uint256 shares = 1000 * 10 ** 18; -// // Give user1 some vault shares -// deal(address(superloop), user1, shares * 4); + // Give user1 some vault shares + deal(address(superloop), user1, shares * 4); -// vm.startPrank(user1); -// IERC20(address(superloop)).approve(address(withdrawManager), shares * 4); + vm.startPrank(user1); + IERC20(address(superloop)).approve(address(withdrawManager), shares * 4); -// // Test all request types -// withdrawManager.requestWithdraw(shares, DataTypes.WithdrawRequestType.GENERAL); -// withdrawManager.requestWithdraw(shares, DataTypes.WithdrawRequestType.INSTANT); -// withdrawManager.requestWithdraw(shares, DataTypes.WithdrawRequestType.PRIORITY); -// withdrawManager.requestWithdraw(shares, DataTypes.WithdrawRequestType.DEFERRED); + // Test all request types + withdrawManager.requestWithdraw(shares, DataTypes.WithdrawRequestType.GENERAL); + withdrawManager.requestWithdraw(shares, DataTypes.WithdrawRequestType.INSTANT); + withdrawManager.requestWithdraw(shares, DataTypes.WithdrawRequestType.PRIORITY); + withdrawManager.requestWithdraw(shares, DataTypes.WithdrawRequestType.DEFERRED); -// vm.stopPrank(); + vm.stopPrank(); -// // Verify all requests were created -// assertEq(withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.GENERAL), 2); -// assertEq(withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.INSTANT), 2); -// assertEq(withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.PRIORITY), 2); -// assertEq(withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.DEFERRED), 2); -// } + // Verify all requests were created + assertEq(withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.GENERAL), 2); + assertEq(withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.INSTANT), 2); + assertEq(withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.PRIORITY), 2); + assertEq(withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.DEFERRED), 2); + } -// // ============ cancelWithdrawRequest Tests ============ + // ============ cancelWithdrawRequest Tests ============ -// function test_cancelWithdrawRequest_Success() public { -// uint256 shares = 1000 * 10 ** 18; -// DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; + function test_cancelWithdrawRequest_Success() public { + uint256 shares = 1000 * 10 ** 18; + DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; -// // Give user1 some vault shares and make a withdraw request -// deal(address(superloop), user1, shares); + // Give user1 some vault shares and make a withdraw request + deal(address(superloop), user1, shares); -// vm.startPrank(user1); -// IERC20(address(superloop)).approve(address(withdrawManager), shares); -// withdrawManager.requestWithdraw(shares, requestType); + vm.startPrank(user1); + IERC20(address(superloop)).approve(address(withdrawManager), shares); + withdrawManager.requestWithdraw(shares, requestType); -// uint256 userBalanceBefore = IERC20(address(superloop)).balanceOf(user1); + uint256 userBalanceBefore = IERC20(address(superloop)).balanceOf(user1); -// // Expect the WithdrawRequestCancelled event -// vm.expectEmit(true, true, true, true); -// emit WithdrawManager.WithdrawRequestCancelled(1, user1, shares, 0, requestType); + // Expect the WithdrawRequestCancelled event + vm.expectEmit(true, true, true, true); + emit WithdrawManager.WithdrawRequestCancelled(1, user1, shares, 0, requestType); -// withdrawManager.cancelWithdrawRequest(1, requestType); -// vm.stopPrank(); + withdrawManager.cancelWithdrawRequest(1, requestType); + vm.stopPrank(); -// // Verify the withdraw request was cancelled -// DataTypes.WithdrawRequestData memory request = withdrawManager.withdrawRequest(1, requestType); + // Verify the withdraw request was cancelled + DataTypes.WithdrawRequestData memory request = withdrawManager.withdrawRequest(1, requestType); -// assertEq(uint256(request.state), uint256(DataTypes.RequestProcessingState.CANCELLED)); + assertEq(uint256(request.state), uint256(DataTypes.RequestProcessingState.CANCELLED)); -// // Verify user's withdraw request ID was cleared -// (, uint256 userRequestId) = withdrawManager.userWithdrawRequest(user1, requestType); -// assertEq(userRequestId, 0); + // Verify user's withdraw request ID was cleared + (, uint256 userRequestId) = withdrawManager.userWithdrawRequest(user1, requestType); + assertEq(userRequestId, 0); -// // Verify shares were refunded to user -// assertEq(IERC20(address(superloop)).balanceOf(user1), userBalanceBefore + shares); -// assertEq(IERC20(address(superloop)).balanceOf(address(withdrawManager)), 0); -// } + // Verify shares were refunded to user + assertEq(IERC20(address(superloop)).balanceOf(user1), userBalanceBefore + shares); + assertEq(IERC20(address(superloop)).balanceOf(address(withdrawManager)), 0); + } -// function test_cancelWithdrawRequest_WrongOwner() public { -// uint256 shares = 1000 * 10 ** 18; -// DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; + function test_cancelWithdrawRequest_WrongOwner() public { + uint256 shares = 1000 * 10 ** 18; + DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; -// // Give user1 some vault shares and make a withdraw request -// deal(address(superloop), user1, shares); + // Give user1 some vault shares and make a withdraw request + deal(address(superloop), user1, shares); -// vm.startPrank(user1); -// IERC20(address(superloop)).approve(address(withdrawManager), shares); -// withdrawManager.requestWithdraw(shares, requestType); -// vm.stopPrank(); + vm.startPrank(user1); + IERC20(address(superloop)).approve(address(withdrawManager), shares); + withdrawManager.requestWithdraw(shares, requestType); + vm.stopPrank(); -// // Try to cancel from user2 - should fail -// vm.startPrank(user2); -// vm.expectRevert(bytes(Errors.CALLER_NOT_WITHDRAW_REQUEST_OWNER)); -// withdrawManager.cancelWithdrawRequest(1, requestType); -// vm.stopPrank(); -// } - -// function test_cancelWithdrawRequest_AlreadyCancelled() public { -// uint256 shares = 1000 * 10 ** 18; -// DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; - -// // Give user1 some vault shares and make a withdraw request -// deal(address(superloop), user1, shares); - -// vm.startPrank(user1); -// IERC20(address(superloop)).approve(address(withdrawManager), shares); -// withdrawManager.requestWithdraw(shares, requestType); - -// // Cancel the request -// withdrawManager.cancelWithdrawRequest(1, requestType); - -// // Try to cancel again - should fail -// vm.expectRevert(bytes(Errors.INVALID_WITHDRAW_REQUEST_STATE)); -// withdrawManager.cancelWithdrawRequest(1, requestType); -// vm.stopPrank(); -// } - -// function test_cancelWithdrawRequest_NonExistentRequest() public { -// DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; - -// vm.startPrank(user1); -// vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_NOT_FOUND)); -// withdrawManager.cancelWithdrawRequest(999, requestType); // Non-existent request ID -// vm.stopPrank(); -// } - -// // ============ withdraw Tests ============ - -// function test_withdraw_NoRequestFound() public { -// DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; - -// vm.startPrank(user1); -// vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_NOT_FOUND)); -// withdrawManager.withdraw(requestType); -// vm.stopPrank(); -// } - -// function test_withdraw_WrongOwner() public { -// uint256 shares = 1000 * 10 ** 18; -// DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; - -// // Give user1 some vault shares and make a withdraw request -// deal(address(superloop), user1, shares); - -// vm.startPrank(user1); -// IERC20(address(superloop)).approve(address(withdrawManager), shares); -// withdrawManager.requestWithdraw(shares, requestType); -// vm.stopPrank(); - -// // Try to withdraw from user2 - should fail -// vm.startPrank(user2); -// vm.expectRevert(); -// withdrawManager.withdraw(requestType); -// vm.stopPrank(); -// } - -// function test_withdraw_RequestActive() public { -// uint256 shares = 1000 * 10 ** 18; -// DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; - -// // Give user1 some vault shares and make a withdraw request -// deal(address(superloop), user1, shares); - -// vm.startPrank(user1); -// IERC20(address(superloop)).approve(address(withdrawManager), shares); -// withdrawManager.requestWithdraw(shares, requestType); - -// // Try to withdraw immediately - should fail (request is still active/unprocessed) -// vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_ACTIVE)); -// withdrawManager.withdraw(requestType); -// vm.stopPrank(); -// } - -// // ============ resolveWithdrawRequests Tests ============ - -// function test_resolveWithdrawRequests_OnlyVault() public { -// DataTypes.ResolveWithdrawRequestsData memory data = DataTypes.ResolveWithdrawRequestsData({ -// shares: 1000 * 10 ** 18, -// requestType: DataTypes.WithdrawRequestType.GENERAL, -// callbackExecutionData: "" -// }); - -// vm.startPrank(user1); -// vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT)); -// withdrawManager.resolveWithdrawRequests(data); -// vm.stopPrank(); -// } - -// function test_resolveWithdrawRequests_ZeroShares() public { -// DataTypes.ResolveWithdrawRequestsData memory data = DataTypes.ResolveWithdrawRequestsData({ -// shares: 0, -// requestType: DataTypes.WithdrawRequestType.GENERAL, -// callbackExecutionData: "" -// }); - -// vm.startPrank(address(superloop)); -// vm.expectRevert(bytes(Errors.INVALID_SHARES_AMOUNT)); -// withdrawManager.resolveWithdrawRequests(data); -// vm.stopPrank(); -// } - -// function test_resolveWithdrawRequests_ExceedsPending() public { -// DataTypes.ResolveWithdrawRequestsData memory data = DataTypes.ResolveWithdrawRequestsData({ -// shares: 1000 * 10 ** 18, -// requestType: DataTypes.WithdrawRequestType.GENERAL, -// callbackExecutionData: "" -// }); - -// vm.startPrank(address(superloop)); -// vm.expectRevert(bytes(Errors.INVALID_SHARES_AMOUNT)); -// withdrawManager.resolveWithdrawRequests(data); -// vm.stopPrank(); -// } - -// // TODO: Test partial cancellation, partial processing, and resolveWithdrawRequests functions -// // NOTE: Cannot test these functions without manipulating the internal state, so will be tested in integration tests -// } + // Try to cancel from user2 - should fail + vm.startPrank(user2); + vm.expectRevert(bytes(Errors.CALLER_NOT_WITHDRAW_REQUEST_OWNER)); + withdrawManager.cancelWithdrawRequest(1, requestType); + vm.stopPrank(); + } + + function test_cancelWithdrawRequest_AlreadyCancelled() public { + uint256 shares = 1000 * 10 ** 18; + DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; + + // Give user1 some vault shares and make a withdraw request + deal(address(superloop), user1, shares); + + vm.startPrank(user1); + IERC20(address(superloop)).approve(address(withdrawManager), shares); + withdrawManager.requestWithdraw(shares, requestType); + + // Cancel the request + withdrawManager.cancelWithdrawRequest(1, requestType); + + // Try to cancel again - should fail + vm.expectRevert(bytes(Errors.INVALID_WITHDRAW_REQUEST_STATE)); + withdrawManager.cancelWithdrawRequest(1, requestType); + vm.stopPrank(); + } + + function test_cancelWithdrawRequest_NonExistentRequest() public { + DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; + + vm.startPrank(user1); + vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_NOT_FOUND)); + withdrawManager.cancelWithdrawRequest(999, requestType); // Non-existent request ID + vm.stopPrank(); + } + + // ============ withdraw Tests ============ + + function test_withdraw_NoRequestFound() public { + DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; + + vm.startPrank(user1); + vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_NOT_FOUND)); + withdrawManager.withdraw(requestType); + vm.stopPrank(); + } + + function test_withdraw_WrongOwner() public { + uint256 shares = 1000 * 10 ** 18; + DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; + + // Give user1 some vault shares and make a withdraw request + deal(address(superloop), user1, shares); + + vm.startPrank(user1); + IERC20(address(superloop)).approve(address(withdrawManager), shares); + withdrawManager.requestWithdraw(shares, requestType); + vm.stopPrank(); + + // Try to withdraw from user2 - should fail + vm.startPrank(user2); + vm.expectRevert(); + withdrawManager.withdraw(requestType); + vm.stopPrank(); + } + + function test_withdraw_RequestActive() public { + uint256 shares = 1000 * 10 ** 18; + DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; + + // Give user1 some vault shares and make a withdraw request + deal(address(superloop), user1, shares); + + vm.startPrank(user1); + IERC20(address(superloop)).approve(address(withdrawManager), shares); + withdrawManager.requestWithdraw(shares, requestType); + + // Try to withdraw immediately - should fail (request is still active/unprocessed) + vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_ACTIVE)); + withdrawManager.withdraw(requestType); + vm.stopPrank(); + } + + // ============ resolveWithdrawRequests Tests ============ + + function test_resolveWithdrawRequests_OnlyVault() public { + DataTypes.ResolveWithdrawRequestsData memory data = DataTypes.ResolveWithdrawRequestsData({ + shares: 1000 * 10 ** 18, + requestType: DataTypes.WithdrawRequestType.GENERAL, + callbackExecutionData: "" + }); + + vm.startPrank(user1); + vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT)); + withdrawManager.resolveWithdrawRequests(data); + vm.stopPrank(); + } + + function test_resolveWithdrawRequests_ZeroShares() public { + DataTypes.ResolveWithdrawRequestsData memory data = DataTypes.ResolveWithdrawRequestsData({ + shares: 0, + requestType: DataTypes.WithdrawRequestType.GENERAL, + callbackExecutionData: "" + }); + + vm.startPrank(address(superloop)); + vm.expectRevert(bytes(Errors.INVALID_SHARES_AMOUNT)); + withdrawManager.resolveWithdrawRequests(data); + vm.stopPrank(); + } + + function test_resolveWithdrawRequests_ExceedsPending() public { + DataTypes.ResolveWithdrawRequestsData memory data = DataTypes.ResolveWithdrawRequestsData({ + shares: 1000 * 10 ** 18, + requestType: DataTypes.WithdrawRequestType.GENERAL, + callbackExecutionData: "" + }); + + vm.startPrank(address(superloop)); + vm.expectRevert(bytes(Errors.INVALID_SHARES_AMOUNT)); + withdrawManager.resolveWithdrawRequests(data); + vm.stopPrank(); + } + + // TODO: Test partial cancellation, partial processing, and resolveWithdrawRequests functions + // NOTE: Cannot test these functions without manipulating the internal state, so will be tested in integration tests +} diff --git a/test/helpers/VaultRouter.t.sol b/test/helpers/VaultRouter.t.sol index c706ac1..046d478 100644 --- a/test/helpers/VaultRouter.t.sol +++ b/test/helpers/VaultRouter.t.sol @@ -1,4 +1,5 @@ -// // SPDX-License-Identifier: MIT + // TODO : Update tests for periphery contract +// SPDX-License-Identifier: MIT // pragma solidity ^0.8.13; // import {Test, console} from "forge-std/Test.sol"; diff --git a/test/helpers/integration/VaultRouter.t.sol b/test/helpers/integration/VaultRouter.t.sol index 018b034..09f34cc 100644 --- a/test/helpers/integration/VaultRouter.t.sol +++ b/test/helpers/integration/VaultRouter.t.sol @@ -1,4 +1,6 @@ -// // SPDX-License-Identifier: MIT + // TODO : Update tests for periphery contract + +// SPDX-License-Identifier: MIT // pragma solidity ^0.8.13; From 9bf2bce5dae14ec2753fe045bf3385257ee1a703 Mon Sep 17 00:00:00 2001 From: Priyam Anand Date: Mon, 26 Jan 2026 10:30:01 +0530 Subject: [PATCH 08/21] test: unit test for vault module and integration test for universal accountant --- test/core/TestBase.sol | 15 ++- test/core/TestEnv.sol | 3 +- .../integration/UniversalAccountant.t.sol | 106 +++++++++++++++++ test/modules/vault/ERC4626Module.t.sol | 108 ++++++++++++++++++ 4 files changed, 230 insertions(+), 2 deletions(-) create mode 100644 test/core/integration/UniversalAccountant.t.sol create mode 100644 test/modules/vault/ERC4626Module.t.sol diff --git a/test/core/TestBase.sol b/test/core/TestBase.sol index 0fee7d7..10fc9d3 100644 --- a/test/core/TestBase.sol +++ b/test/core/TestBase.sol @@ -35,6 +35,8 @@ import {IERC20Metadata} from "openzeppelin-contracts/contracts/token/ERC20/exten import {HyperliquidStakeModule} from "../../src/modules/stake/hyperliquid/HyperliquidStakeModule.sol"; import {KinetiqStakeModule} from "../../src/modules/stake/hyperliquid/KinetiqStakeModule.sol"; import {HyperbeatStakingModule} from "../../src/modules/stake/hyperliquid/HyperbeatStakingModule.sol"; +import {VaultSupplyModule} from "../../src/modules/vault/VaultSupplyModule.sol"; +import {VaultWithdrawModule} from "../../src/modules/vault/VaultWithdrawModule.sol"; abstract contract TestBase is TestEnv { // address public constant AAVE_V3_POOL_ADDRESSES_PROVIDER = @@ -100,6 +102,9 @@ abstract contract TestBase is TestEnv { UniversalAccountant public accountantBtc; AccountantAaveV3 public accountantAaveV3Btc; + VaultSupplyModule public vaultSupplyModule; + VaultWithdrawModule public vaultWithdrawModule; + address public mockModule; AaveV3EmodeModule public emodeModule; IPoolDataProvider public poolDataProvider; @@ -112,7 +117,7 @@ abstract contract TestBase is TestEnv { function setUp() public virtual override { super.setUp(); - uint256 envIndex = 0; // TODO: move this to config + uint256 envIndex = 2; // TODO: move this to config environment = testEnvironments[envIndex]; vm.createSelectFork(environment.chainName); @@ -186,6 +191,12 @@ abstract contract TestBase is TestEnv { wrapModule = new WrapModule(environment.vaultAsset); moduleRegistry.setModule("WrapModule", address(wrapModule)); + vaultSupplyModule = new VaultSupplyModule(); + moduleRegistry.setModule("VaultSupplyModule", address(vaultSupplyModule)); + + vaultWithdrawModule = new VaultWithdrawModule(); + moduleRegistry.setModule("VaultWithdrawModule", address(vaultWithdrawModule)); + vm.label(address(flashloanModule), "flashloanModule"); vm.label(address(callbackHandler), "callbackHandler"); vm.label(address(emodeModule), "emodeModule"); @@ -196,6 +207,8 @@ abstract contract TestBase is TestEnv { vm.label(address(dexModule), "dexModule"); vm.label(address(depositManagerCallbackHandler), "depositManagerCallbackHandler"); vm.label(address(withdrawManagerCallbackHandler), "withdrawManagerCallbackHandler"); + vm.label(address(vaultSupplyModule), "vaultSupplyModule"); + vm.label(address(vaultWithdrawModule), "vaultWithdrawModule"); } function _deployHyperliquidStakeModule() internal { diff --git a/test/core/TestEnv.sol b/test/core/TestEnv.sol index fde47c1..e2c84a1 100644 --- a/test/core/TestEnv.sol +++ b/test/core/TestEnv.sol @@ -40,7 +40,7 @@ abstract contract TestEnv is Test { address public constant sUSDe = 0x9D39A5DE30e57443BfF2A8307A4256c8797A3497; address public constant USDC_ETH = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; address public constant USDC_ETH_Whale = 0x98C23E9d8f34FEFb1B7BD6a91B7FF122F4e16F5c; - address public constant USDT_ETH = 0x2C03058C8AFC06713be23e58D2febC8337dbfE6A; + address public constant USDT_ETH = 0xdAC17F958D2ee523a2206206994597C13D831ec7; // hyperevm address public constant WHYPE = 0x5555555555555555555555555555555555555555; @@ -56,6 +56,7 @@ abstract contract TestEnv is Test { // address public constant uint256 public constant PERFORMANCE_FEE = 2000; // 20% TestEnvironment[] internal testEnvironments; + uint256 public constant INTEREST_RATE_MODE = 2; // variable rate function setUp() public virtual { // etherlink xtz diff --git a/test/core/integration/UniversalAccountant.t.sol b/test/core/integration/UniversalAccountant.t.sol new file mode 100644 index 0000000..ae48a6f --- /dev/null +++ b/test/core/integration/UniversalAccountant.t.sol @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import {TestBase} from "../TestBase.sol"; +import {UniversalAccountant} from "../../../src/core/Accountant/universalAccountant/UniversalAccountant.sol"; +import {AaveV3AccountantPlugin} from "../../../src/plugins/Accountant/AaveV3AccountantPlugin.sol"; +import {TransparentUpgradeableProxy} from + "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {DataTypes} from "../../../src/common/DataTypes.sol"; +import {console} from "forge-std/console.sol"; +import {MockVault} from "../../../src/mock/MockVault.sol"; +import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +import {IERC20Metadata} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import {IPool} from "aave-v3-core/contracts/interfaces/IPool.sol"; +import {IPoolDataProvider} from "aave-v3-core/contracts/interfaces/IPoolDataProvider.sol"; +import {IERC20Metadata} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import {IPool} from "aave-v3-core/contracts/interfaces/IPool.sol"; +import {IPoolDataProvider} from "aave-v3-core/contracts/interfaces/IPoolDataProvider.sol"; +import {IERC20Metadata} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import {IPool} from "aave-v3-core/contracts/interfaces/IPool.sol"; +import {IPoolDataProvider} from "aave-v3-core/contracts/interfaces/IPoolDataProvider.sol"; +import {IERC20Metadata} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import {IPool} from "aave-v3-core/contracts/interfaces/IPool.sol"; +import {IPoolDataProvider} from "aave-v3-core/contracts/interfaces/IPoolDataProvider.sol"; +import {IAaveOracle} from "aave-v3-core/contracts/interfaces/IAaveOracle.sol"; +import {IPoolAddressesProvider} from "aave-v3-core/contracts/interfaces/IPoolAddressesProvider.sol"; + +contract UniversalAccountantTest is TestBase { + UniversalAccountant public accountantImplementation; + AaveV3AccountantPlugin public accountantPlugin; + + MockVault public vault; + + function setUp() public override { + super.setUp(); + + vault = new MockVault(IERC20(environment.vaultAsset), "Mock Vault", "mVLT"); + + // deploy accountant plugin + DataTypes.AaveV3AccountantPluginModuleInitData memory accountantPluginInitData = DataTypes + .AaveV3AccountantPluginModuleInitData({ + poolAddressesProvider: environment.poolAddressesProvider, + lendAssets: environment.lendAssets, + borrowAssets: environment.borrowAssets + }); + accountantPlugin = new AaveV3AccountantPlugin(accountantPluginInitData); + + address[] memory registeredAccountants = new address[](1); + registeredAccountants[0] = address(accountantPlugin); + + // deploy accountant + DataTypes.UniversalAccountantModuleInitData memory initData = DataTypes.UniversalAccountantModuleInitData({ + registeredAccountants: registeredAccountants, + performanceFee: uint16(PERFORMANCE_FEE), + vault: address(vault) + }); + + accountantImplementation = new UniversalAccountant(); + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(accountantImplementation), + address(this), + abi.encodeWithSelector(UniversalAccountant.initialize.selector, initData) + ); + + accountant = UniversalAccountant(address(proxy)); + } + + function test_UniversalAccountWithMutlipleTokens() public { + // create lend posiitions + vm.startPrank(address(vault)); + for (uint256 i = 0; i < environment.lendAssets.length; i++) { + uint8 decimals = IERC20Metadata(environment.lendAssets[i]).decimals(); + deal(environment.lendAssets[i], address(vault), 1000 * 10 ** decimals); + IERC20(environment.lendAssets[i]).approve(environment.pool, 1000 * 10 ** decimals); + IPool(environment.pool).supply(environment.lendAssets[i], 1000 * 10 ** decimals, address(vault), 0); + } + + // create borrow positions + for (uint256 i = 0; i < environment.borrowAssets.length; i++) { + uint8 decimals = IERC20Metadata(environment.borrowAssets[i]).decimals(); + IPool(environment.pool).borrow( + environment.borrowAssets[i], 500 * 10 ** decimals, INTEREST_RATE_MODE, 0, address(vault) + ); + } + vm.stopPrank(); + + for (uint256 i = 0; i < environment.lendAssets.length; i++) { + (uint256 currentSupply,,,,,,,,) = IPoolDataProvider(environment.poolDataProvider).getUserReserveData( + environment.lendAssets[i], address(vault) + ); + assertApproxEqAbs(currentSupply, 1000 * 10 ** IERC20Metadata(environment.lendAssets[i]).decimals(), 2); + } + for (uint256 i = 0; i < environment.borrowAssets.length; i++) { + (,, uint256 currentBorrow,,,,,,) = IPoolDataProvider(environment.poolDataProvider).getUserReserveData( + environment.borrowAssets[i], address(vault) + ); + + assertApproxEqAbs(currentBorrow, 500 * 10 ** IERC20Metadata(environment.borrowAssets[i]).decimals(), 2); + } + + // fund the vault for some tokens + deal(environment.vaultAsset, address(vault), 1000 * 10 ** environment.vaultAssetDecimals); + uint256 totalAssets = accountant.getTotalAssets(); + assert(totalAssets > (1200 + 1000) * 10 ** environment.vaultAssetDecimals); // aprox 1200 for lend and borrow positions and 1000 for the vault asset + } +} diff --git a/test/modules/vault/ERC4626Module.t.sol b/test/modules/vault/ERC4626Module.t.sol new file mode 100644 index 0000000..10c96a5 --- /dev/null +++ b/test/modules/vault/ERC4626Module.t.sol @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.13; + +import {TestBase} from "../../core/TestBase.sol"; +import {DataTypes} from "../../../src/common/DataTypes.sol"; +import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {console} from "forge-std/console.sol"; + +contract ERC4626ModuleTest is TestBase { + Superloop public superloopImplementation; + address public user; + + function setUp() public override { + super.setUp(); + + vm.startPrank(admin); + _deployModules(); + + address[] memory modules = new address[](2); + modules[0] = address(vaultSupplyModule); + modules[1] = address(vaultWithdrawModule); + + DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ + asset: environment.vaultAsset, + name: "Vault", + symbol: "VLT", + supplyCap: 100000 * 10 ** environment.vaultAssetDecimals, + minimumDepositAmount: 100, + instantWithdrawFee: 0, + superloopModuleRegistry: address(moduleRegistry), + modules: modules, + accountant: mockModule, + withdrawManager: mockModule, + depositManager: mockModule, + cashReserve: 1000, + vaultAdmin: admin, + treasury: treasury, + vaultOperator: admin + }); + superloopImplementation = new Superloop(); + + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(superloopImplementation), + address(this), + abi.encodeWithSelector(Superloop.initialize.selector, initData) + ); + superloop = Superloop(payable(address(proxy))); + vm.stopPrank(); + + user = makeAddr("user"); + vm.label(user, "user"); + vm.label(address(superloop), "superloop"); + } + + function test_VaultSupply() public { + if (environment.chainId != 1) return; + + address stakingVault = environment.lendAssets[1]; + + deal(environment.vaultAsset, address(superloop), 1000 * 10 ** environment.vaultAssetDecimals); + + DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); + moduleExecutionData[0] = DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.DELEGATECALL, + module: address(vaultSupplyModule), + data: abi.encodeWithSelector( + vaultSupplyModule.execute.selector, + DataTypes.VaultActionParams({vault: stakingVault, amount: 1000 * 10 ** environment.vaultAssetDecimals}) + ) + }); + + uint256 currentBalance = IERC20(stakingVault).balanceOf(address(superloop)); + + vm.prank(admin); + superloop.operate(moduleExecutionData); + + uint256 finalBalance = IERC20(stakingVault).balanceOf(address(superloop)); + + assert(finalBalance > currentBalance); + } + + function test_VaultWithdraw() public { + if (environment.chainId != 1) return; + // create supply + test_VaultSupply(); + + address stakingVault = environment.lendAssets[1]; + address underlyingAsset = environment.lendAssets[0]; + + DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); + moduleExecutionData[0] = DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.DELEGATECALL, + module: address(vaultWithdrawModule), + data: abi.encodeWithSelector( + vaultWithdrawModule.execute.selector, + DataTypes.VaultActionParams({vault: stakingVault, amount: type(uint256).max}) + ) + }); + + // expected to revert in this case becase direct withdraw from vault is not allowed for sUSDe + vm.prank(admin); + vm.expectRevert(); + superloop.operate(moduleExecutionData); + } +} From 0275a6540fc15859c95be5b7308eac250f8be10f Mon Sep 17 00:00:00 2001 From: Priyam Anand Date: Mon, 26 Jan 2026 10:45:23 +0530 Subject: [PATCH 09/21] chore: refactored vault module --- src/modules/morpho/MorphoFlashloanModule.sol | 14 ++++++ src/modules/vault/VaultActionModule.sol | 53 ++++++++++++++++++++ src/modules/vault/VaultSupplyModule.sol | 13 ++--- src/modules/vault/VaultWithdrawModule.sol | 13 ++--- test/modules/vault/ERC4626Module.t.sol | 1 - 5 files changed, 73 insertions(+), 21 deletions(-) create mode 100644 src/modules/morpho/MorphoFlashloanModule.sol create mode 100644 src/modules/vault/VaultActionModule.sol diff --git a/src/modules/morpho/MorphoFlashloanModule.sol b/src/modules/morpho/MorphoFlashloanModule.sol new file mode 100644 index 0000000..2eec22c --- /dev/null +++ b/src/modules/morpho/MorphoFlashloanModule.sol @@ -0,0 +1,14 @@ +// // SPDX-License-Identifier: MIT +// pragma solidity ^0.8.13; + +// import {IMorpho} from "./IMorpho.sol"; +// import {DataTypes} from "../../common/DataTypes.sol"; +// import {Context} from "openzeppelin-contracts/contracts/utils/Context.sol"; + +// contract MorphoFlashloanModule is Context { +// IMorpho public immutable morpho; + +// constructor(address morpho_) { +// morpho = IMorpho(morpho_); +// } +// } \ No newline at end of file diff --git a/src/modules/vault/VaultActionModule.sol b/src/modules/vault/VaultActionModule.sol new file mode 100644 index 0000000..82027c9 --- /dev/null +++ b/src/modules/vault/VaultActionModule.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import {Errors} from "../../common/Errors.sol"; +import {SuperloopStorage} from "../../core/lib/SuperloopStorage.sol"; +import {DataTypes} from "../../common/DataTypes.sol"; + +/** + * @title VaultActionModule + * @author Superlend + * @notice Abstract contract for vault action modules providing base functionality + * @dev Provides common vault integration and execution context validation + */ +abstract contract VaultActionModule { + /** + * @notice Emitted when assets are supplied to a vault + * @param vault The address of the vault + * @param amount The amount of the underlying asset supplied + * @param shares The amount of shares minted + */ + event VaultSupplied(address indexed vault, uint256 amount, uint256 shares); + + /** + * @notice Emitted when assets are withdrawn from a vault + * @param vault The address of the vault + * @param amount The amount of the underlying asset withdrawn + * @param shares The amount of shares withdrawn + */ + event VaultWithdrawn(address indexed vault, uint256 amount, uint256 shares); + + /** + * @notice Executes a vault action with the provided parameters + * @param params The parameters for the vault action + */ + function execute(DataTypes.VaultActionParams memory params) external virtual; + + /** + * @notice Modifier to ensure the function is called within an execution context + * @dev Reverts if not in execution context + */ + modifier onlyExecutionContext() { + require(_isExecutionContext(), Errors.NOT_IN_EXECUTION_CONTEXT); + _; + } + + /** + * @notice Internal function to check if the current call is within an execution context + * @return True if in execution context, false otherwise + */ + function _isExecutionContext() internal view returns (bool) { + return SuperloopStorage.isInExecutionContext(); + } +} diff --git a/src/modules/vault/VaultSupplyModule.sol b/src/modules/vault/VaultSupplyModule.sol index 0d62a88..ae162ec 100644 --- a/src/modules/vault/VaultSupplyModule.sol +++ b/src/modules/vault/VaultSupplyModule.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.13; import {IERC4626} from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import {VaultActionModule} from "./VaultActionModule.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {DataTypes} from "../../common/DataTypes.sol"; @@ -12,20 +13,12 @@ import {DataTypes} from "../../common/DataTypes.sol"; * @notice Module for supplying assets to a vault * @dev Extends IERC4626 to provide vault supply functionality */ -contract VaultSupplyModule { - /** - * @notice Emitted when assets are supplied to a vault - * @param vault The address of the vault - * @param amount The amount of the underlying asset supplied - * @param shares The amount of shares minted - */ - event VaultSupplied(address indexed vault, uint256 amount, uint256 shares); - +contract VaultSupplyModule is VaultActionModule { /** * @notice Executes the supply operation * @param params The parameters for the supply operation */ - function execute(DataTypes.VaultActionParams memory params) external { + function execute(DataTypes.VaultActionParams memory params) external override onlyExecutionContext { address underlyingAsset = IERC4626(params.vault).asset(); uint256 amount = params.amount == type(uint256).max ? IERC20(underlyingAsset).balanceOf(address(this)) : params.amount; diff --git a/src/modules/vault/VaultWithdrawModule.sol b/src/modules/vault/VaultWithdrawModule.sol index 3833a5c..5ec5f4c 100644 --- a/src/modules/vault/VaultWithdrawModule.sol +++ b/src/modules/vault/VaultWithdrawModule.sol @@ -5,6 +5,7 @@ import {IERC4626} from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.s import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {DataTypes} from "../../common/DataTypes.sol"; +import {VaultActionModule} from "./VaultActionModule.sol"; /** * @title VaultWithdrawModule @@ -12,20 +13,12 @@ import {DataTypes} from "../../common/DataTypes.sol"; * @notice Module for withdrawing assets from a vault * @dev Extends IERC4626 to provide vault withdraw functionality */ -contract VaultWithdrawModule { - /** - * @notice Emitted when assets are withdrawn from a vault - * @param vault The address of the vault - * @param amount The amount of the underlying asset withdrawn - * @param shares The amount of shares withdrawn - */ - event VaultWithdrawn(address indexed vault, uint256 amount, uint256 shares); - +contract VaultWithdrawModule is VaultActionModule { /** * @notice Executes the withdraw operation * @param params The parameters for the withdraw operation */ - function execute(DataTypes.VaultActionParams memory params) external { + function execute(DataTypes.VaultActionParams memory params) external override onlyExecutionContext { uint256 amount = params.amount == type(uint256).max ? IERC4626(params.vault).maxWithdraw(address(this)) : params.amount; diff --git a/test/modules/vault/ERC4626Module.t.sol b/test/modules/vault/ERC4626Module.t.sol index 10c96a5..ed260fc 100644 --- a/test/modules/vault/ERC4626Module.t.sol +++ b/test/modules/vault/ERC4626Module.t.sol @@ -88,7 +88,6 @@ contract ERC4626ModuleTest is TestBase { test_VaultSupply(); address stakingVault = environment.lendAssets[1]; - address underlyingAsset = environment.lendAssets[0]; DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); moduleExecutionData[0] = DataTypes.ModuleExecutionData({ From 3079a5a351991c3e6c1e7610785a254b60e75aa1 Mon Sep 17 00:00:00 2001 From: Priyam Anand Date: Mon, 26 Jan 2026 11:42:53 +0530 Subject: [PATCH 10/21] feat: morpho flashloan module --- .gitmodules | 3 + lib/morpho-blue | 1 + remappings.txt | 3 +- src/common/DataTypes.sol | 12 ++++ .../callback/MorphoCallbackHandler.sol | 28 +++++++++ src/modules/morpho/MorphoFlashloanModule.sol | 57 +++++++++++++++---- 6 files changed, 92 insertions(+), 12 deletions(-) create mode 160000 lib/morpho-blue create mode 100644 src/modules/callback/MorphoCallbackHandler.sol diff --git a/.gitmodules b/.gitmodules index e26b691..4b7350e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "lib/aave-v3-core"] path = lib/aave-v3-core url = https://github.com/aave/aave-v3-core +[submodule "lib/morpho-blue"] + path = lib/morpho-blue + url = https://github.com/morpho-org/morpho-blue diff --git a/lib/morpho-blue b/lib/morpho-blue new file mode 160000 index 0000000..55d2d99 --- /dev/null +++ b/lib/morpho-blue @@ -0,0 +1 @@ +Subproject commit 55d2d99304fb3fb930c688462ae2ccabb1d533ad diff --git a/remappings.txt b/remappings.txt index 64bcbbd..8d6c14f 100644 --- a/remappings.txt +++ b/remappings.txt @@ -4,4 +4,5 @@ forge-std/=lib/forge-std/src/ halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/ openzeppelin-contracts/=lib/openzeppelin-contracts/ aave-v3-core/=lib/aave-v3-core/ -openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/ \ No newline at end of file +openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/ +morpho-blue/=lib/morpho-blue/src/ \ No newline at end of file diff --git a/src/common/DataTypes.sol b/src/common/DataTypes.sol index 632b70a..f1c6ca1 100644 --- a/src/common/DataTypes.sol +++ b/src/common/DataTypes.sol @@ -387,4 +387,16 @@ library DataTypes { address vault; uint256 amount; } + + /** + * @notice Structure for Morpho flashloan parameters + * @param asset The address of the asset to flashloan + * @param amount The amount to flashloan + * @param callbackExecutionData The data to execute in the flashloan callback + */ + struct MorphoFlashloanParams { + address asset; + uint256 amount; + bytes callbackExecutionData; + } } diff --git a/src/modules/callback/MorphoCallbackHandler.sol b/src/modules/callback/MorphoCallbackHandler.sol new file mode 100644 index 0000000..576c6e3 --- /dev/null +++ b/src/modules/callback/MorphoCallbackHandler.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import {Context} from "openzeppelin-contracts/contracts/utils/Context.sol"; +import {DataTypes} from "../../common/DataTypes.sol"; + +/** + * @title MorphoCallbackHandler + * @author Superlend + * @notice Callback handler for Morpho flashloan operations + * @dev Handles flashloan callbacks and adjusts approval amounts to include premiums + */ +contract MorphoCallbackHandler is Context { + /** + * @notice Executes the flashloan callback operation + * @param params The encoded callback data parameters + * @return callbackData The processed callback data + * @return success Always returns true to indicate successful execution + */ + function onMorphoFlashLoan(uint256, bytes calldata params) + external + pure + returns (DataTypes.CallbackData memory, bool) + { + DataTypes.CallbackData memory callbackData = abi.decode(params, (DataTypes.CallbackData)); + return (callbackData, true); + } +} diff --git a/src/modules/morpho/MorphoFlashloanModule.sol b/src/modules/morpho/MorphoFlashloanModule.sol index 2eec22c..2ae5ee0 100644 --- a/src/modules/morpho/MorphoFlashloanModule.sol +++ b/src/modules/morpho/MorphoFlashloanModule.sol @@ -1,14 +1,49 @@ -// // SPDX-License-Identifier: MIT -// pragma solidity ^0.8.13; +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; -// import {IMorpho} from "./IMorpho.sol"; -// import {DataTypes} from "../../common/DataTypes.sol"; -// import {Context} from "openzeppelin-contracts/contracts/utils/Context.sol"; +import {IMorphoBase} from "morpho-blue/interfaces/IMorpho.sol"; +import {DataTypes} from "../../common/DataTypes.sol"; +import {Context} from "openzeppelin-contracts/contracts/utils/Context.sol"; +import {Errors} from "../../common/Errors.sol"; +import {SuperloopStorage} from "../../core/lib/SuperloopStorage.sol"; +import {SafeERC20} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol"; +import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -// contract MorphoFlashloanModule is Context { -// IMorpho public immutable morpho; +contract MorphoFlashloanModule is Context { + event MorphoFlashloanExecuted(address indexed asset, uint256 amount, address indexed borrower); -// constructor(address morpho_) { -// morpho = IMorpho(morpho_); -// } -// } \ No newline at end of file + IMorphoBase public immutable morpho; + + constructor(address morpho_) { + morpho = IMorphoBase(morpho_); + } + + function execute(DataTypes.MorphoFlashloanParams memory params) external onlyExecutionContext { + // format the params for flashloan + bytes memory callbackData = abi.encode( + DataTypes.CallbackData({ + asset: params.asset, + addressToApprove: address(morpho), + amountToApprove: params.amount, + executionData: params.callbackExecutionData + }) + ); + + // call the flashloan + morpho.flashLoan(params.asset, params.amount, callbackData); + + // remove approval from morpho after flashloan is done + SafeERC20.forceApprove(IERC20(params.asset), address(morpho), 0); + + emit MorphoFlashloanExecuted(params.asset, params.amount, address(this)); + } + + modifier onlyExecutionContext() { + require(_isExecutionContext(), Errors.NOT_IN_EXECUTION_CONTEXT); + _; + } + + function _isExecutionContext() internal view returns (bool) { + return SuperloopStorage.isInExecutionContext(); + } +} From de5b9c52c11f8c24c3d35fce5f738c185b137b62 Mon Sep 17 00:00:00 2001 From: Priyam Anand Date: Tue, 27 Jan 2026 10:31:09 +0530 Subject: [PATCH 11/21] test: added morpho flashloan module --- test/core/TestBase.sol | 11 +++ test/core/TestEnv.sol | 13 ++- .../morpho/MorphoFlashloanModule.t.sol | 96 +++++++++++++++++++ 3 files changed, 116 insertions(+), 4 deletions(-) create mode 100644 test/modules/morpho/MorphoFlashloanModule.t.sol diff --git a/test/core/TestBase.sol b/test/core/TestBase.sol index 10fc9d3..60d16b2 100644 --- a/test/core/TestBase.sol +++ b/test/core/TestBase.sol @@ -10,6 +10,7 @@ import {DataTypes} from "../../src/common/DataTypes.sol"; import {SuperloopModuleRegistry} from "../../src/core/ModuleRegistry/ModuleRegistry.sol"; import {AaveV3FlashloanModule} from "../../src/modules/aave/AaveV3FlashloanModule.sol"; import {AaveV3CallbackHandler} from "../../src/modules/callback/AaveV3CallbackHandler.sol"; +import {MorphoCallbackHandler} from "../../src/modules/callback/MorphoCallbackHandler.sol"; import {AaveV3EmodeModule} from "../../src/modules/aave/AaveV3EmodeModule.sol"; import {AaveV3SupplyModule} from "../../src/modules/aave/AaveV3SupplyModule.sol"; import {AaveV3WithdrawModule} from "../../src/modules/aave/AaveV3WithdrawModule.sol"; @@ -38,6 +39,8 @@ import {HyperbeatStakingModule} from "../../src/modules/stake/hyperliquid/Hyperb import {VaultSupplyModule} from "../../src/modules/vault/VaultSupplyModule.sol"; import {VaultWithdrawModule} from "../../src/modules/vault/VaultWithdrawModule.sol"; +import {MorphoFlashloanModule} from "../../src/modules/morpho/MorphoFlashloanModule.sol"; + abstract contract TestBase is TestEnv { // address public constant AAVE_V3_POOL_ADDRESSES_PROVIDER = // 0x5ccF60c7E10547c5389E9cBFf543E5D0Db9F4feC; @@ -80,10 +83,12 @@ abstract contract TestBase is TestEnv { SuperloopModuleRegistry public moduleRegistry; Superloop public superloop; AaveV3FlashloanModule public flashloanModule; + MorphoFlashloanModule public morphoFlashloanModule; DepositManagerCallbackHandler public depositManagerCallbackHandler; WithdrawManagerCallbackHandler public withdrawManagerCallbackHandler; AaveV3PreliquidationFallbackHandler public preliquidationFallbackHandler; AaveV3CallbackHandler public callbackHandler; + MorphoCallbackHandler public morphoCallbackHandler; AaveV3SupplyModule public supplyModule; AaveV3WithdrawModule public withdrawModule; AaveV3BorrowModule public borrowModule; @@ -155,12 +160,18 @@ abstract contract TestBase is TestEnv { } function _deployModules() internal { + morphoFlashloanModule = new MorphoFlashloanModule(environment.morpho); + moduleRegistry.setModule("MorphoFlashloanModule", address(morphoFlashloanModule)); + flashloanModule = new AaveV3FlashloanModule(environment.poolAddressesProvider); moduleRegistry.setModule("AaveV3FlashloanModule", address(flashloanModule)); callbackHandler = new AaveV3CallbackHandler(); moduleRegistry.setModule("AaveV3CallbackHandler", address(callbackHandler)); + morphoCallbackHandler = new MorphoCallbackHandler(); + moduleRegistry.setModule("MorphoCallbackHandler", address(morphoCallbackHandler)); + emodeModule = new AaveV3EmodeModule(environment.poolAddressesProvider); moduleRegistry.setModule("AaveV3EmodeModule", address(emodeModule)); diff --git a/test/core/TestEnv.sol b/test/core/TestEnv.sol index e2c84a1..139fbe3 100644 --- a/test/core/TestEnv.sol +++ b/test/core/TestEnv.sol @@ -21,6 +21,7 @@ abstract contract TestEnv is Test { address router; address stablecoin; address stablecoinWhale; + address morpho; } // etlk chain @@ -77,7 +78,8 @@ abstract contract TestEnv is Test { poolAdmin: 0x669bd328f6C494949Ed9fB2dc8021557A6Dd005f, router: 0xbfe9C246A5EdB4F021C8910155EC93e7CfDaB7a0, stablecoin: 0x2C03058C8AFC06713be23e58D2febC8337dbfE6A, - stablecoinWhale: 0x998098A1B2E95e2b8f15360676428EdFd976861f + stablecoinWhale: 0x998098A1B2E95e2b8f15360676428EdFd976861f, + morpho: 0x0000000000000000000000000000000000000000 }) ); @@ -99,7 +101,8 @@ abstract contract TestEnv is Test { poolAdmin: 0x669bd328f6C494949Ed9fB2dc8021557A6Dd005f, router: 0xbfe9C246A5EdB4F021C8910155EC93e7CfDaB7a0, stablecoin: 0x2C03058C8AFC06713be23e58D2febC8337dbfE6A, - stablecoinWhale: 0x998098A1B2E95e2b8f15360676428EdFd976861f + stablecoinWhale: 0x998098A1B2E95e2b8f15360676428EdFd976861f, + morpho: 0x0000000000000000000000000000000000000000 }) ); @@ -121,7 +124,8 @@ abstract contract TestEnv is Test { poolAdmin: 0x72B8fD3eb0c08275b8B60F96aAb0C8a50Cb80EcA, router: address(0), stablecoin: USDC_ETH, - stablecoinWhale: USDC_ETH_Whale + stablecoinWhale: USDC_ETH_Whale, + morpho: 0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb }) ); @@ -143,7 +147,8 @@ abstract contract TestEnv is Test { poolAdmin: 0x72B8fD3eb0c08275b8B60F96aAb0C8a50Cb80EcA, router: address(0), stablecoin: USDC_ETH, - stablecoinWhale: USDC_ETH_Whale + stablecoinWhale: USDC_ETH_Whale, + morpho: 0x0000000000000000000000000000000000000000 }) ); } diff --git a/test/modules/morpho/MorphoFlashloanModule.t.sol b/test/modules/morpho/MorphoFlashloanModule.t.sol new file mode 100644 index 0000000..0953f04 --- /dev/null +++ b/test/modules/morpho/MorphoFlashloanModule.t.sol @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.13; + +import {TestBase} from "../../core/TestBase.sol"; +import {DataTypes} from "../../../src/common/DataTypes.sol"; +import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; +import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; +import {TransparentUpgradeableProxy} from + "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +import {IMorphoFlashLoanCallback} from "morpho-blue/interfaces/IMorphoCallbacks.sol"; +import {MorphoFlashloanModule} from "../../../src/modules/morpho/MorphoFlashloanModule.sol"; + +contract MorphoFlashloanModuleTest is TestBase { + Superloop public superloopImplementation; + ProxyAdmin public proxyAdmin; + address public user; + + function setUp() public override { + super.setUp(); + + vm.startPrank(admin); + _deployModules(); + + address[] memory modules = new address[](1); + modules[0] = address(morphoFlashloanModule); + + DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ + asset: environment.vaultAsset, + name: "Vault", + symbol: "VLT", + supplyCap: 100000 * 10 ** environment.vaultAssetDecimals, + minimumDepositAmount: 100, + instantWithdrawFee: 0, + superloopModuleRegistry: address(moduleRegistry), + modules: modules, + accountant: mockModule, + withdrawManager: mockModule, + depositManager: mockModule, + cashReserve: 1000, + vaultAdmin: admin, + treasury: treasury, + vaultOperator: admin + }); + superloopImplementation = new Superloop(); + proxyAdmin = new ProxyAdmin(address(this)); + + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(superloopImplementation), + address(proxyAdmin), + abi.encodeWithSelector(Superloop.initialize.selector, initData) + ); + superloop = Superloop(payable(address(proxy))); + + bytes32 key = + keccak256(abi.encodePacked(environment.morpho, IMorphoFlashLoanCallback.onMorphoFlashLoan.selector)); + superloop.setCallbackHandler(key, address(morphoCallbackHandler)); + vm.stopPrank(); + user = makeAddr("user"); + + vm.label(user, "user"); + vm.label(address(superloop), "superloop"); + } + + function test_FlashloanBasicFlow() public { + vm.startPrank(environment.vaultAssetWhale); + IERC20(environment.vaultAsset).transfer(address(superloop), 1000 * 10 ** environment.vaultAssetDecimals); + vm.stopPrank(); + + // Arrange + uint256 flashloanAmount = 1000 * 10 ** environment.vaultAssetDecimals; // 1000 vaultAsset + + // Create flashloan params + DataTypes.MorphoFlashloanParams memory flashloanParams = DataTypes.MorphoFlashloanParams({ + asset: environment.vaultAsset, + amount: flashloanAmount, + callbackExecutionData: "" // No additional execution data for simple return + }); + + // Create module execution data + DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); + moduleExecutionData[0] = DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.DELEGATECALL, + module: address(morphoFlashloanModule), + data: abi.encodeWithSelector(morphoFlashloanModule.execute.selector, flashloanParams) + }); + + // Act + vm.prank(admin); + // catch event MorphoFlashloanExecuted + vm.expectEmit(); + emit MorphoFlashloanModule.MorphoFlashloanExecuted(environment.vaultAsset, flashloanAmount, address(superloop)); + superloop.operate(moduleExecutionData); + } +} From ac9d89799f734c471d6c7abe928b91a93a4d3407 Mon Sep 17 00:00:00 2001 From: Priyam Anand Date: Tue, 27 Jan 2026 11:04:54 +0530 Subject: [PATCH 12/21] fix: octane finding 1 --- src/core/DepositManager/DepositManager.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/DepositManager/DepositManager.sol b/src/core/DepositManager/DepositManager.sol index b6e67ad..654f21d 100644 --- a/src/core/DepositManager/DepositManager.sol +++ b/src/core/DepositManager/DepositManager.sol @@ -267,7 +267,7 @@ contract DepositManager is Initializable, ReentrancyGuardUpgradeable, Context, D uint256 totalSupplyAfter = Math.mulDiv( snapshot.totalSupplyBefore, snapshot.totalAssetsAfter, snapshot.totalAssetsBefore, Math.Rounding.Ceil ); - uint256 totalNewSharesToMint = (totalSupplyAfter + 10 ** decimalOffset) - snapshot.totalSupplyBefore; + uint256 totalNewSharesToMint = totalSupplyAfter - snapshot.totalSupplyBefore; return totalNewSharesToMint; } From e75d6e59f0986025164247ea7b1cbffd5ebd055b Mon Sep 17 00:00:00 2001 From: Priyam Anand Date: Tue, 27 Jan 2026 11:41:33 +0530 Subject: [PATCH 13/21] feat: octane findings --- src/core/DepositManager/DepositManager.sol | 2 +- src/core/Superloop/Superloop.sol | 3 +- src/core/Superloop/SuperloopVault.sol | 80 ++++++++++++---------- 3 files changed, 46 insertions(+), 39 deletions(-) diff --git a/src/core/DepositManager/DepositManager.sol b/src/core/DepositManager/DepositManager.sol index 654f21d..d3d53fc 100644 --- a/src/core/DepositManager/DepositManager.sol +++ b/src/core/DepositManager/DepositManager.sol @@ -267,7 +267,7 @@ contract DepositManager is Initializable, ReentrancyGuardUpgradeable, Context, D uint256 totalSupplyAfter = Math.mulDiv( snapshot.totalSupplyBefore, snapshot.totalAssetsAfter, snapshot.totalAssetsBefore, Math.Rounding.Ceil ); - uint256 totalNewSharesToMint = totalSupplyAfter - snapshot.totalSupplyBefore; + uint256 totalNewSharesToMint = totalSupplyAfter - snapshot.totalSupplyBefore; return totalNewSharesToMint; } diff --git a/src/core/Superloop/Superloop.sol b/src/core/Superloop/Superloop.sol index cc58d6e..c359f42 100644 --- a/src/core/Superloop/Superloop.sol +++ b/src/core/Superloop/Superloop.sol @@ -134,7 +134,8 @@ contract Superloop is SuperloopVault, SuperloopActions, SuperloopBase { } function realizePerformanceFee() external { - _realizePerformanceFee(); + uint256 totalAssetsCached = totalAssets(); + _realizePerformanceFee(totalAssetsCached); } /** diff --git a/src/core/Superloop/SuperloopVault.sol b/src/core/Superloop/SuperloopVault.sol index 24964cf..699cc00 100644 --- a/src/core/Superloop/SuperloopVault.sol +++ b/src/core/Superloop/SuperloopVault.sol @@ -50,19 +50,23 @@ abstract contract SuperloopVault is ERC4626Upgradeable, ReentrancyGuardUpgradeab } function previewDeposit(uint256 assets) public view override returns (uint256) { - return _convertToSharesWithPerformanceFee(assets, Math.Rounding.Floor); + uint256 totalAssetsCached = totalAssets(); + return _convertToSharesWithPerformanceFee(assets, totalAssetsCached, Math.Rounding.Floor); } function previewMint(uint256 shares) public view override returns (uint256) { - return _convertToAssetsWithPerformanceFee(shares, Math.Rounding.Ceil); + uint256 totalAssetsCached = totalAssets(); + return _convertToAssetsWithPerformanceFee(shares, totalAssetsCached, Math.Rounding.Ceil); } function previewWithdraw(uint256 assets) public view override returns (uint256) { - return _convertToSharesWithPerformanceFee(assets, Math.Rounding.Ceil); + uint256 totalAssetsCached = totalAssets(); + return _convertToSharesWithPerformanceFee(assets, totalAssetsCached, Math.Rounding.Ceil); } function previewRedeem(uint256 shares) public view override returns (uint256) { - return _convertToAssetsWithPerformanceFee(shares, Math.Rounding.Floor); + uint256 totalAssetsCached = totalAssets(); + return _convertToAssetsWithPerformanceFee(shares, totalAssetsCached, Math.Rounding.Floor); } function maxMint(address) public view override returns (uint256) { @@ -77,15 +81,15 @@ abstract contract SuperloopVault is ERC4626Upgradeable, ReentrancyGuardUpgradeab } function deposit(uint256 assets, address receiver) public override nonReentrant whenNotPaused returns (uint256) { - // realize performance fee - _realizePerformanceFee(); + uint256 totalAssetsCached = totalAssets(); + _realizePerformanceFee(totalAssetsCached); require(assets > 0, Errors.INVALID_AMOUNT); // check supply cap require(assets <= maxDeposit(address(0)), Errors.SUPPLY_CAP_EXCEEDED); - // calculate the cash reserve at curren total assets - uint256 cashReserveShortfall = _getCashReserveShortfall(); + // calculate the cash reserve at curren total + uint256 cashReserveShortfall = _getCashReserveShortfall(totalAssetsCached); require(assets <= cashReserveShortfall, Errors.INSUFFICIENT_CASH_SHORTFALL); // preview deposit @@ -98,14 +102,15 @@ abstract contract SuperloopVault is ERC4626Upgradeable, ReentrancyGuardUpgradeab function mint(uint256 shares, address receiver) public override nonReentrant whenNotPaused returns (uint256) { // realize performance fee - _realizePerformanceFee(); + uint256 totalAssetsCached = totalAssets(); + _realizePerformanceFee(totalAssetsCached); require(shares > 0, Errors.INVALID_SHARES_AMOUNT); require(shares <= maxMint(address(0)), Errors.SUPPLY_CAP_EXCEEDED); uint256 assets = previewMint(shares); - uint256 cashReserveShortfall = _getCashReserveShortfall(); + uint256 cashReserveShortfall = _getCashReserveShortfall(totalAssetsCached); require(assets <= cashReserveShortfall, Errors.INSUFFICIENT_CASH_SHORTFALL); require(assets > 0, Errors.INVALID_AMOUNT); @@ -122,7 +127,8 @@ abstract contract SuperloopVault is ERC4626Upgradeable, ReentrancyGuardUpgradeab returns (uint256) { // realize performance fee - _realizePerformanceFee(); + uint256 totalAssetsCached = totalAssets(); + _realizePerformanceFee(totalAssetsCached); // check for max withdraw require(assets <= maxWithdraw(owner), Errors.INSUFFICIENT_BALANCE); @@ -150,7 +156,8 @@ abstract contract SuperloopVault is ERC4626Upgradeable, ReentrancyGuardUpgradeab returns (uint256) { // realize performance fee - _realizePerformanceFee(); + uint256 totalAssetsCached = totalAssets(); + _realizePerformanceFee(totalAssetsCached); // check for max redeem require(shares <= maxRedeem(owner), Errors.INSUFFICIENT_BALANCE); @@ -199,7 +206,8 @@ abstract contract SuperloopVault is ERC4626Upgradeable, ReentrancyGuardUpgradeab function _seed(uint256 assets) internal returns (uint256) { // realize performance fee => set last ex. rate to 1 - _realizePerformanceFee(); + uint256 totalAssetsCached = totalAssets(); + _realizePerformanceFee(totalAssetsCached); require(assets > 0, Errors.INVALID_AMOUNT); // check supply cap @@ -213,13 +221,11 @@ abstract contract SuperloopVault is ERC4626Upgradeable, ReentrancyGuardUpgradeab return shares; } - function _realizePerformanceFee() internal { + function _realizePerformanceFee(uint256 totalAssetsCached) internal { SuperloopStorage.SuperloopEssentialRoles storage $ = SuperloopStorage.getSuperloopEssentialRolesStorage(); + (uint256 exchangeRate, uint8 decimals) = _getCurrentExchangeRate(totalAssetsCached); - // calculate current exchange rate - (uint256 exchangeRate, uint8 decimals) = _getCurrentExchangeRate(); - - uint256 sharesToMint = _getPerformanceFeeAndShares(exchangeRate, $.accountant, decimals); + uint256 sharesToMint = _getPerformanceFeeAndShares(exchangeRate, $.accountant, decimals, totalAssetsCached); // if vault made profit, take a performance fee if (sharesToMint > 0) { @@ -230,16 +236,16 @@ abstract contract SuperloopVault is ERC4626Upgradeable, ReentrancyGuardUpgradeab } // update the last realized fee exchange rate on the accountant module - (exchangeRate,) = _getCurrentExchangeRate(); + (exchangeRate,) = _getCurrentExchangeRate(totalAssetsCached); IAccountantModule($.accountant).setLastRealizedFeeExchangeRate(exchangeRate, totalSupply()); } - function _getPerformanceFeeAndShares(uint256 exchangeRate, address accountant, uint8 decimals) - internal - view - returns (uint256 shares) - { - uint256 totalAssetsCached = totalAssets(); + function _getPerformanceFeeAndShares( + uint256 exchangeRate, + address accountant, + uint8 decimals, + uint256 totalAssetsCached + ) internal view returns (uint256 shares) { uint256 totalSupplyCached = totalSupply(); // get performance fee @@ -256,43 +262,43 @@ abstract contract SuperloopVault is ERC4626Upgradeable, ReentrancyGuardUpgradeab return shares; } - function _getCurrentExchangeRate() internal view returns (uint256, uint8) { + function _getCurrentExchangeRate(uint256 totalAssetsCached) internal view returns (uint256, uint8) { uint8 decimals = IERC20Metadata(asset()).decimals(); uint256 assets = 1 * 10 ** decimals; return ( - Math.mulDiv(assets, totalAssets() + 1, totalSupply() + 10 ** _decimalsOffset(), Math.Rounding.Floor), + Math.mulDiv(assets, totalAssetsCached + 1, totalSupply() + 10 ** _decimalsOffset(), Math.Rounding.Floor), decimals ); } - function _convertToSharesWithPerformanceFee(uint256 assets, Math.Rounding rounding) + function _convertToSharesWithPerformanceFee(uint256 assets, uint256 totalAssetsCached, Math.Rounding rounding) internal view returns (uint256) { - (uint256 exchangeRate, uint8 decimals) = _getCurrentExchangeRate(); + (uint256 exchangeRate, uint8 decimals) = _getCurrentExchangeRate(totalAssetsCached); uint256 treasuryShares = _getPerformanceFeeAndShares( - exchangeRate, SuperloopStorage.getSuperloopEssentialRolesStorage().accountant, decimals + exchangeRate, SuperloopStorage.getSuperloopEssentialRolesStorage().accountant, decimals, totalAssetsCached ); uint256 _totalSupply = totalSupply() + treasuryShares + 10 ** _decimalsOffset(); - uint256 _totalAssets = totalAssets() + 1; + uint256 _totalAssets = totalAssetsCached + 1; uint256 shares = Math.mulDiv(assets, _totalSupply, _totalAssets, rounding); return shares; } - function _convertToAssetsWithPerformanceFee(uint256 shares, Math.Rounding rounding) + function _convertToAssetsWithPerformanceFee(uint256 shares, uint256 totalAssetsCached, Math.Rounding rounding) internal view returns (uint256) { - (uint256 exchangeRate, uint8 decimals) = _getCurrentExchangeRate(); + (uint256 exchangeRate, uint8 decimals) = _getCurrentExchangeRate(totalAssetsCached); uint256 treasuryShares = _getPerformanceFeeAndShares( - exchangeRate, SuperloopStorage.getSuperloopEssentialRolesStorage().accountant, decimals + exchangeRate, SuperloopStorage.getSuperloopEssentialRolesStorage().accountant, decimals, totalAssetsCached ); uint256 _totalSupply = totalSupply() + treasuryShares + 10 ** _decimalsOffset(); - uint256 _totalAssets = totalAssets() + 1; + uint256 _totalAssets = totalAssetsCached + 1; uint256 assets = Math.mulDiv(shares, _totalAssets, _totalSupply, rounding); @@ -303,9 +309,9 @@ abstract contract SuperloopVault is ERC4626Upgradeable, ReentrancyGuardUpgradeab return SuperloopStorage.DECIMALS_OFFSET; } - function _getCashReserveShortfall() internal view returns (uint256) { + function _getCashReserveShortfall(uint256 totalAssetsCached) internal view returns (uint256) { uint256 cashReserveExpected = Math.mulDiv( - totalAssets(), + totalAssetsCached, SuperloopStorage.getSuperloopStorage().cashReserve, SuperloopStorage.MAX_BPS_VALUE, Math.Rounding.Floor From b8b3fc83e091c8182b4891730d277049035ab238 Mon Sep 17 00:00:00 2001 From: Priyam Anand Date: Wed, 28 Jan 2026 13:18:44 +0530 Subject: [PATCH 14/21] test: added integration test for deposit flow --- src/core/DepositManager/DepositManager.sol | 8 +- src/mock/IUniPool.sol | 32 + src/mock/MockIRouter.sol | 2 + test/core/TestBase.sol | 6 + test/core/TestEnv.sol | 17 +- test/core/integration/DepositManager.t.sol | 651 ++++++----- test/core/integration/IntegrationBase.sol | 1156 ++++++++++---------- test/modules/dex/UniversalDexModule.t.sol | 6 +- 8 files changed, 1003 insertions(+), 875 deletions(-) create mode 100644 src/mock/IUniPool.sol diff --git a/src/core/DepositManager/DepositManager.sol b/src/core/DepositManager/DepositManager.sol index d3d53fc..ff9cc81 100644 --- a/src/core/DepositManager/DepositManager.sol +++ b/src/core/DepositManager/DepositManager.sol @@ -107,7 +107,7 @@ contract DepositManager is Initializable, ReentrancyGuardUpgradeable, Context, D snapshot.totalAssetsAfter = ISuperloop(cache.vault).totalAssets() + 1; // calculate shares such that the exchange rate is not updated - uint256 totalNewSharesToMint = _calculateSharesToMint(snapshot, cache.vaultDecimalOffset); + uint256 totalNewSharesToMint = _calculateSharesToMint(snapshot); // decrease the total pending deposits before minting shares $.totalPendingDeposits -= data.amount; @@ -259,11 +259,7 @@ contract DepositManager is Initializable, ReentrancyGuardUpgradeable, Context, D }); } - function _calculateSharesToMint(DataTypes.ExchangeRateSnapshot memory snapshot, uint8 decimalOffset) - internal - pure - returns (uint256) - { + function _calculateSharesToMint(DataTypes.ExchangeRateSnapshot memory snapshot) internal pure returns (uint256) { uint256 totalSupplyAfter = Math.mulDiv( snapshot.totalSupplyBefore, snapshot.totalAssetsAfter, snapshot.totalAssetsBefore, Math.Rounding.Ceil ); diff --git a/src/mock/IUniPool.sol b/src/mock/IUniPool.sol new file mode 100644 index 0000000..9c2e31f --- /dev/null +++ b/src/mock/IUniPool.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +interface IUniPool { + struct Slot0 { + // the current price + uint160 sqrtPriceX96; + // the current tick + int24 tick; + // the most-recently updated index of the observations array + uint16 observationIndex; + // the current maximum number of observations that are being stored + uint16 observationCardinality; + // the next maximum number of observations to store, triggered in observations.write + uint16 observationCardinalityNext; + // the current protocol fee as a percentage of the swap fee taken on withdrawal + // represented as an integer denominator (1/x)% + uint8 feeProtocol; + // whether the pool is locked + bool unlocked; + } + + function swap( + address recipient, + bool zeroForOne, + int256 amountSpecified, // +ive means exact input, -ive means exact output + uint160 sqrtPriceLimitX96, + bytes calldata data + ) external returns (int256 amount0, int256 amount1); + + function slot0() external view returns (Slot0 memory); +} diff --git a/src/mock/MockIRouter.sol b/src/mock/MockIRouter.sol index 495b1c9..c838513 100644 --- a/src/mock/MockIRouter.sol +++ b/src/mock/MockIRouter.sol @@ -14,6 +14,7 @@ interface IRouter { address tokenOut; uint24 fee; address recipient; + uint256 deadline; uint256 amountIn; uint256 amountOutMinimum; uint160 sqrtPriceLimitX96; @@ -26,6 +27,7 @@ interface IRouter { address tokenOut; uint24 fee; address recipient; + uint256 deadline; uint256 amountOut; uint256 amountInMaximum; uint160 sqrtPriceLimitX96; diff --git a/test/core/TestBase.sol b/test/core/TestBase.sol index 60d16b2..17b1bd1 100644 --- a/test/core/TestBase.sol +++ b/test/core/TestBase.sol @@ -75,6 +75,8 @@ abstract contract TestBase is TestEnv { uint256 public constant PRE_IF2 = ((80 + BPS) * WAD) / BPS; uint256 public LLTV = (9600 * WAD) / BPS; + bool public USE_MORPHO = false; + TestEnvironment public environment; address public admin; @@ -125,6 +127,10 @@ abstract contract TestBase is TestEnv { uint256 envIndex = 2; // TODO: move this to config environment = testEnvironments[envIndex]; + if (environment.morpho != address(0)) { + USE_MORPHO = true; + } + vm.createSelectFork(environment.chainName); admin = makeAddr("admin"); treasury = makeAddr("treasury"); diff --git a/test/core/TestEnv.sol b/test/core/TestEnv.sol index 139fbe3..f6b9f4c 100644 --- a/test/core/TestEnv.sol +++ b/test/core/TestEnv.sol @@ -22,6 +22,7 @@ abstract contract TestEnv is Test { address stablecoin; address stablecoinWhale; address morpho; + uint8 emodeCategory; } // etlk chain @@ -79,7 +80,8 @@ abstract contract TestEnv is Test { router: 0xbfe9C246A5EdB4F021C8910155EC93e7CfDaB7a0, stablecoin: 0x2C03058C8AFC06713be23e58D2febC8337dbfE6A, stablecoinWhale: 0x998098A1B2E95e2b8f15360676428EdFd976861f, - morpho: 0x0000000000000000000000000000000000000000 + morpho: 0x0000000000000000000000000000000000000000, + emodeCategory: 3 }) ); @@ -102,7 +104,8 @@ abstract contract TestEnv is Test { router: 0xbfe9C246A5EdB4F021C8910155EC93e7CfDaB7a0, stablecoin: 0x2C03058C8AFC06713be23e58D2febC8337dbfE6A, stablecoinWhale: 0x998098A1B2E95e2b8f15360676428EdFd976861f, - morpho: 0x0000000000000000000000000000000000000000 + morpho: 0x0000000000000000000000000000000000000000, + emodeCategory: 2 }) ); @@ -114,7 +117,7 @@ abstract contract TestEnv is Test { vaultAsset: USDe, vaultAssetDecimals: 18, lendAssets: _twoAddressArray(USDe, sUSDe), - borrowAssets: _twoAddressArray(USDT_ETH, USDC_ETH), + borrowAssets: _twoAddressArray(USDC_ETH, USDT_ETH), poolAddressesProvider: 0x2f39d218133AFaB8F2B819B1066c7E434Ad94E9e, poolDataProvider: 0x0a16f2FCC0D44FaE41cc54e079281D84A363bECD, priceOracle: 0x54586bE62E3c3580375aE3723C145253060Ca0C2, @@ -122,10 +125,11 @@ abstract contract TestEnv is Test { vaultAssetWhale: USDeWhale, poolConfigurator: 0x64b761D848206f447Fe2dd461b0c635Ec39EbB27, poolAdmin: 0x72B8fD3eb0c08275b8B60F96aAb0C8a50Cb80EcA, - router: address(0), + router: 0xE592427A0AEce92De3Edee1F18E0157C05861564, stablecoin: USDC_ETH, stablecoinWhale: USDC_ETH_Whale, - morpho: 0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb + morpho: 0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb, + emodeCategory: 2 }) ); @@ -148,7 +152,8 @@ abstract contract TestEnv is Test { router: address(0), stablecoin: USDC_ETH, stablecoinWhale: USDC_ETH_Whale, - morpho: 0x0000000000000000000000000000000000000000 + morpho: 0x0000000000000000000000000000000000000000, + emodeCategory: 1 }) ); } diff --git a/test/core/integration/DepositManager.t.sol b/test/core/integration/DepositManager.t.sol index 95129b0..1e11b58 100644 --- a/test/core/integration/DepositManager.t.sol +++ b/test/core/integration/DepositManager.t.sol @@ -1,290 +1,361 @@ -// // SPDX-License-Identifier: MIT - -// pragma solidity ^0.8.13; - -// import {IntegrationBase} from "./IntegrationBase.sol"; -// import {DataTypes} from "../../../src/common/DataTypes.sol"; -// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -// import {DepositManager} from "../../../src/core/DepositManager/DepositManager.sol"; -// import {console} from "forge-std/Test.sol"; -// import {Errors} from "../../../src/common/Errors.sol"; - -// contract DepositManagerTest is IntegrationBase { -// bool depositAll = false; -// DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; - -// function setUp() public override { -// super.setUp(); - -// DataTypes.AaveV3EmodeParams memory params = DataTypes.AaveV3EmodeParams({emodeCategory: 3}); - -// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); -// moduleExecutionData[0] = DataTypes.ModuleExecutionData({ -// executionType: DataTypes.CallType.DELEGATECALL, -// module: address(emodeModule), -// data: abi.encodeWithSelector(emodeModule.execute.selector, params) -// }); - -// vm.prank(admin); -// superloop.operate(moduleExecutionData); -// } - -// function test_initialize() public view { -// assertEq(depositManager.vault(), address(superloop)); -// assertEq(depositManager.asset(), XTZ); -// assertEq(depositManager.nextDepositRequestId(), 1); -// } - -// function test_requestDeposit_MinimumDepositAmountNotMet() public { -// uint256 depositAmount = 99; - -// vm.startPrank(user1); -// vm.expectRevert(bytes(Errors.INVALID_AMOUNT)); -// depositManager.requestDeposit(depositAmount, address(0)); -// vm.stopPrank(); -// } - -// function test_resolveDepositRequestResolution() public { -// uint256 exchangeRateBefore = superloop.convertToAssets(ONE_SHARE); -// uint256 depositAmount = 100 * XTZ_SCALE; -// _makeDepositRequest(depositAmount, user1, true); - -// // build the operate call -// uint256 supplyAmount = 150 * STXTZ_SCALE; -// uint256 borrowAmount = 60 * XTZ_SCALE; -// uint256 swapAmount = borrowAmount + depositAmount; -// uint256 supplyAmountWithPremium = supplyAmount + (supplyAmount * 1) / 10000; - -// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](3); -// moduleExecutionData[0] = _supplyCall(ST_XTZ, supplyAmount); -// moduleExecutionData[1] = _borrowCall(XTZ, borrowAmount); - -// moduleExecutionData[2] = -// _swapCallExactOutCurve(XTZ, ST_XTZ, XTZ_STXTZ_POOL, swapAmount, supplyAmountWithPremium, XTZ_STXTZ_SWAP); - -// DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); -// intermediateExecutionData[0] = _flashloanCall(ST_XTZ, supplyAmount, abi.encode(moduleExecutionData)); - -// DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); -// finalExecutionData[0] = _resolveDepositRequestsCall(XTZ, depositAmount, abi.encode(intermediateExecutionData)); - -// vm.prank(admin); -// superloop.operate(finalExecutionData); - -// // user 1 should get shares -// DataTypes.DepositRequestData memory depositRequest1 = depositManager.depositRequest(1); -// uint256 user1Shares = superloop.balanceOf(user1); -// assertEq(user1Shares, depositRequest1.sharesMinted); - -// // deposit maanger should not have any xtz now, resolution ptr must have increased, pendingDepsotAmout should be 0 -// assertEq(IERC20(XTZ).balanceOf(address(depositManager)), 0); -// assertEq(depositManager.totalPendingDeposits(), 0); -// assertEq(depositManager.resolutionIdPointer(), 2); - -// // exchange rate before should equal to exchange rate after -// uint256 exchangeRateAfter = superloop.convertToAssets(ONE_SHARE); -// assertTrue( -// exchangeRateAfter > exchangeRateBefore -// ? exchangeRateAfter - exchangeRateBefore < 100 -// : exchangeRateBefore - exchangeRateAfter < 100 -// ); -// } - -// function test_instantDepositWithLimit() public { -// _createPartialDepositWithResolution(depositAll); - -// // i should be able to do an instant deposit of 0.001 xtz -// uint256 user1SharesBalanceBefore = superloop.balanceOf(user1); -// deal(XTZ, user1, XTZ_SCALE); -// vm.startPrank(user1); -// IERC20(XTZ).approve(address(superloop), XTZ_SCALE); -// superloop.deposit(XTZ_SCALE / 1000, user1); -// uint256 user1SharesBalanceAfter = superloop.balanceOf(user1); - -// assertTrue(user1SharesBalanceAfter > user1SharesBalanceBefore); - -// // i should not be able to do an instant deposit of 100 xtz due to cash reserve -// deal(XTZ, user1, 100 * XTZ_SCALE); -// vm.startPrank(user1); -// IERC20(XTZ).approve(address(superloop), 100 * XTZ_SCALE); -// vm.expectRevert(bytes(Errors.INSUFFICIENT_CASH_SHORTFALL)); -// superloop.deposit(100 * XTZ_SCALE, user1); -// vm.stopPrank(); -// } - -// function test_resolveDepositRequestWithPartials() public { -// _createPartialDepositWithResolution(depositAll); // 300 worth of depostits, 150 pending - -// // try to operate again - -// uint256 depositAmount_secondBatch = 90 * XTZ_SCALE; -// uint256 supplyAmount = 180 * STXTZ_SCALE; -// uint256 borrowAmount = 110 * XTZ_SCALE; -// uint256 swapAmount = borrowAmount + depositAmount_secondBatch; -// uint256 supplyAmountWithPremium = supplyAmount + (supplyAmount * 1) / 10000; - -// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](3); -// moduleExecutionData[0] = _supplyCall(ST_XTZ, supplyAmount); -// moduleExecutionData[1] = _borrowCall(XTZ, borrowAmount); - -// moduleExecutionData[2] = -// _swapCallExactOutCurve(XTZ, ST_XTZ, XTZ_STXTZ_POOL, swapAmount, supplyAmountWithPremium, XTZ_STXTZ_SWAP); - -// DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); -// intermediateExecutionData[0] = _flashloanCall(ST_XTZ, supplyAmount, abi.encode(moduleExecutionData)); - -// DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); -// finalExecutionData[0] = -// _resolveDepositRequestsCall(XTZ, depositAmount_secondBatch, abi.encode(intermediateExecutionData)); - -// uint256 exchangeRateBefore = superloop.convertToAssets(ONE_SHARE); -// DataTypes.DepositRequestData memory depositRequest2_before = depositManager.depositRequest(2); - -// vm.prank(admin); -// superloop.operate(finalExecutionData); - -// uint256 exchangeRateAfter = superloop.convertToAssets(ONE_SHARE); -// DataTypes.DepositRequestData memory depositRequest2_after = depositManager.depositRequest(2); - -// assertApproxEqAbs(exchangeRateBefore, exchangeRateAfter, 1000); // 1000 is the precision - -// assertTrue(depositRequest2_after.sharesMinted > depositRequest2_before.sharesMinted); -// // deposit request 2 should be fully processed, deposit request 3 should be partially processed -// assertTrue(superloop.balanceOf(user3) > 0); - -// // pending deposits should be 150 xtz -// assertEq(depositManager.totalPendingDeposits(), 60 * XTZ_SCALE); - -// // resolution id pointer should be 2 -// assertEq(depositManager.resolutionIdPointer(), 3); - -// // deposit request 2 should be partially processed -// DataTypes.DepositRequestData memory depositRequest3 = depositManager.depositRequest(3); -// assertEq(uint256(depositRequest3.state), uint256(DataTypes.RequestProcessingState.PARTIALLY_PROCESSED)); -// assertEq(depositRequest3.amountProcessed, 40 * XTZ_SCALE); - -// // deposit another batch, with 60 xtz resolving a request partially - -// uint256 depositAmount_thirdBatch = 60 * XTZ_SCALE; -// uint256 supplyAmountSecondBatch = 120 * STXTZ_SCALE; -// uint256 borrowAmountSecondBatch = 75 * XTZ_SCALE; -// uint256 swapAmountSecondBatch = borrowAmountSecondBatch + depositAmount_thirdBatch; -// uint256 supplyAmountWithPremiumSecondBatch = supplyAmountSecondBatch + (supplyAmountSecondBatch * 1) / 10000; - -// DataTypes.ModuleExecutionData[] memory moduleExecutionDataSecondBatch = new DataTypes.ModuleExecutionData[](3); -// moduleExecutionDataSecondBatch[0] = _supplyCall(ST_XTZ, supplyAmountSecondBatch); -// moduleExecutionDataSecondBatch[1] = _borrowCall(XTZ, borrowAmountSecondBatch); - -// moduleExecutionDataSecondBatch[2] = _swapCallExactOutCurve( -// XTZ, ST_XTZ, XTZ_STXTZ_POOL, swapAmountSecondBatch, supplyAmountWithPremiumSecondBatch, XTZ_STXTZ_SWAP -// ); - -// DataTypes.ModuleExecutionData[] memory intermediateExecutionDataSecondBatch = -// new DataTypes.ModuleExecutionData[](1); -// intermediateExecutionDataSecondBatch[0] = -// _flashloanCall(ST_XTZ, supplyAmountSecondBatch, abi.encode(moduleExecutionDataSecondBatch)); - -// DataTypes.ModuleExecutionData[] memory finalExecutionDataSecondBatch = new DataTypes.ModuleExecutionData[](1); -// finalExecutionDataSecondBatch[0] = -// _resolveDepositRequestsCall(XTZ, depositAmount_thirdBatch, abi.encode(intermediateExecutionDataSecondBatch)); - -// uint256 exchangeRateBeforeSecondBatch = superloop.convertToAssets(ONE_SHARE); - -// vm.prank(admin); -// superloop.operate(finalExecutionDataSecondBatch); - -// uint256 exchangeRateAfterSecondBatch = superloop.convertToAssets(ONE_SHARE); - -// assertApproxEqAbs(exchangeRateBeforeSecondBatch, exchangeRateAfterSecondBatch, 1000); - -// // pending deposits should be 150 xtz -// assertEq(depositManager.totalPendingDeposits(), 0); - -// // resolution id pointer should be 2 -// assertEq(depositManager.resolutionIdPointer(), 4); - -// depositRequest3 = depositManager.depositRequest(3); -// assertEq(uint256(depositRequest3.state), uint256(DataTypes.RequestProcessingState.FULLY_PROCESSED)); -// } - -// function test_resolveDepositRequestWithCancellation() public { -// _createPartialDepositWithResolution(depositAll); // 300 worth of depostits, 150 pending - -// // user1 should not be able to cancel deposit request 1 because it's already processed -// vm.startPrank(user1); -// vm.expectRevert(bytes(Errors.DEPOSIT_REQUEST_ALREADY_PROCESSED)); -// depositManager.cancelDepositRequest(1); -// vm.stopPrank(); - -// // cancel deposit request 2 => it should be partially cancelled and user 2 should get back the remaining amount -// uint256 user2BalanceBefore = IERC20(XTZ).balanceOf(user2); -// vm.startPrank(user2); -// depositManager.cancelDepositRequest(2); -// vm.stopPrank(); -// // user 2 should get back the remaining amount -// uint256 user2BalanceAfter = IERC20(XTZ).balanceOf(user2); -// assertEq(user2BalanceAfter - user2BalanceBefore, 50 * XTZ_SCALE); -// // deposit request 2 should be partially cancelled -// DataTypes.DepositRequestData memory depositRequest = depositManager.depositRequest(2); -// assertEq(uint256(depositRequest.state), uint256(DataTypes.RequestProcessingState.PARTIALLY_CANCELLED)); -// assertEq(depositRequest.amountProcessed, 50 * XTZ_SCALE); - -// // pending deposits should be 100 xtz -// assertEq(depositManager.totalPendingDeposits(), 100 * XTZ_SCALE); - -// // try to operate again with 75 xtz => request 3 should be partially processed -// uint256 depositAmount_secondBatch = 75 * XTZ_SCALE; -// uint256 supplyAmount = 150 * STXTZ_SCALE; -// uint256 borrowAmount = 85 * XTZ_SCALE; -// uint256 swapAmount = borrowAmount + depositAmount_secondBatch; -// uint256 supplyAmountWithPremium = supplyAmount + (supplyAmount * 1) / 10000; - -// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](3); -// moduleExecutionData[0] = _supplyCall(ST_XTZ, supplyAmount); -// moduleExecutionData[1] = _borrowCall(XTZ, borrowAmount); - -// moduleExecutionData[2] = -// _swapCallExactOutCurve(XTZ, ST_XTZ, XTZ_STXTZ_POOL, swapAmount, supplyAmountWithPremium, XTZ_STXTZ_SWAP); - -// DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); -// intermediateExecutionData[0] = _flashloanCall(ST_XTZ, supplyAmount, abi.encode(moduleExecutionData)); - -// DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); -// finalExecutionData[0] = -// _resolveDepositRequestsCall(XTZ, depositAmount_secondBatch, abi.encode(intermediateExecutionData)); - -// uint256 exchangeRateBefore = superloop.convertToAssets(ONE_SHARE); - -// vm.prank(admin); -// superloop.operate(finalExecutionData); - -// uint256 exchangeRateAfter = superloop.convertToAssets(ONE_SHARE); - -// assertApproxEqAbs(exchangeRateBefore, exchangeRateAfter, 1000); // 1000 is the precision - -// // pending deposit should be 25 -// assertEq(depositManager.totalPendingDeposits(), 25 * XTZ_SCALE); -// // resolution id pointer should be 3 -// assertEq(depositManager.resolutionIdPointer(), 3); - -// // deposit request 3 should be partially processed -// DataTypes.DepositRequestData memory depositRequest3 = depositManager.depositRequest(3); -// assertEq(uint256(depositRequest3.state), uint256(DataTypes.RequestProcessingState.PARTIALLY_PROCESSED)); -// assertEq(depositRequest3.amountProcessed, 75 * XTZ_SCALE); - -// // user3 should get shares -// assertTrue(superloop.balanceOf(user3) > 0); - -// //////////////// Make sure new deposits are working as expected //////////////// - -// // user1 and user2 should be able to request new deposits -// _makeDepositRequest(100 * XTZ_SCALE, user1, true); -// _makeDepositRequest(100 * XTZ_SCALE, user2, true); - -// // user3 should not be able to request new deposits because one request is still under process -// vm.expectRevert(bytes(Errors.DEPOSIT_REQUEST_ACTIVE)); -// vm.startPrank(user3); -// depositManager.requestDeposit(100 * XTZ_SCALE, address(0)); -// vm.stopPrank(); -// } -// } +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.13; + +import {IntegrationBase} from "./IntegrationBase.sol"; +import {DataTypes} from "../../../src/common/DataTypes.sol"; +import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +import {DepositManager} from "../../../src/core/DepositManager/DepositManager.sol"; +import {console} from "forge-std/Test.sol"; +import {Errors} from "../../../src/common/Errors.sol"; +import {IERC20Metadata} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol"; + +contract DepositManagerTest is IntegrationBase { + bool depositAll = false; + DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.GENERAL; + + function setUp() public override { + super.setUp(); + + DataTypes.AaveV3EmodeParams memory params = + DataTypes.AaveV3EmodeParams({emodeCategory: environment.emodeCategory}); + + DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); + moduleExecutionData[0] = DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.DELEGATECALL, + module: address(emodeModule), + data: abi.encodeWithSelector(emodeModule.execute.selector, params) + }); + + vm.prank(admin); + superloop.operate(moduleExecutionData); + } + + function test_initialize() public view { + assertEq(depositManager.vault(), address(superloop)); + assertEq(depositManager.asset(), environment.vaultAsset); + assertEq(depositManager.nextDepositRequestId(), 1); + } + + function test_requestDeposit_MinimumDepositAmountNotMet() public { + uint256 depositAmount = 99; + + vm.startPrank(user1); + vm.expectRevert(bytes(Errors.INVALID_AMOUNT)); + depositManager.requestDeposit(depositAmount, address(0)); + vm.stopPrank(); + } + + function test_resolveDepositRequestResolution() public { + uint256 exchangeRateBefore = superloop.convertToAssets(ONE_SHARE); + uint256 depositAmount = 100 * 10 ** environment.vaultAssetDecimals; + _makeDepositRequest(depositAmount, user1, true); + + // build the operate call + uint256 supplyAmount = 150 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals(); // USDe supply + uint256 borrowAmount = 60 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals(); // USDC borrow + uint256 flashLoanAmount = supplyAmount - depositAmount; + uint256 swapAmount = borrowAmount + depositAmount; + uint256 supplyAmountWithPremium = supplyAmount + (supplyAmount * 1) / 10000; + + DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](3); + moduleExecutionData[0] = _supplyCall(environment.lendAssets[0], supplyAmount); + moduleExecutionData[1] = _borrowCall(environment.borrowAssets[0], borrowAmount); + + moduleExecutionData[2] = USE_MORPHO + ? _swapCallExactOut( + environment.borrowAssets[0], + environment.lendAssets[0], + borrowAmount, + flashLoanAmount, + environment.router, + USDC_USDE_POOL_FEE, + block.timestamp + 100 + ) + : _swapCallExactOutCurve(XTZ, ST_XTZ, XTZ_STXTZ_POOL, swapAmount, supplyAmountWithPremium, XTZ_STXTZ_SWAP); + + DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); + intermediateExecutionData[0] = USE_MORPHO + ? _morphoFlashloanCall(environment.lendAssets[0], flashLoanAmount, abi.encode(moduleExecutionData)) + : _flashloanCall(environment.lendAssets[0], supplyAmount, abi.encode(moduleExecutionData)); + + DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); + finalExecutionData[0] = + _resolveDepositRequestsCall(environment.vaultAsset, depositAmount, abi.encode(intermediateExecutionData)); + + vm.prank(admin); + superloop.operate(finalExecutionData); + + // user 1 should get shares + DataTypes.DepositRequestData memory depositRequest1 = depositManager.depositRequest(1); + uint256 user1Shares = superloop.balanceOf(user1); + assertEq(user1Shares, depositRequest1.sharesMinted); + + // deposit maanger should not have any xtz now, resolution ptr must have increased, pendingDepsotAmout should be 0 + assertEq(IERC20(environment.vaultAsset).balanceOf(address(depositManager)), 0); + assertEq(depositManager.totalPendingDeposits(), 0); + assertEq(depositManager.resolutionIdPointer(), 2); + + // exchange rate before should equal to exchange rate after + uint256 exchangeRateAfter = superloop.convertToAssets(ONE_SHARE); + assertTrue( + exchangeRateAfter > exchangeRateBefore + ? exchangeRateAfter - exchangeRateBefore < 100 + : exchangeRateBefore - exchangeRateAfter < 100 + ); + } + + function test_instantDepositWithLimit() public { + _createPartialDepositWithResolution(depositAll); + + // i should be able to do an instant deposit of 0.001 xtz + uint256 scale = 10 ** environment.vaultAssetDecimals; + uint256 user1SharesBalanceBefore = superloop.balanceOf(user1); + deal(environment.vaultAsset, user1, scale); + vm.startPrank(user1); + IERC20(environment.vaultAsset).approve(address(superloop), scale); + superloop.deposit(scale / 1000, user1); + uint256 user1SharesBalanceAfter = superloop.balanceOf(user1); + + assertTrue(user1SharesBalanceAfter > user1SharesBalanceBefore); + + // i should not be able to do an instant deposit of 100 xtz due to cash reserve + deal(environment.vaultAsset, user1, 100 * scale); + vm.startPrank(user1); + IERC20(environment.vaultAsset).approve(address(superloop), 100 * scale); + vm.expectRevert(bytes(Errors.INSUFFICIENT_CASH_SHORTFALL)); + superloop.deposit(100 * scale, user1); + vm.stopPrank(); + } + + function test_resolveDepositRequestWithPartials() public { + _createPartialDepositWithResolution(depositAll); // 300 worth of depostits, 150 pending + + // try to operate again + uint256 vaultTokenScale = 10 ** environment.vaultAssetDecimals; + uint256 lendTokenScale = 10 ** IERC20Metadata(environment.lendAssets[0]).decimals(); + uint256 borrowTokenScale = 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals(); + + uint256 depositAmount_secondBatch = 90 * vaultTokenScale; + uint256 supplyAmount = 180 * lendTokenScale; + uint256 borrowAmount = 110 * borrowTokenScale; + uint256 swapAmount = borrowAmount + depositAmount_secondBatch; + uint256 supplyAmountWithPremium = supplyAmount + (supplyAmount * 1) / 10000; + + uint256 flashLoanAmount = supplyAmount - depositAmount_secondBatch; + + DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](3); + moduleExecutionData[0] = _supplyCall(environment.lendAssets[0], supplyAmount); + moduleExecutionData[1] = _borrowCall(environment.borrowAssets[0], borrowAmount); + + moduleExecutionData[2] = USE_MORPHO + ? _swapCallExactOut( + environment.borrowAssets[0], + environment.lendAssets[0], + borrowAmount, + flashLoanAmount, + environment.router, + USDC_USDE_POOL_FEE, + block.timestamp + 100 + ) + : _swapCallExactOutCurve(XTZ, ST_XTZ, XTZ_STXTZ_POOL, swapAmount, supplyAmountWithPremium, XTZ_STXTZ_SWAP); + + DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); + intermediateExecutionData[0] = USE_MORPHO + ? _morphoFlashloanCall(environment.lendAssets[0], flashLoanAmount, abi.encode(moduleExecutionData)) + : _flashloanCall(environment.lendAssets[0], supplyAmount, abi.encode(moduleExecutionData)); + + DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); + finalExecutionData[0] = _resolveDepositRequestsCall( + environment.vaultAsset, depositAmount_secondBatch, abi.encode(intermediateExecutionData) + ); + + uint256 exchangeRateBefore = superloop.convertToAssets(ONE_SHARE); + DataTypes.DepositRequestData memory depositRequest2_before = depositManager.depositRequest(2); + + vm.prank(admin); + superloop.operate(finalExecutionData); + + uint256 exchangeRateAfter = superloop.convertToAssets(ONE_SHARE); + DataTypes.DepositRequestData memory depositRequest2_after = depositManager.depositRequest(2); + + assertApproxEqAbs(exchangeRateBefore, exchangeRateAfter, 1000); // 1000 is the precision + + assertTrue(depositRequest2_after.sharesMinted > depositRequest2_before.sharesMinted); + // deposit request 2 should be fully processed, deposit request 3 should be partially processed + assertTrue(superloop.balanceOf(user3) > 0); + + // pending deposits should be 150 xtz + assertEq(depositManager.totalPendingDeposits(), 60 * vaultTokenScale); + + // resolution id pointer should be 2 + assertEq(depositManager.resolutionIdPointer(), 3); + + // deposit request 2 should be partially processed + DataTypes.DepositRequestData memory depositRequest3 = depositManager.depositRequest(3); + assertEq(uint256(depositRequest3.state), uint256(DataTypes.RequestProcessingState.PARTIALLY_PROCESSED)); + assertEq(depositRequest3.amountProcessed, 40 * vaultTokenScale); + + // deposit another batch, with 60 xtz resolving a request partially + + uint256 depositAmount_thirdBatch = 60 * vaultTokenScale; + uint256 supplyAmountSecondBatch = 120 * lendTokenScale; + uint256 borrowAmountSecondBatch = 75 * borrowTokenScale; + uint256 swapAmountSecondBatch = borrowAmountSecondBatch + depositAmount_thirdBatch; + uint256 supplyAmountWithPremiumSecondBatch = supplyAmountSecondBatch + (supplyAmountSecondBatch * 1) / 10000; + + DataTypes.ModuleExecutionData[] memory moduleExecutionDataSecondBatch = new DataTypes.ModuleExecutionData[](3); + moduleExecutionDataSecondBatch[0] = _supplyCall(environment.lendAssets[0], supplyAmountSecondBatch); + moduleExecutionDataSecondBatch[1] = _borrowCall(environment.borrowAssets[0], borrowAmountSecondBatch); + + uint256 flashLoanAmountSecondBatch = supplyAmountSecondBatch - depositAmount_thirdBatch; + + moduleExecutionDataSecondBatch[2] = USE_MORPHO + ? _swapCallExactOut( + environment.borrowAssets[0], + environment.lendAssets[0], + borrowAmountSecondBatch, + flashLoanAmountSecondBatch, + environment.router, + USDC_USDE_POOL_FEE, + block.timestamp + 100 + ) + : _swapCallExactOutCurve( + XTZ, ST_XTZ, XTZ_STXTZ_POOL, swapAmountSecondBatch, supplyAmountWithPremiumSecondBatch, XTZ_STXTZ_SWAP + ); + + DataTypes.ModuleExecutionData[] memory intermediateExecutionDataSecondBatch = + new DataTypes.ModuleExecutionData[](1); + intermediateExecutionDataSecondBatch[0] = USE_MORPHO + ? _morphoFlashloanCall(environment.lendAssets[0], flashLoanAmount, abi.encode(moduleExecutionDataSecondBatch)) + : _flashloanCall(environment.lendAssets[0], supplyAmountSecondBatch, abi.encode(moduleExecutionDataSecondBatch)); + + DataTypes.ModuleExecutionData[] memory finalExecutionDataSecondBatch = new DataTypes.ModuleExecutionData[](1); + finalExecutionDataSecondBatch[0] = _resolveDepositRequestsCall( + environment.lendAssets[0], depositAmount_thirdBatch, abi.encode(intermediateExecutionDataSecondBatch) + ); + + uint256 exchangeRateBeforeSecondBatch = superloop.convertToAssets(ONE_SHARE); + + vm.prank(admin); + superloop.operate(finalExecutionDataSecondBatch); + + uint256 exchangeRateAfterSecondBatch = superloop.convertToAssets(ONE_SHARE); + + assertApproxEqAbs(exchangeRateBeforeSecondBatch, exchangeRateAfterSecondBatch, 1000); + + // pending deposits should be 150 xtz + assertEq(depositManager.totalPendingDeposits(), 0); + + // resolution id pointer should be 2 + assertEq(depositManager.resolutionIdPointer(), 4); + + depositRequest3 = depositManager.depositRequest(3); + assertEq(uint256(depositRequest3.state), uint256(DataTypes.RequestProcessingState.FULLY_PROCESSED)); + } + + function test_resolveDepositRequestWithCancellation() public { + uint256 vaultTokenScale = 10 ** IERC20Metadata(environment.vaultAsset).decimals(); + uint256 lendTokenScale = 10 ** IERC20Metadata(environment.lendAssets[0]).decimals(); + uint256 borrowTokenScale = 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals(); + + _createPartialDepositWithResolution(depositAll); // 300 worth of depostits, 150 pending + + // user1 should not be able to cancel deposit request 1 because it's already processed + vm.startPrank(user1); + vm.expectRevert(bytes(Errors.DEPOSIT_REQUEST_ALREADY_PROCESSED)); + depositManager.cancelDepositRequest(1); + vm.stopPrank(); + + // cancel deposit request 2 => it should be partially cancelled and user 2 should get back the remaining amount + uint256 user2BalanceBefore = IERC20(environment.vaultAsset).balanceOf(user2); + vm.startPrank(user2); + depositManager.cancelDepositRequest(2); + vm.stopPrank(); + // user 2 should get back the remaining amount + uint256 user2BalanceAfter = IERC20(environment.vaultAsset).balanceOf(user2); + assertEq(user2BalanceAfter - user2BalanceBefore, 50 * vaultTokenScale); + // deposit request 2 should be partially cancelled + DataTypes.DepositRequestData memory depositRequest = depositManager.depositRequest(2); + assertEq(uint256(depositRequest.state), uint256(DataTypes.RequestProcessingState.PARTIALLY_CANCELLED)); + assertEq(depositRequest.amountProcessed, 50 * vaultTokenScale); + + // pending deposits should be 100 xtz + assertEq(depositManager.totalPendingDeposits(), 100 * vaultTokenScale); + + // try to operate again with 75 xtz => request 3 should be partially processed + uint256 depositAmount_secondBatch = 75 * vaultTokenScale; + uint256 supplyAmount = 150 * lendTokenScale; + uint256 borrowAmount = 85 * borrowTokenScale; + uint256 swapAmount = borrowAmount + depositAmount_secondBatch; + uint256 supplyAmountWithPremium = supplyAmount + (supplyAmount * 1) / 10000; + + DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](3); + moduleExecutionData[0] = _supplyCall(environment.lendAssets[0], supplyAmount); + moduleExecutionData[1] = _borrowCall(environment.borrowAssets[0], borrowAmount); + uint256 flashLoanAmount = supplyAmount - depositAmount_secondBatch; + + moduleExecutionData[2] = USE_MORPHO + ? _swapCallExactOut( + environment.borrowAssets[0], + environment.lendAssets[0], + borrowAmount, + flashLoanAmount, + environment.router, + USDC_USDE_POOL_FEE, + block.timestamp + 100 + ) + : _swapCallExactOutCurve( + environment.borrowAssets[0], + environment.lendAssets[0], + XTZ_STXTZ_POOL, + swapAmount, + supplyAmountWithPremium, + XTZ_STXTZ_SWAP + ); + + DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); + intermediateExecutionData[0] = USE_MORPHO + ? _morphoFlashloanCall(environment.lendAssets[0], flashLoanAmount, abi.encode(moduleExecutionData)) + : _flashloanCall(environment.lendAssets[0], supplyAmount, abi.encode(moduleExecutionData)); + + DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); + finalExecutionData[0] = _resolveDepositRequestsCall( + environment.vaultAsset, depositAmount_secondBatch, abi.encode(intermediateExecutionData) + ); + + uint256 exchangeRateBefore = superloop.convertToAssets(ONE_SHARE); + + vm.prank(admin); + superloop.operate(finalExecutionData); + + uint256 exchangeRateAfter = superloop.convertToAssets(ONE_SHARE); + + assertApproxEqAbs(exchangeRateBefore, exchangeRateAfter, 1000); // 1000 is the precision + + // pending deposit should be 25 + assertEq(depositManager.totalPendingDeposits(), 25 * XTZ_SCALE); + // resolution id pointer should be 3 + assertEq(depositManager.resolutionIdPointer(), 3); + + // deposit request 3 should be partially processed + DataTypes.DepositRequestData memory depositRequest3 = depositManager.depositRequest(3); + assertEq(uint256(depositRequest3.state), uint256(DataTypes.RequestProcessingState.PARTIALLY_PROCESSED)); + assertEq(depositRequest3.amountProcessed, 75 * XTZ_SCALE); + + // user3 should get shares + assertTrue(superloop.balanceOf(user3) > 0); + + //////////////// Make sure new deposits are working as expected //////////////// + + // user1 and user2 should be able to request new deposits + _makeDepositRequest(100 * vaultTokenScale, user1, true); + _makeDepositRequest(100 * vaultTokenScale, user2, true); + + // user3 should not be able to request new deposits because one request is still under process + vm.expectRevert(bytes(Errors.DEPOSIT_REQUEST_ACTIVE)); + vm.startPrank(user3); + depositManager.requestDeposit(100 * vaultTokenScale, address(0)); + vm.stopPrank(); + } +} diff --git a/test/core/integration/IntegrationBase.sol b/test/core/integration/IntegrationBase.sol index 6b62ebb..7b79ae5 100644 --- a/test/core/integration/IntegrationBase.sol +++ b/test/core/integration/IntegrationBase.sol @@ -1,571 +1,585 @@ -// // SPDX-License-Identifier: MIT - -// pragma solidity ^0.8.13; - -// import {TestBase} from "../TestBase.sol"; -// import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; -// import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; -// import {TransparentUpgradeableProxy} from -// "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -// import {DataTypes} from "../../../src/common/DataTypes.sol"; -// import {IFlashLoanSimpleReceiver} from "aave-v3-core/contracts/flashloan/interfaces/IFlashLoanSimpleReceiver.sol"; -// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -// import {IPoolConfigurator} from "aave-v3-core/contracts/interfaces/IPoolConfigurator.sol"; -// import {IRouter} from "../../../src/mock/MockIRouter.sol"; -// import {ICurvePool} from "../../../src/mock/ICurvePool.sol"; -// import {console} from "forge-std/console.sol"; - -// abstract contract IntegrationBase is TestBase { -// struct CURVE_IJ { -// int128 i; -// int128 j; -// } - -// Superloop public superloopImplementation; -// ProxyAdmin public proxyAdmin; - -// address public user1; -// address public user2; -// address public user3; - -// uint24 public constant XTZ_STXTZ_POOL_FEE = 100; // 0.01% -// address public constant XTZ_STXTZ_POOL = 0x74d80eE400D3026FDd2520265cC98300710b25D4; - -// uint256 public constant ONE_SHARE = 10 ** 20; - -// uint256 public constant INSTANT_WITHDRAW_FEE = 100; - -// uint256 public constant XTZ_SCALE = 10 ** 18; -// uint256 public constant STXTZ_SCALE = 10 ** 6; - -// CURVE_IJ public XTZ_STXTZ_SWAP; -// CURVE_IJ public STXTZ_XTZ_SWAP; - -// function setUp() public virtual override { -// super.setUp(); - -// XTZ_STXTZ_SWAP = CURVE_IJ({i: 1, j: 0}); -// STXTZ_XTZ_SWAP = CURVE_IJ({i: 0, j: 1}); - -// vm.startPrank(admin); -// _deployModules(); - -// address[] memory modules = new address[](9); -// modules[0] = address(dexModule); -// modules[1] = address(flashloanModule); -// modules[2] = address(callbackHandler); -// modules[3] = address(emodeModule); -// modules[4] = address(supplyModule); -// modules[5] = address(withdrawModule); -// modules[6] = address(borrowModule); -// modules[7] = address(repayModule); -// modules[8] = address(depositManagerCallbackHandler); - -// DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ -// asset: XTZ, -// name: "XTZ Vault", -// symbol: "XTZV", -// supplyCap: 100000 * 10 ** 18, -// minimumDepositAmount: 100, -// instantWithdrawFee: INSTANT_WITHDRAW_FEE, -// superloopModuleRegistry: address(moduleRegistry), -// modules: modules, -// accountant: address(accountant), -// withdrawManager: address(withdrawManager), -// depositManager: address(0), -// cashReserve: 1000, -// vaultAdmin: admin, -// treasury: treasury, -// vaultOperator: admin -// }); -// DataTypes.VaultInitData memory initDataBtc = DataTypes.VaultInitData({ -// asset: WBTC, -// name: "BTC Vault", -// symbol: "BTC", -// supplyCap: 100000 * 10 ** 18, -// minimumDepositAmount: 100, -// instantWithdrawFee: INSTANT_WITHDRAW_FEE, -// superloopModuleRegistry: address(moduleRegistry), -// modules: modules, -// accountant: address(accountantBtc), -// withdrawManager: address(withdrawManagerBtc), -// depositManager: address(0), -// cashReserve: 1000, -// vaultAdmin: admin, -// treasury: treasury, -// vaultOperator: admin -// }); -// superloopImplementation = new Superloop(); -// proxyAdmin = new ProxyAdmin(address(this)); - -// TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( -// address(superloopImplementation), -// address(proxyAdmin), -// abi.encodeWithSelector(Superloop.initialize.selector, initData) -// ); -// TransparentUpgradeableProxy proxyBtc = new TransparentUpgradeableProxy( -// address(superloopImplementation), -// address(proxyAdmin), -// abi.encodeWithSelector(Superloop.initialize.selector, initDataBtc) -// ); -// superloop = Superloop(payable(address(proxy))); -// superloopBtc = Superloop(payable(address(proxyBtc))); - -// accountant = _deployAccountant(address(superloop), _singleAddressArray(ST_XTZ), _singleAddressArray(XTZ)); -// accountantBtc = _deployAccountant(address(superloopBtc), _singleAddressArray(LBTC), _singleAddressArray(WBTC)); - -// withdrawManager = _deployWithdrawManager(address(superloop)); -// withdrawManagerBtc = _deployWithdrawManager(address(superloopBtc)); - -// depositManager = _deployDepositManager(address(superloop)); -// depositManagerBtc = _deployDepositManager(address(superloopBtc)); - -// bytes32 key = keccak256(abi.encodePacked(POOL, IFlashLoanSimpleReceiver.executeOperation.selector)); -// bytes32 depositKey = -// keccak256(abi.encodePacked(address(depositManager), depositManagerCallbackHandler.executeDeposit.selector)); -// bytes32 withdrawKey = keccak256( -// abi.encodePacked(address(withdrawManager), withdrawManagerCallbackHandler.executeWithdraw.selector) -// ); -// superloop.setCallbackHandler(key, address(callbackHandler)); -// superloop.setCallbackHandler(depositKey, address(depositManagerCallbackHandler)); -// superloop.setCallbackHandler(withdrawKey, address(withdrawManagerCallbackHandler)); - -// moduleRegistry.setModule("withdrawManager", address(withdrawManager)); -// moduleRegistry.setModule("universalAccountant", address(accountant)); -// moduleRegistry.setModule("depositManager", address(depositManager)); - -// superloop.setRegisteredModule(address(withdrawManager), true); -// superloop.setRegisteredModule(address(accountant), true); -// superloop.setRegisteredModule(address(depositManager), true); - -// superloopBtc.setRegisteredModule(address(withdrawManagerBtc), true); -// superloopBtc.setRegisteredModule(address(accountantBtc), true); -// superloopBtc.setRegisteredModule(address(depositManagerBtc), true); - -// superloop.setAccountantModule(address(accountant)); -// superloop.setWithdrawManagerModule(address(withdrawManager)); -// superloop.setDepositManagerModule(address(depositManager)); - -// superloopBtc.setAccountantModule(address(accountantBtc)); -// superloopBtc.setWithdrawManagerModule(address(withdrawManagerBtc)); -// superloopBtc.setDepositManagerModule(address(depositManagerBtc)); - -// vm.stopPrank(); -// vm.label(address(superloop), "superloop"); - -// user1 = makeAddr("user1"); -// user2 = makeAddr("user2"); -// user3 = makeAddr("user3"); - -// vm.label(user1, "user1"); -// vm.label(user2, "user2"); -// vm.label(user3, "user3"); - -// vm.startPrank(XTZ_WHALE); -// IERC20(XTZ).transfer(user1, 100 * 10 ** 18); -// IERC20(XTZ).transfer(user2, 100 * 10 ** 18); -// IERC20(XTZ).transfer(user3, 100 * 10 ** 18); -// vm.stopPrank(); - -// vm.startPrank(POOL_ADMIN); -// IPoolConfigurator(POOL_CONFIGURATOR).setReserveFlashLoaning(ST_XTZ, true); -// IPoolConfigurator(POOL_CONFIGURATOR).setSupplyCap(ST_XTZ, 10000000); -// vm.stopPrank(); -// } - -// function _resolveDepositRequestsCall(address asset, uint256 amount, bytes memory data) -// internal -// view -// returns (DataTypes.ModuleExecutionData memory) -// { -// DataTypes.ResolveDepositRequestsData memory resolveDepositRequestsData = -// DataTypes.ResolveDepositRequestsData({asset: asset, amount: amount, callbackExecutionData: data}); -// return DataTypes.ModuleExecutionData({ -// executionType: DataTypes.CallType.CALL, -// module: address(depositManager), -// data: abi.encodeWithSelector(depositManager.resolveDepositRequests.selector, resolveDepositRequestsData) -// }); -// } - -// function _resolveWithdrawRequestsCall(uint256 shares, DataTypes.WithdrawRequestType requestType, bytes memory data) -// internal -// view -// returns (DataTypes.ModuleExecutionData memory) -// { -// DataTypes.ResolveWithdrawRequestsData memory resolveWithdrawRequestsData = DataTypes.ResolveWithdrawRequestsData({ -// shares: shares, -// requestType: requestType, -// callbackExecutionData: data -// }); -// return DataTypes.ModuleExecutionData({ -// executionType: DataTypes.CallType.CALL, -// module: address(withdrawManager), -// data: abi.encodeWithSelector(withdrawManager.resolveWithdrawRequests.selector, resolveWithdrawRequestsData) -// }); -// } - -// function _flashloanCall(address asset, uint256 amount, bytes memory data) -// internal -// view -// returns (DataTypes.ModuleExecutionData memory) -// { -// DataTypes.AaveV3FlashloanParams memory flashloanParams = DataTypes.AaveV3FlashloanParams({ -// asset: asset, -// amount: amount, -// referralCode: 0, -// callbackExecutionData: data -// }); -// return DataTypes.ModuleExecutionData({ -// executionType: DataTypes.CallType.DELEGATECALL, -// module: address(flashloanModule), -// data: abi.encodeWithSelector(flashloanModule.execute.selector, flashloanParams) -// }); -// } - -// function _supplyCall(address asset, uint256 amount) internal view returns (DataTypes.ModuleExecutionData memory) { -// DataTypes.AaveV3ActionParams memory supplyParams = DataTypes.AaveV3ActionParams({asset: asset, amount: amount}); -// return DataTypes.ModuleExecutionData({ -// executionType: DataTypes.CallType.DELEGATECALL, -// module: address(supplyModule), -// data: abi.encodeWithSelector(supplyModule.execute.selector, supplyParams) -// }); -// } - -// function _borrowCall(address asset, uint256 amount) internal view returns (DataTypes.ModuleExecutionData memory) { -// DataTypes.AaveV3ActionParams memory borrowParams = DataTypes.AaveV3ActionParams({asset: asset, amount: amount}); -// return DataTypes.ModuleExecutionData({ -// executionType: DataTypes.CallType.DELEGATECALL, -// module: address(borrowModule), -// data: abi.encodeWithSelector(borrowModule.execute.selector, borrowParams) -// }); -// } - -// function _swapCallExactOut( -// address tokenIn, -// address tokenOut, -// uint256 swapAmount, -// uint256 amountOut, -// address router, -// uint24 fee -// ) internal view returns (DataTypes.ModuleExecutionData memory) { -// DataTypes.ExecuteSwapParamsData[] memory swapParamsData = new DataTypes.ExecuteSwapParamsData[](2); -// swapParamsData[0] = DataTypes.ExecuteSwapParamsData({ -// target: tokenIn, -// data: abi.encodeWithSelector(IERC20.approve.selector, router, swapAmount) -// }); -// swapParamsData[1] = DataTypes.ExecuteSwapParamsData({ -// target: router, -// data: abi.encodeWithSelector( -// IRouter.exactOutputSingle.selector, -// IRouter.ExactOutputSingleParams({ -// tokenIn: tokenIn, -// tokenOut: tokenOut, -// fee: fee, -// recipient: address(superloop), -// amountOut: amountOut, -// amountInMaximum: swapAmount, -// sqrtPriceLimitX96: 0 -// }) -// ) -// }); -// DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ -// tokenIn: tokenIn, -// tokenOut: tokenOut, -// amountIn: swapAmount, -// maxAmountIn: swapAmount, -// minAmountOut: amountOut, -// data: swapParamsData -// }); - -// return DataTypes.ModuleExecutionData({ -// executionType: DataTypes.CallType.DELEGATECALL, -// module: address(dexModule), -// data: abi.encodeWithSelector(dexModule.execute.selector, swapParams) -// }); -// } - -// function _swapCallExactIn( -// address tokenIn, -// address tokenOut, -// uint256 withdrawAmount, -// uint256 amountOut, -// address router, -// uint24 fee -// ) internal view returns (DataTypes.ModuleExecutionData memory) { -// DataTypes.ExecuteSwapParamsData[] memory swapParamsData = new DataTypes.ExecuteSwapParamsData[](2); -// swapParamsData[0] = DataTypes.ExecuteSwapParamsData({ -// target: tokenIn, -// data: abi.encodeWithSelector(IERC20.approve.selector, router, withdrawAmount) -// }); -// swapParamsData[1] = DataTypes.ExecuteSwapParamsData({ -// target: router, -// data: abi.encodeWithSelector( -// IRouter.exactInputSingle.selector, -// IRouter.ExactInputSingleParams({ -// tokenIn: tokenIn, -// tokenOut: tokenOut, -// fee: fee, -// recipient: address(superloop), -// amountIn: withdrawAmount, -// amountOutMinimum: amountOut, -// sqrtPriceLimitX96: 0 -// }) -// ) -// }); - -// DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ -// tokenIn: tokenIn, -// tokenOut: tokenOut, -// amountIn: withdrawAmount, -// maxAmountIn: withdrawAmount, -// minAmountOut: amountOut, -// data: swapParamsData -// }); - -// return DataTypes.ModuleExecutionData({ -// executionType: DataTypes.CallType.DELEGATECALL, -// module: address(dexModule), -// data: abi.encodeWithSelector(dexModule.execute.selector, swapParams) -// }); -// } - -// function _swapCallExactOutCurve( -// address tokenIn, -// address tokenOut, -// address pool, -// uint256 amountIn, -// uint256 amountOut, -// CURVE_IJ memory swap -// ) internal view returns (DataTypes.ModuleExecutionData memory) { -// DataTypes.ExecuteSwapParamsData[] memory swapParamsData = new DataTypes.ExecuteSwapParamsData[](2); -// swapParamsData[0] = DataTypes.ExecuteSwapParamsData({ -// target: tokenIn, -// data: abi.encodeWithSelector(IERC20.approve.selector, pool, amountIn) -// }); -// swapParamsData[1] = DataTypes.ExecuteSwapParamsData({ -// target: pool, -// data: abi.encodeWithSelector(ICurvePool.exchange.selector, swap.i, swap.j, amountIn, amountOut) -// }); - -// DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ -// tokenIn: tokenIn, -// tokenOut: tokenOut, -// amountIn: amountIn, -// maxAmountIn: amountIn, -// minAmountOut: amountOut, -// data: swapParamsData -// }); - -// return DataTypes.ModuleExecutionData({ -// executionType: DataTypes.CallType.DELEGATECALL, -// module: address(dexModule), -// data: abi.encodeWithSelector(dexModule.execute.selector, swapParams) -// }); -// } - -// function _repayCall(address asset, uint256 amount) internal view returns (DataTypes.ModuleExecutionData memory) { -// DataTypes.AaveV3ActionParams memory repayParams = DataTypes.AaveV3ActionParams({asset: asset, amount: amount}); -// return DataTypes.ModuleExecutionData({ -// executionType: DataTypes.CallType.DELEGATECALL, -// module: address(repayModule), -// data: abi.encodeWithSelector(repayModule.execute.selector, repayParams) -// }); -// } - -// function _withdrawCall(address asset, uint256 amount) -// internal -// view -// returns (DataTypes.ModuleExecutionData memory) -// { -// DataTypes.AaveV3ActionParams memory withdrawParams = -// DataTypes.AaveV3ActionParams({asset: asset, amount: amount}); -// return DataTypes.ModuleExecutionData({ -// executionType: DataTypes.CallType.DELEGATECALL, -// module: address(withdrawModule), -// data: abi.encodeWithSelector(withdrawModule.execute.selector, withdrawParams) -// }); -// } - -// /** -// * @dev Creates a partial deposit with resolution. -// * @notice Creates 3 deposit requests with 100 xtz each. -// * @notice request 1 is fully processed, request 2 is partially processed, request 3 is unprocessed. -// */ -// function _createPartialDepositWithResolution(bool depositAll) internal returns (uint256, uint256) { -// uint256 depositAmountUnscaled = 100; -// uint256 depositAmount = depositAmountUnscaled * XTZ_SCALE; -// _makeDepositRequest(depositAmount, user1, true); -// _makeDepositRequest(depositAmount, user2, true); -// _makeDepositRequest(depositAmount, user3, true); - -// uint256 depositAmountUnscaledBatch = depositAll ? depositAmountUnscaled * 3 : (3 * depositAmountUnscaled) / 2; -// uint256 supplyAmountUnscaled = (3 * depositAmountUnscaledBatch); // 3x -// uint256 borrowAmountUnscaled = (3 * supplyAmountUnscaled) / 4; - -// uint256 depositAmountBatch = depositAmountUnscaledBatch * XTZ_SCALE; -// // build the operate call -// uint256 supplyAmount = supplyAmountUnscaled * STXTZ_SCALE; -// uint256 borrowAmount = borrowAmountUnscaled * XTZ_SCALE; -// uint256 swapAmount = borrowAmount + depositAmountBatch; -// uint256 supplyAmountWithPremium = supplyAmount + (supplyAmount * 1) / 10000; - -// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](3); -// moduleExecutionData[0] = _supplyCall(ST_XTZ, supplyAmount); -// moduleExecutionData[1] = _borrowCall(XTZ, borrowAmount); - -// moduleExecutionData[2] = -// _swapCallExactOutCurve(XTZ, ST_XTZ, XTZ_STXTZ_POOL, swapAmount, supplyAmountWithPremium, XTZ_STXTZ_SWAP); - -// DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); -// intermediateExecutionData[0] = _flashloanCall(ST_XTZ, supplyAmount, abi.encode(moduleExecutionData)); - -// DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); -// finalExecutionData[0] = -// _resolveDepositRequestsCall(XTZ, depositAmountBatch, abi.encode(intermediateExecutionData)); - -// uint256 exchangeRateBefore = superloop.convertToAssets(ONE_SHARE); - -// uint256 user1SharesBefore = superloop.balanceOf(user1); -// uint256 user2SharesBefore = superloop.balanceOf(user2); -// uint256 user3SharesBefore = superloop.balanceOf(user3); - -// vm.prank(admin); -// superloop.operate(finalExecutionData); - -// if (!depositAll) { -// // user 1 and user user 2 should get shares -// // user 3 should not get shares - -// uint256 user1SharesAfter = superloop.balanceOf(user1); -// uint256 user2SharesAfter = superloop.balanceOf(user2); -// uint256 user3SharesAfter = superloop.balanceOf(user3); - -// // deposit manager should have 150 xtz now -// assertEq(IERC20(XTZ).balanceOf(address(depositManager)), 150 * XTZ_SCALE); -// // pending deposits should be 150 xtz -// assertEq(depositManager.totalPendingDeposits(), 150 * XTZ_SCALE); - -// // resolution id pointer should be 2 -// assertEq(depositManager.resolutionIdPointer(), 2); - -// // deposit request 1 should be fully processed -// DataTypes.DepositRequestData memory depositRequest1 = depositManager.depositRequest(1); -// assertEq(uint256(depositRequest1.state), uint256(DataTypes.RequestProcessingState.FULLY_PROCESSED)); -// assertEq(depositRequest1.amountProcessed, 100 * XTZ_SCALE); - -// // deposit request 2 should be partially processed -// DataTypes.DepositRequestData memory depositRequest2 = depositManager.depositRequest(2); -// assertEq(uint256(depositRequest2.state), uint256(DataTypes.RequestProcessingState.PARTIALLY_PROCESSED)); -// assertEq(depositRequest2.amountProcessed, 50 * XTZ_SCALE); - -// // deposit request 3 should be unprocessed -// DataTypes.DepositRequestData memory depositRequest3 = depositManager.depositRequest(3); -// assertEq(uint256(depositRequest3.state), uint256(DataTypes.RequestProcessingState.UNPROCESSED)); - -// assertEq(user1SharesAfter - user1SharesBefore, depositRequest1.sharesMinted); -// assertEq(user2SharesAfter - user2SharesBefore, depositRequest2.sharesMinted); -// assertEq(user3SharesAfter - user3SharesBefore, depositRequest3.sharesMinted); - -// // exchange rate before should equal to exchange rate after -// uint256 exchangeRateAfter = superloop.convertToAssets(ONE_SHARE); -// assertApproxEqAbs(exchangeRateBefore, exchangeRateAfter, 1000); -// } - -// return (supplyAmountUnscaled, borrowAmountUnscaled); -// } - -// function _makeDepositRequest(uint256 depositAmount, address user, bool _deal) internal { -// if (_deal) { -// deal(XTZ, user, depositAmount); -// } - -// vm.startPrank(user); -// IERC20(XTZ).approve(address(depositManager), depositAmount); -// depositManager.requestDeposit(depositAmount, address(0)); -// vm.stopPrank(); -// } - -// function _makeWithdrawRequest( -// address share, -// uint256 shareAmount, -// address user, -// DataTypes.WithdrawRequestType requestType -// ) internal { -// vm.startPrank(user); -// IERC20(share).approve(address(withdrawManager), shareAmount); -// withdrawManager.requestWithdraw(shareAmount, requestType); -// vm.stopPrank(); -// } - -// /** -// * @dev Creates a partial withdraw with resolution. -// * @notice Creates 3 withdraw requests with 100 shares each. -// * @notice request 1 is fully processed, request 2 is partially processed, request 3 is unprocessed. -// */ -// function _createPartialWithdrawWithResolution(DataTypes.WithdrawRequestType requestType) -// internal -// returns (uint256, uint256, uint256) -// { -// (uint256 supplyAmountUnscaled, uint256 borrowAmountUnscaled) = _createPartialDepositWithResolution(true); - -// // make 3 withdraw requests -// uint256 user1ShareBalance = superloop.balanceOf(user1); -// uint256 user2ShareBalance = superloop.balanceOf(user2); -// uint256 user3ShareBalance = superloop.balanceOf(user3); -// _makeWithdrawRequest(address(superloop), user1ShareBalance, user1, requestType); -// _makeWithdrawRequest(address(superloop), user2ShareBalance, user2, requestType); -// _makeWithdrawRequest(address(superloop), user3ShareBalance, user3, requestType); - -// // resolve 1st fully, and 2nd partially -// // repay half of borrow amount -// uint256 repayAmount = (borrowAmountUnscaled * XTZ_SCALE) / 2; -// uint256 withdrawAmount = ((supplyAmountUnscaled + 10) * STXTZ_SCALE) / 2; -// uint256 repayAmountWithPremium = repayAmount + (repayAmount * 1) / 10000; -// uint256 totalShares = superloop.totalSupply(); -// uint256 sharesToResolve = totalShares / 2; - -// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](3); -// moduleExecutionData[0] = _repayCall(XTZ, repayAmount); -// moduleExecutionData[1] = _withdrawCall(ST_XTZ, withdrawAmount); -// moduleExecutionData[2] = -// _swapCallExactOutCurve(ST_XTZ, XTZ, XTZ_STXTZ_POOL, withdrawAmount, repayAmountWithPremium, STXTZ_XTZ_SWAP); - -// DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); -// intermediateExecutionData[0] = _flashloanCall(XTZ, repayAmount, abi.encode(moduleExecutionData)); - -// DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); -// finalExecutionData[0] = -// _resolveWithdrawRequestsCall(sharesToResolve, requestType, abi.encode(intermediateExecutionData)); - -// uint256 exchangeRateBefore = superloop.convertToAssets(ONE_SHARE); - -// vm.prank(admin); -// superloop.operate(finalExecutionData); - -// // withdraw requests should have claimable > 0 and sharesProcessed > 0, exchange rate remains the same -// DataTypes.WithdrawRequestData memory withdrawRequest1 = withdrawManager.withdrawRequest(1, requestType); -// DataTypes.WithdrawRequestData memory withdrawRequest2 = withdrawManager.withdrawRequest(2, requestType); -// DataTypes.WithdrawRequestData memory withdrawRequest3 = withdrawManager.withdrawRequest(3, requestType); -// uint256 resolutionIdPointer = withdrawManager.resolutionIdPointer(requestType); -// uint256 totalPendingWithdraws = withdrawManager.totalPendingWithdraws(requestType); - -// assertTrue(withdrawRequest1.amountClaimable > 0); -// assertTrue(withdrawRequest1.sharesProcessed == withdrawRequest1.shares); -// assertTrue(withdrawRequest2.amountClaimable > 0); -// assertTrue(withdrawRequest2.sharesProcessed > 0 && withdrawRequest2.sharesProcessed < withdrawRequest2.shares); -// assertTrue(withdrawRequest3.amountClaimable == 0); -// assertTrue(withdrawRequest3.sharesProcessed == 0); - -// assertTrue(withdrawRequest1.state == DataTypes.RequestProcessingState.FULLY_PROCESSED); -// assertTrue(withdrawRequest2.state == DataTypes.RequestProcessingState.PARTIALLY_PROCESSED); -// assertTrue(withdrawRequest3.state == DataTypes.RequestProcessingState.UNPROCESSED); - -// uint256 exchangeRateAfter = superloop.convertToAssets(ONE_SHARE); - -// assertApproxEqAbs(exchangeRateAfter, exchangeRateBefore, 1000); -// assertEq(totalPendingWithdraws, totalShares - sharesToResolve); -// assertEq(resolutionIdPointer, 2); - -// return (totalShares - sharesToResolve, repayAmount, withdrawAmount); -// } -// } +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.13; + +import {TestBase} from "../TestBase.sol"; +import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; +import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; +import {TransparentUpgradeableProxy} from + "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {DataTypes} from "../../../src/common/DataTypes.sol"; +import {IFlashLoanSimpleReceiver} from "aave-v3-core/contracts/flashloan/interfaces/IFlashLoanSimpleReceiver.sol"; +import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +import {IPoolConfigurator} from "aave-v3-core/contracts/interfaces/IPoolConfigurator.sol"; +import {IRouter} from "../../../src/mock/MockIRouter.sol"; +import {ICurvePool} from "../../../src/mock/ICurvePool.sol"; +import {console} from "forge-std/console.sol"; +import {IMorphoFlashLoanCallback} from "morpho-blue/interfaces/IMorphoCallbacks.sol"; +import {IUniPool} from "../../../src/mock/IUniPool.sol"; +import {IERC20Metadata} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol"; + +abstract contract IntegrationBase is TestBase { + struct CURVE_IJ { + int128 i; + int128 j; + } + + Superloop public superloopImplementation; + ProxyAdmin public proxyAdmin; + + address public user1; + address public user2; + address public user3; + + uint24 public constant XTZ_STXTZ_POOL_FEE = 100; // 0.01% + address public constant XTZ_STXTZ_POOL = 0x74d80eE400D3026FDd2520265cC98300710b25D4; + uint24 public constant USDC_USDE_POOL_FEE = 100; // 0.01% + address public constant USDE_USDC_POOL = 0xE6D7EbB9f1a9519dc06D557e03C522d53520e76A; + uint160 constant MIN_SQRT_RATIO = 4295128739; + uint160 constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342; + + uint256 public constant ONE_SHARE = 10 ** 20; + + uint256 public constant INSTANT_WITHDRAW_FEE = 100; + + uint256 public constant XTZ_SCALE = 10 ** 18; + uint256 public constant STXTZ_SCALE = 10 ** 6; + + CURVE_IJ public XTZ_STXTZ_SWAP; + CURVE_IJ public STXTZ_XTZ_SWAP; + + bool public USDC_USDE_SWAP = false; + bool public USDE_USDC_SWAP = true; + + function setUp() public virtual override { + super.setUp(); + + XTZ_STXTZ_SWAP = CURVE_IJ({i: 1, j: 0}); + STXTZ_XTZ_SWAP = CURVE_IJ({i: 0, j: 1}); + + vm.startPrank(admin); + _deployModules(); + + address[] memory modules = new address[](11); + modules[0] = address(dexModule); + modules[1] = address(flashloanModule); + modules[2] = address(callbackHandler); + modules[3] = address(emodeModule); + modules[4] = address(supplyModule); + modules[5] = address(withdrawModule); + modules[6] = address(borrowModule); + modules[7] = address(repayModule); + modules[8] = address(depositManagerCallbackHandler); + modules[9] = address(morphoFlashloanModule); + modules[10] = address(morphoCallbackHandler); + + DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ + asset: environment.vaultAsset, + name: "Vault", + symbol: "VLT", + supplyCap: 100000 * 10 ** environment.vaultAssetDecimals, + minimumDepositAmount: 100, + instantWithdrawFee: INSTANT_WITHDRAW_FEE, + superloopModuleRegistry: address(moduleRegistry), + modules: modules, + accountant: address(accountant), + withdrawManager: address(withdrawManager), + depositManager: address(0), + cashReserve: 1000, + vaultAdmin: admin, + treasury: treasury, + vaultOperator: admin + }); + superloopImplementation = new Superloop(); + proxyAdmin = new ProxyAdmin(address(this)); + + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(superloopImplementation), + address(proxyAdmin), + abi.encodeWithSelector(Superloop.initialize.selector, initData) + ); + superloop = Superloop(payable(address(proxy))); + + accountant = _deployAccountant(address(superloop), environment.lendAssets, environment.borrowAssets); + + withdrawManager = _deployWithdrawManager(address(superloop)); + + depositManager = _deployDepositManager(address(superloop)); + + bytes32 key = keccak256(abi.encodePacked(environment.pool, IFlashLoanSimpleReceiver.executeOperation.selector)); + bytes32 morphoKey = + keccak256(abi.encodePacked(environment.morpho, IMorphoFlashLoanCallback.onMorphoFlashLoan.selector)); + bytes32 depositKey = + keccak256(abi.encodePacked(address(depositManager), depositManagerCallbackHandler.executeDeposit.selector)); + bytes32 withdrawKey = keccak256( + abi.encodePacked(address(withdrawManager), withdrawManagerCallbackHandler.executeWithdraw.selector) + ); + superloop.setCallbackHandler(key, address(callbackHandler)); + superloop.setCallbackHandler(morphoKey, address(morphoCallbackHandler)); + superloop.setCallbackHandler(depositKey, address(depositManagerCallbackHandler)); + superloop.setCallbackHandler(withdrawKey, address(withdrawManagerCallbackHandler)); + + moduleRegistry.setModule("withdrawManager", address(withdrawManager)); + moduleRegistry.setModule("universalAccountant", address(accountant)); + moduleRegistry.setModule("depositManager", address(depositManager)); + + superloop.setRegisteredModule(address(withdrawManager), true); + superloop.setRegisteredModule(address(accountant), true); + superloop.setRegisteredModule(address(depositManager), true); + + superloop.setAccountantModule(address(accountant)); + superloop.setWithdrawManagerModule(address(withdrawManager)); + superloop.setDepositManagerModule(address(depositManager)); + + vm.stopPrank(); + vm.label(address(superloop), "superloop"); + + user1 = makeAddr("user1"); + user2 = makeAddr("user2"); + user3 = makeAddr("user3"); + + vm.label(user1, "user1"); + vm.label(user2, "user2"); + vm.label(user3, "user3"); + + deal(environment.vaultAsset, user1, 100 * 10 ** environment.vaultAssetDecimals); + deal(environment.vaultAsset, user2, 100 * 10 ** environment.vaultAssetDecimals); + deal(environment.vaultAsset, user3, 100 * 10 ** environment.vaultAssetDecimals); + } + + function _resolveDepositRequestsCall(address asset, uint256 amount, bytes memory data) + internal + view + returns (DataTypes.ModuleExecutionData memory) + { + DataTypes.ResolveDepositRequestsData memory resolveDepositRequestsData = + DataTypes.ResolveDepositRequestsData({asset: asset, amount: amount, callbackExecutionData: data}); + return DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.CALL, + module: address(depositManager), + data: abi.encodeWithSelector(depositManager.resolveDepositRequests.selector, resolveDepositRequestsData) + }); + } + + function _resolveWithdrawRequestsCall(uint256 shares, DataTypes.WithdrawRequestType requestType, bytes memory data) + internal + view + returns (DataTypes.ModuleExecutionData memory) + { + DataTypes.ResolveWithdrawRequestsData memory resolveWithdrawRequestsData = DataTypes.ResolveWithdrawRequestsData({ + shares: shares, + requestType: requestType, + callbackExecutionData: data + }); + return DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.CALL, + module: address(withdrawManager), + data: abi.encodeWithSelector(withdrawManager.resolveWithdrawRequests.selector, resolveWithdrawRequestsData) + }); + } + + function _flashloanCall(address asset, uint256 amount, bytes memory data) + internal + view + returns (DataTypes.ModuleExecutionData memory) + { + DataTypes.AaveV3FlashloanParams memory flashloanParams = DataTypes.AaveV3FlashloanParams({ + asset: asset, + amount: amount, + referralCode: 0, + callbackExecutionData: data + }); + return DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.DELEGATECALL, + module: address(flashloanModule), + data: abi.encodeWithSelector(flashloanModule.execute.selector, flashloanParams) + }); + } + + function _morphoFlashloanCall(address asset, uint256 amount, bytes memory data) + internal + view + returns (DataTypes.ModuleExecutionData memory) + { + DataTypes.MorphoFlashloanParams memory flashloanParams = + DataTypes.MorphoFlashloanParams({asset: asset, amount: amount, callbackExecutionData: data}); + return DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.DELEGATECALL, + module: address(morphoFlashloanModule), + data: abi.encodeWithSelector(morphoFlashloanModule.execute.selector, flashloanParams) + }); + } + + function _supplyCall(address asset, uint256 amount) internal view returns (DataTypes.ModuleExecutionData memory) { + DataTypes.AaveV3ActionParams memory supplyParams = DataTypes.AaveV3ActionParams({asset: asset, amount: amount}); + return DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.DELEGATECALL, + module: address(supplyModule), + data: abi.encodeWithSelector(supplyModule.execute.selector, supplyParams) + }); + } + + function _borrowCall(address asset, uint256 amount) internal view returns (DataTypes.ModuleExecutionData memory) { + DataTypes.AaveV3ActionParams memory borrowParams = DataTypes.AaveV3ActionParams({asset: asset, amount: amount}); + return DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.DELEGATECALL, + module: address(borrowModule), + data: abi.encodeWithSelector(borrowModule.execute.selector, borrowParams) + }); + } + + function _swapCallExactOut( + address tokenIn, + address tokenOut, + uint256 swapAmount, + uint256 amountOut, + address router, + uint24 fee, + uint256 deadline + ) internal view returns (DataTypes.ModuleExecutionData memory) { + DataTypes.ExecuteSwapParamsData[] memory swapParamsData = new DataTypes.ExecuteSwapParamsData[](2); + swapParamsData[0] = DataTypes.ExecuteSwapParamsData({ + target: tokenIn, + data: abi.encodeWithSelector(IERC20.approve.selector, router, swapAmount) + }); + swapParamsData[1] = DataTypes.ExecuteSwapParamsData({ + target: router, + data: abi.encodeWithSelector( + IRouter.exactOutputSingle.selector, + IRouter.ExactOutputSingleParams({ + tokenIn: tokenIn, + tokenOut: tokenOut, + fee: fee, + recipient: address(superloop), + amountOut: amountOut, + amountInMaximum: swapAmount, + sqrtPriceLimitX96: 0, + deadline: deadline + }) + ) + }); + DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ + tokenIn: tokenIn, + tokenOut: tokenOut, + amountIn: swapAmount, + maxAmountIn: swapAmount, + minAmountOut: amountOut, + data: swapParamsData + }); + + return DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.DELEGATECALL, + module: address(dexModule), + data: abi.encodeWithSelector(dexModule.execute.selector, swapParams) + }); + } + + function _swapCallExactIn( + address tokenIn, + address tokenOut, + uint256 withdrawAmount, + uint256 amountOut, + address router, + uint24 fee, + uint256 deadline + ) internal view returns (DataTypes.ModuleExecutionData memory) { + DataTypes.ExecuteSwapParamsData[] memory swapParamsData = new DataTypes.ExecuteSwapParamsData[](2); + swapParamsData[0] = DataTypes.ExecuteSwapParamsData({ + target: tokenIn, + data: abi.encodeWithSelector(IERC20.approve.selector, router, withdrawAmount) + }); + swapParamsData[1] = DataTypes.ExecuteSwapParamsData({ + target: router, + data: abi.encodeWithSelector( + IRouter.exactInputSingle.selector, + IRouter.ExactInputSingleParams({ + tokenIn: tokenIn, + tokenOut: tokenOut, + fee: fee, + recipient: address(superloop), + amountIn: withdrawAmount, + amountOutMinimum: amountOut, + sqrtPriceLimitX96: 0, + deadline: deadline + }) + ) + }); + + DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ + tokenIn: tokenIn, + tokenOut: tokenOut, + amountIn: withdrawAmount, + maxAmountIn: withdrawAmount, + minAmountOut: amountOut, + data: swapParamsData + }); + + return DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.DELEGATECALL, + module: address(dexModule), + data: abi.encodeWithSelector(dexModule.execute.selector, swapParams) + }); + } + + function _swapCallExactOutCurve( + address tokenIn, + address tokenOut, + address pool, + uint256 amountIn, + uint256 amountOut, + CURVE_IJ memory swap + ) internal view returns (DataTypes.ModuleExecutionData memory) { + DataTypes.ExecuteSwapParamsData[] memory swapParamsData = new DataTypes.ExecuteSwapParamsData[](2); + swapParamsData[0] = DataTypes.ExecuteSwapParamsData({ + target: tokenIn, + data: abi.encodeWithSelector(IERC20.approve.selector, pool, amountIn) + }); + swapParamsData[1] = DataTypes.ExecuteSwapParamsData({ + target: pool, + data: abi.encodeWithSelector(ICurvePool.exchange.selector, swap.i, swap.j, amountIn, amountOut) + }); + + DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ + tokenIn: tokenIn, + tokenOut: tokenOut, + amountIn: amountIn, + maxAmountIn: amountIn, + minAmountOut: amountOut, + data: swapParamsData + }); + + return DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.DELEGATECALL, + module: address(dexModule), + data: abi.encodeWithSelector(dexModule.execute.selector, swapParams) + }); + } + + function _repayCall(address asset, uint256 amount) internal view returns (DataTypes.ModuleExecutionData memory) { + DataTypes.AaveV3ActionParams memory repayParams = DataTypes.AaveV3ActionParams({asset: asset, amount: amount}); + return DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.DELEGATECALL, + module: address(repayModule), + data: abi.encodeWithSelector(repayModule.execute.selector, repayParams) + }); + } + + function _withdrawCall(address asset, uint256 amount) + internal + view + returns (DataTypes.ModuleExecutionData memory) + { + DataTypes.AaveV3ActionParams memory withdrawParams = + DataTypes.AaveV3ActionParams({asset: asset, amount: amount}); + return DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.DELEGATECALL, + module: address(withdrawModule), + data: abi.encodeWithSelector(withdrawModule.execute.selector, withdrawParams) + }); + } + + /** + * @dev Creates a partial deposit with resolution. + * @notice Creates 3 deposit requests with 100 xtz each. + * @notice request 1 is fully processed, request 2 is partially processed, request 3 is unprocessed. + */ + function _createPartialDepositWithResolution(bool depositAll) internal returns (uint256, uint256) { + uint256 vaultTokenScale = 10 ** IERC20Metadata(environment.vaultAsset).decimals(); + uint256 lendTokenScale = 10 ** IERC20Metadata(environment.lendAssets[0]).decimals(); + uint256 borrowTokenScale = 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals(); + uint256 depositAmountUnscaled = 100; + uint256 depositAmount = depositAmountUnscaled * vaultTokenScale; + _makeDepositRequest(depositAmount, user1, true); + _makeDepositRequest(depositAmount, user2, true); + _makeDepositRequest(depositAmount, user3, true); + + uint256 depositAmountUnscaledBatch = depositAll ? depositAmountUnscaled * 3 : (3 * depositAmountUnscaled) / 2; + uint256 supplyAmountUnscaled = (3 * depositAmountUnscaledBatch); // 3x + uint256 borrowAmountUnscaled = (3 * supplyAmountUnscaled) / 4; + + uint256 depositAmountBatch = depositAmountUnscaledBatch * lendTokenScale; + // build the operate call + uint256 supplyAmount = supplyAmountUnscaled * lendTokenScale; + uint256 borrowAmount = borrowAmountUnscaled * borrowTokenScale; + uint256 swapAmount = borrowAmount + depositAmountBatch; + uint256 supplyAmountWithPremium = supplyAmount + (supplyAmount * 1) / 10000; + + DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](3); + moduleExecutionData[0] = _supplyCall(environment.lendAssets[0], supplyAmount); + moduleExecutionData[1] = _borrowCall(environment.borrowAssets[0], borrowAmount); + uint256 flashLoanAmount = supplyAmount - depositAmountBatch; + moduleExecutionData[2] = USE_MORPHO + ? _swapCallExactOut( + environment.borrowAssets[0], + environment.lendAssets[0], + borrowAmount, + flashLoanAmount, + environment.router, + USDC_USDE_POOL_FEE, + block.timestamp + 100 + ) + : _swapCallExactOutCurve( + environment.borrowAssets[0], + environment.lendAssets[0], + XTZ_STXTZ_POOL, + swapAmount, + supplyAmountWithPremium, + XTZ_STXTZ_SWAP + ); + + DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); + intermediateExecutionData[0] = USE_MORPHO + ? _morphoFlashloanCall(environment.lendAssets[0], flashLoanAmount, abi.encode(moduleExecutionData)) + : _flashloanCall(environment.lendAssets[0], supplyAmount, abi.encode(moduleExecutionData)); + + DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); + finalExecutionData[0] = _resolveDepositRequestsCall( + environment.vaultAsset, depositAmountBatch, abi.encode(intermediateExecutionData) + ); + + uint256 exchangeRateBefore = superloop.convertToAssets(ONE_SHARE); + + uint256 user1SharesBefore = superloop.balanceOf(user1); + uint256 user2SharesBefore = superloop.balanceOf(user2); + uint256 user3SharesBefore = superloop.balanceOf(user3); + + vm.prank(admin); + superloop.operate(finalExecutionData); + + if (!depositAll) { + // user 1 and user user 2 should get shares + // user 3 should not get shares + + uint256 user1SharesAfter = superloop.balanceOf(user1); + uint256 user2SharesAfter = superloop.balanceOf(user2); + uint256 user3SharesAfter = superloop.balanceOf(user3); + + // deposit manager should have 150 xtz now + assertEq(IERC20(environment.vaultAsset).balanceOf(address(depositManager)), 150 * vaultTokenScale); + // pending deposits should be 150 xtz + assertEq(depositManager.totalPendingDeposits(), 150 * vaultTokenScale); + + // resolution id pointer should be 2 + assertEq(depositManager.resolutionIdPointer(), 2); + + // deposit request 1 should be fully processed + DataTypes.DepositRequestData memory depositRequest1 = depositManager.depositRequest(1); + assertEq(uint256(depositRequest1.state), uint256(DataTypes.RequestProcessingState.FULLY_PROCESSED)); + assertEq(depositRequest1.amountProcessed, 100 * vaultTokenScale); + + // deposit request 2 should be partially processed + DataTypes.DepositRequestData memory depositRequest2 = depositManager.depositRequest(2); + assertEq(uint256(depositRequest2.state), uint256(DataTypes.RequestProcessingState.PARTIALLY_PROCESSED)); + assertEq(depositRequest2.amountProcessed, 50 * vaultTokenScale); + + // deposit request 3 should be unprocessed + DataTypes.DepositRequestData memory depositRequest3 = depositManager.depositRequest(3); + assertEq(uint256(depositRequest3.state), uint256(DataTypes.RequestProcessingState.UNPROCESSED)); + + assertEq(user1SharesAfter - user1SharesBefore, depositRequest1.sharesMinted); + assertEq(user2SharesAfter - user2SharesBefore, depositRequest2.sharesMinted); + assertEq(user3SharesAfter - user3SharesBefore, depositRequest3.sharesMinted); + + // exchange rate before should equal to exchange rate after + uint256 exchangeRateAfter = superloop.convertToAssets(ONE_SHARE); + assertApproxEqAbs(exchangeRateBefore, exchangeRateAfter, 1000); + } + + return (supplyAmountUnscaled, borrowAmountUnscaled); + } + + function _makeDepositRequest(uint256 depositAmount, address user, bool _deal) internal { + if (_deal) { + deal(environment.vaultAsset, user, depositAmount); + } + + vm.startPrank(user); + IERC20(environment.vaultAsset).approve(address(depositManager), depositAmount); + depositManager.requestDeposit(depositAmount, address(0)); + vm.stopPrank(); + } + + function _makeWithdrawRequest( + address share, + uint256 shareAmount, + address user, + DataTypes.WithdrawRequestType requestType + ) internal { + vm.startPrank(user); + IERC20(share).approve(address(withdrawManager), shareAmount); + withdrawManager.requestWithdraw(shareAmount, requestType); + vm.stopPrank(); + } + + /** + * @dev Creates a partial withdraw with resolution. + * @notice Creates 3 withdraw requests with 100 shares each. + * @notice request 1 is fully processed, request 2 is partially processed, request 3 is unprocessed. + */ + function _createPartialWithdrawWithResolution(DataTypes.WithdrawRequestType requestType) + internal + returns (uint256, uint256, uint256) + { + (uint256 supplyAmountUnscaled, uint256 borrowAmountUnscaled) = _createPartialDepositWithResolution(true); + + // make 3 withdraw requests + uint256 user1ShareBalance = superloop.balanceOf(user1); + uint256 user2ShareBalance = superloop.balanceOf(user2); + uint256 user3ShareBalance = superloop.balanceOf(user3); + _makeWithdrawRequest(address(superloop), user1ShareBalance, user1, requestType); + _makeWithdrawRequest(address(superloop), user2ShareBalance, user2, requestType); + _makeWithdrawRequest(address(superloop), user3ShareBalance, user3, requestType); + + // resolve 1st fully, and 2nd partially + // repay half of borrow amount + uint256 repayAmount = (borrowAmountUnscaled * XTZ_SCALE) / 2; + uint256 withdrawAmount = ((supplyAmountUnscaled + 10) * STXTZ_SCALE) / 2; + uint256 repayAmountWithPremium = repayAmount + (repayAmount * 1) / 10000; + uint256 totalShares = superloop.totalSupply(); + uint256 sharesToResolve = totalShares / 2; + + DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](3); + moduleExecutionData[0] = _repayCall(XTZ, repayAmount); + moduleExecutionData[1] = _withdrawCall(ST_XTZ, withdrawAmount); + moduleExecutionData[2] = + _swapCallExactOutCurve(ST_XTZ, XTZ, XTZ_STXTZ_POOL, withdrawAmount, repayAmountWithPremium, STXTZ_XTZ_SWAP); + + DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); + intermediateExecutionData[0] = _flashloanCall(XTZ, repayAmount, abi.encode(moduleExecutionData)); + + DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); + finalExecutionData[0] = + _resolveWithdrawRequestsCall(sharesToResolve, requestType, abi.encode(intermediateExecutionData)); + + uint256 exchangeRateBefore = superloop.convertToAssets(ONE_SHARE); + + vm.prank(admin); + superloop.operate(finalExecutionData); + + // withdraw requests should have claimable > 0 and sharesProcessed > 0, exchange rate remains the same + DataTypes.WithdrawRequestData memory withdrawRequest1 = withdrawManager.withdrawRequest(1, requestType); + DataTypes.WithdrawRequestData memory withdrawRequest2 = withdrawManager.withdrawRequest(2, requestType); + DataTypes.WithdrawRequestData memory withdrawRequest3 = withdrawManager.withdrawRequest(3, requestType); + uint256 resolutionIdPointer = withdrawManager.resolutionIdPointer(requestType); + uint256 totalPendingWithdraws = withdrawManager.totalPendingWithdraws(requestType); + + assertTrue(withdrawRequest1.amountClaimable > 0); + assertTrue(withdrawRequest1.sharesProcessed == withdrawRequest1.shares); + assertTrue(withdrawRequest2.amountClaimable > 0); + assertTrue(withdrawRequest2.sharesProcessed > 0 && withdrawRequest2.sharesProcessed < withdrawRequest2.shares); + assertTrue(withdrawRequest3.amountClaimable == 0); + assertTrue(withdrawRequest3.sharesProcessed == 0); + + assertTrue(withdrawRequest1.state == DataTypes.RequestProcessingState.FULLY_PROCESSED); + assertTrue(withdrawRequest2.state == DataTypes.RequestProcessingState.PARTIALLY_PROCESSED); + assertTrue(withdrawRequest3.state == DataTypes.RequestProcessingState.UNPROCESSED); + + uint256 exchangeRateAfter = superloop.convertToAssets(ONE_SHARE); + + assertApproxEqAbs(exchangeRateAfter, exchangeRateBefore, 1000); + assertEq(totalPendingWithdraws, totalShares - sharesToResolve); + assertEq(resolutionIdPointer, 2); + + return (totalShares - sharesToResolve, repayAmount, withdrawAmount); + } +} diff --git a/test/modules/dex/UniversalDexModule.t.sol b/test/modules/dex/UniversalDexModule.t.sol index 2c9ec25..14fc9ec 100644 --- a/test/modules/dex/UniversalDexModule.t.sol +++ b/test/modules/dex/UniversalDexModule.t.sol @@ -85,7 +85,8 @@ contract UniversalDexModuleTest is TestBase { recipient: address(superloop), amountIn: amountIn, amountOutMinimum: 0, - sqrtPriceLimitX96: 0 + sqrtPriceLimitX96: 0, + deadline: block.timestamp + 100 }) ) }); @@ -146,7 +147,8 @@ contract UniversalDexModuleTest is TestBase { recipient: address(dexModule), amountIn: amountIn, amountOutMinimum: 0, - sqrtPriceLimitX96: 0 + sqrtPriceLimitX96: 0, + deadline: block.timestamp + 100 }) ) }); From bf940600bf75b0e655006d89b5d0fb4a6c8629a7 Mon Sep 17 00:00:00 2001 From: Priyam Anand Date: Wed, 28 Jan 2026 13:25:35 +0530 Subject: [PATCH 15/21] test: init tests for seed --- test/core/integration/FallbackHandler.t.sol | 58 ++++---- test/core/integration/InstantWithdraw.t.sol | 151 ++++++++++---------- test/core/integration/Seed.t.sol | 128 +++++++++-------- 3 files changed, 171 insertions(+), 166 deletions(-) diff --git a/test/core/integration/FallbackHandler.t.sol b/test/core/integration/FallbackHandler.t.sol index dc5a3a5..1cfaf93 100644 --- a/test/core/integration/FallbackHandler.t.sol +++ b/test/core/integration/FallbackHandler.t.sol @@ -1,38 +1,38 @@ -// // SPDX-License-Identifier: MIT +// SPDX-License-Identifier: MIT -// pragma solidity ^0.8.13; +pragma solidity ^0.8.13; -// import {IntegrationBase} from "./IntegrationBase.sol"; -// import {DataTypes} from "../../../src/common/DataTypes.sol"; -// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -// import {DepositManager} from "../../../src/core/DepositManager/DepositManager.sol"; -// import {Errors} from "../../../src/common/Errors.sol"; -// import {MockPreliquidation} from "../../../src/mock/MockPreliquidation.sol"; +import {IntegrationBase} from "./IntegrationBase.sol"; +import {DataTypes} from "../../../src/common/DataTypes.sol"; +import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +import {DepositManager} from "../../../src/core/DepositManager/DepositManager.sol"; +import {Errors} from "../../../src/common/Errors.sol"; +import {MockPreliquidation} from "../../../src/mock/MockPreliquidation.sol"; -// contract FallbackHandlerTest is IntegrationBase { -// address public preliquidation; -// DataTypes.CallType public callType; -// bytes32 public key; +contract FallbackHandlerTest is IntegrationBase { + address public preliquidation; + DataTypes.CallType public callType; + bytes32 public key; -// function setUp() public override { -// super.setUp(); -// callType = DataTypes.CallType.DELEGATECALL; + function setUp() public override { + super.setUp(); + callType = DataTypes.CallType.DELEGATECALL; -// preliquidation = address(new MockPreliquidation()); + preliquidation = address(new MockPreliquidation()); -// key = -// keccak256(abi.encodePacked(abi.encodeWithSelector(MockPreliquidation.preliquidate.selector), id, callType)); -// vm.startPrank(admin); -// superloop.setFallbackHandler(key, preliquidation); -// vm.stopPrank(); -// } + key = + keccak256(abi.encodePacked(abi.encodeWithSelector(MockPreliquidation.preliquidate.selector), id, callType)); + vm.startPrank(admin); + superloop.setFallbackHandler(key, preliquidation); + vm.stopPrank(); + } -// function test_fallbackHandler() public { -// assertEq(superloop.fallbackHandler(key), preliquidation); + function test_fallbackHandler() public { + assertEq(superloop.fallbackHandler(key), preliquidation); -// // make a call to superloop with context of IPreliquidation.preliquidate -// MockPreliquidation(address(superloop)).preliquidate(id, callType, ""); + // make a call to superloop with context of IPreliquidation.preliquidate + MockPreliquidation(address(superloop)).preliquidate(id, callType, ""); -// // this should not revert, not reverting means the decoding in fallback handler is working correctly -// } -// } + // this should not revert, not reverting means the decoding in fallback handler is working correctly + } +} diff --git a/test/core/integration/InstantWithdraw.t.sol b/test/core/integration/InstantWithdraw.t.sol index f76b32c..5d18335 100644 --- a/test/core/integration/InstantWithdraw.t.sol +++ b/test/core/integration/InstantWithdraw.t.sol @@ -1,74 +1,77 @@ -// // SPDX-License-Identifier: MIT - -// pragma solidity ^0.8.13; - -// import {IntegrationBase} from "./IntegrationBase.sol"; -// import {DataTypes} from "../../../src/common/DataTypes.sol"; -// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -// import {DepositManager} from "../../../src/core/DepositManager/DepositManager.sol"; -// import {console} from "forge-std/Test.sol"; -// import {Errors} from "../../../src/common/Errors.sol"; - -// contract InstantWithdrawTest is IntegrationBase { -// function setUp() public override { -// super.setUp(); -// } - -// function test_instantWithdraw() public { -// _seed(); - -// uint256 userSharesBalanceBefore = superloop.balanceOf(admin); -// uint256 userTokenBalanceBefore = IERC20(XTZ).balanceOf(admin); -// uint256 treasurySharesBalanceBefore = superloop.balanceOf(treasury); - -// vm.startPrank(admin); -// uint256 instantWithdrawAmount = 10 * XTZ_SCALE; -// superloop.withdraw(instantWithdrawAmount, admin, admin); - -// uint256 userSharesBalanceAfter = superloop.balanceOf(admin); -// uint256 userTokenBalanceAfter = IERC20(XTZ).balanceOf(admin); -// uint256 treasurySharesBalanceAfter = superloop.balanceOf(treasury); - -// uint256 instantWithdrawFee = (instantWithdrawAmount * INSTANT_WITHDRAW_FEE) / 10_000; -// assertEq(userSharesBalanceBefore - userSharesBalanceAfter, instantWithdrawAmount * 100); -// assertEq(userTokenBalanceAfter - userTokenBalanceBefore, instantWithdrawAmount - instantWithdrawFee); -// assertEq(treasurySharesBalanceAfter - treasurySharesBalanceBefore, instantWithdrawFee * 100); -// } - -// function test_instantRedeem() public { -// _seed(); - -// uint256 userSharesBalanceBefore = superloop.balanceOf(admin); -// uint256 userTokenBalanceBefore = IERC20(XTZ).balanceOf(admin); -// uint256 treasurySharesBalanceBefore = superloop.balanceOf(treasury); - -// vm.startPrank(admin); -// uint256 instantRedeemAmount = 10 * ONE_SHARE; -// superloop.redeem(instantRedeemAmount, admin, admin); - -// uint256 userSharesBalanceAfter = superloop.balanceOf(admin); -// uint256 userTokenBalanceAfter = IERC20(XTZ).balanceOf(admin); -// uint256 treasurySharesBalanceAfter = superloop.balanceOf(treasury); - -// uint256 instantRedeemFee = (instantRedeemAmount * INSTANT_WITHDRAW_FEE) / 10_000; -// assertEq(userSharesBalanceBefore - userSharesBalanceAfter, instantRedeemAmount); -// assertEq(userTokenBalanceAfter - userTokenBalanceBefore, (instantRedeemAmount - instantRedeemFee) / 100); -// assertEq(treasurySharesBalanceAfter - treasurySharesBalanceBefore, instantRedeemFee); -// } - -// function _seed() internal { -// uint256 seedAmount = 100 * XTZ_SCALE; -// deal(XTZ, admin, seedAmount); - -// vm.startPrank(admin); -// IERC20(XTZ).approve(address(superloop), seedAmount); -// superloop.seed(seedAmount); -// vm.stopPrank(); - -// uint256 totalSupply = superloop.totalSupply(); - -// assertApproxEqAbs(totalSupply, seedAmount * 100, 100); -// assertEq(superloop.totalAssets(), seedAmount); -// assertEq(superloop.balanceOf(admin), totalSupply); -// } -// } +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.13; + +import {IntegrationBase} from "./IntegrationBase.sol"; +import {DataTypes} from "../../../src/common/DataTypes.sol"; +import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +import {DepositManager} from "../../../src/core/DepositManager/DepositManager.sol"; +import {console} from "forge-std/Test.sol"; +import {Errors} from "../../../src/common/Errors.sol"; + +contract InstantWithdrawTest is IntegrationBase { + function setUp() public override { + super.setUp(); + } + + function test_instantWithdraw() public { + _seed(); + + uint256 vaultScale = 10 ** environment.vaultAssetDecimals; + + uint256 userSharesBalanceBefore = superloop.balanceOf(admin); + uint256 userTokenBalanceBefore = IERC20(environment.vaultAsset).balanceOf(admin); + uint256 treasurySharesBalanceBefore = superloop.balanceOf(treasury); + + vm.startPrank(admin); + uint256 instantWithdrawAmount = 10 * vaultScale; + superloop.withdraw(instantWithdrawAmount, admin, admin); + + uint256 userSharesBalanceAfter = superloop.balanceOf(admin); + uint256 userTokenBalanceAfter = IERC20(environment.vaultAsset).balanceOf(admin); + uint256 treasurySharesBalanceAfter = superloop.balanceOf(treasury); + + uint256 instantWithdrawFee = (instantWithdrawAmount * INSTANT_WITHDRAW_FEE) / 10_000; + assertEq(userSharesBalanceBefore - userSharesBalanceAfter, instantWithdrawAmount * 100); + assertEq(userTokenBalanceAfter - userTokenBalanceBefore, instantWithdrawAmount - instantWithdrawFee); + assertEq(treasurySharesBalanceAfter - treasurySharesBalanceBefore, instantWithdrawFee * 100); + } + + function test_instantRedeem() public { + _seed(); + + uint256 userSharesBalanceBefore = superloop.balanceOf(admin); + uint256 userTokenBalanceBefore = IERC20(environment.vaultAsset).balanceOf(admin); + uint256 treasurySharesBalanceBefore = superloop.balanceOf(treasury); + + vm.startPrank(admin); + uint256 instantRedeemAmount = 10 * ONE_SHARE; + superloop.redeem(instantRedeemAmount, admin, admin); + + uint256 userSharesBalanceAfter = superloop.balanceOf(admin); + uint256 userTokenBalanceAfter = IERC20(environment.vaultAsset).balanceOf(admin); + uint256 treasurySharesBalanceAfter = superloop.balanceOf(treasury); + + uint256 instantRedeemFee = (instantRedeemAmount * INSTANT_WITHDRAW_FEE) / 10_000; + assertEq(userSharesBalanceBefore - userSharesBalanceAfter, instantRedeemAmount); + assertEq(userTokenBalanceAfter - userTokenBalanceBefore, (instantRedeemAmount - instantRedeemFee) / 100); + assertEq(treasurySharesBalanceAfter - treasurySharesBalanceBefore, instantRedeemFee); + } + + function _seed() internal { + uint256 vaultScale = 10 ** environment.vaultAssetDecimals; + uint256 seedAmount = 100 * vaultScale; + deal(environment.vaultAsset, admin, seedAmount); + + vm.startPrank(admin); + IERC20(environment.vaultAsset).approve(address(superloop), seedAmount); + superloop.seed(seedAmount); + vm.stopPrank(); + + uint256 totalSupply = superloop.totalSupply(); + + assertApproxEqAbs(totalSupply, seedAmount * 100, 100); + assertEq(superloop.totalAssets(), seedAmount); + assertEq(superloop.balanceOf(admin), totalSupply); + } +} diff --git a/test/core/integration/Seed.t.sol b/test/core/integration/Seed.t.sol index 30f9ff9..8a4170b 100644 --- a/test/core/integration/Seed.t.sol +++ b/test/core/integration/Seed.t.sol @@ -1,63 +1,65 @@ -// // SPDX-License-Identifier: MIT - -// pragma solidity ^0.8.13; - -// import {IntegrationBase} from "./IntegrationBase.sol"; -// import {DataTypes} from "../../../src/common/DataTypes.sol"; -// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -// import {DepositManager} from "../../../src/core/DepositManager/DepositManager.sol"; -// import {console} from "forge-std/Test.sol"; -// import {Errors} from "../../../src/common/Errors.sol"; - -// contract SeedTest is IntegrationBase { -// function setUp() public override { -// super.setUp(); -// } - -// function test_seed() public { -// uint256 seedAmount = 10 * XTZ_SCALE; -// deal(XTZ, admin, seedAmount); - -// vm.startPrank(admin); -// IERC20(XTZ).approve(address(superloop), seedAmount); -// superloop.seed(seedAmount); -// vm.stopPrank(); - -// uint256 totalSupply = superloop.totalSupply(); - -// assertApproxEqAbs(totalSupply, 10 * (XTZ_SCALE) * 100, 100); -// assertEq(superloop.totalAssets(), seedAmount); -// assertEq(superloop.balanceOf(admin), totalSupply); -// } - -// function test_seed_failing_cases() public { -// uint256 seedAmount = 10 * XTZ_SCALE; -// deal(XTZ, user1, seedAmount); -// deal(XTZ, admin, seedAmount); - -// // should rever if not called by admin -// vm.startPrank(user1); -// IERC20(XTZ).approve(address(superloop), seedAmount); -// vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); -// superloop.seed(seedAmount); -// vm.stopPrank(); - -// // should not seed with 0 assets -// vm.startPrank(admin); -// vm.expectRevert(bytes(Errors.INVALID_AMOUNT)); -// superloop.seed(0); -// vm.stopPrank(); - -// // should seed -// vm.startPrank(admin); -// IERC20(XTZ).approve(address(superloop), seedAmount); -// superloop.seed(seedAmount); -// vm.stopPrank(); - -// // should revert if already seeded -// vm.startPrank(admin); -// vm.expectRevert(bytes(Errors.VAULT_ALREADY_SEEDED)); -// superloop.seed(seedAmount); -// vm.stopPrank(); -// } -// } +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.13; + +import {IntegrationBase} from "./IntegrationBase.sol"; +import {DataTypes} from "../../../src/common/DataTypes.sol"; +import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +import {DepositManager} from "../../../src/core/DepositManager/DepositManager.sol"; +import {console} from "forge-std/Test.sol"; +import {Errors} from "../../../src/common/Errors.sol"; + +contract SeedTest is IntegrationBase { + function setUp() public override { + super.setUp(); + } + + function test_seed() public { + uint256 vaultScale = 10 ** environment.vaultAssetDecimals; + uint256 seedAmount = 10 * vaultScale; + deal(environment.vaultAsset, admin, seedAmount); + + vm.startPrank(admin); + IERC20(environment.vaultAsset).approve(address(superloop), seedAmount); + superloop.seed(seedAmount); + vm.stopPrank(); + + uint256 totalSupply = superloop.totalSupply(); + + assertApproxEqAbs(totalSupply, 10 * (vaultScale) * 100, 100); + assertEq(superloop.totalAssets(), seedAmount); + assertEq(superloop.balanceOf(admin), totalSupply); + } + + function test_seed_failing_cases() public { + uint256 vaultScale = 10 ** environment.vaultAssetDecimals; + uint256 seedAmount = 10 * vaultScale; + deal(environment.vaultAsset, user1, seedAmount); + deal(environment.vaultAsset, admin, seedAmount); + + // should rever if not called by admin + vm.startPrank(user1); + IERC20(environment.vaultAsset).approve(address(superloop), seedAmount); + vm.expectRevert(bytes(Errors.CALLER_NOT_VAULT_ADMIN)); + superloop.seed(seedAmount); + vm.stopPrank(); + + // should not seed with 0 assets + vm.startPrank(admin); + vm.expectRevert(bytes(Errors.INVALID_AMOUNT)); + superloop.seed(0); + vm.stopPrank(); + + // should seed + vm.startPrank(admin); + IERC20(environment.vaultAsset).approve(address(superloop), seedAmount); + superloop.seed(seedAmount); + vm.stopPrank(); + + // should revert if already seeded + vm.startPrank(admin); + vm.expectRevert(bytes(Errors.VAULT_ALREADY_SEEDED)); + superloop.seed(seedAmount); + vm.stopPrank(); + } +} From 837e7d6991bbad24686486dd2c705bf45a6d700f Mon Sep 17 00:00:00 2001 From: Priyam Anand Date: Wed, 28 Jan 2026 15:17:09 +0530 Subject: [PATCH 16/21] test: withdraw manager tests --- test/core/integration/IntegrationBase.sol | 29 +- test/core/integration/WithdrawManager.t.sol | 784 ++++++++++---------- 2 files changed, 434 insertions(+), 379 deletions(-) diff --git a/test/core/integration/IntegrationBase.sol b/test/core/integration/IntegrationBase.sol index 7b79ae5..b3cdada 100644 --- a/test/core/integration/IntegrationBase.sol +++ b/test/core/integration/IntegrationBase.sol @@ -520,6 +520,10 @@ abstract contract IntegrationBase is TestBase { internal returns (uint256, uint256, uint256) { + uint256 vaultTokenScale = 10 ** IERC20Metadata(environment.vaultAsset).decimals(); + uint256 lendTokenScale = 10 ** IERC20Metadata(environment.lendAssets[0]).decimals(); + uint256 borrowTokenScale = 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals(); + (uint256 supplyAmountUnscaled, uint256 borrowAmountUnscaled) = _createPartialDepositWithResolution(true); // make 3 withdraw requests @@ -532,20 +536,31 @@ abstract contract IntegrationBase is TestBase { // resolve 1st fully, and 2nd partially // repay half of borrow amount - uint256 repayAmount = (borrowAmountUnscaled * XTZ_SCALE) / 2; - uint256 withdrawAmount = ((supplyAmountUnscaled + 10) * STXTZ_SCALE) / 2; + uint256 repayAmount = (borrowAmountUnscaled * borrowTokenScale) / 2; + uint256 withdrawAmount = ((supplyAmountUnscaled + 10) * lendTokenScale) / 2; uint256 repayAmountWithPremium = repayAmount + (repayAmount * 1) / 10000; uint256 totalShares = superloop.totalSupply(); uint256 sharesToResolve = totalShares / 2; DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](3); - moduleExecutionData[0] = _repayCall(XTZ, repayAmount); - moduleExecutionData[1] = _withdrawCall(ST_XTZ, withdrawAmount); - moduleExecutionData[2] = - _swapCallExactOutCurve(ST_XTZ, XTZ, XTZ_STXTZ_POOL, withdrawAmount, repayAmountWithPremium, STXTZ_XTZ_SWAP); + moduleExecutionData[0] = _repayCall(environment.borrowAssets[0], repayAmount); + moduleExecutionData[1] = _withdrawCall(environment.lendAssets[0], withdrawAmount); + moduleExecutionData[2] = USE_MORPHO + ? _swapCallExactOut( + environment.lendAssets[0], + environment.borrowAssets[0], + withdrawAmount, + repayAmount, + environment.router, + USDC_USDE_POOL_FEE, + block.timestamp + 100 + ) + : _swapCallExactOutCurve(ST_XTZ, XTZ, XTZ_STXTZ_POOL, withdrawAmount, repayAmountWithPremium, STXTZ_XTZ_SWAP); DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); - intermediateExecutionData[0] = _flashloanCall(XTZ, repayAmount, abi.encode(moduleExecutionData)); + intermediateExecutionData[0] = USE_MORPHO + ? _morphoFlashloanCall(environment.borrowAssets[0], repayAmount, abi.encode(moduleExecutionData)) + : _flashloanCall(environment.borrowAssets[0], repayAmount, abi.encode(moduleExecutionData)); DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); finalExecutionData[0] = diff --git a/test/core/integration/WithdrawManager.t.sol b/test/core/integration/WithdrawManager.t.sol index 6b28c84..1214d4e 100644 --- a/test/core/integration/WithdrawManager.t.sol +++ b/test/core/integration/WithdrawManager.t.sol @@ -1,372 +1,412 @@ -// // SPDX-License-Identifier: MIT - -// pragma solidity ^0.8.13; - -// import {IntegrationBase} from "./IntegrationBase.sol"; -// import {DataTypes} from "../../../src/common/DataTypes.sol"; -// import {console} from "forge-std/console.sol"; -// import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -// import {Errors} from "../../../src/common/Errors.sol"; - -// contract WithdrawManagerTest is IntegrationBase { -// bool depositAll = true; -// DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.INSTANT; - -// function setUp() public override { -// super.setUp(); - -// DataTypes.AaveV3EmodeParams memory params = DataTypes.AaveV3EmodeParams({emodeCategory: 3}); - -// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); -// moduleExecutionData[0] = DataTypes.ModuleExecutionData({ -// executionType: DataTypes.CallType.DELEGATECALL, -// module: address(emodeModule), -// data: abi.encodeWithSelector(emodeModule.execute.selector, params) -// }); - -// vm.prank(admin); -// superloop.operate(moduleExecutionData); -// } - -// function test_initialize() public view { -// assertEq(withdrawManager.vault(), address(superloop)); -// assertEq(withdrawManager.asset(), XTZ); -// assertEq(withdrawManager.nextWithdrawRequestId(requestType), 1); -// assertEq(withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.INSTANT), 1); -// assertEq(withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.PRIORITY), 1); -// assertEq(withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.DEFERRED), 1); -// } - -// function test_resolveWithdrawRequests() public { -// _createPartialDepositWithResolution(depositAll); - -// // make 3 withdraw requests -// uint256 user1ShareBalance = superloop.balanceOf(user1); -// uint256 user2ShareBalance = superloop.balanceOf(user2); -// uint256 user3ShareBalance = superloop.balanceOf(user3); -// _makeWithdrawRequest(address(superloop), user1ShareBalance, user1, requestType); -// _makeWithdrawRequest(address(superloop), user2ShareBalance, user2, requestType); -// _makeWithdrawRequest(address(superloop), user3ShareBalance, user3, requestType); - -// _resolveAllRequests(user1ShareBalance + user2ShareBalance + user3ShareBalance, requestType); -// } - -// function test_resolveWithdrawRequestsWithPartials() public { -// // make 3 withdraw requests -// // resolve 1st fully, and 2nd partially -// // assert claimable and sharesProcessed for 1st and 2nd requests -// (uint256 sharesLeftToResolve,,) = _createPartialWithdrawWithResolution(requestType); - -// (,, uint256 repayAmount,,,,,,) = poolDataProvider.getUserReserveData(XTZ, address(superloop)); -// (uint256 withdrawAmount,,,,,,,,) = poolDataProvider.getUserReserveData(ST_XTZ, address(superloop)); - -// // resolve 2nd fuly and 3rd partially. I will unwind the entire amount but claim only partial amount -// uint256 repayAmountWithPremium = repayAmount + (repayAmount * 1) / 10000; -// uint256 sharesToResolve = sharesLeftToResolve / 2; - -// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](3); -// moduleExecutionData[0] = _repayCall(XTZ, repayAmount); -// moduleExecutionData[1] = _withdrawCall(ST_XTZ, withdrawAmount); -// moduleExecutionData[2] = -// _swapCallExactOutCurve(ST_XTZ, XTZ, XTZ_STXTZ_POOL, withdrawAmount, repayAmountWithPremium, STXTZ_XTZ_SWAP); - -// DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); -// intermediateExecutionData[0] = _flashloanCall(XTZ, repayAmount, abi.encode(moduleExecutionData)); - -// DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); -// finalExecutionData[0] = -// _resolveWithdrawRequestsCall(sharesToResolve, requestType, abi.encode(intermediateExecutionData)); - -// uint256 exchangeRateBefore = superloop.convertToAssets(ONE_SHARE); - -// vm.prank(admin); -// superloop.operate(finalExecutionData); - -// DataTypes.WithdrawRequestData memory withdrawRequest2 = withdrawManager.withdrawRequest(2, requestType); -// DataTypes.WithdrawRequestData memory withdrawRequest3 = withdrawManager.withdrawRequest(3, requestType); -// uint256 resolutionIdPointer = withdrawManager.resolutionIdPointer(requestType); -// uint256 totalPendingWithdraws = withdrawManager.totalPendingWithdraws(requestType); - -// assertTrue(withdrawRequest2.amountClaimable > 0); -// assertTrue(withdrawRequest2.sharesProcessed == withdrawRequest2.shares); -// assertTrue(withdrawRequest3.amountClaimable > 0); -// assertTrue(withdrawRequest3.sharesProcessed > 0 && withdrawRequest3.sharesProcessed < withdrawRequest3.shares); - -// assertTrue(withdrawRequest2.state == DataTypes.RequestProcessingState.FULLY_PROCESSED); -// assertTrue(withdrawRequest3.state == DataTypes.RequestProcessingState.PARTIALLY_PROCESSED); - -// uint256 exchangeRateAfter = superloop.convertToAssets(ONE_SHARE); -// assertApproxEqAbs(exchangeRateAfter, exchangeRateBefore, 1000); -// assertEq(totalPendingWithdraws, sharesLeftToResolve - sharesToResolve); -// assertEq(resolutionIdPointer, 3); -// } - -// function test_resolveWithdrawRequestsWithCancellation() public { -// (uint256 sharesLeftToResolve,,) = _createPartialWithdrawWithResolution(requestType); - -// // shoud not be able to cancel withdraw request if it's already processed -// vm.startPrank(user1); -// vm.expectRevert(bytes(Errors.INVALID_WITHDRAW_REQUEST_STATE)); -// withdrawManager.cancelWithdrawRequest(1, requestType); -// vm.stopPrank(); - -// // req 2 is partially processed, hence it should be partially cancelled and user 2 should get back the remaining shares + claimable amount -// uint256 user2BalanceBefore = IERC20(XTZ).balanceOf(user2); -// uint256 user2ShareBalanceBefore = superloop.balanceOf(user2); -// vm.startPrank(user2); -// withdrawManager.cancelWithdrawRequest(2, requestType); -// vm.stopPrank(); -// uint256 user2BalanceAfter = IERC20(XTZ).balanceOf(user2); -// uint256 user2ShareBalanceAfter = superloop.balanceOf(user2); - -// DataTypes.WithdrawRequestData memory withdrawRequest2 = withdrawManager.withdrawRequest(2, requestType); -// assertApproxEqRel( -// uint256(withdrawRequest2.state), uint256(DataTypes.RequestProcessingState.PARTIALLY_CANCELLED), 1e18 -// ); -// assertApproxEqRel( -// user2ShareBalanceAfter - user2ShareBalanceBefore, -// withdrawRequest2.shares - withdrawRequest2.sharesProcessed, -// 1e18 -// ); -// assertApproxEqRel(withdrawRequest2.amountClaimable, 0, 1e18); -// assertApproxEqRel(withdrawRequest2.amountClaimed, user2BalanceAfter - user2BalanceBefore, 1e18); -// uint256 totalPendingWithdraws = withdrawManager.totalPendingWithdraws(requestType); -// assertApproxEqRel(totalPendingWithdraws, sharesLeftToResolve - withdrawRequest2.sharesProcessed, 1e18); - -// (,, uint256 repayAmount,,,,,,) = poolDataProvider.getUserReserveData(XTZ, address(superloop)); -// (uint256 withdrawAmount,,,,,,,,) = poolDataProvider.getUserReserveData(ST_XTZ, address(superloop)); - -// // unwind the remaining position -// uint256 repayAmountWithPremium = repayAmount + (repayAmount * 1) / 10000; -// uint256 sharesToResolve = totalPendingWithdraws; - -// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](3); -// moduleExecutionData[0] = _repayCall(XTZ, repayAmount); -// moduleExecutionData[1] = _withdrawCall(ST_XTZ, withdrawAmount); -// moduleExecutionData[2] = -// _swapCallExactOutCurve(ST_XTZ, XTZ, XTZ_STXTZ_POOL, withdrawAmount, repayAmountWithPremium, STXTZ_XTZ_SWAP); - -// DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); -// intermediateExecutionData[0] = _flashloanCall(XTZ, repayAmount, abi.encode(moduleExecutionData)); - -// DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); -// finalExecutionData[0] = -// _resolveWithdrawRequestsCall(sharesToResolve, requestType, abi.encode(intermediateExecutionData)); - -// uint256 exchangeRateBefore = superloop.convertToAssets(ONE_SHARE); - -// vm.prank(admin); -// superloop.operate(finalExecutionData); - -// uint256 exchangeRateAfter = superloop.convertToAssets(ONE_SHARE); -// assertApproxEqAbs(exchangeRateAfter, exchangeRateBefore, 1000); - -// // pending withdraws should be only worth 3rd request -// // 3rd request shoudl be resolved based on amount, ie. 2nd should get skipped -// uint256 resolutionIdPointer = withdrawManager.resolutionIdPointer(requestType); -// DataTypes.WithdrawRequestData memory withdrawRequest3 = withdrawManager.withdrawRequest(3, requestType); -// assertApproxEqRel( -// uint256(withdrawRequest3.state), uint256(DataTypes.RequestProcessingState.FULLY_PROCESSED), 1e18 -// ); -// assertApproxEqRel(withdrawRequest3.sharesProcessed, withdrawRequest3.shares, 1e18); -// assertTrue(withdrawRequest3.amountClaimable > 0); -// totalPendingWithdraws = withdrawManager.totalPendingWithdraws(requestType); -// assertApproxEqRel(totalPendingWithdraws, 0, 1e18); -// assertApproxEqRel(resolutionIdPointer, 4, 1e18); - -// // new withdraw request should be able to be made, ie not blocked because of cancellation -// // user 2 shoudl be able to make a new withdraw request -// vm.startPrank(user2); -// superloop.approve(address(withdrawManager), 10 * ONE_SHARE); -// withdrawManager.requestWithdraw(10 * ONE_SHARE, requestType); -// vm.stopPrank(); - -// vm.startPrank(user3); -// vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_UNCLAIMED)); -// withdrawManager.requestWithdraw(10 * ONE_SHARE, requestType); -// vm.stopPrank(); - -// vm.startPrank(user1); -// vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_UNCLAIMED)); -// withdrawManager.requestWithdraw(10 * ONE_SHARE, requestType); -// vm.stopPrank(); -// } - -// function test_withdraw() public { -// _createPartialWithdrawWithResolution(requestType); - -// // should be able to withdraw if fully processed -// uint256 user1BalanceBefore = IERC20(XTZ).balanceOf(user1); -// uint256 user1ShareBalanceBefore = superloop.balanceOf(user1); -// vm.startPrank(user1); -// withdrawManager.withdraw(requestType); -// vm.stopPrank(); -// uint256 user1BalanceAfter = IERC20(XTZ).balanceOf(user1); -// uint256 user1ShareBalanceAfter = superloop.balanceOf(user1); -// DataTypes.WithdrawRequestData memory withdrawRequest1 = withdrawManager.withdrawRequest(1, requestType); -// assertEq(uint256(withdrawRequest1.state), uint256(DataTypes.RequestProcessingState.FULLY_PROCESSED)); -// assertEq(withdrawRequest1.sharesProcessed, withdrawRequest1.shares); -// assertTrue(withdrawRequest1.amountClaimable == 0); -// assertEq(withdrawRequest1.amountClaimed, user1BalanceAfter - user1BalanceBefore); -// assertEq(user1ShareBalanceAfter - user1ShareBalanceBefore, 0); - -// // should not be able to claim if already claimed -// vm.startPrank(user1); -// vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_ALREADY_CLAIMED)); -// withdrawManager.withdraw(requestType); -// vm.stopPrank(); - -// // should be able to withdraw partially processed -// uint256 user2BalanceBefore = IERC20(XTZ).balanceOf(user2); -// uint256 user2ShareBalanceBefore = superloop.balanceOf(user2); -// vm.startPrank(user2); -// withdrawManager.withdraw(requestType); -// vm.stopPrank(); -// uint256 user2BalanceAfter = IERC20(XTZ).balanceOf(user2); -// uint256 user2ShareBalanceAfter = superloop.balanceOf(user2); -// DataTypes.WithdrawRequestData memory withdrawRequest2 = withdrawManager.withdrawRequest(2, requestType); -// assertEq(uint256(withdrawRequest2.state), uint256(DataTypes.RequestProcessingState.PARTIALLY_PROCESSED)); -// assertTrue(withdrawRequest2.amountClaimable == 0); -// assertEq(withdrawRequest2.amountClaimed, user2BalanceAfter - user2BalanceBefore); -// assertEq(user2ShareBalanceAfter - user2ShareBalanceBefore, 0); - -// // should not be able to claim if nothing left to claim -// vm.startPrank(user2); -// vm.expectRevert(bytes(Errors.CANNOT_CLAIM_ZERO_AMOUNT)); -// withdrawManager.withdraw(requestType); -// vm.stopPrank(); - -// // should not be able to claim if cancelled -// user2BalanceBefore = IERC20(XTZ).balanceOf(user2); -// user2ShareBalanceBefore = superloop.balanceOf(user2); -// vm.startPrank(user2); -// withdrawManager.cancelWithdrawRequest(2, requestType); -// withdrawRequest2 = withdrawManager.withdrawRequest(2, requestType); -// assertEq(uint256(withdrawRequest2.state), uint256(DataTypes.RequestProcessingState.PARTIALLY_CANCELLED)); -// assertEq(withdrawRequest2.amountClaimable, 0); -// vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_NOT_FOUND)); -// withdrawManager.withdraw(requestType); -// vm.stopPrank(); -// user2BalanceAfter = IERC20(XTZ).balanceOf(user2); -// user2ShareBalanceAfter = superloop.balanceOf(user2); -// assertEq(user2BalanceAfter - user2BalanceBefore, 0); -// assertEq( -// user2ShareBalanceAfter - user2ShareBalanceBefore, withdrawRequest2.shares - withdrawRequest2.sharesProcessed -// ); - -// // should not be able to claim if unprocessed -// vm.startPrank(user3); -// vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_ACTIVE)); -// withdrawManager.withdraw(requestType); -// vm.stopPrank(); -// } - -// function test_isolationOfQueues() public { -// if (requestType != DataTypes.WithdrawRequestType.GENERAL) return; - -// _createPartialDepositWithResolution(true); - -// // make a withdraw request by every user on every queue -// _makeWithdrawRequest(address(superloop), ONE_SHARE, user1, DataTypes.WithdrawRequestType.GENERAL); -// _makeWithdrawRequest(address(superloop), ONE_SHARE, user2, DataTypes.WithdrawRequestType.GENERAL); -// _makeWithdrawRequest(address(superloop), ONE_SHARE, user3, DataTypes.WithdrawRequestType.GENERAL); - -// _makeWithdrawRequest(address(superloop), ONE_SHARE, user1, DataTypes.WithdrawRequestType.DEFERRED); -// _makeWithdrawRequest(address(superloop), ONE_SHARE, user2, DataTypes.WithdrawRequestType.DEFERRED); -// _makeWithdrawRequest(address(superloop), ONE_SHARE, user3, DataTypes.WithdrawRequestType.DEFERRED); - -// _makeWithdrawRequest(address(superloop), ONE_SHARE, user1, DataTypes.WithdrawRequestType.PRIORITY); -// _makeWithdrawRequest(address(superloop), ONE_SHARE, user2, DataTypes.WithdrawRequestType.PRIORITY); -// _makeWithdrawRequest(address(superloop), ONE_SHARE, user3, DataTypes.WithdrawRequestType.PRIORITY); - -// _makeWithdrawRequest(address(superloop), ONE_SHARE, user1, DataTypes.WithdrawRequestType.INSTANT); -// _makeWithdrawRequest(address(superloop), ONE_SHARE, user2, DataTypes.WithdrawRequestType.INSTANT); -// _makeWithdrawRequest(address(superloop), ONE_SHARE, user3, DataTypes.WithdrawRequestType.INSTANT); - -// uint256[] memory ids = new uint256[](3); -// ids[0] = 1; -// ids[1] = 2; -// ids[2] = 3; - -// address[] memory users = new address[](3); -// users[0] = user1; -// users[1] = user2; -// users[2] = user3; - -// DataTypes.WithdrawRequestData[] memory withdrawRequestsGeneral = -// withdrawManager.withdrawRequests(ids, DataTypes.WithdrawRequestType.GENERAL); -// DataTypes.WithdrawRequestData[] memory withdrawRequestsDeferred = -// withdrawManager.withdrawRequests(ids, DataTypes.WithdrawRequestType.DEFERRED); -// DataTypes.WithdrawRequestData[] memory withdrawRequestsPriority = -// withdrawManager.withdrawRequests(ids, DataTypes.WithdrawRequestType.PRIORITY); -// DataTypes.WithdrawRequestData[] memory withdrawRequestsInstant = -// withdrawManager.withdrawRequests(ids, DataTypes.WithdrawRequestType.INSTANT); - -// assertEq(withdrawRequestsGeneral.length, 3); -// assertEq(withdrawRequestsDeferred.length, 3); -// assertEq(withdrawRequestsPriority.length, 3); -// assertEq(withdrawRequestsInstant.length, 3); - -// for (uint256 i = 0; i < 3; i++) { -// assertEq(withdrawRequestsGeneral[i].user, users[i]); -// assertEq(withdrawRequestsDeferred[i].user, users[i]); -// assertEq(withdrawRequestsPriority[i].user, users[i]); - -// assertEq(withdrawRequestsInstant[i].shares, ONE_SHARE); -// assertEq(withdrawRequestsDeferred[i].shares, ONE_SHARE); -// assertEq(withdrawRequestsPriority[i].shares, ONE_SHARE); -// assertEq(withdrawRequestsGeneral[i].shares, ONE_SHARE); -// } - -// uint256 totalPendingWithdraws = withdrawManager.totalPendingWithdraws(DataTypes.WithdrawRequestType.GENERAL); -// assertEq(totalPendingWithdraws, 3 * ONE_SHARE); -// totalPendingWithdraws = withdrawManager.totalPendingWithdraws(DataTypes.WithdrawRequestType.DEFERRED); -// assertEq(totalPendingWithdraws, 3 * ONE_SHARE); -// totalPendingWithdraws = withdrawManager.totalPendingWithdraws(DataTypes.WithdrawRequestType.PRIORITY); -// assertEq(totalPendingWithdraws, 3 * ONE_SHARE); -// totalPendingWithdraws = withdrawManager.totalPendingWithdraws(DataTypes.WithdrawRequestType.INSTANT); -// assertEq(totalPendingWithdraws, 3 * ONE_SHARE); -// } - -// function _resolveAllRequests(uint256 sharesToResolve, DataTypes.WithdrawRequestType _requestType) internal { -// (,, uint256 repayAmount,,,,,,) = poolDataProvider.getUserReserveData(XTZ, address(superloop)); -// (uint256 withdrawAmount,,,,,,,,) = poolDataProvider.getUserReserveData(ST_XTZ, address(superloop)); -// uint256 repayAmountWithPremium = repayAmount + (repayAmount * 1) / 10000; - -// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](3); -// moduleExecutionData[0] = _repayCall(XTZ, repayAmount); -// moduleExecutionData[1] = _withdrawCall(ST_XTZ, withdrawAmount); -// moduleExecutionData[2] = -// _swapCallExactOutCurve(ST_XTZ, XTZ, XTZ_STXTZ_POOL, withdrawAmount, repayAmountWithPremium, STXTZ_XTZ_SWAP); - -// DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); -// intermediateExecutionData[0] = _flashloanCall(XTZ, repayAmount, abi.encode(moduleExecutionData)); - -// DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); -// finalExecutionData[0] = -// _resolveWithdrawRequestsCall(sharesToResolve, _requestType, abi.encode(intermediateExecutionData)); - -// uint256 exchangeRateBefore = superloop.convertToAssets(ONE_SHARE); - -// vm.prank(admin); -// superloop.operate(finalExecutionData); - -// // withdraw requests should have claimable > 0 and sharesProcessed > 0, exchange rate remains the same -// DataTypes.WithdrawRequestData memory withdrawRequest1 = withdrawManager.withdrawRequest(1, _requestType); -// DataTypes.WithdrawRequestData memory withdrawRequest2 = withdrawManager.withdrawRequest(2, _requestType); -// DataTypes.WithdrawRequestData memory withdrawRequest3 = withdrawManager.withdrawRequest(3, _requestType); -// uint256 resolutionIdPointer = withdrawManager.resolutionIdPointer(_requestType); -// uint256 totalPendingWithdraws = withdrawManager.totalPendingWithdraws(_requestType); - -// assertTrue(withdrawRequest1.amountClaimable > 0); -// assertTrue(withdrawRequest1.sharesProcessed == withdrawRequest1.shares); -// assertTrue(withdrawRequest2.amountClaimable > 0); -// assertTrue(withdrawRequest2.sharesProcessed == withdrawRequest2.shares); -// assertTrue(withdrawRequest3.amountClaimable > 0); -// assertTrue(withdrawRequest3.sharesProcessed == withdrawRequest3.shares); -// uint256 exchangeRateAfter = superloop.convertToAssets(ONE_SHARE); -// assertApproxEqAbs(exchangeRateAfter, exchangeRateBefore, 1000); -// assertEq(totalPendingWithdraws, 0); -// assertEq(resolutionIdPointer, 4); -// } -// } +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.13; + +import {IntegrationBase} from "./IntegrationBase.sol"; +import {DataTypes} from "../../../src/common/DataTypes.sol"; +import {console} from "forge-std/console.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {Errors} from "../../../src/common/Errors.sol"; + +contract WithdrawManagerTest is IntegrationBase { + bool depositAll = true; + DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.INSTANT; + + function setUp() public override { + super.setUp(); + + DataTypes.AaveV3EmodeParams memory params = + DataTypes.AaveV3EmodeParams({emodeCategory: environment.emodeCategory}); + + DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); + moduleExecutionData[0] = DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.DELEGATECALL, + module: address(emodeModule), + data: abi.encodeWithSelector(emodeModule.execute.selector, params) + }); + + vm.prank(admin); + superloop.operate(moduleExecutionData); + } + + function test_initialize() public view { + assertEq(withdrawManager.vault(), address(superloop)); + assertEq(withdrawManager.asset(), environment.vaultAsset); + assertEq(withdrawManager.nextWithdrawRequestId(requestType), 1); + assertEq(withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.INSTANT), 1); + assertEq(withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.PRIORITY), 1); + assertEq(withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.DEFERRED), 1); + } + + function test_resolveWithdrawRequests() public { + _createPartialDepositWithResolution(depositAll); + + // make 3 withdraw requests + uint256 user1ShareBalance = superloop.balanceOf(user1); + uint256 user2ShareBalance = superloop.balanceOf(user2); + uint256 user3ShareBalance = superloop.balanceOf(user3); + _makeWithdrawRequest(address(superloop), user1ShareBalance, user1, requestType); + _makeWithdrawRequest(address(superloop), user2ShareBalance, user2, requestType); + _makeWithdrawRequest(address(superloop), user3ShareBalance, user3, requestType); + + _resolveAllRequests(user1ShareBalance + user2ShareBalance + user3ShareBalance, requestType); + } + + function test_resolveWithdrawRequestsWithPartials() public { + // make 3 withdraw requests + // resolve 1st fully, and 2nd partially + // assert claimable and sharesProcessed for 1st and 2nd requests + (uint256 sharesLeftToResolve,,) = _createPartialWithdrawWithResolution(requestType); + + (,, uint256 repayAmount,,,,,,) = + poolDataProvider.getUserReserveData(environment.borrowAssets[0], address(superloop)); + (uint256 withdrawAmount,,,,,,,,) = + poolDataProvider.getUserReserveData(environment.lendAssets[0], address(superloop)); + + // resolve 2nd fuly and 3rd partially. I will unwind the entire amount but claim only partial amount + uint256 repayAmountWithPremium = repayAmount + (repayAmount * 1) / 10000; + uint256 sharesToResolve = sharesLeftToResolve / 2; + + DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](3); + moduleExecutionData[0] = _repayCall(environment.borrowAssets[0], repayAmount); + moduleExecutionData[1] = _withdrawCall(environment.lendAssets[0], withdrawAmount); + moduleExecutionData[2] = USE_MORPHO + ? _swapCallExactOut( + environment.lendAssets[0], + environment.borrowAssets[0], + withdrawAmount, + repayAmount, + environment.router, + USDC_USDE_POOL_FEE, + block.timestamp + 100 + ) + : _swapCallExactOutCurve(ST_XTZ, XTZ, XTZ_STXTZ_POOL, withdrawAmount, repayAmountWithPremium, STXTZ_XTZ_SWAP); + + DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); + intermediateExecutionData[0] = USE_MORPHO + ? _morphoFlashloanCall(environment.borrowAssets[0], repayAmount, abi.encode(moduleExecutionData)) + : _flashloanCall(environment.borrowAssets[0], repayAmount, abi.encode(moduleExecutionData)); + + DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); + finalExecutionData[0] = + _resolveWithdrawRequestsCall(sharesToResolve, requestType, abi.encode(intermediateExecutionData)); + + uint256 exchangeRateBefore = superloop.convertToAssets(ONE_SHARE); + + vm.prank(admin); + superloop.operate(finalExecutionData); + + DataTypes.WithdrawRequestData memory withdrawRequest2 = withdrawManager.withdrawRequest(2, requestType); + DataTypes.WithdrawRequestData memory withdrawRequest3 = withdrawManager.withdrawRequest(3, requestType); + uint256 resolutionIdPointer = withdrawManager.resolutionIdPointer(requestType); + uint256 totalPendingWithdraws = withdrawManager.totalPendingWithdraws(requestType); + + assertTrue(withdrawRequest2.amountClaimable > 0); + assertTrue(withdrawRequest2.sharesProcessed == withdrawRequest2.shares); + assertTrue(withdrawRequest3.amountClaimable > 0); + assertTrue(withdrawRequest3.sharesProcessed > 0 && withdrawRequest3.sharesProcessed < withdrawRequest3.shares); + + assertTrue(withdrawRequest2.state == DataTypes.RequestProcessingState.FULLY_PROCESSED); + assertTrue(withdrawRequest3.state == DataTypes.RequestProcessingState.PARTIALLY_PROCESSED); + + uint256 exchangeRateAfter = superloop.convertToAssets(ONE_SHARE); + assertApproxEqAbs(exchangeRateAfter, exchangeRateBefore, 1000); + assertEq(totalPendingWithdraws, sharesLeftToResolve - sharesToResolve); + assertEq(resolutionIdPointer, 3); + } + + function test_resolveWithdrawRequestsWithCancellation() public { + (uint256 sharesLeftToResolve,,) = _createPartialWithdrawWithResolution(requestType); + + // shoud not be able to cancel withdraw request if it's already processed + vm.startPrank(user1); + vm.expectRevert(bytes(Errors.INVALID_WITHDRAW_REQUEST_STATE)); + withdrawManager.cancelWithdrawRequest(1, requestType); + vm.stopPrank(); + + // req 2 is partially processed, hence it should be partially cancelled and user 2 should get back the remaining shares + claimable amount + uint256 user2BalanceBefore = IERC20(environment.vaultAsset).balanceOf(user2); + uint256 user2ShareBalanceBefore = superloop.balanceOf(user2); + vm.startPrank(user2); + withdrawManager.cancelWithdrawRequest(2, requestType); + vm.stopPrank(); + uint256 user2BalanceAfter = IERC20(environment.vaultAsset).balanceOf(user2); + uint256 user2ShareBalanceAfter = superloop.balanceOf(user2); + + DataTypes.WithdrawRequestData memory withdrawRequest2 = withdrawManager.withdrawRequest(2, requestType); + assertApproxEqRel( + uint256(withdrawRequest2.state), uint256(DataTypes.RequestProcessingState.PARTIALLY_CANCELLED), 1e18 + ); + assertApproxEqRel( + user2ShareBalanceAfter - user2ShareBalanceBefore, + withdrawRequest2.shares - withdrawRequest2.sharesProcessed, + 1e18 + ); + assertApproxEqRel(withdrawRequest2.amountClaimable, 0, 1e18); + assertApproxEqRel(withdrawRequest2.amountClaimed, user2BalanceAfter - user2BalanceBefore, 1e18); + uint256 totalPendingWithdraws = withdrawManager.totalPendingWithdraws(requestType); + assertApproxEqRel(totalPendingWithdraws, sharesLeftToResolve - withdrawRequest2.sharesProcessed, 1e18); + + (,, uint256 repayAmount,,,,,,) = + poolDataProvider.getUserReserveData(environment.borrowAssets[0], address(superloop)); + (uint256 withdrawAmount,,,,,,,,) = + poolDataProvider.getUserReserveData(environment.lendAssets[0], address(superloop)); + + // unwind the remaining position + uint256 repayAmountWithPremium = repayAmount + (repayAmount * 1) / 10000; + uint256 sharesToResolve = totalPendingWithdraws; + + DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](3); + moduleExecutionData[0] = _repayCall(environment.borrowAssets[0], repayAmount); + moduleExecutionData[1] = _withdrawCall(environment.lendAssets[0], withdrawAmount); + moduleExecutionData[2] = USE_MORPHO + ? _swapCallExactOut( + environment.lendAssets[0], + environment.borrowAssets[0], + withdrawAmount, + repayAmount, + environment.router, + USDC_USDE_POOL_FEE, + block.timestamp + 100 + ) + : _swapCallExactOutCurve(ST_XTZ, XTZ, XTZ_STXTZ_POOL, withdrawAmount, repayAmountWithPremium, STXTZ_XTZ_SWAP); + + DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); + intermediateExecutionData[0] = USE_MORPHO + ? _morphoFlashloanCall(environment.borrowAssets[0], repayAmount, abi.encode(moduleExecutionData)) + : _flashloanCall(environment.borrowAssets[0], repayAmount, abi.encode(moduleExecutionData)); + + DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); + finalExecutionData[0] = + _resolveWithdrawRequestsCall(sharesToResolve, requestType, abi.encode(intermediateExecutionData)); + + uint256 exchangeRateBefore = superloop.convertToAssets(ONE_SHARE); + + vm.prank(admin); + superloop.operate(finalExecutionData); + + uint256 exchangeRateAfter = superloop.convertToAssets(ONE_SHARE); + assertApproxEqAbs(exchangeRateAfter, exchangeRateBefore, 1000); + + // pending withdraws should be only worth 3rd request + // 3rd request shoudl be resolved based on amount, ie. 2nd should get skipped + uint256 resolutionIdPointer = withdrawManager.resolutionIdPointer(requestType); + DataTypes.WithdrawRequestData memory withdrawRequest3 = withdrawManager.withdrawRequest(3, requestType); + assertApproxEqRel( + uint256(withdrawRequest3.state), uint256(DataTypes.RequestProcessingState.FULLY_PROCESSED), 1e18 + ); + assertApproxEqRel(withdrawRequest3.sharesProcessed, withdrawRequest3.shares, 1e18); + assertTrue(withdrawRequest3.amountClaimable > 0); + totalPendingWithdraws = withdrawManager.totalPendingWithdraws(requestType); + assertApproxEqRel(totalPendingWithdraws, 0, 1e18); + assertApproxEqRel(resolutionIdPointer, 4, 1e18); + + // new withdraw request should be able to be made, ie not blocked because of cancellation + // user 2 shoudl be able to make a new withdraw request + vm.startPrank(user2); + superloop.approve(address(withdrawManager), 10 * ONE_SHARE); + withdrawManager.requestWithdraw(10 * ONE_SHARE, requestType); + vm.stopPrank(); + + vm.startPrank(user3); + vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_UNCLAIMED)); + withdrawManager.requestWithdraw(10 * ONE_SHARE, requestType); + vm.stopPrank(); + + vm.startPrank(user1); + vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_UNCLAIMED)); + withdrawManager.requestWithdraw(10 * ONE_SHARE, requestType); + vm.stopPrank(); + } + + function test_withdraw() public { + _createPartialWithdrawWithResolution(requestType); + + // should be able to withdraw if fully processed + uint256 user1BalanceBefore = IERC20(environment.vaultAsset).balanceOf(user1); + uint256 user1ShareBalanceBefore = superloop.balanceOf(user1); + vm.startPrank(user1); + withdrawManager.withdraw(requestType); + vm.stopPrank(); + uint256 user1BalanceAfter = IERC20(environment.vaultAsset).balanceOf(user1); + uint256 user1ShareBalanceAfter = superloop.balanceOf(user1); + DataTypes.WithdrawRequestData memory withdrawRequest1 = withdrawManager.withdrawRequest(1, requestType); + assertEq(uint256(withdrawRequest1.state), uint256(DataTypes.RequestProcessingState.FULLY_PROCESSED)); + assertEq(withdrawRequest1.sharesProcessed, withdrawRequest1.shares); + assertTrue(withdrawRequest1.amountClaimable == 0); + assertEq(withdrawRequest1.amountClaimed, user1BalanceAfter - user1BalanceBefore); + assertEq(user1ShareBalanceAfter - user1ShareBalanceBefore, 0); + + // should not be able to claim if already claimed + vm.startPrank(user1); + vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_ALREADY_CLAIMED)); + withdrawManager.withdraw(requestType); + vm.stopPrank(); + + // should be able to withdraw partially processed + uint256 user2BalanceBefore = IERC20(environment.vaultAsset).balanceOf(user2); + uint256 user2ShareBalanceBefore = superloop.balanceOf(user2); + vm.startPrank(user2); + withdrawManager.withdraw(requestType); + vm.stopPrank(); + uint256 user2BalanceAfter = IERC20(environment.vaultAsset).balanceOf(user2); + uint256 user2ShareBalanceAfter = superloop.balanceOf(user2); + DataTypes.WithdrawRequestData memory withdrawRequest2 = withdrawManager.withdrawRequest(2, requestType); + assertEq(uint256(withdrawRequest2.state), uint256(DataTypes.RequestProcessingState.PARTIALLY_PROCESSED)); + assertTrue(withdrawRequest2.amountClaimable == 0); + assertEq(withdrawRequest2.amountClaimed, user2BalanceAfter - user2BalanceBefore); + assertEq(user2ShareBalanceAfter - user2ShareBalanceBefore, 0); + + // should not be able to claim if nothing left to claim + vm.startPrank(user2); + vm.expectRevert(bytes(Errors.CANNOT_CLAIM_ZERO_AMOUNT)); + withdrawManager.withdraw(requestType); + vm.stopPrank(); + + // should not be able to claim if cancelled + user2BalanceBefore = IERC20(environment.vaultAsset).balanceOf(user2); + user2ShareBalanceBefore = superloop.balanceOf(user2); + vm.startPrank(user2); + withdrawManager.cancelWithdrawRequest(2, requestType); + withdrawRequest2 = withdrawManager.withdrawRequest(2, requestType); + assertEq(uint256(withdrawRequest2.state), uint256(DataTypes.RequestProcessingState.PARTIALLY_CANCELLED)); + assertEq(withdrawRequest2.amountClaimable, 0); + vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_NOT_FOUND)); + withdrawManager.withdraw(requestType); + vm.stopPrank(); + user2BalanceAfter = IERC20(environment.vaultAsset).balanceOf(user2); + user2ShareBalanceAfter = superloop.balanceOf(user2); + assertEq(user2BalanceAfter - user2BalanceBefore, 0); + assertEq( + user2ShareBalanceAfter - user2ShareBalanceBefore, withdrawRequest2.shares - withdrawRequest2.sharesProcessed + ); + + // should not be able to claim if unprocessed + vm.startPrank(user3); + vm.expectRevert(bytes(Errors.WITHDRAW_REQUEST_ACTIVE)); + withdrawManager.withdraw(requestType); + vm.stopPrank(); + } + + function test_isolationOfQueues() public { + if (requestType != DataTypes.WithdrawRequestType.GENERAL) return; + + _createPartialDepositWithResolution(true); + + // make a withdraw request by every user on every queue + _makeWithdrawRequest(address(superloop), ONE_SHARE, user1, DataTypes.WithdrawRequestType.GENERAL); + _makeWithdrawRequest(address(superloop), ONE_SHARE, user2, DataTypes.WithdrawRequestType.GENERAL); + _makeWithdrawRequest(address(superloop), ONE_SHARE, user3, DataTypes.WithdrawRequestType.GENERAL); + + _makeWithdrawRequest(address(superloop), ONE_SHARE, user1, DataTypes.WithdrawRequestType.DEFERRED); + _makeWithdrawRequest(address(superloop), ONE_SHARE, user2, DataTypes.WithdrawRequestType.DEFERRED); + _makeWithdrawRequest(address(superloop), ONE_SHARE, user3, DataTypes.WithdrawRequestType.DEFERRED); + + _makeWithdrawRequest(address(superloop), ONE_SHARE, user1, DataTypes.WithdrawRequestType.PRIORITY); + _makeWithdrawRequest(address(superloop), ONE_SHARE, user2, DataTypes.WithdrawRequestType.PRIORITY); + _makeWithdrawRequest(address(superloop), ONE_SHARE, user3, DataTypes.WithdrawRequestType.PRIORITY); + + _makeWithdrawRequest(address(superloop), ONE_SHARE, user1, DataTypes.WithdrawRequestType.INSTANT); + _makeWithdrawRequest(address(superloop), ONE_SHARE, user2, DataTypes.WithdrawRequestType.INSTANT); + _makeWithdrawRequest(address(superloop), ONE_SHARE, user3, DataTypes.WithdrawRequestType.INSTANT); + + uint256[] memory ids = new uint256[](3); + ids[0] = 1; + ids[1] = 2; + ids[2] = 3; + + address[] memory users = new address[](3); + users[0] = user1; + users[1] = user2; + users[2] = user3; + + DataTypes.WithdrawRequestData[] memory withdrawRequestsGeneral = + withdrawManager.withdrawRequests(ids, DataTypes.WithdrawRequestType.GENERAL); + DataTypes.WithdrawRequestData[] memory withdrawRequestsDeferred = + withdrawManager.withdrawRequests(ids, DataTypes.WithdrawRequestType.DEFERRED); + DataTypes.WithdrawRequestData[] memory withdrawRequestsPriority = + withdrawManager.withdrawRequests(ids, DataTypes.WithdrawRequestType.PRIORITY); + DataTypes.WithdrawRequestData[] memory withdrawRequestsInstant = + withdrawManager.withdrawRequests(ids, DataTypes.WithdrawRequestType.INSTANT); + + assertEq(withdrawRequestsGeneral.length, 3); + assertEq(withdrawRequestsDeferred.length, 3); + assertEq(withdrawRequestsPriority.length, 3); + assertEq(withdrawRequestsInstant.length, 3); + + for (uint256 i = 0; i < 3; i++) { + assertEq(withdrawRequestsGeneral[i].user, users[i]); + assertEq(withdrawRequestsDeferred[i].user, users[i]); + assertEq(withdrawRequestsPriority[i].user, users[i]); + + assertEq(withdrawRequestsInstant[i].shares, ONE_SHARE); + assertEq(withdrawRequestsDeferred[i].shares, ONE_SHARE); + assertEq(withdrawRequestsPriority[i].shares, ONE_SHARE); + assertEq(withdrawRequestsGeneral[i].shares, ONE_SHARE); + } + + uint256 totalPendingWithdraws = withdrawManager.totalPendingWithdraws(DataTypes.WithdrawRequestType.GENERAL); + assertEq(totalPendingWithdraws, 3 * ONE_SHARE); + totalPendingWithdraws = withdrawManager.totalPendingWithdraws(DataTypes.WithdrawRequestType.DEFERRED); + assertEq(totalPendingWithdraws, 3 * ONE_SHARE); + totalPendingWithdraws = withdrawManager.totalPendingWithdraws(DataTypes.WithdrawRequestType.PRIORITY); + assertEq(totalPendingWithdraws, 3 * ONE_SHARE); + totalPendingWithdraws = withdrawManager.totalPendingWithdraws(DataTypes.WithdrawRequestType.INSTANT); + assertEq(totalPendingWithdraws, 3 * ONE_SHARE); + } + + function _resolveAllRequests(uint256 sharesToResolve, DataTypes.WithdrawRequestType _requestType) internal { + (,, uint256 repayAmount,,,,,,) = + poolDataProvider.getUserReserveData(environment.borrowAssets[0], address(superloop)); + (uint256 withdrawAmount,,,,,,,,) = + poolDataProvider.getUserReserveData(environment.lendAssets[0], address(superloop)); + uint256 repayAmountWithPremium = repayAmount + (repayAmount * 1) / 10000; + + DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](3); + moduleExecutionData[0] = _repayCall(environment.borrowAssets[0], repayAmount); + moduleExecutionData[1] = _withdrawCall(environment.lendAssets[0], withdrawAmount); + moduleExecutionData[2] = USE_MORPHO + ? _swapCallExactOut( + environment.lendAssets[0], + environment.borrowAssets[0], + withdrawAmount, + repayAmount, + environment.router, + USDC_USDE_POOL_FEE, + block.timestamp + 100 + ) + : _swapCallExactOutCurve(ST_XTZ, XTZ, XTZ_STXTZ_POOL, withdrawAmount, repayAmountWithPremium, STXTZ_XTZ_SWAP); + + DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); + intermediateExecutionData[0] = USE_MORPHO + ? _morphoFlashloanCall(environment.borrowAssets[0], repayAmount, abi.encode(moduleExecutionData)) + : _flashloanCall(environment.borrowAssets[0], repayAmount, abi.encode(moduleExecutionData)); + + DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); + finalExecutionData[0] = + _resolveWithdrawRequestsCall(sharesToResolve, _requestType, abi.encode(intermediateExecutionData)); + + uint256 exchangeRateBefore = superloop.convertToAssets(ONE_SHARE); + + vm.prank(admin); + superloop.operate(finalExecutionData); + + // withdraw requests should have claimable > 0 and sharesProcessed > 0, exchange rate remains the same + DataTypes.WithdrawRequestData memory withdrawRequest1 = withdrawManager.withdrawRequest(1, _requestType); + DataTypes.WithdrawRequestData memory withdrawRequest2 = withdrawManager.withdrawRequest(2, _requestType); + DataTypes.WithdrawRequestData memory withdrawRequest3 = withdrawManager.withdrawRequest(3, _requestType); + uint256 resolutionIdPointer = withdrawManager.resolutionIdPointer(_requestType); + uint256 totalPendingWithdraws = withdrawManager.totalPendingWithdraws(_requestType); + + assertTrue(withdrawRequest1.amountClaimable > 0); + assertTrue(withdrawRequest1.sharesProcessed == withdrawRequest1.shares); + assertTrue(withdrawRequest2.amountClaimable > 0); + assertTrue(withdrawRequest2.sharesProcessed == withdrawRequest2.shares); + assertTrue(withdrawRequest3.amountClaimable > 0); + assertTrue(withdrawRequest3.sharesProcessed == withdrawRequest3.shares); + uint256 exchangeRateAfter = superloop.convertToAssets(ONE_SHARE); + assertApproxEqAbs(exchangeRateAfter, exchangeRateBefore, 1000); + assertEq(totalPendingWithdraws, 0); + assertEq(resolutionIdPointer, 4); + } +} From 4197572fd03ea03c7e7f4c578048fd9859342c52 Mon Sep 17 00:00:00 2001 From: Priyam Anand Date: Wed, 28 Jan 2026 17:23:19 +0530 Subject: [PATCH 17/21] test: added tests for merkl module --- src/modules/merkl/IDistributor.sol | 7 ++ test/core/TestBase.sol | 30 ++----- test/core/TestEnv.sol | 13 ++- test/core/unit/DepositManager.t.sol | 1 - test/modules/merkl/MerklModule.t.sol | 120 +++++++++++++++++++++++++++ 5 files changed, 143 insertions(+), 28 deletions(-) create mode 100644 test/modules/merkl/MerklModule.t.sol diff --git a/src/modules/merkl/IDistributor.sol b/src/modules/merkl/IDistributor.sol index c6295a5..86dea74 100644 --- a/src/modules/merkl/IDistributor.sol +++ b/src/modules/merkl/IDistributor.sol @@ -2,6 +2,13 @@ pragma solidity ^0.8.13; interface IDistributor { + struct MerkleTree { + bytes32 merkleRoot; + bytes32 ipfsHash; + } + + function updateTree(MerkleTree calldata _tree) external; + function claim( address[] calldata users, address[] calldata tokens, diff --git a/test/core/TestBase.sol b/test/core/TestBase.sol index 17b1bd1..b990fcc 100644 --- a/test/core/TestBase.sol +++ b/test/core/TestBase.sol @@ -39,32 +39,11 @@ import {HyperbeatStakingModule} from "../../src/modules/stake/hyperliquid/Hyperb import {VaultSupplyModule} from "../../src/modules/vault/VaultSupplyModule.sol"; import {VaultWithdrawModule} from "../../src/modules/vault/VaultWithdrawModule.sol"; +import {MerklModule} from "../../src/modules/merkl/MerklModule.sol"; + import {MorphoFlashloanModule} from "../../src/modules/morpho/MorphoFlashloanModule.sol"; abstract contract TestBase is TestEnv { - // address public constant AAVE_V3_POOL_ADDRESSES_PROVIDER = - // 0x5ccF60c7E10547c5389E9cBFf543E5D0Db9F4feC; - // address public constant AAVE_V3_POOL_DATA_PROVIDER = - // 0x99e8269dDD5c7Af0F1B3973A591b47E8E001BCac; - // address public constant AAVE_V3_PRICE_ORACLE = - // 0xeCF313dE38aA85EF618D06D1A602bAa917D62525; - // address public constant POOL = 0x3bD16D195786fb2F509f2E2D7F69920262EF114D; - // address public constant XTZ_WHALE = - // 0x008ae222661B6A42e3A097bd7AAC15412829106b; - // address public constant STXTZ_WHALE = - // 0x65142dEC2969f1a3083Ad31541Ef4B73871C8C9B; - // address public constant USDT_WHALE = - // 0x998098A1B2E95e2b8f15360676428EdFd976861f; - // address public constant USDT = 0x2C03058C8AFC06713be23e58D2febC8337dbfE6A; - // address public constant USDC = 0x796Ea11Fa2dD751eD01b53C372fFDB4AAa8f00F9; - // address public constant ROUTER = 0xbfe9C246A5EdB4F021C8910155EC93e7CfDaB7a0; - // address public constant USDC_WHALE = - // 0xd03bfdF9B26DB1e6764724d914d7c3d18106a9Fb; - // address public constant POOL_CONFIGURATOR = - // 0x30F6880Bb1cF780a49eB4Ef64E64585780AAe060; - // address public constant POOL_ADMIN = - // 0x669bd328f6C494949Ed9fB2dc8021557A6Dd005f; - uint256 public constant WAD = 10 ** 18; uint256 public constant BPS = 10000; bytes32 id = bytes32("1"); @@ -102,6 +81,7 @@ abstract contract TestBase is TestEnv { UnwrapModule public unwrapModule; WrapModule public wrapModule; DepositManager public depositManager; + MerklModule public merklModule; Superloop public superloopBtc; DepositManager public depositManagerBtc; @@ -166,6 +146,9 @@ abstract contract TestBase is TestEnv { } function _deployModules() internal { + merklModule = new MerklModule(environment.distributor); + moduleRegistry.setModule("MerklModule", address(merklModule)); + morphoFlashloanModule = new MorphoFlashloanModule(environment.morpho); moduleRegistry.setModule("MorphoFlashloanModule", address(morphoFlashloanModule)); @@ -226,6 +209,7 @@ abstract contract TestBase is TestEnv { vm.label(address(withdrawManagerCallbackHandler), "withdrawManagerCallbackHandler"); vm.label(address(vaultSupplyModule), "vaultSupplyModule"); vm.label(address(vaultWithdrawModule), "vaultWithdrawModule"); + vm.label(address(merklModule), "merklModule"); } function _deployHyperliquidStakeModule() internal { diff --git a/test/core/TestEnv.sol b/test/core/TestEnv.sol index f6b9f4c..bb90e59 100644 --- a/test/core/TestEnv.sol +++ b/test/core/TestEnv.sol @@ -23,6 +23,7 @@ abstract contract TestEnv is Test { address stablecoinWhale; address morpho; uint8 emodeCategory; + address distributor; } // etlk chain @@ -81,7 +82,8 @@ abstract contract TestEnv is Test { stablecoin: 0x2C03058C8AFC06713be23e58D2febC8337dbfE6A, stablecoinWhale: 0x998098A1B2E95e2b8f15360676428EdFd976861f, morpho: 0x0000000000000000000000000000000000000000, - emodeCategory: 3 + emodeCategory: 3, + distributor: 0x0000000000000000000000000000000000000000 }) ); @@ -105,7 +107,8 @@ abstract contract TestEnv is Test { stablecoin: 0x2C03058C8AFC06713be23e58D2febC8337dbfE6A, stablecoinWhale: 0x998098A1B2E95e2b8f15360676428EdFd976861f, morpho: 0x0000000000000000000000000000000000000000, - emodeCategory: 2 + emodeCategory: 2, + distributor: 0x0000000000000000000000000000000000000000 }) ); @@ -129,7 +132,8 @@ abstract contract TestEnv is Test { stablecoin: USDC_ETH, stablecoinWhale: USDC_ETH_Whale, morpho: 0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb, - emodeCategory: 2 + emodeCategory: 2, + distributor: 0x3Ef3D8bA38EBe18DB133cEc108f4D14CE00Dd9Ae }) ); @@ -153,7 +157,8 @@ abstract contract TestEnv is Test { stablecoin: USDC_ETH, stablecoinWhale: USDC_ETH_Whale, morpho: 0x0000000000000000000000000000000000000000, - emodeCategory: 1 + emodeCategory: 1, + distributor: 0x0000000000000000000000000000000000000000 }) ); } diff --git a/test/core/unit/DepositManager.t.sol b/test/core/unit/DepositManager.t.sol index 2b03229..c7dbc53 100644 --- a/test/core/unit/DepositManager.t.sol +++ b/test/core/unit/DepositManager.t.sol @@ -13,7 +13,6 @@ import {Errors} from "../../../src/common/Errors.sol"; import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import {DepositManager} from "../../../src/core/DepositManager/DepositManager.sol"; -// TODO : revisit with mutli token setup for eth mainnet contract DepositManagerTest is TestBase { Superloop public superloopImplementation; ProxyAdmin public proxyAdmin; diff --git a/test/modules/merkl/MerklModule.t.sol b/test/modules/merkl/MerklModule.t.sol new file mode 100644 index 0000000..77828f5 --- /dev/null +++ b/test/modules/merkl/MerklModule.t.sol @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.13; + +import {TestBase} from "../../core/TestBase.sol"; +import {DataTypes} from "../../../src/common/DataTypes.sol"; +import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +import { + DepositManager +} from "../../../src/core/DepositManager/DepositManager.sol"; +import {console} from "forge-std/Test.sol"; +import {Errors} from "../../../src/common/Errors.sol"; +import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; +import { + ProxyAdmin +} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import { + TransparentUpgradeableProxy +} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {IDistributor} from "../../../src/modules/merkl/IDistributor.sol"; + +contract MerklModuleTest is TestBase { + Superloop public superloopImplementation; + ProxyAdmin public proxyAdmin; + IDistributor public distributor; + address public user; + + uint256 public AMOUNT; + + function setUp() public override { + super.setUp(); + + vm.startPrank(admin); + _deployModules(); + + address[] memory modules = new address[](1); + modules[0] = address(merklModule); + + DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ + asset: environment.vaultAsset, + name: "Vault", + symbol: "VLT", + supplyCap: 100000 * 10 ** environment.vaultAssetDecimals, + minimumDepositAmount: 100, + instantWithdrawFee: 0, + superloopModuleRegistry: address(moduleRegistry), + modules: modules, + accountant: mockModule, + withdrawManager: mockModule, + depositManager: mockModule, + cashReserve: 1000, + vaultAdmin: admin, + treasury: treasury, + vaultOperator: admin + }); + superloopImplementation = new Superloop(); + proxyAdmin = new ProxyAdmin(address(this)); + + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(superloopImplementation), + address(proxyAdmin), + abi.encodeWithSelector(Superloop.initialize.selector, initData) + ); + superloop = Superloop(payable(address(proxy))); + vm.stopPrank(); + + distributor = IDistributor(environment.distributor); + + user = makeAddr("user"); + vm.label(user, "user"); + vm.label(address(superloop), "superloop"); + + AMOUNT = 1000 * 10 ** environment.vaultAssetDecimals; + address distributorAdmin = 0x435046800Fb9149eE65159721A92cB7d50a7534b; + + // set the root of distributor as hash of vault asset + deal(environment.vaultAsset, environment.distributor, AMOUNT); + bytes32 root = keccak256( + abi.encode(address(superloop), environment.vaultAsset, AMOUNT) + ); + bytes32 ipfsHash = bytes32(0); + + vm.prank(distributorAdmin); + distributor.updateTree(IDistributor.MerkleTree(root, ipfsHash)); + + vm.warp(block.timestamp + 86400); + } + + function test_claim() public { + DataTypes.ModuleExecutionData[] + memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); + address[] memory users = new address[](1); + users[0] = address(superloop); + address[] memory tokens = new address[](1); + tokens[0] = environment.vaultAsset; + uint256[] memory amounts = new uint256[](1); + amounts[0] = AMOUNT; + bytes32[][] memory proofs = new bytes32[][](1); + + moduleExecutionData[0] = DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.DELEGATECALL, + module: address(merklModule), + data: abi.encodeWithSelector( + merklModule.execute.selector, + DataTypes.MerklClaimParams({ + users: users, + tokens: tokens, + amounts: amounts, + proofs: proofs + }) + ) + }); + + vm.prank(admin); + superloop.operate(moduleExecutionData); + + assertEq(IERC20(environment.vaultAsset).balanceOf(address(superloop)), AMOUNT); + + } +} From fe6127eee3ce900aa679c1ca06c144a1dee7d6a9 Mon Sep 17 00:00:00 2001 From: Priyam Anand Date: Wed, 28 Jan 2026 20:13:12 +0530 Subject: [PATCH 18/21] deployment --- .../deploy-eth.s.sol/1/run-1769611299.json | 128 +++++ broadcast/deploy-eth.s.sol/1/run-latest.json | 128 +++++ foundry.toml | 1 + script/deploy-eth.s.sol | 509 ++++++++++++++++++ test/modules/merkl/MerklModule.t.sol | 29 +- 5 files changed, 773 insertions(+), 22 deletions(-) create mode 100644 broadcast/deploy-eth.s.sol/1/run-1769611299.json create mode 100644 broadcast/deploy-eth.s.sol/1/run-latest.json create mode 100644 script/deploy-eth.s.sol diff --git a/broadcast/deploy-eth.s.sol/1/run-1769611299.json b/broadcast/deploy-eth.s.sol/1/run-1769611299.json new file mode 100644 index 0000000..4811b67 --- /dev/null +++ b/broadcast/deploy-eth.s.sol/1/run-1769611299.json @@ -0,0 +1,128 @@ +{ + "transactions": [ + { + "hash": "0xc444efa8f79193262d23fb8db2650e732690bdc1afa465c11d9558c2abc2ed5f", + "transactionType": "CALL", + "contractName": null, + "contractAddress": "0xb402e0479579b00f9dafc0526234bbc4583920b4", + "function": "seed(uint256)", + "arguments": [ + "10000000000000000" + ], + "transaction": { + "from": "0xf5ac5943d16fc865824910033b756519dc396682", + "to": "0xb402e0479579b00f9dafc0526234bbc4583920b4", + "gas": "0x11092a", + "value": "0x0", + "input": "0x95564837000000000000000000000000000000000000000000000000002386f26fc10000", + "nonce": "0x3c", + "chainId": "0x1" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x2c18394", + "logs": [ + { + "address": "0xc808b5f2315e47ddf95d2ce63e1445024a3dc4c9", + "topics": [ + "0x9a3674ff60a2c4c28f4df6f1f1c4a5f9721658e388e8a66ac38bf55e7de4d953" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002386f26fc10000", + "blockHash": "0xfaa5b5eaf14d8410443732e11a079a52d41f85c69f1781781e26116fc359d865", + "blockNumber": "0x1734eb3", + "blockTimestamp": "0x697a201f", + "transactionHash": "0xc444efa8f79193262d23fb8db2650e732690bdc1afa465c11d9558c2abc2ed5f", + "transactionIndex": "0x1ba", + "logIndex": "0x441", + "removed": false + }, + { + "address": "0x4c9edd5852cd905f086c759e8383e09bff1e68b3", + "topics": [ + "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "0x000000000000000000000000f5ac5943d16fc865824910033b756519dc396682", + "0x000000000000000000000000b402e0479579b00f9dafc0526234bbc4583920b4" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0xfaa5b5eaf14d8410443732e11a079a52d41f85c69f1781781e26116fc359d865", + "blockNumber": "0x1734eb3", + "blockTimestamp": "0x697a201f", + "transactionHash": "0xc444efa8f79193262d23fb8db2650e732690bdc1afa465c11d9558c2abc2ed5f", + "transactionIndex": "0x1ba", + "logIndex": "0x442", + "removed": false + }, + { + "address": "0x4c9edd5852cd905f086c759e8383e09bff1e68b3", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x000000000000000000000000f5ac5943d16fc865824910033b756519dc396682", + "0x000000000000000000000000b402e0479579b00f9dafc0526234bbc4583920b4" + ], + "data": "0x000000000000000000000000000000000000000000000000002386f26fc10000", + "blockHash": "0xfaa5b5eaf14d8410443732e11a079a52d41f85c69f1781781e26116fc359d865", + "blockNumber": "0x1734eb3", + "blockTimestamp": "0x697a201f", + "transactionHash": "0xc444efa8f79193262d23fb8db2650e732690bdc1afa465c11d9558c2abc2ed5f", + "transactionIndex": "0x1ba", + "logIndex": "0x443", + "removed": false + }, + { + "address": "0xb402e0479579b00f9dafc0526234bbc4583920b4", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000f5ac5943d16fc865824910033b756519dc396682" + ], + "data": "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000", + "blockHash": "0xfaa5b5eaf14d8410443732e11a079a52d41f85c69f1781781e26116fc359d865", + "blockNumber": "0x1734eb3", + "blockTimestamp": "0x697a201f", + "transactionHash": "0xc444efa8f79193262d23fb8db2650e732690bdc1afa465c11d9558c2abc2ed5f", + "transactionIndex": "0x1ba", + "logIndex": "0x444", + "removed": false + }, + { + "address": "0xb402e0479579b00f9dafc0526234bbc4583920b4", + "topics": [ + "0xdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7", + "0x000000000000000000000000f5ac5943d16fc865824910033b756519dc396682", + "0x000000000000000000000000f5ac5943d16fc865824910033b756519dc396682" + ], + "data": "0x000000000000000000000000000000000000000000000000002386f26fc100000000000000000000000000000000000000000000000000000de0b6b3a7640000", + "blockHash": "0xfaa5b5eaf14d8410443732e11a079a52d41f85c69f1781781e26116fc359d865", + "blockNumber": "0x1734eb3", + "blockTimestamp": "0x697a201f", + "transactionHash": "0xc444efa8f79193262d23fb8db2650e732690bdc1afa465c11d9558c2abc2ed5f", + "transactionIndex": "0x1ba", + "logIndex": "0x445", + "removed": false + } + ], + "logsBloom": "0x00000000000400008000000080000400000000000000000000000004000000000000000000000000000000000000000800000000000000000004000004200000000000410800000000000008000000000000000000000000000100000000000000000002020000000000000000000800000000000004000000000010000000000000000800000000002000000000000000000000008000000000000000000000020000000000000000000000000000000000000000100000000000000000000000000002000000000000000000000000020000000008000000000000000020000010000000000000000000000000000000000000000000000000000000040000", + "type": "0x2", + "transactionHash": "0xc444efa8f79193262d23fb8db2650e732690bdc1afa465c11d9558c2abc2ed5f", + "transactionIndex": "0x1ba", + "blockHash": "0xfaa5b5eaf14d8410443732e11a079a52d41f85c69f1781781e26116fc359d865", + "blockNumber": "0x1734eb3", + "gasUsed": "0xc6219", + "effectiveGasPrice": "0xc1e07ab", + "from": "0xf5ac5943d16fc865824910033b756519dc396682", + "to": "0xb402e0479579b00f9dafc0526234bbc4583920b4", + "contractAddress": null + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1769611299, + "chain": 1, + "commit": "4197572" +} \ No newline at end of file diff --git a/broadcast/deploy-eth.s.sol/1/run-latest.json b/broadcast/deploy-eth.s.sol/1/run-latest.json new file mode 100644 index 0000000..4811b67 --- /dev/null +++ b/broadcast/deploy-eth.s.sol/1/run-latest.json @@ -0,0 +1,128 @@ +{ + "transactions": [ + { + "hash": "0xc444efa8f79193262d23fb8db2650e732690bdc1afa465c11d9558c2abc2ed5f", + "transactionType": "CALL", + "contractName": null, + "contractAddress": "0xb402e0479579b00f9dafc0526234bbc4583920b4", + "function": "seed(uint256)", + "arguments": [ + "10000000000000000" + ], + "transaction": { + "from": "0xf5ac5943d16fc865824910033b756519dc396682", + "to": "0xb402e0479579b00f9dafc0526234bbc4583920b4", + "gas": "0x11092a", + "value": "0x0", + "input": "0x95564837000000000000000000000000000000000000000000000000002386f26fc10000", + "nonce": "0x3c", + "chainId": "0x1" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x2c18394", + "logs": [ + { + "address": "0xc808b5f2315e47ddf95d2ce63e1445024a3dc4c9", + "topics": [ + "0x9a3674ff60a2c4c28f4df6f1f1c4a5f9721658e388e8a66ac38bf55e7de4d953" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002386f26fc10000", + "blockHash": "0xfaa5b5eaf14d8410443732e11a079a52d41f85c69f1781781e26116fc359d865", + "blockNumber": "0x1734eb3", + "blockTimestamp": "0x697a201f", + "transactionHash": "0xc444efa8f79193262d23fb8db2650e732690bdc1afa465c11d9558c2abc2ed5f", + "transactionIndex": "0x1ba", + "logIndex": "0x441", + "removed": false + }, + { + "address": "0x4c9edd5852cd905f086c759e8383e09bff1e68b3", + "topics": [ + "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "0x000000000000000000000000f5ac5943d16fc865824910033b756519dc396682", + "0x000000000000000000000000b402e0479579b00f9dafc0526234bbc4583920b4" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0xfaa5b5eaf14d8410443732e11a079a52d41f85c69f1781781e26116fc359d865", + "blockNumber": "0x1734eb3", + "blockTimestamp": "0x697a201f", + "transactionHash": "0xc444efa8f79193262d23fb8db2650e732690bdc1afa465c11d9558c2abc2ed5f", + "transactionIndex": "0x1ba", + "logIndex": "0x442", + "removed": false + }, + { + "address": "0x4c9edd5852cd905f086c759e8383e09bff1e68b3", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x000000000000000000000000f5ac5943d16fc865824910033b756519dc396682", + "0x000000000000000000000000b402e0479579b00f9dafc0526234bbc4583920b4" + ], + "data": "0x000000000000000000000000000000000000000000000000002386f26fc10000", + "blockHash": "0xfaa5b5eaf14d8410443732e11a079a52d41f85c69f1781781e26116fc359d865", + "blockNumber": "0x1734eb3", + "blockTimestamp": "0x697a201f", + "transactionHash": "0xc444efa8f79193262d23fb8db2650e732690bdc1afa465c11d9558c2abc2ed5f", + "transactionIndex": "0x1ba", + "logIndex": "0x443", + "removed": false + }, + { + "address": "0xb402e0479579b00f9dafc0526234bbc4583920b4", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000f5ac5943d16fc865824910033b756519dc396682" + ], + "data": "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000", + "blockHash": "0xfaa5b5eaf14d8410443732e11a079a52d41f85c69f1781781e26116fc359d865", + "blockNumber": "0x1734eb3", + "blockTimestamp": "0x697a201f", + "transactionHash": "0xc444efa8f79193262d23fb8db2650e732690bdc1afa465c11d9558c2abc2ed5f", + "transactionIndex": "0x1ba", + "logIndex": "0x444", + "removed": false + }, + { + "address": "0xb402e0479579b00f9dafc0526234bbc4583920b4", + "topics": [ + "0xdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7", + "0x000000000000000000000000f5ac5943d16fc865824910033b756519dc396682", + "0x000000000000000000000000f5ac5943d16fc865824910033b756519dc396682" + ], + "data": "0x000000000000000000000000000000000000000000000000002386f26fc100000000000000000000000000000000000000000000000000000de0b6b3a7640000", + "blockHash": "0xfaa5b5eaf14d8410443732e11a079a52d41f85c69f1781781e26116fc359d865", + "blockNumber": "0x1734eb3", + "blockTimestamp": "0x697a201f", + "transactionHash": "0xc444efa8f79193262d23fb8db2650e732690bdc1afa465c11d9558c2abc2ed5f", + "transactionIndex": "0x1ba", + "logIndex": "0x445", + "removed": false + } + ], + "logsBloom": "0x00000000000400008000000080000400000000000000000000000004000000000000000000000000000000000000000800000000000000000004000004200000000000410800000000000008000000000000000000000000000100000000000000000002020000000000000000000800000000000004000000000010000000000000000800000000002000000000000000000000008000000000000000000000020000000000000000000000000000000000000000100000000000000000000000000002000000000000000000000000020000000008000000000000000020000010000000000000000000000000000000000000000000000000000000040000", + "type": "0x2", + "transactionHash": "0xc444efa8f79193262d23fb8db2650e732690bdc1afa465c11d9558c2abc2ed5f", + "transactionIndex": "0x1ba", + "blockHash": "0xfaa5b5eaf14d8410443732e11a079a52d41f85c69f1781781e26116fc359d865", + "blockNumber": "0x1734eb3", + "gasUsed": "0xc6219", + "effectiveGasPrice": "0xc1e07ab", + "from": "0xf5ac5943d16fc865824910033b756519dc396682", + "to": "0xb402e0479579b00f9dafc0526234bbc4583920b4", + "contractAddress": null + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1769611299, + "chain": 1, + "commit": "4197572" +} \ No newline at end of file diff --git a/foundry.toml b/foundry.toml index b10e7e5..35349e8 100644 --- a/foundry.toml +++ b/foundry.toml @@ -14,3 +14,4 @@ mainnet="https://ethereum-rpc.publicnode.com" [etherscan] etherlink={ key = "NO_KEY", chain = 42793, url= "https://explorer.etherlink.com/api/" } + diff --git a/script/deploy-eth.s.sol b/script/deploy-eth.s.sol new file mode 100644 index 0000000..ff398ae --- /dev/null +++ b/script/deploy-eth.s.sol @@ -0,0 +1,509 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.13; + +import {Script} from "forge-std/Script.sol"; +import {console} from "forge-std/console.sol"; +import {UniversalDexModule} from "../src/modules/dex/UniversalDexModule.sol"; +import {AaveV3FlashloanModule} from "../src/modules/aave/AaveV3FlashloanModule.sol"; +import {AaveV3CallbackHandler} from "../src/modules/callback/AaveV3CallbackHandler.sol"; +import {AaveV3EmodeModule} from "../src/modules/aave/AaveV3EmodeModule.sol"; +import {AaveV3SupplyModule} from "../src/modules/aave/AaveV3SupplyModule.sol"; +import {AaveV3WithdrawModule} from "../src/modules/aave/AaveV3WithdrawModule.sol"; +import {AaveV3BorrowModule} from "../src/modules/aave/AaveV3BorrowModule.sol"; +import {AaveV3RepayModule} from "../src/modules/aave/AaveV3RepayModule.sol"; +import {SuperloopModuleRegistry} from "../src/core/ModuleRegistry/ModuleRegistry.sol"; +import {TransparentUpgradeableProxy} from + "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {DataTypes} from "../src/common/DataTypes.sol"; +import {Superloop} from "../src/core/Superloop/Superloop.sol"; +import {IFlashLoanSimpleReceiver} from "aave-v3-core/contracts/flashloan/interfaces/IFlashLoanSimpleReceiver.sol"; +import {DepositManagerCallbackHandler} from "../src/modules/callback/DepositManagerCallbackHandler.sol"; +import {WithdrawManagerCallbackHandler} from "../src/modules/callback/WithdrawManagerCallbackHandler.sol"; +import {DepositManager} from "../src/core/DepositManager/DepositManager.sol"; +import {WithdrawManager} from "../src/core/WithdrawManager/WithdrawManager.sol"; +import {UniversalAccountant} from "../src/core/Accountant/universalAccountant/UniversalAccountant.sol"; +import {AaveV3AccountantPlugin} from "../src/plugins/accountant/AaveV3AccountantPlugin.sol"; +import {AaveV3PreliquidationFallbackHandler} from "../src/modules/fallback/AaveV3PreliquidationFallbackHandler.sol"; +import {VaultRouter} from "../src/helpers/VaultRouter.sol"; +import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +import {MorphoFlashloanModule} from "../src/modules/morpho/MorphoFlashloanModule.sol"; +import {MorphoCallbackHandler} from "../src/modules/callback/MorphoCallbackHandler.sol"; +import {MerklModule} from "../src/modules/merkl/MerklModule.sol"; +import {IMorphoFlashLoanCallback} from "morpho-blue/interfaces/IMorphoCallbacks.sol"; + +contract Deploy is Script { + address public deployer; + uint256 public deployerPvtKey; + address public vaultAdmin; + address public rebalanceAdmin; + address public treasury; + address public vaultOperator; + + address public asset; + address[] public lendAssets; + address[] public borrowAssets; + string public name; + string public symbol; + uint256 public supplyCap; + uint256 public minimumDepositAmount; + uint256 public instantWithdrawFee; + uint256 public cashReserve; + uint256 public performanceFee; + uint256 public seedAmount; + + address public AAVE_V3_POOL_ADDRESSES_PROVIDER = 0x2f39d218133AFaB8F2B819B1066c7E434Ad94E9e; + address public constant USDe = 0x4c9EDD5852cd905f086C759E8383e09bff1E68B3; + address public constant sUSDe = 0x9D39A5DE30e57443BfF2A8307A4256c8797A3497; + address public constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; + address public constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7; + address public constant POOL = 0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2; + address public constant MORPHO = 0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb; + address public constant DISTRIBUTOR = 0x3Ef3D8bA38EBe18DB133cEc108f4D14CE00Dd9Ae; + address public constant VAULT_ADMIN = address(0); // TODO: change to actual address + address public constant TREASURY = address(0); // TODO: change to actual address + uint8 public constant EMODE_CATEGORY = 2; + uint256 public constant USDe_SCALE = 10 ** 18; + uint256 public constant sUSDe_SCALE = 10 ** 18; + uint256 public constant USDC_SCALE = 10 ** 6; + uint256 public constant USDT_SCALE = 10 ** 6; + + SuperloopModuleRegistry public moduleRegistry; + + // aave modules + AaveV3FlashloanModule public flashloanModule; + AaveV3CallbackHandler public aaveFlashloanCallbackHandler; + AaveV3EmodeModule public emodeModule; + AaveV3SupplyModule public supplyModule; + AaveV3WithdrawModule public withdrawModule; + AaveV3BorrowModule public borrowModule; + AaveV3RepayModule public repayModule; + + // morpho module + MorphoFlashloanModule public morphoFlashloanModule; + MorphoCallbackHandler public morphoCallbackHandler; + + // dex module + UniversalDexModule public dexModule; + + // merkl module + MerklModule public merklModule; + + DepositManagerCallbackHandler public depositManagerCallbackHandler; + WithdrawManagerCallbackHandler public withdrawManagerCallbackHandler; + + // TODO: Add later + AaveV3PreliquidationFallbackHandler public preliquidationFallbackHandler; + + address public accountantImplementation; + address public withdrawManagerImplementation; + address public depositManagerImplementation; + address public vaultImplementation; + + address public accountantAaveV3Plugin; + + UniversalAccountant public accountant; + WithdrawManager public withdrawManager; + DepositManager public depositManager; + + Superloop public superloop; + VaultRouter public vaultRouter; + + function setUp() public { + asset = USDe; + name = "Test Superloop USDe"; + symbol = "TestsloopUSDe"; + supplyCap = 100_000 * USDe_SCALE; // in token terms + minimumDepositAmount = 100; // in token terms + instantWithdrawFee = 10; // 0.1% in BPS + cashReserve = 100; // 1% in BPS + performanceFee = 1000; // 10% in bps + lendAssets = new address[](2); + lendAssets[0] = USDe; + lendAssets[1] = sUSDe; + borrowAssets = new address[](2); + borrowAssets[0] = USDC; + borrowAssets[1] = USDT; + + seedAmount = (1 * USDe_SCALE) / 100; // in token terms + + // TODO: Check this config every time before deploying + console.log("--------------------------------"); + console.log("Using configs: "); + console.log("ASSET", asset); + console.log("NAME", name); + console.log("SYMBOL", symbol); + console.log("PERFORMANCE_FEE", performanceFee); + console.log("SUPPLY_CAP", supplyCap); + console.log("MINIMUM_DEPOSIT_AMOUNT", minimumDepositAmount); + console.log("INSTANT_WITHDRAW_FEE", instantWithdrawFee); + console.log("CASH_RESERVE", cashReserve); + console.log("SEED_AMOUNT", seedAmount); + console.log("--------------------------------"); + + vm.createSelectFork("mainnet"); + + deployerPvtKey = vm.envUint("PRIVATE_KEY"); + deployer = vm.addr(deployerPvtKey); + + vaultAdmin = deployer; // TODO: change to actual address + treasury = deployer; // TODO: change to actual address + // xtz vault's operator + vaultOperator = 0x0E9852b16AE49C99B84b0241E3C6F4a5692C6b05; + + console.log("--------------------------------"); + console.log("Depolyer config and roles: "); + console.log("DEPLOYER", deployer); + console.log("VAULT_ADMIN", vaultAdmin); + console.log("TREASURY", treasury); + console.log("VAULT_OPERATOR", vaultOperator); + console.log("--------------------------------"); + } + + function run() public { + vm.startBroadcast(deployerPvtKey); + + // deploy module registry + moduleRegistry = SuperloopModuleRegistry(0x6C520b2f5daDcFdA822AAF7f2b7470b3A686Fe1A); + + // deploy all the modules + deployModules(); + + address[] memory modules = new address[](13); + modules[0] = address(flashloanModule); + modules[1] = address(aaveFlashloanCallbackHandler); + modules[2] = address(emodeModule); + modules[3] = address(supplyModule); + modules[4] = address(withdrawModule); + modules[5] = address(borrowModule); + modules[6] = address(repayModule); + modules[7] = address(dexModule); + modules[8] = address(morphoFlashloanModule); + modules[9] = address(morphoCallbackHandler); + modules[10] = address(merklModule); + modules[11] = address(depositManagerCallbackHandler); + modules[12] = address(withdrawManagerCallbackHandler); + + // deploy vault + DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ + asset: asset, + name: name, + symbol: symbol, + supplyCap: supplyCap, + superloopModuleRegistry: address(moduleRegistry), + modules: modules, + accountant: address(accountant), + withdrawManager: address(withdrawManager), + minimumDepositAmount: minimumDepositAmount, + instantWithdrawFee: instantWithdrawFee, + cashReserve: cashReserve, + depositManager: address(depositManager), + vaultAdmin: vaultAdmin, + treasury: treasury, + vaultOperator: vaultOperator + }); + vaultImplementation = address(new Superloop()); + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(vaultImplementation), + address(vaultAdmin), + abi.encodeWithSelector(Superloop.initialize.selector, initData) + ); + superloop = Superloop(payable(address(proxy))); + + // deploy accountant + _deployAccountant(address(superloop)); + moduleRegistry.setModule("universalAccountant", address(accountant)); + superloop.setRegisteredModule(address(accountant), true); + superloop.setAccountantModule(address(accountant)); + + // deploy withdraw manager + _deployWithdrawManager(address(superloop)); + moduleRegistry.setModule("withdrawManager", address(withdrawManager)); + superloop.setRegisteredModule(address(withdrawManager), true); + superloop.setWithdrawManagerModule(address(withdrawManager)); + + // deploy deposit manager + _deployDepositManager(address(superloop)); + moduleRegistry.setModule("depositManager", address(depositManager)); + superloop.setRegisteredModule(address(depositManager), true); + superloop.setDepositManagerModule(address(depositManager)); + + // set callback handler + _setupCallbackHandlers(); + + // call emode module and setup emode 3 + _setupEmode(); + + // setup vault router + _setupVaultRouter(); + + // seed the vault + _seedVault(); + + /** + * ROLE TRANSFERS : + * 1. Vault admin => safe + * 2. Vault proxy admin => safe + * 3. Accountant plugin owner => safe + * 4. Accountant => safe + * 5. Accountant proxy admin => safe + * 6. Deposit manager proxy admin => safe + * 7. Withdraw manager proxy admin => safe + * 8. Module registry owner => safe + * 9. Vault operator => rebalance admin + * 10. Vault router owner => vault admin + */ + _logAddresses(); + + vm.stopBroadcast(); + } + + function deployModules() internal { + // deploy morpho flashloan module + morphoFlashloanModule = + // new MorphoFlashloanModule(MORPHO); + // moduleRegistry.setModule("MorphoFlashloanModule", address(morphoFlashloanModule)); + morphoCallbackHandler = new MorphoCallbackHandler(); + moduleRegistry.setModule("MorphoCallbackHandler", address(morphoCallbackHandler)); + + // deploy aave flashloan module + flashloanModule = new AaveV3FlashloanModule(AAVE_V3_POOL_ADDRESSES_PROVIDER); + moduleRegistry.setModule("AaveV3FlashloanModule", address(flashloanModule)); + + aaveFlashloanCallbackHandler = new AaveV3CallbackHandler(); + moduleRegistry.setModule("AaveV3CallbackHandler", address(aaveFlashloanCallbackHandler)); + + depositManagerCallbackHandler = new DepositManagerCallbackHandler(); + moduleRegistry.setModule("DepositManagerCallbackHandler", address(depositManagerCallbackHandler)); + + withdrawManagerCallbackHandler = new WithdrawManagerCallbackHandler(); + moduleRegistry.setModule("WithdrawManagerCallbackHandler", address(withdrawManagerCallbackHandler)); + + // TODO: add later + // preliquidationFallbackHandler = new AaveV3PreliquidationFallbackHandler( + // AAVE_V3_POOL_ADDRESSES_PROVIDER, + // address(superloop), + // 2, + // 8, + // DataTypes.AaveV3PreliquidationParamsInit({}) + // ); + // moduleRegistry.setModule("AaveV3PreliquidationFallbackHandler", address(preliquidationFallbackHandler)); + + emodeModule = new AaveV3EmodeModule(AAVE_V3_POOL_ADDRESSES_PROVIDER); + moduleRegistry.setModule("AaveV3EmodeModule", address(emodeModule)); + + supplyModule = new AaveV3SupplyModule(AAVE_V3_POOL_ADDRESSES_PROVIDER); + moduleRegistry.setModule("AaveV3SupplyModule", address(supplyModule)); + + withdrawModule = new AaveV3WithdrawModule(AAVE_V3_POOL_ADDRESSES_PROVIDER); + moduleRegistry.setModule("AaveV3WithdrawModule", address(withdrawModule)); + + borrowModule = new AaveV3BorrowModule(AAVE_V3_POOL_ADDRESSES_PROVIDER); + moduleRegistry.setModule("AaveV3BorrowModule", address(borrowModule)); + + repayModule = new AaveV3RepayModule(AAVE_V3_POOL_ADDRESSES_PROVIDER); + moduleRegistry.setModule("AaveV3RepayModule", address(repayModule)); + + dexModule = new UniversalDexModule(); + moduleRegistry.setModule("UniversalDexModule", address(dexModule)); + + // deploy merkl module + merklModule = new MerklModule(DISTRIBUTOR); + moduleRegistry.setModule("MerklModule", address(merklModule)); + } + + function _deployAccountant(address vault) internal { + DataTypes.AaveV3AccountantPluginModuleInitData memory accountantPluginInitData = DataTypes + .AaveV3AccountantPluginModuleInitData({ + poolAddressesProvider: AAVE_V3_POOL_ADDRESSES_PROVIDER, + lendAssets: lendAssets, + borrowAssets: borrowAssets + }); + accountantAaveV3Plugin = address(new AaveV3AccountantPlugin(accountantPluginInitData)); + + address[] memory registeredAccountants = new address[](1); + registeredAccountants[0] = accountantAaveV3Plugin; + + // deploy accountant + DataTypes.UniversalAccountantModuleInitData memory initData = DataTypes.UniversalAccountantModuleInitData({ + registeredAccountants: registeredAccountants, + performanceFee: uint16(performanceFee), + vault: address(vault) + }); + + accountantImplementation = address(new UniversalAccountant()); + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + accountantImplementation, + address(vaultAdmin), + abi.encodeWithSelector(UniversalAccountant.initialize.selector, initData) + ); + + accountant = UniversalAccountant(address(proxy)); + } + + function _deployWithdrawManager(address vault) internal { + withdrawManagerImplementation = address(new WithdrawManager()); + + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(withdrawManagerImplementation), + address(vaultAdmin), + abi.encodeWithSelector(WithdrawManager.initialize.selector, vault) + ); + + withdrawManager = WithdrawManager(address(proxy)); + } + + function _deployDepositManager(address vault) internal { + depositManagerImplementation = address(new DepositManager()); + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(depositManagerImplementation), + address(vaultAdmin), + abi.encodeWithSelector(DepositManager.initialize.selector, vault) + ); + depositManager = DepositManager(address(proxy)); + } + + function _setupEmode() internal { + DataTypes.AaveV3EmodeParams memory params = DataTypes.AaveV3EmodeParams({emodeCategory: EMODE_CATEGORY}); + + DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); + moduleExecutionData[0] = DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.DELEGATECALL, + module: address(emodeModule), + data: abi.encodeWithSelector(emodeModule.execute.selector, params) + }); + + superloop.operate(moduleExecutionData); + } + + function _setupCallbackHandlers() internal { + bytes32 key = keccak256(abi.encodePacked(POOL, IFlashLoanSimpleReceiver.executeOperation.selector)); + bytes32 morphoKey = keccak256(abi.encodePacked(MORPHO, IMorphoFlashLoanCallback.onMorphoFlashLoan.selector)); + bytes32 depositKey = + keccak256(abi.encodePacked(address(depositManager), depositManagerCallbackHandler.executeDeposit.selector)); + bytes32 withdrawKey = keccak256( + abi.encodePacked(address(withdrawManager), withdrawManagerCallbackHandler.executeWithdraw.selector) + ); + superloop.setCallbackHandler(morphoKey, address(morphoCallbackHandler)); + superloop.setCallbackHandler(key, address(aaveFlashloanCallbackHandler)); + superloop.setCallbackHandler(depositKey, address(depositManagerCallbackHandler)); + superloop.setCallbackHandler(withdrawKey, address(withdrawManagerCallbackHandler)); + } + + function _seedVault() internal { + IERC20(asset).approve(address(superloop), seedAmount); + superloop.seed(seedAmount); + } + + function _logAddresses() internal view { + // log module registry address + console.log("--------------------------------"); + console.log("Module Registry: %s", address(moduleRegistry)); + console.log("--------------------------------"); + + // log morpho flashloan module + console.log("--------------------------------"); + console.log("Morpho Flashloan Module: %s", address(morphoFlashloanModule)); + console.log("--------------------------------"); + + // log all aave module addresses + console.log("--------------------------------"); + console.log("AaveV3 Modules: %s", "--------------------------------"); + console.log("--------------------------------"); + console.log("AaveV3FlashloanModule: %s", address(flashloanModule)); + console.log("--------------------------------"); + console.log("AaveV3EmodeModule: %s", address(emodeModule)); + console.log("--------------------------------"); + console.log("AaveV3SupplyModule: %s", address(supplyModule)); + console.log("--------------------------------"); + console.log("AaveV3WithdrawModule: %s", address(withdrawModule)); + console.log("--------------------------------"); + console.log("AaveV3BorrowModule: %s", address(borrowModule)); + console.log("--------------------------------"); + console.log("AaveV3RepayModule: %s", address(repayModule)); + + // log all the dex modules + console.log("--------------------------------"); + console.log("Dex Modules: %s", "--------------------------------"); + console.log("--------------------------------"); + console.log("Universal Dex Module: %s", address(dexModule)); + console.log("--------------------------------"); + + // log all the callback handler modules + console.log("--------------------------------"); + console.log("Callback Handler Modules: %s", "--------------------------------"); + console.log("--------------------------------"); + console.log("Deposit Manager Callback Handler: %s", address(depositManagerCallbackHandler)); + console.log("--------------------------------"); + console.log("Withdraw Manager Callback Handler: %s", address(withdrawManagerCallbackHandler)); + console.log("--------------------------------"); + console.log("AaveV3CallbackHandler: %s", address(aaveFlashloanCallbackHandler)); + console.log("--------------------------------"); + console.log("Morpho Callback Handler: %s", address(morphoCallbackHandler)); + + // log merkl module + console.log("--------------------------------"); + console.log("Merkl Module: %s", address(merklModule)); + console.log("--------------------------------"); + + // log accountant along with accountant plugin + console.log("--------------------------------"); + console.log("Accountant: %s", "--------------------------------"); + console.log("--------------------------------"); + console.log("Accountant: %s", address(accountant)); + console.log("--------------------------------"); + console.log("Accountant Implementation: %s", address(accountantImplementation)); + console.log("--------------------------------"); + console.log("Accountant Plugin: %s", address(accountantAaveV3Plugin)); + console.log("--------------------------------"); + + // log withdraw manager along with withdraw manager implementation + console.log("--------------------------------"); + console.log("Withdraw Manager: %s", "--------------------------------"); + console.log("--------------------------------"); + console.log("Withdraw Manager: %s", address(withdrawManager)); + console.log("--------------------------------"); + console.log("Withdraw Manager Implementation: %s", address(withdrawManagerImplementation)); + console.log("--------------------------------"); + + // log deposit manager along with deposit manager implementation + console.log("--------------------------------"); + console.log("Deposit Manager: %s", "--------------------------------"); + console.log("--------------------------------"); + console.log("Deposit Manager: %s", address(depositManager)); + console.log("--------------------------------"); + console.log("Deposit Manager Implementation: %s", address(depositManagerImplementation)); + console.log("--------------------------------"); + + // log superloop along with implementation and proxy admin + console.log("--------------------------------"); + console.log("Superloop: %s", "--------------------------------"); + console.log("--------------------------------"); + console.log("Superloop: %s", address(superloop)); + console.log("--------------------------------"); + console.log("Superloop Implementation: %s", address(vaultImplementation)); + console.log("--------------------------------"); + + console.log("--------------------------------"); + console.log("Vault Router: %s", "--------------------------------"); + console.log("--------------------------------"); + console.log("Vault Router: %s", address(vaultRouter)); + } + + function _setupVaultRouter() internal { + address[] memory supportedVaults = new address[](1); + supportedVaults[0] = address(superloop); + + address[] memory supportedDepositManagers = new address[](1); + supportedDepositManagers[0] = address(depositManager); + + address[] memory supportedTokens = new address[](3); + supportedTokens[0] = USDe; + supportedTokens[1] = USDC; + supportedTokens[2] = USDT; + + vaultRouter = new VaultRouter(supportedVaults, supportedTokens, address(dexModule), supportedDepositManagers); + + vaultRouter.transferOwnership(vaultAdmin); + } +} diff --git a/test/modules/merkl/MerklModule.t.sol b/test/modules/merkl/MerklModule.t.sol index 77828f5..59a62a3 100644 --- a/test/modules/merkl/MerklModule.t.sol +++ b/test/modules/merkl/MerklModule.t.sol @@ -5,18 +5,12 @@ pragma solidity ^0.8.13; import {TestBase} from "../../core/TestBase.sol"; import {DataTypes} from "../../../src/common/DataTypes.sol"; import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -import { - DepositManager -} from "../../../src/core/DepositManager/DepositManager.sol"; +import {DepositManager} from "../../../src/core/DepositManager/DepositManager.sol"; import {console} from "forge-std/Test.sol"; import {Errors} from "../../../src/common/Errors.sol"; import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; -import { - ProxyAdmin -} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; -import { - TransparentUpgradeableProxy -} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {IDistributor} from "../../../src/modules/merkl/IDistributor.sol"; contract MerklModuleTest is TestBase { @@ -55,7 +49,7 @@ contract MerklModuleTest is TestBase { }); superloopImplementation = new Superloop(); proxyAdmin = new ProxyAdmin(address(this)); - + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( address(superloopImplementation), address(proxyAdmin), @@ -75,9 +69,7 @@ contract MerklModuleTest is TestBase { // set the root of distributor as hash of vault asset deal(environment.vaultAsset, environment.distributor, AMOUNT); - bytes32 root = keccak256( - abi.encode(address(superloop), environment.vaultAsset, AMOUNT) - ); + bytes32 root = keccak256(abi.encode(address(superloop), environment.vaultAsset, AMOUNT)); bytes32 ipfsHash = bytes32(0); vm.prank(distributorAdmin); @@ -87,8 +79,7 @@ contract MerklModuleTest is TestBase { } function test_claim() public { - DataTypes.ModuleExecutionData[] - memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); + DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); address[] memory users = new address[](1); users[0] = address(superloop); address[] memory tokens = new address[](1); @@ -102,12 +93,7 @@ contract MerklModuleTest is TestBase { module: address(merklModule), data: abi.encodeWithSelector( merklModule.execute.selector, - DataTypes.MerklClaimParams({ - users: users, - tokens: tokens, - amounts: amounts, - proofs: proofs - }) + DataTypes.MerklClaimParams({users: users, tokens: tokens, amounts: amounts, proofs: proofs}) ) }); @@ -115,6 +101,5 @@ contract MerklModuleTest is TestBase { superloop.operate(moduleExecutionData); assertEq(IERC20(environment.vaultAsset).balanceOf(address(superloop)), AMOUNT); - } } From 8ad3bd8bd0b09dd88a22b56f862799d568ee0b73 Mon Sep 17 00:00:00 2001 From: Priyam Anand Date: Thu, 29 Jan 2026 12:51:20 +0530 Subject: [PATCH 19/21] formatter --- foundry.toml | 1 + script/deploy-eth.s.sol | 26 +- script/deploy-v2-for-migration.s.sol | 21 +- script/deploy-v2.s.sol | 21 +- script/migration-btc-vault.s.sol | 5 +- script/migration-xtz-vault.s.sol | 5 +- src/common/DataTypes.sol | 3 - .../aaveV3Accountant/AccountantAaveV3.sol | 9 +- .../UniversalAccountant.sol | 5 +- .../UniversalAccountantBase.sol | 2 +- src/core/DepositManager/DepositManager.sol | 5 +- src/core/Superloop/SuperloopVault.sol | 5 +- src/core/WithdrawManager/WithdrawManager.sol | 5 +- src/helpers/MigrationHelper.sol | 6 +- src/interfaces/IWithdrawManager.sol | 5 +- src/mock/MockWithdrawManager.sol | 5 +- .../AaveV3PreliquidationFallbackHandler.sol | 6 +- test/core/TestBase.sol | 21 +- .../core/integration/AavePreliquidation.t.sol | 241 ++--- test/core/integration/DepositManager.t.sol | 8 +- test/core/integration/IntegrationBase.sol | 33 +- test/core/integration/Migration.t.sol | 964 +++++++++--------- .../integration/UniversalAccountant.t.sol | 36 +- test/core/integration/WithdrawManager.t.sol | 12 +- test/core/unit/DepositManager.t.sol | 5 +- test/core/unit/UniversalAccountant.t.sol | 20 +- test/core/unit/WithdrawManager.t.sol | 17 +- test/helpers/VaultRouter.t.sol | 2 +- test/helpers/integration/VaultRouter.t.sol | 2 +- test/modules/aave/AaveV3BorrowModule.t.sol | 5 +- test/modules/aave/AaveV3EmodeModule.t.sol | 5 +- test/modules/aave/AaveV3FlashloanModule.t.sol | 5 +- test/modules/aave/AaveV3RepayModule.t.sol | 5 +- test/modules/aave/AaveV3SupplyModule.t.sol | 5 +- test/modules/aave/AaveV3WithdrawModule.t.sol | 5 +- test/modules/dex/UniversalDexModule.t.sol | 11 +- .../AaveV3PreliquidationFallbackHandler.t.sol | 125 +-- test/modules/helpers/UnwrapModule.t.sol | 5 +- test/modules/helpers/WrapModule.t.sol | 5 +- .../morpho/MorphoFlashloanModule.t.sol | 5 +- .../hyperliquid/HyperbeatStakingModule.t.sol | 12 +- .../HyperliquidStakingModule.t.sol | 5 +- .../hyperliquid/KinetiqStakingModule.t.sol | 9 +- 43 files changed, 840 insertions(+), 863 deletions(-) diff --git a/foundry.toml b/foundry.toml index 35349e8..ee0797a 100644 --- a/foundry.toml +++ b/foundry.toml @@ -14,4 +14,5 @@ mainnet="https://ethereum-rpc.publicnode.com" [etherscan] etherlink={ key = "NO_KEY", chain = 42793, url= "https://explorer.etherlink.com/api/" } +mainnet={ key = "${ETHERSCAN_API_KEY}", chain = 1 } diff --git a/script/deploy-eth.s.sol b/script/deploy-eth.s.sol index ff398ae..376ddfd 100644 --- a/script/deploy-eth.s.sol +++ b/script/deploy-eth.s.sol @@ -13,8 +13,9 @@ import {AaveV3WithdrawModule} from "../src/modules/aave/AaveV3WithdrawModule.sol import {AaveV3BorrowModule} from "../src/modules/aave/AaveV3BorrowModule.sol"; import {AaveV3RepayModule} from "../src/modules/aave/AaveV3RepayModule.sol"; import {SuperloopModuleRegistry} from "../src/core/ModuleRegistry/ModuleRegistry.sol"; -import {TransparentUpgradeableProxy} from - "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import { + TransparentUpgradeableProxy +} from "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {DataTypes} from "../src/common/DataTypes.sol"; import {Superloop} from "../src/core/Superloop/Superloop.sol"; import {IFlashLoanSimpleReceiver} from "aave-v3-core/contracts/flashloan/interfaces/IFlashLoanSimpleReceiver.sol"; @@ -260,10 +261,9 @@ contract Deploy is Script { function deployModules() internal { // deploy morpho flashloan module - morphoFlashloanModule = - // new MorphoFlashloanModule(MORPHO); + morphoFlashloanModule = new MorphoFlashloanModule(MORPHO); // moduleRegistry.setModule("MorphoFlashloanModule", address(morphoFlashloanModule)); - morphoCallbackHandler = new MorphoCallbackHandler(); + morphoCallbackHandler = new MorphoCallbackHandler(); moduleRegistry.setModule("MorphoCallbackHandler", address(morphoCallbackHandler)); // deploy aave flashloan module @@ -313,12 +313,12 @@ contract Deploy is Script { } function _deployAccountant(address vault) internal { - DataTypes.AaveV3AccountantPluginModuleInitData memory accountantPluginInitData = DataTypes - .AaveV3AccountantPluginModuleInitData({ - poolAddressesProvider: AAVE_V3_POOL_ADDRESSES_PROVIDER, - lendAssets: lendAssets, - borrowAssets: borrowAssets - }); + DataTypes.AaveV3AccountantPluginModuleInitData memory accountantPluginInitData = + DataTypes.AaveV3AccountantPluginModuleInitData({ + poolAddressesProvider: AAVE_V3_POOL_ADDRESSES_PROVIDER, + lendAssets: lendAssets, + borrowAssets: borrowAssets + }); accountantAaveV3Plugin = address(new AaveV3AccountantPlugin(accountantPluginInitData)); address[] memory registeredAccountants = new address[](1); @@ -326,9 +326,7 @@ contract Deploy is Script { // deploy accountant DataTypes.UniversalAccountantModuleInitData memory initData = DataTypes.UniversalAccountantModuleInitData({ - registeredAccountants: registeredAccountants, - performanceFee: uint16(performanceFee), - vault: address(vault) + registeredAccountants: registeredAccountants, performanceFee: uint16(performanceFee), vault: address(vault) }); accountantImplementation = address(new UniversalAccountant()); diff --git a/script/deploy-v2-for-migration.s.sol b/script/deploy-v2-for-migration.s.sol index 76dd0c7..f1f3b06 100644 --- a/script/deploy-v2-for-migration.s.sol +++ b/script/deploy-v2-for-migration.s.sol @@ -13,8 +13,9 @@ import {AaveV3WithdrawModule} from "../src/modules/aave/AaveV3WithdrawModule.sol import {AaveV3BorrowModule} from "../src/modules/aave/AaveV3BorrowModule.sol"; import {AaveV3RepayModule} from "../src/modules/aave/AaveV3RepayModule.sol"; import {SuperloopModuleRegistry} from "../src/core/ModuleRegistry/ModuleRegistry.sol"; -import {TransparentUpgradeableProxy} from - "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import { + TransparentUpgradeableProxy +} from "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {DataTypes} from "../src/common/DataTypes.sol"; import {Superloop} from "../src/core/Superloop/Superloop.sol"; import {IFlashLoanSimpleReceiver} from "aave-v3-core/contracts/flashloan/interfaces/IFlashLoanSimpleReceiver.sol"; @@ -280,12 +281,12 @@ contract Deploy is Script { address[] memory borrowAssets = new address[](1); borrowAssets[0] = borrowAsset; - DataTypes.AaveV3AccountantPluginModuleInitData memory accountantPluginInitData = DataTypes - .AaveV3AccountantPluginModuleInitData({ - poolAddressesProvider: AAVE_V3_POOL_ADDRESSES_PROVIDER, - lendAssets: lendAssets, - borrowAssets: borrowAssets - }); + DataTypes.AaveV3AccountantPluginModuleInitData memory accountantPluginInitData = + DataTypes.AaveV3AccountantPluginModuleInitData({ + poolAddressesProvider: AAVE_V3_POOL_ADDRESSES_PROVIDER, + lendAssets: lendAssets, + borrowAssets: borrowAssets + }); accountantAaveV3Plugin = address(new AaveV3AccountantPlugin(accountantPluginInitData)); address[] memory registeredAccountants = new address[](1); @@ -293,9 +294,7 @@ contract Deploy is Script { // deploy accountant DataTypes.UniversalAccountantModuleInitData memory initData = DataTypes.UniversalAccountantModuleInitData({ - registeredAccountants: registeredAccountants, - performanceFee: uint16(performanceFee), - vault: address(vault) + registeredAccountants: registeredAccountants, performanceFee: uint16(performanceFee), vault: address(vault) }); accountantImplementation = address(new UniversalAccountant()); diff --git a/script/deploy-v2.s.sol b/script/deploy-v2.s.sol index 2bf2381..60c3a9f 100644 --- a/script/deploy-v2.s.sol +++ b/script/deploy-v2.s.sol @@ -13,8 +13,9 @@ import {AaveV3WithdrawModule} from "../src/modules/aave/AaveV3WithdrawModule.sol import {AaveV3BorrowModule} from "../src/modules/aave/AaveV3BorrowModule.sol"; import {AaveV3RepayModule} from "../src/modules/aave/AaveV3RepayModule.sol"; import {SuperloopModuleRegistry} from "../src/core/ModuleRegistry/ModuleRegistry.sol"; -import {TransparentUpgradeableProxy} from - "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import { + TransparentUpgradeableProxy +} from "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {DataTypes} from "../src/common/DataTypes.sol"; import {Superloop} from "../src/core/Superloop/Superloop.sol"; import {IFlashLoanSimpleReceiver} from "aave-v3-core/contracts/flashloan/interfaces/IFlashLoanSimpleReceiver.sol"; @@ -283,12 +284,12 @@ contract Deploy is Script { address[] memory borrowAssets = new address[](1); borrowAssets[0] = borrowAsset; - DataTypes.AaveV3AccountantPluginModuleInitData memory accountantPluginInitData = DataTypes - .AaveV3AccountantPluginModuleInitData({ - poolAddressesProvider: AAVE_V3_POOL_ADDRESSES_PROVIDER, - lendAssets: lendAssets, - borrowAssets: borrowAssets - }); + DataTypes.AaveV3AccountantPluginModuleInitData memory accountantPluginInitData = + DataTypes.AaveV3AccountantPluginModuleInitData({ + poolAddressesProvider: AAVE_V3_POOL_ADDRESSES_PROVIDER, + lendAssets: lendAssets, + borrowAssets: borrowAssets + }); accountantAaveV3Plugin = address(new AaveV3AccountantPlugin(accountantPluginInitData)); address[] memory registeredAccountants = new address[](1); @@ -296,9 +297,7 @@ contract Deploy is Script { // deploy accountant DataTypes.UniversalAccountantModuleInitData memory initData = DataTypes.UniversalAccountantModuleInitData({ - registeredAccountants: registeredAccountants, - performanceFee: uint16(performanceFee), - vault: address(vault) + registeredAccountants: registeredAccountants, performanceFee: uint16(performanceFee), vault: address(vault) }); accountantImplementation = address(new UniversalAccountant()); diff --git a/script/migration-btc-vault.s.sol b/script/migration-btc-vault.s.sol index d3ec4b5..4866a97 100644 --- a/script/migration-btc-vault.s.sol +++ b/script/migration-btc-vault.s.sol @@ -158,9 +158,8 @@ contract Migration is Script { ISuperloop(newVault).setDepositManagerModule(address(migrationHelper)); ISuperloop(newVault).setVaultOperator(address(migrationHelper)); IAccountantModule(ISuperloop(newVault).accountant()).setVault(address(migrationHelper)); - Ownable(address(IAccountantModule(ISuperloop(newVault).accountant()))).transferOwnership( - address(migrationHelper) - ); + Ownable(address(IAccountantModule(ISuperloop(newVault).accountant()))) + .transferOwnership(address(migrationHelper)); // assert these roles address withdrawManager = ISuperloopLegacy(oldVault).withdrawManagerModule(); diff --git a/script/migration-xtz-vault.s.sol b/script/migration-xtz-vault.s.sol index 8ae6aad..3bfa39f 100644 --- a/script/migration-xtz-vault.s.sol +++ b/script/migration-xtz-vault.s.sol @@ -235,9 +235,8 @@ contract Migration is Script { ISuperloop(newVault).setDepositManagerModule(address(migrationHelper)); ISuperloop(newVault).setVaultOperator(address(migrationHelper)); IAccountantModule(ISuperloop(newVault).accountant()).setVault(address(migrationHelper)); - Ownable(address(IAccountantModule(ISuperloop(newVault).accountant()))).transferOwnership( - address(migrationHelper) - ); + Ownable(address(IAccountantModule(ISuperloop(newVault).accountant()))) + .transferOwnership(address(migrationHelper)); // assert these roles address withdrawManager = ISuperloopLegacy(oldVault).withdrawManagerModule(); diff --git a/src/common/DataTypes.sol b/src/common/DataTypes.sol index f1c6ca1..0c1d07f 100644 --- a/src/common/DataTypes.sol +++ b/src/common/DataTypes.sol @@ -54,7 +54,6 @@ library DataTypes { DEFERRED, // low slippage queue PRIORITY, // medium slippage queue INSTANT // high slippage queue - } struct WithdrawQueue { @@ -105,7 +104,6 @@ library DataTypes { UNPROCESSED, // Request is pending processing CLAIMABLE, // Request is ready to be claimed CANCELLED // Request has been cancelled - } /** @@ -222,7 +220,6 @@ library DataTypes { enum CallType { CALL, // Regular call to external contract DELEGATECALL // Delegate call to external contract - } /** diff --git a/src/core/Accountant/aaveV3Accountant/AccountantAaveV3.sol b/src/core/Accountant/aaveV3Accountant/AccountantAaveV3.sol index 37e3113..15ce54f 100644 --- a/src/core/Accountant/aaveV3Accountant/AccountantAaveV3.sol +++ b/src/core/Accountant/aaveV3Accountant/AccountantAaveV3.sol @@ -2,8 +2,9 @@ pragma solidity ^0.8.13; -import {ReentrancyGuardUpgradeable} from - "openzeppelin-contracts-upgradeable/contracts/utils/ReentrancyGuardUpgradeable.sol"; +import { + ReentrancyGuardUpgradeable +} from "openzeppelin-contracts-upgradeable/contracts/utils/ReentrancyGuardUpgradeable.sol"; import {IPoolAddressesProvider} from "aave-v3-core/contracts/interfaces/IPoolAddressesProvider.sol"; import {IPoolDataProvider} from "aave-v3-core/contracts/interfaces/IPoolDataProvider.sol"; import {IAaveOracle} from "aave-v3-core/contracts/interfaces/IAaveOracle.sol"; @@ -83,8 +84,8 @@ contract AccountantAaveV3 is ReentrancyGuardUpgradeable, AccountantAaveV3Base { } uint256 baseAssetPrice = aaveOracle.getAssetPrice(baseAsset); - positiveBalance += - (IERC20(baseAsset).balanceOf($.vault) * commonDecimalFactor * baseAssetPrice) / (10 ** baseDecimals); + positiveBalance += (IERC20(baseAsset).balanceOf($.vault) * commonDecimalFactor * baseAssetPrice) + / (10 ** baseDecimals); // convert to base asset uint256 totalAssetsInBaseAsset = (positiveBalance - negativeBalance) / baseAssetPrice; diff --git a/src/core/Accountant/universalAccountant/UniversalAccountant.sol b/src/core/Accountant/universalAccountant/UniversalAccountant.sol index 17410b0..af0409f 100644 --- a/src/core/Accountant/universalAccountant/UniversalAccountant.sol +++ b/src/core/Accountant/universalAccountant/UniversalAccountant.sol @@ -5,8 +5,9 @@ pragma solidity ^0.8.13; import {UniversalAccountantBase} from "./UniversalAccountantBase.sol"; import {DataTypes} from "../../../common/DataTypes.sol"; import {UniversalAccountantStorage} from "../../lib/UniversalAccountantStorage.sol"; -import {ReentrancyGuardUpgradeable} from - "openzeppelin-contracts-upgradeable/contracts/utils/ReentrancyGuardUpgradeable.sol"; +import { + ReentrancyGuardUpgradeable +} from "openzeppelin-contracts-upgradeable/contracts/utils/ReentrancyGuardUpgradeable.sol"; import {Errors} from "../../../common/Errors.sol"; import {IAaveV3AccountantPlugin} from "../../../interfaces/IAaveV3AccountantPlugin.sol"; diff --git a/src/core/Accountant/universalAccountant/UniversalAccountantBase.sol b/src/core/Accountant/universalAccountant/UniversalAccountantBase.sol index 5bb6d2f..923c3a6 100644 --- a/src/core/Accountant/universalAccountant/UniversalAccountantBase.sol +++ b/src/core/Accountant/universalAccountant/UniversalAccountantBase.sol @@ -17,7 +17,7 @@ abstract contract UniversalAccountantBase is OwnableUpgradeable, IAccountantModu function setRegisteredAccountants(address[] memory registeredAccountants_) external onlyOwner { address[] memory oldAccountants = - UniversalAccountantStorage.getUniversalAccountantStorage().registeredAccountants; + UniversalAccountantStorage.getUniversalAccountantStorage().registeredAccountants; UniversalAccountantStorage.setRegisteredAccountants(registeredAccountants_); emit RegisteredAccountantsUpdated(oldAccountants, registeredAccountants_); } diff --git a/src/core/DepositManager/DepositManager.sol b/src/core/DepositManager/DepositManager.sol index ff9cc81..748f4f6 100644 --- a/src/core/DepositManager/DepositManager.sol +++ b/src/core/DepositManager/DepositManager.sol @@ -3,8 +3,9 @@ pragma solidity ^0.8.13; import {Initializable} from "openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol"; -import {ReentrancyGuardUpgradeable} from - "openzeppelin-contracts-upgradeable/contracts/utils/ReentrancyGuardUpgradeable.sol"; +import { + ReentrancyGuardUpgradeable +} from "openzeppelin-contracts-upgradeable/contracts/utils/ReentrancyGuardUpgradeable.sol"; import {SafeERC20} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol"; import {IERC20Metadata, IERC20} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {Math} from "openzeppelin-contracts/contracts/utils/math/Math.sol"; diff --git a/src/core/Superloop/SuperloopVault.sol b/src/core/Superloop/SuperloopVault.sol index 699cc00..e621fb9 100644 --- a/src/core/Superloop/SuperloopVault.sol +++ b/src/core/Superloop/SuperloopVault.sol @@ -8,8 +8,9 @@ import { } from "openzeppelin-contracts-upgradeable/contracts/token/ERC20/extensions/ERC4626Upgradeable.sol"; import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import {SuperloopStorage} from "../lib/SuperloopStorage.sol"; -import {ReentrancyGuardUpgradeable} from - "openzeppelin-contracts-upgradeable/contracts/utils/ReentrancyGuardUpgradeable.sol"; +import { + ReentrancyGuardUpgradeable +} from "openzeppelin-contracts-upgradeable/contracts/utils/ReentrancyGuardUpgradeable.sol"; import {IAccountantModule} from "../../interfaces/IAccountantModule.sol"; import {IERC20Metadata} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {Math} from "openzeppelin-contracts/contracts/utils/math/Math.sol"; diff --git a/src/core/WithdrawManager/WithdrawManager.sol b/src/core/WithdrawManager/WithdrawManager.sol index f7c4cd8..90608c5 100644 --- a/src/core/WithdrawManager/WithdrawManager.sol +++ b/src/core/WithdrawManager/WithdrawManager.sol @@ -5,8 +5,9 @@ pragma solidity ^0.8.13; import {WithdrawManagerBase} from "./WithdrawManagerBase.sol"; import {WithdrawManagerStorage} from "../lib/WithdrawManagerStorage.sol"; import {DataTypes} from "../../common/DataTypes.sol"; -import {ReentrancyGuardUpgradeable} from - "openzeppelin-contracts-upgradeable/contracts/utils/ReentrancyGuardUpgradeable.sol"; +import { + ReentrancyGuardUpgradeable +} from "openzeppelin-contracts-upgradeable/contracts/utils/ReentrancyGuardUpgradeable.sol"; import {Context} from "openzeppelin-contracts/contracts/utils/Context.sol"; import {ISuperloop} from "../../interfaces/ISuperloop.sol"; import {IERC20Metadata} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol"; diff --git a/src/helpers/MigrationHelper.sol b/src/helpers/MigrationHelper.sol index e57c910..4f74b9b 100644 --- a/src/helpers/MigrationHelper.sol +++ b/src/helpers/MigrationHelper.sol @@ -388,8 +388,7 @@ contract MigrationHelper is FlashLoanSimpleReceiverBase, Ownable, ReentrancyGuar // Step 3: Transfer all assets from old vault to new vault via DEX module DataTypes.ExecuteSwapParamsData[] memory swapParamsData = new DataTypes.ExecuteSwapParamsData[](2); swapParamsData[0] = DataTypes.ExecuteSwapParamsData({ - target: lendAsset, - data: abi.encodeWithSelector(IERC20.transfer.selector, address(newVault), lendBalance) + target: lendAsset, data: abi.encodeWithSelector(IERC20.transfer.selector, address(newVault), lendBalance) }); uint256 xtzBalanceOldVault = IERC20(borrowAsset).balanceOf(oldVault); swapParamsData[1] = DataTypes.ExecuteSwapParamsData({ @@ -455,8 +454,7 @@ contract MigrationHelper is FlashLoanSimpleReceiverBase, Ownable, ReentrancyGuar // Step 3: Transfer borrowed asset back to migration helper for flash loan repayment DataTypes.ExecuteSwapParamsData[] memory swapParamsData = new DataTypes.ExecuteSwapParamsData[](1); swapParamsData[0] = DataTypes.ExecuteSwapParamsData({ - target: borrowAsset, - data: abi.encodeWithSelector(IERC20.transfer.selector, address(this), borrowBalance) + target: borrowAsset, data: abi.encodeWithSelector(IERC20.transfer.selector, address(this), borrowBalance) }); DataTypes.ExecuteSwapParams memory swapParams = DataTypes.ExecuteSwapParams({ tokenIn: placeholderAsset, diff --git a/src/interfaces/IWithdrawManager.sol b/src/interfaces/IWithdrawManager.sol index e97bdd5..a4f10ac 100644 --- a/src/interfaces/IWithdrawManager.sol +++ b/src/interfaces/IWithdrawManager.sol @@ -86,10 +86,7 @@ interface IWithdrawManager { * @param ids Array of withdrawal request IDs * @return Array of withdrawal request data */ - function withdrawRequests(uint256[] memory ids) - external - view - returns (DataTypes.WithdrawRequestDataLegacy[] memory); + function withdrawRequests(uint256[] memory ids) external view returns (DataTypes.WithdrawRequestDataLegacy[] memory); /** * @notice Gets the withdrawal request ID for a specific user diff --git a/src/mock/MockWithdrawManager.sol b/src/mock/MockWithdrawManager.sol index 34ead7d..5b4c90d 100644 --- a/src/mock/MockWithdrawManager.sol +++ b/src/mock/MockWithdrawManager.sol @@ -3,8 +3,9 @@ pragma solidity ^0.8.13; import {Initializable} from "openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol"; -import {ReentrancyGuardUpgradeable} from - "openzeppelin-contracts-upgradeable/contracts/utils/ReentrancyGuardUpgradeable.sol"; +import { + ReentrancyGuardUpgradeable +} from "openzeppelin-contracts-upgradeable/contracts/utils/ReentrancyGuardUpgradeable.sol"; import {IERC20} from "openzeppelin-contracts/contracts/interfaces/IERC20.sol"; import {IERC4626} from "openzeppelin-contracts/contracts/interfaces/IERC4626.sol"; import {SafeERC20} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol"; diff --git a/src/modules/fallback/AaveV3PreliquidationFallbackHandler.sol b/src/modules/fallback/AaveV3PreliquidationFallbackHandler.sol index 85d8cbb..54672f5 100644 --- a/src/modules/fallback/AaveV3PreliquidationFallbackHandler.sol +++ b/src/modules/fallback/AaveV3PreliquidationFallbackHandler.sol @@ -139,9 +139,9 @@ contract AaveV3PreliquidationFallbackHandler is Context { uint256 debtToCover = Math.min(Math.min(params.debtToCover, maxDebtToCover), borrowTokens); uint256 debtCoverUSD = (debtToCover * borrowPriceUSD) / (10 ** borrowReserveDecimals); // in oracle decimals - uint256 collateralToSieze = ( - ((debtCoverUSD * 10 ** lendReserveDecimals) / collateralPriceUSD) * currentIncentiveFactor - ) / WadRayMath.WAD; // in tokens + uint256 collateralToSieze = + (((debtCoverUSD * 10 ** lendReserveDecimals) / collateralPriceUSD) * currentIncentiveFactor) + / WadRayMath.WAD; // in tokens // transfer debt tokens to self SafeERC20.safeTransferFrom(IERC20(borrowReserve), _msgSender(), address(this), debtToCover); diff --git a/test/core/TestBase.sol b/test/core/TestBase.sol index b990fcc..c3a8594 100644 --- a/test/core/TestBase.sol +++ b/test/core/TestBase.sol @@ -24,8 +24,9 @@ import {WithdrawManager} from "../../src/core/WithdrawManager/WithdrawManager.so import {DepositManager} from "../../src/core/DepositManager/DepositManager.sol"; import {DepositManagerCallbackHandler} from "../../src/modules/callback/DepositManagerCallbackHandler.sol"; import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; -import {TransparentUpgradeableProxy} from - "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import { + TransparentUpgradeableProxy +} from "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {UniversalAccountant} from "../../src/core/Accountant/universalAccountant/UniversalAccountant.sol"; import {AaveV3AccountantPlugin} from "../../src/plugins/Accountant/AaveV3AccountantPlugin.sol"; import {WithdrawManagerCallbackHandler} from "../../src/modules/callback/WithdrawManagerCallbackHandler.sol"; @@ -231,12 +232,12 @@ abstract contract TestBase is TestEnv { internal returns (UniversalAccountant) { - DataTypes.AaveV3AccountantPluginModuleInitData memory accountantPluginInitData = DataTypes - .AaveV3AccountantPluginModuleInitData({ - poolAddressesProvider: environment.poolAddressesProvider, - lendAssets: lendAssets, - borrowAssets: borrowAssets - }); + DataTypes.AaveV3AccountantPluginModuleInitData memory accountantPluginInitData = + DataTypes.AaveV3AccountantPluginModuleInitData({ + poolAddressesProvider: environment.poolAddressesProvider, + lendAssets: lendAssets, + borrowAssets: borrowAssets + }); address accountantPlugin = address(new AaveV3AccountantPlugin(accountantPluginInitData)); address[] memory registeredAccountants = new address[](1); @@ -244,9 +245,7 @@ abstract contract TestBase is TestEnv { // deploy accountant DataTypes.UniversalAccountantModuleInitData memory initData = DataTypes.UniversalAccountantModuleInitData({ - registeredAccountants: registeredAccountants, - performanceFee: uint16(PERFORMANCE_FEE), - vault: address(vault) + registeredAccountants: registeredAccountants, performanceFee: uint16(PERFORMANCE_FEE), vault: address(vault) }); address accountantImplementation = address(new UniversalAccountant()); diff --git a/test/core/integration/AavePreliquidation.t.sol b/test/core/integration/AavePreliquidation.t.sol index 83576ed..69aa2f9 100644 --- a/test/core/integration/AavePreliquidation.t.sol +++ b/test/core/integration/AavePreliquidation.t.sol @@ -1,118 +1,123 @@ -// // SPDX-License-Identifier: MIT - -// pragma solidity ^0.8.13; - -// import {IntegrationBase} from "./IntegrationBase.sol"; -// import {console} from "forge-std/console.sol"; -// import {DataTypes} from "../../../src/common/DataTypes.sol"; -// import {AaveV3PreliquidationFallbackHandler} from -// "../../../src/modules/fallback/AaveV3PreliquidationFallbackHandler.sol"; -// import {console} from "forge-std/console.sol"; -// import {IPoolDataProvider} from "aave-v3-core/contracts/interfaces/IPoolDataProvider.sol"; -// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -// import {IAaveOracle} from "aave-v3-core/contracts/interfaces/IAaveOracle.sol"; -// import {IPoolAddressesProvider} from "aave-v3-core/contracts/interfaces/IPoolAddressesProvider.sol"; - -// contract AavePreliquidationTest is IntegrationBase { -// function setUp() public override { -// super.setUp(); - -// DataTypes.AaveV3EmodeParams memory params = DataTypes.AaveV3EmodeParams({emodeCategory: 3}); - -// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); -// moduleExecutionData[0] = DataTypes.ModuleExecutionData({ -// executionType: DataTypes.CallType.DELEGATECALL, -// module: address(emodeModule), -// data: abi.encodeWithSelector(emodeModule.execute.selector, params) -// }); - -// vm.startPrank(admin); -// superloop.operate(moduleExecutionData); - -// _deployPreliquidationFallbackHandler(address(superloop)); -// bytes32 key1 = keccak256( -// abi.encodePacked( -// abi.encodeWithSelector(AaveV3PreliquidationFallbackHandler.preliquidate.selector), -// id, -// DataTypes.CallType.DELEGATECALL -// ) -// ); -// bytes32 key2 = keccak256( -// abi.encodePacked( -// abi.encodeWithSelector(AaveV3PreliquidationFallbackHandler.preliquidationParams.selector), -// id, -// DataTypes.CallType.DELEGATECALL -// ) -// ); - -// superloop.setFallbackHandler(key1, address(preliquidationFallbackHandler)); -// superloop.setFallbackHandler(key2, address(preliquidationFallbackHandler)); -// vm.stopPrank(); -// } - -// function test_aavePreliquidationParams() public view { -// DataTypes.AaveV3PreliquidationParams memory params = AaveV3PreliquidationFallbackHandler(address(superloop)) -// .preliquidationParams(id, DataTypes.CallType.DELEGATECALL); - -// assertEq(params.lendReserve, ST_XTZ); -// assertEq(params.borrowReserve, XTZ); -// assertEq(params.Lltv, LLTV); -// assertEq(params.preLltv, PRE_LLTV); -// assertEq(params.preCF1, PRE_CF1); -// assertEq(params.preCF2, PRE_CF2); -// assertEq(params.preIF1, PRE_IF1); -// assertEq(params.preIF2, PRE_IF2); -// } - -// function test_aavePreliquidation() public { -// _createPartialDepositWithResolution(true); - -// (,, uint256 currentVariableDebt,,,,,,) = -// IPoolDataProvider(AAVE_V3_POOL_DATA_PROVIDER).getUserReserveData(XTZ, address(superloop)); -// (uint256 currentATokenBalance,,,,,,,,) = -// IPoolDataProvider(AAVE_V3_POOL_DATA_PROVIDER).getUserReserveData(ST_XTZ, address(superloop)); - -// address liquidator = makeAddr("liquidator"); -// deal(XTZ, liquidator, 1000 * XTZ_SCALE); - -// uint256 userBalanceXTZBefore = IERC20(XTZ).balanceOf(liquidator); -// uint256 userBalanceSTXTZBefore = IERC20(ST_XTZ).balanceOf(liquidator); - -// vm.startPrank(liquidator); -// IERC20(XTZ).approve(address(superloop), 1000 * XTZ_SCALE); - -// AaveV3PreliquidationFallbackHandler(address(superloop)).preliquidate( -// id, -// DataTypes.CallType.DELEGATECALL, -// DataTypes.AaveV3ExecutePreliquidationParams({user: address(superloop), debtToCover: currentVariableDebt}) -// ); -// vm.stopPrank(); - -// (,, uint256 currentVariableDebtAfter,,,,,,) = -// IPoolDataProvider(AAVE_V3_POOL_DATA_PROVIDER).getUserReserveData(XTZ, address(superloop)); -// (uint256 currentATokenBalanceAfter,,,,,,,,) = -// IPoolDataProvider(AAVE_V3_POOL_DATA_PROVIDER).getUserReserveData(ST_XTZ, address(superloop)); - -// uint256 userBalanceXTZAfter = IERC20(XTZ).balanceOf(liquidator); -// uint256 userBalanceSTXTZAfter = IERC20(ST_XTZ).balanceOf(liquidator); -// uint256 debtRepaid = currentVariableDebt - currentVariableDebtAfter; -// uint256 collateralWithdrawn = currentATokenBalance - currentATokenBalanceAfter; - -// uint256 userBalanceXTZDiff = userBalanceXTZBefore - userBalanceXTZAfter; -// uint256 userBalanceSTXTZDiff = userBalanceSTXTZAfter - userBalanceSTXTZBefore; - -// assertApproxEqRel(debtRepaid, userBalanceXTZDiff, 1e18); -// assertApproxEqRel(collateralWithdrawn, userBalanceSTXTZDiff, 1e18); - -// IAaveOracle oracle = IAaveOracle(IPoolAddressesProvider(AAVE_V3_POOL_ADDRESSES_PROVIDER).getPriceOracle()); -// uint256 stXtzPrice = oracle.getAssetPrice(ST_XTZ); -// uint256 xtzPrice = oracle.getAssetPrice(XTZ); - -// uint256 debtRepaidUsd = (userBalanceXTZDiff * xtzPrice) / (10 ** 18); -// uint256 collateralWithdrawnUsd = (userBalanceSTXTZDiff * stXtzPrice) / (10 ** 6); - -// uint256 incentiveWAD = (collateralWithdrawnUsd * WAD) / debtRepaidUsd; -// assertGe(incentiveWAD, PRE_IF1); -// assertLe(incentiveWAD, PRE_IF2); -// } -// } +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.13; + +import {IntegrationBase} from "./IntegrationBase.sol"; +import {console} from "forge-std/console.sol"; +import {DataTypes} from "../../../src/common/DataTypes.sol"; +import { + AaveV3PreliquidationFallbackHandler +} from "../../../src/modules/fallback/AaveV3PreliquidationFallbackHandler.sol"; +import {console} from "forge-std/console.sol"; +import {IPoolDataProvider} from "aave-v3-core/contracts/interfaces/IPoolDataProvider.sol"; +import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +import {IAaveOracle} from "aave-v3-core/contracts/interfaces/IAaveOracle.sol"; +import {IPoolAddressesProvider} from "aave-v3-core/contracts/interfaces/IPoolAddressesProvider.sol"; + +contract AavePreliquidationTest is IntegrationBase { + function setUp() public override { + super.setUp(); + + DataTypes.AaveV3EmodeParams memory params = + DataTypes.AaveV3EmodeParams({emodeCategory: environment.emodeCategory}); + + DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); + moduleExecutionData[0] = DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.DELEGATECALL, + module: address(emodeModule), + data: abi.encodeWithSelector(emodeModule.execute.selector, params) + }); + + vm.startPrank(admin); + superloop.operate(moduleExecutionData); + + _deployPreliquidationFallbackHandler(address(superloop)); + bytes32 key1 = keccak256( + abi.encodePacked( + abi.encodeWithSelector(AaveV3PreliquidationFallbackHandler.preliquidate.selector), + id, + DataTypes.CallType.DELEGATECALL + ) + ); + bytes32 key2 = keccak256( + abi.encodePacked( + abi.encodeWithSelector(AaveV3PreliquidationFallbackHandler.preliquidationParams.selector), + id, + DataTypes.CallType.DELEGATECALL + ) + ); + + superloop.setFallbackHandler(key1, address(preliquidationFallbackHandler)); + superloop.setFallbackHandler(key2, address(preliquidationFallbackHandler)); + vm.stopPrank(); + } + + function test_aavePreliquidationParams() public view { + DataTypes.AaveV3PreliquidationParams memory params = AaveV3PreliquidationFallbackHandler(address(superloop)) + .preliquidationParams(id, DataTypes.CallType.DELEGATECALL); + + assertEq(params.lendReserve, environment.lendAssets[0]); + assertEq(params.borrowReserve, environment.borrowAssets[0]); + assertEq(params.Lltv, LLTV); + assertEq(params.preLltv, PRE_LLTV); + assertEq(params.preCF1, PRE_CF1); + assertEq(params.preCF2, PRE_CF2); + assertEq(params.preIF1, PRE_IF1); + assertEq(params.preIF2, PRE_IF2); + } + + function test_aavePreliquidation() public { + _createPartialDepositWithResolution(true); + + (,, uint256 currentVariableDebt,,,,,,) = IPoolDataProvider(environment.poolAddressesProvider) + .getUserReserveData(environment.borrowAssets[0], address(superloop)); + (uint256 currentATokenBalance,,,,,,,,) = IPoolDataProvider(environment.poolDataProvider) + .getUserReserveData(environment.lendAssets[0], address(superloop)); + + address liquidator = makeAddr("liquidator"); + deal(environment.borrowAssets[0], liquidator, 1000 * environment.vaultAssetDecimals); + + uint256 userBalanceXTZBefore = IERC20(environment.borrowAssets[0]).balanceOf(liquidator); + uint256 userBalanceSTXTZBefore = IERC20(environment.lendAssets[0]).balanceOf(liquidator); + + vm.startPrank(liquidator); + IERC20(environment.borrowAssets[0]).approve(address(superloop), 1000 * environment.vaultAssetDecimals); + + AaveV3PreliquidationFallbackHandler(address(superloop)) + .preliquidate( + id, + DataTypes.CallType.DELEGATECALL, + DataTypes.AaveV3ExecutePreliquidationParams({ + user: address(superloop), debtToCover: currentVariableDebt + }) + ); + vm.stopPrank(); + + (,, uint256 currentVariableDebtAfter,,,,,,) = IPoolDataProvider(environment.poolDataProvider) + .getUserReserveData(environment.borrowAssets[0], address(superloop)); + (uint256 currentATokenBalanceAfter,,,,,,,,) = IPoolDataProvider(environment.poolDataProvider) + .getUserReserveData(environment.lendAssets[0], address(superloop)); + + uint256 userBalanceXTZAfter = IERC20(environment.borrowAssets[0]).balanceOf(liquidator); + uint256 userBalanceSTXTZAfter = IERC20(environment.lendAssets[0]).balanceOf(liquidator); + uint256 debtRepaid = currentVariableDebt - currentVariableDebtAfter; + uint256 collateralWithdrawn = currentATokenBalance - currentATokenBalanceAfter; + + uint256 userBalanceXTZDiff = userBalanceXTZBefore - userBalanceXTZAfter; + uint256 userBalanceSTXTZDiff = userBalanceSTXTZAfter - userBalanceSTXTZBefore; + + assertApproxEqRel(debtRepaid, userBalanceXTZDiff, 1e18); + assertApproxEqRel(collateralWithdrawn, userBalanceSTXTZDiff, 1e18); + + IAaveOracle oracle = IAaveOracle(IPoolAddressesProvider(environment.poolAddressesProvider).getPriceOracle()); + uint256 stXtzPrice = oracle.getAssetPrice(environment.lendAssets[0]); + uint256 xtzPrice = oracle.getAssetPrice(environment.borrowAssets[0]); + + uint256 debtRepaidUsd = (userBalanceXTZDiff * xtzPrice) / (10 ** 18); + uint256 collateralWithdrawnUsd = (userBalanceSTXTZDiff * stXtzPrice) / (10 ** 6); + + uint256 incentiveWAD = (collateralWithdrawnUsd * WAD) / debtRepaidUsd; + assertGe(incentiveWAD, PRE_IF1); + assertLe(incentiveWAD, PRE_IF2); + } +} diff --git a/test/core/integration/DepositManager.t.sol b/test/core/integration/DepositManager.t.sol index 1e11b58..a8c8623 100644 --- a/test/core/integration/DepositManager.t.sol +++ b/test/core/integration/DepositManager.t.sol @@ -227,8 +227,12 @@ contract DepositManagerTest is IntegrationBase { DataTypes.ModuleExecutionData[] memory intermediateExecutionDataSecondBatch = new DataTypes.ModuleExecutionData[](1); intermediateExecutionDataSecondBatch[0] = USE_MORPHO - ? _morphoFlashloanCall(environment.lendAssets[0], flashLoanAmount, abi.encode(moduleExecutionDataSecondBatch)) - : _flashloanCall(environment.lendAssets[0], supplyAmountSecondBatch, abi.encode(moduleExecutionDataSecondBatch)); + ? _morphoFlashloanCall( + environment.lendAssets[0], flashLoanAmount, abi.encode(moduleExecutionDataSecondBatch) + ) + : _flashloanCall( + environment.lendAssets[0], supplyAmountSecondBatch, abi.encode(moduleExecutionDataSecondBatch) + ); DataTypes.ModuleExecutionData[] memory finalExecutionDataSecondBatch = new DataTypes.ModuleExecutionData[](1); finalExecutionDataSecondBatch[0] = _resolveDepositRequestsCall( diff --git a/test/core/integration/IntegrationBase.sol b/test/core/integration/IntegrationBase.sol index b3cdada..f2d761e 100644 --- a/test/core/integration/IntegrationBase.sol +++ b/test/core/integration/IntegrationBase.sol @@ -5,8 +5,9 @@ pragma solidity ^0.8.13; import {TestBase} from "../TestBase.sol"; import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; -import {TransparentUpgradeableProxy} from - "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import { + TransparentUpgradeableProxy +} from "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {DataTypes} from "../../../src/common/DataTypes.sol"; import {IFlashLoanSimpleReceiver} from "aave-v3-core/contracts/flashloan/interfaces/IFlashLoanSimpleReceiver.sol"; import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; @@ -167,9 +168,7 @@ abstract contract IntegrationBase is TestBase { returns (DataTypes.ModuleExecutionData memory) { DataTypes.ResolveWithdrawRequestsData memory resolveWithdrawRequestsData = DataTypes.ResolveWithdrawRequestsData({ - shares: shares, - requestType: requestType, - callbackExecutionData: data + shares: shares, requestType: requestType, callbackExecutionData: data }); return DataTypes.ModuleExecutionData({ executionType: DataTypes.CallType.CALL, @@ -184,10 +183,7 @@ abstract contract IntegrationBase is TestBase { returns (DataTypes.ModuleExecutionData memory) { DataTypes.AaveV3FlashloanParams memory flashloanParams = DataTypes.AaveV3FlashloanParams({ - asset: asset, - amount: amount, - referralCode: 0, - callbackExecutionData: data + asset: asset, amount: amount, referralCode: 0, callbackExecutionData: data }); return DataTypes.ModuleExecutionData({ executionType: DataTypes.CallType.DELEGATECALL, @@ -239,8 +235,7 @@ abstract contract IntegrationBase is TestBase { ) internal view returns (DataTypes.ModuleExecutionData memory) { DataTypes.ExecuteSwapParamsData[] memory swapParamsData = new DataTypes.ExecuteSwapParamsData[](2); swapParamsData[0] = DataTypes.ExecuteSwapParamsData({ - target: tokenIn, - data: abi.encodeWithSelector(IERC20.approve.selector, router, swapAmount) + target: tokenIn, data: abi.encodeWithSelector(IERC20.approve.selector, router, swapAmount) }); swapParamsData[1] = DataTypes.ExecuteSwapParamsData({ target: router, @@ -285,8 +280,7 @@ abstract contract IntegrationBase is TestBase { ) internal view returns (DataTypes.ModuleExecutionData memory) { DataTypes.ExecuteSwapParamsData[] memory swapParamsData = new DataTypes.ExecuteSwapParamsData[](2); swapParamsData[0] = DataTypes.ExecuteSwapParamsData({ - target: tokenIn, - data: abi.encodeWithSelector(IERC20.approve.selector, router, withdrawAmount) + target: tokenIn, data: abi.encodeWithSelector(IERC20.approve.selector, router, withdrawAmount) }); swapParamsData[1] = DataTypes.ExecuteSwapParamsData({ target: router, @@ -331,8 +325,7 @@ abstract contract IntegrationBase is TestBase { ) internal view returns (DataTypes.ModuleExecutionData memory) { DataTypes.ExecuteSwapParamsData[] memory swapParamsData = new DataTypes.ExecuteSwapParamsData[](2); swapParamsData[0] = DataTypes.ExecuteSwapParamsData({ - target: tokenIn, - data: abi.encodeWithSelector(IERC20.approve.selector, pool, amountIn) + target: tokenIn, data: abi.encodeWithSelector(IERC20.approve.selector, pool, amountIn) }); swapParamsData[1] = DataTypes.ExecuteSwapParamsData({ target: pool, @@ -364,11 +357,7 @@ abstract contract IntegrationBase is TestBase { }); } - function _withdrawCall(address asset, uint256 amount) - internal - view - returns (DataTypes.ModuleExecutionData memory) - { + function _withdrawCall(address asset, uint256 amount) internal view returns (DataTypes.ModuleExecutionData memory) { DataTypes.AaveV3ActionParams memory withdrawParams = DataTypes.AaveV3ActionParams({asset: asset, amount: amount}); return DataTypes.ModuleExecutionData({ @@ -555,7 +544,9 @@ abstract contract IntegrationBase is TestBase { USDC_USDE_POOL_FEE, block.timestamp + 100 ) - : _swapCallExactOutCurve(ST_XTZ, XTZ, XTZ_STXTZ_POOL, withdrawAmount, repayAmountWithPremium, STXTZ_XTZ_SWAP); + : _swapCallExactOutCurve( + ST_XTZ, XTZ, XTZ_STXTZ_POOL, withdrawAmount, repayAmountWithPremium, STXTZ_XTZ_SWAP + ); DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); intermediateExecutionData[0] = USE_MORPHO diff --git a/test/core/integration/Migration.t.sol b/test/core/integration/Migration.t.sol index e4a95f9..e1eb10d 100644 --- a/test/core/integration/Migration.t.sol +++ b/test/core/integration/Migration.t.sol @@ -1,482 +1,482 @@ -// // SPDX-License-Identifier: MIT - -// pragma solidity ^0.8.13; - -// import {IntegrationBase} from "../integration/IntegrationBase.sol"; -// import {DataTypes} from "../../../src/common/DataTypes.sol"; -// import {MigrationHelper} from "../../../src/helpers/MigrationHelper.sol"; -// import {ISuperloop} from "../../../src/interfaces/ISuperloop.sol"; -// import {IAccountantModule} from "../../../src/interfaces/IAccountantModule.sol"; -// import {console} from "forge-std/console.sol"; -// import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -// import {ISuperloopLegacy} from "../../../src/helpers/ISuperloopLegacy.sol"; -// import {Ownable} from "openzeppelin-contracts/contracts/access/Ownable.sol"; - -// /** -// * Prerequisites for the migration to work -// * 1. The old vault's withdraw manager must be empty -// * 2. Both the vaults should share common aave action modules and dex module -// * 2. Migration helper contract must be set as priveledged address in the old vault -// * 3. Migration helper contract must be set as these in the new vault -// * 1. vault operator -// * 2. deposit manager in the new vault -// * 3. vault in the new accountant module -// * 4. Post migration -// * 1. Update vault operator on the new vault -// * 2. Update vault in the new accountant module -// * 3. Update deposit manager on the new vault -// * 4. Remove migration helper as priveledged address from the old vault -// */ -// contract MigrationTest is IntegrationBase { -// address REPAY_MODULE = 0x9AF8cCabC21ff594dA237f9694C4A9C6123480c6; -// address WITHDRAW_MODULE = 0x1f5Ba080B9E5705DA47212167cA44611F78DB130; -// address DEPOSIT_MODULE = 0x66e82124412C61D7fF90ACFBa82936DD037D737E; -// address BORROW_MODULE = 0x3de57294989d12066a94a8A16E977992F3cF8433; -// address DEX_MODULE = 0x38F5efC1267F6103c9b0FE802E1731E245f09Cd0; - -// address oldVault = 0xe24e5DEbA01Ab0B5D78A0093442De0864832803E; -// address oldVaultBtc = 0xC557529dd252e5a02E6C653b0B88984aFa3c8199; - -// // currently there are 119 holder who need to be migrated to the new vault -// address[] public ALL_HOLDERS_XTZ = [ -// 0x2B38f0De645E05Ec410AE86Ef479a1e42E45Bd12, -// 0xb3d1aCA9bF004d0da560930Cf9277c604eDf2283, -// 0x11fC7853944570C1F9D9EBE7Ac24e2FeFddf0314, -// 0xced306BfCB4a057aE3905063fD601774F9F18730, -// 0x8fb2FB82fF44da85E43d0A71CA992DbBC3243c5a, -// 0x5d8809340760b1bB54642BE91Bb5A2871C0d7a10, -// 0x40F832B71D2C525A9aa4b4908Ec511Ed93c8a308, -// 0xde5017f32C3AB32dd0286891A11F5d5D54C13728, -// 0x4f2421553E571627C6801521316732693016d9cF, -// 0x6EA314366871459Dbc3e1A2Eb422e23B538c7ADC, -// 0x953D1668BC03e0EE9145A7c4F79956b73db90B67, -// 0x46C9CCC6857E33a8209706BB5043700b4608Dda8, -// 0x67Efd0FfD493Fa5C55f2a19033557557e3A5197C, -// 0xbE00FA424243C56c7CF5792aFBd6b8Dc4d6CBE4E, -// 0xEC23a0C9D4107cAD33271c4F1955a21f4c041208, -// 0x3d72971b117EE8EF445D138557151f66CB0D408F, -// 0xc2E97Ae6Ca9aAFB19CE0B8bCd1F4C50285db2377, -// 0x5EdF9DeC5b003AF80B65e129Ab17DBE64106a8e3, -// 0x669bd328f6C494949Ed9fB2dc8021557A6Dd005f, -// 0x664324F8B7430b4C22Dc036234Dd752A4755Fda7, -// 0x07ABc6225724a3f29e09173f585AacDE7A701dB3, -// 0xB35722e595F2B974c2B1D14A80Fab5d0c60c2fa3, -// 0x37b14bF02B1D01E196E3439d1689fEf845E9314A, -// 0x93e3A2a50e7A6091B877Fc939e0a3f43954811d8, -// 0xd9aeb979cbe3a85F2d6fca3723f846Af8CA56E05, -// 0x4A2cFAa5B24850493298DCb8969fe11FEeFfc366, -// 0x1C6Aed70006d8d65c6407Fa9b27C9359C1572f40, -// 0x92c34B736B832d33209497f3904C4423d11E2a8a, -// 0xCAf9131ADF841cC8A3f8C14cBc94Aa16cF0a2faA, -// 0x9214835f82752E0574F3828A4400C36Ba386Df82, -// 0xF09E6d5EE9b5B7FC84412260FD6E6D70dCadcd9C, -// 0x8b529eF78046008f9d1FbC91c7407030De96EE32, -// 0xa77E705d7166750F53F60ca7e246BAFBE40f5c42, -// 0x4fb30f8CcE1F80FC9CC45F7F626069be7549aF59, -// 0xc210a4D56D99E095b126664fDCE545b369ED3FD0, -// 0x03adFaA573aC1a9b19D2b8F79a5aAFFb9c2A0532, -// 0x79bc970B69CC8fb31406C409692eD19612E39731, -// 0xb9b4800282BDC0DD2284fD321cf6800a24a7B8B0, -// 0x06e477F88ae55721B72f0F2c545328eDe998803e, -// 0xc25404122C1776689A6fD620d0F816Cb9e36e5dD, -// 0x2192013589F926637cB288004db2f1dadb71D390, -// 0xb0DebcB643eE79f19Ef659Bd01D0FAC12d058604, -// 0x09e63186b4F94FafcCC60a42D3f77b4A10995674, -// 0x521e6f07bDfa2fF8D072887F0ef3bB908a3D9e2a, -// 0x341979e95a3e2969EaDa286b635d6e29F7a76a71, -// 0xAD89C778C7dBC0C7546cE7Fdf6A1d87C369A7892, -// 0x4987f13D50AC3246E589433349095492e820f8fd, -// 0x22472e7cA126b25fA031DeA6A7E0a7fbD9B2cdfd, -// 0x595e187831CB1ebAf3e4c09423eef3E74DfDaD1E, -// 0xaD96ACD2e6574682AEBf8f25774FA643bcaa3B5a, -// 0x82C421023dBE34CC526F4c027577363E6C7Af9b6, -// 0xc51101BAbC9Cc75118fBCe5187914e45841A7f01, -// 0x09505A42D693D828B78CC87ed64DB9A0348Ca5e6, -// 0xdD543DC0efeA305A4c595305C784166fbe87897B, -// 0xbb85bFF98F145af30BA935Ae4DeD97C7A5Ce9bF2, -// 0xfd005e01FAa45d3CD3fD4641FBCd9FFa1d26C703, -// 0xaB14C744e5316475dd2384E6Bb95F9eCF4480bD5, -// 0x595f5e9cF36dF0AAf12Ae27f1b3dC18622Fc5748, -// 0xF5AC5943D16fC865824910033B756519DC396682, -// 0xa0c507E52359160a334d9f1AeAA459b8BD39568a, -// 0x0E16cAA0E000cD3B68b4128C6aE62E846cb5b431, -// 0x603c081e1d72063Cd691CAD3Bd2FEd68eBE8922a, -// 0x5c53414E1f15D7668c2b9EC0A92482A64845f5f6, -// 0xa87D56273CBE184927D92eEF1613ab2a72eacAC8, -// 0xCc9757171B3cf4Dc86D1a86370D5B10662519096, -// 0xeB92f0f58b218ad22a22741D5aa5bb7250E70045, -// 0xa2608e76d835D0b3DB2Bbcbd4Bd1F78D47208353, -// 0x9fD8Ef7E3867E962B401bC7289272653FEC11245, -// 0x73cF6fCbEb5a5f56e550Cce5e0eeAC47f622EA2e, -// 0x9b4A7002a086d4F8dFf04Af524987DdCa9b6ddCd, -// 0x94Ae5310ba47b42D6D8DF3F20cD55650Ad43Be37, -// 0xFd01eF34D30E6fA1169CBa769a22eEE454906f38, -// 0xE3c3cD192Bdf289558A0bA645bc2c2c822F7EB06, -// 0x18514a31077b33f32BDbeA52443f2C0EEb094Be1, -// 0x0E9852b16AE49C99B84b0241E3C6F4a5692C6b05, -// 0x2E6Ff365367c9DB7C4a6140d37394aB8CF25C8A2, -// 0x49400E8A2d270669ED7cfc4Fd5A2804c0A87e1BB, -// 0xDDc91d503Eeb7C3CB78559fB998C0a23354821ee, -// 0x0C5Cd230e9EBF5A707CF1f822Dfc49664304DfD2, -// 0x92751712B2F6ADE4AD1F35a837591A66394C6799, -// 0x57Fe7D8A10F5C4aC2Ca953a5B0a6b4BBfEC187C9, -// 0x5413E315A8c242132aC5DD626B0eceE616e5023D, -// 0xf458B8FB16D661bC1E260991EBa9b1FFBC8bA39b, -// 0xC6Ad105483F89de96A3E423980fB550465A05D9b, -// 0x26C43439031301a85e19868647DF46177869c4C8, -// 0x9cB4a586bbdE7528DB256c3FCE08845255d892D9, -// 0xb1f607F3B753bEf4756a30cfC235629A0c2d1E57, -// 0x1CbB1E8274E44287517A34De6AF3CF97dfD9E0Cb, -// 0x2b3F539dAf59126F6F566adD0dEDc19959305e9d, -// 0x2eCe359621497F4C9053c30912e9ecB7e74718c6, -// 0x1A50D1AeD031eE6B22b37c84eD8f3f3487C5Ca47, -// 0x117AD17Bf596D6337931f41141F34275Eab16129, -// 0x5BD39fcEE33D45C5c5f615e09D2AA785Aa0c36C6, -// 0x698230D8445C21E99f4CC81F8F733d205a661cD3, -// 0x10b453C5379877d6b55B71D73Af7D8Fbc69eeF91, -// 0x13deFCe8Be050f902f8eE06CB7f0E743e3e8b705, -// 0xFF5A5bBC1b0f124974165b2190f460438d7CD220, -// 0x7F5B2c355bFC0C6F3aE2D394534C29D3609FF542, -// 0x007EA857fdbE8D19997b507a21B4c377e3B21D82, -// 0x97F4aa12F637BAe9c07cA492eE281534b4F3BB50, -// 0x23b3511474c960960E4C030e572dADE633Cfc0F6, -// 0x7cA50563448C1f87cF131D96dEF7124f995cf3aC, -// 0xA8617c6BdE242B213bA283c93aCf034dfcbE66fA, -// 0x68f6609d45A9dA001A88f7A3b9ec236Acf27e1f5, -// 0x37ae94b377B6D6522BCED1a448D144C69B6bfa9a, -// 0x8Fb2B9010780c418fbf84733ca3F0F5623EC2e7a, -// 0x5F4BD8107FF5EB8fD25480C938662bBC11da27B8, -// 0x57453361422BfBE5332eF75CBE1C38242c5deEEA, -// 0x96b29b8dD6240144e203F9f45D55F8FE6fC466aB, -// 0x00ab2b9D924aaDCb60126A4050B68e8531A6A5a6, -// 0x431588Aff8ea1BEcB1d8188D87195Aa95678BA0A, -// 0x0E9B063789909565CEdA1Fba162474405A151E66, -// 0x5952f70FEF1CbC26856d149646D4A8F97E923eE7, -// 0x894d222eCeC91B9dFFf8bcBD164B7a72DF7469aD, -// 0x8B714cB45683733fD4Dc423FE6BF9A1d9F8DDD2C, -// 0x5E9b31FA592913C2aD2356ed843F66057c81455D, -// 0x58F1eFd2fBf415CFC0Ce30A13c36A2642690eDA0, -// 0x84e47C7E20Ee8e8d6195A99788226EB488f5Fad7, -// 0xCce7AcFb0d4760df15a1f87C92324B166e9C0638 -// ]; - -// address[] public ALL_HOLDERS_BTC = [ -// 0x9D5132Dd45CdaCd1De3a7b7f1f13da7F025fF726, -// 0xa7CBb758849979CEc43FD146fe01EB2BF560202C, -// 0x6bA9c7d86eBD6A20817d482C42C9dE2806EeFB7e, -// 0x9D0214C3dB28875fe2b9eb9cD9d4F71E7817890A, -// 0x2B38f0De645E05Ec410AE86Ef479a1e42E45Bd12, -// 0x8fb2FB82fF44da85E43d0A71CA992DbBC3243c5a, -// 0xced306BfCB4a057aE3905063fD601774F9F18730, -// 0x0C1856Cb7444cc8596c706185b54535F45C2623B, -// 0x11fC7853944570C1F9D9EBE7Ac24e2FeFddf0314, -// 0xb3d1aCA9bF004d0da560930Cf9277c604eDf2283, -// 0xB438Dbaae78225eEfcDBB04E207A8eFB06036a2c, -// 0x997b96BAD648c39226281Bc002f9857274E42A01, -// 0xd52929B69680A6f74D2eB9c8F1ef482f37b1b32B, -// 0x706FC1a8e457De0cf52e7679C2922aEF7F7a397e, -// 0x3497818F50f79A11236B941dDAd35Da68A57864E, -// 0xB35722e595F2B974c2B1D14A80Fab5d0c60c2fa3, -// 0xA9f7b86FCC86EA1b913dDcaB8c9514a8e677666E, -// 0x7077e395C9FA4E480366A5DC5792d2504d78dffF, -// 0x664324F8B7430b4C22Dc036234Dd752A4755Fda7, -// 0xD22ADba687aAd7DD9CEC3b18D970C581734783bA, -// 0x5BC81274740A73D33ec8539182c326b8b58004C2, -// 0x02F73B8e29dD6Ec38Bc5D8B3826051DC562c3060, -// 0x4e90Da1dDCD5B0Fb295b6A69251b5D04D5bCCA7a, -// 0x150a0D516C0ceFa39f980Da57d5422E91A94238b, -// 0x4fb30f8CcE1F80FC9CC45F7F626069be7549aF59, -// 0xF382ce5457Bba6113e082DD638b6671Cb2277B1f, -// 0xb95f08B2eda7B23Ce412A6dE82eA5f5100335A3A, -// 0x5b6Da8BC8696Aa4D20030151345ec652cf1eC727, -// 0x0396816A361e2353a91Fde8438600a9353b34ce7, -// 0xA1ebf2043e76446eFA2724bD1Ec18321776096FF, -// 0x9214835f82752E0574F3828A4400C36Ba386Df82, -// 0xb0DebcB643eE79f19Ef659Bd01D0FAC12d058604, -// 0x3d72971b117EE8EF445D138557151f66CB0D408F, -// 0x09e63186b4F94FafcCC60a42D3f77b4A10995674, -// 0xc210a4D56D99E095b126664fDCE545b369ED3FD0, -// 0xEC23a0C9D4107cAD33271c4F1955a21f4c041208, -// 0x03adFaA573aC1a9b19D2b8F79a5aAFFb9c2A0532, -// 0x6a58EBB35664A171A1B070DE48ee93278A63c168, -// 0x521e6f07bDfa2fF8D072887F0ef3bB908a3D9e2a, -// 0xF5AC5943D16fC865824910033B756519DC396682, -// 0xBD73cF5baf12F120Ee3f6C4ad82df9a12649e578, -// 0x44119A62EA645242234cAF408c4c20513E660EBb -// ]; - -// function setUp() public override { -// super.setUp(); - -// DataTypes.AaveV3EmodeParams memory params = DataTypes.AaveV3EmodeParams({emodeCategory: 3}); - -// DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); -// moduleExecutionData[0] = DataTypes.ModuleExecutionData({ -// executionType: DataTypes.CallType.DELEGATECALL, -// module: address(emodeModule), -// data: abi.encodeWithSelector(emodeModule.execute.selector, params) -// }); - -// DataTypes.AaveV3EmodeParams memory paramsBtc = DataTypes.AaveV3EmodeParams({emodeCategory: 2}); -// DataTypes.ModuleExecutionData[] memory moduleExecutionDataBtc = new DataTypes.ModuleExecutionData[](1); -// moduleExecutionDataBtc[0] = DataTypes.ModuleExecutionData({ -// executionType: DataTypes.CallType.DELEGATECALL, -// module: address(emodeModule), -// data: abi.encodeWithSelector(emodeModule.execute.selector, paramsBtc) -// }); - -// vm.startPrank(admin); -// superloop.operate(moduleExecutionData); -// superloopBtc.operate(moduleExecutionDataBtc); - -// // register the above modules -// moduleRegistry.setModule("REPAY_MODULE", REPAY_MODULE); -// moduleRegistry.setModule("WITHDRAW_MODULE", WITHDRAW_MODULE); -// moduleRegistry.setModule("DEPOSIT_MODULE", DEPOSIT_MODULE); -// moduleRegistry.setModule("BORROW_MODULE", BORROW_MODULE); -// moduleRegistry.setModule("DEX_MODULE", DEX_MODULE); - -// superloop.setRegisteredModule(REPAY_MODULE, true); -// superloop.setRegisteredModule(WITHDRAW_MODULE, true); -// superloop.setRegisteredModule(DEPOSIT_MODULE, true); -// superloop.setRegisteredModule(BORROW_MODULE, true); -// superloop.setRegisteredModule(DEX_MODULE, true); - -// superloopBtc.setRegisteredModule(REPAY_MODULE, true); -// superloopBtc.setRegisteredModule(WITHDRAW_MODULE, true); -// superloopBtc.setRegisteredModule(DEPOSIT_MODULE, true); -// superloopBtc.setRegisteredModule(BORROW_MODULE, true); -// superloopBtc.setRegisteredModule(DEX_MODULE, true); -// vm.stopPrank(); - -// vm.label(oldVault, "oldVault"); -// vm.label(oldVaultBtc, "oldVaultBtc"); -// vm.label(address(superloop), "newVault"); -// vm.label(address(superloopBtc), "newVaultBtc"); -// vm.label(REPAY_MODULE, "REPAY_MODULE_OLD"); -// vm.label(WITHDRAW_MODULE, "WITHDRAW_MODULE_OLD"); -// vm.label(DEPOSIT_MODULE, "DEPOSIT_MODULE_OLD"); -// vm.label(BORROW_MODULE, "BORROW_MODULE_OLD"); -// vm.label(DEX_MODULE, "DEX_MODULE_OLD"); - -// // TODO: do deposit and withdraw for both -// } - -// // function test_migration() public { -// // // deploy migration helper -// // MigrationHelper migrationHelper = new MigrationHelper( -// // AAVE_V3_POOL_ADDRESSES_PROVIDER, -// // REPAY_MODULE, -// // WITHDRAW_MODULE, -// // DEPOSIT_MODULE, -// // BORROW_MODULE, -// // DEX_MODULE, -// // USDC -// // ); -// // vm.label(address(migrationHelper), "migrationHelper"); - -// // // set migration helper contract as priveledged address in old vault -// // address oldVaultAdmin = ISuperloop(oldVault).vaultAdmin(); -// // vm.prank(oldVaultAdmin); -// // ISuperloop(oldVault).setPrivilegedAddress(address(migrationHelper), true); - -// // // set migration helper contract as deposit manager and vault operator in new vault -// // vm.startPrank(admin); -// // superloop.setDepositManagerModule(address(migrationHelper)); -// // superloop.setVaultOperator(address(migrationHelper)); - -// // // set migration helper as vault in new accountant module -// // accountant.setVault(address(migrationHelper)); -// // accountant.transferOwnership(address(migrationHelper)); -// // vm.stopPrank(); - -// // address[] memory users = ALL_HOLDERS_XTZ; - -// // // old balances -// // uint256 oldVaultXTZBalance = IERC20(XTZ).balanceOf(oldVault); -// // uint256 oldVaultSTXTZBalance = IERC20(ST_XTZ).balanceOf(oldVault); -// // (uint256 oldVaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(ST_XTZ, oldVault); -// // (,, uint256 oldVaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(XTZ, oldVault); - -// // console.log("OLD VAULT STATE INITIAL"); -// // console.log("oldVaultXTZBalance", oldVaultXTZBalance); -// // console.log("oldVaultSTXTZBalance", oldVaultSTXTZBalance); -// // console.log("oldVaultLendBalance", oldVaultLendBalance); -// // console.log("oldVaultBorrowBalance", oldVaultBorrowBalance); -// // console.log("old vault total supply", ISuperloop(oldVault).totalSupply()); -// // console.log("old vault total assets", ISuperloop(oldVault).totalAssets()); - -// // assertEq(Ownable(address(accountant)).owner(), address(migrationHelper)); -// // assertEq(accountant.vault(), address(migrationHelper)); - -// // uint256 maxSharesDelta = 100; - -// // // start recording gas -// // uint256 startGas = gasleft(); -// // uint256 batches = 4; -// // migrationHelper.migrate(oldVault, address(superloop), users, ST_XTZ, XTZ, batches, maxSharesDelta, 5); - -// // console.log("migration complete"); - -// // uint256 endGas = gasleft(); -// // uint256 gasUsed = startGas - endGas; -// // console.log("gas used", gasUsed); -// // uint256 newVaultXTZBalance = IERC20(XTZ).balanceOf(address(superloop)); -// // uint256 newVaultSTXTZBalance = IERC20(ST_XTZ).balanceOf(address(superloop)); -// // (uint256 newVaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(ST_XTZ, address(superloop)); -// // (,, uint256 newVaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(XTZ, address(superloop)); -// // uint256 newVaultLastRealizedFeeExchangeRate = -// // IAccountantModule(ISuperloop(address(superloop)).accountant()).lastRealizedFeeExchangeRate(); -// // uint256 oldVaultLastRealizedFeeExchangeRate = -// // IAccountantModule(ISuperloopLegacy(oldVault).accountantModule()).lastRealizedFeeExchangeRate(); - -// // console.log("NEW VAULT STATE AFTER MIGRATION"); -// // console.log("newVaultXTZBalance", newVaultXTZBalance); -// // console.log("newVaultSTXTZBalance", newVaultSTXTZBalance); -// // console.log("newVaultLendBalance", newVaultLendBalance); -// // console.log("newVaultBorrowBalance", newVaultBorrowBalance); -// // console.log("new vault total supply", ISuperloop(address(superloop)).totalSupply()); -// // console.log("new vault total assets", ISuperloop(address(superloop)).totalAssets()); -// // console.log( -// // "new vault last realized fee exchange rate", -// // IAccountantModule(ISuperloop(address(superloop)).accountant()).lastRealizedFeeExchangeRate() -// // ); - -// // // assert balances of each of the users are the same - -// // for (uint256 i = 0; i < users.length; i++) { -// // assertEq(ISuperloop(address(superloop)).balanceOf(users[i]), ISuperloop(oldVault).balanceOf(users[i])); -// // } - -// // assertEq(newVaultLastRealizedFeeExchangeRate, oldVaultLastRealizedFeeExchangeRate); -// // assertEq(Ownable(address(accountant)).owner(), address(admin)); -// // assertEq(accountant.vault(), address(superloop)); - -// // oldVaultXTZBalance = IERC20(XTZ).balanceOf(oldVault); -// // oldVaultSTXTZBalance = IERC20(ST_XTZ).balanceOf(oldVault); -// // (oldVaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(ST_XTZ, oldVault); -// // (,, oldVaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(XTZ, oldVault); - -// // console.log("OLD VAULT STATE AFTER MIGRATION"); -// // console.log("oldVaultXTZBalance", oldVaultXTZBalance); -// // console.log("oldVaultSTXTZBalance", oldVaultSTXTZBalance); -// // console.log("oldVaultLendBalance", oldVaultLendBalance); -// // console.log("oldVaultBorrowBalance", oldVaultBorrowBalance); -// // console.log("old vault total supply", ISuperloop(oldVault).totalSupply()); -// // console.log("old vault total assets", ISuperloop(oldVault).totalAssets()); -// // console.log( -// // "old vault last realized fee exchange rate", -// // IAccountantModule(ISuperloopLegacy(oldVault).accountantModule()).lastRealizedFeeExchangeRate() -// // ); -// // } - -// function test_migrationBtc() public { -// // deploy migration helper -// MigrationHelper migrationHelper = new MigrationHelper( -// AAVE_V3_POOL_ADDRESSES_PROVIDER, -// REPAY_MODULE, -// WITHDRAW_MODULE, -// DEPOSIT_MODULE, -// BORROW_MODULE, -// DEX_MODULE, -// USDC -// ); -// vm.label(address(migrationHelper), "migrationHelper"); - -// // set migration helper contract as priveledged address in old vault -// address oldVaultAdmin = ISuperloop(oldVaultBtc).vaultAdmin(); -// vm.prank(oldVaultAdmin); -// ISuperloop(oldVaultBtc).setPrivilegedAddress(address(migrationHelper), true); - -// // set migration helper contract as deposit manager and vault operator in new vault -// vm.startPrank(admin); -// superloopBtc.setDepositManagerModule(address(migrationHelper)); -// superloopBtc.setVaultOperator(address(migrationHelper)); - -// // set migration helper as vault in new accountant module -// accountantBtc.setVault(address(migrationHelper)); -// accountantBtc.transferOwnership(address(migrationHelper)); -// vm.stopPrank(); - -// address[] memory users = ALL_HOLDERS_BTC; - -// // old balances -// uint256 oldVaultBTCBalance = IERC20(WBTC).balanceOf(oldVaultBtc); -// uint256 oldVaultLBTCBalance = IERC20(LBTC).balanceOf(oldVaultBtc); -// (uint256 oldVaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(LBTC, oldVaultBtc); -// (,, uint256 oldVaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(WBTC, oldVaultBtc); - -// console.log("OLD VAULT STATE INITIAL"); -// console.log("oldVaultBTCBalance", oldVaultBTCBalance); -// console.log("oldVaultLBTCBalance", oldVaultLBTCBalance); -// console.log("oldVaultLendBalance", oldVaultLendBalance); -// console.log("oldVaultBorrowBalance", oldVaultBorrowBalance); -// console.log("old vault total supply", ISuperloop(oldVaultBtc).totalSupply()); -// console.log("old vault total assets", ISuperloop(oldVaultBtc).totalAssets()); - -// assertEq(Ownable(address(accountantBtc)).owner(), address(migrationHelper)); -// assertEq(accountantBtc.vault(), address(migrationHelper)); - -// uint256 maxSharesDelta = 100; - -// // start recording gas -// uint256 startGas = gasleft(); -// uint256 batches = 4; -// migrationHelper.migrate(oldVaultBtc, address(superloopBtc), users, LBTC, WBTC, batches, maxSharesDelta, 5); - -// console.log("migration complete"); - -// uint256 endGas = gasleft(); -// uint256 gasUsed = startGas - endGas; -// console.log("gas used", gasUsed); -// uint256 newVaultBTCBalance = IERC20(WBTC).balanceOf(address(superloopBtc)); -// uint256 newVaultLBTCBalance = IERC20(LBTC).balanceOf(address(superloopBtc)); -// (uint256 newVaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(LBTC, address(superloopBtc)); -// (,, uint256 newVaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(WBTC, address(superloopBtc)); -// uint256 newVaultLastRealizedFeeExchangeRate = -// IAccountantModule(ISuperloop(address(superloopBtc)).accountant()).lastRealizedFeeExchangeRate(); -// uint256 oldVaultLastRealizedFeeExchangeRate = -// IAccountantModule(ISuperloopLegacy(oldVaultBtc).accountantModule()).lastRealizedFeeExchangeRate(); - -// console.log("NEW VAULT STATE AFTER MIGRATION"); -// console.log("newVaultXTZBalance", newVaultBTCBalance); -// console.log("newVaultLBTCBalance", newVaultLBTCBalance); -// console.log("newVaultLendBalance", newVaultLendBalance); -// console.log("newVaultBorrowBalance", newVaultBorrowBalance); -// console.log("new vault total supply", ISuperloop(address(superloopBtc)).totalSupply()); -// console.log("new vault total assets", ISuperloop(address(superloopBtc)).totalAssets()); -// console.log( -// "new vault last realized fee exchange rate", -// IAccountantModule(ISuperloop(address(superloop)).accountant()).lastRealizedFeeExchangeRate() -// ); - -// // assert balances of each of the users are the same - -// for (uint256 i = 0; i < users.length; i++) { -// assertEq(ISuperloop(address(superloopBtc)).balanceOf(users[i]), ISuperloop(oldVaultBtc).balanceOf(users[i])); -// } - -// assertEq(newVaultLastRealizedFeeExchangeRate, oldVaultLastRealizedFeeExchangeRate); -// assertEq(Ownable(address(accountantBtc)).owner(), address(admin)); -// assertEq(accountantBtc.vault(), address(superloopBtc)); - -// console.log("gas used", gasUsed); - -// oldVaultBTCBalance = IERC20(WBTC).balanceOf(oldVaultBtc); -// oldVaultLBTCBalance = IERC20(LBTC).balanceOf(oldVaultBtc); -// (oldVaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(LBTC, oldVaultBtc); -// (,, oldVaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(WBTC, oldVaultBtc); - -// console.log("OLD VAULT STATE AFTER MIGRATION"); -// console.log("oldVaultBTCBalance", oldVaultBTCBalance); -// console.log("oldVaultLBTCBalance", oldVaultLBTCBalance); -// console.log("oldVaultLendBalance", oldVaultLendBalance); -// console.log("oldVaultBorrowBalance", oldVaultBorrowBalance); -// console.log("old vault total supply", ISuperloop(oldVaultBtc).totalSupply()); -// console.log("old vault total assets", ISuperloop(oldVaultBtc).totalAssets()); -// console.log( -// "old vault last realized fee exchange rate", -// IAccountantModule(ISuperloopLegacy(oldVaultBtc).accountantModule()).lastRealizedFeeExchangeRate() -// ); -// } -// } +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.13; + +import {IntegrationBase} from "../integration/IntegrationBase.sol"; +import {DataTypes} from "../../../src/common/DataTypes.sol"; +import {MigrationHelper} from "../../../src/helpers/MigrationHelper.sol"; +import {ISuperloop} from "../../../src/interfaces/ISuperloop.sol"; +import {IAccountantModule} from "../../../src/interfaces/IAccountantModule.sol"; +import {console} from "forge-std/console.sol"; +import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +import {ISuperloopLegacy} from "../../../src/helpers/ISuperloopLegacy.sol"; +import {Ownable} from "openzeppelin-contracts/contracts/access/Ownable.sol"; + +/** + * Prerequisites for the migration to work + * 1. The old vault's withdraw manager must be empty + * 2. Both the vaults should share common aave action modules and dex module + * 2. Migration helper contract must be set as priveledged address in the old vault + * 3. Migration helper contract must be set as these in the new vault + * 1. vault operator + * 2. deposit manager in the new vault + * 3. vault in the new accountant module + * 4. Post migration + * 1. Update vault operator on the new vault + * 2. Update vault in the new accountant module + * 3. Update deposit manager on the new vault + * 4. Remove migration helper as priveledged address from the old vault + */ +contract MigrationTest is IntegrationBase { + address REPAY_MODULE = 0x9AF8cCabC21ff594dA237f9694C4A9C6123480c6; + address WITHDRAW_MODULE = 0x1f5Ba080B9E5705DA47212167cA44611F78DB130; + address DEPOSIT_MODULE = 0x66e82124412C61D7fF90ACFBa82936DD037D737E; + address BORROW_MODULE = 0x3de57294989d12066a94a8A16E977992F3cF8433; + address DEX_MODULE = 0x38F5efC1267F6103c9b0FE802E1731E245f09Cd0; + + address oldVault = 0xe24e5DEbA01Ab0B5D78A0093442De0864832803E; + address oldVaultBtc = 0xC557529dd252e5a02E6C653b0B88984aFa3c8199; + + // currently there are 119 holder who need to be migrated to the new vault + address[] public ALL_HOLDERS_XTZ = [ + 0x2B38f0De645E05Ec410AE86Ef479a1e42E45Bd12, + 0xb3d1aCA9bF004d0da560930Cf9277c604eDf2283, + 0x11fC7853944570C1F9D9EBE7Ac24e2FeFddf0314, + 0xced306BfCB4a057aE3905063fD601774F9F18730, + 0x8fb2FB82fF44da85E43d0A71CA992DbBC3243c5a, + 0x5d8809340760b1bB54642BE91Bb5A2871C0d7a10, + 0x40F832B71D2C525A9aa4b4908Ec511Ed93c8a308, + 0xde5017f32C3AB32dd0286891A11F5d5D54C13728, + 0x4f2421553E571627C6801521316732693016d9cF, + 0x6EA314366871459Dbc3e1A2Eb422e23B538c7ADC, + 0x953D1668BC03e0EE9145A7c4F79956b73db90B67, + 0x46C9CCC6857E33a8209706BB5043700b4608Dda8, + 0x67Efd0FfD493Fa5C55f2a19033557557e3A5197C, + 0xbE00FA424243C56c7CF5792aFBd6b8Dc4d6CBE4E, + 0xEC23a0C9D4107cAD33271c4F1955a21f4c041208, + 0x3d72971b117EE8EF445D138557151f66CB0D408F, + 0xc2E97Ae6Ca9aAFB19CE0B8bCd1F4C50285db2377, + 0x5EdF9DeC5b003AF80B65e129Ab17DBE64106a8e3, + 0x669bd328f6C494949Ed9fB2dc8021557A6Dd005f, + 0x664324F8B7430b4C22Dc036234Dd752A4755Fda7, + 0x07ABc6225724a3f29e09173f585AacDE7A701dB3, + 0xB35722e595F2B974c2B1D14A80Fab5d0c60c2fa3, + 0x37b14bF02B1D01E196E3439d1689fEf845E9314A, + 0x93e3A2a50e7A6091B877Fc939e0a3f43954811d8, + 0xd9aeb979cbe3a85F2d6fca3723f846Af8CA56E05, + 0x4A2cFAa5B24850493298DCb8969fe11FEeFfc366, + 0x1C6Aed70006d8d65c6407Fa9b27C9359C1572f40, + 0x92c34B736B832d33209497f3904C4423d11E2a8a, + 0xCAf9131ADF841cC8A3f8C14cBc94Aa16cF0a2faA, + 0x9214835f82752E0574F3828A4400C36Ba386Df82, + 0xF09E6d5EE9b5B7FC84412260FD6E6D70dCadcd9C, + 0x8b529eF78046008f9d1FbC91c7407030De96EE32, + 0xa77E705d7166750F53F60ca7e246BAFBE40f5c42, + 0x4fb30f8CcE1F80FC9CC45F7F626069be7549aF59, + 0xc210a4D56D99E095b126664fDCE545b369ED3FD0, + 0x03adFaA573aC1a9b19D2b8F79a5aAFFb9c2A0532, + 0x79bc970B69CC8fb31406C409692eD19612E39731, + 0xb9b4800282BDC0DD2284fD321cf6800a24a7B8B0, + 0x06e477F88ae55721B72f0F2c545328eDe998803e, + 0xc25404122C1776689A6fD620d0F816Cb9e36e5dD, + 0x2192013589F926637cB288004db2f1dadb71D390, + 0xb0DebcB643eE79f19Ef659Bd01D0FAC12d058604, + 0x09e63186b4F94FafcCC60a42D3f77b4A10995674, + 0x521e6f07bDfa2fF8D072887F0ef3bB908a3D9e2a, + 0x341979e95a3e2969EaDa286b635d6e29F7a76a71, + 0xAD89C778C7dBC0C7546cE7Fdf6A1d87C369A7892, + 0x4987f13D50AC3246E589433349095492e820f8fd, + 0x22472e7cA126b25fA031DeA6A7E0a7fbD9B2cdfd, + 0x595e187831CB1ebAf3e4c09423eef3E74DfDaD1E, + 0xaD96ACD2e6574682AEBf8f25774FA643bcaa3B5a, + 0x82C421023dBE34CC526F4c027577363E6C7Af9b6, + 0xc51101BAbC9Cc75118fBCe5187914e45841A7f01, + 0x09505A42D693D828B78CC87ed64DB9A0348Ca5e6, + 0xdD543DC0efeA305A4c595305C784166fbe87897B, + 0xbb85bFF98F145af30BA935Ae4DeD97C7A5Ce9bF2, + 0xfd005e01FAa45d3CD3fD4641FBCd9FFa1d26C703, + 0xaB14C744e5316475dd2384E6Bb95F9eCF4480bD5, + 0x595f5e9cF36dF0AAf12Ae27f1b3dC18622Fc5748, + 0xF5AC5943D16fC865824910033B756519DC396682, + 0xa0c507E52359160a334d9f1AeAA459b8BD39568a, + 0x0E16cAA0E000cD3B68b4128C6aE62E846cb5b431, + 0x603c081e1d72063Cd691CAD3Bd2FEd68eBE8922a, + 0x5c53414E1f15D7668c2b9EC0A92482A64845f5f6, + 0xa87D56273CBE184927D92eEF1613ab2a72eacAC8, + 0xCc9757171B3cf4Dc86D1a86370D5B10662519096, + 0xeB92f0f58b218ad22a22741D5aa5bb7250E70045, + 0xa2608e76d835D0b3DB2Bbcbd4Bd1F78D47208353, + 0x9fD8Ef7E3867E962B401bC7289272653FEC11245, + 0x73cF6fCbEb5a5f56e550Cce5e0eeAC47f622EA2e, + 0x9b4A7002a086d4F8dFf04Af524987DdCa9b6ddCd, + 0x94Ae5310ba47b42D6D8DF3F20cD55650Ad43Be37, + 0xFd01eF34D30E6fA1169CBa769a22eEE454906f38, + 0xE3c3cD192Bdf289558A0bA645bc2c2c822F7EB06, + 0x18514a31077b33f32BDbeA52443f2C0EEb094Be1, + 0x0E9852b16AE49C99B84b0241E3C6F4a5692C6b05, + 0x2E6Ff365367c9DB7C4a6140d37394aB8CF25C8A2, + 0x49400E8A2d270669ED7cfc4Fd5A2804c0A87e1BB, + 0xDDc91d503Eeb7C3CB78559fB998C0a23354821ee, + 0x0C5Cd230e9EBF5A707CF1f822Dfc49664304DfD2, + 0x92751712B2F6ADE4AD1F35a837591A66394C6799, + 0x57Fe7D8A10F5C4aC2Ca953a5B0a6b4BBfEC187C9, + 0x5413E315A8c242132aC5DD626B0eceE616e5023D, + 0xf458B8FB16D661bC1E260991EBa9b1FFBC8bA39b, + 0xC6Ad105483F89de96A3E423980fB550465A05D9b, + 0x26C43439031301a85e19868647DF46177869c4C8, + 0x9cB4a586bbdE7528DB256c3FCE08845255d892D9, + 0xb1f607F3B753bEf4756a30cfC235629A0c2d1E57, + 0x1CbB1E8274E44287517A34De6AF3CF97dfD9E0Cb, + 0x2b3F539dAf59126F6F566adD0dEDc19959305e9d, + 0x2eCe359621497F4C9053c30912e9ecB7e74718c6, + 0x1A50D1AeD031eE6B22b37c84eD8f3f3487C5Ca47, + 0x117AD17Bf596D6337931f41141F34275Eab16129, + 0x5BD39fcEE33D45C5c5f615e09D2AA785Aa0c36C6, + 0x698230D8445C21E99f4CC81F8F733d205a661cD3, + 0x10b453C5379877d6b55B71D73Af7D8Fbc69eeF91, + 0x13deFCe8Be050f902f8eE06CB7f0E743e3e8b705, + 0xFF5A5bBC1b0f124974165b2190f460438d7CD220, + 0x7F5B2c355bFC0C6F3aE2D394534C29D3609FF542, + 0x007EA857fdbE8D19997b507a21B4c377e3B21D82, + 0x97F4aa12F637BAe9c07cA492eE281534b4F3BB50, + 0x23b3511474c960960E4C030e572dADE633Cfc0F6, + 0x7cA50563448C1f87cF131D96dEF7124f995cf3aC, + 0xA8617c6BdE242B213bA283c93aCf034dfcbE66fA, + 0x68f6609d45A9dA001A88f7A3b9ec236Acf27e1f5, + 0x37ae94b377B6D6522BCED1a448D144C69B6bfa9a, + 0x8Fb2B9010780c418fbf84733ca3F0F5623EC2e7a, + 0x5F4BD8107FF5EB8fD25480C938662bBC11da27B8, + 0x57453361422BfBE5332eF75CBE1C38242c5deEEA, + 0x96b29b8dD6240144e203F9f45D55F8FE6fC466aB, + 0x00ab2b9D924aaDCb60126A4050B68e8531A6A5a6, + 0x431588Aff8ea1BEcB1d8188D87195Aa95678BA0A, + 0x0E9B063789909565CEdA1Fba162474405A151E66, + 0x5952f70FEF1CbC26856d149646D4A8F97E923eE7, + 0x894d222eCeC91B9dFFf8bcBD164B7a72DF7469aD, + 0x8B714cB45683733fD4Dc423FE6BF9A1d9F8DDD2C, + 0x5E9b31FA592913C2aD2356ed843F66057c81455D, + 0x58F1eFd2fBf415CFC0Ce30A13c36A2642690eDA0, + 0x84e47C7E20Ee8e8d6195A99788226EB488f5Fad7, + 0xCce7AcFb0d4760df15a1f87C92324B166e9C0638 + ]; + + address[] public ALL_HOLDERS_BTC = [ + 0x9D5132Dd45CdaCd1De3a7b7f1f13da7F025fF726, + 0xa7CBb758849979CEc43FD146fe01EB2BF560202C, + 0x6bA9c7d86eBD6A20817d482C42C9dE2806EeFB7e, + 0x9D0214C3dB28875fe2b9eb9cD9d4F71E7817890A, + 0x2B38f0De645E05Ec410AE86Ef479a1e42E45Bd12, + 0x8fb2FB82fF44da85E43d0A71CA992DbBC3243c5a, + 0xced306BfCB4a057aE3905063fD601774F9F18730, + 0x0C1856Cb7444cc8596c706185b54535F45C2623B, + 0x11fC7853944570C1F9D9EBE7Ac24e2FeFddf0314, + 0xb3d1aCA9bF004d0da560930Cf9277c604eDf2283, + 0xB438Dbaae78225eEfcDBB04E207A8eFB06036a2c, + 0x997b96BAD648c39226281Bc002f9857274E42A01, + 0xd52929B69680A6f74D2eB9c8F1ef482f37b1b32B, + 0x706FC1a8e457De0cf52e7679C2922aEF7F7a397e, + 0x3497818F50f79A11236B941dDAd35Da68A57864E, + 0xB35722e595F2B974c2B1D14A80Fab5d0c60c2fa3, + 0xA9f7b86FCC86EA1b913dDcaB8c9514a8e677666E, + 0x7077e395C9FA4E480366A5DC5792d2504d78dffF, + 0x664324F8B7430b4C22Dc036234Dd752A4755Fda7, + 0xD22ADba687aAd7DD9CEC3b18D970C581734783bA, + 0x5BC81274740A73D33ec8539182c326b8b58004C2, + 0x02F73B8e29dD6Ec38Bc5D8B3826051DC562c3060, + 0x4e90Da1dDCD5B0Fb295b6A69251b5D04D5bCCA7a, + 0x150a0D516C0ceFa39f980Da57d5422E91A94238b, + 0x4fb30f8CcE1F80FC9CC45F7F626069be7549aF59, + 0xF382ce5457Bba6113e082DD638b6671Cb2277B1f, + 0xb95f08B2eda7B23Ce412A6dE82eA5f5100335A3A, + 0x5b6Da8BC8696Aa4D20030151345ec652cf1eC727, + 0x0396816A361e2353a91Fde8438600a9353b34ce7, + 0xA1ebf2043e76446eFA2724bD1Ec18321776096FF, + 0x9214835f82752E0574F3828A4400C36Ba386Df82, + 0xb0DebcB643eE79f19Ef659Bd01D0FAC12d058604, + 0x3d72971b117EE8EF445D138557151f66CB0D408F, + 0x09e63186b4F94FafcCC60a42D3f77b4A10995674, + 0xc210a4D56D99E095b126664fDCE545b369ED3FD0, + 0xEC23a0C9D4107cAD33271c4F1955a21f4c041208, + 0x03adFaA573aC1a9b19D2b8F79a5aAFFb9c2A0532, + 0x6a58EBB35664A171A1B070DE48ee93278A63c168, + 0x521e6f07bDfa2fF8D072887F0ef3bB908a3D9e2a, + 0xF5AC5943D16fC865824910033B756519DC396682, + 0xBD73cF5baf12F120Ee3f6C4ad82df9a12649e578, + 0x44119A62EA645242234cAF408c4c20513E660EBb + ]; + + function setUp() public override { + super.setUp(); + + DataTypes.AaveV3EmodeParams memory params = DataTypes.AaveV3EmodeParams({emodeCategory: 3}); + + DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); + moduleExecutionData[0] = DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.DELEGATECALL, + module: address(emodeModule), + data: abi.encodeWithSelector(emodeModule.execute.selector, params) + }); + + DataTypes.AaveV3EmodeParams memory paramsBtc = DataTypes.AaveV3EmodeParams({emodeCategory: 2}); + DataTypes.ModuleExecutionData[] memory moduleExecutionDataBtc = new DataTypes.ModuleExecutionData[](1); + moduleExecutionDataBtc[0] = DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.DELEGATECALL, + module: address(emodeModule), + data: abi.encodeWithSelector(emodeModule.execute.selector, paramsBtc) + }); + + vm.startPrank(admin); + superloop.operate(moduleExecutionData); + superloopBtc.operate(moduleExecutionDataBtc); + + // register the above modules + moduleRegistry.setModule("REPAY_MODULE", REPAY_MODULE); + moduleRegistry.setModule("WITHDRAW_MODULE", WITHDRAW_MODULE); + moduleRegistry.setModule("DEPOSIT_MODULE", DEPOSIT_MODULE); + moduleRegistry.setModule("BORROW_MODULE", BORROW_MODULE); + moduleRegistry.setModule("DEX_MODULE", DEX_MODULE); + + superloop.setRegisteredModule(REPAY_MODULE, true); + superloop.setRegisteredModule(WITHDRAW_MODULE, true); + superloop.setRegisteredModule(DEPOSIT_MODULE, true); + superloop.setRegisteredModule(BORROW_MODULE, true); + superloop.setRegisteredModule(DEX_MODULE, true); + + superloopBtc.setRegisteredModule(REPAY_MODULE, true); + superloopBtc.setRegisteredModule(WITHDRAW_MODULE, true); + superloopBtc.setRegisteredModule(DEPOSIT_MODULE, true); + superloopBtc.setRegisteredModule(BORROW_MODULE, true); + superloopBtc.setRegisteredModule(DEX_MODULE, true); + vm.stopPrank(); + + vm.label(oldVault, "oldVault"); + vm.label(oldVaultBtc, "oldVaultBtc"); + vm.label(address(superloop), "newVault"); + vm.label(address(superloopBtc), "newVaultBtc"); + vm.label(REPAY_MODULE, "REPAY_MODULE_OLD"); + vm.label(WITHDRAW_MODULE, "WITHDRAW_MODULE_OLD"); + vm.label(DEPOSIT_MODULE, "DEPOSIT_MODULE_OLD"); + vm.label(BORROW_MODULE, "BORROW_MODULE_OLD"); + vm.label(DEX_MODULE, "DEX_MODULE_OLD"); + + // TODO: do deposit and withdraw for both + } + + // function test_migration() public { + // // deploy migration helper + // MigrationHelper migrationHelper = new MigrationHelper( + // AAVE_V3_POOL_ADDRESSES_PROVIDER, + // REPAY_MODULE, + // WITHDRAW_MODULE, + // DEPOSIT_MODULE, + // BORROW_MODULE, + // DEX_MODULE, + // USDC + // ); + // vm.label(address(migrationHelper), "migrationHelper"); + + // // set migration helper contract as priveledged address in old vault + // address oldVaultAdmin = ISuperloop(oldVault).vaultAdmin(); + // vm.prank(oldVaultAdmin); + // ISuperloop(oldVault).setPrivilegedAddress(address(migrationHelper), true); + + // // set migration helper contract as deposit manager and vault operator in new vault + // vm.startPrank(admin); + // superloop.setDepositManagerModule(address(migrationHelper)); + // superloop.setVaultOperator(address(migrationHelper)); + + // // set migration helper as vault in new accountant module + // accountant.setVault(address(migrationHelper)); + // accountant.transferOwnership(address(migrationHelper)); + // vm.stopPrank(); + + // address[] memory users = ALL_HOLDERS_XTZ; + + // // old balances + // uint256 oldVaultXTZBalance = IERC20(XTZ).balanceOf(oldVault); + // uint256 oldVaultSTXTZBalance = IERC20(ST_XTZ).balanceOf(oldVault); + // (uint256 oldVaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(ST_XTZ, oldVault); + // (,, uint256 oldVaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(XTZ, oldVault); + + // console.log("OLD VAULT STATE INITIAL"); + // console.log("oldVaultXTZBalance", oldVaultXTZBalance); + // console.log("oldVaultSTXTZBalance", oldVaultSTXTZBalance); + // console.log("oldVaultLendBalance", oldVaultLendBalance); + // console.log("oldVaultBorrowBalance", oldVaultBorrowBalance); + // console.log("old vault total supply", ISuperloop(oldVault).totalSupply()); + // console.log("old vault total assets", ISuperloop(oldVault).totalAssets()); + + // assertEq(Ownable(address(accountant)).owner(), address(migrationHelper)); + // assertEq(accountant.vault(), address(migrationHelper)); + + // uint256 maxSharesDelta = 100; + + // // start recording gas + // uint256 startGas = gasleft(); + // uint256 batches = 4; + // migrationHelper.migrate(oldVault, address(superloop), users, ST_XTZ, XTZ, batches, maxSharesDelta, 5); + + // console.log("migration complete"); + + // uint256 endGas = gasleft(); + // uint256 gasUsed = startGas - endGas; + // console.log("gas used", gasUsed); + // uint256 newVaultXTZBalance = IERC20(XTZ).balanceOf(address(superloop)); + // uint256 newVaultSTXTZBalance = IERC20(ST_XTZ).balanceOf(address(superloop)); + // (uint256 newVaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(ST_XTZ, address(superloop)); + // (,, uint256 newVaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(XTZ, address(superloop)); + // uint256 newVaultLastRealizedFeeExchangeRate = + // IAccountantModule(ISuperloop(address(superloop)).accountant()).lastRealizedFeeExchangeRate(); + // uint256 oldVaultLastRealizedFeeExchangeRate = + // IAccountantModule(ISuperloopLegacy(oldVault).accountantModule()).lastRealizedFeeExchangeRate(); + + // console.log("NEW VAULT STATE AFTER MIGRATION"); + // console.log("newVaultXTZBalance", newVaultXTZBalance); + // console.log("newVaultSTXTZBalance", newVaultSTXTZBalance); + // console.log("newVaultLendBalance", newVaultLendBalance); + // console.log("newVaultBorrowBalance", newVaultBorrowBalance); + // console.log("new vault total supply", ISuperloop(address(superloop)).totalSupply()); + // console.log("new vault total assets", ISuperloop(address(superloop)).totalAssets()); + // console.log( + // "new vault last realized fee exchange rate", + // IAccountantModule(ISuperloop(address(superloop)).accountant()).lastRealizedFeeExchangeRate() + // ); + + // // assert balances of each of the users are the same + + // for (uint256 i = 0; i < users.length; i++) { + // assertEq(ISuperloop(address(superloop)).balanceOf(users[i]), ISuperloop(oldVault).balanceOf(users[i])); + // } + + // assertEq(newVaultLastRealizedFeeExchangeRate, oldVaultLastRealizedFeeExchangeRate); + // assertEq(Ownable(address(accountant)).owner(), address(admin)); + // assertEq(accountant.vault(), address(superloop)); + + // oldVaultXTZBalance = IERC20(XTZ).balanceOf(oldVault); + // oldVaultSTXTZBalance = IERC20(ST_XTZ).balanceOf(oldVault); + // (oldVaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(ST_XTZ, oldVault); + // (,, oldVaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(XTZ, oldVault); + + // console.log("OLD VAULT STATE AFTER MIGRATION"); + // console.log("oldVaultXTZBalance", oldVaultXTZBalance); + // console.log("oldVaultSTXTZBalance", oldVaultSTXTZBalance); + // console.log("oldVaultLendBalance", oldVaultLendBalance); + // console.log("oldVaultBorrowBalance", oldVaultBorrowBalance); + // console.log("old vault total supply", ISuperloop(oldVault).totalSupply()); + // console.log("old vault total assets", ISuperloop(oldVault).totalAssets()); + // console.log( + // "old vault last realized fee exchange rate", + // IAccountantModule(ISuperloopLegacy(oldVault).accountantModule()).lastRealizedFeeExchangeRate() + // ); + // } + + function test_migrationBtc() public { + // deploy migration helper + MigrationHelper migrationHelper = new MigrationHelper( + environment.poolAddressesProvider, + REPAY_MODULE, + WITHDRAW_MODULE, + DEPOSIT_MODULE, + BORROW_MODULE, + DEX_MODULE, + environment.stablecoin + ); + vm.label(address(migrationHelper), "migrationHelper"); + + // set migration helper contract as priveledged address in old vault + address oldVaultAdmin = ISuperloop(oldVaultBtc).vaultAdmin(); + vm.prank(oldVaultAdmin); + ISuperloop(oldVaultBtc).setPrivilegedAddress(address(migrationHelper), true); + + // set migration helper contract as deposit manager and vault operator in new vault + vm.startPrank(admin); + superloopBtc.setDepositManagerModule(address(migrationHelper)); + superloopBtc.setVaultOperator(address(migrationHelper)); + + // set migration helper as vault in new accountant module + accountantBtc.setVault(address(migrationHelper)); + accountantBtc.transferOwnership(address(migrationHelper)); + vm.stopPrank(); + + address[] memory users = ALL_HOLDERS_BTC; + + // old balances + uint256 oldVaultBTCBalance = IERC20(WBTC).balanceOf(oldVaultBtc); + uint256 oldVaultLBTCBalance = IERC20(LBTC).balanceOf(oldVaultBtc); + (uint256 oldVaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(LBTC, oldVaultBtc); + (,, uint256 oldVaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(WBTC, oldVaultBtc); + + console.log("OLD VAULT STATE INITIAL"); + console.log("oldVaultBTCBalance", oldVaultBTCBalance); + console.log("oldVaultLBTCBalance", oldVaultLBTCBalance); + console.log("oldVaultLendBalance", oldVaultLendBalance); + console.log("oldVaultBorrowBalance", oldVaultBorrowBalance); + console.log("old vault total supply", ISuperloop(oldVaultBtc).totalSupply()); + console.log("old vault total assets", ISuperloop(oldVaultBtc).totalAssets()); + + assertEq(Ownable(address(accountantBtc)).owner(), address(migrationHelper)); + assertEq(accountantBtc.vault(), address(migrationHelper)); + + uint256 maxSharesDelta = 100; + + // start recording gas + uint256 startGas = gasleft(); + uint256 batches = 4; + migrationHelper.migrate(oldVaultBtc, address(superloopBtc), users, LBTC, WBTC, batches, maxSharesDelta, 5); + + console.log("migration complete"); + + uint256 endGas = gasleft(); + uint256 gasUsed = startGas - endGas; + console.log("gas used", gasUsed); + uint256 newVaultBTCBalance = IERC20(WBTC).balanceOf(address(superloopBtc)); + uint256 newVaultLBTCBalance = IERC20(LBTC).balanceOf(address(superloopBtc)); + (uint256 newVaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(LBTC, address(superloopBtc)); + (,, uint256 newVaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(WBTC, address(superloopBtc)); + uint256 newVaultLastRealizedFeeExchangeRate = + IAccountantModule(ISuperloop(address(superloopBtc)).accountant()).lastRealizedFeeExchangeRate(); + uint256 oldVaultLastRealizedFeeExchangeRate = + IAccountantModule(ISuperloopLegacy(oldVaultBtc).accountantModule()).lastRealizedFeeExchangeRate(); + + console.log("NEW VAULT STATE AFTER MIGRATION"); + console.log("newVaultXTZBalance", newVaultBTCBalance); + console.log("newVaultLBTCBalance", newVaultLBTCBalance); + console.log("newVaultLendBalance", newVaultLendBalance); + console.log("newVaultBorrowBalance", newVaultBorrowBalance); + console.log("new vault total supply", ISuperloop(address(superloopBtc)).totalSupply()); + console.log("new vault total assets", ISuperloop(address(superloopBtc)).totalAssets()); + console.log( + "new vault last realized fee exchange rate", + IAccountantModule(ISuperloop(address(superloop)).accountant()).lastRealizedFeeExchangeRate() + ); + + // assert balances of each of the users are the same + + for (uint256 i = 0; i < users.length; i++) { + assertEq(ISuperloop(address(superloopBtc)).balanceOf(users[i]), ISuperloop(oldVaultBtc).balanceOf(users[i])); + } + + assertEq(newVaultLastRealizedFeeExchangeRate, oldVaultLastRealizedFeeExchangeRate); + assertEq(Ownable(address(accountantBtc)).owner(), address(admin)); + assertEq(accountantBtc.vault(), address(superloopBtc)); + + console.log("gas used", gasUsed); + + oldVaultBTCBalance = IERC20(WBTC).balanceOf(oldVaultBtc); + oldVaultLBTCBalance = IERC20(LBTC).balanceOf(oldVaultBtc); + (oldVaultLendBalance,,,,,,,,) = poolDataProvider.getUserReserveData(LBTC, oldVaultBtc); + (,, oldVaultBorrowBalance,,,,,,) = poolDataProvider.getUserReserveData(WBTC, oldVaultBtc); + + console.log("OLD VAULT STATE AFTER MIGRATION"); + console.log("oldVaultBTCBalance", oldVaultBTCBalance); + console.log("oldVaultLBTCBalance", oldVaultLBTCBalance); + console.log("oldVaultLendBalance", oldVaultLendBalance); + console.log("oldVaultBorrowBalance", oldVaultBorrowBalance); + console.log("old vault total supply", ISuperloop(oldVaultBtc).totalSupply()); + console.log("old vault total assets", ISuperloop(oldVaultBtc).totalAssets()); + console.log( + "old vault last realized fee exchange rate", + IAccountantModule(ISuperloopLegacy(oldVaultBtc).accountantModule()).lastRealizedFeeExchangeRate() + ); + } +} diff --git a/test/core/integration/UniversalAccountant.t.sol b/test/core/integration/UniversalAccountant.t.sol index ae48a6f..e035644 100644 --- a/test/core/integration/UniversalAccountant.t.sol +++ b/test/core/integration/UniversalAccountant.t.sol @@ -4,8 +4,9 @@ pragma solidity ^0.8.13; import {TestBase} from "../TestBase.sol"; import {UniversalAccountant} from "../../../src/core/Accountant/universalAccountant/UniversalAccountant.sol"; import {AaveV3AccountantPlugin} from "../../../src/plugins/Accountant/AaveV3AccountantPlugin.sol"; -import {TransparentUpgradeableProxy} from - "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import { + TransparentUpgradeableProxy +} from "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {DataTypes} from "../../../src/common/DataTypes.sol"; import {console} from "forge-std/console.sol"; import {MockVault} from "../../../src/mock/MockVault.sol"; @@ -37,12 +38,12 @@ contract UniversalAccountantTest is TestBase { vault = new MockVault(IERC20(environment.vaultAsset), "Mock Vault", "mVLT"); // deploy accountant plugin - DataTypes.AaveV3AccountantPluginModuleInitData memory accountantPluginInitData = DataTypes - .AaveV3AccountantPluginModuleInitData({ - poolAddressesProvider: environment.poolAddressesProvider, - lendAssets: environment.lendAssets, - borrowAssets: environment.borrowAssets - }); + DataTypes.AaveV3AccountantPluginModuleInitData memory accountantPluginInitData = + DataTypes.AaveV3AccountantPluginModuleInitData({ + poolAddressesProvider: environment.poolAddressesProvider, + lendAssets: environment.lendAssets, + borrowAssets: environment.borrowAssets + }); accountantPlugin = new AaveV3AccountantPlugin(accountantPluginInitData); address[] memory registeredAccountants = new address[](1); @@ -50,9 +51,7 @@ contract UniversalAccountantTest is TestBase { // deploy accountant DataTypes.UniversalAccountantModuleInitData memory initData = DataTypes.UniversalAccountantModuleInitData({ - registeredAccountants: registeredAccountants, - performanceFee: uint16(PERFORMANCE_FEE), - vault: address(vault) + registeredAccountants: registeredAccountants, performanceFee: uint16(PERFORMANCE_FEE), vault: address(vault) }); accountantImplementation = new UniversalAccountant(); @@ -78,22 +77,19 @@ contract UniversalAccountantTest is TestBase { // create borrow positions for (uint256 i = 0; i < environment.borrowAssets.length; i++) { uint8 decimals = IERC20Metadata(environment.borrowAssets[i]).decimals(); - IPool(environment.pool).borrow( - environment.borrowAssets[i], 500 * 10 ** decimals, INTEREST_RATE_MODE, 0, address(vault) - ); + IPool(environment.pool) + .borrow(environment.borrowAssets[i], 500 * 10 ** decimals, INTEREST_RATE_MODE, 0, address(vault)); } vm.stopPrank(); for (uint256 i = 0; i < environment.lendAssets.length; i++) { - (uint256 currentSupply,,,,,,,,) = IPoolDataProvider(environment.poolDataProvider).getUserReserveData( - environment.lendAssets[i], address(vault) - ); + (uint256 currentSupply,,,,,,,,) = IPoolDataProvider(environment.poolDataProvider) + .getUserReserveData(environment.lendAssets[i], address(vault)); assertApproxEqAbs(currentSupply, 1000 * 10 ** IERC20Metadata(environment.lendAssets[i]).decimals(), 2); } for (uint256 i = 0; i < environment.borrowAssets.length; i++) { - (,, uint256 currentBorrow,,,,,,) = IPoolDataProvider(environment.poolDataProvider).getUserReserveData( - environment.borrowAssets[i], address(vault) - ); + (,, uint256 currentBorrow,,,,,,) = IPoolDataProvider(environment.poolDataProvider) + .getUserReserveData(environment.borrowAssets[i], address(vault)); assertApproxEqAbs(currentBorrow, 500 * 10 ** IERC20Metadata(environment.borrowAssets[i]).decimals(), 2); } diff --git a/test/core/integration/WithdrawManager.t.sol b/test/core/integration/WithdrawManager.t.sol index 1214d4e..269da96 100644 --- a/test/core/integration/WithdrawManager.t.sol +++ b/test/core/integration/WithdrawManager.t.sol @@ -80,7 +80,9 @@ contract WithdrawManagerTest is IntegrationBase { USDC_USDE_POOL_FEE, block.timestamp + 100 ) - : _swapCallExactOutCurve(ST_XTZ, XTZ, XTZ_STXTZ_POOL, withdrawAmount, repayAmountWithPremium, STXTZ_XTZ_SWAP); + : _swapCallExactOutCurve( + ST_XTZ, XTZ, XTZ_STXTZ_POOL, withdrawAmount, repayAmountWithPremium, STXTZ_XTZ_SWAP + ); DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); intermediateExecutionData[0] = USE_MORPHO @@ -169,7 +171,9 @@ contract WithdrawManagerTest is IntegrationBase { USDC_USDE_POOL_FEE, block.timestamp + 100 ) - : _swapCallExactOutCurve(ST_XTZ, XTZ, XTZ_STXTZ_POOL, withdrawAmount, repayAmountWithPremium, STXTZ_XTZ_SWAP); + : _swapCallExactOutCurve( + ST_XTZ, XTZ, XTZ_STXTZ_POOL, withdrawAmount, repayAmountWithPremium, STXTZ_XTZ_SWAP + ); DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); intermediateExecutionData[0] = USE_MORPHO @@ -375,7 +379,9 @@ contract WithdrawManagerTest is IntegrationBase { USDC_USDE_POOL_FEE, block.timestamp + 100 ) - : _swapCallExactOutCurve(ST_XTZ, XTZ, XTZ_STXTZ_POOL, withdrawAmount, repayAmountWithPremium, STXTZ_XTZ_SWAP); + : _swapCallExactOutCurve( + ST_XTZ, XTZ, XTZ_STXTZ_POOL, withdrawAmount, repayAmountWithPremium, STXTZ_XTZ_SWAP + ); DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); intermediateExecutionData[0] = USE_MORPHO diff --git a/test/core/unit/DepositManager.t.sol b/test/core/unit/DepositManager.t.sol index c7dbc53..b3cf221 100644 --- a/test/core/unit/DepositManager.t.sol +++ b/test/core/unit/DepositManager.t.sol @@ -6,8 +6,9 @@ import {TestBase} from "../TestBase.sol"; import {console} from "forge-std/console.sol"; import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; -import {TransparentUpgradeableProxy} from - "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import { + TransparentUpgradeableProxy +} from "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {DataTypes} from "../../../src/common/DataTypes.sol"; import {Errors} from "../../../src/common/Errors.sol"; import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; diff --git a/test/core/unit/UniversalAccountant.t.sol b/test/core/unit/UniversalAccountant.t.sol index 2d81e03..d738b1c 100644 --- a/test/core/unit/UniversalAccountant.t.sol +++ b/test/core/unit/UniversalAccountant.t.sol @@ -34,12 +34,12 @@ contract AccountantAaveV3Test is TestBase { vault = new MockVault(asset, "Mock Vault", "mVLT"); // deploy accountant plugin - DataTypes.AaveV3AccountantPluginModuleInitData memory accountantPluginInitData = DataTypes - .AaveV3AccountantPluginModuleInitData({ - poolAddressesProvider: environment.poolAddressesProvider, - lendAssets: environment.lendAssets, - borrowAssets: environment.borrowAssets - }); + DataTypes.AaveV3AccountantPluginModuleInitData memory accountantPluginInitData = + DataTypes.AaveV3AccountantPluginModuleInitData({ + poolAddressesProvider: environment.poolAddressesProvider, + lendAssets: environment.lendAssets, + borrowAssets: environment.borrowAssets + }); accountantPlugin = new AaveV3AccountantPlugin(accountantPluginInitData); address[] memory registeredAccountants = new address[](1); @@ -47,9 +47,7 @@ contract AccountantAaveV3Test is TestBase { // deploy accountant DataTypes.UniversalAccountantModuleInitData memory initData = DataTypes.UniversalAccountantModuleInitData({ - registeredAccountants: registeredAccountants, - performanceFee: uint16(PERFORMANCE_FEE), - vault: address(vault) + registeredAccountants: registeredAccountants, performanceFee: uint16(PERFORMANCE_FEE), vault: address(vault) }); accountantImplementation = new UniversalAccountant(); @@ -80,9 +78,7 @@ contract AccountantAaveV3Test is TestBase { registeredAccountants[0] = address(accountantPlugin); DataTypes.UniversalAccountantModuleInitData memory initData = DataTypes.UniversalAccountantModuleInitData({ - registeredAccountants: registeredAccountants, - performanceFee: uint16(PERFORMANCE_FEE), - vault: address(vault) + registeredAccountants: registeredAccountants, performanceFee: uint16(PERFORMANCE_FEE), vault: address(vault) }); vm.expectRevert(); diff --git a/test/core/unit/WithdrawManager.t.sol b/test/core/unit/WithdrawManager.t.sol index 691a896..6801b8a 100644 --- a/test/core/unit/WithdrawManager.t.sol +++ b/test/core/unit/WithdrawManager.t.sol @@ -6,8 +6,9 @@ import {TestBase} from "../TestBase.sol"; import {console} from "forge-std/console.sol"; import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; -import {TransparentUpgradeableProxy} from - "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import { + TransparentUpgradeableProxy +} from "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {DataTypes} from "../../../src/common/DataTypes.sol"; import {Errors} from "../../../src/common/Errors.sol"; import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; @@ -347,9 +348,7 @@ contract WithdrawManagerTest is TestBase { function test_resolveWithdrawRequests_OnlyVault() public { DataTypes.ResolveWithdrawRequestsData memory data = DataTypes.ResolveWithdrawRequestsData({ - shares: 1000 * 10 ** 18, - requestType: DataTypes.WithdrawRequestType.GENERAL, - callbackExecutionData: "" + shares: 1000 * 10 ** 18, requestType: DataTypes.WithdrawRequestType.GENERAL, callbackExecutionData: "" }); vm.startPrank(user1); @@ -360,9 +359,7 @@ contract WithdrawManagerTest is TestBase { function test_resolveWithdrawRequests_ZeroShares() public { DataTypes.ResolveWithdrawRequestsData memory data = DataTypes.ResolveWithdrawRequestsData({ - shares: 0, - requestType: DataTypes.WithdrawRequestType.GENERAL, - callbackExecutionData: "" + shares: 0, requestType: DataTypes.WithdrawRequestType.GENERAL, callbackExecutionData: "" }); vm.startPrank(address(superloop)); @@ -373,9 +370,7 @@ contract WithdrawManagerTest is TestBase { function test_resolveWithdrawRequests_ExceedsPending() public { DataTypes.ResolveWithdrawRequestsData memory data = DataTypes.ResolveWithdrawRequestsData({ - shares: 1000 * 10 ** 18, - requestType: DataTypes.WithdrawRequestType.GENERAL, - callbackExecutionData: "" + shares: 1000 * 10 ** 18, requestType: DataTypes.WithdrawRequestType.GENERAL, callbackExecutionData: "" }); vm.startPrank(address(superloop)); diff --git a/test/helpers/VaultRouter.t.sol b/test/helpers/VaultRouter.t.sol index 046d478..bd9a646 100644 --- a/test/helpers/VaultRouter.t.sol +++ b/test/helpers/VaultRouter.t.sol @@ -1,4 +1,4 @@ - // TODO : Update tests for periphery contract +// TODO : Update tests for periphery contract // SPDX-License-Identifier: MIT // pragma solidity ^0.8.13; diff --git a/test/helpers/integration/VaultRouter.t.sol b/test/helpers/integration/VaultRouter.t.sol index 09f34cc..083e305 100644 --- a/test/helpers/integration/VaultRouter.t.sol +++ b/test/helpers/integration/VaultRouter.t.sol @@ -1,4 +1,4 @@ - // TODO : Update tests for periphery contract +// TODO : Update tests for periphery contract // SPDX-License-Identifier: MIT diff --git a/test/modules/aave/AaveV3BorrowModule.t.sol b/test/modules/aave/AaveV3BorrowModule.t.sol index 7b26cbc..9e2c590 100644 --- a/test/modules/aave/AaveV3BorrowModule.t.sol +++ b/test/modules/aave/AaveV3BorrowModule.t.sol @@ -7,8 +7,9 @@ import {IFlashLoanSimpleReceiver} from "aave-v3-core/contracts/flashloan/interfa import {DataTypes} from "../../../src/common/DataTypes.sol"; import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; -import {TransparentUpgradeableProxy} from - "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import { + TransparentUpgradeableProxy +} from "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import {IPoolConfigurator} from "aave-v3-core/contracts/interfaces/IPoolConfigurator.sol"; import {IERC20Metadata} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol"; diff --git a/test/modules/aave/AaveV3EmodeModule.t.sol b/test/modules/aave/AaveV3EmodeModule.t.sol index 0c183c8..34ba553 100644 --- a/test/modules/aave/AaveV3EmodeModule.t.sol +++ b/test/modules/aave/AaveV3EmodeModule.t.sol @@ -5,8 +5,9 @@ pragma solidity ^0.8.13; import {TestBase} from "../../core/TestBase.sol"; import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; -import {TransparentUpgradeableProxy} from - "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import { + TransparentUpgradeableProxy +} from "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import {DataTypes} from "../../../src/common/DataTypes.sol"; import {console} from "forge-std/console.sol"; diff --git a/test/modules/aave/AaveV3FlashloanModule.t.sol b/test/modules/aave/AaveV3FlashloanModule.t.sol index 0061907..e48f6ec 100644 --- a/test/modules/aave/AaveV3FlashloanModule.t.sol +++ b/test/modules/aave/AaveV3FlashloanModule.t.sol @@ -7,8 +7,9 @@ import {IFlashLoanSimpleReceiver} from "aave-v3-core/contracts/flashloan/interfa import {DataTypes} from "../../../src/common/DataTypes.sol"; import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; -import {TransparentUpgradeableProxy} from - "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import { + TransparentUpgradeableProxy +} from "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; contract AaveV3FlashloanModuleTest is TestBase { diff --git a/test/modules/aave/AaveV3RepayModule.t.sol b/test/modules/aave/AaveV3RepayModule.t.sol index 847e1bc..7a0c2cf 100644 --- a/test/modules/aave/AaveV3RepayModule.t.sol +++ b/test/modules/aave/AaveV3RepayModule.t.sol @@ -7,8 +7,9 @@ import {IFlashLoanSimpleReceiver} from "aave-v3-core/contracts/flashloan/interfa import {DataTypes} from "../../../src/common/DataTypes.sol"; import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; -import {TransparentUpgradeableProxy} from - "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import { + TransparentUpgradeableProxy +} from "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import {IPoolConfigurator} from "aave-v3-core/contracts/interfaces/IPoolConfigurator.sol"; import {IERC20Metadata} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol"; diff --git a/test/modules/aave/AaveV3SupplyModule.t.sol b/test/modules/aave/AaveV3SupplyModule.t.sol index 37c2caf..aa9a1b4 100644 --- a/test/modules/aave/AaveV3SupplyModule.t.sol +++ b/test/modules/aave/AaveV3SupplyModule.t.sol @@ -7,8 +7,9 @@ import {IFlashLoanSimpleReceiver} from "aave-v3-core/contracts/flashloan/interfa import {DataTypes} from "../../../src/common/DataTypes.sol"; import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; -import {TransparentUpgradeableProxy} from - "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import { + TransparentUpgradeableProxy +} from "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import {console} from "forge-std/console.sol"; diff --git a/test/modules/aave/AaveV3WithdrawModule.t.sol b/test/modules/aave/AaveV3WithdrawModule.t.sol index e75a9fe..c136d43 100644 --- a/test/modules/aave/AaveV3WithdrawModule.t.sol +++ b/test/modules/aave/AaveV3WithdrawModule.t.sol @@ -7,8 +7,9 @@ import {IFlashLoanSimpleReceiver} from "aave-v3-core/contracts/flashloan/interfa import {DataTypes} from "../../../src/common/DataTypes.sol"; import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; -import {TransparentUpgradeableProxy} from - "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import { + TransparentUpgradeableProxy +} from "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import {console} from "forge-std/console.sol"; diff --git a/test/modules/dex/UniversalDexModule.t.sol b/test/modules/dex/UniversalDexModule.t.sol index 14fc9ec..853d4d3 100644 --- a/test/modules/dex/UniversalDexModule.t.sol +++ b/test/modules/dex/UniversalDexModule.t.sol @@ -9,8 +9,9 @@ import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import {TestBase} from "../../core/TestBase.sol"; import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; -import {TransparentUpgradeableProxy} from - "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import { + TransparentUpgradeableProxy +} from "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {IRouter} from "../../../src/mock/MockIRouter.sol"; // not maintained, may fail @@ -71,8 +72,7 @@ contract UniversalDexModuleTest is TestBase { DataTypes.ExecuteSwapParamsData[] memory swapParamsData = new DataTypes.ExecuteSwapParamsData[](2); swapParamsData[0] = DataTypes.ExecuteSwapParamsData({ - target: tokenIn, - data: abi.encodeWithSelector(IERC20.approve.selector, environment.router, amountIn) + target: tokenIn, data: abi.encodeWithSelector(IERC20.approve.selector, environment.router, amountIn) }); swapParamsData[1] = DataTypes.ExecuteSwapParamsData({ target: environment.router, @@ -133,8 +133,7 @@ contract UniversalDexModuleTest is TestBase { DataTypes.ExecuteSwapParamsData[] memory data = new DataTypes.ExecuteSwapParamsData[](2); data[0] = DataTypes.ExecuteSwapParamsData({ - target: tokenIn, - data: abi.encodeWithSelector(IERC20.approve.selector, environment.router, amountIn) + target: tokenIn, data: abi.encodeWithSelector(IERC20.approve.selector, environment.router, amountIn) }); data[1] = DataTypes.ExecuteSwapParamsData({ target: environment.router, diff --git a/test/modules/fallback/AaveV3PreliquidationFallbackHandler.t.sol b/test/modules/fallback/AaveV3PreliquidationFallbackHandler.t.sol index 172e08f..6de8175 100644 --- a/test/modules/fallback/AaveV3PreliquidationFallbackHandler.t.sol +++ b/test/modules/fallback/AaveV3PreliquidationFallbackHandler.t.sol @@ -2,8 +2,9 @@ pragma solidity ^0.8.13; import {TestBase} from "../../core/TestBase.sol"; -import {AaveV3PreliquidationFallbackHandler} from - "../../../src/modules/fallback/AaveV3PreliquidationFallbackHandler.sol"; +import { + AaveV3PreliquidationFallbackHandler +} from "../../../src/modules/fallback/AaveV3PreliquidationFallbackHandler.sol"; import {DataTypes} from "../../../src/common/DataTypes.sol"; import {Errors} from "../../../src/common/Errors.sol"; import {console} from "forge-std/console.sol"; @@ -65,9 +66,8 @@ contract AaveV3PreliquidationFallbackHandlerTest is TestBase { ); vm.startPrank(address(preliquidation)); - IERC20(environment.lendAssets[0]).approve( - address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() - ); + IERC20(environment.lendAssets[0]) + .approve(address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals()); pool.supply( environment.lendAssets[0], 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals(), @@ -100,9 +100,8 @@ contract AaveV3PreliquidationFallbackHandlerTest is TestBase { uint256 debtRepaidUSD = (debtRepaid * debtPriceUsd) / (10 ** IERC20Metadata(environment.borrowAssets[0]).decimals()); - IERC20(environment.borrowAssets[0]).approve( - address(preliquidation), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() - ); + IERC20(environment.borrowAssets[0]) + .approve(address(preliquidation), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals()); preliquidation.preliquidate( id, DataTypes.CallType.DELEGATECALL, @@ -140,9 +139,8 @@ contract AaveV3PreliquidationFallbackHandlerTest is TestBase { ); vm.startPrank(address(preliquidation)); - IERC20(environment.lendAssets[0]).approve( - address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() - ); + IERC20(environment.lendAssets[0]) + .approve(address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals()); pool.supply( environment.lendAssets[0], 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals(), @@ -166,13 +164,11 @@ contract AaveV3PreliquidationFallbackHandlerTest is TestBase { // updat emode ltv to 9900 => it should revert vm.prank(environment.poolAdmin); - IPoolConfigurator(environment.poolConfigurator).configureReserveAsCollateral( - environment.lendAssets[0], 9000, 9200, 10100 - ); + IPoolConfigurator(environment.poolConfigurator) + .configureReserveAsCollateral(environment.lendAssets[0], 9000, 9200, 10100); - IERC20(environment.borrowAssets[0]).approve( - address(preliquidation), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() - ); + IERC20(environment.borrowAssets[0]) + .approve(address(preliquidation), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals()); vm.expectRevert(bytes(Errors.AAVE_V3_PRELIQUIDATION_INVALID_LLTV)); preliquidation.preliquidate( @@ -195,9 +191,8 @@ contract AaveV3PreliquidationFallbackHandlerTest is TestBase { ); vm.startPrank(address(preliquidation)); - IERC20(environment.lendAssets[0]).approve( - address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() - ); + IERC20(environment.lendAssets[0]) + .approve(address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals()); pool.supply( environment.lendAssets[0], 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals(), @@ -218,9 +213,8 @@ contract AaveV3PreliquidationFallbackHandlerTest is TestBase { address(this), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() ); - IERC20(environment.borrowAssets[0]).approve( - address(preliquidation), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() - ); + IERC20(environment.borrowAssets[0]) + .approve(address(preliquidation), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals()); bytes32 wrongId = bytes32("wrong_id"); vm.expectRevert(bytes(Errors.PRELIQUIDATION_INVALID_ID)); @@ -244,9 +238,8 @@ contract AaveV3PreliquidationFallbackHandlerTest is TestBase { ); vm.startPrank(address(preliquidation)); - IERC20(environment.lendAssets[0]).approve( - address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() - ); + IERC20(environment.lendAssets[0]) + .approve(address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals()); pool.supply( environment.lendAssets[0], 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals(), @@ -267,9 +260,8 @@ contract AaveV3PreliquidationFallbackHandlerTest is TestBase { address(this), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() ); - IERC20(environment.borrowAssets[0]).approve( - address(preliquidation), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() - ); + IERC20(environment.borrowAssets[0]) + .approve(address(preliquidation), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals()); address wrongUser = makeAddr("wrongUser"); vm.expectRevert(bytes(Errors.PRELIQUIDATION_INVALID_USER)); @@ -294,9 +286,8 @@ contract AaveV3PreliquidationFallbackHandlerTest is TestBase { ); // High borrow amount vm.startPrank(address(preliquidation)); - IERC20(environment.lendAssets[0]).approve( - address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() - ); + IERC20(environment.lendAssets[0]) + .approve(address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals()); pool.supply( environment.lendAssets[0], 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals(), @@ -328,9 +319,8 @@ contract AaveV3PreliquidationFallbackHandlerTest is TestBase { ); // Low borrow amount vm.startPrank(address(preliquidation)); - IERC20(environment.lendAssets[0]).approve( - address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() - ); + IERC20(environment.lendAssets[0]) + .approve(address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals()); pool.supply( environment.lendAssets[0], 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals(), @@ -351,9 +341,8 @@ contract AaveV3PreliquidationFallbackHandlerTest is TestBase { address(this), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() ); - IERC20(environment.borrowAssets[0]).approve( - address(preliquidation), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() - ); + IERC20(environment.borrowAssets[0]) + .approve(address(preliquidation), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals()); vm.expectRevert(bytes(Errors.PRELIQUIDATION_NOT_IN_PRELIQUIDATION_STATE)); preliquidation.preliquidate( @@ -381,9 +370,8 @@ contract AaveV3PreliquidationFallbackHandlerTest is TestBase { deal(environment.borrowAssets[0], address(preliquidation), borrowAmount); vm.startPrank(address(preliquidation)); - IERC20(environment.lendAssets[0]).approve( - address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() - ); + IERC20(environment.lendAssets[0]) + .approve(address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals()); pool.supply( environment.lendAssets[0], 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals(), @@ -398,9 +386,8 @@ contract AaveV3PreliquidationFallbackHandlerTest is TestBase { address(this), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() ); - IERC20(environment.borrowAssets[0]).approve( - address(preliquidation), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() - ); + IERC20(environment.borrowAssets[0]) + .approve(address(preliquidation), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals()); // This should revert because LTV <= preLltv vm.expectRevert(bytes(Errors.PRELIQUIDATION_NOT_IN_PRELIQUIDATION_STATE)); @@ -425,9 +412,8 @@ contract AaveV3PreliquidationFallbackHandlerTest is TestBase { deal(environment.borrowAssets[0], address(preliquidation), borrowAmount); vm.startPrank(address(preliquidation)); - IERC20(environment.lendAssets[0]).approve( - address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() - ); + IERC20(environment.lendAssets[0]) + .approve(address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals()); pool.supply( environment.lendAssets[0], 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals(), @@ -442,9 +428,8 @@ contract AaveV3PreliquidationFallbackHandlerTest is TestBase { address(this), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() ); - IERC20(environment.borrowAssets[0]).approve( - address(preliquidation), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() - ); + IERC20(environment.borrowAssets[0]) + .approve(address(preliquidation), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals()); // This should succeed as it's exactly at the boundary preliquidation.preliquidate( @@ -467,9 +452,8 @@ contract AaveV3PreliquidationFallbackHandlerTest is TestBase { ); vm.startPrank(address(preliquidation)); - IERC20(environment.lendAssets[0]).approve( - address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() - ); + IERC20(environment.lendAssets[0]) + .approve(address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals()); pool.supply( environment.lendAssets[0], 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals(), @@ -490,9 +474,8 @@ contract AaveV3PreliquidationFallbackHandlerTest is TestBase { uint256 borrowAssetBalanceBefore = IERC20(environment.borrowAssets[0]).balanceOf(address(this)); uint256 lendAssetBalanceBefore = IERC20(environment.lendAssets[0]).balanceOf(address(this)); - IERC20(environment.borrowAssets[0]).approve( - address(preliquidation), 70 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() - ); + IERC20(environment.borrowAssets[0]) + .approve(address(preliquidation), 70 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals()); preliquidation.preliquidate( id, DataTypes.CallType.DELEGATECALL, @@ -525,9 +508,8 @@ contract AaveV3PreliquidationFallbackHandlerTest is TestBase { deal(environment.borrowAssets[0], address(preliquidation), borrowAmount); vm.startPrank(address(preliquidation)); - IERC20(environment.lendAssets[0]).approve( - address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() - ); + IERC20(environment.lendAssets[0]) + .approve(address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals()); pool.supply( environment.lendAssets[0], 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals(), @@ -542,9 +524,8 @@ contract AaveV3PreliquidationFallbackHandlerTest is TestBase { address(this), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() ); - IERC20(environment.borrowAssets[0]).approve( - address(preliquidation), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() - ); + IERC20(environment.borrowAssets[0]) + .approve(address(preliquidation), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals()); preliquidation.preliquidate( id, @@ -565,9 +546,8 @@ contract AaveV3PreliquidationFallbackHandlerTest is TestBase { deal(environment.borrowAssets[0], address(preliquidation), borrowAmount); vm.startPrank(address(preliquidation)); - IERC20(environment.lendAssets[0]).approve( - address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() - ); + IERC20(environment.lendAssets[0]) + .approve(address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals()); pool.supply( environment.lendAssets[0], 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals(), @@ -582,9 +562,8 @@ contract AaveV3PreliquidationFallbackHandlerTest is TestBase { address(this), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() ); - IERC20(environment.borrowAssets[0]).approve( - address(preliquidation), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() - ); + IERC20(environment.borrowAssets[0]) + .approve(address(preliquidation), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals()); preliquidation.preliquidate( id, @@ -605,9 +584,8 @@ contract AaveV3PreliquidationFallbackHandlerTest is TestBase { deal(environment.borrowAssets[0], address(preliquidation), borrowAmount); vm.startPrank(address(preliquidation)); - IERC20(environment.lendAssets[0]).approve( - address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals() - ); + IERC20(environment.lendAssets[0]) + .approve(address(pool), 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals()); pool.supply( environment.lendAssets[0], 100 * 10 ** IERC20Metadata(environment.lendAssets[0]).decimals(), @@ -622,9 +600,8 @@ contract AaveV3PreliquidationFallbackHandlerTest is TestBase { address(this), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() ); - IERC20(environment.borrowAssets[0]).approve( - address(preliquidation), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals() - ); + IERC20(environment.borrowAssets[0]) + .approve(address(preliquidation), 12 * 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals()); preliquidation.preliquidate( id, diff --git a/test/modules/helpers/UnwrapModule.t.sol b/test/modules/helpers/UnwrapModule.t.sol index 6b0df59..952e2bb 100644 --- a/test/modules/helpers/UnwrapModule.t.sol +++ b/test/modules/helpers/UnwrapModule.t.sol @@ -7,8 +7,9 @@ import {IFlashLoanSimpleReceiver} from "aave-v3-core/contracts/flashloan/interfa import {DataTypes} from "../../../src/common/DataTypes.sol"; import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; -import {TransparentUpgradeableProxy} from - "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import { + TransparentUpgradeableProxy +} from "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import {console} from "forge-std/console.sol"; diff --git a/test/modules/helpers/WrapModule.t.sol b/test/modules/helpers/WrapModule.t.sol index 8b2dc06..754c1c2 100644 --- a/test/modules/helpers/WrapModule.t.sol +++ b/test/modules/helpers/WrapModule.t.sol @@ -7,8 +7,9 @@ import {IFlashLoanSimpleReceiver} from "aave-v3-core/contracts/flashloan/interfa import {DataTypes} from "../../../src/common/DataTypes.sol"; import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; -import {TransparentUpgradeableProxy} from - "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import { + TransparentUpgradeableProxy +} from "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import {console} from "forge-std/console.sol"; diff --git a/test/modules/morpho/MorphoFlashloanModule.t.sol b/test/modules/morpho/MorphoFlashloanModule.t.sol index 0953f04..3b98edc 100644 --- a/test/modules/morpho/MorphoFlashloanModule.t.sol +++ b/test/modules/morpho/MorphoFlashloanModule.t.sol @@ -6,8 +6,9 @@ import {TestBase} from "../../core/TestBase.sol"; import {DataTypes} from "../../../src/common/DataTypes.sol"; import {Superloop} from "../../../src/core/Superloop/Superloop.sol"; import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; -import {TransparentUpgradeableProxy} from - "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import { + TransparentUpgradeableProxy +} from "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import {IMorphoFlashLoanCallback} from "morpho-blue/interfaces/IMorphoCallbacks.sol"; import {MorphoFlashloanModule} from "../../../src/modules/morpho/MorphoFlashloanModule.sol"; diff --git a/test/modules/stake/hyperliquid/HyperbeatStakingModule.t.sol b/test/modules/stake/hyperliquid/HyperbeatStakingModule.t.sol index c860d1e..d202bf2 100644 --- a/test/modules/stake/hyperliquid/HyperbeatStakingModule.t.sol +++ b/test/modules/stake/hyperliquid/HyperbeatStakingModule.t.sol @@ -5,8 +5,9 @@ pragma solidity ^0.8.13; import {TestBase} from "../../../core/TestBase.sol"; import {DataTypes} from "../../../../src/common/DataTypes.sol"; import {Superloop} from "../../../../src/core/Superloop/Superloop.sol"; -import {TransparentUpgradeableProxy} from - "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import { + TransparentUpgradeableProxy +} from "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import {IPoolConfigurator} from "aave-v3-core/contracts/interfaces/IPoolConfigurator.sol"; import {console} from "forge-std/console.sol"; @@ -76,8 +77,7 @@ contract HyperbeatStakingModuleTest is TestBase { data: abi.encodeWithSelector( unwrapModule.execute.selector, DataTypes.AaveV3ActionParams({ - asset: environment.vaultAsset, - amount: 100 * 10 ** environment.vaultAssetDecimals + asset: environment.vaultAsset, amount: 100 * 10 ** environment.vaultAssetDecimals }) ) }); @@ -86,7 +86,9 @@ contract HyperbeatStakingModuleTest is TestBase { module: address(hyperbeatStakingModule), data: abi.encodeWithSelector( hyperbeatStakingModule.execute.selector, - DataTypes.StakeParams({assets: 100 * 10 ** environment.vaultAssetDecimals, data: abi.encode(string(""))}) + DataTypes.StakeParams({ + assets: 100 * 10 ** environment.vaultAssetDecimals, data: abi.encode(string("")) + }) ) }); diff --git a/test/modules/stake/hyperliquid/HyperliquidStakingModule.t.sol b/test/modules/stake/hyperliquid/HyperliquidStakingModule.t.sol index 85bf3b1..9d4b729 100644 --- a/test/modules/stake/hyperliquid/HyperliquidStakingModule.t.sol +++ b/test/modules/stake/hyperliquid/HyperliquidStakingModule.t.sol @@ -5,8 +5,9 @@ pragma solidity ^0.8.13; import {TestBase} from "../../../core/TestBase.sol"; import {DataTypes} from "../../../../src/common/DataTypes.sol"; import {Superloop} from "../../../../src/core/Superloop/Superloop.sol"; -import {TransparentUpgradeableProxy} from - "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import { + TransparentUpgradeableProxy +} from "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import {IPoolConfigurator} from "aave-v3-core/contracts/interfaces/IPoolConfigurator.sol"; diff --git a/test/modules/stake/hyperliquid/KinetiqStakingModule.t.sol b/test/modules/stake/hyperliquid/KinetiqStakingModule.t.sol index 0563fd0..754ddd7 100644 --- a/test/modules/stake/hyperliquid/KinetiqStakingModule.t.sol +++ b/test/modules/stake/hyperliquid/KinetiqStakingModule.t.sol @@ -5,8 +5,9 @@ pragma solidity ^0.8.13; import {TestBase} from "../../../core/TestBase.sol"; import {DataTypes} from "../../../../src/common/DataTypes.sol"; import {Superloop} from "../../../../src/core/Superloop/Superloop.sol"; -import {TransparentUpgradeableProxy} from - "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import { + TransparentUpgradeableProxy +} from "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import {IPoolConfigurator} from "aave-v3-core/contracts/interfaces/IPoolConfigurator.sol"; import {console} from "forge-std/console.sol"; @@ -80,7 +81,9 @@ contract KinetiqStakingModuleTest is TestBase { module: address(kinetiqStakeModule), data: abi.encodeWithSelector( kinetiqStakeModule.execute.selector, - DataTypes.StakeParams({assets: 100 * 10 ** environment.vaultAssetDecimals, data: abi.encode(string(""))}) + DataTypes.StakeParams({ + assets: 100 * 10 ** environment.vaultAssetDecimals, data: abi.encode(string("")) + }) ) }); From e6e851640446ac8701b08512739f6f9acf0ff4c2 Mon Sep 17 00:00:00 2001 From: Priyam Anand Date: Fri, 30 Jan 2026 10:50:00 +0530 Subject: [PATCH 20/21] chore: added test for full loop --- test/core/integration/EthenaLoop.t.sol | 116 ++++++++++++++++++++++ test/core/integration/IntegrationBase.sol | 17 +++- 2 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 test/core/integration/EthenaLoop.t.sol diff --git a/test/core/integration/EthenaLoop.t.sol b/test/core/integration/EthenaLoop.t.sol new file mode 100644 index 0000000..b1ab8ef --- /dev/null +++ b/test/core/integration/EthenaLoop.t.sol @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.13; + +import {IntegrationBase} from "./IntegrationBase.sol"; +import {DataTypes} from "../../../src/common/DataTypes.sol"; +import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +import {IERC20Metadata} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import {console} from "forge-std/console.sol"; +import {IAaveOracle} from "aave-v3-core/contracts/interfaces/IAaveOracle.sol"; +import {IPoolAddressesProvider} from "aave-v3-core/contracts/interfaces/IPoolAddressesProvider.sol"; +import {IERC4626} from "openzeppelin-contracts/contracts/interfaces/IERC4626.sol"; +import {IPoolDataProvider} from "aave-v3-core/contracts/interfaces/IPoolDataProvider.sol"; + +contract EthenaLoopTest is IntegrationBase { + DataTypes.WithdrawRequestType requestType = DataTypes.WithdrawRequestType.INSTANT; + + function setUp() public override { + super.setUp(); + + DataTypes.AaveV3EmodeParams memory params = + DataTypes.AaveV3EmodeParams({emodeCategory: environment.emodeCategory}); + + DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](1); + moduleExecutionData[0] = DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.DELEGATECALL, + module: address(emodeModule), + data: abi.encodeWithSelector(emodeModule.execute.selector, params) + }); + + vm.prank(admin); + superloop.operate(moduleExecutionData); + } + + function test_initialize() public view { + assertEq(depositManager.vault(), address(superloop)); + assertEq(depositManager.asset(), environment.vaultAsset); + assertEq(depositManager.nextDepositRequestId(), 1); + assertEq(withdrawManager.vault(), address(superloop)); + assertEq(withdrawManager.asset(), environment.vaultAsset); + assertEq(withdrawManager.nextWithdrawRequestId(requestType), 1); + assertEq(withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.INSTANT), 1); + assertEq(withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.PRIORITY), 1); + assertEq(withdrawManager.nextWithdrawRequestId(DataTypes.WithdrawRequestType.DEFERRED), 1); + } + + function test_depositAndFullLoop() public { + uint256 vaultAssetScale = 10 ** environment.vaultAssetDecimals; + uint256 borrowTokenScale = 10 ** IERC20Metadata(environment.borrowAssets[0]).decimals(); + + uint256 depositAmountUnscaled = 100; + uint256 depositAmount = depositAmountUnscaled * vaultAssetScale; + uint256 leverage = 5; + + // deposit 100 USDe + deal(environment.vaultAsset, user1, depositAmount); + vm.startPrank(user1); + IERC20(environment.vaultAsset).approve(address(depositManager), depositAmount); + depositManager.requestDeposit(depositAmount, address(0)); + vm.stopPrank(); + + uint256 borrowAmount = depositAmountUnscaled * borrowTokenScale * (leverage - 1) + 1 * borrowTokenScale; // +1 to cover the slippage if any + uint256 flashLoanAmount = depositAmount * (leverage - 1); + + DataTypes.ModuleExecutionData[] memory moduleExecutionData = new DataTypes.ModuleExecutionData[](5); + // lend, borrow and swap to USDe + moduleExecutionData[0] = _stakeCall(environment.lendAssets[1], (depositAmount * leverage) / 2); + moduleExecutionData[1] = _supplyCall(environment.lendAssets[0], type(uint256).max); + moduleExecutionData[2] = _supplyCall(environment.lendAssets[1], type(uint256).max); + moduleExecutionData[3] = _borrowCall(environment.borrowAssets[0], borrowAmount); + moduleExecutionData[4] = _swapCallExactIn( + environment.borrowAssets[0], + environment.lendAssets[0], + borrowAmount, + flashLoanAmount, + environment.router, + USDC_USDE_POOL_FEE, + block.timestamp + 100 + ); + + DataTypes.ModuleExecutionData[] memory intermediateExecutionData = new DataTypes.ModuleExecutionData[](1); + intermediateExecutionData[0] = + _morphoFlashloanCall(environment.lendAssets[0], flashLoanAmount, abi.encode(moduleExecutionData)); + + DataTypes.ModuleExecutionData[] memory finalExecutionData = new DataTypes.ModuleExecutionData[](1); + finalExecutionData[0] = + _resolveDepositRequestsCall(environment.vaultAsset, depositAmount, abi.encode(intermediateExecutionData)); + + vm.prank(admin); + superloop.operate(finalExecutionData); + + uint256 totalAssets = superloop.totalAssets(); + uint256 borrowBalance = IERC20(environment.borrowAssets[0]).balanceOf(address(superloop)); + uint256 lendBalance = IERC20(environment.lendAssets[0]).balanceOf(address(superloop)); + uint256 stakedBalance = IERC20(environment.lendAssets[1]).balanceOf(address(superloop)); + + assertApproxEqAbs(totalAssets, depositAmount, 1 * vaultAssetScale); + assertEq(borrowBalance, 0); + + console.log("totalAssets", totalAssets); + console.log("lendBalance", lendBalance); + console.log("stakedBalance", stakedBalance); + + (uint256 currentSupply,,,,,,,,) = IPoolDataProvider(environment.poolDataProvider) + .getUserReserveData(environment.lendAssets[0], address(superloop)); + (uint256 currentStakedSupply,,,,,,,,) = IPoolDataProvider(environment.poolDataProvider) + .getUserReserveData(environment.lendAssets[1], address(superloop)); + + (,, uint256 currentBorrowBalance,,,,,,) = IPoolDataProvider(environment.poolDataProvider) + .getUserReserveData(environment.borrowAssets[0], address(superloop)); + + console.log("currentSupply", currentSupply); + console.log("currentStakedSupply", currentStakedSupply); + console.log("currentBorrowBalance", currentBorrowBalance); + } +} diff --git a/test/core/integration/IntegrationBase.sol b/test/core/integration/IntegrationBase.sol index f2d761e..be2edda 100644 --- a/test/core/integration/IntegrationBase.sol +++ b/test/core/integration/IntegrationBase.sol @@ -61,7 +61,7 @@ abstract contract IntegrationBase is TestBase { vm.startPrank(admin); _deployModules(); - address[] memory modules = new address[](11); + address[] memory modules = new address[](12); modules[0] = address(dexModule); modules[1] = address(flashloanModule); modules[2] = address(callbackHandler); @@ -73,6 +73,7 @@ abstract contract IntegrationBase is TestBase { modules[8] = address(depositManagerCallbackHandler); modules[9] = address(morphoFlashloanModule); modules[10] = address(morphoCallbackHandler); + modules[11] = address(vaultSupplyModule); DataTypes.VaultInitData memory initData = DataTypes.VaultInitData({ asset: environment.vaultAsset, @@ -588,4 +589,18 @@ abstract contract IntegrationBase is TestBase { return (totalShares - sharesToResolve, repayAmount, withdrawAmount); } + + function _stakeCall(address stakingVault, uint256 amount) + internal + view + returns (DataTypes.ModuleExecutionData memory) + { + DataTypes.VaultActionParams memory vaultActionParams = + DataTypes.VaultActionParams({vault: stakingVault, amount: amount}); + return DataTypes.ModuleExecutionData({ + executionType: DataTypes.CallType.DELEGATECALL, + module: address(vaultSupplyModule), + data: abi.encodeWithSelector(vaultSupplyModule.execute.selector, vaultActionParams) + }); + } } From 7ac5a7940cd83811637f1e5bdb03362fc191e030 Mon Sep 17 00:00:00 2001 From: Priyam Anand Date: Fri, 30 Jan 2026 11:08:35 +0530 Subject: [PATCH 21/21] chore: added comments for accountant --- .../aaveV3Accountant/AccountantAaveV3.sol | 33 ++++++++++++++++++- .../aaveV3Accountant/AccountantAaveV3Base.sol | 26 +++++++++++++++ .../UniversalAccountant.sol | 30 +++++++++++++++++ .../UniversalAccountantBase.sol | 18 ++++++++++ 4 files changed, 106 insertions(+), 1 deletion(-) diff --git a/src/core/Accountant/aaveV3Accountant/AccountantAaveV3.sol b/src/core/Accountant/aaveV3Accountant/AccountantAaveV3.sol index 15ce54f..d1ef19a 100644 --- a/src/core/Accountant/aaveV3Accountant/AccountantAaveV3.sol +++ b/src/core/Accountant/aaveV3Accountant/AccountantAaveV3.sol @@ -16,6 +16,12 @@ import {IERC4626} from "openzeppelin-contracts/contracts/interfaces/IERC4626.sol import {Errors} from "../../../common/Errors.sol"; import {AccountantAaveV3Base} from "./AccountantAaveV3Base.sol"; +/** + * @title AccountantAaveV3 + * @author Superlend + * @notice Manages total assets calculation and performance fee tracking for Aave V3 positions + * @dev Calculates total assets by aggregating lending positions, subtracting borrowing positions, and converting to base asset using Aave oracle prices + */ contract AccountantAaveV3 is ReentrancyGuardUpgradeable, AccountantAaveV3Base { event LastRealizedFeeExchangeRateUpdated(uint256 oldRate, uint256 newRate); @@ -29,6 +35,10 @@ contract AccountantAaveV3 is ReentrancyGuardUpgradeable, AccountantAaveV3Base { __AccountantAaveV3Base_init(_msgSender()); } + /** + * @notice Initializes the AccountantAaveV3 module with Aave V3 configuration + * @param data Initialization data containing pool addresses provider, lend/borrow assets, performance fee, and vault address + */ function __AccountantAaveV3Module_init(DataTypes.AaveV3AccountantModuleInitData memory data) internal onlyInitializing @@ -40,7 +50,11 @@ contract AccountantAaveV3 is ReentrancyGuardUpgradeable, AccountantAaveV3Base { AccountantAaveV3Storage.setVault(data.vault); } - // get total assets for the contract + /** + * @notice Calculates total assets by aggregating Aave V3 positions and converting to base asset + * @return Total assets value in base asset terms + * @dev Sums lending positions (positive balance), subtracts borrowing positions (negative balance), adds base asset balance, and converts to base asset using oracle prices + */ function getTotalAssets() public view returns (uint256) { AccountantAaveV3Storage.AccountantAaveV3State storage $ = AccountantAaveV3Storage.getAccountantAaveV3Storage(); address baseAsset = IERC4626($.vault).asset(); @@ -69,6 +83,7 @@ contract AccountantAaveV3 is ReentrancyGuardUpgradeable, AccountantAaveV3Base { } } + // sum all borrowing positions len = $.borrowAssets.length; for (uint256 i; i < len;) { address borrowAsset = $.borrowAssets[i]; @@ -83,6 +98,7 @@ contract AccountantAaveV3 is ReentrancyGuardUpgradeable, AccountantAaveV3Base { } } + // add base asset balance held in vault uint256 baseAssetPrice = aaveOracle.getAssetPrice(baseAsset); positiveBalance += (IERC20(baseAsset).balanceOf($.vault) * commonDecimalFactor * baseAssetPrice) / (10 ** baseDecimals); @@ -93,6 +109,14 @@ contract AccountantAaveV3 is ReentrancyGuardUpgradeable, AccountantAaveV3Base { return totalAssetsInBaseAsset; } + /** + * @notice Calculates the performance fee based on exchange rate appreciation + * @param totalShares Total number of shares in the vault + * @param exchangeRate Current exchange rate (assets per share) + * @param decimals Decimal precision for the calculation + * @return Performance fee amount in base asset terms + * @dev Calculates fee only on positive performance (when current rate > last realized rate) + */ function getPerformanceFee(uint256 totalShares, uint256 exchangeRate, uint8 decimals) public view @@ -104,6 +128,7 @@ contract AccountantAaveV3 is ReentrancyGuardUpgradeable, AccountantAaveV3Base { uint256 latestAssetAmount = totalShares * exchangeRate; uint256 prevAssetAmount = totalShares * $.lastRealizedFeeExchangeRate; + // return 0 if there's no positive performance if (prevAssetAmount > latestAssetAmount) return 0; uint256 interestGenerated = latestAssetAmount - prevAssetAmount; @@ -114,6 +139,12 @@ contract AccountantAaveV3 is ReentrancyGuardUpgradeable, AccountantAaveV3Base { return performanceFee; } + /** + * @notice Updates the last realized fee exchange rate after performance fee collection + * @param lastRealizedFeeExchangeRate_ New exchange rate to set as the last realized rate + * @param totalSupply Total supply of shares in the vault + * @dev Only allows rate updates that are higher than the previous rate (unless totalSupply is 0) + */ function setLastRealizedFeeExchangeRate(uint256 lastRealizedFeeExchangeRate_, uint256 totalSupply) public onlyVault diff --git a/src/core/Accountant/aaveV3Accountant/AccountantAaveV3Base.sol b/src/core/Accountant/aaveV3Accountant/AccountantAaveV3Base.sol index a7432ce..f087fbf 100644 --- a/src/core/Accountant/aaveV3Accountant/AccountantAaveV3Base.sol +++ b/src/core/Accountant/aaveV3Accountant/AccountantAaveV3Base.sol @@ -6,6 +6,12 @@ import {OwnableUpgradeable} from "openzeppelin-contracts-upgradeable/contracts/a import {AccountantAaveV3Storage} from "../../lib/AccountantAaveV3Storage.sol"; import {IAccountantAaveV3Module} from "../../../interfaces/IAccountantAaveV3Module.sol"; +/** + * @title AccountantAaveV3Base + * @author Superlend + * @notice Base contract providing owner-controlled configuration for AccountantAaveV3 + * @dev Handles owner-only functions for setting Aave V3 pool addresses provider, lend/borrow assets, performance fee, and vault address + */ abstract contract AccountantAaveV3Base is OwnableUpgradeable, IAccountantAaveV3Module { event PoolAddressesProviderUpdated(address indexed oldProvider, address indexed newProvider); event LendAssetsUpdated(address[] oldAssets, address[] newAssets); @@ -17,30 +23,50 @@ abstract contract AccountantAaveV3Base is OwnableUpgradeable, IAccountantAaveV3M __Ownable_init(owner); } + /** + * @notice Sets the Aave V3 pool addresses provider + * @param poolAddressesProvider_ Address of the Aave V3 pool addresses provider + */ function setPoolAddressesProvider(address poolAddressesProvider_) external onlyOwner { address oldProvider = AccountantAaveV3Storage.getAccountantAaveV3Storage().poolAddressesProvider; AccountantAaveV3Storage.setPoolAddressesProvider(poolAddressesProvider_); emit PoolAddressesProviderUpdated(oldProvider, poolAddressesProvider_); } + /** + * @notice Sets the list of assets that are being lent on Aave V3 + * @param lendAssets_ Array of asset addresses being lent + */ function setLendAssets(address[] memory lendAssets_) external onlyOwner { address[] memory oldAssets = AccountantAaveV3Storage.getAccountantAaveV3Storage().lendAssets; AccountantAaveV3Storage.setLendAssets(lendAssets_); emit LendAssetsUpdated(oldAssets, lendAssets_); } + /** + * @notice Sets the list of assets that are being borrowed on Aave V3 + * @param borrowAssets_ Array of asset addresses being borrowed + */ function setBorrowAssets(address[] memory borrowAssets_) external onlyOwner { address[] memory oldAssets = AccountantAaveV3Storage.getAccountantAaveV3Storage().borrowAssets; AccountantAaveV3Storage.setBorrowAssets(borrowAssets_); emit BorrowAssetsUpdated(oldAssets, borrowAssets_); } + /** + * @notice Sets the performance fee rate in basis points + * @param performanceFee_ Performance fee in basis points (BPS) + */ function setPerformanceFee(uint16 performanceFee_) external onlyOwner { uint16 oldFee = AccountantAaveV3Storage.getAccountantAaveV3Storage().performanceFee; AccountantAaveV3Storage.setPerformanceFee(performanceFee_); emit PerformanceFeeUpdated(oldFee, performanceFee_); } + /** + * @notice Sets the vault address that this accountant is associated with + * @param vault_ Address of the Superloop vault + */ function setVault(address vault_) external onlyOwner { address oldVault = AccountantAaveV3Storage.getAccountantAaveV3Storage().vault; AccountantAaveV3Storage.setVault(vault_); diff --git a/src/core/Accountant/universalAccountant/UniversalAccountant.sol b/src/core/Accountant/universalAccountant/UniversalAccountant.sol index af0409f..8dabd9d 100644 --- a/src/core/Accountant/universalAccountant/UniversalAccountant.sol +++ b/src/core/Accountant/universalAccountant/UniversalAccountant.sol @@ -11,6 +11,12 @@ import { import {Errors} from "../../../common/Errors.sol"; import {IAaveV3AccountantPlugin} from "../../../interfaces/IAaveV3AccountantPlugin.sol"; +/** + * @title UniversalAccountant + * @author Superlend + * @notice Manages total assets aggregation and performance fee calculations across multiple accountant plugins + * @dev Aggregates asset values from registered accountant plugins and calculates performance fees based on exchange rate changes + */ contract UniversalAccountant is UniversalAccountantBase, ReentrancyGuardUpgradeable { event LastRealizedFeeExchangeRateUpdated(uint256 oldRate, uint256 newRate); @@ -24,6 +30,10 @@ contract UniversalAccountant is UniversalAccountantBase, ReentrancyGuardUpgradea __UniversalAccountantBase_init(_msgSender()); } + /** + * @notice Initializes the UniversalAccountant module with registered accountants, performance fee, and vault address + * @param data Initialization data containing registered accountants, performance fee, and vault address + */ function __UniversalAccountantModule_init(DataTypes.UniversalAccountantModuleInitData memory data) internal onlyInitializing @@ -33,6 +43,11 @@ contract UniversalAccountant is UniversalAccountantBase, ReentrancyGuardUpgradea UniversalAccountantStorage.setVault(data.vault); } + /** + * @notice Aggregates total assets from all registered accountant plugins + * @return Total assets value across all registered accountant plugins + * @dev Iterates through all registered accountants and sums their total assets for the vault + */ function getTotalAssets() public view returns (uint256) { UniversalAccountantStorage.UniversalAccountantState storage $ = UniversalAccountantStorage.getUniversalAccountantStorage(); @@ -48,6 +63,14 @@ contract UniversalAccountant is UniversalAccountantBase, ReentrancyGuardUpgradea return totalAssets; } + /** + * @notice Calculates the performance fee based on exchange rate appreciation + * @param totalShares Total number of shares in the vault + * @param exchangeRate Current exchange rate (assets per share) + * @param decimals Decimal precision for the calculation + * @return Performance fee amount in base asset terms + * @dev Calculates fee only on positive performance (when current rate > last realized rate) + */ function getPerformanceFee(uint256 totalShares, uint256 exchangeRate, uint8 decimals) public view @@ -60,6 +83,7 @@ contract UniversalAccountant is UniversalAccountantBase, ReentrancyGuardUpgradea uint256 latestAssetAmount = totalShares * exchangeRate; uint256 prevAssetAmount = totalShares * $.lastRealizedFeeExchangeRate; + // return 0 if there's no positive performance if (prevAssetAmount > latestAssetAmount) return 0; uint256 interestGenerated = latestAssetAmount - prevAssetAmount; @@ -70,6 +94,12 @@ contract UniversalAccountant is UniversalAccountantBase, ReentrancyGuardUpgradea return performanceFee; } + /** + * @notice Updates the last realized fee exchange rate after performance fee collection + * @param lastRealizedFeeExchangeRate_ New exchange rate to set as the last realized rate + * @param totalSupply Total supply of shares in the vault + * @dev Only allows rate updates that are higher than the previous rate (unless totalSupply is 0) + */ function setLastRealizedFeeExchangeRate(uint256 lastRealizedFeeExchangeRate_, uint256 totalSupply) public onlyVault diff --git a/src/core/Accountant/universalAccountant/UniversalAccountantBase.sol b/src/core/Accountant/universalAccountant/UniversalAccountantBase.sol index 923c3a6..811b916 100644 --- a/src/core/Accountant/universalAccountant/UniversalAccountantBase.sol +++ b/src/core/Accountant/universalAccountant/UniversalAccountantBase.sol @@ -6,6 +6,12 @@ import {OwnableUpgradeable} from "openzeppelin-contracts-upgradeable/contracts/a import {UniversalAccountantStorage} from "../../lib/UniversalAccountantStorage.sol"; import {IAccountantModule} from "../../../interfaces/IAccountantModule.sol"; +/** + * @title UniversalAccountantBase + * @author Superlend + * @notice Base contract providing owner-controlled configuration for UniversalAccountant + * @dev Handles owner-only functions for setting registered accountants, performance fee, and vault address + */ abstract contract UniversalAccountantBase is OwnableUpgradeable, IAccountantModule { event PerformanceFeeUpdated(uint16 oldFee, uint16 newFee); event VaultUpdated(address indexed oldVault, address indexed newVault); @@ -15,6 +21,10 @@ abstract contract UniversalAccountantBase is OwnableUpgradeable, IAccountantModu __Ownable_init(owner); } + /** + * @notice Sets the list of registered accountant plugins to aggregate assets from + * @param registeredAccountants_ Array of accountant plugin addresses + */ function setRegisteredAccountants(address[] memory registeredAccountants_) external onlyOwner { address[] memory oldAccountants = UniversalAccountantStorage.getUniversalAccountantStorage().registeredAccountants; @@ -22,12 +32,20 @@ abstract contract UniversalAccountantBase is OwnableUpgradeable, IAccountantModu emit RegisteredAccountantsUpdated(oldAccountants, registeredAccountants_); } + /** + * @notice Sets the performance fee rate in basis points + * @param performanceFee_ Performance fee in basis points (BPS) + */ function setPerformanceFee(uint16 performanceFee_) external onlyOwner { uint16 oldFee = UniversalAccountantStorage.getUniversalAccountantStorage().performanceFee; UniversalAccountantStorage.setPerformanceFee(performanceFee_); emit PerformanceFeeUpdated(oldFee, performanceFee_); } + /** + * @notice Sets the vault address that this accountant is associated with + * @param vault_ Address of the Superloop vault + */ function setVault(address vault_) external onlyOwner { address oldVault = UniversalAccountantStorage.getUniversalAccountantStorage().vault; UniversalAccountantStorage.setVault(vault_);