Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize TickMath bounds check in assembly #669

Merged
merged 5 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .forge-snapshots/TickMathGetTickAtSqrtPrice.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
224621
218521
Original file line number Diff line number Diff line change
@@ -1 +1 @@
153427
153346
2 changes: 1 addition & 1 deletion .forge-snapshots/addLiquidity CA fee.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
331093
331012
2 changes: 1 addition & 1 deletion .forge-snapshots/addLiquidity with empty hook.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
286086
286005
2 changes: 1 addition & 1 deletion .forge-snapshots/addLiquidity with native token.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
143637
143556
Original file line number Diff line number Diff line change
@@ -1 +1 @@
301605
301524
2 changes: 1 addition & 1 deletion .forge-snapshots/initialize.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
61775
61714
2 changes: 1 addition & 1 deletion .forge-snapshots/poolManager bytecode size.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
21575
21463
2 changes: 1 addition & 1 deletion .forge-snapshots/removeLiquidity CA fee.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
186559
186473
2 changes: 1 addition & 1 deletion .forge-snapshots/removeLiquidity with empty hook.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
122927
122841
2 changes: 1 addition & 1 deletion .forge-snapshots/removeLiquidity with native token.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
119714
119628
Original file line number Diff line number Diff line change
@@ -1 +1 @@
104746
104665
2 changes: 1 addition & 1 deletion .forge-snapshots/simple addLiquidity.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
167238
167157
Original file line number Diff line number Diff line change
@@ -1 +1 @@
98343
98257
2 changes: 1 addition & 1 deletion .forge-snapshots/simple removeLiquidity.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
90383
90297
2 changes: 1 addition & 1 deletion .forge-snapshots/simple swap with native.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
116430
116207
2 changes: 1 addition & 1 deletion .forge-snapshots/simple swap.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
131591
131368
2 changes: 1 addition & 1 deletion .forge-snapshots/swap CA fee on unspecified.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
182729
182506
Original file line number Diff line number Diff line change
@@ -1 +1 @@
112303
112161
2 changes: 1 addition & 1 deletion .forge-snapshots/swap against liquidity.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
123646
123504
2 changes: 1 addition & 1 deletion .forge-snapshots/swap burn 6909 for input.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
135638
135496
2 changes: 1 addition & 1 deletion .forge-snapshots/swap burn native 6909 for input.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
124901
124759
2 changes: 1 addition & 1 deletion .forge-snapshots/swap mint native output as 6909.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
146895
146753
2 changes: 1 addition & 1 deletion .forge-snapshots/swap mint output as 6909.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
163445
163222
Original file line number Diff line number Diff line change
@@ -1 +1 @@
221557
221192
2 changes: 1 addition & 1 deletion .forge-snapshots/swap with dynamic fee.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
147646
147423
2 changes: 1 addition & 1 deletion .forge-snapshots/swap with hooks.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
123658
123516
2 changes: 1 addition & 1 deletion .forge-snapshots/swap with lp fee and protocol fee.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
179826
179603
2 changes: 1 addition & 1 deletion .forge-snapshots/swap with return dynamic fee.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
155505
155282
Original file line number Diff line number Diff line change
@@ -1 +1 @@
192
197
Original file line number Diff line number Diff line change
@@ -1 +1 @@
192
197
Original file line number Diff line number Diff line change
@@ -1 +1 @@
192
197
2 changes: 1 addition & 1 deletion .forge-snapshots/update dynamic fee in before swap.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
158108
157885
28 changes: 25 additions & 3 deletions src/libraries/TickMath.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
hensha256 marked this conversation as resolved.
Show resolved Hide resolved

/// @notice Given a tickSpacing, compute the maximum usable tick
function maxUsableTick(int24 tickSpacing) internal pure returns (int24) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
20 changes: 20 additions & 0 deletions test/libraries/TickMath.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
Expand Down
Loading