From 03b1a5e9029106a5d7de33f21c9effecf922b12d Mon Sep 17 00:00:00 2001 From: gs8nrv <55771972+GuillaumeNervoXS@users.noreply.github.com> Date: Tue, 11 Jun 2024 15:14:43 +0200 Subject: [PATCH] fix tests for Renzo --- .../PendleLevSwapperMorphoEzETHDec24.sol | 46 +++++ package.json | 2 +- .../mainnet/morpho/MorphoDeployMarket.s.sol | 36 ++-- ...PendleLevSwapperMorphoEzETHDec24Test.t.sol | 182 ++++++++++++++++++ 4 files changed, 247 insertions(+), 19 deletions(-) create mode 100644 contracts/swapper/LevSwapper/morpho/implementations/PendleLevSwapperMorphoEzETHDec24.sol create mode 100644 test/foundry/swapper/morpho/PendleLevSwapperMorphoEzETHDec24Test.t.sol diff --git a/contracts/swapper/LevSwapper/morpho/implementations/PendleLevSwapperMorphoEzETHDec24.sol b/contracts/swapper/LevSwapper/morpho/implementations/PendleLevSwapperMorphoEzETHDec24.sol new file mode 100644 index 0000000..ed433b9 --- /dev/null +++ b/contracts/swapper/LevSwapper/morpho/implementations/PendleLevSwapperMorphoEzETHDec24.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.17; + +import "borrow-staked/swapper/LevSwapper/morpho/PendleLevSwapperMorpho.sol"; + +/// @author Angle Labs, Inc. +/// @notice PT ezETH leverage swapper with maturity Dec 24 +contract PendleLevSwapperMorphoEzETHDec24 is PendleLevSwapperMorpho { + constructor( + ICoreBorrow _core, + IUniswapV3Router _uniV3Router, + address _aggregator, + IAngleRouterSidechain _angleRouter, + IMorphoBase _morpho + ) PendleLevSwapperMorpho(_core, _uniV3Router, _aggregator, _angleRouter, _morpho) {} + + /// @inheritdoc BaseLevSwapper + function angleStaker() public pure override returns (IBorrowStaker) { + return IBorrowStaker(address(0)); + } + + /// @inheritdoc PendleLevSwapperMorpho + function PT() public pure override returns (IERC20) { + return IERC20(0xf7906F274c174A52d444175729E3fa98f9bde285); + } + + /// @inheritdoc PendleLevSwapperMorpho + function SY() public pure override returns (IStandardizedYield) { + return IStandardizedYield(0x22E12A50e3ca49FB183074235cB1db84Fe4C716D); + } + + /// @inheritdoc PendleLevSwapperMorpho + function YT() public pure override returns (IPYieldTokenV2) { + return IPYieldTokenV2(0x7749F5Ed1e356EDc63D469c2fcaC9adEB56d1C2b); + } + + /// @inheritdoc PendleLevSwapperMorpho + function market() public pure override returns (IPMarketV3) { + return IPMarketV3(0xD8F12bCDE578c653014F27379a6114F67F0e445f); + } + + /// @inheritdoc PendleLevSwapperMorpho + function collateral() public pure override returns (IERC20) { + return IERC20(0xbf5495Efe5DB9ce00f80364C8B423567e58d2110); + } +} diff --git a/package.json b/package.json index 2d281c6..741b24d 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "test:unit": "forge test -vvv --gas-report --match-path \"test/unit/**/*.sol\"", "test:invariant": "forge test -vvv --gas-report --match-path \"test/invariant/**/*.sol\"", "test:fuzz": "forge test -vvv --gas-report --match-path \"test/fuzz/**/*.sol\"", - "test": "FOUNDRY_PROFILE=dev forge test -vvvv", + "test": "FOUNDRY_PROFILE=dev forge test -vvv", "slither": "slither .", "lcov:clean": "lcov --remove lcov.info -o lcov.info 'test/**' 'scripts/**' 'contracts/transmuter/configs/**' 'contracts/utils/**'", "lcov:generate-html": "genhtml lcov.info --output=coverage", diff --git a/scripts/foundry/mainnet/morpho/MorphoDeployMarket.s.sol b/scripts/foundry/mainnet/morpho/MorphoDeployMarket.s.sol index 4cd78ad..0f88047 100644 --- a/scripts/foundry/mainnet/morpho/MorphoDeployMarket.s.sol +++ b/scripts/foundry/mainnet/morpho/MorphoDeployMarket.s.sol @@ -16,7 +16,7 @@ import { IOracle as IMorphoOracle } from "morpho-blue/interfaces/IOracle.sol"; import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import { MorphoFeedPTweETH } from "borrow/oracle/morpho/mainnet/MorphoFeedPTweETH.sol"; -import { MorphoFeedPTweETHDec24 } from "borrow/oracle/morpho/mainnet/MorphoFeedPTweETHDec24.sol"; +// import { MorphoFeedPTweETHDec24 } from "borrow/oracle/morpho/mainnet/MorphoFeedPTweETHDec24.sol"; import { IAccessControlManager } from "borrow/interfaces/IAccessControlManager.sol"; import "borrow-staked/mock/MockCoreBorrow.sol"; import "borrow-staked/interfaces/external/morpho/IMorphoChainlinkOracleV2Factory.sol"; @@ -43,23 +43,23 @@ contract MorphoDeployMarket is Script, MainnetConstants, StdCheats, StdAssertion address oracle; bytes32 salt; - // PT weETH market - address priceFeed = address( - new MorphoFeedPTweETHDec24(IAccessControlManager(address(coreBorrow)), _MAX_IMPLIED_RATE, _TWAP_DURATION) - ); - oracle = IMorphoChainlinkOracleV2Factory(MORPHO_ORACLE_FACTORY).createMorphoChainlinkOracleV2( - address(0), - 1, - address(priceFeed), - address(WEETH_USD_ORACLE), - IERC20Metadata(PTWeETH).decimals(), - address(0), - 1, - address(0), - address(0), - IERC20Metadata(USDA).decimals(), - salt - ); + // // PT weETH market + // address priceFeed = address( + // new MorphoFeedPTweETHDec24(IAccessControlManager(address(coreBorrow)), _MAX_IMPLIED_RATE, _TWAP_DURATION) + // ); + // oracle = IMorphoChainlinkOracleV2Factory(MORPHO_ORACLE_FACTORY).createMorphoChainlinkOracleV2( + // address(0), + // 1, + // address(priceFeed), + // address(WEETH_USD_ORACLE), + // IERC20Metadata(PTWeETH).decimals(), + // address(0), + // 1, + // address(0), + // address(0), + // IERC20Metadata(USDA).decimals(), + // salt + // ); // // GTUSDCPrime market // oracle = IMorphoChainlinkOracleV2Factory(MORPHO_ORACLE_FACTORY).createMorphoChainlinkOracleV2( diff --git a/test/foundry/swapper/morpho/PendleLevSwapperMorphoEzETHDec24Test.t.sol b/test/foundry/swapper/morpho/PendleLevSwapperMorphoEzETHDec24Test.t.sol new file mode 100644 index 0000000..8770c46 --- /dev/null +++ b/test/foundry/swapper/morpho/PendleLevSwapperMorphoEzETHDec24Test.t.sol @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.17; + +import "../../BaseTest.test.sol"; +import "borrow-staked/interfaces/IBorrowStaker.sol"; +import "borrow/interfaces/ICoreBorrow.sol"; +import "borrow-staked/mock/MockTokenPermit.sol"; +import { SwapType, BaseLevSwapper, PendleLevSwapperMorphoEzETHDec24, PendleLevSwapperMorpho, Swapper, IUniswapV3Router, IAngleRouterSidechain } from "borrow-staked/swapper/LevSwapper/morpho/implementations/PendleLevSwapperMorphoEzETHDec24.sol"; +import { IMorphoBase } from "morpho-blue/interfaces/IMorpho.sol"; + +contract PendleLevSwapperMorphoEzETHDec24Test is BaseTest { + using stdStorage for StdStorage; + using SafeERC20 for IERC20; + + address internal constant _ONE_INCH = 0x111111125421cA6dc452d289314280a0f8842A65; + IUniswapV3Router internal constant _UNI_V3_ROUTER = IUniswapV3Router(0xE592427A0AEce92De3Edee1F18E0157C05861564); + IAngleRouterSidechain internal constant _ANGLE_ROUTER = + IAngleRouterSidechain(address(uint160(uint256(keccak256(abi.encodePacked("_fakeAngleRouter")))))); + + uint256 internal constant _BPS = 10000; + IERC20 constant WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); + IMorphoBase constant MORPHO = IMorphoBase(0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb); + PendleLevSwapperMorpho public swapper; + IERC20 public asset; + IERC20 public collateral; + + uint256 public constant DEPOSIT_LENGTH = 10; + uint256 public constant WITHDRAW_LENGTH = 10; + + function setUp() public override { + super.setUp(); + + _ethereum = vm.createFork(vm.envString("ETH_NODE_URI_MAINNET"), 20062045); + vm.selectFork(_ethereum); + + // reset coreBorrow because the `makePersistent()` doens't work on my end + coreBorrow = new MockCoreBorrow(); + coreBorrow.toggleGuardian(_GUARDIAN); + coreBorrow.toggleGovernor(_GOVERNOR); + + swapper = new PendleLevSwapperMorphoEzETHDec24(coreBorrow, _UNI_V3_ROUTER, _ONE_INCH, _ANGLE_ROUTER, MORPHO); + asset = swapper.PT(); + collateral = swapper.collateral(); + + vm.startPrank(_alice); + asset.approve(address(swapper), type(uint256).max); + collateral.approve(address(swapper), type(uint256).max); + vm.stopPrank(); + } + + function test_Leverage_NoSwap_Success(uint256 amount) public { + amount = bound(amount, 10 ** 15, 10 ** 20); + deal(address(collateral), address(_alice), amount); + + vm.startPrank(_alice); + + // intermediary variables + bytes[] memory oneInchData = new bytes[](0); + + uint256 minAmountOut = amount / 2; + bytes memory addData; + bytes memory swapData = abi.encode(oneInchData, addData); + bytes memory leverageData = abi.encode(true, _alice, swapData); + bytes memory data = abi.encode(address(0), 0, SwapType.Leverage, leverageData); + + // we first need to send the tokens before hand, you should always use the swapper + // in another tx to not loose your funds by front running + collateral.transfer(address(swapper), amount); + swapper.swap(IERC20(address(collateral)), IERC20(address(asset)), _alice, 0, amount, data); + + vm.stopPrank(); + + assertEq(collateral.balanceOf(_alice), 0); + assertEq(collateral.balanceOf(address(swapper)), 0); + assertEq(asset.balanceOf(address(swapper)), 0); + assertGe(asset.balanceOf(_alice), minAmountOut); + } + + function test_Deleverage_NoSwap_Success(uint256 amount) public { + amount = bound(amount, 10 ** 15, 10 ** 20); + deal(address(asset), address(_alice), amount); + + vm.startPrank(_alice); + + // intermediary variables + bytes[] memory oneInchData = new bytes[](0); + + uint256 minAmountOut = amount / 2; + IERC20[] memory sweepTokens = new IERC20[](0); + bytes memory removeData = abi.encode(uint256(minAmountOut)); + bytes memory swapData = abi.encode(0, amount, sweepTokens, oneInchData, removeData); + bytes memory leverageData = abi.encode(false, _alice, swapData); + bytes memory data = abi.encode(address(0), 0, SwapType.Leverage, leverageData); + + // we first need to send the tokens before hand, you should always use the swapper + // in another tx to not loose your funds by front running + asset.transfer(address(swapper), amount); + swapper.swap(IERC20(address(asset)), IERC20(address(collateral)), _alice, 0, amount, data); + + vm.stopPrank(); + + assertGe(collateral.balanceOf(_alice), minAmountOut); + assertEq(collateral.balanceOf(address(swapper)), 0); + assertEq(asset.balanceOf(address(swapper)), 0); + assertEq(asset.balanceOf(_alice), 0); + } + + function test_Leverage_Swap_Success() public { + uint256 amount = 10 ether; + deal(address(WETH), address(_alice), amount); + + vm.startPrank(_alice); + + // intermediary variables + bytes[] memory oneInchData = new bytes[](1); + // swap WETH for ezETH + oneInchData[0] = abi.encode( + address(WETH), + 0, + hex"83800a8e000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000008ac7230489e8000000000000000000000000000000000000000000000000000089a0435b8d704154280000000000000000000000be80225f09645f172b079394312220637c440a63f737be46" + ); + + uint256 minAmountOut = amount / 2; + bytes memory addData; + bytes memory swapData = abi.encode(oneInchData, addData); + bytes memory leverageData = abi.encode(true, _alice, swapData); + bytes memory data = abi.encode(address(0), 0, SwapType.Leverage, leverageData); + + // we first need to send the tokens before hand, you should always use the swapper + // in another tx to not loose your funds by front running + WETH.transfer(address(swapper), amount); + swapper.swap(IERC20(address(WETH)), IERC20(address(asset)), _alice, 0, amount, data); + + vm.stopPrank(); + + assertEq(WETH.balanceOf(_alice), 0); + assertEq(WETH.balanceOf(address(swapper)), 0); + assertEq(collateral.balanceOf(_alice), 0); + assertEq(collateral.balanceOf(address(swapper)), 0); + assertEq(asset.balanceOf(address(swapper)), 0); + assertGe(asset.balanceOf(_alice), minAmountOut); + } + + function test_Deleverage_Swap_Success() public { + uint256 eps = 2 ether; + uint256 amount = 10 ether; + deal(address(asset), address(_alice), amount + eps); + + vm.startPrank(_alice); + + // intermediary variables + bytes[] memory oneInchData = new bytes[](1); + // swap weETH for WETH + oneInchData[0] = abi.encode( + address(collateral), + 0, + hex"07ed2379000000000000000000000000e37e799d5077682fa0a244d46e5649f71457bd09000000000000000000000000bf5495efe5db9ce00f80364c8b423567e58d2110000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000e37e799d5077682fa0a244d46e5649f71457bd090000000000000000000000005991a2df15a8f6a256d3ec51e99254cd3fb576a90000000000000000000000000000000000000000000000008ac7230489e800000000000000000000000000000000000000000000000000008924bd0fa19555130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001130000000000000000000000000000000000000000f50000c700009900004f00a0fbb7cd0600596192bb6e41802428ac943d2f1476c1af25cc0e000000000000000000000659bf5495efe5db9ce00f80364c8b423567e58d2110c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200a0f2fa6b66c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000008a875f4b4389e95400000000000000000005058c6b58c80e80a06c4eca27c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2111111125421ca6dc452d289314280a0f8842a650020d6bdbf78c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2111111125421ca6dc452d289314280a0f8842a6500000000000000000000000000f737be46" + ); + + uint256 minAmountOut = amount / 2; + IERC20[] memory sweepTokens = new IERC20[](1); + sweepTokens[0] = collateral; + bytes memory removeData = abi.encode(uint256(minAmountOut)); + bytes memory swapData = abi.encode(0, amount + eps, sweepTokens, oneInchData, removeData); + bytes memory leverageData = abi.encode(false, _alice, swapData); + bytes memory data = abi.encode(address(0), 0, SwapType.Leverage, leverageData); + + // we first need to send the tokens before hand, you should always use the swapper + // in another tx to not loose your funds by front running + asset.transfer(address(swapper), amount + eps); + swapper.swap(IERC20(address(asset)), IERC20(address(WETH)), _alice, 0, amount, data); + + vm.stopPrank(); + + assertGe(WETH.balanceOf(_alice), (amount * 99) / 100); + assertEq(WETH.balanceOf(address(swapper)), 0); + assertGt(collateral.balanceOf(_alice), 0); + assertEq(collateral.balanceOf(address(swapper)), 0); + assertEq(asset.balanceOf(address(swapper)), 0); + assertEq(asset.balanceOf(_alice), 0); + } +}