From a2c4b880d1c3969689ee4ad0b83e37ec98380ce8 Mon Sep 17 00:00:00 2001 From: Shuhui Luo <107524008+shuhuiluo@users.noreply.github.com> Date: Sun, 19 May 2024 06:00:38 -0400 Subject: [PATCH] Optimize signed variants of `getAmount0Delta` and `getAmount1Delta` The code in `SqrtPriceMath` has been refactored to optimize the functions `getAmount0Delta` and `getAmount1Delta`. The changes replaced previous return statements with inline assembly to handle operations on `int256` more efficiently. This has resulted in reduction in bytecode size and runtime gas. --- ...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 +- .../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 +- src/libraries/SqrtPriceMath.sol | 56 ++++++++++++++++--- 14 files changed, 61 insertions(+), 21 deletions(-) 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..1434cfdf8 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 +153569 \ No newline at end of file diff --git a/.forge-snapshots/addLiquidity CA fee.snap b/.forge-snapshots/addLiquidity CA fee.snap index b4678d19b..ab6c6cbb0 100644 --- a/.forge-snapshots/addLiquidity CA fee.snap +++ b/.forge-snapshots/addLiquidity CA fee.snap @@ -1 +1 @@ -331309 \ No newline at end of file +331235 \ 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..1bdb8b929 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 +286228 \ 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..4201eadcf 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 +143779 \ 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..eb1755871 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 +301747 \ No newline at end of file diff --git a/.forge-snapshots/poolManager bytecode size.snap b/.forge-snapshots/poolManager bytecode size.snap index 81ca44cf1..ac47b22a0 100644 --- a/.forge-snapshots/poolManager bytecode size.snap +++ b/.forge-snapshots/poolManager bytecode size.snap @@ -1 +1 @@ -21709 \ No newline at end of file +21684 \ No newline at end of file diff --git a/.forge-snapshots/removeLiquidity CA fee.snap b/.forge-snapshots/removeLiquidity CA fee.snap index 082d59d10..80367b2f3 100644 --- a/.forge-snapshots/removeLiquidity CA fee.snap +++ b/.forge-snapshots/removeLiquidity CA fee.snap @@ -1 +1 @@ -186775 \ No newline at end of file +186725 \ 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..003ba99af 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 +123093 \ 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..1d2f70439 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 +119880 \ 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..14be4dac5 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 +104895 \ No newline at end of file diff --git a/.forge-snapshots/simple addLiquidity.snap b/.forge-snapshots/simple addLiquidity.snap index 8f3accdaa..0d4d720e6 100644 --- a/.forge-snapshots/simple addLiquidity.snap +++ b/.forge-snapshots/simple addLiquidity.snap @@ -1 +1 @@ -167424 \ No newline at end of file +167387 \ 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..064c0fdab 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 +98504 \ No newline at end of file diff --git a/.forge-snapshots/simple removeLiquidity.snap b/.forge-snapshots/simple removeLiquidity.snap index 0eb1ac8de..2ed62b07f 100644 --- a/.forge-snapshots/simple removeLiquidity.snap +++ b/.forge-snapshots/simple removeLiquidity.snap @@ -1 +1 @@ -90569 \ No newline at end of file +90544 \ No newline at end of file diff --git a/src/libraries/SqrtPriceMath.sol b/src/libraries/SqrtPriceMath.sol index 71262ffa2..7d71c52d2 100644 --- a/src/libraries/SqrtPriceMath.sol +++ b/src/libraries/SqrtPriceMath.sol @@ -198,10 +198,30 @@ library SqrtPriceMath { pure returns (int256 amount0) { - unchecked { - return liquidity < 0 - ? getAmount0Delta(sqrtPriceAX96, sqrtPriceBX96, uint128(-liquidity), false).toInt256() - : -getAmount0Delta(sqrtPriceAX96, sqrtPriceBX96, uint128(liquidity), true).toInt256(); + /** + * Equivalent to: + * amount0 = liquidity < 0 + * ? getAmount0Delta(sqrtPriceAX96, sqrtPriceBX96, uint128(-liquidity), false).toInt256() + * : -getAmount0Delta(sqrtPriceAX96, sqrtPriceBX96, uint128(liquidity), true).toInt256(); + */ + bool roundUp; + uint128 liquidityAbs; + assembly { + // mask = 0 if liquidity >= 0 else -1 + let mask := sar(255, liquidity) + // roundUp = 1 if liquidity >= 0 else 0 + roundUp := iszero(mask) + liquidityAbs := xor(mask, add(mask, liquidity)) + } + // amount0Abs = liquidity / sqrt(lower) - liquidity / sqrt(upper) < type(uint224).max + // always fits in 224 bits, no need for toInt256() + uint256 amount0Abs = getAmount0Delta(sqrtPriceAX96, sqrtPriceBX96, liquidityAbs, roundUp); + assembly { + // mask = -1 if liquidity >= 0 else 0 + let mask := sub(0, roundUp) + // If liquidity < 0, amount0 = |amount0| = 0 ^ |amount0| + // If liquidity >= 0, amount0 = -|amount0| = ~|amount0| + 1 = (-1) ^ |amount0| - (-1) + amount0 := sub(xor(amount0Abs, mask), mask) } } @@ -215,10 +235,30 @@ library SqrtPriceMath { pure returns (int256 amount1) { - unchecked { - return liquidity < 0 - ? getAmount1Delta(sqrtPriceAX96, sqrtPriceBX96, uint128(-liquidity), false).toInt256() - : -getAmount1Delta(sqrtPriceAX96, sqrtPriceBX96, uint128(liquidity), true).toInt256(); + /** + * Equivalent to: + * amount1 = liquidity < 0 + * ? getAmount1Delta(sqrtPriceAX96, sqrtPriceBX96, uint128(-liquidity), false).toInt256() + * : -getAmount1Delta(sqrtPriceAX96, sqrtPriceBX96, uint128(liquidity), true).toInt256(); + */ + bool roundUp; + uint128 liquidityAbs; + assembly { + // mask = 0 if liquidity >= 0 else -1 + let mask := sar(255, liquidity) + // roundUp = 1 if liquidity >= 0 else 0 + roundUp := iszero(mask) + liquidityAbs := xor(mask, add(mask, liquidity)) + } + // amount1Abs = liquidity * (sqrt(upper) - sqrt(lower)) < type(uint192).max + // always fits in 192 bits, no need for toInt256() + uint256 amount1Abs = getAmount1Delta(sqrtPriceAX96, sqrtPriceBX96, liquidityAbs, roundUp); + assembly { + // mask = -1 if liquidity >= 0 else 0 + let mask := sub(0, roundUp) + // If liquidity < 0, amount1 = |amount1| = 0 ^ |amount1| + // If liquidity >= 0, amount1 = -|amount1| = ~|amount1| + 1 = (-1) ^ |amount1| - (-1) + amount1 := sub(xor(amount1Abs, mask), mask) } } }