diff --git a/src/PoolManager.sol b/src/PoolManager.sol index 90f700dda..0a5dca6e7 100644 --- a/src/PoolManager.sol +++ b/src/PoolManager.sol @@ -27,13 +27,7 @@ import {Extsload} from "./Extsload.sol"; /// @notice Holds the state for all pools -contract PoolManager is - IPoolManager, - ProtocolFees, - NoDelegateCall, - ERC6909Claims, - Extsload -{ +contract PoolManager is IPoolManager, ProtocolFees, NoDelegateCall, ERC6909Claims, Extsload { using PoolIdLibrary for PoolKey; using SafeCast for *; using Pool for *; @@ -55,67 +49,48 @@ contract PoolManager is constructor(uint256 controllerGasLimit) ProtocolFees(controllerGasLimit) {} - function _getPool( - PoolId id - ) internal view override returns (Pool.State storage) { + function _getPool(PoolId id) internal view override returns (Pool.State storage) { return pools[id]; } /// @inheritdoc IPoolManager - function getSlot0( - PoolId id - ) + function getSlot0(PoolId id) external view override - returns ( - uint160 sqrtPriceX96, - int24 tick, - uint24 protocolFee, - uint24 lpFee - ) + returns (uint160 sqrtPriceX96, int24 tick, uint24 protocolFee, uint24 lpFee) { Slot0Packed slot0 = pools[id].slot0.loadPacked(); - return ( - slot0.sqrtPriceX96(), - slot0.tick(), - slot0.protocolFee(), - slot0.lpFee() - ); + return (slot0.sqrtPriceX96(), slot0.tick(), slot0.protocolFee(), slot0.lpFee()); } /// @inheritdoc IPoolManager - function getLiquidity( - PoolId id - ) external view override returns (uint128 liquidity) { + function getLiquidity(PoolId id) external view override returns (uint128 liquidity) { return pools[id].liquidity; } /// @inheritdoc IPoolManager - function getLiquidity( - PoolId id, - address _owner, - int24 tickLower, - int24 tickUpper - ) external view override returns (uint128 liquidity) { + function getLiquidity(PoolId id, address _owner, int24 tickLower, int24 tickUpper) + external + view + override + returns (uint128 liquidity) + { return pools[id].positions.get(_owner, tickLower, tickUpper).liquidity; } - function getPosition( - PoolId id, - address _owner, - int24 tickLower, - int24 tickUpper - ) external view override returns (Position.Info memory position) { + function getPosition(PoolId id, address _owner, int24 tickLower, int24 tickUpper) + external + view + override + returns (Position.Info memory position) + { return pools[id].positions.get(_owner, tickLower, tickUpper); } /// @inheritdoc IPoolManager - function currencyDelta( - address caller, - Currency currency - ) external view returns (int256) { + function currencyDelta(address caller, Currency currency) external view returns (int256) { return currency.getDelta(caller); } @@ -131,18 +106,21 @@ contract PoolManager is } /// @inheritdoc IPoolManager - function initialize( - PoolKey memory key, - uint160 sqrtPriceX96, - bytes calldata hookData - ) external override noDelegateCall returns (int24 tick) { + function initialize(PoolKey memory key, uint160 sqrtPriceX96, bytes calldata hookData) + external + override + noDelegateCall + returns (int24 tick) + { // see TickBitmap.sol for overflow conditions that can arise from tick spacing being too large if (key.tickSpacing > MAX_TICK_SPACING) revert TickSpacingTooLarge(); if (key.tickSpacing < MIN_TICK_SPACING) revert TickSpacingTooSmall(); - if (key.currency0 >= key.currency1) + if (key.currency0 >= key.currency1) { revert CurrenciesOutOfOrderOrEqual(); - if (!key.hooks.isValidHookAddress(key.fee)) + } + if (!key.hooks.isValidHookAddress(key.fee)) { revert Hooks.HookAddressNotValid(address(key.hooks)); + } uint24 lpFee = key.fee.getInitialLPFee(); @@ -156,20 +134,11 @@ contract PoolManager is key.hooks.afterInitialize(key, sqrtPriceX96, tick, hookData); // On intitalize we emit the key's fee, which tells us all fee settings a pool can have: either a static swap fee or dynamic swap fee and if the hook has enabled swap or withdraw fees. - emit Initialize( - id, - key.currency0, - key.currency1, - key.fee, - key.tickSpacing, - key.hooks - ); + emit Initialize(id, key.currency0, key.currency1, key.fee, key.tickSpacing, key.hooks); } /// @inheritdoc IPoolManager - function unlock( - bytes calldata data - ) external override noDelegateCall returns (bytes memory result) { + function unlock(bytes calldata data) external override noDelegateCall returns (bytes memory result) { if (Lock.isUnlocked()) revert AlreadyUnlocked(); Lock.unlock(); @@ -203,10 +172,7 @@ contract PoolManager is } /// @dev Accumulates a balance change to a map of currency to balance changes - function _accountPoolBalanceDelta( - PoolKey memory key, - BalanceDelta delta - ) internal { + function _accountPoolBalanceDelta(PoolKey memory key, BalanceDelta delta) internal { _accountDelta(key.currency0, delta.amount0()); _accountDelta(key.currency1, delta.amount1()); } @@ -220,12 +186,7 @@ contract PoolManager is PoolKey memory key, IPoolManager.ModifyLiquidityParams memory params, bytes calldata hookData - ) - external - override - onlyWhenUnlocked - returns (BalanceDelta delta, BalanceDelta feeDelta) - { + ) external override onlyWhenUnlocked returns (BalanceDelta delta, BalanceDelta feeDelta) { PoolId id = key.toId(); _checkPoolInitialized(id); @@ -243,23 +204,18 @@ contract PoolManager is _accountPoolBalanceDelta(key, delta + feeDelta); - emit ModifyLiquidity( - id, - msg.sender, - params.tickLower, - params.tickUpper, - params.liquidityDelta - ); + emit ModifyLiquidity(id, msg.sender, params.tickLower, params.tickUpper, params.liquidityDelta); key.hooks.afterModifyLiquidity(key, params, delta, hookData); } /// @inheritdoc IPoolManager - function swap( - PoolKey memory key, - IPoolManager.SwapParams memory params, - bytes calldata hookData - ) external override onlyWhenUnlocked returns (BalanceDelta delta) { + function swap(PoolKey memory key, IPoolManager.SwapParams memory params, bytes calldata hookData) + external + override + onlyWhenUnlocked + returns (BalanceDelta delta) + { PoolId id = key.toId(); _checkPoolInitialized(id); @@ -281,33 +237,23 @@ contract PoolManager is // The fee is on the input currency. if (feeForProtocol > 0) { - _updateProtocolFees( - params.zeroForOne ? key.currency0 : key.currency1, - feeForProtocol - ); + _updateProtocolFees(params.zeroForOne ? key.currency0 : key.currency1, feeForProtocol); } emit Swap( - id, - msg.sender, - delta.amount0(), - delta.amount1(), - state.sqrtPriceX96, - state.liquidity, - state.tick, - swapFee + id, msg.sender, delta.amount0(), delta.amount1(), state.sqrtPriceX96, state.liquidity, state.tick, swapFee ); key.hooks.afterSwap(key, params, delta, hookData); } /// @inheritdoc IPoolManager - function donate( - PoolKey memory key, - uint256 amount0, - uint256 amount1, - bytes calldata hookData - ) external override onlyWhenUnlocked returns (BalanceDelta delta) { + function donate(PoolKey memory key, uint256 amount0, uint256 amount1, bytes calldata hookData) + external + override + onlyWhenUnlocked + returns (BalanceDelta delta) + { PoolId id = key.toId(); _checkPoolInitialized(id); @@ -321,11 +267,7 @@ contract PoolManager is } /// @inheritdoc IPoolManager - function take( - Currency currency, - address to, - uint256 amount - ) external override onlyWhenUnlocked { + function take(Currency currency, address to, uint256 amount) external override onlyWhenUnlocked { unchecked { // subtraction must be safe _accountDelta(currency, -(amount.toInt128())); @@ -334,9 +276,7 @@ contract PoolManager is } /// @inheritdoc IPoolManager - function settle( - Currency currency - ) external payable override onlyWhenUnlocked returns (uint256 paid) { + function settle(Currency currency) external payable override onlyWhenUnlocked returns (uint256 paid) { if (currency.isNative()) { paid = msg.value; } else { @@ -349,11 +289,7 @@ contract PoolManager is } /// @inheritdoc IPoolManager - function mint( - address to, - uint256 id, - uint256 amount - ) external override onlyWhenUnlocked { + function mint(address to, uint256 id, uint256 amount) external override onlyWhenUnlocked { unchecked { // subtraction must be safe _accountDelta(CurrencyLibrary.fromId(id), -(amount.toInt128())); @@ -362,58 +298,38 @@ contract PoolManager is } /// @inheritdoc IPoolManager - function burn( - address from, - uint256 id, - uint256 amount - ) external override onlyWhenUnlocked { + function burn(address from, uint256 id, uint256 amount) external override onlyWhenUnlocked { _accountDelta(CurrencyLibrary.fromId(id), amount.toInt128()); _burnFrom(from, id, amount); } - function updateDynamicLPFee( - PoolKey memory key, - uint24 newDynamicLPFee - ) external { - if (!key.fee.isDynamicFee() || msg.sender != address(key.hooks)) + function updateDynamicLPFee(PoolKey memory key, uint24 newDynamicLPFee) external { + if (!key.fee.isDynamicFee() || msg.sender != address(key.hooks)) { revert UnauthorizedDynamicLPFeeUpdate(); + } newDynamicLPFee.validate(); PoolId id = key.toId(); pools[id].setLPFee(newDynamicLPFee); } - function getNonzeroDeltaCount() - external - view - returns (uint256 _nonzeroDeltaCount) - { + function getNonzeroDeltaCount() external view returns (uint256 _nonzeroDeltaCount) { return NonZeroDeltaCount.read(); } - function getPoolTickInfo( - PoolId id, - int24 tick - ) external view returns (Pool.TickInfo memory) { + function getPoolTickInfo(PoolId id, int24 tick) external view returns (Pool.TickInfo memory) { return pools[id].getPoolTickInfo(tick); } - function getPoolBitmapInfo( - PoolId id, - int16 word - ) external view returns (uint256 tickBitmap) { + function getPoolBitmapInfo(PoolId id, int16 word) external view returns (uint256 tickBitmap) { return pools[id].getPoolBitmapInfo(word); } /// @notice Temporary view function. Replaceable by transient EXTSLOAD. - function getReserves( - Currency currency - ) external view returns (uint256 balance) { + function getReserves(Currency currency) external view returns (uint256 balance) { return currency.getReserves(); } - function getFeeGrowthGlobals( - PoolId id - ) + function getFeeGrowthGlobals(PoolId id) external view returns (uint256 feeGrowthGlobal0x128, uint256 feeGrowthGlobal1x128) diff --git a/src/libraries/Pool.sol b/src/libraries/Pool.sol index 75d8b2a6e..da3cc6825 100644 --- a/src/libraries/Pool.sol +++ b/src/libraries/Pool.sol @@ -55,10 +55,7 @@ library Pool { /// @notice Thrown when sqrtPriceLimitX96 on a swap has already exceeded its limit /// @param sqrtPriceCurrentX96 The invalid, already surpassed sqrtPriceLimitX96 /// @param sqrtPriceLimitX96 The surpassed price limit - error PriceLimitAlreadyExceeded( - uint160 sqrtPriceCurrentX96, - uint160 sqrtPriceLimitX96 - ); + error PriceLimitAlreadyExceeded(uint160 sqrtPriceCurrentX96, uint160 sqrtPriceLimitX96); /// @notice Thrown when sqrtPriceLimitX96 lies outside of valid tick/price range /// @param sqrtPriceLimitX96 The invalid, out-of-bounds sqrtPriceLimitX96 @@ -95,31 +92,28 @@ library Pool { /// @dev Common checks for valid tick inputs. function checkTicks(int24 tickLower, int24 tickUpper) private pure { - if (tickLower >= tickUpper) + if (tickLower >= tickUpper) { revert TicksMisordered(tickLower, tickUpper); - if (tickLower < TickMath.MIN_TICK) + } + if (tickLower < TickMath.MIN_TICK) { revert TickLowerOutOfBounds(tickLower); - if (tickUpper > TickMath.MAX_TICK) + } + if (tickUpper > TickMath.MAX_TICK) { revert TickUpperOutOfBounds(tickUpper); + } } - function initialize( - State storage self, - uint160 sqrtPriceX96, - uint24 protocolFee, - uint24 lpFee - ) internal returns (int24 tick) { + function initialize(State storage self, uint160 sqrtPriceX96, uint24 protocolFee, uint24 lpFee) + internal + returns (int24 tick) + { if (self.slot0.sqrtPriceX96 != 0) revert PoolAlreadyInitialized(); tick = TickMath.getTickAtSqrtRatio(sqrtPriceX96); Slot0Packed _slot0; self.slot0.storePacked( - _slot0 - .setSqrtPriceX96(sqrtPriceX96) - .setTick(tick) - .setProtocolFee(protocolFee) - .setLpFee(lpFee) + _slot0.setSqrtPriceX96(sqrtPriceX96).setTick(tick).setProtocolFee(protocolFee).setLpFee(lpFee) ); } @@ -161,10 +155,10 @@ library Pool { /// @param params the position details and the change to the position's liquidity to effect /// @return delta the deltas of the token balances of the pool, from the liquidity change /// @return feeDelta the fees generated by the liquidity range - function modifyLiquidity( - State storage self, - ModifyLiquidityParams memory params - ) internal returns (BalanceDelta delta, BalanceDelta feeDelta) { + function modifyLiquidity(State storage self, ModifyLiquidityParams memory params) + internal + returns (BalanceDelta delta, BalanceDelta feeDelta) + { int128 liquidityDelta = params.liquidityDelta; int24 tickLower = params.tickLower; int24 tickUpper = params.tickUpper; @@ -177,20 +171,13 @@ library Pool { // if we need to update the ticks, do it if (liquidityDelta != 0) { - ( - state.flippedLower, - state.liquidityGrossAfterLower - ) = updateTick(self, tickLower, liquidityDelta, false); - ( - state.flippedUpper, - state.liquidityGrossAfterUpper - ) = updateTick(self, tickUpper, liquidityDelta, true); + (state.flippedLower, state.liquidityGrossAfterLower) = + updateTick(self, tickLower, liquidityDelta, false); + (state.flippedUpper, state.liquidityGrossAfterUpper) = updateTick(self, tickUpper, liquidityDelta, true); // `>` and `>=` are logically equivalent here but `>=` is cheaper if (liquidityDelta >= 0) { - uint128 maxLiquidityPerTick = tickSpacingToMaxLiquidityPerTick( - params.tickSpacing - ); + uint128 maxLiquidityPerTick = tickSpacingToMaxLiquidityPerTick(params.tickSpacing); if (state.liquidityGrossAfterLower > maxLiquidityPerTick) { revert TickLiquidityOverflow(tickLower); } @@ -207,21 +194,11 @@ library Pool { } } - ( - state.feeGrowthInside0X128, - state.feeGrowthInside1X128 - ) = getFeeGrowthInside(self, tickLower, tickUpper); + (state.feeGrowthInside0X128, state.feeGrowthInside1X128) = getFeeGrowthInside(self, tickLower, tickUpper); - Position.Info storage position = self.positions.get( - params.owner, - tickLower, - tickUpper - ); - (feesOwed0, feesOwed1) = position.update( - liquidityDelta, - state.feeGrowthInside0X128, - state.feeGrowthInside1X128 - ); + Position.Info storage position = self.positions.get(params.owner, tickLower, tickUpper); + (feesOwed0, feesOwed1) = + position.update(liquidityDelta, state.feeGrowthInside0X128, state.feeGrowthInside1X128); // clear any tick data that is no longer needed if (liquidityDelta < 0) { @@ -241,49 +218,28 @@ library Pool { // current tick is below the passed range; liquidity can only become in range by crossing from left to // right, when we'll need _more_ currency0 (it's becoming more valuable) so user must provide it delta = toBalanceDelta( - SqrtPriceMath - .getAmount0Delta( - TickMath.getSqrtRatioAtTick(tickLower), - TickMath.getSqrtRatioAtTick(tickUpper), - liquidityDelta - ) - .toInt128(), + SqrtPriceMath.getAmount0Delta( + TickMath.getSqrtRatioAtTick(tickLower), TickMath.getSqrtRatioAtTick(tickUpper), liquidityDelta + ).toInt128(), 0 ); } else if (tick < tickUpper) { delta = toBalanceDelta( - SqrtPriceMath - .getAmount0Delta( - sqrtPriceX96, - TickMath.getSqrtRatioAtTick(tickUpper), - liquidityDelta - ) + SqrtPriceMath.getAmount0Delta(sqrtPriceX96, TickMath.getSqrtRatioAtTick(tickUpper), liquidityDelta) .toInt128(), - SqrtPriceMath - .getAmount1Delta( - TickMath.getSqrtRatioAtTick(tickLower), - sqrtPriceX96, - liquidityDelta - ) + SqrtPriceMath.getAmount1Delta(TickMath.getSqrtRatioAtTick(tickLower), sqrtPriceX96, liquidityDelta) .toInt128() ); - self.liquidity = LiquidityMath.addDelta( - self.liquidity, - liquidityDelta - ); + self.liquidity = LiquidityMath.addDelta(self.liquidity, 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_ currency1 (it's becoming more valuable) so user must provide it delta = toBalanceDelta( 0, - SqrtPriceMath - .getAmount1Delta( - TickMath.getSqrtRatioAtTick(tickLower), - TickMath.getSqrtRatioAtTick(tickUpper), - liquidityDelta - ) - .toInt128() + SqrtPriceMath.getAmount1Delta( + TickMath.getSqrtRatioAtTick(tickLower), TickMath.getSqrtRatioAtTick(tickUpper), liquidityDelta + ).toInt128() ); } } @@ -341,17 +297,9 @@ library Pool { /// @notice Executes a swap against the state, and returns the amount deltas of the pool /// @dev PoolManager checks that the pool is initialized before calling - function swap( - State storage self, - SwapParams memory params - ) + function swap(State storage self, SwapParams memory params) internal - returns ( - BalanceDelta result, - uint256 feeForProtocol, - uint24 swapFee, - SwapState memory state - ) + returns (BalanceDelta result, uint256 feeForProtocol, uint24 swapFee, SwapState memory state) { if (params.amountSpecified == 0) revert SwapAmountCannotBeZero(); @@ -359,20 +307,14 @@ library Pool { bool zeroForOne = params.zeroForOne; if (zeroForOne) { if (params.sqrtPriceLimitX96 >= slot0Start.sqrtPriceX96()) { - revert PriceLimitAlreadyExceeded( - slot0Start.sqrtPriceX96(), - params.sqrtPriceLimitX96 - ); + revert PriceLimitAlreadyExceeded(slot0Start.sqrtPriceX96(), params.sqrtPriceLimitX96); } if (params.sqrtPriceLimitX96 <= TickMath.MIN_SQRT_RATIO) { revert PriceLimitOutOfBounds(params.sqrtPriceLimitX96); } } else { if (params.sqrtPriceLimitX96 <= slot0Start.sqrtPriceX96()) { - revert PriceLimitAlreadyExceeded( - slot0Start.sqrtPriceX96(), - params.sqrtPriceLimitX96 - ); + revert PriceLimitAlreadyExceeded(slot0Start.sqrtPriceX96(), params.sqrtPriceLimitX96); } if (params.sqrtPriceLimitX96 >= TickMath.MAX_SQRT_RATIO) { revert PriceLimitOutOfBounds(params.sqrtPriceLimitX96); @@ -393,35 +335,24 @@ library Pool { amountCalculated: 0, sqrtPriceX96: slot0Start.sqrtPriceX96(), tick: slot0Start.tick(), - feeGrowthGlobalX128: zeroForOne - ? self.feeGrowthGlobal0X128 - : self.feeGrowthGlobal1X128, + feeGrowthGlobalX128: zeroForOne ? self.feeGrowthGlobal0X128 : self.feeGrowthGlobal1X128, liquidity: cache.liquidityStart }); StepComputations memory step; - swapFee = cache.protocolFee == 0 - ? slot0Start.lpFee() - : uint24(cache.protocolFee).calculateSwapFee(slot0Start.lpFee()); + swapFee = + cache.protocolFee == 0 ? slot0Start.lpFee() : uint24(cache.protocolFee).calculateSwapFee(slot0Start.lpFee()); if (!exactInput && (swapFee == LPFeeLibrary.MAX_LP_FEE)) { revert InvalidFeeForExactOut(); } // continue swapping as long as we haven't used the entire input/output and haven't reached the price limit - while ( - state.amountSpecifiedRemaining != 0 && - state.sqrtPriceX96 != params.sqrtPriceLimitX96 - ) { + while (state.amountSpecifiedRemaining != 0 && state.sqrtPriceX96 != params.sqrtPriceLimitX96) { step.sqrtPriceStartX96 = state.sqrtPriceX96; - (step.tickNext, step.initialized) = self - .tickBitmap - .nextInitializedTickWithinOneWord( - state.tick, - params.tickSpacing, - zeroForOne - ); + (step.tickNext, step.initialized) = + self.tickBitmap.nextInitializedTickWithinOneWord(state.tick, params.tickSpacing, zeroForOne); // ensure that we do not overshoot the min/max tick, as the tick bitmap is not aware of these bounds if (step.tickNext < TickMath.MIN_TICK) { @@ -434,20 +365,13 @@ library Pool { step.sqrtPriceNextX96 = TickMath.getSqrtRatioAtTick(step.tickNext); // compute values to swap to the target tick, price limit, or point where input/output amount is exhausted - ( - state.sqrtPriceX96, - step.amountIn, - step.amountOut, - step.feeAmount - ) = SwapMath.computeSwapStep( + (state.sqrtPriceX96, step.amountIn, step.amountOut, step.feeAmount) = SwapMath.computeSwapStep( state.sqrtPriceX96, ( zeroForOne ? step.sqrtPriceNextX96 < params.sqrtPriceLimitX96 : step.sqrtPriceNextX96 > params.sqrtPriceLimitX96 - ) - ? params.sqrtPriceLimitX96 - : step.sqrtPriceNextX96, + ) ? params.sqrtPriceLimitX96 : step.sqrtPriceNextX96, state.liquidity, state.amountSpecifiedRemaining, swapFee @@ -456,19 +380,14 @@ library Pool { if (exactInput) { // safe because we test that amountSpecified > amountIn + feeAmount in SwapMath unchecked { - state.amountSpecifiedRemaining += (step.amountIn + - step.feeAmount).toInt256(); + state.amountSpecifiedRemaining += (step.amountIn + step.feeAmount).toInt256(); } - state.amountCalculated = - state.amountCalculated + - step.amountOut.toInt256(); + state.amountCalculated = state.amountCalculated + step.amountOut.toInt256(); } else { unchecked { state.amountSpecifiedRemaining -= step.amountOut.toInt256(); } - state.amountCalculated = - state.amountCalculated - - (step.amountIn + step.feeAmount).toInt256(); + state.amountCalculated = state.amountCalculated - (step.amountIn + step.feeAmount).toInt256(); } // if the protocol fee is on, calculate how much is owed, decrement feeAmount, and increment protocolFee @@ -476,9 +395,8 @@ library Pool { unchecked { // step.amountIn does not include the swap fee, as it's already been taken from it, // so add it back to get the total amountIn and use that to calculate the amount of fees owed to the protocol - uint256 delta = ((step.amountIn + step.feeAmount) * - cache.protocolFee) / - ProtocolFeeLibrary.PIPS_DENOMINATOR; + uint256 delta = + ((step.amountIn + step.feeAmount) * cache.protocolFee) / ProtocolFeeLibrary.PIPS_DENOMINATOR; // subtract it from the total fee and add it to the protocol fee step.feeAmount -= delta; feeForProtocol += delta; @@ -488,11 +406,7 @@ library Pool { // update global fee tracker if (state.liquidity > 0) { unchecked { - state.feeGrowthGlobalX128 += FullMath.mulDiv( - step.feeAmount, - FixedPoint128.Q128, - state.liquidity - ); + state.feeGrowthGlobalX128 += FullMath.mulDiv(step.feeAmount, FixedPoint128.Q128, state.liquidity); } } @@ -509,22 +423,15 @@ library Pool { feeGrowthGlobal0X128 = state.feeGrowthGlobalX128; feeGrowthGlobal1X128 = self.feeGrowthGlobal1X128; } - int128 liquidityNet = Pool.crossTick( - self, - step.tickNext, - feeGrowthGlobal0X128, - feeGrowthGlobal1X128 - ); + int128 liquidityNet = + Pool.crossTick(self, step.tickNext, feeGrowthGlobal0X128, feeGrowthGlobal1X128); // if we're moving leftward, we interpret liquidityNet as the opposite sign // safe because liquidityNet cannot be type(int128).min unchecked { if (zeroForOne) liquidityNet = -liquidityNet; } - state.liquidity = LiquidityMath.addDelta( - state.liquidity, - liquidityNet - ); + state.liquidity = LiquidityMath.addDelta(state.liquidity, liquidityNet); } unchecked { @@ -536,13 +443,12 @@ library Pool { } } - self.slot0.storePacked( - slot0Start.setTick(state.tick).setSqrtPriceX96(state.sqrtPriceX96) - ); + self.slot0.storePacked(slot0Start.setTick(state.tick).setSqrtPriceX96(state.sqrtPriceX96)); // update liquidity if it changed - if (cache.liquidityStart != state.liquidity) + if (cache.liquidityStart != state.liquidity) { self.liquidity = state.liquidity; + } // update fee growth global if (zeroForOne) { @@ -554,42 +460,28 @@ library Pool { unchecked { if (zeroForOne == exactInput) { result = toBalanceDelta( - (params.amountSpecified - state.amountSpecifiedRemaining) - .toInt128(), + (params.amountSpecified - state.amountSpecifiedRemaining).toInt128(), state.amountCalculated.toInt128() ); } else { result = toBalanceDelta( state.amountCalculated.toInt128(), - (params.amountSpecified - state.amountSpecifiedRemaining) - .toInt128() + (params.amountSpecified - state.amountSpecifiedRemaining).toInt128() ); } } } /// @notice Donates the given amount of currency0 and currency1 to the pool - function donate( - State storage state, - uint256 amount0, - uint256 amount1 - ) internal returns (BalanceDelta delta) { + function donate(State storage state, uint256 amount0, uint256 amount1) internal returns (BalanceDelta delta) { if (state.liquidity == 0) revert NoLiquidityToReceiveFees(); delta = toBalanceDelta(-(amount0.toInt128()), -(amount1.toInt128())); unchecked { if (amount0 > 0) { - state.feeGrowthGlobal0X128 += FullMath.mulDiv( - amount0, - FixedPoint128.Q128, - state.liquidity - ); + state.feeGrowthGlobal0X128 += FullMath.mulDiv(amount0, FixedPoint128.Q128, state.liquidity); } if (amount1 > 0) { - state.feeGrowthGlobal1X128 += FullMath.mulDiv( - amount1, - FixedPoint128.Q128, - state.liquidity - ); + state.feeGrowthGlobal1X128 += FullMath.mulDiv(amount1, FixedPoint128.Q128, state.liquidity); } } } @@ -600,11 +492,7 @@ library Pool { /// @param tickUpper The upper tick boundary of the position /// @return feeGrowthInside0X128 The all-time fee growth in token0, per unit of liquidity, inside the position's tick boundaries /// @return feeGrowthInside1X128 The all-time fee growth in token1, per unit of liquidity, inside the position's tick boundaries - function getFeeGrowthInside( - State storage self, - int24 tickLower, - int24 tickUpper - ) + function getFeeGrowthInside(State storage self, int24 tickLower, int24 tickUpper) internal view returns (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) @@ -615,28 +503,16 @@ library Pool { unchecked { if (tickCurrent < tickLower) { - feeGrowthInside0X128 = - lower.feeGrowthOutside0X128 - - upper.feeGrowthOutside0X128; - feeGrowthInside1X128 = - lower.feeGrowthOutside1X128 - - upper.feeGrowthOutside1X128; + feeGrowthInside0X128 = lower.feeGrowthOutside0X128 - upper.feeGrowthOutside0X128; + feeGrowthInside1X128 = lower.feeGrowthOutside1X128 - upper.feeGrowthOutside1X128; } else if (tickCurrent >= tickUpper) { - feeGrowthInside0X128 = - upper.feeGrowthOutside0X128 - - lower.feeGrowthOutside0X128; - feeGrowthInside1X128 = - upper.feeGrowthOutside1X128 - - lower.feeGrowthOutside1X128; + feeGrowthInside0X128 = upper.feeGrowthOutside0X128 - lower.feeGrowthOutside0X128; + feeGrowthInside1X128 = upper.feeGrowthOutside1X128 - lower.feeGrowthOutside1X128; } else { feeGrowthInside0X128 = - self.feeGrowthGlobal0X128 - - lower.feeGrowthOutside0X128 - - upper.feeGrowthOutside0X128; + self.feeGrowthGlobal0X128 - lower.feeGrowthOutside0X128 - upper.feeGrowthOutside0X128; feeGrowthInside1X128 = - self.feeGrowthGlobal1X128 - - lower.feeGrowthOutside1X128 - - upper.feeGrowthOutside1X128; + self.feeGrowthGlobal1X128 - lower.feeGrowthOutside1X128 - upper.feeGrowthOutside1X128; } } } @@ -648,12 +524,10 @@ library Pool { /// @param upper true for updating a position's upper tick, or false for updating a position's lower tick /// @return flipped Whether the tick was flipped from initialized to uninitialized, or vice versa /// @return liquidityGrossAfter The total amount of liquidity for all positions that references the tick after the update - function updateTick( - State storage self, - int24 tick, - int128 liquidityDelta, - bool upper - ) internal returns (bool flipped, uint128 liquidityGrossAfter) { + function updateTick(State storage self, int24 tick, int128 liquidityDelta, bool upper) + internal + returns (bool flipped, uint128 liquidityGrossAfter) + { TickInfo storage info = self.ticks[tick]; uint128 liquidityGrossBefore; @@ -668,10 +542,7 @@ library Pool { liquidityNetBefore := shr(128, liquidity) } - liquidityGrossAfter = LiquidityMath.addDelta( - liquidityGrossBefore, - liquidityDelta - ); + liquidityGrossAfter = LiquidityMath.addDelta(liquidityGrossBefore, liquidityDelta); flipped = (liquidityGrossAfter == 0) != (liquidityGrossBefore == 0); @@ -684,9 +555,7 @@ library Pool { } // when the lower (upper) tick is crossed left to right (right to left), liquidity must be added (removed) - int128 liquidityNet = upper - ? liquidityNetBefore - liquidityDelta - : liquidityNetBefore + liquidityDelta; + int128 liquidityNet = upper ? liquidityNetBefore - liquidityDelta : liquidityNetBefore + liquidityDelta; assembly { // liquidityGrossAfter and liquidityNet are packed in the first slot of `info` // So we can store them with a single sstore by packing them ourselves first @@ -708,15 +577,12 @@ library Pool { /// @param tickSpacing The amount of required tick separation, realized in multiples of `tickSpacing` /// e.g., a tickSpacing of 3 requires ticks to be initialized every 3rd tick i.e., ..., -6, -3, 0, 3, 6, ... /// @return The max liquidity per tick - function tickSpacingToMaxLiquidityPerTick( - int24 tickSpacing - ) internal pure returns (uint128) { + function tickSpacingToMaxLiquidityPerTick(int24 tickSpacing) internal pure returns (uint128) { unchecked { - return - uint128( - (type(uint128).max * uint256(int256(tickSpacing))) / - uint256(int256(TickMath.MAX_TICK * 2 + tickSpacing)) - ); + return uint128( + (type(uint128).max * uint256(int256(tickSpacing))) + / uint256(int256(TickMath.MAX_TICK * 2 + tickSpacing)) + ); } } @@ -737,20 +603,14 @@ library Pool { /// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0 /// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1 /// @return liquidityNet The amount of liquidity added (subtracted) when tick is crossed from left to right (right to left) - function crossTick( - State storage self, - int24 tick, - uint256 feeGrowthGlobal0X128, - uint256 feeGrowthGlobal1X128 - ) internal returns (int128 liquidityNet) { + function crossTick(State storage self, int24 tick, uint256 feeGrowthGlobal0X128, uint256 feeGrowthGlobal1X128) + internal + returns (int128 liquidityNet) + { unchecked { TickInfo storage info = self.ticks[tick]; - info.feeGrowthOutside0X128 = - feeGrowthGlobal0X128 - - info.feeGrowthOutside0X128; - info.feeGrowthOutside1X128 = - feeGrowthGlobal1X128 - - info.feeGrowthOutside1X128; + info.feeGrowthOutside0X128 = feeGrowthGlobal0X128 - info.feeGrowthOutside0X128; + info.feeGrowthOutside1X128 = feeGrowthGlobal1X128 - info.feeGrowthOutside1X128; liquidityNet = info.liquidityNet; } } diff --git a/src/types/Slot0.sol b/src/types/Slot0.sol index 8d3404fee..7ace0c38e 100644 --- a/src/types/Slot0.sol +++ b/src/types/Slot0.sol @@ -28,9 +28,7 @@ library Slot0Library { } } - function loadPacked( - Slot0 storage pointer - ) internal view returns (Slot0Packed _packed) { + function loadPacked(Slot0 storage pointer) internal view returns (Slot0Packed _packed) { /// @solidity memory-safe-assembly assembly { _packed := sload(pointer.slot) @@ -39,8 +37,7 @@ library Slot0Library { } library Slot0PackedLibrary { - uint256 private constant UINT160_MASK = - 0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; + uint256 private constant UINT160_MASK = 0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; uint256 private constant INT24_MASK = 0xFFFFFF; uint256 private constant UINT24_MASK = 0xFFFFFF; @@ -49,9 +46,7 @@ library Slot0PackedLibrary { uint256 private constant LP_FEE_OFFSET = 208; // #### GETTERS #### - function sqrtPriceX96( - Slot0Packed _packed - ) internal pure returns (uint160 _sqrtPriceX96) { + function sqrtPriceX96(Slot0Packed _packed) internal pure returns (uint160 _sqrtPriceX96) { /// @solidity memory-safe-assembly assembly { _sqrtPriceX96 := and(UINT160_MASK, _packed) @@ -65,9 +60,7 @@ library Slot0PackedLibrary { } } - function protocolFee( - Slot0Packed _packed - ) internal pure returns (uint24 _protocolFee) { + function protocolFee(Slot0Packed _packed) internal pure returns (uint24 _protocolFee) { /// @solidity memory-safe-assembly assembly { _protocolFee := and(UINT24_MASK, shr(PROTOCOL_FEE_OFFSET, _packed)) @@ -82,55 +75,36 @@ library Slot0PackedLibrary { } // #### SETTERS #### - function setSqrtPriceX96( - Slot0Packed _packed, - uint160 _sqrtPriceX96 - ) internal pure returns (Slot0Packed _result) { + function setSqrtPriceX96(Slot0Packed _packed, uint160 _sqrtPriceX96) internal pure returns (Slot0Packed _result) { /// @solidity memory-safe-assembly assembly { - _result := or( - and(not(UINT160_MASK), _packed), - and(UINT160_MASK, _sqrtPriceX96) - ) + _result := or(and(not(UINT160_MASK), _packed), and(UINT160_MASK, _sqrtPriceX96)) } } - function setTick( - Slot0Packed _packed, - int24 _tick - ) internal pure returns (Slot0Packed _result) { + function setTick(Slot0Packed _packed, int24 _tick) internal pure returns (Slot0Packed _result) { /// @solidity memory-safe-assembly assembly { - _result := or( - and(not(shl(TICK_OFFSET, INT24_MASK)), _packed), - shl(TICK_OFFSET, and(INT24_MASK, _tick)) - ) + _result := or(and(not(shl(TICK_OFFSET, INT24_MASK)), _packed), shl(TICK_OFFSET, and(INT24_MASK, _tick))) } } - function setProtocolFee( - Slot0Packed _packed, - uint24 _protocolFee - ) internal pure returns (Slot0Packed _result) { + function setProtocolFee(Slot0Packed _packed, uint24 _protocolFee) internal pure returns (Slot0Packed _result) { /// @solidity memory-safe-assembly assembly { - _result := or( - and(not(shl(PROTOCOL_FEE_OFFSET, UINT24_MASK)), _packed), - shl(PROTOCOL_FEE_OFFSET, and(UINT24_MASK, _protocolFee)) - ) + _result := + or( + and(not(shl(PROTOCOL_FEE_OFFSET, UINT24_MASK)), _packed), + shl(PROTOCOL_FEE_OFFSET, and(UINT24_MASK, _protocolFee)) + ) } } - function setLpFee( - Slot0Packed _packed, - uint24 _lpFee - ) internal pure returns (Slot0Packed _result) { + function setLpFee(Slot0Packed _packed, uint24 _lpFee) internal pure returns (Slot0Packed _result) { /// @solidity memory-safe-assembly assembly { - _result := or( - and(not(shl(LP_FEE_OFFSET, UINT24_MASK)), _packed), - shl(LP_FEE_OFFSET, and(UINT24_MASK, _lpFee)) - ) + _result := + or(and(not(shl(LP_FEE_OFFSET, UINT24_MASK)), _packed), shl(LP_FEE_OFFSET, and(UINT24_MASK, _lpFee))) } } } diff --git a/test/libraries/Pool.t.sol b/test/libraries/Pool.t.sol index 5def4305d..511ae82c2 100644 --- a/test/libraries/Pool.t.sol +++ b/test/libraries/Pool.t.sol @@ -23,25 +23,15 @@ contract PoolTest is Test { uint24 constant MAX_PROTOCOL_FEE = ProtocolFeeLibrary.MAX_PROTOCOL_FEE; // 0.1% uint24 constant MAX_LP_FEE = LPFeeLibrary.MAX_LP_FEE; // 100% - function testPoolInitialize( - uint160 sqrtPriceX96, - uint24 protocolFee, - uint24 dynamicFee - ) public { - if ( - sqrtPriceX96 < TickMath.MIN_SQRT_RATIO || - sqrtPriceX96 >= TickMath.MAX_SQRT_RATIO - ) { + function testPoolInitialize(uint160 sqrtPriceX96, uint24 protocolFee, uint24 dynamicFee) public { + if (sqrtPriceX96 < TickMath.MIN_SQRT_RATIO || sqrtPriceX96 >= TickMath.MAX_SQRT_RATIO) { vm.expectRevert(TickMath.InvalidSqrtRatio.selector); state.initialize(sqrtPriceX96, protocolFee, dynamicFee); } else { state.initialize(sqrtPriceX96, protocolFee, dynamicFee); assertEq(state.slot0.sqrtPriceX96, sqrtPriceX96); assertEq(state.slot0.protocolFee, protocolFee); - assertEq( - state.slot0.tick, - TickMath.getTickAtSqrtRatio(sqrtPriceX96) - ); + assertEq(state.slot0.tick, TickMath.getTickAtSqrtRatio(sqrtPriceX96)); assertLt(state.slot0.tick, TickMath.MAX_TICK); assertGt(state.slot0.tick, TickMath.MIN_TICK - 1); } @@ -54,88 +44,42 @@ contract PoolTest is Test { Pool.ModifyLiquidityParams memory params ) public { // Assumptions tested in PoolManager.t.sol - params.tickSpacing = int24( - bound( - params.tickSpacing, - TickMath.MIN_TICK_SPACING, - TickMath.MAX_TICK_SPACING - ) - ); + params.tickSpacing = int24(bound(params.tickSpacing, TickMath.MIN_TICK_SPACING, TickMath.MAX_TICK_SPACING)); testPoolInitialize(sqrtPriceX96, protocolFee, lpFee); if (params.tickLower >= params.tickUpper) { - vm.expectRevert( - abi.encodeWithSelector( - Pool.TicksMisordered.selector, - params.tickLower, - params.tickUpper - ) - ); + vm.expectRevert(abi.encodeWithSelector(Pool.TicksMisordered.selector, params.tickLower, params.tickUpper)); } else if (params.tickLower < TickMath.MIN_TICK) { - vm.expectRevert( - abi.encodeWithSelector( - Pool.TickLowerOutOfBounds.selector, - params.tickLower - ) - ); + vm.expectRevert(abi.encodeWithSelector(Pool.TickLowerOutOfBounds.selector, params.tickLower)); } else if (params.tickUpper > TickMath.MAX_TICK) { - vm.expectRevert( - abi.encodeWithSelector( - Pool.TickUpperOutOfBounds.selector, - params.tickUpper - ) - ); + vm.expectRevert(abi.encodeWithSelector(Pool.TickUpperOutOfBounds.selector, params.tickUpper)); } else if (params.liquidityDelta < 0) { vm.expectRevert(SafeCast.SafeCastOverflow.selector); } else if (params.liquidityDelta == 0) { vm.expectRevert(Position.CannotUpdateEmptyPosition.selector); - } else if ( - params.liquidityDelta > - int128(Pool.tickSpacingToMaxLiquidityPerTick(params.tickSpacing)) - ) { - vm.expectRevert( - abi.encodeWithSelector( - Pool.TickLiquidityOverflow.selector, - params.tickLower - ) - ); + } else if (params.liquidityDelta > int128(Pool.tickSpacingToMaxLiquidityPerTick(params.tickSpacing))) { + vm.expectRevert(abi.encodeWithSelector(Pool.TickLiquidityOverflow.selector, params.tickLower)); } else if (params.tickLower % params.tickSpacing != 0) { vm.expectRevert( - abi.encodeWithSelector( - TickBitmap.TickMisaligned.selector, - params.tickLower, - params.tickSpacing - ) + abi.encodeWithSelector(TickBitmap.TickMisaligned.selector, params.tickLower, params.tickSpacing) ); } else if (params.tickUpper % params.tickSpacing != 0) { vm.expectRevert( - abi.encodeWithSelector( - TickBitmap.TickMisaligned.selector, - params.tickUpper, - params.tickSpacing - ) + abi.encodeWithSelector(TickBitmap.TickMisaligned.selector, params.tickUpper, params.tickSpacing) ); } else { // We need the assumptions above to calculate this - uint256 maxInt128InTypeU256 = uint256( - uint128(Constants.MAX_UINT128) + uint256 maxInt128InTypeU256 = uint256(uint128(Constants.MAX_UINT128)); + (uint256 amount0, uint256 amount1) = LiquidityAmounts.getAmountsForLiquidity( + sqrtPriceX96, + TickMath.getSqrtRatioAtTick(params.tickLower), + TickMath.getSqrtRatioAtTick(params.tickUpper), + uint128(params.liquidityDelta) ); - (uint256 amount0, uint256 amount1) = LiquidityAmounts - .getAmountsForLiquidity( - sqrtPriceX96, - TickMath.getSqrtRatioAtTick(params.tickLower), - TickMath.getSqrtRatioAtTick(params.tickUpper), - uint128(params.liquidityDelta) - ); - if ( - (amount0 > maxInt128InTypeU256) || - (amount1 > maxInt128InTypeU256) - ) { - vm.expectRevert( - abi.encodeWithSelector(SafeCast.SafeCastOverflow.selector) - ); + if ((amount0 > maxInt128InTypeU256) || (amount1 > maxInt128InTypeU256)) { + vm.expectRevert(abi.encodeWithSelector(SafeCast.SafeCastOverflow.selector)); } } @@ -151,13 +95,7 @@ contract PoolTest is Test { Pool.SwapParams memory params ) public { // Assumptions tested in PoolManager.t.sol - params.tickSpacing = int24( - bound( - params.tickSpacing, - TickMath.MIN_TICK_SPACING, - TickMath.MAX_TICK_SPACING - ) - ); + params.tickSpacing = int24(bound(params.tickSpacing, TickMath.MIN_TICK_SPACING, TickMath.MAX_TICK_SPACING)); lpFee = uint24(bound(lpFee, 0, MAX_LP_FEE)); protocolFee0 = uint16(bound(protocolFee0, 0, MAX_PROTOCOL_FEE)); protocolFee1 = uint16(bound(protocolFee1, 0, MAX_PROTOCOL_FEE)); @@ -184,35 +122,21 @@ contract PoolTest is Test { if (params.sqrtPriceLimitX96 >= slot0.sqrtPriceX96) { vm.expectRevert( abi.encodeWithSelector( - Pool.PriceLimitAlreadyExceeded.selector, - slot0.sqrtPriceX96, - params.sqrtPriceLimitX96 + Pool.PriceLimitAlreadyExceeded.selector, slot0.sqrtPriceX96, params.sqrtPriceLimitX96 ) ); } else if (params.sqrtPriceLimitX96 <= TickMath.MIN_SQRT_RATIO) { - vm.expectRevert( - abi.encodeWithSelector( - Pool.PriceLimitOutOfBounds.selector, - params.sqrtPriceLimitX96 - ) - ); + vm.expectRevert(abi.encodeWithSelector(Pool.PriceLimitOutOfBounds.selector, params.sqrtPriceLimitX96)); } } else if (!params.zeroForOne) { if (params.sqrtPriceLimitX96 <= slot0.sqrtPriceX96) { vm.expectRevert( abi.encodeWithSelector( - Pool.PriceLimitAlreadyExceeded.selector, - slot0.sqrtPriceX96, - params.sqrtPriceLimitX96 + Pool.PriceLimitAlreadyExceeded.selector, slot0.sqrtPriceX96, params.sqrtPriceLimitX96 ) ); } else if (params.sqrtPriceLimitX96 >= TickMath.MAX_SQRT_RATIO) { - vm.expectRevert( - abi.encodeWithSelector( - Pool.PriceLimitOutOfBounds.selector, - params.sqrtPriceLimitX96 - ) - ); + vm.expectRevert(abi.encodeWithSelector(Pool.PriceLimitOutOfBounds.selector, params.sqrtPriceLimitX96)); } } else if (params.amountSpecified > 0) { if (lpFee == MAX_LP_FEE) {