feat(pool): add concentrated-liquidity pool with swap across ticks
This commit is contained in:
1 parent
ab61aa0ecf
commit
2eeb91e0f1
1 file changed
+450
@@ -0,0 +1,450 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
pragma solidity 0.8.36;
|
||||||
|
|
||||||
|
import {IErc20} from "./interfaces/IErc20.sol";
|
||||||
|
import {IFluxClPool} from "./interfaces/IFluxClPool.sol";
|
||||||
|
import {IFluxMintCallback} from "./interfaces/IFluxMintCallback.sol";
|
||||||
|
import {IFluxSwapCallback} from "./interfaces/IFluxSwapCallback.sol";
|
||||||
|
import {TickMath} from "./libraries/TickMath.sol";
|
||||||
|
import {SqrtPriceMath} from "./libraries/SqrtPriceMath.sol";
|
||||||
|
import {SwapMath} from "./libraries/SwapMath.sol";
|
||||||
|
import {FullMath} from "./libraries/FullMath.sol";
|
||||||
|
import {FixedPoint128} from "./libraries/FixedPoint128.sol";
|
||||||
|
import {LiquidityMath} from "./libraries/LiquidityMath.sol";
|
||||||
|
import {SafeCast} from "./libraries/SafeCast.sol";
|
||||||
|
import {Tick} from "./libraries/Tick.sol";
|
||||||
|
import {TickBitmap} from "./libraries/TickBitmap.sol";
|
||||||
|
import {Position} from "./libraries/Position.sol";
|
||||||
|
import {Oracle} from "./libraries/Oracle.sol";
|
||||||
|
|
||||||
|
/// @title Concentrated-liquidity pool: liquidity indexed by ticks, priced in Q64.96 sqrt space.
|
||||||
|
contract FluxClPool is IFluxClPool {
|
||||||
|
using SafeCast for uint256;
|
||||||
|
using SafeCast for int256;
|
||||||
|
using Tick for mapping(int24 => Tick.Info);
|
||||||
|
using TickBitmap for mapping(int16 => uint256);
|
||||||
|
using Position for mapping(bytes32 => Position.Info);
|
||||||
|
using Position for Position.Info;
|
||||||
|
using Oracle for Oracle.Observation[65535];
|
||||||
|
|
||||||
|
address public immutable factory;
|
||||||
|
address public immutable token0;
|
||||||
|
address public immutable token1;
|
||||||
|
uint24 public immutable fee;
|
||||||
|
int24 public immutable tickSpacing;
|
||||||
|
uint128 public immutable maxLiquidityPerTick;
|
||||||
|
|
||||||
|
struct Slot0 {
|
||||||
|
uint160 sqrtPriceX96;
|
||||||
|
int24 tick;
|
||||||
|
uint16 observationIndex;
|
||||||
|
uint16 observationCardinality;
|
||||||
|
uint16 observationCardinalityNext;
|
||||||
|
bool unlocked;
|
||||||
|
}
|
||||||
|
|
||||||
|
Slot0 public slot0;
|
||||||
|
|
||||||
|
uint256 public feeGrowthGlobal0X128;
|
||||||
|
uint256 public feeGrowthGlobal1X128;
|
||||||
|
uint128 public liquidity;
|
||||||
|
|
||||||
|
mapping(int24 => Tick.Info) public ticks;
|
||||||
|
mapping(int16 => uint256) public tickBitmap;
|
||||||
|
mapping(bytes32 => Position.Info) public positions;
|
||||||
|
Oracle.Observation[65535] public observations;
|
||||||
|
|
||||||
|
modifier lock() {
|
||||||
|
require(slot0.unlocked, "Flux: LOCKED");
|
||||||
|
slot0.unlocked = false;
|
||||||
|
_;
|
||||||
|
slot0.unlocked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(address factoryAddress, address firstToken, address secondToken, uint24 feeAmount, int24 spacing) {
|
||||||
|
factory = factoryAddress;
|
||||||
|
token0 = firstToken;
|
||||||
|
token1 = secondToken;
|
||||||
|
fee = feeAmount;
|
||||||
|
tickSpacing = spacing;
|
||||||
|
maxLiquidityPerTick = Tick.tickSpacingToMaxLiquidityPerTick(spacing);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _blockTimestamp() internal view returns (uint32) {
|
||||||
|
return uint32(block.timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _balance0() private view returns (uint256) {
|
||||||
|
return IErc20(token0).balanceOf(address(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
function _balance1() private view returns (uint256) {
|
||||||
|
return IErc20(token1).balanceOf(address(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
function _safeTransfer(address token, address to, uint256 value) private {
|
||||||
|
(bool success, bytes memory returndata) =
|
||||||
|
token.call(abi.encodeWithSelector(IErc20.transfer.selector, to, value));
|
||||||
|
require(success && (returndata.length == 0 || abi.decode(returndata, (bool))), "Flux: TRANSFER_FAILED");
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkTicks(int24 tickLower, int24 tickUpper) private view {
|
||||||
|
require(tickLower < tickUpper, "Flux: TLU");
|
||||||
|
require(tickLower >= TickMath.minTick, "Flux: TLM");
|
||||||
|
require(tickUpper <= TickMath.maxTick, "Flux: TUM");
|
||||||
|
require(tickLower % tickSpacing == 0, "Flux: TLS");
|
||||||
|
require(tickUpper % tickSpacing == 0, "Flux: TUS");
|
||||||
|
}
|
||||||
|
|
||||||
|
function initialize(uint160 sqrtPriceX96) external {
|
||||||
|
require(slot0.sqrtPriceX96 == 0, "Flux: INIT");
|
||||||
|
int24 tick = TickMath.getTickAtSqrtRatio(sqrtPriceX96);
|
||||||
|
(uint16 cardinality, uint16 cardinalityNext) = observations.initialize(_blockTimestamp());
|
||||||
|
slot0 = Slot0({
|
||||||
|
sqrtPriceX96: sqrtPriceX96,
|
||||||
|
tick: tick,
|
||||||
|
observationIndex: 0,
|
||||||
|
observationCardinality: cardinality,
|
||||||
|
observationCardinalityNext: cardinalityNext,
|
||||||
|
unlocked: true
|
||||||
|
});
|
||||||
|
emit Initialize(sqrtPriceX96, tick);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ModifyPositionParams {
|
||||||
|
address owner;
|
||||||
|
int24 tickLower;
|
||||||
|
int24 tickUpper;
|
||||||
|
int128 liquidityDelta;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _updatePosition(address owner, int24 tickLower, int24 tickUpper, int128 liquidityDelta, int24 tick)
|
||||||
|
private
|
||||||
|
returns (Position.Info storage position)
|
||||||
|
{
|
||||||
|
position = positions.get(owner, tickLower, tickUpper);
|
||||||
|
|
||||||
|
uint256 feeGrowthGlobal0 = feeGrowthGlobal0X128;
|
||||||
|
uint256 feeGrowthGlobal1 = feeGrowthGlobal1X128;
|
||||||
|
|
||||||
|
bool flippedLower;
|
||||||
|
bool flippedUpper;
|
||||||
|
if (liquidityDelta != 0) {
|
||||||
|
flippedLower = ticks.update(
|
||||||
|
tickLower, tick, liquidityDelta, feeGrowthGlobal0, feeGrowthGlobal1, false, maxLiquidityPerTick
|
||||||
|
);
|
||||||
|
flippedUpper = ticks.update(
|
||||||
|
tickUpper, tick, liquidityDelta, feeGrowthGlobal0, feeGrowthGlobal1, true, maxLiquidityPerTick
|
||||||
|
);
|
||||||
|
|
||||||
|
if (flippedLower) tickBitmap.flipTick(tickLower, tickSpacing);
|
||||||
|
if (flippedUpper) tickBitmap.flipTick(tickUpper, tickSpacing);
|
||||||
|
}
|
||||||
|
|
||||||
|
(uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) =
|
||||||
|
ticks.getFeeGrowthInside(tickLower, tickUpper, tick, feeGrowthGlobal0, feeGrowthGlobal1);
|
||||||
|
|
||||||
|
position.update(liquidityDelta, feeGrowthInside0X128, feeGrowthInside1X128);
|
||||||
|
|
||||||
|
if (liquidityDelta < 0) {
|
||||||
|
if (flippedLower) ticks.clear(tickLower);
|
||||||
|
if (flippedUpper) ticks.clear(tickUpper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _modifyPosition(ModifyPositionParams memory params)
|
||||||
|
private
|
||||||
|
returns (Position.Info storage position, int256 amount0, int256 amount1)
|
||||||
|
{
|
||||||
|
checkTicks(params.tickLower, params.tickUpper);
|
||||||
|
Slot0 memory slot0Cache = slot0;
|
||||||
|
|
||||||
|
position =
|
||||||
|
_updatePosition(params.owner, params.tickLower, params.tickUpper, params.liquidityDelta, slot0Cache.tick);
|
||||||
|
|
||||||
|
if (params.liquidityDelta != 0) {
|
||||||
|
if (slot0Cache.tick < params.tickLower) {
|
||||||
|
amount0 = SqrtPriceMath.getAmount0Delta(
|
||||||
|
TickMath.getSqrtRatioAtTick(params.tickLower),
|
||||||
|
TickMath.getSqrtRatioAtTick(params.tickUpper),
|
||||||
|
params.liquidityDelta
|
||||||
|
);
|
||||||
|
} else if (slot0Cache.tick < params.tickUpper) {
|
||||||
|
uint128 liquidityBefore = liquidity;
|
||||||
|
|
||||||
|
(slot0.observationIndex, slot0.observationCardinality) = observations.write(
|
||||||
|
slot0Cache.observationIndex,
|
||||||
|
_blockTimestamp(),
|
||||||
|
slot0Cache.tick,
|
||||||
|
liquidityBefore,
|
||||||
|
slot0Cache.observationCardinality,
|
||||||
|
slot0Cache.observationCardinalityNext
|
||||||
|
);
|
||||||
|
|
||||||
|
amount0 = SqrtPriceMath.getAmount0Delta(
|
||||||
|
slot0Cache.sqrtPriceX96, TickMath.getSqrtRatioAtTick(params.tickUpper), params.liquidityDelta
|
||||||
|
);
|
||||||
|
amount1 = SqrtPriceMath.getAmount1Delta(
|
||||||
|
TickMath.getSqrtRatioAtTick(params.tickLower), slot0Cache.sqrtPriceX96, params.liquidityDelta
|
||||||
|
);
|
||||||
|
|
||||||
|
liquidity = LiquidityMath.addDelta(liquidityBefore, params.liquidityDelta);
|
||||||
|
} else {
|
||||||
|
amount1 = SqrtPriceMath.getAmount1Delta(
|
||||||
|
TickMath.getSqrtRatioAtTick(params.tickLower),
|
||||||
|
TickMath.getSqrtRatioAtTick(params.tickUpper),
|
||||||
|
params.liquidityDelta
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mint(address recipient, int24 tickLower, int24 tickUpper, uint128 amount, bytes calldata data)
|
||||||
|
external
|
||||||
|
lock
|
||||||
|
returns (uint256 amount0, uint256 amount1)
|
||||||
|
{
|
||||||
|
require(amount > 0, "Flux: AMOUNT");
|
||||||
|
(, int256 amount0Int, int256 amount1Int) = _modifyPosition(
|
||||||
|
ModifyPositionParams({
|
||||||
|
owner: recipient,
|
||||||
|
tickLower: tickLower,
|
||||||
|
tickUpper: tickUpper,
|
||||||
|
liquidityDelta: SafeCast.toInt128(int256(uint256(amount)))
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
amount0 = uint256(amount0Int);
|
||||||
|
amount1 = uint256(amount1Int);
|
||||||
|
|
||||||
|
uint256 balance0Before;
|
||||||
|
uint256 balance1Before;
|
||||||
|
if (amount0 > 0) balance0Before = _balance0();
|
||||||
|
if (amount1 > 0) balance1Before = _balance1();
|
||||||
|
IFluxMintCallback(msg.sender).fluxMintCallback(amount0, amount1, data);
|
||||||
|
if (amount0 > 0) require(balance0Before + amount0 <= _balance0(), "Flux: M0");
|
||||||
|
if (amount1 > 0) require(balance1Before + amount1 <= _balance1(), "Flux: M1");
|
||||||
|
|
||||||
|
emit Mint(msg.sender, recipient, tickLower, tickUpper, amount, amount0, amount1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function collect(
|
||||||
|
address recipient,
|
||||||
|
int24 tickLower,
|
||||||
|
int24 tickUpper,
|
||||||
|
uint128 amount0Requested,
|
||||||
|
uint128 amount1Requested
|
||||||
|
) external lock returns (uint128 amount0, uint128 amount1) {
|
||||||
|
Position.Info storage position = positions.get(msg.sender, tickLower, tickUpper);
|
||||||
|
|
||||||
|
amount0 = amount0Requested > position.tokensOwed0 ? position.tokensOwed0 : amount0Requested;
|
||||||
|
amount1 = amount1Requested > position.tokensOwed1 ? position.tokensOwed1 : amount1Requested;
|
||||||
|
|
||||||
|
if (amount0 > 0) {
|
||||||
|
position.tokensOwed0 -= amount0;
|
||||||
|
_safeTransfer(token0, recipient, amount0);
|
||||||
|
}
|
||||||
|
if (amount1 > 0) {
|
||||||
|
position.tokensOwed1 -= amount1;
|
||||||
|
_safeTransfer(token1, recipient, amount1);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit Collect(msg.sender, recipient, tickLower, tickUpper, amount0, amount1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function burn(int24 tickLower, int24 tickUpper, uint128 amount)
|
||||||
|
external
|
||||||
|
lock
|
||||||
|
returns (uint256 amount0, uint256 amount1)
|
||||||
|
{
|
||||||
|
(Position.Info storage position, int256 amount0Int, int256 amount1Int) = _modifyPosition(
|
||||||
|
ModifyPositionParams({
|
||||||
|
owner: msg.sender,
|
||||||
|
tickLower: tickLower,
|
||||||
|
tickUpper: tickUpper,
|
||||||
|
liquidityDelta: -SafeCast.toInt128(int256(uint256(amount)))
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
amount0 = uint256(-amount0Int);
|
||||||
|
amount1 = uint256(-amount1Int);
|
||||||
|
|
||||||
|
if (amount0 > 0 || amount1 > 0) {
|
||||||
|
(position.tokensOwed0, position.tokensOwed1) =
|
||||||
|
(position.tokensOwed0 + uint128(amount0), position.tokensOwed1 + uint128(amount1));
|
||||||
|
}
|
||||||
|
|
||||||
|
emit Burn(msg.sender, tickLower, tickUpper, amount, amount0, amount1);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SwapCache {
|
||||||
|
uint128 liquidityStart;
|
||||||
|
uint32 blockTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SwapState {
|
||||||
|
int256 amountSpecifiedRemaining;
|
||||||
|
int256 amountCalculated;
|
||||||
|
uint160 sqrtPriceX96;
|
||||||
|
int24 tick;
|
||||||
|
uint256 feeGrowthGlobalX128;
|
||||||
|
uint128 liquidity;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct StepComputations {
|
||||||
|
uint160 sqrtPriceStartX96;
|
||||||
|
int24 tickNext;
|
||||||
|
bool initialized;
|
||||||
|
uint160 sqrtPriceNextX96;
|
||||||
|
uint256 amountIn;
|
||||||
|
uint256 amountOut;
|
||||||
|
uint256 feeAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
function swap(
|
||||||
|
address recipient,
|
||||||
|
bool zeroForOne,
|
||||||
|
int256 amountSpecified,
|
||||||
|
uint160 sqrtPriceLimitX96,
|
||||||
|
bytes calldata data
|
||||||
|
) external returns (int256 amount0, int256 amount1) {
|
||||||
|
require(amountSpecified != 0, "Flux: AS");
|
||||||
|
Slot0 memory slot0Start = slot0;
|
||||||
|
require(slot0Start.unlocked, "Flux: LOCKED");
|
||||||
|
require(
|
||||||
|
zeroForOne
|
||||||
|
? sqrtPriceLimitX96 < slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 > TickMath.minSqrtRatio
|
||||||
|
: sqrtPriceLimitX96 > slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 < TickMath.maxSqrtRatio,
|
||||||
|
"Flux: SPL"
|
||||||
|
);
|
||||||
|
|
||||||
|
slot0.unlocked = false;
|
||||||
|
|
||||||
|
SwapCache memory cache = SwapCache({liquidityStart: liquidity, blockTimestamp: _blockTimestamp()});
|
||||||
|
|
||||||
|
bool exactInput = amountSpecified > 0;
|
||||||
|
|
||||||
|
SwapState memory state = SwapState({
|
||||||
|
amountSpecifiedRemaining: amountSpecified,
|
||||||
|
amountCalculated: 0,
|
||||||
|
sqrtPriceX96: slot0Start.sqrtPriceX96,
|
||||||
|
tick: slot0Start.tick,
|
||||||
|
feeGrowthGlobalX128: zeroForOne ? feeGrowthGlobal0X128 : feeGrowthGlobal1X128,
|
||||||
|
liquidity: cache.liquidityStart
|
||||||
|
});
|
||||||
|
|
||||||
|
while (state.amountSpecifiedRemaining != 0 && state.sqrtPriceX96 != sqrtPriceLimitX96) {
|
||||||
|
StepComputations memory step;
|
||||||
|
step.sqrtPriceStartX96 = state.sqrtPriceX96;
|
||||||
|
|
||||||
|
(step.tickNext, step.initialized) =
|
||||||
|
tickBitmap.nextInitializedTickWithinOneWord(state.tick, tickSpacing, zeroForOne);
|
||||||
|
|
||||||
|
if (step.tickNext < TickMath.minTick) step.tickNext = TickMath.minTick;
|
||||||
|
else if (step.tickNext > TickMath.maxTick) step.tickNext = TickMath.maxTick;
|
||||||
|
|
||||||
|
step.sqrtPriceNextX96 = TickMath.getSqrtRatioAtTick(step.tickNext);
|
||||||
|
|
||||||
|
(state.sqrtPriceX96, step.amountIn, step.amountOut, step.feeAmount) = SwapMath.computeSwapStep(
|
||||||
|
state.sqrtPriceX96,
|
||||||
|
(zeroForOne ? step.sqrtPriceNextX96 < sqrtPriceLimitX96 : step.sqrtPriceNextX96 > sqrtPriceLimitX96)
|
||||||
|
? sqrtPriceLimitX96
|
||||||
|
: step.sqrtPriceNextX96,
|
||||||
|
state.liquidity,
|
||||||
|
state.amountSpecifiedRemaining,
|
||||||
|
fee
|
||||||
|
);
|
||||||
|
|
||||||
|
if (exactInput) {
|
||||||
|
unchecked {
|
||||||
|
state.amountSpecifiedRemaining -= (step.amountIn + step.feeAmount).toInt256();
|
||||||
|
}
|
||||||
|
state.amountCalculated -= step.amountOut.toInt256();
|
||||||
|
} else {
|
||||||
|
unchecked {
|
||||||
|
state.amountSpecifiedRemaining += step.amountOut.toInt256();
|
||||||
|
}
|
||||||
|
state.amountCalculated += (step.amountIn + step.feeAmount).toInt256();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.liquidity > 0) {
|
||||||
|
unchecked {
|
||||||
|
state.feeGrowthGlobalX128 += FullMath.mulDiv(step.feeAmount, FixedPoint128.q128, state.liquidity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.sqrtPriceX96 == step.sqrtPriceNextX96) {
|
||||||
|
if (step.initialized) {
|
||||||
|
int128 liquidityNet = ticks.cross(
|
||||||
|
step.tickNext,
|
||||||
|
zeroForOne ? state.feeGrowthGlobalX128 : feeGrowthGlobal0X128,
|
||||||
|
zeroForOne ? feeGrowthGlobal1X128 : state.feeGrowthGlobalX128
|
||||||
|
);
|
||||||
|
if (zeroForOne) liquidityNet = -liquidityNet;
|
||||||
|
state.liquidity = LiquidityMath.addDelta(state.liquidity, liquidityNet);
|
||||||
|
}
|
||||||
|
state.tick = zeroForOne ? step.tickNext - 1 : step.tickNext;
|
||||||
|
} else if (state.sqrtPriceX96 != step.sqrtPriceStartX96) {
|
||||||
|
state.tick = TickMath.getTickAtSqrtRatio(state.sqrtPriceX96);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.tick != slot0Start.tick) {
|
||||||
|
(uint16 observationIndex, uint16 observationCardinality) = observations.write(
|
||||||
|
slot0Start.observationIndex,
|
||||||
|
cache.blockTimestamp,
|
||||||
|
slot0Start.tick,
|
||||||
|
cache.liquidityStart,
|
||||||
|
slot0Start.observationCardinality,
|
||||||
|
slot0Start.observationCardinalityNext
|
||||||
|
);
|
||||||
|
(slot0.sqrtPriceX96, slot0.tick, slot0.observationIndex, slot0.observationCardinality) =
|
||||||
|
(state.sqrtPriceX96, state.tick, observationIndex, observationCardinality);
|
||||||
|
} else {
|
||||||
|
slot0.sqrtPriceX96 = state.sqrtPriceX96;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cache.liquidityStart != state.liquidity) liquidity = state.liquidity;
|
||||||
|
|
||||||
|
if (zeroForOne) {
|
||||||
|
feeGrowthGlobal0X128 = state.feeGrowthGlobalX128;
|
||||||
|
} else {
|
||||||
|
feeGrowthGlobal1X128 = state.feeGrowthGlobalX128;
|
||||||
|
}
|
||||||
|
|
||||||
|
(amount0, amount1) = zeroForOne == exactInput
|
||||||
|
? (amountSpecified - state.amountSpecifiedRemaining, state.amountCalculated)
|
||||||
|
: (state.amountCalculated, amountSpecified - state.amountSpecifiedRemaining);
|
||||||
|
|
||||||
|
if (zeroForOne) {
|
||||||
|
if (amount1 < 0) _safeTransfer(token1, recipient, uint256(-amount1));
|
||||||
|
uint256 balance0Before = _balance0();
|
||||||
|
IFluxSwapCallback(msg.sender).fluxSwapCallback(amount0, amount1, data);
|
||||||
|
require(balance0Before + uint256(amount0) <= _balance0(), "Flux: IIA");
|
||||||
|
} else {
|
||||||
|
if (amount0 < 0) _safeTransfer(token0, recipient, uint256(-amount0));
|
||||||
|
uint256 balance1Before = _balance1();
|
||||||
|
IFluxSwapCallback(msg.sender).fluxSwapCallback(amount0, amount1, data);
|
||||||
|
require(balance1Before + uint256(amount1) <= _balance1(), "Flux: IIA");
|
||||||
|
}
|
||||||
|
|
||||||
|
emit Swap(msg.sender, recipient, amount0, amount1, state.sqrtPriceX96, state.liquidity, state.tick);
|
||||||
|
slot0.unlocked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function increaseObservationCardinalityNext(uint16 observationCardinalityNext) external lock {
|
||||||
|
uint16 previous = slot0.observationCardinalityNext;
|
||||||
|
uint16 next = observations.grow(previous, observationCardinalityNext);
|
||||||
|
slot0.observationCardinalityNext = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
function observe(uint32[] calldata secondsAgos)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s)
|
||||||
|
{
|
||||||
|
return observations.observe(
|
||||||
|
_blockTimestamp(), secondsAgos, slot0.tick, slot0.observationIndex, liquidity, slot0.observationCardinality
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user