From 7f4c9bd9b02d55d1811096390f6019632f43f608 Mon Sep 17 00:00:00 2001 From: Shuhui Luo <107524008+shuhuiluo@users.noreply.github.com> Date: Fri, 17 May 2024 00:04:52 -0400 Subject: [PATCH] Refactor swap logic and add `getSqrtPriceTarget` function The commit refactors the swap logic to use a newly added function, getSqrtPriceTarget. This function, defined in the SwapMath library, computes the square root price target for the next swap step. A corresponding fuzz test has been included in test/libraries/SwapMath.t.sol to confirm the correctness of the function. The update simplifies the code and enhances readability by abstracting complex operations into a standalone function. --- .../poolManager bytecode size.snap | 2 +- .forge-snapshots/simple swap with native.snap | 2 +- .forge-snapshots/simple swap.snap | 2 +- .../swap CA fee on unspecified.snap | 2 +- ...p against liquidity with native token.snap | 2 +- .forge-snapshots/swap against liquidity.snap | 2 +- .../swap burn 6909 for input.snap | 2 +- .../swap burn native 6909 for input.snap | 2 +- .../swap mint native output as 6909.snap | 2 +- .../swap mint output as 6909.snap | 2 +- ...wap skips hook call if hook is caller.snap | 2 +- .forge-snapshots/swap with dynamic fee.snap | 2 +- .forge-snapshots/swap with hooks.snap | 2 +- .../swap with lp fee and protocol fee.snap | 2 +- .../swap with return dynamic fee.snap | 2 +- .../update dynamic fee in before swap.snap | 2 +- src/libraries/Pool.sol | 6 +---- src/libraries/SwapMath.sol | 23 +++++++++++++++++++ test/libraries/SwapMath.t.sol | 12 ++++++++++ 19 files changed, 52 insertions(+), 21 deletions(-) diff --git a/.forge-snapshots/poolManager bytecode size.snap b/.forge-snapshots/poolManager bytecode size.snap index d0b5dc5fe..2ba8b3df3 100644 --- a/.forge-snapshots/poolManager bytecode size.snap +++ b/.forge-snapshots/poolManager bytecode size.snap @@ -1 +1 @@ -22118 \ No newline at end of file +22046 \ No newline at end of file diff --git a/.forge-snapshots/simple swap with native.snap b/.forge-snapshots/simple swap with native.snap index 2ad268097..bf5b57c6a 100644 --- a/.forge-snapshots/simple swap with native.snap +++ b/.forge-snapshots/simple swap with native.snap @@ -1 +1 @@ -117431 \ No newline at end of file +117301 \ No newline at end of file diff --git a/.forge-snapshots/simple swap.snap b/.forge-snapshots/simple swap.snap index 37bd6d94c..79ad3f252 100644 --- a/.forge-snapshots/simple swap.snap +++ b/.forge-snapshots/simple swap.snap @@ -1 +1 @@ -132592 \ No newline at end of file +132462 \ No newline at end of file diff --git a/.forge-snapshots/swap CA fee on unspecified.snap b/.forge-snapshots/swap CA fee on unspecified.snap index b4673db0a..aa7d3a92e 100644 --- a/.forge-snapshots/swap CA fee on unspecified.snap +++ b/.forge-snapshots/swap CA fee on unspecified.snap @@ -1 +1 @@ -183730 \ No newline at end of file +183600 \ No newline at end of file diff --git a/.forge-snapshots/swap against liquidity with native token.snap b/.forge-snapshots/swap against liquidity with native token.snap index 976368665..ed20d9df1 100644 --- a/.forge-snapshots/swap against liquidity with native token.snap +++ b/.forge-snapshots/swap against liquidity with native token.snap @@ -1 +1 @@ -113056 \ No newline at end of file +112991 \ No newline at end of file diff --git a/.forge-snapshots/swap against liquidity.snap b/.forge-snapshots/swap against liquidity.snap index c7dc9c39b..135903d1a 100644 --- a/.forge-snapshots/swap against liquidity.snap +++ b/.forge-snapshots/swap against liquidity.snap @@ -1 +1 @@ -124399 \ No newline at end of file +124334 \ No newline at end of file diff --git a/.forge-snapshots/swap burn 6909 for input.snap b/.forge-snapshots/swap burn 6909 for input.snap index dd0a4cda7..ab1fb3cd6 100644 --- a/.forge-snapshots/swap burn 6909 for input.snap +++ b/.forge-snapshots/swap burn 6909 for input.snap @@ -1 +1 @@ -136452 \ No newline at end of file +136377 \ No newline at end of file diff --git a/.forge-snapshots/swap burn native 6909 for input.snap b/.forge-snapshots/swap burn native 6909 for input.snap index a3b40bd26..f96779583 100644 --- a/.forge-snapshots/swap burn native 6909 for input.snap +++ b/.forge-snapshots/swap burn native 6909 for input.snap @@ -1 +1 @@ -125580 \ No newline at end of file +125515 \ No newline at end of file diff --git a/.forge-snapshots/swap mint native output as 6909.snap b/.forge-snapshots/swap mint native output as 6909.snap index eae2906eb..46de71b19 100644 --- a/.forge-snapshots/swap mint native output as 6909.snap +++ b/.forge-snapshots/swap mint native output as 6909.snap @@ -1 +1 @@ -147641 \ No newline at end of file +147566 \ No newline at end of file diff --git a/.forge-snapshots/swap mint output as 6909.snap b/.forge-snapshots/swap mint output as 6909.snap index 9b81ca131..e8e360105 100644 --- a/.forge-snapshots/swap mint output as 6909.snap +++ b/.forge-snapshots/swap mint output as 6909.snap @@ -1 +1 @@ -164446 \ No newline at end of file +164316 \ No newline at end of file diff --git a/.forge-snapshots/swap skips hook call if hook is caller.snap b/.forge-snapshots/swap skips hook call if hook is caller.snap index d8c43d6d2..6b3f812b4 100644 --- a/.forge-snapshots/swap skips hook call if hook is caller.snap +++ b/.forge-snapshots/swap skips hook call if hook is caller.snap @@ -1 +1 @@ -223312 \ No newline at end of file +223117 \ No newline at end of file diff --git a/.forge-snapshots/swap with dynamic fee.snap b/.forge-snapshots/swap with dynamic fee.snap index fc2465906..b5a202d90 100644 --- a/.forge-snapshots/swap with dynamic fee.snap +++ b/.forge-snapshots/swap with dynamic fee.snap @@ -1 +1 @@ -148647 \ No newline at end of file +148517 \ No newline at end of file diff --git a/.forge-snapshots/swap with hooks.snap b/.forge-snapshots/swap with hooks.snap index ca9e7dbe2..e30c1bec0 100644 --- a/.forge-snapshots/swap with hooks.snap +++ b/.forge-snapshots/swap with hooks.snap @@ -1 +1 @@ -124411 \ No newline at end of file +124346 \ No newline at end of file diff --git a/.forge-snapshots/swap with lp fee and protocol fee.snap b/.forge-snapshots/swap with lp fee and protocol fee.snap index c0266ee6c..f799686d2 100644 --- a/.forge-snapshots/swap with lp fee and protocol fee.snap +++ b/.forge-snapshots/swap with lp fee and protocol fee.snap @@ -1 +1 @@ -180884 \ No newline at end of file +180754 \ No newline at end of file diff --git a/.forge-snapshots/swap with return dynamic fee.snap b/.forge-snapshots/swap with return dynamic fee.snap index f32d505b4..d4b623c94 100644 --- a/.forge-snapshots/swap with return dynamic fee.snap +++ b/.forge-snapshots/swap with return dynamic fee.snap @@ -1 +1 @@ -156513 \ No newline at end of file +156383 \ No newline at end of file diff --git a/.forge-snapshots/update dynamic fee in before swap.snap b/.forge-snapshots/update dynamic fee in before swap.snap index 7453e1ef0..1b5f1f919 100644 --- a/.forge-snapshots/update dynamic fee in before swap.snap +++ b/.forge-snapshots/update dynamic fee in before swap.snap @@ -1 +1 @@ -159121 \ No newline at end of file +158991 \ No newline at end of file diff --git a/src/libraries/Pool.sol b/src/libraries/Pool.sol index c8ed43b3f..19f42cb0a 100644 --- a/src/libraries/Pool.sol +++ b/src/libraries/Pool.sol @@ -372,11 +372,7 @@ library Pool { // compute values to swap to the target tick, price limit, or point where input/output amount is exhausted (state.sqrtPriceX96, step.amountIn, step.amountOut, step.feeAmount) = SwapMath.computeSwapStep( state.sqrtPriceX96, - ( - zeroForOne - ? step.sqrtPriceNextX96 < params.sqrtPriceLimitX96 - : step.sqrtPriceNextX96 > params.sqrtPriceLimitX96 - ) ? params.sqrtPriceLimitX96 : step.sqrtPriceNextX96, + SwapMath.getSqrtPriceTarget(zeroForOne, step.sqrtPriceNextX96, params.sqrtPriceLimitX96), state.liquidity, state.amountSpecifiedRemaining, swapFee diff --git a/src/libraries/SwapMath.sol b/src/libraries/SwapMath.sol index 90214e293..c31c6d7cc 100644 --- a/src/libraries/SwapMath.sol +++ b/src/libraries/SwapMath.sol @@ -7,6 +7,29 @@ import {SqrtPriceMath} from "./SqrtPriceMath.sol"; /// @title Computes the result of a swap within ticks /// @notice Contains methods for computing the result of a swap within a single tick price range, i.e., a single tick. library SwapMath { + /// @notice Computes the sqrt price target for the next swap step + /// @param zeroForOne The direction of the swap, true for currency0 to currency1, false for currency1 to currency0 + /// @param sqrtPriceNextX96 The Q64.96 sqrt price for the next initialized tick + /// @param sqrtPriceLimitX96 The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this value + /// after the swap. If one for zero, the price cannot be greater than this value after the swap + /// @return sqrtPriceTargetX96 The price target for the next swap step + function getSqrtPriceTarget(bool zeroForOne, uint160 sqrtPriceNextX96, uint160 sqrtPriceLimitX96) + internal + pure + returns (uint160 sqrtPriceTargetX96) + { + assembly { + // a flag to toggle between sqrtPriceNextX96 and sqrtPriceLimitX96 + // when zeroForOne == true, nextOrLimit reduces to sqrtPriceNextX96 >= sqrtPriceLimitX96 + // sqrtPriceTargetX96 = max(sqrtPriceNextX96, sqrtPriceLimitX96) + // when zeroForOne == false, nextOrLimit reduces to sqrtPriceNextX96 < sqrtPriceLimitX96 + // sqrtPriceTargetX96 = min(sqrtPriceNextX96, sqrtPriceLimitX96) + let nextOrLimit := xor(lt(sqrtPriceNextX96, sqrtPriceLimitX96), zeroForOne) + let symDiff := xor(sqrtPriceNextX96, sqrtPriceLimitX96) + sqrtPriceTargetX96 := xor(sqrtPriceLimitX96, mul(symDiff, nextOrLimit)) + } + } + /// @notice Computes the result of swapping some amount in, or amount out, given the parameters of the swap /// @dev If the swap's amountSpecified is negative, the combined fee and input amount will never exceed the absolute value of the remaining amount. /// @param sqrtPriceCurrentX96 The current sqrt price of the pool diff --git a/test/libraries/SwapMath.t.sol b/test/libraries/SwapMath.t.sol index 74fed5aa8..043931869 100644 --- a/test/libraries/SwapMath.t.sol +++ b/test/libraries/SwapMath.t.sol @@ -16,6 +16,18 @@ contract SwapMathTest is Test, GasSnapshot { uint160 private constant SQRT_PRICE_1010_100 = 251791039410471229173201122529; uint160 private constant SQRT_PRICE_10000_100 = 792281625142643375935439503360; + function test_fuzz_getSqrtPriceTarget(bool zeroForOne, uint160 sqrtPriceNextX96, uint160 sqrtPriceLimitX96) + public + pure + { + assertEq( + SwapMath.getSqrtPriceTarget(zeroForOne, sqrtPriceNextX96, sqrtPriceLimitX96), + (zeroForOne ? sqrtPriceNextX96 < sqrtPriceLimitX96 : sqrtPriceNextX96 > sqrtPriceLimitX96) + ? sqrtPriceLimitX96 + : sqrtPriceNextX96 + ); + } + function test_computeSwapStep_exactAmountIn_oneForZero_thatGetsCappedAtPriceTargetIn() public pure { uint160 priceTarget = SQRT_PRICE_101_100; uint160 price = SQRT_PRICE_1_1;