Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: PT markets Dec maturity #43

Merged
merged 7 commits into from
Jun 19, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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 weETH leverage swapper with maturity Dec 24
contract PendleLevSwapperMorphoWeETHDec24 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(0x6ee2b5E19ECBa773a352E5B21415Dc419A700d1d);
}

/// @inheritdoc PendleLevSwapperMorpho
function SY() public pure override returns (IStandardizedYield) {
return IStandardizedYield(0xAC0047886a985071476a1186bE89222659970d65);
}

/// @inheritdoc PendleLevSwapperMorpho
function YT() public pure override returns (IPYieldTokenV2) {
return IPYieldTokenV2(0x129e6B5DBC0Ecc12F9e486C5BC9cDF1a6A80bc6A);
}

/// @inheritdoc PendleLevSwapperMorpho
function market() public pure override returns (IPMarketV3) {
return IPMarketV3(0x7d372819240D14fB477f17b964f95F33BeB4c704);
}

/// @inheritdoc PendleLevSwapperMorpho
function collateral() public pure override returns (IERC20) {
return IERC20(0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee);
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
1 change: 1 addition & 0 deletions scripts/foundry/mainnet/MainnetConstants.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ contract MainnetConstants {
address constant USDA = 0x0000206329b97DB379d5E1Bf586BbDB969C63274;
address constant EZETH = 0xbf5495Efe5DB9ce00f80364C8B423567e58d2110;
address constant PTWeETH = 0xc69Ad9baB1dEE23F4605a82b3354F8E40d1E5966;
address constant PTWeETHDec24 = 0x6ee2b5E19ECBa773a352E5B21415Dc419A700d1d;
address constant PTUSDe = 0xa0021EF8970104c2d008F38D92f115ad56a9B8e1;
address constant RSETH = 0xA1290d69c65A6Fe4DF752f95823fae25cB99e5A7;
address constant GTETHPRIME = 0x2371e134e3455e0593363cBF89d3b6cf53740618;
Expand Down
39 changes: 20 additions & 19 deletions scripts/foundry/mainnet/morpho/MorphoDeployMarket.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { StdCheats, StdAssertions } from "forge-std/Test.sol";
import "borrow/interfaces/ICoreBorrow.sol";
import "borrow/interfaces/IAngleRouterSidechain.sol";
import "borrow/interfaces/external/uniswap/IUniswapRouter.sol";
import { SwapType, BaseLevSwapper, PendleLevSwapperMorphoWeETH, PendleLevSwapperMorpho, Swapper } from "borrow-staked/swapper/LevSwapper/morpho/implementations/PendleLevSwapperMorphoWeETH.sol";
import { SwapType, BaseLevSwapper, PendleLevSwapperMorpho, Swapper } from "borrow-staked/swapper/LevSwapper/morpho/implementations/PendleLevSwapperMorphoWeETH.sol";
import "../MainnetConstants.s.sol";
import { MarketParams } from "morpho-blue/libraries/MarketParamsLib.sol";
import { IIrm } from "morpho-blue/interfaces/IIRM.sol";
Expand All @@ -16,6 +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 { IAccessControlManager } from "borrow/interfaces/IAccessControlManager.sol";
import "borrow-staked/mock/MockCoreBorrow.sol";
import "borrow-staked/interfaces/external/morpho/IMorphoChainlinkOracleV2Factory.sol";
Expand All @@ -42,23 +43,23 @@ contract MorphoDeployMarket is Script, MainnetConstants, StdCheats, StdAssertion
address oracle;
bytes32 salt;

// PT weETH market
address priceFeed = address(
new MorphoFeedPTweETH(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(
Expand All @@ -77,7 +78,7 @@ contract MorphoDeployMarket is Script, MainnetConstants, StdCheats, StdAssertion

uint256 price = IMorphoOracle(oracle).price();
assertApproxEqRel(price, 3350 * 10 ** 36, 100 ** 36);
params.collateralToken = PTWeETH;
params.collateralToken = PTWeETHDec24;
params.lltv = LLTV_86;
params.irm = IRM_MODEL;
params.oracle = oracle;
Expand Down
2 changes: 1 addition & 1 deletion scripts/foundry/mainnet/morpho/MorphoInteractMarket.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { console } from "forge-std/console.sol";
import { StdCheats, StdAssertions } from "forge-std/Test.sol";
import "borrow/interfaces/IAngleRouterSidechain.sol";
import "borrow/interfaces/external/uniswap/IUniswapRouter.sol";
import { SwapType, BaseLevSwapper, PendleLevSwapperMorphoWeETH, PendleLevSwapperMorpho, Swapper } from "borrow-staked/swapper/LevSwapper/morpho/implementations/PendleLevSwapperMorphoWeETH.sol";
import { SwapType, BaseLevSwapper, PendleLevSwapperMorpho, Swapper } from "borrow-staked/swapper/LevSwapper/morpho/implementations/PendleLevSwapperMorphoWeETH.sol";
import { MarketParams } from "morpho-blue/libraries/MarketParamsLib.sol";
import { IIrm } from "morpho-blue/interfaces/IIRM.sol";
import { IMorpho, Position } from "morpho-blue/interfaces/IMorpho.sol";
Expand Down
4 changes: 2 additions & 2 deletions scripts/foundry/mainnet/morpho/SwapperLevMorphoPTWeETH.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { StdCheats, StdAssertions } from "forge-std/Test.sol";
import "borrow/interfaces/ICoreBorrow.sol";
import "borrow/interfaces/IAngleRouterSidechain.sol";
import "borrow/interfaces/external/uniswap/IUniswapRouter.sol";
import { PendleLevSwapperMorphoWeETH } from "borrow-staked/swapper/LevSwapper/morpho/implementations/PendleLevSwapperMorphoWeETH.sol";
import { PendleLevSwapperMorphoWeETHDec24 } from "borrow-staked/swapper/LevSwapper/morpho/implementations/PendleLevSwapperMorphoWeETHDec24.sol";
import "../MainnetConstants.s.sol";
import { IMorpho } from "morpho-blue/interfaces/IMorpho.sol";
import "borrow-staked/mock/MockCoreBorrow.sol";
Expand All @@ -25,7 +25,7 @@ contract SwapperLevMorphoPTWeETH is Script, MainnetConstants, StdCheats, StdAsse
// coreBorrow = new MockCoreBorrow();
// coreBorrow.toggleGuardian(deployer);

PendleLevSwapperMorphoWeETH swapperMorphoPTWeETH = new PendleLevSwapperMorphoWeETH(
PendleLevSwapperMorphoWeETHDec24 swapperMorphoPTWeETH = new PendleLevSwapperMorphoWeETHDec24(
ICoreBorrow(coreBorrow),
IUniswapV3Router(UNI_V3_ROUTER),
ONE_INCH,
Expand Down
182 changes: 182 additions & 0 deletions test/foundry/swapper/morpho/PendleLevSwapperMorphoEzETHDec24Test.t.sol
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading
Loading