From 7d8ecadc3f7b00483675f0ae20bcf3edbb72f556 Mon Sep 17 00:00:00 2001 From: Tamara Ringas <69479754+TamaraRingas@users.noreply.github.com> Date: Fri, 25 Oct 2024 16:21:55 +0200 Subject: [PATCH 01/14] add ZERO_LENGTH() error --- src/libraries/Error.sol | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libraries/Error.sol b/src/libraries/Error.sol index 4c90f51fb..f11a7517d 100644 --- a/src/libraries/Error.sol +++ b/src/libraries/Error.sol @@ -155,6 +155,9 @@ library Error { /// @dev thrown if value input is 0 error ZERO_INPUT_VALUE(); + /// @dev thrown if array length is 0 + error ZERO_LENGTH(); + /// SUPERFORM ROUTER INPUT VALIDATION ERRORS /// --------------------------------------------------------- From 01a3d074747a09835f0a4a12a7233d61d0e26312 Mon Sep 17 00:00:00 2001 From: Tamara Ringas <69479754+TamaraRingas@users.noreply.github.com> Date: Fri, 25 Oct 2024 16:22:28 +0200 Subject: [PATCH 02/14] refactor: make deposit4626() param types arrays --- src/interfaces/ISuperformRouterPlus.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interfaces/ISuperformRouterPlus.sol b/src/interfaces/ISuperformRouterPlus.sol index f3b51aadb..97f417059 100644 --- a/src/interfaces/ISuperformRouterPlus.sol +++ b/src/interfaces/ISuperformRouterPlus.sol @@ -223,9 +223,9 @@ interface ISuperformRouterPlus is IBaseSuperformRouterPlus { function startCrossChainRebalanceMulti(InitiateXChainRebalanceMultiArgs memory args) external payable; /// @notice deposits ERC4626 vault shares into superform - /// @param vault_ The ERC4626 vault to redeem from + /// @param vaults_ The ERC4626 vaults to redeem from /// @param args Rest of the arguments to deposit 4626 - function deposit4626(address vault_, Deposit4626Args calldata args) external payable; + function deposit4626(address[] calldata vaults_, Deposit4626Args[] calldata args) external payable; /// @dev Forwards dust to Paymaster /// @param token_ the token to forward From 99596361a4fa3e37efcd1ba0f8184d44137e1b07 Mon Sep 17 00:00:00 2001 From: Tamara Ringas <69479754+TamaraRingas@users.noreply.github.com> Date: Fri, 25 Oct 2024 16:23:41 +0200 Subject: [PATCH 03/14] refactor: make deposit4626() internal --- src/router-plus/SuperformRouterPlus.sol | 48 +++++++++++++------------ 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/src/router-plus/SuperformRouterPlus.sol b/src/router-plus/SuperformRouterPlus.sol index 5f596e7cc..f31469930 100644 --- a/src/router-plus/SuperformRouterPlus.sol +++ b/src/router-plus/SuperformRouterPlus.sol @@ -355,29 +355,6 @@ contract SuperformRouterPlus is ISuperformRouterPlus, BaseSuperformRouterPlus { ); } - /// @inheritdoc ISuperformRouterPlus - function deposit4626(address vault_, Deposit4626Args calldata args) external payable override { - _transferERC20In(IERC20(vault_), args.receiverAddressSP, args.amount); - IERC4626 vault = IERC4626(vault_); - address assetAdr = vault.asset(); - IERC20 asset = IERC20(assetAdr); - - uint256 balanceBefore = asset.balanceOf(address(this)); - - uint256 amountRedeemed = _redeemShare(vault, assetAdr, args.amount, args.expectedOutputAmount, args.maxSlippage); - - if (!whitelistedSelectors[Actions.DEPOSIT][_parseSelectorMem(args.depositCallData)]) { - revert INVALID_DEPOSIT_SELECTOR(); - } - - address router = _getAddress(keccak256("SUPERFORM_ROUTER")); - _deposit(router, asset, amountRedeemed, msg.value, args.depositCallData); - - _tokenRefunds(router, assetAdr, args.receiverAddressSP, balanceBefore); - - emit Deposit4626Completed(args.receiverAddressSP, vault_); - } - /// @inheritdoc ISuperformRouterPlus function forwardDustToPaymaster(address token_) external override { if (token_ == address(0)) revert Error.ZERO_ADDRESS(); @@ -603,4 +580,29 @@ contract SuperformRouterPlus is ISuperformRouterPlus, BaseSuperformRouterPlus { } } } + + /// @notice deposits ERC4626 vault shares into superform + /// @param vault_ The ERC4626 vault to redeem from + /// @param args Rest of the arguments to deposit 4626 + function _deposit4626(address vault_, Deposit4626Args calldata args) internal { + _transferERC20In(IERC20(vault_), args.receiverAddressSP, args.amount); + IERC4626 vault = IERC4626(vault_); + address assetAdr = vault.asset(); + IERC20 asset = IERC20(assetAdr); + + uint256 balanceBefore = asset.balanceOf(address(this)); + + uint256 amountRedeemed = _redeemShare(vault, assetAdr, args.amount, args.expectedOutputAmount, args.maxSlippage); + + if (!whitelistedSelectors[Actions.DEPOSIT][_parseSelectorMem(args.depositCallData)]) { + revert INVALID_DEPOSIT_SELECTOR(); + } + + address router = _getAddress(keccak256("SUPERFORM_ROUTER")); + _deposit(router, asset, amountRedeemed, msg.value, args.depositCallData); + + _tokenRefunds(router, assetAdr, args.receiverAddressSP, balanceBefore); + + emit Deposit4626Completed(args.receiverAddressSP, vault_); + } } From 4678150b2cffadf72563ff244f940fc92ded314c Mon Sep 17 00:00:00 2001 From: Tamara Ringas <69479754+TamaraRingas@users.noreply.github.com> Date: Fri, 25 Oct 2024 16:24:22 +0200 Subject: [PATCH 04/14] refactor: update deposit4626() to handle array params --- src/router-plus/SuperformRouterPlus.sol | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/router-plus/SuperformRouterPlus.sol b/src/router-plus/SuperformRouterPlus.sol index f31469930..6837849dd 100644 --- a/src/router-plus/SuperformRouterPlus.sol +++ b/src/router-plus/SuperformRouterPlus.sol @@ -355,6 +355,25 @@ contract SuperformRouterPlus is ISuperformRouterPlus, BaseSuperformRouterPlus { ); } + /// @inheritdoc ISuperformRouterPlus + function deposit4626(address[] calldata vaults_, Deposit4626Args[] calldata args) external payable { + + if (vaults_.length != args.length) { + revert Error.ARRAY_LENGTH_MISMATCH(); + } + + if (vaults_.length == 0) { + revert Error.ZERO_LENGTH(); + } + + for (uint256 i; i < vaults_.length; ++i) { + if (!whitelistedSelectors[Actions.DEPOSIT][_parseSelectorMem(args[i].depositCallData)]) { + revert INVALID_DEPOSIT_SELECTOR(); + } + _deposit4626(vaults_[i], args[i]); + } + } + /// @inheritdoc ISuperformRouterPlus function forwardDustToPaymaster(address token_) external override { if (token_ == address(0)) revert Error.ZERO_ADDRESS(); From e276a54eefc0ee92abf9988c4b1864093b52ee8a Mon Sep 17 00:00:00 2001 From: Tamara Ringas <69479754+TamaraRingas@users.noreply.github.com> Date: Fri, 25 Oct 2024 16:41:21 +0200 Subject: [PATCH 05/14] fix: rm ZERO_LENGTH() error --- src/libraries/Error.sol | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/libraries/Error.sol b/src/libraries/Error.sol index f11a7517d..4c90f51fb 100644 --- a/src/libraries/Error.sol +++ b/src/libraries/Error.sol @@ -155,9 +155,6 @@ library Error { /// @dev thrown if value input is 0 error ZERO_INPUT_VALUE(); - /// @dev thrown if array length is 0 - error ZERO_LENGTH(); - /// SUPERFORM ROUTER INPUT VALIDATION ERRORS /// --------------------------------------------------------- From 92f87b1706a4b9e93b85492f617f885672cfc32d Mon Sep 17 00:00:00 2001 From: Tamara Ringas <69479754+TamaraRingas@users.noreply.github.com> Date: Fri, 25 Oct 2024 16:42:18 +0200 Subject: [PATCH 06/14] fix: update errors in deposit4626() --- src/router-plus/SuperformRouterPlus.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/router-plus/SuperformRouterPlus.sol b/src/router-plus/SuperformRouterPlus.sol index 6837849dd..eedd49b09 100644 --- a/src/router-plus/SuperformRouterPlus.sol +++ b/src/router-plus/SuperformRouterPlus.sol @@ -363,7 +363,7 @@ contract SuperformRouterPlus is ISuperformRouterPlus, BaseSuperformRouterPlus { } if (vaults_.length == 0) { - revert Error.ZERO_LENGTH(); + revert Error.ARRAY_LENGTH_MISMATCH(); } for (uint256 i; i < vaults_.length; ++i) { From 8d17fde85c46a3f4bfd87bdc7f80f2518163445c Mon Sep 17 00:00:00 2001 From: Tamara Ringas <69479754+TamaraRingas@users.noreply.github.com> Date: Fri, 25 Oct 2024 16:47:40 +0200 Subject: [PATCH 07/14] fix: update deposit4626 tests to use arrays --- .../router-plus/SuperformRouterPlus.t.sol | 43 ++++++++++++++++--- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/test/unit/router-plus/SuperformRouterPlus.t.sol b/test/unit/router-plus/SuperformRouterPlus.t.sol index 5d16ecbbb..3403efab1 100644 --- a/test/unit/router-plus/SuperformRouterPlus.t.sol +++ b/test/unit/router-plus/SuperformRouterPlus.t.sol @@ -436,6 +436,8 @@ contract SuperformRouterPlusTest is ProtocolActions { // Deploy a mock ERC4626 vault VaultMock mockVault = new VaultMock(IERC20(getContract(SOURCE_CHAIN, "DAI")), "Mock Vault", "mVLT"); + address[] calldata vaults = new address[](1); + vaults[0] = address(mockVault); // Mint some DAI to the deployer uint256 daiAmount = 1e18; @@ -445,6 +447,7 @@ contract SuperformRouterPlusTest is ProtocolActions { uint256 vaultTokenAmount = mockVault.deposit(daiAmount, deployer); // Prepare deposit4626 args with an invalid deposit selector + Deposit4626Args[] calldata argsArray = new Deposit4626Args[](1); ISuperformRouterPlus.Deposit4626Args memory args = ISuperformRouterPlus.Deposit4626Args({ amount: vaultTokenAmount, expectedOutputAmount: daiAmount, @@ -452,17 +455,24 @@ contract SuperformRouterPlusTest is ProtocolActions { receiverAddressSP: deployer, depositCallData: abi.encodeWithSelector(bytes4(keccak256("invalidDepositSelector()")), superformId1, daiAmount) }); + argsArray[0] = args; // Approve RouterPlus to spend vault tokens mockVault.approve(ROUTER_PLUS_SOURCE, vaultTokenAmount); // Expect the function to revert with INVALID_DEPOSIT_SELECTOR error vm.expectRevert(ISuperformRouterPlus.INVALID_DEPOSIT_SELECTOR.selector); - SuperformRouterPlus(ROUTER_PLUS_SOURCE).deposit4626{ value: 1 ether }(address(mockVault), args); + SuperformRouterPlus(ROUTER_PLUS_SOURCE).deposit4626{ value: 1 ether }(vaults, argsArray); vm.stopPrank(); } + + + function test_deposit4626_multipleVaults() public { + // TODO + } + function test_rebalanceSinglePosition_zeroAddressInterimAsset() public { vm.startPrank(deployer); @@ -529,6 +539,8 @@ contract SuperformRouterPlusTest is ProtocolActions { // Deploy a mock ERC4626 vault VaultMock mockVault = new VaultMock(IERC20(getContract(SOURCE_CHAIN, "DAI")), "Mock Vault", "mVLT"); + address[] calldata vaults = new address[](1); + vaults[0] = address(mockVault); // Mint some DAI to the deployer uint256 daiAmount = 1e18; @@ -538,6 +550,7 @@ contract SuperformRouterPlusTest is ProtocolActions { uint256 vaultTokenAmount = mockVault.deposit(daiAmount, deployer); // Prepare deposit4626 args + Deposit4626Args[] calldata argsArray = new Deposit4626Args[](1); ISuperformRouterPlus.Deposit4626Args memory args = ISuperformRouterPlus.Deposit4626Args({ amount: vaultTokenAmount, expectedOutputAmount: daiAmount * 10, // Assuming a large value for revert @@ -545,13 +558,13 @@ contract SuperformRouterPlusTest is ProtocolActions { receiverAddressSP: deployer, depositCallData: _buildDepositCallData(superformId1, daiAmount) }); - + argsArray[0] = args; // Approve RouterPlus to spend vault tokens mockVault.approve(ROUTER_PLUS_SOURCE, vaultTokenAmount); // Execute deposit4626 vm.expectRevert(ISuperformRouterPlus.ASSETS_RECEIVED_OUT_OF_SLIPPAGE.selector); - SuperformRouterPlus(ROUTER_PLUS_SOURCE).deposit4626{ value: 1 ether }(address(mockVault), args); + SuperformRouterPlus(ROUTER_PLUS_SOURCE).deposit4626{ value: 1 ether }(vaults, argsArray); } function test_deposit4626_toleranceExceeded() public { @@ -559,6 +572,8 @@ contract SuperformRouterPlusTest is ProtocolActions { // Deploy a mock ERC4626 vault VaultMock mockVault = new VaultMock(IERC20(getContract(SOURCE_CHAIN, "DAI")), "Mock Vault", "mVLT"); + address[] calldata vaults = new address[](1); + vaults[0] = address(mockVault); // Mint some DAI to the deployer uint256 daiAmount = 1e18; @@ -576,6 +591,7 @@ contract SuperformRouterPlusTest is ProtocolActions { ); // Prepare deposit4626 args + Deposit4626Args[] calldata argsArray = new Deposit4626Args[](1); ISuperformRouterPlus.Deposit4626Args memory args = ISuperformRouterPlus.Deposit4626Args({ amount: vaultTokenAmount, expectedOutputAmount: daiAmount, @@ -583,6 +599,7 @@ contract SuperformRouterPlusTest is ProtocolActions { receiverAddressSP: deployer, depositCallData: _buildDepositCallData(superformId1, daiAmount) }); + argsArray[0] = args; // Approve RouterPlus to spend vault tokens mockVault.approve(ROUTER_PLUS_SOURCE, vaultTokenAmount); @@ -590,7 +607,7 @@ contract SuperformRouterPlusTest is ProtocolActions { // Execute deposit4626 vm.recordLogs(); vm.expectRevert(ISuperformRouterPlus.TOLERANCE_EXCEEDED.selector); - SuperformRouterPlus(ROUTER_PLUS_SOURCE).deposit4626{ value: 1 ether }(address(mockVault), args); + SuperformRouterPlus(ROUTER_PLUS_SOURCE).deposit4626{ value: 1 ether }(vaults, argsArray); vm.stopPrank(); } @@ -600,6 +617,8 @@ contract SuperformRouterPlusTest is ProtocolActions { // Deploy a mock ERC4626 vault VaultMock mockVault = new VaultMock(IERC20(getContract(SOURCE_CHAIN, "DAI")), "Mock Vault", "mVLT"); + address[] calldata vaults = new address[](1); + vaults[0] = address(mockVault); // Mint some DAI to the deployer uint256 daiAmount = 1e18; @@ -617,6 +636,7 @@ contract SuperformRouterPlusTest is ProtocolActions { ); // Prepare deposit4626 args + Deposit4626Args[] calldata argsArray = new Deposit4626Args[](1); ISuperformRouterPlus.Deposit4626Args memory args = ISuperformRouterPlus.Deposit4626Args({ amount: vaultTokenAmount, expectedOutputAmount: daiAmount, @@ -624,6 +644,7 @@ contract SuperformRouterPlusTest is ProtocolActions { receiverAddressSP: deployer, depositCallData: _buildDepositCallData(superformId1, daiAmount) }); + argsArray[0] = args; // Approve RouterPlus to spend vault tokens mockVault.approve(ROUTER_PLUS_SOURCE, vaultTokenAmount); @@ -631,7 +652,7 @@ contract SuperformRouterPlusTest is ProtocolActions { // Execute deposit4626 vm.recordLogs(); vm.expectRevert(ISuperformRouterPlus.TOLERANCE_EXCEEDED.selector); - SuperformRouterPlus(ROUTER_PLUS_SOURCE).deposit4626{ value: 1 ether }(address(mockVault), args); + SuperformRouterPlus(ROUTER_PLUS_SOURCE).deposit4626{ value: 1 ether }(vaults, argsArray); vm.stopPrank(); } @@ -641,6 +662,8 @@ contract SuperformRouterPlusTest is ProtocolActions { // Deploy a mock ERC4626 vault VaultMock mockVault = new VaultMock(IERC20(getContract(SOURCE_CHAIN, "DAI")), "Mock Vault", "mVLT"); + address[] calldata vaults = new address[](1); + vaults[0] = address(mockVault); // Mint some DAI to the deployer uint256 daiAmount = 1e18 - 2 wei; @@ -650,6 +673,7 @@ contract SuperformRouterPlusTest is ProtocolActions { uint256 vaultTokenAmount = mockVault.deposit(daiAmount, deployer); // Prepare deposit4626 args + Deposit4626Args[] calldata argsArray = new Deposit4626Args[](1); ISuperformRouterPlus.Deposit4626Args memory args = ISuperformRouterPlus.Deposit4626Args({ amount: vaultTokenAmount, expectedOutputAmount: daiAmount, // Assuming 1:1 ratio for simplicity @@ -657,13 +681,14 @@ contract SuperformRouterPlusTest is ProtocolActions { receiverAddressSP: deployer, depositCallData: _buildDepositCallData(superformId1, daiAmount) }); + argsArray[0] = args; // Approve RouterPlus to spend vault tokens mockVault.approve(ROUTER_PLUS_SOURCE, vaultTokenAmount); // Execute deposit4626 vm.recordLogs(); - SuperformRouterPlus(ROUTER_PLUS_SOURCE).deposit4626{ value: 1 ether }(address(mockVault), args); + SuperformRouterPlus(ROUTER_PLUS_SOURCE).deposit4626{ value: 1 ether }(vaults, argsArray); // Verify the results assertGt( @@ -2520,6 +2545,8 @@ contract SuperformRouterPlusTest is ProtocolActions { // Deploy a mock ERC4626 vault VaultMock mockVault = new VaultMock(IERC20(getContract(SOURCE_CHAIN, "DAI")), "Mock Vault", "mVLT"); + address[] calldata vaults = new address[](1); + vaults[0] = address(mockVault); // Mint some DAI to the deployer uint256 daiAmount = 1e18; @@ -2529,6 +2556,7 @@ contract SuperformRouterPlusTest is ProtocolActions { uint256 vaultTokenAmount = mockVault.deposit(daiAmount, deployer); // Prepare deposit4626 args + Deposit4626Args[] calldata argsArray = new Deposit4626Args[](1); ISuperformRouterPlus.Deposit4626Args memory args = ISuperformRouterPlus.Deposit4626Args({ amount: vaultTokenAmount, expectedOutputAmount: daiAmount, // Assuming 1:1 ratio for simplicity @@ -2536,13 +2564,14 @@ contract SuperformRouterPlusTest is ProtocolActions { receiverAddressSP: deployer, depositCallData: _buildDepositCallData(superformId1, daiAmount) }); + argsArray[0] = args; // Approve RouterPlus to spend vault tokens mockVault.approve(ROUTER_PLUS_SOURCE, vaultTokenAmount); // Execute deposit4626 vm.recordLogs(); - SuperformRouterPlus(ROUTER_PLUS_SOURCE).deposit4626{ value: 1 ether }(address(mockVault), args); + SuperformRouterPlus(ROUTER_PLUS_SOURCE).deposit4626{ value: 1 ether }(vaults, argsArray); // Verify the results assertGt( From 68b27477d2ff9f40b9d504c6f3db684fefd1fbbc Mon Sep 17 00:00:00 2001 From: Tamara Ringas <69479754+TamaraRingas@users.noreply.github.com> Date: Fri, 25 Oct 2024 17:35:58 +0200 Subject: [PATCH 08/14] chore: add test_deposit4626_multipleVaults() --- .../router-plus/SuperformRouterPlus.t.sol | 155 ++++++++++++++++-- 1 file changed, 141 insertions(+), 14 deletions(-) diff --git a/test/unit/router-plus/SuperformRouterPlus.t.sol b/test/unit/router-plus/SuperformRouterPlus.t.sol index 3403efab1..9d06abefd 100644 --- a/test/unit/router-plus/SuperformRouterPlus.t.sol +++ b/test/unit/router-plus/SuperformRouterPlus.t.sol @@ -436,7 +436,8 @@ contract SuperformRouterPlusTest is ProtocolActions { // Deploy a mock ERC4626 vault VaultMock mockVault = new VaultMock(IERC20(getContract(SOURCE_CHAIN, "DAI")), "Mock Vault", "mVLT"); - address[] calldata vaults = new address[](1); + + address[] memory vaults = new address[](1); vaults[0] = address(mockVault); // Mint some DAI to the deployer @@ -447,7 +448,8 @@ contract SuperformRouterPlusTest is ProtocolActions { uint256 vaultTokenAmount = mockVault.deposit(daiAmount, deployer); // Prepare deposit4626 args with an invalid deposit selector - Deposit4626Args[] calldata argsArray = new Deposit4626Args[](1); + ISuperformRouterPlus.Deposit4626Args[] memory argsArray = new ISuperformRouterPlus.Deposit4626Args[](1); + ISuperformRouterPlus.Deposit4626Args memory args = ISuperformRouterPlus.Deposit4626Args({ amount: vaultTokenAmount, expectedOutputAmount: daiAmount, @@ -467,10 +469,124 @@ contract SuperformRouterPlusTest is ProtocolActions { vm.stopPrank(); } - + function test_deposit4626_multipleVaults_arrayMismatch() public { + vm.startPrank(deployer); + + // Deploy two mock ERC4626 vaults + VaultMock mockVault1 = new VaultMock(IERC20(getContract(SOURCE_CHAIN, "DAI")), "Mock Vault 1", "mVLT1"); + VaultMock mockVault2 = new VaultMock(IERC20(getContract(SOURCE_CHAIN, "USDC")), "Mock Vault 2", "mVLT2"); + + address[] memory vaults = new address[](2); + vaults[0] = address(mockVault1); + vaults[1] = address(mockVault2); + + // Mint some DAI to the deployer + uint256 daiAmount = 1e18; + + // Approve and deposit DAI into the mock vault + MockERC20(getContract(SOURCE_CHAIN, "DAI")).approve(address(mockVault1), daiAmount); + uint256 vaultTokenAmount = mockVault1.deposit(daiAmount, deployer); + + // Prepare deposit4626 args + ISuperformRouterPlus.Deposit4626Args[] memory argsArray = new ISuperformRouterPlus.Deposit4626Args[](1); + + argsArray[0] = ISuperformRouterPlus.Deposit4626Args({ + amount: vaultTokenAmount, + expectedOutputAmount: daiAmount, + maxSlippage: 100, // 1% + receiverAddressSP: deployer, + depositCallData: _buildDepositCallData(superformId1, daiAmount) + }); + + // Approve RouterPlus to spend vault tokens + mockVault1.approve(ROUTER_PLUS_SOURCE, vaultTokenAmount); + mockVault2.approve(ROUTER_PLUS_SOURCE, vaultTokenAmount); + + // Expect the function to revert with ARRAY_LENGTH_MISMATCH error + vm.expectRevert(Error.ARRAY_LENGTH_MISMATCH.selector); + SuperformRouterPlus(ROUTER_PLUS_SOURCE).deposit4626{ value: 1 ether }(vaults, argsArray); + + vm.stopPrank(); + } function test_deposit4626_multipleVaults() public { - // TODO + vm.startPrank(deployer); + + // Deploy two mock ERC4626 vaults + VaultMock mockVault1 = new VaultMock(IERC20(getContract(SOURCE_CHAIN, "DAI")), "Mock Vault 1", "mVLT1"); + VaultMock mockVault2 = new VaultMock(IERC20(getContract(SOURCE_CHAIN, "USDC")), "Mock Vault 2", "mVLT2"); + + address[] memory vaults = new address[](2); + vaults[0] = address(mockVault1); + vaults[1] = address(mockVault2); + + // Mint some DAI to the deployer + uint256 daiAmount = 1e18; + + // Mint some USDC to the deployer + uint256 usdcAmount = 1e18; + + // Approve and deposit DAI into the mock vault + MockERC20(getContract(SOURCE_CHAIN, "DAI")).approve(address(mockVault1), daiAmount); + uint256 vaultTokenAmount1 = mockVault1.deposit(daiAmount, deployer); + + // Approve and deposit USDC into the mock vault + MockERC20(getContract(SOURCE_CHAIN, "USDC")).approve(address(mockVault2), usdcAmount); + uint256 vaultTokenAmount2 = mockVault2.deposit(usdcAmount, deployer); + + // Prepare deposit4626 args for both deposits + ISuperformRouterPlus.Deposit4626Args[] memory argsArray = new ISuperformRouterPlus.Deposit4626Args[](2); + + argsArray[0] = ISuperformRouterPlus.Deposit4626Args({ + amount: vaultTokenAmount1, + expectedOutputAmount: daiAmount, + maxSlippage: 100, // 1% + receiverAddressSP: deployer, + depositCallData: _buildDepositCallData(superformId1, daiAmount) + }); + + argsArray[1] = ISuperformRouterPlus.Deposit4626Args({ + amount: vaultTokenAmount2, + expectedOutputAmount: usdcAmount, + maxSlippage: 100, // 1% + receiverAddressSP: deployer, + depositCallData: _buildDepositCallData(superformId2, usdcAmount) + }); + + // Approve RouterPlus to spend vault tokens + mockVault1.approve(ROUTER_PLUS_SOURCE, vaultTokenAmount1); + mockVault2.approve(ROUTER_PLUS_SOURCE, vaultTokenAmount2); + + // Execute deposit4626 + SuperformRouterPlus(ROUTER_PLUS_SOURCE).deposit4626{ value: 1 ether }(vaults, argsArray); + + vm.stopPrank(); + + // Verify the results + assertGt( + SuperPositions(SUPER_POSITIONS_SOURCE).balanceOf(deployer, superformId2), + 0, + "Superform balance should be greater than 0" + ); + + // Check that the vault tokens were transferred from the deployer + assertEq(mockVault1.balanceOf(deployer), 0, "Deployer's vault token balance should be 0"); + assertEq(mockVault2.balanceOf(deployer), 0, "Deployer's vault token balance should be 0"); + + // Check that the RouterPlus contract doesn't hold any tokens + assertEq(mockVault1.balanceOf(ROUTER_PLUS_SOURCE), 0, "RouterPlus should not hold any vault tokens"); + assertEq(mockVault2.balanceOf(ROUTER_PLUS_SOURCE), 0, "RouterPlus should not hold any vault tokens"); + + assertEq( + MockERC20(getContract(SOURCE_CHAIN, "DAI")).balanceOf(ROUTER_PLUS_SOURCE), + 0, + "RouterPlus should not hold any DAI" + ); + assertEq( + MockERC20(getContract(SOURCE_CHAIN, "USDC")).balanceOf(ROUTER_PLUS_SOURCE), + 0, + "RouterPlus should not hold any USDC" + ); } function test_rebalanceSinglePosition_zeroAddressInterimAsset() public { @@ -539,7 +655,8 @@ contract SuperformRouterPlusTest is ProtocolActions { // Deploy a mock ERC4626 vault VaultMock mockVault = new VaultMock(IERC20(getContract(SOURCE_CHAIN, "DAI")), "Mock Vault", "mVLT"); - address[] calldata vaults = new address[](1); + + address[] memory vaults = new address[](1); vaults[0] = address(mockVault); // Mint some DAI to the deployer @@ -550,7 +667,8 @@ contract SuperformRouterPlusTest is ProtocolActions { uint256 vaultTokenAmount = mockVault.deposit(daiAmount, deployer); // Prepare deposit4626 args - Deposit4626Args[] calldata argsArray = new Deposit4626Args[](1); + ISuperformRouterPlus.Deposit4626Args[] memory argsArray = new ISuperformRouterPlus.Deposit4626Args[](1); + ISuperformRouterPlus.Deposit4626Args memory args = ISuperformRouterPlus.Deposit4626Args({ amount: vaultTokenAmount, expectedOutputAmount: daiAmount * 10, // Assuming a large value for revert @@ -559,6 +677,7 @@ contract SuperformRouterPlusTest is ProtocolActions { depositCallData: _buildDepositCallData(superformId1, daiAmount) }); argsArray[0] = args; + // Approve RouterPlus to spend vault tokens mockVault.approve(ROUTER_PLUS_SOURCE, vaultTokenAmount); @@ -572,7 +691,8 @@ contract SuperformRouterPlusTest is ProtocolActions { // Deploy a mock ERC4626 vault VaultMock mockVault = new VaultMock(IERC20(getContract(SOURCE_CHAIN, "DAI")), "Mock Vault", "mVLT"); - address[] calldata vaults = new address[](1); + + address[] memory vaults = new address[](1); vaults[0] = address(mockVault); // Mint some DAI to the deployer @@ -591,7 +711,8 @@ contract SuperformRouterPlusTest is ProtocolActions { ); // Prepare deposit4626 args - Deposit4626Args[] calldata argsArray = new Deposit4626Args[](1); + ISuperformRouterPlus.Deposit4626Args[] memory argsArray = new ISuperformRouterPlus.Deposit4626Args[](1); + ISuperformRouterPlus.Deposit4626Args memory args = ISuperformRouterPlus.Deposit4626Args({ amount: vaultTokenAmount, expectedOutputAmount: daiAmount, @@ -617,7 +738,8 @@ contract SuperformRouterPlusTest is ProtocolActions { // Deploy a mock ERC4626 vault VaultMock mockVault = new VaultMock(IERC20(getContract(SOURCE_CHAIN, "DAI")), "Mock Vault", "mVLT"); - address[] calldata vaults = new address[](1); + + address[] memory vaults = new address[](1); vaults[0] = address(mockVault); // Mint some DAI to the deployer @@ -636,7 +758,8 @@ contract SuperformRouterPlusTest is ProtocolActions { ); // Prepare deposit4626 args - Deposit4626Args[] calldata argsArray = new Deposit4626Args[](1); + ISuperformRouterPlus.Deposit4626Args[] memory argsArray = new ISuperformRouterPlus.Deposit4626Args[](1); + ISuperformRouterPlus.Deposit4626Args memory args = ISuperformRouterPlus.Deposit4626Args({ amount: vaultTokenAmount, expectedOutputAmount: daiAmount, @@ -662,7 +785,8 @@ contract SuperformRouterPlusTest is ProtocolActions { // Deploy a mock ERC4626 vault VaultMock mockVault = new VaultMock(IERC20(getContract(SOURCE_CHAIN, "DAI")), "Mock Vault", "mVLT"); - address[] calldata vaults = new address[](1); + + address[] memory vaults = new address[](1); vaults[0] = address(mockVault); // Mint some DAI to the deployer @@ -673,7 +797,8 @@ contract SuperformRouterPlusTest is ProtocolActions { uint256 vaultTokenAmount = mockVault.deposit(daiAmount, deployer); // Prepare deposit4626 args - Deposit4626Args[] calldata argsArray = new Deposit4626Args[](1); + ISuperformRouterPlus.Deposit4626Args[] memory argsArray = new ISuperformRouterPlus.Deposit4626Args[](1); + ISuperformRouterPlus.Deposit4626Args memory args = ISuperformRouterPlus.Deposit4626Args({ amount: vaultTokenAmount, expectedOutputAmount: daiAmount, // Assuming 1:1 ratio for simplicity @@ -2545,7 +2670,8 @@ contract SuperformRouterPlusTest is ProtocolActions { // Deploy a mock ERC4626 vault VaultMock mockVault = new VaultMock(IERC20(getContract(SOURCE_CHAIN, "DAI")), "Mock Vault", "mVLT"); - address[] calldata vaults = new address[](1); + + address[] memory vaults = new address[](1); vaults[0] = address(mockVault); // Mint some DAI to the deployer @@ -2556,7 +2682,8 @@ contract SuperformRouterPlusTest is ProtocolActions { uint256 vaultTokenAmount = mockVault.deposit(daiAmount, deployer); // Prepare deposit4626 args - Deposit4626Args[] calldata argsArray = new Deposit4626Args[](1); + ISuperformRouterPlus.Deposit4626Args[] memory argsArray = new ISuperformRouterPlus.Deposit4626Args[](1); + ISuperformRouterPlus.Deposit4626Args memory args = ISuperformRouterPlus.Deposit4626Args({ amount: vaultTokenAmount, expectedOutputAmount: daiAmount, // Assuming 1:1 ratio for simplicity From af6ed6623f68f65f818a02682beaf9e733d3137a Mon Sep 17 00:00:00 2001 From: Tamara Ringas <69479754+TamaraRingas@users.noreply.github.com> Date: Fri, 25 Oct 2024 18:41:09 +0200 Subject: [PATCH 09/14] fix: update approvals in test_deposit4626_multipleVaults() --- .../router-plus/SuperformRouterPlus.t.sol | 39 ++++++++----------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/test/unit/router-plus/SuperformRouterPlus.t.sol b/test/unit/router-plus/SuperformRouterPlus.t.sol index 9d06abefd..1d25d6630 100644 --- a/test/unit/router-plus/SuperformRouterPlus.t.sol +++ b/test/unit/router-plus/SuperformRouterPlus.t.sol @@ -514,7 +514,7 @@ contract SuperformRouterPlusTest is ProtocolActions { // Deploy two mock ERC4626 vaults VaultMock mockVault1 = new VaultMock(IERC20(getContract(SOURCE_CHAIN, "DAI")), "Mock Vault 1", "mVLT1"); - VaultMock mockVault2 = new VaultMock(IERC20(getContract(SOURCE_CHAIN, "USDC")), "Mock Vault 2", "mVLT2"); + VaultMock mockVault2 = new VaultMock(IERC20(getContract(SOURCE_CHAIN, "DAI")), "Mock Vault 2", "mVLT2"); address[] memory vaults = new address[](2); vaults[0] = address(mockVault1); @@ -522,49 +522,49 @@ contract SuperformRouterPlusTest is ProtocolActions { // Mint some DAI to the deployer uint256 daiAmount = 1e18; + deal(getContract(SOURCE_CHAIN, "DAI"), deployer, 10e18); - // Mint some USDC to the deployer - uint256 usdcAmount = 1e18; - - // Approve and deposit DAI into the mock vault + // Approve and deposit DAI into the first mock vault MockERC20(getContract(SOURCE_CHAIN, "DAI")).approve(address(mockVault1), daiAmount); - uint256 vaultTokenAmount1 = mockVault1.deposit(daiAmount, deployer); - // Approve and deposit USDC into the mock vault - MockERC20(getContract(SOURCE_CHAIN, "USDC")).approve(address(mockVault2), usdcAmount); - uint256 vaultTokenAmount2 = mockVault2.deposit(usdcAmount, deployer); + uint256 vaultTokenAmount1 = mockVault1.deposit(daiAmount/2, deployer); + + // Approve and deposit DAI into the secondmock vault + MockERC20(getContract(SOURCE_CHAIN, "DAI")).approve(address(mockVault2), daiAmount); + uint256 vaultTokenAmount2 = mockVault2.deposit(daiAmount/2, deployer); // Prepare deposit4626 args for both deposits ISuperformRouterPlus.Deposit4626Args[] memory argsArray = new ISuperformRouterPlus.Deposit4626Args[](2); argsArray[0] = ISuperformRouterPlus.Deposit4626Args({ amount: vaultTokenAmount1, - expectedOutputAmount: daiAmount, + expectedOutputAmount: daiAmount/2, maxSlippage: 100, // 1% receiverAddressSP: deployer, - depositCallData: _buildDepositCallData(superformId1, daiAmount) + depositCallData: _buildDepositCallData(superformId1, daiAmount/2) }); argsArray[1] = ISuperformRouterPlus.Deposit4626Args({ amount: vaultTokenAmount2, - expectedOutputAmount: usdcAmount, + expectedOutputAmount: daiAmount/2, maxSlippage: 100, // 1% receiverAddressSP: deployer, - depositCallData: _buildDepositCallData(superformId2, usdcAmount) + depositCallData: _buildDepositCallData(superformId1, daiAmount/2) }); // Approve RouterPlus to spend vault tokens - mockVault1.approve(ROUTER_PLUS_SOURCE, vaultTokenAmount1); - mockVault2.approve(ROUTER_PLUS_SOURCE, vaultTokenAmount2); + mockVault1.approve(ROUTER_PLUS_SOURCE, daiAmount); + mockVault2.approve(ROUTER_PLUS_SOURCE, daiAmount); // Execute deposit4626 - SuperformRouterPlus(ROUTER_PLUS_SOURCE).deposit4626{ value: 1 ether }(vaults, argsArray); + vm.recordLogs(); + SuperformRouterPlus(ROUTER_PLUS_SOURCE).deposit4626{ value: 2 ether }(vaults, argsArray); vm.stopPrank(); // Verify the results assertGt( - SuperPositions(SUPER_POSITIONS_SOURCE).balanceOf(deployer, superformId2), + SuperPositions(SUPER_POSITIONS_SOURCE).balanceOf(deployer, superformId1), 0, "Superform balance should be greater than 0" ); @@ -582,11 +582,6 @@ contract SuperformRouterPlusTest is ProtocolActions { 0, "RouterPlus should not hold any DAI" ); - assertEq( - MockERC20(getContract(SOURCE_CHAIN, "USDC")).balanceOf(ROUTER_PLUS_SOURCE), - 0, - "RouterPlus should not hold any USDC" - ); } function test_rebalanceSinglePosition_zeroAddressInterimAsset() public { From 9ede180976b74ae561208b992be2def261dc0959 Mon Sep 17 00:00:00 2001 From: Tamara Ringas <69479754+TamaraRingas@users.noreply.github.com> Date: Fri, 25 Oct 2024 19:00:00 +0200 Subject: [PATCH 10/14] fix: update msg.value in _deposit4626() --- src/router-plus/SuperformRouterPlus.sol | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/router-plus/SuperformRouterPlus.sol b/src/router-plus/SuperformRouterPlus.sol index eedd49b09..bca85b604 100644 --- a/src/router-plus/SuperformRouterPlus.sol +++ b/src/router-plus/SuperformRouterPlus.sol @@ -370,7 +370,7 @@ contract SuperformRouterPlus is ISuperformRouterPlus, BaseSuperformRouterPlus { if (!whitelistedSelectors[Actions.DEPOSIT][_parseSelectorMem(args[i].depositCallData)]) { revert INVALID_DEPOSIT_SELECTOR(); } - _deposit4626(vaults_[i], args[i]); + _deposit4626(vaults_[i], args[i], vaults_.length); } } @@ -603,7 +603,7 @@ contract SuperformRouterPlus is ISuperformRouterPlus, BaseSuperformRouterPlus { /// @notice deposits ERC4626 vault shares into superform /// @param vault_ The ERC4626 vault to redeem from /// @param args Rest of the arguments to deposit 4626 - function _deposit4626(address vault_, Deposit4626Args calldata args) internal { + function _deposit4626(address vault_, Deposit4626Args calldata args, uint256 arrayLength) internal { _transferERC20In(IERC20(vault_), args.receiverAddressSP, args.amount); IERC4626 vault = IERC4626(vault_); address assetAdr = vault.asset(); @@ -617,8 +617,9 @@ contract SuperformRouterPlus is ISuperformRouterPlus, BaseSuperformRouterPlus { revert INVALID_DEPOSIT_SELECTOR(); } + uint256 msgValue = msg.value / arrayLength; address router = _getAddress(keccak256("SUPERFORM_ROUTER")); - _deposit(router, asset, amountRedeemed, msg.value, args.depositCallData); + _deposit(router, asset, amountRedeemed, msgValue, args.depositCallData); _tokenRefunds(router, assetAdr, args.receiverAddressSP, balanceBefore); From dc308cf408a7d4dd85317cf3043dfd6f755d61c5 Mon Sep 17 00:00:00 2001 From: Tamara Ringas <69479754+TamaraRingas@users.noreply.github.com> Date: Fri, 25 Oct 2024 19:21:02 +0200 Subject: [PATCH 11/14] fix: store vaults length as var --- src/router-plus/SuperformRouterPlus.sol | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/router-plus/SuperformRouterPlus.sol b/src/router-plus/SuperformRouterPlus.sol index bca85b604..db9af4c64 100644 --- a/src/router-plus/SuperformRouterPlus.sol +++ b/src/router-plus/SuperformRouterPlus.sol @@ -357,20 +357,22 @@ contract SuperformRouterPlus is ISuperformRouterPlus, BaseSuperformRouterPlus { /// @inheritdoc ISuperformRouterPlus function deposit4626(address[] calldata vaults_, Deposit4626Args[] calldata args) external payable { + + uint256 length = vaults_.length; - if (vaults_.length != args.length) { + if (length != args.length) { revert Error.ARRAY_LENGTH_MISMATCH(); } - if (vaults_.length == 0) { + if (length == 0) { revert Error.ARRAY_LENGTH_MISMATCH(); } - for (uint256 i; i < vaults_.length; ++i) { + for (uint256 i; i < length; ++i) { if (!whitelistedSelectors[Actions.DEPOSIT][_parseSelectorMem(args[i].depositCallData)]) { revert INVALID_DEPOSIT_SELECTOR(); } - _deposit4626(vaults_[i], args[i], vaults_.length); + _deposit4626(vaults_[i], args[i], length); } } From a1c4574b831de69cdfac51cd6ca1d17118b1735e Mon Sep 17 00:00:00 2001 From: Tamara Ringas <69479754+TamaraRingas@users.noreply.github.com> Date: Sat, 26 Oct 2024 10:11:19 +0200 Subject: [PATCH 12/14] fix: update errors in deposit4626() --- src/router-plus/SuperformRouterPlus.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/router-plus/SuperformRouterPlus.sol b/src/router-plus/SuperformRouterPlus.sol index db9af4c64..e49100275 100644 --- a/src/router-plus/SuperformRouterPlus.sol +++ b/src/router-plus/SuperformRouterPlus.sol @@ -365,7 +365,7 @@ contract SuperformRouterPlus is ISuperformRouterPlus, BaseSuperformRouterPlus { } if (length == 0) { - revert Error.ARRAY_LENGTH_MISMATCH(); + revert Error.ZERO_INPUT_VALUE(); } for (uint256 i; i < length; ++i) { From 69f524a43e018113c8b1e860477dc4408a774ff4 Mon Sep 17 00:00:00 2001 From: Tamara Ringas <69479754+TamaraRingas@users.noreply.github.com> Date: Sat, 26 Oct 2024 10:13:42 +0200 Subject: [PATCH 13/14] fix: rm unnecessary check in _deposit4626() --- src/router-plus/SuperformRouterPlus.sol | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/router-plus/SuperformRouterPlus.sol b/src/router-plus/SuperformRouterPlus.sol index e49100275..419aa6dde 100644 --- a/src/router-plus/SuperformRouterPlus.sol +++ b/src/router-plus/SuperformRouterPlus.sol @@ -615,10 +615,6 @@ contract SuperformRouterPlus is ISuperformRouterPlus, BaseSuperformRouterPlus { uint256 amountRedeemed = _redeemShare(vault, assetAdr, args.amount, args.expectedOutputAmount, args.maxSlippage); - if (!whitelistedSelectors[Actions.DEPOSIT][_parseSelectorMem(args.depositCallData)]) { - revert INVALID_DEPOSIT_SELECTOR(); - } - uint256 msgValue = msg.value / arrayLength; address router = _getAddress(keccak256("SUPERFORM_ROUTER")); _deposit(router, asset, amountRedeemed, msgValue, args.depositCallData); From 671d0d43dbc3eba0035d1c483003e5a45bf90e48 Mon Sep 17 00:00:00 2001 From: Tamara Ringas <69479754+TamaraRingas@users.noreply.github.com> Date: Sat, 26 Oct 2024 10:14:57 +0200 Subject: [PATCH 14/14] chore: add test_deposit4626_multipleVaults_zeroInputValue() --- test/unit/router-plus/SuperformRouterPlus.t.sol | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/unit/router-plus/SuperformRouterPlus.t.sol b/test/unit/router-plus/SuperformRouterPlus.t.sol index 1d25d6630..8e029af84 100644 --- a/test/unit/router-plus/SuperformRouterPlus.t.sol +++ b/test/unit/router-plus/SuperformRouterPlus.t.sol @@ -509,6 +509,16 @@ contract SuperformRouterPlusTest is ProtocolActions { vm.stopPrank(); } + function test_deposit4626_multipleVaults_zeroInputValue() public { + vm.startPrank(deployer); + + address[] memory vaults = new address[](0); + ISuperformRouterPlus.Deposit4626Args[] memory argsArray = new ISuperformRouterPlus.Deposit4626Args[](0); + + vm.expectRevert(Error.ZERO_INPUT_VALUE.selector); + SuperformRouterPlus(ROUTER_PLUS_SOURCE).deposit4626{ value: 1 ether }(vaults, argsArray); + } + function test_deposit4626_multipleVaults() public { vm.startPrank(deployer);