diff --git a/.forge-snapshots/SwapMath_oneForZero_exactInCapped.snap b/.forge-snapshots/SwapMath_oneForZero_exactInCapped.snap index 27d48e9a4..c7f4ad925 100644 --- a/.forge-snapshots/SwapMath_oneForZero_exactInCapped.snap +++ b/.forge-snapshots/SwapMath_oneForZero_exactInCapped.snap @@ -1 +1 @@ -1947 \ No newline at end of file +1662 \ No newline at end of file diff --git a/.forge-snapshots/SwapMath_oneForZero_exactInPartial.snap b/.forge-snapshots/SwapMath_oneForZero_exactInPartial.snap index decfddd46..efed77e14 100644 --- a/.forge-snapshots/SwapMath_oneForZero_exactInPartial.snap +++ b/.forge-snapshots/SwapMath_oneForZero_exactInPartial.snap @@ -1 +1 @@ -2390 \ No newline at end of file +2152 \ No newline at end of file diff --git a/.forge-snapshots/SwapMath_oneForZero_exactOutCapped.snap b/.forge-snapshots/SwapMath_oneForZero_exactOutCapped.snap index f2b2a17d5..473aa3ce3 100644 --- a/.forge-snapshots/SwapMath_oneForZero_exactOutCapped.snap +++ b/.forge-snapshots/SwapMath_oneForZero_exactOutCapped.snap @@ -1 +1 @@ -1686 \ No newline at end of file +1413 \ No newline at end of file diff --git a/.forge-snapshots/SwapMath_oneForZero_exactOutPartial.snap b/.forge-snapshots/SwapMath_oneForZero_exactOutPartial.snap index 2a9d55ff3..5ec250ecd 100644 --- a/.forge-snapshots/SwapMath_oneForZero_exactOutPartial.snap +++ b/.forge-snapshots/SwapMath_oneForZero_exactOutPartial.snap @@ -1 +1 @@ -2831 \ No newline at end of file +2120 \ No newline at end of file diff --git a/.forge-snapshots/SwapMath_zeroForOne_exactInCapped.snap b/.forge-snapshots/SwapMath_zeroForOne_exactInCapped.snap index 0f14524db..7dd08159a 100644 --- a/.forge-snapshots/SwapMath_zeroForOne_exactInCapped.snap +++ b/.forge-snapshots/SwapMath_zeroForOne_exactInCapped.snap @@ -1 +1 @@ -2059 \ No newline at end of file +1753 \ No newline at end of file diff --git a/.forge-snapshots/SwapMath_zeroForOne_exactInPartial.snap b/.forge-snapshots/SwapMath_zeroForOne_exactInPartial.snap index c1c401ac5..33c15029b 100644 --- a/.forge-snapshots/SwapMath_zeroForOne_exactInPartial.snap +++ b/.forge-snapshots/SwapMath_zeroForOne_exactInPartial.snap @@ -1 +1 @@ -2941 \ No newline at end of file +2671 \ No newline at end of file diff --git a/.forge-snapshots/SwapMath_zeroForOne_exactOutCapped.snap b/.forge-snapshots/SwapMath_zeroForOne_exactOutCapped.snap index 514744644..8f05b3360 100644 --- a/.forge-snapshots/SwapMath_zeroForOne_exactOutCapped.snap +++ b/.forge-snapshots/SwapMath_zeroForOne_exactOutCapped.snap @@ -1 +1 @@ -1798 \ No newline at end of file +1504 \ No newline at end of file diff --git a/.forge-snapshots/SwapMath_zeroForOne_exactOutPartial.snap b/.forge-snapshots/SwapMath_zeroForOne_exactOutPartial.snap index f82e7a710..d1cd77334 100644 --- a/.forge-snapshots/SwapMath_zeroForOne_exactOutPartial.snap +++ b/.forge-snapshots/SwapMath_zeroForOne_exactOutPartial.snap @@ -1 +1 @@ -2458 \ No newline at end of file +1850 \ No newline at end of file diff --git a/.forge-snapshots/poolManager bytecode size.snap b/.forge-snapshots/poolManager bytecode size.snap index 362c6ba63..e754c52b2 100644 --- a/.forge-snapshots/poolManager bytecode size.snap +++ b/.forge-snapshots/poolManager bytecode size.snap @@ -1 +1 @@ -19986 \ No newline at end of file +19871 \ 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 29dc1b028..8d4eef07d 100644 --- a/.forge-snapshots/simple swap with native.snap +++ b/.forge-snapshots/simple swap with native.snap @@ -1 +1 @@ -114924 \ No newline at end of file +114348 \ No newline at end of file diff --git a/.forge-snapshots/simple swap.snap b/.forge-snapshots/simple swap.snap index fedf0a7d4..5859df2d4 100644 --- a/.forge-snapshots/simple swap.snap +++ b/.forge-snapshots/simple swap.snap @@ -1 +1 @@ -130089 \ No newline at end of file +129513 \ 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 d5c3c2aac..4a163060d 100644 --- a/.forge-snapshots/swap CA fee on unspecified.snap +++ b/.forge-snapshots/swap CA fee on unspecified.snap @@ -1 +1 @@ -179527 \ No newline at end of file +178951 \ 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 0d926133f..5fa0cbc6c 100644 --- a/.forge-snapshots/swap against liquidity with native token.snap +++ b/.forge-snapshots/swap against liquidity with native token.snap @@ -1 +1 @@ -110279 \ No newline at end of file +110009 \ No newline at end of file diff --git a/.forge-snapshots/swap against liquidity.snap b/.forge-snapshots/swap against liquidity.snap index 66df7bb6e..a53d268b1 100644 --- a/.forge-snapshots/swap against liquidity.snap +++ b/.forge-snapshots/swap against liquidity.snap @@ -1 +1 @@ -121626 \ No newline at end of file +121356 \ 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 5c186015d..ad8ca2336 100644 --- a/.forge-snapshots/swap burn 6909 for input.snap +++ b/.forge-snapshots/swap burn 6909 for input.snap @@ -1 +1 @@ -133455 \ No newline at end of file +132744 \ 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 5e0d92bd7..5a7dec7c4 100644 --- a/.forge-snapshots/swap burn native 6909 for input.snap +++ b/.forge-snapshots/swap burn native 6909 for input.snap @@ -1 +1 @@ -122743 \ No newline at end of file +122135 \ 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 4de22f8fb..c96b18e6e 100644 --- a/.forge-snapshots/swap mint native output as 6909.snap +++ b/.forge-snapshots/swap mint native output as 6909.snap @@ -1 +1 @@ -144531 \ No newline at end of file +144293 \ 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 a55cd0c6d..78b454398 100644 --- a/.forge-snapshots/swap mint output as 6909.snap +++ b/.forge-snapshots/swap mint output as 6909.snap @@ -1 +1 @@ -161161 \ No newline at end of file +160585 \ 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 0b2045cfd..09e03592e 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 @@ -217571 \ No newline at end of file +216725 \ 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 b53d55449..f8a9ca01a 100644 --- a/.forge-snapshots/swap with dynamic fee.snap +++ b/.forge-snapshots/swap with dynamic fee.snap @@ -1 +1 @@ -145362 \ No newline at end of file +144786 \ No newline at end of file diff --git a/.forge-snapshots/swap with hooks.snap b/.forge-snapshots/swap with hooks.snap index 39fb8c6ae..46ec020d9 100644 --- a/.forge-snapshots/swap with hooks.snap +++ b/.forge-snapshots/swap with hooks.snap @@ -1 +1 @@ -138626 \ No newline at end of file +138356 \ 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 1e47bf617..ec1be6f5e 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 @@ -176624 \ No newline at end of file +175722 \ 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 e2323da0c..3dbffcf73 100644 --- a/.forge-snapshots/swap with return dynamic fee.snap +++ b/.forge-snapshots/swap with return dynamic fee.snap @@ -1 +1 @@ -152398 \ No newline at end of file +151822 \ 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 ac7801add..99c635122 100644 --- a/.forge-snapshots/update dynamic fee in before swap.snap +++ b/.forge-snapshots/update dynamic fee in before swap.snap @@ -1 +1 @@ -155040 \ No newline at end of file +154464 \ No newline at end of file diff --git a/src/libraries/SwapMath.sol b/src/libraries/SwapMath.sol index c31c6d7cc..264a02e07 100644 --- a/src/libraries/SwapMath.sol +++ b/src/libraries/SwapMath.sol @@ -7,6 +7,8 @@ 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 { + uint256 internal constant MAX_FEE_PIPS = 1e6; + /// @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 @@ -49,63 +51,53 @@ library SwapMath { uint24 feePips ) internal pure returns (uint160 sqrtPriceNextX96, uint256 amountIn, uint256 amountOut, uint256 feeAmount) { unchecked { + uint256 _feePips = feePips; // upcast once and cache bool zeroForOne = sqrtPriceCurrentX96 >= sqrtPriceTargetX96; bool exactIn = amountRemaining < 0; if (exactIn) { - uint256 amountRemainingLessFee = FullMath.mulDiv(uint256(-amountRemaining), 1e6 - feePips, 1e6); + uint256 amountRemainingLessFee = + FullMath.mulDiv(uint256(-amountRemaining), MAX_FEE_PIPS - _feePips, MAX_FEE_PIPS); amountIn = zeroForOne ? SqrtPriceMath.getAmount0Delta(sqrtPriceTargetX96, sqrtPriceCurrentX96, liquidity, true) : SqrtPriceMath.getAmount1Delta(sqrtPriceCurrentX96, sqrtPriceTargetX96, liquidity, true); if (amountRemainingLessFee >= amountIn) { + // `amountIn` is capped by the target price sqrtPriceNextX96 = sqrtPriceTargetX96; + feeAmount = _feePips == MAX_FEE_PIPS + ? amountIn + : FullMath.mulDivRoundingUp(amountIn, _feePips, MAX_FEE_PIPS - _feePips); } else { sqrtPriceNextX96 = SqrtPriceMath.getNextSqrtPriceFromInput( sqrtPriceCurrentX96, liquidity, amountRemainingLessFee, zeroForOne ); + amountIn = zeroForOne + ? SqrtPriceMath.getAmount0Delta(sqrtPriceNextX96, sqrtPriceCurrentX96, liquidity, true) + : SqrtPriceMath.getAmount1Delta(sqrtPriceCurrentX96, sqrtPriceNextX96, liquidity, true); + // we didn't reach the target, so take the remainder of the maximum input as fee + feeAmount = uint256(-amountRemaining) - amountIn; } + amountOut = zeroForOne + ? SqrtPriceMath.getAmount1Delta(sqrtPriceNextX96, sqrtPriceCurrentX96, liquidity, false) + : SqrtPriceMath.getAmount0Delta(sqrtPriceCurrentX96, sqrtPriceNextX96, liquidity, false); } else { amountOut = zeroForOne ? SqrtPriceMath.getAmount1Delta(sqrtPriceTargetX96, sqrtPriceCurrentX96, liquidity, false) : SqrtPriceMath.getAmount0Delta(sqrtPriceCurrentX96, sqrtPriceTargetX96, liquidity, false); if (uint256(amountRemaining) >= amountOut) { + // `amountOut` is capped by the target price sqrtPriceNextX96 = sqrtPriceTargetX96; } else { - sqrtPriceNextX96 = SqrtPriceMath.getNextSqrtPriceFromOutput( - sqrtPriceCurrentX96, liquidity, uint256(amountRemaining), zeroForOne - ); + // cap the output amount to not exceed the remaining output amount + amountOut = uint256(amountRemaining); + sqrtPriceNextX96 = + SqrtPriceMath.getNextSqrtPriceFromOutput(sqrtPriceCurrentX96, liquidity, amountOut, zeroForOne); } - } - - bool max = sqrtPriceTargetX96 == sqrtPriceNextX96; - - // get the input/output amounts - if (zeroForOne) { - amountIn = max && exactIn - ? amountIn - : SqrtPriceMath.getAmount0Delta(sqrtPriceNextX96, sqrtPriceCurrentX96, liquidity, true); - amountOut = max && !exactIn - ? amountOut - : SqrtPriceMath.getAmount1Delta(sqrtPriceNextX96, sqrtPriceCurrentX96, liquidity, false); - } else { - amountIn = max && exactIn - ? amountIn + amountIn = zeroForOne + ? SqrtPriceMath.getAmount0Delta(sqrtPriceNextX96, sqrtPriceCurrentX96, liquidity, true) : SqrtPriceMath.getAmount1Delta(sqrtPriceCurrentX96, sqrtPriceNextX96, liquidity, true); - amountOut = max && !exactIn - ? amountOut - : SqrtPriceMath.getAmount0Delta(sqrtPriceCurrentX96, sqrtPriceNextX96, liquidity, false); - } - - // cap the output amount to not exceed the remaining output amount - if (!exactIn && amountOut > uint256(amountRemaining)) { - amountOut = uint256(amountRemaining); - } - - if (exactIn && sqrtPriceNextX96 != sqrtPriceTargetX96) { - // we didn't reach the target, so take the remainder of the maximum input as fee - feeAmount = uint256(-amountRemaining) - amountIn; - } else { - feeAmount = feePips == 1e6 ? amountIn : FullMath.mulDivRoundingUp(amountIn, feePips, 1e6 - feePips); + // `feePips` cannot be `MAX_FEE_PIPS` for exact out + feeAmount = FullMath.mulDivRoundingUp(amountIn, _feePips, MAX_FEE_PIPS - _feePips); } } }