diff --git a/src/SYCoveredCall/SYCoveredCall.sol b/src/SYCoveredCall/SYCoveredCall.sol index 51afd74e..47e46252 100644 --- a/src/SYCoveredCall/SYCoveredCall.sol +++ b/src/SYCoveredCall/SYCoveredCall.sol @@ -218,45 +218,19 @@ contract SYCoveredCall is PairStrategy { SYCoveredCallParams memory ccParams = abi.decode(params, (SYCoveredCallParams)); - uint256 computedL; - uint256 swapTimestamp; - ( - tokenInIndex, - tokenOutIndex, - amountIn, - amountOut, - computedL, - swapTimestamp - ) = abi.decode( - data, (uint256, uint256, uint256, uint256, uint256, uint256) - ); - - console2.log("swapTimestamp", swapTimestamp); - console2.log("block.timestamp", block.timestamp); + (tokenInIndex, tokenOutIndex, amountIn, amountOut) = + abi.decode(data, (uint256, uint256, uint256, uint256)); - if ( - swapTimestamp < internalParams[poolId].lastTimestamp - || swapTimestamp < block.timestamp - T_EPSILON - || swapTimestamp > block.timestamp + T_EPSILON - ) { - revert InvalidTimestamp(); - } + ccParams.lastTimestamp = block.timestamp; - // if timestamp is valid, append it to the poolParams for validation check - ccParams.lastTimestamp = swapTimestamp; - - ccParams.mean = - computeKGivenLastPrice(pool.reserves[0], computedL, ccParams); - - int256 computedInvariant = - tradingFunction(pool.reserves, computedL, abi.encode(ccParams)); - console2.log("got here"); + ccParams.mean = computeKGivenLastPrice( + pool.reserves[0], pool.totalLiquidity, ccParams + ); - if (computedInvariant < 0 || computedInvariant > EPSILON) { - revert InvalidComputedLiquidity(computedInvariant); - } + int256 prevInvariant = tradingFunction( + pool.reserves, pool.totalLiquidity, abi.encode(ccParams) + ); - console2.log("now we compute dl"); deltaLiquidity = _computeSwapDeltaLiquidity( pool, abi.encode(ccParams), @@ -265,18 +239,21 @@ contract SYCoveredCall is PairStrategy { amountIn, amountOut ); - console2.log("deltaLiquidity", deltaLiquidity); pool.reserves[tokenInIndex] += amountIn; pool.reserves[tokenOutIndex] -= amountOut; invariant = tradingFunction( - pool.reserves, computedL + deltaLiquidity, abi.encode(ccParams) + pool.reserves, + pool.totalLiquidity + deltaLiquidity, + abi.encode(ccParams) ); params = abi.encode(ccParams); - valid = invariant >= 0; - //valid = invariant >= 0 && invariant <= EPSILON; + console2.log("prevInvariant: ", prevInvariant); + console2.log("invariant: ", invariant); + console2.log("deltaLiquidity: ", deltaLiquidity); + valid = prevInvariant <= invariant; } function postSwapHook( diff --git a/src/SYCoveredCall/SYCoveredCallMath.sol b/src/SYCoveredCall/SYCoveredCallMath.sol index 94440f9d..b72d4c31 100644 --- a/src/SYCoveredCall/SYCoveredCallMath.sol +++ b/src/SYCoveredCall/SYCoveredCallMath.sol @@ -323,20 +323,28 @@ function computeDeallocationGivenDeltaY( /// @dev This is a pure anonymous function defined at the file level, which allows /// it to be passed as an argument to another function. BisectionLib.sol takes this -/// function as an argument to find the root of the trading function given the reserveYWad. +/// function as an argument to find the root of the trading function given the reserveXWad. function findRootY(bytes memory data, uint256 ry) pure returns (int256) { - (uint256 rx, uint256 L, SYCoveredCallParams memory params) = - abi.decode(data, (uint256, uint256, SYCoveredCallParams)); - return computeTradingFunction(rx, ry, L, params); + ( + uint256 rx, + uint256 L, + int256 prevInvariant, + SYCoveredCallParams memory params + ) = abi.decode(data, (uint256, uint256, int256, SYCoveredCallParams)); + return computeTradingFunction(rx, ry, L, params) - (prevInvariant + 1); } /// @dev This is a pure anonymous function defined at the file level, which allows /// it to be passed as an argument to another function. BisectionLib.sol takes this -/// function as an argument to find the root of the trading function given the reserveXWad. +/// function as an argument to find the root of the trading function given the reserveYWad. function findRootX(bytes memory data, uint256 rx) pure returns (int256) { - (uint256 ry, uint256 L, SYCoveredCallParams memory params) = - abi.decode(data, (uint256, uint256, SYCoveredCallParams)); - return computeTradingFunction(rx, ry, L, params); + ( + uint256 ry, + uint256 L, + int256 prevInvariant, + SYCoveredCallParams memory params + ) = abi.decode(data, (uint256, uint256, int256, SYCoveredCallParams)); + return computeTradingFunction(rx, ry, L, params) - (prevInvariant + 1); } /// @dev This is a pure anonymous function defined at the file level, which allows @@ -404,15 +412,16 @@ function computeNextRx( uint256 rY, uint256 L, int256 invariant, + int256 prevInvariant, uint256 approximatedRx, SYCoveredCallParams memory params ) pure returns (uint256 rX) { uint256 upper = approximatedRx; uint256 lower = approximatedRx; int256 computedInvariant = invariant; - if (computedInvariant < 0) { - while (computedInvariant < 0) { - upper = upper.mulDivUp(1001, 1000); + if (computedInvariant - prevInvariant < 0) { + while (computedInvariant - prevInvariant < 0) { + upper = upper.mulDivUp(101, 100); upper = upper > L ? L : upper; computedInvariant = computeTradingFunction({ rX: upper, @@ -422,8 +431,8 @@ function computeNextRx( }); } } else { - while (computedInvariant > 0) { - lower = lower.mulDivDown(999, 1000); + while (computedInvariant - prevInvariant > 0) { + lower = lower.mulDivDown(99, 100); lower = lower > L ? L : lower; computedInvariant = computeTradingFunction({ rX: lower, @@ -434,7 +443,12 @@ function computeNextRx( } } (uint256 rootInput, uint256 upperInput,) = bisection( - abi.encode(rY, L, params), lower, upper, 0, MAX_ITER, findRootX + abi.encode(rY, L, prevInvariant, params), + lower, + upper, + 0, + MAX_ITER, + findRootX ); // `upperInput` should be positive, so if root is < 0 return upperInput instead if ( @@ -451,15 +465,16 @@ function computeNextRy( uint256 rX, uint256 L, int256 invariant, + int256 prevInvariant, uint256 approximatedRy, SYCoveredCallParams memory params ) pure returns (uint256 rY) { uint256 upper = approximatedRy; uint256 lower = approximatedRy; int256 computedInvariant = invariant; - if (computedInvariant < 0) { - while (computedInvariant < 0) { - upper = upper.mulDivUp(1001, 1000); + if (computedInvariant - prevInvariant < 0) { + while (computedInvariant - prevInvariant < 0) { + upper = upper.mulDivUp(101, 100); computedInvariant = computeTradingFunction({ rX: rX, rY: upper, @@ -468,8 +483,8 @@ function computeNextRy( }); } } else { - while (computedInvariant > 0) { - lower = lower.mulDivDown(999, 1000); + while (computedInvariant - prevInvariant > 0) { + lower = lower.mulDivDown(99, 100); computedInvariant = computeTradingFunction({ rX: rX, rY: lower, @@ -479,7 +494,12 @@ function computeNextRy( } } (uint256 rootInput, uint256 upperInput,) = bisection( - abi.encode(rX, L, params), lower, upper, 0, MAX_ITER, findRootY + abi.encode(rX, L, prevInvariant, params), + lower, + upper, + 0, + MAX_ITER, + findRootY ); // `upperInput` should be positive, so if root is < 0 return upperInput instead if ( diff --git a/src/SYCoveredCall/SYCoveredCallSolver.sol b/src/SYCoveredCall/SYCoveredCallSolver.sol index 5b98232b..53cd2d9f 100644 --- a/src/SYCoveredCall/SYCoveredCallSolver.sol +++ b/src/SYCoveredCall/SYCoveredCallSolver.sol @@ -46,6 +46,7 @@ import { computeDeltaYGivenDeltaL, computeDeltaXGivenDeltaL } from "src/lib/StrategyLib.sol"; +import "forge-std/console2.sol"; contract SYCoveredCallSolver is ISolver { using FixedPointMathLib for uint256; @@ -184,7 +185,6 @@ contract SYCoveredCallSolver is ISolver { uint256 amountOut; uint256 deltaLiquidity; uint256 fees; - uint256 timestamp; } function prepareSwap( @@ -200,15 +200,17 @@ contract SYCoveredCallSolver is ISolver { getPoolParamsCustomTimestamp(poolId, block.timestamp); SimulateSwapState memory state; - state.timestamp = block.timestamp; - uint256 startComputedL = getNextLiquidity( - poolId, preReserves[0], preReserves[1], preTotalLiquidity - ); poolParams.mean = computeKGivenLastPrice( preReserves[0], preTotalLiquidity, poolParams ); + int256 prevInvariant = computeTradingFunction( + preReserves[0], preReserves[1], preTotalLiquidity, poolParams + ); + + console2.log("prevInvariant", prevInvariant); + uint256 poolId = poolId; uint256 swapAmountIn = amountIn; { @@ -222,11 +224,17 @@ contract SYCoveredCallSolver is ISolver { ); endReserves.rx = preReserves[0] + swapAmountIn; - endReserves.L = startComputedL + state.deltaLiquidity; - uint256 approxPrice = getEstimatedPrice(poolId, 0, 1); + endReserves.L = preTotalLiquidity + state.deltaLiquidity; + uint256 approxPrice = getIntermediatePrice( + endReserves.rx, endReserves.L, 0, 1, poolParams + ); endReserves.ry = getNextReserveY( - poolId, endReserves.rx, endReserves.L, approxPrice + endReserves.rx, + endReserves.L, + approxPrice, + prevInvariant, + poolParams ); require( @@ -244,11 +252,17 @@ contract SYCoveredCallSolver is ISolver { ); endReserves.ry = preReserves[1] + swapAmountIn; - endReserves.L = startComputedL + state.deltaLiquidity; - uint256 approxPrice = getEstimatedPrice(poolId, 1, 0); + endReserves.L = preTotalLiquidity + state.deltaLiquidity; + uint256 approxPrice = getIntermediatePrice( + endReserves.ry, endReserves.L, 1, 0, poolParams + ); endReserves.rx = getNextReserveX( - poolId, endReserves.ry, endReserves.L, approxPrice + endReserves.ry, + endReserves.L, + approxPrice, + prevInvariant, + poolParams ); require( @@ -266,23 +280,9 @@ contract SYCoveredCallSolver is ISolver { bytes memory swapData; if (tokenInIndex == 0) { - swapData = abi.encode( - 0, - 1, - swapAmountIn, - state.amountOut, - startComputedL, - state.timestamp - ); + swapData = abi.encode(0, 1, swapAmountIn, state.amountOut); } else { - swapData = abi.encode( - 1, - 0, - swapAmountIn, - state.amountOut, - startComputedL, - state.timestamp - ); + swapData = abi.encode(1, 0, swapAmountIn, state.amountOut); } (bool valid,,,,,,,) = IStrategy(strategy).validateSwap( @@ -292,6 +292,25 @@ contract SYCoveredCallSolver is ISolver { return (valid, state.amountOut, swapData); } + function getIntermediatePrice( + uint256 independentReserve, + uint256 L, + uint256 tokenInIndex, + uint256 tokenOutIndex, + SYCoveredCallParams memory params + ) public view returns (uint256 price) { + if ( + tokenInIndex > 1 || tokenOutIndex > 1 + || tokenInIndex == tokenOutIndex + ) revert InvalidTokenIndex(); + + if (tokenInIndex == 0) { + price = computePriceGivenX(independentReserve, L, params); + } else { + price = computePriceGivenY(independentReserve, L, params); + } + } + /// @inheritdoc ISolver function getEstimatedPrice( uint256 poolId, @@ -344,30 +363,32 @@ contract SYCoveredCallSolver is ISolver { } function getNextReserveX( - uint256 poolId, uint256 ry, uint256 L, - uint256 S + uint256 S, + int256 prevInvariant, + SYCoveredCallParams memory params ) public view returns (uint256) { - SYCoveredCallParams memory poolParams = - getPoolParamsCustomTimestamp(poolId, block.timestamp); - uint256 approximatedRx = computeXGivenL(L, S, poolParams); - int256 invariant = - computeTradingFunction(approximatedRx, ry, L, poolParams); - return computeNextRx(ry, L, invariant, approximatedRx, poolParams); + uint256 approximatedRx = computeXGivenL(L, S, params); + int256 invariant = computeTradingFunction(approximatedRx, ry, L, params); + console2.log("intermediate invariant", invariant); + return computeNextRx( + ry, L, invariant, prevInvariant, approximatedRx, params + ); } function getNextReserveY( - uint256 poolId, uint256 rx, uint256 L, - uint256 S + uint256 S, + int256 prevInvariant, + SYCoveredCallParams memory params ) public view returns (uint256) { - SYCoveredCallParams memory poolParams = - getPoolParamsCustomTimestamp(poolId, block.timestamp); - uint256 approximatedRy = computeYGivenL(L, S, poolParams); - int256 invariant = - computeTradingFunction(rx, approximatedRy, L, poolParams); - return computeNextRy(rx, L, invariant, approximatedRy, poolParams); + uint256 approximatedRy = computeYGivenL(L, S, params); + int256 invariant = computeTradingFunction(rx, approximatedRy, L, params); + console2.log("intermediate invariant", invariant); + return computeNextRy( + rx, L, invariant, prevInvariant, approximatedRy, params + ); } } diff --git a/test/SYCoveredCall/unit/SetUp.sol b/test/SYCoveredCall/unit/SetUp.sol index 89c0ea0a..26d46434 100644 --- a/test/SYCoveredCall/unit/SetUp.sol +++ b/test/SYCoveredCall/unit/SetUp.sol @@ -24,8 +24,6 @@ contract SYCoveredCallSetUp is SetUp { uint256 public constant FEE = 0.00001 ether; uint256 defaultReserveX = 100 ether; - uint256 defaultReserveXMil = 1_000_000 ether; - uint256 defaultReserveXDeep = ONE * 10_000_000; uint256 defaultPrice = ONE; uint256 defaultPricePoint9Rate = 0.84167999326 ether; diff --git a/test/SYCoveredCall/unit/Swap.t.sol b/test/SYCoveredCall/unit/Swap.t.sol index 361b2a9d..c049d2fa 100644 --- a/test/SYCoveredCall/unit/Swap.t.sol +++ b/test/SYCoveredCall/unit/Swap.t.sol @@ -8,14 +8,15 @@ import { FixedPointMathLib } from "solmate/utils/FixedPointMathLib.sol"; contract SYCoveredCallSwapTest is SYCoveredCallSetUp { using FixedPointMathLib for uint256; - function test_SYCoveredCall_swap_SwapsXforY() public init { + function test_SYCoveredCall_swap_SwapsXforYWarpOneBlock() public init { + vm.warp(block.timestamp + 12); uint256 preDfmmBalanceX = tokenX.balanceOf(address(dfmm)); uint256 preDfmmBalanceY = tokenY.balanceOf(address(dfmm)); uint256 preUserBalanceX = tokenX.balanceOf(address(this)); uint256 preUserBalanceY = tokenY.balanceOf(address(this)); - uint256 amountIn = 0.1 ether; + uint256 amountIn = 0.75 ether; bool swapXForY = true; (bool valid,, bytes memory payload) = @@ -64,14 +65,14 @@ contract SYCoveredCallSwapTest is SYCoveredCallSetUp { ); } - function test_SYCoveredCall_swap_SwapsYforX() public init { + function test_SYCoveredCall_swap_SwapsYforXNoWarp() public init { uint256 preDfmmBalanceX = tokenX.balanceOf(address(dfmm)); uint256 preDfmmBalanceY = tokenY.balanceOf(address(dfmm)); uint256 preUserBalanceX = tokenX.balanceOf(address(this)); uint256 preUserBalanceY = tokenY.balanceOf(address(this)); - uint256 amountIn = 0.1 ether; + uint256 amountIn = 0.75 ether; bool swapXForY = false; (bool valid,, bytes memory payload) = @@ -108,8 +109,13 @@ contract SYCoveredCallSwapTest is SYCoveredCallSetUp { uint256 ry = preReserves[1] + amountIn; uint256 L = startL + deltaLiquidity; uint256 approxPrice = solver.getEstimatedPrice(POOL_ID, 1, 0); + int256 prevInvariant = computeTradingFunction( + preReserves[0], preReserves[1], preTotalLiquidity, poolParams + ); - uint256 rx = solver.getNextReserveX(POOL_ID, ry, L, approxPrice); + uint256 rx = solver.getNextReserveX( + ry, L, approxPrice, prevInvariant, poolParams + ); int256 invariant = computeTradingFunction(rx, ry, L, poolParams); while (invariant >= 0) {