diff --git a/.forge-snapshots/TickMathGetTickAtSqrtPrice.snap b/.forge-snapshots/TickMathGetTickAtSqrtPrice.snap index 611c1ba05..0cd367bec 100644 --- a/.forge-snapshots/TickMathGetTickAtSqrtPrice.snap +++ b/.forge-snapshots/TickMathGetTickAtSqrtPrice.snap @@ -1 +1 @@ -224621 \ No newline at end of file +218521 \ 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 70cb20284..d97c907fc 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 @@ -153427 \ No newline at end of file +153346 \ No newline at end of file diff --git a/.forge-snapshots/addLiquidity CA fee.snap b/.forge-snapshots/addLiquidity CA fee.snap index 927bf0ecc..219e244ce 100644 --- a/.forge-snapshots/addLiquidity CA fee.snap +++ b/.forge-snapshots/addLiquidity CA fee.snap @@ -1 +1 @@ -331093 \ No newline at end of file +331012 \ 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 75c65f10a..89098aba9 100644 --- a/.forge-snapshots/addLiquidity with empty hook.snap +++ b/.forge-snapshots/addLiquidity with empty hook.snap @@ -1 +1 @@ -286086 \ No newline at end of file +286005 \ 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 25ba858d4..e88e3cc8a 100644 --- a/.forge-snapshots/addLiquidity with native token.snap +++ b/.forge-snapshots/addLiquidity with native token.snap @@ -1 +1 @@ -143637 \ No newline at end of file +143556 \ 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 2f53eb249..717681e90 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 @@ -301605 \ No newline at end of file +301524 \ No newline at end of file diff --git a/.forge-snapshots/initialize.snap b/.forge-snapshots/initialize.snap index 32826ad3b..19305f802 100644 --- a/.forge-snapshots/initialize.snap +++ b/.forge-snapshots/initialize.snap @@ -1 +1 @@ -61775 \ No newline at end of file +61714 \ No newline at end of file diff --git a/.forge-snapshots/poolManager bytecode size.snap b/.forge-snapshots/poolManager bytecode size.snap index f12f6cdd0..ee3007261 100644 --- a/.forge-snapshots/poolManager bytecode size.snap +++ b/.forge-snapshots/poolManager bytecode size.snap @@ -1 +1 @@ -21575 \ No newline at end of file +21463 \ No newline at end of file diff --git a/.forge-snapshots/removeLiquidity CA fee.snap b/.forge-snapshots/removeLiquidity CA fee.snap index 29e8612d9..0b16ab028 100644 --- a/.forge-snapshots/removeLiquidity CA fee.snap +++ b/.forge-snapshots/removeLiquidity CA fee.snap @@ -1 +1 @@ -186559 \ No newline at end of file +186473 \ 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 ee41dab80..58c5e06e6 100644 --- a/.forge-snapshots/removeLiquidity with empty hook.snap +++ b/.forge-snapshots/removeLiquidity with empty hook.snap @@ -1 +1 @@ -122927 \ No newline at end of file +122841 \ 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 4e26e8532..953563126 100644 --- a/.forge-snapshots/removeLiquidity with native token.snap +++ b/.forge-snapshots/removeLiquidity with native token.snap @@ -1 +1 @@ -119714 \ No newline at end of file +119628 \ 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 c63746e60..841d0a054 100644 --- a/.forge-snapshots/simple addLiquidity second addition same range.snap +++ b/.forge-snapshots/simple addLiquidity second addition same range.snap @@ -1 +1 @@ -104746 \ No newline at end of file +104665 \ No newline at end of file diff --git a/.forge-snapshots/simple addLiquidity.snap b/.forge-snapshots/simple addLiquidity.snap index 230487a70..fcbdb380a 100644 --- a/.forge-snapshots/simple addLiquidity.snap +++ b/.forge-snapshots/simple addLiquidity.snap @@ -1 +1 @@ -167238 \ No newline at end of file +167157 \ 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 d525fb645..092b07c87 100644 --- a/.forge-snapshots/simple removeLiquidity some liquidity remains.snap +++ b/.forge-snapshots/simple removeLiquidity some liquidity remains.snap @@ -1 +1 @@ -98343 \ No newline at end of file +98257 \ No newline at end of file diff --git a/.forge-snapshots/simple removeLiquidity.snap b/.forge-snapshots/simple removeLiquidity.snap index aabdfdaea..d08171026 100644 --- a/.forge-snapshots/simple removeLiquidity.snap +++ b/.forge-snapshots/simple removeLiquidity.snap @@ -1 +1 @@ -90383 \ No newline at end of file +90297 \ 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 6930bbdad..c369d6254 100644 --- a/.forge-snapshots/simple swap with native.snap +++ b/.forge-snapshots/simple swap with native.snap @@ -1 +1 @@ -116430 \ No newline at end of file +116207 \ No newline at end of file diff --git a/.forge-snapshots/simple swap.snap b/.forge-snapshots/simple swap.snap index e1ea23d40..48efb98bf 100644 --- a/.forge-snapshots/simple swap.snap +++ b/.forge-snapshots/simple swap.snap @@ -1 +1 @@ -131591 \ No newline at end of file +131368 \ 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 ff485e628..48d84f50b 100644 --- a/.forge-snapshots/swap CA fee on unspecified.snap +++ b/.forge-snapshots/swap CA fee on unspecified.snap @@ -1 +1 @@ -182729 \ No newline at end of file +182506 \ 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 436848b5c..688ca3de6 100644 --- a/.forge-snapshots/swap against liquidity with native token.snap +++ b/.forge-snapshots/swap against liquidity with native token.snap @@ -1 +1 @@ -112303 \ No newline at end of file +112161 \ No newline at end of file diff --git a/.forge-snapshots/swap against liquidity.snap b/.forge-snapshots/swap against liquidity.snap index c618134dc..e213b0720 100644 --- a/.forge-snapshots/swap against liquidity.snap +++ b/.forge-snapshots/swap against liquidity.snap @@ -1 +1 @@ -123646 \ No newline at end of file +123504 \ 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 f72d8b74e..9e3e9800d 100644 --- a/.forge-snapshots/swap burn 6909 for input.snap +++ b/.forge-snapshots/swap burn 6909 for input.snap @@ -1 +1 @@ -135638 \ No newline at end of file +135496 \ 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 6a4a08361..d2f737183 100644 --- a/.forge-snapshots/swap burn native 6909 for input.snap +++ b/.forge-snapshots/swap burn native 6909 for input.snap @@ -1 +1 @@ -124901 \ No newline at end of file +124759 \ 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 4ea974586..07f96fae0 100644 --- a/.forge-snapshots/swap mint native output as 6909.snap +++ b/.forge-snapshots/swap mint native output as 6909.snap @@ -1 +1 @@ -146895 \ No newline at end of file +146753 \ 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 320083d9c..66d13af05 100644 --- a/.forge-snapshots/swap mint output as 6909.snap +++ b/.forge-snapshots/swap mint output as 6909.snap @@ -1 +1 @@ -163445 \ No newline at end of file +163222 \ 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 459f4b83e..e3a042d8c 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 @@ -221557 \ No newline at end of file +221192 \ 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 c88065c03..c7f5e04fc 100644 --- a/.forge-snapshots/swap with dynamic fee.snap +++ b/.forge-snapshots/swap with dynamic fee.snap @@ -1 +1 @@ -147646 \ No newline at end of file +147423 \ No newline at end of file diff --git a/.forge-snapshots/swap with hooks.snap b/.forge-snapshots/swap with hooks.snap index f4183d682..7b0905d7c 100644 --- a/.forge-snapshots/swap with hooks.snap +++ b/.forge-snapshots/swap with hooks.snap @@ -1 +1 @@ -123658 \ No newline at end of file +123516 \ 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 276d93e43..eb59d8ca3 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 @@ -179826 \ No newline at end of file +179603 \ 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 b4ba009f6..033dbeb6a 100644 --- a/.forge-snapshots/swap with return dynamic fee.snap +++ b/.forge-snapshots/swap with return dynamic fee.snap @@ -1 +1 @@ -155505 \ No newline at end of file +155282 \ No newline at end of file diff --git a/.forge-snapshots/tickSpacingToMaxLiquidityPerTick_gasCost60TickSpacing.snap b/.forge-snapshots/tickSpacingToMaxLiquidityPerTick_gasCost60TickSpacing.snap index 681cf043c..fb3518156 100644 --- a/.forge-snapshots/tickSpacingToMaxLiquidityPerTick_gasCost60TickSpacing.snap +++ b/.forge-snapshots/tickSpacingToMaxLiquidityPerTick_gasCost60TickSpacing.snap @@ -1 +1 @@ -192 \ No newline at end of file +197 \ No newline at end of file diff --git a/.forge-snapshots/tickSpacingToMaxLiquidityPerTick_gasCostMaxTickSpacing.snap b/.forge-snapshots/tickSpacingToMaxLiquidityPerTick_gasCostMaxTickSpacing.snap index 681cf043c..fb3518156 100644 --- a/.forge-snapshots/tickSpacingToMaxLiquidityPerTick_gasCostMaxTickSpacing.snap +++ b/.forge-snapshots/tickSpacingToMaxLiquidityPerTick_gasCostMaxTickSpacing.snap @@ -1 +1 @@ -192 \ No newline at end of file +197 \ No newline at end of file diff --git a/.forge-snapshots/tickSpacingToMaxLiquidityPerTick_gasCostMinTickSpacing.snap b/.forge-snapshots/tickSpacingToMaxLiquidityPerTick_gasCostMinTickSpacing.snap index 681cf043c..fb3518156 100644 --- a/.forge-snapshots/tickSpacingToMaxLiquidityPerTick_gasCostMinTickSpacing.snap +++ b/.forge-snapshots/tickSpacingToMaxLiquidityPerTick_gasCostMinTickSpacing.snap @@ -1 +1 @@ -192 \ No newline at end of file +197 \ 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 26f67861e..6325fd14e 100644 --- a/.forge-snapshots/update dynamic fee in before swap.snap +++ b/.forge-snapshots/update dynamic fee in before swap.snap @@ -1 +1 @@ -158108 \ No newline at end of file +157885 \ No newline at end of file diff --git a/src/libraries/TickMath.sol b/src/libraries/TickMath.sol index 7498f9952..a0c0f2180 100644 --- a/src/libraries/TickMath.sol +++ b/src/libraries/TickMath.sol @@ -13,7 +13,7 @@ library TickMath { /// @dev The minimum tick that may be passed to #getSqrtPriceAtTick computed from log base 1.0001 of 2**-128 int24 internal constant MIN_TICK = -887272; /// @dev The maximum tick that may be passed to #getSqrtPriceAtTick computed from log base 1.0001 of 2**128 - int24 internal constant MAX_TICK = -MIN_TICK; + int24 internal constant MAX_TICK = 887272; /// @dev The minimum tick spacing value drawn from the range of type int16 that is greater than 0, i.e. min from the range [1, 32767] int24 internal constant MIN_TICK_SPACING = 1; @@ -24,6 +24,9 @@ library TickMath { uint160 internal constant MIN_SQRT_PRICE = 4295128739; /// @dev The maximum value that can be returned from #getSqrtPriceAtTick. Equivalent to getSqrtPriceAtTick(MAX_TICK) uint160 internal constant MAX_SQRT_PRICE = 1461446703485210103287273052203988822378723970342; + /// @dev A threshold used for optimized bounds check, equals `MAX_SQRT_PRICE - MIN_SQRT_PRICE - 1` + uint160 internal constant MAX_SQRT_PRICE_MINUS_MIN_SQRT_PRICE_MINUS_ONE = + 1461446703485210103287273052203988822378723970342 - 4295128739 - 1; /// @notice Given a tickSpacing, compute the maximum usable tick function maxUsableTick(int24 tickSpacing) internal pure returns (int24) { @@ -55,7 +58,15 @@ library TickMath { // Either case, |tick| = mask ^ (tick + mask) absTick := xor(mask, add(mask, tick)) } - if (absTick > uint256(int256(MAX_TICK))) revert InvalidTick(); + // Equivalent: if (absTick > MAX_TICK) revert InvalidTick(); + /// @solidity memory-safe-assembly + assembly { + if gt(absTick, MAX_TICK) { + // store 4-byte selector of "InvalidTick()" at memory [0x1c, 0x20) + mstore(0, 0xce8ef7fc) + revert(0x1c, 0x04) + } + } uint256 price = absTick & 0x1 != 0 ? 0xfffcb933bd6fad37aa2d162d1a594001 : 0x100000000000000000000000000000000; @@ -100,8 +111,19 @@ library TickMath { /// @return tick The greatest tick for which the price is less than or equal to the input price function getTickAtSqrtPrice(uint160 sqrtPriceX96) internal pure returns (int24 tick) { unchecked { + // Equivalent: if (sqrtPriceX96 < MIN_SQRT_PRICE || sqrtPriceX96 >= MAX_SQRT_PRICE) revert InvalidSqrtPrice(); // second inequality must be < because the price can never reach the price at the max tick - if (sqrtPriceX96 < MIN_SQRT_PRICE || sqrtPriceX96 >= MAX_SQRT_PRICE) revert InvalidSqrtPrice(); + /// @solidity memory-safe-assembly + assembly { + // if sqrtPriceX96 < MIN_SQRT_PRICE, the `sub` underflows and `gt` is true + // if sqrtPriceX96 >= MAX_SQRT_PRICE, sqrtPriceX96 - MIN_SQRT_PRICE > MAX_SQRT_PRICE - MIN_SQRT_PRICE - 1 + if gt(sub(sqrtPriceX96, MIN_SQRT_PRICE), MAX_SQRT_PRICE_MINUS_MIN_SQRT_PRICE_MINUS_ONE) { + // store 4-byte selector of "InvalidSqrtPrice()" at memory [0x1c, 0x20) + mstore(0, 0x31efafe8) + revert(0x1c, 0x04) + } + } + uint256 price = uint256(sqrtPriceX96) << 32; uint256 r = price; diff --git a/test/libraries/TickMath.t.sol b/test/libraries/TickMath.t.sol index fa04fc015..62b4077cc 100644 --- a/test/libraries/TickMath.t.sol +++ b/test/libraries/TickMath.t.sol @@ -60,6 +60,16 @@ contract TickMathTestTest is Test, JavascriptFfi, GasSnapshot { tickMath.getSqrtPriceAtTick(MAX_TICK + 1); } + function test_fuzz_getSqrtPriceAtTick_throwsForTooLarge(int24 tick) public { + if (tick > 0) { + tick = int24(bound(tick, MAX_TICK + 1, type(int24).max)); + } else { + tick = int24(bound(tick, type(int24).min, MIN_TICK - 1)); + } + vm.expectRevert(TickMath.InvalidTick.selector); + tickMath.getSqrtPriceAtTick(tick); + } + function test_getSqrtPriceAtTick_isValidMinTick() public view { assertEq(tickMath.getSqrtPriceAtTick(MIN_TICK), tickMath.MIN_SQRT_PRICE()); assertEq(tickMath.getSqrtPriceAtTick(MIN_TICK), 4295128739); @@ -102,6 +112,16 @@ contract TickMathTestTest is Test, JavascriptFfi, GasSnapshot { tickMath.getTickAtSqrtPrice(MAX_SQRT_PRICE); } + function test_fuzz_getTickAtSqrtPrice_throwsForInvalid(uint160 sqrtPriceX96, bool gte) public { + if (gte) { + sqrtPriceX96 = uint160(bound(sqrtPriceX96, MAX_SQRT_PRICE, type(uint160).max)); + } else { + sqrtPriceX96 = uint160(bound(sqrtPriceX96, 0, MIN_SQRT_PRICE - 1)); + } + vm.expectRevert(TickMath.InvalidSqrtPrice.selector); + tickMath.getTickAtSqrtPrice(sqrtPriceX96); + } + function test_getTickAtSqrtPrice_isValidMinSqrtPrice() public view { assertEq(tickMath.getTickAtSqrtPrice(MIN_SQRT_PRICE), MIN_TICK); }