From 173c71d90318c4bd90f491ba3e49f5306393beff Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Wed, 10 Nov 2021 19:28:26 -0500 Subject: [PATCH 01/40] first pass: turn the pool into a library --- contracts/PoolDeployer.sol | 38 -- contracts/PoolFactory.sol | 73 --- contracts/interfaces/IConfiguration.sol | 14 + contracts/interfaces/IPoolDeployer.sol | 26 - contracts/libraries/LowGasERC20.sol | 18 + contracts/{ => libraries}/Pool.sol | 646 +++++++++++++----------- contracts/test/MockTimePool.sol | 26 - contracts/test/MockTimePoolDeployer.sol | 33 -- test/shared/utilities.ts | 1 - 9 files changed, 371 insertions(+), 504 deletions(-) delete mode 100644 contracts/PoolDeployer.sol delete mode 100644 contracts/PoolFactory.sol create mode 100644 contracts/interfaces/IConfiguration.sol delete mode 100644 contracts/interfaces/IPoolDeployer.sol create mode 100644 contracts/libraries/LowGasERC20.sol rename contracts/{ => libraries}/Pool.sol (58%) delete mode 100644 contracts/test/MockTimePool.sol delete mode 100644 contracts/test/MockTimePoolDeployer.sol diff --git a/contracts/PoolDeployer.sol b/contracts/PoolDeployer.sol deleted file mode 100644 index c61f1e758..000000000 --- a/contracts/PoolDeployer.sol +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.9; - -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 512c29118..000000000 --- a/contracts/PoolFactory.sol +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.9; - -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/interfaces/IConfiguration.sol b/contracts/interfaces/IConfiguration.sol new file mode 100644 index 000000000..a9802a912 --- /dev/null +++ b/contracts/interfaces/IConfiguration.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @notice Represents a particular configuration of a Pool +interface IConfiguration { + /// @notice Returns the fee in pips for the pool + function fee() external view returns (uint24); + + /// @notice Returns the spacing of ticks + function tickSpacing() external view returns (int24); + + /// @notice Returns the maximum amount of liquidity that can be placed on any tick + function maxLiquidityPerTick() external view returns (uint128); +} 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/libraries/LowGasERC20.sol b/contracts/libraries/LowGasERC20.sol new file mode 100644 index 000000000..a4246562e --- /dev/null +++ b/contracts/libraries/LowGasERC20.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.9; + +import {IERC20Minimal} from '../interfaces/external/IERC20Minimal.sol'; + +/// @notice Helper functions for more cheaply interacting with ERC20 contracts +library LowGasERC20 { + /// @dev Get the balance of a token with minimum gas + /// @dev This function is gas optimized to avoid a redundant extcodesize check in addition to the returndatasize + /// check + function balance(address token) internal view returns (uint256) { + (bool success, bytes memory data) = token.staticcall( + abi.encodeWithSelector(IERC20Minimal.balanceOf.selector, address(this)) + ); + require(success && data.length >= 32); + return abi.decode(data, (uint256)); + } +} diff --git a/contracts/Pool.sol b/contracts/libraries/Pool.sol similarity index 58% rename from contracts/Pool.sol rename to contracts/libraries/Pool.sol index f62cb18be..f177bd5b0 100644 --- a/contracts/Pool.sol +++ b/contracts/libraries/Pool.sol @@ -1,53 +1,166 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.9; -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; +import {IPoolEvents} from '../interfaces/pool/IPoolEvents.sol'; + +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 {TransferHelper} from './TransferHelper.sol'; +import {TickMath} from './TickMath.sol'; +import {SqrtPriceMath} from './SqrtPriceMath.sol'; +import {SwapMath} from './SwapMath.sol'; +import {LowGasERC20} from './LowGasERC20.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'; + +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]; - /// @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; + /// @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); + + /// @notice Returns the key for identifying a pool + struct Key { + /// @notice The lower token of the pool, sorted numerically + address token0; + /// @notice The higher token of the pool, sorted numerically + address token1; + /// @notice The fee for the pool + uint24 fee; + } - /// @inheritdoc IPoolImmutables - uint128 public immutable override maxLiquidityPerTick; + /// @notice Configuration associated with a given fee tier + struct FeeConfiguration { + /// @notice Initialized tick must be a multiple of this number + int24 tickSpacing; + /// @notice The maximum amount of position liquidity that can use any tick in the range + uint128 maxLiquidityPerTick; + } struct Slot0 { // the current price @@ -66,98 +179,50 @@ contract Pool is IPool, NoDelegateCall { // 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 + // todo: this might be better accumulated in a pool 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()); - _; + /// @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; } - constructor() { - int24 _tickSpacing; - (factory, token0, token1, fee, _tickSpacing) = IPoolDeployer(msg.sender).parameters(); - tickSpacing = _tickSpacing; - - maxLiquidityPerTick = Tick.tickSpacingToMaxLiquidityPerTick(_tickSpacing); + /// @dev Locks the pool to perform some action on it while protected from reentrancy + modifier lock(State storage self) { + require(self.slot0.unlocked, 'LOK'); + self.slot0.unlocked = false; + _; + self.slot0.unlocked = true; } /// @dev Common checks for valid tick inputs. - function checkTicks(int24 tickLower, int24 tickUpper) private pure { + function checkTicks(int24 tickLower, int24 tickUpper) internal pure { require(tickLower < tickUpper, 'TLU'); require(tickLower >= TickMath.MIN_TICK, 'TLM'); require(tickUpper <= TickMath.MAX_TICK, 'TUM'); } - /// @dev Returns the block timestamp truncated to 32 bits, i.e. mod 2**32. This method is overridden in tests. - function _blockTimestamp() internal view virtual returns (uint32) { - return uint32(block.timestamp); // truncation is desired - } - - /// @dev Get the pool's balance of token0 - /// @dev This function is gas optimized to avoid a redundant extcodesize check in addition to the returndatasize - /// check - function balance0() private view returns (uint256) { - (bool success, bytes memory data) = token0.staticcall( - abi.encodeWithSelector(IERC20Minimal.balanceOf.selector, address(this)) - ); - require(success && data.length >= 32); - return abi.decode(data, (uint256)); - } - - /// @dev Get the pool's balance of token1 - /// @dev This function is gas optimized to avoid a redundant extcodesize check in addition to the returndatasize - /// check - function balance1() private view returns (uint256) { - (bool success, bytes memory data) = token1.staticcall( - abi.encodeWithSelector(IERC20Minimal.balanceOf.selector, address(this)) - ); - require(success && data.length >= 32); - return abi.decode(data, (uint256)); - } - - /// @inheritdoc IPoolDerivedState - function snapshotCumulativesInside(int24 tickLower, int24 tickUpper) - external + function snapshotCumulativesInside( + State storage self, + int24 tickLower, + int24 tickUpper, + uint32 time + ) + internal view - override - noDelegateCall returns ( int56 tickCumulativeInside, uint160 secondsPerLiquidityInsideX128, @@ -174,8 +239,8 @@ contract Pool is IPool, NoDelegateCall { uint32 secondsOutsideUpper; { - Tick.Info storage lower = ticks[tickLower]; - Tick.Info storage upper = ticks[tickUpper]; + Tick.Info storage lower = self.ticks[tickLower]; + Tick.Info storage upper = self.ticks[tickUpper]; bool initializedLower; (tickCumulativeLower, secondsPerLiquidityOutsideLowerX128, secondsOutsideLower, initializedLower) = ( lower.tickCumulativeOutside, @@ -195,7 +260,7 @@ contract Pool is IPool, NoDelegateCall { require(initializedUpper); } - Slot0 memory _slot0 = slot0; + Slot0 memory _slot0 = self.slot0; unchecked { if (_slot0.tick < tickLower) { @@ -205,13 +270,12 @@ contract Pool is IPool, NoDelegateCall { secondsOutsideLower - secondsOutsideUpper ); } else if (_slot0.tick < tickUpper) { - uint32 time = _blockTimestamp(); - (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) = observations.observeSingle( + (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) = self.observations.observeSingle( time, 0, _slot0.tick, _slot0.observationIndex, - liquidity, + self.liquidity, _slot0.observationCardinality ); return ( @@ -231,52 +295,49 @@ contract Pool is IPool, NoDelegateCall { } } - /// @inheritdoc IPoolDerivedState - function observe(uint32[] calldata secondsAgos) - external - view - override - noDelegateCall - returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) - { + function observe( + State storage self, + uint32 time, + uint32[] calldata secondsAgos + ) internal view returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) { return - observations.observe( - _blockTimestamp(), + self.observations.observe( + time, secondsAgos, - slot0.tick, - slot0.observationIndex, - liquidity, - slot0.observationCardinality + self.slot0.tick, + self.slot0.observationIndex, + self.liquidity, + self.slot0.observationCardinality ); } - /// @inheritdoc IPoolActions - function increaseObservationCardinalityNext(uint16 observationCardinalityNext) - external - override - lock - noDelegateCall + function increaseObservationCardinalityNext(State storage self, uint16 observationCardinalityNext) + internal + lock(self) { - uint16 observationCardinalityNextOld = slot0.observationCardinalityNext; // for the event - uint16 observationCardinalityNextNew = observations.grow( + uint16 observationCardinalityNextOld = self.slot0.observationCardinalityNext; // for the event + uint16 observationCardinalityNextNew = self.observations.grow( observationCardinalityNextOld, observationCardinalityNext ); - slot0.observationCardinalityNext = observationCardinalityNextNew; + self.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'); + function initialize( + State storage self, + uint32 time, + uint160 sqrtPriceX96 + ) internal { + require(self.slot0.sqrtPriceX96 == 0, 'AI'); int24 tick = TickMath.getTickAtSqrtRatio(sqrtPriceX96); - (uint16 cardinality, uint16 cardinalityNext) = observations.initialize(_blockTimestamp()); + (uint16 cardinality, uint16 cardinalityNext) = self.observations.initialize(time); - slot0 = Slot0({ + self.slot0 = Slot0({ sqrtPriceX96: sqrtPriceX96, tick: tick, observationIndex: 0, @@ -304,9 +365,13 @@ contract Pool is IPool, NoDelegateCall { /// @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) + function _modifyPosition( + State storage self, + FeeConfiguration memory config, + ModifyPositionParams memory params, + uint32 time + ) private - noDelegateCall returns ( Position.Info storage position, int256 amount0, @@ -315,14 +380,17 @@ contract Pool is IPool, NoDelegateCall { { checkTicks(params.tickLower, params.tickUpper); - Slot0 memory _slot0 = slot0; // SLOAD for gas optimization + Slot0 memory _slot0 = self.slot0; // SLOAD for gas optimization position = _updatePosition( + self, + config, params.owner, params.tickLower, params.tickUpper, params.liquidityDelta, - _slot0.tick + _slot0.tick, + time ); if (params.liquidityDelta != 0) { @@ -336,12 +404,12 @@ contract Pool is IPool, NoDelegateCall { ); } else if (_slot0.tick < params.tickUpper) { // current tick is inside the passed range - uint128 liquidityBefore = liquidity; // SLOAD for gas optimization + uint128 liquidityBefore = self.liquidity; // SLOAD for gas optimization // write an oracle entry - (slot0.observationIndex, slot0.observationCardinality) = observations.write( + (self.slot0.observationIndex, self.slot0.observationCardinality) = self.observations.write( _slot0.observationIndex, - _blockTimestamp(), + time, _slot0.tick, liquidityBefore, _slot0.observationCardinality, @@ -359,7 +427,7 @@ contract Pool is IPool, NoDelegateCall { params.liquidityDelta ); - liquidity = params.liquidityDelta < 0 + self.liquidity = params.liquidityDelta < 0 ? liquidityBefore - uint128(-params.liquidityDelta) : liquidityBefore + uint128(params.liquidityDelta); } else { @@ -380,33 +448,35 @@ contract Pool is IPool, NoDelegateCall { /// @param tickUpper the upper tick of the position's tick range /// @param tick the current tick, passed to avoid sloads function _updatePosition( + State storage self, + FeeConfiguration memory config, address owner, int24 tickLower, int24 tickUpper, int128 liquidityDelta, - int24 tick - ) private returns (Position.Info storage position) { + int24 tick, + uint32 time + ) internal returns (Position.Info storage position) { unchecked { - position = positions.get(owner, tickLower, tickUpper); + position = self.positions.get(owner, tickLower, tickUpper); - uint256 _feeGrowthGlobal0X128 = feeGrowthGlobal0X128; // SLOAD for gas optimization - uint256 _feeGrowthGlobal1X128 = feeGrowthGlobal1X128; // SLOAD for gas optimization + uint256 _feeGrowthGlobal0X128 = self.feeGrowthGlobal0X128; // SLOAD for gas optimization + uint256 _feeGrowthGlobal1X128 = self.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( + (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) = self.observations.observeSingle( time, 0, - slot0.tick, - slot0.observationIndex, - liquidity, - slot0.observationCardinality + self.slot0.tick, + self.slot0.observationIndex, + self.liquidity, + self.slot0.observationCardinality ); - flippedLower = ticks.update( + flippedLower = self.ticks.update( tickLower, tick, liquidityDelta, @@ -416,9 +486,9 @@ contract Pool is IPool, NoDelegateCall { tickCumulative, time, false, - maxLiquidityPerTick + config.maxLiquidityPerTick ); - flippedUpper = ticks.update( + flippedUpper = self.ticks.update( tickUpper, tick, liquidityDelta, @@ -428,18 +498,18 @@ contract Pool is IPool, NoDelegateCall { tickCumulative, time, true, - maxLiquidityPerTick + config.maxLiquidityPerTick ); if (flippedLower) { - tickBitmap.flipTick(tickLower, tickSpacing); + self.tickBitmap.flipTick(tickLower, config.tickSpacing); } if (flippedUpper) { - tickBitmap.flipTick(tickUpper, tickSpacing); + self.tickBitmap.flipTick(tickUpper, config.tickSpacing); } } - (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) = ticks.getFeeGrowthInside( + (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) = self.ticks.getFeeGrowthInside( tickLower, tickUpper, tick, @@ -452,32 +522,37 @@ contract Pool is IPool, NoDelegateCall { // clear any tick data that is no longer needed if (liquidityDelta < 0) { if (flippedLower) { - ticks.clear(tickLower); + self.ticks.clear(tickLower); } if (flippedUpper) { - ticks.clear(tickUpper); + self.ticks.clear(tickUpper); } } } } - /// @inheritdoc IPoolActions - /// @dev noDelegateCall is applied indirectly via _modifyPosition function mint( + State storage self, + FeeConfiguration memory config, + Key memory key, address recipient, int24 tickLower, int24 tickUpper, uint128 amount, + uint32 time, bytes calldata data - ) external override lock returns (uint256 amount0, uint256 amount1) { + ) internal lock(self) returns (uint256 amount0, uint256 amount1) { require(amount > 0); (, int256 amount0Int, int256 amount1Int) = _modifyPosition( + self, + config, ModifyPositionParams({ owner: recipient, tickLower: tickLower, tickUpper: tickUpper, liquidityDelta: int256(uint256(amount)).toInt128() - }) + }), + time ); amount0 = uint256(amount0Int); @@ -485,58 +560,63 @@ contract Pool is IPool, NoDelegateCall { uint256 balance0Before; uint256 balance1Before; - if (amount0 > 0) balance0Before = balance0(); - if (amount1 > 0) balance1Before = balance1(); + if (amount0 > 0) balance0Before = LowGasERC20.balance(key.token0); + if (amount1 > 0) balance1Before = LowGasERC20.balance(key.token1); IMintCallback(msg.sender).mintCallback(amount0, amount1, data); - if (amount0 > 0) require(balance0Before + amount0 <= balance0(), 'M0'); - if (amount1 > 0) require(balance1Before + amount1 <= balance1(), 'M1'); + if (amount0 > 0) require(balance0Before + amount0 <= LowGasERC20.balance(key.token0), 'M0'); + if (amount1 > 0) require(balance1Before + amount1 <= LowGasERC20.balance(key.token1), 'M1'); emit Mint(msg.sender, recipient, tickLower, tickUpper, amount, amount0, amount1); } - /// @inheritdoc IPoolActions function collect( + State storage self, + Key memory key, address recipient, int24 tickLower, int24 tickUpper, uint128 amount0Requested, uint128 amount1Requested - ) external override lock returns (uint128 amount0, uint128 amount1) { + ) internal lock(self) 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); + Position.Info storage position = self.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); + TransferHelper.safeTransfer(key.token0, recipient, amount0); } if (amount1 > 0) { position.tokensOwed1 -= amount1; - TransferHelper.safeTransfer(token1, recipient, amount1); + TransferHelper.safeTransfer(key.token1, recipient, amount1); } emit Collect(msg.sender, recipient, tickLower, tickUpper, amount0, amount1); } } - /// @inheritdoc IPoolActions - /// @dev noDelegateCall is applied indirectly via _modifyPosition function burn( + State storage self, + FeeConfiguration memory config, int24 tickLower, int24 tickUpper, - uint128 amount - ) external override lock returns (uint256 amount0, uint256 amount1) { + uint128 amount, + uint32 time + ) internal lock(self) returns (uint256 amount0, uint256 amount1) { unchecked { (Position.Info storage position, int256 amount0Int, int256 amount1Int) = _modifyPosition( + self, + config, ModifyPositionParams({ owner: msg.sender, tickLower: tickLower, tickUpper: tickUpper, liquidityDelta: -int256(uint256(amount)).toInt128() - }) + }), + time ); amount0 = uint256(-amount0Int); @@ -558,8 +638,6 @@ contract Pool is IPool, NoDelegateCall { 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 @@ -603,17 +681,20 @@ contract Pool is IPool, NoDelegateCall { uint256 feeAmount; } - /// @inheritdoc IPoolActions function swap( + State storage self, + Key memory key, + FeeConfiguration memory config, + uint32 time, address recipient, bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96, bytes calldata data - ) external override noDelegateCall returns (int256 amount0, int256 amount1) { + ) internal returns (int256 amount0, int256 amount1) { require(amountSpecified != 0, 'AS'); - Slot0 memory slot0Start = slot0; + Slot0 memory slot0Start = self.slot0; require(slot0Start.unlocked, 'LOK'); require( @@ -623,11 +704,10 @@ contract Pool is IPool, NoDelegateCall { 'SPL' ); - slot0.unlocked = false; + self.slot0.unlocked = false; SwapCache memory cache = SwapCache({ - liquidityStart: liquidity, - blockTimestamp: _blockTimestamp(), + liquidityStart: self.liquidity, feeProtocol: zeroForOne ? (slot0Start.feeProtocol % 16) : (slot0Start.feeProtocol >> 4), secondsPerLiquidityCumulativeX128: 0, tickCumulative: 0, @@ -641,7 +721,7 @@ contract Pool is IPool, NoDelegateCall { amountCalculated: 0, sqrtPriceX96: slot0Start.sqrtPriceX96, tick: slot0Start.tick, - feeGrowthGlobalX128: zeroForOne ? feeGrowthGlobal0X128 : feeGrowthGlobal1X128, + feeGrowthGlobalX128: zeroForOne ? self.feeGrowthGlobal0X128 : self.feeGrowthGlobal1X128, protocolFee: 0, liquidity: cache.liquidityStart }); @@ -652,9 +732,9 @@ contract Pool is IPool, NoDelegateCall { step.sqrtPriceStartX96 = state.sqrtPriceX96; - (step.tickNext, step.initialized) = tickBitmap.nextInitializedTickWithinOneWord( + (step.tickNext, step.initialized) = self.tickBitmap.nextInitializedTickWithinOneWord( state.tick, - tickSpacing, + config.tickSpacing, zeroForOne ); @@ -676,7 +756,7 @@ contract Pool is IPool, NoDelegateCall { : step.sqrtPriceNextX96, state.liquidity, state.amountSpecifiedRemaining, - fee + key.fee ); if (exactInput) { @@ -715,23 +795,25 @@ contract Pool is IPool, NoDelegateCall { // 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.tickCumulative, cache.secondsPerLiquidityCumulativeX128) = self + .observations + .observeSingle( + time, + 0, + slot0Start.tick, + slot0Start.observationIndex, + cache.liquidityStart, + slot0Start.observationCardinality + ); cache.computedLatestObservation = true; } - int128 liquidityNet = ticks.cross( + int128 liquidityNet = self.ticks.cross( step.tickNext, - (zeroForOne ? state.feeGrowthGlobalX128 : feeGrowthGlobal0X128), - (zeroForOne ? feeGrowthGlobal1X128 : state.feeGrowthGlobalX128), + (zeroForOne ? state.feeGrowthGlobalX128 : self.feeGrowthGlobal0X128), + (zeroForOne ? self.feeGrowthGlobal1X128 : state.feeGrowthGlobalX128), cache.secondsPerLiquidityCumulativeX128, cache.tickCumulative, - cache.blockTimestamp + time ); // if we're moving leftward, we interpret liquidityNet as the opposite sign // safe because liquidityNet cannot be type(int128).min @@ -755,39 +837,39 @@ contract Pool is IPool, NoDelegateCall { // update tick and write an oracle entry if the tick change if (state.tick != slot0Start.tick) { - (uint16 observationIndex, uint16 observationCardinality) = observations.write( + (uint16 observationIndex, uint16 observationCardinality) = self.observations.write( slot0Start.observationIndex, - cache.blockTimestamp, + time, slot0Start.tick, cache.liquidityStart, slot0Start.observationCardinality, slot0Start.observationCardinalityNext ); - (slot0.sqrtPriceX96, slot0.tick, slot0.observationIndex, slot0.observationCardinality) = ( - state.sqrtPriceX96, - state.tick, - observationIndex, - observationCardinality - ); + ( + self.slot0.sqrtPriceX96, + self.slot0.tick, + self.slot0.observationIndex, + self.slot0.observationCardinality + ) = (state.sqrtPriceX96, state.tick, observationIndex, observationCardinality); } else { // otherwise just update the price - slot0.sqrtPriceX96 = state.sqrtPriceX96; + self.slot0.sqrtPriceX96 = state.sqrtPriceX96; } // update liquidity if it changed - if (cache.liquidityStart != state.liquidity) liquidity = state.liquidity; + 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 (zeroForOne) { - feeGrowthGlobal0X128 = state.feeGrowthGlobalX128; + self.feeGrowthGlobal0X128 = state.feeGrowthGlobalX128; unchecked { - if (state.protocolFee > 0) protocolFees.token0 += state.protocolFee; + if (state.protocolFee > 0) self.protocolFees.token0 += state.protocolFee; } } else { - feeGrowthGlobal1X128 = state.feeGrowthGlobalX128; + self.feeGrowthGlobal1X128 = state.feeGrowthGlobalX128; unchecked { - if (state.protocolFee > 0) protocolFees.token1 += state.protocolFee; + if (state.protocolFee > 0) self.protocolFees.token1 += state.protocolFee; } } @@ -800,110 +882,60 @@ contract Pool is IPool, NoDelegateCall { // do the transfers and collect payment if (zeroForOne) { unchecked { - if (amount1 < 0) TransferHelper.safeTransfer(token1, recipient, uint256(-amount1)); + if (amount1 < 0) TransferHelper.safeTransfer(key.token1, recipient, uint256(-amount1)); } - uint256 balance0Before = balance0(); + uint256 balance0Before = LowGasERC20.balance(key.token0); ISwapCallback(msg.sender).swapCallback(amount0, amount1, data); - require(balance0Before + uint256(amount0) <= balance0(), 'IIA'); + require(balance0Before + uint256(amount0) <= LowGasERC20.balance(key.token0), 'IIA'); } else { unchecked { - if (amount0 < 0) TransferHelper.safeTransfer(token0, recipient, uint256(-amount0)); + if (amount0 < 0) TransferHelper.safeTransfer(key.token0, recipient, uint256(-amount0)); } - uint256 balance1Before = balance1(); + uint256 balance1Before = LowGasERC20.balance(key.token1); ISwapCallback(msg.sender).swapCallback(amount0, amount1, data); - require(balance1Before + uint256(amount1) <= balance1(), 'IIA'); + require(balance1Before + uint256(amount1) <= LowGasERC20.balance(key.token1), '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); + self.slot0.unlocked = true; } - /// @inheritdoc IPoolOwnerActions - function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external override lock onlyFactoryOwner { + function setFeeProtocol( + State storage self, + uint8 feeProtocol0, + uint8 feeProtocol1 + ) internal lock(self) { require( (feeProtocol0 == 0 || (feeProtocol0 >= 4 && feeProtocol0 <= 10)) && (feeProtocol1 == 0 || (feeProtocol1 >= 4 && feeProtocol1 <= 10)) ); - uint8 feeProtocolOld = slot0.feeProtocol; - slot0.feeProtocol = feeProtocol0 + (feeProtocol1 << 4); + uint8 feeProtocolOld = self.slot0.feeProtocol; + self.slot0.feeProtocol = feeProtocol0 + (feeProtocol1 << 4); emit SetFeeProtocol(feeProtocolOld % 16, feeProtocolOld >> 4, feeProtocol0, feeProtocol1); } - /// @inheritdoc IPoolOwnerActions function collectProtocol( + State storage self, + Key memory key, address recipient, uint128 amount0Requested, uint128 amount1Requested - ) external override lock onlyFactoryOwner returns (uint128 amount0, uint128 amount1) { + ) internal lock(self) returns (uint128 amount0, uint128 amount1) { unchecked { - amount0 = amount0Requested > protocolFees.token0 ? protocolFees.token0 : amount0Requested; - amount1 = amount1Requested > protocolFees.token1 ? protocolFees.token1 : amount1Requested; + amount0 = amount0Requested > self.protocolFees.token0 ? self.protocolFees.token0 : amount0Requested; + amount1 = amount1Requested > self.protocolFees.token1 ? self.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 (amount0 == self.protocolFees.token0) amount0--; // ensure that the slot is not cleared, for gas savings + self.protocolFees.token0 -= amount0; + TransferHelper.safeTransfer(key.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); + if (amount1 == self.protocolFees.token1) amount1--; // ensure that the slot is not cleared, for gas savings + self.protocolFees.token1 -= amount1; + TransferHelper.safeTransfer(key.token1, recipient, amount1); } emit CollectProtocol(msg.sender, recipient, amount0, amount1); diff --git a/contracts/test/MockTimePool.sol b/contracts/test/MockTimePool.sol deleted file mode 100644 index 53784c6b3..000000000 --- a/contracts/test/MockTimePool.sol +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity =0.8.9; - -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 824cb50c0..000000000 --- a/contracts/test/MockTimePoolDeployer.sol +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity =0.8.9; - -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/test/shared/utilities.ts b/test/shared/utilities.ts index c4d796a21..f9b134d5e 100644 --- a/test/shared/utilities.ts +++ b/test/shared/utilities.ts @@ -2,7 +2,6 @@ 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 { TestERC20 } from '../../typechain/TestERC20' export const MaxUint128 = BigNumber.from(2).pow(128).sub(1) From dbbfcf6d0d2e831c0967c36b1b00fc8f4a0ff3f5 Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Wed, 10 Nov 2021 19:30:40 -0500 Subject: [PATCH 02/40] remove IConfiguration.sol --- contracts/interfaces/IConfiguration.sol | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 contracts/interfaces/IConfiguration.sol diff --git a/contracts/interfaces/IConfiguration.sol b/contracts/interfaces/IConfiguration.sol deleted file mode 100644 index a9802a912..000000000 --- a/contracts/interfaces/IConfiguration.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @notice Represents a particular configuration of a Pool -interface IConfiguration { - /// @notice Returns the fee in pips for the pool - function fee() external view returns (uint24); - - /// @notice Returns the spacing of ticks - function tickSpacing() external view returns (int24); - - /// @notice Returns the maximum amount of liquidity that can be placed on any tick - function maxLiquidityPerTick() external view returns (uint128); -} From d21b6d4f925905b3da0f5a2d9abf6c96cfbb36fa Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Wed, 10 Nov 2021 23:08:35 -0500 Subject: [PATCH 03/40] trying to get the refactor to compile --- contracts/SingletonPool.sol | 67 +++ contracts/interfaces/IPoolFactory.sol | 78 --- contracts/libraries/LowGasERC20.sol | 6 +- contracts/libraries/Pool.sol | 653 ++++++++----------------- contracts/libraries/TransferHelper.sol | 4 +- 5 files changed, 279 insertions(+), 529 deletions(-) create mode 100644 contracts/SingletonPool.sol delete mode 100644 contracts/interfaces/IPoolFactory.sol diff --git a/contracts/SingletonPool.sol b/contracts/SingletonPool.sol new file mode 100644 index 000000000..67dda838e --- /dev/null +++ b/contracts/SingletonPool.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.9; + +import {Pool} from './libraries/Pool.sol'; +import {SafeCast} from './libraries/SafeCast.sol'; + +contract SingletonPool { + using SafeCast for *; + using Pool for *; + + mapping(bytes32 => Pool.State) public pools; + + /// @dev For mocking in unit tests + function _blockTimestamp() internal view virtual returns (uint32) { + return uint32(block.timestamp); + } + + function _getPool(Pool.Key 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(Pool.Key memory key, uint160 sqrtPriceX96) external { + _getPool(key).initialize(_blockTimestamp(), sqrtPriceX96); + } + + function increaseObservationCardinalityNext(Pool.Key memory key, uint16 observationCardinalityNext) + external + returns (uint16 observationCardinalityNextOld, uint16 observationCardinalityNextNew) + { + (observationCardinalityNextOld, observationCardinalityNextNew) = _getPool(key) + .increaseObservationCardinalityNext(observationCardinalityNext); + } + + struct MintParams { + // the address that will receive the liquidity + address recipient; + // the lower and upper tick of the position + int24 tickLower; + int24 tickUpper; + // any change in liquidity + uint256 amount; + } + + /// @dev Mint some liquidity for the given pool + function mint(Pool.Key memory key, MintParams memory params) external returns (uint256 amount0, uint256 amount1) { + require(params.amount > 0); + + // (int256 amount0Int, int256 amount1Int) = _getPool(key).modifyPosition( + // Pool.ModifyPositionParams({ + // owner: params.recipient, + // tickLower: params.tickLower, + // tickUpper: params.tickUpper, + // liquidityDelta: int256(uint256(params.amount)).toInt128(), + // time: _blockTimestamp(), + // // todo: where to get these, probably from storage + // maxLiquidityPerTick: type(uint128).max, + // tickSpacing: 60 + // }) + // ); + // + // amount0 = uint256(amount0Int); + // amount1 = uint256(amount1Int); + + // todo: account the delta via the vault + } +} 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/libraries/LowGasERC20.sol b/contracts/libraries/LowGasERC20.sol index a4246562e..301e27ab1 100644 --- a/contracts/libraries/LowGasERC20.sol +++ b/contracts/libraries/LowGasERC20.sol @@ -5,11 +5,11 @@ import {IERC20Minimal} from '../interfaces/external/IERC20Minimal.sol'; /// @notice Helper functions for more cheaply interacting with ERC20 contracts library LowGasERC20 { - /// @dev Get the balance of a token with minimum gas + /// @notice Get the balance of this address for a token with minimum gas /// @dev This function is gas optimized to avoid a redundant extcodesize check in addition to the returndatasize /// check - function balance(address token) internal view returns (uint256) { - (bool success, bytes memory data) = token.staticcall( + function balance(IERC20Minimal token) internal view returns (uint256) { + (bool success, bytes memory data) = address(token).staticcall( abi.encodeWithSelector(IERC20Minimal.balanceOf.selector, address(this)) ); require(success && data.length >= 32); diff --git a/contracts/libraries/Pool.sol b/contracts/libraries/Pool.sol index f177bd5b0..1f678158e 100644 --- a/contracts/libraries/Pool.sol +++ b/contracts/libraries/Pool.sol @@ -29,139 +29,16 @@ library Pool { using Position for Position.Info; using Oracle for Oracle.Observation[65535]; - /// @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); - /// @notice Returns the key for identifying a pool struct Key { /// @notice The lower token of the pool, sorted numerically - address token0; + IERC20Minimal token0; /// @notice The higher token of the pool, sorted numerically - address token1; + IERC20Minimal token1; /// @notice The fee for the pool uint24 fee; } - /// @notice Configuration associated with a given fee tier - struct FeeConfiguration { - /// @notice Initialized tick must be a multiple of this number - int24 tickSpacing; - /// @notice The maximum amount of position liquidity that can use any tick in the range - uint128 maxLiquidityPerTick; - } - struct Slot0 { // the current price uint160 sqrtPriceX96; @@ -215,6 +92,7 @@ library Pool { require(tickUpper <= TickMath.MAX_TICK, 'TUM'); } + /// @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, @@ -311,29 +189,15 @@ library Pool { ); } - function increaseObservationCardinalityNext(State storage self, uint16 observationCardinalityNext) - internal - lock(self) - { - uint16 observationCardinalityNextOld = self.slot0.observationCardinalityNext; // for the event - uint16 observationCardinalityNextNew = self.observations.grow( - observationCardinalityNextOld, - observationCardinalityNext - ); - self.slot0.observationCardinalityNext = observationCardinalityNextNew; - if (observationCardinalityNextOld != observationCardinalityNextNew) - emit IncreaseObservationCardinalityNext(observationCardinalityNextOld, observationCardinalityNextNew); - } - - /// @dev not locked because it initializes unlocked + /// @dev Not locked because it initializes the slot0.unlocked variable function initialize( State storage self, uint32 time, uint160 sqrtPriceX96 - ) internal { + ) internal returns (int24 tick) { require(self.slot0.sqrtPriceX96 == 0, 'AI'); - int24 tick = TickMath.getTickAtSqrtRatio(sqrtPriceX96); + tick = TickMath.getTickAtSqrtRatio(sqrtPriceX96); (uint16 cardinality, uint16 cardinalityNext) = self.observations.initialize(time); @@ -346,8 +210,20 @@ library Pool { feeProtocol: 0, unlocked: true }); + } - emit Initialize(sqrtPriceX96, tick); + /// @dev Increase the number of stored observations + function increaseObservationCardinalityNext(State storage self, uint16 observationCardinalityNext) + internal + lock(self) + returns (uint16 observationCardinalityNextOld, uint16 observationCardinalityNextNew) + { + observationCardinalityNextOld = self.slot0.observationCardinalityNext; + observationCardinalityNextNew = self.observations.grow( + observationCardinalityNextOld, + observationCardinalityNext + ); + self.slot0.observationCardinalityNext = observationCardinalityNextNew; } struct ModifyPositionParams { @@ -358,117 +234,34 @@ library Pool { 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; } - /// @dev Effect some changes to a position + /// @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 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( - State storage self, - FeeConfiguration memory config, - ModifyPositionParams memory params, - uint32 time - ) - private - returns ( - Position.Info storage position, - int256 amount0, - int256 amount1 - ) + /// @return amount0 the delta of the token0 balance of the pool, negative if the pool should pay the recipient + /// @return amount1 the delta of the token1 balance of the pool, negative if the pool should pay the recipient + function modifyPosition(State storage self, ModifyPositionParams memory params) + internal + lock(self) + returns (int256 amount0, int256 amount1) { checkTicks(params.tickLower, params.tickUpper); - Slot0 memory _slot0 = self.slot0; // SLOAD for gas optimization - - position = _updatePosition( - self, - config, - params.owner, - params.tickLower, - params.tickUpper, - params.liquidityDelta, - _slot0.tick, - time - ); - - 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 = self.liquidity; // SLOAD for gas optimization - - // write an oracle entry - (self.slot0.observationIndex, self.slot0.observationCardinality) = self.observations.write( - _slot0.observationIndex, - time, - _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 - ); - - self.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( - State storage self, - FeeConfiguration memory config, - address owner, - int24 tickLower, - int24 tickUpper, - int128 liquidityDelta, - int24 tick, - uint32 time - ) internal returns (Position.Info storage position) { - unchecked { - position = self.positions.get(owner, tickLower, tickUpper); - - uint256 _feeGrowthGlobal0X128 = self.feeGrowthGlobal0X128; // SLOAD for gas optimization - uint256 _feeGrowthGlobal1X128 = self.feeGrowthGlobal1X128; // SLOAD for gas optimization + Position.Info storage position = self.positions.get(params.owner, params.tickLower, params.tickUpper); + { // if we need to update the ticks, do it bool flippedLower; bool flippedUpper; - if (liquidityDelta != 0) { + if (params.liquidityDelta != 0) { (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) = self.observations.observeSingle( - time, + params.time, 0, self.slot0.tick, self.slot0.observationIndex, @@ -477,162 +270,169 @@ library Pool { ); flippedLower = self.ticks.update( - tickLower, - tick, - liquidityDelta, - _feeGrowthGlobal0X128, - _feeGrowthGlobal1X128, + params.tickLower, + self.slot0.tick, + params.liquidityDelta, + self.feeGrowthGlobal0X128, + self.feeGrowthGlobal1X128, secondsPerLiquidityCumulativeX128, tickCumulative, - time, + params.time, false, - config.maxLiquidityPerTick + params.maxLiquidityPerTick ); flippedUpper = self.ticks.update( - tickUpper, - tick, - liquidityDelta, - _feeGrowthGlobal0X128, - _feeGrowthGlobal1X128, + params.tickUpper, + self.slot0.tick, + params.liquidityDelta, + self.feeGrowthGlobal0X128, + self.feeGrowthGlobal1X128, secondsPerLiquidityCumulativeX128, tickCumulative, - time, + params.time, true, - config.maxLiquidityPerTick + params.maxLiquidityPerTick ); if (flippedLower) { - self.tickBitmap.flipTick(tickLower, config.tickSpacing); + self.tickBitmap.flipTick(params.tickLower, params.tickSpacing); } if (flippedUpper) { - self.tickBitmap.flipTick(tickUpper, config.tickSpacing); + self.tickBitmap.flipTick(params.tickUpper, params.tickSpacing); } } (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) = self.ticks.getFeeGrowthInside( - tickLower, - tickUpper, - tick, - _feeGrowthGlobal0X128, - _feeGrowthGlobal1X128 + params.tickLower, + params.tickUpper, + self.slot0.tick, + self.feeGrowthGlobal0X128, + self.feeGrowthGlobal1X128 ); - position.update(liquidityDelta, feeGrowthInside0X128, feeGrowthInside1X128); + position.update(params.liquidityDelta, feeGrowthInside0X128, feeGrowthInside1X128); // clear any tick data that is no longer needed - if (liquidityDelta < 0) { + if (params.liquidityDelta < 0) { if (flippedLower) { - self.ticks.clear(tickLower); + self.ticks.clear(params.tickLower); } if (flippedUpper) { - self.ticks.clear(tickUpper); + self.ticks.clear(params.tickUpper); } } } - } - - function mint( - State storage self, - FeeConfiguration memory config, - Key memory key, - address recipient, - int24 tickLower, - int24 tickUpper, - uint128 amount, - uint32 time, - bytes calldata data - ) internal lock(self) returns (uint256 amount0, uint256 amount1) { - require(amount > 0); - (, int256 amount0Int, int256 amount1Int) = _modifyPosition( - self, - config, - ModifyPositionParams({ - owner: recipient, - tickLower: tickLower, - tickUpper: tickUpper, - liquidityDelta: int256(uint256(amount)).toInt128() - }), - time - ); - - amount0 = uint256(amount0Int); - amount1 = uint256(amount1Int); - - uint256 balance0Before; - uint256 balance1Before; - if (amount0 > 0) balance0Before = LowGasERC20.balance(key.token0); - if (amount1 > 0) balance1Before = LowGasERC20.balance(key.token1); - IMintCallback(msg.sender).mintCallback(amount0, amount1, data); - if (amount0 > 0) require(balance0Before + amount0 <= LowGasERC20.balance(key.token0), 'M0'); - if (amount1 > 0) require(balance1Before + amount1 <= LowGasERC20.balance(key.token1), 'M1'); - - emit Mint(msg.sender, recipient, tickLower, tickUpper, amount, amount0, amount1); - } - function collect( - State storage self, - Key memory key, - address recipient, - int24 tickLower, - int24 tickUpper, - uint128 amount0Requested, - uint128 amount1Requested - ) internal lock(self) 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 = self.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(key.token0, recipient, amount0); - } - if (amount1 > 0) { - position.tokensOwed1 -= amount1; - TransferHelper.safeTransfer(key.token1, recipient, amount1); - } - - emit Collect(msg.sender, recipient, tickLower, tickUpper, amount0, amount1); - } - } + 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 + 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 + uint128 liquidityBefore = self.liquidity; // SLOAD for gas optimization - function burn( - State storage self, - FeeConfiguration memory config, - int24 tickLower, - int24 tickUpper, - uint128 amount, - uint32 time - ) internal lock(self) returns (uint256 amount0, uint256 amount1) { - unchecked { - (Position.Info storage position, int256 amount0Int, int256 amount1Int) = _modifyPosition( - self, - config, - ModifyPositionParams({ - owner: msg.sender, - tickLower: tickLower, - tickUpper: tickUpper, - liquidityDelta: -int256(uint256(amount)).toInt128() - }), - time - ); + // write an oracle entry + (self.slot0.observationIndex, self.slot0.observationCardinality) = self.observations.write( + self.slot0.observationIndex, + params.time, + self.slot0.tick, + liquidityBefore, + self.slot0.observationCardinality, + self.slot0.observationCardinalityNext + ); - amount0 = uint256(-amount0Int); - amount1 = uint256(-amount1Int); + amount0 = SqrtPriceMath.getAmount0Delta( + self.slot0.sqrtPriceX96, + TickMath.getSqrtRatioAtTick(params.tickUpper), + params.liquidityDelta + ); + amount1 = SqrtPriceMath.getAmount1Delta( + TickMath.getSqrtRatioAtTick(params.tickLower), + self.slot0.sqrtPriceX96, + params.liquidityDelta + ); - if (amount0 > 0 || amount1 > 0) { - (position.tokensOwed0, position.tokensOwed1) = ( - position.tokensOwed0 + uint128(amount0), - position.tokensOwed1 + uint128(amount1) + self.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 ); } - - emit Burn(msg.sender, tickLower, tickUpper, amount, amount0, amount1); } } + // function collect( + // State storage self, + // Key memory key, + // address recipient, + // int24 tickLower, + // int24 tickUpper, + // uint128 amount0Requested, + // uint128 amount1Requested + // ) internal lock(self) 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 = self.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(key.token0, recipient, amount0); + // } + // if (amount1 > 0) { + // position.tokensOwed1 -= amount1; + // TransferHelper.safeTransfer(key.token1, recipient, amount1); + // } + // } + // } + + // function burn( + // State storage self, + // Configuration memory config, + // int24 tickLower, + // int24 tickUpper, + // uint128 amount, + // uint32 time + // ) internal lock(self) returns (uint256 amount0, uint256 amount1) { + // unchecked { + // (Position.Info storage position, int256 amount0Int, int256 amount1Int) = _modifyPosition( + // self, + // config, + // ModifyPositionParams({ + // owner: msg.sender, + // tickLower: tickLower, + // tickUpper: tickUpper, + // liquidityDelta: -int256(uint256(amount)).toInt128(), + // time: time + // }) + // ); + // + // amount0 = uint256(-amount0Int); + // amount1 = uint256(-amount1Int); + // + // if (amount0 > 0 || amount1 > 0) { + // (position.tokensOwed0, position.tokensOwed1) = ( + // position.tokensOwed0 + uint128(amount0), + // position.tokensOwed1 + uint128(amount1) + // ); + // } + // + // } + // } + struct SwapCache { // the protocol fee for the input token uint8 feeProtocol; @@ -681,26 +481,30 @@ library Pool { uint256 feeAmount; } - function swap( - State storage self, - Key memory key, - FeeConfiguration memory config, - uint32 time, - address recipient, - bool zeroForOne, - int256 amountSpecified, - uint160 sqrtPriceLimitX96, - bytes calldata data - ) internal returns (int256 amount0, int256 amount1) { - require(amountSpecified != 0, 'AS'); + struct SwapParams { + uint24 fee; + int24 tickSpacing; + uint32 time; + address recipient; + bool zeroForOne; + int256 amountSpecified; + uint160 sqrtPriceLimitX96; + bytes data; + } + + /// @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 (int256 amount0, int256 amount1) { + require(params.amountSpecified != 0, 'AS'); Slot0 memory slot0Start = self.slot0; require(slot0Start.unlocked, 'LOK'); require( - zeroForOne - ? sqrtPriceLimitX96 < slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 > TickMath.MIN_SQRT_RATIO - : sqrtPriceLimitX96 > slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 < TickMath.MAX_SQRT_RATIO, + params.zeroForOne + ? params.sqrtPriceLimitX96 < slot0Start.sqrtPriceX96 && + params.sqrtPriceLimitX96 > TickMath.MIN_SQRT_RATIO + : params.sqrtPriceLimitX96 > slot0Start.sqrtPriceX96 && + params.sqrtPriceLimitX96 < TickMath.MAX_SQRT_RATIO, 'SPL' ); @@ -708,34 +512,34 @@ library Pool { SwapCache memory cache = SwapCache({ liquidityStart: self.liquidity, - feeProtocol: zeroForOne ? (slot0Start.feeProtocol % 16) : (slot0Start.feeProtocol >> 4), + feeProtocol: params.zeroForOne ? (slot0Start.feeProtocol % 16) : (slot0Start.feeProtocol >> 4), secondsPerLiquidityCumulativeX128: 0, tickCumulative: 0, computedLatestObservation: false }); - bool exactInput = amountSpecified > 0; + bool exactInput = params.amountSpecified > 0; SwapState memory state = SwapState({ - amountSpecifiedRemaining: amountSpecified, + amountSpecifiedRemaining: params.amountSpecified, amountCalculated: 0, sqrtPriceX96: slot0Start.sqrtPriceX96, tick: slot0Start.tick, - feeGrowthGlobalX128: zeroForOne ? self.feeGrowthGlobal0X128 : self.feeGrowthGlobal1X128, + 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 != sqrtPriceLimitX96) { + while (state.amountSpecifiedRemaining != 0 && state.sqrtPriceX96 != params.sqrtPriceLimitX96) { StepComputations memory step; step.sqrtPriceStartX96 = state.sqrtPriceX96; (step.tickNext, step.initialized) = self.tickBitmap.nextInitializedTickWithinOneWord( state.tick, - config.tickSpacing, - zeroForOne + params.tickSpacing, + params.zeroForOne ); // ensure that we do not overshoot the min/max tick, as the tick bitmap is not aware of these bounds @@ -751,12 +555,16 @@ library Pool { // 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 + ( + params.zeroForOne + ? step.sqrtPriceNextX96 < params.sqrtPriceLimitX96 + : step.sqrtPriceNextX96 > params.sqrtPriceLimitX96 + ) + ? params.sqrtPriceLimitX96 : step.sqrtPriceNextX96, state.liquidity, state.amountSpecifiedRemaining, - key.fee + params.fee ); if (exactInput) { @@ -798,7 +606,7 @@ library Pool { (cache.tickCumulative, cache.secondsPerLiquidityCumulativeX128) = self .observations .observeSingle( - time, + params.time, 0, slot0Start.tick, slot0Start.observationIndex, @@ -809,16 +617,16 @@ library Pool { } int128 liquidityNet = self.ticks.cross( step.tickNext, - (zeroForOne ? state.feeGrowthGlobalX128 : self.feeGrowthGlobal0X128), - (zeroForOne ? self.feeGrowthGlobal1X128 : state.feeGrowthGlobalX128), + (params.zeroForOne ? state.feeGrowthGlobalX128 : self.feeGrowthGlobal0X128), + (params.zeroForOne ? self.feeGrowthGlobal1X128 : state.feeGrowthGlobalX128), cache.secondsPerLiquidityCumulativeX128, cache.tickCumulative, - time + params.time ); // 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; + if (params.zeroForOne) liquidityNet = -liquidityNet; } state.liquidity = liquidityNet < 0 @@ -827,7 +635,7 @@ library Pool { } unchecked { - state.tick = zeroForOne ? step.tickNext - 1 : step.tickNext; + 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 @@ -839,7 +647,7 @@ library Pool { if (state.tick != slot0Start.tick) { (uint16 observationIndex, uint16 observationCardinality) = self.observations.write( slot0Start.observationIndex, - time, + params.time, slot0Start.tick, cache.liquidityStart, slot0Start.observationCardinality, @@ -861,7 +669,7 @@ library Pool { // 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) { + if (params.zeroForOne) { self.feeGrowthGlobal0X128 = state.feeGrowthGlobalX128; unchecked { if (state.protocolFee > 0) self.protocolFees.token0 += state.protocolFee; @@ -874,31 +682,11 @@ library Pool { } 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(key.token1, recipient, uint256(-amount1)); - } - - uint256 balance0Before = LowGasERC20.balance(key.token0); - ISwapCallback(msg.sender).swapCallback(amount0, amount1, data); - require(balance0Before + uint256(amount0) <= LowGasERC20.balance(key.token0), 'IIA'); - } else { - unchecked { - if (amount0 < 0) TransferHelper.safeTransfer(key.token0, recipient, uint256(-amount0)); - } - - uint256 balance1Before = LowGasERC20.balance(key.token1); - ISwapCallback(msg.sender).swapCallback(amount0, amount1, data); - require(balance1Before + uint256(amount1) <= LowGasERC20.balance(key.token1), 'IIA'); + (amount0, amount1) = params.zeroForOne == exactInput + ? (params.amountSpecified - state.amountSpecifiedRemaining, state.amountCalculated) + : (state.amountCalculated, params.amountSpecified - state.amountSpecifiedRemaining); } - emit Swap(msg.sender, recipient, amount0, amount1, state.sqrtPriceX96, state.liquidity, state.tick); self.slot0.unlocked = true; } @@ -913,32 +701,5 @@ library Pool { ); uint8 feeProtocolOld = self.slot0.feeProtocol; self.slot0.feeProtocol = feeProtocol0 + (feeProtocol1 << 4); - emit SetFeeProtocol(feeProtocolOld % 16, feeProtocolOld >> 4, feeProtocol0, feeProtocol1); - } - - function collectProtocol( - State storage self, - Key memory key, - address recipient, - uint128 amount0Requested, - uint128 amount1Requested - ) internal lock(self) returns (uint128 amount0, uint128 amount1) { - unchecked { - amount0 = amount0Requested > self.protocolFees.token0 ? self.protocolFees.token0 : amount0Requested; - amount1 = amount1Requested > self.protocolFees.token1 ? self.protocolFees.token1 : amount1Requested; - - if (amount0 > 0) { - if (amount0 == self.protocolFees.token0) amount0--; // ensure that the slot is not cleared, for gas savings - self.protocolFees.token0 -= amount0; - TransferHelper.safeTransfer(key.token0, recipient, amount0); - } - if (amount1 > 0) { - if (amount1 == self.protocolFees.token1) amount1--; // ensure that the slot is not cleared, for gas savings - self.protocolFees.token1 -= amount1; - TransferHelper.safeTransfer(key.token1, recipient, amount1); - } - - emit CollectProtocol(msg.sender, recipient, amount0, amount1); - } } } diff --git a/contracts/libraries/TransferHelper.sol b/contracts/libraries/TransferHelper.sol index ce70176b3..4e501d08c 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'); From 4791e961e4bd023a61b3cee5bcc25ced16653b39 Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Thu, 11 Nov 2021 10:58:22 -0500 Subject: [PATCH 04/40] more structifying, still not compiling --- contracts/SingletonPool.sol | 30 ++++++++-------- contracts/libraries/Pool.sol | 66 +++++++++++++++++++++--------------- 2 files changed, 53 insertions(+), 43 deletions(-) diff --git a/contracts/SingletonPool.sol b/contracts/SingletonPool.sol index 67dda838e..ace8a6aa8 100644 --- a/contracts/SingletonPool.sol +++ b/contracts/SingletonPool.sol @@ -46,21 +46,21 @@ contract SingletonPool { function mint(Pool.Key memory key, MintParams memory params) external returns (uint256 amount0, uint256 amount1) { require(params.amount > 0); - // (int256 amount0Int, int256 amount1Int) = _getPool(key).modifyPosition( - // Pool.ModifyPositionParams({ - // owner: params.recipient, - // tickLower: params.tickLower, - // tickUpper: params.tickUpper, - // liquidityDelta: int256(uint256(params.amount)).toInt128(), - // time: _blockTimestamp(), - // // todo: where to get these, probably from storage - // maxLiquidityPerTick: type(uint128).max, - // tickSpacing: 60 - // }) - // ); - // - // amount0 = uint256(amount0Int); - // amount1 = uint256(amount1Int); + Pool.ModifyPositionResult memory result = _getPool(key).modifyPosition( + Pool.ModifyPositionParams({ + owner: params.recipient, + tickLower: params.tickLower, + tickUpper: params.tickUpper, + liquidityDelta: int256(uint256(params.amount)).toInt128(), + time: _blockTimestamp(), + // todo: where to get these, probably from storage + maxLiquidityPerTick: type(uint128).max, + tickSpacing: 60 + }) + ); + + amount0 = uint256(result.amount0); + amount1 = uint256(result.amount1); // todo: account the delta via the vault } diff --git a/contracts/libraries/Pool.sol b/contracts/libraries/Pool.sol index 1f678158e..4ab9af0ef 100644 --- a/contracts/libraries/Pool.sol +++ b/contracts/libraries/Pool.sol @@ -242,25 +242,37 @@ library Pool { int24 tickSpacing; } + struct ModifyPositionState { + int56 tickCumulative; + uint160 secondsPerLiquidityCumulativeX128; + bool flippedLower; + bool flippedUpper; + uint256 feeGrowthInside0X128; + uint256 feeGrowthInside1X128; + } + + struct ModifyPositionResult { + int256 amount0; + int256 amount1; + } + /// @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 amount0 the delta of the token0 balance of the pool, negative if the pool should pay the recipient - /// @return amount1 the delta of the token1 balance of the pool, negative if the pool should pay the recipient + /// @return result the deltas of the token balances of the pool function modifyPosition(State storage self, ModifyPositionParams memory params) internal lock(self) - returns (int256 amount0, int256 amount1) + returns (ModifyPositionResult memory result) { checkTicks(params.tickLower, params.tickUpper); Position.Info storage position = self.positions.get(params.owner, params.tickLower, params.tickUpper); { + ModifyPositionState memory state; // if we need to update the ticks, do it - bool flippedLower; - bool flippedUpper; if (params.liquidityDelta != 0) { - (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) = self.observations.observeSingle( + (state.tickCumulative, state.secondsPerLiquidityCumulativeX128) = self.observations.observeSingle( params.time, 0, self.slot0.tick, @@ -269,40 +281,40 @@ library Pool { self.slot0.observationCardinality ); - flippedLower = self.ticks.update( + state.flippedLower = self.ticks.update( params.tickLower, self.slot0.tick, params.liquidityDelta, self.feeGrowthGlobal0X128, self.feeGrowthGlobal1X128, - secondsPerLiquidityCumulativeX128, - tickCumulative, + state.secondsPerLiquidityCumulativeX128, + state.tickCumulative, params.time, false, params.maxLiquidityPerTick ); - flippedUpper = self.ticks.update( + state.flippedUpper = self.ticks.update( params.tickUpper, self.slot0.tick, params.liquidityDelta, self.feeGrowthGlobal0X128, self.feeGrowthGlobal1X128, - secondsPerLiquidityCumulativeX128, - tickCumulative, + state.secondsPerLiquidityCumulativeX128, + state.tickCumulative, params.time, true, params.maxLiquidityPerTick ); - if (flippedLower) { + if (state.flippedLower) { self.tickBitmap.flipTick(params.tickLower, params.tickSpacing); } - if (flippedUpper) { + if (state.flippedUpper) { self.tickBitmap.flipTick(params.tickUpper, params.tickSpacing); } } - (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) = self.ticks.getFeeGrowthInside( + (state.feeGrowthInside0X128, state.feeGrowthInside1X128) = self.ticks.getFeeGrowthInside( params.tickLower, params.tickUpper, self.slot0.tick, @@ -310,14 +322,14 @@ library Pool { self.feeGrowthGlobal1X128 ); - position.update(params.liquidityDelta, feeGrowthInside0X128, feeGrowthInside1X128); + position.update(params.liquidityDelta, state.feeGrowthInside0X128, state.feeGrowthInside1X128); // clear any tick data that is no longer needed if (params.liquidityDelta < 0) { - if (flippedLower) { + if (state.flippedLower) { self.ticks.clear(params.tickLower); } - if (flippedUpper) { + if (state.flippedUpper) { self.ticks.clear(params.tickUpper); } } @@ -327,43 +339,41 @@ library Pool { 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 - amount0 = SqrtPriceMath.getAmount0Delta( + 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 - uint128 liquidityBefore = self.liquidity; // SLOAD for gas optimization - + // 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, - liquidityBefore, + self.liquidity, self.slot0.observationCardinality, self.slot0.observationCardinalityNext ); - amount0 = SqrtPriceMath.getAmount0Delta( + result.amount0 = SqrtPriceMath.getAmount0Delta( self.slot0.sqrtPriceX96, TickMath.getSqrtRatioAtTick(params.tickUpper), params.liquidityDelta ); - amount1 = SqrtPriceMath.getAmount1Delta( + result.amount1 = SqrtPriceMath.getAmount1Delta( TickMath.getSqrtRatioAtTick(params.tickLower), self.slot0.sqrtPriceX96, params.liquidityDelta ); self.liquidity = params.liquidityDelta < 0 - ? liquidityBefore - uint128(-params.liquidityDelta) - : liquidityBefore + uint128(params.liquidityDelta); + ? 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 - amount1 = SqrtPriceMath.getAmount1Delta( + result.amount1 = SqrtPriceMath.getAmount1Delta( TickMath.getSqrtRatioAtTick(params.tickLower), TickMath.getSqrtRatioAtTick(params.tickUpper), params.liquidityDelta From e866db719ed32975f2afa9c97e6c974a086d58ae Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Thu, 11 Nov 2021 14:40:06 -0500 Subject: [PATCH 05/40] mint/burn compiling --- contracts/SingletonPool.sol | 31 +++++++++++++++++++++++++++++++ contracts/libraries/Pool.sol | 8 +++++--- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/contracts/SingletonPool.sol b/contracts/SingletonPool.sol index ace8a6aa8..2be41b23e 100644 --- a/contracts/SingletonPool.sol +++ b/contracts/SingletonPool.sol @@ -64,4 +64,35 @@ contract SingletonPool { // todo: account the delta via the vault } + + struct BurnParams { + // the lower and upper tick of the position + int24 tickLower; + int24 tickUpper; + // any change in liquidity + uint256 amount; + } + + /// @dev Mint some liquidity for the given pool + function burn(Pool.Key memory key, BurnParams memory params) external returns (uint256 amount0, uint256 amount1) { + require(params.amount > 0); + + Pool.ModifyPositionResult memory result = _getPool(key).modifyPosition( + Pool.ModifyPositionParams({ + owner: msg.sender, + tickLower: params.tickLower, + tickUpper: params.tickUpper, + liquidityDelta: -int256(uint256(params.amount)).toInt128(), + time: _blockTimestamp(), + // todo: where to get these, probably from storage + maxLiquidityPerTick: type(uint128).max, + tickSpacing: 60 + }) + ); + + amount0 = uint256(-result.amount0); + amount1 = uint256(-result.amount1); + + // todo: account the delta via the vault + } } diff --git a/contracts/libraries/Pool.sol b/contracts/libraries/Pool.sol index 4ab9af0ef..378c4ef7b 100644 --- a/contracts/libraries/Pool.sol +++ b/contracts/libraries/Pool.sol @@ -266,8 +266,6 @@ library Pool { { checkTicks(params.tickLower, params.tickUpper); - Position.Info storage position = self.positions.get(params.owner, params.tickLower, params.tickUpper); - { ModifyPositionState memory state; // if we need to update the ticks, do it @@ -322,7 +320,11 @@ library Pool { self.feeGrowthGlobal1X128 ); - position.update(params.liquidityDelta, state.feeGrowthInside0X128, state.feeGrowthInside1X128); + self.positions.get(params.owner, params.tickLower, params.tickUpper).update( + params.liquidityDelta, + state.feeGrowthInside0X128, + state.feeGrowthInside1X128 + ); // clear any tick data that is no longer needed if (params.liquidityDelta < 0) { From 134f8b2c89223e12f5200d4772a5f0290a6e5a31 Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Thu, 11 Nov 2021 17:10:32 -0500 Subject: [PATCH 06/40] swap compiling! --- contracts/SingletonPool.sol | 31 +- contracts/libraries/Pool.sol | 83 +- test/Multihop.spec.ts | 133 --- test/Oracle.spec.ts | 4 +- test/Pool.arbitrage.spec.ts | 358 ------ test/Pool.gas.spec.ts | 315 ------ test/Pool.spec.ts | 1995 ---------------------------------- test/Pool.swaps.spec.ts | 590 ---------- test/PoolFactory.spec.ts | 177 --- test/shared/fixtures.ts | 65 -- test/shared/utilities.ts | 251 ++--- 11 files changed, 150 insertions(+), 3852 deletions(-) delete mode 100644 test/Multihop.spec.ts delete mode 100644 test/Pool.arbitrage.spec.ts delete mode 100644 test/Pool.gas.spec.ts delete mode 100644 test/Pool.spec.ts delete mode 100644 test/Pool.swaps.spec.ts delete mode 100644 test/PoolFactory.spec.ts diff --git a/contracts/SingletonPool.sol b/contracts/SingletonPool.sol index 2be41b23e..9fdd45a32 100644 --- a/contracts/SingletonPool.sol +++ b/contracts/SingletonPool.sol @@ -46,6 +46,7 @@ contract SingletonPool { function mint(Pool.Key memory key, MintParams memory params) external returns (uint256 amount0, uint256 amount1) { require(params.amount > 0); + // todo: where to get maxLiquidityPerTick, tickSpacing, probably from storage Pool.ModifyPositionResult memory result = _getPool(key).modifyPosition( Pool.ModifyPositionParams({ owner: params.recipient, @@ -53,7 +54,6 @@ contract SingletonPool { tickUpper: params.tickUpper, liquidityDelta: int256(uint256(params.amount)).toInt128(), time: _blockTimestamp(), - // todo: where to get these, probably from storage maxLiquidityPerTick: type(uint128).max, tickSpacing: 60 }) @@ -77,6 +77,7 @@ contract SingletonPool { function burn(Pool.Key memory key, BurnParams memory params) external returns (uint256 amount0, uint256 amount1) { require(params.amount > 0); + // todo: where to get maxLiquidityPerTick, tickSpacing, probably from storage Pool.ModifyPositionResult memory result = _getPool(key).modifyPosition( Pool.ModifyPositionParams({ owner: msg.sender, @@ -84,7 +85,6 @@ contract SingletonPool { tickUpper: params.tickUpper, liquidityDelta: -int256(uint256(params.amount)).toInt128(), time: _blockTimestamp(), - // todo: where to get these, probably from storage maxLiquidityPerTick: type(uint128).max, tickSpacing: 60 }) @@ -95,4 +95,31 @@ contract SingletonPool { // todo: account the delta via the vault } + + struct SwapParams { + address recipient; + bool zeroForOne; + int256 amountSpecified; + uint160 sqrtPriceLimitX96; + bytes data; + } + + function swap(Pool.Key memory key, SwapParams memory params) external returns (int256 amount0, int256 amount1) { + Pool.SwapResult memory result = _getPool(key).swap( + Pool.SwapParams({ + time: _blockTimestamp(), + fee: key.fee, + recipient: params.recipient, + zeroForOne: params.zeroForOne, + amountSpecified: params.amountSpecified, + sqrtPriceLimitX96: params.sqrtPriceLimitX96, + data: params.data, + tickSpacing: 60 + }) + ); + + (amount0, amount1) = (result.amount0, result.amount1); + + // todo: account the delta via the vault + } } diff --git a/contracts/libraries/Pool.sol b/contracts/libraries/Pool.sol index 378c4ef7b..1b659afd1 100644 --- a/contracts/libraries/Pool.sol +++ b/contracts/libraries/Pool.sol @@ -384,67 +384,6 @@ library Pool { } } - // function collect( - // State storage self, - // Key memory key, - // address recipient, - // int24 tickLower, - // int24 tickUpper, - // uint128 amount0Requested, - // uint128 amount1Requested - // ) internal lock(self) 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 = self.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(key.token0, recipient, amount0); - // } - // if (amount1 > 0) { - // position.tokensOwed1 -= amount1; - // TransferHelper.safeTransfer(key.token1, recipient, amount1); - // } - // } - // } - - // function burn( - // State storage self, - // Configuration memory config, - // int24 tickLower, - // int24 tickUpper, - // uint128 amount, - // uint32 time - // ) internal lock(self) returns (uint256 amount0, uint256 amount1) { - // unchecked { - // (Position.Info storage position, int256 amount0Int, int256 amount1Int) = _modifyPosition( - // self, - // config, - // ModifyPositionParams({ - // owner: msg.sender, - // tickLower: tickLower, - // tickUpper: tickUpper, - // liquidityDelta: -int256(uint256(amount)).toInt128(), - // time: time - // }) - // ); - // - // amount0 = uint256(-amount0Int); - // amount1 = uint256(-amount1Int); - // - // if (amount0 > 0 || amount1 > 0) { - // (position.tokensOwed0, position.tokensOwed1) = ( - // position.tokensOwed0 + uint128(amount0), - // position.tokensOwed1 + uint128(amount1) - // ); - // } - // - // } - // } - struct SwapCache { // the protocol fee for the input token uint8 feeProtocol; @@ -504,8 +443,13 @@ library Pool { bytes data; } + struct SwapResult { + int256 amount0; + int256 amount1; + } + /// @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 (int256 amount0, int256 amount1) { + function swap(State storage self, SwapParams memory params) internal returns (SwapResult memory result) { require(params.amountSpecified != 0, 'AS'); Slot0 memory slot0Start = self.slot0; @@ -657,7 +601,7 @@ library Pool { // update tick and write an oracle entry if the tick change if (state.tick != slot0Start.tick) { - (uint16 observationIndex, uint16 observationCardinality) = self.observations.write( + (self.slot0.observationIndex, self.slot0.observationCardinality) = self.observations.write( slot0Start.observationIndex, params.time, slot0Start.tick, @@ -665,12 +609,7 @@ library Pool { slot0Start.observationCardinality, slot0Start.observationCardinalityNext ); - ( - self.slot0.sqrtPriceX96, - self.slot0.tick, - self.slot0.observationIndex, - self.slot0.observationCardinality - ) = (state.sqrtPriceX96, state.tick, observationIndex, observationCardinality); + (self.slot0.sqrtPriceX96, self.slot0.tick) = (state.sqrtPriceX96, state.tick); } else { // otherwise just update the price self.slot0.sqrtPriceX96 = state.sqrtPriceX96; @@ -694,7 +633,7 @@ library Pool { } unchecked { - (amount0, amount1) = params.zeroForOne == exactInput + (result.amount0, result.amount1) = params.zeroForOne == exactInput ? (params.amountSpecified - state.amountSpecifiedRemaining, state.amountCalculated) : (state.amountCalculated, params.amountSpecified - state.amountSpecifiedRemaining); } @@ -706,12 +645,12 @@ library Pool { State storage self, uint8 feeProtocol0, uint8 feeProtocol1 - ) internal lock(self) { + ) internal lock(self) returns (uint8 feeProtocolOld) { require( (feeProtocol0 == 0 || (feeProtocol0 >= 4 && feeProtocol0 <= 10)) && (feeProtocol1 == 0 || (feeProtocol1 >= 4 && feeProtocol1 <= 10)) ); - uint8 feeProtocolOld = self.slot0.feeProtocol; + feeProtocolOld = self.slot0.feeProtocol; self.slot0.feeProtocol = feeProtocol0 + (feeProtocol1 << 4); } } diff --git a/test/Multihop.spec.ts b/test/Multihop.spec.ts deleted file mode 100644 index 650f3ff4d..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, - encodePriceSqrt, - 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(encodePriceSqrt(1, 1)) - await pool1.initialize(encodePriceSqrt(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 8d8fdd9a1..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, - encodePriceSqrt, - 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 = encodePriceSqrt(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 - ? encodePriceSqrt(amount1Delta, amount0Delta.mul(-1)) - : encodePriceSqrt(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: encodePriceSqrt(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: encodePriceSqrt(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.gas.spec.ts b/test/Pool.gas.spec.ts deleted file mode 100644 index 0dde8d246..000000000 --- a/test/Pool.gas.spec.ts +++ /dev/null @@ -1,315 +0,0 @@ -import { ethers, waffle } from 'hardhat' -import { Wallet } from 'ethers' -import { MockTimePool } from '../typechain/MockTimePool' -import { expect } from './shared/expect' - -import { poolFixture } from './shared/fixtures' -import snapshotGasCost from './shared/snapshotGasCost' - -import { - expandTo18Decimals, - FeeAmount, - getMinTick, - encodePriceSqrt, - TICK_SPACINGS, - createPoolFunctions, - SwapFunction, - MintFunction, - getMaxTick, - MaxUint128, - SwapToPriceFunction, - MAX_SQRT_RATIO, - MIN_SQRT_RATIO, -} from './shared/utilities' - -const createFixtureLoader = waffle.createFixtureLoader - -describe('Pool gas tests', () => { - let wallet: Wallet, other: Wallet - - let loadFixture: ReturnType - - before('create fixture loader', async () => { - ;[wallet, other] = await (ethers as any).getSigners() - loadFixture = createFixtureLoader([wallet, other]) - }) - - for (const feeProtocol of [0, 6]) { - describe(feeProtocol > 0 ? 'fee is on' : 'fee is off', () => { - const startingPrice = encodePriceSqrt(100001, 100000) - const startingTick = 0 - const feeAmount = FeeAmount.MEDIUM - const tickSpacing = TICK_SPACINGS[feeAmount] - const minTick = getMinTick(tickSpacing) - const maxTick = getMaxTick(tickSpacing) - - const gasTestFixture = async ([wallet]: Wallet[]) => { - const fix = await poolFixture([wallet], waffle.provider) - - const pool = await fix.createPool(feeAmount, tickSpacing) - - const { swapExact0For1, swapToHigherPrice, mint, swapToLowerPrice } = await createPoolFunctions({ - swapTarget: fix.swapTargetCallee, - token0: fix.token0, - token1: fix.token1, - pool, - }) - - await pool.initialize(encodePriceSqrt(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 swapToHigherPrice(startingPrice, wallet.address) - await pool.advanceTime(1) - expect((await pool.slot0()).tick).to.eq(startingTick) - expect((await pool.slot0()).sqrtPriceX96).to.eq(startingPrice) - - return { pool, swapExact0For1, mint, swapToHigherPrice, swapToLowerPrice } - } - - let swapExact0For1: SwapFunction - let swapToHigherPrice: SwapToPriceFunction - let swapToLowerPrice: SwapToPriceFunction - let pool: MockTimePool - let mint: MintFunction - - beforeEach('load the fixture', async () => { - ;({ swapExact0For1, pool, mint, swapToHigherPrice, swapToLowerPrice } = 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) - }) - - 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) - }) - - 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) - await snapshotGasCost(swapExact0For1(2000, wallet.address)) - expect((await pool.slot0()).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) - await snapshotGasCost(swapExact0For1(expandTo18Decimals(1).div(10000), wallet.address)) - expect((await pool.slot0()).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) - }) - - it('first swap in block, large swap crossing several initialized ticks', async () => { - await mint(wallet.address, startingTick - 3 * tickSpacing, startingTick - tickSpacing, expandTo18Decimals(1)) - await mint( - wallet.address, - startingTick - 4 * tickSpacing, - startingTick - 2 * tickSpacing, - expandTo18Decimals(1) - ) - expect((await pool.slot0()).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 - }) - - 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 - }) - - it('second swap in block, large swap crossing several initialized ticks', async () => { - await mint(wallet.address, startingTick - 3 * tickSpacing, startingTick - tickSpacing, expandTo18Decimals(1)) - await mint( - wallet.address, - startingTick - 4 * tickSpacing, - startingTick - 2 * tickSpacing, - expandTo18Decimals(1) - ) - 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) - }) - - 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 - await snapshotGasCost(swapExact0For1(expandTo18Decimals(1), wallet.address)) - expect((await pool.slot0()).tick).to.be.lt(startingTick - 2 * tickSpacing) // we crossed the last tick - }) - - it('large swap crossing several initialized ticks after some time passes', async () => { - await mint(wallet.address, startingTick - 3 * tickSpacing, startingTick - tickSpacing, expandTo18Decimals(1)) - await mint( - wallet.address, - startingTick - 4 * tickSpacing, - startingTick - 2 * tickSpacing, - expandTo18Decimals(1) - ) - await swapExact0For1(2, wallet.address) - await pool.advanceTime(1) - await snapshotGasCost(swapExact0For1(expandTo18Decimals(1), wallet.address)) - expect((await pool.slot0()).tick).to.be.lt(startingTick - 4 * tickSpacing) - }) - - it('large swap crossing several initialized ticks second time after some time passes', async () => { - await mint(wallet.address, startingTick - 3 * tickSpacing, startingTick - tickSpacing, expandTo18Decimals(1)) - await mint( - wallet.address, - startingTick - 4 * tickSpacing, - startingTick - 2 * tickSpacing, - expandTo18Decimals(1) - ) - await swapExact0For1(expandTo18Decimals(1), wallet.address) - await swapToHigherPrice(startingPrice, wallet.address) - await pool.advanceTime(1) - await snapshotGasCost(swapExact0For1(expandTo18Decimals(1), wallet.address)) - expect((await pool.slot0()).tick).to.be.lt(tickSpacing * -4) - }) - }) - - describe('#mint', () => { - 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, () => { - it('new position mint first in range', async () => { - await snapshotGasCost(mint(wallet.address, tickLower, tickUpper, expandTo18Decimals(1))) - }) - it('add to position existing', async () => { - await mint(wallet.address, tickLower, tickUpper, expandTo18Decimals(1)) - await snapshotGasCost(mint(wallet.address, tickLower, tickUpper, expandTo18Decimals(1))) - }) - it('second position in same range', async () => { - await mint(wallet.address, tickLower, tickUpper, expandTo18Decimals(1)) - await snapshotGasCost(mint(other.address, tickLower, tickUpper, expandTo18Decimals(1))) - }) - it('add to position after some time passes', async () => { - await mint(wallet.address, tickLower, tickUpper, expandTo18Decimals(1)) - await pool.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('#increaseObservationCardinalityNext', () => { - it('grow by 1 slot', async () => { - await snapshotGasCost(pool.increaseObservationCardinalityNext(5)) - }) - it('no op', async () => { - await snapshotGasCost(pool.increaseObservationCardinalityNext(3)) - }) - }) - - describe('#snapshotCumulativesInside', () => { - it('tick inside', async () => { - await snapshotGasCost(pool.estimateGas.snapshotCumulativesInside(minTick, maxTick)) - }) - it('tick above', async () => { - await swapToHigherPrice(MAX_SQRT_RATIO.sub(1), wallet.address) - await snapshotGasCost(pool.estimateGas.snapshotCumulativesInside(minTick, maxTick)) - }) - it('tick below', async () => { - await swapToLowerPrice(MIN_SQRT_RATIO.add(1), wallet.address) - await snapshotGasCost(pool.estimateGas.snapshotCumulativesInside(minTick, maxTick)) - }) - }) - }) - } -}) diff --git a/test/Pool.spec.ts b/test/Pool.spec.ts deleted file mode 100644 index 2df795746..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, - encodePriceSqrt, - 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(encodePriceSqrt(1, 1)) - await expect(pool.initialize(encodePriceSqrt(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 = encodePriceSqrt(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(encodePriceSqrt(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 = encodePriceSqrt(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(encodePriceSqrt(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(encodePriceSqrt(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(encodePriceSqrt(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(encodePriceSqrt(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(encodePriceSqrt(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(encodePriceSqrt(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(encodePriceSqrt(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(encodePriceSqrt(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(encodePriceSqrt(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(encodePriceSqrt(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(encodePriceSqrt(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(encodePriceSqrt(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(encodePriceSqrt(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(encodePriceSqrt(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(encodePriceSqrt(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(encodePriceSqrt(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(encodePriceSqrt(1, 2), wallet.address) - await pool.advanceTime(5) - await swapToHigherPrice(encodePriceSqrt(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(encodePriceSqrt(2, 1), wallet.address) - await pool.advanceTime(5) - await swapToLowerPrice(encodePriceSqrt(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(encodePriceSqrt(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(encodePriceSqrt(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(encodePriceSqrt(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(encodePriceSqrt(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(encodePriceSqrt(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(encodePriceSqrt(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(encodePriceSqrt(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(encodePriceSqrt(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(encodePriceSqrt(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 cea3b0e8a..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, - encodePriceSqrt, - 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: encodePriceSqrt(50, 100), - }, - { - zeroForOne: false, - exactOut: false, - amount1: expandTo18Decimals(1), - sqrtPriceLimit: encodePriceSqrt(200, 100), - }, - { - zeroForOne: true, - exactOut: true, - amount1: expandTo18Decimals(1), - sqrtPriceLimit: encodePriceSqrt(50, 100), - }, - { - zeroForOne: false, - exactOut: true, - amount0: expandTo18Decimals(1), - sqrtPriceLimit: encodePriceSqrt(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: encodePriceSqrt(5, 2), - zeroForOne: false, - }, - { - sqrtPriceLimit: encodePriceSqrt(2, 5), - zeroForOne: true, - }, - { - sqrtPriceLimit: encodePriceSqrt(5, 2), - zeroForOne: true, - }, - { - sqrtPriceLimit: encodePriceSqrt(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: encodePriceSqrt(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: encodePriceSqrt(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: encodePriceSqrt(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: encodePriceSqrt(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: encodePriceSqrt(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: encodePriceSqrt(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: encodePriceSqrt(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: encodePriceSqrt(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: encodePriceSqrt(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: encodePriceSqrt(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: encodePriceSqrt(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: encodePriceSqrt(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: encodePriceSqrt(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/shared/fixtures.ts b/test/shared/fixtures.ts index 5be39ba9b..b4c02992a 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 @@ -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 f9b134d5e..fc3cdb245 100644 --- a/test/shared/utilities.ts +++ b/test/shared/utilities.ts @@ -1,7 +1,6 @@ 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 { TestERC20 } from '../../typechain/TestERC20' export const MaxUint128 = BigNumber.from(2).pow(128).sub(1) @@ -103,155 +102,121 @@ export interface PoolFunctions { flash: FlashFunction mint: MintFunction } + export function createPoolFunctions({ swapTarget, token0, token1, - pool, + fee, }: { 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, + // } } From 5a9ef73f35318b9da4fc9967803d27be11863230 Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Thu, 11 Nov 2021 17:22:37 -0500 Subject: [PATCH 07/40] add a bytecode test and an initialize test --- test/SingletonPool.spec.ts | 66 ++++++++++++++ test/SqrtPriceMath.spec.ts | 86 +++++++++---------- test/SwapMath.spec.ts | 50 +++++------ test/TickMath.spec.ts | 28 +++--- test/__snapshots__/SingletonPool.spec.ts.snap | 3 + test/shared/fixtures.ts | 2 +- test/shared/utilities.ts | 48 +++++------ 7 files changed, 176 insertions(+), 107 deletions(-) create mode 100644 test/SingletonPool.spec.ts create mode 100644 test/__snapshots__/SingletonPool.spec.ts.snap diff --git a/test/SingletonPool.spec.ts b/test/SingletonPool.spec.ts new file mode 100644 index 000000000..67e7bdb95 --- /dev/null +++ b/test/SingletonPool.spec.ts @@ -0,0 +1,66 @@ +import { Wallet } from 'ethers' +import { ethers, waffle } from 'hardhat' +import { SingletonPool, TestERC20 } from '../typechain' +import { expect } from './shared/expect' +import { tokensFixture } from './shared/fixtures' +import snapshotGasCost from './shared/snapshotGasCost' +import { encodeSqrtPriceX96, FeeAmount } from './shared/utilities' + +const createFixtureLoader = waffle.createFixtureLoader + +describe('SingletonPool', () => { + let wallet: Wallet, other: Wallet + + let singleton: SingletonPool + let tokens: { token0: TestERC20; token1: TestERC20; token2: TestERC20 } + const fixture = async () => { + const singletonPoolFactory = await ethers.getContractFactory('SingletonPool') + const tokens = await tokensFixture() + return { + singleton: (await singletonPoolFactory.deploy()) as SingletonPool, + tokens, + } + } + + let loadFixture: ReturnType + before('create fixture loader', async () => { + ;[wallet, other] = await (ethers as any).getSigners() + + loadFixture = createFixtureLoader([wallet, other]) + }) + + beforeEach('deploy fixture', async () => { + ;({ singleton, tokens } = await loadFixture(fixture)) + }) + + it('bytecode size', async () => { + expect(((await waffle.provider.getCode(singleton.address)).length - 2) / 2).to.matchSnapshot() + }) + + describe('#initialize', async () => { + it('initializes a pool', async () => { + await singleton.initialize( + { + token0: tokens.token0.address, + token1: tokens.token1.address, + fee: FeeAmount.MEDIUM, + }, + encodeSqrtPriceX96(10, 1) + ) + // todo: check the pool + }) + + it('gas cost', async () => { + await snapshotGasCost( + singleton.initialize( + { + token0: tokens.token0.address, + token1: tokens.token1.address, + fee: FeeAmount.MEDIUM, + }, + encodeSqrtPriceX96(10, 1) + ) + ) + }) + }) +}) diff --git a/test/SqrtPriceMath.spec.ts b/test/SqrtPriceMath.spec.ts index ef012459c..6d3f36e1f 100644 --- a/test/SqrtPriceMath.spec.ts +++ b/test/SqrtPriceMath.spec.ts @@ -4,7 +4,7 @@ import { SqrtPriceMathTest } from '../typechain/SqrtPriceMathTest' import { expect } from './shared/expect' import snapshotGasCost from './shared/snapshotGasCost' -import { encodePriceSqrt, expandTo18Decimals, MaxUint128 } from './shared/utilities' +import { encodeSqrtPriceX96, expandTo18Decimals, MaxUint128 } from './shared/utilities' const { constants: { MaxUint256 }, @@ -41,12 +41,12 @@ describe('SqrtPriceMath', () => { }) it('returns input price if amount in is zero and zeroForOne = true', async () => { - const price = encodePriceSqrt(1, 1) + const price = encodeSqrtPriceX96(1, 1) expect(await sqrtPriceMath.getNextSqrtPriceFromInput(price, expandTo18Decimals(1).div(10), 0, true)).to.eq(price) }) it('returns input price if amount in is zero and zeroForOne = false', async () => { - const price = encodePriceSqrt(1, 1) + const price = encodeSqrtPriceX96(1, 1) expect(await sqrtPriceMath.getNextSqrtPriceFromInput(price, expandTo18Decimals(1).div(10), 0, false)).to.eq(price) }) @@ -59,7 +59,7 @@ describe('SqrtPriceMath', () => { it('input amount of 0.1 token1', async () => { const sqrtQ = await sqrtPriceMath.getNextSqrtPriceFromInput( - encodePriceSqrt(1, 1), + encodeSqrtPriceX96(1, 1), expandTo18Decimals(1), expandTo18Decimals(1).div(10), false @@ -69,7 +69,7 @@ describe('SqrtPriceMath', () => { it('input amount of 0.1 token0', async () => { const sqrtQ = await sqrtPriceMath.getNextSqrtPriceFromInput( - encodePriceSqrt(1, 1), + encodeSqrtPriceX96(1, 1), expandTo18Decimals(1), expandTo18Decimals(1).div(10), true @@ -80,7 +80,7 @@ describe('SqrtPriceMath', () => { it('amountIn > type(uint96).max and zeroForOne = true', async () => { expect( await sqrtPriceMath.getNextSqrtPriceFromInput( - encodePriceSqrt(1, 1), + encodeSqrtPriceX96(1, 1), expandTo18Decimals(10), BigNumber.from(2).pow(100), true @@ -92,14 +92,14 @@ describe('SqrtPriceMath', () => { it('can return 1 with enough amountIn and zeroForOne = true', async () => { expect( - await sqrtPriceMath.getNextSqrtPriceFromInput(encodePriceSqrt(1, 1), 1, constants.MaxUint256.div(2), true) + await sqrtPriceMath.getNextSqrtPriceFromInput(encodeSqrtPriceX96(1, 1), 1, constants.MaxUint256.div(2), true) ).to.eq(1) }) it('zeroForOne = true gas', async () => { await snapshotGasCost( sqrtPriceMath.getGasCostOfGetNextSqrtPriceFromInput( - encodePriceSqrt(1, 1), + encodeSqrtPriceX96(1, 1), expandTo18Decimals(1), expandTo18Decimals(1).div(10), true @@ -110,7 +110,7 @@ describe('SqrtPriceMath', () => { it('zeroForOne = false gas', async () => { await snapshotGasCost( sqrtPriceMath.getGasCostOfGetNextSqrtPriceFromInput( - encodePriceSqrt(1, 1), + encodeSqrtPriceX96(1, 1), expandTo18Decimals(1), expandTo18Decimals(1).div(10), false @@ -173,12 +173,12 @@ describe('SqrtPriceMath', () => { }) it('returns input price if amount in is zero and zeroForOne = true', async () => { - const price = encodePriceSqrt(1, 1) + const price = encodeSqrtPriceX96(1, 1) expect(await sqrtPriceMath.getNextSqrtPriceFromOutput(price, expandTo18Decimals(1).div(10), 0, true)).to.eq(price) }) it('returns input price if amount in is zero and zeroForOne = false', async () => { - const price = encodePriceSqrt(1, 1) + const price = encodeSqrtPriceX96(1, 1) expect(await sqrtPriceMath.getNextSqrtPriceFromOutput(price, expandTo18Decimals(1).div(10), 0, false)).to.eq( price ) @@ -186,7 +186,7 @@ describe('SqrtPriceMath', () => { it('output amount of 0.1 token1', async () => { const sqrtQ = await sqrtPriceMath.getNextSqrtPriceFromOutput( - encodePriceSqrt(1, 1), + encodeSqrtPriceX96(1, 1), expandTo18Decimals(1), expandTo18Decimals(1).div(10), false @@ -196,7 +196,7 @@ describe('SqrtPriceMath', () => { it('output amount of 0.1 token1', async () => { const sqrtQ = await sqrtPriceMath.getNextSqrtPriceFromOutput( - encodePriceSqrt(1, 1), + encodeSqrtPriceX96(1, 1), expandTo18Decimals(1), expandTo18Decimals(1).div(10), true @@ -205,19 +205,19 @@ describe('SqrtPriceMath', () => { }) it('reverts if amountOut is impossible in zero for one direction', async () => { - await expect(sqrtPriceMath.getNextSqrtPriceFromOutput(encodePriceSqrt(1, 1), 1, constants.MaxUint256, true)).to.be - .reverted + await expect(sqrtPriceMath.getNextSqrtPriceFromOutput(encodeSqrtPriceX96(1, 1), 1, constants.MaxUint256, true)).to + .be.reverted }) it('reverts if amountOut is impossible in one for zero direction', async () => { - await expect(sqrtPriceMath.getNextSqrtPriceFromOutput(encodePriceSqrt(1, 1), 1, constants.MaxUint256, false)).to - .be.reverted + await expect(sqrtPriceMath.getNextSqrtPriceFromOutput(encodeSqrtPriceX96(1, 1), 1, constants.MaxUint256, false)) + .to.be.reverted }) it('zeroForOne = true gas', async () => { await snapshotGasCost( sqrtPriceMath.getGasCostOfGetNextSqrtPriceFromOutput( - encodePriceSqrt(1, 1), + encodeSqrtPriceX96(1, 1), expandTo18Decimals(1), expandTo18Decimals(1).div(10), true @@ -228,7 +228,7 @@ describe('SqrtPriceMath', () => { it('zeroForOne = false gas', async () => { await snapshotGasCost( sqrtPriceMath.getGasCostOfGetNextSqrtPriceFromOutput( - encodePriceSqrt(1, 1), + encodeSqrtPriceX96(1, 1), expandTo18Decimals(1), expandTo18Decimals(1).div(10), false @@ -239,28 +239,28 @@ describe('SqrtPriceMath', () => { describe('#getAmount0Delta', () => { it('returns 0 if liquidity is 0', async () => { - const amount0 = await sqrtPriceMath.getAmount0Delta(encodePriceSqrt(1, 1), encodePriceSqrt(2, 1), 0, true) + const amount0 = await sqrtPriceMath.getAmount0Delta(encodeSqrtPriceX96(1, 1), encodeSqrtPriceX96(2, 1), 0, true) expect(amount0).to.eq(0) }) it('returns 0 if prices are equal', async () => { - const amount0 = await sqrtPriceMath.getAmount0Delta(encodePriceSqrt(1, 1), encodePriceSqrt(1, 1), 0, true) + const amount0 = await sqrtPriceMath.getAmount0Delta(encodeSqrtPriceX96(1, 1), encodeSqrtPriceX96(1, 1), 0, true) expect(amount0).to.eq(0) }) it('returns 0.1 amount1 for price of 1 to 1.21', async () => { const amount0 = await sqrtPriceMath.getAmount0Delta( - encodePriceSqrt(1, 1), - encodePriceSqrt(121, 100), + encodeSqrtPriceX96(1, 1), + encodeSqrtPriceX96(121, 100), expandTo18Decimals(1), true ) expect(amount0).to.eq('90909090909090910') const amount0RoundedDown = await sqrtPriceMath.getAmount0Delta( - encodePriceSqrt(1, 1), - encodePriceSqrt(121, 100), + encodeSqrtPriceX96(1, 1), + encodeSqrtPriceX96(121, 100), expandTo18Decimals(1), false ) @@ -270,14 +270,14 @@ describe('SqrtPriceMath', () => { it('works for prices that overflow', async () => { const amount0Up = await sqrtPriceMath.getAmount0Delta( - encodePriceSqrt(BigNumber.from(2).pow(90), 1), - encodePriceSqrt(BigNumber.from(2).pow(96), 1), + encodeSqrtPriceX96(BigNumber.from(2).pow(90), 1), + encodeSqrtPriceX96(BigNumber.from(2).pow(96), 1), expandTo18Decimals(1), true ) const amount0Down = await sqrtPriceMath.getAmount0Delta( - encodePriceSqrt(BigNumber.from(2).pow(90), 1), - encodePriceSqrt(BigNumber.from(2).pow(96), 1), + encodeSqrtPriceX96(BigNumber.from(2).pow(90), 1), + encodeSqrtPriceX96(BigNumber.from(2).pow(96), 1), expandTo18Decimals(1), false ) @@ -287,8 +287,8 @@ describe('SqrtPriceMath', () => { it(`gas cost for amount0 where roundUp = true`, async () => { await snapshotGasCost( sqrtPriceMath.getGasCostOfGetAmount0Delta( - encodePriceSqrt(100, 121), - encodePriceSqrt(1, 1), + encodeSqrtPriceX96(100, 121), + encodeSqrtPriceX96(1, 1), expandTo18Decimals(1), true ) @@ -298,8 +298,8 @@ describe('SqrtPriceMath', () => { it(`gas cost for amount0 where roundUp = true`, async () => { await snapshotGasCost( sqrtPriceMath.getGasCostOfGetAmount0Delta( - encodePriceSqrt(100, 121), - encodePriceSqrt(1, 1), + encodeSqrtPriceX96(100, 121), + encodeSqrtPriceX96(1, 1), expandTo18Decimals(1), false ) @@ -309,28 +309,28 @@ describe('SqrtPriceMath', () => { describe('#getAmount1Delta', () => { it('returns 0 if liquidity is 0', async () => { - const amount1 = await sqrtPriceMath.getAmount1Delta(encodePriceSqrt(1, 1), encodePriceSqrt(2, 1), 0, true) + const amount1 = await sqrtPriceMath.getAmount1Delta(encodeSqrtPriceX96(1, 1), encodeSqrtPriceX96(2, 1), 0, true) expect(amount1).to.eq(0) }) it('returns 0 if prices are equal', async () => { - const amount1 = await sqrtPriceMath.getAmount0Delta(encodePriceSqrt(1, 1), encodePriceSqrt(1, 1), 0, true) + const amount1 = await sqrtPriceMath.getAmount0Delta(encodeSqrtPriceX96(1, 1), encodeSqrtPriceX96(1, 1), 0, true) expect(amount1).to.eq(0) }) it('returns 0.1 amount1 for price of 1 to 1.21', async () => { const amount1 = await sqrtPriceMath.getAmount1Delta( - encodePriceSqrt(1, 1), - encodePriceSqrt(121, 100), + encodeSqrtPriceX96(1, 1), + encodeSqrtPriceX96(121, 100), expandTo18Decimals(1), true ) expect(amount1).to.eq('100000000000000000') const amount1RoundedDown = await sqrtPriceMath.getAmount1Delta( - encodePriceSqrt(1, 1), - encodePriceSqrt(121, 100), + encodeSqrtPriceX96(1, 1), + encodeSqrtPriceX96(121, 100), expandTo18Decimals(1), false ) @@ -341,8 +341,8 @@ describe('SqrtPriceMath', () => { it(`gas cost for amount0 where roundUp = true`, async () => { await snapshotGasCost( sqrtPriceMath.getGasCostOfGetAmount0Delta( - encodePriceSqrt(100, 121), - encodePriceSqrt(1, 1), + encodeSqrtPriceX96(100, 121), + encodeSqrtPriceX96(1, 1), expandTo18Decimals(1), true ) @@ -352,8 +352,8 @@ describe('SqrtPriceMath', () => { it(`gas cost for amount0 where roundUp = false`, async () => { await snapshotGasCost( sqrtPriceMath.getGasCostOfGetAmount0Delta( - encodePriceSqrt(100, 121), - encodePriceSqrt(1, 1), + encodeSqrtPriceX96(100, 121), + encodeSqrtPriceX96(1, 1), expandTo18Decimals(1), false ) diff --git a/test/SwapMath.spec.ts b/test/SwapMath.spec.ts index 2dbd9205e..70a44a456 100644 --- a/test/SwapMath.spec.ts +++ b/test/SwapMath.spec.ts @@ -4,7 +4,7 @@ import { SwapMathTest } from '../typechain/SwapMathTest' import { expect } from './shared/expect' import snapshotGasCost from './shared/snapshotGasCost' -import { encodePriceSqrt, expandTo18Decimals } from './shared/utilities' +import { encodeSqrtPriceX96, expandTo18Decimals } from './shared/utilities' import { SqrtPriceMathTest } from '../typechain/SqrtPriceMathTest' describe('SwapMath', () => { @@ -19,8 +19,8 @@ describe('SwapMath', () => { describe('#computeSwapStep', () => { it('exact amount in that gets capped at price target in one for zero', async () => { - const price = encodePriceSqrt(1, 1) - const priceTarget = encodePriceSqrt(101, 100) + const price = encodeSqrtPriceX96(1, 1) + const priceTarget = encodeSqrtPriceX96(101, 100) const liquidity = expandTo18Decimals(2) const amount = expandTo18Decimals(1) const fee = 600 @@ -51,8 +51,8 @@ describe('SwapMath', () => { }) it('exact amount out that gets capped at price target in one for zero', async () => { - const price = encodePriceSqrt(1, 1) - const priceTarget = encodePriceSqrt(101, 100) + const price = encodeSqrtPriceX96(1, 1) + const priceTarget = encodeSqrtPriceX96(101, 100) const liquidity = expandTo18Decimals(2) const amount = expandTo18Decimals(1).mul(-1) const fee = 600 @@ -83,8 +83,8 @@ describe('SwapMath', () => { }) it('exact amount in that is fully spent in one for zero', async () => { - const price = encodePriceSqrt(1, 1) - const priceTarget = encodePriceSqrt(1000, 100) + const price = encodeSqrtPriceX96(1, 1) + const priceTarget = encodeSqrtPriceX96(1000, 100) const liquidity = expandTo18Decimals(2) const amount = expandTo18Decimals(1) const fee = 600 @@ -115,8 +115,8 @@ describe('SwapMath', () => { }) it('exact amount out that is fully received in one for zero', async () => { - const price = encodePriceSqrt(1, 1) - const priceTarget = encodePriceSqrt(10000, 100) + const price = encodeSqrtPriceX96(1, 1) + const priceTarget = encodeSqrtPriceX96(10000, 100) const liquidity = expandTo18Decimals(2) const amount = expandTo18Decimals(1).mul(-1) const fee = 600 @@ -234,8 +234,8 @@ describe('SwapMath', () => { it('swap one for zero exact in capped', async () => { await snapshotGasCost( swapMath.getGasCostOfComputeSwapStep( - encodePriceSqrt(1, 1), - encodePriceSqrt(101, 100), + encodeSqrtPriceX96(1, 1), + encodeSqrtPriceX96(101, 100), expandTo18Decimals(2), expandTo18Decimals(1), 600 @@ -245,8 +245,8 @@ describe('SwapMath', () => { it('swap zero for one exact in capped', async () => { await snapshotGasCost( swapMath.getGasCostOfComputeSwapStep( - encodePriceSqrt(1, 1), - encodePriceSqrt(99, 100), + encodeSqrtPriceX96(1, 1), + encodeSqrtPriceX96(99, 100), expandTo18Decimals(2), expandTo18Decimals(1), 600 @@ -256,8 +256,8 @@ describe('SwapMath', () => { it('swap one for zero exact out capped', async () => { await snapshotGasCost( swapMath.getGasCostOfComputeSwapStep( - encodePriceSqrt(1, 1), - encodePriceSqrt(101, 100), + encodeSqrtPriceX96(1, 1), + encodeSqrtPriceX96(101, 100), expandTo18Decimals(2), expandTo18Decimals(1).mul(-1), 600 @@ -267,8 +267,8 @@ describe('SwapMath', () => { it('swap zero for one exact out capped', async () => { await snapshotGasCost( swapMath.getGasCostOfComputeSwapStep( - encodePriceSqrt(1, 1), - encodePriceSqrt(99, 100), + encodeSqrtPriceX96(1, 1), + encodeSqrtPriceX96(99, 100), expandTo18Decimals(2), expandTo18Decimals(1).mul(-1), 600 @@ -278,8 +278,8 @@ describe('SwapMath', () => { it('swap one for zero exact in partial', async () => { await snapshotGasCost( swapMath.getGasCostOfComputeSwapStep( - encodePriceSqrt(1, 1), - encodePriceSqrt(1010, 100), + encodeSqrtPriceX96(1, 1), + encodeSqrtPriceX96(1010, 100), expandTo18Decimals(2), 1000, 600 @@ -289,8 +289,8 @@ describe('SwapMath', () => { it('swap zero for one exact in partial', async () => { await snapshotGasCost( swapMath.getGasCostOfComputeSwapStep( - encodePriceSqrt(1, 1), - encodePriceSqrt(99, 1000), + encodeSqrtPriceX96(1, 1), + encodeSqrtPriceX96(99, 1000), expandTo18Decimals(2), 1000, 600 @@ -300,8 +300,8 @@ describe('SwapMath', () => { it('swap one for zero exact out partial', async () => { await snapshotGasCost( swapMath.getGasCostOfComputeSwapStep( - encodePriceSqrt(1, 1), - encodePriceSqrt(1010, 100), + encodeSqrtPriceX96(1, 1), + encodeSqrtPriceX96(1010, 100), expandTo18Decimals(2), 1000, 600 @@ -311,8 +311,8 @@ describe('SwapMath', () => { it('swap zero for one exact out partial', async () => { await snapshotGasCost( swapMath.getGasCostOfComputeSwapStep( - encodePriceSqrt(1, 1), - encodePriceSqrt(99, 1000), + encodeSqrtPriceX96(1, 1), + encodeSqrtPriceX96(99, 1000), expandTo18Decimals(2), 1000, 600 diff --git a/test/TickMath.spec.ts b/test/TickMath.spec.ts index 0f5f5bbd5..821986ee4 100644 --- a/test/TickMath.spec.ts +++ b/test/TickMath.spec.ts @@ -3,7 +3,7 @@ import { ethers } from 'hardhat' import { TickMathTest } from '../typechain/TickMathTest' import { expect } from './shared/expect' import snapshotGasCost from './shared/snapshotGasCost' -import { encodePriceSqrt, MIN_SQRT_RATIO, MAX_SQRT_RATIO } from './shared/utilities' +import { encodeSqrtPriceX96, MIN_SQRT_RATIO, MAX_SQRT_RATIO } from './shared/utilities' import Decimal from 'decimal.js' const MIN_TICK = -887272 @@ -41,11 +41,11 @@ describe('TickMath', () => { }) it('min tick ratio is less than js implementation', async () => { - expect(await tickMath.getSqrtRatioAtTick(MIN_TICK)).to.be.lt(encodePriceSqrt(1, BigNumber.from(2).pow(127))) + expect(await tickMath.getSqrtRatioAtTick(MIN_TICK)).to.be.lt(encodeSqrtPriceX96(1, BigNumber.from(2).pow(127))) }) it('max tick ratio is greater than js implementation', async () => { - expect(await tickMath.getSqrtRatioAtTick(MAX_TICK)).to.be.gt(encodePriceSqrt(BigNumber.from(2).pow(127), 1)) + expect(await tickMath.getSqrtRatioAtTick(MAX_TICK)).to.be.gt(encodeSqrtPriceX96(BigNumber.from(2).pow(127), 1)) }) it('max tick', async () => { @@ -114,17 +114,17 @@ describe('TickMath', () => { for (const ratio of [ MIN_SQRT_RATIO, - encodePriceSqrt(BigNumber.from(10).pow(12), 1), - encodePriceSqrt(BigNumber.from(10).pow(6), 1), - encodePriceSqrt(1, 64), - encodePriceSqrt(1, 8), - encodePriceSqrt(1, 2), - encodePriceSqrt(1, 1), - encodePriceSqrt(2, 1), - encodePriceSqrt(8, 1), - encodePriceSqrt(64, 1), - encodePriceSqrt(1, BigNumber.from(10).pow(6)), - encodePriceSqrt(1, BigNumber.from(10).pow(12)), + encodeSqrtPriceX96(BigNumber.from(10).pow(12), 1), + encodeSqrtPriceX96(BigNumber.from(10).pow(6), 1), + encodeSqrtPriceX96(1, 64), + encodeSqrtPriceX96(1, 8), + encodeSqrtPriceX96(1, 2), + encodeSqrtPriceX96(1, 1), + encodeSqrtPriceX96(2, 1), + encodeSqrtPriceX96(8, 1), + encodeSqrtPriceX96(64, 1), + encodeSqrtPriceX96(1, BigNumber.from(10).pow(6)), + encodeSqrtPriceX96(1, BigNumber.from(10).pow(12)), MAX_SQRT_RATIO.sub(1), ]) { describe(`ratio ${ratio}`, () => { diff --git a/test/__snapshots__/SingletonPool.spec.ts.snap b/test/__snapshots__/SingletonPool.spec.ts.snap new file mode 100644 index 000000000..2b4cfe135 --- /dev/null +++ b/test/__snapshots__/SingletonPool.spec.ts.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SingletonPool bytecode size 1`] = `16036`; diff --git a/test/shared/fixtures.ts b/test/shared/fixtures.ts index b4c02992a..dfb9e668f 100644 --- a/test/shared/fixtures.ts +++ b/test/shared/fixtures.ts @@ -8,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 diff --git a/test/shared/utilities.ts b/test/shared/utilities.ts index fc3cdb245..86d1b9371 100644 --- a/test/shared/utilities.ts +++ b/test/shared/utilities.ts @@ -32,33 +32,10 @@ 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 -export function encodePriceSqrt(reserve1: BigNumberish, reserve0: BigNumberish): BigNumber { +export function encodeSqrtPriceX96(reserve1: BigNumberish, reserve0: BigNumberish): BigNumber { return BigNumber.from( new bn(reserve1.toString()) .div(reserve0.toString()) @@ -73,6 +50,29 @@ 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.solidityPack( + ['tuple(address,address,uint24)'], + [ + [ + typeof token0 === 'string' ? token0 : token0.address, + typeof token1 === 'string' ? token1 : token1.address, + fee, + ], + ] + ) + ) +} + export type SwapFunction = ( amount: BigNumberish, to: Wallet | string, From e1e1cb549bda58a5f6bed587078477c12bbca402 Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Thu, 11 Nov 2021 17:22:47 -0500 Subject: [PATCH 08/40] initialize gas test --- test/__snapshots__/SingletonPool.spec.ts.snap | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/__snapshots__/SingletonPool.spec.ts.snap b/test/__snapshots__/SingletonPool.spec.ts.snap index 2b4cfe135..57af96722 100644 --- a/test/__snapshots__/SingletonPool.spec.ts.snap +++ b/test/__snapshots__/SingletonPool.spec.ts.snap @@ -1,3 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`SingletonPool #initialize gas cost 1`] = `70533`; + exports[`SingletonPool bytecode size 1`] = `16036`; From 424eb1bbaa3849b4c32f49c23c7324a87cd65621 Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Thu, 11 Nov 2021 17:26:55 -0500 Subject: [PATCH 09/40] remove unused contracts --- contracts/interfaces/IPool.sol | 17 --- contracts/interfaces/pool/IPoolActions.sol | 103 ------------- .../interfaces/pool/IPoolDerivedState.sol | 40 ----- contracts/interfaces/pool/IPoolEvents.sol | 121 --------------- contracts/interfaces/pool/IPoolImmutables.sol | 35 ----- .../interfaces/pool/IPoolOwnerActions.sol | 23 --- contracts/interfaces/pool/IPoolState.sol | 117 --------------- contracts/libraries/Pool.sol | 2 - contracts/test/MultihopTester.sol | 82 ---------- contracts/test/PoolSwapTest.sol | 50 ------- contracts/test/ReentrancyTester.sol | 54 ------- contracts/test/SwapPaymentTest.sol | 35 ----- contracts/test/SwapTarget.sol | 140 ------------------ 13 files changed, 819 deletions(-) delete mode 100644 contracts/interfaces/IPool.sol delete mode 100644 contracts/interfaces/pool/IPoolActions.sol delete mode 100644 contracts/interfaces/pool/IPoolDerivedState.sol delete mode 100644 contracts/interfaces/pool/IPoolEvents.sol delete mode 100644 contracts/interfaces/pool/IPoolImmutables.sol delete mode 100644 contracts/interfaces/pool/IPoolOwnerActions.sol delete mode 100644 contracts/interfaces/pool/IPoolState.sol delete mode 100644 contracts/test/MultihopTester.sol delete mode 100644 contracts/test/PoolSwapTest.sol delete mode 100644 contracts/test/ReentrancyTester.sol delete mode 100644 contracts/test/SwapPaymentTest.sol delete mode 100644 contracts/test/SwapTarget.sol 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/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 index 1b659afd1..3272c521d 100644 --- a/contracts/libraries/Pool.sol +++ b/contracts/libraries/Pool.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.9; -import {IPoolEvents} from '../interfaces/pool/IPoolEvents.sol'; - import {SafeCast} from './SafeCast.sol'; import {Tick} from './Tick.sol'; import {TickBitmap} from './TickBitmap.sol'; diff --git a/contracts/test/MultihopTester.sol b/contracts/test/MultihopTester.sol deleted file mode 100644 index 43553164e..000000000 --- a/contracts/test/MultihopTester.sol +++ /dev/null @@ -1,82 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity =0.8.9; - -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/PoolSwapTest.sol b/contracts/test/PoolSwapTest.sol deleted file mode 100644 index cf2fd6458..000000000 --- a/contracts/test/PoolSwapTest.sol +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity =0.8.9; - -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 - ) - external - returns ( - int256 amount0Delta, - int256 amount1Delta, - uint160 nextSqrtRatio - ) - { - (amount0Delta, amount1Delta) = IPool(pool).swap( - address(0), - zeroForOne, - amountSpecified, - sqrtPriceLimitX96, - abi.encode(msg.sender) - ); - - (nextSqrtRatio, , , , , , ) = IPool(pool).slot0(); - } - - 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)); - } - } -} diff --git a/contracts/test/ReentrancyTester.sol b/contracts/test/ReentrancyTester.sol deleted file mode 100644 index 27f5fdd47..000000000 --- a/contracts/test/ReentrancyTester.sol +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity =0.8.9; - -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 ae2c4e2bd..000000000 --- a/contracts/test/SwapPaymentTest.sol +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity =0.8.9; - -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 658ce2d53..000000000 --- a/contracts/test/SwapTarget.sol +++ /dev/null @@ -1,140 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity =0.8.9; - -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); - } -} From 2a81316b1dcb3bf41a8185a8e1c9f42b96da3955 Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Thu, 11 Nov 2021 19:14:52 -0500 Subject: [PATCH 10/40] check the pool is initialized, mint test --- test/SingletonPool.spec.ts | 88 ++++++++++++++++++- test/__snapshots__/SingletonPool.spec.ts.snap | 4 +- test/shared/utilities.ts | 14 +-- 3 files changed, 92 insertions(+), 14 deletions(-) diff --git a/test/SingletonPool.spec.ts b/test/SingletonPool.spec.ts index 67e7bdb95..4bb184900 100644 --- a/test/SingletonPool.spec.ts +++ b/test/SingletonPool.spec.ts @@ -4,11 +4,11 @@ import { SingletonPool, TestERC20 } from '../typechain' import { expect } from './shared/expect' import { tokensFixture } from './shared/fixtures' import snapshotGasCost from './shared/snapshotGasCost' -import { encodeSqrtPriceX96, FeeAmount } from './shared/utilities' +import { encodeSqrtPriceX96, FeeAmount, getPoolId } from './shared/utilities' const createFixtureLoader = waffle.createFixtureLoader -describe('SingletonPool', () => { +describe.only('SingletonPool', () => { let wallet: Wallet, other: Wallet let singleton: SingletonPool @@ -47,7 +47,17 @@ describe('SingletonPool', () => { }, encodeSqrtPriceX96(10, 1) ) - // todo: check the pool + + const { + slot0: { sqrtPriceX96 }, + } = await singleton.pools( + getPoolId({ + token0: tokens.token0.address, + token1: tokens.token1.address, + fee: FeeAmount.MEDIUM, + }) + ) + expect(sqrtPriceX96).to.eq(encodeSqrtPriceX96(10, 1)) }) it('gas cost', async () => { @@ -63,4 +73,76 @@ describe('SingletonPool', () => { ) }) }) + + describe('#mint', async () => { + it('reverts if pool not initialized', async () => { + await expect( + singleton.mint( + { + token0: tokens.token0.address, + token1: tokens.token1.address, + fee: FeeAmount.MEDIUM, + }, + { + tickLower: 0, + tickUpper: 60, + amount: 100, + recipient: wallet.address, + } + ) + ).to.be.revertedWith('LOK') + }) + + it('succeeds if pool is initialized', async () => { + await singleton.initialize( + { + token0: tokens.token0.address, + token1: tokens.token1.address, + fee: FeeAmount.MEDIUM, + }, + encodeSqrtPriceX96(1, 1) + ) + + await singleton.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 singleton.initialize( + { + token0: tokens.token0.address, + token1: tokens.token1.address, + fee: FeeAmount.MEDIUM, + }, + encodeSqrtPriceX96(1, 1) + ) + + await snapshotGasCost( + singleton.mint( + { + token0: tokens.token0.address, + token1: tokens.token1.address, + fee: FeeAmount.MEDIUM, + }, + { + tickLower: 0, + tickUpper: 60, + amount: 100, + recipient: wallet.address, + } + ) + ) + }) + }) }) diff --git a/test/__snapshots__/SingletonPool.spec.ts.snap b/test/__snapshots__/SingletonPool.spec.ts.snap index 57af96722..2774489ce 100644 --- a/test/__snapshots__/SingletonPool.spec.ts.snap +++ b/test/__snapshots__/SingletonPool.spec.ts.snap @@ -1,5 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`SingletonPool #initialize gas cost 1`] = `70533`; +exports[`SingletonPool #initialize gas cost 1`] = `70545`; + +exports[`SingletonPool #mint gas cost 1`] = `221787`; exports[`SingletonPool bytecode size 1`] = `16036`; diff --git a/test/shared/utilities.ts b/test/shared/utilities.ts index 86d1b9371..dcb4baa2c 100644 --- a/test/shared/utilities.ts +++ b/test/shared/utilities.ts @@ -1,5 +1,5 @@ import bn from 'bignumber.js' -import { BigNumber, BigNumberish, constants, Contract, ContractTransaction, utils, Wallet } from 'ethers' +import { BigNumber, BigNumberish, Contract, ContractTransaction, utils, Wallet } from 'ethers' import { SwapTarget } from '../../typechain/SwapTarget' import { TestERC20 } from '../../typechain/TestERC20' @@ -60,15 +60,9 @@ export function getPoolId({ fee: number }): string { return utils.keccak256( - utils.solidityPack( - ['tuple(address,address,uint24)'], - [ - [ - typeof token0 === 'string' ? token0 : token0.address, - typeof token1 === 'string' ? token1 : token1.address, - fee, - ], - ] + utils.defaultAbiCoder.encode( + ['address', 'address', 'uint24'], + [typeof token0 === 'string' ? token0 : token0.address, typeof token1 === 'string' ? token1 : token1.address, fee] ) ) } From a5d5675fe8d26af292854f4059db2428764519a3 Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Fri, 12 Nov 2021 08:14:00 -0500 Subject: [PATCH 11/40] swap gas test --- contracts/SingletonPool.sol | 2 +- contracts/libraries/LowGasERC20.sol | 18 ----- contracts/libraries/Pool.sol | 3 +- test/SingletonPool.spec.ts | 71 +++++++++++++++++++ test/__snapshots__/SingletonPool.spec.ts.snap | 2 + 5 files changed, 75 insertions(+), 21 deletions(-) delete mode 100644 contracts/libraries/LowGasERC20.sol diff --git a/contracts/SingletonPool.sol b/contracts/SingletonPool.sol index 9fdd45a32..d473a99aa 100644 --- a/contracts/SingletonPool.sol +++ b/contracts/SingletonPool.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.9; +pragma solidity ^0.8.10; import {Pool} from './libraries/Pool.sol'; import {SafeCast} from './libraries/SafeCast.sol'; diff --git a/contracts/libraries/LowGasERC20.sol b/contracts/libraries/LowGasERC20.sol deleted file mode 100644 index 301e27ab1..000000000 --- a/contracts/libraries/LowGasERC20.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.9; - -import {IERC20Minimal} from '../interfaces/external/IERC20Minimal.sol'; - -/// @notice Helper functions for more cheaply interacting with ERC20 contracts -library LowGasERC20 { - /// @notice Get the balance of this address for a token with minimum gas - /// @dev This function is gas optimized to avoid a redundant extcodesize check in addition to the returndatasize - /// check - function balance(IERC20Minimal token) internal view returns (uint256) { - (bool success, bytes memory data) = address(token).staticcall( - abi.encodeWithSelector(IERC20Minimal.balanceOf.selector, address(this)) - ); - require(success && data.length >= 32); - return abi.decode(data, (uint256)); - } -} diff --git a/contracts/libraries/Pool.sol b/contracts/libraries/Pool.sol index 3272c521d..c415efc3c 100644 --- a/contracts/libraries/Pool.sol +++ b/contracts/libraries/Pool.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.9; +pragma solidity =0.8.10; import {SafeCast} from './SafeCast.sol'; import {Tick} from './Tick.sol'; @@ -12,7 +12,6 @@ import {TransferHelper} from './TransferHelper.sol'; import {TickMath} from './TickMath.sol'; import {SqrtPriceMath} from './SqrtPriceMath.sol'; import {SwapMath} from './SwapMath.sol'; -import {LowGasERC20} from './LowGasERC20.sol'; import {IERC20Minimal} from '../interfaces/external/IERC20Minimal.sol'; import {IMintCallback} from '../interfaces/callback/IMintCallback.sol'; diff --git a/test/SingletonPool.spec.ts b/test/SingletonPool.spec.ts index 4bb184900..f261dc916 100644 --- a/test/SingletonPool.spec.ts +++ b/test/SingletonPool.spec.ts @@ -145,4 +145,75 @@ describe.only('SingletonPool', () => { ) }) }) + + describe('#swap', () => { + it('fails if pool is not initialized', async () => { + await expect( + singleton.swap( + { + token0: tokens.token0.address, + token1: tokens.token1.address, + fee: FeeAmount.MEDIUM, + }, + { + data: '0x', + recipient: wallet.address, + amountSpecified: 100, + sqrtPriceLimitX96: encodeSqrtPriceX96(100, 1), + zeroForOne: true, + } + ) + ).to.be.revertedWith('LOK') + }) + it('succeeds if pool is not initialized', async () => { + await singleton.initialize( + { + token0: tokens.token0.address, + token1: tokens.token1.address, + fee: FeeAmount.MEDIUM, + }, + encodeSqrtPriceX96(1, 1) + ) + await singleton.swap( + { + token0: tokens.token0.address, + token1: tokens.token1.address, + fee: FeeAmount.MEDIUM, + }, + { + data: '0x', + recipient: wallet.address, + amountSpecified: 100, + sqrtPriceLimitX96: encodeSqrtPriceX96(1, 100), + zeroForOne: true, + } + ) + }) + it('gas', async () => { + await singleton.initialize( + { + token0: tokens.token0.address, + token1: tokens.token1.address, + fee: FeeAmount.MEDIUM, + }, + encodeSqrtPriceX96(1, 1) + ) + await snapshotGasCost( + singleton.swap( + { + token0: tokens.token0.address, + token1: tokens.token1.address, + fee: FeeAmount.MEDIUM, + }, + { + data: '0x', + recipient: wallet.address, + amountSpecified: 100, + sqrtPriceLimitX96: encodeSqrtPriceX96(1, 100), + zeroForOne: true, + } + ) + ) + }) + }) }) diff --git a/test/__snapshots__/SingletonPool.spec.ts.snap b/test/__snapshots__/SingletonPool.spec.ts.snap index 2774489ce..a153c9552 100644 --- a/test/__snapshots__/SingletonPool.spec.ts.snap +++ b/test/__snapshots__/SingletonPool.spec.ts.snap @@ -4,4 +4,6 @@ exports[`SingletonPool #initialize gas cost 1`] = `70545`; exports[`SingletonPool #mint gas cost 1`] = `221787`; +exports[`SingletonPool #swap gas 1`] = `74554`; + exports[`SingletonPool bytecode size 1`] = `16036`; From 5dbbfa5cfed49dd92782939c8d0a6b89470c9668 Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Fri, 12 Nov 2021 08:49:42 -0500 Subject: [PATCH 12/40] move the struct to where it's used --- contracts/SingletonPool.sol | 24 ++++++++++++++++++------ contracts/libraries/Pool.sol | 11 ----------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/contracts/SingletonPool.sol b/contracts/SingletonPool.sol index d473a99aa..ab5c5ee37 100644 --- a/contracts/SingletonPool.sol +++ b/contracts/SingletonPool.sol @@ -4,10 +4,22 @@ pragma solidity ^0.8.10; import {Pool} from './libraries/Pool.sol'; import {SafeCast} from './libraries/SafeCast.sol'; +import {IERC20Minimal} from './interfaces/external/IERC20Minimal.sol'; + contract SingletonPool { using SafeCast for *; using Pool for *; + /// @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; + } + mapping(bytes32 => Pool.State) public pools; /// @dev For mocking in unit tests @@ -15,16 +27,16 @@ contract SingletonPool { return uint32(block.timestamp); } - function _getPool(Pool.Key memory key) private view returns (Pool.State storage) { + function _getPool(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(Pool.Key memory key, uint160 sqrtPriceX96) external { + function initialize(PoolKey memory key, uint160 sqrtPriceX96) external { _getPool(key).initialize(_blockTimestamp(), sqrtPriceX96); } - function increaseObservationCardinalityNext(Pool.Key memory key, uint16 observationCardinalityNext) + function increaseObservationCardinalityNext(PoolKey memory key, uint16 observationCardinalityNext) external returns (uint16 observationCardinalityNextOld, uint16 observationCardinalityNextNew) { @@ -43,7 +55,7 @@ contract SingletonPool { } /// @dev Mint some liquidity for the given pool - function mint(Pool.Key memory key, MintParams memory params) external returns (uint256 amount0, uint256 amount1) { + function mint(PoolKey memory key, MintParams memory params) external returns (uint256 amount0, uint256 amount1) { require(params.amount > 0); // todo: where to get maxLiquidityPerTick, tickSpacing, probably from storage @@ -74,7 +86,7 @@ contract SingletonPool { } /// @dev Mint some liquidity for the given pool - function burn(Pool.Key memory key, BurnParams memory params) external returns (uint256 amount0, uint256 amount1) { + function burn(PoolKey memory key, BurnParams memory params) external returns (uint256 amount0, uint256 amount1) { require(params.amount > 0); // todo: where to get maxLiquidityPerTick, tickSpacing, probably from storage @@ -104,7 +116,7 @@ contract SingletonPool { bytes data; } - function swap(Pool.Key memory key, SwapParams memory params) external returns (int256 amount0, int256 amount1) { + function swap(PoolKey memory key, SwapParams memory params) external returns (int256 amount0, int256 amount1) { Pool.SwapResult memory result = _getPool(key).swap( Pool.SwapParams({ time: _blockTimestamp(), diff --git a/contracts/libraries/Pool.sol b/contracts/libraries/Pool.sol index c415efc3c..01f6b9aa2 100644 --- a/contracts/libraries/Pool.sol +++ b/contracts/libraries/Pool.sol @@ -13,7 +13,6 @@ import {TickMath} from './TickMath.sol'; import {SqrtPriceMath} from './SqrtPriceMath.sol'; import {SwapMath} from './SwapMath.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'; @@ -26,16 +25,6 @@ library Pool { using Position for Position.Info; using Oracle for Oracle.Observation[65535]; - /// @notice Returns the key for identifying a pool - struct Key { - /// @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; - } - struct Slot0 { // the current price uint160 sqrtPriceX96; From a14e662317df1f541ccb2aa3842e1d53cf77f88c Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Fri, 12 Nov 2021 09:09:39 -0500 Subject: [PATCH 13/40] add some logic for max liquidity per tick and tick spacings --- .../{SingletonPool.sol => PoolManager.sol} | 44 +++++++++++++++---- contracts/Vault.sol | 13 ++++++ contracts/libraries/Pool.sol | 5 --- test/SingletonPool.spec.ts | 10 ++--- test/__snapshots__/SingletonPool.spec.ts.snap | 8 ++-- 5 files changed, 58 insertions(+), 22 deletions(-) rename contracts/{SingletonPool.sol => PoolManager.sol} (79%) create mode 100644 contracts/Vault.sol diff --git a/contracts/SingletonPool.sol b/contracts/PoolManager.sol similarity index 79% rename from contracts/SingletonPool.sol rename to contracts/PoolManager.sol index ab5c5ee37..13964ae8d 100644 --- a/contracts/SingletonPool.sol +++ b/contracts/PoolManager.sol @@ -2,11 +2,13 @@ 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'; -contract SingletonPool { +/// @notice Holds the state for all pools +contract PoolManager { using SafeCast for *; using Pool for *; @@ -20,7 +22,28 @@ contract SingletonPool { uint24 fee; } + struct FeeConfig { + int24 tickSpacing; + uint128 maxLiquidityPerTick; + } + mapping(bytes32 => Pool.State) public pools; + mapping(uint24 => FeeConfig) public 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) { @@ -58,7 +81,8 @@ contract SingletonPool { function mint(PoolKey memory key, MintParams memory params) external returns (uint256 amount0, uint256 amount1) { require(params.amount > 0); - // todo: where to get maxLiquidityPerTick, tickSpacing, probably from storage + FeeConfig memory config = configs[key.fee]; + Pool.ModifyPositionResult memory result = _getPool(key).modifyPosition( Pool.ModifyPositionParams({ owner: params.recipient, @@ -66,8 +90,8 @@ contract SingletonPool { tickUpper: params.tickUpper, liquidityDelta: int256(uint256(params.amount)).toInt128(), time: _blockTimestamp(), - maxLiquidityPerTick: type(uint128).max, - tickSpacing: 60 + maxLiquidityPerTick: config.maxLiquidityPerTick, + tickSpacing: config.tickSpacing }) ); @@ -89,6 +113,8 @@ contract SingletonPool { function burn(PoolKey memory key, BurnParams memory params) external returns (uint256 amount0, uint256 amount1) { require(params.amount > 0); + FeeConfig memory config = configs[key.fee]; + // todo: where to get maxLiquidityPerTick, tickSpacing, probably from storage Pool.ModifyPositionResult memory result = _getPool(key).modifyPosition( Pool.ModifyPositionParams({ @@ -97,8 +123,8 @@ contract SingletonPool { tickUpper: params.tickUpper, liquidityDelta: -int256(uint256(params.amount)).toInt128(), time: _blockTimestamp(), - maxLiquidityPerTick: type(uint128).max, - tickSpacing: 60 + maxLiquidityPerTick: config.maxLiquidityPerTick, + tickSpacing: config.tickSpacing }) ); @@ -117,16 +143,18 @@ contract SingletonPool { } function swap(PoolKey memory key, SwapParams memory params) external returns (int256 amount0, int256 amount1) { + FeeConfig memory config = configs[key.fee]; + Pool.SwapResult memory result = _getPool(key).swap( Pool.SwapParams({ time: _blockTimestamp(), - fee: key.fee, recipient: params.recipient, zeroForOne: params.zeroForOne, amountSpecified: params.amountSpecified, sqrtPriceLimitX96: params.sqrtPriceLimitX96, data: params.data, - tickSpacing: 60 + fee: key.fee, + tickSpacing: config.tickSpacing }) ); diff --git a/contracts/Vault.sol b/contracts/Vault.sol new file mode 100644 index 000000000..39c38b381 --- /dev/null +++ b/contracts/Vault.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.9; + +import './PoolManager.sol'; + +/// @notice Manages the balances for each pool +contract Vault { + PoolManager public immutable manager; + + constructor(PoolManager _manager) { + manager = _manager; + } +} diff --git a/contracts/libraries/Pool.sol b/contracts/libraries/Pool.sol index 01f6b9aa2..9cddbe9a8 100644 --- a/contracts/libraries/Pool.sol +++ b/contracts/libraries/Pool.sol @@ -8,15 +8,10 @@ import {Position} from './Position.sol'; import {Oracle} from './Oracle.sol'; import {FullMath} from './FullMath.sol'; import {FixedPoint128} from './FixedPoint128.sol'; -import {TransferHelper} from './TransferHelper.sol'; import {TickMath} from './TickMath.sol'; import {SqrtPriceMath} from './SqrtPriceMath.sol'; import {SwapMath} from './SwapMath.sol'; -import {IMintCallback} from '../interfaces/callback/IMintCallback.sol'; -import {ISwapCallback} from '../interfaces/callback/ISwapCallback.sol'; -import {IFlashCallback} from '../interfaces/callback/IFlashCallback.sol'; - library Pool { using SafeCast for *; using Tick for mapping(int24 => Tick.Info); diff --git a/test/SingletonPool.spec.ts b/test/SingletonPool.spec.ts index f261dc916..3a6d7ddbb 100644 --- a/test/SingletonPool.spec.ts +++ b/test/SingletonPool.spec.ts @@ -1,6 +1,6 @@ import { Wallet } from 'ethers' import { ethers, waffle } from 'hardhat' -import { SingletonPool, TestERC20 } from '../typechain' +import { PoolManager, TestERC20 } from '../typechain' import { expect } from './shared/expect' import { tokensFixture } from './shared/fixtures' import snapshotGasCost from './shared/snapshotGasCost' @@ -8,16 +8,16 @@ import { encodeSqrtPriceX96, FeeAmount, getPoolId } from './shared/utilities' const createFixtureLoader = waffle.createFixtureLoader -describe.only('SingletonPool', () => { +describe.only('PoolManager', () => { let wallet: Wallet, other: Wallet - let singleton: SingletonPool + let singleton: PoolManager let tokens: { token0: TestERC20; token1: TestERC20; token2: TestERC20 } const fixture = async () => { - const singletonPoolFactory = await ethers.getContractFactory('SingletonPool') + const singletonPoolFactory = await ethers.getContractFactory('PoolManager') const tokens = await tokensFixture() return { - singleton: (await singletonPoolFactory.deploy()) as SingletonPool, + singleton: (await singletonPoolFactory.deploy()) as PoolManager, tokens, } } diff --git a/test/__snapshots__/SingletonPool.spec.ts.snap b/test/__snapshots__/SingletonPool.spec.ts.snap index a153c9552..c8a149f33 100644 --- a/test/__snapshots__/SingletonPool.spec.ts.snap +++ b/test/__snapshots__/SingletonPool.spec.ts.snap @@ -1,9 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`SingletonPool #initialize gas cost 1`] = `70545`; +exports[`PoolManager #initialize gas cost 1`] = `70598`; -exports[`SingletonPool #mint gas cost 1`] = `221787`; +exports[`PoolManager #mint gas cost 1`] = `224180`; -exports[`SingletonPool #swap gas 1`] = `74554`; +exports[`PoolManager #swap gas 1`] = `76815`; -exports[`SingletonPool bytecode size 1`] = `16036`; +exports[`PoolManager bytecode size 1`] = `16423`; From 29f32f663ca093b1af7585e91ae652d17f77b2ca Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Fri, 12 Nov 2021 10:30:37 -0500 Subject: [PATCH 14/40] get all the code into the pool manager --- contracts/PoolManager.sol | 22 ++++- contracts/libraries/Pool.sol | 99 ++++++++++--------- test/__snapshots__/SingletonPool.spec.ts.snap | 2 +- 3 files changed, 73 insertions(+), 50 deletions(-) diff --git a/contracts/PoolManager.sol b/contracts/PoolManager.sol index 13964ae8d..5aa32b68f 100644 --- a/contracts/PoolManager.sol +++ b/contracts/PoolManager.sol @@ -83,7 +83,7 @@ contract PoolManager { FeeConfig memory config = configs[key.fee]; - Pool.ModifyPositionResult memory result = _getPool(key).modifyPosition( + Pool.BalanceDelta memory result = _getPool(key).modifyPosition( Pool.ModifyPositionParams({ owner: params.recipient, tickLower: params.tickLower, @@ -116,7 +116,7 @@ contract PoolManager { FeeConfig memory config = configs[key.fee]; // todo: where to get maxLiquidityPerTick, tickSpacing, probably from storage - Pool.ModifyPositionResult memory result = _getPool(key).modifyPosition( + Pool.BalanceDelta memory result = _getPool(key).modifyPosition( Pool.ModifyPositionParams({ owner: msg.sender, tickLower: params.tickLower, @@ -145,7 +145,7 @@ contract PoolManager { function swap(PoolKey memory key, SwapParams memory params) external returns (int256 amount0, int256 amount1) { FeeConfig memory config = configs[key.fee]; - Pool.SwapResult memory result = _getPool(key).swap( + Pool.BalanceDelta memory result = _getPool(key).swap( Pool.SwapParams({ time: _blockTimestamp(), recipient: params.recipient, @@ -162,4 +162,20 @@ contract PoolManager { // todo: account the delta via the vault } + + function observe(PoolKey calldata key, uint32[] calldata secondsAgos) + external + view + returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) + { + return _getPool(key).observe(_blockTimestamp(), secondsAgos); + } + + function snapshotCumulativesInside( + PoolKey calldata key, + int24 tickLower, + int24 tickUpper + ) external view returns (Pool.Snapshot memory) { + return _getPool(key).snapshotCumulativesInside(tickLower, tickUpper, _blockTimestamp()); + } } diff --git a/contracts/libraries/Pool.sol b/contracts/libraries/Pool.sol index 9cddbe9a8..07326f65d 100644 --- a/contracts/libraries/Pool.sol +++ b/contracts/libraries/Pool.sol @@ -20,6 +20,13 @@ library Pool { 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; @@ -73,35 +80,41 @@ library Pool { require(tickUpper <= TickMath.MAX_TICK, 'TUM'); } + struct ComputeSnapshotState { + 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 ( - int56 tickCumulativeInside, - uint160 secondsPerLiquidityInsideX128, - uint32 secondsInside - ) - { + ) internal view returns (Snapshot memory result) { checkTicks(tickLower, tickUpper); - int56 tickCumulativeLower; - int56 tickCumulativeUpper; - uint160 secondsPerLiquidityOutsideLowerX128; - uint160 secondsPerLiquidityOutsideUpperX128; - uint32 secondsOutsideLower; - uint32 secondsOutsideUpper; - + ComputeSnapshotState memory state; { Tick.Info storage lower = self.ticks[tickLower]; Tick.Info storage upper = self.ticks[tickUpper]; bool initializedLower; - (tickCumulativeLower, secondsPerLiquidityOutsideLowerX128, secondsOutsideLower, initializedLower) = ( + ( + state.tickCumulativeLower, + state.secondsPerLiquidityOutsideLowerX128, + state.secondsOutsideLower, + initializedLower + ) = ( lower.tickCumulativeOutside, lower.secondsPerLiquidityOutsideX128, lower.secondsOutside, @@ -110,7 +123,12 @@ library Pool { require(initializedLower); bool initializedUpper; - (tickCumulativeUpper, secondsPerLiquidityOutsideUpperX128, secondsOutsideUpper, initializedUpper) = ( + ( + state.tickCumulativeUpper, + state.secondsPerLiquidityOutsideUpperX128, + state.secondsOutsideUpper, + initializedUpper + ) = ( upper.tickCumulativeOutside, upper.secondsPerLiquidityOutsideX128, upper.secondsOutside, @@ -123,11 +141,11 @@ library Pool { unchecked { if (_slot0.tick < tickLower) { - return ( - tickCumulativeLower - tickCumulativeUpper, - secondsPerLiquidityOutsideLowerX128 - secondsPerLiquidityOutsideUpperX128, - secondsOutsideLower - secondsOutsideUpper - ); + 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, @@ -137,19 +155,18 @@ library Pool { self.liquidity, _slot0.observationCardinality ); - return ( - tickCumulative - tickCumulativeLower - tickCumulativeUpper, + result.tickCumulativeInside = tickCumulative - state.tickCumulativeLower - state.tickCumulativeUpper; + result.secondsPerLiquidityInsideX128 = secondsPerLiquidityCumulativeX128 - - secondsPerLiquidityOutsideLowerX128 - - secondsPerLiquidityOutsideUpperX128, - time - secondsOutsideLower - secondsOutsideUpper - ); + state.secondsPerLiquidityOutsideLowerX128 - + state.secondsPerLiquidityOutsideUpperX128; + result.secondsInside = time - state.secondsOutsideLower - state.secondsOutsideUpper; } else { - return ( - tickCumulativeUpper - tickCumulativeLower, - secondsPerLiquidityOutsideUpperX128 - secondsPerLiquidityOutsideLowerX128, - secondsOutsideUpper - secondsOutsideLower - ); + result.tickCumulativeInside = state.tickCumulativeUpper - state.tickCumulativeLower; + result.secondsPerLiquidityInsideX128 = + state.secondsPerLiquidityOutsideUpperX128 - + state.secondsPerLiquidityOutsideLowerX128; + result.secondsInside = state.secondsOutsideUpper - state.secondsOutsideLower; } } } @@ -232,18 +249,13 @@ library Pool { uint256 feeGrowthInside1X128; } - struct ModifyPositionResult { - int256 amount0; - int256 amount1; - } - /// @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 lock(self) - returns (ModifyPositionResult memory result) + returns (BalanceDelta memory result) { checkTicks(params.tickLower, params.tickUpper); @@ -424,13 +436,8 @@ library Pool { bytes data; } - struct SwapResult { - int256 amount0; - int256 amount1; - } - /// @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 (SwapResult memory result) { + function swap(State storage self, SwapParams memory params) internal returns (BalanceDelta memory result) { require(params.amountSpecified != 0, 'AS'); Slot0 memory slot0Start = self.slot0; diff --git a/test/__snapshots__/SingletonPool.spec.ts.snap b/test/__snapshots__/SingletonPool.spec.ts.snap index c8a149f33..77e2a7ed3 100644 --- a/test/__snapshots__/SingletonPool.spec.ts.snap +++ b/test/__snapshots__/SingletonPool.spec.ts.snap @@ -6,4 +6,4 @@ exports[`PoolManager #mint gas cost 1`] = `224180`; exports[`PoolManager #swap gas 1`] = `76815`; -exports[`PoolManager bytecode size 1`] = `16423`; +exports[`PoolManager bytecode size 1`] = `18361`; From 4c80ad8dde76bd856c9a784aac9d6ea909dd0005 Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Fri, 12 Nov 2021 16:33:29 -0500 Subject: [PATCH 15/40] feature parity --- contracts/PoolManager.sol | 12 ++++++++++-- contracts/libraries/Pool.sol | 16 +++++++--------- ...SingletonPool.spec.ts => PoolManager.spec.ts} | 0 test/__snapshots__/PoolManager.spec.ts.snap | 9 +++++++++ test/__snapshots__/SingletonPool.spec.ts.snap | 9 --------- 5 files changed, 26 insertions(+), 20 deletions(-) rename test/{SingletonPool.spec.ts => PoolManager.spec.ts} (100%) create mode 100644 test/__snapshots__/PoolManager.spec.ts.snap delete mode 100644 test/__snapshots__/SingletonPool.spec.ts.snap diff --git a/contracts/PoolManager.sol b/contracts/PoolManager.sol index 5aa32b68f..1304d6d88 100644 --- a/contracts/PoolManager.sol +++ b/contracts/PoolManager.sol @@ -55,10 +55,11 @@ contract PoolManager { } /// @notice Initialize the state for a given pool ID - function initialize(PoolKey memory key, uint160 sqrtPriceX96) external { - _getPool(key).initialize(_blockTimestamp(), sqrtPriceX96); + function initialize(PoolKey memory key, uint160 sqrtPriceX96) external returns (int24) { + return _getPool(key).initialize(_blockTimestamp(), sqrtPriceX96); } + /// @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) @@ -163,6 +164,7 @@ contract PoolManager { // todo: account the delta via the vault } + /// @notice Observe a past state of a pool function observe(PoolKey calldata key, uint32[] calldata secondsAgos) external view @@ -171,6 +173,7 @@ contract PoolManager { return _getPool(key).observe(_blockTimestamp(), secondsAgos); } + /// @notice Get the snapshot of the cumulative values of a tick range function snapshotCumulativesInside( PoolKey calldata key, int24 tickLower, @@ -178,4 +181,9 @@ contract PoolManager { ) external view returns (Pool.Snapshot memory) { return _getPool(key).snapshotCumulativesInside(tickLower, tickUpper, _blockTimestamp()); } + + /// @notice Update the protocol fee for a given pool + function setFeeProtocol(PoolKey calldata key, uint8 feeProtocol) external returns (uint8 feeProtocolOld) { + return _getPool(key).setFeeProtocol(feeProtocol); + } } diff --git a/contracts/libraries/Pool.sol b/contracts/libraries/Pool.sol index 07326f65d..6cdc62689 100644 --- a/contracts/libraries/Pool.sol +++ b/contracts/libraries/Pool.sol @@ -74,13 +74,13 @@ library Pool { } /// @dev Common checks for valid tick inputs. - function checkTicks(int24 tickLower, int24 tickUpper) internal pure { + 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 ComputeSnapshotState { + struct SnapshotCumulativesInsideState { int56 tickCumulativeLower; int56 tickCumulativeUpper; uint160 secondsPerLiquidityOutsideLowerX128; @@ -104,7 +104,7 @@ library Pool { ) internal view returns (Snapshot memory result) { checkTicks(tickLower, tickUpper); - ComputeSnapshotState memory state; + SnapshotCumulativesInsideState memory state; { Tick.Info storage lower = self.ticks[tickLower]; Tick.Info storage upper = self.ticks[tickUpper]; @@ -629,16 +629,14 @@ library Pool { self.slot0.unlocked = true; } - function setFeeProtocol( - State storage self, - uint8 feeProtocol0, - uint8 feeProtocol1 - ) internal lock(self) returns (uint8 feeProtocolOld) { + /// @notice Updates the protocol fee for a given pool + function setFeeProtocol(State storage self, uint8 feeProtocol) internal lock(self) 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 = feeProtocol0 + (feeProtocol1 << 4); + self.slot0.feeProtocol = feeProtocol; } } diff --git a/test/SingletonPool.spec.ts b/test/PoolManager.spec.ts similarity index 100% rename from test/SingletonPool.spec.ts rename to test/PoolManager.spec.ts diff --git a/test/__snapshots__/PoolManager.spec.ts.snap b/test/__snapshots__/PoolManager.spec.ts.snap new file mode 100644 index 000000000..6ea412706 --- /dev/null +++ b/test/__snapshots__/PoolManager.spec.ts.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`PoolManager #initialize gas cost 1`] = `70676`; + +exports[`PoolManager #mint gas cost 1`] = `224180`; + +exports[`PoolManager #swap gas 1`] = `76793`; + +exports[`PoolManager bytecode size 1`] = `18796`; diff --git a/test/__snapshots__/SingletonPool.spec.ts.snap b/test/__snapshots__/SingletonPool.spec.ts.snap deleted file mode 100644 index 77e2a7ed3..000000000 --- a/test/__snapshots__/SingletonPool.spec.ts.snap +++ /dev/null @@ -1,9 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`PoolManager #initialize gas cost 1`] = `70598`; - -exports[`PoolManager #mint gas cost 1`] = `224180`; - -exports[`PoolManager #swap gas 1`] = `76815`; - -exports[`PoolManager bytecode size 1`] = `18361`; From 45c474358625d30a4ba4d19ba36b77093a69c438 Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Sat, 13 Nov 2021 10:09:42 -0500 Subject: [PATCH 16/40] adapt how fees are handled to match the pattern of the vault --- contracts/PoolManager.sol | 24 ++++---------- contracts/libraries/Pool.sol | 19 +++++------ contracts/libraries/Position.sol | 36 +++++++-------------- test/__snapshots__/PoolManager.spec.ts.snap | 6 ++-- 4 files changed, 30 insertions(+), 55 deletions(-) diff --git a/contracts/PoolManager.sol b/contracts/PoolManager.sol index 1304d6d88..14f1962c7 100644 --- a/contracts/PoolManager.sol +++ b/contracts/PoolManager.sol @@ -79,12 +79,12 @@ contract PoolManager { } /// @dev Mint some liquidity for the given pool - function mint(PoolKey memory key, MintParams memory params) external returns (uint256 amount0, uint256 amount1) { + function mint(PoolKey memory key, MintParams memory params) external returns (Pool.BalanceDelta memory delta) { require(params.amount > 0); FeeConfig memory config = configs[key.fee]; - Pool.BalanceDelta memory result = _getPool(key).modifyPosition( + delta = _getPool(key).modifyPosition( Pool.ModifyPositionParams({ owner: params.recipient, tickLower: params.tickLower, @@ -95,11 +95,6 @@ contract PoolManager { tickSpacing: config.tickSpacing }) ); - - amount0 = uint256(result.amount0); - amount1 = uint256(result.amount1); - - // todo: account the delta via the vault } struct BurnParams { @@ -111,13 +106,13 @@ contract PoolManager { } /// @dev Mint some liquidity for the given pool - function burn(PoolKey memory key, BurnParams memory params) external returns (uint256 amount0, uint256 amount1) { + function burn(PoolKey memory key, BurnParams memory params) external returns (Pool.BalanceDelta memory delta) { require(params.amount > 0); FeeConfig memory config = configs[key.fee]; // todo: where to get maxLiquidityPerTick, tickSpacing, probably from storage - Pool.BalanceDelta memory result = _getPool(key).modifyPosition( + delta = _getPool(key).modifyPosition( Pool.ModifyPositionParams({ owner: msg.sender, tickLower: params.tickLower, @@ -128,11 +123,6 @@ contract PoolManager { tickSpacing: config.tickSpacing }) ); - - amount0 = uint256(-result.amount0); - amount1 = uint256(-result.amount1); - - // todo: account the delta via the vault } struct SwapParams { @@ -143,10 +133,10 @@ contract PoolManager { bytes data; } - function swap(PoolKey memory key, SwapParams memory params) external returns (int256 amount0, int256 amount1) { + function swap(PoolKey memory key, SwapParams memory params) external returns (Pool.BalanceDelta memory delta) { FeeConfig memory config = configs[key.fee]; - Pool.BalanceDelta memory result = _getPool(key).swap( + delta = _getPool(key).swap( Pool.SwapParams({ time: _blockTimestamp(), recipient: params.recipient, @@ -159,8 +149,6 @@ contract PoolManager { }) ); - (amount0, amount1) = (result.amount0, result.amount1); - // todo: account the delta via the vault } diff --git a/contracts/libraries/Pool.sol b/contracts/libraries/Pool.sol index 6cdc62689..5c196b4c7 100644 --- a/contracts/libraries/Pool.sol +++ b/contracts/libraries/Pool.sol @@ -313,11 +313,12 @@ library Pool { self.feeGrowthGlobal1X128 ); - self.positions.get(params.owner, params.tickLower, params.tickUpper).update( - params.liquidityDelta, - state.feeGrowthInside0X128, - state.feeGrowthInside1X128 - ); + (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) { @@ -334,7 +335,7 @@ library Pool { 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( + result.amount0 += SqrtPriceMath.getAmount0Delta( TickMath.getSqrtRatioAtTick(params.tickLower), TickMath.getSqrtRatioAtTick(params.tickUpper), params.liquidityDelta @@ -351,12 +352,12 @@ library Pool { self.slot0.observationCardinalityNext ); - result.amount0 = SqrtPriceMath.getAmount0Delta( + result.amount0 += SqrtPriceMath.getAmount0Delta( self.slot0.sqrtPriceX96, TickMath.getSqrtRatioAtTick(params.tickUpper), params.liquidityDelta ); - result.amount1 = SqrtPriceMath.getAmount1Delta( + result.amount1 += SqrtPriceMath.getAmount1Delta( TickMath.getSqrtRatioAtTick(params.tickLower), self.slot0.sqrtPriceX96, params.liquidityDelta @@ -368,7 +369,7 @@ library Pool { } 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( + result.amount1 += SqrtPriceMath.getAmount1Delta( TickMath.getSqrtRatioAtTick(params.tickLower), TickMath.getSqrtRatioAtTick(params.tickUpper), params.liquidityDelta 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/test/__snapshots__/PoolManager.spec.ts.snap b/test/__snapshots__/PoolManager.spec.ts.snap index 6ea412706..db7ab1360 100644 --- a/test/__snapshots__/PoolManager.spec.ts.snap +++ b/test/__snapshots__/PoolManager.spec.ts.snap @@ -2,8 +2,8 @@ exports[`PoolManager #initialize gas cost 1`] = `70676`; -exports[`PoolManager #mint gas cost 1`] = `224180`; +exports[`PoolManager #mint gas cost 1`] = `222748`; -exports[`PoolManager #swap gas 1`] = `76793`; +exports[`PoolManager #swap gas 1`] = `76831`; -exports[`PoolManager bytecode size 1`] = `18796`; +exports[`PoolManager bytecode size 1`] = `18820`; From 6d1ee750073a5a6576ac1e1e4241e9db6314c701 Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Sat, 13 Nov 2021 11:18:23 -0500 Subject: [PATCH 17/40] remove the vault --- contracts/Vault.sol | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 contracts/Vault.sol diff --git a/contracts/Vault.sol b/contracts/Vault.sol deleted file mode 100644 index 39c38b381..000000000 --- a/contracts/Vault.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.9; - -import './PoolManager.sol'; - -/// @notice Manages the balances for each pool -contract Vault { - PoolManager public immutable manager; - - constructor(PoolManager _manager) { - manager = _manager; - } -} From fda8d103725ac9eb9e8122750167e0eb54f3d18f Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Tue, 16 Nov 2021 10:36:59 -0500 Subject: [PATCH 18/40] trimming things that are not necessary anymore --- contracts/PoolManager.sol | 25 ++++++++------------- contracts/libraries/Pool.sol | 2 -- test/PoolManager.spec.ts | 6 ----- test/__snapshots__/PoolManager.spec.ts.snap | 4 ++-- 4 files changed, 11 insertions(+), 26 deletions(-) diff --git a/contracts/PoolManager.sol b/contracts/PoolManager.sol index 14f1962c7..9f3c236f5 100644 --- a/contracts/PoolManager.sol +++ b/contracts/PoolManager.sol @@ -69,7 +69,7 @@ contract PoolManager { } struct MintParams { - // the address that will receive the liquidity + // the address that will own the minted liquidity address recipient; // the lower and upper tick of the position int24 tickLower; @@ -101,7 +101,7 @@ contract PoolManager { // the lower and upper tick of the position int24 tickLower; int24 tickUpper; - // any change in liquidity + // the reduction in liquidity to effect uint256 amount; } @@ -111,7 +111,6 @@ contract PoolManager { FeeConfig memory config = configs[key.fee]; - // todo: where to get maxLiquidityPerTick, tickSpacing, probably from storage delta = _getPool(key).modifyPosition( Pool.ModifyPositionParams({ owner: msg.sender, @@ -126,11 +125,9 @@ contract PoolManager { } struct SwapParams { - address recipient; bool zeroForOne; int256 amountSpecified; uint160 sqrtPriceLimitX96; - bytes data; } function swap(PoolKey memory key, SwapParams memory params) external returns (Pool.BalanceDelta memory delta) { @@ -139,17 +136,18 @@ contract PoolManager { delta = _getPool(key).swap( Pool.SwapParams({ time: _blockTimestamp(), - recipient: params.recipient, + fee: key.fee, + tickSpacing: config.tickSpacing, zeroForOne: params.zeroForOne, amountSpecified: params.amountSpecified, - sqrtPriceLimitX96: params.sqrtPriceLimitX96, - data: params.data, - fee: key.fee, - tickSpacing: config.tickSpacing + sqrtPriceLimitX96: params.sqrtPriceLimitX96 }) ); + } - // todo: account the delta via the vault + /// @notice Update the protocol fee for a given pool + function setFeeProtocol(PoolKey calldata key, uint8 feeProtocol) external returns (uint8 feeProtocolOld) { + return _getPool(key).setFeeProtocol(feeProtocol); } /// @notice Observe a past state of a pool @@ -169,9 +167,4 @@ contract PoolManager { ) external view returns (Pool.Snapshot memory) { return _getPool(key).snapshotCumulativesInside(tickLower, tickUpper, _blockTimestamp()); } - - /// @notice Update the protocol fee for a given pool - function setFeeProtocol(PoolKey calldata key, uint8 feeProtocol) external returns (uint8 feeProtocolOld) { - return _getPool(key).setFeeProtocol(feeProtocol); - } } diff --git a/contracts/libraries/Pool.sol b/contracts/libraries/Pool.sol index 5c196b4c7..6a53e909b 100644 --- a/contracts/libraries/Pool.sol +++ b/contracts/libraries/Pool.sol @@ -430,11 +430,9 @@ library Pool { uint24 fee; int24 tickSpacing; uint32 time; - address recipient; bool zeroForOne; int256 amountSpecified; uint160 sqrtPriceLimitX96; - bytes data; } /// @dev Executes a swap against the state, and returns the amount deltas of the pool diff --git a/test/PoolManager.spec.ts b/test/PoolManager.spec.ts index 3a6d7ddbb..8127f64f6 100644 --- a/test/PoolManager.spec.ts +++ b/test/PoolManager.spec.ts @@ -156,8 +156,6 @@ describe.only('PoolManager', () => { fee: FeeAmount.MEDIUM, }, { - data: '0x', - recipient: wallet.address, amountSpecified: 100, sqrtPriceLimitX96: encodeSqrtPriceX96(100, 1), zeroForOne: true, @@ -181,8 +179,6 @@ describe.only('PoolManager', () => { fee: FeeAmount.MEDIUM, }, { - data: '0x', - recipient: wallet.address, amountSpecified: 100, sqrtPriceLimitX96: encodeSqrtPriceX96(1, 100), zeroForOne: true, @@ -206,8 +202,6 @@ describe.only('PoolManager', () => { fee: FeeAmount.MEDIUM, }, { - data: '0x', - recipient: wallet.address, amountSpecified: 100, sqrtPriceLimitX96: encodeSqrtPriceX96(1, 100), zeroForOne: true, diff --git a/test/__snapshots__/PoolManager.spec.ts.snap b/test/__snapshots__/PoolManager.spec.ts.snap index db7ab1360..189b3fcb7 100644 --- a/test/__snapshots__/PoolManager.spec.ts.snap +++ b/test/__snapshots__/PoolManager.spec.ts.snap @@ -4,6 +4,6 @@ exports[`PoolManager #initialize gas cost 1`] = `70676`; exports[`PoolManager #mint gas cost 1`] = `222748`; -exports[`PoolManager #swap gas 1`] = `76831`; +exports[`PoolManager #swap gas 1`] = `75488`; -exports[`PoolManager bytecode size 1`] = `18820`; +exports[`PoolManager bytecode size 1`] = `18580`; From 6eaf3f7bea91f05b472df5a270a6a78217307328 Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Thu, 18 Nov 2021 12:43:50 -0500 Subject: [PATCH 19/40] add the lock function and require every call to be within the context of a lock --- contracts/PoolManager.sol | 82 ++++++++++++++++++- .../interfaces/callback/IFlashCallback.sol | 18 ---- .../interfaces/callback/ILockCallback.sol | 7 ++ .../interfaces/callback/IMintCallback.sol | 18 ---- .../interfaces/callback/IPayCallback.sol | 12 +++ .../interfaces/callback/ISwapCallback.sol | 21 ----- contracts/libraries/Pool.sol | 27 ++---- test/PoolManager.spec.ts | 8 +- test/__snapshots__/PoolManager.spec.ts.snap | 8 +- 9 files changed, 109 insertions(+), 92 deletions(-) delete mode 100644 contracts/interfaces/callback/IFlashCallback.sol create mode 100644 contracts/interfaces/callback/ILockCallback.sol delete mode 100644 contracts/interfaces/callback/IMintCallback.sol create mode 100644 contracts/interfaces/callback/IPayCallback.sol delete mode 100644 contracts/interfaces/callback/ISwapCallback.sol diff --git a/contracts/PoolManager.sol b/contracts/PoolManager.sol index 9f3c236f5..c25460d15 100644 --- a/contracts/PoolManager.sol +++ b/contracts/PoolManager.sol @@ -6,6 +6,7 @@ import {Tick} from './libraries/Tick.sol'; import {SafeCast} from './libraries/SafeCast.sol'; import {IERC20Minimal} from './interfaces/external/IERC20Minimal.sol'; +import {ILockCallback} from './interfaces/callback/ILockCallback.sol'; /// @notice Holds the state for all pools contract PoolManager { @@ -78,8 +79,69 @@ contract PoolManager { uint256 amount; } + /// @notice Represents the address that has currently locked the pool + address public lockedBy; + + /// @notice Internal transient enumerable set + uint256 tokensTouchedBloomFilter; + IERC20Minimal[] public tokensTouched; + mapping(IERC20Minimal => int256) public tokenDelta; + + function lock() external { + require(lockedBy == address(0)); + lockedBy = msg.sender; + ILockCallback(msg.sender).lockAcquired(); + + // todo: handle payment of the deltas + + for (uint256 i = 0; i < tokensTouched.length; i++) { + delete tokenDelta[tokensTouched[i]]; + } + delete tokensTouchedBloomFilter; + delete tokensTouched; + + // delimiter to indicate where the lock is cleared + delete lockedBy; + } + + /// @dev Adds a token to a unique list of tokens that have been touched + function _addTokenToSet(IERC20Minimal token) internal { + // if the bloom filter doesn't hit, we know it's not in the set, push it + if (tokensTouchedBloomFilter & uint160(uint160(address(token))) != uint160(uint160(address(token)))) { + tokensTouched.push(token); + } else { + bool seen; + for (uint256 i = 0; i < tokensTouched.length; i++) { + if (seen = (tokensTouched[i] == token)) { + break; + } + } + if (!seen) { + tokensTouched.push(token); + } + } + tokensTouchedBloomFilter |= uint160(uint160(address(token))); + } + + /// @dev Accumulates a balance change to a map of token to balance changes + function _accountDelta(PoolKey memory key, Pool.BalanceDelta memory delta) internal { + _addTokenToSet(key.token0); + _addTokenToSet(key.token1); + tokenDelta[key.token0] += delta.amount0; + tokenDelta[key.token1] += delta.amount1; + } + + modifier onlyLocker() { + require(msg.sender == lockedBy, 'LOK'); + _; + } + /// @dev Mint some liquidity for the given pool - function mint(PoolKey memory key, MintParams memory params) external returns (Pool.BalanceDelta memory delta) { + function mint(PoolKey memory key, MintParams memory params) + external + onlyLocker + returns (Pool.BalanceDelta memory delta) + { require(params.amount > 0); FeeConfig memory config = configs[key.fee]; @@ -95,6 +157,8 @@ contract PoolManager { tickSpacing: config.tickSpacing }) ); + + _accountDelta(key, delta); } struct BurnParams { @@ -106,7 +170,11 @@ contract PoolManager { } /// @dev Mint some liquidity for the given pool - function burn(PoolKey memory key, BurnParams memory params) external returns (Pool.BalanceDelta memory delta) { + function burn(PoolKey memory key, BurnParams memory params) + external + onlyLocker + returns (Pool.BalanceDelta memory delta) + { require(params.amount > 0); FeeConfig memory config = configs[key.fee]; @@ -122,6 +190,8 @@ contract PoolManager { tickSpacing: config.tickSpacing }) ); + + _accountDelta(key, delta); } struct SwapParams { @@ -130,7 +200,11 @@ contract PoolManager { uint160 sqrtPriceLimitX96; } - function swap(PoolKey memory key, SwapParams memory params) external returns (Pool.BalanceDelta memory delta) { + function swap(PoolKey memory key, SwapParams memory params) + external + onlyLocker + returns (Pool.BalanceDelta memory delta) + { FeeConfig memory config = configs[key.fee]; delta = _getPool(key).swap( @@ -143,6 +217,8 @@ contract PoolManager { sqrtPriceLimitX96: params.sqrtPriceLimitX96 }) ); + + _accountDelta(key, delta); } /// @notice Update the protocol fee for a given pool 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..3b2ab1345 --- /dev/null +++ b/contracts/interfaces/callback/ILockCallback.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.9; + +interface ILockCallback { + /// @notice Called by the pool manager on `msg.sender` when a lock is acquired + function lockAcquired() external; +} 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/IPayCallback.sol b/contracts/interfaces/callback/IPayCallback.sol new file mode 100644 index 000000000..34b8216cc --- /dev/null +++ b/contracts/interfaces/callback/IPayCallback.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.9; + +interface IPayCallback { + struct TokenAmountOwed { + address token; + uint256 amount; + } + + /// @notice Called on the `msg.sender` after a lock is released + function payCallback(TokenAmountOwed[] calldata amountOwed) 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/libraries/Pool.sol b/contracts/libraries/Pool.sol index 6a53e909b..855981d93 100644 --- a/contracts/libraries/Pool.sol +++ b/contracts/libraries/Pool.sol @@ -41,8 +41,6 @@ library Pool { // 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; } // accumulated protocol fees in token0/token1 units @@ -65,14 +63,6 @@ library Pool { Oracle.Observation[65535] observations; } - /// @dev Locks the pool to perform some action on it while protected from reentrancy - modifier lock(State storage self) { - require(self.slot0.unlocked, 'LOK'); - self.slot0.unlocked = false; - _; - self.slot0.unlocked = true; - } - /// @dev Common checks for valid tick inputs. function checkTicks(int24 tickLower, int24 tickUpper) private pure { require(tickLower < tickUpper, 'TLU'); @@ -187,7 +177,6 @@ library Pool { ); } - /// @dev Not locked because it initializes the slot0.unlocked variable function initialize( State storage self, uint32 time, @@ -205,15 +194,13 @@ library Pool { observationIndex: 0, observationCardinality: cardinality, observationCardinalityNext: cardinalityNext, - feeProtocol: 0, - unlocked: true + feeProtocol: 0 }); } /// @dev Increase the number of stored observations function increaseObservationCardinalityNext(State storage self, uint16 observationCardinalityNext) internal - lock(self) returns (uint16 observationCardinalityNextOld, uint16 observationCardinalityNextNew) { observationCardinalityNextOld = self.slot0.observationCardinalityNext; @@ -254,9 +241,10 @@ library Pool { /// @return result the deltas of the token balances of the pool function modifyPosition(State storage self, ModifyPositionParams memory params) internal - lock(self) returns (BalanceDelta memory result) { + require(self.slot0.sqrtPriceX96 != 0, 'I'); + checkTicks(params.tickLower, params.tickUpper); { @@ -440,8 +428,7 @@ library Pool { require(params.amountSpecified != 0, 'AS'); Slot0 memory slot0Start = self.slot0; - - require(slot0Start.unlocked, 'LOK'); + require(slot0Start.sqrtPriceX96 != 0, 'I'); require( params.zeroForOne ? params.sqrtPriceLimitX96 < slot0Start.sqrtPriceX96 && @@ -451,8 +438,6 @@ library Pool { 'SPL' ); - self.slot0.unlocked = false; - SwapCache memory cache = SwapCache({ liquidityStart: self.liquidity, feeProtocol: params.zeroForOne ? (slot0Start.feeProtocol % 16) : (slot0Start.feeProtocol >> 4), @@ -624,12 +609,10 @@ library Pool { ? (params.amountSpecified - state.amountSpecifiedRemaining, state.amountCalculated) : (state.amountCalculated, params.amountSpecified - state.amountSpecifiedRemaining); } - - self.slot0.unlocked = true; } /// @notice Updates the protocol fee for a given pool - function setFeeProtocol(State storage self, uint8 feeProtocol) internal lock(self) returns (uint8 feeProtocolOld) { + 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)) && diff --git a/test/PoolManager.spec.ts b/test/PoolManager.spec.ts index 8127f64f6..101f24c21 100644 --- a/test/PoolManager.spec.ts +++ b/test/PoolManager.spec.ts @@ -74,7 +74,7 @@ describe.only('PoolManager', () => { }) }) - describe('#mint', async () => { + describe.skip('#mint', async () => { it('reverts if pool not initialized', async () => { await expect( singleton.mint( @@ -90,7 +90,7 @@ describe.only('PoolManager', () => { recipient: wallet.address, } ) - ).to.be.revertedWith('LOK') + ).to.be.revertedWith('I') }) it('succeeds if pool is initialized', async () => { @@ -146,7 +146,7 @@ describe.only('PoolManager', () => { }) }) - describe('#swap', () => { + describe.skip('#swap', () => { it('fails if pool is not initialized', async () => { await expect( singleton.swap( @@ -161,7 +161,7 @@ describe.only('PoolManager', () => { zeroForOne: true, } ) - ).to.be.revertedWith('LOK') + ).to.be.revertedWith('I') }) it('succeeds if pool is not initialized', async () => { await singleton.initialize( diff --git a/test/__snapshots__/PoolManager.spec.ts.snap b/test/__snapshots__/PoolManager.spec.ts.snap index 189b3fcb7..c885c1fc5 100644 --- a/test/__snapshots__/PoolManager.spec.ts.snap +++ b/test/__snapshots__/PoolManager.spec.ts.snap @@ -1,9 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`PoolManager #initialize gas cost 1`] = `70676`; +exports[`PoolManager #initialize gas cost 1`] = `70679`; -exports[`PoolManager #mint gas cost 1`] = `222748`; - -exports[`PoolManager #swap gas 1`] = `75488`; - -exports[`PoolManager bytecode size 1`] = `18580`; +exports[`PoolManager bytecode size 1`] = `19393`; From 17c7026eed847d3be3e40f291d756b2425ff75fa Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Thu, 18 Nov 2021 23:18:35 -0500 Subject: [PATCH 20/40] think payment is figured out --- contracts/PoolManager.sol | 44 +++++++++++++++------ test/__snapshots__/PoolManager.spec.ts.snap | 2 +- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/contracts/PoolManager.sol b/contracts/PoolManager.sol index c25460d15..6f71bc50e 100644 --- a/contracts/PoolManager.sol +++ b/contracts/PoolManager.sol @@ -83,19 +83,19 @@ contract PoolManager { address public lockedBy; /// @notice Internal transient enumerable set - uint256 tokensTouchedBloomFilter; + uint256 public tokensTouchedBloomFilter; IERC20Minimal[] public tokensTouched; mapping(IERC20Minimal => int256) public tokenDelta; function lock() external { require(lockedBy == address(0)); lockedBy = msg.sender; - ILockCallback(msg.sender).lockAcquired(); - // todo: handle payment of the deltas + // the caller does everything in this callback, including paying what they owe + ILockCallback(msg.sender).lockAcquired(); for (uint256 i = 0; i < tokensTouched.length; i++) { - delete tokenDelta[tokensTouched[i]]; + require(tokenDelta[tokensTouched[i]] == 0, 'Not settled'); } delete tokensTouchedBloomFilter; delete tokensTouched; @@ -123,12 +123,15 @@ contract PoolManager { tokensTouchedBloomFilter |= uint160(uint160(address(token))); } + function _accountDelta(IERC20Minimal token, int256 delta) internal { + _addTokenToSet(token); + tokenDelta[token] += delta; + } + /// @dev Accumulates a balance change to a map of token to balance changes - function _accountDelta(PoolKey memory key, Pool.BalanceDelta memory delta) internal { - _addTokenToSet(key.token0); - _addTokenToSet(key.token1); - tokenDelta[key.token0] += delta.amount0; - tokenDelta[key.token1] += delta.amount1; + function _accountPoolBalanceDelta(PoolKey memory key, Pool.BalanceDelta memory delta) internal { + _accountDelta(key.token0, delta.amount0); + _accountDelta(key.token1, delta.amount1); } modifier onlyLocker() { @@ -158,7 +161,7 @@ contract PoolManager { }) ); - _accountDelta(key, delta); + _accountPoolBalanceDelta(key, delta); } struct BurnParams { @@ -191,7 +194,7 @@ contract PoolManager { }) ); - _accountDelta(key, delta); + _accountPoolBalanceDelta(key, delta); } struct SwapParams { @@ -218,7 +221,24 @@ contract PoolManager { }) ); - _accountDelta(key, delta); + _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 onlyLocker { + _accountDelta(token, amount.toInt256()); + token.transfer(to, amount); + } + + /// @notice Called by the user to pay what is owed + function settle(IERC20Minimal token, uint256 amount) external onlyLocker { + _accountDelta(token, -amount.toInt256()); + token.transferFrom(msg.sender, address(this), amount); } /// @notice Update the protocol fee for a given pool diff --git a/test/__snapshots__/PoolManager.spec.ts.snap b/test/__snapshots__/PoolManager.spec.ts.snap index c885c1fc5..007ee7867 100644 --- a/test/__snapshots__/PoolManager.spec.ts.snap +++ b/test/__snapshots__/PoolManager.spec.ts.snap @@ -2,4 +2,4 @@ exports[`PoolManager #initialize gas cost 1`] = `70679`; -exports[`PoolManager bytecode size 1`] = `19393`; +exports[`PoolManager bytecode size 1`] = `20114`; From 1a621ed1a686788aa1ebae9777b805641ef7ec3f Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Thu, 18 Nov 2021 23:52:04 -0500 Subject: [PATCH 21/40] use reserves --- contracts/PoolManager.sol | 13 ++++++++++--- contracts/interfaces/callback/IPayCallback.sol | 12 ------------ contracts/interfaces/callback/ISettleCallback.sol | 8 ++++++++ test/__snapshots__/PoolManager.spec.ts.snap | 4 ++-- 4 files changed, 20 insertions(+), 17 deletions(-) delete mode 100644 contracts/interfaces/callback/IPayCallback.sol create mode 100644 contracts/interfaces/callback/ISettleCallback.sol diff --git a/contracts/PoolManager.sol b/contracts/PoolManager.sol index 6f71bc50e..1b65cf68c 100644 --- a/contracts/PoolManager.sol +++ b/contracts/PoolManager.sol @@ -7,6 +7,7 @@ import {SafeCast} from './libraries/SafeCast.sol'; import {IERC20Minimal} from './interfaces/external/IERC20Minimal.sol'; import {ILockCallback} from './interfaces/callback/ILockCallback.sol'; +import {ISettleCallback} from './interfaces/callback/ISettleCallback.sol'; /// @notice Holds the state for all pools contract PoolManager { @@ -82,6 +83,9 @@ contract PoolManager { /// @notice Represents the address that has currently locked the pool address public lockedBy; + /// @notice All the latest tracked balances of tokens + mapping(IERC20Minimal => uint256) public reserves; + /// @notice Internal transient enumerable set uint256 public tokensTouchedBloomFilter; IERC20Minimal[] public tokensTouched; @@ -236,9 +240,12 @@ contract PoolManager { } /// @notice Called by the user to pay what is owed - function settle(IERC20Minimal token, uint256 amount) external onlyLocker { - _accountDelta(token, -amount.toInt256()); - token.transferFrom(msg.sender, address(this), amount); + function settle(IERC20Minimal token) external onlyLocker { + uint256 reservesBefore = reserves[token]; + ISettleCallback(msg.sender).settleCallback(token); + reserves[token] = token.balanceOf(address(this)); + // subtraction must be safe + _accountDelta(token, -(reserves[token] - reservesBefore).toInt256()); } /// @notice Update the protocol fee for a given pool diff --git a/contracts/interfaces/callback/IPayCallback.sol b/contracts/interfaces/callback/IPayCallback.sol deleted file mode 100644 index 34b8216cc..000000000 --- a/contracts/interfaces/callback/IPayCallback.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.9; - -interface IPayCallback { - struct TokenAmountOwed { - address token; - uint256 amount; - } - - /// @notice Called on the `msg.sender` after a lock is released - function payCallback(TokenAmountOwed[] calldata amountOwed) external; -} diff --git a/contracts/interfaces/callback/ISettleCallback.sol b/contracts/interfaces/callback/ISettleCallback.sol new file mode 100644 index 000000000..ec9ea2cce --- /dev/null +++ b/contracts/interfaces/callback/ISettleCallback.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.9; + +import {IERC20Minimal} from '../external/IERC20Minimal.sol'; + +interface ISettleCallback { + function settleCallback(IERC20Minimal token) external; +} diff --git a/test/__snapshots__/PoolManager.spec.ts.snap b/test/__snapshots__/PoolManager.spec.ts.snap index 007ee7867..d6aad3e5f 100644 --- a/test/__snapshots__/PoolManager.spec.ts.snap +++ b/test/__snapshots__/PoolManager.spec.ts.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`PoolManager #initialize gas cost 1`] = `70679`; +exports[`PoolManager #initialize gas cost 1`] = `70657`; -exports[`PoolManager bytecode size 1`] = `20114`; +exports[`PoolManager bytecode size 1`] = `20278`; From a6d4a02bc3bcb5aed48081f0464d4825934ea765 Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Thu, 18 Nov 2021 23:53:46 -0500 Subject: [PATCH 22/40] build in no delegate call --- contracts/PoolManager.sol | 13 +++++++++---- test/__snapshots__/PoolManager.spec.ts.snap | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/contracts/PoolManager.sol b/contracts/PoolManager.sol index 1b65cf68c..b00729e92 100644 --- a/contracts/PoolManager.sol +++ b/contracts/PoolManager.sol @@ -8,9 +8,10 @@ import {SafeCast} from './libraries/SafeCast.sol'; import {IERC20Minimal} from './interfaces/external/IERC20Minimal.sol'; import {ILockCallback} from './interfaces/callback/ILockCallback.sol'; import {ISettleCallback} from './interfaces/callback/ISettleCallback.sol'; +import {NoDelegateCall} from './NoDelegateCall.sol'; /// @notice Holds the state for all pools -contract PoolManager { +contract PoolManager is NoDelegateCall { using SafeCast for *; using Pool for *; @@ -146,6 +147,7 @@ contract PoolManager { /// @dev Mint some liquidity for the given pool function mint(PoolKey memory key, MintParams memory params) external + noDelegateCall onlyLocker returns (Pool.BalanceDelta memory delta) { @@ -179,6 +181,7 @@ contract PoolManager { /// @dev Mint some liquidity for the given pool function burn(PoolKey memory key, BurnParams memory params) external + noDelegateCall onlyLocker returns (Pool.BalanceDelta memory delta) { @@ -209,6 +212,7 @@ contract PoolManager { function swap(PoolKey memory key, SwapParams memory params) external + noDelegateCall onlyLocker returns (Pool.BalanceDelta memory delta) { @@ -234,13 +238,13 @@ contract PoolManager { IERC20Minimal token, address to, uint256 amount - ) external onlyLocker { + ) external noDelegateCall onlyLocker { _accountDelta(token, amount.toInt256()); token.transfer(to, amount); } /// @notice Called by the user to pay what is owed - function settle(IERC20Minimal token) external onlyLocker { + function settle(IERC20Minimal token) external noDelegateCall onlyLocker { uint256 reservesBefore = reserves[token]; ISettleCallback(msg.sender).settleCallback(token); reserves[token] = token.balanceOf(address(this)); @@ -257,6 +261,7 @@ contract PoolManager { function observe(PoolKey calldata key, uint32[] calldata secondsAgos) external view + noDelegateCall returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) { return _getPool(key).observe(_blockTimestamp(), secondsAgos); @@ -267,7 +272,7 @@ contract PoolManager { PoolKey calldata key, int24 tickLower, int24 tickUpper - ) external view returns (Pool.Snapshot memory) { + ) external view noDelegateCall returns (Pool.Snapshot memory) { return _getPool(key).snapshotCumulativesInside(tickLower, tickUpper, _blockTimestamp()); } } diff --git a/test/__snapshots__/PoolManager.spec.ts.snap b/test/__snapshots__/PoolManager.spec.ts.snap index d6aad3e5f..c4779d10a 100644 --- a/test/__snapshots__/PoolManager.spec.ts.snap +++ b/test/__snapshots__/PoolManager.spec.ts.snap @@ -2,4 +2,4 @@ exports[`PoolManager #initialize gas cost 1`] = `70657`; -exports[`PoolManager bytecode size 1`] = `20278`; +exports[`PoolManager bytecode size 1`] = `20389`; From 8b8b115d9fb5b681fc6d315fce05b976fc39260e Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Fri, 19 Nov 2021 12:54:49 -0500 Subject: [PATCH 23/40] start testing the lock function, pass through some data, add interfaces --- contracts/PoolManager.sol | 112 ++++++++---------- contracts/interfaces/IPoolManager.sol | 102 ++++++++++++++++ .../interfaces/callback/ILockCallback.sol | 7 -- .../interfaces/callback/IPoolManagerUser.sol | 13 ++ .../interfaces/callback/ISettleCallback.sol | 8 -- contracts/test/MockTimePoolManager.sol | 18 +++ contracts/test/PoolManagerTest.sol | 22 ++++ test/PoolManager.spec.ts | 49 +++++--- test/__snapshots__/PoolManager.spec.ts.snap | 6 +- 9 files changed, 237 insertions(+), 100 deletions(-) create mode 100644 contracts/interfaces/IPoolManager.sol delete mode 100644 contracts/interfaces/callback/ILockCallback.sol create mode 100644 contracts/interfaces/callback/IPoolManagerUser.sol delete mode 100644 contracts/interfaces/callback/ISettleCallback.sol create mode 100644 contracts/test/MockTimePoolManager.sol create mode 100644 contracts/test/PoolManagerTest.sol diff --git a/contracts/PoolManager.sol b/contracts/PoolManager.sol index b00729e92..640cfd5f5 100644 --- a/contracts/PoolManager.sol +++ b/contracts/PoolManager.sol @@ -6,32 +6,24 @@ import {Tick} from './libraries/Tick.sol'; import {SafeCast} from './libraries/SafeCast.sol'; import {IERC20Minimal} from './interfaces/external/IERC20Minimal.sol'; -import {ILockCallback} from './interfaces/callback/ILockCallback.sol'; -import {ISettleCallback} from './interfaces/callback/ISettleCallback.sol'; +import {IPoolManagerUser} from './interfaces/callback/IPoolManagerUser.sol'; import {NoDelegateCall} from './NoDelegateCall.sol'; +import {IPoolManager} from './interfaces/IPoolManager.sol'; /// @notice Holds the state for all pools -contract PoolManager is NoDelegateCall { +contract PoolManager is IPoolManager, NoDelegateCall { using SafeCast for *; using Pool for *; - /// @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 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 configs; + mapping(uint24 => FeeConfig) public override configs; constructor() { _configure(100, 1); @@ -53,64 +45,60 @@ contract PoolManager is NoDelegateCall { return uint32(block.timestamp); } - function _getPool(PoolKey memory key) private view returns (Pool.State storage) { + 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(PoolKey memory key, uint160 sqrtPriceX96) external returns (int24) { - return _getPool(key).initialize(_blockTimestamp(), sqrtPriceX96); + 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(PoolKey memory key, uint16 observationCardinalityNext) + function increaseObservationCardinalityNext(IPoolManager.PoolKey memory key, uint16 observationCardinalityNext) external + override returns (uint16 observationCardinalityNextOld, uint16 observationCardinalityNextNew) { (observationCardinalityNextOld, observationCardinalityNextNew) = _getPool(key) .increaseObservationCardinalityNext(observationCardinalityNext); } - 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 - address public lockedBy; + address public override lockedBy; /// @notice All the latest tracked balances of tokens - mapping(IERC20Minimal => uint256) public reserves; + mapping(IERC20Minimal => uint256) public override reservesOf; /// @notice Internal transient enumerable set - uint256 public tokensTouchedBloomFilter; - IERC20Minimal[] public tokensTouched; - mapping(IERC20Minimal => int256) public tokenDelta; + uint256 public override tokensTouchedBloomFilter; + IERC20Minimal[] public override tokensTouched; + mapping(IERC20Minimal => int256) public override tokenDelta; - function lock() external { + /// @dev Used to make sure all actions within the lock function are wrapped in the lock acquisition. Note this has no gas overhead because it's inlined. + modifier acquiresLock() { require(lockedBy == address(0)); lockedBy = msg.sender; + _; + + delete lockedBy; + } + + function lock(bytes calldata data) external override acquiresLock { // the caller does everything in this callback, including paying what they owe - ILockCallback(msg.sender).lockAcquired(); + require(IPoolManagerUser(msg.sender).lockAcquired(data), 'No data'); for (uint256 i = 0; i < tokensTouched.length; i++) { require(tokenDelta[tokensTouched[i]] == 0, 'Not settled'); } delete tokensTouchedBloomFilter; delete tokensTouched; - - // delimiter to indicate where the lock is cleared - delete lockedBy; } /// @dev Adds a token to a unique list of tokens that have been touched function _addTokenToSet(IERC20Minimal token) internal { + // todo: is it cheaper to mstore `uint160(uint160(address(token)))` or cast everywhere it's used? // if the bloom filter doesn't hit, we know it's not in the set, push it if (tokensTouchedBloomFilter & uint160(uint160(address(token))) != uint160(uint160(address(token)))) { tokensTouched.push(token); @@ -145,8 +133,9 @@ contract PoolManager is NoDelegateCall { } /// @dev Mint some liquidity for the given pool - function mint(PoolKey memory key, MintParams memory params) + function mint(IPoolManager.PoolKey memory key, IPoolManager.MintParams memory params) external + override noDelegateCall onlyLocker returns (Pool.BalanceDelta memory delta) @@ -170,17 +159,10 @@ contract PoolManager is NoDelegateCall { _accountPoolBalanceDelta(key, 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) + function burn(IPoolManager.PoolKey memory key, IPoolManager.BurnParams memory params) external + override noDelegateCall onlyLocker returns (Pool.BalanceDelta memory delta) @@ -204,14 +186,9 @@ contract PoolManager is NoDelegateCall { _accountPoolBalanceDelta(key, delta); } - struct SwapParams { - bool zeroForOne; - int256 amountSpecified; - uint160 sqrtPriceLimitX96; - } - - function swap(PoolKey memory key, SwapParams memory params) + function swap(IPoolManager.PoolKey memory key, IPoolManager.SwapParams memory params) external + override noDelegateCall onlyLocker returns (Pool.BalanceDelta memory delta) @@ -238,29 +215,34 @@ contract PoolManager is NoDelegateCall { IERC20Minimal token, address to, uint256 amount - ) external noDelegateCall onlyLocker { + ) external override noDelegateCall onlyLocker { _accountDelta(token, amount.toInt256()); token.transfer(to, amount); } /// @notice Called by the user to pay what is owed - function settle(IERC20Minimal token) external noDelegateCall onlyLocker { - uint256 reservesBefore = reserves[token]; - ISettleCallback(msg.sender).settleCallback(token); - reserves[token] = token.balanceOf(address(this)); + function settle(IERC20Minimal token) external override noDelegateCall onlyLocker { + uint256 reservesBefore = reservesOf[token]; + IPoolManagerUser(msg.sender).settleCallback(token, tokenDelta[token]); + reservesOf[token] = token.balanceOf(address(this)); // subtraction must be safe - _accountDelta(token, -(reserves[token] - reservesBefore).toInt256()); + _accountDelta(token, -(reservesOf[token] - reservesBefore).toInt256()); } /// @notice Update the protocol fee for a given pool - function setFeeProtocol(PoolKey calldata key, uint8 feeProtocol) external returns (uint8 feeProtocolOld) { + 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(PoolKey calldata key, uint32[] calldata secondsAgos) + function observe(IPoolManager.PoolKey calldata key, uint32[] calldata secondsAgos) external view + override noDelegateCall returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) { @@ -269,10 +251,10 @@ contract PoolManager is NoDelegateCall { /// @notice Get the snapshot of the cumulative values of a tick range function snapshotCumulativesInside( - PoolKey calldata key, + IPoolManager.PoolKey calldata key, int24 tickLower, int24 tickUpper - ) external view noDelegateCall returns (Pool.Snapshot memory) { + ) external view override noDelegateCall returns (Pool.Snapshot memory) { return _getPool(key).snapshotCumulativesInside(tickLower, tickUpper, _blockTimestamp()); } } diff --git a/contracts/interfaces/IPoolManager.sol b/contracts/interfaces/IPoolManager.sol new file mode 100644 index 000000000..405c2610f --- /dev/null +++ b/contracts/interfaces/IPoolManager.sol @@ -0,0 +1,102 @@ +// 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 tokensTouchedBloomFilter() external view returns (uint256); + + function tokensTouched(uint256 index) external view returns (IERC20Minimal); + + function tokenDelta(IERC20Minimal token) external view returns (int256); + + /// @notice All operations go through this function + function lock(bytes calldata data) external; + + /// @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; + + /// @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/ILockCallback.sol b/contracts/interfaces/callback/ILockCallback.sol deleted file mode 100644 index 3b2ab1345..000000000 --- a/contracts/interfaces/callback/ILockCallback.sol +++ /dev/null @@ -1,7 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.9; - -interface ILockCallback { - /// @notice Called by the pool manager on `msg.sender` when a lock is acquired - function lockAcquired() external; -} diff --git a/contracts/interfaces/callback/IPoolManagerUser.sol b/contracts/interfaces/callback/IPoolManagerUser.sol new file mode 100644 index 000000000..371e4d75f --- /dev/null +++ b/contracts/interfaces/callback/IPoolManagerUser.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.6.2; + +import {IERC20Minimal} from '../external/IERC20Minimal.sol'; + +/// @notice Capable of acquiring locks and interacting with the IPoolManager +interface IPoolManagerUser { + /// @notice Called by the pool manager on `msg.sender` when a lock is acquired + function lockAcquired(bytes calldata data) external returns (bool); + + /// @notice Called on the caller when settle is called, in order for the caller to send payment + function settleCallback(IERC20Minimal token, int256 delta) external; +} diff --git a/contracts/interfaces/callback/ISettleCallback.sol b/contracts/interfaces/callback/ISettleCallback.sol deleted file mode 100644 index ec9ea2cce..000000000 --- a/contracts/interfaces/callback/ISettleCallback.sol +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.9; - -import {IERC20Minimal} from '../external/IERC20Minimal.sol'; - -interface ISettleCallback { - function settleCallback(IERC20Minimal token) external; -} 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/PoolManagerTest.sol b/contracts/test/PoolManagerTest.sol new file mode 100644 index 000000000..cd02db4b3 --- /dev/null +++ b/contracts/test/PoolManagerTest.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.8.10; + +import {IERC20Minimal} from '../interfaces/external/IERC20Minimal.sol'; +import {IPoolManager} from '../interfaces/IPoolManager.sol'; +import {IPoolManagerUser} from '../interfaces/callback/IPoolManagerUser.sol'; + +contract PoolManagerTest is IPoolManagerUser { + 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 (bool) { + return true; + } + + /// @notice Called on the caller when settle is called, in order for the caller to send payment + function settleCallback(IERC20Minimal, int256) external { + revert('cannot pay'); + } +} diff --git a/test/PoolManager.spec.ts b/test/PoolManager.spec.ts index 101f24c21..b1c10656e 100644 --- a/test/PoolManager.spec.ts +++ b/test/PoolManager.spec.ts @@ -1,6 +1,6 @@ import { Wallet } from 'ethers' import { ethers, waffle } from 'hardhat' -import { PoolManager, TestERC20 } from '../typechain' +import { PoolManager, TestERC20, PoolManagerTest } from '../typechain' import { expect } from './shared/expect' import { tokensFixture } from './shared/fixtures' import snapshotGasCost from './shared/snapshotGasCost' @@ -11,13 +11,16 @@ const createFixtureLoader = waffle.createFixtureLoader describe.only('PoolManager', () => { let wallet: Wallet, other: Wallet - let singleton: PoolManager + let manager: PoolManager + let managerTest: PoolManagerTest let tokens: { token0: TestERC20; token1: TestERC20; token2: TestERC20 } const fixture = async () => { const singletonPoolFactory = await ethers.getContractFactory('PoolManager') + const managerTestFactory = await ethers.getContractFactory('PoolManagerTest') const tokens = await tokensFixture() return { - singleton: (await singletonPoolFactory.deploy()) as PoolManager, + manager: (await singletonPoolFactory.deploy()) as PoolManager, + managerTest: (await managerTestFactory.deploy()) as PoolManagerTest, tokens, } } @@ -30,16 +33,26 @@ describe.only('PoolManager', () => { }) beforeEach('deploy fixture', async () => { - ;({ singleton, tokens } = await loadFixture(fixture)) + ;({ manager, tokens, managerTest } = await loadFixture(fixture)) }) it('bytecode size', async () => { - expect(((await waffle.provider.getCode(singleton.address)).length - 2) / 2).to.matchSnapshot() + expect(((await waffle.provider.getCode(manager.address)).length - 2) / 2).to.matchSnapshot() + }) + + describe('#lock', () => { + it('no-op lock is ok', async () => { + await managerTest.lock(manager.address) + }) + + it('gas overhead of no-op lock', async () => { + await snapshotGasCost(managerTest.lock(manager.address)) + }) }) describe('#initialize', async () => { it('initializes a pool', async () => { - await singleton.initialize( + await manager.initialize( { token0: tokens.token0.address, token1: tokens.token1.address, @@ -50,7 +63,7 @@ describe.only('PoolManager', () => { const { slot0: { sqrtPriceX96 }, - } = await singleton.pools( + } = await manager.pools( getPoolId({ token0: tokens.token0.address, token1: tokens.token1.address, @@ -62,7 +75,7 @@ describe.only('PoolManager', () => { it('gas cost', async () => { await snapshotGasCost( - singleton.initialize( + manager.initialize( { token0: tokens.token0.address, token1: tokens.token1.address, @@ -77,7 +90,7 @@ describe.only('PoolManager', () => { describe.skip('#mint', async () => { it('reverts if pool not initialized', async () => { await expect( - singleton.mint( + manager.mint( { token0: tokens.token0.address, token1: tokens.token1.address, @@ -94,7 +107,7 @@ describe.only('PoolManager', () => { }) it('succeeds if pool is initialized', async () => { - await singleton.initialize( + await manager.initialize( { token0: tokens.token0.address, token1: tokens.token1.address, @@ -103,7 +116,7 @@ describe.only('PoolManager', () => { encodeSqrtPriceX96(1, 1) ) - await singleton.mint( + await manager.mint( { token0: tokens.token0.address, token1: tokens.token1.address, @@ -119,7 +132,7 @@ describe.only('PoolManager', () => { }) it('gas cost', async () => { - await singleton.initialize( + await manager.initialize( { token0: tokens.token0.address, token1: tokens.token1.address, @@ -129,7 +142,7 @@ describe.only('PoolManager', () => { ) await snapshotGasCost( - singleton.mint( + manager.mint( { token0: tokens.token0.address, token1: tokens.token1.address, @@ -149,7 +162,7 @@ describe.only('PoolManager', () => { describe.skip('#swap', () => { it('fails if pool is not initialized', async () => { await expect( - singleton.swap( + manager.swap( { token0: tokens.token0.address, token1: tokens.token1.address, @@ -164,7 +177,7 @@ describe.only('PoolManager', () => { ).to.be.revertedWith('I') }) it('succeeds if pool is not initialized', async () => { - await singleton.initialize( + await manager.initialize( { token0: tokens.token0.address, token1: tokens.token1.address, @@ -172,7 +185,7 @@ describe.only('PoolManager', () => { }, encodeSqrtPriceX96(1, 1) ) - await singleton.swap( + await manager.swap( { token0: tokens.token0.address, token1: tokens.token1.address, @@ -186,7 +199,7 @@ describe.only('PoolManager', () => { ) }) it('gas', async () => { - await singleton.initialize( + await manager.initialize( { token0: tokens.token0.address, token1: tokens.token1.address, @@ -195,7 +208,7 @@ describe.only('PoolManager', () => { encodeSqrtPriceX96(1, 1) ) await snapshotGasCost( - singleton.swap( + manager.swap( { token0: tokens.token0.address, token1: tokens.token1.address, diff --git a/test/__snapshots__/PoolManager.spec.ts.snap b/test/__snapshots__/PoolManager.spec.ts.snap index c4779d10a..d21967878 100644 --- a/test/__snapshots__/PoolManager.spec.ts.snap +++ b/test/__snapshots__/PoolManager.spec.ts.snap @@ -1,5 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`PoolManager #initialize gas cost 1`] = `70657`; +exports[`PoolManager #initialize gas cost 1`] = `70701`; -exports[`PoolManager bytecode size 1`] = `20389`; +exports[`PoolManager #lock gas overhead of no-op lock 1`] = `42664`; + +exports[`PoolManager bytecode size 1`] = `20677`; From acae23cf3dcd99fd1e960272572fed82f48d22ed Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Fri, 19 Nov 2021 13:34:53 -0500 Subject: [PATCH 24/40] remove the generic stuff --- contracts/PoolManager.sol | 18 +++++++++++++----- contracts/interfaces/IPoolManager.sol | 2 +- .../interfaces/callback/ILockCallback.sol | 8 ++++++++ .../interfaces/callback/IPoolManagerUser.sol | 13 ------------- .../interfaces/callback/ISettleCallback.sol | 13 +++++++++++++ contracts/test/PoolManagerTest.sol | 9 ++------- test/__snapshots__/PoolManager.spec.ts.snap | 6 +++--- 7 files changed, 40 insertions(+), 29 deletions(-) create mode 100644 contracts/interfaces/callback/ILockCallback.sol delete mode 100644 contracts/interfaces/callback/IPoolManagerUser.sol create mode 100644 contracts/interfaces/callback/ISettleCallback.sol diff --git a/contracts/PoolManager.sol b/contracts/PoolManager.sol index 640cfd5f5..a70ab000a 100644 --- a/contracts/PoolManager.sol +++ b/contracts/PoolManager.sol @@ -6,9 +6,10 @@ import {Tick} from './libraries/Tick.sol'; import {SafeCast} from './libraries/SafeCast.sol'; import {IERC20Minimal} from './interfaces/external/IERC20Minimal.sol'; -import {IPoolManagerUser} from './interfaces/callback/IPoolManagerUser.sol'; import {NoDelegateCall} from './NoDelegateCall.sol'; import {IPoolManager} from './interfaces/IPoolManager.sol'; +import {ILockCallback} from './interfaces/callback/ILockCallback.sol'; +import {ISettleCallback} from './interfaces/callback/ISettleCallback.sol'; /// @notice Holds the state for all pools contract PoolManager is IPoolManager, NoDelegateCall { @@ -87,7 +88,7 @@ contract PoolManager is IPoolManager, NoDelegateCall { function lock(bytes calldata data) external override acquiresLock { // the caller does everything in this callback, including paying what they owe - require(IPoolManagerUser(msg.sender).lockAcquired(data), 'No data'); + require(ILockCallback(msg.sender).lockAcquired(data), 'No data'); for (uint256 i = 0; i < tokensTouched.length; i++) { require(tokenDelta[tokensTouched[i]] == 0, 'Not settled'); @@ -221,12 +222,19 @@ contract PoolManager is IPoolManager, NoDelegateCall { } /// @notice Called by the user to pay what is owed - function settle(IERC20Minimal token) external override noDelegateCall onlyLocker { + function settle(IERC20Minimal token, bytes calldata data) + external + override + noDelegateCall + onlyLocker + returns (uint256 paid) + { uint256 reservesBefore = reservesOf[token]; - IPoolManagerUser(msg.sender).settleCallback(token, tokenDelta[token]); + ISettleCallback(msg.sender).settleCallback(token, tokenDelta[token], data); reservesOf[token] = token.balanceOf(address(this)); + paid = reservesOf[token] - reservesBefore; // subtraction must be safe - _accountDelta(token, -(reservesOf[token] - reservesBefore).toInt256()); + _accountDelta(token, -(paid.toInt256())); } /// @notice Update the protocol fee for a given pool diff --git a/contracts/interfaces/IPoolManager.sol b/contracts/interfaces/IPoolManager.sol index 405c2610f..bca4bd317 100644 --- a/contracts/interfaces/IPoolManager.sol +++ b/contracts/interfaces/IPoolManager.sol @@ -82,7 +82,7 @@ interface IPoolManager { ) external; /// @notice Called by the user to pay what is owed - function settle(IERC20Minimal token) external; + function settle(IERC20Minimal token, bytes calldata data) external returns (uint256 paid); /// @notice Update the protocol fee for a given pool function setFeeProtocol(PoolKey calldata key, uint8 feeProtocol) external returns (uint8 feeProtocolOld); diff --git a/contracts/interfaces/callback/ILockCallback.sol b/contracts/interfaces/callback/ILockCallback.sol new file mode 100644 index 000000000..21b98103c --- /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 (bool); +} diff --git a/contracts/interfaces/callback/IPoolManagerUser.sol b/contracts/interfaces/callback/IPoolManagerUser.sol deleted file mode 100644 index 371e4d75f..000000000 --- a/contracts/interfaces/callback/IPoolManagerUser.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.6.2; - -import {IERC20Minimal} from '../external/IERC20Minimal.sol'; - -/// @notice Capable of acquiring locks and interacting with the IPoolManager -interface IPoolManagerUser { - /// @notice Called by the pool manager on `msg.sender` when a lock is acquired - function lockAcquired(bytes calldata data) external returns (bool); - - /// @notice Called on the caller when settle is called, in order for the caller to send payment - function settleCallback(IERC20Minimal token, int256 delta) external; -} diff --git a/contracts/interfaces/callback/ISettleCallback.sol b/contracts/interfaces/callback/ISettleCallback.sol new file mode 100644 index 000000000..4e3f478ee --- /dev/null +++ b/contracts/interfaces/callback/ISettleCallback.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.6.2; + +import {IERC20Minimal} from '../external/IERC20Minimal.sol'; + +interface ISettleCallback { + /// @notice Called on the caller when settle is called, in order for the caller to send payment + function settleCallback( + IERC20Minimal token, + int256 delta, + bytes calldata data + ) external; +} diff --git a/contracts/test/PoolManagerTest.sol b/contracts/test/PoolManagerTest.sol index cd02db4b3..a551f2cee 100644 --- a/contracts/test/PoolManagerTest.sol +++ b/contracts/test/PoolManagerTest.sol @@ -3,9 +3,9 @@ pragma solidity =0.8.10; import {IERC20Minimal} from '../interfaces/external/IERC20Minimal.sol'; import {IPoolManager} from '../interfaces/IPoolManager.sol'; -import {IPoolManagerUser} from '../interfaces/callback/IPoolManagerUser.sol'; +import {ILockCallback} from '../interfaces/callback/ILockCallback.sol'; -contract PoolManagerTest is IPoolManagerUser { +contract PoolManagerTest is ILockCallback { function lock(IPoolManager manager) external { manager.lock(''); } @@ -14,9 +14,4 @@ contract PoolManagerTest is IPoolManagerUser { function lockAcquired(bytes calldata) external override returns (bool) { return true; } - - /// @notice Called on the caller when settle is called, in order for the caller to send payment - function settleCallback(IERC20Minimal, int256) external { - revert('cannot pay'); - } } diff --git a/test/__snapshots__/PoolManager.spec.ts.snap b/test/__snapshots__/PoolManager.spec.ts.snap index d21967878..1f41ddc84 100644 --- a/test/__snapshots__/PoolManager.spec.ts.snap +++ b/test/__snapshots__/PoolManager.spec.ts.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`PoolManager #initialize gas cost 1`] = `70701`; +exports[`PoolManager #initialize gas cost 1`] = `70817`; -exports[`PoolManager #lock gas overhead of no-op lock 1`] = `42664`; +exports[`PoolManager #lock gas overhead of no-op lock 1`] = `42665`; -exports[`PoolManager bytecode size 1`] = `20677`; +exports[`PoolManager bytecode size 1`] = `20841`; From 4a81c6f9b946c375af0a63214864882af2d9fd13 Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Fri, 19 Nov 2021 15:35:42 -0500 Subject: [PATCH 25/40] add the swap test code that should work --- contracts/PoolManager.sol | 16 ++--- contracts/interfaces/IPoolManager.sol | 2 +- .../interfaces/callback/ILockCallback.sol | 2 +- .../interfaces/callback/ISettleCallback.sol | 1 + contracts/test/PoolManagerTest.sol | 4 +- contracts/test/PoolSwapTest.sol | 58 +++++++++++++++++++ test/__snapshots__/PoolManager.spec.ts.snap | 4 +- 7 files changed, 72 insertions(+), 15 deletions(-) create mode 100644 contracts/test/PoolSwapTest.sol diff --git a/contracts/PoolManager.sol b/contracts/PoolManager.sol index a70ab000a..eb18b1297 100644 --- a/contracts/PoolManager.sol +++ b/contracts/PoolManager.sol @@ -86,9 +86,9 @@ contract PoolManager is IPoolManager, NoDelegateCall { delete lockedBy; } - function lock(bytes calldata data) external override acquiresLock { + function lock(bytes calldata data) external override acquiresLock returns (bytes memory result) { // the caller does everything in this callback, including paying what they owe - require(ILockCallback(msg.sender).lockAcquired(data), 'No data'); + result = ILockCallback(msg.sender).lockAcquired(data); for (uint256 i = 0; i < tokensTouched.length; i++) { require(tokenDelta[tokensTouched[i]] == 0, 'Not settled'); @@ -128,7 +128,7 @@ contract PoolManager is IPoolManager, NoDelegateCall { _accountDelta(key.token1, delta.amount1); } - modifier onlyLocker() { + modifier onlyByLocker() { require(msg.sender == lockedBy, 'LOK'); _; } @@ -138,7 +138,7 @@ contract PoolManager is IPoolManager, NoDelegateCall { external override noDelegateCall - onlyLocker + onlyByLocker returns (Pool.BalanceDelta memory delta) { require(params.amount > 0); @@ -165,7 +165,7 @@ contract PoolManager is IPoolManager, NoDelegateCall { external override noDelegateCall - onlyLocker + onlyByLocker returns (Pool.BalanceDelta memory delta) { require(params.amount > 0); @@ -191,7 +191,7 @@ contract PoolManager is IPoolManager, NoDelegateCall { external override noDelegateCall - onlyLocker + onlyByLocker returns (Pool.BalanceDelta memory delta) { FeeConfig memory config = configs[key.fee]; @@ -216,7 +216,7 @@ contract PoolManager is IPoolManager, NoDelegateCall { IERC20Minimal token, address to, uint256 amount - ) external override noDelegateCall onlyLocker { + ) external override noDelegateCall onlyByLocker { _accountDelta(token, amount.toInt256()); token.transfer(to, amount); } @@ -226,7 +226,7 @@ contract PoolManager is IPoolManager, NoDelegateCall { external override noDelegateCall - onlyLocker + onlyByLocker returns (uint256 paid) { uint256 reservesBefore = reservesOf[token]; diff --git a/contracts/interfaces/IPoolManager.sol b/contracts/interfaces/IPoolManager.sol index bca4bd317..c3b070572 100644 --- a/contracts/interfaces/IPoolManager.sol +++ b/contracts/interfaces/IPoolManager.sol @@ -49,7 +49,7 @@ interface IPoolManager { function tokenDelta(IERC20Minimal token) external view returns (int256); /// @notice All operations go through this function - function lock(bytes calldata data) external; + 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); diff --git a/contracts/interfaces/callback/ILockCallback.sol b/contracts/interfaces/callback/ILockCallback.sol index 21b98103c..0e10c71df 100644 --- a/contracts/interfaces/callback/ILockCallback.sol +++ b/contracts/interfaces/callback/ILockCallback.sol @@ -4,5 +4,5 @@ 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 (bool); + function lockAcquired(bytes calldata data) external returns (bytes memory); } diff --git a/contracts/interfaces/callback/ISettleCallback.sol b/contracts/interfaces/callback/ISettleCallback.sol index 4e3f478ee..3f9ea67f0 100644 --- a/contracts/interfaces/callback/ISettleCallback.sol +++ b/contracts/interfaces/callback/ISettleCallback.sol @@ -3,6 +3,7 @@ pragma solidity >=0.6.2; import {IERC20Minimal} from '../external/IERC20Minimal.sol'; +/// @dev This interface is separate because some uses may #lock but not ever need to #settle because they are arbitrage-only bots interface ISettleCallback { /// @notice Called on the caller when settle is called, in order for the caller to send payment function settleCallback( diff --git a/contracts/test/PoolManagerTest.sol b/contracts/test/PoolManagerTest.sol index a551f2cee..2ffb5afb1 100644 --- a/contracts/test/PoolManagerTest.sol +++ b/contracts/test/PoolManagerTest.sol @@ -11,7 +11,5 @@ contract PoolManagerTest is ILockCallback { } /// @notice Called by the pool manager on `msg.sender` when a lock is acquired - function lockAcquired(bytes calldata) external override returns (bool) { - return true; - } + function lockAcquired(bytes calldata) external override returns (bytes memory) {} } diff --git a/contracts/test/PoolSwapTest.sol b/contracts/test/PoolSwapTest.sol new file mode 100644 index 000000000..80b02967d --- /dev/null +++ b/contracts/test/PoolSwapTest.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.8.10; + +import {IERC20Minimal} from '../interfaces/external/IERC20Minimal.sol'; + +import {ILockCallback} from '../interfaces/callback/ILockCallback.sol'; +import {ISettleCallback} from '../interfaces/callback/ISettleCallback.sol'; +import {IPoolManager} from '../interfaces/IPoolManager.sol'; + +import {Pool} from '../libraries/Pool.sol'; + +contract PoolSwapTest is ILockCallback, ISettleCallback { + 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 (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.swap(data.key, data.params); + + manager.settle(data.key.token0, abi.encode(data.sender)); + manager.settle(data.key.token1, abi.encode(data.sender)); + + return abi.encode(delta); + } + + function settleCallback( + IERC20Minimal token, + int256 delta, + bytes calldata data + ) external override { + require(msg.sender == address(manager)); + + address sender = abi.decode(data, (address)); + + if (delta > 0) { + token.transferFrom(sender, msg.sender, uint256(delta)); + } + } +} diff --git a/test/__snapshots__/PoolManager.spec.ts.snap b/test/__snapshots__/PoolManager.spec.ts.snap index 1f41ddc84..debadcf58 100644 --- a/test/__snapshots__/PoolManager.spec.ts.snap +++ b/test/__snapshots__/PoolManager.spec.ts.snap @@ -2,6 +2,6 @@ exports[`PoolManager #initialize gas cost 1`] = `70817`; -exports[`PoolManager #lock gas overhead of no-op lock 1`] = `42665`; +exports[`PoolManager #lock gas overhead of no-op lock 1`] = `43584`; -exports[`PoolManager bytecode size 1`] = `20841`; +exports[`PoolManager bytecode size 1`] = `21004`; From 9b608d53f815b7852286199ec1ec0485eb332021 Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Fri, 19 Nov 2021 16:01:36 -0500 Subject: [PATCH 26/40] unskip the tests --- contracts/test/PoolBurnTest.sol | 58 +++++++++++++++++++++ contracts/test/PoolMintTest.sol | 58 +++++++++++++++++++++ test/PoolManager.spec.ts | 55 +++++++++++++------ test/__snapshots__/PoolManager.spec.ts.snap | 4 ++ 4 files changed, 158 insertions(+), 17 deletions(-) create mode 100644 contracts/test/PoolBurnTest.sol create mode 100644 contracts/test/PoolMintTest.sol diff --git a/contracts/test/PoolBurnTest.sol b/contracts/test/PoolBurnTest.sol new file mode 100644 index 000000000..154947a13 --- /dev/null +++ b/contracts/test/PoolBurnTest.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.8.10; + +import {IERC20Minimal} from '../interfaces/external/IERC20Minimal.sol'; + +import {ILockCallback} from '../interfaces/callback/ILockCallback.sol'; +import {ISettleCallback} from '../interfaces/callback/ISettleCallback.sol'; +import {IPoolManager} from '../interfaces/IPoolManager.sol'; + +import {Pool} from '../libraries/Pool.sol'; + +contract PoolBurnTest is ILockCallback, ISettleCallback { + 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); + + manager.settle(data.key.token0, abi.encode(data.sender)); + manager.settle(data.key.token1, abi.encode(data.sender)); + + return abi.encode(delta); + } + + function settleCallback( + IERC20Minimal token, + int256 delta, + bytes calldata data + ) external override { + require(msg.sender == address(manager)); + + address sender = abi.decode(data, (address)); + + if (delta > 0) { + token.transferFrom(sender, msg.sender, uint256(delta)); + } + } +} diff --git a/contracts/test/PoolMintTest.sol b/contracts/test/PoolMintTest.sol new file mode 100644 index 000000000..195851eda --- /dev/null +++ b/contracts/test/PoolMintTest.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.8.10; + +import {IERC20Minimal} from '../interfaces/external/IERC20Minimal.sol'; + +import {ILockCallback} from '../interfaces/callback/ILockCallback.sol'; +import {ISettleCallback} from '../interfaces/callback/ISettleCallback.sol'; +import {IPoolManager} from '../interfaces/IPoolManager.sol'; + +import {Pool} from '../libraries/Pool.sol'; + +contract PoolMintTest is ILockCallback, ISettleCallback { + 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); + + manager.settle(data.key.token0, abi.encode(data.sender)); + manager.settle(data.key.token1, abi.encode(data.sender)); + + return abi.encode(delta); + } + + function settleCallback( + IERC20Minimal token, + int256 delta, + bytes calldata data + ) external override { + require(msg.sender == address(manager)); + + address sender = abi.decode(data, (address)); + + if (delta > 0) { + token.transferFrom(sender, msg.sender, uint256(delta)); + } + } +} diff --git a/test/PoolManager.spec.ts b/test/PoolManager.spec.ts index b1c10656e..18651d2a1 100644 --- a/test/PoolManager.spec.ts +++ b/test/PoolManager.spec.ts @@ -1,6 +1,6 @@ import { Wallet } from 'ethers' import { ethers, waffle } from 'hardhat' -import { PoolManager, TestERC20, PoolManagerTest } from '../typechain' +import { PoolManager, TestERC20, PoolManagerTest, PoolSwapTest, PoolMintTest, PoolBurnTest } from '../typechain' import { expect } from './shared/expect' import { tokensFixture } from './shared/fixtures' import snapshotGasCost from './shared/snapshotGasCost' @@ -8,21 +8,42 @@ import { encodeSqrtPriceX96, FeeAmount, getPoolId } from './shared/utilities' const createFixtureLoader = waffle.createFixtureLoader +const { constants } = ethers + describe.only('PoolManager', () => { let wallet: Wallet, other: Wallet let manager: PoolManager - let managerTest: PoolManagerTest + 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() - return { - manager: (await singletonPoolFactory.deploy()) as PoolManager, - managerTest: (await managerTestFactory.deploy()) as PoolManagerTest, + 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 @@ -33,7 +54,7 @@ describe.only('PoolManager', () => { }) beforeEach('deploy fixture', async () => { - ;({ manager, tokens, managerTest } = await loadFixture(fixture)) + ;({ manager, tokens, lockTest, mintTest, burnTest, swapTest } = await loadFixture(fixture)) }) it('bytecode size', async () => { @@ -42,11 +63,11 @@ describe.only('PoolManager', () => { describe('#lock', () => { it('no-op lock is ok', async () => { - await managerTest.lock(manager.address) + await lockTest.lock(manager.address) }) it('gas overhead of no-op lock', async () => { - await snapshotGasCost(managerTest.lock(manager.address)) + await snapshotGasCost(lockTest.lock(manager.address)) }) }) @@ -87,10 +108,10 @@ describe.only('PoolManager', () => { }) }) - describe.skip('#mint', async () => { + describe('#mint', async () => { it('reverts if pool not initialized', async () => { await expect( - manager.mint( + mintTest.mint( { token0: tokens.token0.address, token1: tokens.token1.address, @@ -116,7 +137,7 @@ describe.only('PoolManager', () => { encodeSqrtPriceX96(1, 1) ) - await manager.mint( + await mintTest.mint( { token0: tokens.token0.address, token1: tokens.token1.address, @@ -142,7 +163,7 @@ describe.only('PoolManager', () => { ) await snapshotGasCost( - manager.mint( + mintTest.mint( { token0: tokens.token0.address, token1: tokens.token1.address, @@ -159,10 +180,10 @@ describe.only('PoolManager', () => { }) }) - describe.skip('#swap', () => { + describe('#swap', () => { it('fails if pool is not initialized', async () => { await expect( - manager.swap( + swapTest.swap( { token0: tokens.token0.address, token1: tokens.token1.address, @@ -176,7 +197,7 @@ describe.only('PoolManager', () => { ) ).to.be.revertedWith('I') }) - it('succeeds if pool is not initialized', async () => { + it('succeeds if pool is initialized', async () => { await manager.initialize( { token0: tokens.token0.address, @@ -185,7 +206,7 @@ describe.only('PoolManager', () => { }, encodeSqrtPriceX96(1, 1) ) - await manager.swap( + await swapTest.swap( { token0: tokens.token0.address, token1: tokens.token1.address, @@ -208,7 +229,7 @@ describe.only('PoolManager', () => { encodeSqrtPriceX96(1, 1) ) await snapshotGasCost( - manager.swap( + swapTest.swap( { token0: tokens.token0.address, token1: tokens.token1.address, diff --git a/test/__snapshots__/PoolManager.spec.ts.snap b/test/__snapshots__/PoolManager.spec.ts.snap index debadcf58..721c345bc 100644 --- a/test/__snapshots__/PoolManager.spec.ts.snap +++ b/test/__snapshots__/PoolManager.spec.ts.snap @@ -4,4 +4,8 @@ exports[`PoolManager #initialize gas cost 1`] = `70817`; exports[`PoolManager #lock gas overhead of no-op lock 1`] = `43584`; +exports[`PoolManager #mint gas cost 1`] = `362333`; + +exports[`PoolManager #swap gas 1`] = `185340`; + exports[`PoolManager bytecode size 1`] = `21004`; From d8a1c7fe56c795da12de16fadec859b71bd9d36a Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Sat, 20 Nov 2021 16:01:46 -0500 Subject: [PATCH 27/40] improve how settle works (only one callback) --- contracts/PoolManager.sol | 24 +++------- contracts/interfaces/IPoolManager.sol | 2 +- .../interfaces/callback/ISettleCallback.sol | 14 ------ contracts/test/PoolBurnTest.sol | 25 +++-------- contracts/test/PoolMintTest.sol | 27 ++++------- contracts/test/PoolSwapTest.sol | 28 +++++------- test/PoolManager.spec.ts | 45 +++++++++++++++++-- test/__snapshots__/PoolManager.spec.ts.snap | 12 ++--- 8 files changed, 81 insertions(+), 96 deletions(-) delete mode 100644 contracts/interfaces/callback/ISettleCallback.sol diff --git a/contracts/PoolManager.sol b/contracts/PoolManager.sol index eb18b1297..9df5c1ec1 100644 --- a/contracts/PoolManager.sol +++ b/contracts/PoolManager.sol @@ -9,7 +9,8 @@ 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 {ISettleCallback} from './interfaces/callback/ISettleCallback.sol'; + +import {console} from 'hardhat/console.sol'; /// @notice Holds the state for all pools contract PoolManager is IPoolManager, NoDelegateCall { @@ -76,18 +77,11 @@ contract PoolManager is IPoolManager, NoDelegateCall { IERC20Minimal[] public override tokensTouched; mapping(IERC20Minimal => int256) public override tokenDelta; - /// @dev Used to make sure all actions within the lock function are wrapped in the lock acquisition. Note this has no gas overhead because it's inlined. - modifier acquiresLock() { + function lock(bytes calldata data) external override returns (bytes memory result) { require(lockedBy == address(0)); lockedBy = msg.sender; - _; - - delete lockedBy; - } - - function lock(bytes calldata data) external override acquiresLock returns (bytes memory result) { - // the caller does everything in this callback, including paying what they owe + // the caller does everything in this callback, including paying what they owe via calls to settle result = ILockCallback(msg.sender).lockAcquired(data); for (uint256 i = 0; i < tokensTouched.length; i++) { @@ -95,6 +89,7 @@ contract PoolManager is IPoolManager, NoDelegateCall { } delete tokensTouchedBloomFilter; delete tokensTouched; + delete lockedBy; } /// @dev Adds a token to a unique list of tokens that have been touched @@ -222,15 +217,8 @@ contract PoolManager is IPoolManager, NoDelegateCall { } /// @notice Called by the user to pay what is owed - function settle(IERC20Minimal token, bytes calldata data) - external - override - noDelegateCall - onlyByLocker - returns (uint256 paid) - { + function settle(IERC20Minimal token) external override noDelegateCall onlyByLocker returns (uint256 paid) { uint256 reservesBefore = reservesOf[token]; - ISettleCallback(msg.sender).settleCallback(token, tokenDelta[token], data); reservesOf[token] = token.balanceOf(address(this)); paid = reservesOf[token] - reservesBefore; // subtraction must be safe diff --git a/contracts/interfaces/IPoolManager.sol b/contracts/interfaces/IPoolManager.sol index c3b070572..40ba56fb6 100644 --- a/contracts/interfaces/IPoolManager.sol +++ b/contracts/interfaces/IPoolManager.sol @@ -82,7 +82,7 @@ interface IPoolManager { ) external; /// @notice Called by the user to pay what is owed - function settle(IERC20Minimal token, bytes calldata data) external returns (uint256 paid); + 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); diff --git a/contracts/interfaces/callback/ISettleCallback.sol b/contracts/interfaces/callback/ISettleCallback.sol deleted file mode 100644 index 3f9ea67f0..000000000 --- a/contracts/interfaces/callback/ISettleCallback.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.6.2; - -import {IERC20Minimal} from '../external/IERC20Minimal.sol'; - -/// @dev This interface is separate because some uses may #lock but not ever need to #settle because they are arbitrage-only bots -interface ISettleCallback { - /// @notice Called on the caller when settle is called, in order for the caller to send payment - function settleCallback( - IERC20Minimal token, - int256 delta, - bytes calldata data - ) external; -} diff --git a/contracts/test/PoolBurnTest.sol b/contracts/test/PoolBurnTest.sol index 154947a13..ba8eb9005 100644 --- a/contracts/test/PoolBurnTest.sol +++ b/contracts/test/PoolBurnTest.sol @@ -4,12 +4,11 @@ pragma solidity =0.8.10; import {IERC20Minimal} from '../interfaces/external/IERC20Minimal.sol'; import {ILockCallback} from '../interfaces/callback/ILockCallback.sol'; -import {ISettleCallback} from '../interfaces/callback/ISettleCallback.sol'; import {IPoolManager} from '../interfaces/IPoolManager.sol'; import {Pool} from '../libraries/Pool.sol'; -contract PoolBurnTest is ILockCallback, ISettleCallback { +contract PoolBurnTest is ILockCallback { IPoolManager public immutable manager; constructor(IPoolManager _manager) { @@ -36,23 +35,13 @@ contract PoolBurnTest is ILockCallback, ISettleCallback { Pool.BalanceDelta memory delta = manager.burn(data.key, data.params); - manager.settle(data.key.token0, abi.encode(data.sender)); - manager.settle(data.key.token1, abi.encode(data.sender)); + 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); } - - function settleCallback( - IERC20Minimal token, - int256 delta, - bytes calldata data - ) external override { - require(msg.sender == address(manager)); - - address sender = abi.decode(data, (address)); - - if (delta > 0) { - token.transferFrom(sender, msg.sender, uint256(delta)); - } - } } diff --git a/contracts/test/PoolMintTest.sol b/contracts/test/PoolMintTest.sol index 195851eda..cb83e5fb9 100644 --- a/contracts/test/PoolMintTest.sol +++ b/contracts/test/PoolMintTest.sol @@ -4,12 +4,11 @@ pragma solidity =0.8.10; import {IERC20Minimal} from '../interfaces/external/IERC20Minimal.sol'; import {ILockCallback} from '../interfaces/callback/ILockCallback.sol'; -import {ISettleCallback} from '../interfaces/callback/ISettleCallback.sol'; import {IPoolManager} from '../interfaces/IPoolManager.sol'; import {Pool} from '../libraries/Pool.sol'; -contract PoolMintTest is ILockCallback, ISettleCallback { +contract PoolMintTest is ILockCallback { IPoolManager public immutable manager; constructor(IPoolManager _manager) { @@ -36,23 +35,15 @@ contract PoolMintTest is ILockCallback, ISettleCallback { Pool.BalanceDelta memory delta = manager.mint(data.key, data.params); - manager.settle(data.key.token0, abi.encode(data.sender)); - manager.settle(data.key.token1, abi.encode(data.sender)); + 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); } - - function settleCallback( - IERC20Minimal token, - int256 delta, - bytes calldata data - ) external override { - require(msg.sender == address(manager)); - - address sender = abi.decode(data, (address)); - - if (delta > 0) { - token.transferFrom(sender, msg.sender, uint256(delta)); - } - } } diff --git a/contracts/test/PoolSwapTest.sol b/contracts/test/PoolSwapTest.sol index 80b02967d..9ec8234fe 100644 --- a/contracts/test/PoolSwapTest.sol +++ b/contracts/test/PoolSwapTest.sol @@ -4,12 +4,11 @@ pragma solidity =0.8.10; import {IERC20Minimal} from '../interfaces/external/IERC20Minimal.sol'; import {ILockCallback} from '../interfaces/callback/ILockCallback.sol'; -import {ISettleCallback} from '../interfaces/callback/ISettleCallback.sol'; import {IPoolManager} from '../interfaces/IPoolManager.sol'; import {Pool} from '../libraries/Pool.sol'; -contract PoolSwapTest is ILockCallback, ISettleCallback { +contract PoolSwapTest is ILockCallback { IPoolManager public immutable manager; constructor(IPoolManager _manager) { @@ -36,23 +35,16 @@ contract PoolSwapTest is ILockCallback, ISettleCallback { Pool.BalanceDelta memory delta = manager.swap(data.key, data.params); - manager.settle(data.key.token0, abi.encode(data.sender)); - manager.settle(data.key.token1, abi.encode(data.sender)); + if (data.params.zeroForOne) { + data.key.token0.transferFrom(data.sender, address(manager), uint256(delta.amount0)); + manager.settle(data.key.token0); + manager.take(data.key.token1, data.sender, uint256(-delta.amount1)); + } else { + data.key.token1.transferFrom(data.sender, address(manager), uint256(delta.amount1)); + manager.settle(data.key.token1); + manager.take(data.key.token0, data.sender, uint256(-delta.amount0)); + } return abi.encode(delta); } - - function settleCallback( - IERC20Minimal token, - int256 delta, - bytes calldata data - ) external override { - require(msg.sender == address(manager)); - - address sender = abi.decode(data, (address)); - - if (delta > 0) { - token.transferFrom(sender, msg.sender, uint256(delta)); - } - } } diff --git a/test/PoolManager.spec.ts b/test/PoolManager.spec.ts index 18651d2a1..9bc8ec2df 100644 --- a/test/PoolManager.spec.ts +++ b/test/PoolManager.spec.ts @@ -4,7 +4,7 @@ import { PoolManager, TestERC20, PoolManagerTest, PoolSwapTest, PoolMintTest, Po import { expect } from './shared/expect' import { tokensFixture } from './shared/fixtures' import snapshotGasCost from './shared/snapshotGasCost' -import { encodeSqrtPriceX96, FeeAmount, getPoolId } from './shared/utilities' +import { encodeSqrtPriceX96, expandTo18Decimals, FeeAmount, getPoolId } from './shared/utilities' const createFixtureLoader = waffle.createFixtureLoader @@ -191,7 +191,7 @@ describe.only('PoolManager', () => { }, { amountSpecified: 100, - sqrtPriceLimitX96: encodeSqrtPriceX96(100, 1), + sqrtPriceLimitX96: encodeSqrtPriceX96(1, 2), zeroForOne: true, } ) @@ -214,7 +214,7 @@ describe.only('PoolManager', () => { }, { amountSpecified: 100, - sqrtPriceLimitX96: encodeSqrtPriceX96(1, 100), + sqrtPriceLimitX96: encodeSqrtPriceX96(1, 2), zeroForOne: true, } ) @@ -237,7 +237,44 @@ describe.only('PoolManager', () => { }, { amountSpecified: 100, - sqrtPriceLimitX96: encodeSqrtPriceX96(1, 100), + sqrtPriceLimitX96: encodeSqrtPriceX96(1, 2), + zeroForOne: true, + } + ) + ) + }) + it('gas for actual swap', 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 snapshotGasCost( + swapTest.swap( + { + token0: tokens.token0.address, + token1: tokens.token1.address, + fee: FeeAmount.MEDIUM, + }, + { + amountSpecified: 100, + sqrtPriceLimitX96: encodeSqrtPriceX96(1, 2), zeroForOne: true, } ) diff --git a/test/__snapshots__/PoolManager.spec.ts.snap b/test/__snapshots__/PoolManager.spec.ts.snap index 721c345bc..bfe56e663 100644 --- a/test/__snapshots__/PoolManager.spec.ts.snap +++ b/test/__snapshots__/PoolManager.spec.ts.snap @@ -1,11 +1,13 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`PoolManager #initialize gas cost 1`] = `70817`; +exports[`PoolManager #initialize gas cost 1`] = `70701`; -exports[`PoolManager #lock gas overhead of no-op lock 1`] = `43584`; +exports[`PoolManager #lock gas overhead of no-op lock 1`] = `43525`; -exports[`PoolManager #mint gas cost 1`] = `362333`; +exports[`PoolManager #mint gas cost 1`] = `349656`; -exports[`PoolManager #swap gas 1`] = `185340`; +exports[`PoolManager #swap gas 1`] = `178695`; -exports[`PoolManager bytecode size 1`] = `21004`; +exports[`PoolManager #swap gas for actual swap 1`] = `241316`; + +exports[`PoolManager bytecode size 1`] = `20741`; From 2ce95e5423392b6514b136ed0c6697c64fba3e43 Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Sat, 20 Nov 2021 16:16:09 -0500 Subject: [PATCH 28/40] remove bloom filter for now --- contracts/PoolManager.sol | 22 +++++++-------------- contracts/interfaces/IPoolManager.sol | 2 -- test/__snapshots__/PoolManager.spec.ts.snap | 10 +++++----- 3 files changed, 12 insertions(+), 22 deletions(-) diff --git a/contracts/PoolManager.sol b/contracts/PoolManager.sol index 9df5c1ec1..f13e93935 100644 --- a/contracts/PoolManager.sol +++ b/contracts/PoolManager.sol @@ -73,7 +73,6 @@ contract PoolManager is IPoolManager, NoDelegateCall { mapping(IERC20Minimal => uint256) public override reservesOf; /// @notice Internal transient enumerable set - uint256 public override tokensTouchedBloomFilter; IERC20Minimal[] public override tokensTouched; mapping(IERC20Minimal => int256) public override tokenDelta; @@ -87,29 +86,22 @@ contract PoolManager is IPoolManager, NoDelegateCall { for (uint256 i = 0; i < tokensTouched.length; i++) { require(tokenDelta[tokensTouched[i]] == 0, 'Not settled'); } - delete tokensTouchedBloomFilter; delete tokensTouched; delete lockedBy; } /// @dev Adds a token to a unique list of tokens that have been touched function _addTokenToSet(IERC20Minimal token) internal { - // todo: is it cheaper to mstore `uint160(uint160(address(token)))` or cast everywhere it's used? // if the bloom filter doesn't hit, we know it's not in the set, push it - if (tokensTouchedBloomFilter & uint160(uint160(address(token))) != uint160(uint160(address(token)))) { - tokensTouched.push(token); - } else { - bool seen; - for (uint256 i = 0; i < tokensTouched.length; i++) { - if (seen = (tokensTouched[i] == token)) { - break; - } - } - if (!seen) { - tokensTouched.push(token); + bool seen; + for (uint256 i = 0; i < tokensTouched.length; i++) { + if (seen = (tokensTouched[i] == token)) { + break; } } - tokensTouchedBloomFilter |= uint160(uint160(address(token))); + if (!seen) { + tokensTouched.push(token); + } } function _accountDelta(IERC20Minimal token, int256 delta) internal { diff --git a/contracts/interfaces/IPoolManager.sol b/contracts/interfaces/IPoolManager.sol index 40ba56fb6..d51391136 100644 --- a/contracts/interfaces/IPoolManager.sol +++ b/contracts/interfaces/IPoolManager.sol @@ -42,8 +42,6 @@ interface IPoolManager { /// @notice Represents the address that has currently locked the pool function lockedBy() external view returns (address); - function tokensTouchedBloomFilter() external view returns (uint256); - function tokensTouched(uint256 index) external view returns (IERC20Minimal); function tokenDelta(IERC20Minimal token) external view returns (int256); diff --git a/test/__snapshots__/PoolManager.spec.ts.snap b/test/__snapshots__/PoolManager.spec.ts.snap index bfe56e663..627d35dfe 100644 --- a/test/__snapshots__/PoolManager.spec.ts.snap +++ b/test/__snapshots__/PoolManager.spec.ts.snap @@ -2,12 +2,12 @@ exports[`PoolManager #initialize gas cost 1`] = `70701`; -exports[`PoolManager #lock gas overhead of no-op lock 1`] = `43525`; +exports[`PoolManager #lock gas overhead of no-op lock 1`] = `41753`; -exports[`PoolManager #mint gas cost 1`] = `349656`; +exports[`PoolManager #mint gas cost 1`] = `331759`; -exports[`PoolManager #swap gas 1`] = `178695`; +exports[`PoolManager #swap gas 1`] = `160471`; -exports[`PoolManager #swap gas for actual swap 1`] = `241316`; +exports[`PoolManager #swap gas for actual swap 1`] = `223092`; -exports[`PoolManager bytecode size 1`] = `20741`; +exports[`PoolManager bytecode size 1`] = `20568`; From a6b9ebeb37ae41e6d38c6b30573d5eeb617f2ce3 Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Sat, 20 Nov 2021 16:17:44 -0500 Subject: [PATCH 29/40] cheaper no-op swap --- contracts/test/PoolSwapTest.sol | 18 ++++++++++++------ test/__snapshots__/PoolManager.spec.ts.snap | 4 ++-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/contracts/test/PoolSwapTest.sol b/contracts/test/PoolSwapTest.sol index 9ec8234fe..2dc9bece9 100644 --- a/contracts/test/PoolSwapTest.sol +++ b/contracts/test/PoolSwapTest.sol @@ -36,13 +36,19 @@ contract PoolSwapTest is ILockCallback { Pool.BalanceDelta memory delta = manager.swap(data.key, data.params); if (data.params.zeroForOne) { - data.key.token0.transferFrom(data.sender, address(manager), uint256(delta.amount0)); - manager.settle(data.key.token0); - manager.take(data.key.token1, data.sender, uint256(-delta.amount1)); + 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 { - data.key.token1.transferFrom(data.sender, address(manager), uint256(delta.amount1)); - manager.settle(data.key.token1); - manager.take(data.key.token0, data.sender, uint256(-delta.amount0)); + 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/test/__snapshots__/PoolManager.spec.ts.snap b/test/__snapshots__/PoolManager.spec.ts.snap index 627d35dfe..2e4f863a1 100644 --- a/test/__snapshots__/PoolManager.spec.ts.snap +++ b/test/__snapshots__/PoolManager.spec.ts.snap @@ -6,8 +6,8 @@ exports[`PoolManager #lock gas overhead of no-op lock 1`] = `41753`; exports[`PoolManager #mint gas cost 1`] = `331759`; -exports[`PoolManager #swap gas 1`] = `160471`; +exports[`PoolManager #swap gas 1`] = `135285`; -exports[`PoolManager #swap gas for actual swap 1`] = `223092`; +exports[`PoolManager #swap gas for actual swap 1`] = `223143`; exports[`PoolManager bytecode size 1`] = `20568`; From 0b6e1311d1964c7bb3b332d309828ff9d2926e01 Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Sat, 20 Nov 2021 16:20:39 -0500 Subject: [PATCH 30/40] ts error --- test/shared/utilities.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/shared/utilities.ts b/test/shared/utilities.ts index dcb4baa2c..cf88a66d0 100644 --- a/test/shared/utilities.ts +++ b/test/shared/utilities.ts @@ -1,6 +1,5 @@ import bn from 'bignumber.js' import { BigNumber, BigNumberish, Contract, ContractTransaction, utils, Wallet } from 'ethers' -import { SwapTarget } from '../../typechain/SwapTarget' import { TestERC20 } from '../../typechain/TestERC20' export const MaxUint128 = BigNumber.from(2).pow(128).sub(1) @@ -98,12 +97,12 @@ export interface PoolFunctions { } export function createPoolFunctions({ - swapTarget, + // swapTarget, token0, token1, fee, }: { - swapTarget: SwapTarget + // swapTarget: SwapTarget token0: TestERC20 token1: TestERC20 fee: number From 637a801cce5e9c63da3a2e236bacb474608a40d1 Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Sun, 21 Nov 2021 13:26:33 -0500 Subject: [PATCH 31/40] initialize the fee growth global in gas test --- test/PoolManager.spec.ts | 34 +++++++++++++++++++-- test/__snapshots__/PoolManager.spec.ts.snap | 4 +-- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/test/PoolManager.spec.ts b/test/PoolManager.spec.ts index 9bc8ec2df..d040a8fcb 100644 --- a/test/PoolManager.spec.ts +++ b/test/PoolManager.spec.ts @@ -228,6 +228,20 @@ describe.only('PoolManager', () => { }, 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( { @@ -237,13 +251,13 @@ describe.only('PoolManager', () => { }, { amountSpecified: 100, - sqrtPriceLimitX96: encodeSqrtPriceX96(1, 2), + sqrtPriceLimitX96: encodeSqrtPriceX96(1, 4), zeroForOne: true, } ) ) }) - it('gas for actual swap', async () => { + it('gas for swap against liquidity', async () => { await manager.initialize( { token0: tokens.token0.address, @@ -265,6 +279,20 @@ describe.only('PoolManager', () => { 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( { @@ -274,7 +302,7 @@ describe.only('PoolManager', () => { }, { amountSpecified: 100, - sqrtPriceLimitX96: encodeSqrtPriceX96(1, 2), + sqrtPriceLimitX96: encodeSqrtPriceX96(1, 4), zeroForOne: true, } ) diff --git a/test/__snapshots__/PoolManager.spec.ts.snap b/test/__snapshots__/PoolManager.spec.ts.snap index 2e4f863a1..f47d2da25 100644 --- a/test/__snapshots__/PoolManager.spec.ts.snap +++ b/test/__snapshots__/PoolManager.spec.ts.snap @@ -6,8 +6,8 @@ exports[`PoolManager #lock gas overhead of no-op lock 1`] = `41753`; exports[`PoolManager #mint gas cost 1`] = `331759`; -exports[`PoolManager #swap gas 1`] = `135285`; +exports[`PoolManager #swap gas 1`] = `129569`; -exports[`PoolManager #swap gas for actual swap 1`] = `223143`; +exports[`PoolManager #swap gas for swap against liquidity 1`] = `198324`; exports[`PoolManager bytecode size 1`] = `20568`; From e252852c5bbdb72834bfbdb243b74efde0c7baef Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Tue, 23 Nov 2021 00:37:58 -0500 Subject: [PATCH 32/40] get the gas tests for swaps running --- contracts/PoolManager.sol | 1 + test/PoolManager.gas.spec.ts | 363 ++++++++++++++++++ test/__snapshots__/Pool.gas.spec.ts.snap | 169 -------- .../PoolManager.gas.spec.ts.snap | 113 ++++++ test/__snapshots__/PoolManager.spec.ts.snap | 10 +- 5 files changed, 482 insertions(+), 174 deletions(-) create mode 100644 test/PoolManager.gas.spec.ts delete mode 100644 test/__snapshots__/Pool.gas.spec.ts.snap create mode 100644 test/__snapshots__/PoolManager.gas.spec.ts.snap diff --git a/contracts/PoolManager.sol b/contracts/PoolManager.sol index f13e93935..fbf5986b8 100644 --- a/contracts/PoolManager.sol +++ b/contracts/PoolManager.sol @@ -205,6 +205,7 @@ contract PoolManager is IPoolManager, NoDelegateCall { uint256 amount ) external override noDelegateCall onlyByLocker { _accountDelta(token, amount.toInt256()); + reservesOf[token] -= amount; token.transfer(to, amount); } diff --git a/test/PoolManager.gas.spec.ts b/test/PoolManager.gas.spec.ts new file mode 100644 index 000000000..2439d68a0 --- /dev/null +++ b/test/PoolManager.gas.spec.ts @@ -0,0 +1,363 @@ +import { ethers, waffle } from 'hardhat' +import { Wallet } from 'ethers' +import { MockTimePoolManager, PoolSwapTest, PoolMintTest, PoolBurnTest } from '../typechain' +import { expect } from './shared/expect' + +import { tokensFixture } from './shared/fixtures' +import snapshotGasCost from './shared/snapshotGasCost' + +import { + expandTo18Decimals, + FeeAmount, + getMinTick, + encodeSqrtPriceX96, + TICK_SPACINGS, + SwapFunction, + MintFunction, + getMaxTick, + MaxUint128, + SwapToPriceFunction, + MAX_SQRT_RATIO, + MIN_SQRT_RATIO, + getPoolId, +} from './shared/utilities' + +const { constants } = ethers + +const createFixtureLoader = waffle.createFixtureLoader + +type AsyncReturnType any> = T extends (...args: any) => Promise + ? U + : T extends (...args: any) => infer U + ? U + : any + +describe.only('Pool gas tests', () => { + let wallet: Wallet, other: Wallet + + let loadFixture: ReturnType + + before('create fixture loader', async () => { + ;[wallet, other] = await (ethers as any).getSigners() + loadFixture = createFixtureLoader([wallet, other]) + }) + + for (const feeProtocol of [0, 6]) { + describe(feeProtocol > 0 ? 'fee is on' : 'fee is off', () => { + const startingPrice = encodeSqrtPriceX96(100001, 100000) + const startingTick = 0 + const feeAmount = FeeAmount.MEDIUM + const tickSpacing = TICK_SPACINGS[feeAmount] + const minTick = getMinTick(tickSpacing) + const maxTick = getMaxTick(tickSpacing) + + const gasTestFixture = async ([wallet]: Wallet[]) => { + 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 poolKey = { token0: token0.address, token1: token1.address, fee: FeeAmount.MEDIUM } + + 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 mint(wallet.address, minTick, maxTick, expandTo18Decimals(2)) + + await swapExact0For1(expandTo18Decimals(1), wallet.address) + await manager.advanceTime(1) + await swapToHigherPrice(startingPrice, wallet.address) + await manager.advanceTime(1) + + const { tick, sqrtPriceX96 } = await getSlot0() + + expect(tick).to.eq(startingTick) + expect(sqrtPriceX96).to.eq(startingPrice) + + return { manager, getSlot0, poolKey, swapExact0For1, mint, swapToHigherPrice, swapToLowerPrice } + } + + let swapExact0For1: SwapFunction + let swapToHigherPrice: SwapToPriceFunction + let swapToLowerPrice: SwapToPriceFunction + let manager: MockTimePoolManager + let mint: MintFunction + let getSlot0: AsyncReturnType['getSlot0'] + let poolKey: AsyncReturnType['poolKey'] + + beforeEach('load the fixture', async () => { + ;({ 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 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 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 getSlot0()).tick).to.eq(startingTick - 1) + await snapshotGasCost(swapExact0For1(2000, wallet.address)) + 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 getSlot0()).tick).to.eq(startingTick) + await snapshotGasCost(swapExact0For1(expandTo18Decimals(1).div(10000), wallet.address)) + 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 getSlot0()).tick).to.eq(-35787) + }) + + it('first swap in block, large swap crossing several initialized ticks', async () => { + await mint(wallet.address, startingTick - 3 * tickSpacing, startingTick - tickSpacing, expandTo18Decimals(1)) + await mint( + wallet.address, + startingTick - 4 * tickSpacing, + startingTick - 2 * tickSpacing, + expandTo18Decimals(1) + ) + expect((await getSlot0()).tick).to.eq(startingTick) + await snapshotGasCost(swapExact0For1(expandTo18Decimals(1), wallet.address)) + 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 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 () => { + await mint(wallet.address, startingTick - 3 * tickSpacing, startingTick - tickSpacing, expandTo18Decimals(1)) + await mint( + wallet.address, + startingTick - 4 * tickSpacing, + startingTick - 2 * tickSpacing, + expandTo18Decimals(1) + ) + await swapExact0For1(expandTo18Decimals(1).div(10000), wallet.address) + await snapshotGasCost(swapExact0For1(expandTo18Decimals(1), wallet.address)) + 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 getSlot0()).tick).to.be.gt(startingTick - 2 * tickSpacing) // we didn't cross the initialized tick + await snapshotGasCost(swapExact0For1(expandTo18Decimals(1), wallet.address)) + 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 () => { + await mint(wallet.address, startingTick - 3 * tickSpacing, startingTick - tickSpacing, expandTo18Decimals(1)) + await mint( + wallet.address, + startingTick - 4 * tickSpacing, + startingTick - 2 * tickSpacing, + expandTo18Decimals(1) + ) + await swapExact0For1(2, wallet.address) + await manager.advanceTime(1) + await snapshotGasCost(swapExact0For1(expandTo18Decimals(1), wallet.address)) + expect((await getSlot0()).tick).to.be.lt(startingTick - 4 * tickSpacing) + }) + + it('large swap crossing several initialized ticks second time after some time passes', async () => { + await mint(wallet.address, startingTick - 3 * tickSpacing, startingTick - tickSpacing, expandTo18Decimals(1)) + await mint( + wallet.address, + startingTick - 4 * tickSpacing, + startingTick - 2 * tickSpacing, + expandTo18Decimals(1) + ) + await swapExact0For1(expandTo18Decimals(1), wallet.address) + await swapToHigherPrice(startingPrice, wallet.address) + await manager.advanceTime(1) + await snapshotGasCost(swapExact0For1(expandTo18Decimals(1), wallet.address)) + expect((await getSlot0()).tick).to.be.lt(tickSpacing * -4) + }) + }) + + describe('#mint', () => { + 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, () => { + it('new position mint first in range', async () => { + await snapshotGasCost(mint(wallet.address, tickLower, tickUpper, expandTo18Decimals(1))) + }) + it('add to position existing', async () => { + await mint(wallet.address, tickLower, tickUpper, expandTo18Decimals(1)) + await snapshotGasCost(mint(wallet.address, tickLower, tickUpper, expandTo18Decimals(1))) + }) + it('second position in same range', async () => { + await mint(wallet.address, tickLower, tickUpper, expandTo18Decimals(1)) + await snapshotGasCost(mint(other.address, tickLower, tickUpper, expandTo18Decimals(1))) + }) + it('add to position after some time passes', async () => { + await mint(wallet.address, tickLower, tickUpper, expandTo18Decimals(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 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(manager.increaseObservationCardinalityNext(poolKey, 5)) + }) + it('no op', async () => { + await snapshotGasCost(manager.increaseObservationCardinalityNext(poolKey, 3)) + }) + }) + + describe('#snapshotCumulativesInside', () => { + it('tick inside', async () => { + await snapshotGasCost(manager.estimateGas.snapshotCumulativesInside(poolKey, minTick, maxTick)) + }) + it('tick above', async () => { + await swapToHigherPrice(MAX_SQRT_RATIO.sub(1), wallet.address) + await snapshotGasCost(manager.estimateGas.snapshotCumulativesInside(poolKey, minTick, maxTick)) + }) + it('tick below', async () => { + await swapToLowerPrice(MIN_SQRT_RATIO.add(1), wallet.address) + await snapshotGasCost(manager.estimateGas.snapshotCumulativesInside(poolKey, minTick, maxTick)) + }) + }) + }) + } +}) 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__/PoolManager.gas.spec.ts.snap b/test/__snapshots__/PoolManager.gas.spec.ts.snap new file mode 100644 index 000000000..fa4854a29 --- /dev/null +++ b/test/__snapshots__/PoolManager.gas.spec.ts.snap @@ -0,0 +1,113 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Pool gas tests fee is off #increaseObservationCardinalityNext grow by 1 slot 1`] = `50918`; + +exports[`Pool gas tests fee is off #increaseObservationCardinalityNext no op 1`] = `25849`; + +exports[`Pool gas tests fee is off #mint above current price add to position after some time passes 1`] = `189292`; + +exports[`Pool gas tests fee is off #mint above current price add to position existing 1`] = `189292`; + +exports[`Pool gas tests fee is off #mint above current price new position mint first in range 1`] = `284248`; + +exports[`Pool gas tests fee is off #mint above current price second position in same range 1`] = `202972`; + +exports[`Pool gas tests fee is off #mint around current price add to position after some time passes 1`] = `241232`; + +exports[`Pool gas tests fee is off #mint around current price add to position existing 1`] = `233947`; + +exports[`Pool gas tests fee is off #mint around current price new position mint first in range 1`] = `386133`; + +exports[`Pool gas tests fee is off #mint around current price second position in same range 1`] = `247627`; + +exports[`Pool gas tests fee is off #mint below current price add to position after some time passes 1`] = `190431`; + +exports[`Pool gas tests fee is off #mint below current price add to position existing 1`] = `190431`; + +exports[`Pool gas tests fee is off #mint below current price new position mint first in range 1`] = `350239`; + +exports[`Pool gas tests fee is off #mint below current price second position in same range 1`] = `204111`; + +exports[`Pool gas tests fee is off #snapshotCumulativesInside tick above 1`] = `34301`; + +exports[`Pool gas tests fee is off #snapshotCumulativesInside tick below 1`] = `34260`; + +exports[`Pool gas tests fee is off #snapshotCumulativesInside tick inside 1`] = `39533`; + +exports[`Pool gas tests fee is off #swapExact0For1 first swap in block moves tick, no initialized crossings 1`] = `216598`; + +exports[`Pool gas tests fee is off #swapExact0For1 first swap in block with no tick movement 1`] = `203797`; + +exports[`Pool gas tests fee is off #swapExact0For1 first swap in block, large swap crossing a single initialized tick 1`] = `240805`; + +exports[`Pool gas tests fee is off #swapExact0For1 first swap in block, large swap crossing several initialized ticks 1`] = `295900`; + +exports[`Pool gas tests fee is off #swapExact0For1 first swap in block, large swap, no initialized crossings 1`] = `228676`; + +exports[`Pool gas tests fee is off #swapExact0For1 large swap crossing several initialized ticks after some time passes 1`] = `295900`; + +exports[`Pool gas tests fee is off #swapExact0For1 large swap crossing several initialized ticks second time after some time passes 1`] = `295900`; + +exports[`Pool gas tests fee is off #swapExact0For1 second swap in block moves tick, no initialized crossings 1`] = `216598`; + +exports[`Pool gas tests fee is off #swapExact0For1 second swap in block with no tick movement 1`] = `203886`; + +exports[`Pool gas tests fee is off #swapExact0For1 second swap in block, large swap crossing a single initialized tick 1`] = `229936`; + +exports[`Pool gas tests fee is off #swapExact0For1 second swap in block, large swap crossing several initialized ticks 1`] = `285004`; + +exports[`Pool gas tests fee is on #increaseObservationCardinalityNext grow by 1 slot 1`] = `50918`; + +exports[`Pool gas tests fee is on #increaseObservationCardinalityNext no op 1`] = `25849`; + +exports[`Pool gas tests fee is on #mint above current price add to position after some time passes 1`] = `189292`; + +exports[`Pool gas tests fee is on #mint above current price add to position existing 1`] = `189292`; + +exports[`Pool gas tests fee is on #mint above current price new position mint first in range 1`] = `284248`; + +exports[`Pool gas tests fee is on #mint above current price second position in same range 1`] = `202972`; + +exports[`Pool gas tests fee is on #mint around current price add to position after some time passes 1`] = `241232`; + +exports[`Pool gas tests fee is on #mint around current price add to position existing 1`] = `233947`; + +exports[`Pool gas tests fee is on #mint around current price new position mint first in range 1`] = `386133`; + +exports[`Pool gas tests fee is on #mint around current price second position in same range 1`] = `247627`; + +exports[`Pool gas tests fee is on #mint below current price add to position after some time passes 1`] = `190431`; + +exports[`Pool gas tests fee is on #mint below current price add to position existing 1`] = `190431`; + +exports[`Pool gas tests fee is on #mint below current price new position mint first in range 1`] = `350239`; + +exports[`Pool gas tests fee is on #mint below current price second position in same range 1`] = `204111`; + +exports[`Pool gas tests fee is on #snapshotCumulativesInside tick above 1`] = `34301`; + +exports[`Pool gas tests fee is on #snapshotCumulativesInside tick below 1`] = `34260`; + +exports[`Pool gas tests fee is on #snapshotCumulativesInside tick inside 1`] = `39533`; + +exports[`Pool gas tests fee is on #swapExact0For1 first swap in block moves tick, no initialized crossings 1`] = `220903`; + +exports[`Pool gas tests fee is on #swapExact0For1 first swap in block with no tick movement 1`] = `207984`; + +exports[`Pool gas tests fee is on #swapExact0For1 first swap in block, large swap crossing a single initialized tick 1`] = `245228`; + +exports[`Pool gas tests fee is on #swapExact0For1 first swap in block, large swap crossing several initialized ticks 1`] = `300675`; + +exports[`Pool gas tests fee is on #swapExact0For1 first swap in block, large swap, no initialized crossings 1`] = `233216`; + +exports[`Pool gas tests fee is on #swapExact0For1 large swap crossing several initialized ticks after some time passes 1`] = `300675`; + +exports[`Pool gas tests fee is on #swapExact0For1 large swap crossing several initialized ticks second time after some time passes 1`] = `300675`; + +exports[`Pool gas tests fee is on #swapExact0For1 second swap in block moves tick, no initialized crossings 1`] = `220903`; + +exports[`Pool gas tests fee is on #swapExact0For1 second swap in block with no tick movement 1`] = `208073`; + +exports[`Pool gas tests fee is on #swapExact0For1 second swap in block, large swap crossing a single initialized tick 1`] = `234241`; + +exports[`Pool gas tests fee is on #swapExact0For1 second swap in block, large swap crossing several initialized ticks 1`] = `289662`; diff --git a/test/__snapshots__/PoolManager.spec.ts.snap b/test/__snapshots__/PoolManager.spec.ts.snap index f47d2da25..f59c45442 100644 --- a/test/__snapshots__/PoolManager.spec.ts.snap +++ b/test/__snapshots__/PoolManager.spec.ts.snap @@ -1,13 +1,13 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`PoolManager #initialize gas cost 1`] = `70701`; +exports[`PoolManager #initialize gas cost 1`] = `70689`; exports[`PoolManager #lock gas overhead of no-op lock 1`] = `41753`; -exports[`PoolManager #mint gas cost 1`] = `331759`; +exports[`PoolManager #mint gas cost 1`] = `331749`; -exports[`PoolManager #swap gas 1`] = `129569`; +exports[`PoolManager #swap gas 1`] = `129560`; -exports[`PoolManager #swap gas for swap against liquidity 1`] = `198324`; +exports[`PoolManager #swap gas for swap against liquidity 1`] = `202460`; -exports[`PoolManager bytecode size 1`] = `20568`; +exports[`PoolManager bytecode size 1`] = `20613`; From 8d267c7c1c51d7a998356bf8fefc86feed84821c Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Tue, 23 Nov 2021 00:42:36 -0500 Subject: [PATCH 33/40] remove file --- .../PoolManager.gas.spec.ts.snap | 113 ------------------ 1 file changed, 113 deletions(-) delete mode 100644 test/__snapshots__/PoolManager.gas.spec.ts.snap diff --git a/test/__snapshots__/PoolManager.gas.spec.ts.snap b/test/__snapshots__/PoolManager.gas.spec.ts.snap deleted file mode 100644 index fa4854a29..000000000 --- a/test/__snapshots__/PoolManager.gas.spec.ts.snap +++ /dev/null @@ -1,113 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Pool gas tests fee is off #increaseObservationCardinalityNext grow by 1 slot 1`] = `50918`; - -exports[`Pool gas tests fee is off #increaseObservationCardinalityNext no op 1`] = `25849`; - -exports[`Pool gas tests fee is off #mint above current price add to position after some time passes 1`] = `189292`; - -exports[`Pool gas tests fee is off #mint above current price add to position existing 1`] = `189292`; - -exports[`Pool gas tests fee is off #mint above current price new position mint first in range 1`] = `284248`; - -exports[`Pool gas tests fee is off #mint above current price second position in same range 1`] = `202972`; - -exports[`Pool gas tests fee is off #mint around current price add to position after some time passes 1`] = `241232`; - -exports[`Pool gas tests fee is off #mint around current price add to position existing 1`] = `233947`; - -exports[`Pool gas tests fee is off #mint around current price new position mint first in range 1`] = `386133`; - -exports[`Pool gas tests fee is off #mint around current price second position in same range 1`] = `247627`; - -exports[`Pool gas tests fee is off #mint below current price add to position after some time passes 1`] = `190431`; - -exports[`Pool gas tests fee is off #mint below current price add to position existing 1`] = `190431`; - -exports[`Pool gas tests fee is off #mint below current price new position mint first in range 1`] = `350239`; - -exports[`Pool gas tests fee is off #mint below current price second position in same range 1`] = `204111`; - -exports[`Pool gas tests fee is off #snapshotCumulativesInside tick above 1`] = `34301`; - -exports[`Pool gas tests fee is off #snapshotCumulativesInside tick below 1`] = `34260`; - -exports[`Pool gas tests fee is off #snapshotCumulativesInside tick inside 1`] = `39533`; - -exports[`Pool gas tests fee is off #swapExact0For1 first swap in block moves tick, no initialized crossings 1`] = `216598`; - -exports[`Pool gas tests fee is off #swapExact0For1 first swap in block with no tick movement 1`] = `203797`; - -exports[`Pool gas tests fee is off #swapExact0For1 first swap in block, large swap crossing a single initialized tick 1`] = `240805`; - -exports[`Pool gas tests fee is off #swapExact0For1 first swap in block, large swap crossing several initialized ticks 1`] = `295900`; - -exports[`Pool gas tests fee is off #swapExact0For1 first swap in block, large swap, no initialized crossings 1`] = `228676`; - -exports[`Pool gas tests fee is off #swapExact0For1 large swap crossing several initialized ticks after some time passes 1`] = `295900`; - -exports[`Pool gas tests fee is off #swapExact0For1 large swap crossing several initialized ticks second time after some time passes 1`] = `295900`; - -exports[`Pool gas tests fee is off #swapExact0For1 second swap in block moves tick, no initialized crossings 1`] = `216598`; - -exports[`Pool gas tests fee is off #swapExact0For1 second swap in block with no tick movement 1`] = `203886`; - -exports[`Pool gas tests fee is off #swapExact0For1 second swap in block, large swap crossing a single initialized tick 1`] = `229936`; - -exports[`Pool gas tests fee is off #swapExact0For1 second swap in block, large swap crossing several initialized ticks 1`] = `285004`; - -exports[`Pool gas tests fee is on #increaseObservationCardinalityNext grow by 1 slot 1`] = `50918`; - -exports[`Pool gas tests fee is on #increaseObservationCardinalityNext no op 1`] = `25849`; - -exports[`Pool gas tests fee is on #mint above current price add to position after some time passes 1`] = `189292`; - -exports[`Pool gas tests fee is on #mint above current price add to position existing 1`] = `189292`; - -exports[`Pool gas tests fee is on #mint above current price new position mint first in range 1`] = `284248`; - -exports[`Pool gas tests fee is on #mint above current price second position in same range 1`] = `202972`; - -exports[`Pool gas tests fee is on #mint around current price add to position after some time passes 1`] = `241232`; - -exports[`Pool gas tests fee is on #mint around current price add to position existing 1`] = `233947`; - -exports[`Pool gas tests fee is on #mint around current price new position mint first in range 1`] = `386133`; - -exports[`Pool gas tests fee is on #mint around current price second position in same range 1`] = `247627`; - -exports[`Pool gas tests fee is on #mint below current price add to position after some time passes 1`] = `190431`; - -exports[`Pool gas tests fee is on #mint below current price add to position existing 1`] = `190431`; - -exports[`Pool gas tests fee is on #mint below current price new position mint first in range 1`] = `350239`; - -exports[`Pool gas tests fee is on #mint below current price second position in same range 1`] = `204111`; - -exports[`Pool gas tests fee is on #snapshotCumulativesInside tick above 1`] = `34301`; - -exports[`Pool gas tests fee is on #snapshotCumulativesInside tick below 1`] = `34260`; - -exports[`Pool gas tests fee is on #snapshotCumulativesInside tick inside 1`] = `39533`; - -exports[`Pool gas tests fee is on #swapExact0For1 first swap in block moves tick, no initialized crossings 1`] = `220903`; - -exports[`Pool gas tests fee is on #swapExact0For1 first swap in block with no tick movement 1`] = `207984`; - -exports[`Pool gas tests fee is on #swapExact0For1 first swap in block, large swap crossing a single initialized tick 1`] = `245228`; - -exports[`Pool gas tests fee is on #swapExact0For1 first swap in block, large swap crossing several initialized ticks 1`] = `300675`; - -exports[`Pool gas tests fee is on #swapExact0For1 first swap in block, large swap, no initialized crossings 1`] = `233216`; - -exports[`Pool gas tests fee is on #swapExact0For1 large swap crossing several initialized ticks after some time passes 1`] = `300675`; - -exports[`Pool gas tests fee is on #swapExact0For1 large swap crossing several initialized ticks second time after some time passes 1`] = `300675`; - -exports[`Pool gas tests fee is on #swapExact0For1 second swap in block moves tick, no initialized crossings 1`] = `220903`; - -exports[`Pool gas tests fee is on #swapExact0For1 second swap in block with no tick movement 1`] = `208073`; - -exports[`Pool gas tests fee is on #swapExact0For1 second swap in block, large swap crossing a single initialized tick 1`] = `234241`; - -exports[`Pool gas tests fee is on #swapExact0For1 second swap in block, large swap crossing several initialized ticks 1`] = `289662`; From a2a14bd9b0df9cb8823957b86f8d0fdab9119953 Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Tue, 23 Nov 2021 00:42:51 -0500 Subject: [PATCH 34/40] remove more unused files --- .../__snapshots__/Pool.arbitrage.spec.ts.snap | 977 ----- test/__snapshots__/Pool.swaps.spec.ts.snap | 3541 ----------------- 2 files changed, 4518 deletions(-) delete mode 100644 test/__snapshots__/Pool.arbitrage.spec.ts.snap delete mode 100644 test/__snapshots__/Pool.swaps.spec.ts.snap 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.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, -} -`; From f25da291c57eea1bc4b564f8d63a3b3e45d4e16c Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Tue, 23 Nov 2021 00:43:09 -0500 Subject: [PATCH 35/40] bring back old file --- test/__snapshots__/Pool.gas.spec.ts.snap | 169 +++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 test/__snapshots__/Pool.gas.spec.ts.snap diff --git a/test/__snapshots__/Pool.gas.spec.ts.snap b/test/__snapshots__/Pool.gas.spec.ts.snap new file mode 100644 index 000000000..4d2e64ff0 --- /dev/null +++ b/test/__snapshots__/Pool.gas.spec.ts.snap @@ -0,0 +1,169 @@ +// 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`; From 3ae8b181a43cdfc79add60de8b8bf2c09d35e0f3 Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Tue, 23 Nov 2021 00:43:38 -0500 Subject: [PATCH 36/40] rename --- .../{Pool.gas.spec.ts.snap => PoolManager.gas.spec.ts.snap} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/__snapshots__/{Pool.gas.spec.ts.snap => PoolManager.gas.spec.ts.snap} (100%) diff --git a/test/__snapshots__/Pool.gas.spec.ts.snap b/test/__snapshots__/PoolManager.gas.spec.ts.snap similarity index 100% rename from test/__snapshots__/Pool.gas.spec.ts.snap rename to test/__snapshots__/PoolManager.gas.spec.ts.snap From d4de24dc111edf63bd1d5e5ab335d112505da11b Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Tue, 23 Nov 2021 00:44:00 -0500 Subject: [PATCH 37/40] run the tests --- .../PoolManager.gas.spec.ts.snap | 168 ++++++------------ 1 file changed, 56 insertions(+), 112 deletions(-) diff --git a/test/__snapshots__/PoolManager.gas.spec.ts.snap b/test/__snapshots__/PoolManager.gas.spec.ts.snap index 4d2e64ff0..fa4854a29 100644 --- a/test/__snapshots__/PoolManager.gas.spec.ts.snap +++ b/test/__snapshots__/PoolManager.gas.spec.ts.snap @@ -1,169 +1,113 @@ // 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 #increaseObservationCardinalityNext grow by 1 slot 1`] = `50918`; -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 #increaseObservationCardinalityNext no op 1`] = `25849`; -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 #mint above current price add to position after some time passes 1`] = `189292`; -exports[`Pool gas tests fee is off #burn above current price partial position burn 1`] = `97995`; +exports[`Pool gas tests fee is off #mint above current price add to position existing 1`] = `189292`; -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 #mint above current price new position mint first in range 1`] = `284248`; -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 #mint above current price second position in same range 1`] = `202972`; -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 #mint around current price add to position after some time passes 1`] = `241232`; -exports[`Pool gas tests fee is off #burn around current price partial position burn 1`] = `102974`; +exports[`Pool gas tests fee is off #mint around current price add to position existing 1`] = `233947`; -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 #mint around current price new position mint first in range 1`] = `386133`; -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 #mint around current price second position in same range 1`] = `247627`; -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 #mint below current price add to position after some time passes 1`] = `190431`; -exports[`Pool gas tests fee is off #burn below current price partial position burn 1`] = `98734`; +exports[`Pool gas tests fee is off #mint below current price add to position existing 1`] = `190431`; -exports[`Pool gas tests fee is off #collect close to worst case 1`] = `46676`; +exports[`Pool gas tests fee is off #mint below current price new position mint first in range 1`] = `350239`; -exports[`Pool gas tests fee is off #increaseObservationCardinalityNext grow by 1 slot 1`] = `51153`; +exports[`Pool gas tests fee is off #mint below current price second position in same range 1`] = `204111`; -exports[`Pool gas tests fee is off #increaseObservationCardinalityNext no op 1`] = `24740`; +exports[`Pool gas tests fee is off #snapshotCumulativesInside tick above 1`] = `34301`; -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 #snapshotCumulativesInside tick below 1`] = `34260`; -exports[`Pool gas tests fee is off #mint above current price add to position existing 1`] = `109822`; +exports[`Pool gas tests fee is off #snapshotCumulativesInside tick inside 1`] = `39533`; -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 #swapExact0For1 first swap in block moves tick, no initialized crossings 1`] = `216598`; -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 #swapExact0For1 first swap in block with no tick movement 1`] = `203797`; -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 #swapExact0For1 first swap in block, large swap crossing a single initialized tick 1`] = `240805`; -exports[`Pool gas tests fee is off #mint around current price add to position existing 1`] = `138819`; +exports[`Pool gas tests fee is off #swapExact0For1 first swap in block, large swap crossing several initialized ticks 1`] = `295900`; -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 #swapExact0For1 first swap in block, large swap, no initialized crossings 1`] = `228676`; -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 #swapExact0For1 large swap crossing several initialized ticks after some time passes 1`] = `295900`; -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 #swapExact0For1 large swap crossing several initialized ticks second time after some time passes 1`] = `295900`; -exports[`Pool gas tests fee is off #mint below current price add to position existing 1`] = `110548`; +exports[`Pool gas tests fee is off #swapExact0For1 second swap in block moves tick, no initialized crossings 1`] = `216598`; -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 #swapExact0For1 second swap in block with no tick movement 1`] = `203886`; -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 #swapExact0For1 second swap in block, large swap crossing a single initialized tick 1`] = `229936`; -exports[`Pool gas tests fee is off #poke best case 1`] = `52238`; +exports[`Pool gas tests fee is off #swapExact0For1 second swap in block, large swap crossing several initialized ticks 1`] = `285004`; -exports[`Pool gas tests fee is off #snapshotCumulativesInside tick above 1`] = `29870`; +exports[`Pool gas tests fee is on #increaseObservationCardinalityNext grow by 1 slot 1`] = `50918`; -exports[`Pool gas tests fee is off #snapshotCumulativesInside tick below 1`] = `29832`; +exports[`Pool gas tests fee is on #increaseObservationCardinalityNext no op 1`] = `25849`; -exports[`Pool gas tests fee is off #snapshotCumulativesInside tick inside 1`] = `37219`; +exports[`Pool gas tests fee is on #mint above current price add to position after some time passes 1`] = `189292`; -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 on #mint above current price add to position existing 1`] = `189292`; -exports[`Pool gas tests fee is off #swapExact0For1 first swap in block with no tick movement 1`] = `100058`; +exports[`Pool gas tests fee is on #mint above current price new position mint first in range 1`] = `284248`; -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 on #mint above current price second position in same range 1`] = `202972`; -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 on #mint around current price add to position after some time passes 1`] = `241232`; -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 on #mint around current price add to position existing 1`] = `233947`; -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 on #mint around current price new position mint first in range 1`] = `386133`; -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 on #mint around current price second position in same range 1`] = `247627`; -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 on #mint below current price add to position after some time passes 1`] = `190431`; -exports[`Pool gas tests fee is off #swapExact0For1 second swap in block with no tick movement 1`] = `100169`; +exports[`Pool gas tests fee is on #mint below current price add to position existing 1`] = `190431`; -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 on #mint below current price new position mint first in range 1`] = `350239`; -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 #mint below current price second position in same range 1`] = `204111`; -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 #snapshotCumulativesInside tick above 1`] = `34301`; -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 #snapshotCumulativesInside tick below 1`] = `34260`; -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 #snapshotCumulativesInside tick inside 1`] = `39533`; -exports[`Pool gas tests fee is on #burn above current price partial position burn 1`] = `97995`; +exports[`Pool gas tests fee is on #swapExact0For1 first swap in block moves tick, no initialized crossings 1`] = `220903`; -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 #swapExact0For1 first swap in block with no tick movement 1`] = `207984`; -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 #swapExact0For1 first swap in block, large swap crossing a single initialized tick 1`] = `245228`; -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 #swapExact0For1 first swap in block, large swap crossing several initialized ticks 1`] = `300675`; -exports[`Pool gas tests fee is on #burn around current price partial position burn 1`] = `102974`; +exports[`Pool gas tests fee is on #swapExact0For1 first swap in block, large swap, no initialized crossings 1`] = `233216`; -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 #swapExact0For1 large swap crossing several initialized ticks after some time passes 1`] = `300675`; -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 #swapExact0For1 large swap crossing several initialized ticks second time after some time passes 1`] = `300675`; -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 #swapExact0For1 second swap in block moves tick, no initialized crossings 1`] = `220903`; -exports[`Pool gas tests fee is on #burn below current price partial position burn 1`] = `98734`; +exports[`Pool gas tests fee is on #swapExact0For1 second swap in block with no tick movement 1`] = `208073`; -exports[`Pool gas tests fee is on #collect close to worst case 1`] = `46676`; +exports[`Pool gas tests fee is on #swapExact0For1 second swap in block, large swap crossing a single initialized tick 1`] = `234241`; -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`; +exports[`Pool gas tests fee is on #swapExact0For1 second swap in block, large swap crossing several initialized ticks 1`] = `289662`; From ddc72018f37039e0f4d28e0449b43ebe4f1ac4e4 Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Tue, 23 Nov 2021 01:04:25 -0500 Subject: [PATCH 38/40] cheaper mapping --- contracts/PoolManager.sol | 30 +++--- contracts/interfaces/IPoolManager.sol | 2 +- .../PoolManager.gas.spec.ts.snap | 92 +++++++++---------- test/__snapshots__/PoolManager.spec.ts.snap | 8 +- 4 files changed, 69 insertions(+), 63 deletions(-) diff --git a/contracts/PoolManager.sol b/contracts/PoolManager.sol index fbf5986b8..35a2419de 100644 --- a/contracts/PoolManager.sol +++ b/contracts/PoolManager.sol @@ -74,7 +74,11 @@ contract PoolManager is IPoolManager, NoDelegateCall { /// @notice Internal transient enumerable set IERC20Minimal[] public override tokensTouched; - mapping(IERC20Minimal => int256) public override tokenDelta; + 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)); @@ -84,29 +88,31 @@ contract PoolManager is IPoolManager, NoDelegateCall { result = ILockCallback(msg.sender).lockAcquired(data); for (uint256 i = 0; i < tokensTouched.length; i++) { - require(tokenDelta[tokensTouched[i]] == 0, 'Not settled'); + 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 { - // if the bloom filter doesn't hit, we know it's not in the set, push it - bool seen; - for (uint256 i = 0; i < tokensTouched.length; i++) { - if (seen = (tokensTouched[i] == token)) { - break; - } - } - if (!seen) { + function _addTokenToSet(IERC20Minimal token) internal returns (uint8 slot) { + PositionAndDelta storage pd = tokenDelta[token]; + uint256 len = tokensTouched.length; + slot = pd.slot; + if (slot < len) { + return slot; + } else { + require(len < type(uint8).max); + slot = uint8(len); + pd.slot = slot; tokensTouched.push(token); } } function _accountDelta(IERC20Minimal token, int256 delta) internal { _addTokenToSet(token); - tokenDelta[token] += delta; + tokenDelta[token].delta += int248(delta); } /// @dev Accumulates a balance change to a map of token to balance changes diff --git a/contracts/interfaces/IPoolManager.sol b/contracts/interfaces/IPoolManager.sol index d51391136..03747725b 100644 --- a/contracts/interfaces/IPoolManager.sol +++ b/contracts/interfaces/IPoolManager.sol @@ -44,7 +44,7 @@ interface IPoolManager { function tokensTouched(uint256 index) external view returns (IERC20Minimal); - function tokenDelta(IERC20Minimal token) external view returns (int256); + 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); diff --git a/test/__snapshots__/PoolManager.gas.spec.ts.snap b/test/__snapshots__/PoolManager.gas.spec.ts.snap index fa4854a29..49015532a 100644 --- a/test/__snapshots__/PoolManager.gas.spec.ts.snap +++ b/test/__snapshots__/PoolManager.gas.spec.ts.snap @@ -4,29 +4,29 @@ exports[`Pool gas tests fee is off #increaseObservationCardinalityNext grow by 1 exports[`Pool gas tests fee is off #increaseObservationCardinalityNext no op 1`] = `25849`; -exports[`Pool gas tests fee is off #mint above current price add to position after some time passes 1`] = `189292`; +exports[`Pool gas tests fee is off #mint above current price add to position after some time passes 1`] = `171651`; -exports[`Pool gas tests fee is off #mint above current price add to position existing 1`] = `189292`; +exports[`Pool gas tests fee is off #mint above current price add to position existing 1`] = `171651`; -exports[`Pool gas tests fee is off #mint above current price new position mint first in range 1`] = `284248`; +exports[`Pool gas tests fee is off #mint above current price new position mint first in range 1`] = `266625`; -exports[`Pool gas tests fee is off #mint above current price second position in same range 1`] = `202972`; +exports[`Pool gas tests fee is off #mint above current price second position in same range 1`] = `185331`; -exports[`Pool gas tests fee is off #mint around current price add to position after some time passes 1`] = `241232`; +exports[`Pool gas tests fee is off #mint around current price add to position after some time passes 1`] = `223218`; -exports[`Pool gas tests fee is off #mint around current price add to position existing 1`] = `233947`; +exports[`Pool gas tests fee is off #mint around current price add to position existing 1`] = `215924`; -exports[`Pool gas tests fee is off #mint around current price new position mint first in range 1`] = `386133`; +exports[`Pool gas tests fee is off #mint around current price new position mint first in range 1`] = `368139`; -exports[`Pool gas tests fee is off #mint around current price second position in same range 1`] = `247627`; +exports[`Pool gas tests fee is off #mint around current price second position in same range 1`] = `229604`; -exports[`Pool gas tests fee is off #mint below current price add to position after some time passes 1`] = `190431`; +exports[`Pool gas tests fee is off #mint below current price add to position after some time passes 1`] = `172331`; -exports[`Pool gas tests fee is off #mint below current price add to position existing 1`] = `190431`; +exports[`Pool gas tests fee is off #mint below current price add to position existing 1`] = `172331`; -exports[`Pool gas tests fee is off #mint below current price new position mint first in range 1`] = `350239`; +exports[`Pool gas tests fee is off #mint below current price new position mint first in range 1`] = `335597`; -exports[`Pool gas tests fee is off #mint below current price second position in same range 1`] = `204111`; +exports[`Pool gas tests fee is off #mint below current price second position in same range 1`] = `186011`; exports[`Pool gas tests fee is off #snapshotCumulativesInside tick above 1`] = `34301`; @@ -34,55 +34,55 @@ exports[`Pool gas tests fee is off #snapshotCumulativesInside tick below 1`] = ` exports[`Pool gas tests fee is off #snapshotCumulativesInside tick inside 1`] = `39533`; -exports[`Pool gas tests fee is off #swapExact0For1 first swap in block moves tick, no initialized crossings 1`] = `216598`; +exports[`Pool gas tests fee is off #swapExact0For1 first swap in block moves tick, no initialized crossings 1`] = `198584`; -exports[`Pool gas tests fee is off #swapExact0For1 first swap in block with no tick movement 1`] = `203797`; +exports[`Pool gas tests fee is off #swapExact0For1 first swap in block with no tick movement 1`] = `185774`; -exports[`Pool gas tests fee is off #swapExact0For1 first swap in block, large swap crossing a single initialized tick 1`] = `240805`; +exports[`Pool gas tests fee is off #swapExact0For1 first swap in block, large swap crossing a single initialized tick 1`] = `222792`; -exports[`Pool gas tests fee is off #swapExact0For1 first swap in block, large swap crossing several initialized ticks 1`] = `295900`; +exports[`Pool gas tests fee is off #swapExact0For1 first swap in block, large swap crossing several initialized ticks 1`] = `277886`; -exports[`Pool gas tests fee is off #swapExact0For1 first swap in block, large swap, no initialized crossings 1`] = `228676`; +exports[`Pool gas tests fee is off #swapExact0For1 first swap in block, large swap, no initialized crossings 1`] = `210662`; -exports[`Pool gas tests fee is off #swapExact0For1 large swap crossing several initialized ticks after some time passes 1`] = `295900`; +exports[`Pool gas tests fee is off #swapExact0For1 large swap crossing several initialized ticks after some time passes 1`] = `277886`; -exports[`Pool gas tests fee is off #swapExact0For1 large swap crossing several initialized ticks second time after some time passes 1`] = `295900`; +exports[`Pool gas tests fee is off #swapExact0For1 large swap crossing several initialized ticks second time after some time passes 1`] = `277886`; -exports[`Pool gas tests fee is off #swapExact0For1 second swap in block moves tick, no initialized crossings 1`] = `216598`; +exports[`Pool gas tests fee is off #swapExact0For1 second swap in block moves tick, no initialized crossings 1`] = `198584`; -exports[`Pool gas tests fee is off #swapExact0For1 second swap in block with no tick movement 1`] = `203886`; +exports[`Pool gas tests fee is off #swapExact0For1 second swap in block with no tick movement 1`] = `185863`; -exports[`Pool gas tests fee is off #swapExact0For1 second swap in block, large swap crossing a single initialized tick 1`] = `229936`; +exports[`Pool gas tests fee is off #swapExact0For1 second swap in block, large swap crossing a single initialized tick 1`] = `211913`; -exports[`Pool gas tests fee is off #swapExact0For1 second swap in block, large swap crossing several initialized ticks 1`] = `285004`; +exports[`Pool gas tests fee is off #swapExact0For1 second swap in block, large swap crossing several initialized ticks 1`] = `266981`; exports[`Pool gas tests fee is on #increaseObservationCardinalityNext grow by 1 slot 1`] = `50918`; exports[`Pool gas tests fee is on #increaseObservationCardinalityNext no op 1`] = `25849`; -exports[`Pool gas tests fee is on #mint above current price add to position after some time passes 1`] = `189292`; +exports[`Pool gas tests fee is on #mint above current price add to position after some time passes 1`] = `171651`; -exports[`Pool gas tests fee is on #mint above current price add to position existing 1`] = `189292`; +exports[`Pool gas tests fee is on #mint above current price add to position existing 1`] = `171651`; -exports[`Pool gas tests fee is on #mint above current price new position mint first in range 1`] = `284248`; +exports[`Pool gas tests fee is on #mint above current price new position mint first in range 1`] = `266625`; -exports[`Pool gas tests fee is on #mint above current price second position in same range 1`] = `202972`; +exports[`Pool gas tests fee is on #mint above current price second position in same range 1`] = `185331`; -exports[`Pool gas tests fee is on #mint around current price add to position after some time passes 1`] = `241232`; +exports[`Pool gas tests fee is on #mint around current price add to position after some time passes 1`] = `223218`; -exports[`Pool gas tests fee is on #mint around current price add to position existing 1`] = `233947`; +exports[`Pool gas tests fee is on #mint around current price add to position existing 1`] = `215924`; -exports[`Pool gas tests fee is on #mint around current price new position mint first in range 1`] = `386133`; +exports[`Pool gas tests fee is on #mint around current price new position mint first in range 1`] = `368139`; -exports[`Pool gas tests fee is on #mint around current price second position in same range 1`] = `247627`; +exports[`Pool gas tests fee is on #mint around current price second position in same range 1`] = `229604`; -exports[`Pool gas tests fee is on #mint below current price add to position after some time passes 1`] = `190431`; +exports[`Pool gas tests fee is on #mint below current price add to position after some time passes 1`] = `172331`; -exports[`Pool gas tests fee is on #mint below current price add to position existing 1`] = `190431`; +exports[`Pool gas tests fee is on #mint below current price add to position existing 1`] = `172331`; -exports[`Pool gas tests fee is on #mint below current price new position mint first in range 1`] = `350239`; +exports[`Pool gas tests fee is on #mint below current price new position mint first in range 1`] = `335597`; -exports[`Pool gas tests fee is on #mint below current price second position in same range 1`] = `204111`; +exports[`Pool gas tests fee is on #mint below current price second position in same range 1`] = `186011`; exports[`Pool gas tests fee is on #snapshotCumulativesInside tick above 1`] = `34301`; @@ -90,24 +90,24 @@ exports[`Pool gas tests fee is on #snapshotCumulativesInside tick below 1`] = `3 exports[`Pool gas tests fee is on #snapshotCumulativesInside tick inside 1`] = `39533`; -exports[`Pool gas tests fee is on #swapExact0For1 first swap in block moves tick, no initialized crossings 1`] = `220903`; +exports[`Pool gas tests fee is on #swapExact0For1 first swap in block moves tick, no initialized crossings 1`] = `202889`; -exports[`Pool gas tests fee is on #swapExact0For1 first swap in block with no tick movement 1`] = `207984`; +exports[`Pool gas tests fee is on #swapExact0For1 first swap in block with no tick movement 1`] = `189961`; -exports[`Pool gas tests fee is on #swapExact0For1 first swap in block, large swap crossing a single initialized tick 1`] = `245228`; +exports[`Pool gas tests fee is on #swapExact0For1 first swap in block, large swap crossing a single initialized tick 1`] = `227214`; -exports[`Pool gas tests fee is on #swapExact0For1 first swap in block, large swap crossing several initialized ticks 1`] = `300675`; +exports[`Pool gas tests fee is on #swapExact0For1 first swap in block, large swap crossing several initialized ticks 1`] = `282661`; -exports[`Pool gas tests fee is on #swapExact0For1 first swap in block, large swap, no initialized crossings 1`] = `233216`; +exports[`Pool gas tests fee is on #swapExact0For1 first swap in block, large swap, no initialized crossings 1`] = `215202`; -exports[`Pool gas tests fee is on #swapExact0For1 large swap crossing several initialized ticks after some time passes 1`] = `300675`; +exports[`Pool gas tests fee is on #swapExact0For1 large swap crossing several initialized ticks after some time passes 1`] = `282661`; -exports[`Pool gas tests fee is on #swapExact0For1 large swap crossing several initialized ticks second time after some time passes 1`] = `300675`; +exports[`Pool gas tests fee is on #swapExact0For1 large swap crossing several initialized ticks second time after some time passes 1`] = `282661`; -exports[`Pool gas tests fee is on #swapExact0For1 second swap in block moves tick, no initialized crossings 1`] = `220903`; +exports[`Pool gas tests fee is on #swapExact0For1 second swap in block moves tick, no initialized crossings 1`] = `202889`; -exports[`Pool gas tests fee is on #swapExact0For1 second swap in block with no tick movement 1`] = `208073`; +exports[`Pool gas tests fee is on #swapExact0For1 second swap in block with no tick movement 1`] = `190050`; -exports[`Pool gas tests fee is on #swapExact0For1 second swap in block, large swap crossing a single initialized tick 1`] = `234241`; +exports[`Pool gas tests fee is on #swapExact0For1 second swap in block, large swap crossing a single initialized tick 1`] = `216218`; -exports[`Pool gas tests fee is on #swapExact0For1 second swap in block, large swap crossing several initialized ticks 1`] = `289662`; +exports[`Pool gas tests fee is on #swapExact0For1 second swap in block, large swap crossing several initialized ticks 1`] = `271639`; diff --git a/test/__snapshots__/PoolManager.spec.ts.snap b/test/__snapshots__/PoolManager.spec.ts.snap index f59c45442..737b5863d 100644 --- a/test/__snapshots__/PoolManager.spec.ts.snap +++ b/test/__snapshots__/PoolManager.spec.ts.snap @@ -4,10 +4,10 @@ exports[`PoolManager #initialize gas cost 1`] = `70689`; exports[`PoolManager #lock gas overhead of no-op lock 1`] = `41753`; -exports[`PoolManager #mint gas cost 1`] = `331749`; +exports[`PoolManager #mint gas cost 1`] = `314136`; -exports[`PoolManager #swap gas 1`] = `129560`; +exports[`PoolManager #swap gas 1`] = `111851`; -exports[`PoolManager #swap gas for swap against liquidity 1`] = `202460`; +exports[`PoolManager #swap gas for swap against liquidity 1`] = `184436`; -exports[`PoolManager bytecode size 1`] = `20613`; +exports[`PoolManager bytecode size 1`] = `20817`; From 807579fd00a7ce30ce5c3b88b4a4f7c91eff93c9 Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Tue, 23 Nov 2021 10:47:37 -0500 Subject: [PATCH 39/40] fix the account delta function to correctly append tokens to set --- contracts/PoolManager.sol | 21 +++-- .../PoolManager.gas.spec.ts.snap | 92 +++++++++---------- test/__snapshots__/PoolManager.spec.ts.snap | 8 +- 3 files changed, 64 insertions(+), 57 deletions(-) diff --git a/contracts/PoolManager.sol b/contracts/PoolManager.sol index 35a2419de..626c8b518 100644 --- a/contracts/PoolManager.sol +++ b/contracts/PoolManager.sol @@ -87,9 +87,11 @@ contract PoolManager is IPoolManager, NoDelegateCall { // the caller does everything in this callback, including paying what they owe via calls to settle result = ILockCallback(msg.sender).lockAcquired(data); - for (uint256 i = 0; i < tokensTouched.length; i++) { - require(tokenDelta[tokensTouched[i]].delta == 0, 'Not settled'); - delete tokenDelta[tokensTouched[i]]; + 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; @@ -97,12 +99,16 @@ contract PoolManager is IPoolManager, NoDelegateCall { /// @dev Adds a token to a unique list of tokens that have been touched function _addTokenToSet(IERC20Minimal token) internal returns (uint8 slot) { - PositionAndDelta storage pd = tokenDelta[token]; uint256 len = tokensTouched.length; + if (len == 0) { + tokensTouched.push(token); + return 0; + } + + PositionAndDelta storage pd = tokenDelta[token]; slot = pd.slot; - if (slot < len) { - return slot; - } else { + + if (slot == 0 && tokensTouched[slot] != token) { require(len < type(uint8).max); slot = uint8(len); pd.slot = slot; @@ -111,6 +117,7 @@ contract PoolManager is IPoolManager, NoDelegateCall { } function _accountDelta(IERC20Minimal token, int256 delta) internal { + if (delta == 0) return; _addTokenToSet(token); tokenDelta[token].delta += int248(delta); } diff --git a/test/__snapshots__/PoolManager.gas.spec.ts.snap b/test/__snapshots__/PoolManager.gas.spec.ts.snap index 49015532a..4a2345d1e 100644 --- a/test/__snapshots__/PoolManager.gas.spec.ts.snap +++ b/test/__snapshots__/PoolManager.gas.spec.ts.snap @@ -4,29 +4,29 @@ exports[`Pool gas tests fee is off #increaseObservationCardinalityNext grow by 1 exports[`Pool gas tests fee is off #increaseObservationCardinalityNext no op 1`] = `25849`; -exports[`Pool gas tests fee is off #mint above current price add to position after some time passes 1`] = `171651`; +exports[`Pool gas tests fee is off #mint above current price add to position after some time passes 1`] = `169077`; -exports[`Pool gas tests fee is off #mint above current price add to position existing 1`] = `171651`; +exports[`Pool gas tests fee is off #mint above current price add to position existing 1`] = `169077`; -exports[`Pool gas tests fee is off #mint above current price new position mint first in range 1`] = `266625`; +exports[`Pool gas tests fee is off #mint above current price new position mint first in range 1`] = `264052`; -exports[`Pool gas tests fee is off #mint above current price second position in same range 1`] = `185331`; +exports[`Pool gas tests fee is off #mint above current price second position in same range 1`] = `182757`; -exports[`Pool gas tests fee is off #mint around current price add to position after some time passes 1`] = `223218`; +exports[`Pool gas tests fee is off #mint around current price add to position after some time passes 1`] = `242662`; -exports[`Pool gas tests fee is off #mint around current price add to position existing 1`] = `215924`; +exports[`Pool gas tests fee is off #mint around current price add to position existing 1`] = `235368`; -exports[`Pool gas tests fee is off #mint around current price new position mint first in range 1`] = `368139`; +exports[`Pool gas tests fee is off #mint around current price new position mint first in range 1`] = `387583`; -exports[`Pool gas tests fee is off #mint around current price second position in same range 1`] = `229604`; +exports[`Pool gas tests fee is off #mint around current price second position in same range 1`] = `249048`; -exports[`Pool gas tests fee is off #mint below current price add to position after some time passes 1`] = `172331`; +exports[`Pool gas tests fee is off #mint below current price add to position after some time passes 1`] = `169757`; -exports[`Pool gas tests fee is off #mint below current price add to position existing 1`] = `172331`; +exports[`Pool gas tests fee is off #mint below current price add to position existing 1`] = `169757`; -exports[`Pool gas tests fee is off #mint below current price new position mint first in range 1`] = `335597`; +exports[`Pool gas tests fee is off #mint below current price new position mint first in range 1`] = `332380`; -exports[`Pool gas tests fee is off #mint below current price second position in same range 1`] = `186011`; +exports[`Pool gas tests fee is off #mint below current price second position in same range 1`] = `183437`; exports[`Pool gas tests fee is off #snapshotCumulativesInside tick above 1`] = `34301`; @@ -34,55 +34,55 @@ exports[`Pool gas tests fee is off #snapshotCumulativesInside tick below 1`] = ` exports[`Pool gas tests fee is off #snapshotCumulativesInside tick inside 1`] = `39533`; -exports[`Pool gas tests fee is off #swapExact0For1 first swap in block moves tick, no initialized crossings 1`] = `198584`; +exports[`Pool gas tests fee is off #swapExact0For1 first swap in block moves tick, no initialized crossings 1`] = `218028`; -exports[`Pool gas tests fee is off #swapExact0For1 first swap in block with no tick movement 1`] = `185774`; +exports[`Pool gas tests fee is off #swapExact0For1 first swap in block with no tick movement 1`] = `205218`; -exports[`Pool gas tests fee is off #swapExact0For1 first swap in block, large swap crossing a single initialized tick 1`] = `222792`; +exports[`Pool gas tests fee is off #swapExact0For1 first swap in block, large swap crossing a single initialized tick 1`] = `242236`; -exports[`Pool gas tests fee is off #swapExact0For1 first swap in block, large swap crossing several initialized ticks 1`] = `277886`; +exports[`Pool gas tests fee is off #swapExact0For1 first swap in block, large swap crossing several initialized ticks 1`] = `297330`; -exports[`Pool gas tests fee is off #swapExact0For1 first swap in block, large swap, no initialized crossings 1`] = `210662`; +exports[`Pool gas tests fee is off #swapExact0For1 first swap in block, large swap, no initialized crossings 1`] = `230106`; -exports[`Pool gas tests fee is off #swapExact0For1 large swap crossing several initialized ticks after some time passes 1`] = `277886`; +exports[`Pool gas tests fee is off #swapExact0For1 large swap crossing several initialized ticks after some time passes 1`] = `297330`; -exports[`Pool gas tests fee is off #swapExact0For1 large swap crossing several initialized ticks second time after some time passes 1`] = `277886`; +exports[`Pool gas tests fee is off #swapExact0For1 large swap crossing several initialized ticks second time after some time passes 1`] = `297330`; -exports[`Pool gas tests fee is off #swapExact0For1 second swap in block moves tick, no initialized crossings 1`] = `198584`; +exports[`Pool gas tests fee is off #swapExact0For1 second swap in block moves tick, no initialized crossings 1`] = `218028`; -exports[`Pool gas tests fee is off #swapExact0For1 second swap in block with no tick movement 1`] = `185863`; +exports[`Pool gas tests fee is off #swapExact0For1 second swap in block with no tick movement 1`] = `205307`; -exports[`Pool gas tests fee is off #swapExact0For1 second swap in block, large swap crossing a single initialized tick 1`] = `211913`; +exports[`Pool gas tests fee is off #swapExact0For1 second swap in block, large swap crossing a single initialized tick 1`] = `231357`; -exports[`Pool gas tests fee is off #swapExact0For1 second swap in block, large swap crossing several initialized ticks 1`] = `266981`; +exports[`Pool gas tests fee is off #swapExact0For1 second swap in block, large swap crossing several initialized ticks 1`] = `286425`; exports[`Pool gas tests fee is on #increaseObservationCardinalityNext grow by 1 slot 1`] = `50918`; exports[`Pool gas tests fee is on #increaseObservationCardinalityNext no op 1`] = `25849`; -exports[`Pool gas tests fee is on #mint above current price add to position after some time passes 1`] = `171651`; +exports[`Pool gas tests fee is on #mint above current price add to position after some time passes 1`] = `169077`; -exports[`Pool gas tests fee is on #mint above current price add to position existing 1`] = `171651`; +exports[`Pool gas tests fee is on #mint above current price add to position existing 1`] = `169077`; -exports[`Pool gas tests fee is on #mint above current price new position mint first in range 1`] = `266625`; +exports[`Pool gas tests fee is on #mint above current price new position mint first in range 1`] = `264052`; -exports[`Pool gas tests fee is on #mint above current price second position in same range 1`] = `185331`; +exports[`Pool gas tests fee is on #mint above current price second position in same range 1`] = `182757`; -exports[`Pool gas tests fee is on #mint around current price add to position after some time passes 1`] = `223218`; +exports[`Pool gas tests fee is on #mint around current price add to position after some time passes 1`] = `242662`; -exports[`Pool gas tests fee is on #mint around current price add to position existing 1`] = `215924`; +exports[`Pool gas tests fee is on #mint around current price add to position existing 1`] = `235368`; -exports[`Pool gas tests fee is on #mint around current price new position mint first in range 1`] = `368139`; +exports[`Pool gas tests fee is on #mint around current price new position mint first in range 1`] = `387583`; -exports[`Pool gas tests fee is on #mint around current price second position in same range 1`] = `229604`; +exports[`Pool gas tests fee is on #mint around current price second position in same range 1`] = `249048`; -exports[`Pool gas tests fee is on #mint below current price add to position after some time passes 1`] = `172331`; +exports[`Pool gas tests fee is on #mint below current price add to position after some time passes 1`] = `169757`; -exports[`Pool gas tests fee is on #mint below current price add to position existing 1`] = `172331`; +exports[`Pool gas tests fee is on #mint below current price add to position existing 1`] = `169757`; -exports[`Pool gas tests fee is on #mint below current price new position mint first in range 1`] = `335597`; +exports[`Pool gas tests fee is on #mint below current price new position mint first in range 1`] = `332380`; -exports[`Pool gas tests fee is on #mint below current price second position in same range 1`] = `186011`; +exports[`Pool gas tests fee is on #mint below current price second position in same range 1`] = `183437`; exports[`Pool gas tests fee is on #snapshotCumulativesInside tick above 1`] = `34301`; @@ -90,24 +90,24 @@ exports[`Pool gas tests fee is on #snapshotCumulativesInside tick below 1`] = `3 exports[`Pool gas tests fee is on #snapshotCumulativesInside tick inside 1`] = `39533`; -exports[`Pool gas tests fee is on #swapExact0For1 first swap in block moves tick, no initialized crossings 1`] = `202889`; +exports[`Pool gas tests fee is on #swapExact0For1 first swap in block moves tick, no initialized crossings 1`] = `222333`; -exports[`Pool gas tests fee is on #swapExact0For1 first swap in block with no tick movement 1`] = `189961`; +exports[`Pool gas tests fee is on #swapExact0For1 first swap in block with no tick movement 1`] = `209405`; -exports[`Pool gas tests fee is on #swapExact0For1 first swap in block, large swap crossing a single initialized tick 1`] = `227214`; +exports[`Pool gas tests fee is on #swapExact0For1 first swap in block, large swap crossing a single initialized tick 1`] = `246658`; -exports[`Pool gas tests fee is on #swapExact0For1 first swap in block, large swap crossing several initialized ticks 1`] = `282661`; +exports[`Pool gas tests fee is on #swapExact0For1 first swap in block, large swap crossing several initialized ticks 1`] = `302105`; -exports[`Pool gas tests fee is on #swapExact0For1 first swap in block, large swap, no initialized crossings 1`] = `215202`; +exports[`Pool gas tests fee is on #swapExact0For1 first swap in block, large swap, no initialized crossings 1`] = `234646`; -exports[`Pool gas tests fee is on #swapExact0For1 large swap crossing several initialized ticks after some time passes 1`] = `282661`; +exports[`Pool gas tests fee is on #swapExact0For1 large swap crossing several initialized ticks after some time passes 1`] = `302105`; -exports[`Pool gas tests fee is on #swapExact0For1 large swap crossing several initialized ticks second time after some time passes 1`] = `282661`; +exports[`Pool gas tests fee is on #swapExact0For1 large swap crossing several initialized ticks second time after some time passes 1`] = `302105`; -exports[`Pool gas tests fee is on #swapExact0For1 second swap in block moves tick, no initialized crossings 1`] = `202889`; +exports[`Pool gas tests fee is on #swapExact0For1 second swap in block moves tick, no initialized crossings 1`] = `222333`; -exports[`Pool gas tests fee is on #swapExact0For1 second swap in block with no tick movement 1`] = `190050`; +exports[`Pool gas tests fee is on #swapExact0For1 second swap in block with no tick movement 1`] = `209494`; -exports[`Pool gas tests fee is on #swapExact0For1 second swap in block, large swap crossing a single initialized tick 1`] = `216218`; +exports[`Pool gas tests fee is on #swapExact0For1 second swap in block, large swap crossing a single initialized tick 1`] = `235662`; -exports[`Pool gas tests fee is on #swapExact0For1 second swap in block, large swap crossing several initialized ticks 1`] = `271639`; +exports[`Pool 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 index 737b5863d..6d5296629 100644 --- a/test/__snapshots__/PoolManager.spec.ts.snap +++ b/test/__snapshots__/PoolManager.spec.ts.snap @@ -4,10 +4,10 @@ exports[`PoolManager #initialize gas cost 1`] = `70689`; exports[`PoolManager #lock gas overhead of no-op lock 1`] = `41753`; -exports[`PoolManager #mint gas cost 1`] = `314136`; +exports[`PoolManager #mint gas cost 1`] = `311563`; -exports[`PoolManager #swap gas 1`] = `111851`; +exports[`PoolManager #swap gas 1`] = `71741`; -exports[`PoolManager #swap gas for swap against liquidity 1`] = `184436`; +exports[`PoolManager #swap gas for swap against liquidity 1`] = `203880`; -exports[`PoolManager bytecode size 1`] = `20817`; +exports[`PoolManager bytecode size 1`] = `20939`; From 21700b1489c57ebb1a64b9485027538435cff268 Mon Sep 17 00:00:00 2001 From: Moody Salem Date: Tue, 30 Nov 2021 15:39:19 -0500 Subject: [PATCH 40/40] rename --- test/PoolManager.gas.spec.ts | 2 +- .../PoolManager.gas.spec.ts.snap | 112 +++++++++--------- 2 files changed, 57 insertions(+), 57 deletions(-) diff --git a/test/PoolManager.gas.spec.ts b/test/PoolManager.gas.spec.ts index 2439d68a0..c63a9d548 100644 --- a/test/PoolManager.gas.spec.ts +++ b/test/PoolManager.gas.spec.ts @@ -32,7 +32,7 @@ type AsyncReturnType any> = T extends (...args: any) ? U : any -describe.only('Pool gas tests', () => { +describe.only('PoolManager gas tests', () => { let wallet: Wallet, other: Wallet let loadFixture: ReturnType diff --git a/test/__snapshots__/PoolManager.gas.spec.ts.snap b/test/__snapshots__/PoolManager.gas.spec.ts.snap index 4a2345d1e..14481a20f 100644 --- a/test/__snapshots__/PoolManager.gas.spec.ts.snap +++ b/test/__snapshots__/PoolManager.gas.spec.ts.snap @@ -1,113 +1,113 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Pool gas tests fee is off #increaseObservationCardinalityNext grow by 1 slot 1`] = `50918`; +exports[`PoolManager gas tests fee is off #increaseObservationCardinalityNext grow by 1 slot 1`] = `50918`; -exports[`Pool gas tests fee is off #increaseObservationCardinalityNext no op 1`] = `25849`; +exports[`PoolManager gas tests fee is off #increaseObservationCardinalityNext no op 1`] = `25849`; -exports[`Pool 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 after some time passes 1`] = `169077`; -exports[`Pool 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 add to position existing 1`] = `169077`; -exports[`Pool 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 new position mint first in range 1`] = `264052`; -exports[`Pool gas tests fee is off #mint above current price second position in same range 1`] = `182757`; +exports[`PoolManager gas tests fee is off #mint above current price second position in same range 1`] = `182757`; -exports[`Pool 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 after some time passes 1`] = `242662`; -exports[`Pool 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 add to position existing 1`] = `235368`; -exports[`Pool 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 new position mint first in range 1`] = `387583`; -exports[`Pool gas tests fee is off #mint around current price second position in same range 1`] = `249048`; +exports[`PoolManager gas tests fee is off #mint around current price second position in same range 1`] = `249048`; -exports[`Pool 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 after some time passes 1`] = `169757`; -exports[`Pool 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 add to position existing 1`] = `169757`; -exports[`Pool 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 new position mint first in range 1`] = `332380`; -exports[`Pool gas tests fee is off #mint below current price second position in same range 1`] = `183437`; +exports[`PoolManager gas tests fee is off #mint below current price second position in same range 1`] = `183437`; -exports[`Pool gas tests fee is off #snapshotCumulativesInside tick above 1`] = `34301`; +exports[`PoolManager gas tests fee is off #snapshotCumulativesInside tick above 1`] = `34301`; -exports[`Pool gas tests fee is off #snapshotCumulativesInside tick below 1`] = `34260`; +exports[`PoolManager gas tests fee is off #snapshotCumulativesInside tick below 1`] = `34260`; -exports[`Pool gas tests fee is off #snapshotCumulativesInside tick inside 1`] = `39533`; +exports[`PoolManager gas tests fee is off #snapshotCumulativesInside tick inside 1`] = `39533`; -exports[`Pool 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 moves tick, no initialized crossings 1`] = `218028`; -exports[`Pool 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 with no tick movement 1`] = `205218`; -exports[`Pool 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 a single initialized tick 1`] = `242236`; -exports[`Pool 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 crossing several initialized ticks 1`] = `297330`; -exports[`Pool 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 first swap in block, large swap, no initialized crossings 1`] = `230106`; -exports[`Pool 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 after some time passes 1`] = `297330`; -exports[`Pool 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 large swap crossing several initialized ticks second time after some time passes 1`] = `297330`; -exports[`Pool 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 moves tick, no initialized crossings 1`] = `218028`; -exports[`Pool 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 with no tick movement 1`] = `205307`; -exports[`Pool 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 a single initialized tick 1`] = `231357`; -exports[`Pool gas tests fee is off #swapExact0For1 second swap in block, large swap crossing several initialized ticks 1`] = `286425`; +exports[`PoolManager gas tests fee is off #swapExact0For1 second swap in block, large swap crossing several initialized ticks 1`] = `286425`; -exports[`Pool gas tests fee is on #increaseObservationCardinalityNext grow by 1 slot 1`] = `50918`; +exports[`PoolManager gas tests fee is on #increaseObservationCardinalityNext grow by 1 slot 1`] = `50918`; -exports[`Pool gas tests fee is on #increaseObservationCardinalityNext no op 1`] = `25849`; +exports[`PoolManager gas tests fee is on #increaseObservationCardinalityNext no op 1`] = `25849`; -exports[`Pool 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 after some time passes 1`] = `169077`; -exports[`Pool 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 add to position existing 1`] = `169077`; -exports[`Pool 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 new position mint first in range 1`] = `264052`; -exports[`Pool gas tests fee is on #mint above current price second position in same range 1`] = `182757`; +exports[`PoolManager gas tests fee is on #mint above current price second position in same range 1`] = `182757`; -exports[`Pool 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 after some time passes 1`] = `242662`; -exports[`Pool 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 add to position existing 1`] = `235368`; -exports[`Pool 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 new position mint first in range 1`] = `387583`; -exports[`Pool gas tests fee is on #mint around current price second position in same range 1`] = `249048`; +exports[`PoolManager gas tests fee is on #mint around current price second position in same range 1`] = `249048`; -exports[`Pool 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 after some time passes 1`] = `169757`; -exports[`Pool 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 add to position existing 1`] = `169757`; -exports[`Pool 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 new position mint first in range 1`] = `332380`; -exports[`Pool gas tests fee is on #mint below current price second position in same range 1`] = `183437`; +exports[`PoolManager gas tests fee is on #mint below current price second position in same range 1`] = `183437`; -exports[`Pool gas tests fee is on #snapshotCumulativesInside tick above 1`] = `34301`; +exports[`PoolManager gas tests fee is on #snapshotCumulativesInside tick above 1`] = `34301`; -exports[`Pool gas tests fee is on #snapshotCumulativesInside tick below 1`] = `34260`; +exports[`PoolManager gas tests fee is on #snapshotCumulativesInside tick below 1`] = `34260`; -exports[`Pool gas tests fee is on #snapshotCumulativesInside tick inside 1`] = `39533`; +exports[`PoolManager gas tests fee is on #snapshotCumulativesInside tick inside 1`] = `39533`; -exports[`Pool 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 moves tick, no initialized crossings 1`] = `222333`; -exports[`Pool 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 with no tick movement 1`] = `209405`; -exports[`Pool 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 a single initialized tick 1`] = `246658`; -exports[`Pool 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 crossing several initialized ticks 1`] = `302105`; -exports[`Pool 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 first swap in block, large swap, no initialized crossings 1`] = `234646`; -exports[`Pool 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 after some time passes 1`] = `302105`; -exports[`Pool 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 large swap crossing several initialized ticks second time after some time passes 1`] = `302105`; -exports[`Pool 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 moves tick, no initialized crossings 1`] = `222333`; -exports[`Pool 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 with no tick movement 1`] = `209494`; -exports[`Pool 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 a single initialized tick 1`] = `235662`; -exports[`Pool gas tests fee is on #swapExact0For1 second swap in block, large swap crossing several initialized ticks 1`] = `291083`; +exports[`PoolManager gas tests fee is on #swapExact0For1 second swap in block, large swap crossing several initialized ticks 1`] = `291083`;