feat(pool): add tick, tick-bitmap and position libraries
This commit is contained in:
1 parent
8c16223be5
commit
040cec1c55
3 files changed
+236
No files matched your search
@@ -0,0 +1,67 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity 0.8.36;
|
||||
|
||||
import {FullMath} from "./FullMath.sol";
|
||||
import {FixedPoint128} from "./FixedPoint128.sol";
|
||||
import {LiquidityMath} from "./LiquidityMath.sol";
|
||||
|
||||
/// @title Per-owner liquidity range positions and their accrued fees.
|
||||
library Position {
|
||||
struct Info {
|
||||
uint128 liquidity;
|
||||
uint256 feeGrowthInside0LastX128;
|
||||
uint256 feeGrowthInside1LastX128;
|
||||
uint128 tokensOwed0;
|
||||
uint128 tokensOwed1;
|
||||
}
|
||||
|
||||
function get(mapping(bytes32 => Info) storage self, address owner, int24 tickLower, int24 tickUpper)
|
||||
internal
|
||||
view
|
||||
returns (Position.Info storage position)
|
||||
{
|
||||
position = self[keccak256(abi.encodePacked(owner, tickLower, tickUpper))];
|
||||
}
|
||||
|
||||
function update(
|
||||
Info storage self,
|
||||
int128 liquidityDelta,
|
||||
uint256 feeGrowthInside0X128,
|
||||
uint256 feeGrowthInside1X128
|
||||
) internal {
|
||||
Info memory current = self;
|
||||
|
||||
uint128 liquidityNext;
|
||||
if (liquidityDelta == 0) {
|
||||
require(current.liquidity > 0, "Position: EMPTY");
|
||||
liquidityNext = current.liquidity;
|
||||
} else {
|
||||
liquidityNext = LiquidityMath.addDelta(current.liquidity, liquidityDelta);
|
||||
}
|
||||
|
||||
uint128 tokensOwed0;
|
||||
uint128 tokensOwed1;
|
||||
unchecked {
|
||||
tokensOwed0 = uint128(
|
||||
FullMath.mulDiv(
|
||||
feeGrowthInside0X128 - current.feeGrowthInside0LastX128, current.liquidity, FixedPoint128.q128
|
||||
)
|
||||
);
|
||||
tokensOwed1 = uint128(
|
||||
FullMath.mulDiv(
|
||||
feeGrowthInside1X128 - current.feeGrowthInside1LastX128, current.liquidity, FixedPoint128.q128
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (liquidityDelta != 0) self.liquidity = liquidityNext;
|
||||
self.feeGrowthInside0LastX128 = feeGrowthInside0X128;
|
||||
self.feeGrowthInside1LastX128 = feeGrowthInside1X128;
|
||||
if (tokensOwed0 > 0 || tokensOwed1 > 0) {
|
||||
unchecked {
|
||||
self.tokensOwed0 += tokensOwed0;
|
||||
self.tokensOwed1 += tokensOwed1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity 0.8.36;
|
||||
|
||||
import {TickMath} from "./TickMath.sol";
|
||||
import {LiquidityMath} from "./LiquidityMath.sol";
|
||||
|
||||
/// @title Per-tick liquidity and fee-growth bookkeeping.
|
||||
library Tick {
|
||||
struct Info {
|
||||
uint128 liquidityGross;
|
||||
int128 liquidityNet;
|
||||
uint256 feeGrowthOutside0X128;
|
||||
uint256 feeGrowthOutside1X128;
|
||||
bool initialized;
|
||||
}
|
||||
|
||||
function tickSpacingToMaxLiquidityPerTick(int24 tickSpacing) internal pure returns (uint128) {
|
||||
unchecked {
|
||||
int24 minUsableTick = (TickMath.minTick / tickSpacing) * tickSpacing;
|
||||
int24 maxUsableTick = (TickMath.maxTick / tickSpacing) * tickSpacing;
|
||||
uint24 numTicks = uint24((maxUsableTick - minUsableTick) / tickSpacing) + 1;
|
||||
return type(uint128).max / numTicks;
|
||||
}
|
||||
}
|
||||
|
||||
function getFeeGrowthInside(
|
||||
mapping(int24 => Info) storage self,
|
||||
int24 tickLower,
|
||||
int24 tickUpper,
|
||||
int24 tickCurrent,
|
||||
uint256 feeGrowthGlobal0X128,
|
||||
uint256 feeGrowthGlobal1X128
|
||||
) internal view returns (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) {
|
||||
unchecked {
|
||||
Info storage lower = self[tickLower];
|
||||
Info storage upper = self[tickUpper];
|
||||
|
||||
uint256 feeGrowthBelow0X128;
|
||||
uint256 feeGrowthBelow1X128;
|
||||
if (tickCurrent >= tickLower) {
|
||||
feeGrowthBelow0X128 = lower.feeGrowthOutside0X128;
|
||||
feeGrowthBelow1X128 = lower.feeGrowthOutside1X128;
|
||||
} else {
|
||||
feeGrowthBelow0X128 = feeGrowthGlobal0X128 - lower.feeGrowthOutside0X128;
|
||||
feeGrowthBelow1X128 = feeGrowthGlobal1X128 - lower.feeGrowthOutside1X128;
|
||||
}
|
||||
|
||||
uint256 feeGrowthAbove0X128;
|
||||
uint256 feeGrowthAbove1X128;
|
||||
if (tickCurrent < tickUpper) {
|
||||
feeGrowthAbove0X128 = upper.feeGrowthOutside0X128;
|
||||
feeGrowthAbove1X128 = upper.feeGrowthOutside1X128;
|
||||
} else {
|
||||
feeGrowthAbove0X128 = feeGrowthGlobal0X128 - upper.feeGrowthOutside0X128;
|
||||
feeGrowthAbove1X128 = feeGrowthGlobal1X128 - upper.feeGrowthOutside1X128;
|
||||
}
|
||||
|
||||
feeGrowthInside0X128 = feeGrowthGlobal0X128 - feeGrowthBelow0X128 - feeGrowthAbove0X128;
|
||||
feeGrowthInside1X128 = feeGrowthGlobal1X128 - feeGrowthBelow1X128 - feeGrowthAbove1X128;
|
||||
}
|
||||
}
|
||||
|
||||
function update(
|
||||
mapping(int24 => Info) storage self,
|
||||
int24 tick,
|
||||
int24 tickCurrent,
|
||||
int128 liquidityDelta,
|
||||
uint256 feeGrowthGlobal0X128,
|
||||
uint256 feeGrowthGlobal1X128,
|
||||
bool upper,
|
||||
uint128 maxLiquidity
|
||||
) internal returns (bool flipped) {
|
||||
Info storage info = self[tick];
|
||||
|
||||
uint128 liquidityGrossBefore = info.liquidityGross;
|
||||
uint128 liquidityGrossAfter = LiquidityMath.addDelta(liquidityGrossBefore, liquidityDelta);
|
||||
|
||||
require(liquidityGrossAfter <= maxLiquidity, "Tick: LIQ_MAX");
|
||||
|
||||
flipped = (liquidityGrossAfter == 0) != (liquidityGrossBefore == 0);
|
||||
|
||||
if (liquidityGrossBefore == 0) {
|
||||
if (tick <= tickCurrent) {
|
||||
info.feeGrowthOutside0X128 = feeGrowthGlobal0X128;
|
||||
info.feeGrowthOutside1X128 = feeGrowthGlobal1X128;
|
||||
}
|
||||
info.initialized = true;
|
||||
}
|
||||
|
||||
info.liquidityGross = liquidityGrossAfter;
|
||||
|
||||
info.liquidityNet = upper
|
||||
? int128(int256(info.liquidityNet) - liquidityDelta)
|
||||
: int128(int256(info.liquidityNet) + liquidityDelta);
|
||||
}
|
||||
|
||||
function clear(mapping(int24 => Info) storage self, int24 tick) internal {
|
||||
delete self[tick];
|
||||
}
|
||||
|
||||
function cross(
|
||||
mapping(int24 => Info) storage self,
|
||||
int24 tick,
|
||||
uint256 feeGrowthGlobal0X128,
|
||||
uint256 feeGrowthGlobal1X128
|
||||
) internal returns (int128 liquidityNet) {
|
||||
unchecked {
|
||||
Info storage info = self[tick];
|
||||
info.feeGrowthOutside0X128 = feeGrowthGlobal0X128 - info.feeGrowthOutside0X128;
|
||||
info.feeGrowthOutside1X128 = feeGrowthGlobal1X128 - info.feeGrowthOutside1X128;
|
||||
liquidityNet = info.liquidityNet;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity 0.8.36;
|
||||
|
||||
import {BitMath} from "./BitMath.sol";
|
||||
|
||||
/// @title Packed bitmap tracking which ticks hold liquidity.
|
||||
library TickBitmap {
|
||||
function position(int24 tick) private pure returns (int16 wordPos, uint8 bitPos) {
|
||||
unchecked {
|
||||
wordPos = int16(tick >> 8);
|
||||
bitPos = uint8(int8(tick % 256));
|
||||
}
|
||||
}
|
||||
|
||||
function flipTick(mapping(int16 => uint256) storage self, int24 tick, int24 tickSpacing) internal {
|
||||
unchecked {
|
||||
require(tick % tickSpacing == 0, "TickBitmap: SPACING");
|
||||
(int16 wordPos, uint8 bitPos) = position(tick / tickSpacing);
|
||||
uint256 mask = 1 << bitPos;
|
||||
self[wordPos] ^= mask;
|
||||
}
|
||||
}
|
||||
|
||||
function nextInitializedTickWithinOneWord(
|
||||
mapping(int16 => uint256) storage self,
|
||||
int24 tick,
|
||||
int24 tickSpacing,
|
||||
bool lte
|
||||
) internal view returns (int24 next, bool initialized) {
|
||||
unchecked {
|
||||
int24 compressed = tick / tickSpacing;
|
||||
if (tick < 0 && tick % tickSpacing != 0) compressed--;
|
||||
|
||||
if (lte) {
|
||||
(int16 wordPos, uint8 bitPos) = position(compressed);
|
||||
uint256 mask = (1 << bitPos) - 1 + (1 << bitPos);
|
||||
uint256 masked = self[wordPos] & mask;
|
||||
|
||||
initialized = masked != 0;
|
||||
next = initialized
|
||||
? (compressed - int24(uint24(bitPos - BitMath.mostSignificantBit(masked)))) * tickSpacing
|
||||
: (compressed - int24(uint24(bitPos))) * tickSpacing;
|
||||
} else {
|
||||
(int16 wordPos, uint8 bitPos) = position(compressed + 1);
|
||||
uint256 mask = ~((1 << bitPos) - 1);
|
||||
uint256 masked = self[wordPos] & mask;
|
||||
|
||||
initialized = masked != 0;
|
||||
next = initialized
|
||||
? (compressed + 1 + int24(uint24(BitMath.leastSignificantBit(masked) - bitPos))) * tickSpacing
|
||||
: (compressed + 1 + int24(uint24(type(uint8).max - bitPos))) * tickSpacing;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user