mirror of https://github.com/crytic/slither
parent
2fbc8d2a9a
commit
d4b8ff011e
@ -0,0 +1,27 @@ |
||||
// SPDX-License-Identifier: BUSL-1.1 |
||||
pragma solidity =0.7.6; |
||||
|
||||
/// @title Prevents delegatecall to a contract |
||||
/// @notice Base contract that provides a modifier for preventing delegatecall to methods in a child contract |
||||
abstract contract NoDelegateCall { |
||||
/// @dev The original address of this contract |
||||
address private immutable original; |
||||
|
||||
constructor() { |
||||
// Immutables are computed in the init code of the contract, and then inlined into the deployed bytecode. |
||||
// In other words, this variable won't change when it's checked at runtime. |
||||
original = address(this); |
||||
} |
||||
|
||||
/// @dev Private method is used instead of inlining into modifier because modifiers are copied into each method, |
||||
/// and the use of immutable means the address bytes are copied in every place the modifier is used. |
||||
function checkNotDelegateCall() private view { |
||||
require(address(this) == original); |
||||
} |
||||
|
||||
/// @notice Prevents delegatecall into the modified method |
||||
modifier noDelegateCall() { |
||||
checkNotDelegateCall(); |
||||
_; |
||||
} |
||||
} |
@ -0,0 +1,869 @@ |
||||
// SPDX-License-Identifier: BUSL-1.1 |
||||
pragma solidity =0.7.6; |
||||
|
||||
import './interfaces/IUniswapV3Pool.sol'; |
||||
|
||||
import './NoDelegateCall.sol'; |
||||
|
||||
import './libraries/LowGasSafeMath.sol'; |
||||
import './libraries/SafeCast.sol'; |
||||
import './libraries/Tick.sol'; |
||||
import './libraries/TickBitmap.sol'; |
||||
import './libraries/Position.sol'; |
||||
import './libraries/Oracle.sol'; |
||||
|
||||
import './libraries/FullMath.sol'; |
||||
import './libraries/FixedPoint128.sol'; |
||||
import './libraries/TransferHelper.sol'; |
||||
import './libraries/TickMath.sol'; |
||||
import './libraries/LiquidityMath.sol'; |
||||
import './libraries/SqrtPriceMath.sol'; |
||||
import './libraries/SwapMath.sol'; |
||||
|
||||
import './interfaces/IUniswapV3PoolDeployer.sol'; |
||||
import './interfaces/IUniswapV3Factory.sol'; |
||||
import './interfaces/IERC20Minimal.sol'; |
||||
import './interfaces/callback/IUniswapV3MintCallback.sol'; |
||||
import './interfaces/callback/IUniswapV3SwapCallback.sol'; |
||||
import './interfaces/callback/IUniswapV3FlashCallback.sol'; |
||||
|
||||
contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall { |
||||
using LowGasSafeMath for uint256; |
||||
using LowGasSafeMath for int256; |
||||
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]; |
||||
|
||||
/// @inheritdoc IUniswapV3PoolImmutables |
||||
address public immutable override factory; |
||||
/// @inheritdoc IUniswapV3PoolImmutables |
||||
address public immutable override token0; |
||||
/// @inheritdoc IUniswapV3PoolImmutables |
||||
address public immutable override token1; |
||||
/// @inheritdoc IUniswapV3PoolImmutables |
||||
uint24 public immutable override fee; |
||||
|
||||
/// @inheritdoc IUniswapV3PoolImmutables |
||||
int24 public immutable override tickSpacing; |
||||
|
||||
/// @inheritdoc IUniswapV3PoolImmutables |
||||
uint128 public immutable override maxLiquidityPerTick; |
||||
|
||||
struct Slot0 { |
||||
// the current price |
||||
uint160 sqrtPriceX96; |
||||
// the current tick |
||||
int24 tick; |
||||
// the most-recently updated index of the observations array |
||||
uint16 observationIndex; |
||||
// the current maximum number of observations that are being stored |
||||
uint16 observationCardinality; |
||||
// the next maximum number of observations to store, triggered in observations.write |
||||
uint16 observationCardinalityNext; |
||||
// the current protocol fee as a percentage of the swap fee taken on withdrawal |
||||
// represented as an integer denominator (1/x)% |
||||
uint8 feeProtocol; |
||||
// whether the pool is locked |
||||
bool unlocked; |
||||
} |
||||
/// @inheritdoc IUniswapV3PoolState |
||||
Slot0 public override slot0; |
||||
|
||||
/// @inheritdoc IUniswapV3PoolState |
||||
uint256 public override feeGrowthGlobal0X128; |
||||
/// @inheritdoc IUniswapV3PoolState |
||||
uint256 public override feeGrowthGlobal1X128; |
||||
|
||||
// accumulated protocol fees in token0/token1 units |
||||
struct ProtocolFees { |
||||
uint128 token0; |
||||
uint128 token1; |
||||
} |
||||
/// @inheritdoc IUniswapV3PoolState |
||||
ProtocolFees public override protocolFees; |
||||
|
||||
/// @inheritdoc IUniswapV3PoolState |
||||
uint128 public override liquidity; |
||||
|
||||
/// @inheritdoc IUniswapV3PoolState |
||||
mapping(int24 => Tick.Info) public override ticks; |
||||
/// @inheritdoc IUniswapV3PoolState |
||||
mapping(int16 => uint256) public override tickBitmap; |
||||
/// @inheritdoc IUniswapV3PoolState |
||||
mapping(bytes32 => Position.Info) public override positions; |
||||
/// @inheritdoc IUniswapV3PoolState |
||||
Oracle.Observation[65535] public override observations; |
||||
|
||||
/// @dev Mutually exclusive reentrancy protection into the pool to/from a method. This method also prevents entrance |
||||
/// to a function before the pool is initialized. The reentrancy guard is required throughout the contract because |
||||
/// we use balance checks to determine the payment status of interactions such as mint, swap and flash. |
||||
modifier lock() { |
||||
require(slot0.unlocked, 'LOK'); |
||||
slot0.unlocked = false; |
||||
_; |
||||
slot0.unlocked = true; |
||||
} |
||||
|
||||
/// @dev Prevents calling a function from anyone except the address returned by IUniswapV3Factory#owner() |
||||
modifier onlyFactoryOwner() { |
||||
require(msg.sender == IUniswapV3Factory(factory).owner()); |
||||
_; |
||||
} |
||||
|
||||
constructor() { |
||||
int24 _tickSpacing; |
||||
(factory, token0, token1, fee, _tickSpacing) = IUniswapV3PoolDeployer(msg.sender).parameters(); |
||||
tickSpacing = _tickSpacing; |
||||
|
||||
maxLiquidityPerTick = Tick.tickSpacingToMaxLiquidityPerTick(_tickSpacing); |
||||
} |
||||
|
||||
/// @dev Common checks for valid tick inputs. |
||||
function checkTicks(int24 tickLower, int24 tickUpper) private pure { |
||||
require(tickLower < tickUpper, 'TLU'); |
||||
require(tickLower >= TickMath.MIN_TICK, 'TLM'); |
||||
require(tickUpper <= TickMath.MAX_TICK, 'TUM'); |
||||
} |
||||
|
||||
/// @dev Returns the block timestamp truncated to 32 bits, i.e. mod 2**32. This method is overridden in tests. |
||||
function _blockTimestamp() internal view virtual returns (uint32) { |
||||
return uint32(block.timestamp); // truncation is desired |
||||
} |
||||
|
||||
/// @dev Get the pool's balance of token0 |
||||
/// @dev This function is gas optimized to avoid a redundant extcodesize check in addition to the returndatasize |
||||
/// check |
||||
function balance0() private view returns (uint256) { |
||||
(bool success, bytes memory data) = |
||||
token0.staticcall(abi.encodeWithSelector(IERC20Minimal.balanceOf.selector, address(this))); |
||||
require(success && data.length >= 32); |
||||
return abi.decode(data, (uint256)); |
||||
} |
||||
|
||||
/// @dev Get the pool's balance of token1 |
||||
/// @dev This function is gas optimized to avoid a redundant extcodesize check in addition to the returndatasize |
||||
/// check |
||||
function balance1() private view returns (uint256) { |
||||
(bool success, bytes memory data) = |
||||
token1.staticcall(abi.encodeWithSelector(IERC20Minimal.balanceOf.selector, address(this))); |
||||
require(success && data.length >= 32); |
||||
return abi.decode(data, (uint256)); |
||||
} |
||||
|
||||
/// @inheritdoc IUniswapV3PoolDerivedState |
||||
function snapshotCumulativesInside(int24 tickLower, int24 tickUpper) |
||||
external |
||||
view |
||||
override |
||||
noDelegateCall |
||||
returns ( |
||||
int56 tickCumulativeInside, |
||||
uint160 secondsPerLiquidityInsideX128, |
||||
uint32 secondsInside |
||||
) |
||||
{ |
||||
checkTicks(tickLower, tickUpper); |
||||
|
||||
int56 tickCumulativeLower; |
||||
int56 tickCumulativeUpper; |
||||
uint160 secondsPerLiquidityOutsideLowerX128; |
||||
uint160 secondsPerLiquidityOutsideUpperX128; |
||||
uint32 secondsOutsideLower; |
||||
uint32 secondsOutsideUpper; |
||||
|
||||
{ |
||||
Tick.Info storage lower = ticks[tickLower]; |
||||
Tick.Info storage upper = ticks[tickUpper]; |
||||
bool initializedLower; |
||||
(tickCumulativeLower, secondsPerLiquidityOutsideLowerX128, secondsOutsideLower, initializedLower) = ( |
||||
lower.tickCumulativeOutside, |
||||
lower.secondsPerLiquidityOutsideX128, |
||||
lower.secondsOutside, |
||||
lower.initialized |
||||
); |
||||
require(initializedLower); |
||||
|
||||
bool initializedUpper; |
||||
(tickCumulativeUpper, secondsPerLiquidityOutsideUpperX128, secondsOutsideUpper, initializedUpper) = ( |
||||
upper.tickCumulativeOutside, |
||||
upper.secondsPerLiquidityOutsideX128, |
||||
upper.secondsOutside, |
||||
upper.initialized |
||||
); |
||||
require(initializedUpper); |
||||
} |
||||
|
||||
Slot0 memory _slot0 = slot0; |
||||
|
||||
if (_slot0.tick < tickLower) { |
||||
return ( |
||||
tickCumulativeLower - tickCumulativeUpper, |
||||
secondsPerLiquidityOutsideLowerX128 - secondsPerLiquidityOutsideUpperX128, |
||||
secondsOutsideLower - secondsOutsideUpper |
||||
); |
||||
} else if (_slot0.tick < tickUpper) { |
||||
uint32 time = _blockTimestamp(); |
||||
(int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) = |
||||
observations.observeSingle( |
||||
time, |
||||
0, |
||||
_slot0.tick, |
||||
_slot0.observationIndex, |
||||
liquidity, |
||||
_slot0.observationCardinality |
||||
); |
||||
return ( |
||||
tickCumulative - tickCumulativeLower - tickCumulativeUpper, |
||||
secondsPerLiquidityCumulativeX128 - |
||||
secondsPerLiquidityOutsideLowerX128 - |
||||
secondsPerLiquidityOutsideUpperX128, |
||||
time - secondsOutsideLower - secondsOutsideUpper |
||||
); |
||||
} else { |
||||
return ( |
||||
tickCumulativeUpper - tickCumulativeLower, |
||||
secondsPerLiquidityOutsideUpperX128 - secondsPerLiquidityOutsideLowerX128, |
||||
secondsOutsideUpper - secondsOutsideLower |
||||
); |
||||
} |
||||
} |
||||
|
||||
/// @inheritdoc IUniswapV3PoolDerivedState |
||||
function observe(uint32[] calldata secondsAgos) |
||||
external |
||||
view |
||||
override |
||||
noDelegateCall |
||||
returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) |
||||
{ |
||||
return |
||||
observations.observe( |
||||
_blockTimestamp(), |
||||
secondsAgos, |
||||
slot0.tick, |
||||
slot0.observationIndex, |
||||
liquidity, |
||||
slot0.observationCardinality |
||||
); |
||||
} |
||||
|
||||
/// @inheritdoc IUniswapV3PoolActions |
||||
function increaseObservationCardinalityNext(uint16 observationCardinalityNext) |
||||
external |
||||
override |
||||
lock |
||||
noDelegateCall |
||||
{ |
||||
uint16 observationCardinalityNextOld = slot0.observationCardinalityNext; // for the event |
||||
uint16 observationCardinalityNextNew = |
||||
observations.grow(observationCardinalityNextOld, observationCardinalityNext); |
||||
slot0.observationCardinalityNext = observationCardinalityNextNew; |
||||
if (observationCardinalityNextOld != observationCardinalityNextNew) |
||||
emit IncreaseObservationCardinalityNext(observationCardinalityNextOld, observationCardinalityNextNew); |
||||
} |
||||
|
||||
/// @inheritdoc IUniswapV3PoolActions |
||||
/// @dev not locked because it initializes unlocked |
||||
function initialize(uint160 sqrtPriceX96) external override { |
||||
require(slot0.sqrtPriceX96 == 0, 'AI'); |
||||
|
||||
int24 tick = TickMath.getTickAtSqrtRatio(sqrtPriceX96); |
||||
|
||||
(uint16 cardinality, uint16 cardinalityNext) = observations.initialize(_blockTimestamp()); |
||||
|
||||
slot0 = Slot0({ |
||||
sqrtPriceX96: sqrtPriceX96, |
||||
tick: tick, |
||||
observationIndex: 0, |
||||
observationCardinality: cardinality, |
||||
observationCardinalityNext: cardinalityNext, |
||||
feeProtocol: 0, |
||||
unlocked: true |
||||
}); |
||||
|
||||
emit Initialize(sqrtPriceX96, tick); |
||||
} |
||||
|
||||
struct ModifyPositionParams { |
||||
// the address that owns the position |
||||
address owner; |
||||
// the lower and upper tick of the position |
||||
int24 tickLower; |
||||
int24 tickUpper; |
||||
// any change in liquidity |
||||
int128 liquidityDelta; |
||||
} |
||||
|
||||
/// @dev Effect some changes to a position |
||||
/// @param params the position details and the change to the position's liquidity to effect |
||||
/// @return position a storage pointer referencing the position with the given owner and tick range |
||||
/// @return amount0 the amount of token0 owed to the pool, negative if the pool should pay the recipient |
||||
/// @return amount1 the amount of token1 owed to the pool, negative if the pool should pay the recipient |
||||
function _modifyPosition(ModifyPositionParams memory params) |
||||
private |
||||
noDelegateCall |
||||
returns ( |
||||
Position.Info storage position, |
||||
int256 amount0, |
||||
int256 amount1 |
||||
) |
||||
{ |
||||
checkTicks(params.tickLower, params.tickUpper); |
||||
|
||||
Slot0 memory _slot0 = slot0; // SLOAD for gas optimization |
||||
|
||||
position = _updatePosition( |
||||
params.owner, |
||||
params.tickLower, |
||||
params.tickUpper, |
||||
params.liquidityDelta, |
||||
_slot0.tick |
||||
); |
||||
|
||||
if (params.liquidityDelta != 0) { |
||||
if (_slot0.tick < params.tickLower) { |
||||
// current tick is below the passed range; liquidity can only become in range by crossing from left to |
||||
// right, when we'll need _more_ token0 (it's becoming more valuable) so user must provide it |
||||
amount0 = SqrtPriceMath.getAmount0Delta( |
||||
TickMath.getSqrtRatioAtTick(params.tickLower), |
||||
TickMath.getSqrtRatioAtTick(params.tickUpper), |
||||
params.liquidityDelta |
||||
); |
||||
} else if (_slot0.tick < params.tickUpper) { |
||||
// current tick is inside the passed range |
||||
uint128 liquidityBefore = liquidity; // SLOAD for gas optimization |
||||
|
||||
// write an oracle entry |
||||
(slot0.observationIndex, slot0.observationCardinality) = observations.write( |
||||
_slot0.observationIndex, |
||||
_blockTimestamp(), |
||||
_slot0.tick, |
||||
liquidityBefore, |
||||
_slot0.observationCardinality, |
||||
_slot0.observationCardinalityNext |
||||
); |
||||
|
||||
amount0 = SqrtPriceMath.getAmount0Delta( |
||||
_slot0.sqrtPriceX96, |
||||
TickMath.getSqrtRatioAtTick(params.tickUpper), |
||||
params.liquidityDelta |
||||
); |
||||
amount1 = SqrtPriceMath.getAmount1Delta( |
||||
TickMath.getSqrtRatioAtTick(params.tickLower), |
||||
_slot0.sqrtPriceX96, |
||||
params.liquidityDelta |
||||
); |
||||
|
||||
liquidity = LiquidityMath.addDelta(liquidityBefore, params.liquidityDelta); |
||||
} else { |
||||
// current tick is above the passed range; liquidity can only become in range by crossing from right to |
||||
// left, when we'll need _more_ token1 (it's becoming more valuable) so user must provide it |
||||
amount1 = SqrtPriceMath.getAmount1Delta( |
||||
TickMath.getSqrtRatioAtTick(params.tickLower), |
||||
TickMath.getSqrtRatioAtTick(params.tickUpper), |
||||
params.liquidityDelta |
||||
); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// @dev Gets and updates a position with the given liquidity delta |
||||
/// @param owner the owner of the position |
||||
/// @param tickLower the lower tick of the position's tick range |
||||
/// @param tickUpper the upper tick of the position's tick range |
||||
/// @param tick the current tick, passed to avoid sloads |
||||
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 _feeGrowthGlobal0X128 = feeGrowthGlobal0X128; // SLOAD for gas optimization |
||||
uint256 _feeGrowthGlobal1X128 = feeGrowthGlobal1X128; // SLOAD for gas optimization |
||||
|
||||
// if we need to update the ticks, do it |
||||
bool flippedLower; |
||||
bool flippedUpper; |
||||
if (liquidityDelta != 0) { |
||||
uint32 time = _blockTimestamp(); |
||||
(int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) = |
||||
observations.observeSingle( |
||||
time, |
||||
0, |
||||
slot0.tick, |
||||
slot0.observationIndex, |
||||
liquidity, |
||||
slot0.observationCardinality |
||||
); |
||||
|
||||
flippedLower = ticks.update( |
||||
tickLower, |
||||
tick, |
||||
liquidityDelta, |
||||
_feeGrowthGlobal0X128, |
||||
_feeGrowthGlobal1X128, |
||||
secondsPerLiquidityCumulativeX128, |
||||
tickCumulative, |
||||
time, |
||||
false, |
||||
maxLiquidityPerTick |
||||
); |
||||
flippedUpper = ticks.update( |
||||
tickUpper, |
||||
tick, |
||||
liquidityDelta, |
||||
_feeGrowthGlobal0X128, |
||||
_feeGrowthGlobal1X128, |
||||
secondsPerLiquidityCumulativeX128, |
||||
tickCumulative, |
||||
time, |
||||
true, |
||||
maxLiquidityPerTick |
||||
); |
||||
|
||||
if (flippedLower) { |
||||
tickBitmap.flipTick(tickLower, tickSpacing); |
||||
} |
||||
if (flippedUpper) { |
||||
tickBitmap.flipTick(tickUpper, tickSpacing); |
||||
} |
||||
} |
||||
|
||||
(uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) = |
||||
ticks.getFeeGrowthInside(tickLower, tickUpper, tick, _feeGrowthGlobal0X128, _feeGrowthGlobal1X128); |
||||
|
||||
position.update(liquidityDelta, feeGrowthInside0X128, feeGrowthInside1X128); |
||||
|
||||
// clear any tick data that is no longer needed |
||||
if (liquidityDelta < 0) { |
||||
if (flippedLower) { |
||||
ticks.clear(tickLower); |
||||
} |
||||
if (flippedUpper) { |
||||
ticks.clear(tickUpper); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// @inheritdoc IUniswapV3PoolActions |
||||
/// @dev noDelegateCall is applied indirectly via _modifyPosition |
||||
function mint( |
||||
address recipient, |
||||
int24 tickLower, |
||||
int24 tickUpper, |
||||
uint128 amount, |
||||
bytes calldata data |
||||
) external override lock returns (uint256 amount0, uint256 amount1) { |
||||
require(amount > 0); |
||||
(, int256 amount0Int, int256 amount1Int) = |
||||
_modifyPosition( |
||||
ModifyPositionParams({ |
||||
owner: recipient, |
||||
tickLower: tickLower, |
||||
tickUpper: tickUpper, |
||||
liquidityDelta: int256(amount).toInt128() |
||||
}) |
||||
); |
||||
|
||||
amount0 = uint256(amount0Int); |
||||
amount1 = uint256(amount1Int); |
||||
|
||||
uint256 balance0Before; |
||||
uint256 balance1Before; |
||||
if (amount0 > 0) balance0Before = balance0(); |
||||
if (amount1 > 0) balance1Before = balance1(); |
||||
IUniswapV3MintCallback(msg.sender).uniswapV3MintCallback(amount0, amount1, data); |
||||
if (amount0 > 0) require(balance0Before.add(amount0) <= balance0(), 'M0'); |
||||
if (amount1 > 0) require(balance1Before.add(amount1) <= balance1(), 'M1'); |
||||
|
||||
emit Mint(msg.sender, recipient, tickLower, tickUpper, amount, amount0, amount1); |
||||
} |
||||
|
||||
/// @inheritdoc IUniswapV3PoolActions |
||||
function collect( |
||||
address recipient, |
||||
int24 tickLower, |
||||
int24 tickUpper, |
||||
uint128 amount0Requested, |
||||
uint128 amount1Requested |
||||
) external override lock returns (uint128 amount0, uint128 amount1) { |
||||
// we don't need to checkTicks here, because invalid positions will never have non-zero tokensOwed{0,1} |
||||
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; |
||||
TransferHelper.safeTransfer(token0, recipient, amount0); |
||||
} |
||||
if (amount1 > 0) { |
||||
position.tokensOwed1 -= amount1; |
||||
TransferHelper.safeTransfer(token1, recipient, amount1); |
||||
} |
||||
|
||||
emit Collect(msg.sender, recipient, tickLower, tickUpper, amount0, amount1); |
||||
} |
||||
|
||||
/// @inheritdoc IUniswapV3PoolActions |
||||
/// @dev noDelegateCall is applied indirectly via _modifyPosition |
||||
function burn( |
||||
int24 tickLower, |
||||
int24 tickUpper, |
||||
uint128 amount |
||||
) external override lock returns (uint256 amount0, uint256 amount1) { |
||||
(Position.Info storage position, int256 amount0Int, int256 amount1Int) = |
||||
_modifyPosition( |
||||
ModifyPositionParams({ |
||||
owner: msg.sender, |
||||
tickLower: tickLower, |
||||
tickUpper: tickUpper, |
||||
liquidityDelta: -int256(amount).toInt128() |
||||
}) |
||||
); |
||||
|
||||
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 { |
||||
// the protocol fee for the input token |
||||
uint8 feeProtocol; |
||||
// liquidity at the beginning of the swap |
||||
uint128 liquidityStart; |
||||
// the timestamp of the current block |
||||
uint32 blockTimestamp; |
||||
// the current value of the tick accumulator, computed only if we cross an initialized tick |
||||
int56 tickCumulative; |
||||
// the current value of seconds per liquidity accumulator, computed only if we cross an initialized tick |
||||
uint160 secondsPerLiquidityCumulativeX128; |
||||
// whether we've computed and cached the above two accumulators |
||||
bool computedLatestObservation; |
||||
} |
||||
|
||||
// the top level state of the swap, the results of which are recorded in storage at the end |
||||
struct SwapState { |
||||
// the amount remaining to be swapped in/out of the input/output asset |
||||
int256 amountSpecifiedRemaining; |
||||
// the amount already swapped out/in of the output/input asset |
||||
int256 amountCalculated; |
||||
// current sqrt(price) |
||||
uint160 sqrtPriceX96; |
||||
// the tick associated with the current price |
||||
int24 tick; |
||||
// the global fee growth of the input token |
||||
uint256 feeGrowthGlobalX128; |
||||
// amount of input token paid as protocol fee |
||||
uint128 protocolFee; |
||||
// the current liquidity in range |
||||
uint128 liquidity; |
||||
} |
||||
|
||||
struct StepComputations { |
||||
// the price at the beginning of the step |
||||
uint160 sqrtPriceStartX96; |
||||
// the next tick to swap to from the current tick in the swap direction |
||||
int24 tickNext; |
||||
// whether tickNext is initialized or not |
||||
bool initialized; |
||||
// sqrt(price) for the next tick (1/0) |
||||
uint160 sqrtPriceNextX96; |
||||
// how much is being swapped in in this step |
||||
uint256 amountIn; |
||||
// how much is being swapped out |
||||
uint256 amountOut; |
||||
// how much fee is being paid in |
||||
uint256 feeAmount; |
||||
} |
||||
|
||||
/// @inheritdoc IUniswapV3PoolActions |
||||
function swap( |
||||
address recipient, |
||||
bool zeroForOne, |
||||
int256 amountSpecified, |
||||
uint160 sqrtPriceLimitX96, |
||||
bytes calldata data |
||||
) external override noDelegateCall returns (int256 amount0, int256 amount1) { |
||||
require(amountSpecified != 0, 'AS'); |
||||
|
||||
Slot0 memory slot0Start = slot0; |
||||
|
||||
require(slot0Start.unlocked, 'LOK'); |
||||
require( |
||||
zeroForOne |
||||
? sqrtPriceLimitX96 < slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 > TickMath.MIN_SQRT_RATIO |
||||
: sqrtPriceLimitX96 > slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 < TickMath.MAX_SQRT_RATIO, |
||||
'SPL' |
||||
); |
||||
|
||||
slot0.unlocked = false; |
||||
|
||||
SwapCache memory cache = |
||||
SwapCache({ |
||||
liquidityStart: liquidity, |
||||
blockTimestamp: _blockTimestamp(), |
||||
feeProtocol: zeroForOne ? (slot0Start.feeProtocol % 16) : (slot0Start.feeProtocol >> 4), |
||||
secondsPerLiquidityCumulativeX128: 0, |
||||
tickCumulative: 0, |
||||
computedLatestObservation: false |
||||
}); |
||||
|
||||
bool exactInput = amountSpecified > 0; |
||||
|
||||
SwapState memory state = |
||||
SwapState({ |
||||
amountSpecifiedRemaining: amountSpecified, |
||||
amountCalculated: 0, |
||||
sqrtPriceX96: slot0Start.sqrtPriceX96, |
||||
tick: slot0Start.tick, |
||||
feeGrowthGlobalX128: zeroForOne ? feeGrowthGlobal0X128 : feeGrowthGlobal1X128, |
||||
protocolFee: 0, |
||||
liquidity: cache.liquidityStart |
||||
}); |
||||
|
||||
// continue swapping as long as we haven't used the entire input/output and haven't reached the price limit |
||||
while (state.amountSpecifiedRemaining != 0 && state.sqrtPriceX96 != sqrtPriceLimitX96) { |
||||
StepComputations memory step; |
||||
|
||||
step.sqrtPriceStartX96 = state.sqrtPriceX96; |
||||
|
||||
(step.tickNext, step.initialized) = tickBitmap.nextInitializedTickWithinOneWord( |
||||
state.tick, |
||||
tickSpacing, |
||||
zeroForOne |
||||
); |
||||
|
||||
// ensure that we do not overshoot the min/max tick, as the tick bitmap is not aware of these bounds |
||||
if (step.tickNext < TickMath.MIN_TICK) { |
||||
step.tickNext = TickMath.MIN_TICK; |
||||
} else if (step.tickNext > TickMath.MAX_TICK) { |
||||
step.tickNext = TickMath.MAX_TICK; |
||||
} |
||||
|
||||
// get the price for the next tick |
||||
step.sqrtPriceNextX96 = TickMath.getSqrtRatioAtTick(step.tickNext); |
||||
|
||||
// compute values to swap to the target tick, price limit, or point where input/output amount is exhausted |
||||
(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) { |
||||
state.amountSpecifiedRemaining -= (step.amountIn + step.feeAmount).toInt256(); |
||||
state.amountCalculated = state.amountCalculated.sub(step.amountOut.toInt256()); |
||||
} else { |
||||
state.amountSpecifiedRemaining += step.amountOut.toInt256(); |
||||
state.amountCalculated = state.amountCalculated.add((step.amountIn + step.feeAmount).toInt256()); |
||||
} |
||||
|
||||
// if the protocol fee is on, calculate how much is owed, decrement feeAmount, and increment protocolFee |
||||
if (cache.feeProtocol > 0) { |
||||
uint256 delta = step.feeAmount / cache.feeProtocol; |
||||
step.feeAmount -= delta; |
||||
state.protocolFee += uint128(delta); |
||||
} |
||||
|
||||
// update global fee tracker |
||||
if (state.liquidity > 0) |
||||
state.feeGrowthGlobalX128 += FullMath.mulDiv(step.feeAmount, FixedPoint128.Q128, state.liquidity); |
||||
|
||||
// shift tick if we reached the next price |
||||
if (state.sqrtPriceX96 == step.sqrtPriceNextX96) { |
||||
// if the tick is initialized, run the tick transition |
||||
if (step.initialized) { |
||||
// check for the placeholder value, which we replace with the actual value the first time the swap |
||||
// crosses an initialized tick |
||||
if (!cache.computedLatestObservation) { |
||||
(cache.tickCumulative, cache.secondsPerLiquidityCumulativeX128) = observations.observeSingle( |
||||
cache.blockTimestamp, |
||||
0, |
||||
slot0Start.tick, |
||||
slot0Start.observationIndex, |
||||
cache.liquidityStart, |
||||
slot0Start.observationCardinality |
||||
); |
||||
cache.computedLatestObservation = true; |
||||
} |
||||
int128 liquidityNet = |
||||
ticks.cross( |
||||
step.tickNext, |
||||
(zeroForOne ? state.feeGrowthGlobalX128 : feeGrowthGlobal0X128), |
||||
(zeroForOne ? feeGrowthGlobal1X128 : state.feeGrowthGlobalX128), |
||||
cache.secondsPerLiquidityCumulativeX128, |
||||
cache.tickCumulative, |
||||
cache.blockTimestamp |
||||
); |
||||
// if we're moving leftward, we interpret liquidityNet as the opposite sign |
||||
// safe because liquidityNet cannot be type(int128).min |
||||
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) { |
||||
// recompute unless we're on a lower tick boundary (i.e. already transitioned ticks), and haven't moved |
||||
state.tick = TickMath.getTickAtSqrtRatio(state.sqrtPriceX96); |
||||
} |
||||
} |
||||
|
||||
// update tick and write an oracle entry if the tick change |
||||
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 { |
||||
// otherwise just update the price |
||||
slot0.sqrtPriceX96 = state.sqrtPriceX96; |
||||
} |
||||
|
||||
// update liquidity if it changed |
||||
if (cache.liquidityStart != state.liquidity) liquidity = state.liquidity; |
||||
|
||||
// update fee growth global and, if necessary, protocol fees |
||||
// overflow is acceptable, protocol has to withdraw before it hits type(uint128).max fees |
||||
if (zeroForOne) { |
||||
feeGrowthGlobal0X128 = state.feeGrowthGlobalX128; |
||||
if (state.protocolFee > 0) protocolFees.token0 += state.protocolFee; |
||||
} else { |
||||
feeGrowthGlobal1X128 = state.feeGrowthGlobalX128; |
||||
if (state.protocolFee > 0) protocolFees.token1 += state.protocolFee; |
||||
} |
||||
|
||||
(amount0, amount1) = zeroForOne == exactInput |
||||
? (amountSpecified - state.amountSpecifiedRemaining, state.amountCalculated) |
||||
: (state.amountCalculated, amountSpecified - state.amountSpecifiedRemaining); |
||||
|
||||
// do the transfers and collect payment |
||||
if (zeroForOne) { |
||||
if (amount1 < 0) TransferHelper.safeTransfer(token1, recipient, uint256(-amount1)); |
||||
|
||||
uint256 balance0Before = balance0(); |
||||
IUniswapV3SwapCallback(msg.sender).uniswapV3SwapCallback(amount0, amount1, data); |
||||
require(balance0Before.add(uint256(amount0)) <= balance0(), 'IIA'); |
||||
} else { |
||||
if (amount0 < 0) TransferHelper.safeTransfer(token0, recipient, uint256(-amount0)); |
||||
|
||||
uint256 balance1Before = balance1(); |
||||
IUniswapV3SwapCallback(msg.sender).uniswapV3SwapCallback(amount0, amount1, data); |
||||
require(balance1Before.add(uint256(amount1)) <= balance1(), 'IIA'); |
||||
} |
||||
|
||||
emit Swap(msg.sender, recipient, amount0, amount1, state.sqrtPriceX96, state.liquidity, state.tick); |
||||
slot0.unlocked = true; |
||||
} |
||||
|
||||
/// @inheritdoc IUniswapV3PoolActions |
||||
function flash( |
||||
address recipient, |
||||
uint256 amount0, |
||||
uint256 amount1, |
||||
bytes calldata data |
||||
) external override lock noDelegateCall { |
||||
uint128 _liquidity = liquidity; |
||||
require(_liquidity > 0, 'L'); |
||||
|
||||
uint256 fee0 = FullMath.mulDivRoundingUp(amount0, fee, 1e6); |
||||
uint256 fee1 = FullMath.mulDivRoundingUp(amount1, fee, 1e6); |
||||
uint256 balance0Before = balance0(); |
||||
uint256 balance1Before = balance1(); |
||||
|
||||
if (amount0 > 0) TransferHelper.safeTransfer(token0, recipient, amount0); |
||||
if (amount1 > 0) TransferHelper.safeTransfer(token1, recipient, amount1); |
||||
|
||||
IUniswapV3FlashCallback(msg.sender).uniswapV3FlashCallback(fee0, fee1, data); |
||||
|
||||
uint256 balance0After = balance0(); |
||||
uint256 balance1After = balance1(); |
||||
|
||||
require(balance0Before.add(fee0) <= balance0After, 'F0'); |
||||
require(balance1Before.add(fee1) <= balance1After, 'F1'); |
||||
|
||||
// sub is safe because we know balanceAfter is gt balanceBefore by at least fee |
||||
uint256 paid0 = balance0After - balance0Before; |
||||
uint256 paid1 = balance1After - balance1Before; |
||||
|
||||
if (paid0 > 0) { |
||||
uint8 feeProtocol0 = slot0.feeProtocol % 16; |
||||
uint256 fees0 = feeProtocol0 == 0 ? 0 : paid0 / feeProtocol0; |
||||
if (uint128(fees0) > 0) protocolFees.token0 += uint128(fees0); |
||||
feeGrowthGlobal0X128 += FullMath.mulDiv(paid0 - fees0, FixedPoint128.Q128, _liquidity); |
||||
} |
||||
if (paid1 > 0) { |
||||
uint8 feeProtocol1 = slot0.feeProtocol >> 4; |
||||
uint256 fees1 = feeProtocol1 == 0 ? 0 : paid1 / feeProtocol1; |
||||
if (uint128(fees1) > 0) protocolFees.token1 += uint128(fees1); |
||||
feeGrowthGlobal1X128 += FullMath.mulDiv(paid1 - fees1, FixedPoint128.Q128, _liquidity); |
||||
} |
||||
|
||||
emit Flash(msg.sender, recipient, amount0, amount1, paid0, paid1); |
||||
} |
||||
|
||||
/// @inheritdoc IUniswapV3PoolOwnerActions |
||||
function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external override lock onlyFactoryOwner { |
||||
require( |
||||
(feeProtocol0 == 0 || (feeProtocol0 >= 4 && feeProtocol0 <= 10)) && |
||||
(feeProtocol1 == 0 || (feeProtocol1 >= 4 && feeProtocol1 <= 10)) |
||||
); |
||||
uint8 feeProtocolOld = slot0.feeProtocol; |
||||
slot0.feeProtocol = feeProtocol0 + (feeProtocol1 << 4); |
||||
emit SetFeeProtocol(feeProtocolOld % 16, feeProtocolOld >> 4, feeProtocol0, feeProtocol1); |
||||
} |
||||
|
||||
/// @inheritdoc IUniswapV3PoolOwnerActions |
||||
function collectProtocol( |
||||
address recipient, |
||||
uint128 amount0Requested, |
||||
uint128 amount1Requested |
||||
) external override lock onlyFactoryOwner returns (uint128 amount0, uint128 amount1) { |
||||
amount0 = amount0Requested > protocolFees.token0 ? protocolFees.token0 : amount0Requested; |
||||
amount1 = amount1Requested > protocolFees.token1 ? protocolFees.token1 : amount1Requested; |
||||
|
||||
if (amount0 > 0) { |
||||
if (amount0 == protocolFees.token0) amount0--; // ensure that the slot is not cleared, for gas savings |
||||
protocolFees.token0 -= amount0; |
||||
TransferHelper.safeTransfer(token0, recipient, amount0); |
||||
} |
||||
if (amount1 > 0) { |
||||
if (amount1 == protocolFees.token1) amount1--; // ensure that the slot is not cleared, for gas savings |
||||
protocolFees.token1 -= amount1; |
||||
TransferHelper.safeTransfer(token1, recipient, amount1); |
||||
} |
||||
|
||||
emit CollectProtocol(msg.sender, recipient, amount0, amount1); |
||||
} |
||||
} |
@ -0,0 +1,52 @@ |
||||
// SPDX-License-Identifier: GPL-2.0-or-later |
||||
pragma solidity >=0.5.0; |
||||
|
||||
/// @title Minimal ERC20 interface for Uniswap |
||||
/// @notice Contains a subset of the full ERC20 interface that is used in Uniswap V3 |
||||
interface IERC20Minimal { |
||||
/// @notice Returns the balance of a token |
||||
/// @param account The account for which to look up the number of tokens it has, i.e. its balance |
||||
/// @return The number of tokens held by the account |
||||
function balanceOf(address account) external view returns (uint256); |
||||
|
||||
/// @notice Transfers the amount of token from the `msg.sender` to the recipient |
||||
/// @param recipient The account that will receive the amount transferred |
||||
/// @param amount The number of tokens to send from the sender to the recipient |
||||
/// @return Returns true for a successful transfer, false for an unsuccessful transfer |
||||
function transfer(address recipient, uint256 amount) external returns (bool); |
||||
|
||||
/// @notice Returns the current allowance given to a spender by an owner |
||||
/// @param owner The account of the token owner |
||||
/// @param spender The account of the token spender |
||||
/// @return The current allowance granted by `owner` to `spender` |
||||
function allowance(address owner, address spender) external view returns (uint256); |
||||
|
||||
/// @notice Sets the allowance of a spender from the `msg.sender` to the value `amount` |
||||
/// @param spender The account which will be allowed to spend a given amount of the owners tokens |
||||
/// @param amount The amount of tokens allowed to be used by `spender` |
||||
/// @return Returns true for a successful approval, false for unsuccessful |
||||
function approve(address spender, uint256 amount) external returns (bool); |
||||
|
||||
/// @notice Transfers `amount` tokens from `sender` to `recipient` up to the allowance given to the `msg.sender` |
||||
/// @param sender The account from which the transfer will be initiated |
||||
/// @param recipient The recipient of the transfer |
||||
/// @param amount The amount of the transfer |
||||
/// @return Returns true for a successful transfer, false for unsuccessful |
||||
function transferFrom( |
||||
address sender, |
||||
address recipient, |
||||
uint256 amount |
||||
) external returns (bool); |
||||
|
||||
/// @notice Event emitted when tokens are transferred from one address to another, either via `#transfer` or `#transferFrom`. |
||||
/// @param from The account from which the tokens were sent, i.e. the balance decreased |
||||
/// @param to The account to which the tokens were sent, i.e. the balance increased |
||||
/// @param value The amount of tokens that were transferred |
||||
event Transfer(address indexed from, address indexed to, uint256 value); |
||||
|
||||
/// @notice Event emitted when the approval amount for the spender of a given owner's tokens changes. |
||||
/// @param owner The account that approved spending of its tokens |
||||
/// @param spender The account for which the spending allowance was modified |
||||
/// @param value The new allowance from the owner to the spender |
||||
event Approval(address indexed owner, address indexed spender, uint256 value); |
||||
} |
@ -0,0 +1,78 @@ |
||||
// SPDX-License-Identifier: GPL-2.0-or-later |
||||
pragma solidity >=0.5.0; |
||||
|
||||
/// @title The interface for the Uniswap V3 Factory |
||||
/// @notice The Uniswap V3 Factory facilitates creation of Uniswap V3 pools and control over the protocol fees |
||||
interface IUniswapV3Factory { |
||||
/// @notice Emitted when the owner of the factory is changed |
||||
/// @param oldOwner The owner before the owner was changed |
||||
/// @param newOwner The owner after the owner was changed |
||||
event OwnerChanged(address indexed oldOwner, address indexed newOwner); |
||||
|
||||
/// @notice Emitted when a pool is created |
||||
/// @param token0 The first token of the pool by address sort order |
||||
/// @param token1 The second token of the pool by address sort order |
||||
/// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip |
||||
/// @param tickSpacing The minimum number of ticks between initialized ticks |
||||
/// @param pool The address of the created pool |
||||
event PoolCreated( |
||||
address indexed token0, |
||||
address indexed token1, |
||||
uint24 indexed fee, |
||||
int24 tickSpacing, |
||||
address pool |
||||
); |
||||
|
||||
/// @notice Emitted when a new fee amount is enabled for pool creation via the factory |
||||
/// @param fee The enabled fee, denominated in hundredths of a bip |
||||
/// @param tickSpacing The minimum number of ticks between initialized ticks for pools created with the given fee |
||||
event FeeAmountEnabled(uint24 indexed fee, int24 indexed tickSpacing); |
||||
|
||||
/// @notice Returns the current owner of the factory |
||||
/// @dev Can be changed by the current owner via setOwner |
||||
/// @return The address of the factory owner |
||||
function owner() external view returns (address); |
||||
|
||||
/// @notice Returns the tick spacing for a given fee amount, if enabled, or 0 if not enabled |
||||
/// @dev A fee amount can never be removed, so this value should be hard coded or cached in the calling context |
||||
/// @param fee The enabled fee, denominated in hundredths of a bip. Returns 0 in case of unenabled fee |
||||
/// @return The tick spacing |
||||
function feeAmountTickSpacing(uint24 fee) external view returns (int24); |
||||
|
||||
/// @notice Returns the pool address for a given pair of tokens and a fee, or address 0 if it does not exist |
||||
/// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order |
||||
/// @param tokenA The contract address of either token0 or token1 |
||||
/// @param tokenB The contract address of the other token |
||||
/// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip |
||||
/// @return pool The pool address |
||||
function getPool( |
||||
address tokenA, |
||||
address tokenB, |
||||
uint24 fee |
||||
) external view returns (address pool); |
||||
|
||||
/// @notice Creates a pool for the given two tokens and fee |
||||
/// @param tokenA One of the two tokens in the desired pool |
||||
/// @param tokenB The other of the two tokens in the desired pool |
||||
/// @param fee The desired fee for the pool |
||||
/// @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0. tickSpacing is retrieved |
||||
/// from the fee. The call will revert if the pool already exists, the fee is invalid, or the token arguments |
||||
/// are invalid. |
||||
/// @return pool The address of the newly created pool |
||||
function createPool( |
||||
address tokenA, |
||||
address tokenB, |
||||
uint24 fee |
||||
) external returns (address pool); |
||||
|
||||
/// @notice Updates the owner of the factory |
||||
/// @dev Must be called by the current owner |
||||
/// @param _owner The new owner of the factory |
||||
function setOwner(address _owner) external; |
||||
|
||||
/// @notice Enables a fee amount with the given tickSpacing |
||||
/// @dev Fee amounts may never be removed once enabled |
||||
/// @param fee The fee amount to enable, denominated in hundredths of a bip (i.e. 1e-6) |
||||
/// @param tickSpacing The spacing between ticks to be enforced for all pools created with the given fee amount |
||||
function enableFeeAmount(uint24 fee, int24 tickSpacing) external; |
||||
} |
@ -0,0 +1,24 @@ |
||||
// SPDX-License-Identifier: GPL-2.0-or-later |
||||
pragma solidity >=0.5.0; |
||||
|
||||
import './pool/IUniswapV3PoolImmutables.sol'; |
||||
import './pool/IUniswapV3PoolState.sol'; |
||||
import './pool/IUniswapV3PoolDerivedState.sol'; |
||||
import './pool/IUniswapV3PoolActions.sol'; |
||||
import './pool/IUniswapV3PoolOwnerActions.sol'; |
||||
import './pool/IUniswapV3PoolEvents.sol'; |
||||
|
||||
/// @title The interface for a Uniswap V3 Pool |
||||
/// @notice A Uniswap pool facilitates swapping and automated market making between any two assets that strictly conform |
||||
/// to the ERC20 specification |
||||
/// @dev The pool interface is broken up into many smaller pieces |
||||
interface IUniswapV3Pool is |
||||
IUniswapV3PoolImmutables, |
||||
IUniswapV3PoolState, |
||||
IUniswapV3PoolDerivedState, |
||||
IUniswapV3PoolActions, |
||||
IUniswapV3PoolOwnerActions, |
||||
IUniswapV3PoolEvents |
||||
{ |
||||
|
||||
} |
@ -0,0 +1,26 @@ |
||||
// SPDX-License-Identifier: GPL-2.0-or-later |
||||
pragma solidity >=0.5.0; |
||||
|
||||
/// @title An interface for a contract that is capable of deploying Uniswap V3 Pools |
||||
/// @notice A contract that constructs a pool must implement this to pass arguments to the pool |
||||
/// @dev This is used to avoid having constructor arguments in the pool contract, which results in the init code hash |
||||
/// of the pool being constant allowing the CREATE2 address of the pool to be cheaply computed on-chain |
||||
interface IUniswapV3PoolDeployer { |
||||
/// @notice Get the parameters to be used in constructing the pool, set transiently during pool creation. |
||||
/// @dev Called by the pool constructor to fetch the parameters of the pool |
||||
/// Returns factory The factory address |
||||
/// Returns token0 The first token of the pool by address sort order |
||||
/// Returns token1 The second token of the pool by address sort order |
||||
/// Returns fee The fee collected upon every swap in the pool, denominated in hundredths of a bip |
||||
/// Returns tickSpacing The minimum number of ticks between initialized ticks |
||||
function parameters() |
||||
external |
||||
view |
||||
returns ( |
||||
address factory, |
||||
address token0, |
||||
address token1, |
||||
uint24 fee, |
||||
int24 tickSpacing |
||||
); |
||||
} |
@ -0,0 +1,18 @@ |
||||
// SPDX-License-Identifier: GPL-2.0-or-later |
||||
pragma solidity >=0.5.0; |
||||
|
||||
/// @title Callback for IUniswapV3PoolActions#flash |
||||
/// @notice Any contract that calls IUniswapV3PoolActions#flash must implement this interface |
||||
interface IUniswapV3FlashCallback { |
||||
/// @notice Called to `msg.sender` after transferring to the recipient from IUniswapV3Pool#flash. |
||||
/// @dev In the implementation you must repay the pool the tokens sent by flash plus the computed fee amounts. |
||||
/// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory. |
||||
/// @param fee0 The fee amount in token0 due to the pool by the end of the flash |
||||
/// @param fee1 The fee amount in token1 due to the pool by the end of the flash |
||||
/// @param data Any data passed through by the caller via the IUniswapV3PoolActions#flash call |
||||
function uniswapV3FlashCallback( |
||||
uint256 fee0, |
||||
uint256 fee1, |
||||
bytes calldata data |
||||
) external; |
||||
} |
@ -0,0 +1,18 @@ |
||||
// SPDX-License-Identifier: GPL-2.0-or-later |
||||
pragma solidity >=0.5.0; |
||||
|
||||
/// @title Callback for IUniswapV3PoolActions#mint |
||||
/// @notice Any contract that calls IUniswapV3PoolActions#mint must implement this interface |
||||
interface IUniswapV3MintCallback { |
||||
/// @notice Called to `msg.sender` after minting liquidity to a position from IUniswapV3Pool#mint. |
||||
/// @dev In the implementation you must pay the pool tokens owed for the minted liquidity. |
||||
/// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory. |
||||
/// @param amount0Owed The amount of token0 due to the pool for the minted liquidity |
||||
/// @param amount1Owed The amount of token1 due to the pool for the minted liquidity |
||||
/// @param data Any data passed through by the caller via the IUniswapV3PoolActions#mint call |
||||
function uniswapV3MintCallback( |
||||
uint256 amount0Owed, |
||||
uint256 amount1Owed, |
||||
bytes calldata data |
||||
) external; |
||||
} |
@ -0,0 +1,21 @@ |
||||
// SPDX-License-Identifier: GPL-2.0-or-later |
||||
pragma solidity >=0.5.0; |
||||
|
||||
/// @title Callback for IUniswapV3PoolActions#swap |
||||
/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface |
||||
interface IUniswapV3SwapCallback { |
||||
/// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap. |
||||
/// @dev In the implementation you must pay the pool tokens owed for the swap. |
||||
/// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory. |
||||
/// amount0Delta and amount1Delta can both be 0 if no tokens were swapped. |
||||
/// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by |
||||
/// the end of the swap. If positive, the callback must send that amount of token0 to the pool. |
||||
/// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by |
||||
/// the end of the swap. If positive, the callback must send that amount of token1 to the pool. |
||||
/// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call |
||||
function uniswapV3SwapCallback( |
||||
int256 amount0Delta, |
||||
int256 amount1Delta, |
||||
bytes calldata data |
||||
) external; |
||||
} |
@ -0,0 +1,103 @@ |
||||
// SPDX-License-Identifier: GPL-2.0-or-later |
||||
pragma solidity >=0.5.0; |
||||
|
||||
/// @title Permissionless pool actions |
||||
/// @notice Contains pool methods that can be called by anyone |
||||
interface IUniswapV3PoolActions { |
||||
/// @notice Sets the initial price for the pool |
||||
/// @dev Price is represented as a sqrt(amountToken1/amountToken0) Q64.96 value |
||||
/// @param sqrtPriceX96 the initial sqrt price of the pool as a Q64.96 |
||||
function initialize(uint160 sqrtPriceX96) external; |
||||
|
||||
/// @notice Adds liquidity for the given recipient/tickLower/tickUpper position |
||||
/// @dev The caller of this method receives a callback in the form of IUniswapV3MintCallback#uniswapV3MintCallback |
||||
/// in which they must pay any token0 or token1 owed for the liquidity. The amount of token0/token1 due depends |
||||
/// on tickLower, tickUpper, the amount of liquidity, and the current price. |
||||
/// @param recipient The address for which the liquidity will be created |
||||
/// @param tickLower The lower tick of the position in which to add liquidity |
||||
/// @param tickUpper The upper tick of the position in which to add liquidity |
||||
/// @param amount The amount of liquidity to mint |
||||
/// @param data Any data that should be passed through to the callback |
||||
/// @return amount0 The amount of token0 that was paid to mint the given amount of liquidity. Matches the value in the callback |
||||
/// @return amount1 The amount of token1 that was paid to mint the given amount of liquidity. Matches the value in the callback |
||||
function mint( |
||||
address recipient, |
||||
int24 tickLower, |
||||
int24 tickUpper, |
||||
uint128 amount, |
||||
bytes calldata data |
||||
) external returns (uint256 amount0, uint256 amount1); |
||||
|
||||
/// @notice Collects tokens owed to a position |
||||
/// @dev Does not recompute fees earned, which must be done either via mint or burn of any amount of liquidity. |
||||
/// Collect must be called by the position owner. To withdraw only token0 or only token1, amount0Requested or |
||||
/// amount1Requested may be set to zero. To withdraw all tokens owed, caller may pass any value greater than the |
||||
/// actual tokens owed, e.g. type(uint128).max. Tokens owed may be from accumulated swap fees or burned liquidity. |
||||
/// @param recipient The address which should receive the fees collected |
||||
/// @param tickLower The lower tick of the position for which to collect fees |
||||
/// @param tickUpper The upper tick of the position for which to collect fees |
||||
/// @param amount0Requested How much token0 should be withdrawn from the fees owed |
||||
/// @param amount1Requested How much token1 should be withdrawn from the fees owed |
||||
/// @return amount0 The amount of fees collected in token0 |
||||
/// @return amount1 The amount of fees collected in token1 |
||||
function collect( |
||||
address recipient, |
||||
int24 tickLower, |
||||
int24 tickUpper, |
||||
uint128 amount0Requested, |
||||
uint128 amount1Requested |
||||
) external returns (uint128 amount0, uint128 amount1); |
||||
|
||||
/// @notice Burn liquidity from the sender and account tokens owed for the liquidity to the position |
||||
/// @dev Can be used to trigger a recalculation of fees owed to a position by calling with an amount of 0 |
||||
/// @dev Fees must be collected separately via a call to #collect |
||||
/// @param tickLower The lower tick of the position for which to burn liquidity |
||||
/// @param tickUpper The upper tick of the position for which to burn liquidity |
||||
/// @param amount How much liquidity to burn |
||||
/// @return amount0 The amount of token0 sent to the recipient |
||||
/// @return amount1 The amount of token1 sent to the recipient |
||||
function burn( |
||||
int24 tickLower, |
||||
int24 tickUpper, |
||||
uint128 amount |
||||
) external returns (uint256 amount0, uint256 amount1); |
||||
|
||||
/// @notice Swap token0 for token1, or token1 for token0 |
||||
/// @dev The caller of this method receives a callback in the form of IUniswapV3SwapCallback#uniswapV3SwapCallback |
||||
/// @param recipient The address to receive the output of the swap |
||||
/// @param zeroForOne The direction of the swap, true for token0 to token1, false for token1 to token0 |
||||
/// @param amountSpecified The amount of the swap, which implicitly configures the swap as exact input (positive), or exact output (negative) |
||||
/// @param sqrtPriceLimitX96 The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this |
||||
/// value after the swap. If one for zero, the price cannot be greater than this value after the swap |
||||
/// @param data Any data to be passed through to the callback |
||||
/// @return amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive |
||||
/// @return amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positive |
||||
function swap( |
||||
address recipient, |
||||
bool zeroForOne, |
||||
int256 amountSpecified, |
||||
uint160 sqrtPriceLimitX96, |
||||
bytes calldata data |
||||
) external returns (int256 amount0, int256 amount1); |
||||
|
||||
/// @notice Receive token0 and/or token1 and pay it back, plus a fee, in the callback |
||||
/// @dev The caller of this method receives a callback in the form of IUniswapV3FlashCallback#uniswapV3FlashCallback |
||||
/// @dev Can be used to donate underlying tokens pro-rata to currently in-range liquidity providers by calling |
||||
/// with 0 amount{0,1} and sending the donation amount(s) from the callback |
||||
/// @param recipient The address which will receive the token0 and token1 amounts |
||||
/// @param amount0 The amount of token0 to send |
||||
/// @param amount1 The amount of token1 to send |
||||
/// @param data Any data to be passed through to the callback |
||||
function flash( |
||||
address recipient, |
||||
uint256 amount0, |
||||
uint256 amount1, |
||||
bytes calldata data |
||||
) external; |
||||
|
||||
/// @notice Increase the maximum number of price and liquidity observations that this pool will store |
||||
/// @dev This method is no-op if the pool already has an observationCardinalityNext greater than or equal to |
||||
/// the input observationCardinalityNext. |
||||
/// @param observationCardinalityNext The desired minimum number of observations for the pool to store |
||||
function increaseObservationCardinalityNext(uint16 observationCardinalityNext) external; |
||||
} |
@ -0,0 +1,40 @@ |
||||
// SPDX-License-Identifier: GPL-2.0-or-later |
||||
pragma solidity >=0.5.0; |
||||
|
||||
/// @title Pool state that is not stored |
||||
/// @notice Contains view functions to provide information about the pool that is computed rather than stored on the |
||||
/// blockchain. The functions here may have variable gas costs. |
||||
interface IUniswapV3PoolDerivedState { |
||||
/// @notice Returns the cumulative tick and liquidity as of each timestamp `secondsAgo` from the current block timestamp |
||||
/// @dev To get a time weighted average tick or liquidity-in-range, you must call this with two values, one representing |
||||
/// the beginning of the period and another for the end of the period. E.g., to get the last hour time-weighted average tick, |
||||
/// you must call it with secondsAgos = [3600, 0]. |
||||
/// @dev The time weighted average tick represents the geometric time weighted average price of the pool, in |
||||
/// log base sqrt(1.0001) of token1 / token0. The TickMath library can be used to go from a tick value to a ratio. |
||||
/// @param secondsAgos From how long ago each cumulative tick and liquidity value should be returned |
||||
/// @return tickCumulatives Cumulative tick values as of each `secondsAgos` from the current block timestamp |
||||
/// @return secondsPerLiquidityCumulativeX128s Cumulative seconds per liquidity-in-range value as of each `secondsAgos` from the current block |
||||
/// timestamp |
||||
function observe(uint32[] calldata secondsAgos) |
||||
external |
||||
view |
||||
returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s); |
||||
|
||||
/// @notice Returns a snapshot of the tick cumulative, seconds per liquidity and seconds inside a tick range |
||||
/// @dev Snapshots must only be compared to other snapshots, taken over a period for which a position existed. |
||||
/// I.e., snapshots cannot be compared if a position is not held for the entire period between when the first |
||||
/// snapshot is taken and the second snapshot is taken. |
||||
/// @param tickLower The lower tick of the range |
||||
/// @param tickUpper The upper tick of the range |
||||
/// @return tickCumulativeInside The snapshot of the tick accumulator for the range |
||||
/// @return secondsPerLiquidityInsideX128 The snapshot of seconds per liquidity for the range |
||||
/// @return secondsInside The snapshot of seconds per liquidity for the range |
||||
function snapshotCumulativesInside(int24 tickLower, int24 tickUpper) |
||||
external |
||||
view |
||||
returns ( |
||||
int56 tickCumulativeInside, |
||||
uint160 secondsPerLiquidityInsideX128, |
||||
uint32 secondsInside |
||||
); |
||||
} |
@ -0,0 +1,121 @@ |
||||
// SPDX-License-Identifier: GPL-2.0-or-later |
||||
pragma solidity >=0.5.0; |
||||
|
||||
/// @title Events emitted by a pool |
||||
/// @notice Contains all events emitted by the pool |
||||
interface IUniswapV3PoolEvents { |
||||
/// @notice Emitted exactly once by a pool when #initialize is first called on the pool |
||||
/// @dev Mint/Burn/Swap cannot be emitted by the pool before Initialize |
||||
/// @param sqrtPriceX96 The initial sqrt price of the pool, as a Q64.96 |
||||
/// @param tick The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool |
||||
event Initialize(uint160 sqrtPriceX96, int24 tick); |
||||
|
||||
/// @notice Emitted when liquidity is minted for a given position |
||||
/// @param sender The address that minted the liquidity |
||||
/// @param owner The owner of the position and recipient of any minted liquidity |
||||
/// @param tickLower The lower tick of the position |
||||
/// @param tickUpper The upper tick of the position |
||||
/// @param amount The amount of liquidity minted to the position range |
||||
/// @param amount0 How much token0 was required for the minted liquidity |
||||
/// @param amount1 How much token1 was required for the minted liquidity |
||||
event Mint( |
||||
address sender, |
||||
address indexed owner, |
||||
int24 indexed tickLower, |
||||
int24 indexed tickUpper, |
||||
uint128 amount, |
||||
uint256 amount0, |
||||
uint256 amount1 |
||||
); |
||||
|
||||
/// @notice Emitted when fees are collected by the owner of a position |
||||
/// @dev Collect events may be emitted with zero amount0 and amount1 when the caller chooses not to collect fees |
||||
/// @param owner The owner of the position for which fees are collected |
||||
/// @param tickLower The lower tick of the position |
||||
/// @param tickUpper The upper tick of the position |
||||
/// @param amount0 The amount of token0 fees collected |
||||
/// @param amount1 The amount of token1 fees collected |
||||
event Collect( |
||||
address indexed owner, |
||||
address recipient, |
||||
int24 indexed tickLower, |
||||
int24 indexed tickUpper, |
||||
uint128 amount0, |
||||
uint128 amount1 |
||||
); |
||||
|
||||
/// @notice Emitted when a position's liquidity is removed |
||||
/// @dev Does not withdraw any fees earned by the liquidity position, which must be withdrawn via #collect |
||||
/// @param owner The owner of the position for which liquidity is removed |
||||
/// @param tickLower The lower tick of the position |
||||
/// @param tickUpper The upper tick of the position |
||||
/// @param amount The amount of liquidity to remove |
||||
/// @param amount0 The amount of token0 withdrawn |
||||
/// @param amount1 The amount of token1 withdrawn |
||||
event Burn( |
||||
address indexed owner, |
||||
int24 indexed tickLower, |
||||
int24 indexed tickUpper, |
||||
uint128 amount, |
||||
uint256 amount0, |
||||
uint256 amount1 |
||||
); |
||||
|
||||
/// @notice Emitted by the pool for any swaps between token0 and token1 |
||||
/// @param sender The address that initiated the swap call, and that received the callback |
||||
/// @param recipient The address that received the output of the swap |
||||
/// @param amount0 The delta of the token0 balance of the pool |
||||
/// @param amount1 The delta of the token1 balance of the pool |
||||
/// @param sqrtPriceX96 The sqrt(price) of the pool after the swap, as a Q64.96 |
||||
/// @param liquidity The liquidity of the pool after the swap |
||||
/// @param tick The log base 1.0001 of price of the pool after the swap |
||||
event Swap( |
||||
address indexed sender, |
||||
address indexed recipient, |
||||
int256 amount0, |
||||
int256 amount1, |
||||
uint160 sqrtPriceX96, |
||||
uint128 liquidity, |
||||
int24 tick |
||||
); |
||||
|
||||
/// @notice Emitted by the pool for any flashes of token0/token1 |
||||
/// @param sender The address that initiated the swap call, and that received the callback |
||||
/// @param recipient The address that received the tokens from flash |
||||
/// @param amount0 The amount of token0 that was flashed |
||||
/// @param amount1 The amount of token1 that was flashed |
||||
/// @param paid0 The amount of token0 paid for the flash, which can exceed the amount0 plus the fee |
||||
/// @param paid1 The amount of token1 paid for the flash, which can exceed the amount1 plus the fee |
||||
event Flash( |
||||
address indexed sender, |
||||
address indexed recipient, |
||||
uint256 amount0, |
||||
uint256 amount1, |
||||
uint256 paid0, |
||||
uint256 paid1 |
||||
); |
||||
|
||||
/// @notice Emitted by the pool for increases to the number of observations that can be stored |
||||
/// @dev observationCardinalityNext is not the observation cardinality until an observation is written at the index |
||||
/// just before a mint/swap/burn. |
||||
/// @param observationCardinalityNextOld The previous value of the next observation cardinality |
||||
/// @param observationCardinalityNextNew The updated value of the next observation cardinality |
||||
event IncreaseObservationCardinalityNext( |
||||
uint16 observationCardinalityNextOld, |
||||
uint16 observationCardinalityNextNew |
||||
); |
||||
|
||||
/// @notice Emitted when the protocol fee is changed by the pool |
||||
/// @param feeProtocol0Old The previous value of the token0 protocol fee |
||||
/// @param feeProtocol1Old The previous value of the token1 protocol fee |
||||
/// @param feeProtocol0New The updated value of the token0 protocol fee |
||||
/// @param feeProtocol1New The updated value of the token1 protocol fee |
||||
event SetFeeProtocol(uint8 feeProtocol0Old, uint8 feeProtocol1Old, uint8 feeProtocol0New, uint8 feeProtocol1New); |
||||
|
||||
/// @notice Emitted when the collected protocol fees are withdrawn by the factory owner |
||||
/// @param sender The address that collects the protocol fees |
||||
/// @param recipient The address that receives the collected protocol fees |
||||
/// @param amount0 The amount of token0 protocol fees that is withdrawn |
||||
/// @param amount0 The amount of token1 protocol fees that is withdrawn |
||||
event CollectProtocol(address indexed sender, address indexed recipient, uint128 amount0, uint128 amount1); |
||||
} |
@ -0,0 +1,35 @@ |
||||
// SPDX-License-Identifier: GPL-2.0-or-later |
||||
pragma solidity >=0.5.0; |
||||
|
||||
/// @title Pool state that never changes |
||||
/// @notice These parameters are fixed for a pool forever, i.e., the methods will always return the same values |
||||
interface IUniswapV3PoolImmutables { |
||||
/// @notice The contract that deployed the pool, which must adhere to the IUniswapV3Factory interface |
||||
/// @return The contract address |
||||
function factory() external view returns (address); |
||||
|
||||
/// @notice The first of the two tokens of the pool, sorted by address |
||||
/// @return The token contract address |
||||
function token0() external view returns (address); |
||||
|
||||
/// @notice The second of the two tokens of the pool, sorted by address |
||||
/// @return The token contract address |
||||
function token1() external view returns (address); |
||||
|
||||
/// @notice The pool's fee in hundredths of a bip, i.e. 1e-6 |
||||
/// @return The fee |
||||
function fee() external view returns (uint24); |
||||
|
||||
/// @notice The pool tick spacing |
||||
/// @dev Ticks can only be used at multiples of this value, minimum of 1 and always positive |
||||
/// e.g.: a tickSpacing of 3 means ticks can be initialized every 3rd tick, i.e., ..., -6, -3, 0, 3, 6, ... |
||||
/// This value is an int24 to avoid casting even though it is always positive. |
||||
/// @return The tick spacing |
||||
function tickSpacing() external view returns (int24); |
||||
|
||||
/// @notice The maximum amount of position liquidity that can use any tick in the range |
||||
/// @dev This parameter is enforced per tick to prevent liquidity from overflowing a uint128 at any point, and |
||||
/// also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool |
||||
/// @return The max amount of liquidity per tick |
||||
function maxLiquidityPerTick() external view returns (uint128); |
||||
} |
@ -0,0 +1,23 @@ |
||||
// SPDX-License-Identifier: GPL-2.0-or-later |
||||
pragma solidity >=0.5.0; |
||||
|
||||
/// @title Permissioned pool actions |
||||
/// @notice Contains pool methods that may only be called by the factory owner |
||||
interface IUniswapV3PoolOwnerActions { |
||||
/// @notice Set the denominator of the protocol's % share of the fees |
||||
/// @param feeProtocol0 new protocol fee for token0 of the pool |
||||
/// @param feeProtocol1 new protocol fee for token1 of the pool |
||||
function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external; |
||||
|
||||
/// @notice Collect the protocol fee accrued to the pool |
||||
/// @param recipient The address to which collected protocol fees should be sent |
||||
/// @param amount0Requested The maximum amount of token0 to send, can be 0 to collect fees in only token1 |
||||
/// @param amount1Requested The maximum amount of token1 to send, can be 0 to collect fees in only token0 |
||||
/// @return amount0 The protocol fee collected in token0 |
||||
/// @return amount1 The protocol fee collected in token1 |
||||
function collectProtocol( |
||||
address recipient, |
||||
uint128 amount0Requested, |
||||
uint128 amount1Requested |
||||
) external returns (uint128 amount0, uint128 amount1); |
||||
} |
@ -0,0 +1,116 @@ |
||||
// SPDX-License-Identifier: GPL-2.0-or-later |
||||
pragma solidity >=0.5.0; |
||||
|
||||
/// @title Pool state that can change |
||||
/// @notice These methods compose the pool's state, and can change with any frequency including multiple times |
||||
/// per transaction |
||||
interface IUniswapV3PoolState { |
||||
/// @notice The 0th storage slot in the pool stores many values, and is exposed as a single method to save gas |
||||
/// when accessed externally. |
||||
/// @return sqrtPriceX96 The current price of the pool as a sqrt(token1/token0) Q64.96 value |
||||
/// tick The current tick of the pool, i.e. according to the last tick transition that was run. |
||||
/// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the price is on a tick |
||||
/// boundary. |
||||
/// observationIndex The index of the last oracle observation that was written, |
||||
/// observationCardinality The current maximum number of observations stored in the pool, |
||||
/// observationCardinalityNext The next maximum number of observations, to be updated when the observation. |
||||
/// feeProtocol The protocol fee for both tokens of the pool. |
||||
/// Encoded as two 4 bit values, where the protocol fee of token1 is shifted 4 bits and the protocol fee of token0 |
||||
/// is the lower 4 bits. Used as the denominator of a fraction of the swap fee, e.g. 4 means 1/4th of the swap fee. |
||||
/// unlocked Whether the pool is currently locked to reentrancy |
||||
function slot0() |
||||
external |
||||
view |
||||
returns ( |
||||
uint160 sqrtPriceX96, |
||||
int24 tick, |
||||
uint16 observationIndex, |
||||
uint16 observationCardinality, |
||||
uint16 observationCardinalityNext, |
||||
uint8 feeProtocol, |
||||
bool unlocked |
||||
); |
||||
|
||||
/// @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the pool |
||||
/// @dev This value can overflow the uint256 |
||||
function feeGrowthGlobal0X128() external view returns (uint256); |
||||
|
||||
/// @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the pool |
||||
/// @dev This value can overflow the uint256 |
||||
function feeGrowthGlobal1X128() external view returns (uint256); |
||||
|
||||
/// @notice The amounts of token0 and token1 that are owed to the protocol |
||||
/// @dev Protocol fees will never exceed uint128 max in either token |
||||
function protocolFees() external view returns (uint128 token0, uint128 token1); |
||||
|
||||
/// @notice The currently in range liquidity available to the pool |
||||
/// @dev This value has no relationship to the total liquidity across all ticks |
||||
function liquidity() external view returns (uint128); |
||||
|
||||
/// @notice Look up information about a specific tick in the pool |
||||
/// @param tick The tick to look up |
||||
/// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or |
||||
/// tick upper, |
||||
/// liquidityNet how much liquidity changes when the pool price crosses the tick, |
||||
/// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0, |
||||
/// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1, |
||||
/// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick |
||||
/// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from the current tick, |
||||
/// secondsOutside the seconds spent on the other side of the tick from the current tick, |
||||
/// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise equal to false. |
||||
/// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0. |
||||
/// In addition, these values are only relative and must be used only in comparison to previous snapshots for |
||||
/// a specific position. |
||||
function ticks(int24 tick) |
||||
external |
||||
view |
||||
returns ( |
||||
uint128 liquidityGross, |
||||
int128 liquidityNet, |
||||
uint256 feeGrowthOutside0X128, |
||||
uint256 feeGrowthOutside1X128, |
||||
int56 tickCumulativeOutside, |
||||
uint160 secondsPerLiquidityOutsideX128, |
||||
uint32 secondsOutside, |
||||
bool initialized |
||||
); |
||||
|
||||
/// @notice Returns 256 packed tick initialized boolean values. See TickBitmap for more information |
||||
function tickBitmap(int16 wordPosition) external view returns (uint256); |
||||
|
||||
/// @notice Returns the information about a position by the position's key |
||||
/// @param key The position's key is a hash of a preimage composed by the owner, tickLower and tickUpper |
||||
/// @return _liquidity The amount of liquidity in the position, |
||||
/// Returns feeGrowthInside0LastX128 fee growth of token0 inside the tick range as of the last mint/burn/poke, |
||||
/// Returns feeGrowthInside1LastX128 fee growth of token1 inside the tick range as of the last mint/burn/poke, |
||||
/// Returns tokensOwed0 the computed amount of token0 owed to the position as of the last mint/burn/poke, |
||||
/// Returns tokensOwed1 the computed amount of token1 owed to the position as of the last mint/burn/poke |
||||
function positions(bytes32 key) |
||||
external |
||||
view |
||||
returns ( |
||||
uint128 _liquidity, |
||||
uint256 feeGrowthInside0LastX128, |
||||
uint256 feeGrowthInside1LastX128, |
||||
uint128 tokensOwed0, |
||||
uint128 tokensOwed1 |
||||
); |
||||
|
||||
/// @notice Returns data about a specific observation index |
||||
/// @param index The element of the observations array to fetch |
||||
/// @dev You most likely want to use #observe() instead of this method to get an observation as of some amount of time |
||||
/// ago, rather than at a specific index in the array. |
||||
/// @return blockTimestamp The timestamp of the observation, |
||||
/// Returns tickCumulative the tick multiplied by seconds elapsed for the life of the pool as of the observation timestamp, |
||||
/// Returns secondsPerLiquidityCumulativeX128 the seconds per in range liquidity for the life of the pool as of the observation timestamp, |
||||
/// Returns initialized whether the observation has been initialized and the values are safe to use |
||||
function observations(uint256 index) |
||||
external |
||||
view |
||||
returns ( |
||||
uint32 blockTimestamp, |
||||
int56 tickCumulative, |
||||
uint160 secondsPerLiquidityCumulativeX128, |
||||
bool initialized |
||||
); |
||||
} |
@ -0,0 +1,94 @@ |
||||
// SPDX-License-Identifier: GPL-2.0-or-later |
||||
pragma solidity >=0.5.0; |
||||
|
||||
/// @title BitMath |
||||
/// @dev This library provides functionality for computing bit properties of an unsigned integer |
||||
library BitMath { |
||||
/// @notice Returns the index of the most significant bit of the number, |
||||
/// where the least significant bit is at index 0 and the most significant bit is at index 255 |
||||
/// @dev The function satisfies the property: |
||||
/// x >= 2**mostSignificantBit(x) and x < 2**(mostSignificantBit(x)+1) |
||||
/// @param x the value for which to compute the most significant bit, must be greater than 0 |
||||
/// @return r the index of the most significant bit |
||||
function mostSignificantBit(uint256 x) internal pure returns (uint8 r) { |
||||
require(x > 0); |
||||
|
||||
if (x >= 0x100000000000000000000000000000000) { |
||||
x >>= 128; |
||||
r += 128; |
||||
} |
||||
if (x >= 0x10000000000000000) { |
||||
x >>= 64; |
||||
r += 64; |
||||
} |
||||
if (x >= 0x100000000) { |
||||
x >>= 32; |
||||
r += 32; |
||||
} |
||||
if (x >= 0x10000) { |
||||
x >>= 16; |
||||
r += 16; |
||||
} |
||||
if (x >= 0x100) { |
||||
x >>= 8; |
||||
r += 8; |
||||
} |
||||
if (x >= 0x10) { |
||||
x >>= 4; |
||||
r += 4; |
||||
} |
||||
if (x >= 0x4) { |
||||
x >>= 2; |
||||
r += 2; |
||||
} |
||||
if (x >= 0x2) r += 1; |
||||
} |
||||
|
||||
/// @notice Returns the index of the least significant bit of the number, |
||||
/// where the least significant bit is at index 0 and the most significant bit is at index 255 |
||||
/// @dev The function satisfies the property: |
||||
/// (x & 2**leastSignificantBit(x)) != 0 and (x & (2**(leastSignificantBit(x)) - 1)) == 0) |
||||
/// @param x the value for which to compute the least significant bit, must be greater than 0 |
||||
/// @return r the index of the least significant bit |
||||
function leastSignificantBit(uint256 x) internal pure returns (uint8 r) { |
||||
require(x > 0); |
||||
|
||||
r = 255; |
||||
if (x & type(uint128).max > 0) { |
||||
r -= 128; |
||||
} else { |
||||
x >>= 128; |
||||
} |
||||
if (x & type(uint64).max > 0) { |
||||
r -= 64; |
||||
} else { |
||||
x >>= 64; |
||||
} |
||||
if (x & type(uint32).max > 0) { |
||||
r -= 32; |
||||
} else { |
||||
x >>= 32; |
||||
} |
||||
if (x & type(uint16).max > 0) { |
||||
r -= 16; |
||||
} else { |
||||
x >>= 16; |
||||
} |
||||
if (x & type(uint8).max > 0) { |
||||
r -= 8; |
||||
} else { |
||||
x >>= 8; |
||||
} |
||||
if (x & 0xf > 0) { |
||||
r -= 4; |
||||
} else { |
||||
x >>= 4; |
||||
} |
||||
if (x & 0x3 > 0) { |
||||
r -= 2; |
||||
} else { |
||||
x >>= 2; |
||||
} |
||||
if (x & 0x1 > 0) r -= 1; |
||||
} |
||||
} |
@ -0,0 +1,8 @@ |
||||
// SPDX-License-Identifier: GPL-2.0-or-later |
||||
pragma solidity >=0.4.0; |
||||
|
||||
/// @title FixedPoint128 |
||||
/// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format) |
||||
library FixedPoint128 { |
||||
uint256 internal constant Q128 = 0x100000000000000000000000000000000; |
||||
} |
@ -0,0 +1,10 @@ |
||||
// SPDX-License-Identifier: GPL-2.0-or-later |
||||
pragma solidity >=0.4.0; |
||||
|
||||
/// @title FixedPoint96 |
||||
/// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format) |
||||
/// @dev Used in SqrtPriceMath.sol |
||||
library FixedPoint96 { |
||||
uint8 internal constant RESOLUTION = 96; |
||||
uint256 internal constant Q96 = 0x1000000000000000000000000; |
||||
} |
@ -0,0 +1,124 @@ |
||||
// SPDX-License-Identifier: MIT |
||||
pragma solidity >=0.4.0; |
||||
|
||||
/// @title Contains 512-bit math functions |
||||
/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision |
||||
/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits |
||||
library FullMath { |
||||
/// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 |
||||
/// @param a The multiplicand |
||||
/// @param b The multiplier |
||||
/// @param denominator The divisor |
||||
/// @return result The 256-bit result |
||||
/// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv |
||||
function mulDiv( |
||||
uint256 a, |
||||
uint256 b, |
||||
uint256 denominator |
||||
) internal pure returns (uint256 result) { |
||||
// 512-bit multiply [prod1 prod0] = a * b |
||||
// Compute the product mod 2**256 and mod 2**256 - 1 |
||||
// then use the Chinese Remainder Theorem to reconstruct |
||||
// the 512 bit result. The result is stored in two 256 |
||||
// variables such that product = prod1 * 2**256 + prod0 |
||||
uint256 prod0; // Least significant 256 bits of the product |
||||
uint256 prod1; // Most significant 256 bits of the product |
||||
assembly { |
||||
let mm := mulmod(a, b, not(0)) |
||||
prod0 := mul(a, b) |
||||
prod1 := sub(sub(mm, prod0), lt(mm, prod0)) |
||||
} |
||||
|
||||
// Handle non-overflow cases, 256 by 256 division |
||||
if (prod1 == 0) { |
||||
require(denominator > 0); |
||||
assembly { |
||||
result := div(prod0, denominator) |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
// Make sure the result is less than 2**256. |
||||
// Also prevents denominator == 0 |
||||
require(denominator > prod1); |
||||
|
||||
/////////////////////////////////////////////// |
||||
// 512 by 256 division. |
||||
/////////////////////////////////////////////// |
||||
|
||||
// Make division exact by subtracting the remainder from [prod1 prod0] |
||||
// Compute remainder using mulmod |
||||
uint256 remainder; |
||||
assembly { |
||||
remainder := mulmod(a, b, denominator) |
||||
} |
||||
// Subtract 256 bit number from 512 bit number |
||||
assembly { |
||||
prod1 := sub(prod1, gt(remainder, prod0)) |
||||
prod0 := sub(prod0, remainder) |
||||
} |
||||
|
||||
// Factor powers of two out of denominator |
||||
// Compute largest power of two divisor of denominator. |
||||
// Always >= 1. |
||||
uint256 twos = -denominator & denominator; |
||||
// Divide denominator by power of two |
||||
assembly { |
||||
denominator := div(denominator, twos) |
||||
} |
||||
|
||||
// Divide [prod1 prod0] by the factors of two |
||||
assembly { |
||||
prod0 := div(prod0, twos) |
||||
} |
||||
// Shift in bits from prod1 into prod0. For this we need |
||||
// to flip `twos` such that it is 2**256 / twos. |
||||
// If twos is zero, then it becomes one |
||||
assembly { |
||||
twos := add(div(sub(0, twos), twos), 1) |
||||
} |
||||
prod0 |= prod1 * twos; |
||||
|
||||
// Invert denominator mod 2**256 |
||||
// Now that denominator is an odd number, it has an inverse |
||||
// modulo 2**256 such that denominator * inv = 1 mod 2**256. |
||||
// Compute the inverse by starting with a seed that is correct |
||||
// correct for four bits. That is, denominator * inv = 1 mod 2**4 |
||||
uint256 inv = (3 * denominator) ^ 2; |
||||
// Now use Newton-Raphson iteration to improve the precision. |
||||
// Thanks to Hensel's lifting lemma, this also works in modular |
||||
// arithmetic, doubling the correct bits in each step. |
||||
inv *= 2 - denominator * inv; // inverse mod 2**8 |
||||
inv *= 2 - denominator * inv; // inverse mod 2**16 |
||||
inv *= 2 - denominator * inv; // inverse mod 2**32 |
||||
inv *= 2 - denominator * inv; // inverse mod 2**64 |
||||
inv *= 2 - denominator * inv; // inverse mod 2**128 |
||||
inv *= 2 - denominator * inv; // inverse mod 2**256 |
||||
|
||||
// Because the division is now exact we can divide by multiplying |
||||
// with the modular inverse of denominator. This will give us the |
||||
// correct result modulo 2**256. Since the precoditions guarantee |
||||
// that the outcome is less than 2**256, this is the final result. |
||||
// We don't need to compute the high bits of the result and prod1 |
||||
// is no longer required. |
||||
result = prod0 * inv; |
||||
return result; |
||||
} |
||||
|
||||
/// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 |
||||
/// @param a The multiplicand |
||||
/// @param b The multiplier |
||||
/// @param denominator The divisor |
||||
/// @return result The 256-bit result |
||||
function mulDivRoundingUp( |
||||
uint256 a, |
||||
uint256 b, |
||||
uint256 denominator |
||||
) internal pure returns (uint256 result) { |
||||
result = mulDiv(a, b, denominator); |
||||
if (mulmod(a, b, denominator) > 0) { |
||||
require(result < type(uint256).max); |
||||
result++; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,17 @@ |
||||
// SPDX-License-Identifier: GPL-2.0-or-later |
||||
pragma solidity >=0.5.0; |
||||
|
||||
/// @title Math library for liquidity |
||||
library LiquidityMath { |
||||
/// @notice Add a signed liquidity delta to liquidity and revert if it overflows or underflows |
||||
/// @param x The liquidity before change |
||||
/// @param y The delta by which liquidity should be changed |
||||
/// @return z The liquidity delta |
||||
function addDelta(uint128 x, int128 y) internal pure returns (uint128 z) { |
||||
if (y < 0) { |
||||
require((z = x - uint128(-y)) < x, 'LS'); |
||||
} else { |
||||
require((z = x + uint128(y)) >= x, 'LA'); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,46 @@ |
||||
// SPDX-License-Identifier: GPL-2.0-or-later |
||||
pragma solidity >=0.7.0; |
||||
|
||||
/// @title Optimized overflow and underflow safe math operations |
||||
/// @notice Contains methods for doing math operations that revert on overflow or underflow for minimal gas cost |
||||
library LowGasSafeMath { |
||||
/// @notice Returns x + y, reverts if sum overflows uint256 |
||||
/// @param x The augend |
||||
/// @param y The addend |
||||
/// @return z The sum of x and y |
||||
function add(uint256 x, uint256 y) internal pure returns (uint256 z) { |
||||
require((z = x + y) >= x); |
||||
} |
||||
|
||||
/// @notice Returns x - y, reverts if underflows |
||||
/// @param x The minuend |
||||
/// @param y The subtrahend |
||||
/// @return z The difference of x and y |
||||
function sub(uint256 x, uint256 y) internal pure returns (uint256 z) { |
||||
require((z = x - y) <= x); |
||||
} |
||||
|
||||
/// @notice Returns x * y, reverts if overflows |
||||
/// @param x The multiplicand |
||||
/// @param y The multiplier |
||||
/// @return z The product of x and y |
||||
function mul(uint256 x, uint256 y) internal pure returns (uint256 z) { |
||||
require(x == 0 || (z = x * y) / x == y); |
||||
} |
||||
|
||||
/// @notice Returns x + y, reverts if overflows or underflows |
||||
/// @param x The augend |
||||
/// @param y The addend |
||||
/// @return z The sum of x and y |
||||
function add(int256 x, int256 y) internal pure returns (int256 z) { |
||||
require((z = x + y) >= x == (y >= 0)); |
||||
} |
||||
|
||||
/// @notice Returns x - y, reverts if overflows or underflows |
||||
/// @param x The minuend |
||||
/// @param y The subtrahend |
||||
/// @return z The difference of x and y |
||||
function sub(int256 x, int256 y) internal pure returns (int256 z) { |
||||
require((z = x - y) <= x == (y >= 0)); |
||||
} |
||||
} |
@ -0,0 +1,325 @@ |
||||
// SPDX-License-Identifier: BUSL-1.1 |
||||
pragma solidity >=0.5.0; |
||||
|
||||
/// @title Oracle |
||||
/// @notice Provides price and liquidity data useful for a wide variety of system designs |
||||
/// @dev Instances of stored oracle data, "observations", are collected in the oracle array |
||||
/// Every pool is initialized with an oracle array length of 1. Anyone can pay the SSTOREs to increase the |
||||
/// maximum length of the oracle array. New slots will be added when the array is fully populated. |
||||
/// Observations are overwritten when the full length of the oracle array is populated. |
||||
/// The most recent observation is available, independent of the length of the oracle array, by passing 0 to observe() |
||||
library Oracle { |
||||
struct Observation { |
||||
// the block timestamp of the observation |
||||
uint32 blockTimestamp; |
||||
// the tick accumulator, i.e. tick * time elapsed since the pool was first initialized |
||||
int56 tickCumulative; |
||||
// the seconds per liquidity, i.e. seconds elapsed / max(1, liquidity) since the pool was first initialized |
||||
uint160 secondsPerLiquidityCumulativeX128; |
||||
// whether or not the observation is initialized |
||||
bool initialized; |
||||
} |
||||
|
||||
/// @notice Transforms a previous observation into a new observation, given the passage of time and the current tick and liquidity values |
||||
/// @dev blockTimestamp _must_ be chronologically equal to or greater than last.blockTimestamp, safe for 0 or 1 overflows |
||||
/// @param last The specified observation to be transformed |
||||
/// @param blockTimestamp The timestamp of the new observation |
||||
/// @param tick The active tick at the time of the new observation |
||||
/// @param liquidity The total in-range liquidity at the time of the new observation |
||||
/// @return Observation The newly populated observation |
||||
function transform( |
||||
Observation memory last, |
||||
uint32 blockTimestamp, |
||||
int24 tick, |
||||
uint128 liquidity |
||||
) private pure returns (Observation memory) { |
||||
uint32 delta = blockTimestamp - last.blockTimestamp; |
||||
return |
||||
Observation({ |
||||
blockTimestamp: blockTimestamp, |
||||
tickCumulative: last.tickCumulative + int56(tick) * delta, |
||||
secondsPerLiquidityCumulativeX128: last.secondsPerLiquidityCumulativeX128 + |
||||
((uint160(delta) << 128) / (liquidity > 0 ? liquidity : 1)), |
||||
initialized: true |
||||
}); |
||||
} |
||||
|
||||
/// @notice Initialize the oracle array by writing the first slot. Called once for the lifecycle of the observations array |
||||
/// @param self The stored oracle array |
||||
/// @param time The time of the oracle initialization, via block.timestamp truncated to uint32 |
||||
/// @return cardinality The number of populated elements in the oracle array |
||||
/// @return cardinalityNext The new length of the oracle array, independent of population |
||||
function initialize(Observation[65535] storage self, uint32 time) |
||||
internal |
||||
returns (uint16 cardinality, uint16 cardinalityNext) |
||||
{ |
||||
self[0] = Observation({ |
||||
blockTimestamp: time, |
||||
tickCumulative: 0, |
||||
secondsPerLiquidityCumulativeX128: 0, |
||||
initialized: true |
||||
}); |
||||
return (1, 1); |
||||
} |
||||
|
||||
/// @notice Writes an oracle observation to the array |
||||
/// @dev Writable at most once per block. Index represents the most recently written element. cardinality and index must be tracked externally. |
||||
/// If the index is at the end of the allowable array length (according to cardinality), and the next cardinality |
||||
/// is greater than the current one, cardinality may be increased. This restriction is created to preserve ordering. |
||||
/// @param self The stored oracle array |
||||
/// @param index The index of the observation that was most recently written to the observations array |
||||
/// @param blockTimestamp The timestamp of the new observation |
||||
/// @param tick The active tick at the time of the new observation |
||||
/// @param liquidity The total in-range liquidity at the time of the new observation |
||||
/// @param cardinality The number of populated elements in the oracle array |
||||
/// @param cardinalityNext The new length of the oracle array, independent of population |
||||
/// @return indexUpdated The new index of the most recently written element in the oracle array |
||||
/// @return cardinalityUpdated The new cardinality of the oracle array |
||||
function write( |
||||
Observation[65535] storage self, |
||||
uint16 index, |
||||
uint32 blockTimestamp, |
||||
int24 tick, |
||||
uint128 liquidity, |
||||
uint16 cardinality, |
||||
uint16 cardinalityNext |
||||
) internal returns (uint16 indexUpdated, uint16 cardinalityUpdated) { |
||||
Observation memory last = self[index]; |
||||
|
||||
// early return if we've already written an observation this block |
||||
if (last.blockTimestamp == blockTimestamp) return (index, cardinality); |
||||
|
||||
// if the conditions are right, we can bump the cardinality |
||||
if (cardinalityNext > cardinality && index == (cardinality - 1)) { |
||||
cardinalityUpdated = cardinalityNext; |
||||
} else { |
||||
cardinalityUpdated = cardinality; |
||||
} |
||||
|
||||
indexUpdated = (index + 1) % cardinalityUpdated; |
||||
self[indexUpdated] = transform(last, blockTimestamp, tick, liquidity); |
||||
} |
||||
|
||||
/// @notice Prepares the oracle array to store up to `next` observations |
||||
/// @param self The stored oracle array |
||||
/// @param current The current next cardinality of the oracle array |
||||
/// @param next The proposed next cardinality which will be populated in the oracle array |
||||
/// @return next The next cardinality which will be populated in the oracle array |
||||
function grow( |
||||
Observation[65535] storage self, |
||||
uint16 current, |
||||
uint16 next |
||||
) internal returns (uint16) { |
||||
require(current > 0, 'I'); |
||||
// no-op if the passed next value isn't greater than the current next value |
||||
if (next <= current) return current; |
||||
// store in each slot to prevent fresh SSTOREs in swaps |
||||
// this data will not be used because the initialized boolean is still false |
||||
for (uint16 i = current; i < next; i++) self[i].blockTimestamp = 1; |
||||
return next; |
||||
} |
||||
|
||||
/// @notice comparator for 32-bit timestamps |
||||
/// @dev safe for 0 or 1 overflows, a and b _must_ be chronologically before or equal to time |
||||
/// @param time A timestamp truncated to 32 bits |
||||
/// @param a A comparison timestamp from which to determine the relative position of `time` |
||||
/// @param b From which to determine the relative position of `time` |
||||
/// @return bool Whether `a` is chronologically <= `b` |
||||
function lte( |
||||
uint32 time, |
||||
uint32 a, |
||||
uint32 b |
||||
) private pure returns (bool) { |
||||
// if there hasn't been overflow, no need to adjust |
||||
if (a <= time && b <= time) return a <= b; |
||||
|
||||
uint256 aAdjusted = a > time ? a : a + 2**32; |
||||
uint256 bAdjusted = b > time ? b : b + 2**32; |
||||
|
||||
return aAdjusted <= bAdjusted; |
||||
} |
||||
|
||||
/// @notice Fetches the observations beforeOrAt and atOrAfter a target, i.e. where [beforeOrAt, atOrAfter] is satisfied. |
||||
/// The result may be the same observation, or adjacent observations. |
||||
/// @dev The answer must be contained in the array, used when the target is located within the stored observation |
||||
/// boundaries: older than the most recent observation and younger, or the same age as, the oldest observation |
||||
/// @param self The stored oracle array |
||||
/// @param time The current block.timestamp |
||||
/// @param target The timestamp at which the reserved observation should be for |
||||
/// @param index The index of the observation that was most recently written to the observations array |
||||
/// @param cardinality The number of populated elements in the oracle array |
||||
/// @return beforeOrAt The observation recorded before, or at, the target |
||||
/// @return atOrAfter The observation recorded at, or after, the target |
||||
function binarySearch( |
||||
Observation[65535] storage self, |
||||
uint32 time, |
||||
uint32 target, |
||||
uint16 index, |
||||
uint16 cardinality |
||||
) private view returns (Observation memory beforeOrAt, Observation memory atOrAfter) { |
||||
uint256 l = (index + 1) % cardinality; // oldest observation |
||||
uint256 r = l + cardinality - 1; // newest observation |
||||
uint256 i; |
||||
while (true) { |
||||
i = (l + r) / 2; |
||||
|
||||
beforeOrAt = self[i % cardinality]; |
||||
|
||||
// we've landed on an uninitialized tick, keep searching higher (more recently) |
||||
if (!beforeOrAt.initialized) { |
||||
l = i + 1; |
||||
continue; |
||||
} |
||||
|
||||
atOrAfter = self[(i + 1) % cardinality]; |
||||
|
||||
bool targetAtOrAfter = lte(time, beforeOrAt.blockTimestamp, target); |
||||
|
||||
// check if we've found the answer! |
||||
if (targetAtOrAfter && lte(time, target, atOrAfter.blockTimestamp)) break; |
||||
|
||||
if (!targetAtOrAfter) r = i - 1; |
||||
else l = i + 1; |
||||
} |
||||
} |
||||
|
||||
/// @notice Fetches the observations beforeOrAt and atOrAfter a given target, i.e. where [beforeOrAt, atOrAfter] is satisfied |
||||
/// @dev Assumes there is at least 1 initialized observation. |
||||
/// Used by observeSingle() to compute the counterfactual accumulator values as of a given block timestamp. |
||||
/// @param self The stored oracle array |
||||
/// @param time The current block.timestamp |
||||
/// @param target The timestamp at which the reserved observation should be for |
||||
/// @param tick The active tick at the time of the returned or simulated observation |
||||
/// @param index The index of the observation that was most recently written to the observations array |
||||
/// @param liquidity The total pool liquidity at the time of the call |
||||
/// @param cardinality The number of populated elements in the oracle array |
||||
/// @return beforeOrAt The observation which occurred at, or before, the given timestamp |
||||
/// @return atOrAfter The observation which occurred at, or after, the given timestamp |
||||
function getSurroundingObservations( |
||||
Observation[65535] storage self, |
||||
uint32 time, |
||||
uint32 target, |
||||
int24 tick, |
||||
uint16 index, |
||||
uint128 liquidity, |
||||
uint16 cardinality |
||||
) private view returns (Observation memory beforeOrAt, Observation memory atOrAfter) { |
||||
// optimistically set before to the newest observation |
||||
beforeOrAt = self[index]; |
||||
|
||||
// if the target is chronologically at or after the newest observation, we can early return |
||||
if (lte(time, beforeOrAt.blockTimestamp, target)) { |
||||
if (beforeOrAt.blockTimestamp == target) { |
||||
// if newest observation equals target, we're in the same block, so we can ignore atOrAfter |
||||
return (beforeOrAt, atOrAfter); |
||||
} else { |
||||
// otherwise, we need to transform |
||||
return (beforeOrAt, transform(beforeOrAt, target, tick, liquidity)); |
||||
} |
||||
} |
||||
|
||||
// now, set before to the oldest observation |
||||
beforeOrAt = self[(index + 1) % cardinality]; |
||||
if (!beforeOrAt.initialized) beforeOrAt = self[0]; |
||||
|
||||
// ensure that the target is chronologically at or after the oldest observation |
||||
require(lte(time, beforeOrAt.blockTimestamp, target), 'OLD'); |
||||
|
||||
// if we've reached this point, we have to binary search |
||||
return binarySearch(self, time, target, index, cardinality); |
||||
} |
||||
|
||||
/// @dev Reverts if an observation at or before the desired observation timestamp does not exist. |
||||
/// 0 may be passed as `secondsAgo' to return the current cumulative values. |
||||
/// If called with a timestamp falling between two observations, returns the counterfactual accumulator values |
||||
/// at exactly the timestamp between the two observations. |
||||
/// @param self The stored oracle array |
||||
/// @param time The current block timestamp |
||||
/// @param secondsAgo The amount of time to look back, in seconds, at which point to return an observation |
||||
/// @param tick The current tick |
||||
/// @param index The index of the observation that was most recently written to the observations array |
||||
/// @param liquidity The current in-range pool liquidity |
||||
/// @param cardinality The number of populated elements in the oracle array |
||||
/// @return tickCumulative The tick * time elapsed since the pool was first initialized, as of `secondsAgo` |
||||
/// @return secondsPerLiquidityCumulativeX128 The time elapsed / max(1, liquidity) since the pool was first initialized, as of `secondsAgo` |
||||
function observeSingle( |
||||
Observation[65535] storage self, |
||||
uint32 time, |
||||
uint32 secondsAgo, |
||||
int24 tick, |
||||
uint16 index, |
||||
uint128 liquidity, |
||||
uint16 cardinality |
||||
) internal view returns (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) { |
||||
if (secondsAgo == 0) { |
||||
Observation memory last = self[index]; |
||||
if (last.blockTimestamp != time) last = transform(last, time, tick, liquidity); |
||||
return (last.tickCumulative, last.secondsPerLiquidityCumulativeX128); |
||||
} |
||||
|
||||
uint32 target = time - secondsAgo; |
||||
|
||||
(Observation memory beforeOrAt, Observation memory atOrAfter) = |
||||
getSurroundingObservations(self, time, target, tick, index, liquidity, cardinality); |
||||
|
||||
if (target == beforeOrAt.blockTimestamp) { |
||||
// we're at the left boundary |
||||
return (beforeOrAt.tickCumulative, beforeOrAt.secondsPerLiquidityCumulativeX128); |
||||
} else if (target == atOrAfter.blockTimestamp) { |
||||
// we're at the right boundary |
||||
return (atOrAfter.tickCumulative, atOrAfter.secondsPerLiquidityCumulativeX128); |
||||
} else { |
||||
// we're in the middle |
||||
uint32 observationTimeDelta = atOrAfter.blockTimestamp - beforeOrAt.blockTimestamp; |
||||
uint32 targetDelta = target - beforeOrAt.blockTimestamp; |
||||
return ( |
||||
beforeOrAt.tickCumulative + |
||||
((atOrAfter.tickCumulative - beforeOrAt.tickCumulative) / observationTimeDelta) * |
||||
targetDelta, |
||||
beforeOrAt.secondsPerLiquidityCumulativeX128 + |
||||
uint160( |
||||
(uint256( |
||||
atOrAfter.secondsPerLiquidityCumulativeX128 - beforeOrAt.secondsPerLiquidityCumulativeX128 |
||||
) * targetDelta) / observationTimeDelta |
||||
) |
||||
); |
||||
} |
||||
} |
||||
|
||||
/// @notice Returns the accumulator values as of each time seconds ago from the given time in the array of `secondsAgos` |
||||
/// @dev Reverts if `secondsAgos` > oldest observation |
||||
/// @param self The stored oracle array |
||||
/// @param time The current block.timestamp |
||||
/// @param secondsAgos Each amount of time to look back, in seconds, at which point to return an observation |
||||
/// @param tick The current tick |
||||
/// @param index The index of the observation that was most recently written to the observations array |
||||
/// @param liquidity The current in-range pool liquidity |
||||
/// @param cardinality The number of populated elements in the oracle array |
||||
/// @return tickCumulatives The tick * time elapsed since the pool was first initialized, as of each `secondsAgo` |
||||
/// @return secondsPerLiquidityCumulativeX128s The cumulative seconds / max(1, liquidity) since the pool was first initialized, as of each `secondsAgo` |
||||
function observe( |
||||
Observation[65535] storage self, |
||||
uint32 time, |
||||
uint32[] memory secondsAgos, |
||||
int24 tick, |
||||
uint16 index, |
||||
uint128 liquidity, |
||||
uint16 cardinality |
||||
) internal view returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) { |
||||
require(cardinality > 0, 'I'); |
||||
|
||||
tickCumulatives = new int56[](secondsAgos.length); |
||||
secondsPerLiquidityCumulativeX128s = new uint160[](secondsAgos.length); |
||||
for (uint256 i = 0; i < secondsAgos.length; i++) { |
||||
(tickCumulatives[i], secondsPerLiquidityCumulativeX128s[i]) = observeSingle( |
||||
self, |
||||
time, |
||||
secondsAgos[i], |
||||
tick, |
||||
index, |
||||
liquidity, |
||||
cardinality |
||||
); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,88 @@ |
||||
// SPDX-License-Identifier: BUSL-1.1 |
||||
pragma solidity >=0.5.0; |
||||
|
||||
import './FullMath.sol'; |
||||
import './FixedPoint128.sol'; |
||||
import './LiquidityMath.sol'; |
||||
|
||||
/// @title Position |
||||
/// @notice Positions represent an owner address' liquidity between a lower and upper tick boundary |
||||
/// @dev Positions store additional state for tracking fees owed to the position |
||||
library Position { |
||||
// info stored for each user's position |
||||
struct Info { |
||||
// the amount of liquidity owned by this position |
||||
uint128 liquidity; |
||||
// fee growth per unit of liquidity as of the last update to liquidity or fees owed |
||||
uint256 feeGrowthInside0LastX128; |
||||
uint256 feeGrowthInside1LastX128; |
||||
// the fees owed to the position owner in token0/token1 |
||||
uint128 tokensOwed0; |
||||
uint128 tokensOwed1; |
||||
} |
||||
|
||||
/// @notice Returns the Info struct of a position, given an owner and position boundaries |
||||
/// @param self The mapping containing all user positions |
||||
/// @param owner The address of the position owner |
||||
/// @param tickLower The lower tick boundary of the position |
||||
/// @param tickUpper The upper tick boundary of the position |
||||
/// @return position The position info struct of the given owners' position |
||||
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))]; |
||||
} |
||||
|
||||
/// @notice Credits accumulated fees to a user's position |
||||
/// @param self The individual position to update |
||||
/// @param liquidityDelta The change in pool liquidity as a result of the position update |
||||
/// @param feeGrowthInside0X128 The all-time fee growth in token0, per unit of liquidity, inside the position's tick boundaries |
||||
/// @param feeGrowthInside1X128 The all-time fee growth in token1, per unit of liquidity, inside the position's tick boundaries |
||||
function update( |
||||
Info storage self, |
||||
int128 liquidityDelta, |
||||
uint256 feeGrowthInside0X128, |
||||
uint256 feeGrowthInside1X128 |
||||
) internal { |
||||
Info memory _self = self; |
||||
|
||||
uint128 liquidityNext; |
||||
if (liquidityDelta == 0) { |
||||
require(_self.liquidity > 0, 'NP'); // disallow pokes for 0 liquidity positions |
||||
liquidityNext = _self.liquidity; |
||||
} else { |
||||
liquidityNext = LiquidityMath.addDelta(_self.liquidity, liquidityDelta); |
||||
} |
||||
|
||||
// calculate accumulated fees |
||||
uint128 tokensOwed0 = |
||||
uint128( |
||||
FullMath.mulDiv( |
||||
feeGrowthInside0X128 - _self.feeGrowthInside0LastX128, |
||||
_self.liquidity, |
||||
FixedPoint128.Q128 |
||||
) |
||||
); |
||||
uint128 tokensOwed1 = |
||||
uint128( |
||||
FullMath.mulDiv( |
||||
feeGrowthInside1X128 - _self.feeGrowthInside1LastX128, |
||||
_self.liquidity, |
||||
FixedPoint128.Q128 |
||||
) |
||||
); |
||||
|
||||
// update the position |
||||
if (liquidityDelta != 0) self.liquidity = liquidityNext; |
||||
self.feeGrowthInside0LastX128 = feeGrowthInside0X128; |
||||
self.feeGrowthInside1LastX128 = feeGrowthInside1X128; |
||||
if (tokensOwed0 > 0 || tokensOwed1 > 0) { |
||||
// overflow is acceptable, have to withdraw before you hit type(uint128).max fees |
||||
self.tokensOwed0 += tokensOwed0; |
||||
self.tokensOwed1 += tokensOwed1; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,28 @@ |
||||
// SPDX-License-Identifier: GPL-2.0-or-later |
||||
pragma solidity >=0.5.0; |
||||
|
||||
/// @title Safe casting methods |
||||
/// @notice Contains methods for safely casting between types |
||||
library SafeCast { |
||||
/// @notice Cast a uint256 to a uint160, revert on overflow |
||||
/// @param y The uint256 to be downcasted |
||||
/// @return z The downcasted integer, now type uint160 |
||||
function toUint160(uint256 y) internal pure returns (uint160 z) { |
||||
require((z = uint160(y)) == y); |
||||
} |
||||
|
||||
/// @notice Cast a int256 to a int128, revert on overflow or underflow |
||||
/// @param y The int256 to be downcasted |
||||
/// @return z The downcasted integer, now type int128 |
||||
function toInt128(int256 y) internal pure returns (int128 z) { |
||||
require((z = int128(y)) == y); |
||||
} |
||||
|
||||
/// @notice Cast a uint256 to a int256, revert on overflow |
||||
/// @param y The uint256 to be casted |
||||
/// @return z The casted integer, now type int256 |
||||
function toInt256(uint256 y) internal pure returns (int256 z) { |
||||
require(y < 2**255); |
||||
z = int256(y); |
||||
} |
||||
} |
@ -0,0 +1,227 @@ |
||||
// SPDX-License-Identifier: BUSL-1.1 |
||||
pragma solidity >=0.5.0; |
||||
|
||||
import './LowGasSafeMath.sol'; |
||||
import './SafeCast.sol'; |
||||
|
||||
import './FullMath.sol'; |
||||
import './UnsafeMath.sol'; |
||||
import './FixedPoint96.sol'; |
||||
|
||||
/// @title Functions based on Q64.96 sqrt price and liquidity |
||||
/// @notice Contains the math that uses square root of price as a Q64.96 and liquidity to compute deltas |
||||
library SqrtPriceMath { |
||||
using LowGasSafeMath for uint256; |
||||
using SafeCast for uint256; |
||||
|
||||
/// @notice Gets the next sqrt price given a delta of token0 |
||||
/// @dev Always rounds up, because in the exact output case (increasing price) we need to move the price at least |
||||
/// far enough to get the desired output amount, and in the exact input case (decreasing price) we need to move the |
||||
/// price less in order to not send too much output. |
||||
/// The most precise formula for this is liquidity * sqrtPX96 / (liquidity +- amount * sqrtPX96), |
||||
/// if this is impossible because of overflow, we calculate liquidity / (liquidity / sqrtPX96 +- amount). |
||||
/// @param sqrtPX96 The starting price, i.e. before accounting for the token0 delta |
||||
/// @param liquidity The amount of usable liquidity |
||||
/// @param amount How much of token0 to add or remove from virtual reserves |
||||
/// @param add Whether to add or remove the amount of token0 |
||||
/// @return The price after adding or removing amount, depending on add |
||||
function getNextSqrtPriceFromAmount0RoundingUp( |
||||
uint160 sqrtPX96, |
||||
uint128 liquidity, |
||||
uint256 amount, |
||||
bool add |
||||
) internal pure returns (uint160) { |
||||
// we short circuit amount == 0 because the result is otherwise not guaranteed to equal the input price |
||||
if (amount == 0) return sqrtPX96; |
||||
uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION; |
||||
|
||||
if (add) { |
||||
uint256 product; |
||||
if ((product = amount * sqrtPX96) / amount == sqrtPX96) { |
||||
uint256 denominator = numerator1 + product; |
||||
if (denominator >= numerator1) |
||||
// always fits in 160 bits |
||||
return uint160(FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator)); |
||||
} |
||||
|
||||
return uint160(UnsafeMath.divRoundingUp(numerator1, (numerator1 / sqrtPX96).add(amount))); |
||||
} else { |
||||
uint256 product; |
||||
// if the product overflows, we know the denominator underflows |
||||
// in addition, we must check that the denominator does not underflow |
||||
require((product = amount * sqrtPX96) / amount == sqrtPX96 && numerator1 > product); |
||||
uint256 denominator = numerator1 - product; |
||||
return FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator).toUint160(); |
||||
} |
||||
} |
||||
|
||||
/// @notice Gets the next sqrt price given a delta of token1 |
||||
/// @dev Always rounds down, because in the exact output case (decreasing price) we need to move the price at least |
||||
/// far enough to get the desired output amount, and in the exact input case (increasing price) we need to move the |
||||
/// price less in order to not send too much output. |
||||
/// The formula we compute is within <1 wei of the lossless version: sqrtPX96 +- amount / liquidity |
||||
/// @param sqrtPX96 The starting price, i.e., before accounting for the token1 delta |
||||
/// @param liquidity The amount of usable liquidity |
||||
/// @param amount How much of token1 to add, or remove, from virtual reserves |
||||
/// @param add Whether to add, or remove, the amount of token1 |
||||
/// @return The price after adding or removing `amount` |
||||
function getNextSqrtPriceFromAmount1RoundingDown( |
||||
uint160 sqrtPX96, |
||||
uint128 liquidity, |
||||
uint256 amount, |
||||
bool add |
||||
) internal pure returns (uint160) { |
||||
// if we're adding (subtracting), rounding down requires rounding the quotient down (up) |
||||
// in both cases, avoid a mulDiv for most inputs |
||||
if (add) { |
||||
uint256 quotient = |
||||
( |
||||
amount <= type(uint160).max |
||||
? (amount << FixedPoint96.RESOLUTION) / liquidity |
||||
: FullMath.mulDiv(amount, FixedPoint96.Q96, liquidity) |
||||
); |
||||
|
||||
return uint256(sqrtPX96).add(quotient).toUint160(); |
||||
} else { |
||||
uint256 quotient = |
||||
( |
||||
amount <= type(uint160).max |
||||
? UnsafeMath.divRoundingUp(amount << FixedPoint96.RESOLUTION, liquidity) |
||||
: FullMath.mulDivRoundingUp(amount, FixedPoint96.Q96, liquidity) |
||||
); |
||||
|
||||
require(sqrtPX96 > quotient); |
||||
// always fits 160 bits |
||||
return uint160(sqrtPX96 - quotient); |
||||
} |
||||
} |
||||
|
||||
/// @notice Gets the next sqrt price given an input amount of token0 or token1 |
||||
/// @dev Throws if price or liquidity are 0, or if the next price is out of bounds |
||||
/// @param sqrtPX96 The starting price, i.e., before accounting for the input amount |
||||
/// @param liquidity The amount of usable liquidity |
||||
/// @param amountIn How much of token0, or token1, is being swapped in |
||||
/// @param zeroForOne Whether the amount in is token0 or token1 |
||||
/// @return sqrtQX96 The price after adding the input amount to token0 or token1 |
||||
function getNextSqrtPriceFromInput( |
||||
uint160 sqrtPX96, |
||||
uint128 liquidity, |
||||
uint256 amountIn, |
||||
bool zeroForOne |
||||
) internal pure returns (uint160 sqrtQX96) { |
||||
require(sqrtPX96 > 0); |
||||
require(liquidity > 0); |
||||
|
||||
// round to make sure that we don't pass the target price |
||||
return |
||||
zeroForOne |
||||
? getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountIn, true) |
||||
: getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountIn, true); |
||||
} |
||||
|
||||
/// @notice Gets the next sqrt price given an output amount of token0 or token1 |
||||
/// @dev Throws if price or liquidity are 0 or the next price is out of bounds |
||||
/// @param sqrtPX96 The starting price before accounting for the output amount |
||||
/// @param liquidity The amount of usable liquidity |
||||
/// @param amountOut How much of token0, or token1, is being swapped out |
||||
/// @param zeroForOne Whether the amount out is token0 or token1 |
||||
/// @return sqrtQX96 The price after removing the output amount of token0 or token1 |
||||
function getNextSqrtPriceFromOutput( |
||||
uint160 sqrtPX96, |
||||
uint128 liquidity, |
||||
uint256 amountOut, |
||||
bool zeroForOne |
||||
) internal pure returns (uint160 sqrtQX96) { |
||||
require(sqrtPX96 > 0); |
||||
require(liquidity > 0); |
||||
|
||||
// round to make sure that we pass the target price |
||||
return |
||||
zeroForOne |
||||
? getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountOut, false) |
||||
: getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountOut, false); |
||||
} |
||||
|
||||
/// @notice Gets the amount0 delta between two prices |
||||
/// @dev Calculates liquidity / sqrt(lower) - liquidity / sqrt(upper), |
||||
/// i.e. liquidity * (sqrt(upper) - sqrt(lower)) / (sqrt(upper) * sqrt(lower)) |
||||
/// @param sqrtRatioAX96 A sqrt price |
||||
/// @param sqrtRatioBX96 Another sqrt price |
||||
/// @param liquidity The amount of usable liquidity |
||||
/// @param roundUp Whether to round the amount up or down |
||||
/// @return amount0 Amount of token0 required to cover a position of size liquidity between the two passed prices |
||||
function getAmount0Delta( |
||||
uint160 sqrtRatioAX96, |
||||
uint160 sqrtRatioBX96, |
||||
uint128 liquidity, |
||||
bool roundUp |
||||
) internal pure returns (uint256 amount0) { |
||||
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); |
||||
|
||||
uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION; |
||||
uint256 numerator2 = sqrtRatioBX96 - sqrtRatioAX96; |
||||
|
||||
require(sqrtRatioAX96 > 0); |
||||
|
||||
return |
||||
roundUp |
||||
? UnsafeMath.divRoundingUp( |
||||
FullMath.mulDivRoundingUp(numerator1, numerator2, sqrtRatioBX96), |
||||
sqrtRatioAX96 |
||||
) |
||||
: FullMath.mulDiv(numerator1, numerator2, sqrtRatioBX96) / sqrtRatioAX96; |
||||
} |
||||
|
||||
/// @notice Gets the amount1 delta between two prices |
||||
/// @dev Calculates liquidity * (sqrt(upper) - sqrt(lower)) |
||||
/// @param sqrtRatioAX96 A sqrt price |
||||
/// @param sqrtRatioBX96 Another sqrt price |
||||
/// @param liquidity The amount of usable liquidity |
||||
/// @param roundUp Whether to round the amount up, or down |
||||
/// @return amount1 Amount of token1 required to cover a position of size liquidity between the two passed prices |
||||
function getAmount1Delta( |
||||
uint160 sqrtRatioAX96, |
||||
uint160 sqrtRatioBX96, |
||||
uint128 liquidity, |
||||
bool roundUp |
||||
) internal pure returns (uint256 amount1) { |
||||
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); |
||||
|
||||
return |
||||
roundUp |
||||
? FullMath.mulDivRoundingUp(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96) |
||||
: FullMath.mulDiv(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96); |
||||
} |
||||
|
||||
/// @notice Helper that gets signed token0 delta |
||||
/// @param sqrtRatioAX96 A sqrt price |
||||
/// @param sqrtRatioBX96 Another sqrt price |
||||
/// @param liquidity The change in liquidity for which to compute the amount0 delta |
||||
/// @return amount0 Amount of token0 corresponding to the passed liquidityDelta between the two prices |
||||
function getAmount0Delta( |
||||
uint160 sqrtRatioAX96, |
||||
uint160 sqrtRatioBX96, |
||||
int128 liquidity |
||||
) internal pure returns (int256 amount0) { |
||||
return |
||||
liquidity < 0 |
||||
? -getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256() |
||||
: getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256(); |
||||
} |
||||
|
||||
/// @notice Helper that gets signed token1 delta |
||||
/// @param sqrtRatioAX96 A sqrt price |
||||
/// @param sqrtRatioBX96 Another sqrt price |
||||
/// @param liquidity The change in liquidity for which to compute the amount1 delta |
||||
/// @return amount1 Amount of token1 corresponding to the passed liquidityDelta between the two prices |
||||
function getAmount1Delta( |
||||
uint160 sqrtRatioAX96, |
||||
uint160 sqrtRatioBX96, |
||||
int128 liquidity |
||||
) internal pure returns (int256 amount1) { |
||||
return |
||||
liquidity < 0 |
||||
? -getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256() |
||||
: getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256(); |
||||
} |
||||
} |
@ -0,0 +1,98 @@ |
||||
// SPDX-License-Identifier: BUSL-1.1 |
||||
pragma solidity >=0.5.0; |
||||
|
||||
import './FullMath.sol'; |
||||
import './SqrtPriceMath.sol'; |
||||
|
||||
/// @title Computes the result of a swap within ticks |
||||
/// @notice Contains methods for computing the result of a swap within a single tick price range, i.e., a single tick. |
||||
library SwapMath { |
||||
/// @notice Computes the result of swapping some amount in, or amount out, given the parameters of the swap |
||||
/// @dev The fee, plus the amount in, will never exceed the amount remaining if the swap's `amountSpecified` is positive |
||||
/// @param sqrtRatioCurrentX96 The current sqrt price of the pool |
||||
/// @param sqrtRatioTargetX96 The price that cannot be exceeded, from which the direction of the swap is inferred |
||||
/// @param liquidity The usable liquidity |
||||
/// @param amountRemaining How much input or output amount is remaining to be swapped in/out |
||||
/// @param feePips The fee taken from the input amount, expressed in hundredths of a bip |
||||
/// @return sqrtRatioNextX96 The price after swapping the amount in/out, not to exceed the price target |
||||
/// @return amountIn The amount to be swapped in, of either token0 or token1, based on the direction of the swap |
||||
/// @return amountOut The amount to be received, of either token0 or token1, based on the direction of the swap |
||||
/// @return feeAmount The amount of input that will be taken as a fee |
||||
function computeSwapStep( |
||||
uint160 sqrtRatioCurrentX96, |
||||
uint160 sqrtRatioTargetX96, |
||||
uint128 liquidity, |
||||
int256 amountRemaining, |
||||
uint24 feePips |
||||
) |
||||
internal |
||||
pure |
||||
returns ( |
||||
uint160 sqrtRatioNextX96, |
||||
uint256 amountIn, |
||||
uint256 amountOut, |
||||
uint256 feeAmount |
||||
) |
||||
{ |
||||
bool zeroForOne = sqrtRatioCurrentX96 >= sqrtRatioTargetX96; |
||||
bool exactIn = amountRemaining >= 0; |
||||
|
||||
if (exactIn) { |
||||
uint256 amountRemainingLessFee = FullMath.mulDiv(uint256(amountRemaining), 1e6 - feePips, 1e6); |
||||
amountIn = zeroForOne |
||||
? SqrtPriceMath.getAmount0Delta(sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity, true) |
||||
: SqrtPriceMath.getAmount1Delta(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity, true); |
||||
if (amountRemainingLessFee >= amountIn) sqrtRatioNextX96 = sqrtRatioTargetX96; |
||||
else |
||||
sqrtRatioNextX96 = SqrtPriceMath.getNextSqrtPriceFromInput( |
||||
sqrtRatioCurrentX96, |
||||
liquidity, |
||||
amountRemainingLessFee, |
||||
zeroForOne |
||||
); |
||||
} else { |
||||
amountOut = zeroForOne |
||||
? SqrtPriceMath.getAmount1Delta(sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity, false) |
||||
: SqrtPriceMath.getAmount0Delta(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity, false); |
||||
if (uint256(-amountRemaining) >= amountOut) sqrtRatioNextX96 = sqrtRatioTargetX96; |
||||
else |
||||
sqrtRatioNextX96 = SqrtPriceMath.getNextSqrtPriceFromOutput( |
||||
sqrtRatioCurrentX96, |
||||
liquidity, |
||||
uint256(-amountRemaining), |
||||
zeroForOne |
||||
); |
||||
} |
||||
|
||||
bool max = sqrtRatioTargetX96 == sqrtRatioNextX96; |
||||
|
||||
// get the input/output amounts |
||||
if (zeroForOne) { |
||||
amountIn = max && exactIn |
||||
? amountIn |
||||
: SqrtPriceMath.getAmount0Delta(sqrtRatioNextX96, sqrtRatioCurrentX96, liquidity, true); |
||||
amountOut = max && !exactIn |
||||
? amountOut |
||||
: SqrtPriceMath.getAmount1Delta(sqrtRatioNextX96, sqrtRatioCurrentX96, liquidity, false); |
||||
} else { |
||||
amountIn = max && exactIn |
||||
? amountIn |
||||
: SqrtPriceMath.getAmount1Delta(sqrtRatioCurrentX96, sqrtRatioNextX96, liquidity, true); |
||||
amountOut = max && !exactIn |
||||
? amountOut |
||||
: SqrtPriceMath.getAmount0Delta(sqrtRatioCurrentX96, sqrtRatioNextX96, liquidity, false); |
||||
} |
||||
|
||||
// cap the output amount to not exceed the remaining output amount |
||||
if (!exactIn && amountOut > uint256(-amountRemaining)) { |
||||
amountOut = uint256(-amountRemaining); |
||||
} |
||||
|
||||
if (exactIn && sqrtRatioNextX96 != sqrtRatioTargetX96) { |
||||
// we didn't reach the target, so take the remainder of the maximum input as fee |
||||
feeAmount = uint256(amountRemaining) - amountIn; |
||||
} else { |
||||
feeAmount = FullMath.mulDivRoundingUp(amountIn, feePips, 1e6 - feePips); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,183 @@ |
||||
// SPDX-License-Identifier: BUSL-1.1 |
||||
pragma solidity >=0.5.0; |
||||
|
||||
import './LowGasSafeMath.sol'; |
||||
import './SafeCast.sol'; |
||||
|
||||
import './TickMath.sol'; |
||||
import './LiquidityMath.sol'; |
||||
|
||||
/// @title Tick |
||||
/// @notice Contains functions for managing tick processes and relevant calculations |
||||
library Tick { |
||||
using LowGasSafeMath for int256; |
||||
using SafeCast for int256; |
||||
|
||||
// info stored for each initialized individual tick |
||||
struct Info { |
||||
// the total position liquidity that references this tick |
||||
uint128 liquidityGross; |
||||
// amount of net liquidity added (subtracted) when tick is crossed from left to right (right to left), |
||||
int128 liquidityNet; |
||||
// fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick) |
||||
// only has relative meaning, not absolute — the value depends on when the tick is initialized |
||||
uint256 feeGrowthOutside0X128; |
||||
uint256 feeGrowthOutside1X128; |
||||
// the cumulative tick value on the other side of the tick |
||||
int56 tickCumulativeOutside; |
||||
// the seconds per unit of liquidity on the _other_ side of this tick (relative to the current tick) |
||||
// only has relative meaning, not absolute — the value depends on when the tick is initialized |
||||
uint160 secondsPerLiquidityOutsideX128; |
||||
// the seconds spent on the other side of the tick (relative to the current tick) |
||||
// only has relative meaning, not absolute — the value depends on when the tick is initialized |
||||
uint32 secondsOutside; |
||||
// true iff the tick is initialized, i.e. the value is exactly equivalent to the expression liquidityGross != 0 |
||||
// these 8 bits are set to prevent fresh sstores when crossing newly initialized ticks |
||||
bool initialized; |
||||
} |
||||
|
||||
/// @notice Derives max liquidity per tick from given tick spacing |
||||
/// @dev Executed within the pool constructor |
||||
/// @param tickSpacing The amount of required tick separation, realized in multiples of `tickSpacing` |
||||
/// e.g., a tickSpacing of 3 requires ticks to be initialized every 3rd tick i.e., ..., -6, -3, 0, 3, 6, ... |
||||
/// @return The max liquidity per tick |
||||
function tickSpacingToMaxLiquidityPerTick(int24 tickSpacing) internal pure returns (uint128) { |
||||
int24 minTick = (TickMath.MIN_TICK / tickSpacing) * tickSpacing; |
||||
int24 maxTick = (TickMath.MAX_TICK / tickSpacing) * tickSpacing; |
||||
uint24 numTicks = uint24((maxTick - minTick) / tickSpacing) + 1; |
||||
return type(uint128).max / numTicks; |
||||
} |
||||
|
||||
/// @notice Retrieves fee growth data |
||||
/// @param self The mapping containing all tick information for initialized ticks |
||||
/// @param tickLower The lower tick boundary of the position |
||||
/// @param tickUpper The upper tick boundary of the position |
||||
/// @param tickCurrent The current tick |
||||
/// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0 |
||||
/// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1 |
||||
/// @return feeGrowthInside0X128 The all-time fee growth in token0, per unit of liquidity, inside the position's tick boundaries |
||||
/// @return feeGrowthInside1X128 The all-time fee growth in token1, per unit of liquidity, inside the position's tick boundaries |
||||
function getFeeGrowthInside( |
||||
mapping(int24 => Tick.Info) storage self, |
||||
int24 tickLower, |
||||
int24 tickUpper, |
||||
int24 tickCurrent, |
||||
uint256 feeGrowthGlobal0X128, |
||||
uint256 feeGrowthGlobal1X128 |
||||
) internal view returns (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) { |
||||
Info storage lower = self[tickLower]; |
||||
Info storage upper = self[tickUpper]; |
||||
|
||||
// calculate fee growth below |
||||
uint256 feeGrowthBelow0X128; |
||||
uint256 feeGrowthBelow1X128; |
||||
if (tickCurrent >= tickLower) { |
||||
feeGrowthBelow0X128 = lower.feeGrowthOutside0X128; |
||||
feeGrowthBelow1X128 = lower.feeGrowthOutside1X128; |
||||
} else { |
||||
feeGrowthBelow0X128 = feeGrowthGlobal0X128 - lower.feeGrowthOutside0X128; |
||||
feeGrowthBelow1X128 = feeGrowthGlobal1X128 - lower.feeGrowthOutside1X128; |
||||
} |
||||
|
||||
// calculate fee growth above |
||||
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; |
||||
} |
||||
|
||||
/// @notice Updates a tick and returns true if the tick was flipped from initialized to uninitialized, or vice versa |
||||
/// @param self The mapping containing all tick information for initialized ticks |
||||
/// @param tick The tick that will be updated |
||||
/// @param tickCurrent The current tick |
||||
/// @param liquidityDelta A new amount of liquidity to be added (subtracted) when tick is crossed from left to right (right to left) |
||||
/// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0 |
||||
/// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1 |
||||
/// @param secondsPerLiquidityCumulativeX128 The all-time seconds per max(1, liquidity) of the pool |
||||
/// @param time The current block timestamp cast to a uint32 |
||||
/// @param upper true for updating a position's upper tick, or false for updating a position's lower tick |
||||
/// @param maxLiquidity The maximum liquidity allocation for a single tick |
||||
/// @return flipped Whether the tick was flipped from initialized to uninitialized, or vice versa |
||||
function update( |
||||
mapping(int24 => Tick.Info) storage self, |
||||
int24 tick, |
||||
int24 tickCurrent, |
||||
int128 liquidityDelta, |
||||
uint256 feeGrowthGlobal0X128, |
||||
uint256 feeGrowthGlobal1X128, |
||||
uint160 secondsPerLiquidityCumulativeX128, |
||||
int56 tickCumulative, |
||||
uint32 time, |
||||
bool upper, |
||||
uint128 maxLiquidity |
||||
) internal returns (bool flipped) { |
||||
Tick.Info storage info = self[tick]; |
||||
|
||||
uint128 liquidityGrossBefore = info.liquidityGross; |
||||
uint128 liquidityGrossAfter = LiquidityMath.addDelta(liquidityGrossBefore, liquidityDelta); |
||||
|
||||
require(liquidityGrossAfter <= maxLiquidity, 'LO'); |
||||
|
||||
flipped = (liquidityGrossAfter == 0) != (liquidityGrossBefore == 0); |
||||
|
||||
if (liquidityGrossBefore == 0) { |
||||
// by convention, we assume that all growth before a tick was initialized happened _below_ the tick |
||||
if (tick <= tickCurrent) { |
||||
info.feeGrowthOutside0X128 = feeGrowthGlobal0X128; |
||||
info.feeGrowthOutside1X128 = feeGrowthGlobal1X128; |
||||
info.secondsPerLiquidityOutsideX128 = secondsPerLiquidityCumulativeX128; |
||||
info.tickCumulativeOutside = tickCumulative; |
||||
info.secondsOutside = time; |
||||
} |
||||
info.initialized = true; |
||||
} |
||||
|
||||
info.liquidityGross = liquidityGrossAfter; |
||||
|
||||
// when the lower (upper) tick is crossed left to right (right to left), liquidity must be added (removed) |
||||
info.liquidityNet = upper |
||||
? int256(info.liquidityNet).sub(liquidityDelta).toInt128() |
||||
: int256(info.liquidityNet).add(liquidityDelta).toInt128(); |
||||
} |
||||
|
||||
/// @notice Clears tick data |
||||
/// @param self The mapping containing all initialized tick information for initialized ticks |
||||
/// @param tick The tick that will be cleared |
||||
function clear(mapping(int24 => Tick.Info) storage self, int24 tick) internal { |
||||
delete self[tick]; |
||||
} |
||||
|
||||
/// @notice Transitions to next tick as needed by price movement |
||||
/// @param self The mapping containing all tick information for initialized ticks |
||||
/// @param tick The destination tick of the transition |
||||
/// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0 |
||||
/// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1 |
||||
/// @param secondsPerLiquidityCumulativeX128 The current seconds per liquidity |
||||
/// @param time The current block.timestamp |
||||
/// @return liquidityNet The amount of liquidity added (subtracted) when tick is crossed from left to right (right to left) |
||||
function cross( |
||||
mapping(int24 => Tick.Info) storage self, |
||||
int24 tick, |
||||
uint256 feeGrowthGlobal0X128, |
||||
uint256 feeGrowthGlobal1X128, |
||||
uint160 secondsPerLiquidityCumulativeX128, |
||||
int56 tickCumulative, |
||||
uint32 time |
||||
) internal returns (int128 liquidityNet) { |
||||
Tick.Info storage info = self[tick]; |
||||
info.feeGrowthOutside0X128 = feeGrowthGlobal0X128 - info.feeGrowthOutside0X128; |
||||
info.feeGrowthOutside1X128 = feeGrowthGlobal1X128 - info.feeGrowthOutside1X128; |
||||
info.secondsPerLiquidityOutsideX128 = secondsPerLiquidityCumulativeX128 - info.secondsPerLiquidityOutsideX128; |
||||
info.tickCumulativeOutside = tickCumulative - info.tickCumulativeOutside; |
||||
info.secondsOutside = time - info.secondsOutside; |
||||
liquidityNet = info.liquidityNet; |
||||
} |
||||
} |
@ -0,0 +1,78 @@ |
||||
// SPDX-License-Identifier: BUSL-1.1 |
||||
pragma solidity >=0.5.0; |
||||
|
||||
import './BitMath.sol'; |
||||
|
||||
/// @title Packed tick initialized state library |
||||
/// @notice Stores a packed mapping of tick index to its initialized state |
||||
/// @dev The mapping uses int16 for keys since ticks are represented as int24 and there are 256 (2^8) values per word. |
||||
library TickBitmap { |
||||
/// @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) private pure returns (int16 wordPos, uint8 bitPos) { |
||||
wordPos = int16(tick >> 8); |
||||
bitPos = uint8(tick % 256); |
||||
} |
||||
|
||||
/// @notice Flips the initialized state for a given tick from false to true, or vice versa |
||||
/// @param self The mapping in which to flip the tick |
||||
/// @param tick The tick to flip |
||||
/// @param tickSpacing The spacing between usable ticks |
||||
function flipTick( |
||||
mapping(int16 => uint256) storage self, |
||||
int24 tick, |
||||
int24 tickSpacing |
||||
) internal { |
||||
require(tick % tickSpacing == 0); // ensure that the tick is spaced |
||||
(int16 wordPos, uint8 bitPos) = position(tick / tickSpacing); |
||||
uint256 mask = 1 << bitPos; |
||||
self[wordPos] ^= mask; |
||||
} |
||||
|
||||
/// @notice Returns the next initialized tick contained in the same word (or adjacent word) as the tick that is either |
||||
/// to the left (less than or equal to) or right (greater than) of the given tick |
||||
/// @param self The mapping in which to compute the next initialized tick |
||||
/// @param tick The starting tick |
||||
/// @param tickSpacing The spacing between usable ticks |
||||
/// @param lte Whether to search for the next initialized tick to the left (less than or equal to the starting tick) |
||||
/// @return next The next initialized or uninitialized tick up to 256 ticks away from the current tick |
||||
/// @return initialized Whether the next tick is initialized, as the function only searches within up to 256 ticks |
||||
function nextInitializedTickWithinOneWord( |
||||
mapping(int16 => uint256) storage self, |
||||
int24 tick, |
||||
int24 tickSpacing, |
||||
bool lte |
||||
) internal view returns (int24 next, bool initialized) { |
||||
int24 compressed = tick / tickSpacing; |
||||
if (tick < 0 && tick % tickSpacing != 0) compressed--; // round towards negative infinity |
||||
|
||||
if (lte) { |
||||
(int16 wordPos, uint8 bitPos) = position(compressed); |
||||
// all the 1s at or to the right of the current bitPos |
||||
uint256 mask = (1 << bitPos) - 1 + (1 << bitPos); |
||||
uint256 masked = self[wordPos] & mask; |
||||
|
||||
// if there are no initialized ticks to the right of or at the current tick, return rightmost in the word |
||||
initialized = masked != 0; |
||||
// overflow/underflow is possible, but prevented externally by limiting both tickSpacing and tick |
||||
next = initialized |
||||
? (compressed - int24(bitPos - BitMath.mostSignificantBit(masked))) * tickSpacing |
||||
: (compressed - int24(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); |
||||
// all the 1s at or to the left of the bitPos |
||||
uint256 mask = ~((1 << bitPos) - 1); |
||||
uint256 masked = self[wordPos] & mask; |
||||
|
||||
// if there are no initialized ticks to the left of the current tick, return leftmost in the word |
||||
initialized = masked != 0; |
||||
// overflow/underflow is possible, but prevented externally by limiting both tickSpacing and tick |
||||
next = initialized |
||||
? (compressed + 1 + int24(BitMath.leastSignificantBit(masked) - bitPos)) * tickSpacing |
||||
: (compressed + 1 + int24(type(uint8).max - bitPos)) * tickSpacing; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,205 @@ |
||||
// SPDX-License-Identifier: GPL-2.0-or-later |
||||
pragma solidity >=0.5.0; |
||||
|
||||
/// @title Math library for computing sqrt prices from ticks and vice versa |
||||
/// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports |
||||
/// prices between 2**-128 and 2**128 |
||||
library TickMath { |
||||
/// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128 |
||||
int24 internal constant MIN_TICK = -887272; |
||||
/// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128 |
||||
int24 internal constant MAX_TICK = -MIN_TICK; |
||||
|
||||
/// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK) |
||||
uint160 internal constant MIN_SQRT_RATIO = 4295128739; |
||||
/// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK) |
||||
uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342; |
||||
|
||||
/// @notice Calculates sqrt(1.0001^tick) * 2^96 |
||||
/// @dev Throws if |tick| > max tick |
||||
/// @param tick The input tick for the above formula |
||||
/// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0) |
||||
/// at the given tick |
||||
function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) { |
||||
uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick)); |
||||
require(absTick <= uint256(MAX_TICK), 'T'); |
||||
|
||||
uint256 ratio = absTick & 0x1 != 0 ? 0xfffcb933bd6fad37aa2d162d1a594001 : 0x100000000000000000000000000000000; |
||||
if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128; |
||||
if (absTick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128; |
||||
if (absTick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128; |
||||
if (absTick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128; |
||||
if (absTick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128; |
||||
if (absTick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128; |
||||
if (absTick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128; |
||||
if (absTick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128; |
||||
if (absTick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128; |
||||
if (absTick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128; |
||||
if (absTick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128; |
||||
if (absTick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128; |
||||
if (absTick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128; |
||||
if (absTick & 0x4000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128; |
||||
if (absTick & 0x8000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128; |
||||
if (absTick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128; |
||||
if (absTick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128; |
||||
if (absTick & 0x40000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128; |
||||
if (absTick & 0x80000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128; |
||||
|
||||
if (tick > 0) ratio = type(uint256).max / ratio; |
||||
|
||||
// this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96. |
||||
// we then downcast because we know the result always fits within 160 bits due to our tick input constraint |
||||
// we round up in the division so getTickAtSqrtRatio of the output price is always consistent |
||||
sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1)); |
||||
} |
||||
|
||||
/// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio |
||||
/// @dev Throws in case sqrtPriceX96 < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may |
||||
/// ever return. |
||||
/// @param sqrtPriceX96 The sqrt ratio for which to compute the tick as a Q64.96 |
||||
/// @return tick The greatest tick for which the ratio is less than or equal to the input ratio |
||||
function getTickAtSqrtRatio(uint160 sqrtPriceX96) internal pure returns (int24 tick) { |
||||
// second inequality must be < because the price can never reach the price at the max tick |
||||
require(sqrtPriceX96 >= MIN_SQRT_RATIO && sqrtPriceX96 < MAX_SQRT_RATIO, 'R'); |
||||
uint256 ratio = uint256(sqrtPriceX96) << 32; |
||||
|
||||
uint256 r = ratio; |
||||
uint256 msb = 0; |
||||
|
||||
assembly { |
||||
let f := shl(7, gt(r, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) |
||||
msb := or(msb, f) |
||||
r := shr(f, r) |
||||
} |
||||
assembly { |
||||
let f := shl(6, gt(r, 0xFFFFFFFFFFFFFFFF)) |
||||
msb := or(msb, f) |
||||
r := shr(f, r) |
||||
} |
||||
assembly { |
||||
let f := shl(5, gt(r, 0xFFFFFFFF)) |
||||
msb := or(msb, f) |
||||
r := shr(f, r) |
||||
} |
||||
assembly { |
||||
let f := shl(4, gt(r, 0xFFFF)) |
||||
msb := or(msb, f) |
||||
r := shr(f, r) |
||||
} |
||||
assembly { |
||||
let f := shl(3, gt(r, 0xFF)) |
||||
msb := or(msb, f) |
||||
r := shr(f, r) |
||||
} |
||||
assembly { |
||||
let f := shl(2, gt(r, 0xF)) |
||||
msb := or(msb, f) |
||||
r := shr(f, r) |
||||
} |
||||
assembly { |
||||
let f := shl(1, gt(r, 0x3)) |
||||
msb := or(msb, f) |
||||
r := shr(f, r) |
||||
} |
||||
assembly { |
||||
let f := gt(r, 0x1) |
||||
msb := or(msb, f) |
||||
} |
||||
|
||||
if (msb >= 128) r = ratio >> (msb - 127); |
||||
else r = ratio << (127 - msb); |
||||
|
||||
int256 log_2 = (int256(msb) - 128) << 64; |
||||
|
||||
assembly { |
||||
r := shr(127, mul(r, r)) |
||||
let f := shr(128, r) |
||||
log_2 := or(log_2, shl(63, f)) |
||||
r := shr(f, r) |
||||
} |
||||
assembly { |
||||
r := shr(127, mul(r, r)) |
||||
let f := shr(128, r) |
||||
log_2 := or(log_2, shl(62, f)) |
||||
r := shr(f, r) |
||||
} |
||||
assembly { |
||||
r := shr(127, mul(r, r)) |
||||
let f := shr(128, r) |
||||
log_2 := or(log_2, shl(61, f)) |
||||
r := shr(f, r) |
||||
} |
||||
assembly { |
||||
r := shr(127, mul(r, r)) |
||||
let f := shr(128, r) |
||||
log_2 := or(log_2, shl(60, f)) |
||||
r := shr(f, r) |
||||
} |
||||
assembly { |
||||
r := shr(127, mul(r, r)) |
||||
let f := shr(128, r) |
||||
log_2 := or(log_2, shl(59, f)) |
||||
r := shr(f, r) |
||||
} |
||||
assembly { |
||||
r := shr(127, mul(r, r)) |
||||
let f := shr(128, r) |
||||
log_2 := or(log_2, shl(58, f)) |
||||
r := shr(f, r) |
||||
} |
||||
assembly { |
||||
r := shr(127, mul(r, r)) |
||||
let f := shr(128, r) |
||||
log_2 := or(log_2, shl(57, f)) |
||||
r := shr(f, r) |
||||
} |
||||
assembly { |
||||
r := shr(127, mul(r, r)) |
||||
let f := shr(128, r) |
||||
log_2 := or(log_2, shl(56, f)) |
||||
r := shr(f, r) |
||||
} |
||||
assembly { |
||||
r := shr(127, mul(r, r)) |
||||
let f := shr(128, r) |
||||
log_2 := or(log_2, shl(55, f)) |
||||
r := shr(f, r) |
||||
} |
||||
assembly { |
||||
r := shr(127, mul(r, r)) |
||||
let f := shr(128, r) |
||||
log_2 := or(log_2, shl(54, f)) |
||||
r := shr(f, r) |
||||
} |
||||
assembly { |
||||
r := shr(127, mul(r, r)) |
||||
let f := shr(128, r) |
||||
log_2 := or(log_2, shl(53, f)) |
||||
r := shr(f, r) |
||||
} |
||||
assembly { |
||||
r := shr(127, mul(r, r)) |
||||
let f := shr(128, r) |
||||
log_2 := or(log_2, shl(52, f)) |
||||
r := shr(f, r) |
||||
} |
||||
assembly { |
||||
r := shr(127, mul(r, r)) |
||||
let f := shr(128, r) |
||||
log_2 := or(log_2, shl(51, f)) |
||||
r := shr(f, r) |
||||
} |
||||
assembly { |
||||
r := shr(127, mul(r, r)) |
||||
let f := shr(128, r) |
||||
log_2 := or(log_2, shl(50, f)) |
||||
} |
||||
|
||||
int256 log_sqrt10001 = log_2 * 255738958999603826347141; // 128.128 number |
||||
|
||||
int24 tickLow = int24((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128); |
||||
int24 tickHi = int24((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128); |
||||
|
||||
tick = tickLow == tickHi ? tickLow : getSqrtRatioAtTick(tickHi) <= sqrtPriceX96 ? tickHi : tickLow; |
||||
} |
||||
} |
@ -0,0 +1,23 @@ |
||||
// SPDX-License-Identifier: GPL-2.0-or-later |
||||
pragma solidity >=0.6.0; |
||||
|
||||
import '../interfaces/IERC20Minimal.sol'; |
||||
|
||||
/// @title TransferHelper |
||||
/// @notice Contains helper methods for interacting with ERC20 tokens that do not consistently return true/false |
||||
library TransferHelper { |
||||
/// @notice Transfers tokens from msg.sender to a recipient |
||||
/// @dev Calls transfer on token contract, errors with TF if transfer fails |
||||
/// @param token The contract address of the token which will be transferred |
||||
/// @param to The recipient of the transfer |
||||
/// @param value The value of the transfer |
||||
function safeTransfer( |
||||
address token, |
||||
address to, |
||||
uint256 value |
||||
) internal { |
||||
(bool success, bytes memory data) = |
||||
token.call(abi.encodeWithSelector(IERC20Minimal.transfer.selector, to, value)); |
||||
require(success && (data.length == 0 || abi.decode(data, (bool))), 'TF'); |
||||
} |
||||
} |
@ -0,0 +1,17 @@ |
||||
// SPDX-License-Identifier: GPL-2.0-or-later |
||||
pragma solidity >=0.5.0; |
||||
|
||||
/// @title Math functions that do not check inputs or outputs |
||||
/// @notice Contains methods that perform common math functions but do not do any overflow or underflow checks |
||||
library UnsafeMath { |
||||
/// @notice Returns ceil(x / y) |
||||
/// @dev division by 0 has unspecified behavior, and must be checked externally |
||||
/// @param x The dividend |
||||
/// @param y The divisor |
||||
/// @return z The quotient, ceil(x / y) |
||||
function divRoundingUp(uint256 x, uint256 y) internal pure returns (uint256 z) { |
||||
assembly { |
||||
z := add(div(x, y), gt(mod(x, y), 0)) |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue