From 8e3fc6d015118e0014089ec217fd15ef951976b3 Mon Sep 17 00:00:00 2001 From: Shuhui Luo <107524008+shuhuiluo@users.noreply.github.com> Date: Sun, 19 May 2024 05:21:56 -0400 Subject: [PATCH 1/2] Optimize `SqrtPriceMath.getAmount1Delta` Added a new function `absDiff` to `SqrtPriceMath` library which calculates the absolute difference between two numbers. Refactored the way price delta is computed between two prices in the `getAmount1Delta` function. The calculation is now more efficient. --- .../SwapMath_oneForZero_exactInCapped.snap | 2 +- .../SwapMath_oneForZero_exactInPartial.snap | 2 +- .../SwapMath_oneForZero_exactOutCapped.snap | 2 +- .../SwapMath_oneForZero_exactOutPartial.snap | 2 +- .../SwapMath_zeroForOne_exactInCapped.snap | 2 +- .../SwapMath_zeroForOne_exactInPartial.snap | 2 +- .../SwapMath_zeroForOne_exactOutCapped.snap | 2 +- .../SwapMath_zeroForOne_exactOutPartial.snap | 2 +- ...o already existing position with salt.snap | 2 +- .forge-snapshots/addLiquidity CA fee.snap | 2 +- .../addLiquidity with empty hook.snap | 2 +- .../addLiquidity with native token.snap | 2 +- ...new liquidity to a position with salt.snap | 2 +- ..._gasCostForAmount1WhereRoundUpIsFalse.snap | 2 +- ...a_gasCostForAmount1WhereRoundUpIsTrue.snap | 2 +- .../poolManager bytecode size.snap | 2 +- .forge-snapshots/removeLiquidity CA fee.snap | 2 +- .../removeLiquidity with empty hook.snap | 2 +- .../removeLiquidity with native token.snap | 2 +- ...dLiquidity second addition same range.snap | 2 +- .forge-snapshots/simple addLiquidity.snap | 2 +- ...emoveLiquidity some liquidity remains.snap | 2 +- .forge-snapshots/simple removeLiquidity.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/SqrtPriceMath.sol | 32 ++++++++++++++++--- 39 files changed, 65 insertions(+), 43 deletions(-) diff --git a/.forge-snapshots/SwapMath_oneForZero_exactInCapped.snap b/.forge-snapshots/SwapMath_oneForZero_exactInCapped.snap index 19261c267..062bc1c2d 100644 --- a/.forge-snapshots/SwapMath_oneForZero_exactInCapped.snap +++ b/.forge-snapshots/SwapMath_oneForZero_exactInCapped.snap @@ -1 +1 @@ -2248 \ No newline at end of file +1974 \ No newline at end of file diff --git a/.forge-snapshots/SwapMath_oneForZero_exactInPartial.snap b/.forge-snapshots/SwapMath_oneForZero_exactInPartial.snap index 43e6c3379..cebceba23 100644 --- a/.forge-snapshots/SwapMath_oneForZero_exactInPartial.snap +++ b/.forge-snapshots/SwapMath_oneForZero_exactInPartial.snap @@ -1 +1 @@ -3016 \ No newline at end of file +2468 \ No newline at end of file diff --git a/.forge-snapshots/SwapMath_oneForZero_exactOutCapped.snap b/.forge-snapshots/SwapMath_oneForZero_exactOutCapped.snap index 01a646754..d7bda98e9 100644 --- a/.forge-snapshots/SwapMath_oneForZero_exactOutCapped.snap +++ b/.forge-snapshots/SwapMath_oneForZero_exactOutCapped.snap @@ -1 +1 @@ -1987 \ No newline at end of file +1713 \ No newline at end of file diff --git a/.forge-snapshots/SwapMath_oneForZero_exactOutPartial.snap b/.forge-snapshots/SwapMath_oneForZero_exactOutPartial.snap index 72cd61fe0..ea9412b02 100644 --- a/.forge-snapshots/SwapMath_oneForZero_exactOutPartial.snap +++ b/.forge-snapshots/SwapMath_oneForZero_exactOutPartial.snap @@ -1 +1 @@ -3256 \ No newline at end of file +2982 \ No newline at end of file diff --git a/.forge-snapshots/SwapMath_zeroForOne_exactInCapped.snap b/.forge-snapshots/SwapMath_zeroForOne_exactInCapped.snap index b038f6888..ab0cf33c5 100644 --- a/.forge-snapshots/SwapMath_zeroForOne_exactInCapped.snap +++ b/.forge-snapshots/SwapMath_zeroForOne_exactInCapped.snap @@ -1 +1 @@ -2238 \ No newline at end of file +2091 \ No newline at end of file diff --git a/.forge-snapshots/SwapMath_zeroForOne_exactInPartial.snap b/.forge-snapshots/SwapMath_zeroForOne_exactInPartial.snap index 3832db4f7..849de8bc1 100644 --- a/.forge-snapshots/SwapMath_zeroForOne_exactInPartial.snap +++ b/.forge-snapshots/SwapMath_zeroForOne_exactInPartial.snap @@ -1 +1 @@ -3191 \ No newline at end of file +3044 \ No newline at end of file diff --git a/.forge-snapshots/SwapMath_zeroForOne_exactOutCapped.snap b/.forge-snapshots/SwapMath_zeroForOne_exactOutCapped.snap index ca9836afa..ec2b293ea 100644 --- a/.forge-snapshots/SwapMath_zeroForOne_exactOutCapped.snap +++ b/.forge-snapshots/SwapMath_zeroForOne_exactOutCapped.snap @@ -1 +1 @@ -1977 \ No newline at end of file +1830 \ No newline at end of file diff --git a/.forge-snapshots/SwapMath_zeroForOne_exactOutPartial.snap b/.forge-snapshots/SwapMath_zeroForOne_exactOutPartial.snap index d872354f5..bae32ca68 100644 --- a/.forge-snapshots/SwapMath_zeroForOne_exactOutPartial.snap +++ b/.forge-snapshots/SwapMath_zeroForOne_exactOutPartial.snap @@ -1 +1 @@ -2866 \ No newline at end of file +2572 \ No newline at end of file diff --git a/.forge-snapshots/add liquidity to already existing position with salt.snap b/.forge-snapshots/add liquidity to already existing position with salt.snap index ff76e7f86..92c3e5a1d 100644 --- a/.forge-snapshots/add liquidity to already existing position with salt.snap +++ b/.forge-snapshots/add liquidity to already existing position with salt.snap @@ -1 +1 @@ -153643 \ No newline at end of file +153373 \ No newline at end of file diff --git a/.forge-snapshots/addLiquidity CA fee.snap b/.forge-snapshots/addLiquidity CA fee.snap index b4678d19b..2be7e6f13 100644 --- a/.forge-snapshots/addLiquidity CA fee.snap +++ b/.forge-snapshots/addLiquidity CA fee.snap @@ -1 +1 @@ -331309 \ No newline at end of file +331039 \ No newline at end of file diff --git a/.forge-snapshots/addLiquidity with empty hook.snap b/.forge-snapshots/addLiquidity with empty hook.snap index c59df0298..7085565a9 100644 --- a/.forge-snapshots/addLiquidity with empty hook.snap +++ b/.forge-snapshots/addLiquidity with empty hook.snap @@ -1 +1 @@ -286302 \ No newline at end of file +286032 \ No newline at end of file diff --git a/.forge-snapshots/addLiquidity with native token.snap b/.forge-snapshots/addLiquidity with native token.snap index c5bed833e..dff5c01a7 100644 --- a/.forge-snapshots/addLiquidity with native token.snap +++ b/.forge-snapshots/addLiquidity with native token.snap @@ -1 +1 @@ -143853 \ No newline at end of file +143583 \ No newline at end of file diff --git a/.forge-snapshots/create new liquidity to a position with salt.snap b/.forge-snapshots/create new liquidity to a position with salt.snap index cbb3de708..b8d09cd0e 100644 --- a/.forge-snapshots/create new liquidity to a position with salt.snap +++ b/.forge-snapshots/create new liquidity to a position with salt.snap @@ -1 +1 @@ -301821 \ No newline at end of file +301551 \ No newline at end of file diff --git a/.forge-snapshots/getAmount1Delta_gasCostForAmount1WhereRoundUpIsFalse.snap b/.forge-snapshots/getAmount1Delta_gasCostForAmount1WhereRoundUpIsFalse.snap index 1fc188de7..8c19d14d4 100644 --- a/.forge-snapshots/getAmount1Delta_gasCostForAmount1WhereRoundUpIsFalse.snap +++ b/.forge-snapshots/getAmount1Delta_gasCostForAmount1WhereRoundUpIsFalse.snap @@ -1 +1 @@ -525 \ No newline at end of file +369 \ No newline at end of file diff --git a/.forge-snapshots/getAmount1Delta_gasCostForAmount1WhereRoundUpIsTrue.snap b/.forge-snapshots/getAmount1Delta_gasCostForAmount1WhereRoundUpIsTrue.snap index 929530e88..6c8526a81 100644 --- a/.forge-snapshots/getAmount1Delta_gasCostForAmount1WhereRoundUpIsTrue.snap +++ b/.forge-snapshots/getAmount1Delta_gasCostForAmount1WhereRoundUpIsTrue.snap @@ -1 +1 @@ -653 \ No newline at end of file +370 \ No newline at end of file diff --git a/.forge-snapshots/poolManager bytecode size.snap b/.forge-snapshots/poolManager bytecode size.snap index 81ca44cf1..68278427c 100644 --- a/.forge-snapshots/poolManager bytecode size.snap +++ b/.forge-snapshots/poolManager bytecode size.snap @@ -1 +1 @@ -21709 \ No newline at end of file +21612 \ No newline at end of file diff --git a/.forge-snapshots/removeLiquidity CA fee.snap b/.forge-snapshots/removeLiquidity CA fee.snap index 082d59d10..fd74db276 100644 --- a/.forge-snapshots/removeLiquidity CA fee.snap +++ b/.forge-snapshots/removeLiquidity CA fee.snap @@ -1 +1 @@ -186775 \ No newline at end of file +186632 \ No newline at end of file diff --git a/.forge-snapshots/removeLiquidity with empty hook.snap b/.forge-snapshots/removeLiquidity with empty hook.snap index e7fddfb22..48c05fb36 100644 --- a/.forge-snapshots/removeLiquidity with empty hook.snap +++ b/.forge-snapshots/removeLiquidity with empty hook.snap @@ -1 +1 @@ -123143 \ No newline at end of file +123000 \ No newline at end of file diff --git a/.forge-snapshots/removeLiquidity with native token.snap b/.forge-snapshots/removeLiquidity with native token.snap index 8b983838d..59885c17c 100644 --- a/.forge-snapshots/removeLiquidity with native token.snap +++ b/.forge-snapshots/removeLiquidity with native token.snap @@ -1 +1 @@ -119930 \ No newline at end of file +119787 \ No newline at end of file diff --git a/.forge-snapshots/simple addLiquidity second addition same range.snap b/.forge-snapshots/simple addLiquidity second addition same range.snap index ccf9f3450..9cd7a727d 100644 --- a/.forge-snapshots/simple addLiquidity second addition same range.snap +++ b/.forge-snapshots/simple addLiquidity second addition same range.snap @@ -1 +1 @@ -104932 \ No newline at end of file +104662 \ No newline at end of file diff --git a/.forge-snapshots/simple addLiquidity.snap b/.forge-snapshots/simple addLiquidity.snap index 8f3accdaa..42dfbd0db 100644 --- a/.forge-snapshots/simple addLiquidity.snap +++ b/.forge-snapshots/simple addLiquidity.snap @@ -1 +1 @@ -167424 \ No newline at end of file +167154 \ No newline at end of file diff --git a/.forge-snapshots/simple removeLiquidity some liquidity remains.snap b/.forge-snapshots/simple removeLiquidity some liquidity remains.snap index fc72404ca..12718e6db 100644 --- a/.forge-snapshots/simple removeLiquidity some liquidity remains.snap +++ b/.forge-snapshots/simple removeLiquidity some liquidity remains.snap @@ -1 +1 @@ -98529 \ No newline at end of file +98386 \ No newline at end of file diff --git a/.forge-snapshots/simple removeLiquidity.snap b/.forge-snapshots/simple removeLiquidity.snap index 0eb1ac8de..73549beea 100644 --- a/.forge-snapshots/simple removeLiquidity.snap +++ b/.forge-snapshots/simple removeLiquidity.snap @@ -1 +1 @@ -90569 \ No newline at end of file +90426 \ 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 4fc9f9feb..2dac06bf8 100644 --- a/.forge-snapshots/simple swap with native.snap +++ b/.forge-snapshots/simple swap with native.snap @@ -1 +1 @@ -116827 \ No newline at end of file +116541 \ No newline at end of file diff --git a/.forge-snapshots/simple swap.snap b/.forge-snapshots/simple swap.snap index 7a20c6b3d..bbc66c518 100644 --- a/.forge-snapshots/simple swap.snap +++ b/.forge-snapshots/simple swap.snap @@ -1 +1 @@ -131988 \ No newline at end of file +131702 \ 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 27c7f51ac..d493f7f2c 100644 --- a/.forge-snapshots/swap CA fee on unspecified.snap +++ b/.forge-snapshots/swap CA fee on unspecified.snap @@ -1 +1 @@ -183126 \ No newline at end of file +182840 \ 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 8550686dc..223db8b25 100644 --- a/.forge-snapshots/swap against liquidity with native token.snap +++ b/.forge-snapshots/swap against liquidity with native token.snap @@ -1 +1 @@ -112548 \ No newline at end of file +112405 \ No newline at end of file diff --git a/.forge-snapshots/swap against liquidity.snap b/.forge-snapshots/swap against liquidity.snap index 14ee97dfc..eefc75bf6 100644 --- a/.forge-snapshots/swap against liquidity.snap +++ b/.forge-snapshots/swap against liquidity.snap @@ -1 +1 @@ -123891 \ No newline at end of file +123748 \ 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 b19efd36d..6f9f02a9d 100644 --- a/.forge-snapshots/swap burn 6909 for input.snap +++ b/.forge-snapshots/swap burn 6909 for input.snap @@ -1 +1 @@ -135923 \ No newline at end of file +135653 \ 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 dc06b6c6a..1643c26df 100644 --- a/.forge-snapshots/swap burn native 6909 for input.snap +++ b/.forge-snapshots/swap burn native 6909 for input.snap @@ -1 +1 @@ -125140 \ No newline at end of file +124854 \ 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 fa5caa1ba..5f58a98f4 100644 --- a/.forge-snapshots/swap mint native output as 6909.snap +++ b/.forge-snapshots/swap mint native output as 6909.snap @@ -1 +1 @@ -147180 \ No newline at end of file +146640 \ 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 1acf3c68f..c3e9bcf14 100644 --- a/.forge-snapshots/swap mint output as 6909.snap +++ b/.forge-snapshots/swap mint output as 6909.snap @@ -1 +1 @@ -163842 \ No newline at end of file +163556 \ 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 b2d6ca0cd..fd8c1ed24 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 @@ -222199 \ No newline at end of file +221770 \ 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 96c9f4d55..7c274c01f 100644 --- a/.forge-snapshots/swap with dynamic fee.snap +++ b/.forge-snapshots/swap with dynamic fee.snap @@ -1 +1 @@ -148043 \ No newline at end of file +147757 \ No newline at end of file diff --git a/.forge-snapshots/swap with hooks.snap b/.forge-snapshots/swap with hooks.snap index 897a376b0..7b9207469 100644 --- a/.forge-snapshots/swap with hooks.snap +++ b/.forge-snapshots/swap with hooks.snap @@ -1 +1 @@ -123903 \ No newline at end of file +123760 \ 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 9efd3b6c3..724f13fa4 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 @@ -180223 \ No newline at end of file +179794 \ 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 cb2abe163..f6db374f2 100644 --- a/.forge-snapshots/swap with return dynamic fee.snap +++ b/.forge-snapshots/swap with return dynamic fee.snap @@ -1 +1 @@ -155902 \ No newline at end of file +155616 \ 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 265212952..962367321 100644 --- a/.forge-snapshots/update dynamic fee in before swap.snap +++ b/.forge-snapshots/update dynamic fee in before swap.snap @@ -1 +1 @@ -158505 \ No newline at end of file +158219 \ No newline at end of file diff --git a/src/libraries/SqrtPriceMath.sol b/src/libraries/SqrtPriceMath.sol index 71262ffa2..e8caf5adc 100644 --- a/src/libraries/SqrtPriceMath.sol +++ b/src/libraries/SqrtPriceMath.sol @@ -169,6 +169,15 @@ library SqrtPriceMath { } } + /// @notice Equivalent to: `a > b ? a - b : b - a` + function absDiff(uint160 a, uint160 b) internal pure returns (uint256 res) { + assembly { + let diff := sub(a, b) + let mask := sar(255, diff) + res := xor(mask, add(mask, diff)) + } + } + /// @notice Gets the amount1 delta between two prices /// @dev Calculates liquidity * (sqrt(upper) - sqrt(lower)) /// @param sqrtPriceAX96 A sqrt price @@ -181,11 +190,24 @@ library SqrtPriceMath { pure returns (uint256 amount1) { - if (sqrtPriceAX96 > sqrtPriceBX96) (sqrtPriceAX96, sqrtPriceBX96) = (sqrtPriceBX96, sqrtPriceAX96); - - return roundUp - ? FullMath.mulDivRoundingUp(liquidity, sqrtPriceBX96 - sqrtPriceAX96, FixedPoint96.Q96) - : FullMath.mulDiv(liquidity, sqrtPriceBX96 - sqrtPriceAX96, FixedPoint96.Q96); + uint256 numerator = absDiff(sqrtPriceAX96, sqrtPriceBX96); + uint256 denominator = FixedPoint96.Q96; + uint256 _liquidity; + assembly { + // avoid implicit upcasting + _liquidity := liquidity + } + /** + * Equivalent to: + * amount1 = roundUp + * ? FullMath.mulDivRoundingUp(liquidity, sqrtPriceBX96 - sqrtPriceAX96, FixedPoint96.Q96) + * : FullMath.mulDiv(liquidity, sqrtPriceBX96 - sqrtPriceAX96, FixedPoint96.Q96); + * Cannot overflow because `type(uint128).max * type(uint160).max >> 96 < (1 << 192)`. + */ + amount1 = FullMath.mulDiv(_liquidity, numerator, denominator); + assembly { + amount1 := add(amount1, and(gt(mulmod(_liquidity, numerator, denominator), 0), roundUp)) + } } /// @notice Helper that gets signed currency0 delta From 192b29497fadaa1455f0a94636aac08e1790795f Mon Sep 17 00:00:00 2001 From: Shuhui Luo <107524008+shuhuiluo@users.noreply.github.com> Date: Tue, 21 May 2024 18:54:41 -0400 Subject: [PATCH 2/2] Update comments on use of `xor` --- src/libraries/SqrtPriceMath.sol | 6 +++++- src/libraries/TickMath.sol | 8 ++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/libraries/SqrtPriceMath.sol b/src/libraries/SqrtPriceMath.sol index e8caf5adc..2b463be08 100644 --- a/src/libraries/SqrtPriceMath.sol +++ b/src/libraries/SqrtPriceMath.sol @@ -169,11 +169,15 @@ library SqrtPriceMath { } } - /// @notice Equivalent to: `a > b ? a - b : b - a` + /// @notice Equivalent to: `a >= b ? a - b : b - a` function absDiff(uint160 a, uint160 b) internal pure returns (uint256 res) { assembly { let diff := sub(a, b) + // mask = 0 if a >= b else -1 (all 1s) let mask := sar(255, diff) + // if a >= b, res = a - b = 0 ^ (a - b) + // if a < b, res = b - a = ~~(b - a) = ~(-(b - a) - 1) = ~(a - b - 1) = (-1) ^ (a - b - 1) + // either way, res = mask ^ (a - b + mask) res := xor(mask, add(mask, diff)) } } diff --git a/src/libraries/TickMath.sol b/src/libraries/TickMath.sol index a0c0f2180..3b0c73e74 100644 --- a/src/libraries/TickMath.sol +++ b/src/libraries/TickMath.sol @@ -51,11 +51,11 @@ library TickMath { unchecked { uint256 absTick; assembly { - // mask = 0 if tick >= 0 else -1 + // mask = 0 if tick >= 0 else -1 (all 1s) let mask := sar(255, tick) - // If tick >= 0, |tick| = tick = 0 ^ tick - // If tick < 0, |tick| = ~~|tick| = ~(-|tick| - 1) = ~(tick - 1) = (-1) ^ (tick - 1) - // Either case, |tick| = mask ^ (tick + mask) + // if tick >= 0, |tick| = tick = 0 ^ tick + // if tick < 0, |tick| = ~~|tick| = ~(-|tick| - 1) = ~(tick - 1) = (-1) ^ (tick - 1) + // either way, |tick| = mask ^ (tick + mask) absTick := xor(mask, add(mask, tick)) } // Equivalent: if (absTick > MAX_TICK) revert InvalidTick();