diff --git a/contracts/Pool.sol b/contracts/Pool.sol deleted file mode 100644 index 177e40199..000000000 --- a/contracts/Pool.sol +++ /dev/null @@ -1,900 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.10; - -import {IPoolImmutables, IPoolState, IPoolActions, IPoolEvents, IPoolDerivedState, IPoolOwnerActions, IPool} from './interfaces/IPool.sol'; - -import {NoDelegateCall} from './NoDelegateCall.sol'; - -import {SafeCast} from './libraries/SafeCast.sol'; -import {Tick} from './libraries/Tick.sol'; -import {TickBitmap} from './libraries/TickBitmap.sol'; -import {Position} from './libraries/Position.sol'; -import {Oracle} from './libraries/Oracle.sol'; - -import {FullMath} from './libraries/FullMath.sol'; -import {FixedPoint128} from './libraries/FixedPoint128.sol'; -import {TransferHelper} from './libraries/TransferHelper.sol'; -import {TickMath} from './libraries/TickMath.sol'; -import {SqrtPriceMath} from './libraries/SqrtPriceMath.sol'; -import {SwapMath} from './libraries/SwapMath.sol'; - -import {IPoolDeployer} from './interfaces/IPoolDeployer.sol'; -import {IPoolFactory} from './interfaces/IPoolFactory.sol'; -import {IERC20Minimal} from './interfaces/external/IERC20Minimal.sol'; -import {IMintCallback} from './interfaces/callback/IMintCallback.sol'; -import {ISwapCallback} from './interfaces/callback/ISwapCallback.sol'; -import {IFlashCallback} from './interfaces/callback/IFlashCallback.sol'; - -contract Pool is IPool, NoDelegateCall { - 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 IPoolImmutables - address public immutable override factory; - /// @inheritdoc IPoolImmutables - address public immutable override token0; - /// @inheritdoc IPoolImmutables - address public immutable override token1; - /// @inheritdoc IPoolImmutables - uint24 public immutable override fee; - - /// @inheritdoc IPoolImmutables - int24 public immutable override tickSpacing; - - /// @inheritdoc IPoolImmutables - 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 IPoolState - Slot0 public override slot0; - - /// @inheritdoc IPoolState - uint256 public override feeGrowthGlobal0X128; - /// @inheritdoc IPoolState - uint256 public override feeGrowthGlobal1X128; - - // accumulated protocol fees in token0/token1 units - struct ProtocolFees { - uint128 token0; - uint128 token1; - } - /// @inheritdoc IPoolState - ProtocolFees public override protocolFees; - - /// @inheritdoc IPoolState - uint128 public override liquidity; - - /// @inheritdoc IPoolState - mapping(int24 => Tick.Info) public override ticks; - /// @inheritdoc IPoolState - mapping(int16 => uint256) public override tickBitmap; - /// @inheritdoc IPoolState - mapping(bytes32 => Position.Info) public override positions; - /// @inheritdoc IPoolState - 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 IPoolFactory#owner() - modifier onlyFactoryOwner() { - require(msg.sender == IPoolFactory(factory).owner()); - _; - } - - constructor() { - int24 _tickSpacing; - (factory, token0, token1, fee, _tickSpacing) = IPoolDeployer(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 - function balance0() private view returns (uint256) { - return IERC20Minimal(token0).balanceOf(address(this)); - } - - /// @dev Get the pool's balance of token1 - function balance1() private view returns (uint256) { - return IERC20Minimal(token1).balanceOf(address(this)); - } - - /// @inheritdoc IPoolDerivedState - 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; - - unchecked { - 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 IPoolDerivedState - 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 IPoolActions - 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 IPoolActions - /// @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 = params.liquidityDelta < 0 - ? liquidityBefore - uint128(-params.liquidityDelta) - : liquidityBefore + uint128(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) { - unchecked { - 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 IPoolActions - /// @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(uint256(amount)).toInt128() - }) - ); - - amount0 = uint256(amount0Int); - amount1 = uint256(amount1Int); - - uint256 balance0Before; - uint256 balance1Before; - if (amount0 > 0) balance0Before = balance0(); - if (amount1 > 0) balance1Before = balance1(); - IMintCallback(msg.sender).mintCallback(amount0, amount1, data); - if (amount0 > 0) require(balance0Before + amount0 <= balance0(), 'M0'); - if (amount1 > 0) require(balance1Before + amount1 <= balance1(), 'M1'); - - emit Mint(msg.sender, recipient, tickLower, tickUpper, amount, amount0, amount1); - } - - /// @inheritdoc IPoolActions - function collect( - address recipient, - int24 tickLower, - int24 tickUpper, - uint128 amount0Requested, - uint128 amount1Requested - ) external override lock returns (uint128 amount0, uint128 amount1) { - unchecked { - // 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 IPoolActions - /// @dev noDelegateCall is applied indirectly via _modifyPosition - function burn( - int24 tickLower, - int24 tickUpper, - uint128 amount - ) external override lock returns (uint256 amount0, uint256 amount1) { - unchecked { - (Position.Info storage position, int256 amount0Int, int256 amount1Int) = _modifyPosition( - ModifyPositionParams({ - owner: msg.sender, - tickLower: tickLower, - tickUpper: tickUpper, - liquidityDelta: -int256(uint256(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 IPoolActions - 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) { - // safe because we test that amountSpecified > amountIn + feeAmount in SwapMath - unchecked { - state.amountSpecifiedRemaining -= (step.amountIn + step.feeAmount).toInt256(); - } - state.amountCalculated = state.amountCalculated - step.amountOut.toInt256(); - } else { - unchecked { - state.amountSpecifiedRemaining += step.amountOut.toInt256(); - } - state.amountCalculated = state.amountCalculated + (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) { - unchecked { - uint256 delta = step.feeAmount / cache.feeProtocol; - step.feeAmount -= delta; - state.protocolFee += uint128(delta); - } - } - - // update global fee tracker - if (state.liquidity > 0) { - unchecked { - 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 - unchecked { - if (zeroForOne) liquidityNet = -liquidityNet; - } - - state.liquidity = liquidityNet < 0 - ? state.liquidity - uint128(-liquidityNet) - : state.liquidity + uint128(liquidityNet); - } - - unchecked { - 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; - unchecked { - if (state.protocolFee > 0) protocolFees.token0 += state.protocolFee; - } - } else { - feeGrowthGlobal1X128 = state.feeGrowthGlobalX128; - unchecked { - if (state.protocolFee > 0) protocolFees.token1 += state.protocolFee; - } - } - - unchecked { - (amount0, amount1) = zeroForOne == exactInput - ? (amountSpecified - state.amountSpecifiedRemaining, state.amountCalculated) - : (state.amountCalculated, amountSpecified - state.amountSpecifiedRemaining); - } - - // do the transfers and collect payment - if (zeroForOne) { - unchecked { - if (amount1 < 0) TransferHelper.safeTransfer(token1, recipient, uint256(-amount1)); - } - - uint256 balance0Before = balance0(); - ISwapCallback(msg.sender).swapCallback(amount0, amount1, data); - require(balance0Before + uint256(amount0) <= balance0(), 'IIA'); - } else { - unchecked { - if (amount0 < 0) TransferHelper.safeTransfer(token0, recipient, uint256(-amount0)); - } - - uint256 balance1Before = balance1(); - ISwapCallback(msg.sender).swapCallback(amount0, amount1, data); - require(balance1Before + uint256(amount1) <= balance1(), 'IIA'); - } - - emit Swap(msg.sender, recipient, amount0, amount1, state.sqrtPriceX96, state.liquidity, state.tick); - slot0.unlocked = true; - } - - /// @inheritdoc IPoolActions - 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); - - IFlashCallback(msg.sender).flashCallback(fee0, fee1, data); - - uint256 balance0After = balance0(); - uint256 balance1After = balance1(); - - require(balance0Before + fee0 <= balance0After, 'F0'); - require(balance1Before + fee1 <= balance1After, 'F1'); - - // sub is safe because we know balanceAfter is gt balanceBefore by at least fee - uint256 paid0; - uint256 paid1; - unchecked { - paid0 = balance0After - balance0Before; - paid1 = balance1After - balance1Before; - } - - if (paid0 > 0) { - unchecked { - uint8 feeProtocol0 = slot0.feeProtocol % 16; - uint256 pFees0 = feeProtocol0 == 0 ? 0 : paid0 / feeProtocol0; - if (uint128(pFees0) > 0) protocolFees.token0 += uint128(pFees0); - feeGrowthGlobal0X128 += FullMath.mulDiv(paid0 - pFees0, FixedPoint128.Q128, _liquidity); - } - } - if (paid1 > 0) { - unchecked { - uint8 feeProtocol1 = slot0.feeProtocol >> 4; - uint256 pFees1 = feeProtocol1 == 0 ? 0 : paid1 / feeProtocol1; - if (uint128(pFees1) > 0) protocolFees.token1 += uint128(pFees1); - feeGrowthGlobal1X128 += FullMath.mulDiv(paid1 - pFees1, FixedPoint128.Q128, _liquidity); - } - } - - emit Flash(msg.sender, recipient, amount0, amount1, paid0, paid1); - } - - /// @inheritdoc IPoolOwnerActions - 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 IPoolOwnerActions - function collectProtocol( - address recipient, - uint128 amount0Requested, - uint128 amount1Requested - ) external override lock onlyFactoryOwner returns (uint128 amount0, uint128 amount1) { - unchecked { - 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); - } - } -} diff --git a/contracts/PoolDeployer.sol b/contracts/PoolDeployer.sol deleted file mode 100644 index c6c456449..000000000 --- a/contracts/PoolDeployer.sol +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.10; - -import {IPoolDeployer} from './interfaces/IPoolDeployer.sol'; - -import {Pool} from './Pool.sol'; - -contract PoolDeployer is IPoolDeployer { - struct Parameters { - address factory; - address token0; - address token1; - uint24 fee; - int24 tickSpacing; - } - - /// @inheritdoc IPoolDeployer - Parameters public override parameters; - - /// @dev Deploys a pool with the given parameters by transiently setting the parameters storage slot and then - /// clearing it after deploying the pool. - /// @param factory The contract address of the Uniswap V3 factory - /// @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 spacing between usable ticks - function deploy( - address factory, - address token0, - address token1, - uint24 fee, - int24 tickSpacing - ) internal returns (address pool) { - parameters = Parameters({factory: factory, token0: token0, token1: token1, fee: fee, tickSpacing: tickSpacing}); - pool = address(new Pool{salt: keccak256(abi.encode(token0, token1, fee))}()); - delete parameters; - } -} diff --git a/contracts/PoolFactory.sol b/contracts/PoolFactory.sol deleted file mode 100644 index e45fc2eea..000000000 --- a/contracts/PoolFactory.sol +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.10; - -import {IPoolFactory} from './interfaces/IPoolFactory.sol'; - -import {PoolDeployer} from './PoolDeployer.sol'; -import {NoDelegateCall} from './NoDelegateCall.sol'; - -import {Pool} from './Pool.sol'; - -/// @title Canonical Uniswap V3 factory -/// @notice Deploys Uniswap V3 pools and manages ownership and control over pool protocol fees -contract PoolFactory is IPoolFactory, PoolDeployer, NoDelegateCall { - /// @inheritdoc IPoolFactory - address public override owner; - - /// @inheritdoc IPoolFactory - mapping(uint24 => int24) public override feeAmountTickSpacing; - /// @inheritdoc IPoolFactory - mapping(address => mapping(address => mapping(uint24 => address))) public override getPool; - - constructor() { - owner = msg.sender; - emit OwnerChanged(address(0), msg.sender); - - feeAmountTickSpacing[500] = 10; - emit FeeAmountEnabled(500, 10); - feeAmountTickSpacing[3000] = 60; - emit FeeAmountEnabled(3000, 60); - feeAmountTickSpacing[10000] = 200; - emit FeeAmountEnabled(10000, 200); - } - - /// @inheritdoc IPoolFactory - function createPool( - address tokenA, - address tokenB, - uint24 fee - ) external override noDelegateCall returns (address pool) { - require(tokenA != tokenB); - (address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); - require(token0 != address(0)); - int24 tickSpacing = feeAmountTickSpacing[fee]; - require(tickSpacing != 0); - require(getPool[token0][token1][fee] == address(0)); - pool = deploy(address(this), token0, token1, fee, tickSpacing); - getPool[token0][token1][fee] = pool; - // populate mapping in the reverse direction, deliberate choice to avoid the cost of comparing addresses - getPool[token1][token0][fee] = pool; - emit PoolCreated(token0, token1, fee, tickSpacing, pool); - } - - /// @inheritdoc IPoolFactory - function setOwner(address _owner) external override { - require(msg.sender == owner); - emit OwnerChanged(owner, _owner); - owner = _owner; - } - - /// @inheritdoc IPoolFactory - function enableFeeAmount(uint24 fee, int24 tickSpacing) public override { - require(msg.sender == owner); - require(fee < 1000000); - // tick spacing is capped at 16384 to prevent the situation where tickSpacing is so large that - // TickBitmap#nextInitializedTickWithinOneWord overflows int24 container from a valid tick - // 16384 ticks represents a >5x price change with ticks of 1 bips - require(tickSpacing > 0 && tickSpacing < 16384); - require(feeAmountTickSpacing[fee] == 0); - - feeAmountTickSpacing[fee] = tickSpacing; - emit FeeAmountEnabled(fee, tickSpacing); - } -} diff --git a/contracts/PoolManager.sol b/contracts/PoolManager.sol new file mode 100644 index 000000000..626c8b518 --- /dev/null +++ b/contracts/PoolManager.sol @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.10; + +import {Pool} from './libraries/Pool.sol'; +import {Tick} from './libraries/Tick.sol'; +import {SafeCast} from './libraries/SafeCast.sol'; + +import {IERC20Minimal} from './interfaces/external/IERC20Minimal.sol'; +import {NoDelegateCall} from './NoDelegateCall.sol'; +import {IPoolManager} from './interfaces/IPoolManager.sol'; +import {ILockCallback} from './interfaces/callback/ILockCallback.sol'; + +import {console} from 'hardhat/console.sol'; + +/// @notice Holds the state for all pools +contract PoolManager is IPoolManager, NoDelegateCall { + using SafeCast for *; + using Pool for *; + + /// @notice Represents the configuration for a fee + struct FeeConfig { + int24 tickSpacing; + uint128 maxLiquidityPerTick; + } + + // todo: can we make this documented in the interface + mapping(bytes32 => Pool.State) public pools; + mapping(uint24 => FeeConfig) public override configs; + + constructor() { + _configure(100, 1); + _configure(500, 10); + _configure(3000, 60); + _configure(10000, 200); + } + + function _configure(uint24 fee, int24 tickSpacing) internal { + require(tickSpacing > 0); + require(configs[fee].tickSpacing == 0); + configs[fee].tickSpacing = tickSpacing; + configs[fee].maxLiquidityPerTick = Tick.tickSpacingToMaxLiquidityPerTick(tickSpacing); + // todo: emit event + } + + /// @dev For mocking in unit tests + function _blockTimestamp() internal view virtual returns (uint32) { + return uint32(block.timestamp); + } + + function _getPool(IPoolManager.PoolKey memory key) private view returns (Pool.State storage) { + return pools[keccak256(abi.encode(key))]; + } + + /// @notice Initialize the state for a given pool ID + function initialize(IPoolManager.PoolKey memory key, uint160 sqrtPriceX96) external override returns (int24 tick) { + tick = _getPool(key).initialize(_blockTimestamp(), sqrtPriceX96); + } + + /// @notice Increase the maximum number of stored observations for the pool's oracle + function increaseObservationCardinalityNext(IPoolManager.PoolKey memory key, uint16 observationCardinalityNext) + external + override + returns (uint16 observationCardinalityNextOld, uint16 observationCardinalityNextNew) + { + (observationCardinalityNextOld, observationCardinalityNextNew) = _getPool(key) + .increaseObservationCardinalityNext(observationCardinalityNext); + } + + /// @notice Represents the address that has currently locked the pool + address public override lockedBy; + + /// @notice All the latest tracked balances of tokens + mapping(IERC20Minimal => uint256) public override reservesOf; + + /// @notice Internal transient enumerable set + IERC20Minimal[] public override tokensTouched; + struct PositionAndDelta { + uint8 slot; + int248 delta; + } + mapping(IERC20Minimal => PositionAndDelta) public override tokenDelta; + + function lock(bytes calldata data) external override returns (bytes memory result) { + require(lockedBy == address(0)); + lockedBy = msg.sender; + + // the caller does everything in this callback, including paying what they owe via calls to settle + result = ILockCallback(msg.sender).lockAcquired(data); + + unchecked { + for (uint256 i = 0; i < tokensTouched.length; i++) { + require(tokenDelta[tokensTouched[i]].delta == 0, 'Not settled'); + delete tokenDelta[tokensTouched[i]]; + } + } + delete tokensTouched; + delete lockedBy; + } + + /// @dev Adds a token to a unique list of tokens that have been touched + function _addTokenToSet(IERC20Minimal token) internal returns (uint8 slot) { + uint256 len = tokensTouched.length; + if (len == 0) { + tokensTouched.push(token); + return 0; + } + + PositionAndDelta storage pd = tokenDelta[token]; + slot = pd.slot; + + if (slot == 0 && tokensTouched[slot] != token) { + require(len < type(uint8).max); + slot = uint8(len); + pd.slot = slot; + tokensTouched.push(token); + } + } + + function _accountDelta(IERC20Minimal token, int256 delta) internal { + if (delta == 0) return; + _addTokenToSet(token); + tokenDelta[token].delta += int248(delta); + } + + /// @dev Accumulates a balance change to a map of token to balance changes + function _accountPoolBalanceDelta(PoolKey memory key, Pool.BalanceDelta memory delta) internal { + _accountDelta(key.token0, delta.amount0); + _accountDelta(key.token1, delta.amount1); + } + + modifier onlyByLocker() { + require(msg.sender == lockedBy, 'LOK'); + _; + } + + /// @dev Mint some liquidity for the given pool + function mint(IPoolManager.PoolKey memory key, IPoolManager.MintParams memory params) + external + override + noDelegateCall + onlyByLocker + returns (Pool.BalanceDelta memory delta) + { + require(params.amount > 0); + + FeeConfig memory config = configs[key.fee]; + + delta = _getPool(key).modifyPosition( + Pool.ModifyPositionParams({ + owner: params.recipient, + tickLower: params.tickLower, + tickUpper: params.tickUpper, + liquidityDelta: int256(uint256(params.amount)).toInt128(), + time: _blockTimestamp(), + maxLiquidityPerTick: config.maxLiquidityPerTick, + tickSpacing: config.tickSpacing + }) + ); + + _accountPoolBalanceDelta(key, delta); + } + + /// @dev Mint some liquidity for the given pool + function burn(IPoolManager.PoolKey memory key, IPoolManager.BurnParams memory params) + external + override + noDelegateCall + onlyByLocker + returns (Pool.BalanceDelta memory delta) + { + require(params.amount > 0); + + FeeConfig memory config = configs[key.fee]; + + delta = _getPool(key).modifyPosition( + Pool.ModifyPositionParams({ + owner: msg.sender, + tickLower: params.tickLower, + tickUpper: params.tickUpper, + liquidityDelta: -int256(uint256(params.amount)).toInt128(), + time: _blockTimestamp(), + maxLiquidityPerTick: config.maxLiquidityPerTick, + tickSpacing: config.tickSpacing + }) + ); + + _accountPoolBalanceDelta(key, delta); + } + + function swap(IPoolManager.PoolKey memory key, IPoolManager.SwapParams memory params) + external + override + noDelegateCall + onlyByLocker + returns (Pool.BalanceDelta memory delta) + { + FeeConfig memory config = configs[key.fee]; + + delta = _getPool(key).swap( + Pool.SwapParams({ + time: _blockTimestamp(), + fee: key.fee, + tickSpacing: config.tickSpacing, + zeroForOne: params.zeroForOne, + amountSpecified: params.amountSpecified, + sqrtPriceLimitX96: params.sqrtPriceLimitX96 + }) + ); + + _accountPoolBalanceDelta(key, delta); + } + + /// @notice Called by the user to net out some value owed to the user + /// @dev Can also be used as a mechanism for _free_ flash loans + function take( + IERC20Minimal token, + address to, + uint256 amount + ) external override noDelegateCall onlyByLocker { + _accountDelta(token, amount.toInt256()); + reservesOf[token] -= amount; + token.transfer(to, amount); + } + + /// @notice Called by the user to pay what is owed + function settle(IERC20Minimal token) external override noDelegateCall onlyByLocker returns (uint256 paid) { + uint256 reservesBefore = reservesOf[token]; + reservesOf[token] = token.balanceOf(address(this)); + paid = reservesOf[token] - reservesBefore; + // subtraction must be safe + _accountDelta(token, -(paid.toInt256())); + } + + /// @notice Update the protocol fee for a given pool + function setFeeProtocol(IPoolManager.PoolKey calldata key, uint8 feeProtocol) + external + override + returns (uint8 feeProtocolOld) + { + return _getPool(key).setFeeProtocol(feeProtocol); + } + + /// @notice Observe a past state of a pool + function observe(IPoolManager.PoolKey calldata key, uint32[] calldata secondsAgos) + external + view + override + noDelegateCall + returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) + { + return _getPool(key).observe(_blockTimestamp(), secondsAgos); + } + + /// @notice Get the snapshot of the cumulative values of a tick range + function snapshotCumulativesInside( + IPoolManager.PoolKey calldata key, + int24 tickLower, + int24 tickUpper + ) external view override noDelegateCall returns (Pool.Snapshot memory) { + return _getPool(key).snapshotCumulativesInside(tickLower, tickUpper, _blockTimestamp()); + } +} diff --git a/contracts/interfaces/IPool.sol b/contracts/interfaces/IPool.sol deleted file mode 100644 index f92df24a2..000000000 --- a/contracts/interfaces/IPool.sol +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -import {IPoolImmutables} from './pool/IPoolImmutables.sol'; -import {IPoolState} from './pool/IPoolState.sol'; -import {IPoolDerivedState} from './pool/IPoolDerivedState.sol'; -import {IPoolActions} from './pool/IPoolActions.sol'; -import {IPoolOwnerActions} from './pool/IPoolOwnerActions.sol'; -import {IPoolEvents} from './pool/IPoolEvents.sol'; - -/// @title The full interface for a 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 IPool is IPoolImmutables, IPoolState, IPoolDerivedState, IPoolActions, IPoolOwnerActions, IPoolEvents { - -} diff --git a/contracts/interfaces/IPoolDeployer.sol b/contracts/interfaces/IPoolDeployer.sol deleted file mode 100644 index 3aed0543b..000000000 --- a/contracts/interfaces/IPoolDeployer.sol +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title An interface for a contract that is capable of deploying 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 IPoolDeployer { - /// @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 - ); -} diff --git a/contracts/interfaces/IPoolFactory.sol b/contracts/interfaces/IPoolFactory.sol deleted file mode 100644 index b45496085..000000000 --- a/contracts/interfaces/IPoolFactory.sol +++ /dev/null @@ -1,78 +0,0 @@ -// 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 IPoolFactory { - /// @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; -} diff --git a/contracts/interfaces/IPoolManager.sol b/contracts/interfaces/IPoolManager.sol new file mode 100644 index 000000000..03747725b --- /dev/null +++ b/contracts/interfaces/IPoolManager.sol @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.6.2; + +import {IERC20Minimal} from './external/IERC20Minimal.sol'; +import {Pool} from '../libraries/Pool.sol'; + +interface IPoolManager { + /// @notice Returns the key for identifying a pool + struct PoolKey { + /// @notice The lower token of the pool, sorted numerically + IERC20Minimal token0; + /// @notice The higher token of the pool, sorted numerically + IERC20Minimal token1; + /// @notice The fee for the pool + uint24 fee; + } + + /// @notice Returns the immutable configuration for a given fee + function configs(uint24 fee) external view returns (int24 tickSpacing, uint128 maxLiquidityPerTick); + + /// @notice Returns the reserves for a given ERC20 token + function reservesOf(IERC20Minimal token) external view returns (uint256); + + /// @notice Initialize the state for a given pool ID + function initialize(PoolKey memory key, uint160 sqrtPriceX96) external returns (int24 tick); + + /// @notice Increase the maximum number of stored observations for the pool's oracle + function increaseObservationCardinalityNext(PoolKey memory key, uint16 observationCardinalityNext) + external + returns (uint16 observationCardinalityNextOld, uint16 observationCardinalityNextNew); + + struct MintParams { + // the address that will own the minted liquidity + address recipient; + // the lower and upper tick of the position + int24 tickLower; + int24 tickUpper; + // any change in liquidity + uint256 amount; + } + + /// @notice Represents the address that has currently locked the pool + function lockedBy() external view returns (address); + + function tokensTouched(uint256 index) external view returns (IERC20Minimal); + + function tokenDelta(IERC20Minimal token) external view returns (uint8, int248); + + /// @notice All operations go through this function + function lock(bytes calldata data) external returns (bytes memory); + + /// @dev Mint some liquidity for the given pool + function mint(PoolKey memory key, MintParams memory params) external returns (Pool.BalanceDelta memory delta); + + struct BurnParams { + // the lower and upper tick of the position + int24 tickLower; + int24 tickUpper; + // the reduction in liquidity to effect + uint256 amount; + } + + /// @dev Mint some liquidity for the given pool + function burn(PoolKey memory key, BurnParams memory params) external returns (Pool.BalanceDelta memory delta); + + struct SwapParams { + bool zeroForOne; + int256 amountSpecified; + uint160 sqrtPriceLimitX96; + } + + function swap(PoolKey memory key, SwapParams memory params) external returns (Pool.BalanceDelta memory delta); + + /// @notice Called by the user to net out some value owed to the user + /// @dev Can also be used as a mechanism for _free_ flash loans + function take( + IERC20Minimal token, + address to, + uint256 amount + ) external; + + /// @notice Called by the user to pay what is owed + function settle(IERC20Minimal token) external returns (uint256 paid); + + /// @notice Update the protocol fee for a given pool + function setFeeProtocol(PoolKey calldata key, uint8 feeProtocol) external returns (uint8 feeProtocolOld); + + /// @notice Observe a past state of a pool + function observe(PoolKey calldata key, uint32[] calldata secondsAgos) + external + view + returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s); + + /// @notice Get the snapshot of the cumulative values of a tick range + function snapshotCumulativesInside( + PoolKey calldata key, + int24 tickLower, + int24 tickUpper + ) external view returns (Pool.Snapshot memory); +} diff --git a/contracts/interfaces/callback/IFlashCallback.sol b/contracts/interfaces/callback/IFlashCallback.sol deleted file mode 100644 index a8a616600..000000000 --- a/contracts/interfaces/callback/IFlashCallback.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title Callback for IPoolActions#flash -/// @notice Any contract that calls IPoolActions#flash must implement this interface -interface IFlashCallback { - /// @notice Called to `msg.sender` after transferring to the recipient from IPool#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 Pool deployed by the canonical PoolFactory. - /// @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 IPoolActions#flash call - function flashCallback( - uint256 fee0, - uint256 fee1, - bytes calldata data - ) external; -} diff --git a/contracts/interfaces/callback/ILockCallback.sol b/contracts/interfaces/callback/ILockCallback.sol new file mode 100644 index 000000000..0e10c71df --- /dev/null +++ b/contracts/interfaces/callback/ILockCallback.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.6.2; + +interface ILockCallback { + /// @notice Called by the pool manager on `msg.sender` when a lock is acquired + /// todo: this should be able to return data that is passed through to the lock caller + function lockAcquired(bytes calldata data) external returns (bytes memory); +} diff --git a/contracts/interfaces/callback/IMintCallback.sol b/contracts/interfaces/callback/IMintCallback.sol deleted file mode 100644 index 3501f9f3f..000000000 --- a/contracts/interfaces/callback/IMintCallback.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title Callback for IPoolActions#mint -/// @notice Any contract that calls IPoolActions#mint must implement this interface -interface IMintCallback { - /// @notice Called to `msg.sender` after minting liquidity to a position from IPool#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 Pool deployed by the canonical PoolFactory. - /// @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 IPoolActions#mint call - function mintCallback( - uint256 amount0Owed, - uint256 amount1Owed, - bytes calldata data - ) external; -} diff --git a/contracts/interfaces/callback/ISwapCallback.sol b/contracts/interfaces/callback/ISwapCallback.sol deleted file mode 100644 index be36bbab3..000000000 --- a/contracts/interfaces/callback/ISwapCallback.sol +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title Callback for IPoolActions#swap -/// @notice Any contract that calls IPoolActions#swap must implement this interface -interface ISwapCallback { - /// @notice Called to `msg.sender` after executing a swap via IPool#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 Pool deployed by the canonical PoolFactory. - /// 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 IPoolActions#swap call - function swapCallback( - int256 amount0Delta, - int256 amount1Delta, - bytes calldata data - ) external; -} diff --git a/contracts/interfaces/pool/IPoolActions.sol b/contracts/interfaces/pool/IPoolActions.sol deleted file mode 100644 index 6fc3cb1d3..000000000 --- a/contracts/interfaces/pool/IPoolActions.sol +++ /dev/null @@ -1,103 +0,0 @@ -// 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 IPoolActions { - /// @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 IMintCallback#mintCallback - /// 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 ISwapCallback#swapCallback - /// @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 IFlashCallback#flashCallback - /// @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; -} diff --git a/contracts/interfaces/pool/IPoolDerivedState.sol b/contracts/interfaces/pool/IPoolDerivedState.sol deleted file mode 100644 index 103d31dfd..000000000 --- a/contracts/interfaces/pool/IPoolDerivedState.sol +++ /dev/null @@ -1,40 +0,0 @@ -// 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 IPoolDerivedState { - /// @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 - ); -} diff --git a/contracts/interfaces/pool/IPoolEvents.sol b/contracts/interfaces/pool/IPoolEvents.sol deleted file mode 100644 index 6d71190cf..000000000 --- a/contracts/interfaces/pool/IPoolEvents.sol +++ /dev/null @@ -1,121 +0,0 @@ -// 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 IPoolEvents { - /// @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); -} diff --git a/contracts/interfaces/pool/IPoolImmutables.sol b/contracts/interfaces/pool/IPoolImmutables.sol deleted file mode 100644 index 03fb316a7..000000000 --- a/contracts/interfaces/pool/IPoolImmutables.sol +++ /dev/null @@ -1,35 +0,0 @@ -// 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 IPoolImmutables { - /// @notice The contract that deployed the pool, which must adhere to the IPoolFactory 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); -} diff --git a/contracts/interfaces/pool/IPoolOwnerActions.sol b/contracts/interfaces/pool/IPoolOwnerActions.sol deleted file mode 100644 index 1546ac904..000000000 --- a/contracts/interfaces/pool/IPoolOwnerActions.sol +++ /dev/null @@ -1,23 +0,0 @@ -// 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 IPoolOwnerActions { - /// @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); -} diff --git a/contracts/interfaces/pool/IPoolState.sol b/contracts/interfaces/pool/IPoolState.sol deleted file mode 100644 index b8ac2023d..000000000 --- a/contracts/interfaces/pool/IPoolState.sol +++ /dev/null @@ -1,117 +0,0 @@ -// 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 IPoolState { - /// @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 - /// @return 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. - /// @return observationIndex The index of the last oracle observation that was written, - /// @return observationCardinality The current maximum number of observations stored in the pool, - /// @return observationCardinalityNext The next maximum number of observations, to be updated when the observation. - /// @return 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 - /// @return The liquidity at the current price of the pool - 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 - /// @return liquidityNet how much liquidity changes when the pool price crosses the tick, - /// @return feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0, - /// @return feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1, - /// @return tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick - /// @return secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from the current tick, - /// @return secondsOutside the seconds spent on the other side of the tick from the current tick, - /// @return 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, - /// @return feeGrowthInside0LastX128 fee growth of token0 inside the tick range as of the last mint/burn/poke, - /// @return feeGrowthInside1LastX128 fee growth of token1 inside the tick range as of the last mint/burn/poke, - /// @return tokensOwed0 the computed amount of token0 owed to the position as of the last mint/burn/poke, - /// @return 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, - /// @return tickCumulative the tick multiplied by seconds elapsed for the life of the pool as of the observation timestamp, - /// @return secondsPerLiquidityCumulativeX128 the seconds per in range liquidity for the life of the pool as of the observation timestamp, - /// @return 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 - ); -} diff --git a/contracts/libraries/Pool.sol b/contracts/libraries/Pool.sol new file mode 100644 index 000000000..855981d93 --- /dev/null +++ b/contracts/libraries/Pool.sol @@ -0,0 +1,624 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity =0.8.10; + +import {SafeCast} from './SafeCast.sol'; +import {Tick} from './Tick.sol'; +import {TickBitmap} from './TickBitmap.sol'; +import {Position} from './Position.sol'; +import {Oracle} from './Oracle.sol'; +import {FullMath} from './FullMath.sol'; +import {FixedPoint128} from './FixedPoint128.sol'; +import {TickMath} from './TickMath.sol'; +import {SqrtPriceMath} from './SqrtPriceMath.sol'; +import {SwapMath} from './SwapMath.sol'; + +library Pool { + using SafeCast for *; + 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]; + + /// @notice Represents a change in the pool's balance of token0 and token1. + /// @dev This is returned from most pool operations + struct BalanceDelta { + int256 amount0; + int256 amount1; + } + + 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; + } + + // accumulated protocol fees in token0/token1 units + // todo: this might be better accumulated in a pool + struct ProtocolFees { + uint128 token0; + uint128 token1; + } + + /// @dev The state of a pool + struct State { + Slot0 slot0; + uint256 feeGrowthGlobal0X128; + uint256 feeGrowthGlobal1X128; + ProtocolFees protocolFees; + uint128 liquidity; + mapping(int24 => Tick.Info) ticks; + mapping(int16 => uint256) tickBitmap; + mapping(bytes32 => Position.Info) positions; + Oracle.Observation[65535] observations; + } + + /// @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'); + } + + struct SnapshotCumulativesInsideState { + int56 tickCumulativeLower; + int56 tickCumulativeUpper; + uint160 secondsPerLiquidityOutsideLowerX128; + uint160 secondsPerLiquidityOutsideUpperX128; + uint32 secondsOutsideLower; + uint32 secondsOutsideUpper; + } + + struct Snapshot { + int56 tickCumulativeInside; + uint160 secondsPerLiquidityInsideX128; + uint32 secondsInside; + } + + /// @dev Take a snapshot of the cumulative values inside a tick range, only consistent as long as a position has non-zero liquidity + function snapshotCumulativesInside( + State storage self, + int24 tickLower, + int24 tickUpper, + uint32 time + ) internal view returns (Snapshot memory result) { + checkTicks(tickLower, tickUpper); + + SnapshotCumulativesInsideState memory state; + { + Tick.Info storage lower = self.ticks[tickLower]; + Tick.Info storage upper = self.ticks[tickUpper]; + bool initializedLower; + ( + state.tickCumulativeLower, + state.secondsPerLiquidityOutsideLowerX128, + state.secondsOutsideLower, + initializedLower + ) = ( + lower.tickCumulativeOutside, + lower.secondsPerLiquidityOutsideX128, + lower.secondsOutside, + lower.initialized + ); + require(initializedLower); + + bool initializedUpper; + ( + state.tickCumulativeUpper, + state.secondsPerLiquidityOutsideUpperX128, + state.secondsOutsideUpper, + initializedUpper + ) = ( + upper.tickCumulativeOutside, + upper.secondsPerLiquidityOutsideX128, + upper.secondsOutside, + upper.initialized + ); + require(initializedUpper); + } + + Slot0 memory _slot0 = self.slot0; + + unchecked { + if (_slot0.tick < tickLower) { + result.tickCumulativeInside = state.tickCumulativeLower - state.tickCumulativeUpper; + result.secondsPerLiquidityInsideX128 = + state.secondsPerLiquidityOutsideLowerX128 - + state.secondsPerLiquidityOutsideUpperX128; + result.secondsInside = state.secondsOutsideLower - state.secondsOutsideUpper; + } else if (_slot0.tick < tickUpper) { + (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) = self.observations.observeSingle( + time, + 0, + _slot0.tick, + _slot0.observationIndex, + self.liquidity, + _slot0.observationCardinality + ); + result.tickCumulativeInside = tickCumulative - state.tickCumulativeLower - state.tickCumulativeUpper; + result.secondsPerLiquidityInsideX128 = + secondsPerLiquidityCumulativeX128 - + state.secondsPerLiquidityOutsideLowerX128 - + state.secondsPerLiquidityOutsideUpperX128; + result.secondsInside = time - state.secondsOutsideLower - state.secondsOutsideUpper; + } else { + result.tickCumulativeInside = state.tickCumulativeUpper - state.tickCumulativeLower; + result.secondsPerLiquidityInsideX128 = + state.secondsPerLiquidityOutsideUpperX128 - + state.secondsPerLiquidityOutsideLowerX128; + result.secondsInside = state.secondsOutsideUpper - state.secondsOutsideLower; + } + } + } + + function observe( + State storage self, + uint32 time, + uint32[] calldata secondsAgos + ) internal view returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) { + return + self.observations.observe( + time, + secondsAgos, + self.slot0.tick, + self.slot0.observationIndex, + self.liquidity, + self.slot0.observationCardinality + ); + } + + function initialize( + State storage self, + uint32 time, + uint160 sqrtPriceX96 + ) internal returns (int24 tick) { + require(self.slot0.sqrtPriceX96 == 0, 'AI'); + + tick = TickMath.getTickAtSqrtRatio(sqrtPriceX96); + + (uint16 cardinality, uint16 cardinalityNext) = self.observations.initialize(time); + + self.slot0 = Slot0({ + sqrtPriceX96: sqrtPriceX96, + tick: tick, + observationIndex: 0, + observationCardinality: cardinality, + observationCardinalityNext: cardinalityNext, + feeProtocol: 0 + }); + } + + /// @dev Increase the number of stored observations + function increaseObservationCardinalityNext(State storage self, uint16 observationCardinalityNext) + internal + returns (uint16 observationCardinalityNextOld, uint16 observationCardinalityNextNew) + { + observationCardinalityNextOld = self.slot0.observationCardinalityNext; + observationCardinalityNextNew = self.observations.grow( + observationCardinalityNextOld, + observationCardinalityNext + ); + self.slot0.observationCardinalityNext = observationCardinalityNextNew; + } + + 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; + // current time + uint32 time; + // the max liquidity per tick + uint128 maxLiquidityPerTick; + // the spacing between ticks + int24 tickSpacing; + } + + struct ModifyPositionState { + int56 tickCumulative; + uint160 secondsPerLiquidityCumulativeX128; + bool flippedLower; + bool flippedUpper; + uint256 feeGrowthInside0X128; + uint256 feeGrowthInside1X128; + } + + /// @dev Effect changes to a position in a pool + /// @param params the position details and the change to the position's liquidity to effect + /// @return result the deltas of the token balances of the pool + function modifyPosition(State storage self, ModifyPositionParams memory params) + internal + returns (BalanceDelta memory result) + { + require(self.slot0.sqrtPriceX96 != 0, 'I'); + + checkTicks(params.tickLower, params.tickUpper); + + { + ModifyPositionState memory state; + // if we need to update the ticks, do it + if (params.liquidityDelta != 0) { + (state.tickCumulative, state.secondsPerLiquidityCumulativeX128) = self.observations.observeSingle( + params.time, + 0, + self.slot0.tick, + self.slot0.observationIndex, + self.liquidity, + self.slot0.observationCardinality + ); + + state.flippedLower = self.ticks.update( + params.tickLower, + self.slot0.tick, + params.liquidityDelta, + self.feeGrowthGlobal0X128, + self.feeGrowthGlobal1X128, + state.secondsPerLiquidityCumulativeX128, + state.tickCumulative, + params.time, + false, + params.maxLiquidityPerTick + ); + state.flippedUpper = self.ticks.update( + params.tickUpper, + self.slot0.tick, + params.liquidityDelta, + self.feeGrowthGlobal0X128, + self.feeGrowthGlobal1X128, + state.secondsPerLiquidityCumulativeX128, + state.tickCumulative, + params.time, + true, + params.maxLiquidityPerTick + ); + + if (state.flippedLower) { + self.tickBitmap.flipTick(params.tickLower, params.tickSpacing); + } + if (state.flippedUpper) { + self.tickBitmap.flipTick(params.tickUpper, params.tickSpacing); + } + } + + (state.feeGrowthInside0X128, state.feeGrowthInside1X128) = self.ticks.getFeeGrowthInside( + params.tickLower, + params.tickUpper, + self.slot0.tick, + self.feeGrowthGlobal0X128, + self.feeGrowthGlobal1X128 + ); + + (uint256 feesOwed0, uint256 feesOwed1) = self + .positions + .get(params.owner, params.tickLower, params.tickUpper) + .update(params.liquidityDelta, state.feeGrowthInside0X128, state.feeGrowthInside1X128); + result.amount0 -= feesOwed0.toInt256(); + result.amount1 -= feesOwed1.toInt256(); + + // clear any tick data that is no longer needed + if (params.liquidityDelta < 0) { + if (state.flippedLower) { + self.ticks.clear(params.tickLower); + } + if (state.flippedUpper) { + self.ticks.clear(params.tickUpper); + } + } + } + + if (params.liquidityDelta != 0) { + if (self.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 + result.amount0 += SqrtPriceMath.getAmount0Delta( + TickMath.getSqrtRatioAtTick(params.tickLower), + TickMath.getSqrtRatioAtTick(params.tickUpper), + params.liquidityDelta + ); + } else if (self.slot0.tick < params.tickUpper) { + // current tick is inside the passed range, must modify liquidity + // write an oracle entry + (self.slot0.observationIndex, self.slot0.observationCardinality) = self.observations.write( + self.slot0.observationIndex, + params.time, + self.slot0.tick, + self.liquidity, + self.slot0.observationCardinality, + self.slot0.observationCardinalityNext + ); + + result.amount0 += SqrtPriceMath.getAmount0Delta( + self.slot0.sqrtPriceX96, + TickMath.getSqrtRatioAtTick(params.tickUpper), + params.liquidityDelta + ); + result.amount1 += SqrtPriceMath.getAmount1Delta( + TickMath.getSqrtRatioAtTick(params.tickLower), + self.slot0.sqrtPriceX96, + params.liquidityDelta + ); + + self.liquidity = params.liquidityDelta < 0 + ? self.liquidity - uint128(-params.liquidityDelta) + : self.liquidity + uint128(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 + result.amount1 += SqrtPriceMath.getAmount1Delta( + TickMath.getSqrtRatioAtTick(params.tickLower), + TickMath.getSqrtRatioAtTick(params.tickUpper), + params.liquidityDelta + ); + } + } + } + + struct SwapCache { + // the protocol fee for the input token + uint8 feeProtocol; + // liquidity at the beginning of the swap + uint128 liquidityStart; + // 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; + } + + struct SwapParams { + uint24 fee; + int24 tickSpacing; + uint32 time; + bool zeroForOne; + int256 amountSpecified; + uint160 sqrtPriceLimitX96; + } + + /// @dev Executes a swap against the state, and returns the amount deltas of the pool + function swap(State storage self, SwapParams memory params) internal returns (BalanceDelta memory result) { + require(params.amountSpecified != 0, 'AS'); + + Slot0 memory slot0Start = self.slot0; + require(slot0Start.sqrtPriceX96 != 0, 'I'); + require( + params.zeroForOne + ? params.sqrtPriceLimitX96 < slot0Start.sqrtPriceX96 && + params.sqrtPriceLimitX96 > TickMath.MIN_SQRT_RATIO + : params.sqrtPriceLimitX96 > slot0Start.sqrtPriceX96 && + params.sqrtPriceLimitX96 < TickMath.MAX_SQRT_RATIO, + 'SPL' + ); + + SwapCache memory cache = SwapCache({ + liquidityStart: self.liquidity, + feeProtocol: params.zeroForOne ? (slot0Start.feeProtocol % 16) : (slot0Start.feeProtocol >> 4), + secondsPerLiquidityCumulativeX128: 0, + tickCumulative: 0, + computedLatestObservation: false + }); + + bool exactInput = params.amountSpecified > 0; + + SwapState memory state = SwapState({ + amountSpecifiedRemaining: params.amountSpecified, + amountCalculated: 0, + sqrtPriceX96: slot0Start.sqrtPriceX96, + tick: slot0Start.tick, + feeGrowthGlobalX128: params.zeroForOne ? self.feeGrowthGlobal0X128 : self.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 != params.sqrtPriceLimitX96) { + StepComputations memory step; + + step.sqrtPriceStartX96 = state.sqrtPriceX96; + + (step.tickNext, step.initialized) = self.tickBitmap.nextInitializedTickWithinOneWord( + state.tick, + params.tickSpacing, + params.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, + ( + params.zeroForOne + ? step.sqrtPriceNextX96 < params.sqrtPriceLimitX96 + : step.sqrtPriceNextX96 > params.sqrtPriceLimitX96 + ) + ? params.sqrtPriceLimitX96 + : step.sqrtPriceNextX96, + state.liquidity, + state.amountSpecifiedRemaining, + params.fee + ); + + if (exactInput) { + // safe because we test that amountSpecified > amountIn + feeAmount in SwapMath + unchecked { + state.amountSpecifiedRemaining -= (step.amountIn + step.feeAmount).toInt256(); + } + state.amountCalculated = state.amountCalculated - step.amountOut.toInt256(); + } else { + unchecked { + state.amountSpecifiedRemaining += step.amountOut.toInt256(); + } + state.amountCalculated = state.amountCalculated + (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) { + unchecked { + uint256 delta = step.feeAmount / cache.feeProtocol; + step.feeAmount -= delta; + state.protocolFee += uint128(delta); + } + } + + // update global fee tracker + if (state.liquidity > 0) { + unchecked { + 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) = self + .observations + .observeSingle( + params.time, + 0, + slot0Start.tick, + slot0Start.observationIndex, + cache.liquidityStart, + slot0Start.observationCardinality + ); + cache.computedLatestObservation = true; + } + int128 liquidityNet = self.ticks.cross( + step.tickNext, + (params.zeroForOne ? state.feeGrowthGlobalX128 : self.feeGrowthGlobal0X128), + (params.zeroForOne ? self.feeGrowthGlobal1X128 : state.feeGrowthGlobalX128), + cache.secondsPerLiquidityCumulativeX128, + cache.tickCumulative, + params.time + ); + // if we're moving leftward, we interpret liquidityNet as the opposite sign + // safe because liquidityNet cannot be type(int128).min + unchecked { + if (params.zeroForOne) liquidityNet = -liquidityNet; + } + + state.liquidity = liquidityNet < 0 + ? state.liquidity - uint128(-liquidityNet) + : state.liquidity + uint128(liquidityNet); + } + + unchecked { + state.tick = params.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) { + (self.slot0.observationIndex, self.slot0.observationCardinality) = self.observations.write( + slot0Start.observationIndex, + params.time, + slot0Start.tick, + cache.liquidityStart, + slot0Start.observationCardinality, + slot0Start.observationCardinalityNext + ); + (self.slot0.sqrtPriceX96, self.slot0.tick) = (state.sqrtPriceX96, state.tick); + } else { + // otherwise just update the price + self.slot0.sqrtPriceX96 = state.sqrtPriceX96; + } + + // update liquidity if it changed + if (cache.liquidityStart != state.liquidity) self.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 (params.zeroForOne) { + self.feeGrowthGlobal0X128 = state.feeGrowthGlobalX128; + unchecked { + if (state.protocolFee > 0) self.protocolFees.token0 += state.protocolFee; + } + } else { + self.feeGrowthGlobal1X128 = state.feeGrowthGlobalX128; + unchecked { + if (state.protocolFee > 0) self.protocolFees.token1 += state.protocolFee; + } + } + + unchecked { + (result.amount0, result.amount1) = params.zeroForOne == exactInput + ? (params.amountSpecified - state.amountSpecifiedRemaining, state.amountCalculated) + : (state.amountCalculated, params.amountSpecified - state.amountSpecifiedRemaining); + } + } + + /// @notice Updates the protocol fee for a given pool + function setFeeProtocol(State storage self, uint8 feeProtocol) internal returns (uint8 feeProtocolOld) { + (uint8 feeProtocol0, uint8 feeProtocol1) = (feeProtocol >> 4, feeProtocol % 16); + require( + (feeProtocol0 == 0 || (feeProtocol0 >= 4 && feeProtocol0 <= 10)) && + (feeProtocol1 == 0 || (feeProtocol1 >= 4 && feeProtocol1 <= 10)) + ); + feeProtocolOld = self.slot0.feeProtocol; + self.slot0.feeProtocol = feeProtocol; + } +} diff --git a/contracts/libraries/Position.sol b/contracts/libraries/Position.sol index 8f1402731..1d6ff339a 100644 --- a/contracts/libraries/Position.sol +++ b/contracts/libraries/Position.sol @@ -15,9 +15,6 @@ library Position { // 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 @@ -40,12 +37,14 @@ library Position { /// @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 + /// @return feesOwed0 The amount of token0 owed to the position owner + /// @return feesOwed1 The amount of token1 owed to the position owner function update( Info storage self, int128 liquidityDelta, uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128 - ) internal { + ) internal returns (uint256 feesOwed0, uint256 feesOwed1) { Info memory _self = self; uint128 liquidityNext; @@ -59,22 +58,16 @@ library Position { } // calculate accumulated fees. overflow in the subtraction of fee growth is expected - uint128 tokensOwed0; - uint128 tokensOwed1; unchecked { - tokensOwed0 = uint128( - FullMath.mulDiv( - feeGrowthInside0X128 - _self.feeGrowthInside0LastX128, - _self.liquidity, - FixedPoint128.Q128 - ) + feesOwed0 = FullMath.mulDiv( + feeGrowthInside0X128 - _self.feeGrowthInside0LastX128, + _self.liquidity, + FixedPoint128.Q128 ); - tokensOwed1 = uint128( - FullMath.mulDiv( - feeGrowthInside1X128 - _self.feeGrowthInside1LastX128, - _self.liquidity, - FixedPoint128.Q128 - ) + feesOwed1 = FullMath.mulDiv( + feeGrowthInside1X128 - _self.feeGrowthInside1LastX128, + _self.liquidity, + FixedPoint128.Q128 ); } @@ -82,12 +75,5 @@ library Position { if (liquidityDelta != 0) self.liquidity = liquidityNext; self.feeGrowthInside0LastX128 = feeGrowthInside0X128; self.feeGrowthInside1LastX128 = feeGrowthInside1X128; - // overflow is acceptable, user must withdraw before they hit type(uint128).max fees - unchecked { - if (tokensOwed0 > 0 || tokensOwed1 > 0) { - self.tokensOwed0 += tokensOwed0; - self.tokensOwed1 += tokensOwed1; - } - } } } diff --git a/contracts/libraries/TransferHelper.sol b/contracts/libraries/TransferHelper.sol index eaa6e8235..7972e66c8 100644 --- a/contracts/libraries/TransferHelper.sol +++ b/contracts/libraries/TransferHelper.sol @@ -12,11 +12,11 @@ library TransferHelper { /// @param to The recipient of the transfer /// @param value The value of the transfer function safeTransfer( - address token, + IERC20Minimal token, address to, uint256 value ) internal { - (bool success, bytes memory data) = token.call( + (bool success, bytes memory data) = address(token).call( abi.encodeWithSelector(IERC20Minimal.transfer.selector, to, value) ); require(success && (data.length == 0 || abi.decode(data, (bool))), 'TF'); diff --git a/contracts/test/MockTimePool.sol b/contracts/test/MockTimePool.sol deleted file mode 100644 index 2b2a96fdd..000000000 --- a/contracts/test/MockTimePool.sol +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity =0.8.10; - -import {Pool} from '../Pool.sol'; - -// used for testing time dependent behavior -contract MockTimePool is Pool { - // Monday, October 5, 2020 9:00:00 AM GMT-05:00 - uint256 public time = 1601906400; - - function setFeeGrowthGlobal0X128(uint256 _feeGrowthGlobal0X128) external { - feeGrowthGlobal0X128 = _feeGrowthGlobal0X128; - } - - function setFeeGrowthGlobal1X128(uint256 _feeGrowthGlobal1X128) external { - feeGrowthGlobal1X128 = _feeGrowthGlobal1X128; - } - - function advanceTime(uint256 by) external { - time += by; - } - - function _blockTimestamp() internal view override returns (uint32) { - return uint32(time); - } -} diff --git a/contracts/test/MockTimePoolDeployer.sol b/contracts/test/MockTimePoolDeployer.sol deleted file mode 100644 index b178dc47b..000000000 --- a/contracts/test/MockTimePoolDeployer.sol +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity =0.8.10; - -import {IPoolDeployer} from '../interfaces/IPoolDeployer.sol'; - -import {MockTimePool} from './MockTimePool.sol'; - -contract MockTimePoolDeployer is IPoolDeployer { - struct Parameters { - address factory; - address token0; - address token1; - uint24 fee; - int24 tickSpacing; - } - - Parameters public override parameters; - - event PoolDeployed(address pool); - - function deploy( - address factory, - address token0, - address token1, - uint24 fee, - int24 tickSpacing - ) external returns (address pool) { - parameters = Parameters({factory: factory, token0: token0, token1: token1, fee: fee, tickSpacing: tickSpacing}); - pool = address(new MockTimePool{salt: keccak256(abi.encodePacked(token0, token1, fee, tickSpacing))}()); - emit PoolDeployed(pool); - delete parameters; - } -} diff --git a/contracts/test/MockTimePoolManager.sol b/contracts/test/MockTimePoolManager.sol new file mode 100644 index 000000000..0a90eb3af --- /dev/null +++ b/contracts/test/MockTimePoolManager.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.10; + +import {PoolManager} from '../PoolManager.sol'; + +contract MockTimePoolManager is PoolManager { + uint32 public time; + + function _blockTimestamp() internal view override returns (uint32) { + return time; + } + + function advanceTime(uint32 by) external { + unchecked { + time += by; + } + } +} diff --git a/contracts/test/MultihopTester.sol b/contracts/test/MultihopTester.sol deleted file mode 100644 index 4d7b2745a..000000000 --- a/contracts/test/MultihopTester.sol +++ /dev/null @@ -1,82 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity =0.8.10; - -import {SafeCast} from '../libraries/SafeCast.sol'; -import {TickMath} from '../libraries/TickMath.sol'; - -import {IERC20Minimal} from '../interfaces/external/IERC20Minimal.sol'; -import {ISwapCallback} from '../interfaces/callback/ISwapCallback.sol'; -import {IPool} from '../interfaces/IPool.sol'; - -contract MultihopTester is ISwapCallback { - using SafeCast for uint256; - - // flash swaps for an exact amount of token0 in the output pool - function swapForExact0Multi( - address recipient, - address poolInput, - address poolOutput, - uint256 amount0Out - ) external { - address[] memory pools = new address[](1); - pools[0] = poolInput; - IPool(poolOutput).swap( - recipient, - false, - -amount0Out.toInt256(), - TickMath.MAX_SQRT_RATIO - 1, - abi.encode(pools, msg.sender) - ); - } - - // flash swaps for an exact amount of token1 in the output pool - function swapForExact1Multi( - address recipient, - address poolInput, - address poolOutput, - uint256 amount1Out - ) external { - address[] memory pools = new address[](1); - pools[0] = poolInput; - IPool(poolOutput).swap( - recipient, - true, - -amount1Out.toInt256(), - TickMath.MIN_SQRT_RATIO + 1, - abi.encode(pools, msg.sender) - ); - } - - event SwapCallback(int256 amount0Delta, int256 amount1Delta); - - function swapCallback( - int256 amount0Delta, - int256 amount1Delta, - bytes calldata data - ) public override { - emit SwapCallback(amount0Delta, amount1Delta); - - (address[] memory pools, address payer) = abi.decode(data, (address[], address)); - - if (pools.length == 1) { - // get the address and amount of the token that we need to pay - address tokenToBePaid = amount0Delta > 0 ? IPool(msg.sender).token0() : IPool(msg.sender).token1(); - int256 amountToBePaid = amount0Delta > 0 ? amount0Delta : amount1Delta; - - bool zeroForOne = tokenToBePaid == IPool(pools[0]).token1(); - IPool(pools[0]).swap( - msg.sender, - zeroForOne, - -amountToBePaid, - zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1, - abi.encode(new address[](0), payer) - ); - } else { - if (amount0Delta > 0) { - IERC20Minimal(IPool(msg.sender).token0()).transferFrom(payer, msg.sender, uint256(amount0Delta)); - } else { - IERC20Minimal(IPool(msg.sender).token1()).transferFrom(payer, msg.sender, uint256(amount1Delta)); - } - } - } -} diff --git a/contracts/test/PoolBurnTest.sol b/contracts/test/PoolBurnTest.sol new file mode 100644 index 000000000..ba8eb9005 --- /dev/null +++ b/contracts/test/PoolBurnTest.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.8.10; + +import {IERC20Minimal} from '../interfaces/external/IERC20Minimal.sol'; + +import {ILockCallback} from '../interfaces/callback/ILockCallback.sol'; +import {IPoolManager} from '../interfaces/IPoolManager.sol'; + +import {Pool} from '../libraries/Pool.sol'; + +contract PoolBurnTest is ILockCallback { + IPoolManager public immutable manager; + + constructor(IPoolManager _manager) { + manager = _manager; + } + + struct CallbackData { + address sender; + IPoolManager.PoolKey key; + IPoolManager.BurnParams params; + } + + function burn(IPoolManager.PoolKey memory key, IPoolManager.BurnParams memory params) + external + returns (Pool.BalanceDelta memory delta) + { + delta = abi.decode(manager.lock(abi.encode(CallbackData(msg.sender, key, params))), (Pool.BalanceDelta)); + } + + function lockAcquired(bytes calldata rawData) external returns (bytes memory) { + require(msg.sender == address(manager)); + + CallbackData memory data = abi.decode(rawData, (CallbackData)); + + Pool.BalanceDelta memory delta = manager.burn(data.key, data.params); + + if (delta.amount0 < 0) { + manager.take(data.key.token0, data.sender, uint256(-delta.amount0)); + } + if (delta.amount1 < 0) { + manager.take(data.key.token1, data.sender, uint256(-delta.amount1)); + } + + return abi.encode(delta); + } +} diff --git a/contracts/test/PoolManagerTest.sol b/contracts/test/PoolManagerTest.sol new file mode 100644 index 000000000..2ffb5afb1 --- /dev/null +++ b/contracts/test/PoolManagerTest.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.8.10; + +import {IERC20Minimal} from '../interfaces/external/IERC20Minimal.sol'; +import {IPoolManager} from '../interfaces/IPoolManager.sol'; +import {ILockCallback} from '../interfaces/callback/ILockCallback.sol'; + +contract PoolManagerTest is ILockCallback { + function lock(IPoolManager manager) external { + manager.lock(''); + } + + /// @notice Called by the pool manager on `msg.sender` when a lock is acquired + function lockAcquired(bytes calldata) external override returns (bytes memory) {} +} diff --git a/contracts/test/PoolMintTest.sol b/contracts/test/PoolMintTest.sol new file mode 100644 index 000000000..cb83e5fb9 --- /dev/null +++ b/contracts/test/PoolMintTest.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.8.10; + +import {IERC20Minimal} from '../interfaces/external/IERC20Minimal.sol'; + +import {ILockCallback} from '../interfaces/callback/ILockCallback.sol'; +import {IPoolManager} from '../interfaces/IPoolManager.sol'; + +import {Pool} from '../libraries/Pool.sol'; + +contract PoolMintTest is ILockCallback { + IPoolManager public immutable manager; + + constructor(IPoolManager _manager) { + manager = _manager; + } + + struct CallbackData { + address sender; + IPoolManager.PoolKey key; + IPoolManager.MintParams params; + } + + function mint(IPoolManager.PoolKey memory key, IPoolManager.MintParams memory params) + external + returns (Pool.BalanceDelta memory delta) + { + delta = abi.decode(manager.lock(abi.encode(CallbackData(msg.sender, key, params))), (Pool.BalanceDelta)); + } + + function lockAcquired(bytes calldata rawData) external returns (bytes memory) { + require(msg.sender == address(manager)); + + CallbackData memory data = abi.decode(rawData, (CallbackData)); + + Pool.BalanceDelta memory delta = manager.mint(data.key, data.params); + + if (delta.amount0 > 0) { + data.key.token0.transferFrom(data.sender, address(manager), uint256(delta.amount0)); + manager.settle(data.key.token0); + } + if (delta.amount1 > 0) { + data.key.token1.transferFrom(data.sender, address(manager), uint256(delta.amount1)); + manager.settle(data.key.token1); + } + + return abi.encode(delta); + } +} diff --git a/contracts/test/PoolSwapTest.sol b/contracts/test/PoolSwapTest.sol index 00390dfaf..2dc9bece9 100644 --- a/contracts/test/PoolSwapTest.sol +++ b/contracts/test/PoolSwapTest.sol @@ -3,48 +3,54 @@ pragma solidity =0.8.10; import {IERC20Minimal} from '../interfaces/external/IERC20Minimal.sol'; -import {ISwapCallback} from '../interfaces/callback/ISwapCallback.sol'; -import {IPool} from '../interfaces/IPool.sol'; - -contract PoolSwapTest is ISwapCallback { - int256 private _amount0Delta; - int256 private _amount1Delta; - - function getSwapResult( - address pool, - bool zeroForOne, - int256 amountSpecified, - uint160 sqrtPriceLimitX96 - ) +import {ILockCallback} from '../interfaces/callback/ILockCallback.sol'; +import {IPoolManager} from '../interfaces/IPoolManager.sol'; + +import {Pool} from '../libraries/Pool.sol'; + +contract PoolSwapTest is ILockCallback { + IPoolManager public immutable manager; + + constructor(IPoolManager _manager) { + manager = _manager; + } + + struct CallbackData { + address sender; + IPoolManager.PoolKey key; + IPoolManager.SwapParams params; + } + + function swap(IPoolManager.PoolKey memory key, IPoolManager.SwapParams memory params) external - returns ( - int256 amount0Delta, - int256 amount1Delta, - uint160 nextSqrtRatio - ) + returns (Pool.BalanceDelta memory delta) { - (amount0Delta, amount1Delta) = IPool(pool).swap( - address(0), - zeroForOne, - amountSpecified, - sqrtPriceLimitX96, - abi.encode(msg.sender) - ); - - (nextSqrtRatio, , , , , , ) = IPool(pool).slot0(); + delta = abi.decode(manager.lock(abi.encode(CallbackData(msg.sender, key, params))), (Pool.BalanceDelta)); } - function swapCallback( - int256 amount0Delta, - int256 amount1Delta, - bytes calldata data - ) external override { - address sender = abi.decode(data, (address)); - - if (amount0Delta > 0) { - IERC20Minimal(IPool(msg.sender).token0()).transferFrom(sender, msg.sender, uint256(amount0Delta)); - } else if (amount1Delta > 0) { - IERC20Minimal(IPool(msg.sender).token1()).transferFrom(sender, msg.sender, uint256(amount1Delta)); + function lockAcquired(bytes calldata rawData) external returns (bytes memory) { + require(msg.sender == address(manager)); + + CallbackData memory data = abi.decode(rawData, (CallbackData)); + + Pool.BalanceDelta memory delta = manager.swap(data.key, data.params); + + if (data.params.zeroForOne) { + if (delta.amount0 > 0) { + data.key.token0.transferFrom(data.sender, address(manager), uint256(delta.amount0)); + manager.settle(data.key.token0); + } + if (delta.amount1 < 0) manager.take(data.key.token1, data.sender, uint256(-delta.amount1)); + } else { + if (delta.amount1 > 0) { + data.key.token1.transferFrom(data.sender, address(manager), uint256(delta.amount1)); + manager.settle(data.key.token1); + } + if (delta.amount0 < 0) { + manager.take(data.key.token0, data.sender, uint256(-delta.amount0)); + } } + + return abi.encode(delta); } } diff --git a/contracts/test/ReentrancyTester.sol b/contracts/test/ReentrancyTester.sol deleted file mode 100644 index f793a721f..000000000 --- a/contracts/test/ReentrancyTester.sol +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity =0.8.10; - -import {TickMath} from '../libraries/TickMath.sol'; - -import {ISwapCallback} from '../interfaces/callback/ISwapCallback.sol'; - -import {IPool} from '../interfaces/IPool.sol'; - -contract ReentrancyTester is ISwapCallback { - string private constant expectedReason = 'LOK'; - - function swapToReenter(address pool) external { - IPool(pool).swap(address(0), false, 1, TickMath.MAX_SQRT_RATIO - 1, new bytes(0)); - } - - function swapCallback( - int256, - int256, - bytes calldata - ) external override { - // try to reenter swap - try IPool(msg.sender).swap(address(0), false, 1, 0, new bytes(0)) {} catch Error(string memory reason) { - require(keccak256(abi.encode(reason)) == keccak256(abi.encode(expectedReason))); - } - - // try to reenter mint - try IPool(msg.sender).mint(address(0), 0, 0, 0, new bytes(0)) {} catch Error(string memory reason) { - require(keccak256(abi.encode(reason)) == keccak256(abi.encode(expectedReason))); - } - - // try to reenter collect - try IPool(msg.sender).collect(address(0), 0, 0, 0, 0) {} catch Error(string memory reason) { - require(keccak256(abi.encode(reason)) == keccak256(abi.encode(expectedReason))); - } - - // try to reenter burn - try IPool(msg.sender).burn(0, 0, 0) {} catch Error(string memory reason) { - require(keccak256(abi.encode(reason)) == keccak256(abi.encode(expectedReason))); - } - - // try to reenter flash - try IPool(msg.sender).flash(address(0), 0, 0, new bytes(0)) {} catch Error(string memory reason) { - require(keccak256(abi.encode(reason)) == keccak256(abi.encode(expectedReason))); - } - - // try to reenter collectProtocol - try IPool(msg.sender).collectProtocol(address(0), 0, 0) {} catch Error(string memory reason) { - require(keccak256(abi.encode(reason)) == keccak256(abi.encode(expectedReason))); - } - - require(false, 'Unable to reenter'); - } -} diff --git a/contracts/test/SwapPaymentTest.sol b/contracts/test/SwapPaymentTest.sol deleted file mode 100644 index cbfb98d99..000000000 --- a/contracts/test/SwapPaymentTest.sol +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity =0.8.10; - -import {IERC20Minimal} from '../interfaces/external/IERC20Minimal.sol'; - -import {ISwapCallback} from '../interfaces/callback/ISwapCallback.sol'; -import {IPool} from '../interfaces/IPool.sol'; - -contract SwapPaymentTest is ISwapCallback { - function swap( - address pool, - address recipient, - bool zeroForOne, - uint160 sqrtPriceX96, - int256 amountSpecified, - uint256 pay0, - uint256 pay1 - ) external { - IPool(pool).swap(recipient, zeroForOne, amountSpecified, sqrtPriceX96, abi.encode(msg.sender, pay0, pay1)); - } - - function swapCallback( - int256, - int256, - bytes calldata data - ) external override { - (address sender, uint256 pay0, uint256 pay1) = abi.decode(data, (address, uint256, uint256)); - - if (pay0 > 0) { - IERC20Minimal(IPool(msg.sender).token0()).transferFrom(sender, msg.sender, uint256(pay0)); - } else if (pay1 > 0) { - IERC20Minimal(IPool(msg.sender).token1()).transferFrom(sender, msg.sender, uint256(pay1)); - } - } -} diff --git a/contracts/test/SwapTarget.sol b/contracts/test/SwapTarget.sol deleted file mode 100644 index 738557bf9..000000000 --- a/contracts/test/SwapTarget.sol +++ /dev/null @@ -1,140 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity =0.8.10; - -import {IERC20Minimal} from '../interfaces/external/IERC20Minimal.sol'; - -import {SafeCast} from '../libraries/SafeCast.sol'; -import {TickMath} from '../libraries/TickMath.sol'; - -import {IMintCallback} from '../interfaces/callback/IMintCallback.sol'; -import {ISwapCallback} from '../interfaces/callback/ISwapCallback.sol'; -import {IFlashCallback} from '../interfaces/callback/IFlashCallback.sol'; - -import {IPool} from '../interfaces/IPool.sol'; - -contract SwapTarget is IMintCallback, ISwapCallback, IFlashCallback { - using SafeCast for uint256; - - function swapExact0For1( - address pool, - uint256 amount0In, - address recipient, - uint160 sqrtPriceLimitX96 - ) external { - IPool(pool).swap(recipient, true, amount0In.toInt256(), sqrtPriceLimitX96, abi.encode(msg.sender)); - } - - function swap0ForExact1( - address pool, - uint256 amount1Out, - address recipient, - uint160 sqrtPriceLimitX96 - ) external { - IPool(pool).swap(recipient, true, -amount1Out.toInt256(), sqrtPriceLimitX96, abi.encode(msg.sender)); - } - - function swapExact1For0( - address pool, - uint256 amount1In, - address recipient, - uint160 sqrtPriceLimitX96 - ) external { - IPool(pool).swap(recipient, false, amount1In.toInt256(), sqrtPriceLimitX96, abi.encode(msg.sender)); - } - - function swap1ForExact0( - address pool, - uint256 amount0Out, - address recipient, - uint160 sqrtPriceLimitX96 - ) external { - IPool(pool).swap(recipient, false, -amount0Out.toInt256(), sqrtPriceLimitX96, abi.encode(msg.sender)); - } - - function swapToLowerSqrtPrice( - address pool, - uint160 sqrtPriceX96, - address recipient - ) external { - IPool(pool).swap(recipient, true, type(int256).max, sqrtPriceX96, abi.encode(msg.sender)); - } - - function swapToHigherSqrtPrice( - address pool, - uint160 sqrtPriceX96, - address recipient - ) external { - IPool(pool).swap(recipient, false, type(int256).max, sqrtPriceX96, abi.encode(msg.sender)); - } - - event SwapCallback(int256 amount0Delta, int256 amount1Delta); - - function swapCallback( - int256 amount0Delta, - int256 amount1Delta, - bytes calldata data - ) external override { - address sender = abi.decode(data, (address)); - - emit SwapCallback(amount0Delta, amount1Delta); - - if (amount0Delta > 0) { - IERC20Minimal(IPool(msg.sender).token0()).transferFrom(sender, msg.sender, uint256(amount0Delta)); - } else if (amount1Delta > 0) { - IERC20Minimal(IPool(msg.sender).token1()).transferFrom(sender, msg.sender, uint256(amount1Delta)); - } else { - // if both are not gt 0, both must be 0. - assert(amount0Delta == 0 && amount1Delta == 0); - } - } - - function mint( - address pool, - address recipient, - int24 tickLower, - int24 tickUpper, - uint128 amount - ) external { - IPool(pool).mint(recipient, tickLower, tickUpper, amount, abi.encode(msg.sender)); - } - - event MintCallback(uint256 amount0Owed, uint256 amount1Owed); - - function mintCallback( - uint256 amount0Owed, - uint256 amount1Owed, - bytes calldata data - ) external override { - address sender = abi.decode(data, (address)); - - emit MintCallback(amount0Owed, amount1Owed); - if (amount0Owed > 0) IERC20Minimal(IPool(msg.sender).token0()).transferFrom(sender, msg.sender, amount0Owed); - if (amount1Owed > 0) IERC20Minimal(IPool(msg.sender).token1()).transferFrom(sender, msg.sender, amount1Owed); - } - - event FlashCallback(uint256 fee0, uint256 fee1); - - function flash( - address pool, - address recipient, - uint256 amount0, - uint256 amount1, - uint256 pay0, - uint256 pay1 - ) external { - IPool(pool).flash(recipient, amount0, amount1, abi.encode(msg.sender, pay0, pay1)); - } - - function flashCallback( - uint256 fee0, - uint256 fee1, - bytes calldata data - ) external override { - emit FlashCallback(fee0, fee1); - - (address sender, uint256 pay0, uint256 pay1) = abi.decode(data, (address, uint256, uint256)); - - if (pay0 > 0) IERC20Minimal(IPool(msg.sender).token0()).transferFrom(sender, msg.sender, pay0); - if (pay1 > 0) IERC20Minimal(IPool(msg.sender).token1()).transferFrom(sender, msg.sender, pay1); - } -} diff --git a/test/Multihop.spec.ts b/test/Multihop.spec.ts deleted file mode 100644 index 0b63f927c..000000000 --- a/test/Multihop.spec.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { Wallet } from 'ethers' -import { ethers, waffle } from 'hardhat' -import { TestERC20 } from '../typechain/TestERC20' -import { PoolFactory } from '../typechain/PoolFactory' -import { MockTimePool } from '../typechain/MockTimePool' -import { expect } from './shared/expect' - -import { poolFixture } from './shared/fixtures' - -import { - FeeAmount, - TICK_SPACINGS, - createPoolFunctions, - PoolFunctions, - createMultiPoolFunctions, - encodeSqrtPriceX96, - getMinTick, - getMaxTick, - expandTo18Decimals, -} from './shared/utilities' -import { MultihopTester } from '../typechain/MultihopTester' -import { SwapTarget } from '../typechain/SwapTarget' - -const feeAmount = FeeAmount.MEDIUM -const tickSpacing = TICK_SPACINGS[feeAmount] - -const createFixtureLoader = waffle.createFixtureLoader - -type ThenArg = T extends PromiseLike ? U : T - -describe('Pool', () => { - let wallet: Wallet, other: Wallet - - let token0: TestERC20 - let token1: TestERC20 - let token2: TestERC20 - let factory: PoolFactory - let pool0: MockTimePool - let pool1: MockTimePool - - let pool0Functions: PoolFunctions - let pool1Functions: PoolFunctions - - let minTick: number - let maxTick: number - - let swapTargetCallee: SwapTarget - let swapTargetRouter: MultihopTester - - let loadFixture: ReturnType - let createPool: ThenArg>['createPool'] - - before('create fixture loader', async () => { - ;[wallet, other] = await (ethers as any).getSigners() - - loadFixture = createFixtureLoader([wallet, other]) - }) - - beforeEach('deploy first fixture', async () => { - ;({ token0, token1, token2, factory, createPool, swapTargetCallee, swapTargetRouter } = await loadFixture( - poolFixture - )) - - const createPoolWrapped = async ( - amount: number, - spacing: number, - firstToken: TestERC20, - secondToken: TestERC20 - ): Promise<[MockTimePool, any]> => { - const pool = await createPool(amount, spacing, firstToken, secondToken) - const poolFunctions = createPoolFunctions({ - swapTarget: swapTargetCallee, - token0: firstToken, - token1: secondToken, - pool, - }) - minTick = getMinTick(spacing) - maxTick = getMaxTick(spacing) - return [pool, poolFunctions] - } - - // default to the 30 bips pool - ;[pool0, pool0Functions] = await createPoolWrapped(feeAmount, tickSpacing, token0, token1) - ;[pool1, pool1Functions] = await createPoolWrapped(feeAmount, tickSpacing, token1, token2) - }) - - it('constructor initializes immutables', async () => { - expect(await pool0.factory()).to.eq(factory.address) - expect(await pool0.token0()).to.eq(token0.address) - expect(await pool0.token1()).to.eq(token1.address) - expect(await pool1.factory()).to.eq(factory.address) - expect(await pool1.token0()).to.eq(token1.address) - expect(await pool1.token1()).to.eq(token2.address) - }) - - describe('multi-swaps', () => { - let inputToken: TestERC20 - let outputToken: TestERC20 - - beforeEach('initialize both pools', async () => { - inputToken = token0 - outputToken = token2 - - await pool0.initialize(encodeSqrtPriceX96(1, 1)) - await pool1.initialize(encodeSqrtPriceX96(1, 1)) - - await pool0Functions.mint(wallet.address, minTick, maxTick, expandTo18Decimals(1)) - await pool1Functions.mint(wallet.address, minTick, maxTick, expandTo18Decimals(1)) - }) - - it('multi-swap', async () => { - const token0OfPoolOutput = await pool1.token0() - const ForExact0 = outputToken.address === token0OfPoolOutput - - const { swapForExact0Multi, swapForExact1Multi } = createMultiPoolFunctions({ - inputToken: token0, - swapTarget: swapTargetRouter, - poolInput: pool0, - poolOutput: pool1, - }) - - const method = ForExact0 ? swapForExact0Multi : swapForExact1Multi - - await expect(method(100, wallet.address)) - .to.emit(outputToken, 'Transfer') - .withArgs(pool1.address, wallet.address, 100) - .to.emit(token1, 'Transfer') - .withArgs(pool0.address, pool1.address, 102) - .to.emit(inputToken, 'Transfer') - .withArgs(wallet.address, pool0.address, 104) - }) - }) -}) diff --git a/test/Oracle.spec.ts b/test/Oracle.spec.ts index 36e6fd065..ee83df1cb 100644 --- a/test/Oracle.spec.ts +++ b/test/Oracle.spec.ts @@ -3,7 +3,6 @@ import { ethers, waffle } from 'hardhat' import { OracleTest } from '../typechain/OracleTest' import checkObservationEquals from './shared/checkObservationEquals' import { expect } from './shared/expect' -import { TEST_POOL_START_TIME } from './shared/fixtures' import snapshotGasCost from './shared/snapshotGasCost' import { MaxUint128 } from './shared/utilities' @@ -635,7 +634,8 @@ describe('Oracle', () => { const BATCH_SIZE = 300 - const STARTING_TIME = TEST_POOL_START_TIME + // Monday, October 5, 2020 9:00:00 AM GMT-05:00 + const STARTING_TIME = 1601906400 const maxedOutOracleFixture = async () => { const oracle = await oracleFixture() diff --git a/test/Pool.arbitrage.spec.ts b/test/Pool.arbitrage.spec.ts deleted file mode 100644 index 0cbb23413..000000000 --- a/test/Pool.arbitrage.spec.ts +++ /dev/null @@ -1,358 +0,0 @@ -import Decimal from 'decimal.js' -import { BigNumber, BigNumberish, Wallet } from 'ethers' -import { ethers, waffle } from 'hardhat' -import { MockTimePool } from '../typechain/MockTimePool' -import { TickMathTest } from '../typechain/TickMathTest' -import { PoolSwapTest } from '../typechain/PoolSwapTest' -import { expect } from './shared/expect' - -import { poolFixture } from './shared/fixtures' -import { formatPrice, formatTokenAmount } from './shared/format' - -import { - createPoolFunctions, - encodeSqrtPriceX96, - expandTo18Decimals, - FeeAmount, - getMaxLiquidityPerTick, - getMaxTick, - getMinTick, - MAX_SQRT_RATIO, - MaxUint128, - MIN_SQRT_RATIO, - MintFunction, - SwapFunction, - TICK_SPACINGS, -} from './shared/utilities' - -const { - constants: { MaxUint256 }, -} = ethers - -const createFixtureLoader = waffle.createFixtureLoader - -Decimal.config({ toExpNeg: -500, toExpPos: 500 }) - -function applySqrtRatioBipsHundredthsDelta(sqrtRatio: BigNumber, bipsHundredths: number): BigNumber { - return BigNumber.from( - new Decimal( - sqrtRatio - .mul(sqrtRatio) - .mul(1e6 + bipsHundredths) - .div(1e6) - .toString() - ) - .sqrt() - .floor() - .toString() - ) -} - -describe('Pool arbitrage tests', () => { - let wallet: Wallet, arbitrageur: Wallet - - let loadFixture: ReturnType - - before('create fixture loader', async () => { - ;[wallet, arbitrageur] = await (ethers as any).getSigners() - loadFixture = createFixtureLoader([wallet, arbitrageur]) - }) - - for (const feeProtocol of [0, 6]) { - describe(`protocol fee = ${feeProtocol};`, () => { - const startingPrice = encodeSqrtPriceX96(1, 1) - const startingTick = 0 - const feeAmount = FeeAmount.MEDIUM - const tickSpacing = TICK_SPACINGS[feeAmount] - const minTick = getMinTick(tickSpacing) - const maxTick = getMaxTick(tickSpacing) - - for (const passiveLiquidity of [ - expandTo18Decimals(1).div(100), - expandTo18Decimals(1), - expandTo18Decimals(10), - expandTo18Decimals(100), - ]) { - describe(`passive liquidity of ${formatTokenAmount(passiveLiquidity)}`, () => { - const arbTestFixture = async ([wallet, arbitrageur]: Wallet[]) => { - const fix = await poolFixture([wallet], waffle.provider) - - const pool = await fix.createPool(feeAmount, tickSpacing) - - await fix.token0.transfer(arbitrageur.address, BigNumber.from(2).pow(254)) - await fix.token1.transfer(arbitrageur.address, BigNumber.from(2).pow(254)) - - const { swapExact0For1, swapToHigherPrice, swapToLowerPrice, swapExact1For0, mint } = - await createPoolFunctions({ - swapTarget: fix.swapTargetCallee, - token0: fix.token0, - token1: fix.token1, - pool, - }) - - const testerFactory = await ethers.getContractFactory('PoolSwapTest') - const tester = (await testerFactory.deploy()) as PoolSwapTest - - const tickMathFactory = await ethers.getContractFactory('TickMathTest') - const tickMath = (await tickMathFactory.deploy()) as TickMathTest - - await fix.token0.approve(tester.address, MaxUint256) - await fix.token1.approve(tester.address, MaxUint256) - - await pool.initialize(startingPrice) - if (feeProtocol != 0) await pool.setFeeProtocol(feeProtocol, feeProtocol) - await mint(wallet.address, minTick, maxTick, passiveLiquidity) - - expect((await pool.slot0()).tick).to.eq(startingTick) - expect((await pool.slot0()).sqrtPriceX96).to.eq(startingPrice) - - return { pool, swapExact0For1, mint, swapToHigherPrice, swapToLowerPrice, swapExact1For0, tester, tickMath } - } - - let swapExact0For1: SwapFunction - let swapToHigherPrice: SwapFunction - let swapToLowerPrice: SwapFunction - let swapExact1For0: SwapFunction - let pool: MockTimePool - let mint: MintFunction - let tester: PoolSwapTest - let tickMath: TickMathTest - - beforeEach('load the fixture', async () => { - ;({ swapExact0For1, pool, mint, swapToHigherPrice, swapToLowerPrice, swapExact1For0, tester, tickMath } = - await loadFixture(arbTestFixture)) - }) - - async function simulateSwap( - zeroForOne: boolean, - amountSpecified: BigNumberish, - sqrtPriceLimitX96?: BigNumber - ): Promise<{ - executionPrice: BigNumber - nextSqrtRatio: BigNumber - amount0Delta: BigNumber - amount1Delta: BigNumber - }> { - const { amount0Delta, amount1Delta, nextSqrtRatio } = await tester.callStatic.getSwapResult( - pool.address, - zeroForOne, - amountSpecified, - sqrtPriceLimitX96 ?? (zeroForOne ? MIN_SQRT_RATIO.add(1) : MAX_SQRT_RATIO.sub(1)) - ) - - const executionPrice = zeroForOne - ? encodeSqrtPriceX96(amount1Delta, amount0Delta.mul(-1)) - : encodeSqrtPriceX96(amount1Delta.mul(-1), amount0Delta) - - return { executionPrice, nextSqrtRatio, amount0Delta, amount1Delta } - } - - for (const { zeroForOne, assumedTruePriceAfterSwap, inputAmount, description } of [ - { - description: 'exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98', - zeroForOne: true, - inputAmount: expandTo18Decimals(10), - assumedTruePriceAfterSwap: encodeSqrtPriceX96(98, 100), - }, - { - description: 'exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01', - zeroForOne: true, - inputAmount: expandTo18Decimals(10), - assumedTruePriceAfterSwap: encodeSqrtPriceX96(101, 100), - }, - ]) { - describe(description, () => { - function valueToken1(arbBalance0: BigNumber, arbBalance1: BigNumber) { - return assumedTruePriceAfterSwap - .mul(assumedTruePriceAfterSwap) - .mul(arbBalance0) - .div(BigNumber.from(2).pow(192)) - .add(arbBalance1) - } - - it('not sandwiched', async () => { - const { executionPrice, amount1Delta, amount0Delta } = await simulateSwap(zeroForOne, inputAmount) - zeroForOne - ? await swapExact0For1(inputAmount, wallet.address) - : await swapExact1For0(inputAmount, wallet.address) - - expect({ - executionPrice: formatPrice(executionPrice), - amount0Delta: formatTokenAmount(amount0Delta), - amount1Delta: formatTokenAmount(amount1Delta), - priceAfter: formatPrice((await pool.slot0()).sqrtPriceX96), - }).to.matchSnapshot() - }) - - it('sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity', async () => { - const { executionPrice } = await simulateSwap(zeroForOne, inputAmount) - - const firstTickAboveMarginalPrice = zeroForOne - ? Math.ceil( - (await tickMath.getTickAtSqrtRatio( - applySqrtRatioBipsHundredthsDelta(executionPrice, feeAmount) - )) / tickSpacing - ) * tickSpacing - : Math.floor( - (await tickMath.getTickAtSqrtRatio( - applySqrtRatioBipsHundredthsDelta(executionPrice, -feeAmount) - )) / tickSpacing - ) * tickSpacing - const tickAfterFirstTickAboveMarginPrice = zeroForOne - ? firstTickAboveMarginalPrice - tickSpacing - : firstTickAboveMarginalPrice + tickSpacing - - const priceSwapStart = await tickMath.getSqrtRatioAtTick(firstTickAboveMarginalPrice) - - let arbBalance0 = BigNumber.from(0) - let arbBalance1 = BigNumber.from(0) - - // first frontrun to the first tick before the execution price - const { - amount0Delta: frontrunDelta0, - amount1Delta: frontrunDelta1, - executionPrice: frontrunExecutionPrice, - } = await simulateSwap(zeroForOne, MaxUint256.div(2), priceSwapStart) - arbBalance0 = arbBalance0.sub(frontrunDelta0) - arbBalance1 = arbBalance1.sub(frontrunDelta1) - zeroForOne - ? await swapToLowerPrice(priceSwapStart, arbitrageur.address) - : await swapToHigherPrice(priceSwapStart, arbitrageur.address) - - const profitToken1AfterFrontRun = valueToken1(arbBalance0, arbBalance1) - - const tickLower = zeroForOne ? tickAfterFirstTickAboveMarginPrice : firstTickAboveMarginalPrice - const tickUpper = zeroForOne ? firstTickAboveMarginalPrice : tickAfterFirstTickAboveMarginPrice - - // deposit max liquidity at the tick - const mintReceipt = await ( - await mint(wallet.address, tickLower, tickUpper, getMaxLiquidityPerTick(tickSpacing)) - ).wait() - // sub the mint costs - const { amount0: amount0Mint, amount1: amount1Mint } = pool.interface.decodeEventLog( - pool.interface.events['Mint(address,address,int24,int24,uint128,uint256,uint256)'], - mintReceipt.events?.[2].data! - ) - arbBalance0 = arbBalance0.sub(amount0Mint) - arbBalance1 = arbBalance1.sub(amount1Mint) - - // execute the user's swap - const { executionPrice: executionPriceAfterFrontrun } = await simulateSwap(zeroForOne, inputAmount) - zeroForOne - ? await swapExact0For1(inputAmount, wallet.address) - : await swapExact1For0(inputAmount, wallet.address) - - // burn the arb's liquidity - const { amount0: amount0Burn, amount1: amount1Burn } = await pool.callStatic.burn( - tickLower, - tickUpper, - getMaxLiquidityPerTick(tickSpacing) - ) - await pool.burn(tickLower, tickUpper, getMaxLiquidityPerTick(tickSpacing)) - arbBalance0 = arbBalance0.add(amount0Burn) - arbBalance1 = arbBalance1.add(amount1Burn) - - // add the fees as well - const { amount0: amount0CollectAndBurn, amount1: amount1CollectAndBurn } = - await pool.callStatic.collect(arbitrageur.address, tickLower, tickUpper, MaxUint128, MaxUint128) - const [amount0Collect, amount1Collect] = [ - amount0CollectAndBurn.sub(amount0Burn), - amount1CollectAndBurn.sub(amount1Burn), - ] - arbBalance0 = arbBalance0.add(amount0Collect) - arbBalance1 = arbBalance1.add(amount1Collect) - - const profitToken1AfterSandwich = valueToken1(arbBalance0, arbBalance1) - - // backrun the swap to true price, i.e. swap to the marginal price = true price - const priceToSwapTo = zeroForOne - ? applySqrtRatioBipsHundredthsDelta(assumedTruePriceAfterSwap, -feeAmount) - : applySqrtRatioBipsHundredthsDelta(assumedTruePriceAfterSwap, feeAmount) - const { - amount0Delta: backrunDelta0, - amount1Delta: backrunDelta1, - executionPrice: backrunExecutionPrice, - } = await simulateSwap(!zeroForOne, MaxUint256.div(2), priceToSwapTo) - await swapToHigherPrice(priceToSwapTo, wallet.address) - arbBalance0 = arbBalance0.sub(backrunDelta0) - arbBalance1 = arbBalance1.sub(backrunDelta1) - - expect({ - sandwichedPrice: formatPrice(executionPriceAfterFrontrun), - arbBalanceDelta0: formatTokenAmount(arbBalance0), - arbBalanceDelta1: formatTokenAmount(arbBalance1), - profit: { - final: formatTokenAmount(valueToken1(arbBalance0, arbBalance1)), - afterFrontrun: formatTokenAmount(profitToken1AfterFrontRun), - afterSandwich: formatTokenAmount(profitToken1AfterSandwich), - }, - backrun: { - executionPrice: formatPrice(backrunExecutionPrice), - delta0: formatTokenAmount(backrunDelta0), - delta1: formatTokenAmount(backrunDelta1), - }, - frontrun: { - executionPrice: formatPrice(frontrunExecutionPrice), - delta0: formatTokenAmount(frontrunDelta0), - delta1: formatTokenAmount(frontrunDelta1), - }, - collect: { - amount0: formatTokenAmount(amount0Collect), - amount1: formatTokenAmount(amount1Collect), - }, - burn: { - amount0: formatTokenAmount(amount0Burn), - amount1: formatTokenAmount(amount1Burn), - }, - mint: { - amount0: formatTokenAmount(amount0Mint), - amount1: formatTokenAmount(amount1Mint), - }, - finalPrice: formatPrice((await pool.slot0()).sqrtPriceX96), - }).to.matchSnapshot() - }) - - it('backrun to true price after swap only', async () => { - let arbBalance0 = BigNumber.from(0) - let arbBalance1 = BigNumber.from(0) - - zeroForOne - ? await swapExact0For1(inputAmount, wallet.address) - : await swapExact1For0(inputAmount, wallet.address) - - // swap to the marginal price = true price - const priceToSwapTo = zeroForOne - ? applySqrtRatioBipsHundredthsDelta(assumedTruePriceAfterSwap, -feeAmount) - : applySqrtRatioBipsHundredthsDelta(assumedTruePriceAfterSwap, feeAmount) - const { - amount0Delta: backrunDelta0, - amount1Delta: backrunDelta1, - executionPrice: backrunExecutionPrice, - } = await simulateSwap(!zeroForOne, MaxUint256.div(2), priceToSwapTo) - zeroForOne - ? await swapToHigherPrice(priceToSwapTo, wallet.address) - : await swapToLowerPrice(priceToSwapTo, wallet.address) - arbBalance0 = arbBalance0.sub(backrunDelta0) - arbBalance1 = arbBalance1.sub(backrunDelta1) - - expect({ - arbBalanceDelta0: formatTokenAmount(arbBalance0), - arbBalanceDelta1: formatTokenAmount(arbBalance1), - profit: { - final: formatTokenAmount(valueToken1(arbBalance0, arbBalance1)), - }, - backrun: { - executionPrice: formatPrice(backrunExecutionPrice), - delta0: formatTokenAmount(backrunDelta0), - delta1: formatTokenAmount(backrunDelta1), - }, - finalPrice: formatPrice((await pool.slot0()).sqrtPriceX96), - }).to.matchSnapshot() - }) - }) - } - }) - } - }) - } -}) diff --git a/test/Pool.spec.ts b/test/Pool.spec.ts deleted file mode 100644 index 3f0f7c492..000000000 --- a/test/Pool.spec.ts +++ /dev/null @@ -1,1995 +0,0 @@ -import { ethers, waffle } from 'hardhat' -import { BigNumber, BigNumberish, constants, Wallet } from 'ethers' -import { TestERC20 } from '../typechain/TestERC20' -import { PoolFactory } from '../typechain/PoolFactory' -import { MockTimePool } from '../typechain/MockTimePool' -import { SwapPaymentTest } from '../typechain/SwapPaymentTest' -import checkObservationEquals from './shared/checkObservationEquals' -import { expect } from './shared/expect' - -import { poolFixture, TEST_POOL_START_TIME } from './shared/fixtures' - -import { - expandTo18Decimals, - FeeAmount, - getPositionKey, - getMaxTick, - getMinTick, - encodeSqrtPriceX96, - TICK_SPACINGS, - createPoolFunctions, - SwapFunction, - MintFunction, - getMaxLiquidityPerTick, - FlashFunction, - MaxUint128, - MAX_SQRT_RATIO, - MIN_SQRT_RATIO, - SwapToPriceFunction, -} from './shared/utilities' -import { SwapTarget } from '../typechain/SwapTarget' -import { ReentrancyTester } from '../typechain/ReentrancyTester' -import { TickMathTest } from '../typechain/TickMathTest' -import { SwapMathTest } from '../typechain/SwapMathTest' - -const createFixtureLoader = waffle.createFixtureLoader - -type ThenArg = T extends PromiseLike ? U : T - -describe('Pool', () => { - let wallet: Wallet, other: Wallet - - let token0: TestERC20 - let token1: TestERC20 - let token2: TestERC20 - - let factory: PoolFactory - let pool: MockTimePool - - let swapTarget: SwapTarget - - let swapToLowerPrice: SwapToPriceFunction - let swapToHigherPrice: SwapToPriceFunction - let swapExact0For1: SwapFunction - let swap0ForExact1: SwapFunction - let swapExact1For0: SwapFunction - let swap1ForExact0: SwapFunction - - let feeAmount: number - let tickSpacing: number - - let minTick: number - let maxTick: number - - let mint: MintFunction - let flash: FlashFunction - - let loadFixture: ReturnType - let createPool: ThenArg>['createPool'] - - before('create fixture loader', async () => { - ;[wallet, other] = await (ethers as any).getSigners() - loadFixture = createFixtureLoader([wallet, other]) - }) - - beforeEach('deploy fixture', async () => { - ;({ token0, token1, token2, factory, createPool, swapTargetCallee: swapTarget } = await loadFixture(poolFixture)) - - const oldCreatePool = createPool - createPool = async (_feeAmount, _tickSpacing) => { - const pool = await oldCreatePool(_feeAmount, _tickSpacing) - ;({ - swapToLowerPrice, - swapToHigherPrice, - swapExact0For1, - swap0ForExact1, - swapExact1For0, - swap1ForExact0, - mint, - flash, - } = createPoolFunctions({ - token0, - token1, - swapTarget, - pool, - })) - minTick = getMinTick(_tickSpacing) - maxTick = getMaxTick(_tickSpacing) - feeAmount = _feeAmount - tickSpacing = _tickSpacing - return pool - } - - // default to the 30 bips pool - pool = await createPool(FeeAmount.MEDIUM, TICK_SPACINGS[FeeAmount.MEDIUM]) - }) - - it('constructor initializes immutables', async () => { - expect(await pool.factory()).to.eq(factory.address) - expect(await pool.token0()).to.eq(token0.address) - expect(await pool.token1()).to.eq(token1.address) - expect(await pool.maxLiquidityPerTick()).to.eq(getMaxLiquidityPerTick(tickSpacing)) - }) - - describe('#initialize', () => { - it('fails if already initialized', async () => { - await pool.initialize(encodeSqrtPriceX96(1, 1)) - await expect(pool.initialize(encodeSqrtPriceX96(1, 1))).to.be.reverted - }) - it('fails if starting price is too low', async () => { - await expect(pool.initialize(1)).to.be.revertedWith('R') - await expect(pool.initialize(MIN_SQRT_RATIO.sub(1))).to.be.revertedWith('R') - }) - it('fails if starting price is too high', async () => { - await expect(pool.initialize(MAX_SQRT_RATIO)).to.be.revertedWith('R') - await expect(pool.initialize(BigNumber.from(2).pow(160).sub(1))).to.be.revertedWith('R') - }) - it('can be initialized at MIN_SQRT_RATIO', async () => { - await pool.initialize(MIN_SQRT_RATIO) - expect((await pool.slot0()).tick).to.eq(getMinTick(1)) - }) - it('can be initialized at MAX_SQRT_RATIO - 1', async () => { - await pool.initialize(MAX_SQRT_RATIO.sub(1)) - expect((await pool.slot0()).tick).to.eq(getMaxTick(1) - 1) - }) - it('sets initial variables', async () => { - const price = encodeSqrtPriceX96(1, 2) - await pool.initialize(price) - - const { sqrtPriceX96, observationIndex } = await pool.slot0() - expect(sqrtPriceX96).to.eq(price) - expect(observationIndex).to.eq(0) - expect((await pool.slot0()).tick).to.eq(-6932) - }) - it('initializes the first observations slot', async () => { - await pool.initialize(encodeSqrtPriceX96(1, 1)) - checkObservationEquals(await pool.observations(0), { - secondsPerLiquidityCumulativeX128: 0, - initialized: true, - blockTimestamp: TEST_POOL_START_TIME, - tickCumulative: 0, - }) - }) - it('emits a Initialized event with the input tick', async () => { - const sqrtPriceX96 = encodeSqrtPriceX96(1, 2) - await expect(pool.initialize(sqrtPriceX96)).to.emit(pool, 'Initialize').withArgs(sqrtPriceX96, -6932) - }) - }) - - describe('#increaseObservationCardinalityNext', () => { - it('can only be called after initialize', async () => { - await expect(pool.increaseObservationCardinalityNext(2)).to.be.revertedWith('LOK') - }) - it('emits an event including both old and new', async () => { - await pool.initialize(encodeSqrtPriceX96(1, 1)) - await expect(pool.increaseObservationCardinalityNext(2)) - .to.emit(pool, 'IncreaseObservationCardinalityNext') - .withArgs(1, 2) - }) - it('does not emit an event for no op call', async () => { - await pool.initialize(encodeSqrtPriceX96(1, 1)) - await pool.increaseObservationCardinalityNext(3) - await expect(pool.increaseObservationCardinalityNext(2)).to.not.emit(pool, 'IncreaseObservationCardinalityNext') - }) - it('does not change cardinality next if less than current', async () => { - await pool.initialize(encodeSqrtPriceX96(1, 1)) - await pool.increaseObservationCardinalityNext(3) - await pool.increaseObservationCardinalityNext(2) - expect((await pool.slot0()).observationCardinalityNext).to.eq(3) - }) - it('increases cardinality and cardinality next first time', async () => { - await pool.initialize(encodeSqrtPriceX96(1, 1)) - await pool.increaseObservationCardinalityNext(2) - const { observationCardinality, observationCardinalityNext } = await pool.slot0() - expect(observationCardinality).to.eq(1) - expect(observationCardinalityNext).to.eq(2) - }) - }) - - describe('#mint', () => { - it('fails if not initialized', async () => { - await expect(mint(wallet.address, -tickSpacing, tickSpacing, 1)).to.be.revertedWith('LOK') - }) - describe('after initialization', () => { - beforeEach('initialize the pool at price of 10:1', async () => { - await pool.initialize(encodeSqrtPriceX96(1, 10)) - await mint(wallet.address, minTick, maxTick, 3161) - }) - - describe('failure cases', () => { - it('fails if tickLower greater than tickUpper', async () => { - // should be TLU but...hardhat - await expect(mint(wallet.address, 1, 0, 1)).to.be.reverted - }) - it('fails if tickLower less than min tick', async () => { - // should be TLM but...hardhat - await expect(mint(wallet.address, -887273, 0, 1)).to.be.reverted - }) - it('fails if tickUpper greater than max tick', async () => { - // should be TUM but...hardhat - await expect(mint(wallet.address, 0, 887273, 1)).to.be.reverted - }) - it('fails if amount exceeds the max', async () => { - // these should fail with 'LO' but hardhat is bugged - const maxLiquidityGross = await pool.maxLiquidityPerTick() - await expect(mint(wallet.address, minTick + tickSpacing, maxTick - tickSpacing, maxLiquidityGross.add(1))).to - .be.reverted - await expect(mint(wallet.address, minTick + tickSpacing, maxTick - tickSpacing, maxLiquidityGross)).to.not.be - .reverted - }) - it('fails if total amount at tick exceeds the max', async () => { - // these should fail with 'LO' but hardhat is bugged - await mint(wallet.address, minTick + tickSpacing, maxTick - tickSpacing, 1000) - - const maxLiquidityGross = await pool.maxLiquidityPerTick() - await expect( - mint(wallet.address, minTick + tickSpacing, maxTick - tickSpacing, maxLiquidityGross.sub(1000).add(1)) - ).to.be.reverted - await expect( - mint(wallet.address, minTick + tickSpacing * 2, maxTick - tickSpacing, maxLiquidityGross.sub(1000).add(1)) - ).to.be.reverted - await expect( - mint(wallet.address, minTick + tickSpacing, maxTick - tickSpacing * 2, maxLiquidityGross.sub(1000).add(1)) - ).to.be.reverted - await expect(mint(wallet.address, minTick + tickSpacing, maxTick - tickSpacing, maxLiquidityGross.sub(1000))) - .to.not.be.reverted - }) - it('fails if amount is 0', async () => { - await expect(mint(wallet.address, minTick + tickSpacing, maxTick - tickSpacing, 0)).to.be.reverted - }) - }) - - describe('success cases', () => { - it('initial balances', async () => { - expect(await token0.balanceOf(pool.address)).to.eq(9996) - expect(await token1.balanceOf(pool.address)).to.eq(1000) - }) - - it('initial tick', async () => { - expect((await pool.slot0()).tick).to.eq(-23028) - }) - - describe('above current price', () => { - it('transfers token0 only', async () => { - await expect(mint(wallet.address, -22980, 0, 10000)) - .to.emit(token0, 'Transfer') - .withArgs(wallet.address, pool.address, 21549) - .to.not.emit(token1, 'Transfer') - expect(await token0.balanceOf(pool.address)).to.eq(9996 + 21549) - expect(await token1.balanceOf(pool.address)).to.eq(1000) - }) - - it('max tick with max leverage', async () => { - await mint(wallet.address, maxTick - tickSpacing, maxTick, BigNumber.from(2).pow(102)) - expect(await token0.balanceOf(pool.address)).to.eq(9996 + 828011525) - expect(await token1.balanceOf(pool.address)).to.eq(1000) - }) - - it('works for max tick', async () => { - await expect(mint(wallet.address, -22980, maxTick, 10000)) - .to.emit(token0, 'Transfer') - .withArgs(wallet.address, pool.address, 31549) - expect(await token0.balanceOf(pool.address)).to.eq(9996 + 31549) - expect(await token1.balanceOf(pool.address)).to.eq(1000) - }) - - it('removing works', async () => { - await mint(wallet.address, -240, 0, 10000) - await pool.burn(-240, 0, 10000) - const { amount0, amount1 } = await pool.callStatic.collect(wallet.address, -240, 0, MaxUint128, MaxUint128) - expect(amount0, 'amount0').to.eq(120) - expect(amount1, 'amount1').to.eq(0) - }) - - it('adds liquidity to liquidityGross', async () => { - await mint(wallet.address, -240, 0, 100) - expect((await pool.ticks(-240)).liquidityGross).to.eq(100) - expect((await pool.ticks(0)).liquidityGross).to.eq(100) - expect((await pool.ticks(tickSpacing)).liquidityGross).to.eq(0) - expect((await pool.ticks(tickSpacing * 2)).liquidityGross).to.eq(0) - await mint(wallet.address, -240, tickSpacing, 150) - expect((await pool.ticks(-240)).liquidityGross).to.eq(250) - expect((await pool.ticks(0)).liquidityGross).to.eq(100) - expect((await pool.ticks(tickSpacing)).liquidityGross).to.eq(150) - expect((await pool.ticks(tickSpacing * 2)).liquidityGross).to.eq(0) - await mint(wallet.address, 0, tickSpacing * 2, 60) - expect((await pool.ticks(-240)).liquidityGross).to.eq(250) - expect((await pool.ticks(0)).liquidityGross).to.eq(160) - expect((await pool.ticks(tickSpacing)).liquidityGross).to.eq(150) - expect((await pool.ticks(tickSpacing * 2)).liquidityGross).to.eq(60) - }) - - it('removes liquidity from liquidityGross', async () => { - await mint(wallet.address, -240, 0, 100) - await mint(wallet.address, -240, 0, 40) - await pool.burn(-240, 0, 90) - expect((await pool.ticks(-240)).liquidityGross).to.eq(50) - expect((await pool.ticks(0)).liquidityGross).to.eq(50) - }) - - it('clears tick lower if last position is removed', async () => { - await mint(wallet.address, -240, 0, 100) - await pool.burn(-240, 0, 100) - const { liquidityGross, feeGrowthOutside0X128, feeGrowthOutside1X128 } = await pool.ticks(-240) - expect(liquidityGross).to.eq(0) - expect(feeGrowthOutside0X128).to.eq(0) - expect(feeGrowthOutside1X128).to.eq(0) - }) - - it('clears tick upper if last position is removed', async () => { - await mint(wallet.address, -240, 0, 100) - await pool.burn(-240, 0, 100) - const { liquidityGross, feeGrowthOutside0X128, feeGrowthOutside1X128 } = await pool.ticks(0) - expect(liquidityGross).to.eq(0) - expect(feeGrowthOutside0X128).to.eq(0) - expect(feeGrowthOutside1X128).to.eq(0) - }) - it('only clears the tick that is not used at all', async () => { - await mint(wallet.address, -240, 0, 100) - await mint(wallet.address, -tickSpacing, 0, 250) - await pool.burn(-240, 0, 100) - - let { liquidityGross, feeGrowthOutside0X128, feeGrowthOutside1X128 } = await pool.ticks(-240) - expect(liquidityGross).to.eq(0) - expect(feeGrowthOutside0X128).to.eq(0) - expect(feeGrowthOutside1X128).to.eq(0) - ;({ liquidityGross, feeGrowthOutside0X128, feeGrowthOutside1X128 } = await pool.ticks(-tickSpacing)) - expect(liquidityGross).to.eq(250) - expect(feeGrowthOutside0X128).to.eq(0) - expect(feeGrowthOutside1X128).to.eq(0) - }) - - it('does not write an observation', async () => { - checkObservationEquals(await pool.observations(0), { - tickCumulative: 0, - blockTimestamp: TEST_POOL_START_TIME, - initialized: true, - secondsPerLiquidityCumulativeX128: 0, - }) - await pool.advanceTime(1) - await mint(wallet.address, -240, 0, 100) - checkObservationEquals(await pool.observations(0), { - tickCumulative: 0, - blockTimestamp: TEST_POOL_START_TIME, - initialized: true, - secondsPerLiquidityCumulativeX128: 0, - }) - }) - }) - - describe('including current price', () => { - it('price within range: transfers current price of both tokens', async () => { - await expect(mint(wallet.address, minTick + tickSpacing, maxTick - tickSpacing, 100)) - .to.emit(token0, 'Transfer') - .withArgs(wallet.address, pool.address, 317) - .to.emit(token1, 'Transfer') - .withArgs(wallet.address, pool.address, 32) - expect(await token0.balanceOf(pool.address)).to.eq(9996 + 317) - expect(await token1.balanceOf(pool.address)).to.eq(1000 + 32) - }) - - it('initializes lower tick', async () => { - await mint(wallet.address, minTick + tickSpacing, maxTick - tickSpacing, 100) - const { liquidityGross } = await pool.ticks(minTick + tickSpacing) - expect(liquidityGross).to.eq(100) - }) - - it('initializes upper tick', async () => { - await mint(wallet.address, minTick + tickSpacing, maxTick - tickSpacing, 100) - const { liquidityGross } = await pool.ticks(maxTick - tickSpacing) - expect(liquidityGross).to.eq(100) - }) - - it('works for min/max tick', async () => { - await expect(mint(wallet.address, minTick, maxTick, 10000)) - .to.emit(token0, 'Transfer') - .withArgs(wallet.address, pool.address, 31623) - .to.emit(token1, 'Transfer') - .withArgs(wallet.address, pool.address, 3163) - expect(await token0.balanceOf(pool.address)).to.eq(9996 + 31623) - expect(await token1.balanceOf(pool.address)).to.eq(1000 + 3163) - }) - - it('removing works', async () => { - await mint(wallet.address, minTick + tickSpacing, maxTick - tickSpacing, 100) - await pool.burn(minTick + tickSpacing, maxTick - tickSpacing, 100) - const { amount0, amount1 } = await pool.callStatic.collect( - wallet.address, - minTick + tickSpacing, - maxTick - tickSpacing, - MaxUint128, - MaxUint128 - ) - expect(amount0, 'amount0').to.eq(316) - expect(amount1, 'amount1').to.eq(31) - }) - - it('writes an observation', async () => { - checkObservationEquals(await pool.observations(0), { - tickCumulative: 0, - blockTimestamp: TEST_POOL_START_TIME, - initialized: true, - secondsPerLiquidityCumulativeX128: 0, - }) - await pool.advanceTime(1) - await mint(wallet.address, minTick, maxTick, 100) - checkObservationEquals(await pool.observations(0), { - tickCumulative: -23028, - blockTimestamp: TEST_POOL_START_TIME + 1, - initialized: true, - secondsPerLiquidityCumulativeX128: '107650226801941937191829992860413859', - }) - }) - }) - - describe('below current price', () => { - it('transfers token1 only', async () => { - await expect(mint(wallet.address, -46080, -23040, 10000)) - .to.emit(token1, 'Transfer') - .withArgs(wallet.address, pool.address, 2162) - .to.not.emit(token0, 'Transfer') - expect(await token0.balanceOf(pool.address)).to.eq(9996) - expect(await token1.balanceOf(pool.address)).to.eq(1000 + 2162) - }) - - it('min tick with max leverage', async () => { - await mint(wallet.address, minTick, minTick + tickSpacing, BigNumber.from(2).pow(102)) - expect(await token0.balanceOf(pool.address)).to.eq(9996) - expect(await token1.balanceOf(pool.address)).to.eq(1000 + 828011520) - }) - - it('works for min tick', async () => { - await expect(mint(wallet.address, minTick, -23040, 10000)) - .to.emit(token1, 'Transfer') - .withArgs(wallet.address, pool.address, 3161) - expect(await token0.balanceOf(pool.address)).to.eq(9996) - expect(await token1.balanceOf(pool.address)).to.eq(1000 + 3161) - }) - - it('removing works', async () => { - await mint(wallet.address, -46080, -46020, 10000) - await pool.burn(-46080, -46020, 10000) - const { amount0, amount1 } = await pool.callStatic.collect( - wallet.address, - -46080, - -46020, - MaxUint128, - MaxUint128 - ) - expect(amount0, 'amount0').to.eq(0) - expect(amount1, 'amount1').to.eq(3) - }) - - it('does not write an observation', async () => { - checkObservationEquals(await pool.observations(0), { - tickCumulative: 0, - blockTimestamp: TEST_POOL_START_TIME, - initialized: true, - secondsPerLiquidityCumulativeX128: 0, - }) - await pool.advanceTime(1) - await mint(wallet.address, -46080, -23040, 100) - checkObservationEquals(await pool.observations(0), { - tickCumulative: 0, - blockTimestamp: TEST_POOL_START_TIME, - initialized: true, - secondsPerLiquidityCumulativeX128: 0, - }) - }) - }) - }) - - it('protocol fees accumulate as expected during swap', async () => { - await pool.setFeeProtocol(6, 6) - - await mint(wallet.address, minTick + tickSpacing, maxTick - tickSpacing, expandTo18Decimals(1)) - await swapExact0For1(expandTo18Decimals(1).div(10), wallet.address) - await swapExact1For0(expandTo18Decimals(1).div(100), wallet.address) - - let { token0: token0ProtocolFees, token1: token1ProtocolFees } = await pool.protocolFees() - expect(token0ProtocolFees).to.eq('50000000000000') - expect(token1ProtocolFees).to.eq('5000000000000') - }) - - it('positions are protected before protocol fee is turned on', async () => { - await mint(wallet.address, minTick + tickSpacing, maxTick - tickSpacing, expandTo18Decimals(1)) - await swapExact0For1(expandTo18Decimals(1).div(10), wallet.address) - await swapExact1For0(expandTo18Decimals(1).div(100), wallet.address) - - let { token0: token0ProtocolFees, token1: token1ProtocolFees } = await pool.protocolFees() - expect(token0ProtocolFees).to.eq(0) - expect(token1ProtocolFees).to.eq(0) - - await pool.setFeeProtocol(6, 6) - ;({ token0: token0ProtocolFees, token1: token1ProtocolFees } = await pool.protocolFees()) - expect(token0ProtocolFees).to.eq(0) - expect(token1ProtocolFees).to.eq(0) - }) - - it('poke is not allowed on uninitialized position', async () => { - await mint(other.address, minTick + tickSpacing, maxTick - tickSpacing, expandTo18Decimals(1)) - await swapExact0For1(expandTo18Decimals(1).div(10), wallet.address) - await swapExact1For0(expandTo18Decimals(1).div(100), wallet.address) - - // missing revert reason due to hardhat - await expect(pool.burn(minTick + tickSpacing, maxTick - tickSpacing, 0)).to.be.reverted - - await mint(wallet.address, minTick + tickSpacing, maxTick - tickSpacing, 1) - let { liquidity, feeGrowthInside0LastX128, feeGrowthInside1LastX128, tokensOwed1, tokensOwed0 } = - await pool.positions(getPositionKey(wallet.address, minTick + tickSpacing, maxTick - tickSpacing)) - expect(liquidity).to.eq(1) - expect(feeGrowthInside0LastX128).to.eq('102084710076281216349243831104605583') - expect(feeGrowthInside1LastX128).to.eq('10208471007628121634924383110460558') - expect(tokensOwed0, 'tokens owed 0 before').to.eq(0) - expect(tokensOwed1, 'tokens owed 1 before').to.eq(0) - - await pool.burn(minTick + tickSpacing, maxTick - tickSpacing, 1) - ;({ liquidity, feeGrowthInside0LastX128, feeGrowthInside1LastX128, tokensOwed1, tokensOwed0 } = - await pool.positions(getPositionKey(wallet.address, minTick + tickSpacing, maxTick - tickSpacing))) - expect(liquidity).to.eq(0) - expect(feeGrowthInside0LastX128).to.eq('102084710076281216349243831104605583') - expect(feeGrowthInside1LastX128).to.eq('10208471007628121634924383110460558') - expect(tokensOwed0, 'tokens owed 0 after').to.eq(3) - expect(tokensOwed1, 'tokens owed 1 after').to.eq(0) - }) - }) - }) - - describe('#burn', () => { - beforeEach('initialize at zero tick', () => initializeAtZeroTick(pool)) - - async function checkTickIsClear(tick: number) { - const { liquidityGross, feeGrowthOutside0X128, feeGrowthOutside1X128, liquidityNet } = await pool.ticks(tick) - expect(liquidityGross).to.eq(0) - expect(feeGrowthOutside0X128).to.eq(0) - expect(feeGrowthOutside1X128).to.eq(0) - expect(liquidityNet).to.eq(0) - } - - async function checkTickIsNotClear(tick: number) { - const { liquidityGross } = await pool.ticks(tick) - expect(liquidityGross).to.not.eq(0) - } - - it('does not clear the position fee growth snapshot if no more liquidity', async () => { - // some activity that would make the ticks non-zero - await pool.advanceTime(10) - await mint(other.address, minTick, maxTick, expandTo18Decimals(1)) - await swapExact0For1(expandTo18Decimals(1), wallet.address) - await swapExact1For0(expandTo18Decimals(1), wallet.address) - await pool.connect(other).burn(minTick, maxTick, expandTo18Decimals(1)) - const { liquidity, tokensOwed0, tokensOwed1, feeGrowthInside0LastX128, feeGrowthInside1LastX128 } = - await pool.positions(getPositionKey(other.address, minTick, maxTick)) - expect(liquidity).to.eq(0) - expect(tokensOwed0).to.not.eq(0) - expect(tokensOwed1).to.not.eq(0) - expect(feeGrowthInside0LastX128).to.eq('340282366920938463463374607431768211') - expect(feeGrowthInside1LastX128).to.eq('340282366920938576890830247744589365') - }) - - it('clears the tick if its the last position using it', async () => { - const tickLower = minTick + tickSpacing - const tickUpper = maxTick - tickSpacing - // some activity that would make the ticks non-zero - await pool.advanceTime(10) - await mint(wallet.address, tickLower, tickUpper, 1) - await swapExact0For1(expandTo18Decimals(1), wallet.address) - await pool.burn(tickLower, tickUpper, 1) - await checkTickIsClear(tickLower) - await checkTickIsClear(tickUpper) - }) - - it('clears only the lower tick if upper is still used', async () => { - const tickLower = minTick + tickSpacing - const tickUpper = maxTick - tickSpacing - // some activity that would make the ticks non-zero - await pool.advanceTime(10) - await mint(wallet.address, tickLower, tickUpper, 1) - await mint(wallet.address, tickLower + tickSpacing, tickUpper, 1) - await swapExact0For1(expandTo18Decimals(1), wallet.address) - await pool.burn(tickLower, tickUpper, 1) - await checkTickIsClear(tickLower) - await checkTickIsNotClear(tickUpper) - }) - - it('clears only the upper tick if lower is still used', async () => { - const tickLower = minTick + tickSpacing - const tickUpper = maxTick - tickSpacing - // some activity that would make the ticks non-zero - await pool.advanceTime(10) - await mint(wallet.address, tickLower, tickUpper, 1) - await mint(wallet.address, tickLower, tickUpper - tickSpacing, 1) - await swapExact0For1(expandTo18Decimals(1), wallet.address) - await pool.burn(tickLower, tickUpper, 1) - await checkTickIsNotClear(tickLower) - await checkTickIsClear(tickUpper) - }) - }) - - // the combined amount of liquidity that the pool is initialized with (including the 1 minimum liquidity that is burned) - const initializeLiquidityAmount = expandTo18Decimals(2) - async function initializeAtZeroTick(pool: MockTimePool): Promise { - await pool.initialize(encodeSqrtPriceX96(1, 1)) - const tickSpacing = await pool.tickSpacing() - const [min, max] = [getMinTick(tickSpacing), getMaxTick(tickSpacing)] - await mint(wallet.address, min, max, initializeLiquidityAmount) - } - - describe('#observe', () => { - beforeEach(() => initializeAtZeroTick(pool)) - - // zero tick - it('current tick accumulator increases by tick over time', async () => { - let { - tickCumulatives: [tickCumulative], - } = await pool.observe([0]) - expect(tickCumulative).to.eq(0) - await pool.advanceTime(10) - ;({ - tickCumulatives: [tickCumulative], - } = await pool.observe([0])) - expect(tickCumulative).to.eq(0) - }) - - it('current tick accumulator after single swap', async () => { - // moves to tick -1 - await swapExact0For1(1000, wallet.address) - await pool.advanceTime(4) - let { - tickCumulatives: [tickCumulative], - } = await pool.observe([0]) - expect(tickCumulative).to.eq(-4) - }) - - it('current tick accumulator after two swaps', async () => { - await swapExact0For1(expandTo18Decimals(1).div(2), wallet.address) - expect((await pool.slot0()).tick).to.eq(-4452) - await pool.advanceTime(4) - await swapExact1For0(expandTo18Decimals(1).div(4), wallet.address) - expect((await pool.slot0()).tick).to.eq(-1558) - await pool.advanceTime(6) - let { - tickCumulatives: [tickCumulative], - } = await pool.observe([0]) - // -4452*4 + -1558*6 - expect(tickCumulative).to.eq(-27156) - }) - }) - - describe('miscellaneous mint tests', () => { - beforeEach('initialize at zero tick', async () => { - pool = await createPool(FeeAmount.LOW, TICK_SPACINGS[FeeAmount.LOW]) - await initializeAtZeroTick(pool) - }) - - it('mint to the right of the current price', async () => { - const liquidityDelta = 1000 - const lowerTick = tickSpacing - const upperTick = tickSpacing * 2 - - const liquidityBefore = await pool.liquidity() - - const b0 = await token0.balanceOf(pool.address) - const b1 = await token1.balanceOf(pool.address) - - await mint(wallet.address, lowerTick, upperTick, liquidityDelta) - - const liquidityAfter = await pool.liquidity() - expect(liquidityAfter).to.be.gte(liquidityBefore) - - expect((await token0.balanceOf(pool.address)).sub(b0)).to.eq(1) - expect((await token1.balanceOf(pool.address)).sub(b1)).to.eq(0) - }) - - it('mint to the left of the current price', async () => { - const liquidityDelta = 1000 - const lowerTick = -tickSpacing * 2 - const upperTick = -tickSpacing - - const liquidityBefore = await pool.liquidity() - - const b0 = await token0.balanceOf(pool.address) - const b1 = await token1.balanceOf(pool.address) - - await mint(wallet.address, lowerTick, upperTick, liquidityDelta) - - const liquidityAfter = await pool.liquidity() - expect(liquidityAfter).to.be.gte(liquidityBefore) - - expect((await token0.balanceOf(pool.address)).sub(b0)).to.eq(0) - expect((await token1.balanceOf(pool.address)).sub(b1)).to.eq(1) - }) - - it('mint within the current price', async () => { - const liquidityDelta = 1000 - const lowerTick = -tickSpacing - const upperTick = tickSpacing - - const liquidityBefore = await pool.liquidity() - - const b0 = await token0.balanceOf(pool.address) - const b1 = await token1.balanceOf(pool.address) - - await mint(wallet.address, lowerTick, upperTick, liquidityDelta) - - const liquidityAfter = await pool.liquidity() - expect(liquidityAfter).to.be.gte(liquidityBefore) - - expect((await token0.balanceOf(pool.address)).sub(b0)).to.eq(1) - expect((await token1.balanceOf(pool.address)).sub(b1)).to.eq(1) - }) - - it('cannot remove more than the entire position', async () => { - const lowerTick = -tickSpacing - const upperTick = tickSpacing - await mint(wallet.address, lowerTick, upperTick, expandTo18Decimals(1000)) - // should be 'LS', hardhat is bugged - await expect(pool.burn(lowerTick, upperTick, expandTo18Decimals(1001))).to.be.reverted - }) - - it('collect fees within the current price after swap', async () => { - const liquidityDelta = expandTo18Decimals(100) - const lowerTick = -tickSpacing * 100 - const upperTick = tickSpacing * 100 - - await mint(wallet.address, lowerTick, upperTick, liquidityDelta) - - const liquidityBefore = await pool.liquidity() - - const amount0In = expandTo18Decimals(1) - await swapExact0For1(amount0In, wallet.address) - - const liquidityAfter = await pool.liquidity() - expect(liquidityAfter, 'k increases').to.be.gte(liquidityBefore) - - const token0BalanceBeforePool = await token0.balanceOf(pool.address) - const token1BalanceBeforePool = await token1.balanceOf(pool.address) - const token0BalanceBeforeWallet = await token0.balanceOf(wallet.address) - const token1BalanceBeforeWallet = await token1.balanceOf(wallet.address) - - await pool.burn(lowerTick, upperTick, 0) - await pool.collect(wallet.address, lowerTick, upperTick, MaxUint128, MaxUint128) - - await pool.burn(lowerTick, upperTick, 0) - const { amount0: fees0, amount1: fees1 } = await pool.callStatic.collect( - wallet.address, - lowerTick, - upperTick, - MaxUint128, - MaxUint128 - ) - expect(fees0).to.be.eq(0) - expect(fees1).to.be.eq(0) - - const token0BalanceAfterWallet = await token0.balanceOf(wallet.address) - const token1BalanceAfterWallet = await token1.balanceOf(wallet.address) - const token0BalanceAfterPool = await token0.balanceOf(pool.address) - const token1BalanceAfterPool = await token1.balanceOf(pool.address) - - expect(token0BalanceAfterWallet).to.be.gt(token0BalanceBeforeWallet) - expect(token1BalanceAfterWallet).to.be.eq(token1BalanceBeforeWallet) - - expect(token0BalanceAfterPool).to.be.lt(token0BalanceBeforePool) - expect(token1BalanceAfterPool).to.be.eq(token1BalanceBeforePool) - }) - }) - - describe('post-initialize at medium fee', () => { - describe('k (implicit)', () => { - it('returns 0 before initialization', async () => { - expect(await pool.liquidity()).to.eq(0) - }) - describe('post initialized', () => { - beforeEach(() => initializeAtZeroTick(pool)) - - it('returns initial liquidity', async () => { - expect(await pool.liquidity()).to.eq(expandTo18Decimals(2)) - }) - it('returns in supply in range', async () => { - await mint(wallet.address, -tickSpacing, tickSpacing, expandTo18Decimals(3)) - expect(await pool.liquidity()).to.eq(expandTo18Decimals(5)) - }) - it('excludes supply at tick above current tick', async () => { - await mint(wallet.address, tickSpacing, tickSpacing * 2, expandTo18Decimals(3)) - expect(await pool.liquidity()).to.eq(expandTo18Decimals(2)) - }) - it('excludes supply at tick below current tick', async () => { - await mint(wallet.address, -tickSpacing * 2, -tickSpacing, expandTo18Decimals(3)) - expect(await pool.liquidity()).to.eq(expandTo18Decimals(2)) - }) - it('updates correctly when exiting range', async () => { - const kBefore = await pool.liquidity() - expect(kBefore).to.be.eq(expandTo18Decimals(2)) - - // add liquidity at and above current tick - const liquidityDelta = expandTo18Decimals(1) - const lowerTick = 0 - const upperTick = tickSpacing - await mint(wallet.address, lowerTick, upperTick, liquidityDelta) - - // ensure virtual supply has increased appropriately - const kAfter = await pool.liquidity() - expect(kAfter).to.be.eq(expandTo18Decimals(3)) - - // swap toward the left (just enough for the tick transition function to trigger) - await swapExact0For1(1, wallet.address) - const { tick } = await pool.slot0() - expect(tick).to.be.eq(-1) - - const kAfterSwap = await pool.liquidity() - expect(kAfterSwap).to.be.eq(expandTo18Decimals(2)) - }) - it('updates correctly when entering range', async () => { - const kBefore = await pool.liquidity() - expect(kBefore).to.be.eq(expandTo18Decimals(2)) - - // add liquidity below the current tick - const liquidityDelta = expandTo18Decimals(1) - const lowerTick = -tickSpacing - const upperTick = 0 - await mint(wallet.address, lowerTick, upperTick, liquidityDelta) - - // ensure virtual supply hasn't changed - const kAfter = await pool.liquidity() - expect(kAfter).to.be.eq(kBefore) - - // swap toward the left (just enough for the tick transition function to trigger) - await swapExact0For1(1, wallet.address) - const { tick } = await pool.slot0() - expect(tick).to.be.eq(-1) - - const kAfterSwap = await pool.liquidity() - expect(kAfterSwap).to.be.eq(expandTo18Decimals(3)) - }) - }) - }) - }) - - describe('limit orders', () => { - beforeEach('initialize at tick 0', () => initializeAtZeroTick(pool)) - - it('limit selling 0 for 1 at tick 0 thru 1', async () => { - await expect(mint(wallet.address, 0, 120, expandTo18Decimals(1))) - .to.emit(token0, 'Transfer') - .withArgs(wallet.address, pool.address, '5981737760509663') - // somebody takes the limit order - await swapExact1For0(expandTo18Decimals(2), other.address) - await expect(pool.burn(0, 120, expandTo18Decimals(1))) - .to.emit(pool, 'Burn') - .withArgs(wallet.address, 0, 120, expandTo18Decimals(1), 0, '6017734268818165') - .to.not.emit(token0, 'Transfer') - .to.not.emit(token1, 'Transfer') - await expect(pool.collect(wallet.address, 0, 120, MaxUint128, MaxUint128)) - .to.emit(token1, 'Transfer') - .withArgs(pool.address, wallet.address, BigNumber.from('6017734268818165').add('18107525382602')) // roughly 0.3% despite other liquidity - .to.not.emit(token0, 'Transfer') - expect((await pool.slot0()).tick).to.be.gte(120) - }) - it('limit selling 1 for 0 at tick 0 thru -1', async () => { - await expect(mint(wallet.address, -120, 0, expandTo18Decimals(1))) - .to.emit(token1, 'Transfer') - .withArgs(wallet.address, pool.address, '5981737760509663') - // somebody takes the limit order - await swapExact0For1(expandTo18Decimals(2), other.address) - await expect(pool.burn(-120, 0, expandTo18Decimals(1))) - .to.emit(pool, 'Burn') - .withArgs(wallet.address, -120, 0, expandTo18Decimals(1), '6017734268818165', 0) - .to.not.emit(token0, 'Transfer') - .to.not.emit(token1, 'Transfer') - await expect(pool.collect(wallet.address, -120, 0, MaxUint128, MaxUint128)) - .to.emit(token0, 'Transfer') - .withArgs(pool.address, wallet.address, BigNumber.from('6017734268818165').add('18107525382602')) // roughly 0.3% despite other liquidity - expect((await pool.slot0()).tick).to.be.lt(-120) - }) - - describe('fee is on', () => { - beforeEach(() => pool.setFeeProtocol(6, 6)) - it('limit selling 0 for 1 at tick 0 thru 1', async () => { - await expect(mint(wallet.address, 0, 120, expandTo18Decimals(1))) - .to.emit(token0, 'Transfer') - .withArgs(wallet.address, pool.address, '5981737760509663') - // somebody takes the limit order - await swapExact1For0(expandTo18Decimals(2), other.address) - await expect(pool.burn(0, 120, expandTo18Decimals(1))) - .to.emit(pool, 'Burn') - .withArgs(wallet.address, 0, 120, expandTo18Decimals(1), 0, '6017734268818165') - .to.not.emit(token0, 'Transfer') - .to.not.emit(token1, 'Transfer') - await expect(pool.collect(wallet.address, 0, 120, MaxUint128, MaxUint128)) - .to.emit(token1, 'Transfer') - .withArgs(pool.address, wallet.address, BigNumber.from('6017734268818165').add('15089604485501')) // roughly 0.25% despite other liquidity - .to.not.emit(token0, 'Transfer') - expect((await pool.slot0()).tick).to.be.gte(120) - }) - it('limit selling 1 for 0 at tick 0 thru -1', async () => { - await expect(mint(wallet.address, -120, 0, expandTo18Decimals(1))) - .to.emit(token1, 'Transfer') - .withArgs(wallet.address, pool.address, '5981737760509663') - // somebody takes the limit order - await swapExact0For1(expandTo18Decimals(2), other.address) - await expect(pool.burn(-120, 0, expandTo18Decimals(1))) - .to.emit(pool, 'Burn') - .withArgs(wallet.address, -120, 0, expandTo18Decimals(1), '6017734268818165', 0) - .to.not.emit(token0, 'Transfer') - .to.not.emit(token1, 'Transfer') - await expect(pool.collect(wallet.address, -120, 0, MaxUint128, MaxUint128)) - .to.emit(token0, 'Transfer') - .withArgs(pool.address, wallet.address, BigNumber.from('6017734268818165').add('15089604485501')) // roughly 0.25% despite other liquidity - expect((await pool.slot0()).tick).to.be.lt(-120) - }) - }) - }) - - describe('#collect', () => { - beforeEach(async () => { - pool = await createPool(FeeAmount.LOW, TICK_SPACINGS[FeeAmount.LOW]) - await pool.initialize(encodeSqrtPriceX96(1, 1)) - }) - - it('works with multiple LPs', async () => { - await mint(wallet.address, minTick, maxTick, expandTo18Decimals(1)) - await mint(wallet.address, minTick + tickSpacing, maxTick - tickSpacing, expandTo18Decimals(2)) - - await swapExact0For1(expandTo18Decimals(1), wallet.address) - - // poke positions - await pool.burn(minTick, maxTick, 0) - await pool.burn(minTick + tickSpacing, maxTick - tickSpacing, 0) - - const { tokensOwed0: tokensOwed0Position0 } = await pool.positions( - getPositionKey(wallet.address, minTick, maxTick) - ) - const { tokensOwed0: tokensOwed0Position1 } = await pool.positions( - getPositionKey(wallet.address, minTick + tickSpacing, maxTick - tickSpacing) - ) - - expect(tokensOwed0Position0).to.be.eq('166666666666667') - expect(tokensOwed0Position1).to.be.eq('333333333333334') - }) - - describe('works across large increases', () => { - beforeEach(async () => { - await mint(wallet.address, minTick, maxTick, expandTo18Decimals(1)) - }) - - // type(uint128).max * 2**128 / 1e18 - // https://www.wolframalpha.com/input/?i=%282**128+-+1%29+*+2**128+%2F+1e18 - const magicNumber = BigNumber.from('115792089237316195423570985008687907852929702298719625575994') - - it('works just before the cap binds', async () => { - await pool.setFeeGrowthGlobal0X128(magicNumber) - await pool.burn(minTick, maxTick, 0) - - const { tokensOwed0, tokensOwed1 } = await pool.positions(getPositionKey(wallet.address, minTick, maxTick)) - - expect(tokensOwed0).to.be.eq(MaxUint128.sub(1)) - expect(tokensOwed1).to.be.eq(0) - }) - - it('works just after the cap binds', async () => { - await pool.setFeeGrowthGlobal0X128(magicNumber.add(1)) - await pool.burn(minTick, maxTick, 0) - - const { tokensOwed0, tokensOwed1 } = await pool.positions(getPositionKey(wallet.address, minTick, maxTick)) - - expect(tokensOwed0).to.be.eq(MaxUint128) - expect(tokensOwed1).to.be.eq(0) - }) - - it('works well after the cap binds', async () => { - await pool.setFeeGrowthGlobal0X128(constants.MaxUint256) - await pool.burn(minTick, maxTick, 0) - - const { tokensOwed0, tokensOwed1 } = await pool.positions(getPositionKey(wallet.address, minTick, maxTick)) - - expect(tokensOwed0).to.be.eq(MaxUint128) - expect(tokensOwed1).to.be.eq(0) - }) - }) - - describe('works across overflow boundaries', () => { - beforeEach(async () => { - await pool.setFeeGrowthGlobal0X128(constants.MaxUint256) - await pool.setFeeGrowthGlobal1X128(constants.MaxUint256) - await mint(wallet.address, minTick, maxTick, expandTo18Decimals(10)) - }) - - it('token0', async () => { - await swapExact0For1(expandTo18Decimals(1), wallet.address) - await pool.burn(minTick, maxTick, 0) - const { amount0, amount1 } = await pool.callStatic.collect( - wallet.address, - minTick, - maxTick, - MaxUint128, - MaxUint128 - ) - expect(amount0).to.be.eq('499999999999999') - expect(amount1).to.be.eq(0) - }) - it('token1', async () => { - await swapExact1For0(expandTo18Decimals(1), wallet.address) - await pool.burn(minTick, maxTick, 0) - const { amount0, amount1 } = await pool.callStatic.collect( - wallet.address, - minTick, - maxTick, - MaxUint128, - MaxUint128 - ) - expect(amount0).to.be.eq(0) - expect(amount1).to.be.eq('499999999999999') - }) - it('token0 and token1', async () => { - await swapExact0For1(expandTo18Decimals(1), wallet.address) - await swapExact1For0(expandTo18Decimals(1), wallet.address) - await pool.burn(minTick, maxTick, 0) - const { amount0, amount1 } = await pool.callStatic.collect( - wallet.address, - minTick, - maxTick, - MaxUint128, - MaxUint128 - ) - expect(amount0).to.be.eq('499999999999999') - expect(amount1).to.be.eq('500000000000000') - }) - }) - }) - - describe('#feeProtocol', () => { - const liquidityAmount = expandTo18Decimals(1000) - - beforeEach(async () => { - pool = await createPool(FeeAmount.LOW, TICK_SPACINGS[FeeAmount.LOW]) - await pool.initialize(encodeSqrtPriceX96(1, 1)) - await mint(wallet.address, minTick, maxTick, liquidityAmount) - }) - - it('is initially set to 0', async () => { - expect((await pool.slot0()).feeProtocol).to.eq(0) - }) - - it('can be changed by the owner', async () => { - await pool.setFeeProtocol(6, 6) - expect((await pool.slot0()).feeProtocol).to.eq(102) - }) - - it('cannot be changed out of bounds', async () => { - await expect(pool.setFeeProtocol(3, 3)).to.be.reverted - await expect(pool.setFeeProtocol(11, 11)).to.be.reverted - }) - - it('cannot be changed by addresses that are not owner', async () => { - await expect(pool.connect(other).setFeeProtocol(6, 6)).to.be.reverted - }) - - async function swapAndGetFeesOwed({ - amount, - zeroForOne, - poke, - }: { - amount: BigNumberish - zeroForOne: boolean - poke: boolean - }) { - await (zeroForOne ? swapExact0For1(amount, wallet.address) : swapExact1For0(amount, wallet.address)) - - if (poke) await pool.burn(minTick, maxTick, 0) - - const { amount0: fees0, amount1: fees1 } = await pool.callStatic.collect( - wallet.address, - minTick, - maxTick, - MaxUint128, - MaxUint128 - ) - - expect(fees0, 'fees owed in token0 are greater than 0').to.be.gte(0) - expect(fees1, 'fees owed in token1 are greater than 0').to.be.gte(0) - - return { token0Fees: fees0, token1Fees: fees1 } - } - - it('position owner gets full fees when protocol fee is off', async () => { - const { token0Fees, token1Fees } = await swapAndGetFeesOwed({ - amount: expandTo18Decimals(1), - zeroForOne: true, - poke: true, - }) - - // 6 bips * 1e18 - expect(token0Fees).to.eq('499999999999999') - expect(token1Fees).to.eq(0) - }) - - it('swap fees accumulate as expected (0 for 1)', async () => { - let token0Fees - let token1Fees - ;({ token0Fees, token1Fees } = await swapAndGetFeesOwed({ - amount: expandTo18Decimals(1), - zeroForOne: true, - poke: true, - })) - expect(token0Fees).to.eq('499999999999999') - expect(token1Fees).to.eq(0) - ;({ token0Fees, token1Fees } = await swapAndGetFeesOwed({ - amount: expandTo18Decimals(1), - zeroForOne: true, - poke: true, - })) - expect(token0Fees).to.eq('999999999999998') - expect(token1Fees).to.eq(0) - ;({ token0Fees, token1Fees } = await swapAndGetFeesOwed({ - amount: expandTo18Decimals(1), - zeroForOne: true, - poke: true, - })) - expect(token0Fees).to.eq('1499999999999997') - expect(token1Fees).to.eq(0) - }) - - it('swap fees accumulate as expected (1 for 0)', async () => { - let token0Fees - let token1Fees - ;({ token0Fees, token1Fees } = await swapAndGetFeesOwed({ - amount: expandTo18Decimals(1), - zeroForOne: false, - poke: true, - })) - expect(token0Fees).to.eq(0) - expect(token1Fees).to.eq('499999999999999') - ;({ token0Fees, token1Fees } = await swapAndGetFeesOwed({ - amount: expandTo18Decimals(1), - zeroForOne: false, - poke: true, - })) - expect(token0Fees).to.eq(0) - expect(token1Fees).to.eq('999999999999998') - ;({ token0Fees, token1Fees } = await swapAndGetFeesOwed({ - amount: expandTo18Decimals(1), - zeroForOne: false, - poke: true, - })) - expect(token0Fees).to.eq(0) - expect(token1Fees).to.eq('1499999999999997') - }) - - it('position owner gets partial fees when protocol fee is on', async () => { - await pool.setFeeProtocol(6, 6) - - const { token0Fees, token1Fees } = await swapAndGetFeesOwed({ - amount: expandTo18Decimals(1), - zeroForOne: true, - poke: true, - }) - - expect(token0Fees).to.be.eq('416666666666666') - expect(token1Fees).to.be.eq(0) - }) - - describe('#collectProtocol', () => { - it('returns 0 if no fees', async () => { - await pool.setFeeProtocol(6, 6) - const { amount0, amount1 } = await pool.callStatic.collectProtocol(wallet.address, MaxUint128, MaxUint128) - expect(amount0).to.be.eq(0) - expect(amount1).to.be.eq(0) - }) - - it('can collect fees', async () => { - await pool.setFeeProtocol(6, 6) - - await swapAndGetFeesOwed({ - amount: expandTo18Decimals(1), - zeroForOne: true, - poke: true, - }) - - await expect(pool.collectProtocol(other.address, MaxUint128, MaxUint128)) - .to.emit(token0, 'Transfer') - .withArgs(pool.address, other.address, '83333333333332') - }) - - it('fees collected can differ between token0 and token1', async () => { - await pool.setFeeProtocol(8, 5) - - await swapAndGetFeesOwed({ - amount: expandTo18Decimals(1), - zeroForOne: true, - poke: false, - }) - await swapAndGetFeesOwed({ - amount: expandTo18Decimals(1), - zeroForOne: false, - poke: false, - }) - - await expect(pool.collectProtocol(other.address, MaxUint128, MaxUint128)) - .to.emit(token0, 'Transfer') - // more token0 fees because it's 1/5th the swap fees - .withArgs(pool.address, other.address, '62499999999999') - .to.emit(token1, 'Transfer') - // less token1 fees because it's 1/8th the swap fees - .withArgs(pool.address, other.address, '99999999999998') - }) - }) - - it('fees collected by lp after two swaps should be double one swap', async () => { - await swapAndGetFeesOwed({ - amount: expandTo18Decimals(1), - zeroForOne: true, - poke: true, - }) - const { token0Fees, token1Fees } = await swapAndGetFeesOwed({ - amount: expandTo18Decimals(1), - zeroForOne: true, - poke: true, - }) - - // 6 bips * 2e18 - expect(token0Fees).to.eq('999999999999998') - expect(token1Fees).to.eq(0) - }) - - it('fees collected after two swaps with fee turned on in middle are fees from last swap (not confiscatory)', async () => { - await swapAndGetFeesOwed({ - amount: expandTo18Decimals(1), - zeroForOne: true, - poke: false, - }) - - await pool.setFeeProtocol(6, 6) - - const { token0Fees, token1Fees } = await swapAndGetFeesOwed({ - amount: expandTo18Decimals(1), - zeroForOne: true, - poke: true, - }) - - expect(token0Fees).to.eq('916666666666666') - expect(token1Fees).to.eq(0) - }) - - it('fees collected by lp after two swaps with intermediate withdrawal', async () => { - await pool.setFeeProtocol(6, 6) - - const { token0Fees, token1Fees } = await swapAndGetFeesOwed({ - amount: expandTo18Decimals(1), - zeroForOne: true, - poke: true, - }) - - expect(token0Fees).to.eq('416666666666666') - expect(token1Fees).to.eq(0) - - // collect the fees - await pool.collect(wallet.address, minTick, maxTick, MaxUint128, MaxUint128) - - const { token0Fees: token0FeesNext, token1Fees: token1FeesNext } = await swapAndGetFeesOwed({ - amount: expandTo18Decimals(1), - zeroForOne: true, - poke: false, - }) - - expect(token0FeesNext).to.eq(0) - expect(token1FeesNext).to.eq(0) - - let { token0: token0ProtocolFees, token1: token1ProtocolFees } = await pool.protocolFees() - expect(token0ProtocolFees).to.eq('166666666666666') - expect(token1ProtocolFees).to.eq(0) - - await pool.burn(minTick, maxTick, 0) // poke to update fees - await expect(pool.collect(wallet.address, minTick, maxTick, MaxUint128, MaxUint128)) - .to.emit(token0, 'Transfer') - .withArgs(pool.address, wallet.address, '416666666666666') - ;({ token0: token0ProtocolFees, token1: token1ProtocolFees } = await pool.protocolFees()) - expect(token0ProtocolFees).to.eq('166666666666666') - expect(token1ProtocolFees).to.eq(0) - }) - }) - - describe('#tickSpacing', () => { - describe('tickSpacing = 12', () => { - beforeEach('deploy pool', async () => { - pool = await createPool(FeeAmount.MEDIUM, 12) - }) - describe('post initialize', () => { - beforeEach('initialize pool', async () => { - await pool.initialize(encodeSqrtPriceX96(1, 1)) - }) - it('mint can only be called for multiples of 12', async () => { - await expect(mint(wallet.address, -6, 0, 1)).to.be.reverted - await expect(mint(wallet.address, 0, 6, 1)).to.be.reverted - }) - it('mint can be called with multiples of 12', async () => { - await mint(wallet.address, 12, 24, 1) - await mint(wallet.address, -144, -120, 1) - }) - it('swapping across gaps works in 1 for 0 direction', async () => { - const liquidityAmount = expandTo18Decimals(1).div(4) - await mint(wallet.address, 120000, 121200, liquidityAmount) - await swapExact1For0(expandTo18Decimals(1), wallet.address) - await expect(pool.burn(120000, 121200, liquidityAmount)) - .to.emit(pool, 'Burn') - .withArgs(wallet.address, 120000, 121200, liquidityAmount, '30027458295511', '996999999999999999') - .to.not.emit(token0, 'Transfer') - .to.not.emit(token1, 'Transfer') - expect((await pool.slot0()).tick).to.eq(120196) - }) - it('swapping across gaps works in 0 for 1 direction', async () => { - const liquidityAmount = expandTo18Decimals(1).div(4) - await mint(wallet.address, -121200, -120000, liquidityAmount) - await swapExact0For1(expandTo18Decimals(1), wallet.address) - await expect(pool.burn(-121200, -120000, liquidityAmount)) - .to.emit(pool, 'Burn') - .withArgs(wallet.address, -121200, -120000, liquidityAmount, '996999999999999999', '30027458295511') - .to.not.emit(token0, 'Transfer') - .to.not.emit(token1, 'Transfer') - expect((await pool.slot0()).tick).to.eq(-120197) - }) - }) - }) - }) - - // https://github.com/Uniswap/v3-core/issues/214 - it('tick transition cannot run twice if zero for one swap ends at fractional price just below tick', async () => { - pool = await createPool(FeeAmount.MEDIUM, 1) - const sqrtTickMath = (await (await ethers.getContractFactory('TickMathTest')).deploy()) as TickMathTest - const swapMath = (await (await ethers.getContractFactory('SwapMathTest')).deploy()) as SwapMathTest - const p0 = (await sqrtTickMath.getSqrtRatioAtTick(-24081)).add(1) - // initialize at a price of ~0.3 token1/token0 - // meaning if you swap in 2 token0, you should end up getting 0 token1 - await pool.initialize(p0) - expect(await pool.liquidity(), 'current pool liquidity is 1').to.eq(0) - expect((await pool.slot0()).tick, 'pool tick is -24081').to.eq(-24081) - - // add a bunch of liquidity around current price - const liquidity = expandTo18Decimals(1000) - await mint(wallet.address, -24082, -24080, liquidity) - expect(await pool.liquidity(), 'current pool liquidity is now liquidity + 1').to.eq(liquidity) - - await mint(wallet.address, -24082, -24081, liquidity) - expect(await pool.liquidity(), 'current pool liquidity is still liquidity + 1').to.eq(liquidity) - - // check the math works out to moving the price down 1, sending no amount out, and having some amount remaining - { - const { feeAmount, amountIn, amountOut, sqrtQ } = await swapMath.computeSwapStep( - p0, - p0.sub(1), - liquidity, - 3, - FeeAmount.MEDIUM - ) - expect(sqrtQ, 'price moves').to.eq(p0.sub(1)) - expect(feeAmount, 'fee amount is 1').to.eq(1) - expect(amountIn, 'amount in is 1').to.eq(1) - expect(amountOut, 'zero amount out').to.eq(0) - } - - // swap 2 amount in, should get 0 amount out - await expect(swapExact0For1(3, wallet.address)) - .to.emit(token0, 'Transfer') - .withArgs(wallet.address, pool.address, 3) - .to.not.emit(token1, 'Transfer') - - const { tick, sqrtPriceX96 } = await pool.slot0() - - expect(tick, 'pool is at the next tick').to.eq(-24082) - expect(sqrtPriceX96, 'pool price is still on the p0 boundary').to.eq(p0.sub(1)) - expect(await pool.liquidity(), 'pool has run tick transition and liquidity changed').to.eq(liquidity.mul(2)) - }) - - describe('#flash', () => { - it('fails if not initialized', async () => { - await expect(flash(100, 200, other.address)).to.be.reverted - await expect(flash(100, 0, other.address)).to.be.reverted - await expect(flash(0, 200, other.address)).to.be.reverted - }) - it('fails if no liquidity', async () => { - await pool.initialize(encodeSqrtPriceX96(1, 1)) - await expect(flash(100, 200, other.address)).to.be.revertedWith('L') - await expect(flash(100, 0, other.address)).to.be.revertedWith('L') - await expect(flash(0, 200, other.address)).to.be.revertedWith('L') - }) - describe('after liquidity added', () => { - let balance0: BigNumber - let balance1: BigNumber - beforeEach('add some tokens', async () => { - await initializeAtZeroTick(pool) - ;[balance0, balance1] = await Promise.all([token0.balanceOf(pool.address), token1.balanceOf(pool.address)]) - }) - - describe('fee off', () => { - it('emits an event', async () => { - await expect(flash(1001, 2001, other.address)) - .to.emit(pool, 'Flash') - .withArgs(swapTarget.address, other.address, 1001, 2001, 4, 7) - }) - - it('transfers the amount0 to the recipient', async () => { - await expect(flash(100, 200, other.address)) - .to.emit(token0, 'Transfer') - .withArgs(pool.address, other.address, 100) - }) - it('transfers the amount1 to the recipient', async () => { - await expect(flash(100, 200, other.address)) - .to.emit(token1, 'Transfer') - .withArgs(pool.address, other.address, 200) - }) - it('can flash only token0', async () => { - await expect(flash(101, 0, other.address)) - .to.emit(token0, 'Transfer') - .withArgs(pool.address, other.address, 101) - .to.not.emit(token1, 'Transfer') - }) - it('can flash only token1', async () => { - await expect(flash(0, 102, other.address)) - .to.emit(token1, 'Transfer') - .withArgs(pool.address, other.address, 102) - .to.not.emit(token0, 'Transfer') - }) - it('can flash entire token balance', async () => { - await expect(flash(balance0, balance1, other.address)) - .to.emit(token0, 'Transfer') - .withArgs(pool.address, other.address, balance0) - .to.emit(token1, 'Transfer') - .withArgs(pool.address, other.address, balance1) - }) - it('no-op if both amounts are 0', async () => { - await expect(flash(0, 0, other.address)).to.not.emit(token0, 'Transfer').to.not.emit(token1, 'Transfer') - }) - it('fails if flash amount is greater than token balance', async () => { - await expect(flash(balance0.add(1), balance1, other.address)).to.be.reverted - await expect(flash(balance0, balance1.add(1), other.address)).to.be.reverted - }) - it('calls the flash callback on the sender with correct fee amounts', async () => { - await expect(flash(1001, 2002, other.address)).to.emit(swapTarget, 'FlashCallback').withArgs(4, 7) - }) - it('increases the fee growth by the expected amount', async () => { - await flash(1001, 2002, other.address) - expect(await pool.feeGrowthGlobal0X128()).to.eq( - BigNumber.from(4).mul(BigNumber.from(2).pow(128)).div(expandTo18Decimals(2)) - ) - expect(await pool.feeGrowthGlobal1X128()).to.eq( - BigNumber.from(7).mul(BigNumber.from(2).pow(128)).div(expandTo18Decimals(2)) - ) - }) - it('fails if original balance not returned in either token', async () => { - await expect(flash(1000, 0, other.address, 999, 0)).to.be.reverted - await expect(flash(0, 1000, other.address, 0, 999)).to.be.reverted - }) - it('fails if underpays either token', async () => { - await expect(flash(1000, 0, other.address, 1002, 0)).to.be.reverted - await expect(flash(0, 1000, other.address, 0, 1002)).to.be.reverted - }) - it('allows donating token0', async () => { - await expect(flash(0, 0, constants.AddressZero, 567, 0)) - .to.emit(token0, 'Transfer') - .withArgs(wallet.address, pool.address, 567) - .to.not.emit(token1, 'Transfer') - expect(await pool.feeGrowthGlobal0X128()).to.eq( - BigNumber.from(567).mul(BigNumber.from(2).pow(128)).div(expandTo18Decimals(2)) - ) - }) - it('allows donating token1', async () => { - await expect(flash(0, 0, constants.AddressZero, 0, 678)) - .to.emit(token1, 'Transfer') - .withArgs(wallet.address, pool.address, 678) - .to.not.emit(token0, 'Transfer') - expect(await pool.feeGrowthGlobal1X128()).to.eq( - BigNumber.from(678).mul(BigNumber.from(2).pow(128)).div(expandTo18Decimals(2)) - ) - }) - it('allows donating token0 and token1 together', async () => { - await expect(flash(0, 0, constants.AddressZero, 789, 1234)) - .to.emit(token0, 'Transfer') - .withArgs(wallet.address, pool.address, 789) - .to.emit(token1, 'Transfer') - .withArgs(wallet.address, pool.address, 1234) - - expect(await pool.feeGrowthGlobal0X128()).to.eq( - BigNumber.from(789).mul(BigNumber.from(2).pow(128)).div(expandTo18Decimals(2)) - ) - expect(await pool.feeGrowthGlobal1X128()).to.eq( - BigNumber.from(1234).mul(BigNumber.from(2).pow(128)).div(expandTo18Decimals(2)) - ) - }) - }) - - describe('fee on', () => { - beforeEach('turn protocol fee on', async () => { - await pool.setFeeProtocol(6, 6) - }) - - it('emits an event', async () => { - await expect(flash(1001, 2001, other.address)) - .to.emit(pool, 'Flash') - .withArgs(swapTarget.address, other.address, 1001, 2001, 4, 7) - }) - - it('increases the fee growth by the expected amount', async () => { - await flash(2002, 4004, other.address) - - const { token0: token0ProtocolFees, token1: token1ProtocolFees } = await pool.protocolFees() - expect(token0ProtocolFees).to.eq(1) - expect(token1ProtocolFees).to.eq(2) - - expect(await pool.feeGrowthGlobal0X128()).to.eq( - BigNumber.from(6).mul(BigNumber.from(2).pow(128)).div(expandTo18Decimals(2)) - ) - expect(await pool.feeGrowthGlobal1X128()).to.eq( - BigNumber.from(11).mul(BigNumber.from(2).pow(128)).div(expandTo18Decimals(2)) - ) - }) - it('allows donating token0', async () => { - await expect(flash(0, 0, constants.AddressZero, 567, 0)) - .to.emit(token0, 'Transfer') - .withArgs(wallet.address, pool.address, 567) - .to.not.emit(token1, 'Transfer') - - const { token0: token0ProtocolFees } = await pool.protocolFees() - expect(token0ProtocolFees).to.eq(94) - - expect(await pool.feeGrowthGlobal0X128()).to.eq( - BigNumber.from(473).mul(BigNumber.from(2).pow(128)).div(expandTo18Decimals(2)) - ) - }) - it('allows donating token1', async () => { - await expect(flash(0, 0, constants.AddressZero, 0, 678)) - .to.emit(token1, 'Transfer') - .withArgs(wallet.address, pool.address, 678) - .to.not.emit(token0, 'Transfer') - - const { token1: token1ProtocolFees } = await pool.protocolFees() - expect(token1ProtocolFees).to.eq(113) - - expect(await pool.feeGrowthGlobal1X128()).to.eq( - BigNumber.from(565).mul(BigNumber.from(2).pow(128)).div(expandTo18Decimals(2)) - ) - }) - it('allows donating token0 and token1 together', async () => { - await expect(flash(0, 0, constants.AddressZero, 789, 1234)) - .to.emit(token0, 'Transfer') - .withArgs(wallet.address, pool.address, 789) - .to.emit(token1, 'Transfer') - .withArgs(wallet.address, pool.address, 1234) - - const { token0: token0ProtocolFees, token1: token1ProtocolFees } = await pool.protocolFees() - expect(token0ProtocolFees).to.eq(131) - expect(token1ProtocolFees).to.eq(205) - - expect(await pool.feeGrowthGlobal0X128()).to.eq( - BigNumber.from(658).mul(BigNumber.from(2).pow(128)).div(expandTo18Decimals(2)) - ) - expect(await pool.feeGrowthGlobal1X128()).to.eq( - BigNumber.from(1029).mul(BigNumber.from(2).pow(128)).div(expandTo18Decimals(2)) - ) - }) - }) - }) - }) - - describe('#increaseObservationCardinalityNext', () => { - it('cannot be called before initialization', async () => { - await expect(pool.increaseObservationCardinalityNext(2)).to.be.reverted - }) - describe('after initialization', () => { - beforeEach('initialize the pool', () => pool.initialize(encodeSqrtPriceX96(1, 1))) - it('oracle starting state after initialization', async () => { - const { observationCardinality, observationIndex, observationCardinalityNext } = await pool.slot0() - expect(observationCardinality).to.eq(1) - expect(observationIndex).to.eq(0) - expect(observationCardinalityNext).to.eq(1) - const { secondsPerLiquidityCumulativeX128, tickCumulative, initialized, blockTimestamp } = - await pool.observations(0) - expect(secondsPerLiquidityCumulativeX128).to.eq(0) - expect(tickCumulative).to.eq(0) - expect(initialized).to.eq(true) - expect(blockTimestamp).to.eq(TEST_POOL_START_TIME) - }) - it('increases observation cardinality next', async () => { - await pool.increaseObservationCardinalityNext(2) - const { observationCardinality, observationIndex, observationCardinalityNext } = await pool.slot0() - expect(observationCardinality).to.eq(1) - expect(observationIndex).to.eq(0) - expect(observationCardinalityNext).to.eq(2) - }) - it('is no op if target is already exceeded', async () => { - await pool.increaseObservationCardinalityNext(5) - await pool.increaseObservationCardinalityNext(3) - const { observationCardinality, observationIndex, observationCardinalityNext } = await pool.slot0() - expect(observationCardinality).to.eq(1) - expect(observationIndex).to.eq(0) - expect(observationCardinalityNext).to.eq(5) - }) - }) - }) - - describe('#setFeeProtocol', () => { - beforeEach('initialize the pool', async () => { - await pool.initialize(encodeSqrtPriceX96(1, 1)) - }) - - it('can only be called by factory owner', async () => { - await expect(pool.connect(other).setFeeProtocol(5, 5)).to.be.reverted - }) - it('fails if fee is lt 4 or gt 10', async () => { - await expect(pool.setFeeProtocol(3, 3)).to.be.reverted - await expect(pool.setFeeProtocol(6, 3)).to.be.reverted - await expect(pool.setFeeProtocol(3, 6)).to.be.reverted - await expect(pool.setFeeProtocol(11, 11)).to.be.reverted - await expect(pool.setFeeProtocol(6, 11)).to.be.reverted - await expect(pool.setFeeProtocol(11, 6)).to.be.reverted - }) - it('succeeds for fee of 4', async () => { - await pool.setFeeProtocol(4, 4) - }) - it('succeeds for fee of 10', async () => { - await pool.setFeeProtocol(10, 10) - }) - it('sets protocol fee', async () => { - await pool.setFeeProtocol(7, 7) - expect((await pool.slot0()).feeProtocol).to.eq(119) - }) - it('can change protocol fee', async () => { - await pool.setFeeProtocol(7, 7) - await pool.setFeeProtocol(5, 8) - expect((await pool.slot0()).feeProtocol).to.eq(133) - }) - it('can turn off protocol fee', async () => { - await pool.setFeeProtocol(4, 4) - await pool.setFeeProtocol(0, 0) - expect((await pool.slot0()).feeProtocol).to.eq(0) - }) - it('emits an event when turned on', async () => { - await expect(pool.setFeeProtocol(7, 7)).to.be.emit(pool, 'SetFeeProtocol').withArgs(0, 0, 7, 7) - }) - it('emits an event when turned off', async () => { - await pool.setFeeProtocol(7, 5) - await expect(pool.setFeeProtocol(0, 0)).to.be.emit(pool, 'SetFeeProtocol').withArgs(7, 5, 0, 0) - }) - it('emits an event when changed', async () => { - await pool.setFeeProtocol(4, 10) - await expect(pool.setFeeProtocol(6, 8)).to.be.emit(pool, 'SetFeeProtocol').withArgs(4, 10, 6, 8) - }) - it('emits an event when unchanged', async () => { - await pool.setFeeProtocol(5, 9) - await expect(pool.setFeeProtocol(5, 9)).to.be.emit(pool, 'SetFeeProtocol').withArgs(5, 9, 5, 9) - }) - }) - - describe('#lock', () => { - beforeEach('initialize the pool', async () => { - await pool.initialize(encodeSqrtPriceX96(1, 1)) - await mint(wallet.address, minTick, maxTick, expandTo18Decimals(1)) - }) - - it('cannot reenter from swap callback', async () => { - const reentrant = (await (await ethers.getContractFactory('ReentrancyTester')).deploy()) as ReentrancyTester - - // the tests happen in solidity - await expect(reentrant.swapToReenter(pool.address)).to.be.revertedWith('Unable to reenter') - }) - }) - - describe('#snapshotCumulativesInside', () => { - const tickLower = -TICK_SPACINGS[FeeAmount.MEDIUM] - const tickUpper = TICK_SPACINGS[FeeAmount.MEDIUM] - const tickSpacing = TICK_SPACINGS[FeeAmount.MEDIUM] - beforeEach(async () => { - await pool.initialize(encodeSqrtPriceX96(1, 1)) - await mint(wallet.address, tickLower, tickUpper, 10) - }) - it('throws if ticks are in reverse order', async () => { - await expect(pool.snapshotCumulativesInside(tickUpper, tickLower)).to.be.reverted - }) - it('throws if ticks are the same', async () => { - await expect(pool.snapshotCumulativesInside(tickUpper, tickUpper)).to.be.reverted - }) - it('throws if tick lower is too low', async () => { - await expect(pool.snapshotCumulativesInside(getMinTick(tickSpacing) - 1, tickUpper)).be.reverted - }) - it('throws if tick upper is too high', async () => { - await expect(pool.snapshotCumulativesInside(tickLower, getMaxTick(tickSpacing) + 1)).be.reverted - }) - it('throws if tick lower is not initialized', async () => { - await expect(pool.snapshotCumulativesInside(tickLower - tickSpacing, tickUpper)).to.be.reverted - }) - it('throws if tick upper is not initialized', async () => { - await expect(pool.snapshotCumulativesInside(tickLower, tickUpper + tickSpacing)).to.be.reverted - }) - it('is zero immediately after initialize', async () => { - const { secondsPerLiquidityInsideX128, tickCumulativeInside, secondsInside } = - await pool.snapshotCumulativesInside(tickLower, tickUpper) - expect(secondsPerLiquidityInsideX128).to.eq(0) - expect(tickCumulativeInside).to.eq(0) - expect(secondsInside).to.eq(0) - }) - it('increases by expected amount when time elapses in the range', async () => { - await pool.advanceTime(5) - const { secondsPerLiquidityInsideX128, tickCumulativeInside, secondsInside } = - await pool.snapshotCumulativesInside(tickLower, tickUpper) - expect(secondsPerLiquidityInsideX128).to.eq(BigNumber.from(5).shl(128).div(10)) - expect(tickCumulativeInside, 'tickCumulativeInside').to.eq(0) - expect(secondsInside).to.eq(5) - }) - it('does not account for time increase above range', async () => { - await pool.advanceTime(5) - await swapToHigherPrice(encodeSqrtPriceX96(2, 1), wallet.address) - await pool.advanceTime(7) - const { secondsPerLiquidityInsideX128, tickCumulativeInside, secondsInside } = - await pool.snapshotCumulativesInside(tickLower, tickUpper) - expect(secondsPerLiquidityInsideX128).to.eq(BigNumber.from(5).shl(128).div(10)) - expect(tickCumulativeInside, 'tickCumulativeInside').to.eq(0) - expect(secondsInside).to.eq(5) - }) - it('does not account for time increase below range', async () => { - await pool.advanceTime(5) - await swapToLowerPrice(encodeSqrtPriceX96(1, 2), wallet.address) - await pool.advanceTime(7) - const { secondsPerLiquidityInsideX128, tickCumulativeInside, secondsInside } = - await pool.snapshotCumulativesInside(tickLower, tickUpper) - expect(secondsPerLiquidityInsideX128).to.eq(BigNumber.from(5).shl(128).div(10)) - // tick is 0 for 5 seconds, then not in range - expect(tickCumulativeInside, 'tickCumulativeInside').to.eq(0) - expect(secondsInside).to.eq(5) - }) - it('time increase below range is not counted', async () => { - await swapToLowerPrice(encodeSqrtPriceX96(1, 2), wallet.address) - await pool.advanceTime(5) - await swapToHigherPrice(encodeSqrtPriceX96(1, 1), wallet.address) - await pool.advanceTime(7) - const { secondsPerLiquidityInsideX128, tickCumulativeInside, secondsInside } = - await pool.snapshotCumulativesInside(tickLower, tickUpper) - expect(secondsPerLiquidityInsideX128).to.eq(BigNumber.from(7).shl(128).div(10)) - // tick is not in range then tick is 0 for 7 seconds - expect(tickCumulativeInside, 'tickCumulativeInside').to.eq(0) - expect(secondsInside).to.eq(7) - }) - it('time increase above range is not counted', async () => { - await swapToHigherPrice(encodeSqrtPriceX96(2, 1), wallet.address) - await pool.advanceTime(5) - await swapToLowerPrice(encodeSqrtPriceX96(1, 1), wallet.address) - await pool.advanceTime(7) - const { secondsPerLiquidityInsideX128, tickCumulativeInside, secondsInside } = - await pool.snapshotCumulativesInside(tickLower, tickUpper) - expect(secondsPerLiquidityInsideX128).to.eq(BigNumber.from(7).shl(128).div(10)) - expect((await pool.slot0()).tick).to.eq(-1) // justify the -7 tick cumulative inside value - expect(tickCumulativeInside, 'tickCumulativeInside').to.eq(-7) - expect(secondsInside).to.eq(7) - }) - it('positions minted after time spent', async () => { - await pool.advanceTime(5) - await mint(wallet.address, tickUpper, getMaxTick(tickSpacing), 15) - await swapToHigherPrice(encodeSqrtPriceX96(2, 1), wallet.address) - await pool.advanceTime(8) - const { secondsPerLiquidityInsideX128, tickCumulativeInside, secondsInside } = - await pool.snapshotCumulativesInside(tickUpper, getMaxTick(tickSpacing)) - expect(secondsPerLiquidityInsideX128).to.eq(BigNumber.from(8).shl(128).div(15)) - // the tick of 2/1 is 6931 - // 8 seconds * 6931 = 55448 - expect(tickCumulativeInside, 'tickCumulativeInside').to.eq(55448) - expect(secondsInside).to.eq(8) - }) - it('overlapping liquidity is aggregated', async () => { - await mint(wallet.address, tickLower, getMaxTick(tickSpacing), 15) - await pool.advanceTime(5) - await swapToHigherPrice(encodeSqrtPriceX96(2, 1), wallet.address) - await pool.advanceTime(8) - const { secondsPerLiquidityInsideX128, tickCumulativeInside, secondsInside } = - await pool.snapshotCumulativesInside(tickLower, tickUpper) - expect(secondsPerLiquidityInsideX128).to.eq(BigNumber.from(5).shl(128).div(25)) - expect(tickCumulativeInside, 'tickCumulativeInside').to.eq(0) - expect(secondsInside).to.eq(5) - }) - it('relative behavior of snapshots', async () => { - await pool.advanceTime(5) - await mint(wallet.address, getMinTick(tickSpacing), tickLower, 15) - const { - secondsPerLiquidityInsideX128: secondsPerLiquidityInsideX128Start, - tickCumulativeInside: tickCumulativeInsideStart, - secondsInside: secondsInsideStart, - } = await pool.snapshotCumulativesInside(getMinTick(tickSpacing), tickLower) - await pool.advanceTime(8) - // 13 seconds in starting range, then 3 seconds in newly minted range - await swapToLowerPrice(encodeSqrtPriceX96(1, 2), wallet.address) - await pool.advanceTime(3) - const { secondsPerLiquidityInsideX128, tickCumulativeInside, secondsInside } = - await pool.snapshotCumulativesInside(getMinTick(tickSpacing), tickLower) - const expectedDiffSecondsPerLiquidity = BigNumber.from(3).shl(128).div(15) - expect(secondsPerLiquidityInsideX128.sub(secondsPerLiquidityInsideX128Start)).to.eq( - expectedDiffSecondsPerLiquidity - ) - expect(secondsPerLiquidityInsideX128).to.not.eq(expectedDiffSecondsPerLiquidity) - // the tick is the one corresponding to the price of 1/2, or log base 1.0001 of 0.5 - // this is -6932, and 3 seconds have passed, so the cumulative computed from the diff equals 6932 * 3 - expect(tickCumulativeInside.sub(tickCumulativeInsideStart), 'tickCumulativeInside').to.eq(-20796) - expect(secondsInside - secondsInsideStart).to.eq(3) - expect(secondsInside).to.not.eq(3) - }) - }) - - describe('fees overflow scenarios', async () => { - it('up to max uint 128', async () => { - await pool.initialize(encodeSqrtPriceX96(1, 1)) - await mint(wallet.address, minTick, maxTick, 1) - await flash(0, 0, wallet.address, MaxUint128, MaxUint128) - - const [feeGrowthGlobal0X128, feeGrowthGlobal1X128] = await Promise.all([ - pool.feeGrowthGlobal0X128(), - pool.feeGrowthGlobal1X128(), - ]) - // all 1s in first 128 bits - expect(feeGrowthGlobal0X128).to.eq(MaxUint128.shl(128)) - expect(feeGrowthGlobal1X128).to.eq(MaxUint128.shl(128)) - await pool.burn(minTick, maxTick, 0) - const { amount0, amount1 } = await pool.callStatic.collect( - wallet.address, - minTick, - maxTick, - MaxUint128, - MaxUint128 - ) - expect(amount0).to.eq(MaxUint128) - expect(amount1).to.eq(MaxUint128) - }) - - it('overflow max uint 128', async () => { - await pool.initialize(encodeSqrtPriceX96(1, 1)) - await mint(wallet.address, minTick, maxTick, 1) - await flash(0, 0, wallet.address, MaxUint128, MaxUint128) - await flash(0, 0, wallet.address, 1, 1) - - const [feeGrowthGlobal0X128, feeGrowthGlobal1X128] = await Promise.all([ - pool.feeGrowthGlobal0X128(), - pool.feeGrowthGlobal1X128(), - ]) - // all 1s in first 128 bits - expect(feeGrowthGlobal0X128).to.eq(0) - expect(feeGrowthGlobal1X128).to.eq(0) - await pool.burn(minTick, maxTick, 0) - const { amount0, amount1 } = await pool.callStatic.collect( - wallet.address, - minTick, - maxTick, - MaxUint128, - MaxUint128 - ) - // fees burned - expect(amount0).to.eq(0) - expect(amount1).to.eq(0) - }) - - it('overflow max uint 128 after poke burns fees owed to 0', async () => { - await pool.initialize(encodeSqrtPriceX96(1, 1)) - await mint(wallet.address, minTick, maxTick, 1) - await flash(0, 0, wallet.address, MaxUint128, MaxUint128) - await pool.burn(minTick, maxTick, 0) - await flash(0, 0, wallet.address, 1, 1) - await pool.burn(minTick, maxTick, 0) - - const { amount0, amount1 } = await pool.callStatic.collect( - wallet.address, - minTick, - maxTick, - MaxUint128, - MaxUint128 - ) - // fees burned - expect(amount0).to.eq(0) - expect(amount1).to.eq(0) - }) - - it('two positions at the same snapshot', async () => { - await pool.initialize(encodeSqrtPriceX96(1, 1)) - await mint(wallet.address, minTick, maxTick, 1) - await mint(other.address, minTick, maxTick, 1) - await flash(0, 0, wallet.address, MaxUint128, 0) - await flash(0, 0, wallet.address, MaxUint128, 0) - const feeGrowthGlobal0X128 = await pool.feeGrowthGlobal0X128() - expect(feeGrowthGlobal0X128).to.eq(MaxUint128.shl(128)) - await flash(0, 0, wallet.address, 2, 0) - await pool.burn(minTick, maxTick, 0) - await pool.connect(other).burn(minTick, maxTick, 0) - let { amount0 } = await pool.callStatic.collect(wallet.address, minTick, maxTick, MaxUint128, MaxUint128) - expect(amount0, 'amount0 of wallet').to.eq(0) - ;({ amount0 } = await pool - .connect(other) - .callStatic.collect(other.address, minTick, maxTick, MaxUint128, MaxUint128)) - expect(amount0, 'amount0 of other').to.eq(0) - }) - - it('two positions 1 wei of fees apart overflows exactly once', async () => { - await pool.initialize(encodeSqrtPriceX96(1, 1)) - await mint(wallet.address, minTick, maxTick, 1) - await flash(0, 0, wallet.address, 1, 0) - await mint(other.address, minTick, maxTick, 1) - await flash(0, 0, wallet.address, MaxUint128, 0) - await flash(0, 0, wallet.address, MaxUint128, 0) - const feeGrowthGlobal0X128 = await pool.feeGrowthGlobal0X128() - expect(feeGrowthGlobal0X128).to.eq(0) - await flash(0, 0, wallet.address, 2, 0) - await pool.burn(minTick, maxTick, 0) - await pool.connect(other).burn(minTick, maxTick, 0) - let { amount0 } = await pool.callStatic.collect(wallet.address, minTick, maxTick, MaxUint128, MaxUint128) - expect(amount0, 'amount0 of wallet').to.eq(1) - ;({ amount0 } = await pool - .connect(other) - .callStatic.collect(other.address, minTick, maxTick, MaxUint128, MaxUint128)) - expect(amount0, 'amount0 of other').to.eq(0) - }) - }) - - describe('swap underpayment tests', () => { - let underpay: SwapPaymentTest - beforeEach('deploy swap test', async () => { - const underpayFactory = await ethers.getContractFactory('SwapPaymentTest') - underpay = (await underpayFactory.deploy()) as SwapPaymentTest - await token0.approve(underpay.address, constants.MaxUint256) - await token1.approve(underpay.address, constants.MaxUint256) - await pool.initialize(encodeSqrtPriceX96(1, 1)) - await mint(wallet.address, minTick, maxTick, expandTo18Decimals(1)) - }) - - it('underpay zero for one and exact in', async () => { - await expect( - underpay.swap(pool.address, wallet.address, true, MIN_SQRT_RATIO.add(1), 1000, 1, 0) - ).to.be.revertedWith('IIA') - }) - it('pay in the wrong token zero for one and exact in', async () => { - await expect( - underpay.swap(pool.address, wallet.address, true, MIN_SQRT_RATIO.add(1), 1000, 0, 2000) - ).to.be.revertedWith('IIA') - }) - it('overpay zero for one and exact in', async () => { - await expect( - underpay.swap(pool.address, wallet.address, true, MIN_SQRT_RATIO.add(1), 1000, 2000, 0) - ).to.not.be.revertedWith('IIA') - }) - it('underpay zero for one and exact out', async () => { - await expect( - underpay.swap(pool.address, wallet.address, true, MIN_SQRT_RATIO.add(1), -1000, 1, 0) - ).to.be.revertedWith('IIA') - }) - it('pay in the wrong token zero for one and exact out', async () => { - await expect( - underpay.swap(pool.address, wallet.address, true, MIN_SQRT_RATIO.add(1), -1000, 0, 2000) - ).to.be.revertedWith('IIA') - }) - it('overpay zero for one and exact out', async () => { - await expect( - underpay.swap(pool.address, wallet.address, true, MIN_SQRT_RATIO.add(1), -1000, 2000, 0) - ).to.not.be.revertedWith('IIA') - }) - it('underpay one for zero and exact in', async () => { - await expect( - underpay.swap(pool.address, wallet.address, false, MAX_SQRT_RATIO.sub(1), 1000, 0, 1) - ).to.be.revertedWith('IIA') - }) - it('pay in the wrong token one for zero and exact in', async () => { - await expect( - underpay.swap(pool.address, wallet.address, false, MAX_SQRT_RATIO.sub(1), 1000, 2000, 0) - ).to.be.revertedWith('IIA') - }) - it('overpay one for zero and exact in', async () => { - await expect( - underpay.swap(pool.address, wallet.address, false, MAX_SQRT_RATIO.sub(1), 1000, 0, 2000) - ).to.not.be.revertedWith('IIA') - }) - it('underpay one for zero and exact out', async () => { - await expect( - underpay.swap(pool.address, wallet.address, false, MAX_SQRT_RATIO.sub(1), -1000, 0, 1) - ).to.be.revertedWith('IIA') - }) - it('pay in the wrong token one for zero and exact out', async () => { - await expect( - underpay.swap(pool.address, wallet.address, false, MAX_SQRT_RATIO.sub(1), -1000, 2000, 0) - ).to.be.revertedWith('IIA') - }) - it('overpay one for zero and exact out', async () => { - await expect( - underpay.swap(pool.address, wallet.address, false, MAX_SQRT_RATIO.sub(1), -1000, 0, 2000) - ).to.not.be.revertedWith('IIA') - }) - }) -}) diff --git a/test/Pool.swaps.spec.ts b/test/Pool.swaps.spec.ts deleted file mode 100644 index 1e81f8730..000000000 --- a/test/Pool.swaps.spec.ts +++ /dev/null @@ -1,590 +0,0 @@ -import { Decimal } from 'decimal.js' -import { BigNumber, BigNumberish, ContractTransaction, Wallet } from 'ethers' -import { ethers, waffle } from 'hardhat' -import { MockTimePool } from '../typechain/MockTimePool' -import { TestERC20 } from '../typechain/TestERC20' - -import { SwapTarget } from '../typechain/SwapTarget' -import { expect } from './shared/expect' -import { poolFixture } from './shared/fixtures' -import { formatPrice, formatTokenAmount } from './shared/format' -import { - createPoolFunctions, - encodeSqrtPriceX96, - expandTo18Decimals, - FeeAmount, - getMaxLiquidityPerTick, - getMaxTick, - getMinTick, - MAX_SQRT_RATIO, - MaxUint128, - MIN_SQRT_RATIO, - TICK_SPACINGS, -} from './shared/utilities' - -Decimal.config({ toExpNeg: -500, toExpPos: 500 }) - -const createFixtureLoader = waffle.createFixtureLoader -const { constants } = ethers - -interface BaseSwapTestCase { - zeroForOne: boolean - sqrtPriceLimit?: BigNumber -} -interface SwapExact0For1TestCase extends BaseSwapTestCase { - zeroForOne: true - exactOut: false - amount0: BigNumberish - sqrtPriceLimit?: BigNumber -} -interface SwapExact1For0TestCase extends BaseSwapTestCase { - zeroForOne: false - exactOut: false - amount1: BigNumberish - sqrtPriceLimit?: BigNumber -} -interface Swap0ForExact1TestCase extends BaseSwapTestCase { - zeroForOne: true - exactOut: true - amount1: BigNumberish - sqrtPriceLimit?: BigNumber -} -interface Swap1ForExact0TestCase extends BaseSwapTestCase { - zeroForOne: false - exactOut: true - amount0: BigNumberish - sqrtPriceLimit?: BigNumber -} -interface SwapToHigherPrice extends BaseSwapTestCase { - zeroForOne: false - sqrtPriceLimit: BigNumber -} -interface SwapToLowerPrice extends BaseSwapTestCase { - zeroForOne: true - sqrtPriceLimit: BigNumber -} -type SwapTestCase = - | SwapExact0For1TestCase - | Swap0ForExact1TestCase - | SwapExact1For0TestCase - | Swap1ForExact0TestCase - | SwapToHigherPrice - | SwapToLowerPrice - -function swapCaseToDescription(testCase: SwapTestCase): string { - const priceClause = testCase?.sqrtPriceLimit ? ` to price ${formatPrice(testCase.sqrtPriceLimit)}` : '' - if ('exactOut' in testCase) { - if (testCase.exactOut) { - if (testCase.zeroForOne) { - return `swap token0 for exactly ${formatTokenAmount(testCase.amount1)} token1${priceClause}` - } else { - return `swap token1 for exactly ${formatTokenAmount(testCase.amount0)} token0${priceClause}` - } - } else { - if (testCase.zeroForOne) { - return `swap exactly ${formatTokenAmount(testCase.amount0)} token0 for token1${priceClause}` - } else { - return `swap exactly ${formatTokenAmount(testCase.amount1)} token1 for token0${priceClause}` - } - } - } else { - if (testCase.zeroForOne) { - return `swap token0 for token1${priceClause}` - } else { - return `swap token1 for token0${priceClause}` - } - } -} - -type PoolFunctions = ReturnType - -// can't use address zero because the ERC20 token does not allow it -const SWAP_RECIPIENT_ADDRESS = constants.AddressZero.slice(0, -1) + '1' -const POSITION_PROCEEDS_OUTPUT_ADDRESS = constants.AddressZero.slice(0, -1) + '2' - -async function executeSwap( - pool: MockTimePool, - testCase: SwapTestCase, - poolFunctions: PoolFunctions -): Promise { - let swap: ContractTransaction - if ('exactOut' in testCase) { - if (testCase.exactOut) { - if (testCase.zeroForOne) { - swap = await poolFunctions.swap0ForExact1(testCase.amount1, SWAP_RECIPIENT_ADDRESS, testCase.sqrtPriceLimit) - } else { - swap = await poolFunctions.swap1ForExact0(testCase.amount0, SWAP_RECIPIENT_ADDRESS, testCase.sqrtPriceLimit) - } - } else { - if (testCase.zeroForOne) { - swap = await poolFunctions.swapExact0For1(testCase.amount0, SWAP_RECIPIENT_ADDRESS, testCase.sqrtPriceLimit) - } else { - swap = await poolFunctions.swapExact1For0(testCase.amount1, SWAP_RECIPIENT_ADDRESS, testCase.sqrtPriceLimit) - } - } - } else { - if (testCase.zeroForOne) { - swap = await poolFunctions.swapToLowerPrice(testCase.sqrtPriceLimit, SWAP_RECIPIENT_ADDRESS) - } else { - swap = await poolFunctions.swapToHigherPrice(testCase.sqrtPriceLimit, SWAP_RECIPIENT_ADDRESS) - } - } - return swap -} - -const DEFAULT_POOL_SWAP_TESTS: SwapTestCase[] = [ - // swap large amounts in/out - { - zeroForOne: true, - exactOut: false, - amount0: expandTo18Decimals(1), - }, - { - zeroForOne: false, - exactOut: false, - amount1: expandTo18Decimals(1), - }, - { - zeroForOne: true, - exactOut: true, - amount1: expandTo18Decimals(1), - }, - { - zeroForOne: false, - exactOut: true, - amount0: expandTo18Decimals(1), - }, - // swap large amounts in/out with a price limit - { - zeroForOne: true, - exactOut: false, - amount0: expandTo18Decimals(1), - sqrtPriceLimit: encodeSqrtPriceX96(50, 100), - }, - { - zeroForOne: false, - exactOut: false, - amount1: expandTo18Decimals(1), - sqrtPriceLimit: encodeSqrtPriceX96(200, 100), - }, - { - zeroForOne: true, - exactOut: true, - amount1: expandTo18Decimals(1), - sqrtPriceLimit: encodeSqrtPriceX96(50, 100), - }, - { - zeroForOne: false, - exactOut: true, - amount0: expandTo18Decimals(1), - sqrtPriceLimit: encodeSqrtPriceX96(200, 100), - }, - // swap small amounts in/out - { - zeroForOne: true, - exactOut: false, - amount0: 1000, - }, - { - zeroForOne: false, - exactOut: false, - amount1: 1000, - }, - { - zeroForOne: true, - exactOut: true, - amount1: 1000, - }, - { - zeroForOne: false, - exactOut: true, - amount0: 1000, - }, - // swap arbitrary input to price - { - sqrtPriceLimit: encodeSqrtPriceX96(5, 2), - zeroForOne: false, - }, - { - sqrtPriceLimit: encodeSqrtPriceX96(2, 5), - zeroForOne: true, - }, - { - sqrtPriceLimit: encodeSqrtPriceX96(5, 2), - zeroForOne: true, - }, - { - sqrtPriceLimit: encodeSqrtPriceX96(2, 5), - zeroForOne: false, - }, -] - -interface Position { - tickLower: number - tickUpper: number - liquidity: BigNumberish -} - -interface PoolTestCase { - description: string - feeAmount: number - tickSpacing: number - startingPrice: BigNumber - positions: Position[] - swapTests?: SwapTestCase[] -} - -const TEST_POOLS: PoolTestCase[] = [ - { - description: 'low fee, 1:1 price, 2e18 max range liquidity', - feeAmount: FeeAmount.LOW, - tickSpacing: TICK_SPACINGS[FeeAmount.LOW], - startingPrice: encodeSqrtPriceX96(1, 1), - positions: [ - { - tickLower: getMinTick(TICK_SPACINGS[FeeAmount.LOW]), - tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.LOW]), - liquidity: expandTo18Decimals(2), - }, - ], - }, - { - description: 'medium fee, 1:1 price, 2e18 max range liquidity', - feeAmount: FeeAmount.MEDIUM, - tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM], - startingPrice: encodeSqrtPriceX96(1, 1), - positions: [ - { - tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), - tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), - liquidity: expandTo18Decimals(2), - }, - ], - }, - { - description: 'high fee, 1:1 price, 2e18 max range liquidity', - feeAmount: FeeAmount.HIGH, - tickSpacing: TICK_SPACINGS[FeeAmount.HIGH], - startingPrice: encodeSqrtPriceX96(1, 1), - positions: [ - { - tickLower: getMinTick(TICK_SPACINGS[FeeAmount.HIGH]), - tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.HIGH]), - liquidity: expandTo18Decimals(2), - }, - ], - }, - { - description: 'medium fee, 10:1 price, 2e18 max range liquidity', - feeAmount: FeeAmount.MEDIUM, - tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM], - startingPrice: encodeSqrtPriceX96(10, 1), - positions: [ - { - tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), - tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), - liquidity: expandTo18Decimals(2), - }, - ], - }, - { - description: 'medium fee, 1:10 price, 2e18 max range liquidity', - feeAmount: FeeAmount.MEDIUM, - tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM], - startingPrice: encodeSqrtPriceX96(1, 10), - positions: [ - { - tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), - tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), - liquidity: expandTo18Decimals(2), - }, - ], - }, - { - description: 'medium fee, 1:1 price, 0 liquidity, all liquidity around current price', - feeAmount: FeeAmount.MEDIUM, - tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM], - startingPrice: encodeSqrtPriceX96(1, 1), - positions: [ - { - tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), - tickUpper: -TICK_SPACINGS[FeeAmount.MEDIUM], - liquidity: expandTo18Decimals(2), - }, - { - tickLower: TICK_SPACINGS[FeeAmount.MEDIUM], - tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), - liquidity: expandTo18Decimals(2), - }, - ], - }, - { - description: 'medium fee, 1:1 price, additional liquidity around current price', - feeAmount: FeeAmount.MEDIUM, - tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM], - startingPrice: encodeSqrtPriceX96(1, 1), - positions: [ - { - tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), - tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), - liquidity: expandTo18Decimals(2), - }, - { - tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), - tickUpper: -TICK_SPACINGS[FeeAmount.MEDIUM], - liquidity: expandTo18Decimals(2), - }, - { - tickLower: TICK_SPACINGS[FeeAmount.MEDIUM], - tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), - liquidity: expandTo18Decimals(2), - }, - ], - }, - { - description: 'low fee, large liquidity around current price (stable swap)', - feeAmount: FeeAmount.LOW, - tickSpacing: TICK_SPACINGS[FeeAmount.LOW], - startingPrice: encodeSqrtPriceX96(1, 1), - positions: [ - { - tickLower: -TICK_SPACINGS[FeeAmount.LOW], - tickUpper: TICK_SPACINGS[FeeAmount.LOW], - liquidity: expandTo18Decimals(2), - }, - ], - }, - { - description: 'medium fee, token0 liquidity only', - feeAmount: FeeAmount.MEDIUM, - tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM], - startingPrice: encodeSqrtPriceX96(1, 1), - positions: [ - { - tickLower: 0, - tickUpper: 2000 * TICK_SPACINGS[FeeAmount.MEDIUM], - liquidity: expandTo18Decimals(2), - }, - ], - }, - { - description: 'medium fee, token1 liquidity only', - feeAmount: FeeAmount.MEDIUM, - tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM], - startingPrice: encodeSqrtPriceX96(1, 1), - positions: [ - { - tickLower: -2000 * TICK_SPACINGS[FeeAmount.MEDIUM], - tickUpper: 0, - liquidity: expandTo18Decimals(2), - }, - ], - }, - { - description: 'close to max price', - feeAmount: FeeAmount.MEDIUM, - tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM], - startingPrice: encodeSqrtPriceX96(BigNumber.from(2).pow(127), 1), - positions: [ - { - tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), - tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), - liquidity: expandTo18Decimals(2), - }, - ], - }, - { - description: 'close to min price', - feeAmount: FeeAmount.MEDIUM, - tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM], - startingPrice: encodeSqrtPriceX96(1, BigNumber.from(2).pow(127)), - positions: [ - { - tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), - tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), - liquidity: expandTo18Decimals(2), - }, - ], - }, - { - description: 'max full range liquidity at 1:1 price with default fee', - feeAmount: FeeAmount.MEDIUM, - tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM], - startingPrice: encodeSqrtPriceX96(1, 1), - positions: [ - { - tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), - tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), - liquidity: getMaxLiquidityPerTick(TICK_SPACINGS[FeeAmount.MEDIUM]), - }, - ], - }, - { - description: 'initialized at the max ratio', - feeAmount: FeeAmount.MEDIUM, - tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM], - startingPrice: MAX_SQRT_RATIO.sub(1), - positions: [ - { - tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), - tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), - liquidity: expandTo18Decimals(2), - }, - ], - }, - { - description: 'initialized at the min ratio', - feeAmount: FeeAmount.MEDIUM, - tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM], - startingPrice: MIN_SQRT_RATIO, - positions: [ - { - tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), - tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), - liquidity: expandTo18Decimals(2), - }, - ], - }, -] - -describe('Pool swap tests', () => { - let wallet: Wallet, other: Wallet - - let loadFixture: ReturnType - - before('create fixture loader', async () => { - ;[wallet, other] = await (ethers as any).getSigners() - - loadFixture = createFixtureLoader([wallet]) - }) - - for (const poolCase of TEST_POOLS) { - describe(poolCase.description, () => { - const poolCaseFixture = async () => { - const { - createPool, - token0, - token1, - swapTargetCallee: swapTarget, - } = await poolFixture([wallet], waffle.provider) - const pool = await createPool(poolCase.feeAmount, poolCase.tickSpacing) - const poolFunctions = createPoolFunctions({ swapTarget, token0, token1, pool }) - await pool.initialize(poolCase.startingPrice) - // mint all positions - for (const position of poolCase.positions) { - await poolFunctions.mint(wallet.address, position.tickLower, position.tickUpper, position.liquidity) - } - - const [poolBalance0, poolBalance1] = await Promise.all([ - token0.balanceOf(pool.address), - token1.balanceOf(pool.address), - ]) - - return { token0, token1, pool, poolFunctions, poolBalance0, poolBalance1, swapTarget } - } - - let token0: TestERC20 - let token1: TestERC20 - - let poolBalance0: BigNumber - let poolBalance1: BigNumber - - let pool: MockTimePool - let swapTarget: SwapTarget - let poolFunctions: PoolFunctions - - beforeEach('load fixture', async () => { - ;({ token0, token1, pool, poolFunctions, poolBalance0, poolBalance1, swapTarget } = await loadFixture( - poolCaseFixture - )) - }) - - afterEach('check can burn positions', async () => { - for (const { liquidity, tickUpper, tickLower } of poolCase.positions) { - await pool.burn(tickLower, tickUpper, liquidity) - await pool.collect(POSITION_PROCEEDS_OUTPUT_ADDRESS, tickLower, tickUpper, MaxUint128, MaxUint128) - } - }) - - for (const testCase of poolCase.swapTests ?? DEFAULT_POOL_SWAP_TESTS) { - it(swapCaseToDescription(testCase), async () => { - const slot0 = await pool.slot0() - const tx = executeSwap(pool, testCase, poolFunctions) - try { - await tx - } catch (error) { - expect({ - swapError: error.message, - poolBalance0: poolBalance0.toString(), - poolBalance1: poolBalance1.toString(), - poolPriceBefore: formatPrice(slot0.sqrtPriceX96), - tickBefore: slot0.tick, - }).to.matchSnapshot('swap error') - return - } - const [ - poolBalance0After, - poolBalance1After, - slot0After, - liquidityAfter, - feeGrowthGlobal0X128, - feeGrowthGlobal1X128, - ] = await Promise.all([ - token0.balanceOf(pool.address), - token1.balanceOf(pool.address), - pool.slot0(), - pool.liquidity(), - pool.feeGrowthGlobal0X128(), - pool.feeGrowthGlobal1X128(), - ]) - const poolBalance0Delta = poolBalance0After.sub(poolBalance0) - const poolBalance1Delta = poolBalance1After.sub(poolBalance1) - - // check all the events were emitted corresponding to balance changes - if (poolBalance0Delta.eq(0)) await expect(tx).to.not.emit(token0, 'Transfer') - else if (poolBalance0Delta.lt(0)) - await expect(tx) - .to.emit(token0, 'Transfer') - .withArgs(pool.address, SWAP_RECIPIENT_ADDRESS, poolBalance0Delta.mul(-1)) - else await expect(tx).to.emit(token0, 'Transfer').withArgs(wallet.address, pool.address, poolBalance0Delta) - - if (poolBalance1Delta.eq(0)) await expect(tx).to.not.emit(token1, 'Transfer') - else if (poolBalance1Delta.lt(0)) - await expect(tx) - .to.emit(token1, 'Transfer') - .withArgs(pool.address, SWAP_RECIPIENT_ADDRESS, poolBalance1Delta.mul(-1)) - else await expect(tx).to.emit(token1, 'Transfer').withArgs(wallet.address, pool.address, poolBalance1Delta) - - // check that the swap event was emitted too - await expect(tx) - .to.emit(pool, 'Swap') - .withArgs( - swapTarget.address, - SWAP_RECIPIENT_ADDRESS, - poolBalance0Delta, - poolBalance1Delta, - slot0After.sqrtPriceX96, - liquidityAfter, - slot0After.tick - ) - - const executionPrice = new Decimal(poolBalance1Delta.toString()).div(poolBalance0Delta.toString()).mul(-1) - - expect({ - amount0Before: poolBalance0.toString(), - amount1Before: poolBalance1.toString(), - amount0Delta: poolBalance0Delta.toString(), - amount1Delta: poolBalance1Delta.toString(), - feeGrowthGlobal0X128Delta: feeGrowthGlobal0X128.toString(), - feeGrowthGlobal1X128Delta: feeGrowthGlobal1X128.toString(), - tickBefore: slot0.tick, - poolPriceBefore: formatPrice(slot0.sqrtPriceX96), - tickAfter: slot0After.tick, - poolPriceAfter: formatPrice(slot0After.sqrtPriceX96), - executionPrice: executionPrice.toPrecision(5), - }).to.matchSnapshot('balances') - }) - } - }) - } -}) diff --git a/test/PoolFactory.spec.ts b/test/PoolFactory.spec.ts deleted file mode 100644 index 0ad74c945..000000000 --- a/test/PoolFactory.spec.ts +++ /dev/null @@ -1,177 +0,0 @@ -import { Wallet } from 'ethers' -import { ethers, waffle } from 'hardhat' -import { PoolFactory } from '../typechain/PoolFactory' -import { expect } from './shared/expect' -import snapshotGasCost from './shared/snapshotGasCost' - -import { FeeAmount, getCreate2Address, TICK_SPACINGS } from './shared/utilities' - -const { constants } = ethers - -const TEST_ADDRESSES: [string, string] = [ - '0x1000000000000000000000000000000000000000', - '0x2000000000000000000000000000000000000000', -] - -const createFixtureLoader = waffle.createFixtureLoader - -describe('PoolFactory', () => { - let wallet: Wallet, other: Wallet - - let factory: PoolFactory - let poolBytecode: string - const fixture = async () => { - const factoryFactory = await ethers.getContractFactory('PoolFactory') - return (await factoryFactory.deploy()) as PoolFactory - } - - let loadFixture: ReturnType - before('create fixture loader', async () => { - ;[wallet, other] = await (ethers as any).getSigners() - - loadFixture = createFixtureLoader([wallet, other]) - }) - - before('load pool bytecode', async () => { - poolBytecode = (await ethers.getContractFactory('Pool')).bytecode - }) - - beforeEach('deploy factory', async () => { - factory = await loadFixture(fixture) - }) - - it('owner is deployer', async () => { - expect(await factory.owner()).to.eq(wallet.address) - }) - - it('factory bytecode size', async () => { - expect(((await waffle.provider.getCode(factory.address)).length - 2) / 2).to.matchSnapshot() - }) - - it('pool bytecode size', async () => { - await factory.createPool(TEST_ADDRESSES[0], TEST_ADDRESSES[1], FeeAmount.MEDIUM) - const poolAddress = getCreate2Address(factory.address, TEST_ADDRESSES, FeeAmount.MEDIUM, poolBytecode) - expect(((await waffle.provider.getCode(poolAddress)).length - 2) / 2).to.matchSnapshot() - }) - - it('initial enabled fee amounts', async () => { - expect(await factory.feeAmountTickSpacing(FeeAmount.LOW)).to.eq(TICK_SPACINGS[FeeAmount.LOW]) - expect(await factory.feeAmountTickSpacing(FeeAmount.MEDIUM)).to.eq(TICK_SPACINGS[FeeAmount.MEDIUM]) - expect(await factory.feeAmountTickSpacing(FeeAmount.HIGH)).to.eq(TICK_SPACINGS[FeeAmount.HIGH]) - }) - - async function createAndCheckPool( - tokens: [string, string], - feeAmount: FeeAmount, - tickSpacing: number = TICK_SPACINGS[feeAmount] - ) { - const create2Address = getCreate2Address(factory.address, tokens, feeAmount, poolBytecode) - const create = factory.createPool(tokens[0], tokens[1], feeAmount) - - await expect(create) - .to.emit(factory, 'PoolCreated') - .withArgs(TEST_ADDRESSES[0], TEST_ADDRESSES[1], feeAmount, tickSpacing, create2Address) - - await expect(factory.createPool(tokens[0], tokens[1], feeAmount)).to.be.reverted - await expect(factory.createPool(tokens[1], tokens[0], feeAmount)).to.be.reverted - expect(await factory.getPool(tokens[0], tokens[1], feeAmount), 'getPool in order').to.eq(create2Address) - expect(await factory.getPool(tokens[1], tokens[0], feeAmount), 'getPool in reverse').to.eq(create2Address) - - const poolContractFactory = await ethers.getContractFactory('Pool') - const pool = poolContractFactory.attach(create2Address) - expect(await pool.factory(), 'pool factory address').to.eq(factory.address) - expect(await pool.token0(), 'pool token0').to.eq(TEST_ADDRESSES[0]) - expect(await pool.token1(), 'pool token1').to.eq(TEST_ADDRESSES[1]) - expect(await pool.fee(), 'pool fee').to.eq(feeAmount) - expect(await pool.tickSpacing(), 'pool tick spacing').to.eq(tickSpacing) - } - - describe('#createPool', () => { - it('succeeds for low fee pool', async () => { - await createAndCheckPool(TEST_ADDRESSES, FeeAmount.LOW) - }) - - it('succeeds for medium fee pool', async () => { - await createAndCheckPool(TEST_ADDRESSES, FeeAmount.MEDIUM) - }) - it('succeeds for high fee pool', async () => { - await createAndCheckPool(TEST_ADDRESSES, FeeAmount.HIGH) - }) - - it('succeeds if tokens are passed in reverse', async () => { - await createAndCheckPool([TEST_ADDRESSES[1], TEST_ADDRESSES[0]], FeeAmount.MEDIUM) - }) - - it('fails if token a == token b', async () => { - await expect(factory.createPool(TEST_ADDRESSES[0], TEST_ADDRESSES[0], FeeAmount.LOW)).to.be.reverted - }) - - it('fails if token a is 0 or token b is 0', async () => { - await expect(factory.createPool(TEST_ADDRESSES[0], constants.AddressZero, FeeAmount.LOW)).to.be.reverted - await expect(factory.createPool(constants.AddressZero, TEST_ADDRESSES[0], FeeAmount.LOW)).to.be.reverted - await expect(factory.createPool(constants.AddressZero, constants.AddressZero, FeeAmount.LOW)).to.be.revertedWith( - '' - ) - }) - - it('fails if fee amount is not enabled', async () => { - await expect(factory.createPool(TEST_ADDRESSES[0], TEST_ADDRESSES[1], 250)).to.be.reverted - }) - - it('gas', async () => { - await snapshotGasCost(factory.createPool(TEST_ADDRESSES[0], TEST_ADDRESSES[1], FeeAmount.MEDIUM)) - }) - }) - - describe('#setOwner', () => { - it('fails if caller is not owner', async () => { - await expect(factory.connect(other).setOwner(wallet.address)).to.be.reverted - }) - - it('updates owner', async () => { - await factory.setOwner(other.address) - expect(await factory.owner()).to.eq(other.address) - }) - - it('emits event', async () => { - await expect(factory.setOwner(other.address)) - .to.emit(factory, 'OwnerChanged') - .withArgs(wallet.address, other.address) - }) - - it('cannot be called by original owner', async () => { - await factory.setOwner(other.address) - await expect(factory.setOwner(wallet.address)).to.be.reverted - }) - }) - - describe('#enableFeeAmount', () => { - it('fails if caller is not owner', async () => { - await expect(factory.connect(other).enableFeeAmount(100, 2)).to.be.reverted - }) - it('fails if fee is too great', async () => { - await expect(factory.enableFeeAmount(1000000, 10)).to.be.reverted - }) - it('fails if tick spacing is too small', async () => { - await expect(factory.enableFeeAmount(500, 0)).to.be.reverted - }) - it('fails if tick spacing is too large', async () => { - await expect(factory.enableFeeAmount(500, 16834)).to.be.reverted - }) - it('fails if already initialized', async () => { - await factory.enableFeeAmount(100, 5) - await expect(factory.enableFeeAmount(100, 10)).to.be.reverted - }) - it('sets the fee amount in the mapping', async () => { - await factory.enableFeeAmount(100, 5) - expect(await factory.feeAmountTickSpacing(100)).to.eq(5) - }) - it('emits an event', async () => { - await expect(factory.enableFeeAmount(100, 5)).to.emit(factory, 'FeeAmountEnabled').withArgs(100, 5) - }) - it('enables pool creation', async () => { - await factory.enableFeeAmount(250, 15) - await createAndCheckPool([TEST_ADDRESSES[0], TEST_ADDRESSES[1]], 250, 15) - }) - }) -}) diff --git a/test/Pool.gas.spec.ts b/test/PoolManager.gas.spec.ts similarity index 50% rename from test/Pool.gas.spec.ts rename to test/PoolManager.gas.spec.ts index 7c71b3059..c63a9d548 100644 --- a/test/Pool.gas.spec.ts +++ b/test/PoolManager.gas.spec.ts @@ -1,9 +1,9 @@ import { ethers, waffle } from 'hardhat' import { Wallet } from 'ethers' -import { MockTimePool } from '../typechain/MockTimePool' +import { MockTimePoolManager, PoolSwapTest, PoolMintTest, PoolBurnTest } from '../typechain' import { expect } from './shared/expect' -import { poolFixture } from './shared/fixtures' +import { tokensFixture } from './shared/fixtures' import snapshotGasCost from './shared/snapshotGasCost' import { @@ -12,7 +12,6 @@ import { getMinTick, encodeSqrtPriceX96, TICK_SPACINGS, - createPoolFunctions, SwapFunction, MintFunction, getMaxTick, @@ -20,11 +19,20 @@ import { SwapToPriceFunction, MAX_SQRT_RATIO, MIN_SQRT_RATIO, + getPoolId, } from './shared/utilities' +const { constants } = ethers + const createFixtureLoader = waffle.createFixtureLoader -describe('Pool gas tests', () => { +type AsyncReturnType any> = T extends (...args: any) => Promise + ? U + : T extends (...args: any) => infer U + ? U + : any + +describe.only('PoolManager gas tests', () => { let wallet: Wallet, other: Wallet let loadFixture: ReturnType @@ -44,72 +52,124 @@ describe('Pool gas tests', () => { const maxTick = getMaxTick(tickSpacing) const gasTestFixture = async ([wallet]: Wallet[]) => { - const fix = await poolFixture([wallet], waffle.provider) + const { token0, token1 } = await tokensFixture() + + const singletonPoolFactory = await ethers.getContractFactory('MockTimePoolManager') + const swapTestFactory = await ethers.getContractFactory('PoolSwapTest') + const mintTestFactory = await ethers.getContractFactory('PoolMintTest') + const burnTestFactory = await ethers.getContractFactory('PoolBurnTest') + const manager = (await singletonPoolFactory.deploy()) as MockTimePoolManager + + const swapTest = (await swapTestFactory.deploy(manager.address)) as PoolSwapTest + const mintTest = (await mintTestFactory.deploy(manager.address)) as PoolMintTest + const burnTest = (await burnTestFactory.deploy(manager.address)) as PoolBurnTest + + for (const token of [token0, token1]) { + for (const spender of [swapTest, mintTest, burnTest]) { + await token.connect(wallet).approve(spender.address, constants.MaxUint256) + } + } - const pool = await fix.createPool(feeAmount, tickSpacing) + const poolKey = { token0: token0.address, token1: token1.address, fee: FeeAmount.MEDIUM } - const { swapExact0For1, swapToHigherPrice, mint, swapToLowerPrice } = await createPoolFunctions({ - swapTarget: fix.swapTargetCallee, - token0: fix.token0, - token1: fix.token1, - pool, - }) + const swapExact0For1: SwapFunction = (amount, to, sqrtPriceLimitX96) => { + return swapTest.swap(poolKey, { + zeroForOne: true, + amountSpecified: amount, + sqrtPriceLimitX96: sqrtPriceLimitX96 ?? MIN_SQRT_RATIO.add(1), + }) + } + const swapToHigherPrice: SwapToPriceFunction = (sqrtPriceX96, to) => { + return swapTest.swap(poolKey, { + zeroForOne: false, + amountSpecified: MaxUint128, + sqrtPriceLimitX96: sqrtPriceX96, + }) + } + const swapToLowerPrice: SwapToPriceFunction = (sqrtPriceX96, to) => { + return swapTest.swap(poolKey, { + zeroForOne: true, + amountSpecified: MaxUint128, + sqrtPriceLimitX96: sqrtPriceX96, + }) + } + const mint: MintFunction = (recipient, tickLower, tickUpper, liquidity) => { + return mintTest.mint(poolKey, { + recipient, + tickLower, + tickUpper, + amount: liquidity, + }) + } + const getSlot0 = async () => { + const { slot0 } = await manager.pools(getPoolId(poolKey)) + return slot0 + } + + await manager.initialize(poolKey, encodeSqrtPriceX96(1, 1)) + await manager.setFeeProtocol(poolKey, feeProtocol + feeProtocol * 2 ** 4) + await manager.increaseObservationCardinalityNext(poolKey, 4) + + await manager.advanceTime(1) - await pool.initialize(encodeSqrtPriceX96(1, 1)) - await pool.setFeeProtocol(feeProtocol, feeProtocol) - await pool.increaseObservationCardinalityNext(4) - await pool.advanceTime(1) await mint(wallet.address, minTick, maxTick, expandTo18Decimals(2)) await swapExact0For1(expandTo18Decimals(1), wallet.address) - await pool.advanceTime(1) + await manager.advanceTime(1) await swapToHigherPrice(startingPrice, wallet.address) - await pool.advanceTime(1) - expect((await pool.slot0()).tick).to.eq(startingTick) - expect((await pool.slot0()).sqrtPriceX96).to.eq(startingPrice) + await manager.advanceTime(1) + + const { tick, sqrtPriceX96 } = await getSlot0() + + expect(tick).to.eq(startingTick) + expect(sqrtPriceX96).to.eq(startingPrice) - return { pool, swapExact0For1, mint, swapToHigherPrice, swapToLowerPrice } + return { manager, getSlot0, poolKey, swapExact0For1, mint, swapToHigherPrice, swapToLowerPrice } } let swapExact0For1: SwapFunction let swapToHigherPrice: SwapToPriceFunction let swapToLowerPrice: SwapToPriceFunction - let pool: MockTimePool + let manager: MockTimePoolManager let mint: MintFunction + let getSlot0: AsyncReturnType['getSlot0'] + let poolKey: AsyncReturnType['poolKey'] beforeEach('load the fixture', async () => { - ;({ swapExact0For1, pool, mint, swapToHigherPrice, swapToLowerPrice } = await loadFixture(gasTestFixture)) + ;({ swapExact0For1, manager, mint, swapToHigherPrice, swapToLowerPrice, getSlot0, poolKey } = await loadFixture( + gasTestFixture + )) }) describe('#swapExact0For1', () => { it('first swap in block with no tick movement', async () => { await snapshotGasCost(swapExact0For1(2000, wallet.address)) - expect((await pool.slot0()).sqrtPriceX96).to.not.eq(startingPrice) - expect((await pool.slot0()).tick).to.eq(startingTick) + expect((await getSlot0()).sqrtPriceX96).to.not.eq(startingPrice) + expect((await getSlot0()).tick).to.eq(startingTick) }) it('first swap in block moves tick, no initialized crossings', async () => { await snapshotGasCost(swapExact0For1(expandTo18Decimals(1).div(10000), wallet.address)) - expect((await pool.slot0()).tick).to.eq(startingTick - 1) + expect((await getSlot0()).tick).to.eq(startingTick - 1) }) it('second swap in block with no tick movement', async () => { await swapExact0For1(expandTo18Decimals(1).div(10000), wallet.address) - expect((await pool.slot0()).tick).to.eq(startingTick - 1) + expect((await getSlot0()).tick).to.eq(startingTick - 1) await snapshotGasCost(swapExact0For1(2000, wallet.address)) - expect((await pool.slot0()).tick).to.eq(startingTick - 1) + expect((await getSlot0()).tick).to.eq(startingTick - 1) }) it('second swap in block moves tick, no initialized crossings', async () => { await swapExact0For1(1000, wallet.address) - expect((await pool.slot0()).tick).to.eq(startingTick) + expect((await getSlot0()).tick).to.eq(startingTick) await snapshotGasCost(swapExact0For1(expandTo18Decimals(1).div(10000), wallet.address)) - expect((await pool.slot0()).tick).to.eq(startingTick - 1) + expect((await getSlot0()).tick).to.eq(startingTick - 1) }) it('first swap in block, large swap, no initialized crossings', async () => { await snapshotGasCost(swapExact0For1(expandTo18Decimals(10), wallet.address)) - expect((await pool.slot0()).tick).to.eq(-35787) + expect((await getSlot0()).tick).to.eq(-35787) }) it('first swap in block, large swap crossing several initialized ticks', async () => { @@ -120,15 +180,15 @@ describe('Pool gas tests', () => { startingTick - 2 * tickSpacing, expandTo18Decimals(1) ) - expect((await pool.slot0()).tick).to.eq(startingTick) + expect((await getSlot0()).tick).to.eq(startingTick) await snapshotGasCost(swapExact0For1(expandTo18Decimals(1), wallet.address)) - expect((await pool.slot0()).tick).to.be.lt(startingTick - 4 * tickSpacing) // we crossed the last tick + expect((await getSlot0()).tick).to.be.lt(startingTick - 4 * tickSpacing) // we crossed the last tick }) it('first swap in block, large swap crossing a single initialized tick', async () => { await mint(wallet.address, minTick, startingTick - 2 * tickSpacing, expandTo18Decimals(1)) await snapshotGasCost(swapExact0For1(expandTo18Decimals(1), wallet.address)) - expect((await pool.slot0()).tick).to.be.lt(startingTick - 2 * tickSpacing) // we crossed the last tick + expect((await getSlot0()).tick).to.be.lt(startingTick - 2 * tickSpacing) // we crossed the last tick }) it('second swap in block, large swap crossing several initialized ticks', async () => { @@ -141,15 +201,15 @@ describe('Pool gas tests', () => { ) await swapExact0For1(expandTo18Decimals(1).div(10000), wallet.address) await snapshotGasCost(swapExact0For1(expandTo18Decimals(1), wallet.address)) - expect((await pool.slot0()).tick).to.be.lt(startingTick - 4 * tickSpacing) + expect((await getSlot0()).tick).to.be.lt(startingTick - 4 * tickSpacing) }) it('second swap in block, large swap crossing a single initialized tick', async () => { await mint(wallet.address, minTick, startingTick - 2 * tickSpacing, expandTo18Decimals(1)) await swapExact0For1(expandTo18Decimals(1).div(10000), wallet.address) - expect((await pool.slot0()).tick).to.be.gt(startingTick - 2 * tickSpacing) // we didn't cross the initialized tick + expect((await getSlot0()).tick).to.be.gt(startingTick - 2 * tickSpacing) // we didn't cross the initialized tick await snapshotGasCost(swapExact0For1(expandTo18Decimals(1), wallet.address)) - expect((await pool.slot0()).tick).to.be.lt(startingTick - 2 * tickSpacing) // we crossed the last tick + expect((await getSlot0()).tick).to.be.lt(startingTick - 2 * tickSpacing) // we crossed the last tick }) it('large swap crossing several initialized ticks after some time passes', async () => { @@ -161,9 +221,9 @@ describe('Pool gas tests', () => { expandTo18Decimals(1) ) await swapExact0For1(2, wallet.address) - await pool.advanceTime(1) + await manager.advanceTime(1) await snapshotGasCost(swapExact0For1(expandTo18Decimals(1), wallet.address)) - expect((await pool.slot0()).tick).to.be.lt(startingTick - 4 * tickSpacing) + expect((await getSlot0()).tick).to.be.lt(startingTick - 4 * tickSpacing) }) it('large swap crossing several initialized ticks second time after some time passes', async () => { @@ -176,9 +236,9 @@ describe('Pool gas tests', () => { ) await swapExact0For1(expandTo18Decimals(1), wallet.address) await swapToHigherPrice(startingPrice, wallet.address) - await pool.advanceTime(1) + await manager.advanceTime(1) await snapshotGasCost(swapExact0For1(expandTo18Decimals(1), wallet.address)) - expect((await pool.slot0()).tick).to.be.lt(tickSpacing * -4) + expect((await getSlot0()).tick).to.be.lt(tickSpacing * -4) }) }) @@ -214,100 +274,88 @@ describe('Pool gas tests', () => { }) it('add to position after some time passes', async () => { await mint(wallet.address, tickLower, tickUpper, expandTo18Decimals(1)) - await pool.advanceTime(1) + await manager.advanceTime(1) await snapshotGasCost(mint(wallet.address, tickLower, tickUpper, expandTo18Decimals(1))) }) }) } }) - describe('#burn', () => { - for (const { description, tickLower, tickUpper } of [ - { - description: 'around current price', - tickLower: startingTick - tickSpacing, - tickUpper: startingTick + tickSpacing, - }, - { - description: 'below current price', - tickLower: startingTick - 2 * tickSpacing, - tickUpper: startingTick - tickSpacing, - }, - { - description: 'above current price', - tickLower: startingTick + tickSpacing, - tickUpper: startingTick + 2 * tickSpacing, - }, - ]) { - describe(description, () => { - const liquidityAmount = expandTo18Decimals(1) - beforeEach('mint a position', async () => { - await mint(wallet.address, tickLower, tickUpper, liquidityAmount) - }) - - it('burn when only position using ticks', async () => { - await snapshotGasCost(pool.burn(tickLower, tickUpper, expandTo18Decimals(1))) - }) - it('partial position burn', async () => { - await snapshotGasCost(pool.burn(tickLower, tickUpper, expandTo18Decimals(1).div(2))) - }) - it('entire position burn but other positions are using the ticks', async () => { - await mint(other.address, tickLower, tickUpper, expandTo18Decimals(1)) - await snapshotGasCost(pool.burn(tickLower, tickUpper, expandTo18Decimals(1))) - }) - it('burn entire position after some time passes', async () => { - await pool.advanceTime(1) - await snapshotGasCost(pool.burn(tickLower, tickUpper, expandTo18Decimals(1))) - }) - }) - } - }) - - describe('#poke', () => { - const tickLower = startingTick - tickSpacing - const tickUpper = startingTick + tickSpacing - - it('best case', async () => { - await mint(wallet.address, tickLower, tickUpper, expandTo18Decimals(1)) - await swapExact0For1(expandTo18Decimals(1).div(100), wallet.address) - await pool.burn(tickLower, tickUpper, 0) - await swapExact0For1(expandTo18Decimals(1).div(100), wallet.address) - await snapshotGasCost(pool.burn(tickLower, tickUpper, 0)) - }) - }) - - describe('#collect', () => { - const tickLower = startingTick - tickSpacing - const tickUpper = startingTick + tickSpacing - - it('close to worst case', async () => { - await mint(wallet.address, tickLower, tickUpper, expandTo18Decimals(1)) - await swapExact0For1(expandTo18Decimals(1).div(100), wallet.address) - await pool.burn(tickLower, tickUpper, 0) // poke to accumulate fees - await snapshotGasCost(pool.collect(wallet.address, tickLower, tickUpper, MaxUint128, MaxUint128)) - }) - }) + // describe('#burn', () => { + // for (const { description, tickLower, tickUpper } of [ + // { + // description: 'around current price', + // tickLower: startingTick - tickSpacing, + // tickUpper: startingTick + tickSpacing, + // }, + // { + // description: 'below current price', + // tickLower: startingTick - 2 * tickSpacing, + // tickUpper: startingTick - tickSpacing, + // }, + // { + // description: 'above current price', + // tickLower: startingTick + tickSpacing, + // tickUpper: startingTick + 2 * tickSpacing, + // }, + // ]) { + // describe(description, () => { + // const liquidityAmount = expandTo18Decimals(1) + // beforeEach('mint a position', async () => { + // await mint(wallet.address, tickLower, tickUpper, liquidityAmount) + // }) + // + // it('burn when only position using ticks', async () => { + // await snapshotGasCost(pool.burn(tickLower, tickUpper, expandTo18Decimals(1))) + // }) + // it('partial position burn', async () => { + // await snapshotGasCost(pool.burn(tickLower, tickUpper, expandTo18Decimals(1).div(2))) + // }) + // it('entire position burn but other positions are using the ticks', async () => { + // await mint(other.address, tickLower, tickUpper, expandTo18Decimals(1)) + // await snapshotGasCost(pool.burn(tickLower, tickUpper, expandTo18Decimals(1))) + // }) + // it('burn entire position after some time passes', async () => { + // await manager.advanceTime(1) + // await snapshotGasCost(pool.burn(tickLower, tickUpper, expandTo18Decimals(1))) + // }) + // }) + // } + // }) + + // describe('#poke', () => { + // const tickLower = startingTick - tickSpacing + // const tickUpper = startingTick + tickSpacing + // + // it('best case', async () => { + // await mint(wallet.address, tickLower, tickUpper, expandTo18Decimals(1)) + // await swapExact0For1(expandTo18Decimals(1).div(100), wallet.address) + // await pool.burn(tickLower, tickUpper, 0) + // await swapExact0For1(expandTo18Decimals(1).div(100), wallet.address) + // await snapshotGasCost(pool.burn(tickLower, tickUpper, 0)) + // }) + // }) describe('#increaseObservationCardinalityNext', () => { it('grow by 1 slot', async () => { - await snapshotGasCost(pool.increaseObservationCardinalityNext(5)) + await snapshotGasCost(manager.increaseObservationCardinalityNext(poolKey, 5)) }) it('no op', async () => { - await snapshotGasCost(pool.increaseObservationCardinalityNext(3)) + await snapshotGasCost(manager.increaseObservationCardinalityNext(poolKey, 3)) }) }) describe('#snapshotCumulativesInside', () => { it('tick inside', async () => { - await snapshotGasCost(pool.estimateGas.snapshotCumulativesInside(minTick, maxTick)) + await snapshotGasCost(manager.estimateGas.snapshotCumulativesInside(poolKey, minTick, maxTick)) }) it('tick above', async () => { await swapToHigherPrice(MAX_SQRT_RATIO.sub(1), wallet.address) - await snapshotGasCost(pool.estimateGas.snapshotCumulativesInside(minTick, maxTick)) + await snapshotGasCost(manager.estimateGas.snapshotCumulativesInside(poolKey, minTick, maxTick)) }) it('tick below', async () => { await swapToLowerPrice(MIN_SQRT_RATIO.add(1), wallet.address) - await snapshotGasCost(pool.estimateGas.snapshotCumulativesInside(minTick, maxTick)) + await snapshotGasCost(manager.estimateGas.snapshotCumulativesInside(poolKey, minTick, maxTick)) }) }) }) diff --git a/test/PoolManager.spec.ts b/test/PoolManager.spec.ts new file mode 100644 index 000000000..d040a8fcb --- /dev/null +++ b/test/PoolManager.spec.ts @@ -0,0 +1,312 @@ +import { Wallet } from 'ethers' +import { ethers, waffle } from 'hardhat' +import { PoolManager, TestERC20, PoolManagerTest, PoolSwapTest, PoolMintTest, PoolBurnTest } from '../typechain' +import { expect } from './shared/expect' +import { tokensFixture } from './shared/fixtures' +import snapshotGasCost from './shared/snapshotGasCost' +import { encodeSqrtPriceX96, expandTo18Decimals, FeeAmount, getPoolId } from './shared/utilities' + +const createFixtureLoader = waffle.createFixtureLoader + +const { constants } = ethers + +describe.only('PoolManager', () => { + let wallet: Wallet, other: Wallet + + let manager: PoolManager + let lockTest: PoolManagerTest + let swapTest: PoolSwapTest + let mintTest: PoolMintTest + let burnTest: PoolBurnTest + let tokens: { token0: TestERC20; token1: TestERC20; token2: TestERC20 } + const fixture = async () => { + const singletonPoolFactory = await ethers.getContractFactory('PoolManager') + const managerTestFactory = await ethers.getContractFactory('PoolManagerTest') + const swapTestFactory = await ethers.getContractFactory('PoolSwapTest') + const mintTestFactory = await ethers.getContractFactory('PoolMintTest') + const burnTestFactory = await ethers.getContractFactory('PoolBurnTest') + const tokens = await tokensFixture() + const manager = (await singletonPoolFactory.deploy()) as PoolManager + + const result = { + manager, + lockTest: (await managerTestFactory.deploy()) as PoolManagerTest, + swapTest: (await swapTestFactory.deploy(manager.address)) as PoolSwapTest, + mintTest: (await mintTestFactory.deploy(manager.address)) as PoolMintTest, + burnTest: (await burnTestFactory.deploy(manager.address)) as PoolBurnTest, + tokens, + } + + for (const token of [tokens.token0, tokens.token1, tokens.token2]) { + for (const spender of [result.swapTest, result.mintTest, result.burnTest]) { + await token.connect(wallet).approve(spender.address, constants.MaxUint256) + } + } + + return result + } + + let loadFixture: ReturnType + before('create fixture loader', async () => { + ;[wallet, other] = await (ethers as any).getSigners() + + loadFixture = createFixtureLoader([wallet, other]) + }) + + beforeEach('deploy fixture', async () => { + ;({ manager, tokens, lockTest, mintTest, burnTest, swapTest } = await loadFixture(fixture)) + }) + + it('bytecode size', async () => { + expect(((await waffle.provider.getCode(manager.address)).length - 2) / 2).to.matchSnapshot() + }) + + describe('#lock', () => { + it('no-op lock is ok', async () => { + await lockTest.lock(manager.address) + }) + + it('gas overhead of no-op lock', async () => { + await snapshotGasCost(lockTest.lock(manager.address)) + }) + }) + + describe('#initialize', async () => { + it('initializes a pool', async () => { + await manager.initialize( + { + token0: tokens.token0.address, + token1: tokens.token1.address, + fee: FeeAmount.MEDIUM, + }, + encodeSqrtPriceX96(10, 1) + ) + + const { + slot0: { sqrtPriceX96 }, + } = await manager.pools( + getPoolId({ + token0: tokens.token0.address, + token1: tokens.token1.address, + fee: FeeAmount.MEDIUM, + }) + ) + expect(sqrtPriceX96).to.eq(encodeSqrtPriceX96(10, 1)) + }) + + it('gas cost', async () => { + await snapshotGasCost( + manager.initialize( + { + token0: tokens.token0.address, + token1: tokens.token1.address, + fee: FeeAmount.MEDIUM, + }, + encodeSqrtPriceX96(10, 1) + ) + ) + }) + }) + + describe('#mint', async () => { + it('reverts if pool not initialized', async () => { + await expect( + mintTest.mint( + { + token0: tokens.token0.address, + token1: tokens.token1.address, + fee: FeeAmount.MEDIUM, + }, + { + tickLower: 0, + tickUpper: 60, + amount: 100, + recipient: wallet.address, + } + ) + ).to.be.revertedWith('I') + }) + + it('succeeds if pool is initialized', async () => { + await manager.initialize( + { + token0: tokens.token0.address, + token1: tokens.token1.address, + fee: FeeAmount.MEDIUM, + }, + encodeSqrtPriceX96(1, 1) + ) + + await mintTest.mint( + { + token0: tokens.token0.address, + token1: tokens.token1.address, + fee: FeeAmount.MEDIUM, + }, + { + tickLower: 0, + tickUpper: 60, + amount: 100, + recipient: wallet.address, + } + ) + }) + + it('gas cost', async () => { + await manager.initialize( + { + token0: tokens.token0.address, + token1: tokens.token1.address, + fee: FeeAmount.MEDIUM, + }, + encodeSqrtPriceX96(1, 1) + ) + + await snapshotGasCost( + mintTest.mint( + { + token0: tokens.token0.address, + token1: tokens.token1.address, + fee: FeeAmount.MEDIUM, + }, + { + tickLower: 0, + tickUpper: 60, + amount: 100, + recipient: wallet.address, + } + ) + ) + }) + }) + + describe('#swap', () => { + it('fails if pool is not initialized', async () => { + await expect( + swapTest.swap( + { + token0: tokens.token0.address, + token1: tokens.token1.address, + fee: FeeAmount.MEDIUM, + }, + { + amountSpecified: 100, + sqrtPriceLimitX96: encodeSqrtPriceX96(1, 2), + zeroForOne: true, + } + ) + ).to.be.revertedWith('I') + }) + it('succeeds if pool is initialized', async () => { + await manager.initialize( + { + token0: tokens.token0.address, + token1: tokens.token1.address, + fee: FeeAmount.MEDIUM, + }, + encodeSqrtPriceX96(1, 1) + ) + await swapTest.swap( + { + token0: tokens.token0.address, + token1: tokens.token1.address, + fee: FeeAmount.MEDIUM, + }, + { + amountSpecified: 100, + sqrtPriceLimitX96: encodeSqrtPriceX96(1, 2), + zeroForOne: true, + } + ) + }) + it('gas', async () => { + await manager.initialize( + { + token0: tokens.token0.address, + token1: tokens.token1.address, + fee: FeeAmount.MEDIUM, + }, + encodeSqrtPriceX96(1, 1) + ) + + await swapTest.swap( + { + token0: tokens.token0.address, + token1: tokens.token1.address, + fee: FeeAmount.MEDIUM, + }, + { + amountSpecified: 100, + sqrtPriceLimitX96: encodeSqrtPriceX96(1, 2), + zeroForOne: true, + } + ) + + await snapshotGasCost( + swapTest.swap( + { + token0: tokens.token0.address, + token1: tokens.token1.address, + fee: FeeAmount.MEDIUM, + }, + { + amountSpecified: 100, + sqrtPriceLimitX96: encodeSqrtPriceX96(1, 4), + zeroForOne: true, + } + ) + ) + }) + it('gas for swap against liquidity', async () => { + await manager.initialize( + { + token0: tokens.token0.address, + token1: tokens.token1.address, + fee: FeeAmount.MEDIUM, + }, + encodeSqrtPriceX96(1, 1) + ) + await mintTest.mint( + { + token0: tokens.token0.address, + token1: tokens.token1.address, + fee: FeeAmount.MEDIUM, + }, + { + tickLower: -120, + tickUpper: 120, + amount: expandTo18Decimals(1), + recipient: wallet.address, + } + ) + + await swapTest.swap( + { + token0: tokens.token0.address, + token1: tokens.token1.address, + fee: FeeAmount.MEDIUM, + }, + { + amountSpecified: 100, + sqrtPriceLimitX96: encodeSqrtPriceX96(1, 2), + zeroForOne: true, + } + ) + + await snapshotGasCost( + swapTest.swap( + { + token0: tokens.token0.address, + token1: tokens.token1.address, + fee: FeeAmount.MEDIUM, + }, + { + amountSpecified: 100, + sqrtPriceLimitX96: encodeSqrtPriceX96(1, 4), + zeroForOne: true, + } + ) + ) + }) + }) +}) diff --git a/test/__snapshots__/Pool.arbitrage.spec.ts.snap b/test/__snapshots__/Pool.arbitrage.spec.ts.snap deleted file mode 100644 index d574c51e9..000000000 --- a/test/__snapshots__/Pool.arbitrage.spec.ts.snap +++ /dev/null @@ -1,977 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Pool arbitrage tests protocol fee = 0; passive liquidity of 0.010000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 backrun to true price after swap only 1`] = ` -Object { - "arbBalanceDelta0": "9.9699", - "arbBalanceDelta1": "-0.0099043", - "backrun": Object { - "delta0": "-9.9699", - "delta1": "0.0099043", - "executionPrice": "0.00099342", - }, - "finalPrice": "0.97706", - "profit": Object { - "final": "9.7606", - }, -} -`; - -exports[`Pool arbitrage tests protocol fee = 0; passive liquidity of 0.010000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 not sandwiched 1`] = ` -Object { - "amount0Delta": "10.000", - "amount1Delta": "-0.0099900", - "executionPrice": "0.00099900", - "priceAfter": "0.0000010040", -} -`; - -exports[`Pool arbitrage tests protocol fee = 0; passive liquidity of 0.010000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity 1`] = ` -Object { - "arbBalanceDelta0": "9.9990", - "arbBalanceDelta1": "-0.0099044", - "backrun": Object { - "delta0": "-0.30578", - "delta1": "0.0095969", - "executionPrice": "0.031385", - }, - "burn": Object { - "amount0": "9.9700", - "amount1": "1.0910e+12", - }, - "collect": Object { - "amount0": "0.030000", - "amount1": "0.0000", - }, - "finalPrice": "0.97706", - "frontrun": Object { - "delta0": "0.30682", - "delta1": "-0.0096834", - "executionPrice": "0.031561", - }, - "mint": Object { - "amount0": "0.0000", - "amount1": "1.0910e+12", - }, - "profit": Object { - "afterFrontrun": "-0.29100", - "afterSandwich": "9.4990", - "final": "9.7891", - }, - "sandwichedPrice": "0.00099910", -} -`; - -exports[`Pool arbitrage tests protocol fee = 0; passive liquidity of 0.010000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 backrun to true price after swap only 1`] = ` -Object { - "arbBalanceDelta0": "9.9700", - "arbBalanceDelta1": "-0.010055", - "backrun": Object { - "delta0": "-9.9700", - "delta1": "0.010055", - "executionPrice": "0.0010085", - }, - "finalPrice": "1.0070", - "profit": Object { - "final": "10.060", - }, -} -`; - -exports[`Pool arbitrage tests protocol fee = 0; passive liquidity of 0.010000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 not sandwiched 1`] = ` -Object { - "amount0Delta": "10.000", - "amount1Delta": "-0.0099900", - "executionPrice": "0.00099900", - "priceAfter": "0.0000010040", -} -`; - -exports[`Pool arbitrage tests protocol fee = 0; passive liquidity of 0.010000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity 1`] = ` -Object { - "arbBalanceDelta0": "9.9991", - "arbBalanceDelta1": "-0.010055", - "backrun": Object { - "delta0": "-0.30593", - "delta1": "0.0097475", - "executionPrice": "0.031862", - }, - "burn": Object { - "amount0": "9.9700", - "amount1": "1.0910e+12", - }, - "collect": Object { - "amount0": "0.030000", - "amount1": "0.0000", - }, - "finalPrice": "1.0070", - "frontrun": Object { - "delta0": "0.30682", - "delta1": "-0.0096834", - "executionPrice": "0.031561", - }, - "mint": Object { - "amount0": "0.0000", - "amount1": "1.0910e+12", - }, - "profit": Object { - "afterFrontrun": "-0.30020", - "afterSandwich": "9.7898", - "final": "10.089", - }, - "sandwichedPrice": "0.00099910", -} -`; - -exports[`Pool arbitrage tests protocol fee = 0; passive liquidity of 1.0000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 backrun to true price after swap only 1`] = ` -Object { - "arbBalanceDelta0": "9.9583", - "arbBalanceDelta1": "-0.90001", - "backrun": Object { - "delta0": "-9.9583", - "delta1": "0.90001", - "executionPrice": "0.090377", - }, - "finalPrice": "0.97706", - "profit": Object { - "final": "8.8592", - }, -} -`; - -exports[`Pool arbitrage tests protocol fee = 0; passive liquidity of 1.0000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 not sandwiched 1`] = ` -Object { - "amount0Delta": "10.000", - "amount1Delta": "-0.90884", - "executionPrice": "0.090884", - "priceAfter": "0.0083097", -} -`; - -exports[`Pool arbitrage tests protocol fee = 0; passive liquidity of 1.0000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity 1`] = ` -Object { - "arbBalanceDelta0": "9.9814", - "arbBalanceDelta1": "-0.90054", - "backrun": Object { - "delta0": "-2.2983", - "delta1": "0.68841", - "executionPrice": "0.29953", - }, - "burn": Object { - "amount0": "9.9700", - "amount1": "1.0412e+13", - }, - "collect": Object { - "amount0": "0.030000", - "amount1": "0.0000", - }, - "finalPrice": "0.97706", - "frontrun": Object { - "delta0": "2.3169", - "delta1": "-0.69788", - "executionPrice": "0.30121", - }, - "mint": Object { - "amount0": "0.0000", - "amount1": "1.0412e+13", - }, - "profit": Object { - "afterFrontrun": "-1.5727", - "afterSandwich": "7.3173", - "final": "8.8812", - }, - "sandwichedPrice": "0.091001", -} -`; - -exports[`Pool arbitrage tests protocol fee = 0; passive liquidity of 1.0000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 backrun to true price after swap only 1`] = ` -Object { - "arbBalanceDelta0": "9.9735", - "arbBalanceDelta1": "-0.91507", - "backrun": Object { - "delta0": "-9.9735", - "delta1": "0.91507", - "executionPrice": "0.091750", - }, - "finalPrice": "1.0070", - "profit": Object { - "final": "9.1581", - }, -} -`; - -exports[`Pool arbitrage tests protocol fee = 0; passive liquidity of 1.0000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 not sandwiched 1`] = ` -Object { - "amount0Delta": "10.000", - "amount1Delta": "-0.90884", - "executionPrice": "0.090884", - "priceAfter": "0.0083097", -} -`; - -exports[`Pool arbitrage tests protocol fee = 0; passive liquidity of 1.0000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity 1`] = ` -Object { - "arbBalanceDelta0": "9.9965", - "arbBalanceDelta1": "-0.91560", - "backrun": Object { - "delta0": "-2.3134", - "delta1": "0.70347", - "executionPrice": "0.30408", - }, - "burn": Object { - "amount0": "9.9700", - "amount1": "1.0412e+13", - }, - "collect": Object { - "amount0": "0.030000", - "amount1": "0.0000", - }, - "finalPrice": "1.0070", - "frontrun": Object { - "delta0": "2.3169", - "delta1": "-0.69788", - "executionPrice": "0.30121", - }, - "mint": Object { - "amount0": "0.0000", - "amount1": "1.0412e+13", - }, - "profit": Object { - "afterFrontrun": "-1.6422", - "afterSandwich": "7.5478", - "final": "9.1809", - }, - "sandwichedPrice": "0.091001", -} -`; - -exports[`Pool arbitrage tests protocol fee = 0; passive liquidity of 10.000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 backrun to true price after swap only 1`] = ` -Object { - "arbBalanceDelta0": "9.8533", - "arbBalanceDelta1": "-4.8918", - "backrun": Object { - "delta0": "-9.8533", - "delta1": "4.8918", - "executionPrice": "0.49646", - }, - "finalPrice": "0.97706", - "profit": Object { - "final": "4.7644", - }, -} -`; - -exports[`Pool arbitrage tests protocol fee = 0; passive liquidity of 10.000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 not sandwiched 1`] = ` -Object { - "amount0Delta": "10.000", - "amount1Delta": "-4.9925", - "executionPrice": "0.49925", - "priceAfter": "0.25075", -} -`; - -exports[`Pool arbitrage tests protocol fee = 0; passive liquidity of 10.000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity 1`] = ` -Object { - "arbBalanceDelta0": "9.8709", - "arbBalanceDelta1": "-4.8940", - "backrun": Object { - "delta0": "-4.0029", - "delta1": "2.8107", - "executionPrice": "0.70217", - }, - "burn": Object { - "amount0": "9.9700", - "amount1": "2.4408e+13", - }, - "collect": Object { - "amount0": "0.030000", - "amount1": "0.0000", - }, - "finalPrice": "0.97706", - "frontrun": Object { - "delta0": "4.1321", - "delta1": "-2.9177", - "executionPrice": "0.70611", - }, - "mint": Object { - "amount0": "0.0000", - "amount1": "2.4408e+13", - }, - "profit": Object { - "afterFrontrun": "-1.1317", - "afterSandwich": "3.6674", - "final": "4.7795", - }, - "sandwichedPrice": "0.50009", -} -`; - -exports[`Pool arbitrage tests protocol fee = 0; passive liquidity of 10.000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 backrun to true price after swap only 1`] = ` -Object { - "arbBalanceDelta0": "10.005", - "arbBalanceDelta1": "-5.0424", - "backrun": Object { - "delta0": "-10.005", - "delta1": "5.0424", - "executionPrice": "0.50401", - }, - "finalPrice": "1.0070", - "profit": Object { - "final": "5.0623", - }, -} -`; - -exports[`Pool arbitrage tests protocol fee = 0; passive liquidity of 10.000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 not sandwiched 1`] = ` -Object { - "amount0Delta": "10.000", - "amount1Delta": "-4.9925", - "executionPrice": "0.49925", - "priceAfter": "0.25075", -} -`; - -exports[`Pool arbitrage tests protocol fee = 0; passive liquidity of 10.000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity 1`] = ` -Object { - "arbBalanceDelta0": "10.022", - "arbBalanceDelta1": "-5.0446", - "backrun": Object { - "delta0": "-4.1543", - "delta1": "2.9613", - "executionPrice": "0.71283", - }, - "burn": Object { - "amount0": "9.9700", - "amount1": "2.4408e+13", - }, - "collect": Object { - "amount0": "0.030000", - "amount1": "0.0000", - }, - "finalPrice": "1.0070", - "frontrun": Object { - "delta0": "4.1321", - "delta1": "-2.9177", - "executionPrice": "0.70611", - }, - "mint": Object { - "amount0": "0.0000", - "amount1": "2.4408e+13", - }, - "profit": Object { - "afterFrontrun": "-1.2557", - "afterSandwich": "3.8434", - "final": "5.0779", - }, - "sandwichedPrice": "0.50009", -} -`; - -exports[`Pool arbitrage tests protocol fee = 0; passive liquidity of 100.00 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 backrun to true price after swap only 1`] = ` -Object { - "arbBalanceDelta0": "8.8029", - "arbBalanceDelta1": "-7.9363", - "backrun": Object { - "delta0": "-8.8029", - "delta1": "7.9363", - "executionPrice": "0.90155", - }, - "finalPrice": "0.97706", - "profit": Object { - "final": "0.69056", - }, -} -`; - -exports[`Pool arbitrage tests protocol fee = 0; passive liquidity of 100.00 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 not sandwiched 1`] = ` -Object { - "amount0Delta": "10.000", - "amount1Delta": "-9.0661", - "executionPrice": "0.90661", - "priceAfter": "0.82690", -} -`; - -exports[`Pool arbitrage tests protocol fee = 0; passive liquidity of 100.00 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity 1`] = ` -Object { - "arbBalanceDelta0": "8.8190", - "arbBalanceDelta1": "-7.9680", - "backrun": Object { - "delta0": "-3.4354", - "delta1": "3.2562", - "executionPrice": "0.94781", - }, - "burn": Object { - "amount0": "9.9700", - "amount1": "3.2947e+13", - }, - "collect": Object { - "amount0": "0.030000", - "amount1": "0.0000", - }, - "finalPrice": "0.97706", - "frontrun": Object { - "delta0": "4.6164", - "delta1": "-4.4000", - "executionPrice": "0.95313", - }, - "mint": Object { - "amount0": "0.0000", - "amount1": "3.2947e+13", - }, - "profit": Object { - "afterFrontrun": "-0.12404", - "afterSandwich": "0.56403", - "final": "0.67460", - }, - "sandwichedPrice": "0.91119", -} -`; - -exports[`Pool arbitrage tests protocol fee = 0; passive liquidity of 100.00 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 backrun to true price after swap only 1`] = ` -Object { - "arbBalanceDelta0": "10.317", - "arbBalanceDelta1": "-9.4423", - "backrun": Object { - "delta0": "-10.317", - "delta1": "9.4423", - "executionPrice": "0.91525", - }, - "finalPrice": "1.0070", - "profit": Object { - "final": "0.97752", - }, -} -`; - -exports[`Pool arbitrage tests protocol fee = 0; passive liquidity of 100.00 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 not sandwiched 1`] = ` -Object { - "amount0Delta": "10.000", - "amount1Delta": "-9.0661", - "executionPrice": "0.90661", - "priceAfter": "0.82690", -} -`; - -exports[`Pool arbitrage tests protocol fee = 0; passive liquidity of 100.00 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity 1`] = ` -Object { - "arbBalanceDelta0": "10.333", - "arbBalanceDelta1": "-9.4741", - "backrun": Object { - "delta0": "-4.9492", - "delta1": "4.7622", - "executionPrice": "0.96221", - }, - "burn": Object { - "amount0": "9.9700", - "amount1": "3.2947e+13", - }, - "collect": Object { - "amount0": "0.030000", - "amount1": "0.0000", - }, - "finalPrice": "1.0070", - "frontrun": Object { - "delta0": "4.6164", - "delta1": "-4.4000", - "executionPrice": "0.95313", - }, - "mint": Object { - "amount0": "0.0000", - "amount1": "3.2947e+13", - }, - "profit": Object { - "afterFrontrun": "-0.26253", - "afterSandwich": "0.72554", - "final": "0.96205", - }, - "sandwichedPrice": "0.91119", -} -`; - -exports[`Pool arbitrage tests protocol fee = 6; passive liquidity of 0.010000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 backrun to true price after swap only 1`] = ` -Object { - "arbBalanceDelta0": "9.9699", - "arbBalanceDelta1": "-0.0099043", - "backrun": Object { - "delta0": "-9.9699", - "delta1": "0.0099043", - "executionPrice": "0.00099342", - }, - "finalPrice": "0.97706", - "profit": Object { - "final": "9.7606", - }, -} -`; - -exports[`Pool arbitrage tests protocol fee = 6; passive liquidity of 0.010000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 not sandwiched 1`] = ` -Object { - "amount0Delta": "10.000", - "amount1Delta": "-0.0099900", - "executionPrice": "0.00099900", - "priceAfter": "0.0000010040", -} -`; - -exports[`Pool arbitrage tests protocol fee = 6; passive liquidity of 0.010000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity 1`] = ` -Object { - "arbBalanceDelta0": "9.9940", - "arbBalanceDelta1": "-0.0099044", - "backrun": Object { - "delta0": "-0.30578", - "delta1": "0.0095969", - "executionPrice": "0.031385", - }, - "burn": Object { - "amount0": "9.9700", - "amount1": "1.0910e+12", - }, - "collect": Object { - "amount0": "0.025000", - "amount1": "0.0000", - }, - "finalPrice": "0.97706", - "frontrun": Object { - "delta0": "0.30682", - "delta1": "-0.0096834", - "executionPrice": "0.031561", - }, - "mint": Object { - "amount0": "0.0000", - "amount1": "1.0910e+12", - }, - "profit": Object { - "afterFrontrun": "-0.29100", - "afterSandwich": "9.4941", - "final": "9.7842", - }, - "sandwichedPrice": "0.00099910", -} -`; - -exports[`Pool arbitrage tests protocol fee = 6; passive liquidity of 0.010000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 backrun to true price after swap only 1`] = ` -Object { - "arbBalanceDelta0": "9.9700", - "arbBalanceDelta1": "-0.010055", - "backrun": Object { - "delta0": "-9.9700", - "delta1": "0.010055", - "executionPrice": "0.0010085", - }, - "finalPrice": "1.0070", - "profit": Object { - "final": "10.060", - }, -} -`; - -exports[`Pool arbitrage tests protocol fee = 6; passive liquidity of 0.010000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 not sandwiched 1`] = ` -Object { - "amount0Delta": "10.000", - "amount1Delta": "-0.0099900", - "executionPrice": "0.00099900", - "priceAfter": "0.0000010040", -} -`; - -exports[`Pool arbitrage tests protocol fee = 6; passive liquidity of 0.010000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity 1`] = ` -Object { - "arbBalanceDelta0": "9.9941", - "arbBalanceDelta1": "-0.010055", - "backrun": Object { - "delta0": "-0.30593", - "delta1": "0.0097475", - "executionPrice": "0.031862", - }, - "burn": Object { - "amount0": "9.9700", - "amount1": "1.0910e+12", - }, - "collect": Object { - "amount0": "0.025000", - "amount1": "0.0000", - }, - "finalPrice": "1.0070", - "frontrun": Object { - "delta0": "0.30682", - "delta1": "-0.0096834", - "executionPrice": "0.031561", - }, - "mint": Object { - "amount0": "0.0000", - "amount1": "1.0910e+12", - }, - "profit": Object { - "afterFrontrun": "-0.30020", - "afterSandwich": "9.7848", - "final": "10.084", - }, - "sandwichedPrice": "0.00099910", -} -`; - -exports[`Pool arbitrage tests protocol fee = 6; passive liquidity of 1.0000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 backrun to true price after swap only 1`] = ` -Object { - "arbBalanceDelta0": "9.9583", - "arbBalanceDelta1": "-0.90001", - "backrun": Object { - "delta0": "-9.9583", - "delta1": "0.90001", - "executionPrice": "0.090377", - }, - "finalPrice": "0.97706", - "profit": Object { - "final": "8.8592", - }, -} -`; - -exports[`Pool arbitrage tests protocol fee = 6; passive liquidity of 1.0000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 not sandwiched 1`] = ` -Object { - "amount0Delta": "10.000", - "amount1Delta": "-0.90884", - "executionPrice": "0.090884", - "priceAfter": "0.0083097", -} -`; - -exports[`Pool arbitrage tests protocol fee = 6; passive liquidity of 1.0000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity 1`] = ` -Object { - "arbBalanceDelta0": "9.9764", - "arbBalanceDelta1": "-0.90054", - "backrun": Object { - "delta0": "-2.2983", - "delta1": "0.68841", - "executionPrice": "0.29953", - }, - "burn": Object { - "amount0": "9.9700", - "amount1": "1.0412e+13", - }, - "collect": Object { - "amount0": "0.025000", - "amount1": "0.0000", - }, - "finalPrice": "0.97706", - "frontrun": Object { - "delta0": "2.3169", - "delta1": "-0.69788", - "executionPrice": "0.30121", - }, - "mint": Object { - "amount0": "0.0000", - "amount1": "1.0412e+13", - }, - "profit": Object { - "afterFrontrun": "-1.5727", - "afterSandwich": "7.3124", - "final": "8.8763", - }, - "sandwichedPrice": "0.091001", -} -`; - -exports[`Pool arbitrage tests protocol fee = 6; passive liquidity of 1.0000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 backrun to true price after swap only 1`] = ` -Object { - "arbBalanceDelta0": "9.9735", - "arbBalanceDelta1": "-0.91507", - "backrun": Object { - "delta0": "-9.9735", - "delta1": "0.91507", - "executionPrice": "0.091750", - }, - "finalPrice": "1.0070", - "profit": Object { - "final": "9.1581", - }, -} -`; - -exports[`Pool arbitrage tests protocol fee = 6; passive liquidity of 1.0000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 not sandwiched 1`] = ` -Object { - "amount0Delta": "10.000", - "amount1Delta": "-0.90884", - "executionPrice": "0.090884", - "priceAfter": "0.0083097", -} -`; - -exports[`Pool arbitrage tests protocol fee = 6; passive liquidity of 1.0000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity 1`] = ` -Object { - "arbBalanceDelta0": "9.9915", - "arbBalanceDelta1": "-0.91560", - "backrun": Object { - "delta0": "-2.3134", - "delta1": "0.70347", - "executionPrice": "0.30408", - }, - "burn": Object { - "amount0": "9.9700", - "amount1": "1.0412e+13", - }, - "collect": Object { - "amount0": "0.025000", - "amount1": "0.0000", - }, - "finalPrice": "1.0070", - "frontrun": Object { - "delta0": "2.3169", - "delta1": "-0.69788", - "executionPrice": "0.30121", - }, - "mint": Object { - "amount0": "0.0000", - "amount1": "1.0412e+13", - }, - "profit": Object { - "afterFrontrun": "-1.6422", - "afterSandwich": "7.5427", - "final": "9.1758", - }, - "sandwichedPrice": "0.091001", -} -`; - -exports[`Pool arbitrage tests protocol fee = 6; passive liquidity of 10.000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 backrun to true price after swap only 1`] = ` -Object { - "arbBalanceDelta0": "9.8533", - "arbBalanceDelta1": "-4.8918", - "backrun": Object { - "delta0": "-9.8533", - "delta1": "4.8918", - "executionPrice": "0.49646", - }, - "finalPrice": "0.97706", - "profit": Object { - "final": "4.7644", - }, -} -`; - -exports[`Pool arbitrage tests protocol fee = 6; passive liquidity of 10.000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 not sandwiched 1`] = ` -Object { - "amount0Delta": "10.000", - "amount1Delta": "-4.9925", - "executionPrice": "0.49925", - "priceAfter": "0.25075", -} -`; - -exports[`Pool arbitrage tests protocol fee = 6; passive liquidity of 10.000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity 1`] = ` -Object { - "arbBalanceDelta0": "9.8659", - "arbBalanceDelta1": "-4.8940", - "backrun": Object { - "delta0": "-4.0029", - "delta1": "2.8107", - "executionPrice": "0.70217", - }, - "burn": Object { - "amount0": "9.9700", - "amount1": "2.4408e+13", - }, - "collect": Object { - "amount0": "0.025000", - "amount1": "0.0000", - }, - "finalPrice": "0.97706", - "frontrun": Object { - "delta0": "4.1321", - "delta1": "-2.9177", - "executionPrice": "0.70611", - }, - "mint": Object { - "amount0": "0.0000", - "amount1": "2.4408e+13", - }, - "profit": Object { - "afterFrontrun": "-1.1317", - "afterSandwich": "3.6625", - "final": "4.7746", - }, - "sandwichedPrice": "0.50009", -} -`; - -exports[`Pool arbitrage tests protocol fee = 6; passive liquidity of 10.000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 backrun to true price after swap only 1`] = ` -Object { - "arbBalanceDelta0": "10.005", - "arbBalanceDelta1": "-5.0424", - "backrun": Object { - "delta0": "-10.005", - "delta1": "5.0424", - "executionPrice": "0.50401", - }, - "finalPrice": "1.0070", - "profit": Object { - "final": "5.0623", - }, -} -`; - -exports[`Pool arbitrage tests protocol fee = 6; passive liquidity of 10.000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 not sandwiched 1`] = ` -Object { - "amount0Delta": "10.000", - "amount1Delta": "-4.9925", - "executionPrice": "0.49925", - "priceAfter": "0.25075", -} -`; - -exports[`Pool arbitrage tests protocol fee = 6; passive liquidity of 10.000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity 1`] = ` -Object { - "arbBalanceDelta0": "10.017", - "arbBalanceDelta1": "-5.0446", - "backrun": Object { - "delta0": "-4.1543", - "delta1": "2.9613", - "executionPrice": "0.71283", - }, - "burn": Object { - "amount0": "9.9700", - "amount1": "2.4408e+13", - }, - "collect": Object { - "amount0": "0.025000", - "amount1": "0.0000", - }, - "finalPrice": "1.0070", - "frontrun": Object { - "delta0": "4.1321", - "delta1": "-2.9177", - "executionPrice": "0.70611", - }, - "mint": Object { - "amount0": "0.0000", - "amount1": "2.4408e+13", - }, - "profit": Object { - "afterFrontrun": "-1.2557", - "afterSandwich": "3.8384", - "final": "5.0729", - }, - "sandwichedPrice": "0.50009", -} -`; - -exports[`Pool arbitrage tests protocol fee = 6; passive liquidity of 100.00 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 backrun to true price after swap only 1`] = ` -Object { - "arbBalanceDelta0": "8.8029", - "arbBalanceDelta1": "-7.9363", - "backrun": Object { - "delta0": "-8.8029", - "delta1": "7.9363", - "executionPrice": "0.90155", - }, - "finalPrice": "0.97706", - "profit": Object { - "final": "0.69056", - }, -} -`; - -exports[`Pool arbitrage tests protocol fee = 6; passive liquidity of 100.00 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 not sandwiched 1`] = ` -Object { - "amount0Delta": "10.000", - "amount1Delta": "-9.0661", - "executionPrice": "0.90661", - "priceAfter": "0.82690", -} -`; - -exports[`Pool arbitrage tests protocol fee = 6; passive liquidity of 100.00 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity 1`] = ` -Object { - "arbBalanceDelta0": "8.8140", - "arbBalanceDelta1": "-7.9680", - "backrun": Object { - "delta0": "-3.4354", - "delta1": "3.2562", - "executionPrice": "0.94781", - }, - "burn": Object { - "amount0": "9.9700", - "amount1": "3.2947e+13", - }, - "collect": Object { - "amount0": "0.025000", - "amount1": "0.0000", - }, - "finalPrice": "0.97706", - "frontrun": Object { - "delta0": "4.6164", - "delta1": "-4.4000", - "executionPrice": "0.95313", - }, - "mint": Object { - "amount0": "0.0000", - "amount1": "3.2947e+13", - }, - "profit": Object { - "afterFrontrun": "-0.12404", - "afterSandwich": "0.55913", - "final": "0.66970", - }, - "sandwichedPrice": "0.91119", -} -`; - -exports[`Pool arbitrage tests protocol fee = 6; passive liquidity of 100.00 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 backrun to true price after swap only 1`] = ` -Object { - "arbBalanceDelta0": "10.317", - "arbBalanceDelta1": "-9.4423", - "backrun": Object { - "delta0": "-10.317", - "delta1": "9.4423", - "executionPrice": "0.91525", - }, - "finalPrice": "1.0070", - "profit": Object { - "final": "0.97752", - }, -} -`; - -exports[`Pool arbitrage tests protocol fee = 6; passive liquidity of 100.00 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 not sandwiched 1`] = ` -Object { - "amount0Delta": "10.000", - "amount1Delta": "-9.0661", - "executionPrice": "0.90661", - "priceAfter": "0.82690", -} -`; - -exports[`Pool arbitrage tests protocol fee = 6; passive liquidity of 100.00 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity 1`] = ` -Object { - "arbBalanceDelta0": "10.328", - "arbBalanceDelta1": "-9.4741", - "backrun": Object { - "delta0": "-4.9492", - "delta1": "4.7622", - "executionPrice": "0.96221", - }, - "burn": Object { - "amount0": "9.9700", - "amount1": "3.2947e+13", - }, - "collect": Object { - "amount0": "0.025000", - "amount1": "0.0000", - }, - "finalPrice": "1.0070", - "frontrun": Object { - "delta0": "4.6164", - "delta1": "-4.4000", - "executionPrice": "0.95313", - }, - "mint": Object { - "amount0": "0.0000", - "amount1": "3.2947e+13", - }, - "profit": Object { - "afterFrontrun": "-0.26253", - "afterSandwich": "0.72049", - "final": "0.95700", - }, - "sandwichedPrice": "0.91119", -} -`; diff --git a/test/__snapshots__/Pool.gas.spec.ts.snap b/test/__snapshots__/Pool.gas.spec.ts.snap deleted file mode 100644 index 4d2e64ff0..000000000 --- a/test/__snapshots__/Pool.gas.spec.ts.snap +++ /dev/null @@ -1,169 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Pool gas tests fee is off #burn above current price burn entire position after some time passes 1`] = `94038`; - -exports[`Pool gas tests fee is off #burn above current price burn when only position using ticks 1`] = `94038`; - -exports[`Pool gas tests fee is off #burn above current price entire position burn but other positions are using the ticks 1`] = `93195`; - -exports[`Pool gas tests fee is off #burn above current price partial position burn 1`] = `97995`; - -exports[`Pool gas tests fee is off #burn around current price burn entire position after some time passes 1`] = `111384`; - -exports[`Pool gas tests fee is off #burn around current price burn when only position using ticks 1`] = `106341`; - -exports[`Pool gas tests fee is off #burn around current price entire position burn but other positions are using the ticks 1`] = `98174`; - -exports[`Pool gas tests fee is off #burn around current price partial position burn 1`] = `102974`; - -exports[`Pool gas tests fee is off #burn below current price burn entire position after some time passes 1`] = `103589`; - -exports[`Pool gas tests fee is off #burn below current price burn when only position using ticks 1`] = `103589`; - -exports[`Pool gas tests fee is off #burn below current price entire position burn but other positions are using the ticks 1`] = `93934`; - -exports[`Pool gas tests fee is off #burn below current price partial position burn 1`] = `98734`; - -exports[`Pool gas tests fee is off #collect close to worst case 1`] = `46676`; - -exports[`Pool gas tests fee is off #increaseObservationCardinalityNext grow by 1 slot 1`] = `51153`; - -exports[`Pool gas tests fee is off #increaseObservationCardinalityNext no op 1`] = `24740`; - -exports[`Pool gas tests fee is off #mint above current price add to position after some time passes 1`] = `109822`; - -exports[`Pool gas tests fee is off #mint above current price add to position existing 1`] = `109822`; - -exports[`Pool gas tests fee is off #mint above current price new position mint first in range 1`] = `228456`; - -exports[`Pool gas tests fee is off #mint above current price second position in same range 1`] = `126922`; - -exports[`Pool gas tests fee is off #mint around current price add to position after some time passes 1`] = `147924`; - -exports[`Pool gas tests fee is off #mint around current price add to position existing 1`] = `138819`; - -exports[`Pool gas tests fee is off #mint around current price new position mint first in range 1`] = `328991`; - -exports[`Pool gas tests fee is off #mint around current price second position in same range 1`] = `155919`; - -exports[`Pool gas tests fee is off #mint below current price add to position after some time passes 1`] = `110548`; - -exports[`Pool gas tests fee is off #mint below current price add to position existing 1`] = `110548`; - -exports[`Pool gas tests fee is off #mint below current price new position mint first in range 1`] = `310248`; - -exports[`Pool gas tests fee is off #mint below current price second position in same range 1`] = `127648`; - -exports[`Pool gas tests fee is off #poke best case 1`] = `52238`; - -exports[`Pool gas tests fee is off #snapshotCumulativesInside tick above 1`] = `29870`; - -exports[`Pool gas tests fee is off #snapshotCumulativesInside tick below 1`] = `29832`; - -exports[`Pool gas tests fee is off #snapshotCumulativesInside tick inside 1`] = `37219`; - -exports[`Pool gas tests fee is off #swapExact0For1 first swap in block moves tick, no initialized crossings 1`] = `115975`; - -exports[`Pool gas tests fee is off #swapExact0For1 first swap in block with no tick movement 1`] = `100058`; - -exports[`Pool gas tests fee is off #swapExact0For1 first swap in block, large swap crossing a single initialized tick 1`] = `141266`; - -exports[`Pool gas tests fee is off #swapExact0For1 first swap in block, large swap crossing several initialized ticks 1`] = `195223`; - -exports[`Pool gas tests fee is off #swapExact0For1 first swap in block, large swap, no initialized crossings 1`] = `130904`; - -exports[`Pool gas tests fee is off #swapExact0For1 large swap crossing several initialized ticks after some time passes 1`] = `195223`; - -exports[`Pool gas tests fee is off #swapExact0For1 large swap crossing several initialized ticks second time after some time passes 1`] = `214423`; - -exports[`Pool gas tests fee is off #swapExact0For1 second swap in block moves tick, no initialized crossings 1`] = `115975`; - -exports[`Pool gas tests fee is off #swapExact0For1 second swap in block with no tick movement 1`] = `100169`; - -exports[`Pool gas tests fee is off #swapExact0For1 second swap in block, large swap crossing a single initialized tick 1`] = `127764`; - -exports[`Pool gas tests fee is off #swapExact0For1 second swap in block, large swap crossing several initialized ticks 1`] = `181689`; - -exports[`Pool gas tests fee is on #burn above current price burn entire position after some time passes 1`] = `94038`; - -exports[`Pool gas tests fee is on #burn above current price burn when only position using ticks 1`] = `94038`; - -exports[`Pool gas tests fee is on #burn above current price entire position burn but other positions are using the ticks 1`] = `93195`; - -exports[`Pool gas tests fee is on #burn above current price partial position burn 1`] = `97995`; - -exports[`Pool gas tests fee is on #burn around current price burn entire position after some time passes 1`] = `111384`; - -exports[`Pool gas tests fee is on #burn around current price burn when only position using ticks 1`] = `106341`; - -exports[`Pool gas tests fee is on #burn around current price entire position burn but other positions are using the ticks 1`] = `98174`; - -exports[`Pool gas tests fee is on #burn around current price partial position burn 1`] = `102974`; - -exports[`Pool gas tests fee is on #burn below current price burn entire position after some time passes 1`] = `103589`; - -exports[`Pool gas tests fee is on #burn below current price burn when only position using ticks 1`] = `103589`; - -exports[`Pool gas tests fee is on #burn below current price entire position burn but other positions are using the ticks 1`] = `93934`; - -exports[`Pool gas tests fee is on #burn below current price partial position burn 1`] = `98734`; - -exports[`Pool gas tests fee is on #collect close to worst case 1`] = `46676`; - -exports[`Pool gas tests fee is on #increaseObservationCardinalityNext grow by 1 slot 1`] = `51153`; - -exports[`Pool gas tests fee is on #increaseObservationCardinalityNext no op 1`] = `24740`; - -exports[`Pool gas tests fee is on #mint above current price add to position after some time passes 1`] = `109822`; - -exports[`Pool gas tests fee is on #mint above current price add to position existing 1`] = `109822`; - -exports[`Pool gas tests fee is on #mint above current price new position mint first in range 1`] = `228456`; - -exports[`Pool gas tests fee is on #mint above current price second position in same range 1`] = `126922`; - -exports[`Pool gas tests fee is on #mint around current price add to position after some time passes 1`] = `147924`; - -exports[`Pool gas tests fee is on #mint around current price add to position existing 1`] = `138819`; - -exports[`Pool gas tests fee is on #mint around current price new position mint first in range 1`] = `328991`; - -exports[`Pool gas tests fee is on #mint around current price second position in same range 1`] = `155919`; - -exports[`Pool gas tests fee is on #mint below current price add to position after some time passes 1`] = `110548`; - -exports[`Pool gas tests fee is on #mint below current price add to position existing 1`] = `110548`; - -exports[`Pool gas tests fee is on #mint below current price new position mint first in range 1`] = `310248`; - -exports[`Pool gas tests fee is on #mint below current price second position in same range 1`] = `127648`; - -exports[`Pool gas tests fee is on #poke best case 1`] = `52238`; - -exports[`Pool gas tests fee is on #snapshotCumulativesInside tick above 1`] = `29870`; - -exports[`Pool gas tests fee is on #snapshotCumulativesInside tick below 1`] = `29832`; - -exports[`Pool gas tests fee is on #snapshotCumulativesInside tick inside 1`] = `37219`; - -exports[`Pool gas tests fee is on #swapExact0For1 first swap in block moves tick, no initialized crossings 1`] = `121362`; - -exports[`Pool gas tests fee is on #swapExact0For1 first swap in block with no tick movement 1`] = `105298`; - -exports[`Pool gas tests fee is on #swapExact0For1 first swap in block, large swap crossing a single initialized tick 1`] = `146800`; - -exports[`Pool gas tests fee is on #swapExact0For1 first swap in block, large swap crossing several initialized ticks 1`] = `201198`; - -exports[`Pool gas tests fee is on #swapExact0For1 first swap in block, large swap, no initialized crossings 1`] = `136585`; - -exports[`Pool gas tests fee is on #swapExact0For1 large swap crossing several initialized ticks after some time passes 1`] = `201198`; - -exports[`Pool gas tests fee is on #swapExact0For1 large swap crossing several initialized ticks second time after some time passes 1`] = `220398`; - -exports[`Pool gas tests fee is on #swapExact0For1 second swap in block moves tick, no initialized crossings 1`] = `121362`; - -exports[`Pool gas tests fee is on #swapExact0For1 second swap in block with no tick movement 1`] = `105409`; - -exports[`Pool gas tests fee is on #swapExact0For1 second swap in block, large swap crossing a single initialized tick 1`] = `133151`; - -exports[`Pool gas tests fee is on #swapExact0For1 second swap in block, large swap crossing several initialized ticks 1`] = `187517`; diff --git a/test/__snapshots__/Pool.swaps.spec.ts.snap b/test/__snapshots__/Pool.swaps.spec.ts.snap deleted file mode 100644 index 2d96e8acd..000000000 --- a/test/__snapshots__/Pool.swaps.spec.ts.snap +++ /dev/null @@ -1,3541 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Pool swap tests close to max price swap exactly 0.0000000000000010000 token0 for token1 1`] = ` -Object { - "amount0Before": "1", - "amount0Delta": "1000", - "amount1Before": "26087635650665564424699143612505016738", - "amount1Delta": "-26083549850867114346332688477747755628", - "executionPrice": "2.6084e+34", - "feeGrowthGlobal0X128Delta": "2381976568446569244235", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "4.1734e+30", - "poolPriceBefore": "1.7014e+38", - "tickAfter": 705098, - "tickBefore": 880340, -} -`; - -exports[`Pool swap tests close to max price swap exactly 0.0000000000000010000 token1 for token0 1`] = ` -Object { - "amount0Before": "1", - "amount0Delta": "0", - "amount1Before": "26087635650665564424699143612505016738", - "amount1Delta": "1000", - "executionPrice": "-Infinity", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "510423550381407695195", - "poolPriceAfter": "1.7014e+38", - "poolPriceBefore": "1.7014e+38", - "tickAfter": 880340, - "tickBefore": 880340, -} -`; - -exports[`Pool swap tests close to max price swap exactly 1.0000 token0 for token1 1`] = ` -Object { - "amount0Before": "1", - "amount0Delta": "1000000000000000000", - "amount1Before": "26087635650665564424699143612505016738", - "amount1Delta": "-26087635650665564420687107504180041533", - "executionPrice": "2.6088e+19", - "feeGrowthGlobal0X128Delta": "510423550381413479995299567101531162", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "4.0241", - "poolPriceBefore": "1.7014e+38", - "tickAfter": 13923, - "tickBefore": 880340, -} -`; - -exports[`Pool swap tests close to max price swap exactly 1.0000 token0 for token1 to price 0.50000 1`] = ` -Object { - "amount0Before": "1", - "amount0Delta": "1000000000000000000", - "amount1Before": "26087635650665564424699143612505016738", - "amount1Delta": "-26087635650665564420687107504180041533", - "executionPrice": "2.6088e+19", - "feeGrowthGlobal0X128Delta": "510423550381413479995299567101531162", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "4.0241", - "poolPriceBefore": "1.7014e+38", - "tickAfter": 13923, - "tickBefore": 880340, -} -`; - -exports[`Pool swap tests close to max price swap exactly 1.0000 token1 for token0 1`] = ` -Object { - "amount0Before": "1", - "amount0Delta": "0", - "amount1Before": "26087635650665564424699143612505016738", - "amount1Delta": "1000000000000000000", - "executionPrice": "-Infinity", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "510423550381407695195061911147652317", - "poolPriceAfter": "1.7014e+38", - "poolPriceBefore": "1.7014e+38", - "tickAfter": 880340, - "tickBefore": 880340, -} -`; - -exports[`Pool swap tests close to max price swap exactly 1.0000 token1 for token0 to price 2.0000 1`] = ` -Object { - "poolBalance0": "1", - "poolBalance1": "26087635650665564424699143612505016738", - "poolPriceBefore": "1.7014e+38", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 880340, -} -`; - -exports[`Pool swap tests close to max price swap token0 for exactly 0.0000000000000010000 token1 1`] = ` -Object { - "amount0Before": "1", - "amount0Delta": "2", - "amount1Before": "26087635650665564424699143612505016738", - "amount1Delta": "-1000", - "executionPrice": "500.00", - "feeGrowthGlobal0X128Delta": "170141183460469231731", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "1.7014e+38", - "poolPriceBefore": "1.7014e+38", - "tickAfter": 880340, - "tickBefore": 880340, -} -`; - -exports[`Pool swap tests close to max price swap token0 for exactly 1.0000 token1 1`] = ` -Object { - "amount0Before": "1", - "amount0Delta": "2", - "amount1Before": "26087635650665564424699143612505016738", - "amount1Delta": "-1000000000000000000", - "executionPrice": "5.0000e+17", - "feeGrowthGlobal0X128Delta": "170141183460469231731", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "1.7014e+38", - "poolPriceBefore": "1.7014e+38", - "tickAfter": 880340, - "tickBefore": 880340, -} -`; - -exports[`Pool swap tests close to max price swap token0 for exactly 1.0000 token1 to price 0.50000 1`] = ` -Object { - "amount0Before": "1", - "amount0Delta": "2", - "amount1Before": "26087635650665564424699143612505016738", - "amount1Delta": "-1000000000000000000", - "executionPrice": "5.0000e+17", - "feeGrowthGlobal0X128Delta": "170141183460469231731", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "1.7014e+38", - "poolPriceBefore": "1.7014e+38", - "tickAfter": 880340, - "tickBefore": 880340, -} -`; - -exports[`Pool swap tests close to max price swap token0 for token1 to price 0.40000 1`] = ` -Object { - "amount0Before": "1", - "amount0Delta": "3171793039286238109", - "amount1Before": "26087635650665564424699143612505016738", - "amount1Delta": "-26087635650665564423434232548437664977", - "executionPrice": "8.2249e+18", - "feeGrowthGlobal0X128Delta": "1618957864187523123655042148763283097", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.40000", - "poolPriceBefore": "1.7014e+38", - "tickAfter": -9164, - "tickBefore": 880340, -} -`; - -exports[`Pool swap tests close to max price swap token0 for token1 to price 2.5000 1`] = ` -Object { - "amount0Before": "1", - "amount0Delta": "1268717215714495281", - "amount1Before": "26087635650665564424699143612505016738", - "amount1Delta": "-26087635650665564421536865952336637378", - "executionPrice": "2.0562e+19", - "feeGrowthGlobal0X128Delta": "647583145675012618257449376796101507", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "2.5000", - "poolPriceBefore": "1.7014e+38", - "tickAfter": 9163, - "tickBefore": 880340, -} -`; - -exports[`Pool swap tests close to max price swap token1 for exactly 0.0000000000000010000 token0 1`] = ` -Object { - "amount0Before": "1", - "amount0Delta": "0", - "amount1Before": "26087635650665564424699143612505016738", - "amount1Delta": "10740898373457544742072477595619363803", - "executionPrice": "-Infinity", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "5482407482066087054477299856254072312542046383926535301", - "poolPriceAfter": "3.4026e+38", - "poolPriceBefore": "1.7014e+38", - "tickAfter": 887271, - "tickBefore": 880340, -} -`; - -exports[`Pool swap tests close to max price swap token1 for exactly 1.0000 token0 1`] = ` -Object { - "amount0Before": "1", - "amount0Delta": "0", - "amount1Before": "26087635650665564424699143612505016738", - "amount1Delta": "10740898373457544742072477595619363803", - "executionPrice": "-Infinity", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "5482407482066087054477299856254072312542046383926535301", - "poolPriceAfter": "3.4026e+38", - "poolPriceBefore": "1.7014e+38", - "tickAfter": 887271, - "tickBefore": 880340, -} -`; - -exports[`Pool swap tests close to max price swap token1 for exactly 1.0000 token0 to price 2.0000 1`] = ` -Object { - "poolBalance0": "1", - "poolBalance1": "26087635650665564424699143612505016738", - "poolPriceBefore": "1.7014e+38", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 880340, -} -`; - -exports[`Pool swap tests close to max price swap token1 for token0 to price 0.40000 1`] = ` -Object { - "poolBalance0": "1", - "poolBalance1": "26087635650665564424699143612505016738", - "poolPriceBefore": "1.7014e+38", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 880340, -} -`; - -exports[`Pool swap tests close to max price swap token1 for token0 to price 2.5000 1`] = ` -Object { - "poolBalance0": "1", - "poolBalance1": "26087635650665564424699143612505016738", - "poolPriceBefore": "1.7014e+38", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 880340, -} -`; - -exports[`Pool swap tests close to min price swap exactly 0.0000000000000010000 token0 for token1 1`] = ` -Object { - "amount0Before": "26037782196502120275425782622539039026", - "amount0Delta": "1000", - "amount1Before": "1", - "amount1Delta": "0", - "executionPrice": "0.0000", - "feeGrowthGlobal0X128Delta": "170141183460469231731687", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.0000000000000000000000000000000000000059000", - "poolPriceBefore": "0.0000000000000000000000000000000000000059000", - "tickAfter": -880303, - "tickBefore": -880303, -} -`; - -exports[`Pool swap tests close to min price swap exactly 0.0000000000000010000 token1 for token0 1`] = ` -Object { - "amount0Before": "26037782196502120275425782622539039026", - "amount0Delta": "-26033697540846965126433148994127431276", - "amount1Before": "1", - "amount1Delta": "1000", - "executionPrice": "0.000000000000000000000000000000000038412", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "2381976568446569244235", - "poolPriceAfter": "0.00000000000000000000000000000023974", - "poolPriceBefore": "0.0000000000000000000000000000000000000059000", - "tickAfter": -705093, - "tickBefore": -880303, -} -`; - -exports[`Pool swap tests close to min price swap exactly 1.0000 token0 for token1 1`] = ` -Object { - "amount0Before": "26037782196502120275425782622539039026", - "amount0Delta": "1000000000000000000", - "amount1Before": "1", - "amount1Delta": "0", - "executionPrice": "0.0000", - "feeGrowthGlobal0X128Delta": "170141183460469231731687303715884105728", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.0000000000000000000000000000000000000059000", - "poolPriceBefore": "0.0000000000000000000000000000000000000059000", - "tickAfter": -880303, - "tickBefore": -880303, -} -`; - -exports[`Pool swap tests close to min price swap exactly 1.0000 token0 for token1 to price 0.50000 1`] = ` -Object { - "poolBalance0": "26037782196502120275425782622539039026", - "poolBalance1": "1", - "poolPriceBefore": "0.0000000000000000000000000000000000000059000", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": -880303, -} -`; - -exports[`Pool swap tests close to min price swap exactly 1.0000 token1 for token0 1`] = ` -Object { - "amount0Before": "26037782196502120275425782622539039026", - "amount0Delta": "-26037782196502120271413746514214063808", - "amount1Before": "1", - "amount1Delta": "1000000000000000000", - "executionPrice": "0.000000000000000000038406", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "510423550381413820277666488039994629", - "poolPriceAfter": "0.24850", - "poolPriceBefore": "0.0000000000000000000000000000000000000059000", - "tickAfter": -13924, - "tickBefore": -880303, -} -`; - -exports[`Pool swap tests close to min price swap exactly 1.0000 token1 for token0 to price 2.0000 1`] = ` -Object { - "amount0Before": "26037782196502120275425782622539039026", - "amount0Delta": "-26037782196502120271413746514214063808", - "amount1Before": "1", - "amount1Delta": "1000000000000000000", - "executionPrice": "0.000000000000000000038406", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "510423550381413820277666488039994629", - "poolPriceAfter": "0.24850", - "poolPriceBefore": "0.0000000000000000000000000000000000000059000", - "tickAfter": -13924, - "tickBefore": -880303, -} -`; - -exports[`Pool swap tests close to min price swap token0 for exactly 0.0000000000000010000 token1 1`] = ` -Object { - "amount0Before": "26037782196502120275425782622539039026", - "amount0Delta": "10790901831095468191587263901270792610", - "amount1Before": "1", - "amount1Delta": "0", - "executionPrice": "0.0000", - "feeGrowthGlobal0X128Delta": "5507930424444982259736347157352787128931407551935325049", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.0000000000000000000000000000000000000029390", - "poolPriceBefore": "0.0000000000000000000000000000000000000059000", - "tickAfter": -887272, - "tickBefore": -880303, -} -`; - -exports[`Pool swap tests close to min price swap token0 for exactly 1.0000 token1 1`] = ` -Object { - "amount0Before": "26037782196502120275425782622539039026", - "amount0Delta": "10790901831095468191587263901270792610", - "amount1Before": "1", - "amount1Delta": "0", - "executionPrice": "0.0000", - "feeGrowthGlobal0X128Delta": "5507930424444982259736347157352787128931407551935325049", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.0000000000000000000000000000000000000029390", - "poolPriceBefore": "0.0000000000000000000000000000000000000059000", - "tickAfter": -887272, - "tickBefore": -880303, -} -`; - -exports[`Pool swap tests close to min price swap token0 for exactly 1.0000 token1 to price 0.50000 1`] = ` -Object { - "poolBalance0": "26037782196502120275425782622539039026", - "poolBalance1": "1", - "poolPriceBefore": "0.0000000000000000000000000000000000000059000", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": -880303, -} -`; - -exports[`Pool swap tests close to min price swap token0 for token1 to price 0.40000 1`] = ` -Object { - "poolBalance0": "26037782196502120275425782622539039026", - "poolBalance1": "1", - "poolPriceBefore": "0.0000000000000000000000000000000000000059000", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": -880303, -} -`; - -exports[`Pool swap tests close to min price swap token0 for token1 to price 2.5000 1`] = ` -Object { - "poolBalance0": "26037782196502120275425782622539039026", - "poolBalance1": "1", - "poolPriceBefore": "0.0000000000000000000000000000000000000059000", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": -880303, -} -`; - -exports[`Pool swap tests close to min price swap token1 for exactly 0.0000000000000010000 token0 1`] = ` -Object { - "amount0Before": "26037782196502120275425782622539039026", - "amount0Delta": "-1000", - "amount1Before": "1", - "amount1Delta": "2", - "executionPrice": "0.0020000", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "170141183460469231731", - "poolPriceAfter": "0.0000000000000000000000000000000000000059000", - "poolPriceBefore": "0.0000000000000000000000000000000000000059000", - "tickAfter": -880303, - "tickBefore": -880303, -} -`; - -exports[`Pool swap tests close to min price swap token1 for exactly 1.0000 token0 1`] = ` -Object { - "amount0Before": "26037782196502120275425782622539039026", - "amount0Delta": "-1000000000000000000", - "amount1Before": "1", - "amount1Delta": "2", - "executionPrice": "0.0000000000000000020000", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "170141183460469231731", - "poolPriceAfter": "0.0000000000000000000000000000000000000059000", - "poolPriceBefore": "0.0000000000000000000000000000000000000059000", - "tickAfter": -880303, - "tickBefore": -880303, -} -`; - -exports[`Pool swap tests close to min price swap token1 for exactly 1.0000 token0 to price 2.0000 1`] = ` -Object { - "amount0Before": "26037782196502120275425782622539039026", - "amount0Delta": "-1000000000000000000", - "amount1Before": "1", - "amount1Delta": "2", - "executionPrice": "0.0000000000000000020000", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "170141183460469231731", - "poolPriceAfter": "0.0000000000000000000000000000000000000059000", - "poolPriceBefore": "0.0000000000000000000000000000000000000059000", - "tickAfter": -880303, - "tickBefore": -880303, -} -`; - -exports[`Pool swap tests close to min price swap token1 for token0 to price 0.40000 1`] = ` -Object { - "amount0Before": "26037782196502120275425782622539039026", - "amount0Delta": "-26037782196502120272263504962370659661", - "amount1Before": "1", - "amount1Delta": "1268717215714495283", - "executionPrice": "0.000000000000000000048726", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "647583145675012958539816297734564973", - "poolPriceAfter": "0.40000", - "poolPriceBefore": "0.0000000000000000000000000000000000000059000", - "tickAfter": -9164, - "tickBefore": -880303, -} -`; - -exports[`Pool swap tests close to min price swap token1 for token0 to price 2.5000 1`] = ` -Object { - "amount0Before": "26037782196502120275425782622539039026", - "amount0Delta": "-26037782196502120274160871558471687260", - "amount1Before": "1", - "amount1Delta": "3171793039286238112", - "executionPrice": "0.00000000000000000012182", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "1618957864187523634078592530170978294", - "poolPriceAfter": "2.5000", - "poolPriceBefore": "0.0000000000000000000000000000000000000059000", - "tickAfter": 9163, - "tickBefore": -880303, -} -`; - -exports[`Pool swap tests high fee, 1:1 price, 2e18 max range liquidity swap exactly 0.0000000000000010000 token0 for token1 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "1000", - "amount1Before": "2000000000000000000", - "amount1Delta": "-989", - "executionPrice": "0.98900", - "feeGrowthGlobal0X128Delta": "1701411834604692317316", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": -1, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests high fee, 1:1 price, 2e18 max range liquidity swap exactly 0.0000000000000010000 token1 for token0 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "-989", - "amount1Before": "2000000000000000000", - "amount1Delta": "1000", - "executionPrice": "1.0111", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "1701411834604692317316", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 0, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests high fee, 1:1 price, 2e18 max range liquidity swap exactly 1.0000 token0 for token1 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "1000000000000000000", - "amount1Before": "2000000000000000000", - "amount1Delta": "-662207357859531772", - "executionPrice": "0.66221", - "feeGrowthGlobal0X128Delta": "1701411834604692317316873037158841057", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.44742", - "poolPriceBefore": "1.0000", - "tickAfter": -8043, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests high fee, 1:1 price, 2e18 max range liquidity swap exactly 1.0000 token0 for token1 to price 0.50000 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "836795075501202120", - "amount1Before": "2000000000000000000", - "amount1Delta": "-585786437626904951", - "executionPrice": "0.70004", - "feeGrowthGlobal0X128Delta": "1423733044596672457631004491657125052", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.50000", - "poolPriceBefore": "1.0000", - "tickAfter": -6932, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests high fee, 1:1 price, 2e18 max range liquidity swap exactly 1.0000 token1 for token0 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "-662207357859531772", - "amount1Before": "2000000000000000000", - "amount1Delta": "1000000000000000000", - "executionPrice": "1.5101", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "1701411834604692317316873037158841057", - "poolPriceAfter": "2.2350", - "poolPriceBefore": "1.0000", - "tickAfter": 8042, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests high fee, 1:1 price, 2e18 max range liquidity swap exactly 1.0000 token1 for token0 to price 2.0000 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "-585786437626904951", - "amount1Before": "2000000000000000000", - "amount1Delta": "836795075501202120", - "executionPrice": "1.4285", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "1423733044596672457631004491657125052", - "poolPriceAfter": "2.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 6931, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests high fee, 1:1 price, 2e18 max range liquidity swap token0 for exactly 0.0000000000000010000 token1 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "1012", - "amount1Before": "2000000000000000000", - "amount1Delta": "-1000", - "executionPrice": "0.98814", - "feeGrowthGlobal0X128Delta": "1871553018065161549048", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": -1, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests high fee, 1:1 price, 2e18 max range liquidity swap token0 for exactly 1.0000 token1 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "2020202020202020203", - "amount1Before": "2000000000000000000", - "amount1Delta": "-1000000000000000000", - "executionPrice": "0.49500", - "feeGrowthGlobal0X128Delta": "3437195625464025050172418213103875650", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.25000", - "poolPriceBefore": "1.0000", - "tickAfter": -13864, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests high fee, 1:1 price, 2e18 max range liquidity swap token0 for exactly 1.0000 token1 to price 0.50000 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "836795075501202120", - "amount1Before": "2000000000000000000", - "amount1Delta": "-585786437626904951", - "executionPrice": "0.70004", - "feeGrowthGlobal0X128Delta": "1423733044596672457631004491657125052", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.50000", - "poolPriceBefore": "1.0000", - "tickAfter": -6932, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests high fee, 1:1 price, 2e18 max range liquidity swap token0 for token1 to price 0.40000 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "1174017838553918518", - "amount1Before": "2000000000000000000", - "amount1Delta": "-735088935932648267", - "executionPrice": "0.62613", - "feeGrowthGlobal0X128Delta": "1997487844552658120479227965844634309", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.40000", - "poolPriceBefore": "1.0000", - "tickAfter": -9164, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests high fee, 1:1 price, 2e18 max range liquidity swap token0 for token1 to price 2.5000 1`] = ` -Object { - "poolBalance0": "2000000000000000000", - "poolBalance1": "2000000000000000000", - "poolPriceBefore": "1.0000", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 0, -} -`; - -exports[`Pool swap tests high fee, 1:1 price, 2e18 max range liquidity swap token1 for exactly 0.0000000000000010000 token0 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "-1000", - "amount1Before": "2000000000000000000", - "amount1Delta": "1012", - "executionPrice": "1.0120", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "1871553018065161549048", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 0, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests high fee, 1:1 price, 2e18 max range liquidity swap token1 for exactly 1.0000 token0 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "-1000000000000000000", - "amount1Before": "2000000000000000000", - "amount1Delta": "2020202020202020203", - "executionPrice": "2.0202", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "3437195625464025050172418213103875650", - "poolPriceAfter": "4.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 13863, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests high fee, 1:1 price, 2e18 max range liquidity swap token1 for exactly 1.0000 token0 to price 2.0000 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "-585786437626904951", - "amount1Before": "2000000000000000000", - "amount1Delta": "836795075501202120", - "executionPrice": "1.4285", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "1423733044596672457631004491657125052", - "poolPriceAfter": "2.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 6931, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests high fee, 1:1 price, 2e18 max range liquidity swap token1 for token0 to price 0.40000 1`] = ` -Object { - "poolBalance0": "2000000000000000000", - "poolBalance1": "2000000000000000000", - "poolPriceBefore": "1.0000", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 0, -} -`; - -exports[`Pool swap tests high fee, 1:1 price, 2e18 max range liquidity swap token1 for token0 to price 2.5000 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "-735088935932648267", - "amount1Before": "2000000000000000000", - "amount1Delta": "1174017838553918518", - "executionPrice": "1.5971", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "1997487844552658120479227965844634309", - "poolPriceAfter": "2.5000", - "poolPriceBefore": "1.0000", - "tickAfter": 9163, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests initialized at the max ratio swap exactly 0.0000000000000010000 token0 for token1 1`] = ` -Object { - "amount0Before": "0", - "amount0Delta": "1000", - "amount1Before": "36796311329002736532545403775337522448", - "amount1Delta": "-36792225529204286454178948640580261338", - "executionPrice": "3.6792e+34", - "feeGrowthGlobal0X128Delta": "2381976568446569244235", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "4.1734e+30", - "poolPriceBefore": "3.4026e+38", - "tickAfter": 705098, - "tickBefore": 887271, -} -`; - -exports[`Pool swap tests initialized at the max ratio swap exactly 0.0000000000000010000 token1 for token0 1`] = ` -Object { - "poolBalance0": "0", - "poolBalance1": "36796311329002736532545403775337522448", - "poolPriceBefore": "3.4026e+38", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 887271, -} -`; - -exports[`Pool swap tests initialized at the max ratio swap exactly 1.0000 token0 for token1 1`] = ` -Object { - "amount0Before": "0", - "amount0Delta": "1000000000000000000", - "amount1Before": "36796311329002736532545403775337522448", - "amount1Delta": "-36796311329002736528533367667012547243", - "executionPrice": "3.6796e+19", - "feeGrowthGlobal0X128Delta": "510423550381413479995299567101531162", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "4.0241", - "poolPriceBefore": "3.4026e+38", - "tickAfter": 13923, - "tickBefore": 887271, -} -`; - -exports[`Pool swap tests initialized at the max ratio swap exactly 1.0000 token0 for token1 to price 0.50000 1`] = ` -Object { - "amount0Before": "0", - "amount0Delta": "1000000000000000000", - "amount1Before": "36796311329002736532545403775337522448", - "amount1Delta": "-36796311329002736528533367667012547243", - "executionPrice": "3.6796e+19", - "feeGrowthGlobal0X128Delta": "510423550381413479995299567101531162", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "4.0241", - "poolPriceBefore": "3.4026e+38", - "tickAfter": 13923, - "tickBefore": 887271, -} -`; - -exports[`Pool swap tests initialized at the max ratio swap exactly 1.0000 token1 for token0 1`] = ` -Object { - "poolBalance0": "0", - "poolBalance1": "36796311329002736532545403775337522448", - "poolPriceBefore": "3.4026e+38", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 887271, -} -`; - -exports[`Pool swap tests initialized at the max ratio swap exactly 1.0000 token1 for token0 to price 2.0000 1`] = ` -Object { - "poolBalance0": "0", - "poolBalance1": "36796311329002736532545403775337522448", - "poolPriceBefore": "3.4026e+38", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 887271, -} -`; - -exports[`Pool swap tests initialized at the max ratio swap token0 for exactly 0.0000000000000010000 token1 1`] = ` -Object { - "amount0Before": "0", - "amount0Delta": "2", - "amount1Before": "36796311329002736532545403775337522448", - "amount1Delta": "-1000", - "executionPrice": "500.00", - "feeGrowthGlobal0X128Delta": "170141183460469231731", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "3.3849e+38", - "poolPriceBefore": "3.4026e+38", - "tickAfter": 887219, - "tickBefore": 887271, -} -`; - -exports[`Pool swap tests initialized at the max ratio swap token0 for exactly 1.0000 token1 1`] = ` -Object { - "amount0Before": "0", - "amount0Delta": "2", - "amount1Before": "36796311329002736532545403775337522448", - "amount1Delta": "-1000000000000000000", - "executionPrice": "5.0000e+17", - "feeGrowthGlobal0X128Delta": "170141183460469231731", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "3.3849e+38", - "poolPriceBefore": "3.4026e+38", - "tickAfter": 887219, - "tickBefore": 887271, -} -`; - -exports[`Pool swap tests initialized at the max ratio swap token0 for exactly 1.0000 token1 to price 0.50000 1`] = ` -Object { - "amount0Before": "0", - "amount0Delta": "2", - "amount1Before": "36796311329002736532545403775337522448", - "amount1Delta": "-1000000000000000000", - "executionPrice": "5.0000e+17", - "feeGrowthGlobal0X128Delta": "170141183460469231731", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "3.3849e+38", - "poolPriceBefore": "3.4026e+38", - "tickAfter": 887219, - "tickBefore": 887271, -} -`; - -exports[`Pool swap tests initialized at the max ratio swap token0 for token1 to price 0.40000 1`] = ` -Object { - "amount0Before": "0", - "amount0Delta": "3171793039286238109", - "amount1Before": "36796311329002736532545403775337522448", - "amount1Delta": "-36796311329002736531280492711270170687", - "executionPrice": "1.1601e+19", - "feeGrowthGlobal0X128Delta": "1618957864187523123655042148763283097", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.40000", - "poolPriceBefore": "3.4026e+38", - "tickAfter": -9164, - "tickBefore": 887271, -} -`; - -exports[`Pool swap tests initialized at the max ratio swap token0 for token1 to price 2.5000 1`] = ` -Object { - "amount0Before": "0", - "amount0Delta": "1268717215714495281", - "amount1Before": "36796311329002736532545403775337522448", - "amount1Delta": "-36796311329002736529383126115169143088", - "executionPrice": "2.9003e+19", - "feeGrowthGlobal0X128Delta": "647583145675012618257449376796101507", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "2.5000", - "poolPriceBefore": "3.4026e+38", - "tickAfter": 9163, - "tickBefore": 887271, -} -`; - -exports[`Pool swap tests initialized at the max ratio swap token1 for exactly 0.0000000000000010000 token0 1`] = ` -Object { - "poolBalance0": "0", - "poolBalance1": "36796311329002736532545403775337522448", - "poolPriceBefore": "3.4026e+38", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 887271, -} -`; - -exports[`Pool swap tests initialized at the max ratio swap token1 for exactly 1.0000 token0 1`] = ` -Object { - "poolBalance0": "0", - "poolBalance1": "36796311329002736532545403775337522448", - "poolPriceBefore": "3.4026e+38", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 887271, -} -`; - -exports[`Pool swap tests initialized at the max ratio swap token1 for exactly 1.0000 token0 to price 2.0000 1`] = ` -Object { - "poolBalance0": "0", - "poolBalance1": "36796311329002736532545403775337522448", - "poolPriceBefore": "3.4026e+38", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 887271, -} -`; - -exports[`Pool swap tests initialized at the max ratio swap token1 for token0 to price 0.40000 1`] = ` -Object { - "poolBalance0": "0", - "poolBalance1": "36796311329002736532545403775337522448", - "poolPriceBefore": "3.4026e+38", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 887271, -} -`; - -exports[`Pool swap tests initialized at the max ratio swap token1 for token0 to price 2.5000 1`] = ` -Object { - "poolBalance0": "0", - "poolBalance1": "36796311329002736532545403775337522448", - "poolPriceBefore": "3.4026e+38", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 887271, -} -`; - -exports[`Pool swap tests initialized at the min ratio swap exactly 0.0000000000000010000 token0 for token1 1`] = ` -Object { - "poolBalance0": "36796311322104302062438284732106019258", - "poolBalance1": "0", - "poolPriceBefore": "0.0000000000000000000000000000000000000029390", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": -887272, -} -`; - -exports[`Pool swap tests initialized at the min ratio swap exactly 0.0000000000000010000 token1 for token0 1`] = ` -Object { - "amount0Before": "36796311322104302062438284732106019258", - "amount0Delta": "-36792226666449146913445651103694411508", - "amount1Before": "0", - "amount1Delta": "1000", - "executionPrice": "0.000000000000000000000000000000000027180", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "2381976568446569244235", - "poolPriceAfter": "0.00000000000000000000000000000023974", - "poolPriceBefore": "0.0000000000000000000000000000000000000029390", - "tickAfter": -705093, - "tickBefore": -887272, -} -`; - -exports[`Pool swap tests initialized at the min ratio swap exactly 1.0000 token0 for token1 1`] = ` -Object { - "poolBalance0": "36796311322104302062438284732106019258", - "poolBalance1": "0", - "poolPriceBefore": "0.0000000000000000000000000000000000000029390", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": -887272, -} -`; - -exports[`Pool swap tests initialized at the min ratio swap exactly 1.0000 token0 for token1 to price 0.50000 1`] = ` -Object { - "poolBalance0": "36796311322104302062438284732106019258", - "poolBalance1": "0", - "poolPriceBefore": "0.0000000000000000000000000000000000000029390", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": -887272, -} -`; - -exports[`Pool swap tests initialized at the min ratio swap exactly 1.0000 token1 for token0 1`] = ` -Object { - "amount0Before": "36796311322104302062438284732106019258", - "amount0Delta": "-36796311322104302058426248623781044040", - "amount1Before": "0", - "amount1Delta": "1000000000000000000", - "executionPrice": "0.000000000000000000027177", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "510423550381413820277666488039994629", - "poolPriceAfter": "0.24850", - "poolPriceBefore": "0.0000000000000000000000000000000000000029390", - "tickAfter": -13924, - "tickBefore": -887272, -} -`; - -exports[`Pool swap tests initialized at the min ratio swap exactly 1.0000 token1 for token0 to price 2.0000 1`] = ` -Object { - "amount0Before": "36796311322104302062438284732106019258", - "amount0Delta": "-36796311322104302058426248623781044040", - "amount1Before": "0", - "amount1Delta": "1000000000000000000", - "executionPrice": "0.000000000000000000027177", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "510423550381413820277666488039994629", - "poolPriceAfter": "0.24850", - "poolPriceBefore": "0.0000000000000000000000000000000000000029390", - "tickAfter": -13924, - "tickBefore": -887272, -} -`; - -exports[`Pool swap tests initialized at the min ratio swap token0 for exactly 0.0000000000000010000 token1 1`] = ` -Object { - "poolBalance0": "36796311322104302062438284732106019258", - "poolBalance1": "0", - "poolPriceBefore": "0.0000000000000000000000000000000000000029390", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": -887272, -} -`; - -exports[`Pool swap tests initialized at the min ratio swap token0 for exactly 1.0000 token1 1`] = ` -Object { - "poolBalance0": "36796311322104302062438284732106019258", - "poolBalance1": "0", - "poolPriceBefore": "0.0000000000000000000000000000000000000029390", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": -887272, -} -`; - -exports[`Pool swap tests initialized at the min ratio swap token0 for exactly 1.0000 token1 to price 0.50000 1`] = ` -Object { - "poolBalance0": "36796311322104302062438284732106019258", - "poolBalance1": "0", - "poolPriceBefore": "0.0000000000000000000000000000000000000029390", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": -887272, -} -`; - -exports[`Pool swap tests initialized at the min ratio swap token0 for token1 to price 0.40000 1`] = ` -Object { - "poolBalance0": "36796311322104302062438284732106019258", - "poolBalance1": "0", - "poolPriceBefore": "0.0000000000000000000000000000000000000029390", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": -887272, -} -`; - -exports[`Pool swap tests initialized at the min ratio swap token0 for token1 to price 2.5000 1`] = ` -Object { - "poolBalance0": "36796311322104302062438284732106019258", - "poolBalance1": "0", - "poolPriceBefore": "0.0000000000000000000000000000000000000029390", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": -887272, -} -`; - -exports[`Pool swap tests initialized at the min ratio swap token1 for exactly 0.0000000000000010000 token0 1`] = ` -Object { - "amount0Before": "36796311322104302062438284732106019258", - "amount0Delta": "-1000", - "amount1Before": "0", - "amount1Delta": "2", - "executionPrice": "0.0020000", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "170141183460469231731", - "poolPriceAfter": "0.0000000000000000000000000000000000000029543", - "poolPriceBefore": "0.0000000000000000000000000000000000000029390", - "tickAfter": -887220, - "tickBefore": -887272, -} -`; - -exports[`Pool swap tests initialized at the min ratio swap token1 for exactly 1.0000 token0 1`] = ` -Object { - "amount0Before": "36796311322104302062438284732106019258", - "amount0Delta": "-1000000000000000000", - "amount1Before": "0", - "amount1Delta": "2", - "executionPrice": "0.0000000000000000020000", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "170141183460469231731", - "poolPriceAfter": "0.0000000000000000000000000000000000000029543", - "poolPriceBefore": "0.0000000000000000000000000000000000000029390", - "tickAfter": -887220, - "tickBefore": -887272, -} -`; - -exports[`Pool swap tests initialized at the min ratio swap token1 for exactly 1.0000 token0 to price 2.0000 1`] = ` -Object { - "amount0Before": "36796311322104302062438284732106019258", - "amount0Delta": "-1000000000000000000", - "amount1Before": "0", - "amount1Delta": "2", - "executionPrice": "0.0000000000000000020000", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "170141183460469231731", - "poolPriceAfter": "0.0000000000000000000000000000000000000029543", - "poolPriceBefore": "0.0000000000000000000000000000000000000029390", - "tickAfter": -887220, - "tickBefore": -887272, -} -`; - -exports[`Pool swap tests initialized at the min ratio swap token1 for token0 to price 0.40000 1`] = ` -Object { - "amount0Before": "36796311322104302062438284732106019258", - "amount0Delta": "-36796311322104302059276007071937639893", - "amount1Before": "0", - "amount1Delta": "1268717215714495283", - "executionPrice": "0.000000000000000000034479", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "647583145675012958539816297734564973", - "poolPriceAfter": "0.40000", - "poolPriceBefore": "0.0000000000000000000000000000000000000029390", - "tickAfter": -9164, - "tickBefore": -887272, -} -`; - -exports[`Pool swap tests initialized at the min ratio swap token1 for token0 to price 2.5000 1`] = ` -Object { - "amount0Before": "36796311322104302062438284732106019258", - "amount0Delta": "-36796311322104302061173373668038667492", - "amount1Before": "0", - "amount1Delta": "3171793039286238112", - "executionPrice": "0.000000000000000000086199", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "1618957864187523634078592530170978294", - "poolPriceAfter": "2.5000", - "poolPriceBefore": "0.0000000000000000000000000000000000000029390", - "tickAfter": 9163, - "tickBefore": -887272, -} -`; - -exports[`Pool swap tests low fee, 1:1 price, 2e18 max range liquidity swap exactly 0.0000000000000010000 token0 for token1 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "1000", - "amount1Before": "2000000000000000000", - "amount1Delta": "-998", - "executionPrice": "0.99800", - "feeGrowthGlobal0X128Delta": "170141183460469231731", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": -1, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests low fee, 1:1 price, 2e18 max range liquidity swap exactly 0.0000000000000010000 token1 for token0 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "-998", - "amount1Before": "2000000000000000000", - "amount1Delta": "1000", - "executionPrice": "1.0020", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "170141183460469231731", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 0, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests low fee, 1:1 price, 2e18 max range liquidity swap exactly 1.0000 token0 for token1 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "1000000000000000000", - "amount1Before": "2000000000000000000", - "amount1Delta": "-666444407401233536", - "executionPrice": "0.66644", - "feeGrowthGlobal0X128Delta": "85070591730234956148210572796405514", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.44459", - "poolPriceBefore": "1.0000", - "tickAfter": -8107, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests low fee, 1:1 price, 2e18 max range liquidity swap exactly 1.0000 token0 for token1 to price 0.50000 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "828841545518949575", - "amount1Before": "2000000000000000000", - "amount1Delta": "-585786437626904950", - "executionPrice": "0.70675", - "feeGrowthGlobal0X128Delta": "70510040727899606087499539976421836", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.50000", - "poolPriceBefore": "1.0000", - "tickAfter": -6932, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests low fee, 1:1 price, 2e18 max range liquidity swap exactly 1.0000 token1 for token0 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "-666444407401233536", - "amount1Before": "2000000000000000000", - "amount1Delta": "1000000000000000000", - "executionPrice": "1.5005", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "85070591730234956148210572796405515", - "poolPriceAfter": "2.2493", - "poolPriceBefore": "1.0000", - "tickAfter": 8106, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests low fee, 1:1 price, 2e18 max range liquidity swap exactly 1.0000 token1 for token0 to price 2.0000 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "-585786437626904950", - "amount1Before": "2000000000000000000", - "amount1Delta": "828841545518949574", - "executionPrice": "1.4149", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "70510040727899435946316079507190105", - "poolPriceAfter": "2.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 6931, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests low fee, 1:1 price, 2e18 max range liquidity swap token0 for exactly 0.0000000000000010000 token1 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "1002", - "amount1Before": "2000000000000000000", - "amount1Delta": "-1000", - "executionPrice": "0.99800", - "feeGrowthGlobal0X128Delta": "170141183460469231731", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": -1, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests low fee, 1:1 price, 2e18 max range liquidity swap token0 for exactly 1.0000 token1 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "2001000500250125077", - "amount1Before": "2000000000000000000", - "amount1Delta": "-1000000000000000000", - "executionPrice": "0.49975", - "feeGrowthGlobal0X128Delta": "170226296608774038574344664756091446", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.25000", - "poolPriceBefore": "1.0000", - "tickAfter": -13864, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests low fee, 1:1 price, 2e18 max range liquidity swap token0 for exactly 1.0000 token1 to price 0.50000 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "828841545518949575", - "amount1Before": "2000000000000000000", - "amount1Delta": "-585786437626904950", - "executionPrice": "0.70675", - "feeGrowthGlobal0X128Delta": "70510040727899606087499539976421836", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.50000", - "poolPriceBefore": "1.0000", - "tickAfter": -6932, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests low fee, 1:1 price, 2e18 max range liquidity swap token0 for token1 to price 0.40000 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "1162859089713235953", - "amount1Before": "2000000000000000000", - "amount1Delta": "-735088935932648266", - "executionPrice": "0.63214", - "feeGrowthGlobal0X128Delta": "98925110860787308007692432636113977", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.40000", - "poolPriceBefore": "1.0000", - "tickAfter": -9164, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests low fee, 1:1 price, 2e18 max range liquidity swap token0 for token1 to price 2.5000 1`] = ` -Object { - "poolBalance0": "2000000000000000000", - "poolBalance1": "2000000000000000000", - "poolPriceBefore": "1.0000", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 0, -} -`; - -exports[`Pool swap tests low fee, 1:1 price, 2e18 max range liquidity swap token1 for exactly 0.0000000000000010000 token0 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "-1000", - "amount1Before": "2000000000000000000", - "amount1Delta": "1002", - "executionPrice": "1.0020", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "170141183460469231731", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 0, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests low fee, 1:1 price, 2e18 max range liquidity swap token1 for exactly 1.0000 token0 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "-1000000000000000000", - "amount1Before": "2000000000000000000", - "amount1Delta": "2001000500250125079", - "executionPrice": "2.0010", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "170226296608774378856711585694554910", - "poolPriceAfter": "4.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 13863, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests low fee, 1:1 price, 2e18 max range liquidity swap token1 for exactly 1.0000 token0 to price 2.0000 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "-585786437626904950", - "amount1Before": "2000000000000000000", - "amount1Delta": "828841545518949574", - "executionPrice": "1.4149", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "70510040727899435946316079507190105", - "poolPriceAfter": "2.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 6931, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests low fee, 1:1 price, 2e18 max range liquidity swap token1 for token0 to price 0.40000 1`] = ` -Object { - "poolBalance0": "2000000000000000000", - "poolBalance1": "2000000000000000000", - "poolPriceBefore": "1.0000", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 0, -} -`; - -exports[`Pool swap tests low fee, 1:1 price, 2e18 max range liquidity swap token1 for token0 to price 2.5000 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "-735088935932648266", - "amount1Before": "2000000000000000000", - "amount1Delta": "1162859089713235954", - "executionPrice": "1.5819", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "98925110860787308007692432636113978", - "poolPriceAfter": "2.5000", - "poolPriceBefore": "1.0000", - "tickAfter": 9163, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests low fee, large liquidity around current price (stable swap) swap exactly 0.0000000000000010000 token0 for token1 1`] = ` -Object { - "amount0Before": "999700069986003", - "amount0Delta": "1000", - "amount1Before": "999700069986003", - "amount1Delta": "-998", - "executionPrice": "0.99800", - "feeGrowthGlobal0X128Delta": "170141183460469231731", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": -1, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests low fee, large liquidity around current price (stable swap) swap exactly 0.0000000000000010000 token1 for token0 1`] = ` -Object { - "amount0Before": "999700069986003", - "amount0Delta": "-998", - "amount1Before": "999700069986003", - "amount1Delta": "1000", - "executionPrice": "1.0020", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "170141183460469231731", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 0, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests low fee, large liquidity around current price (stable swap) swap exactly 1.0000 token0 for token1 1`] = ` -Object { - "amount0Before": "999700069986003", - "amount0Delta": "1000700370186095", - "amount1Before": "999700069986003", - "amount1Delta": "-999700069986002", - "executionPrice": "0.99900", - "feeGrowthGlobal0X128Delta": "85130172636557991529041720559172", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.0000000000000000000000000000000000000029390", - "poolPriceBefore": "1.0000", - "tickAfter": -887272, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests low fee, large liquidity around current price (stable swap) swap exactly 1.0000 token0 for token1 to price 0.50000 1`] = ` -Object { - "amount0Before": "999700069986003", - "amount0Delta": "1000700370186095", - "amount1Before": "999700069986003", - "amount1Delta": "-999700069986002", - "executionPrice": "0.99900", - "feeGrowthGlobal0X128Delta": "85130172636557991529041720559172", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.50000", - "poolPriceBefore": "1.0000", - "tickAfter": -6932, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests low fee, large liquidity around current price (stable swap) swap exactly 1.0000 token1 for token0 1`] = ` -Object { - "amount0Before": "999700069986003", - "amount0Delta": "-999700069986002", - "amount1Before": "999700069986003", - "amount1Delta": "1000700370186095", - "executionPrice": "1.0010", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "85130172636557991529041720559172", - "poolPriceAfter": "3.4026e+38", - "poolPriceBefore": "1.0000", - "tickAfter": 887271, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests low fee, large liquidity around current price (stable swap) swap exactly 1.0000 token1 for token0 to price 2.0000 1`] = ` -Object { - "amount0Before": "999700069986003", - "amount0Delta": "-999700069986002", - "amount1Before": "999700069986003", - "amount1Delta": "1000700370186095", - "executionPrice": "1.0010", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "85130172636557991529041720559172", - "poolPriceAfter": "2.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 6931, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests low fee, large liquidity around current price (stable swap) swap token0 for exactly 0.0000000000000010000 token1 1`] = ` -Object { - "amount0Before": "999700069986003", - "amount0Delta": "1002", - "amount1Before": "999700069986003", - "amount1Delta": "-1000", - "executionPrice": "0.99800", - "feeGrowthGlobal0X128Delta": "170141183460469231731", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": -1, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests low fee, large liquidity around current price (stable swap) swap token0 for exactly 1.0000 token1 1`] = ` -Object { - "amount0Before": "999700069986003", - "amount0Delta": "1000700370186095", - "amount1Before": "999700069986003", - "amount1Delta": "-999700069986002", - "executionPrice": "0.99900", - "feeGrowthGlobal0X128Delta": "85130172636557991529041720559172", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.0000000000000000000000000000000000000029390", - "poolPriceBefore": "1.0000", - "tickAfter": -887272, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests low fee, large liquidity around current price (stable swap) swap token0 for exactly 1.0000 token1 to price 0.50000 1`] = ` -Object { - "amount0Before": "999700069986003", - "amount0Delta": "1000700370186095", - "amount1Before": "999700069986003", - "amount1Delta": "-999700069986002", - "executionPrice": "0.99900", - "feeGrowthGlobal0X128Delta": "85130172636557991529041720559172", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.50000", - "poolPriceBefore": "1.0000", - "tickAfter": -6932, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests low fee, large liquidity around current price (stable swap) swap token0 for token1 to price 0.40000 1`] = ` -Object { - "amount0Before": "999700069986003", - "amount0Delta": "1000700370186095", - "amount1Before": "999700069986003", - "amount1Delta": "-999700069986002", - "executionPrice": "0.99900", - "feeGrowthGlobal0X128Delta": "85130172636557991529041720559172", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.40000", - "poolPriceBefore": "1.0000", - "tickAfter": -9164, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests low fee, large liquidity around current price (stable swap) swap token0 for token1 to price 2.5000 1`] = ` -Object { - "poolBalance0": "999700069986003", - "poolBalance1": "999700069986003", - "poolPriceBefore": "1.0000", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 0, -} -`; - -exports[`Pool swap tests low fee, large liquidity around current price (stable swap) swap token1 for exactly 0.0000000000000010000 token0 1`] = ` -Object { - "amount0Before": "999700069986003", - "amount0Delta": "-1000", - "amount1Before": "999700069986003", - "amount1Delta": "1002", - "executionPrice": "1.0020", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "170141183460469231731", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 0, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests low fee, large liquidity around current price (stable swap) swap token1 for exactly 1.0000 token0 1`] = ` -Object { - "amount0Before": "999700069986003", - "amount0Delta": "-999700069986002", - "amount1Before": "999700069986003", - "amount1Delta": "1000700370186095", - "executionPrice": "1.0010", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "85130172636557991529041720559172", - "poolPriceAfter": "3.4026e+38", - "poolPriceBefore": "1.0000", - "tickAfter": 887271, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests low fee, large liquidity around current price (stable swap) swap token1 for exactly 1.0000 token0 to price 2.0000 1`] = ` -Object { - "amount0Before": "999700069986003", - "amount0Delta": "-999700069986002", - "amount1Before": "999700069986003", - "amount1Delta": "1000700370186095", - "executionPrice": "1.0010", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "85130172636557991529041720559172", - "poolPriceAfter": "2.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 6931, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests low fee, large liquidity around current price (stable swap) swap token1 for token0 to price 0.40000 1`] = ` -Object { - "poolBalance0": "999700069986003", - "poolBalance1": "999700069986003", - "poolPriceBefore": "1.0000", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 0, -} -`; - -exports[`Pool swap tests low fee, large liquidity around current price (stable swap) swap token1 for token0 to price 2.5000 1`] = ` -Object { - "amount0Before": "999700069986003", - "amount0Delta": "-999700069986002", - "amount1Before": "999700069986003", - "amount1Delta": "1000700370186095", - "executionPrice": "1.0010", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "85130172636557991529041720559172", - "poolPriceAfter": "2.5000", - "poolPriceBefore": "1.0000", - "tickAfter": 9163, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests max full range liquidity at 1:1 price with default fee swap exactly 0.0000000000000010000 token0 for token1 1`] = ` -Object { - "amount0Before": "11505743598341114571255423385623647", - "amount0Delta": "1000", - "amount1Before": "11505743598341114571255423385506404", - "amount1Delta": "0", - "executionPrice": "0.0000", - "feeGrowthGlobal0X128Delta": "29575000", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": -1, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests max full range liquidity at 1:1 price with default fee swap exactly 0.0000000000000010000 token1 for token0 1`] = ` -Object { - "amount0Before": "11505743598341114571255423385623647", - "amount0Delta": "0", - "amount1Before": "11505743598341114571255423385506404", - "amount1Delta": "1000", - "executionPrice": "-Infinity", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "29575000", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 0, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests max full range liquidity at 1:1 price with default fee swap exactly 1.0000 token0 for token1 1`] = ` -Object { - "amount0Before": "11505743598341114571255423385623647", - "amount0Delta": "1000000000000000000", - "amount1Before": "11505743598341114571255423385506404", - "amount1Delta": "-996999999999999318", - "executionPrice": "0.99700", - "feeGrowthGlobal0X128Delta": "88725000000017597125", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": -1, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests max full range liquidity at 1:1 price with default fee swap exactly 1.0000 token0 for token1 to price 0.50000 1`] = ` -Object { - "amount0Before": "11505743598341114571255423385623647", - "amount0Delta": "1000000000000000000", - "amount1Before": "11505743598341114571255423385506404", - "amount1Delta": "-996999999999999318", - "executionPrice": "0.99700", - "feeGrowthGlobal0X128Delta": "88725000000017597125", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": -1, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests max full range liquidity at 1:1 price with default fee swap exactly 1.0000 token1 for token0 1`] = ` -Object { - "amount0Before": "11505743598341114571255423385623647", - "amount0Delta": "-996999999999999232", - "amount1Before": "11505743598341114571255423385506404", - "amount1Delta": "1000000000000000000", - "executionPrice": "1.0030", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "88725000000020140575", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 0, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests max full range liquidity at 1:1 price with default fee swap exactly 1.0000 token1 for token0 to price 2.0000 1`] = ` -Object { - "amount0Before": "11505743598341114571255423385623647", - "amount0Delta": "-996999999999999232", - "amount1Before": "11505743598341114571255423385506404", - "amount1Delta": "1000000000000000000", - "executionPrice": "1.0030", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "88725000000020140575", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 0, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests max full range liquidity at 1:1 price with default fee swap token0 for exactly 0.0000000000000010000 token1 1`] = ` -Object { - "amount0Before": "11505743598341114571255423385623647", - "amount0Delta": "145660", - "amount1Before": "11505743598341114571255423385506404", - "amount1Delta": "-1000", - "executionPrice": "0.0068653", - "feeGrowthGlobal0X128Delta": "12924275", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": -1, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests max full range liquidity at 1:1 price with default fee swap token0 for exactly 1.0000 token1 1`] = ` -Object { - "amount0Before": "11505743598341114571255423385623647", - "amount0Delta": "1003009027081361181", - "amount1Before": "11505743598341114571255423385506404", - "amount1Delta": "-1000000000000000000", - "executionPrice": "0.99700", - "feeGrowthGlobal0X128Delta": "88991975927793784300", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": -1, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests max full range liquidity at 1:1 price with default fee swap token0 for exactly 1.0000 token1 to price 0.50000 1`] = ` -Object { - "amount0Before": "11505743598341114571255423385623647", - "amount0Delta": "1003009027081361181", - "amount1Before": "11505743598341114571255423385506404", - "amount1Delta": "-1000000000000000000", - "executionPrice": "0.99700", - "feeGrowthGlobal0X128Delta": "88991975927793784300", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": -1, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests max full range liquidity at 1:1 price with default fee swap token0 for token1 to price 0.40000 1`] = ` -Object { - "amount0Before": "11505743598341114571255423385623647", - "amount0Delta": "6706554036096900675845906992672697", - "amount1Before": "11505743598341114571255423385506404", - "amount1Delta": "-4228872409409224753601131225116702", - "executionPrice": "0.63056", - "feeGrowthGlobal0X128Delta": "595039006852697512464428097924911949", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.40000", - "poolPriceBefore": "1.0000", - "tickAfter": -9164, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests max full range liquidity at 1:1 price with default fee swap token0 for token1 to price 2.5000 1`] = ` -Object { - "poolBalance0": "11505743598341114571255423385623647", - "poolBalance1": "11505743598341114571255423385506404", - "poolPriceBefore": "1.0000", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 0, -} -`; - -exports[`Pool swap tests max full range liquidity at 1:1 price with default fee swap token1 for exactly 0.0000000000000010000 token0 1`] = ` -Object { - "amount0Before": "11505743598341114571255423385623647", - "amount0Delta": "-1000", - "amount1Before": "11505743598341114571255423385506404", - "amount1Delta": "145660", - "executionPrice": "145.66", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "12924275", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 0, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests max full range liquidity at 1:1 price with default fee swap token1 for exactly 1.0000 token0 1`] = ` -Object { - "amount0Before": "11505743598341114571255423385623647", - "amount0Delta": "-1000000000000000000", - "amount1Before": "11505743598341114571255423385506404", - "amount1Delta": "1003009027081361094", - "executionPrice": "1.0030", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "88991975927793784300", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 0, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests max full range liquidity at 1:1 price with default fee swap token1 for exactly 1.0000 token0 to price 2.0000 1`] = ` -Object { - "amount0Before": "11505743598341114571255423385623647", - "amount0Delta": "-1000000000000000000", - "amount1Before": "11505743598341114571255423385506404", - "amount1Delta": "1003009027081361094", - "executionPrice": "1.0030", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "88991975927793784300", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 0, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests max full range liquidity at 1:1 price with default fee swap token1 for token0 to price 0.40000 1`] = ` -Object { - "poolBalance0": "11505743598341114571255423385623647", - "poolBalance1": "11505743598341114571255423385506404", - "poolPriceBefore": "1.0000", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 0, -} -`; - -exports[`Pool swap tests max full range liquidity at 1:1 price with default fee swap token1 for token0 to price 2.5000 1`] = ` -Object { - "amount0Before": "11505743598341114571255423385623647", - "amount0Delta": "-4228872409409224753601131224936259", - "amount1Before": "11505743598341114571255423385506404", - "amount1Delta": "6706554036096900675845906992220230", - "executionPrice": "1.5859", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "595039006852697512464428097884749099", - "poolPriceAfter": "2.5000", - "poolPriceBefore": "1.0000", - "tickAfter": 9163, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, 0 liquidity, all liquidity around current price swap exactly 0.0000000000000010000 token0 for token1 1`] = ` -Object { - "amount0Before": "1994009290088178439", - "amount0Delta": "1000", - "amount1Before": "1994009290088178439", - "amount1Delta": "-991", - "executionPrice": "0.99100", - "feeGrowthGlobal0X128Delta": "510423550381407695195", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.99402", - "poolPriceBefore": "1.0000", - "tickAfter": -61, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, 0 liquidity, all liquidity around current price swap exactly 0.0000000000000010000 token1 for token0 1`] = ` -Object { - "amount0Before": "1994009290088178439", - "amount0Delta": "-991", - "amount1Before": "1994009290088178439", - "amount1Delta": "1000", - "executionPrice": "1.0091", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "510423550381407695195", - "poolPriceAfter": "1.0060", - "poolPriceBefore": "1.0000", - "tickAfter": 60, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, 0 liquidity, all liquidity around current price swap exactly 1.0000 token0 for token1 1`] = ` -Object { - "amount0Before": "1994009290088178439", - "amount0Delta": "1000000000000000000", - "amount1Before": "1994009290088178439", - "amount1Delta": "-662011820624678025", - "executionPrice": "0.66201", - "feeGrowthGlobal0X128Delta": "510423550381407695195061911147652317", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.44355", - "poolPriceBefore": "1.0000", - "tickAfter": -8130, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, 0 liquidity, all liquidity around current price swap exactly 1.0000 token0 for token1 to price 0.50000 1`] = ` -Object { - "amount0Before": "1994009290088178439", - "amount0Delta": "824893095908431542", - "amount1Before": "1994009290088178439", - "amount1Delta": "-579795727715083389", - "executionPrice": "0.70287", - "feeGrowthGlobal0X128Delta": "421044862698692740725170743495410672", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.50000", - "poolPriceBefore": "1.0000", - "tickAfter": -6932, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, 0 liquidity, all liquidity around current price swap exactly 1.0000 token1 for token0 1`] = ` -Object { - "amount0Before": "1994009290088178439", - "amount0Delta": "-662011820624678025", - "amount1Before": "1994009290088178439", - "amount1Delta": "1000000000000000000", - "executionPrice": "1.5105", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "510423550381407695195061911147652317", - "poolPriceAfter": "2.2545", - "poolPriceBefore": "1.0000", - "tickAfter": 8129, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, 0 liquidity, all liquidity around current price swap exactly 1.0000 token1 for token0 to price 2.0000 1`] = ` -Object { - "amount0Before": "1994009290088178439", - "amount0Delta": "-579795727715083389", - "amount1Before": "1994009290088178439", - "amount1Delta": "824893095908431542", - "executionPrice": "1.4227", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "421044862698692740725170743495410672", - "poolPriceAfter": "2.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 6931, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, 0 liquidity, all liquidity around current price swap token0 for exactly 0.0000000000000010000 token1 1`] = ` -Object { - "amount0Before": "1994009290088178439", - "amount0Delta": "1011", - "amount1Before": "1994009290088178439", - "amount1Delta": "-1000", - "executionPrice": "0.98912", - "feeGrowthGlobal0X128Delta": "680564733841876926926", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.99402", - "poolPriceBefore": "1.0000", - "tickAfter": -61, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, 0 liquidity, all liquidity around current price swap token0 for exactly 1.0000 token1 1`] = ` -Object { - "amount0Before": "1994009290088178439", - "amount0Delta": "2024171064311638316", - "amount1Before": "1994009290088178439", - "amount1Delta": "-1000000000000000000", - "executionPrice": "0.49403", - "feeGrowthGlobal0X128Delta": "1033184581225259164735720748018047287", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.24701", - "poolPriceBefore": "1.0000", - "tickAfter": -13984, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, 0 liquidity, all liquidity around current price swap token0 for exactly 1.0000 token1 to price 0.50000 1`] = ` -Object { - "amount0Before": "1994009290088178439", - "amount0Delta": "824893095908431542", - "amount1Before": "1994009290088178439", - "amount1Delta": "-579795727715083389", - "executionPrice": "0.70287", - "feeGrowthGlobal0X128Delta": "421044862698692740725170743495410672", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.50000", - "poolPriceBefore": "1.0000", - "tickAfter": -6932, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, 0 liquidity, all liquidity around current price swap token0 for token1 to price 0.40000 1`] = ` -Object { - "amount0Before": "1994009290088178439", - "amount0Delta": "1159748196632793863", - "amount1Before": "1994009290088178439", - "amount1Delta": "-729098226020826705", - "executionPrice": "0.62867", - "feeGrowthGlobal0X128Delta": "591962792073745646583043420635066071", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.40000", - "poolPriceBefore": "1.0000", - "tickAfter": -9164, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, 0 liquidity, all liquidity around current price swap token0 for token1 to price 2.5000 1`] = ` -Object { - "poolBalance0": "1994009290088178439", - "poolBalance1": "1994009290088178439", - "poolPriceBefore": "1.0000", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, 0 liquidity, all liquidity around current price swap token1 for exactly 0.0000000000000010000 token0 1`] = ` -Object { - "amount0Before": "1994009290088178439", - "amount0Delta": "-1000", - "amount1Before": "1994009290088178439", - "amount1Delta": "1011", - "executionPrice": "1.0110", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "680564733841876926926", - "poolPriceAfter": "1.0060", - "poolPriceBefore": "1.0000", - "tickAfter": 60, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, 0 liquidity, all liquidity around current price swap token1 for exactly 1.0000 token0 1`] = ` -Object { - "amount0Before": "1994009290088178439", - "amount0Delta": "-1000000000000000000", - "amount1Before": "1994009290088178439", - "amount1Delta": "2024171064311638316", - "executionPrice": "2.0242", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "1033184581225259164735720748018047287", - "poolPriceAfter": "4.0484", - "poolPriceBefore": "1.0000", - "tickAfter": 13983, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, 0 liquidity, all liquidity around current price swap token1 for exactly 1.0000 token0 to price 2.0000 1`] = ` -Object { - "amount0Before": "1994009290088178439", - "amount0Delta": "-579795727715083389", - "amount1Before": "1994009290088178439", - "amount1Delta": "824893095908431542", - "executionPrice": "1.4227", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "421044862698692740725170743495410672", - "poolPriceAfter": "2.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 6931, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, 0 liquidity, all liquidity around current price swap token1 for token0 to price 0.40000 1`] = ` -Object { - "poolBalance0": "1994009290088178439", - "poolBalance1": "1994009290088178439", - "poolPriceBefore": "1.0000", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, 0 liquidity, all liquidity around current price swap token1 for token0 to price 2.5000 1`] = ` -Object { - "amount0Before": "1994009290088178439", - "amount0Delta": "-729098226020826705", - "amount1Before": "1994009290088178439", - "amount1Delta": "1159748196632793863", - "executionPrice": "1.5907", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "591962792073745646583043420635066071", - "poolPriceAfter": "2.5000", - "poolPriceBefore": "1.0000", - "tickAfter": 9163, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, 2e18 max range liquidity swap exactly 0.0000000000000010000 token0 for token1 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "1000", - "amount1Before": "2000000000000000000", - "amount1Delta": "-996", - "executionPrice": "0.99600", - "feeGrowthGlobal0X128Delta": "510423550381407695195", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": -1, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, 2e18 max range liquidity swap exactly 0.0000000000000010000 token1 for token0 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "-996", - "amount1Before": "2000000000000000000", - "amount1Delta": "1000", - "executionPrice": "1.0040", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "510423550381407695195", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 0, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, 2e18 max range liquidity swap exactly 1.0000 token0 for token1 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "1000000000000000000", - "amount1Before": "2000000000000000000", - "amount1Delta": "-665331998665331998", - "executionPrice": "0.66533", - "feeGrowthGlobal0X128Delta": "510423550381407695195061911147652317", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.44533", - "poolPriceBefore": "1.0000", - "tickAfter": -8090, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, 2e18 max range liquidity swap exactly 1.0000 token0 for token1 to price 0.50000 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "830919884399388263", - "amount1Before": "2000000000000000000", - "amount1Delta": "-585786437626904951", - "executionPrice": "0.70499", - "feeGrowthGlobal0X128Delta": "424121077477644648929101317621422688", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.50000", - "poolPriceBefore": "1.0000", - "tickAfter": -6932, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, 2e18 max range liquidity swap exactly 1.0000 token1 for token0 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "-665331998665331998", - "amount1Before": "2000000000000000000", - "amount1Delta": "1000000000000000000", - "executionPrice": "1.5030", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "510423550381407695195061911147652317", - "poolPriceAfter": "2.2455", - "poolPriceBefore": "1.0000", - "tickAfter": 8089, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, 2e18 max range liquidity swap exactly 1.0000 token1 for token0 to price 2.0000 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "-585786437626904951", - "amount1Before": "2000000000000000000", - "amount1Delta": "830919884399388263", - "executionPrice": "1.4185", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "424121077477644648929101317621422688", - "poolPriceAfter": "2.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 6931, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, 2e18 max range liquidity swap token0 for exactly 0.0000000000000010000 token1 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "1005", - "amount1Before": "2000000000000000000", - "amount1Delta": "-1000", - "executionPrice": "0.99502", - "feeGrowthGlobal0X128Delta": "680564733841876926926", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": -1, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, 2e18 max range liquidity swap token0 for exactly 1.0000 token1 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "2006018054162487463", - "amount1Before": "2000000000000000000", - "amount1Delta": "-1000000000000000000", - "executionPrice": "0.49850", - "feeGrowthGlobal0X128Delta": "1023918857334819954209013958517557896", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.25000", - "poolPriceBefore": "1.0000", - "tickAfter": -13864, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, 2e18 max range liquidity swap token0 for exactly 1.0000 token1 to price 0.50000 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "830919884399388263", - "amount1Before": "2000000000000000000", - "amount1Delta": "-585786437626904951", - "executionPrice": "0.70499", - "feeGrowthGlobal0X128Delta": "424121077477644648929101317621422688", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.50000", - "poolPriceBefore": "1.0000", - "tickAfter": -6932, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, 2e18 max range liquidity swap token0 for token1 to price 0.40000 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "1165774985123750584", - "amount1Before": "2000000000000000000", - "amount1Delta": "-735088935932648267", - "executionPrice": "0.63056", - "feeGrowthGlobal0X128Delta": "595039006852697554786973994761078087", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.40000", - "poolPriceBefore": "1.0000", - "tickAfter": -9164, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, 2e18 max range liquidity swap token0 for token1 to price 2.5000 1`] = ` -Object { - "poolBalance0": "2000000000000000000", - "poolBalance1": "2000000000000000000", - "poolPriceBefore": "1.0000", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, 2e18 max range liquidity swap token1 for exactly 0.0000000000000010000 token0 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "-1000", - "amount1Before": "2000000000000000000", - "amount1Delta": "1005", - "executionPrice": "1.0050", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "680564733841876926926", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 0, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, 2e18 max range liquidity swap token1 for exactly 1.0000 token0 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "-1000000000000000000", - "amount1Before": "2000000000000000000", - "amount1Delta": "2006018054162487463", - "executionPrice": "2.0060", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "1023918857334819954209013958517557896", - "poolPriceAfter": "4.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 13863, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, 2e18 max range liquidity swap token1 for exactly 1.0000 token0 to price 2.0000 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "-585786437626904951", - "amount1Before": "2000000000000000000", - "amount1Delta": "830919884399388263", - "executionPrice": "1.4185", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "424121077477644648929101317621422688", - "poolPriceAfter": "2.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 6931, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, 2e18 max range liquidity swap token1 for token0 to price 0.40000 1`] = ` -Object { - "poolBalance0": "2000000000000000000", - "poolBalance1": "2000000000000000000", - "poolPriceBefore": "1.0000", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, 2e18 max range liquidity swap token1 for token0 to price 2.5000 1`] = ` -Object { - "amount0Before": "2000000000000000000", - "amount0Delta": "-735088935932648267", - "amount1Before": "2000000000000000000", - "amount1Delta": "1165774985123750584", - "executionPrice": "1.5859", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "595039006852697554786973994761078087", - "poolPriceAfter": "2.5000", - "poolPriceBefore": "1.0000", - "tickAfter": 9163, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, additional liquidity around current price swap exactly 0.0000000000000010000 token0 for token1 1`] = ` -Object { - "amount0Before": "3994009290088178439", - "amount0Delta": "1000", - "amount1Before": "3994009290088178439", - "amount1Delta": "-996", - "executionPrice": "0.99600", - "feeGrowthGlobal0X128Delta": "510423550381407695195", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": -1, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, additional liquidity around current price swap exactly 0.0000000000000010000 token1 for token0 1`] = ` -Object { - "amount0Before": "3994009290088178439", - "amount0Delta": "-996", - "amount1Before": "3994009290088178439", - "amount1Delta": "1000", - "executionPrice": "1.0040", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "510423550381407695195", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 0, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, additional liquidity around current price swap exactly 1.0000 token0 for token1 1`] = ` -Object { - "amount0Before": "3994009290088178439", - "amount0Delta": "1000000000000000000", - "amount1Before": "3994009290088178439", - "amount1Delta": "-795933705287758544", - "executionPrice": "0.79593", - "feeGrowthGlobal0X128Delta": "256749882580179971840679703106063897", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.63923", - "poolPriceBefore": "1.0000", - "tickAfter": -4476, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, additional liquidity around current price swap exactly 1.0000 token0 for token1 to price 0.50000 1`] = ` -Object { - "amount0Before": "3994009290088178439", - "amount0Delta": "1000000000000000000", - "amount1Before": "3994009290088178439", - "amount1Delta": "-795933705287758544", - "executionPrice": "0.79593", - "feeGrowthGlobal0X128Delta": "256749882580179971840679703106063897", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.63923", - "poolPriceBefore": "1.0000", - "tickAfter": -4476, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, additional liquidity around current price swap exactly 1.0000 token1 for token0 1`] = ` -Object { - "amount0Before": "3994009290088178439", - "amount0Delta": "-795933705287758544", - "amount1Before": "3994009290088178439", - "amount1Delta": "1000000000000000000", - "executionPrice": "1.2564", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "256749882580179971840679703106063897", - "poolPriceAfter": "1.5644", - "poolPriceBefore": "1.0000", - "tickAfter": 4475, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, additional liquidity around current price swap exactly 1.0000 token1 for token0 to price 2.0000 1`] = ` -Object { - "amount0Before": "3994009290088178439", - "amount0Delta": "-795933705287758544", - "amount1Before": "3994009290088178439", - "amount1Delta": "1000000000000000000", - "executionPrice": "1.2564", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "256749882580179971840679703106063897", - "poolPriceAfter": "1.5644", - "poolPriceBefore": "1.0000", - "tickAfter": 4475, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, additional liquidity around current price swap token0 for exactly 0.0000000000000010000 token1 1`] = ` -Object { - "amount0Before": "3994009290088178439", - "amount0Delta": "1005", - "amount1Before": "3994009290088178439", - "amount1Delta": "-1000", - "executionPrice": "0.99502", - "feeGrowthGlobal0X128Delta": "680564733841876926926", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": -1, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, additional liquidity around current price swap token0 for exactly 1.0000 token1 1`] = ` -Object { - "amount0Before": "3994009290088178439", - "amount0Delta": "1342022152495072924", - "amount1Before": "3994009290088178439", - "amount1Delta": "-1000000000000000000", - "executionPrice": "0.74514", - "feeGrowthGlobal0X128Delta": "344037963272993171369654596359692757", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.56026", - "poolPriceBefore": "1.0000", - "tickAfter": -5794, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, additional liquidity around current price swap token0 for exactly 1.0000 token1 to price 0.50000 1`] = ` -Object { - "amount0Before": "3994009290088178439", - "amount0Delta": "1342022152495072924", - "amount1Before": "3994009290088178439", - "amount1Delta": "-1000000000000000000", - "executionPrice": "0.74514", - "feeGrowthGlobal0X128Delta": "344037963272993171369654596359692757", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.56026", - "poolPriceBefore": "1.0000", - "tickAfter": -5794, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, additional liquidity around current price swap token0 for token1 to price 0.40000 1`] = ` -Object { - "amount0Before": "3994009290088178439", - "amount0Delta": "2325523181756544449", - "amount1Before": "3994009290088178439", - "amount1Delta": "-1464187161953474971", - "executionPrice": "0.62962", - "feeGrowthGlobal0X128Delta": "595039006852697724928157455230309818", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.40000", - "poolPriceBefore": "1.0000", - "tickAfter": -9164, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, additional liquidity around current price swap token0 for token1 to price 2.5000 1`] = ` -Object { - "poolBalance0": "3994009290088178439", - "poolBalance1": "3994009290088178439", - "poolPriceBefore": "1.0000", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, additional liquidity around current price swap token1 for exactly 0.0000000000000010000 token0 1`] = ` -Object { - "amount0Before": "3994009290088178439", - "amount0Delta": "-1000", - "amount1Before": "3994009290088178439", - "amount1Delta": "1005", - "executionPrice": "1.0050", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "680564733841876926926", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 0, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, additional liquidity around current price swap token1 for exactly 1.0000 token0 1`] = ` -Object { - "amount0Before": "3994009290088178439", - "amount0Delta": "-1000000000000000000", - "amount1Before": "3994009290088178439", - "amount1Delta": "1342022152495072924", - "executionPrice": "1.3420", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "344037963272993171369654596359692757", - "poolPriceAfter": "1.7849", - "poolPriceBefore": "1.0000", - "tickAfter": 5793, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, additional liquidity around current price swap token1 for exactly 1.0000 token0 to price 2.0000 1`] = ` -Object { - "amount0Before": "3994009290088178439", - "amount0Delta": "-1000000000000000000", - "amount1Before": "3994009290088178439", - "amount1Delta": "1342022152495072924", - "executionPrice": "1.3420", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "344037963272993171369654596359692757", - "poolPriceAfter": "1.7849", - "poolPriceBefore": "1.0000", - "tickAfter": 5793, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, additional liquidity around current price swap token1 for token0 to price 0.40000 1`] = ` -Object { - "poolBalance0": "3994009290088178439", - "poolBalance1": "3994009290088178439", - "poolPriceBefore": "1.0000", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:1 price, additional liquidity around current price swap token1 for token0 to price 2.5000 1`] = ` -Object { - "amount0Before": "3994009290088178439", - "amount0Delta": "-1464187161953474971", - "amount1Before": "3994009290088178439", - "amount1Delta": "2325523181756544449", - "executionPrice": "1.5883", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "595039006852697724928157455230309818", - "poolPriceAfter": "2.5000", - "poolPriceBefore": "1.0000", - "tickAfter": 9163, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, 1:10 price, 2e18 max range liquidity swap exactly 0.0000000000000010000 token0 for token1 1`] = ` -Object { - "amount0Before": "6324555320336758664", - "amount0Delta": "1000", - "amount1Before": "632455532033675867", - "amount1Delta": "-99", - "executionPrice": "0.099000", - "feeGrowthGlobal0X128Delta": "510423550381407695195", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.10000", - "poolPriceBefore": "0.10000", - "tickAfter": -23028, - "tickBefore": -23028, -} -`; - -exports[`Pool swap tests medium fee, 1:10 price, 2e18 max range liquidity swap exactly 0.0000000000000010000 token1 for token0 1`] = ` -Object { - "amount0Before": "6324555320336758664", - "amount0Delta": "-9969", - "amount1Before": "632455532033675867", - "amount1Delta": "1000", - "executionPrice": "0.10031", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "510423550381407695195", - "poolPriceAfter": "0.10000", - "poolPriceBefore": "0.10000", - "tickAfter": -23028, - "tickBefore": -23028, -} -`; - -exports[`Pool swap tests medium fee, 1:10 price, 2e18 max range liquidity swap exactly 1.0000 token0 for token1 1`] = ` -Object { - "amount0Before": "6324555320336758664", - "amount0Delta": "1000000000000000000", - "amount1Before": "632455532033675867", - "amount1Delta": "-86123526743846551", - "executionPrice": "0.086124", - "feeGrowthGlobal0X128Delta": "510423550381407695195061911147652317", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.074620", - "poolPriceBefore": "0.10000", - "tickAfter": -25955, - "tickBefore": -23028, -} -`; - -exports[`Pool swap tests medium fee, 1:10 price, 2e18 max range liquidity swap exactly 1.0000 token0 for token1 to price 0.50000 1`] = ` -Object { - "poolBalance0": "6324555320336758664", - "poolBalance1": "632455532033675867", - "poolPriceBefore": "0.10000", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": -23028, -} -`; - -exports[`Pool swap tests medium fee, 1:10 price, 2e18 max range liquidity swap exactly 1.0000 token1 for token0 1`] = ` -Object { - "amount0Before": "6324555320336758664", - "amount0Delta": "-3869747612262812753", - "amount1Before": "632455532033675867", - "amount1Delta": "1000000000000000000", - "executionPrice": "0.25841", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "510423550381407865336245371616884047", - "poolPriceAfter": "0.66378", - "poolPriceBefore": "0.10000", - "tickAfter": -4099, - "tickBefore": -23028, -} -`; - -exports[`Pool swap tests medium fee, 1:10 price, 2e18 max range liquidity swap exactly 1.0000 token1 for token0 to price 2.0000 1`] = ` -Object { - "amount0Before": "6324555320336758664", - "amount0Delta": "-3869747612262812753", - "amount1Before": "632455532033675867", - "amount1Delta": "1000000000000000000", - "executionPrice": "0.25841", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "510423550381407865336245371616884047", - "poolPriceAfter": "0.66378", - "poolPriceBefore": "0.10000", - "tickAfter": -4099, - "tickBefore": -23028, -} -`; - -exports[`Pool swap tests medium fee, 1:10 price, 2e18 max range liquidity swap token0 for exactly 0.0000000000000010000 token1 1`] = ` -Object { - "amount0Before": "6324555320336758664", - "amount0Delta": "10032", - "amount1Before": "632455532033675867", - "amount1Delta": "-1000", - "executionPrice": "0.099681", - "feeGrowthGlobal0X128Delta": "5274376687274546183682", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.10000", - "poolPriceBefore": "0.10000", - "tickAfter": -23028, - "tickBefore": -23028, -} -`; - -exports[`Pool swap tests medium fee, 1:10 price, 2e18 max range liquidity swap token0 for exactly 1.0000 token1 1`] = ` -Object { - "amount0Before": "6324555320336758664", - "amount0Delta": "36907032419362389223785084665766560335", - "amount1Before": "632455532033675867", - "amount1Delta": "-632455532033675838", - "executionPrice": "0.000000000000000000017136", - "feeGrowthGlobal0X128Delta": "18838218521532665615644565874197034349094564536667752274", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.0000000000000000000000000000000000000029390", - "poolPriceBefore": "0.10000", - "tickAfter": -887272, - "tickBefore": -23028, -} -`; - -exports[`Pool swap tests medium fee, 1:10 price, 2e18 max range liquidity swap token0 for exactly 1.0000 token1 to price 0.50000 1`] = ` -Object { - "poolBalance0": "6324555320336758664", - "poolBalance1": "632455532033675867", - "poolPriceBefore": "0.10000", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": -23028, -} -`; - -exports[`Pool swap tests medium fee, 1:10 price, 2e18 max range liquidity swap token0 for token1 to price 0.40000 1`] = ` -Object { - "poolBalance0": "6324555320336758664", - "poolBalance1": "632455532033675867", - "poolPriceBefore": "0.10000", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": -23028, -} -`; - -exports[`Pool swap tests medium fee, 1:10 price, 2e18 max range liquidity swap token0 for token1 to price 2.5000 1`] = ` -Object { - "poolBalance0": "6324555320336758664", - "poolBalance1": "632455532033675867", - "poolPriceBefore": "0.10000", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": -23028, -} -`; - -exports[`Pool swap tests medium fee, 1:10 price, 2e18 max range liquidity swap token1 for exactly 0.0000000000000010000 token0 1`] = ` -Object { - "amount0Before": "6324555320336758664", - "amount0Delta": "-1000", - "amount1Before": "632455532033675867", - "amount1Delta": "102", - "executionPrice": "0.10200", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "170141183460469231731", - "poolPriceAfter": "0.10000", - "poolPriceBefore": "0.10000", - "tickAfter": -23028, - "tickBefore": -23028, -} -`; - -exports[`Pool swap tests medium fee, 1:10 price, 2e18 max range liquidity swap token1 for exactly 1.0000 token0 1`] = ` -Object { - "amount0Before": "6324555320336758664", - "amount0Delta": "-1000000000000000000", - "amount1Before": "632455532033675867", - "amount1Delta": "119138326055954425", - "executionPrice": "0.11914", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "60811007371978153949466126675899993", - "poolPriceAfter": "0.14109", - "poolPriceBefore": "0.10000", - "tickAfter": -19585, - "tickBefore": -23028, -} -`; - -exports[`Pool swap tests medium fee, 1:10 price, 2e18 max range liquidity swap token1 for exactly 1.0000 token0 to price 2.0000 1`] = ` -Object { - "amount0Before": "6324555320336758664", - "amount0Delta": "-1000000000000000000", - "amount1Before": "632455532033675867", - "amount1Delta": "119138326055954425", - "executionPrice": "0.11914", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "60811007371978153949466126675899993", - "poolPriceAfter": "0.14109", - "poolPriceBefore": "0.10000", - "tickAfter": -19585, - "tickBefore": -23028, -} -`; - -exports[`Pool swap tests medium fee, 1:10 price, 2e18 max range liquidity swap token1 for token0 to price 0.40000 1`] = ` -Object { - "amount0Before": "6324555320336758664", - "amount0Delta": "-3162277660168379331", - "amount1Before": "632455532033675867", - "amount1Delta": "634358607857247611", - "executionPrice": "0.20060", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "323791572837503501799197590655727195", - "poolPriceAfter": "0.40000", - "poolPriceBefore": "0.10000", - "tickAfter": -9164, - "tickBefore": -23028, -} -`; - -exports[`Pool swap tests medium fee, 1:10 price, 2e18 max range liquidity swap token1 for token0 to price 2.5000 1`] = ` -Object { - "amount0Before": "6324555320336758664", - "amount0Delta": "-5059644256269406930", - "amount1Before": "632455532033675867", - "amount1Delta": "2537434431428990440", - "executionPrice": "0.50150", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "1295166291350014177337973823092140516", - "poolPriceAfter": "2.5000", - "poolPriceBefore": "0.10000", - "tickAfter": 9163, - "tickBefore": -23028, -} -`; - -exports[`Pool swap tests medium fee, 10:1 price, 2e18 max range liquidity swap exactly 0.0000000000000010000 token0 for token1 1`] = ` -Object { - "amount0Before": "632455532033675867", - "amount0Delta": "1000", - "amount1Before": "6324555320336758664", - "amount1Delta": "-9969", - "executionPrice": "9.9690", - "feeGrowthGlobal0X128Delta": "510423550381407695195", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "10.000", - "poolPriceBefore": "10.000", - "tickAfter": 23027, - "tickBefore": 23027, -} -`; - -exports[`Pool swap tests medium fee, 10:1 price, 2e18 max range liquidity swap exactly 0.0000000000000010000 token1 for token0 1`] = ` -Object { - "amount0Before": "632455532033675867", - "amount0Delta": "-99", - "amount1Before": "6324555320336758664", - "amount1Delta": "1000", - "executionPrice": "10.101", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "510423550381407695195", - "poolPriceAfter": "10.000", - "poolPriceBefore": "10.000", - "tickAfter": 23027, - "tickBefore": 23027, -} -`; - -exports[`Pool swap tests medium fee, 10:1 price, 2e18 max range liquidity swap exactly 1.0000 token0 for token1 1`] = ` -Object { - "amount0Before": "632455532033675867", - "amount0Delta": "1000000000000000000", - "amount1Before": "6324555320336758664", - "amount1Delta": "-3869747612262812754", - "executionPrice": "3.8697", - "feeGrowthGlobal0X128Delta": "510423550381407865336245371616884048", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "1.5065", - "poolPriceBefore": "10.000", - "tickAfter": 4098, - "tickBefore": 23027, -} -`; - -exports[`Pool swap tests medium fee, 10:1 price, 2e18 max range liquidity swap exactly 1.0000 token0 for token1 to price 0.50000 1`] = ` -Object { - "amount0Before": "632455532033675867", - "amount0Delta": "1000000000000000000", - "amount1Before": "6324555320336758664", - "amount1Delta": "-3869747612262812754", - "executionPrice": "3.8697", - "feeGrowthGlobal0X128Delta": "510423550381407865336245371616884048", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "1.5065", - "poolPriceBefore": "10.000", - "tickAfter": 4098, - "tickBefore": 23027, -} -`; - -exports[`Pool swap tests medium fee, 10:1 price, 2e18 max range liquidity swap exactly 1.0000 token1 for token0 1`] = ` -Object { - "amount0Before": "632455532033675867", - "amount0Delta": "-86123526743846551", - "amount1Before": "6324555320336758664", - "amount1Delta": "1000000000000000000", - "executionPrice": "11.611", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "510423550381407695195061911147652317", - "poolPriceAfter": "13.401", - "poolPriceBefore": "10.000", - "tickAfter": 25954, - "tickBefore": 23027, -} -`; - -exports[`Pool swap tests medium fee, 10:1 price, 2e18 max range liquidity swap exactly 1.0000 token1 for token0 to price 2.0000 1`] = ` -Object { - "poolBalance0": "632455532033675867", - "poolBalance1": "6324555320336758664", - "poolPriceBefore": "10.000", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 23027, -} -`; - -exports[`Pool swap tests medium fee, 10:1 price, 2e18 max range liquidity swap token0 for exactly 0.0000000000000010000 token1 1`] = ` -Object { - "amount0Before": "632455532033675867", - "amount0Delta": "102", - "amount1Before": "6324555320336758664", - "amount1Delta": "-1000", - "executionPrice": "9.8039", - "feeGrowthGlobal0X128Delta": "170141183460469231731", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "10.000", - "poolPriceBefore": "10.000", - "tickAfter": 23027, - "tickBefore": 23027, -} -`; - -exports[`Pool swap tests medium fee, 10:1 price, 2e18 max range liquidity swap token0 for exactly 1.0000 token1 1`] = ` -Object { - "amount0Before": "632455532033675867", - "amount0Delta": "119138326055954425", - "amount1Before": "6324555320336758664", - "amount1Delta": "-1000000000000000000", - "executionPrice": "8.3936", - "feeGrowthGlobal0X128Delta": "60811007371978153949466126675899993", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "7.0877", - "poolPriceBefore": "10.000", - "tickAfter": 19584, - "tickBefore": 23027, -} -`; - -exports[`Pool swap tests medium fee, 10:1 price, 2e18 max range liquidity swap token0 for exactly 1.0000 token1 to price 0.50000 1`] = ` -Object { - "amount0Before": "632455532033675867", - "amount0Delta": "119138326055954425", - "amount1Before": "6324555320336758664", - "amount1Delta": "-1000000000000000000", - "executionPrice": "8.3936", - "feeGrowthGlobal0X128Delta": "60811007371978153949466126675899993", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "7.0877", - "poolPriceBefore": "10.000", - "tickAfter": 19584, - "tickBefore": 23027, -} -`; - -exports[`Pool swap tests medium fee, 10:1 price, 2e18 max range liquidity swap token0 for token1 to price 0.40000 1`] = ` -Object { - "amount0Before": "632455532033675867", - "amount0Delta": "2537434431428990438", - "amount1Before": "6324555320336758664", - "amount1Delta": "-5059644256269406930", - "executionPrice": "1.9940", - "feeGrowthGlobal0X128Delta": "1295166291350014007196790362622908786", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.40000", - "poolPriceBefore": "10.000", - "tickAfter": -9164, - "tickBefore": 23027, -} -`; - -exports[`Pool swap tests medium fee, 10:1 price, 2e18 max range liquidity swap token0 for token1 to price 2.5000 1`] = ` -Object { - "amount0Before": "632455532033675867", - "amount0Delta": "634358607857247610", - "amount1Before": "6324555320336758664", - "amount1Delta": "-3162277660168379331", - "executionPrice": "4.9850", - "feeGrowthGlobal0X128Delta": "323791572837503501799197590655727196", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "2.5000", - "poolPriceBefore": "10.000", - "tickAfter": 9163, - "tickBefore": 23027, -} -`; - -exports[`Pool swap tests medium fee, 10:1 price, 2e18 max range liquidity swap token1 for exactly 0.0000000000000010000 token0 1`] = ` -Object { - "amount0Before": "632455532033675867", - "amount0Delta": "-1000", - "amount1Before": "6324555320336758664", - "amount1Delta": "10032", - "executionPrice": "10.032", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "5274376687274546183682", - "poolPriceAfter": "10.000", - "poolPriceBefore": "10.000", - "tickAfter": 23027, - "tickBefore": 23027, -} -`; - -exports[`Pool swap tests medium fee, 10:1 price, 2e18 max range liquidity swap token1 for exactly 1.0000 token0 1`] = ` -Object { - "amount0Before": "632455532033675867", - "amount0Delta": "-632455532033675838", - "amount1Before": "6324555320336758664", - "amount1Delta": "36907032426281581270030941278837275671", - "executionPrice": "5.8355e+19", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "18838218525064384185660173270402201838945341643205005201", - "poolPriceAfter": "3.4026e+38", - "poolPriceBefore": "10.000", - "tickAfter": 887271, - "tickBefore": 23027, -} -`; - -exports[`Pool swap tests medium fee, 10:1 price, 2e18 max range liquidity swap token1 for exactly 1.0000 token0 to price 2.0000 1`] = ` -Object { - "poolBalance0": "632455532033675867", - "poolBalance1": "6324555320336758664", - "poolPriceBefore": "10.000", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 23027, -} -`; - -exports[`Pool swap tests medium fee, 10:1 price, 2e18 max range liquidity swap token1 for token0 to price 0.40000 1`] = ` -Object { - "poolBalance0": "632455532033675867", - "poolBalance1": "6324555320336758664", - "poolPriceBefore": "10.000", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 23027, -} -`; - -exports[`Pool swap tests medium fee, 10:1 price, 2e18 max range liquidity swap token1 for token0 to price 2.5000 1`] = ` -Object { - "poolBalance0": "632455532033675867", - "poolBalance1": "6324555320336758664", - "poolPriceBefore": "10.000", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 23027, -} -`; - -exports[`Pool swap tests medium fee, token0 liquidity only swap exactly 0.0000000000000010000 token0 for token1 1`] = ` -Object { - "amount0Before": "1995041008271423675", - "amount0Delta": "0", - "amount1Before": "0", - "amount1Delta": "0", - "executionPrice": "NaN", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.0000000000000000000000000000000000000029390", - "poolPriceBefore": "1.0000", - "tickAfter": -887272, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, token0 liquidity only swap exactly 0.0000000000000010000 token1 for token0 1`] = ` -Object { - "amount0Before": "1995041008271423675", - "amount0Delta": "-996", - "amount1Before": "0", - "amount1Delta": "1000", - "executionPrice": "1.0040", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "510423550381407695195", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 0, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, token0 liquidity only swap exactly 1.0000 token0 for token1 1`] = ` -Object { - "amount0Before": "1995041008271423675", - "amount0Delta": "0", - "amount1Before": "0", - "amount1Delta": "0", - "executionPrice": "NaN", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.0000000000000000000000000000000000000029390", - "poolPriceBefore": "1.0000", - "tickAfter": -887272, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, token0 liquidity only swap exactly 1.0000 token0 for token1 to price 0.50000 1`] = ` -Object { - "amount0Before": "1995041008271423675", - "amount0Delta": "0", - "amount1Before": "0", - "amount1Delta": "0", - "executionPrice": "NaN", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.50000", - "poolPriceBefore": "1.0000", - "tickAfter": -6932, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, token0 liquidity only swap exactly 1.0000 token1 for token0 1`] = ` -Object { - "amount0Before": "1995041008271423675", - "amount0Delta": "-665331998665331998", - "amount1Before": "0", - "amount1Delta": "1000000000000000000", - "executionPrice": "1.5030", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "510423550381407695195061911147652317", - "poolPriceAfter": "2.2455", - "poolPriceBefore": "1.0000", - "tickAfter": 8089, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, token0 liquidity only swap exactly 1.0000 token1 for token0 to price 2.0000 1`] = ` -Object { - "amount0Before": "1995041008271423675", - "amount0Delta": "-585786437626904951", - "amount1Before": "0", - "amount1Delta": "830919884399388263", - "executionPrice": "1.4185", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "424121077477644648929101317621422688", - "poolPriceAfter": "2.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 6931, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, token0 liquidity only swap token0 for exactly 0.0000000000000010000 token1 1`] = ` -Object { - "amount0Before": "1995041008271423675", - "amount0Delta": "0", - "amount1Before": "0", - "amount1Delta": "0", - "executionPrice": "NaN", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.0000000000000000000000000000000000000029390", - "poolPriceBefore": "1.0000", - "tickAfter": -887272, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, token0 liquidity only swap token0 for exactly 1.0000 token1 1`] = ` -Object { - "amount0Before": "1995041008271423675", - "amount0Delta": "0", - "amount1Before": "0", - "amount1Delta": "0", - "executionPrice": "NaN", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.0000000000000000000000000000000000000029390", - "poolPriceBefore": "1.0000", - "tickAfter": -887272, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, token0 liquidity only swap token0 for exactly 1.0000 token1 to price 0.50000 1`] = ` -Object { - "amount0Before": "1995041008271423675", - "amount0Delta": "0", - "amount1Before": "0", - "amount1Delta": "0", - "executionPrice": "NaN", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.50000", - "poolPriceBefore": "1.0000", - "tickAfter": -6932, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, token0 liquidity only swap token0 for token1 to price 0.40000 1`] = ` -Object { - "amount0Before": "1995041008271423675", - "amount0Delta": "0", - "amount1Before": "0", - "amount1Delta": "0", - "executionPrice": "NaN", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.40000", - "poolPriceBefore": "1.0000", - "tickAfter": -9164, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, token0 liquidity only swap token0 for token1 to price 2.5000 1`] = ` -Object { - "poolBalance0": "1995041008271423675", - "poolBalance1": "0", - "poolPriceBefore": "1.0000", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, token0 liquidity only swap token1 for exactly 0.0000000000000010000 token0 1`] = ` -Object { - "amount0Before": "1995041008271423675", - "amount0Delta": "-1000", - "amount1Before": "0", - "amount1Delta": "1005", - "executionPrice": "1.0050", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "680564733841876926926", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 0, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, token0 liquidity only swap token1 for exactly 1.0000 token0 1`] = ` -Object { - "amount0Before": "1995041008271423675", - "amount0Delta": "-1000000000000000000", - "amount1Before": "0", - "amount1Delta": "2006018054162487463", - "executionPrice": "2.0060", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "1023918857334819954209013958517557896", - "poolPriceAfter": "4.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 13863, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, token0 liquidity only swap token1 for exactly 1.0000 token0 to price 2.0000 1`] = ` -Object { - "amount0Before": "1995041008271423675", - "amount0Delta": "-585786437626904951", - "amount1Before": "0", - "amount1Delta": "830919884399388263", - "executionPrice": "1.4185", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "424121077477644648929101317621422688", - "poolPriceAfter": "2.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 6931, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, token0 liquidity only swap token1 for token0 to price 0.40000 1`] = ` -Object { - "poolBalance0": "1995041008271423675", - "poolBalance1": "0", - "poolPriceBefore": "1.0000", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, token0 liquidity only swap token1 for token0 to price 2.5000 1`] = ` -Object { - "amount0Before": "1995041008271423675", - "amount0Delta": "-735088935932648267", - "amount1Before": "0", - "amount1Delta": "1165774985123750584", - "executionPrice": "1.5859", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "595039006852697554786973994761078087", - "poolPriceAfter": "2.5000", - "poolPriceBefore": "1.0000", - "tickAfter": 9163, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, token1 liquidity only swap exactly 0.0000000000000010000 token0 for token1 1`] = ` -Object { - "amount0Before": "0", - "amount0Delta": "1000", - "amount1Before": "1995041008271423675", - "amount1Delta": "-996", - "executionPrice": "0.99600", - "feeGrowthGlobal0X128Delta": "510423550381407695195", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": -1, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, token1 liquidity only swap exactly 0.0000000000000010000 token1 for token0 1`] = ` -Object { - "amount0Before": "0", - "amount0Delta": "0", - "amount1Before": "1995041008271423675", - "amount1Delta": "0", - "executionPrice": "NaN", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "3.4026e+38", - "poolPriceBefore": "1.0000", - "tickAfter": 887271, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, token1 liquidity only swap exactly 1.0000 token0 for token1 1`] = ` -Object { - "amount0Before": "0", - "amount0Delta": "1000000000000000000", - "amount1Before": "1995041008271423675", - "amount1Delta": "-665331998665331998", - "executionPrice": "0.66533", - "feeGrowthGlobal0X128Delta": "510423550381407695195061911147652317", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.44533", - "poolPriceBefore": "1.0000", - "tickAfter": -8090, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, token1 liquidity only swap exactly 1.0000 token0 for token1 to price 0.50000 1`] = ` -Object { - "amount0Before": "0", - "amount0Delta": "830919884399388263", - "amount1Before": "1995041008271423675", - "amount1Delta": "-585786437626904951", - "executionPrice": "0.70499", - "feeGrowthGlobal0X128Delta": "424121077477644648929101317621422688", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.50000", - "poolPriceBefore": "1.0000", - "tickAfter": -6932, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, token1 liquidity only swap exactly 1.0000 token1 for token0 1`] = ` -Object { - "amount0Before": "0", - "amount0Delta": "0", - "amount1Before": "1995041008271423675", - "amount1Delta": "0", - "executionPrice": "NaN", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "3.4026e+38", - "poolPriceBefore": "1.0000", - "tickAfter": 887271, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, token1 liquidity only swap exactly 1.0000 token1 for token0 to price 2.0000 1`] = ` -Object { - "amount0Before": "0", - "amount0Delta": "0", - "amount1Before": "1995041008271423675", - "amount1Delta": "0", - "executionPrice": "NaN", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "2.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 6931, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, token1 liquidity only swap token0 for exactly 0.0000000000000010000 token1 1`] = ` -Object { - "amount0Before": "0", - "amount0Delta": "1005", - "amount1Before": "1995041008271423675", - "amount1Delta": "-1000", - "executionPrice": "0.99502", - "feeGrowthGlobal0X128Delta": "680564733841876926926", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "1.0000", - "poolPriceBefore": "1.0000", - "tickAfter": -1, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, token1 liquidity only swap token0 for exactly 1.0000 token1 1`] = ` -Object { - "amount0Before": "0", - "amount0Delta": "2006018054162487463", - "amount1Before": "1995041008271423675", - "amount1Delta": "-1000000000000000000", - "executionPrice": "0.49850", - "feeGrowthGlobal0X128Delta": "1023918857334819954209013958517557896", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.25000", - "poolPriceBefore": "1.0000", - "tickAfter": -13864, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, token1 liquidity only swap token0 for exactly 1.0000 token1 to price 0.50000 1`] = ` -Object { - "amount0Before": "0", - "amount0Delta": "830919884399388263", - "amount1Before": "1995041008271423675", - "amount1Delta": "-585786437626904951", - "executionPrice": "0.70499", - "feeGrowthGlobal0X128Delta": "424121077477644648929101317621422688", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.50000", - "poolPriceBefore": "1.0000", - "tickAfter": -6932, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, token1 liquidity only swap token0 for token1 to price 0.40000 1`] = ` -Object { - "amount0Before": "0", - "amount0Delta": "1165774985123750584", - "amount1Before": "1995041008271423675", - "amount1Delta": "-735088935932648267", - "executionPrice": "0.63056", - "feeGrowthGlobal0X128Delta": "595039006852697554786973994761078087", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "0.40000", - "poolPriceBefore": "1.0000", - "tickAfter": -9164, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, token1 liquidity only swap token0 for token1 to price 2.5000 1`] = ` -Object { - "poolBalance0": "0", - "poolBalance1": "1995041008271423675", - "poolPriceBefore": "1.0000", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, token1 liquidity only swap token1 for exactly 0.0000000000000010000 token0 1`] = ` -Object { - "amount0Before": "0", - "amount0Delta": "0", - "amount1Before": "1995041008271423675", - "amount1Delta": "0", - "executionPrice": "NaN", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "3.4026e+38", - "poolPriceBefore": "1.0000", - "tickAfter": 887271, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, token1 liquidity only swap token1 for exactly 1.0000 token0 1`] = ` -Object { - "amount0Before": "0", - "amount0Delta": "0", - "amount1Before": "1995041008271423675", - "amount1Delta": "0", - "executionPrice": "NaN", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "3.4026e+38", - "poolPriceBefore": "1.0000", - "tickAfter": 887271, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, token1 liquidity only swap token1 for exactly 1.0000 token0 to price 2.0000 1`] = ` -Object { - "amount0Before": "0", - "amount0Delta": "0", - "amount1Before": "1995041008271423675", - "amount1Delta": "0", - "executionPrice": "NaN", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "2.0000", - "poolPriceBefore": "1.0000", - "tickAfter": 6931, - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, token1 liquidity only swap token1 for token0 to price 0.40000 1`] = ` -Object { - "poolBalance0": "0", - "poolBalance1": "1995041008271423675", - "poolPriceBefore": "1.0000", - "swapError": "VM Exception while processing transaction: reverted with reason string 'SPL'", - "tickBefore": 0, -} -`; - -exports[`Pool swap tests medium fee, token1 liquidity only swap token1 for token0 to price 2.5000 1`] = ` -Object { - "amount0Before": "0", - "amount0Delta": "0", - "amount1Before": "1995041008271423675", - "amount1Delta": "0", - "executionPrice": "NaN", - "feeGrowthGlobal0X128Delta": "0", - "feeGrowthGlobal1X128Delta": "0", - "poolPriceAfter": "2.5000", - "poolPriceBefore": "1.0000", - "tickAfter": 9163, - "tickBefore": 0, -} -`; diff --git a/test/__snapshots__/PoolManager.gas.spec.ts.snap b/test/__snapshots__/PoolManager.gas.spec.ts.snap new file mode 100644 index 000000000..14481a20f --- /dev/null +++ b/test/__snapshots__/PoolManager.gas.spec.ts.snap @@ -0,0 +1,113 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`PoolManager gas tests fee is off #increaseObservationCardinalityNext grow by 1 slot 1`] = `50918`; + +exports[`PoolManager gas tests fee is off #increaseObservationCardinalityNext no op 1`] = `25849`; + +exports[`PoolManager gas tests fee is off #mint above current price add to position after some time passes 1`] = `169077`; + +exports[`PoolManager gas tests fee is off #mint above current price add to position existing 1`] = `169077`; + +exports[`PoolManager gas tests fee is off #mint above current price new position mint first in range 1`] = `264052`; + +exports[`PoolManager gas tests fee is off #mint above current price second position in same range 1`] = `182757`; + +exports[`PoolManager gas tests fee is off #mint around current price add to position after some time passes 1`] = `242662`; + +exports[`PoolManager gas tests fee is off #mint around current price add to position existing 1`] = `235368`; + +exports[`PoolManager gas tests fee is off #mint around current price new position mint first in range 1`] = `387583`; + +exports[`PoolManager gas tests fee is off #mint around current price second position in same range 1`] = `249048`; + +exports[`PoolManager gas tests fee is off #mint below current price add to position after some time passes 1`] = `169757`; + +exports[`PoolManager gas tests fee is off #mint below current price add to position existing 1`] = `169757`; + +exports[`PoolManager gas tests fee is off #mint below current price new position mint first in range 1`] = `332380`; + +exports[`PoolManager gas tests fee is off #mint below current price second position in same range 1`] = `183437`; + +exports[`PoolManager gas tests fee is off #snapshotCumulativesInside tick above 1`] = `34301`; + +exports[`PoolManager gas tests fee is off #snapshotCumulativesInside tick below 1`] = `34260`; + +exports[`PoolManager gas tests fee is off #snapshotCumulativesInside tick inside 1`] = `39533`; + +exports[`PoolManager gas tests fee is off #swapExact0For1 first swap in block moves tick, no initialized crossings 1`] = `218028`; + +exports[`PoolManager gas tests fee is off #swapExact0For1 first swap in block with no tick movement 1`] = `205218`; + +exports[`PoolManager gas tests fee is off #swapExact0For1 first swap in block, large swap crossing a single initialized tick 1`] = `242236`; + +exports[`PoolManager gas tests fee is off #swapExact0For1 first swap in block, large swap crossing several initialized ticks 1`] = `297330`; + +exports[`PoolManager gas tests fee is off #swapExact0For1 first swap in block, large swap, no initialized crossings 1`] = `230106`; + +exports[`PoolManager gas tests fee is off #swapExact0For1 large swap crossing several initialized ticks after some time passes 1`] = `297330`; + +exports[`PoolManager gas tests fee is off #swapExact0For1 large swap crossing several initialized ticks second time after some time passes 1`] = `297330`; + +exports[`PoolManager gas tests fee is off #swapExact0For1 second swap in block moves tick, no initialized crossings 1`] = `218028`; + +exports[`PoolManager gas tests fee is off #swapExact0For1 second swap in block with no tick movement 1`] = `205307`; + +exports[`PoolManager gas tests fee is off #swapExact0For1 second swap in block, large swap crossing a single initialized tick 1`] = `231357`; + +exports[`PoolManager gas tests fee is off #swapExact0For1 second swap in block, large swap crossing several initialized ticks 1`] = `286425`; + +exports[`PoolManager gas tests fee is on #increaseObservationCardinalityNext grow by 1 slot 1`] = `50918`; + +exports[`PoolManager gas tests fee is on #increaseObservationCardinalityNext no op 1`] = `25849`; + +exports[`PoolManager gas tests fee is on #mint above current price add to position after some time passes 1`] = `169077`; + +exports[`PoolManager gas tests fee is on #mint above current price add to position existing 1`] = `169077`; + +exports[`PoolManager gas tests fee is on #mint above current price new position mint first in range 1`] = `264052`; + +exports[`PoolManager gas tests fee is on #mint above current price second position in same range 1`] = `182757`; + +exports[`PoolManager gas tests fee is on #mint around current price add to position after some time passes 1`] = `242662`; + +exports[`PoolManager gas tests fee is on #mint around current price add to position existing 1`] = `235368`; + +exports[`PoolManager gas tests fee is on #mint around current price new position mint first in range 1`] = `387583`; + +exports[`PoolManager gas tests fee is on #mint around current price second position in same range 1`] = `249048`; + +exports[`PoolManager gas tests fee is on #mint below current price add to position after some time passes 1`] = `169757`; + +exports[`PoolManager gas tests fee is on #mint below current price add to position existing 1`] = `169757`; + +exports[`PoolManager gas tests fee is on #mint below current price new position mint first in range 1`] = `332380`; + +exports[`PoolManager gas tests fee is on #mint below current price second position in same range 1`] = `183437`; + +exports[`PoolManager gas tests fee is on #snapshotCumulativesInside tick above 1`] = `34301`; + +exports[`PoolManager gas tests fee is on #snapshotCumulativesInside tick below 1`] = `34260`; + +exports[`PoolManager gas tests fee is on #snapshotCumulativesInside tick inside 1`] = `39533`; + +exports[`PoolManager gas tests fee is on #swapExact0For1 first swap in block moves tick, no initialized crossings 1`] = `222333`; + +exports[`PoolManager gas tests fee is on #swapExact0For1 first swap in block with no tick movement 1`] = `209405`; + +exports[`PoolManager gas tests fee is on #swapExact0For1 first swap in block, large swap crossing a single initialized tick 1`] = `246658`; + +exports[`PoolManager gas tests fee is on #swapExact0For1 first swap in block, large swap crossing several initialized ticks 1`] = `302105`; + +exports[`PoolManager gas tests fee is on #swapExact0For1 first swap in block, large swap, no initialized crossings 1`] = `234646`; + +exports[`PoolManager gas tests fee is on #swapExact0For1 large swap crossing several initialized ticks after some time passes 1`] = `302105`; + +exports[`PoolManager gas tests fee is on #swapExact0For1 large swap crossing several initialized ticks second time after some time passes 1`] = `302105`; + +exports[`PoolManager gas tests fee is on #swapExact0For1 second swap in block moves tick, no initialized crossings 1`] = `222333`; + +exports[`PoolManager gas tests fee is on #swapExact0For1 second swap in block with no tick movement 1`] = `209494`; + +exports[`PoolManager gas tests fee is on #swapExact0For1 second swap in block, large swap crossing a single initialized tick 1`] = `235662`; + +exports[`PoolManager gas tests fee is on #swapExact0For1 second swap in block, large swap crossing several initialized ticks 1`] = `291083`; diff --git a/test/__snapshots__/PoolManager.spec.ts.snap b/test/__snapshots__/PoolManager.spec.ts.snap new file mode 100644 index 000000000..6d5296629 --- /dev/null +++ b/test/__snapshots__/PoolManager.spec.ts.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`PoolManager #initialize gas cost 1`] = `70689`; + +exports[`PoolManager #lock gas overhead of no-op lock 1`] = `41753`; + +exports[`PoolManager #mint gas cost 1`] = `311563`; + +exports[`PoolManager #swap gas 1`] = `71741`; + +exports[`PoolManager #swap gas for swap against liquidity 1`] = `203880`; + +exports[`PoolManager bytecode size 1`] = `20939`; diff --git a/test/shared/fixtures.ts b/test/shared/fixtures.ts index 5be39ba9b..dfb9e668f 100644 --- a/test/shared/fixtures.ts +++ b/test/shared/fixtures.ts @@ -1,23 +1,6 @@ import { BigNumber } from 'ethers' import { ethers } from 'hardhat' -import { MockTimePool } from '../../typechain/MockTimePool' import { TestERC20 } from '../../typechain/TestERC20' -import { PoolFactory } from '../../typechain/PoolFactory' -import { SwapTarget } from '../../typechain/SwapTarget' -import { MultihopTester } from '../../typechain/MultihopTester' -import { MockTimePoolDeployer } from '../../typechain/MockTimePoolDeployer' - -import { Fixture } from 'ethereum-waffle' - -interface FactoryFixture { - factory: PoolFactory -} - -async function factoryFixture(): Promise { - const factoryFactory = await ethers.getContractFactory('PoolFactory') - const factory = (await factoryFactory.deploy()) as PoolFactory - return { factory } -} interface TokensFixture { token0: TestERC20 @@ -25,7 +8,7 @@ interface TokensFixture { token2: TestERC20 } -async function tokensFixture(): Promise { +export async function tokensFixture(): Promise { const tokenFactory = await ethers.getContractFactory('TestERC20') const tokenA = (await tokenFactory.deploy(BigNumber.from(2).pow(255))) as TestERC20 const tokenB = (await tokenFactory.deploy(BigNumber.from(2).pow(255))) as TestERC20 @@ -37,51 +20,3 @@ async function tokensFixture(): Promise { return { token0, token1, token2 } } - -type TokensAndFactoryFixture = FactoryFixture & TokensFixture - -interface PoolFixture extends TokensAndFactoryFixture { - swapTargetCallee: SwapTarget - swapTargetRouter: MultihopTester - createPool(fee: number, tickSpacing: number, firstToken?: TestERC20, secondToken?: TestERC20): Promise -} - -// Monday, October 5, 2020 9:00:00 AM GMT-05:00 -export const TEST_POOL_START_TIME = 1601906400 - -export const poolFixture: Fixture = async function (): Promise { - const { factory } = await factoryFixture() - const { token0, token1, token2 } = await tokensFixture() - - const mockTimePoolDeployerFactory = await ethers.getContractFactory('MockTimePoolDeployer') - const mockTimePoolFactory = await ethers.getContractFactory('MockTimePool') - - const calleeContractFactory = await ethers.getContractFactory('SwapTarget') - const routerContractFactory = await ethers.getContractFactory('MultihopTester') - - const swapTargetCallee = (await calleeContractFactory.deploy()) as SwapTarget - const swapTargetRouter = (await routerContractFactory.deploy()) as MultihopTester - - return { - token0, - token1, - token2, - factory, - swapTargetCallee, - swapTargetRouter, - createPool: async (fee, tickSpacing, firstToken = token0, secondToken = token1) => { - const mockTimePoolDeployer = (await mockTimePoolDeployerFactory.deploy()) as MockTimePoolDeployer - const tx = await mockTimePoolDeployer.deploy( - factory.address, - firstToken.address, - secondToken.address, - fee, - tickSpacing - ) - - const receipt = await tx.wait() - const poolAddress = receipt.events?.[0].args?.pool as string - return mockTimePoolFactory.attach(poolAddress) as MockTimePool - }, - } -} diff --git a/test/shared/utilities.ts b/test/shared/utilities.ts index 587d14de6..cf88a66d0 100644 --- a/test/shared/utilities.ts +++ b/test/shared/utilities.ts @@ -1,8 +1,5 @@ import bn from 'bignumber.js' -import { BigNumber, BigNumberish, constants, Contract, ContractTransaction, utils, Wallet } from 'ethers' -import { SwapTarget } from '../../typechain/SwapTarget' -import { MultihopTester } from '../../typechain/MultihopTester' -import { MockTimePool } from '../../typechain/MockTimePool' +import { BigNumber, BigNumberish, Contract, ContractTransaction, utils, Wallet } from 'ethers' import { TestERC20 } from '../../typechain/TestERC20' export const MaxUint128 = BigNumber.from(2).pow(128).sub(1) @@ -34,29 +31,6 @@ export function expandTo18Decimals(n: number): BigNumber { return BigNumber.from(n).mul(BigNumber.from(10).pow(18)) } -export function getCreate2Address( - factoryAddress: string, - [tokenA, tokenB]: [string, string], - fee: number, - bytecode: string -): string { - const [token0, token1] = tokenA.toLowerCase() < tokenB.toLowerCase() ? [tokenA, tokenB] : [tokenB, tokenA] - const constructorArgumentsEncoded = utils.defaultAbiCoder.encode( - ['address', 'address', 'uint24'], - [token0, token1, fee] - ) - const create2Inputs = [ - '0xff', - factoryAddress, - // salt - utils.keccak256(constructorArgumentsEncoded), - // init code. bytecode + constructor arguments - utils.keccak256(bytecode), - ] - const sanitizedInputs = `0x${create2Inputs.map((i) => i.slice(2)).join('')}` - return utils.getAddress(`0x${utils.keccak256(sanitizedInputs).slice(-40)}`) -} - bn.config({ EXPONENTIAL_AT: 999999, DECIMAL_PLACES: 40 }) // returns the sqrt price as a 64x96 @@ -75,6 +49,23 @@ export function getPositionKey(address: string, lowerTick: number, upperTick: nu return utils.keccak256(utils.solidityPack(['address', 'int24', 'int24'], [address, lowerTick, upperTick])) } +export function getPoolId({ + token0, + token1, + fee, +}: { + token0: string | Contract + token1: string | Contract + fee: number +}): string { + return utils.keccak256( + utils.defaultAbiCoder.encode( + ['address', 'address', 'uint24'], + [typeof token0 === 'string' ? token0 : token0.address, typeof token1 === 'string' ? token1 : token1.address, fee] + ) + ) +} + export type SwapFunction = ( amount: BigNumberish, to: Wallet | string, @@ -104,155 +95,121 @@ export interface PoolFunctions { flash: FlashFunction mint: MintFunction } + export function createPoolFunctions({ - swapTarget, + // swapTarget, token0, token1, - pool, + fee, }: { - swapTarget: SwapTarget + // swapTarget: SwapTarget token0: TestERC20 token1: TestERC20 - pool: MockTimePool + fee: number }): PoolFunctions { - async function swapToSqrtPrice( - inputToken: Contract, - targetPrice: BigNumberish, - to: Wallet | string - ): Promise { - const method = inputToken === token0 ? swapTarget.swapToLowerSqrtPrice : swapTarget.swapToHigherSqrtPrice - - await inputToken.approve(swapTarget.address, constants.MaxUint256) - - const toAddress = typeof to === 'string' ? to : to.address - - return method(pool.address, targetPrice, toAddress) - } - - async function swap( - inputToken: Contract, - [amountIn, amountOut]: [BigNumberish, BigNumberish], - to: Wallet | string, - sqrtPriceLimitX96?: BigNumberish - ): Promise { - const exactInput = amountOut === 0 - - const method = - inputToken === token0 - ? exactInput - ? swapTarget.swapExact0For1 - : swapTarget.swap0ForExact1 - : exactInput - ? swapTarget.swapExact1For0 - : swapTarget.swap1ForExact0 - - if (typeof sqrtPriceLimitX96 === 'undefined') { - if (inputToken === token0) { - sqrtPriceLimitX96 = MIN_SQRT_RATIO.add(1) - } else { - sqrtPriceLimitX96 = MAX_SQRT_RATIO.sub(1) - } - } - await inputToken.approve(swapTarget.address, constants.MaxUint256) - - const toAddress = typeof to === 'string' ? to : to.address - - return method(pool.address, exactInput ? amountIn : amountOut, toAddress, sqrtPriceLimitX96) - } - - const swapToLowerPrice: SwapToPriceFunction = (sqrtPriceX96, to) => { - return swapToSqrtPrice(token0, sqrtPriceX96, to) - } - - const swapToHigherPrice: SwapToPriceFunction = (sqrtPriceX96, to) => { - return swapToSqrtPrice(token1, sqrtPriceX96, to) - } - - const swapExact0For1: SwapFunction = (amount, to, sqrtPriceLimitX96) => { - return swap(token0, [amount, 0], to, sqrtPriceLimitX96) - } - - const swap0ForExact1: SwapFunction = (amount, to, sqrtPriceLimitX96) => { - return swap(token0, [0, amount], to, sqrtPriceLimitX96) - } - - const swapExact1For0: SwapFunction = (amount, to, sqrtPriceLimitX96) => { - return swap(token1, [amount, 0], to, sqrtPriceLimitX96) - } - - const swap1ForExact0: SwapFunction = (amount, to, sqrtPriceLimitX96) => { - return swap(token1, [0, amount], to, sqrtPriceLimitX96) - } - - const mint: MintFunction = async (recipient, tickLower, tickUpper, liquidity) => { - await token0.approve(swapTarget.address, constants.MaxUint256) - await token1.approve(swapTarget.address, constants.MaxUint256) - return swapTarget.mint(pool.address, recipient, tickLower, tickUpper, liquidity) - } - - const flash: FlashFunction = async (amount0, amount1, to, pay0?: BigNumberish, pay1?: BigNumberish) => { - const fee = await pool.fee() - if (typeof pay0 === 'undefined') { - pay0 = BigNumber.from(amount0) - .mul(fee) - .add(1e6 - 1) - .div(1e6) - .add(amount0) - } - if (typeof pay1 === 'undefined') { - pay1 = BigNumber.from(amount1) - .mul(fee) - .add(1e6 - 1) - .div(1e6) - .add(amount1) - } - return swapTarget.flash(pool.address, typeof to === 'string' ? to : to.address, amount0, amount1, pay0, pay1) - } - - return { - swapToLowerPrice, - swapToHigherPrice, - swapExact0For1, - swap0ForExact1, - swapExact1For0, - swap1ForExact0, - mint, - flash, - } -} - -export interface MultiPoolFunctions { - swapForExact0Multi: SwapFunction - swapForExact1Multi: SwapFunction -} - -export function createMultiPoolFunctions({ - inputToken, - swapTarget, - poolInput, - poolOutput, -}: { - inputToken: TestERC20 - swapTarget: MultihopTester - poolInput: MockTimePool - poolOutput: MockTimePool -}): MultiPoolFunctions { - async function swapForExact0Multi(amountOut: BigNumberish, to: Wallet | string): Promise { - const method = swapTarget.swapForExact0Multi - await inputToken.approve(swapTarget.address, constants.MaxUint256) - const toAddress = typeof to === 'string' ? to : to.address - return method(toAddress, poolInput.address, poolOutput.address, amountOut) - } - - async function swapForExact1Multi(amountOut: BigNumberish, to: Wallet | string): Promise { - const method = swapTarget.swapForExact1Multi - await inputToken.approve(swapTarget.address, constants.MaxUint256) - const toAddress = typeof to === 'string' ? to : to.address - return method(toAddress, poolInput.address, poolOutput.address, amountOut) - } - - return { - swapForExact0Multi, - swapForExact1Multi, - } + throw new Error('todo') + // async function swapToSqrtPrice( + // inputToken: Contract, + // targetPrice: BigNumberish, + // to: Wallet | string + // ): Promise { + // const method = inputToken === token0 ? swapTarget.swapToLowerSqrtPrice : swapTarget.swapToHigherSqrtPrice + // + // await inputToken.approve(swapTarget.address, constants.MaxUint256) + // + // const toAddress = typeof to === 'string' ? to : to.address + // + // return method(pool.address, targetPrice, toAddress) + // } + // + // async function swap( + // inputToken: Contract, + // [amountIn, amountOut]: [BigNumberish, BigNumberish], + // to: Wallet | string, + // sqrtPriceLimitX96?: BigNumberish + // ): Promise { + // const exactInput = amountOut === 0 + // + // const method = + // inputToken === token0 + // ? exactInput + // ? swapTarget.swapExact0For1 + // : swapTarget.swap0ForExact1 + // : exactInput + // ? swapTarget.swapExact1For0 + // : swapTarget.swap1ForExact0 + // + // if (typeof sqrtPriceLimitX96 === 'undefined') { + // if (inputToken === token0) { + // sqrtPriceLimitX96 = MIN_SQRT_RATIO.add(1) + // } else { + // sqrtPriceLimitX96 = MAX_SQRT_RATIO.sub(1) + // } + // } + // await inputToken.approve(swapTarget.address, constants.MaxUint256) + // + // const toAddress = typeof to === 'string' ? to : to.address + // + // return method(pool.address, exactInput ? amountIn : amountOut, toAddress, sqrtPriceLimitX96) + // } + // + // const swapToLowerPrice: SwapToPriceFunction = (sqrtPriceX96, to) => { + // return swapToSqrtPrice(token0, sqrtPriceX96, to) + // } + // + // const swapToHigherPrice: SwapToPriceFunction = (sqrtPriceX96, to) => { + // return swapToSqrtPrice(token1, sqrtPriceX96, to) + // } + // + // const swapExact0For1: SwapFunction = (amount, to, sqrtPriceLimitX96) => { + // return swap(token0, [amount, 0], to, sqrtPriceLimitX96) + // } + // + // const swap0ForExact1: SwapFunction = (amount, to, sqrtPriceLimitX96) => { + // return swap(token0, [0, amount], to, sqrtPriceLimitX96) + // } + // + // const swapExact1For0: SwapFunction = (amount, to, sqrtPriceLimitX96) => { + // return swap(token1, [amount, 0], to, sqrtPriceLimitX96) + // } + // + // const swap1ForExact0: SwapFunction = (amount, to, sqrtPriceLimitX96) => { + // return swap(token1, [0, amount], to, sqrtPriceLimitX96) + // } + // + // const mint: MintFunction = async (recipient, tickLower, tickUpper, liquidity) => { + // await token0.approve(swapTarget.address, constants.MaxUint256) + // await token1.approve(swapTarget.address, constants.MaxUint256) + // return swapTarget.mint(pool.address, recipient, tickLower, tickUpper, liquidity) + // } + // + // const flash: FlashFunction = async (amount0, amount1, to, pay0?: BigNumberish, pay1?: BigNumberish) => { + // const fee = await pool.fee() + // if (typeof pay0 === 'undefined') { + // pay0 = BigNumber.from(amount0) + // .mul(fee) + // .add(1e6 - 1) + // .div(1e6) + // .add(amount0) + // } + // if (typeof pay1 === 'undefined') { + // pay1 = BigNumber.from(amount1) + // .mul(fee) + // .add(1e6 - 1) + // .div(1e6) + // .add(amount1) + // } + // return swapTarget.flash(pool.address, typeof to === 'string' ? to : to.address, amount0, amount1, pay0, pay1) + // } + // + // return { + // swapToLowerPrice, + // swapToHigherPrice, + // swapExact0For1, + // swap0ForExact1, + // swapExact1For0, + // swap1ForExact0, + // mint, + // flash, + // } }