From 28d8e0f15d97738d20d41e48837eb8608965a62b Mon Sep 17 00:00:00 2001 From: 0xtekgrinder <0xtekgrinder@protonmail.com> Date: Mon, 5 Aug 2024 16:10:00 +0200 Subject: [PATCH 01/11] feat: deposit whole balance into vault if max amount provided --- contracts/BaseRouter.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/BaseRouter.sol b/contracts/BaseRouter.sol index 3d6b414..d4b6f17 100644 --- a/contracts/BaseRouter.sol +++ b/contracts/BaseRouter.sol @@ -481,6 +481,7 @@ abstract contract BaseRouter is Initializable, IDepositWithReferral { address to, uint256 minSharesOut ) internal returns (uint256 sharesOut) { + if (amount == type(uint256).max) amount = IERC20(savingsRate.asset()).balanceOf(address(this)); _slippageCheck(sharesOut = savingsRate.deposit(amount, to), minSharesOut); } From 23107e732df787070f2be42c2ce0398a0e167312 Mon Sep 17 00:00:00 2001 From: 0xtekgrinder <0xtekgrinder@protonmail.com> Date: Mon, 5 Aug 2024 18:15:34 +0200 Subject: [PATCH 02/11] tests: add test with max balance --- test/foundry/AngeRouterMainnet.t.sol | 53 +++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/test/foundry/AngeRouterMainnet.t.sol b/test/foundry/AngeRouterMainnet.t.sol index 02ae28a..2807ecb 100644 --- a/test/foundry/AngeRouterMainnet.t.sol +++ b/test/foundry/AngeRouterMainnet.t.sol @@ -101,7 +101,12 @@ contract AngleRouterMainnetTest is BaseTest { assertEq(token.balanceOf(address(to)), 0); } - function testMint4626ForgotFunds(uint256 initShares, uint256 shares, uint256 maxAmount, uint256 gainOrLoss) public { + function testMint4626ForgotFunds( + uint256 initShares, + uint256 shares, + uint256 maxAmount, + uint256 gainOrLoss + ) public { address to = address(router); uint256 balanceUsers = BASE_TOKENS * 1 ether; deal(address(token), address(_alice), balanceUsers); @@ -189,6 +194,52 @@ contract AngleRouterMainnetTest is BaseTest { assertEq(token.balanceOf(address(to)), 0); } + function testDeposit4626MaxBalance( + uint256 initShares, + uint256 amount, + uint256 minSharesOut, + uint256 gainOrLoss + ) public { + address to = address(router); + + uint256 balanceUsers = BASE_TOKENS * 1 ether; + deal(address(token), address(_alice), balanceUsers); + + _randomizeSavingsRate(gainOrLoss, initShares); + + amount = bound(amount, 0, balanceUsers); + uint256 previewDeposit = savingsRate.previewDeposit(amount); + + PermitType[] memory paramsPermit = new PermitType[](0); + ActionType[] memory actionType = new ActionType[](2); + bytes[] memory data = new bytes[](2); + + actionType[0] = ActionType.transfer; + data[0] = abi.encode(token, router, amount); + actionType[1] = ActionType.deposit4626; + data[1] = abi.encode(token, savingsRate, type(uint256).max, to, minSharesOut); + + uint256 mintedShares = savingsRate.convertToShares(amount); + + vm.startPrank(_alice); + token.approve(address(router), type(uint256).max); + // as this is a mock vault, previewMint is exactly what is needed to mint + if (previewDeposit < minSharesOut) { + vm.expectRevert(BaseRouter.TooSmallAmountOut.selector); + router.mixer(paramsPermit, actionType, data); + return; + } else { + router.mixer(paramsPermit, actionType, data); + } + vm.stopPrank(); + + assertEq(savingsRate.balanceOf(address(to)), previewDeposit); + assertEq(savingsRate.balanceOf(address(to)), mintedShares); + + assertEq(token.balanceOf(address(router)), 0); + assertEq(token.balanceOf(address(_alice)), balanceUsers - amount); + } + function testDeposit4626ForgotFunds( uint256 initShares, uint256 amount, From 7e750d2df2028394f5ee98d3b661a4b0a53b0d07 Mon Sep 17 00:00:00 2001 From: 0xtekgrinder <0xtekgrinder@protonmail.com> Date: Tue, 6 Aug 2024 10:13:23 +0200 Subject: [PATCH 03/11] feat: max amount for mint, withdraw and redeem --- contracts/BaseRouter.sol | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contracts/BaseRouter.sol b/contracts/BaseRouter.sol index d4b6f17..a327bae 100644 --- a/contracts/BaseRouter.sol +++ b/contracts/BaseRouter.sol @@ -251,6 +251,7 @@ abstract contract BaseRouter is Initializable, IDepositWithReferral { data[i], (IERC20, IERC4626, uint256, address, uint256) ); + if (shares == type(uint256).max) shares = savingsRate.convertToShares(token.balanceOf(address(this))); _changeAllowance(token, address(savingsRate), type(uint256).max); _mint4626(savingsRate, shares, to, maxAmountIn); } else if (actions[i] == ActionType.deposit4626) { @@ -258,6 +259,7 @@ abstract contract BaseRouter is Initializable, IDepositWithReferral { data[i], (IERC20, IERC4626, uint256, address, uint256) ); + if (amount == type(uint256).max) amount = token.balanceOf(address(this)); _changeAllowance(token, address(savingsRate), type(uint256).max); _deposit4626(savingsRate, amount, to, minSharesOut); } else if (actions[i] == ActionType.deposit4626Referral) { @@ -269,6 +271,7 @@ abstract contract BaseRouter is Initializable, IDepositWithReferral { uint256 minSharesOut, address referrer ) = abi.decode(data[i], (IERC20, IERC4626, uint256, address, uint256, address)); + if (amount == type(uint256).max) amount = token.balanceOf(address(this)); _changeAllowance(token, address(savingsRate), type(uint256).max); _deposit4626Referral(savingsRate, amount, to, minSharesOut, referrer); } else if (actions[i] == ActionType.redeem4626) { @@ -276,6 +279,7 @@ abstract contract BaseRouter is Initializable, IDepositWithReferral { data[i], (IERC4626, uint256, address, uint256) ); + if (shares == type(uint256).max) shares = savingsRate.balanceOf(address(this)); _redeem4626(savingsRate, shares, to, minAmountOut); } else if (actions[i] == ActionType.withdraw4626) { (IERC4626 savingsRate, uint256 amount, address to, uint256 maxSharesOut) = abi.decode( From 184a826839b7b96b2a7b297a7985d6209365c172 Mon Sep 17 00:00:00 2001 From: 0xtekgrinder <0xtekgrinder@protonmail.com> Date: Tue, 6 Aug 2024 10:40:09 +0200 Subject: [PATCH 04/11] tests: max balance --- contracts/BaseRouter.sol | 6 +- test/foundry/AngeRouterMainnet.t.sol | 116 +++++++++++++++++++++++++++ 2 files changed, 120 insertions(+), 2 deletions(-) diff --git a/contracts/BaseRouter.sol b/contracts/BaseRouter.sol index a327bae..b757cb1 100644 --- a/contracts/BaseRouter.sol +++ b/contracts/BaseRouter.sol @@ -48,6 +48,8 @@ import "./interfaces/ILiquidityGauge.sol"; import "./interfaces/ISwapper.sol"; import "./interfaces/IVaultManager.sol"; +import "forge-std/console.sol"; + // ============================== STRUCTS AND ENUM ============================= /// @notice Action types @@ -251,7 +253,7 @@ abstract contract BaseRouter is Initializable, IDepositWithReferral { data[i], (IERC20, IERC4626, uint256, address, uint256) ); - if (shares == type(uint256).max) shares = savingsRate.convertToShares(token.balanceOf(address(this))); + if (shares == type(uint256).max) shares = savingsRate.previewDeposit(token.balanceOf(address(this))); _changeAllowance(token, address(savingsRate), type(uint256).max); _mint4626(savingsRate, shares, to, maxAmountIn); } else if (actions[i] == ActionType.deposit4626) { @@ -279,7 +281,7 @@ abstract contract BaseRouter is Initializable, IDepositWithReferral { data[i], (IERC4626, uint256, address, uint256) ); - if (shares == type(uint256).max) shares = savingsRate.balanceOf(address(this)); + if (shares == type(uint256).max) shares = savingsRate.balanceOf(msg.sender); _redeem4626(savingsRate, shares, to, minAmountOut); } else if (actions[i] == ActionType.withdraw4626) { (IERC4626 savingsRate, uint256 amount, address to, uint256 maxSharesOut) = abi.decode( diff --git a/test/foundry/AngeRouterMainnet.t.sol b/test/foundry/AngeRouterMainnet.t.sol index 2807ecb..34a2b88 100644 --- a/test/foundry/AngeRouterMainnet.t.sol +++ b/test/foundry/AngeRouterMainnet.t.sol @@ -147,6 +147,50 @@ contract AngleRouterMainnetTest is BaseTest { assertEq(token.balanceOf(address(to)), 0); } + function testMint4626MaxBalance( + uint256 initShares, + uint256 maxAmount, + uint256 gainOrLoss + ) public { + address to = address(router); + uint256 balanceUsers = BASE_TOKENS * 1 ether; + deal(address(token), address(_alice), balanceUsers); + + _randomizeSavingsRate(gainOrLoss, initShares); + + uint256 shares = savingsRate.previewDeposit(balanceUsers); + uint256 previewMint = savingsRate.previewMint(shares); + + // this can be done with foundry though + // https://book.getfoundry.sh/tutorials/testing-eip712?highlight=permit#diving-in + PermitType[] memory paramsPermit = new PermitType[](0); + ActionType[] memory actionType = new ActionType[](2); + bytes[] memory data = new bytes[](2); + + actionType[0] = ActionType.transfer; + data[0] = abi.encode(token, router, previewMint); + actionType[1] = ActionType.mint4626; + data[1] = abi.encode(token, savingsRate, type(uint256).max, to, maxAmount); + + vm.startPrank(_alice); + token.approve(address(router), type(uint256).max); + // as this is a mock vault, previewMint is exactly what is needed to mint + if (maxAmount < previewMint) { + vm.expectRevert(BaseRouter.TooSmallAmountOut.selector); + router.mixer(paramsPermit, actionType, data); + return; + } else { + router.mixer(paramsPermit, actionType, data); + } + vm.stopPrank(); + + assertEq(savingsRate.balanceOf(address(to)), shares); + + assertEq(token.balanceOf(address(router)), 0); + assertEq(token.balanceOf(address(_alice)), balanceUsers - previewMint); + assertEq(token.balanceOf(address(to)), 0); + } + function testDeposit4626GoodPractice( uint256 initShares, uint256 amount, @@ -438,6 +482,78 @@ contract AngleRouterMainnetTest is BaseTest { assertEq(token.balanceOf(address(_alice)), balanceUsers - aliceAmount); } + function testRedeem4626MaxBalance( + uint256 initShares, + uint256 aliceAmount, + uint256 minAmount, + uint256 gainOrLoss, + uint256 gainOrLoss2 + ) public { + uint256 balanceUsers = BASE_TOKENS * 1 ether; + deal(address(token), address(_alice), balanceUsers); + + _randomizeSavingsRate(gainOrLoss, initShares); + + aliceAmount = bound(aliceAmount, 0, balanceUsers); + uint256 previewDeposit = savingsRate.previewDeposit(aliceAmount); + // otherwise there could be overflows + vm.assume(previewDeposit < type(uint256).max / BASE_PARAMS); + + uint256 previewRedeem; + { + // do a first deposit + PermitType[] memory paramsPermit = new PermitType[](0); + ActionType[] memory actionType = new ActionType[](2); + bytes[] memory data = new bytes[](2); + + actionType[0] = ActionType.transfer; + data[0] = abi.encode(token, router, aliceAmount); + actionType[1] = ActionType.deposit4626; + data[1] = abi.encode(token, savingsRate, aliceAmount, _alice, previewDeposit); + + vm.startPrank(_alice); + token.approve(address(router), type(uint256).max); + router.mixer(paramsPermit, actionType, data); + vm.stopPrank(); + + assertEq(savingsRate.balanceOf(address(router)), 0); + assertEq(savingsRate.balanceOf(address(_alice)), previewDeposit); + assertEq(token.balanceOf(address(router)), 0); + assertEq(token.balanceOf(address(_alice)), balanceUsers - aliceAmount); + + // make the savings rate have a loss / gain + gainOrLoss2 = bound(gainOrLoss2, 1, 1 ether * 1 ether); + deal(address(token), address(savingsRate), gainOrLoss2); + + // then redeem + uint256 sharesToBurn = savingsRate.balanceOf(_alice); + + actionType = new ActionType[](1); + data = new bytes[](1); + + actionType[0] = ActionType.redeem4626; + data[0] = abi.encode(savingsRate, type(uint256).max, address(router), minAmount); + + previewRedeem = savingsRate.previewRedeem(sharesToBurn); + vm.startPrank(_alice); + savingsRate.approve(address(router), type(uint256).max); + // as this is a mock vault, previewRedeem is exactly what should be received + if (previewRedeem < minAmount) { + vm.expectRevert(BaseRouter.TooSmallAmountOut.selector); + router.mixer(paramsPermit, actionType, data); + return; + } else { + router.mixer(paramsPermit, actionType, data); + } + vm.stopPrank(); + assertEq(savingsRate.balanceOf(address(_alice)), previewDeposit - sharesToBurn); + } + + assertEq(savingsRate.balanceOf(address(router)), 0); + assertEq(token.balanceOf(address(router)), previewRedeem); + assertEq(token.balanceOf(address(_alice)), balanceUsers - aliceAmount); + } + function testWithdraw4626GoodPractice( uint256 initShares, uint256 aliceAmount, From 3dca55a8fe08f50451ea57781ae6ecd42cd874fd Mon Sep 17 00:00:00 2001 From: 0xtekgrinder <0xtekgrinder@protonmail.com> Date: Tue, 6 Aug 2024 11:11:20 +0200 Subject: [PATCH 05/11] feat: remove mint with max balance --- contracts/BaseRouter.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/contracts/BaseRouter.sol b/contracts/BaseRouter.sol index b757cb1..08ab124 100644 --- a/contracts/BaseRouter.sol +++ b/contracts/BaseRouter.sol @@ -253,7 +253,6 @@ abstract contract BaseRouter is Initializable, IDepositWithReferral { data[i], (IERC20, IERC4626, uint256, address, uint256) ); - if (shares == type(uint256).max) shares = savingsRate.previewDeposit(token.balanceOf(address(this))); _changeAllowance(token, address(savingsRate), type(uint256).max); _mint4626(savingsRate, shares, to, maxAmountIn); } else if (actions[i] == ActionType.deposit4626) { @@ -487,7 +486,6 @@ abstract contract BaseRouter is Initializable, IDepositWithReferral { address to, uint256 minSharesOut ) internal returns (uint256 sharesOut) { - if (amount == type(uint256).max) amount = IERC20(savingsRate.asset()).balanceOf(address(this)); _slippageCheck(sharesOut = savingsRate.deposit(amount, to), minSharesOut); } From 18491d66c76041e9d6ff07cf82ee5b2235a52582 Mon Sep 17 00:00:00 2001 From: 0xtekgrinder <0xtekgrinder@protonmail.com> Date: Tue, 6 Aug 2024 11:11:36 +0200 Subject: [PATCH 06/11] tests: add referral deposit test and remove max mint one --- test/foundry/AngeRouterMainnet.t.sol | 61 +++++++++++++++------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/test/foundry/AngeRouterMainnet.t.sol b/test/foundry/AngeRouterMainnet.t.sol index 34a2b88..8b63d6f 100644 --- a/test/foundry/AngeRouterMainnet.t.sol +++ b/test/foundry/AngeRouterMainnet.t.sol @@ -147,35 +147,36 @@ contract AngleRouterMainnetTest is BaseTest { assertEq(token.balanceOf(address(to)), 0); } - function testMint4626MaxBalance( + function testDeposit4626GoodPractice( uint256 initShares, - uint256 maxAmount, + uint256 amount, + uint256 minSharesOut, + address to, uint256 gainOrLoss ) public { - address to = address(router); + vm.assume(to != address(0) && to != address(router) && to != address(savingsRate)); + uint256 balanceUsers = BASE_TOKENS * 1 ether; deal(address(token), address(_alice), balanceUsers); _randomizeSavingsRate(gainOrLoss, initShares); - uint256 shares = savingsRate.previewDeposit(balanceUsers); - uint256 previewMint = savingsRate.previewMint(shares); + amount = bound(amount, 0, balanceUsers); + uint256 previewDeposit = savingsRate.previewDeposit(amount); - // this can be done with foundry though - // https://book.getfoundry.sh/tutorials/testing-eip712?highlight=permit#diving-in PermitType[] memory paramsPermit = new PermitType[](0); ActionType[] memory actionType = new ActionType[](2); bytes[] memory data = new bytes[](2); actionType[0] = ActionType.transfer; - data[0] = abi.encode(token, router, previewMint); - actionType[1] = ActionType.mint4626; - data[1] = abi.encode(token, savingsRate, type(uint256).max, to, maxAmount); + data[0] = abi.encode(token, router, amount); + actionType[1] = ActionType.deposit4626; + data[1] = abi.encode(token, savingsRate, amount, to, minSharesOut); vm.startPrank(_alice); token.approve(address(router), type(uint256).max); // as this is a mock vault, previewMint is exactly what is needed to mint - if (maxAmount < previewMint) { + if (previewDeposit < minSharesOut) { vm.expectRevert(BaseRouter.TooSmallAmountOut.selector); router.mixer(paramsPermit, actionType, data); return; @@ -184,21 +185,22 @@ contract AngleRouterMainnetTest is BaseTest { } vm.stopPrank(); - assertEq(savingsRate.balanceOf(address(to)), shares); + assertEq(savingsRate.balanceOf(address(router)), 0); + assertEq(savingsRate.balanceOf(address(_alice)), 0); + assertEq(savingsRate.balanceOf(address(to)), previewDeposit); assertEq(token.balanceOf(address(router)), 0); - assertEq(token.balanceOf(address(_alice)), balanceUsers - previewMint); + assertEq(token.balanceOf(address(_alice)), balanceUsers - amount); assertEq(token.balanceOf(address(to)), 0); } - function testDeposit4626GoodPractice( + function testDeposit4626MaxBalance( uint256 initShares, uint256 amount, uint256 minSharesOut, - address to, uint256 gainOrLoss ) public { - vm.assume(to != address(0) && to != address(router) && to != address(savingsRate)); + address to = address(router); uint256 balanceUsers = BASE_TOKENS * 1 ether; deal(address(token), address(_alice), balanceUsers); @@ -215,7 +217,9 @@ contract AngleRouterMainnetTest is BaseTest { actionType[0] = ActionType.transfer; data[0] = abi.encode(token, router, amount); actionType[1] = ActionType.deposit4626; - data[1] = abi.encode(token, savingsRate, amount, to, minSharesOut); + data[1] = abi.encode(token, savingsRate, type(uint256).max, to, minSharesOut); + + uint256 mintedShares = savingsRate.convertToShares(amount); vm.startPrank(_alice); token.approve(address(router), type(uint256).max); @@ -229,16 +233,14 @@ contract AngleRouterMainnetTest is BaseTest { } vm.stopPrank(); - assertEq(savingsRate.balanceOf(address(router)), 0); - assertEq(savingsRate.balanceOf(address(_alice)), 0); assertEq(savingsRate.balanceOf(address(to)), previewDeposit); + assertEq(savingsRate.balanceOf(address(to)), mintedShares); assertEq(token.balanceOf(address(router)), 0); assertEq(token.balanceOf(address(_alice)), balanceUsers - amount); - assertEq(token.balanceOf(address(to)), 0); } - function testDeposit4626MaxBalance( + function testDeposit4626ForgotFunds( uint256 initShares, uint256 amount, uint256 minSharesOut, @@ -261,9 +263,7 @@ contract AngleRouterMainnetTest is BaseTest { actionType[0] = ActionType.transfer; data[0] = abi.encode(token, router, amount); actionType[1] = ActionType.deposit4626; - data[1] = abi.encode(token, savingsRate, type(uint256).max, to, minSharesOut); - - uint256 mintedShares = savingsRate.convertToShares(amount); + data[1] = abi.encode(token, savingsRate, amount, to, minSharesOut); vm.startPrank(_alice); token.approve(address(router), type(uint256).max); @@ -278,17 +278,17 @@ contract AngleRouterMainnetTest is BaseTest { vm.stopPrank(); assertEq(savingsRate.balanceOf(address(to)), previewDeposit); - assertEq(savingsRate.balanceOf(address(to)), mintedShares); assertEq(token.balanceOf(address(router)), 0); assertEq(token.balanceOf(address(_alice)), balanceUsers - amount); } - function testDeposit4626ForgotFunds( + function testDepositReferral4626MaxBalance( uint256 initShares, uint256 amount, uint256 minSharesOut, - uint256 gainOrLoss + uint256 gainOrLoss, + address referrer ) public { address to = address(router); @@ -306,8 +306,10 @@ contract AngleRouterMainnetTest is BaseTest { actionType[0] = ActionType.transfer; data[0] = abi.encode(token, router, amount); - actionType[1] = ActionType.deposit4626; - data[1] = abi.encode(token, savingsRate, amount, to, minSharesOut); + actionType[1] = ActionType.deposit4626Referral; + data[1] = abi.encode(token, savingsRate, type(uint256).max, to, minSharesOut, referrer); + + uint256 mintedShares = savingsRate.convertToShares(amount); vm.startPrank(_alice); token.approve(address(router), type(uint256).max); @@ -322,6 +324,7 @@ contract AngleRouterMainnetTest is BaseTest { vm.stopPrank(); assertEq(savingsRate.balanceOf(address(to)), previewDeposit); + assertEq(savingsRate.balanceOf(address(to)), mintedShares); assertEq(token.balanceOf(address(router)), 0); assertEq(token.balanceOf(address(_alice)), balanceUsers - amount); From a393ec2fa05ae43a79b3c25f1bc6e8b82fbd258e Mon Sep 17 00:00:00 2001 From: 0xtekgrinder <0xtekgrinder@protonmail.com> Date: Tue, 6 Aug 2024 11:16:32 +0200 Subject: [PATCH 07/11] feat: remove useless console inside BaserRouter --- contracts/BaseRouter.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/contracts/BaseRouter.sol b/contracts/BaseRouter.sol index 08ab124..b3d468d 100644 --- a/contracts/BaseRouter.sol +++ b/contracts/BaseRouter.sol @@ -48,8 +48,6 @@ import "./interfaces/ILiquidityGauge.sol"; import "./interfaces/ISwapper.sol"; import "./interfaces/IVaultManager.sol"; -import "forge-std/console.sol"; - // ============================== STRUCTS AND ENUM ============================= /// @notice Action types From 8976217ae61564ce2c7ca318f0234626c14b58c7 Mon Sep 17 00:00:00 2001 From: 0xtekgrinder <0xtekgrinder@protonmail.com> Date: Tue, 6 Aug 2024 12:15:48 +0200 Subject: [PATCH 08/11] feat: upgradeRouter script --- .gitmodules | 3 ++ contracts/BaseRouter.sol | 18 ++++++++++-- lib/forge-std | 2 +- lib/utils | 1 + remappings.txt | 1 + scripts/foundry/UpgradeRouter.s.sol | 45 +++++++++++++++++++++++++++++ 6 files changed, 66 insertions(+), 4 deletions(-) create mode 160000 lib/utils create mode 100644 scripts/foundry/UpgradeRouter.s.sol diff --git a/.gitmodules b/.gitmodules index 888d42d..74640c6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "lib/forge-std"] path = lib/forge-std url = https://github.com/foundry-rs/forge-std +[submodule "lib/utils"] + path = lib/utils + url = https://github.com/AngleProtocol/utils diff --git a/contracts/BaseRouter.sol b/contracts/BaseRouter.sol index b3d468d..2d4fde3 100644 --- a/contracts/BaseRouter.sol +++ b/contracts/BaseRouter.sol @@ -155,7 +155,11 @@ abstract contract BaseRouter is Initializable, IDepositWithReferral { ); /// @notice Deploys the router contract on a chain - function initializeRouter(address _core, address _uniswapRouter, address _oneInch) public initializer { + function initializeRouter( + address _core, + address _uniswapRouter, + address _oneInch + ) public initializer { if (_core == address(0)) revert ZeroAddress(); core = ICoreBorrow(_core); uniswapV3Router = IUniswapV3Router(_uniswapRouter); @@ -409,7 +413,11 @@ abstract contract BaseRouter is Initializable, IDepositWithReferral { /// @param tokenOut Token to sweep /// @param minAmountOut Minimum amount of tokens to recover /// @param to Address to which tokens should be sent - function _sweep(address tokenOut, uint256 minAmountOut, address to) internal virtual { + function _sweep( + address tokenOut, + uint256 minAmountOut, + address to + ) internal virtual { uint256 balanceToken = IERC20(tokenOut).balanceOf(address(this)); _slippageCheck(balanceToken, minAmountOut); if (balanceToken != 0) { @@ -580,7 +588,11 @@ abstract contract BaseRouter is Initializable, IDepositWithReferral { /// @param token Address of the token to change allowance /// @param spender Address to change the allowance of /// @param amount Amount allowed - function _changeAllowance(IERC20 token, address spender, uint256 amount) internal { + function _changeAllowance( + IERC20 token, + address spender, + uint256 amount + ) internal { uint256 currentAllowance = token.allowance(address(this), spender); // In case `currentAllowance < type(uint256).max / 2` and we want to increase it: // Do nothing (to handle tokens that need reapprovals to 0 and save gas) diff --git a/lib/forge-std b/lib/forge-std index 26413f2..1714bee 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 26413f24cbbd4b4fb5c855d542ef9721790c1e7f +Subproject commit 1714bee72e286e73f76e320d110e0eaf5c4e649d diff --git a/lib/utils b/lib/utils new file mode 160000 index 0000000..e64751f --- /dev/null +++ b/lib/utils @@ -0,0 +1 @@ +Subproject commit e64751fc263a2f3a7f52563da3c010a13195cb6b diff --git a/remappings.txt b/remappings.txt index c3b565e..362914c 100644 --- a/remappings.txt +++ b/remappings.txt @@ -4,3 +4,4 @@ @uniswap/=node_modules/@uniswap/ ds-test/=lib/forge-std/lib/ds-test/src/ forge-std/=lib/forge-std/src/ +utils/=lib/utils/ \ No newline at end of file diff --git a/scripts/foundry/UpgradeRouter.s.sol b/scripts/foundry/UpgradeRouter.s.sol new file mode 100644 index 0000000..61de30c --- /dev/null +++ b/scripts/foundry/UpgradeRouter.s.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.17; + +import "forge-std/Script.sol"; +import "utils/src/CommonUtils.sol"; +import { AngleRouterMainnet } from "contracts/implementations/mainnet/AngleRouterMainnet.sol"; +import { AngleRouterArbitrum } from "contracts/implementations/arbitrum/AngleRouterArbitrum.sol"; +import { AngleRouterOptimism } from "contracts/implementations/optimism/AngleRouterOptimism.sol"; +import { AngleRouterAvalanche } from "contracts/implementations/avalanche/AngleRouterAvalanche.sol"; +import { AngleRouterBase } from "contracts/implementations/base/AngleRouterBase.sol"; +import { AngleRouterCelo } from "contracts/implementations/celo/AngleRouterCelo.sol"; +import { AngleRouterGnosis } from "contracts/implementations/gnosis/AngleRouterGnosis.sol"; +import { AngleRouterLinea } from "contracts/implementations/linea/AngleRouterLinea.sol"; +import { AngleRouterPolygon } from "contracts/implementations/polygon/AngleRouterPolygon.sol"; + +contract UpgradeRouterScript is Script, CommonUtils { + +function run() public { + uint256 chainId = vm.envUint("CHAIN_ID"); + + address routerImpl; + if (chainId == CHAIN_ETHEREUM) { + routerImpl = address(new AngleRouterMainnet()); + } else if (chainId == CHAIN_ARBITRUM) { + routerImpl = address(new AngleRouterArbitrum()); + } else if (chainId == CHAIN_OPTIMISM) { + routerImpl = address(new AngleRouterOptimism()); + } else if (chainId == CHAIN_AVALANCHE) { + routerImpl = address(new AngleRouterAvalanche()); + } else if (chainId == CHAIN_BASE) { + routerImpl = address(new AngleRouterBase()); + } else if (chainId == CHAIN_CELO) { + routerImpl = address(new AngleRouterCelo()); + } else if (chainId == CHAIN_GNOSIS) { + routerImpl = address(new AngleRouterGnosis()); + } else if (chainId == CHAIN_LINEA) { + routerImpl = address(new AngleRouterLinea()); + } else if (chainId == CHAIN_POLYGON) { + routerImpl = address(new AngleRouterPolygon()); + } + + console.log("Deployed router implementation at address: %s", routerImpl); +} + +} From 007b784f3f8bee87a65d1d0aa5efb35485783b6d Mon Sep 17 00:00:00 2001 From: 0xtekgrinder <0xtekgrinder@protonmail.com> Date: Tue, 6 Aug 2024 12:17:36 +0200 Subject: [PATCH 09/11] style: prettier script --- scripts/foundry/UpgradeRouter.s.sol | 48 ++++++++++++++--------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/scripts/foundry/UpgradeRouter.s.sol b/scripts/foundry/UpgradeRouter.s.sol index 61de30c..7503eed 100644 --- a/scripts/foundry/UpgradeRouter.s.sol +++ b/scripts/foundry/UpgradeRouter.s.sol @@ -14,32 +14,30 @@ import { AngleRouterLinea } from "contracts/implementations/linea/AngleRouterLin import { AngleRouterPolygon } from "contracts/implementations/polygon/AngleRouterPolygon.sol"; contract UpgradeRouterScript is Script, CommonUtils { + function run() public { + uint256 chainId = vm.envUint("CHAIN_ID"); -function run() public { - uint256 chainId = vm.envUint("CHAIN_ID"); + address routerImpl; + if (chainId == CHAIN_ETHEREUM) { + routerImpl = address(new AngleRouterMainnet()); + } else if (chainId == CHAIN_ARBITRUM) { + routerImpl = address(new AngleRouterArbitrum()); + } else if (chainId == CHAIN_OPTIMISM) { + routerImpl = address(new AngleRouterOptimism()); + } else if (chainId == CHAIN_AVALANCHE) { + routerImpl = address(new AngleRouterAvalanche()); + } else if (chainId == CHAIN_BASE) { + routerImpl = address(new AngleRouterBase()); + } else if (chainId == CHAIN_CELO) { + routerImpl = address(new AngleRouterCelo()); + } else if (chainId == CHAIN_GNOSIS) { + routerImpl = address(new AngleRouterGnosis()); + } else if (chainId == CHAIN_LINEA) { + routerImpl = address(new AngleRouterLinea()); + } else if (chainId == CHAIN_POLYGON) { + routerImpl = address(new AngleRouterPolygon()); + } - address routerImpl; - if (chainId == CHAIN_ETHEREUM) { - routerImpl = address(new AngleRouterMainnet()); - } else if (chainId == CHAIN_ARBITRUM) { - routerImpl = address(new AngleRouterArbitrum()); - } else if (chainId == CHAIN_OPTIMISM) { - routerImpl = address(new AngleRouterOptimism()); - } else if (chainId == CHAIN_AVALANCHE) { - routerImpl = address(new AngleRouterAvalanche()); - } else if (chainId == CHAIN_BASE) { - routerImpl = address(new AngleRouterBase()); - } else if (chainId == CHAIN_CELO) { - routerImpl = address(new AngleRouterCelo()); - } else if (chainId == CHAIN_GNOSIS) { - routerImpl = address(new AngleRouterGnosis()); - } else if (chainId == CHAIN_LINEA) { - routerImpl = address(new AngleRouterLinea()); - } else if (chainId == CHAIN_POLYGON) { - routerImpl = address(new AngleRouterPolygon()); + console.log("Deployed router implementation at address: %s", routerImpl); } - - console.log("Deployed router implementation at address: %s", routerImpl); -} - } From c50cfdbc32b7783fdfd6cd71b4f73154d0d0eabf Mon Sep 17 00:00:00 2001 From: 0xtekgrinder <0xtekgrinder@protonmail.com> Date: Tue, 6 Aug 2024 15:40:11 +0200 Subject: [PATCH 10/11] feat: broadcast inside script --- scripts/foundry/UpgradeRouter.s.sol | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts/foundry/UpgradeRouter.s.sol b/scripts/foundry/UpgradeRouter.s.sol index 7503eed..c96bdc7 100644 --- a/scripts/foundry/UpgradeRouter.s.sol +++ b/scripts/foundry/UpgradeRouter.s.sol @@ -17,6 +17,11 @@ contract UpgradeRouterScript is Script, CommonUtils { function run() public { uint256 chainId = vm.envUint("CHAIN_ID"); + uint256 deployerPrivateKey = vm.deriveKey(vm.envString("MNEMONIC_MAINNET"), 0); + address deployer = vm.addr(deployerPrivateKey); + console.log("Address: %s", deployer); + vm.startBroadcast(deployerPrivateKey); + address routerImpl; if (chainId == CHAIN_ETHEREUM) { routerImpl = address(new AngleRouterMainnet()); @@ -39,5 +44,7 @@ contract UpgradeRouterScript is Script, CommonUtils { } console.log("Deployed router implementation at address: %s", routerImpl); + + vm.stopBroadcast(); } } From f29eb856e0887315bf1d6623fadcb704e5095950 Mon Sep 17 00:00:00 2001 From: 0xtekgrinder <0xtekgrinder@protonmail.com> Date: Fri, 9 Aug 2024 11:37:37 +0200 Subject: [PATCH 11/11] chore: new rpc endpoints and etherscan keys --- foundry.toml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/foundry.toml b/foundry.toml index 07b6fa2..b637294 100644 --- a/foundry.toml +++ b/foundry.toml @@ -21,11 +21,25 @@ runs = 500 runs = 500 [rpc_endpoints] +arbitrum = "${ETH_NODE_URI_ARBITRUM}" mainnet = "${ETH_NODE_URI_MAINNET}" polygon = "${ETH_NODE_URI_POLYGON}" goerli = "${ETH_NODE_URI_GOERLI}" +optimism = "${ETH_NODE_URI_OPTIMISM}" +avalanche = "${ETH_NODE_URI_AVALANCHE}" +base = "${ETH_NODE_URI_BASE}" +linea = "${ETH_NODE_URI_LINEA}" +celo = "${ETH_NODE_URI_CELO}" +gnosis = "${ETH_NODE_URI_GNOSIS}" [etherscan] +arbitrum = { key = "${ARBITRUM_ETHERSCAN_API_KEY}" } mainnet = { key = "${MAINNET_ETHERSCAN_API_KEY}" } polygon = { key = "${POLYGON_ETHERSCAN_API_KEY}" } -goerli = { key = "${GOERLI_ETHERSCAN_API_KEY}" } \ No newline at end of file +goerli = { key = "${GOERLI_ETHERSCAN_API_KEY}" } +optimism = { key = "${OPTIMISM_ETHERSCAN_API_KEY}" } +avalanche = { key = "${AVALANCHE_ETHERSCAN_API_KEY}" } +base = { key = "${BASE_ETHERSCAN_API_KEY}", url = "https://api.basescan.org/api" } +linea = { key = "${LINEA_ETHERSCAN_API_KEY}"} +celo = { key = "${CELO_ETHERSCAN_API_KEY}", url = "https://api.celoscan.io/api" } +gnosis = { key = "${GNOSIS_ETHERSCAN_API_KEY}" , url = "https://api.gnosisscan.io/api"}