diff --git a/.forge-snapshots/addLiquidity CA fee.snap b/.forge-snapshots/addLiquidity CA fee.snap index feb6a4a97..59c195b39 100644 --- a/.forge-snapshots/addLiquidity CA fee.snap +++ b/.forge-snapshots/addLiquidity CA fee.snap @@ -1 +1 @@ -331635 \ No newline at end of file +331619 \ 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 a16c59cb1..b8b524ee9 100644 --- a/.forge-snapshots/addLiquidity with empty hook.snap +++ b/.forge-snapshots/addLiquidity with empty hook.snap @@ -1 +1 @@ -286628 \ No newline at end of file +286612 \ 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 e8e6c2b6c..6f9eeabe1 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 @@ -302147 \ No newline at end of file +302131 \ No newline at end of file diff --git a/.forge-snapshots/flipTick_flippingFirstTickInWordToInitialized.snap b/.forge-snapshots/flipTick_flippingFirstTickInWordToInitialized.snap index e602141e2..467ce8a84 100644 --- a/.forge-snapshots/flipTick_flippingFirstTickInWordToInitialized.snap +++ b/.forge-snapshots/flipTick_flippingFirstTickInWordToInitialized.snap @@ -1 +1 @@ -22504 \ No newline at end of file +22496 \ No newline at end of file diff --git a/.forge-snapshots/flipTick_flippingSecondTickInWordToInitialized.snap b/.forge-snapshots/flipTick_flippingSecondTickInWordToInitialized.snap index aa0e4d419..39837fd10 100644 --- a/.forge-snapshots/flipTick_flippingSecondTickInWordToInitialized.snap +++ b/.forge-snapshots/flipTick_flippingSecondTickInWordToInitialized.snap @@ -1 +1 @@ -5513 \ No newline at end of file +5505 \ No newline at end of file diff --git a/.forge-snapshots/nextInitializedTickWithinOneWord_lteFalse_forEntireWord.snap b/.forge-snapshots/nextInitializedTickWithinOneWord_lteFalse_forEntireWord.snap index 0f3866130..0203089ec 100644 --- a/.forge-snapshots/nextInitializedTickWithinOneWord_lteFalse_forEntireWord.snap +++ b/.forge-snapshots/nextInitializedTickWithinOneWord_lteFalse_forEntireWord.snap @@ -1 +1 @@ -2578 \ No newline at end of file +2488 \ No newline at end of file diff --git a/.forge-snapshots/nextInitializedTickWithinOneWord_lteFalse_justBelowBoundary.snap b/.forge-snapshots/nextInitializedTickWithinOneWord_lteFalse_justBelowBoundary.snap index 0f3866130..0203089ec 100644 --- a/.forge-snapshots/nextInitializedTickWithinOneWord_lteFalse_justBelowBoundary.snap +++ b/.forge-snapshots/nextInitializedTickWithinOneWord_lteFalse_justBelowBoundary.snap @@ -1 +1 @@ -2578 \ No newline at end of file +2488 \ No newline at end of file diff --git a/.forge-snapshots/nextInitializedTickWithinOneWord_lteTrue_forEntireWord.snap b/.forge-snapshots/nextInitializedTickWithinOneWord_lteTrue_forEntireWord.snap index a3412f309..de23d9ec9 100644 --- a/.forge-snapshots/nextInitializedTickWithinOneWord_lteTrue_forEntireWord.snap +++ b/.forge-snapshots/nextInitializedTickWithinOneWord_lteTrue_forEntireWord.snap @@ -1 +1 @@ -2556 \ No newline at end of file +2475 \ No newline at end of file diff --git a/.forge-snapshots/nextInitializedTickWithinOneWord_lteTrue_justBelowBoundary.snap b/.forge-snapshots/nextInitializedTickWithinOneWord_lteTrue_justBelowBoundary.snap index 47fd2ab34..a4e1baa59 100644 --- a/.forge-snapshots/nextInitializedTickWithinOneWord_lteTrue_justBelowBoundary.snap +++ b/.forge-snapshots/nextInitializedTickWithinOneWord_lteTrue_justBelowBoundary.snap @@ -1 +1 @@ -2865 \ No newline at end of file +2784 \ No newline at end of file diff --git a/.forge-snapshots/nextInitializedTickWithinOneWord_lteTrue_onBoundary_gas.snap b/.forge-snapshots/nextInitializedTickWithinOneWord_lteTrue_onBoundary_gas.snap index a3412f309..de23d9ec9 100644 --- a/.forge-snapshots/nextInitializedTickWithinOneWord_lteTrue_onBoundary_gas.snap +++ b/.forge-snapshots/nextInitializedTickWithinOneWord_lteTrue_onBoundary_gas.snap @@ -1 +1 @@ -2556 \ No newline at end of file +2475 \ No newline at end of file diff --git a/.forge-snapshots/poolManager bytecode size.snap b/.forge-snapshots/poolManager bytecode size.snap index 0f9a26130..b679c25ea 100644 --- a/.forge-snapshots/poolManager bytecode size.snap +++ b/.forge-snapshots/poolManager bytecode size.snap @@ -1 +1 @@ -21878 \ No newline at end of file +21796 \ No newline at end of file diff --git a/.forge-snapshots/removeLiquidity CA fee.snap b/.forge-snapshots/removeLiquidity CA fee.snap index 2a647bb5c..146fe2466 100644 --- a/.forge-snapshots/removeLiquidity CA fee.snap +++ b/.forge-snapshots/removeLiquidity CA fee.snap @@ -1 +1 @@ -187101 \ No newline at end of file +187085 \ 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 4c25874fc..0b982b822 100644 --- a/.forge-snapshots/removeLiquidity with empty hook.snap +++ b/.forge-snapshots/removeLiquidity with empty hook.snap @@ -1 +1 @@ -123469 \ No newline at end of file +123453 \ 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 70dcdabf5..18bf308de 100644 --- a/.forge-snapshots/removeLiquidity with native token.snap +++ b/.forge-snapshots/removeLiquidity with native token.snap @@ -1 +1 @@ -120256 \ No newline at end of file +120240 \ No newline at end of file diff --git a/.forge-snapshots/simple addLiquidity.snap b/.forge-snapshots/simple addLiquidity.snap index 54ae54908..e079b1611 100644 --- a/.forge-snapshots/simple addLiquidity.snap +++ b/.forge-snapshots/simple addLiquidity.snap @@ -1 +1 @@ -167750 \ No newline at end of file +167734 \ No newline at end of file diff --git a/.forge-snapshots/simple removeLiquidity.snap b/.forge-snapshots/simple removeLiquidity.snap index c304eb826..1a0b801f9 100644 --- a/.forge-snapshots/simple removeLiquidity.snap +++ b/.forge-snapshots/simple removeLiquidity.snap @@ -1 +1 @@ -90895 \ No newline at end of file +90879 \ 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 6499256c3..4fc9f9feb 100644 --- a/.forge-snapshots/simple swap with native.snap +++ b/.forge-snapshots/simple swap with native.snap @@ -1 +1 @@ -117057 \ No newline at end of file +116827 \ No newline at end of file diff --git a/.forge-snapshots/simple swap.snap b/.forge-snapshots/simple swap.snap index e611b41ec..7a20c6b3d 100644 --- a/.forge-snapshots/simple swap.snap +++ b/.forge-snapshots/simple swap.snap @@ -1 +1 @@ -132218 \ No newline at end of file +131988 \ 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 4c3f02f95..27c7f51ac 100644 --- a/.forge-snapshots/swap CA fee on unspecified.snap +++ b/.forge-snapshots/swap CA fee on unspecified.snap @@ -1 +1 @@ -183356 \ No newline at end of file +183126 \ 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 fcd98c992..8550686dc 100644 --- a/.forge-snapshots/swap against liquidity with native token.snap +++ b/.forge-snapshots/swap against liquidity with native token.snap @@ -1 +1 @@ -112697 \ No newline at end of file +112548 \ No newline at end of file diff --git a/.forge-snapshots/swap against liquidity.snap b/.forge-snapshots/swap against liquidity.snap index da8234ae7..14ee97dfc 100644 --- a/.forge-snapshots/swap against liquidity.snap +++ b/.forge-snapshots/swap against liquidity.snap @@ -1 +1 @@ -124040 \ No newline at end of file +123891 \ 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 27607b283..b19efd36d 100644 --- a/.forge-snapshots/swap burn 6909 for input.snap +++ b/.forge-snapshots/swap burn 6909 for input.snap @@ -1 +1 @@ -136081 \ No newline at end of file +135923 \ 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 0372bd1c3..dc06b6c6a 100644 --- a/.forge-snapshots/swap burn native 6909 for input.snap +++ b/.forge-snapshots/swap burn native 6909 for input.snap @@ -1 +1 @@ -125221 \ No newline at end of file +125140 \ 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 cccd898e8..fa5caa1ba 100644 --- a/.forge-snapshots/swap mint native output as 6909.snap +++ b/.forge-snapshots/swap mint native output as 6909.snap @@ -1 +1 @@ -147270 \ No newline at end of file +147180 \ 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 bb34139a6..1acf3c68f 100644 --- a/.forge-snapshots/swap mint output as 6909.snap +++ b/.forge-snapshots/swap mint output as 6909.snap @@ -1 +1 @@ -164072 \ No newline at end of file +163842 \ 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 245a0fddd..b2d6ca0cd 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 @@ -222578 \ No newline at end of file +222199 \ 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 cb8073a51..96c9f4d55 100644 --- a/.forge-snapshots/swap with dynamic fee.snap +++ b/.forge-snapshots/swap with dynamic fee.snap @@ -1 +1 @@ -148273 \ No newline at end of file +148043 \ No newline at end of file diff --git a/.forge-snapshots/swap with hooks.snap b/.forge-snapshots/swap with hooks.snap index 0830d9291..897a376b0 100644 --- a/.forge-snapshots/swap with hooks.snap +++ b/.forge-snapshots/swap with hooks.snap @@ -1 +1 @@ -124052 \ No newline at end of file +123903 \ 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 3b6f6d738..9efd3b6c3 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 @@ -180453 \ No newline at end of file +180223 \ 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 663192c4c..cb2abe163 100644 --- a/.forge-snapshots/swap with return dynamic fee.snap +++ b/.forge-snapshots/swap with return dynamic fee.snap @@ -1 +1 @@ -156132 \ No newline at end of file +155902 \ 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 f01d0684c..265212952 100644 --- a/.forge-snapshots/update dynamic fee in before swap.snap +++ b/.forge-snapshots/update dynamic fee in before swap.snap @@ -1 +1 @@ -158735 \ No newline at end of file +158505 \ No newline at end of file diff --git a/src/libraries/TickBitmap.sol b/src/libraries/TickBitmap.sol index 015ff4c28..6cdde14be 100644 --- a/src/libraries/TickBitmap.sol +++ b/src/libraries/TickBitmap.sol @@ -12,14 +12,29 @@ library TickBitmap { /// @param tickSpacing The tick spacing of the pool error TickMisaligned(int24 tick, int24 tickSpacing); + /// @dev round towards negative infinity + function compress(int24 tick, int24 tickSpacing) internal pure returns (int24 compressed) { + // compressed = tick / tickSpacing; + // if (tick < 0 && tick % tickSpacing != 0) compressed--; + assembly { + compressed := + sub( + sdiv(tick, tickSpacing), + // if (tick < 0 && tick % tickSpacing != 0) then tick % tickSpacing < 0, vice versa + slt(smod(tick, tickSpacing), 0) + ) + } + } + /// @notice Computes the position in the mapping where the initialized bit for a tick lives /// @param tick The tick for which to compute the position /// @return wordPos The key in the mapping containing the word in which the bit is stored /// @return bitPos The bit position in the word where the flag is stored function position(int24 tick) internal pure returns (int16 wordPos, uint8 bitPos) { - unchecked { - wordPos = int16(tick >> 8); - bitPos = uint8(int8(tick & (256 - 1))); + assembly { + // signed arithmetic shift right + wordPos := sar(8, tick) + bitPos := and(tick, 0xff) } } @@ -51,8 +66,7 @@ library TickBitmap { bool lte ) internal view returns (int24 next, bool initialized) { unchecked { - int24 compressed = tick / tickSpacing; - if (tick < 0 && tick % tickSpacing != 0) compressed--; // round towards negative infinity + int24 compressed = compress(tick, tickSpacing); if (lte) { (int16 wordPos, uint8 bitPos) = position(compressed); @@ -68,7 +82,7 @@ library TickBitmap { : (compressed - int24(uint24(bitPos))) * tickSpacing; } else { // start from the word of the next tick, since the current tick state doesn't matter - (int16 wordPos, uint8 bitPos) = position(compressed + 1); + (int16 wordPos, uint8 bitPos) = position(++compressed); // all the 1s at or to the left of the bitPos uint256 mask = ~((1 << bitPos) - 1); uint256 masked = self[wordPos] & mask; @@ -77,8 +91,8 @@ library TickBitmap { initialized = masked != 0; // overflow/underflow is possible, but prevented externally by limiting both tickSpacing and tick next = initialized - ? (compressed + 1 + int24(uint24(BitMath.leastSignificantBit(masked) - bitPos))) * tickSpacing - : (compressed + 1 + int24(uint24(type(uint8).max - bitPos))) * tickSpacing; + ? (compressed + int24(uint24(BitMath.leastSignificantBit(masked) - bitPos))) * tickSpacing + : (compressed + int24(uint24(type(uint8).max - bitPos))) * tickSpacing; } } } diff --git a/test/libraries/TickBitmap.t.sol b/test/libraries/TickBitmap.t.sol index e144cd749..524a4db37 100644 --- a/test/libraries/TickBitmap.t.sol +++ b/test/libraries/TickBitmap.t.sol @@ -23,6 +23,19 @@ contract TickBitmapTest is Test, GasSnapshot { } } + function test_fuzz_compress(int24 tick, int24 tickSpacing) public pure { + tickSpacing = int24(bound(tickSpacing, 1, type(int24).max)); + int24 compressed = tick / tickSpacing; + if (tick < 0 && tick % tickSpacing != 0) compressed--; + assertEq(TickBitmap.compress(tick, tickSpacing), compressed); + } + + function test_fuzz_position(int24 tick) public pure { + (int16 wordPos, uint8 bitPos) = TickBitmap.position(tick); + assertEq(wordPos, tick >> 8); + assertEq(bitPos, uint8(int8(tick % 256))); + } + function test_isInitialized_isFalseAtFirst() public view { assertEq(isInitialized(1), false); }