diff --git a/.forge-snapshots/add liquidity to already existing position with salt.snap b/.forge-snapshots/add liquidity to already existing position with salt.snap index 0cc62a123..7a1586d38 100644 --- a/.forge-snapshots/add liquidity to already existing position with salt.snap +++ b/.forge-snapshots/add liquidity to already existing position with salt.snap @@ -1 +1 @@ -144627 \ No newline at end of file +144651 \ No newline at end of file diff --git a/.forge-snapshots/addLiquidity CA fee.snap b/.forge-snapshots/addLiquidity CA fee.snap index 51059b6ad..25b3f6d86 100644 --- a/.forge-snapshots/addLiquidity CA fee.snap +++ b/.forge-snapshots/addLiquidity CA fee.snap @@ -1 +1 @@ -170916 \ No newline at end of file +170947 \ No newline at end of file diff --git a/.forge-snapshots/addLiquidity with empty hook.snap b/.forge-snapshots/addLiquidity with empty hook.snap index 30bdfbe46..a5f14cb49 100644 --- a/.forge-snapshots/addLiquidity with empty hook.snap +++ b/.forge-snapshots/addLiquidity with empty hook.snap @@ -1 +1 @@ -274194 \ No newline at end of file +274218 \ No newline at end of file diff --git a/.forge-snapshots/addLiquidity with native token.snap b/.forge-snapshots/addLiquidity with native token.snap index cd7fd0616..a7684fdf5 100644 --- a/.forge-snapshots/addLiquidity with native token.snap +++ b/.forge-snapshots/addLiquidity with native token.snap @@ -1 +1 @@ -135120 \ No newline at end of file +135141 \ No newline at end of file diff --git a/.forge-snapshots/create new liquidity to a position with salt.snap b/.forge-snapshots/create new liquidity to a position with salt.snap index 34d77f4b0..cfd48079e 100644 --- a/.forge-snapshots/create new liquidity to a position with salt.snap +++ b/.forge-snapshots/create new liquidity to a position with salt.snap @@ -1 +1 @@ -292819 \ No newline at end of file +292843 \ No newline at end of file diff --git a/.forge-snapshots/donate gas with 1 token.snap b/.forge-snapshots/donate gas with 1 token.snap index a54ba22c4..f2a99d3bb 100644 --- a/.forge-snapshots/donate gas with 1 token.snap +++ b/.forge-snapshots/donate gas with 1 token.snap @@ -1 +1 @@ -106321 \ No newline at end of file +106333 \ No newline at end of file diff --git a/.forge-snapshots/donate gas with 2 tokens.snap b/.forge-snapshots/donate gas with 2 tokens.snap index 60eb0640f..81e2b0d13 100644 --- a/.forge-snapshots/donate gas with 2 tokens.snap +++ b/.forge-snapshots/donate gas with 2 tokens.snap @@ -1 +1 @@ -145736 \ No newline at end of file +145754 \ No newline at end of file diff --git a/.forge-snapshots/erc20 collect protocol fees.snap b/.forge-snapshots/erc20 collect protocol fees.snap index 2a8ab2b2d..bb8641235 100644 --- a/.forge-snapshots/erc20 collect protocol fees.snap +++ b/.forge-snapshots/erc20 collect protocol fees.snap @@ -1 +1 @@ -57439 \ No newline at end of file +57442 \ No newline at end of file diff --git a/.forge-snapshots/extsload getFeeGrowthGlobals.snap b/.forge-snapshots/extsload getFeeGrowthGlobals.snap index fc0b67d07..5c1d4541a 100644 --- a/.forge-snapshots/extsload getFeeGrowthGlobals.snap +++ b/.forge-snapshots/extsload getFeeGrowthGlobals.snap @@ -1 +1 @@ -774 \ No newline at end of file +777 \ No newline at end of file diff --git a/.forge-snapshots/extsload getPositionInfo.snap b/.forge-snapshots/extsload getPositionInfo.snap index 397787a80..9f02e1c9e 100644 --- a/.forge-snapshots/extsload getPositionInfo.snap +++ b/.forge-snapshots/extsload getPositionInfo.snap @@ -1 +1 @@ -949 \ No newline at end of file +952 \ No newline at end of file diff --git a/.forge-snapshots/extsload getTickFeeGrowthOutside.snap b/.forge-snapshots/extsload getTickFeeGrowthOutside.snap index fc0b67d07..5c1d4541a 100644 --- a/.forge-snapshots/extsload getTickFeeGrowthOutside.snap +++ b/.forge-snapshots/extsload getTickFeeGrowthOutside.snap @@ -1 +1 @@ -774 \ No newline at end of file +777 \ No newline at end of file diff --git a/.forge-snapshots/extsload getTickInfo.snap b/.forge-snapshots/extsload getTickInfo.snap index 397787a80..9f02e1c9e 100644 --- a/.forge-snapshots/extsload getTickInfo.snap +++ b/.forge-snapshots/extsload getTickInfo.snap @@ -1 +1 @@ -949 \ No newline at end of file +952 \ No newline at end of file diff --git a/.forge-snapshots/initialize.snap b/.forge-snapshots/initialize.snap index 236da0ded..c9e91716c 100644 --- a/.forge-snapshots/initialize.snap +++ b/.forge-snapshots/initialize.snap @@ -1 +1 @@ -60021 \ No newline at end of file +60025 \ No newline at end of file diff --git a/.forge-snapshots/native collect protocol fees.snap b/.forge-snapshots/native collect protocol fees.snap index a9aafc5e5..1c6c78447 100644 --- a/.forge-snapshots/native collect protocol fees.snap +++ b/.forge-snapshots/native collect protocol fees.snap @@ -1 +1 @@ -59723 \ No newline at end of file +59726 \ No newline at end of file diff --git a/.forge-snapshots/poolManager bytecode size.snap b/.forge-snapshots/poolManager bytecode size.snap index c5436b36d..e085ea34a 100644 --- a/.forge-snapshots/poolManager bytecode size.snap +++ b/.forge-snapshots/poolManager bytecode size.snap @@ -1 +1 @@ -24044 \ No newline at end of file +24073 \ No newline at end of file diff --git a/.forge-snapshots/removeLiquidity CA fee.snap b/.forge-snapshots/removeLiquidity CA fee.snap index 38003fc44..ec2dbf546 100644 --- a/.forge-snapshots/removeLiquidity CA fee.snap +++ b/.forge-snapshots/removeLiquidity CA fee.snap @@ -1 +1 @@ -141204 \ No newline at end of file +141229 \ No newline at end of file diff --git a/.forge-snapshots/removeLiquidity with empty hook.snap b/.forge-snapshots/removeLiquidity with empty hook.snap index d9f5f7111..12528ddad 100644 --- a/.forge-snapshots/removeLiquidity with empty hook.snap +++ b/.forge-snapshots/removeLiquidity with empty hook.snap @@ -1 +1 @@ -130579 \ No newline at end of file +130597 \ No newline at end of file diff --git a/.forge-snapshots/removeLiquidity with native token.snap b/.forge-snapshots/removeLiquidity with native token.snap index 19f882d70..35f7a9f26 100644 --- a/.forge-snapshots/removeLiquidity with native token.snap +++ b/.forge-snapshots/removeLiquidity with native token.snap @@ -1 +1 @@ -112523 \ No newline at end of file +112535 \ No newline at end of file diff --git a/.forge-snapshots/simple addLiquidity second addition same range.snap b/.forge-snapshots/simple addLiquidity second addition same range.snap index 8d0e2523f..d3a590e01 100644 --- a/.forge-snapshots/simple addLiquidity second addition same range.snap +++ b/.forge-snapshots/simple addLiquidity second addition same range.snap @@ -1 +1 @@ -98838 \ No newline at end of file +98850 \ No newline at end of file diff --git a/.forge-snapshots/simple addLiquidity.snap b/.forge-snapshots/simple addLiquidity.snap index 2ac3df3f0..216625993 100644 --- a/.forge-snapshots/simple addLiquidity.snap +++ b/.forge-snapshots/simple addLiquidity.snap @@ -1 +1 @@ -161383 \ No newline at end of file +161395 \ No newline at end of file diff --git a/.forge-snapshots/simple removeLiquidity some liquidity remains.snap b/.forge-snapshots/simple removeLiquidity some liquidity remains.snap index d4e0d16bd..c2a4006d2 100644 --- a/.forge-snapshots/simple removeLiquidity some liquidity remains.snap +++ b/.forge-snapshots/simple removeLiquidity some liquidity remains.snap @@ -1 +1 @@ -92974 \ No newline at end of file +92983 \ No newline at end of file diff --git a/.forge-snapshots/simple removeLiquidity.snap b/.forge-snapshots/simple removeLiquidity.snap index 69b94841f..81dfb1045 100644 --- a/.forge-snapshots/simple removeLiquidity.snap +++ b/.forge-snapshots/simple removeLiquidity.snap @@ -1 +1 @@ -85087 \ No newline at end of file +85096 \ No newline at end of file diff --git a/.forge-snapshots/simple swap with native.snap b/.forge-snapshots/simple swap with native.snap index 46cc1e235..4bf8e4447 100644 --- a/.forge-snapshots/simple swap with native.snap +++ b/.forge-snapshots/simple swap with native.snap @@ -1 +1 @@ -108369 \ No newline at end of file +108515 \ No newline at end of file diff --git a/.forge-snapshots/simple swap.snap b/.forge-snapshots/simple swap.snap index 3e232d361..71f991199 100644 --- a/.forge-snapshots/simple swap.snap +++ b/.forge-snapshots/simple swap.snap @@ -1 +1 @@ -123186 \ No newline at end of file +123335 \ No newline at end of file diff --git a/.forge-snapshots/swap CA custom curve + swap noop.snap b/.forge-snapshots/swap CA custom curve + swap noop.snap index 8dc70d3a8..2bae0f8b5 100644 --- a/.forge-snapshots/swap CA custom curve + swap noop.snap +++ b/.forge-snapshots/swap CA custom curve + swap noop.snap @@ -1 +1 @@ -126821 \ No newline at end of file +124663 \ No newline at end of file diff --git a/.forge-snapshots/swap CA fee on unspecified.snap b/.forge-snapshots/swap CA fee on unspecified.snap index 0650bd07a..0e69c9078 100644 --- a/.forge-snapshots/swap CA fee on unspecified.snap +++ b/.forge-snapshots/swap CA fee on unspecified.snap @@ -1 +1 @@ -154631 \ No newline at end of file +154783 \ No newline at end of file diff --git a/.forge-snapshots/swap against liquidity with native token.snap b/.forge-snapshots/swap against liquidity with native token.snap index 0fb278943..24cd0f5c3 100644 --- a/.forge-snapshots/swap against liquidity with native token.snap +++ b/.forge-snapshots/swap against liquidity with native token.snap @@ -1 +1 @@ -105493 \ No newline at end of file +105637 \ No newline at end of file diff --git a/.forge-snapshots/swap against liquidity.snap b/.forge-snapshots/swap against liquidity.snap index 7a0df15bb..5cf1c8096 100644 --- a/.forge-snapshots/swap against liquidity.snap +++ b/.forge-snapshots/swap against liquidity.snap @@ -1 +1 @@ -116558 \ No newline at end of file +116705 \ No newline at end of file diff --git a/.forge-snapshots/swap burn 6909 for input.snap b/.forge-snapshots/swap burn 6909 for input.snap index 8fcd144f2..9f63f70cf 100644 --- a/.forge-snapshots/swap burn 6909 for input.snap +++ b/.forge-snapshots/swap burn 6909 for input.snap @@ -1 +1 @@ -129116 \ No newline at end of file +129269 \ No newline at end of file diff --git a/.forge-snapshots/swap burn native 6909 for input.snap b/.forge-snapshots/swap burn native 6909 for input.snap index 8b650b781..4b0be0884 100644 --- a/.forge-snapshots/swap burn native 6909 for input.snap +++ b/.forge-snapshots/swap burn native 6909 for input.snap @@ -1 +1 @@ -118569 \ No newline at end of file +118725 \ No newline at end of file diff --git a/.forge-snapshots/swap mint native output as 6909.snap b/.forge-snapshots/swap mint native output as 6909.snap index 41417754a..63a0a75cf 100644 --- a/.forge-snapshots/swap mint native output as 6909.snap +++ b/.forge-snapshots/swap mint native output as 6909.snap @@ -1 +1 @@ -139603 \ No newline at end of file +139747 \ No newline at end of file diff --git a/.forge-snapshots/swap mint output as 6909.snap b/.forge-snapshots/swap mint output as 6909.snap index f1a50bc46..fb2c12b45 100644 --- a/.forge-snapshots/swap mint output as 6909.snap +++ b/.forge-snapshots/swap mint output as 6909.snap @@ -1 +1 @@ -155027 \ No newline at end of file +155176 \ No newline at end of file diff --git a/.forge-snapshots/swap skips hook call if hook is caller.snap b/.forge-snapshots/swap skips hook call if hook is caller.snap index 0ab4deeab..9ffba8461 100644 --- a/.forge-snapshots/swap skips hook call if hook is caller.snap +++ b/.forge-snapshots/swap skips hook call if hook is caller.snap @@ -1 +1 @@ -206132 \ No newline at end of file +206425 \ No newline at end of file diff --git a/.forge-snapshots/swap with dynamic fee.snap b/.forge-snapshots/swap with dynamic fee.snap index 3c8c45efc..7e8319d12 100644 --- a/.forge-snapshots/swap with dynamic fee.snap +++ b/.forge-snapshots/swap with dynamic fee.snap @@ -1 +1 @@ -139207 \ No newline at end of file +139356 \ No newline at end of file diff --git a/.forge-snapshots/swap with hooks.snap b/.forge-snapshots/swap with hooks.snap index 185d91737..a7fd1c54a 100644 --- a/.forge-snapshots/swap with hooks.snap +++ b/.forge-snapshots/swap with hooks.snap @@ -1 +1 @@ -132195 \ No newline at end of file +132343 \ No newline at end of file diff --git a/.forge-snapshots/swap with lp fee and protocol fee.snap b/.forge-snapshots/swap with lp fee and protocol fee.snap index a0a94cd21..8bf4f1a5f 100644 --- a/.forge-snapshots/swap with lp fee and protocol fee.snap +++ b/.forge-snapshots/swap with lp fee and protocol fee.snap @@ -1 +1 @@ -169398 \ No newline at end of file +169603 \ No newline at end of file diff --git a/.forge-snapshots/swap with return dynamic fee.snap b/.forge-snapshots/swap with return dynamic fee.snap index 37efa1363..8e88648ff 100644 --- a/.forge-snapshots/swap with return dynamic fee.snap +++ b/.forge-snapshots/swap with return dynamic fee.snap @@ -1 +1 @@ -145534 \ No newline at end of file +145683 \ No newline at end of file diff --git a/.forge-snapshots/update dynamic fee in before swap.snap b/.forge-snapshots/update dynamic fee in before swap.snap index 4394bf909..0422eb27f 100644 --- a/.forge-snapshots/update dynamic fee in before swap.snap +++ b/.forge-snapshots/update dynamic fee in before swap.snap @@ -1 +1 @@ -147814 \ No newline at end of file +147966 \ No newline at end of file diff --git a/src/PoolManager.sol b/src/PoolManager.sol index 3fb55aaff..f366d33aa 100644 --- a/src/PoolManager.sol +++ b/src/PoolManager.sol @@ -150,25 +150,27 @@ contract PoolManager is IPoolManager, ProtocolFees, NoDelegateCall, ERC6909Claim bytes calldata hookData ) external onlyWhenUnlocked noDelegateCall returns (BalanceDelta callerDelta, BalanceDelta feesAccrued) { PoolId id = key.toId(); - Pool.State storage pool = _getPool(id); - pool.checkPoolInitialized(); - - key.hooks.beforeModifyLiquidity(key, params, hookData); - - BalanceDelta principalDelta; - (principalDelta, feesAccrued) = pool.modifyLiquidity( - Pool.ModifyLiquidityParams({ - owner: msg.sender, - tickLower: params.tickLower, - tickUpper: params.tickUpper, - liquidityDelta: params.liquidityDelta.toInt128(), - tickSpacing: key.tickSpacing, - salt: params.salt - }) - ); + { + Pool.State storage pool = _getPool(id); + pool.checkPoolInitialized(); + + key.hooks.beforeModifyLiquidity(key, params, hookData); + + BalanceDelta principalDelta; + (principalDelta, feesAccrued) = pool.modifyLiquidity( + Pool.ModifyLiquidityParams({ + owner: msg.sender, + tickLower: params.tickLower, + tickUpper: params.tickUpper, + liquidityDelta: params.liquidityDelta.toInt128(), + tickSpacing: key.tickSpacing, + salt: params.salt + }) + ); - // fee delta and principal delta are both accrued to the caller - callerDelta = principalDelta + feesAccrued; + // fee delta and principal delta are both accrued to the caller + callerDelta = principalDelta + feesAccrued; + } // event is emitted before the afterModifyLiquidity call to ensure events are always emitted in order emit ModifyLiquidity(id, msg.sender, params.tickLower, params.tickUpper, params.liquidityDelta, params.salt); diff --git a/src/libraries/Pool.sol b/src/libraries/Pool.sol index 4a1e93d28..0f64f5e00 100644 --- a/src/libraries/Pool.sol +++ b/src/libraries/Pool.sol @@ -262,12 +262,14 @@ library Pool { uint256 amountOut; // how much fee is being paid in uint256 feeAmount; + // the global fee growth of the input token. updated in storage at the end of swap + uint256 feeGrowthGlobalX128; } struct SwapParams { + int256 amountSpecified; int24 tickSpacing; bool zeroForOne; - int256 amountSpecified; uint160 sqrtPriceLimitX96; uint24 lpFeeOverride; } @@ -281,7 +283,6 @@ library Pool { Slot0 slot0Start = self.slot0; bool zeroForOne = params.zeroForOne; - uint128 liquidityStart = self.liquidity; uint256 protocolFee = zeroForOne ? slot0Start.protocolFee().getZeroForOneFee() : slot0Start.protocolFee().getOneForZeroFee(); @@ -289,14 +290,12 @@ library Pool { int256 amountSpecifiedRemaining = params.amountSpecified; // the amount swapped out/in of the output/input asset. initially set to 0 int256 amountCalculated = 0; - // the global fee growth of the input token. updated in storage at the end of swap - uint256 feeGrowthGlobalX128 = zeroForOne ? self.feeGrowthGlobal0X128 : self.feeGrowthGlobal1X128; // initialize to the current sqrt(price) result.sqrtPriceX96 = slot0Start.sqrtPriceX96(); // initialize to the current tick result.tick = slot0Start.tick(); // initialize to the current liquidity - result.liquidity = liquidityStart; + result.liquidity = self.liquidity; // if the beforeSwap hook returned a valid fee override, use that as the LP fee, otherwise load from storage // lpFee, swapFee, and protocolFee are all in pips @@ -308,11 +307,10 @@ library Pool { swapFee = protocolFee == 0 ? lpFee : uint16(protocolFee).calculateSwapFee(lpFee); } - bool exactInput = params.amountSpecified < 0; - // a swap fee totaling MAX_SWAP_FEE (100%) makes exact output swaps impossible since the input is entirely consumed by the fee if (swapFee >= SwapMath.MAX_SWAP_FEE) { - if (!exactInput) { + // if exactOutput + if (params.amountSpecified > 0) { InvalidFeeForExactOut.selector.revertWith(); } } @@ -340,6 +338,7 @@ library Pool { } StepComputations memory step; + step.feeGrowthGlobalX128 = zeroForOne ? self.feeGrowthGlobal0X128 : self.feeGrowthGlobal1X128; // continue swapping as long as we haven't used the entire input/output and haven't reached the price limit while (!(amountSpecifiedRemaining == 0 || result.sqrtPriceX96 == params.sqrtPriceLimitX96)) { @@ -368,7 +367,8 @@ library Pool { swapFee ); - if (!exactInput) { + // if exactOutput + if (params.amountSpecified > 0) { unchecked { amountSpecifiedRemaining -= step.amountOut.toInt256(); } @@ -398,7 +398,8 @@ library Pool { if (result.liquidity > 0) { unchecked { // FullMath.mulDiv isn't needed as the numerator can't overflow uint256 since tokens have a max supply of type(uint128).max - feeGrowthGlobalX128 += UnsafeMath.simpleMulDiv(step.feeAmount, FixedPoint128.Q128, result.liquidity); + step.feeGrowthGlobalX128 += + UnsafeMath.simpleMulDiv(step.feeAmount, FixedPoint128.Q128, result.liquidity); } } @@ -410,8 +411,8 @@ library Pool { // if the tick is initialized, run the tick transition if (step.initialized) { (uint256 feeGrowthGlobal0X128, uint256 feeGrowthGlobal1X128) = zeroForOne - ? (feeGrowthGlobalX128, self.feeGrowthGlobal1X128) - : (self.feeGrowthGlobal0X128, feeGrowthGlobalX128); + ? (step.feeGrowthGlobalX128, self.feeGrowthGlobal1X128) + : (self.feeGrowthGlobal0X128, step.feeGrowthGlobalX128); int128 liquidityNet = Pool.crossTick(self, step.tickNext, feeGrowthGlobal0X128, feeGrowthGlobal1X128); // if we're moving leftward, we interpret liquidityNet as the opposite sign @@ -435,17 +436,18 @@ library Pool { self.slot0 = slot0Start.setTick(result.tick).setSqrtPriceX96(result.sqrtPriceX96); // update liquidity if it changed - if (liquidityStart != result.liquidity) self.liquidity = result.liquidity; + if (self.liquidity != result.liquidity) self.liquidity = result.liquidity; // update fee growth global if (!zeroForOne) { - self.feeGrowthGlobal1X128 = feeGrowthGlobalX128; + self.feeGrowthGlobal1X128 = step.feeGrowthGlobalX128; } else { - self.feeGrowthGlobal0X128 = feeGrowthGlobalX128; + self.feeGrowthGlobal0X128 = step.feeGrowthGlobalX128; } unchecked { - if (zeroForOne != exactInput) { + // "if currency1 is specified" + if (zeroForOne != (params.amountSpecified < 0)) { swapDelta = toBalanceDelta( amountCalculated.toInt128(), (params.amountSpecified - amountSpecifiedRemaining).toInt128() );