From b33c819f289bbc2672625a5c15f00b1c5b3c2c62 Mon Sep 17 00:00:00 2001 From: kinrezc Date: Wed, 5 Jun 2024 15:37:43 -0400 Subject: [PATCH 1/8] Makes the `prepareSwap` signatures more clear --- src/RMM.sol | 82 +++++++++++++++++++++---------------------- src/lib/RmmErrors.sol | 2 ++ test/unit/RMM.t.sol | 16 ++++----- test/unit/RmmSy.t.sol | 22 ++++++++---- test/unit/SwapPt.sol | 8 ++--- test/unit/SwapSy.sol | 8 ++--- 6 files changed, 74 insertions(+), 64 deletions(-) diff --git a/src/RMM.sol b/src/RMM.sol index 7b35a67..d2487ea 100644 --- a/src/RMM.sol +++ b/src/RMM.sol @@ -124,6 +124,39 @@ contract RMM is ERC20 { ); } + /// @dev Swaps SY for YT, sending at least `minAmountOut` YT to `to`. + /// @notice `amountIn` is an amount of PT that needs to be minted from the SY in and the SY flash swapped from the pool + function swapExactSyForYt(uint256 amountIn, uint256 minAmountOut, address to) + external + payable + lock + returns (uint256 amountOut, int256 deltaLiquidity) + { + PYIndex index = YT.newIndex(); + uint256 amountInWad; + uint256 amountOutWad; + uint256 strike_; + + (amountInWad, amountOutWad, amountOut, deltaLiquidity, strike_) = + prepareSwapPtIn(amountIn, block.timestamp, index); + + if (amountOut < minAmountOut) { + revert InsufficientOutput(amountInWad, minAmountOut, amountOut); + } + + _adjust(-toInt(amountOutWad), toInt(amountInWad), deltaLiquidity, strike_, index); + + // SY is needed to cover the minted PT, so we need to debit the delta from the msg.sender + uint256 delta = index.assetToSyUp(amountInWad) - amountOutWad; + uint256 ytOut = amountOut + delta; + (uint256 debitNative) = _debit(address(SY), delta); + + amountOut = mintPtYt(ytOut, address(this)); + _credit(address(YT), to, amountOut); + + emit Swap(msg.sender, to, address(SY), address(YT), debitNative, amountOut, deltaLiquidity); + } + function swapExactTokenForYt(address token, uint256 amountIn, uint256 minSyMinted, uint256 minYtOut, address to) external payable @@ -151,7 +184,7 @@ contract RMM is ERC20 { } if (amountSyMinted < minSyMinted) { - revert InsufficientOutput(amountSyMinted, minSyMinted, amountSyMinted); + revert InsufficientSYMinted(amountSyMinted, minSyMinted); } PYIndex index = YT.newIndex(); @@ -160,7 +193,7 @@ contract RMM is ERC20 { uint256 strike_; (amountInWad, amountOutWad, amountYtOut, deltaLiquidity, strike_) = - prepareSwapPt(amountSyMinted, block.timestamp, index); + prepareSwapPtIn(amountSyMinted, block.timestamp, index); if (amountYtOut < minYtOut) { revert InsufficientOutput(amountInWad, minYtOut, amountYtOut); @@ -173,8 +206,6 @@ contract RMM is ERC20 { // Converts the SY received from minting it into its components PT and YT. amountYtOut = mintPtYt(ytOut, address(this)); - console2.log("got here"); - console2.log("yt out, ", amountYtOut); _credit(address(YT), to, amountYtOut); uint256 debitSurplus = address(this).balance; @@ -197,7 +228,7 @@ contract RMM is ERC20 { uint256 strike_; (amountInWad, amountOutWad, amountOut, deltaLiquidity, strike_) = - prepareSwapPt(amountIn, block.timestamp, index); + prepareSwapPtIn(amountIn, block.timestamp, index); if (amountOut < minAmountOut) { revert InsufficientOutput(amountInWad, minAmountOut, amountOut); @@ -222,7 +253,7 @@ contract RMM is ERC20 { uint256 strike_; (amountInWad, amountOutWad, amountOut, deltaLiquidity, strike_) = - prepareSwapSy(amountIn, block.timestamp, index); + prepareSwapSyIn(amountIn, block.timestamp, index); if (amountOut < minAmountOut) { revert InsufficientOutput(amountInWad, minAmountOut, amountOut); @@ -262,39 +293,6 @@ contract RMM is ERC20 { emit Swap(msg.sender, to, address(PT), address(SY), debitNative, creditNative, deltaLiquidity); } - /// @dev Swaps SY for YT, sending at least `minAmountOut` YT to `to`. - /// @notice `amountIn` is an amount of PT that needs to be minted from the SY in and the SY flash swapped from the pool - function swapExactSyForYt(uint256 amountIn, uint256 minAmountOut, address to) - external - payable - lock - returns (uint256 amountOut, int256 deltaLiquidity) - { - PYIndex index = YT.newIndex(); - uint256 amountInWad; - uint256 amountOutWad; - uint256 strike_; - - (amountInWad, amountOutWad, amountOut, deltaLiquidity, strike_) = - prepareSwapPt(amountIn, block.timestamp, index); - - if (amountOut < minAmountOut) { - revert InsufficientOutput(amountInWad, minAmountOut, amountOut); - } - - _adjust(-toInt(amountOutWad), toInt(amountInWad), deltaLiquidity, strike_, index); - - // SY is needed to cover the minted PT, so we need to debit the delta from the msg.sender - uint256 delta = index.assetToSyUp(amountInWad) - amountOutWad; - uint256 ytOut = amountOut + delta; - (uint256 debitNative) = _debit(address(SY), delta); - - amountOut = mintPtYt(ytOut, address(this)); - _credit(address(YT), to, amountOut); - - emit Swap(msg.sender, to, address(SY), address(YT), debitNative, amountOut, deltaLiquidity); - } - /// todo: should allocates be executed on the stale curve? I dont think the curve should be updated in allocates. function allocate(uint256 deltaX, uint256 deltaY, uint256 minLiquidityOut, address to) external @@ -456,7 +454,7 @@ contract RMM is ERC20 { uint256 max = initialGuess; for (uint256 iter = 0; iter < 100; ++iter) { uint256 guess = (min + max) / 2; - (,, uint256 amountOut,,) = prepareSwapPt(guess, blockTime, index); + (,, uint256 amountOut,,) = prepareSwapPtIn(guess, blockTime, index); uint256 netSyToPt = index.assetToSyUp(guess); uint256 netSyToPull = netSyToPt - amountOut; @@ -506,7 +504,7 @@ contract RMM is ERC20 { deltaLiquidity = toInt(nextLiquidity) - toInt(totalLiquidity); } - function prepareSwapSy(uint256 amountIn, uint256 timestamp, PYIndex index) + function prepareSwapSyIn(uint256 amountIn, uint256 timestamp, PYIndex index) public view returns (uint256 amountInWad, uint256 amountOutWad, uint256 amountOut, int256 deltaLiquidity, uint256 strike_) @@ -532,7 +530,7 @@ contract RMM is ERC20 { deltaLiquidity = toInt(nextLiquidity) - toInt(totalLiquidity); } - function prepareSwapPt(uint256 ptIn, uint256 timestamp, PYIndex index) + function prepareSwapPtIn(uint256 ptIn, uint256 timestamp, PYIndex index) public view returns (uint256 amountInWad, uint256 amountOutWad, uint256 amountOut, int256 deltaLiquidity, uint256 strike_) diff --git a/src/lib/RmmErrors.sol b/src/lib/RmmErrors.sol index f5dd3bc..bccf590 100644 --- a/src/lib/RmmErrors.sol +++ b/src/lib/RmmErrors.sol @@ -13,6 +13,8 @@ error InsufficientPayment(address token, uint256 actual, uint256 expected); error InsufficientLiquidityOut(uint256 deltaX, uint256 deltaY, uint256 minLiquidity, uint256 liquidity); /// @dev Thrown when a swap does not output enough tokens. error InsufficientOutput(uint256 amountIn, uint256 minAmountOut, uint256 amountOut); +/// @dev Thrown when a swap does not mint sufficient SY tokens given the minimum amount. +error InsufficientSYMinted(uint256 amountMinted, uint256 minAmountMinted); /// @dev Thrown when a swap expects greater input than is allowed error ExcessInput(uint256 amountOut, uint256 maxAmountIn, uint256 amountIn); /// @dev Thrown when an allocate would reduce the liquidity. diff --git a/test/unit/RMM.t.sol b/test/unit/RMM.t.sol index 344c7c9..e00d1d0 100644 --- a/test/unit/RMM.t.sol +++ b/test/unit/RMM.t.sol @@ -225,7 +225,7 @@ contract RMMTest is Test { function test_basic_solve_x() public basic { PYIndex index = YT.newIndex(); uint256 deltaSy = 1 ether; - (,, uint256 approximatedDeltaY,,) = subject().prepareSwapSy(deltaSy, block.timestamp, index); + (,, uint256 approximatedDeltaY,,) = subject().prepareSwapSyIn(deltaSy, block.timestamp, index); uint256 proportionalLGivenX = deltaSy * subject().totalLiquidity() / subject().reserveX(); uint256 proportionalLGivenY = approximatedDeltaY * subject().totalLiquidity() / subject().reserveY(); @@ -276,7 +276,7 @@ contract RMMTest is Test { function test_swapSy_over_time_basic() public basic { PYIndex index = YT.newIndex(); uint256 deltaSy = 1 ether; - (,, uint256 minAmountOut,,) = subject().prepareSwapSy(deltaSy, block.timestamp, index); + (,, uint256 minAmountOut,,) = subject().prepareSwapSyIn(deltaSy, block.timestamp, index); deal(address(subject().PT()), address(subject()), 1 ether); deal(address(subject().SY()), address(this), deltaSy); SY.approve(address(subject()), deltaSy); @@ -338,7 +338,7 @@ contract RMMTest is Test { function test_swapSy_basic() public basic { PYIndex index = YT.newIndex(); uint256 deltaSy = 1 ether; - (,, uint256 minAmountOut,,) = subject().prepareSwapSy(deltaSy, block.timestamp, index); + (,, uint256 minAmountOut,,) = subject().prepareSwapSyIn(deltaSy, block.timestamp, index); deal(address(PT), address(subject()), minAmountOut); deal(address(SY), address(this), deltaSy); SY.approve(address(subject()), deltaSy); @@ -378,7 +378,7 @@ contract RMMTest is Test { function test_swapSy_reverts_InsufficientOutput() public basic { PYIndex index = YT.newIndex(); uint256 deltaSy = 1 ether; - (,, uint256 minAmountOut,,) = subject().prepareSwapSy(deltaSy, block.timestamp, index); + (,, uint256 minAmountOut,,) = subject().prepareSwapSyIn(deltaSy, block.timestamp, index); deal(address(PT), address(subject()), minAmountOut); deal(address(SY), address(this), deltaSy); SY.approve(address(subject()), deltaSy); @@ -394,7 +394,7 @@ contract RMMTest is Test { function test_swapSy_event() public basic { PYIndex index = YT.newIndex(); uint256 deltaSy = 1 ether; - (,, uint256 minAmountOut, int256 delLiq,) = subject().prepareSwapSy(deltaSy, block.timestamp, index); + (,, uint256 minAmountOut, int256 delLiq,) = subject().prepareSwapSyIn(deltaSy, block.timestamp, index); deal(address(PT), address(subject()), minAmountOut); deal(address(SY), address(this), deltaSy); SY.approve(address(subject()), deltaSy); @@ -407,7 +407,7 @@ contract RMMTest is Test { function test_swapY() public basic { PYIndex index = YT.newIndex(); uint256 deltaPt = 1 ether; - (,, uint256 minAmountOut,,) = subject().prepareSwapPt(deltaPt, block.timestamp, index); + (,, uint256 minAmountOut,,) = subject().prepareSwapPtIn(deltaPt, block.timestamp, index); deal(address(SY), address(subject()), minAmountOut); deal(address(PT), address(this), deltaPt); PT.approve(address(subject()), deltaPt); @@ -444,7 +444,7 @@ contract RMMTest is Test { function test_swapPt_reverts_InsufficientOutput() public basic { PYIndex index = YT.newIndex(); uint256 deltaPt = 1 ether; - (,, uint256 minAmountOut,,) = subject().prepareSwapPt(deltaPt, block.timestamp, index); + (,, uint256 minAmountOut,,) = subject().prepareSwapPtIn(deltaPt, block.timestamp, index); mintSY(minAmountOut); mintPtYt(deltaPt); @@ -460,7 +460,7 @@ contract RMMTest is Test { function test_swapPt_event() public basic { PYIndex index = YT.newIndex(); uint256 deltaPt = 1 ether; - (,, uint256 minAmountOut, int256 delLiq,) = subject().prepareSwapPt(deltaPt, block.timestamp, index); + (,, uint256 minAmountOut, int256 delLiq,) = subject().prepareSwapPtIn(deltaPt, block.timestamp, index); deal(address(SY), address(subject()), minAmountOut); deal(address(PT), address(this), deltaPt); PT.approve(address(subject()), deltaPt); diff --git a/test/unit/RmmSy.t.sol b/test/unit/RmmSy.t.sol index 2bdb3e8..f9ed3e3 100644 --- a/test/unit/RmmSy.t.sol +++ b/test/unit/RmmSy.t.sol @@ -154,10 +154,10 @@ contract ForkRMMTest is Test { uint256 deltaSy = 1 ether; console2.log("maturity", subject().maturity()); vm.warp(block.timestamp + 5 days); - (,, uint256 minAmountOut,,) = subject().prepareSwapSy(deltaSy, block.timestamp, index); + (,, uint256 minAmountOut,,) = subject().prepareSwapSyIn(deltaSy, block.timestamp, index); (uint256 amountOut, int256 deltaLiquidity) = subject().swapExactSyForPt(deltaSy, 0, address(this)); vm.warp(block.timestamp + 5 days); - (,, minAmountOut,,) = subject().prepareSwapSy(deltaSy, block.timestamp, index); + (,, minAmountOut,,) = subject().prepareSwapSyIn(deltaSy, block.timestamp, index); (amountOut, deltaLiquidity) = subject().swapExactSyForPt(deltaSy, 0, address(this)); } @@ -165,7 +165,7 @@ contract ForkRMMTest is Test { PYIndex index = YT.newIndex(); uint256 deltaPt = 1 ether; uint256 balanceSyBefore = SY.balanceOf(address(this)); - subject().prepareSwapPt(deltaPt, block.timestamp, index); + subject().prepareSwapPtIn(deltaPt, block.timestamp, index); (uint256 amtOut,) = subject().swapExactPtForSy(deltaPt, 0, address(this)); console2.log("amtOut", amtOut); @@ -197,7 +197,7 @@ contract ForkRMMTest is Test { PYIndex index = YT.newIndex(); uint256 deltaSy = 1 ether; vm.warp(subject().maturity()); - subject().prepareSwapSy(deltaSy, block.timestamp, index); + subject().prepareSwapSyIn(deltaSy, block.timestamp, index); subject().swapExactSyForPt(deltaSy, 0, address(this)); assertEq(subject().strike(), 1 ether, "Strike is not approximately 1 ether."); } @@ -206,7 +206,7 @@ contract ForkRMMTest is Test { PYIndex index = YT.newIndex(); uint256 deltaSy = 1 ether; vm.warp(subject().maturity()); - subject().prepareSwapSy(deltaSy, block.timestamp, index); + subject().prepareSwapSyIn(deltaSy, block.timestamp, index); subject().swapExactSyForPt(deltaSy, 0, address(this)); assertApproxEqAbs( subject().approxSpotPrice(index.syToAsset(subject().reserveX())), @@ -263,7 +263,7 @@ contract ForkRMMTest is Test { console2.log("ytOut", ytOut); console2.log("rPT", rPT); console2.log("rSY", rSY); - (uint256 amtOut,) = subject().swapExactSyForYt(ytOut, 0, address(this)); + (uint256 amtOut,) = subject().swapExactSyForYt(ytOut, ytOut.mulDivDown(95, 100), address(this)); console2.log("amtOut", amtOut); console2.log("SY balance after", SY.balanceOf(address(this))); console2.log("YT balance after", YT.balanceOf(address(this))); @@ -418,6 +418,16 @@ contract ForkRMMTest is Test { subject().swapExactTokenForYt{value: amountIn}(address(subject().WETH()), amountIn, minSyMinted, minYtOut, address(this)); } + function test_compute_token_to_yt() public basic_sy { + uint256 amountIn = 1 ether; + PYIndex index = YT.newIndex(); + (uint256 syMinted, uint256 ytOut) = subject().computeTokenToYt(index, address(0), amountIn, block.timestamp, 500 ether); + console2.log("syMinted", syMinted); + console2.log("ytOut", ytOut); + subject().swapExactTokenForYt{value: amountIn}(address(0), 0, syMinted, ytOut, address(this)); + assertEq(YT.balanceOf(address(this)), ytOut, "YT balance of address(this) is not equal to ytOut."); + } + // TODO: add functionality for handling these on the new swaps // function test_swapX_usingIbToken() public basic_sy { // uint256 wstethBalanceInitial = IERC20(wstETH).balanceOf(address(this)); diff --git a/test/unit/SwapPt.sol b/test/unit/SwapPt.sol index b732318..3c7a73c 100644 --- a/test/unit/SwapPt.sol +++ b/test/unit/SwapPt.sol @@ -14,7 +14,7 @@ contract SwapPtTest is SetUp { PYIndex index = PYIndex.wrap(YT.pyIndexCurrent()); uint256 amountIn = 1 ether; - (,, uint256 minAmountOut,,) = rmm.prepareSwapPt(amountIn, block.timestamp, index); + (,, uint256 minAmountOut,,) = rmm.prepareSwapPtIn(amountIn, block.timestamp, index); uint256 preReserveX = rmm.reserveX(); uint256 preReserveY = rmm.reserveY(); @@ -32,7 +32,7 @@ contract SwapPtTest is SetUp { PYIndex index = PYIndex.wrap(YT.pyIndexCurrent()); uint256 amountIn = 1 ether; - (,, uint256 minAmountOut,,) = rmm.prepareSwapPt(amountIn, block.timestamp, index); + (,, uint256 minAmountOut,,) = rmm.prepareSwapPtIn(amountIn, block.timestamp, index); uint256 preRMMBalanceX = SY.balanceOf(address(rmm)); uint256 preRMMBalanceY = PT.balanceOf(address(rmm)); @@ -54,7 +54,7 @@ contract SwapPtTest is SetUp { PYIndex index = PYIndex.wrap(YT.pyIndexCurrent()); uint256 amountIn = 1 ether; - (,, uint256 minAmountOut, int256 deltaLiquidity,) = rmm.prepareSwapPt(amountIn, block.timestamp, index); + (,, uint256 minAmountOut, int256 deltaLiquidity,) = rmm.prepareSwapPtIn(amountIn, block.timestamp, index); vm.expectEmit(true, true, true, true); emit Swap(address(this), address(this), address(PT), address(SY), amountIn, minAmountOut, deltaLiquidity); @@ -68,7 +68,7 @@ contract SwapPtTest is SetUp { PYIndex index = PYIndex.wrap(YT.pyIndexCurrent()); uint256 amountIn = 1 ether; - (,, uint256 minAmountOut,,) = rmm.prepareSwapPt(amountIn, block.timestamp, index); + (,, uint256 minAmountOut,,) = rmm.prepareSwapPtIn(amountIn, block.timestamp, index); vm.expectRevert(abi.encodeWithSelector(InsufficientOutput.selector, amountIn, minAmountOut + 1, minAmountOut)); rmm.swapExactPtForSy(amountIn, minAmountOut + 1, address(this)); } diff --git a/test/unit/SwapSy.sol b/test/unit/SwapSy.sol index 82f767c..cb54b9e 100644 --- a/test/unit/SwapSy.sol +++ b/test/unit/SwapSy.sol @@ -14,7 +14,7 @@ contract SwapSyTest is SetUp { PYIndex index = PYIndex.wrap(YT.pyIndexCurrent()); uint256 amountIn = 1 ether; - (,, uint256 minAmountOut,,) = rmm.prepareSwapSy(amountIn, block.timestamp, index); + (,, uint256 minAmountOut,,) = rmm.prepareSwapSyIn(amountIn, block.timestamp, index); uint256 preReserveX = rmm.reserveX(); uint256 preReserveY = rmm.reserveY(); @@ -32,7 +32,7 @@ contract SwapSyTest is SetUp { PYIndex index = PYIndex.wrap(YT.pyIndexCurrent()); uint256 amountIn = 1 ether; - (,, uint256 minAmountOut,,) = rmm.prepareSwapSy(amountIn, block.timestamp, index); + (,, uint256 minAmountOut,,) = rmm.prepareSwapSyIn(amountIn, block.timestamp, index); uint256 preRMMBalanceX = SY.balanceOf(address(rmm)); uint256 preRMMBalanceY = PT.balanceOf(address(rmm)); @@ -54,7 +54,7 @@ contract SwapSyTest is SetUp { PYIndex index = PYIndex.wrap(YT.pyIndexCurrent()); uint256 amountIn = 1 ether; - (,, uint256 minAmountOut, int256 deltaLiquidity,) = rmm.prepareSwapSy(amountIn, block.timestamp, index); + (,, uint256 minAmountOut, int256 deltaLiquidity,) = rmm.prepareSwapSyIn(amountIn, block.timestamp, index); vm.expectEmit(true, true, true, true); emit Swap(address(this), address(this), address(SY), address(PT), amountIn, minAmountOut, deltaLiquidity); @@ -68,7 +68,7 @@ contract SwapSyTest is SetUp { PYIndex index = PYIndex.wrap(YT.pyIndexCurrent()); uint256 amountIn = 1 ether; - (,, uint256 minAmountOut,,) = rmm.prepareSwapSy(amountIn, block.timestamp, index); + (,, uint256 minAmountOut,,) = rmm.prepareSwapSyIn(amountIn, block.timestamp, index); vm.expectRevert(abi.encodeWithSelector(InsufficientOutput.selector, amountIn, minAmountOut + 1, minAmountOut)); rmm.swapExactSyForPt(amountIn, minAmountOut + 1, address(this)); } From 10dbff7e7e3c9acf1c4a13ea8f5ce942064a8353 Mon Sep 17 00:00:00 2001 From: kinrezc Date: Wed, 5 Jun 2024 16:06:22 -0400 Subject: [PATCH 2/8] fix eth -> yt test --- src/RMM.sol | 51 ++++++++++++++++++++++++------------------- test/unit/RmmSy.t.sol | 36 +++++++++--------------------- 2 files changed, 39 insertions(+), 48 deletions(-) diff --git a/src/RMM.sol b/src/RMM.sol index d2487ea..504ee5e 100644 --- a/src/RMM.sol +++ b/src/RMM.sol @@ -127,8 +127,7 @@ contract RMM is ERC20 { /// @dev Swaps SY for YT, sending at least `minAmountOut` YT to `to`. /// @notice `amountIn` is an amount of PT that needs to be minted from the SY in and the SY flash swapped from the pool function swapExactSyForYt(uint256 amountIn, uint256 minAmountOut, address to) - external - payable + public lock returns (uint256 amountOut, int256 deltaLiquidity) { @@ -140,10 +139,6 @@ contract RMM is ERC20 { (amountInWad, amountOutWad, amountOut, deltaLiquidity, strike_) = prepareSwapPtIn(amountIn, block.timestamp, index); - if (amountOut < minAmountOut) { - revert InsufficientOutput(amountInWad, minAmountOut, amountOut); - } - _adjust(-toInt(amountOutWad), toInt(amountInWad), deltaLiquidity, strike_, index); // SY is needed to cover the minted PT, so we need to debit the delta from the msg.sender @@ -151,17 +146,23 @@ contract RMM is ERC20 { uint256 ytOut = amountOut + delta; (uint256 debitNative) = _debit(address(SY), delta); + amountOut = mintPtYt(ytOut, address(this)); + + if (amountOut < minAmountOut) { + revert InsufficientOutput(amountInWad, minAmountOut, amountOut); + } + _credit(address(YT), to, amountOut); emit Swap(msg.sender, to, address(SY), address(YT), debitNative, amountOut, deltaLiquidity); } - function swapExactTokenForYt(address token, uint256 amountIn, uint256 minSyMinted, uint256 minYtOut, address to) + function swapExactTokenForYt(address token, uint256 amountTokenIn, uint256 amountPtForFlashSwap, uint256 minSyMinted, uint256 minYtOut, address to) external payable lock - returns (uint256 amountYtOut, int256 deltaLiquidity) + returns (uint256 amountOut, int256 deltaLiquidity) { // initialize to msg.value uint256 debitNative = msg.value; @@ -173,14 +174,14 @@ contract RMM is ERC20 { } if (msg.value > 0 && SY.isValidTokenIn(address(0))) { - amountSyMinted += SY.deposit{value: msg.value}(address(this), address(0), msg.value, minSyMinted); + amountSyMinted += SY.deposit{value: msg.value}(address(this), address(0), msg.value, 0); } if (token != address(0)) { - ERC20(token).transferFrom(msg.sender, address(this), amountIn); - ERC20(token).approve(address(SY), amountIn); - amountSyMinted += SY.deposit(address(this), token, amountIn, minSyMinted); - debitNative += amountIn; + ERC20(token).transferFrom(msg.sender, address(this), amountTokenIn); + ERC20(token).approve(address(SY), amountTokenIn); + amountSyMinted += SY.deposit(address(this), token, amountTokenIn, 0); + debitNative += amountTokenIn; } if (amountSyMinted < minSyMinted) { @@ -192,28 +193,34 @@ contract RMM is ERC20 { uint256 amountOutWad; uint256 strike_; - (amountInWad, amountOutWad, amountYtOut, deltaLiquidity, strike_) = - prepareSwapPtIn(amountSyMinted, block.timestamp, index); + (amountInWad, amountOutWad, amountOut, deltaLiquidity, strike_) = + prepareSwapPtIn(amountPtForFlashSwap, block.timestamp, index); - if (amountYtOut < minYtOut) { - revert InsufficientOutput(amountInWad, minYtOut, amountYtOut); - } _adjust(-toInt(amountOutWad), toInt(amountInWad), deltaLiquidity, strike_, index); // SY is needed to cover the minted PT, so we need to debit the delta from the msg.sender - uint256 ytOut = amountYtOut + (index.assetToSyUp(amountInWad) - amountOutWad); + uint256 ytOut = amountOut + (index.assetToSyUp(amountInWad) - amountOutWad); + console2.log("ytOut", ytOut); + console2.log("amt out 1", amountOut); // Converts the SY received from minting it into its components PT and YT. - amountYtOut = mintPtYt(ytOut, address(this)); - _credit(address(YT), to, amountYtOut); + amountOut = mintPtYt(ytOut, address(this)); + console2.log("amountOut", amountOut); + + if (amountOut < minYtOut) { + revert InsufficientOutput(amountInWad, minYtOut, amountOut); + } + + _credit(address(YT), to, amountOut); uint256 debitSurplus = address(this).balance; + if (debitSurplus > 0) { _sendETH(to, debitSurplus); } - emit Swap(msg.sender, to, address(SY), address(YT), debitNative - debitSurplus, amountYtOut, deltaLiquidity); + // emit Swap(msg.sender, to, address(SY), address(YT), debitNative - debitSurplus, amountOut, deltaLiquidity); } function swapExactPtForSy(uint256 amountIn, uint256 minAmountOut, address to) diff --git a/test/unit/RmmSy.t.sol b/test/unit/RmmSy.t.sol index f9ed3e3..6b4487c 100644 --- a/test/unit/RmmSy.t.sol +++ b/test/unit/RmmSy.t.sol @@ -393,39 +393,23 @@ contract ForkRMMTest is Test { assertEq(SY.balanceOf(address(this)), amountOut, "SY balance of address(this) is not equal to amountOut."); } - function test_swap_eth_for_yt() public basic_sy { - uint256 amountIn = 1 ether; - uint256 minSyMinted = 0; - uint256 minYtOut = 0; - subject().swapExactTokenForYt{value: amountIn}(address(0), 0, minSyMinted, minYtOut, address(this)); - } - - function test_swap_weth_for_yt() public basic_sy { - uint256 amountIn = 1 ether; - uint256 minSyMinted = 0; - uint256 minYtOut = 0; - deal(subject().WETH(), address(this), amountIn); - IERC20(subject().WETH()).approve(address(subject()), type(uint256).max); - subject().swapExactTokenForYt(address(subject().WETH()), amountIn, minSyMinted, minYtOut, address(this)); - } + function test_compute_token_to_yt() public basic_sy { + SY.transfer(address(0x55), SY.balanceOf(address(this))); + PT.transfer(address(0x55), PT.balanceOf(address(this))); + YT.transfer(address(0x55), YT.balanceOf(address(this))); - function test_swap_weth_and_eth_for_yt() public basic_sy { - uint256 amountIn = 1 ether; - uint256 minSyMinted = 0; - uint256 minYtOut = 0; - deal(subject().WETH(), address(this), amountIn); - IERC20(subject().WETH()).approve(address(subject()), type(uint256).max); - subject().swapExactTokenForYt{value: amountIn}(address(subject().WETH()), amountIn, minSyMinted, minYtOut, address(this)); - } + // assert balance of address(this) is 0 for SY, PT, and YT + assertEq(SY.balanceOf(address(this)), 0, "SY balance of address(this) is not 0."); + assertEq(PT.balanceOf(address(this)), 0, "PT balance of address(this) is not 0."); + assertEq(YT.balanceOf(address(this)), 0, "YT balance of address(this) is not 0."); - function test_compute_token_to_yt() public basic_sy { uint256 amountIn = 1 ether; PYIndex index = YT.newIndex(); (uint256 syMinted, uint256 ytOut) = subject().computeTokenToYt(index, address(0), amountIn, block.timestamp, 500 ether); console2.log("syMinted", syMinted); console2.log("ytOut", ytOut); - subject().swapExactTokenForYt{value: amountIn}(address(0), 0, syMinted, ytOut, address(this)); - assertEq(YT.balanceOf(address(this)), ytOut, "YT balance of address(this) is not equal to ytOut."); + subject().swapExactTokenForYt{value: amountIn}(address(0), 0, ytOut, syMinted, ytOut, address(this)); + assertApproxEqAbs(YT.balanceOf(address(this)), ytOut, 1_000, "YT balance of address(this) is not equal to ytOut."); } // TODO: add functionality for handling these on the new swaps From 1f071abaf2b7976e73bab75eb26ac03a1961d060 Mon Sep 17 00:00:00 2001 From: kinrezc Date: Wed, 5 Jun 2024 16:08:18 -0400 Subject: [PATCH 3/8] test weth -> yt swap --- test/unit/RmmSy.t.sol | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/test/unit/RmmSy.t.sol b/test/unit/RmmSy.t.sol index 6b4487c..7a6c321 100644 --- a/test/unit/RmmSy.t.sol +++ b/test/unit/RmmSy.t.sol @@ -393,7 +393,7 @@ contract ForkRMMTest is Test { assertEq(SY.balanceOf(address(this)), amountOut, "SY balance of address(this) is not equal to amountOut."); } - function test_compute_token_to_yt() public basic_sy { + function test_compute_eth_to_yt() public basic_sy { SY.transfer(address(0x55), SY.balanceOf(address(this))); PT.transfer(address(0x55), PT.balanceOf(address(this))); YT.transfer(address(0x55), YT.balanceOf(address(this))); @@ -406,12 +406,30 @@ contract ForkRMMTest is Test { uint256 amountIn = 1 ether; PYIndex index = YT.newIndex(); (uint256 syMinted, uint256 ytOut) = subject().computeTokenToYt(index, address(0), amountIn, block.timestamp, 500 ether); - console2.log("syMinted", syMinted); - console2.log("ytOut", ytOut); subject().swapExactTokenForYt{value: amountIn}(address(0), 0, ytOut, syMinted, ytOut, address(this)); assertApproxEqAbs(YT.balanceOf(address(this)), ytOut, 1_000, "YT balance of address(this) is not equal to ytOut."); } + function test_compute_token_to_yt() public basic_sy { + SY.transfer(address(0x55), SY.balanceOf(address(this))); + PT.transfer(address(0x55), PT.balanceOf(address(this))); + YT.transfer(address(0x55), YT.balanceOf(address(this))); + + // assert balance of address(this) is 0 for SY, PT, and YT + assertEq(SY.balanceOf(address(this)), 0, "SY balance of address(this) is not 0."); + assertEq(PT.balanceOf(address(this)), 0, "PT balance of address(this) is not 0."); + assertEq(YT.balanceOf(address(this)), 0, "YT balance of address(this) is not 0."); + + uint256 amountIn = 1 ether; + PYIndex index = YT.newIndex(); + (uint256 syMinted, uint256 ytOut) = subject().computeTokenToYt(index, address(subject().WETH()), amountIn, block.timestamp, 500 ether); + deal(subject().WETH(), address(this), amountIn); + IERC20(subject().WETH()).approve(address(subject()), amountIn); + subject().swapExactTokenForYt(address(subject().WETH()), amountIn, ytOut, syMinted, ytOut, address(this)); + assertApproxEqAbs(YT.balanceOf(address(this)), ytOut, 1_000, "YT balance of address(this) is not equal to ytOut."); + } + + // TODO: add functionality for handling these on the new swaps // function test_swapX_usingIbToken() public basic_sy { // uint256 wstethBalanceInitial = IERC20(wstETH).balanceOf(address(this)); From 66f7f84dd78e92e744fa2b6b29fab8aca71bf72f Mon Sep 17 00:00:00 2001 From: kinrezc Date: Wed, 5 Jun 2024 16:22:10 -0400 Subject: [PATCH 4/8] uncomment swap event --- src/RMM.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/RMM.sol b/src/RMM.sol index 504ee5e..f90863c 100644 --- a/src/RMM.sol +++ b/src/RMM.sol @@ -220,7 +220,7 @@ contract RMM is ERC20 { _sendETH(to, debitSurplus); } - // emit Swap(msg.sender, to, address(SY), address(YT), debitNative - debitSurplus, amountOut, deltaLiquidity); + emit Swap(msg.sender, to, address(SY), address(YT), debitNative - debitSurplus, amountOut, deltaLiquidity); } function swapExactPtForSy(uint256 amountIn, uint256 minAmountOut, address to) From 9b09dfc150042bbafda2312318336b76acc1716a Mon Sep 17 00:00:00 2001 From: kinrezc Date: Wed, 5 Jun 2024 16:30:58 -0400 Subject: [PATCH 5/8] fix chain id in foundry toml --- foundry.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/foundry.toml b/foundry.toml index 3d64cee..695811f 100644 --- a/foundry.toml +++ b/foundry.toml @@ -10,6 +10,5 @@ fail_on_revert = false mainnet = "${MAINNET_RPC_URL}" op_sepolia = "${OP_SEPOLIA_RPC_URL}" testnet = "${TESTNET_RPC_URL}" -rmm_testnet = "${RMM_TESTNET_RPC_URL}" -unknown_chain = { key = "${TENDERLY_ACCESS_KEY}", chain = 1, url = "${RMM_TESTNET_RPC_URL}" } +unknown_chain = { key = "${TENDERLY_ACCESS_KEY}", chain = 8888, url = "${TESTNET_RPC_URL}" } From 69531fad2a335d0210bb59b56e11bb10bdc4f212 Mon Sep 17 00:00:00 2001 From: clemlak Date: Thu, 6 Jun 2024 12:08:23 +0400 Subject: [PATCH 6/8] chore: enable optimizer to reduce contract sizes --- foundry.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/foundry.toml b/foundry.toml index 695811f..5a5dab2 100644 --- a/foundry.toml +++ b/foundry.toml @@ -2,6 +2,8 @@ src = "src" out = "out" libs = ["lib"] +optimizer_runs = 1 +optimizer = true [invariant] fail_on_revert = false From 8f936609ea804b9bbf19c0952553a5de70cc23f5 Mon Sep 17 00:00:00 2001 From: kinrezc Date: Thu, 6 Jun 2024 10:56:24 -0400 Subject: [PATCH 7/8] update chainId --- foundry.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/foundry.toml b/foundry.toml index 5a5dab2..b26d057 100644 --- a/foundry.toml +++ b/foundry.toml @@ -13,4 +13,4 @@ mainnet = "${MAINNET_RPC_URL}" op_sepolia = "${OP_SEPOLIA_RPC_URL}" testnet = "${TESTNET_RPC_URL}" -unknown_chain = { key = "${TENDERLY_ACCESS_KEY}", chain = 8888, url = "${TESTNET_RPC_URL}" } +unknown_chain = { key = "${TENDERLY_ACCESS_KEY}", chain = 753712, url = "${TESTNET_RPC_URL}" } From a129d323dc5a7a94041ecf6a5911732c8c6f5c56 Mon Sep 17 00:00:00 2001 From: kinrezc Date: Thu, 6 Jun 2024 12:04:54 -0400 Subject: [PATCH 8/8] rm console2 import to fix size limit --- src/RMM.sol | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/RMM.sol b/src/RMM.sol index f90863c..57a5f4f 100644 --- a/src/RMM.sol +++ b/src/RMM.sol @@ -8,7 +8,6 @@ import {PYIndexLib, PYIndex} from "pendle/core/StandardizedYield/PYIndex.sol"; import {IPPrincipalToken} from "pendle/interfaces/IPPrincipalToken.sol"; import {IStandardizedYield} from "pendle/interfaces/IStandardizedYield.sol"; import {IPYieldToken} from "pendle/interfaces/IPYieldToken.sol"; -import "forge-std/console2.sol"; import "./lib/RmmLib.sol"; import "./lib/RmmErrors.sol"; @@ -201,12 +200,9 @@ contract RMM is ERC20 { // SY is needed to cover the minted PT, so we need to debit the delta from the msg.sender uint256 ytOut = amountOut + (index.assetToSyUp(amountInWad) - amountOutWad); - console2.log("ytOut", ytOut); - console2.log("amt out 1", amountOut); // Converts the SY received from minting it into its components PT and YT. amountOut = mintPtYt(ytOut, address(this)); - console2.log("amountOut", amountOut); if (amountOut < minYtOut) { revert InsufficientOutput(amountInWad, minYtOut, amountOut);