mirror of
https://github.com/lux-amm/v4-core.git
synced 2026-10-30 20:54:53 +00:00
559 lines
19 KiB
Solidity
559 lines
19 KiB
Solidity
// SPDX-License-Identifier: UNLICENSED
|
|
pragma solidity ^0.8.20;
|
|
|
|
import {Test} from "forge-std/Test.sol";
|
|
import {stdError} from "forge-std/StdError.sol";
|
|
import {Constants} from "./utils/Constants.sol";
|
|
import {Pool} from "../src/libraries/Pool.sol";
|
|
import {TickMath} from "../src/libraries/TickMath.sol";
|
|
|
|
contract LiquidityMathRef {
|
|
function addDelta(uint128 x, int128 y) external pure returns (uint128) {
|
|
return y < 0 ? x - uint128(-y) : x + uint128(y);
|
|
}
|
|
|
|
function addDelta(bool upper, int128 liquidityNetBefore, int128 liquidityDelta)
|
|
external
|
|
pure
|
|
returns (int128 liquidityNet)
|
|
{
|
|
liquidityNet = upper ? liquidityNetBefore - liquidityDelta : liquidityNetBefore + liquidityDelta;
|
|
}
|
|
}
|
|
|
|
contract TickTest is Test {
|
|
using Pool for Pool.State;
|
|
|
|
int24 constant LOW_TICK_SPACING = 10;
|
|
int24 constant MEDIUM_TICK_SPACING = 60;
|
|
int24 constant HIGH_TICK_SPACING = 200;
|
|
|
|
Pool.State public pool;
|
|
|
|
LiquidityMathRef internal liquidityMath;
|
|
|
|
function setUp() public {
|
|
liquidityMath = new LiquidityMathRef();
|
|
}
|
|
|
|
function ticks(int24 tick) internal view returns (Pool.TickInfo memory) {
|
|
return pool.ticks[tick];
|
|
}
|
|
|
|
function tickBitmap(int16 word) internal view returns (uint256) {
|
|
return pool.tickBitmap[word];
|
|
}
|
|
|
|
function tickSpacingToMaxLiquidityPerTick(int24 tickSpacing) internal pure returns (uint128) {
|
|
return Pool.tickSpacingToMaxLiquidityPerTick(tickSpacing);
|
|
}
|
|
|
|
function setTick(int24 tick, Pool.TickInfo memory info) internal {
|
|
pool.ticks[tick] = info;
|
|
}
|
|
|
|
function setTickBitmap(int16 word, uint256 bitmap) internal {
|
|
pool.tickBitmap[word] = bitmap;
|
|
}
|
|
|
|
function getFeeGrowthInside(
|
|
int24 tickLower,
|
|
int24 tickUpper,
|
|
int24 tickCurrent,
|
|
uint256 feeGrowthGlobal0X128,
|
|
uint256 feeGrowthGlobal1X128
|
|
) internal returns (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) {
|
|
pool.slot0 = pool.slot0.setTick(tickCurrent);
|
|
pool.feeGrowthGlobal0X128 = feeGrowthGlobal0X128;
|
|
pool.feeGrowthGlobal1X128 = feeGrowthGlobal1X128;
|
|
return pool.getFeeGrowthInside(tickLower, tickUpper);
|
|
}
|
|
|
|
function update(
|
|
int24 tick,
|
|
int24 tickCurrent,
|
|
int128 liquidityDelta,
|
|
uint256 feeGrowthGlobal0X128,
|
|
uint256 feeGrowthGlobal1X128,
|
|
bool upper
|
|
) internal returns (bool flipped, uint128 liquidityGrossAfter) {
|
|
pool.slot0 = pool.slot0.setTick(tickCurrent);
|
|
pool.feeGrowthGlobal0X128 = feeGrowthGlobal0X128;
|
|
pool.feeGrowthGlobal1X128 = feeGrowthGlobal1X128;
|
|
return pool.updateTick(tick, liquidityDelta, upper);
|
|
}
|
|
|
|
function clear(int24 tick) internal {
|
|
pool.clearTick(tick);
|
|
}
|
|
|
|
function cross(int24 tick, uint256 feeGrowthGlobal0X128, uint256 feeGrowthGlobal1X128)
|
|
internal
|
|
returns (int128 liquidityNet)
|
|
{
|
|
return pool.crossTick(tick, feeGrowthGlobal0X128, feeGrowthGlobal1X128);
|
|
}
|
|
|
|
function getMinTick(int24 tickSpacing) internal pure returns (int256) {
|
|
return (TickMath.MIN_TICK / tickSpacing) * tickSpacing;
|
|
}
|
|
|
|
function getMaxTick(int24 tickSpacing) internal pure returns (int256) {
|
|
return (TickMath.MAX_TICK / tickSpacing) * tickSpacing;
|
|
}
|
|
|
|
function checkCantOverflow(int24 tickSpacing, uint128 maxLiquidityPerTick) internal pure {
|
|
assertLe(
|
|
uint256(
|
|
uint256(maxLiquidityPerTick)
|
|
* uint256((getMaxTick(tickSpacing) - getMinTick(tickSpacing)) / tickSpacing + 1)
|
|
),
|
|
uint256(Constants.MAX_UINT128)
|
|
);
|
|
}
|
|
|
|
function testTick_tickSpacingToMaxLiquidityPerTick_returnsTheCorrectValueForLowFee() public pure {
|
|
uint128 maxLiquidityPerTick = tickSpacingToMaxLiquidityPerTick(LOW_TICK_SPACING);
|
|
|
|
assertEq(maxLiquidityPerTick, 1917559095893846719543856547154045);
|
|
checkCantOverflow(LOW_TICK_SPACING, maxLiquidityPerTick);
|
|
}
|
|
|
|
function testTick_tickSpacingToMaxLiquidityPerTick_returnsTheCorrectValueForMediumFee() public pure {
|
|
uint128 maxLiquidityPerTick = tickSpacingToMaxLiquidityPerTick(MEDIUM_TICK_SPACING);
|
|
|
|
assertEq(maxLiquidityPerTick, 11505354575363080317263139282924270);
|
|
checkCantOverflow(MEDIUM_TICK_SPACING, maxLiquidityPerTick);
|
|
}
|
|
|
|
function testTick_tickSpacingToMaxLiquidityPerTick_returnsTheCorrectValueForHighFee() public pure {
|
|
uint128 maxLiquidityPerTick = tickSpacingToMaxLiquidityPerTick(HIGH_TICK_SPACING);
|
|
|
|
assertEq(maxLiquidityPerTick, 38345995821606768476828330790147420);
|
|
checkCantOverflow(HIGH_TICK_SPACING, maxLiquidityPerTick);
|
|
}
|
|
|
|
function testTick_tickSpacingToMaxLiquidityPerTick_returnsTheCorrectValueForMinTickSpacing() public pure {
|
|
uint128 maxLiquidityPerTick = tickSpacingToMaxLiquidityPerTick(TickMath.MIN_TICK_SPACING);
|
|
|
|
assertEq(maxLiquidityPerTick, 191757530477355301479181766273477);
|
|
checkCantOverflow(TickMath.MIN_TICK_SPACING, maxLiquidityPerTick);
|
|
}
|
|
|
|
function testTick_tickSpacingToMaxLiquidityPerTick_returnsTheCorrectValueForMaxTickSpacing() public pure {
|
|
uint128 maxLiquidityPerTick = tickSpacingToMaxLiquidityPerTick(TickMath.MAX_TICK_SPACING);
|
|
|
|
assertEq(maxLiquidityPerTick, 6076470837873901133274546561281575204);
|
|
checkCantOverflow(TickMath.MAX_TICK_SPACING, maxLiquidityPerTick);
|
|
}
|
|
|
|
function testTick_tickSpacingToMaxLiquidityPerTick_returnsTheCorrectValueForEntireRange() public pure {
|
|
uint128 maxLiquidityPerTick = tickSpacingToMaxLiquidityPerTick(TickMath.MAX_TICK);
|
|
|
|
assertEq(maxLiquidityPerTick, type(uint128).max / 3);
|
|
checkCantOverflow(TickMath.MAX_TICK, maxLiquidityPerTick);
|
|
}
|
|
|
|
function testTick_tickSpacingToMaxLiquidityPerTick_returnsTheCorrectValueFor2302() public pure {
|
|
uint128 maxLiquidityPerTick = tickSpacingToMaxLiquidityPerTick(2302);
|
|
|
|
assertEq(maxLiquidityPerTick, 440780268032303709149448973357212709);
|
|
checkCantOverflow(2302, maxLiquidityPerTick);
|
|
}
|
|
|
|
function testTick_tickSpacingToMaxLiquidityPerTick_gasCostMinTickSpacing() public {
|
|
vm.startSnapshotGas("tickSpacingToMaxLiquidityPerTick_gasCostMinTickSpacing");
|
|
tickSpacingToMaxLiquidityPerTick(TickMath.MIN_TICK_SPACING);
|
|
vm.stopSnapshotGas();
|
|
}
|
|
|
|
function testTick_tickSpacingToMaxLiquidityPerTick_gasCost60TickSpacing() public {
|
|
vm.startSnapshotGas("tickSpacingToMaxLiquidityPerTick_gasCost60TickSpacing");
|
|
tickSpacingToMaxLiquidityPerTick(60);
|
|
vm.stopSnapshotGas();
|
|
}
|
|
|
|
function testTick_tickSpacingToMaxLiquidityPerTick_gasCostMaxTickSpacing() public {
|
|
vm.startSnapshotGas("tickSpacingToMaxLiquidityPerTick_gasCostMaxTickSpacing");
|
|
tickSpacingToMaxLiquidityPerTick(TickMath.MAX_TICK_SPACING);
|
|
vm.stopSnapshotGas();
|
|
}
|
|
|
|
function testTick_getFeeGrowthInside_returnsAllForTwoUninitializedTicksIfTickIsInside() public {
|
|
(uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) = getFeeGrowthInside(-2, 2, 0, 15, 15);
|
|
|
|
assertEq(feeGrowthInside0X128, 15);
|
|
assertEq(feeGrowthInside1X128, 15);
|
|
}
|
|
|
|
function testTick_getFeeGrowthInside_returns0ForTwoUninitializedTicksIfTickIsAbove() public {
|
|
(uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) = getFeeGrowthInside(-2, 2, 4, 15, 15);
|
|
|
|
assertEq(feeGrowthInside0X128, 0);
|
|
assertEq(feeGrowthInside1X128, 0);
|
|
}
|
|
|
|
function testTick_getFeeGrowthInside_returns0ForTwoUninitializedTicksIfTickIsBelow() public {
|
|
(uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) = getFeeGrowthInside(-2, 2, -4, 15, 15);
|
|
|
|
assertEq(feeGrowthInside0X128, 0);
|
|
assertEq(feeGrowthInside1X128, 0);
|
|
}
|
|
|
|
function testTick_getFeeGrowthInside_subtractsUpperTickIfBelow() public {
|
|
Pool.TickInfo memory info;
|
|
|
|
info.feeGrowthOutside0X128 = 2;
|
|
info.feeGrowthOutside1X128 = 3;
|
|
info.liquidityGross = 0;
|
|
info.liquidityNet = 0;
|
|
|
|
setTick(2, info);
|
|
|
|
(uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) = getFeeGrowthInside(-2, 2, 0, 15, 15);
|
|
|
|
assertEq(feeGrowthInside0X128, 13);
|
|
assertEq(feeGrowthInside1X128, 12);
|
|
}
|
|
|
|
function testTick_getFeeGrowthInside_subtractsLowerTickIfAbove() public {
|
|
Pool.TickInfo memory info;
|
|
|
|
info.feeGrowthOutside0X128 = 2;
|
|
info.feeGrowthOutside1X128 = 3;
|
|
info.liquidityGross = 0;
|
|
info.liquidityNet = 0;
|
|
|
|
setTick(-2, info);
|
|
|
|
(uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) = getFeeGrowthInside(-2, 2, 0, 15, 15);
|
|
|
|
assertEq(feeGrowthInside0X128, 13);
|
|
assertEq(feeGrowthInside1X128, 12);
|
|
}
|
|
|
|
function testTick_getFeeGrowthInside_subtractsUpperAndLowerTickIfInside() public {
|
|
Pool.TickInfo memory info;
|
|
|
|
info.feeGrowthOutside0X128 = 2;
|
|
info.feeGrowthOutside1X128 = 3;
|
|
info.liquidityGross = 0;
|
|
info.liquidityNet = 0;
|
|
|
|
setTick(-2, info);
|
|
|
|
info.feeGrowthOutside0X128 = 4;
|
|
info.feeGrowthOutside1X128 = 1;
|
|
info.liquidityGross = 0;
|
|
info.liquidityNet = 0;
|
|
|
|
setTick(2, info);
|
|
|
|
(uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) = getFeeGrowthInside(-2, 2, 0, 15, 15);
|
|
|
|
assertEq(feeGrowthInside0X128, 9);
|
|
assertEq(feeGrowthInside1X128, 11);
|
|
}
|
|
|
|
function testTick_getFeeGrowthInside_worksCorrectlyWithOverflowOnInsideTick() public {
|
|
Pool.TickInfo memory info;
|
|
|
|
info.feeGrowthOutside0X128 = Constants.MAX_UINT256 - 3;
|
|
info.feeGrowthOutside1X128 = Constants.MAX_UINT256 - 2;
|
|
info.liquidityGross = 0;
|
|
info.liquidityNet = 0;
|
|
|
|
setTick(-2, info);
|
|
|
|
info.feeGrowthOutside0X128 = 3;
|
|
info.feeGrowthOutside1X128 = 5;
|
|
info.liquidityGross = 0;
|
|
info.liquidityNet = 0;
|
|
|
|
setTick(2, info);
|
|
|
|
(uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) = getFeeGrowthInside(-2, 2, 0, 15, 15);
|
|
|
|
assertEq(feeGrowthInside0X128, 16);
|
|
assertEq(feeGrowthInside1X128, 13);
|
|
}
|
|
|
|
function testTick_update_flipsFromZeroToNonzero() public {
|
|
(bool flipped, uint128 liquidityGrossAfter) = update(0, 0, 1, 0, 0, false);
|
|
|
|
assertEq(flipped, true);
|
|
assertEq(liquidityGrossAfter, 1);
|
|
}
|
|
|
|
function testTick_update_doesNotFlipFromNonzeroToGreaterNonzero() public {
|
|
update(0, 0, 1, 0, 0, false);
|
|
(bool flipped, uint128 liquidityGrossAfter) = update(0, 0, 1, 0, 0, false);
|
|
|
|
assertEq(flipped, false);
|
|
assertEq(liquidityGrossAfter, 2);
|
|
}
|
|
|
|
function testTick_update_flipsFromNonzeroToZero() public {
|
|
update(0, 0, 1, 0, 0, false);
|
|
(bool flipped, uint128 liquidityGrossAfter) = update(0, 0, -1, 0, 0, false);
|
|
|
|
assertEq(flipped, true);
|
|
assertEq(liquidityGrossAfter, 0);
|
|
}
|
|
|
|
function testTick_update_doesNotFlipFromNonzeroToLesserZero() public {
|
|
update(0, 0, 2, 0, 0, false);
|
|
(bool flipped, uint128 liquidityGrossAfter) = update(0, 0, -1, 0, 0, false);
|
|
|
|
assertEq(flipped, false);
|
|
assertEq(liquidityGrossAfter, 1);
|
|
}
|
|
|
|
function testTick_update_netsTheLiquidityBasedOnUpperFlag() public {
|
|
Pool.TickInfo memory tickInfo;
|
|
|
|
update(0, 0, 2, 0, 0, false);
|
|
update(0, 0, 1, 0, 0, true);
|
|
update(0, 0, 3, 0, 0, true);
|
|
update(0, 0, 1, 0, 0, false);
|
|
tickInfo = ticks(0);
|
|
|
|
assertEq(tickInfo.liquidityGross, 2 + 1 + 3 + 1);
|
|
assertEq(tickInfo.liquidityNet, 2 - 1 - 3 + 1);
|
|
}
|
|
|
|
function testTick_update_revertsOnOverflowLiquidityGross() public {
|
|
update(0, 0, int128(Constants.MAX_UINT128 / 2 - 1), 0, 0, false);
|
|
|
|
vm.expectRevert();
|
|
update(0, 0, int128(Constants.MAX_UINT128 / 2 - 1), 0, 0, false);
|
|
}
|
|
|
|
function testTick_update_assumesAllGrowthHappensBelowTicksLteCurrentTick() public {
|
|
Pool.TickInfo memory tickInfo;
|
|
|
|
update(1, 1, 1, 1, 2, false);
|
|
tickInfo = ticks(1);
|
|
|
|
assertEq(tickInfo.feeGrowthOutside0X128, 1);
|
|
assertEq(tickInfo.feeGrowthOutside1X128, 2);
|
|
}
|
|
|
|
function testTick_update_doesNotSetAnyGrowthFieldsIfTickIsAlreadyInitialized() public {
|
|
Pool.TickInfo memory tickInfo;
|
|
|
|
update(1, 1, 1, 1, 2, false);
|
|
update(1, 1, 1, 6, 7, false);
|
|
tickInfo = ticks(1);
|
|
|
|
assertEq(tickInfo.feeGrowthOutside0X128, 1);
|
|
assertEq(tickInfo.feeGrowthOutside1X128, 2);
|
|
}
|
|
|
|
function testTick_update_doesNotSetAnyGrowthFieldsForTicksGtCurrentTick() public {
|
|
Pool.TickInfo memory tickInfo;
|
|
|
|
update(2, 1, 1, 1, 2, false);
|
|
tickInfo = ticks(2);
|
|
|
|
assertEq(tickInfo.feeGrowthOutside0X128, 0);
|
|
assertEq(tickInfo.feeGrowthOutside1X128, 0);
|
|
}
|
|
|
|
function testTick_update_liquidityParsing_parsesMaxUint128StoredLiquidityGrossBeforeUpdate() public {
|
|
Pool.TickInfo memory info;
|
|
|
|
info.feeGrowthOutside0X128 = 0;
|
|
info.feeGrowthOutside1X128 = 0;
|
|
info.liquidityGross = Constants.MAX_UINT128;
|
|
info.liquidityNet = 0;
|
|
|
|
setTick(2, info);
|
|
update(2, 1, -1, 1, 2, false);
|
|
|
|
info = ticks(2);
|
|
|
|
assertEq(info.liquidityGross, Constants.MAX_UINT128 - 1);
|
|
assertEq(info.liquidityNet, -1);
|
|
}
|
|
|
|
function testTick_update_liquidityParsing_parsesMaxUint128StoredLiquidityGrossAfterUpdate() public {
|
|
Pool.TickInfo memory info;
|
|
|
|
info.feeGrowthOutside0X128 = 0;
|
|
info.feeGrowthOutside1X128 = 0;
|
|
info.liquidityGross = (Constants.MAX_UINT128 / 2) + 1;
|
|
info.liquidityNet = 0;
|
|
|
|
setTick(2, info);
|
|
|
|
update(2, 1, int128(Constants.MAX_UINT128 / 2), 1, 2, false);
|
|
|
|
info = ticks(2);
|
|
|
|
assertEq(info.liquidityGross, Constants.MAX_UINT128);
|
|
assertEq(info.liquidityNet, int128(Constants.MAX_UINT128 / 2));
|
|
}
|
|
|
|
function testTick_update_liquidityParsing_parsesMaxInt128StoredLiquidityGrossBeforeUpdate() public {
|
|
Pool.TickInfo memory info;
|
|
|
|
info.feeGrowthOutside0X128 = 0;
|
|
info.feeGrowthOutside1X128 = 0;
|
|
info.liquidityGross = 1;
|
|
info.liquidityNet = int128(Constants.MAX_UINT128 / 2);
|
|
|
|
setTick(2, info);
|
|
update(2, 1, -1, 1, 2, false);
|
|
|
|
info = ticks(2);
|
|
|
|
assertEq(info.liquidityGross, 0);
|
|
assertEq(info.liquidityNet, int128(Constants.MAX_UINT128 / 2 - 1));
|
|
}
|
|
|
|
function testTick_update_liquidityParsing_parsesMaxInt128StoredLiquidityGrossAfterUpdate() public {
|
|
Pool.TickInfo memory info;
|
|
|
|
info.feeGrowthOutside0X128 = 0;
|
|
info.feeGrowthOutside1X128 = 0;
|
|
info.liquidityGross = 0;
|
|
info.liquidityNet = int128(Constants.MAX_UINT128 / 2 - 1);
|
|
|
|
setTick(2, info);
|
|
|
|
update(2, 1, 1, 1, 2, false);
|
|
|
|
info = ticks(2);
|
|
|
|
assertEq(info.liquidityGross, 1);
|
|
assertEq(info.liquidityNet, int128(Constants.MAX_UINT128 / 2));
|
|
}
|
|
|
|
function testTick_update_fuzz(uint128 liquidityGross, int128 liquidityNet, int128 liquidityDelta, bool upper)
|
|
public
|
|
{
|
|
try liquidityMath.addDelta(liquidityGross, liquidityDelta) returns (uint128 liquidityGrossAfter) {
|
|
try liquidityMath.addDelta(upper, liquidityNet, liquidityDelta) returns (int128 liquidityNetAfter) {
|
|
Pool.TickInfo memory info = Pool.TickInfo({
|
|
liquidityGross: liquidityGross,
|
|
liquidityNet: liquidityNet,
|
|
feeGrowthOutside0X128: 0,
|
|
feeGrowthOutside1X128: 0
|
|
});
|
|
|
|
setTick(2, info);
|
|
update({
|
|
tick: 2,
|
|
tickCurrent: 1,
|
|
liquidityDelta: liquidityDelta,
|
|
feeGrowthGlobal0X128: 0,
|
|
feeGrowthGlobal1X128: 0,
|
|
upper: upper
|
|
});
|
|
|
|
info = ticks(2);
|
|
|
|
assertEq(info.liquidityGross, liquidityGrossAfter);
|
|
assertEq(info.liquidityNet, liquidityNetAfter);
|
|
} catch (bytes memory reason) {
|
|
assertEq(reason, stdError.arithmeticError);
|
|
}
|
|
} catch (bytes memory reason) {
|
|
assertEq(reason, stdError.arithmeticError);
|
|
}
|
|
}
|
|
|
|
function testTick_clear_deletesAllTheDataInTheTick() public {
|
|
Pool.TickInfo memory info;
|
|
|
|
info.feeGrowthOutside0X128 = 1;
|
|
info.feeGrowthOutside1X128 = 2;
|
|
info.liquidityGross = 3;
|
|
info.liquidityNet = 4;
|
|
|
|
setTick(2, info);
|
|
|
|
clear(2);
|
|
|
|
info = ticks(2);
|
|
|
|
assertEq(info.feeGrowthOutside0X128, 0);
|
|
assertEq(info.feeGrowthOutside1X128, 0);
|
|
assertEq(info.liquidityGross, 0);
|
|
assertEq(info.liquidityNet, 0);
|
|
}
|
|
|
|
function testTick_cross_flipsTheGrowthVariables() public {
|
|
Pool.TickInfo memory info;
|
|
|
|
info.feeGrowthOutside0X128 = 1;
|
|
info.feeGrowthOutside1X128 = 2;
|
|
info.liquidityGross = 3;
|
|
info.liquidityNet = 4;
|
|
|
|
setTick(2, info);
|
|
|
|
cross(2, 7, 9);
|
|
|
|
info = ticks(2);
|
|
|
|
assertEq(info.feeGrowthOutside0X128, 6);
|
|
assertEq(info.feeGrowthOutside1X128, 7);
|
|
}
|
|
|
|
function testTick_cross_twoFlipsAreNoOp() public {
|
|
Pool.TickInfo memory info;
|
|
|
|
info.feeGrowthOutside0X128 = 1;
|
|
info.feeGrowthOutside1X128 = 2;
|
|
info.liquidityGross = 3;
|
|
info.liquidityNet = 4;
|
|
|
|
setTick(2, info);
|
|
|
|
cross(2, 7, 9);
|
|
cross(2, 7, 9);
|
|
|
|
info = ticks(2);
|
|
|
|
assertEq(info.feeGrowthOutside0X128, 1);
|
|
assertEq(info.feeGrowthOutside1X128, 2);
|
|
}
|
|
|
|
function test_getPoolTickInfo(int24 tick, Pool.TickInfo memory info) public {
|
|
setTick(tick, info);
|
|
Pool.TickInfo memory actualInfo = ticks(tick);
|
|
assertEq(actualInfo.liquidityGross, info.liquidityGross);
|
|
assertEq(actualInfo.liquidityNet, info.liquidityNet);
|
|
assertEq(actualInfo.feeGrowthOutside0X128, info.feeGrowthOutside0X128);
|
|
assertEq(actualInfo.feeGrowthOutside1X128, info.feeGrowthOutside1X128);
|
|
}
|
|
|
|
function test_getPoolBitmapInfo(int16 word, uint256 bitmap) public {
|
|
setTickBitmap(word, bitmap);
|
|
assertEq(tickBitmap(word), bitmap);
|
|
}
|
|
|
|
function testTick_tickSpacingToParametersInvariants_fuzz(int24 tickSpacing) public pure {
|
|
tickSpacing = int24(bound(tickSpacing, TickMath.MIN_TICK_SPACING, TickMath.MAX_TICK_SPACING));
|
|
|
|
int24 minTick = (TickMath.MIN_TICK / tickSpacing) * tickSpacing;
|
|
int24 maxTick = (TickMath.MAX_TICK / tickSpacing) * tickSpacing;
|
|
|
|
uint128 maxLiquidityPerTick = Pool.tickSpacingToMaxLiquidityPerTick(tickSpacing);
|
|
|
|
// symmetry around 0 tick
|
|
assertEq(maxTick, -minTick);
|
|
// positive max tick
|
|
assertGt(maxTick, 0);
|
|
// divisibility
|
|
assertEq((maxTick - minTick) % tickSpacing, 0);
|
|
|
|
uint256 numTicks = uint256(int256((maxTick - minTick) / tickSpacing)) + 1;
|
|
|
|
// sum of max liquidity on each tick is at most the cap
|
|
assertGe(type(uint128).max, uint256(maxLiquidityPerTick) * numTicks);
|
|
}
|
|
}
|