diff --git a/.forge-snapshots/BinHookTest#testInitializeSucceedsWithHook.snap b/.forge-snapshots/BinHookTest#testInitializeSucceedsWithHook.snap index b268fe15..325864e7 100644 --- a/.forge-snapshots/BinHookTest#testInitializeSucceedsWithHook.snap +++ b/.forge-snapshots/BinHookTest#testInitializeSucceedsWithHook.snap @@ -1 +1 @@ -108918 \ No newline at end of file +108917 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testFuzzUpdateDynamicSwapFee.snap b/.forge-snapshots/BinPoolManagerTest#testFuzzUpdateDynamicSwapFee.snap index d8cd0f7f..20ce20bd 100644 --- a/.forge-snapshots/BinPoolManagerTest#testFuzzUpdateDynamicSwapFee.snap +++ b/.forge-snapshots/BinPoolManagerTest#testFuzzUpdateDynamicSwapFee.snap @@ -1 +1 @@ -5977 \ No newline at end of file +5118 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testNoOpGas_Initialize.snap b/.forge-snapshots/BinPoolManagerTest#testNoOpGas_Initialize.snap index 107c8224..a238b0f8 100644 --- a/.forge-snapshots/BinPoolManagerTest#testNoOpGas_Initialize.snap +++ b/.forge-snapshots/BinPoolManagerTest#testNoOpGas_Initialize.snap @@ -1 +1 @@ -37118 \ No newline at end of file +37117 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#addLiquidity_fromEmpty.snap b/.forge-snapshots/CLPoolManagerTest#addLiquidity_fromEmpty.snap index d16c4297..579fbe3b 100644 --- a/.forge-snapshots/CLPoolManagerTest#addLiquidity_fromEmpty.snap +++ b/.forge-snapshots/CLPoolManagerTest#addLiquidity_fromEmpty.snap @@ -1 +1 @@ -348803 \ No newline at end of file +348804 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#addLiquidity_fromNonEmpty.snap b/.forge-snapshots/CLPoolManagerTest#addLiquidity_fromNonEmpty.snap index 1416f250..86e18869 100644 --- a/.forge-snapshots/CLPoolManagerTest#addLiquidity_fromNonEmpty.snap +++ b/.forge-snapshots/CLPoolManagerTest#addLiquidity_fromNonEmpty.snap @@ -1 +1 @@ -59351 \ No newline at end of file +59352 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#addLiquidity_nativeToken.snap b/.forge-snapshots/CLPoolManagerTest#addLiquidity_nativeToken.snap index b3698c39..628d837b 100644 --- a/.forge-snapshots/CLPoolManagerTest#addLiquidity_nativeToken.snap +++ b/.forge-snapshots/CLPoolManagerTest#addLiquidity_nativeToken.snap @@ -1 +1 @@ -242396 \ No newline at end of file +242397 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#donateBothTokens.snap b/.forge-snapshots/CLPoolManagerTest#donateBothTokens.snap index e26f703b..26a89bcd 100644 --- a/.forge-snapshots/CLPoolManagerTest#donateBothTokens.snap +++ b/.forge-snapshots/CLPoolManagerTest#donateBothTokens.snap @@ -1 +1 @@ -82513 \ No newline at end of file +82470 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#gasDonateOneToken.snap b/.forge-snapshots/CLPoolManagerTest#gasDonateOneToken.snap index 07314794..eaa9ffe1 100644 --- a/.forge-snapshots/CLPoolManagerTest#gasDonateOneToken.snap +++ b/.forge-snapshots/CLPoolManagerTest#gasDonateOneToken.snap @@ -1 +1 @@ -52475 \ No newline at end of file +52432 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#removeLiquidity_toNonEmpty.snap b/.forge-snapshots/CLPoolManagerTest#removeLiquidity_toNonEmpty.snap index 144687f1..a84eee00 100644 --- a/.forge-snapshots/CLPoolManagerTest#removeLiquidity_toNonEmpty.snap +++ b/.forge-snapshots/CLPoolManagerTest#removeLiquidity_toNonEmpty.snap @@ -1 +1 @@ -42329 \ No newline at end of file +42330 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#setLmPool.snap b/.forge-snapshots/CLPoolManagerTest#setLmPool.snap index a31e3566..88cf4b10 100644 --- a/.forge-snapshots/CLPoolManagerTest#setLmPool.snap +++ b/.forge-snapshots/CLPoolManagerTest#setLmPool.snap @@ -1 +1 @@ -26295 \ No newline at end of file +26296 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#swap_againstLiquidity.snap b/.forge-snapshots/CLPoolManagerTest#swap_againstLiquidity.snap index 9133969d..c7be6999 100644 --- a/.forge-snapshots/CLPoolManagerTest#swap_againstLiquidity.snap +++ b/.forge-snapshots/CLPoolManagerTest#swap_againstLiquidity.snap @@ -1 +1 @@ -54821 \ No newline at end of file +54822 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#swap_leaveSurplusTokenInVault.snap b/.forge-snapshots/CLPoolManagerTest#swap_leaveSurplusTokenInVault.snap index 107cd3d2..cee2db0b 100644 --- a/.forge-snapshots/CLPoolManagerTest#swap_leaveSurplusTokenInVault.snap +++ b/.forge-snapshots/CLPoolManagerTest#swap_leaveSurplusTokenInVault.snap @@ -1 +1 @@ -102919 \ No newline at end of file +102920 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#swap_runOutOfLiquidity.snap b/.forge-snapshots/CLPoolManagerTest#swap_runOutOfLiquidity.snap index 061bff6f..86c92b12 100644 --- a/.forge-snapshots/CLPoolManagerTest#swap_runOutOfLiquidity.snap +++ b/.forge-snapshots/CLPoolManagerTest#swap_runOutOfLiquidity.snap @@ -1 +1 @@ -25042645 \ No newline at end of file +25042646 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#swap_simple.snap b/.forge-snapshots/CLPoolManagerTest#swap_simple.snap index b1fc476a..26b24138 100644 --- a/.forge-snapshots/CLPoolManagerTest#swap_simple.snap +++ b/.forge-snapshots/CLPoolManagerTest#swap_simple.snap @@ -1 +1 @@ -36021 \ No newline at end of file +36022 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#swap_useSurplusTokenAsInput.snap b/.forge-snapshots/CLPoolManagerTest#swap_useSurplusTokenAsInput.snap index 1f0b6296..bed6fa78 100644 --- a/.forge-snapshots/CLPoolManagerTest#swap_useSurplusTokenAsInput.snap +++ b/.forge-snapshots/CLPoolManagerTest#swap_useSurplusTokenAsInput.snap @@ -1 +1 @@ -101418 \ No newline at end of file +101419 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#swap_withHooks.snap b/.forge-snapshots/CLPoolManagerTest#swap_withHooks.snap index 7d787d98..16ad56ad 100644 --- a/.forge-snapshots/CLPoolManagerTest#swap_withHooks.snap +++ b/.forge-snapshots/CLPoolManagerTest#swap_withHooks.snap @@ -1 +1 @@ -41671 \ No newline at end of file +41672 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#swap_withNative.snap b/.forge-snapshots/CLPoolManagerTest#swap_withNative.snap index 2d8405df..6db723bb 100644 --- a/.forge-snapshots/CLPoolManagerTest#swap_withNative.snap +++ b/.forge-snapshots/CLPoolManagerTest#swap_withNative.snap @@ -1 +1 @@ -36024 \ No newline at end of file +36025 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#testFuzzUpdateDynamicSwapFee.snap b/.forge-snapshots/CLPoolManagerTest#testFuzzUpdateDynamicSwapFee.snap index 59641c57..f8f41d5c 100644 --- a/.forge-snapshots/CLPoolManagerTest#testFuzzUpdateDynamicSwapFee.snap +++ b/.forge-snapshots/CLPoolManagerTest#testFuzzUpdateDynamicSwapFee.snap @@ -1 +1 @@ -5499 \ No newline at end of file +4847 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#testNoOp_gas_Donate.snap b/.forge-snapshots/CLPoolManagerTest#testNoOp_gas_Donate.snap index 3370ca27..23b6156d 100644 --- a/.forge-snapshots/CLPoolManagerTest#testNoOp_gas_Donate.snap +++ b/.forge-snapshots/CLPoolManagerTest#testNoOp_gas_Donate.snap @@ -1 +1 @@ -19217 \ No newline at end of file +19174 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#testNoOp_gas_ModifyPosition.snap b/.forge-snapshots/CLPoolManagerTest#testNoOp_gas_ModifyPosition.snap index 57b636d7..36847e35 100644 --- a/.forge-snapshots/CLPoolManagerTest#testNoOp_gas_ModifyPosition.snap +++ b/.forge-snapshots/CLPoolManagerTest#testNoOp_gas_ModifyPosition.snap @@ -1 +1 @@ -29360 \ No newline at end of file +29361 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#testNoOp_gas_Swap.snap b/.forge-snapshots/CLPoolManagerTest#testNoOp_gas_Swap.snap index 667ca99f..c6b439c0 100644 --- a/.forge-snapshots/CLPoolManagerTest#testNoOp_gas_Swap.snap +++ b/.forge-snapshots/CLPoolManagerTest#testNoOp_gas_Swap.snap @@ -1 +1 @@ -21666 \ No newline at end of file +21667 \ No newline at end of file diff --git a/src/interfaces/IFees.sol b/src/interfaces/IFees.sol index 2fb4d9d1..14078dd6 100644 --- a/src/interfaces/IFees.sol +++ b/src/interfaces/IFees.sol @@ -7,8 +7,6 @@ import {IProtocolFeeController} from "./IProtocolFeeController.sol"; interface IFees { /// @notice Thrown when the protocol fee denominator is less than 4. Also thrown when the static or dynamic fee on a pool exceeds the upper limit. error FeeTooLarge(); - /// @notice Thrown when an attempt to update pool swap fee but the pool does not have dynamic fee. - error FeeNotDynamic(); /// @notice Thrown when not enough gas is provided to look up the protocol fee error ProtocolFeeCannotBeFetched(); /// @notice Thrown when user not authorized to collect protocol fee diff --git a/src/interfaces/IPoolManager.sol b/src/interfaces/IPoolManager.sol index 84aca34a..b01546e5 100644 --- a/src/interfaces/IPoolManager.sol +++ b/src/interfaces/IPoolManager.sol @@ -11,6 +11,10 @@ interface IPoolManager { /// @notice PoolKey must have currencies where address(currency0) < address(currency1) error CurrenciesInitializedOutOfOrder(); + /// @notice Thrown when a call to updateDynamicSwapFee is made by an address that is not the hook, + /// or on a pool that does not have a dynamic swap fee. + error UnauthorizedDynamicSwapFeeUpdate(); + /// @notice Emitted when protocol fee is updated /// @dev The event is emitted even if the updated protocolFee is the same as previous protocolFee event ProtocolFeeUpdated(PoolId indexed id, uint16 protocolFee); @@ -28,5 +32,5 @@ interface IPoolManager { /// 1) when hook#beforeSwap() is called and hook call this function to update the swap fee /// 2) For BinPool only, when hook#beforeMint() is called and hook call this function to update the swap fee /// 3) other use case where the hook might want to on an ad-hoc basis increase/reduce swap fee - function updateDynamicSwapFee(PoolKey memory key) external; + function updateDynamicSwapFee(PoolKey memory key, uint24 newDynamicSwapFee) external; } diff --git a/src/libraries/Hooks.sol b/src/libraries/Hooks.sol index 356822d7..892ebdcf 100644 --- a/src/libraries/Hooks.sol +++ b/src/libraries/Hooks.sol @@ -5,13 +5,13 @@ pragma solidity ^0.8.24; import {IHooks} from "../interfaces/IHooks.sol"; import {PoolKey} from "../types/PoolKey.sol"; import {Encoded} from "./math/Encoded.sol"; -import {FeeLibrary} from "./FeeLibrary.sol"; +import {SwapFeeLibrary} from "./SwapFeeLibrary.sol"; import {ParametersHelper} from "./math/ParametersHelper.sol"; library Hooks { using Encoded for bytes32; using ParametersHelper for bytes32; - using FeeLibrary for uint24; + using SwapFeeLibrary for uint24; bytes4 constant NO_OP_SELECTOR = bytes4(keccak256(abi.encodePacked("NoOp"))); @@ -34,7 +34,7 @@ library Hooks { if (address(poolKey.hooks) == address(0)) { /// @notice If the hooks address is 0, then the bitmap must be 0, /// in the same time, the dynamic fee should be disabled as well - if (bitmapInParameters == 0 && !poolKey.fee.isDynamicFee()) { + if (bitmapInParameters == 0 && !poolKey.fee.isDynamicSwapFee()) { return; } revert HookConfigValidationError(); diff --git a/src/libraries/FeeLibrary.sol b/src/libraries/SwapFeeLibrary.sol similarity index 66% rename from src/libraries/FeeLibrary.sol rename to src/libraries/SwapFeeLibrary.sol index f1503921..b19640f1 100644 --- a/src/libraries/FeeLibrary.sol +++ b/src/libraries/SwapFeeLibrary.sol @@ -5,11 +5,13 @@ pragma solidity ^0.8.24; /// @dev Library for parsing swap fee info from PoolKey.fee: /// 24 bits (upper 4 bits are used to store flag, if swap fee is static, parse lower 20 bits to get swap fee) /// 1. flag to indicate the activation of dynamic swap fee, otherwise static swap fee is used -/// - if dynamic swap fee is activated, then the swap fee is controlled by IDynamicFeeManager(hook).getFee() +/// - if dynamic swap fee is activated, then the swap fee can be updated by hook /// - if dynamic swap fee is not activated, then the swap fee is controlled by PoolKey.fee itself /// 2. protocol fee is controlled by protocolFeeController, not PoolKey.fee /// - protocol fee is controlled by IProtocolFeeController(hook).protocolFeeForPool() -library FeeLibrary { +library SwapFeeLibrary { + using SwapFeeLibrary for uint24; + /// @dev swap fee is stored in PoolKey as uint24 uint24 public constant STATIC_FEE_MASK = 0x0FFFFF; uint24 public constant DYNAMIC_FEE_FLAG = 0x800000; // 1000 @@ -19,15 +21,17 @@ library FeeLibrary { uint24 public constant TEN_PERCENT_FEE = 100_000; // swap fee for LP - function isDynamicFee(uint24 self) internal pure returns (bool) { + function isDynamicSwapFee(uint24 self) internal pure returns (bool) { return self & DYNAMIC_FEE_FLAG != 0; } - function isStaticFeeTooLarge(uint24 self, uint24 maxFee) internal pure returns (bool) { - return self & STATIC_FEE_MASK > maxFee; + function isSwapFeeTooLarge(uint24 self, uint24 maxFee) internal pure returns (bool) { + return self > maxFee; } - function getStaticFee(uint24 self) internal pure returns (uint24) { - return self & STATIC_FEE_MASK; + function getSwapFee(uint24 self) internal pure returns (uint24 swapFee) { + // the initial fee for a dynamic fee pool is 0 + if (self.isDynamicSwapFee()) return 0; + swapFee = self & STATIC_FEE_MASK; } } diff --git a/src/pool-bin/BinPoolManager.sol b/src/pool-bin/BinPoolManager.sol index 3bd91ccb..feef7b4f 100644 --- a/src/pool-bin/BinPoolManager.sol +++ b/src/pool-bin/BinPoolManager.sol @@ -15,7 +15,7 @@ import {PoolKey} from "../types/PoolKey.sol"; import {BalanceDelta, BalanceDeltaLibrary} from "../types/BalanceDelta.sol"; import {IVault} from "../interfaces/IVault.sol"; import {BinPosition} from "./libraries/BinPosition.sol"; -import {FeeLibrary} from "../libraries/FeeLibrary.sol"; +import {SwapFeeLibrary} from "../libraries/SwapFeeLibrary.sol"; import {PackedUint128Math} from "./libraries/math/PackedUint128Math.sol"; import {Extsload} from "../Extsload.sol"; import "./interfaces/IBinHooks.sol"; @@ -26,7 +26,7 @@ contract BinPoolManager is IBinPoolManager, Fees, Extsload { using BinPool for *; using BinPosition for mapping(bytes32 => BinPosition.Info); using BinPoolParametersHelper for bytes32; - using FeeLibrary for uint24; + using SwapFeeLibrary for uint24; using PackedUint128Math for bytes32; using Hooks for bytes32; @@ -101,9 +101,6 @@ contract BinPoolManager is IBinPoolManager, Fees, Extsload { override poolManagerMatch(address(key.poolManager)) { - /// @dev Accept up to FeeLibrary.TEN_PERCENT_FEE for fee - if (key.fee.isStaticFeeTooLarge(FeeLibrary.TEN_PERCENT_FEE)) revert FeeTooLarge(); - uint16 binStep = key.parameters.getBinStep(); if (binStep < MIN_BIN_STEP) revert BinStepTooSmall(); if (binStep > MAX_BIN_STEP) revert BinStepTooLarge(); @@ -113,6 +110,10 @@ contract BinPoolManager is IBinPoolManager, Fees, Extsload { Hooks.validateHookConfig(key); _validateHookNoOp(key); + /// @notice init value for dynamic swap fee is 0, but hook can still set it in afterInitialize + uint24 swapFee = key.fee.getSwapFee(); + if (swapFee.isSwapFeeTooLarge(SwapFeeLibrary.TEN_PERCENT_FEE)) revert FeeTooLarge(); + if (key.parameters.shouldCall(HOOKS_BEFORE_INITIALIZE_OFFSET)) { if (hooks.beforeInitialize(msg.sender, key, activeId, hookData) != IBinHooks.beforeInitialize.selector) { revert Hooks.InvalidHookResponse(); @@ -122,7 +123,6 @@ contract BinPoolManager is IBinPoolManager, Fees, Extsload { PoolId id = key.toId(); (, uint16 protocolFee) = _fetchProtocolFee(key); - uint24 swapFee = key.fee.isDynamicFee() ? _fetchDynamicSwapFee(key) : key.fee.getStaticFee(); pools[id].initialize(activeId, protocolFee, swapFee); /// @notice Make sure the first event is noted, so that later events from afterHook won't get mixed up with this one @@ -196,14 +196,14 @@ contract BinPoolManager is IBinPoolManager, Fees, Extsload { _checkPoolInitialized(id); uint24 totalSwapFee; - if (key.fee.isDynamicFee()) { + if (key.fee.isDynamicSwapFee()) { totalSwapFee = IBinDynamicFeeManager(address(key.hooks)).getFeeForSwapInSwapOut( msg.sender, key, swapForY, 0, amountOut ); - if (totalSwapFee > FeeLibrary.TEN_PERCENT_FEE) revert FeeTooLarge(); + if (totalSwapFee > SwapFeeLibrary.TEN_PERCENT_FEE) revert FeeTooLarge(); } else { // clear the top 4 bits since they may be flagged - totalSwapFee = key.fee.getStaticFee(); + totalSwapFee = key.fee.getSwapFee(); } (amountIn, amountOutLeft, fee) = pools[id].getSwapIn( @@ -223,12 +223,12 @@ contract BinPoolManager is IBinPoolManager, Fees, Extsload { _checkPoolInitialized(id); uint24 totalSwapFee; - if (key.fee.isDynamicFee()) { + if (key.fee.isDynamicSwapFee()) { totalSwapFee = IBinDynamicFeeManager(address(key.hooks)).getFeeForSwapInSwapOut(msg.sender, key, swapForY, amountIn, 0); - if (totalSwapFee > FeeLibrary.TEN_PERCENT_FEE) revert FeeTooLarge(); + if (totalSwapFee > SwapFeeLibrary.TEN_PERCENT_FEE) revert FeeTooLarge(); } else { - totalSwapFee = key.fee.getStaticFee(); + totalSwapFee = key.fee.getSwapFee(); } (amountInLeft, amountOut, fee) = pools[id].getSwapOut( @@ -393,20 +393,13 @@ contract BinPoolManager is IBinPoolManager, Fees, Extsload { } /// @inheritdoc IPoolManager - function updateDynamicSwapFee(PoolKey memory key) external override { - if (key.fee.isDynamicFee()) { - uint24 newDynamicSwapFee = _fetchDynamicSwapFee(key); - PoolId id = key.toId(); - pools[id].setSwapFee(newDynamicSwapFee); - emit DynamicSwapFeeUpdated(id, newDynamicSwapFee); - } else { - revert FeeNotDynamic(); - } - } + function updateDynamicSwapFee(PoolKey memory key, uint24 newDynamicSwapFee) external override { + if (!key.fee.isDynamicSwapFee() || msg.sender != address(key.hooks)) revert UnauthorizedDynamicSwapFeeUpdate(); + if (newDynamicSwapFee.isSwapFeeTooLarge(SwapFeeLibrary.TEN_PERCENT_FEE)) revert FeeTooLarge(); - function _fetchDynamicSwapFee(PoolKey memory key) internal view returns (uint24 dynamicSwapFee) { - dynamicSwapFee = IBinDynamicFeeManager(address(key.hooks)).getFee(msg.sender, key); - if (dynamicSwapFee > FeeLibrary.TEN_PERCENT_FEE) revert FeeTooLarge(); + PoolId id = key.toId(); + pools[id].setSwapFee(newDynamicSwapFee); + emit DynamicSwapFeeUpdated(id, newDynamicSwapFee); } function _checkPoolInitialized(PoolId id) internal view { diff --git a/src/pool-cl/CLPoolManager.sol b/src/pool-cl/CLPoolManager.sol index f8ed16dd..dbed1c4e 100644 --- a/src/pool-cl/CLPoolManager.sol +++ b/src/pool-cl/CLPoolManager.sol @@ -11,11 +11,10 @@ import {CLPool} from "./libraries/CLPool.sol"; import {CLPosition} from "./libraries/CLPosition.sol"; import {PoolKey} from "../types/PoolKey.sol"; import {IPoolManager} from "../interfaces/IPoolManager.sol"; -import {ICLDynamicFeeManager} from "./interfaces/ICLDynamicFeeManager.sol"; import {Hooks} from "../libraries/Hooks.sol"; import {Tick} from "./libraries/Tick.sol"; import {CLPoolParametersHelper} from "./libraries/CLPoolParametersHelper.sol"; -import {FeeLibrary} from "../libraries/FeeLibrary.sol"; +import {SwapFeeLibrary} from "../libraries/SwapFeeLibrary.sol"; import {PoolId, PoolIdLibrary} from "../types/PoolId.sol"; import {BalanceDelta, BalanceDeltaLibrary} from "../types/BalanceDelta.sol"; import {Extsload} from "../Extsload.sol"; @@ -25,7 +24,7 @@ contract CLPoolManager is ICLPoolManager, Fees, Extsload { using SafeCast for int256; using PoolIdLibrary for PoolKey; using Hooks for bytes32; - using FeeLibrary for uint24; + using SwapFeeLibrary for uint24; using CLPoolParametersHelper for bytes32; using CLPool for *; using CLPosition for mapping(bytes32 => CLPosition.Info); @@ -97,8 +96,6 @@ contract CLPoolManager is ICLPoolManager, Fees, Extsload { poolManagerMatch(address(key.poolManager)) returns (int24 tick) { - if (key.fee.isStaticFeeTooLarge(FeeLibrary.ONE_HUNDRED_PERCENT_FEE)) revert FeeTooLarge(); - int24 tickSpacing = key.parameters.getTickSpacing(); if (tickSpacing > MAX_TICK_SPACING) revert TickSpacingTooLarge(); if (tickSpacing < MIN_TICK_SPACING) revert TickSpacingTooSmall(); @@ -108,6 +105,10 @@ contract CLPoolManager is ICLPoolManager, Fees, Extsload { Hooks.validateHookConfig(key); _validateHookNoOp(key); + /// @notice init value for dynamic swap fee is 0, but hook can still set it in afterInitialize + uint24 swapFee = key.fee.getSwapFee(); + if (swapFee.isSwapFeeTooLarge(SwapFeeLibrary.ONE_HUNDRED_PERCENT_FEE)) revert FeeTooLarge(); + if (key.parameters.shouldCall(HOOKS_BEFORE_INITIALIZE_OFFSET)) { if (hooks.beforeInitialize(msg.sender, key, sqrtPriceX96, hookData) != ICLHooks.beforeInitialize.selector) { revert Hooks.InvalidHookResponse(); @@ -116,7 +117,6 @@ contract CLPoolManager is ICLPoolManager, Fees, Extsload { PoolId id = key.toId(); (, uint16 protocolFee) = _fetchProtocolFee(key); - uint24 swapFee = key.fee.isDynamicFee() ? _fetchDynamicSwapFee(key) : key.fee.getStaticFee(); tick = pools[id].initialize(sqrtPriceX96, protocolFee, swapFee); /// @notice Make sure the first event is noted, so that later events from afterHook won't get mixed up with this one @@ -327,20 +327,13 @@ contract CLPoolManager is ICLPoolManager, Fees, Extsload { } /// @inheritdoc IPoolManager - function updateDynamicSwapFee(PoolKey memory key) external override { - if (key.fee.isDynamicFee()) { - uint24 newDynamicSwapFee = _fetchDynamicSwapFee(key); - PoolId id = key.toId(); - pools[id].setSwapFee(newDynamicSwapFee); - emit DynamicSwapFeeUpdated(id, newDynamicSwapFee); - } else { - revert FeeNotDynamic(); - } - } + function updateDynamicSwapFee(PoolKey memory key, uint24 newDynamicSwapFee) external override { + if (!key.fee.isDynamicSwapFee() || msg.sender != address(key.hooks)) revert UnauthorizedDynamicSwapFeeUpdate(); + if (newDynamicSwapFee.isSwapFeeTooLarge(SwapFeeLibrary.ONE_HUNDRED_PERCENT_FEE)) revert FeeTooLarge(); - function _fetchDynamicSwapFee(PoolKey memory key) internal view returns (uint24 dynamicSwapFee) { - dynamicSwapFee = ICLDynamicFeeManager(address(key.hooks)).getFee(msg.sender, key); - if (dynamicSwapFee > FeeLibrary.ONE_HUNDRED_PERCENT_FEE) revert FeeTooLarge(); + PoolId id = key.toId(); + pools[id].setSwapFee(newDynamicSwapFee); + emit DynamicSwapFeeUpdated(id, newDynamicSwapFee); } function _checkPoolInitialized(PoolId id) internal view { diff --git a/src/pool-cl/interfaces/ICLDynamicFeeManager.sol b/src/pool-cl/interfaces/ICLDynamicFeeManager.sol deleted file mode 100644 index e4c8655b..00000000 --- a/src/pool-cl/interfaces/ICLDynamicFeeManager.sol +++ /dev/null @@ -1,13 +0,0 @@ -//SPDX-License-Identifier: MIT -pragma solidity ^0.8.24; - -import {PoolKey} from "../../types/PoolKey.sol"; -import {ICLPoolManager} from "./ICLPoolManager.sol"; - -/// @notice The dynamic fee manager determines fees for pools -/// @dev note that this pool is only called if the PoolKey fee value is equal to the DYNAMIC_FEE magic value -interface ICLDynamicFeeManager { - /// @notice Called to look up swap fee for pool when PoolManager#updateDynamicSwapFee is called - /// @return swapFee 10_000 represent 1%, 3_000 represent 0.3% - function getFee(address sender, PoolKey calldata key) external view returns (uint24); -} diff --git a/src/test/MockFeePoolManager.sol b/src/test/MockFeePoolManager.sol index 36e83fe4..4756f405 100644 --- a/src/test/MockFeePoolManager.sol +++ b/src/test/MockFeePoolManager.sol @@ -6,7 +6,7 @@ import {PoolId, PoolIdLibrary} from "../types/PoolId.sol"; import {PoolKey} from "../types/PoolKey.sol"; import {BalanceDelta} from "../types/BalanceDelta.sol"; import {Fees} from "../Fees.sol"; -import {FeeLibrary} from "../libraries/FeeLibrary.sol"; +import {SwapFeeLibrary} from "../libraries/SwapFeeLibrary.sol"; import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; /** @@ -14,7 +14,6 @@ import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; */ contract MockFeePoolManager is Fees { using PoolIdLibrary for PoolKey; - using FeeLibrary for uint24; using FixedPointMathLib for uint256; mapping(PoolId poolId => BalanceDelta delta) public balanceDeltaOfPool; diff --git a/src/test/fee/MockFeeManagerHook.sol b/src/test/fee/MockFeeManagerHook.sol index 6d0cbb16..0a6395cd 100644 --- a/src/test/fee/MockFeeManagerHook.sol +++ b/src/test/fee/MockFeeManagerHook.sol @@ -40,4 +40,10 @@ contract MockFeeManagerHook is IHooks, IBinDynamicFeeManager { function getFeeForSwapInSwapOut(address, PoolKey calldata, bool, uint128, uint128) external view returns (uint24) { return swapfeeForSwapInSwapOut; } + + // swap fee for dynamic fee pool is 0 by default, so we need to update it after pool initialization + function afterInitialize(address, PoolKey calldata key, uint24, bytes calldata) external returns (bytes4) { + IBinPoolManager(msg.sender).updateDynamicSwapFee(key, swapfee); + return MockFeeManagerHook.afterInitialize.selector; + } } diff --git a/test/Fees.t.sol b/test/Fees.t.sol index e0c258e7..6538f29f 100644 --- a/test/Fees.t.sol +++ b/test/Fees.t.sol @@ -18,7 +18,7 @@ import "../src/interfaces/IFees.sol"; import "../src/interfaces/IVault.sol"; import "../src/interfaces/IPoolManager.sol"; import "../src/interfaces/IHooks.sol"; -import "../src/libraries/FeeLibrary.sol"; +import "../src/libraries/SwapFeeLibrary.sol"; contract FeesTest is Test { MockFeePoolManager poolManager; diff --git a/test/libraries/FeeLibrary.sol b/test/libraries/FeeLibrary.sol index 27dd7601..1c273a65 100644 --- a/test/libraries/FeeLibrary.sol +++ b/test/libraries/FeeLibrary.sol @@ -3,41 +3,46 @@ pragma solidity ^0.8.24; import {Test} from "forge-std/Test.sol"; -import {FeeLibrary} from "../../src/libraries/FeeLibrary.sol"; +import {SwapFeeLibrary} from "../../src/libraries/SwapFeeLibrary.sol"; -contract FeeLibraryTest is Test { - function testIsDynamicFee() public { +contract SwapFeeLibraryTest is Test { + using SwapFeeLibrary for uint24; + + function testisDynamicSwapFee() public { // 1000 0000 0000 0000 0000 0000 - assertEq(FeeLibrary.isDynamicFee(0x800000), true); + assertEq(SwapFeeLibrary.isDynamicSwapFee(0x800000), true); // 0100 0000 0000 0000 0000 0000 - assertEq(FeeLibrary.isDynamicFee(0x400000), false); + assertEq(SwapFeeLibrary.isDynamicSwapFee(0x400000), false); // 0010 0000 0000 0000 0000 0000 - assertEq(FeeLibrary.isDynamicFee(0x200000), false); + assertEq(SwapFeeLibrary.isDynamicSwapFee(0x200000), false); // 0001 0000 0000 0000 0000 0000 - assertEq(FeeLibrary.isDynamicFee(0x100000), false); + assertEq(SwapFeeLibrary.isDynamicSwapFee(0x100000), false); // 1111 1111 1111 1111 1111 1111 - assertEq(FeeLibrary.isDynamicFee(0xFFFFFF), true); + assertEq(SwapFeeLibrary.isDynamicSwapFee(0xFFFFFF), true); // 0111 1111 1111 1111 1111 1111 - assertEq(FeeLibrary.isDynamicFee(0x7FFFF), false); + assertEq(SwapFeeLibrary.isDynamicSwapFee(0x7FFFF), false); } - function testGetStaticFee() public { - assertEq(FeeLibrary.getStaticFee(0x000001), 0x000001); - assertEq(FeeLibrary.getStaticFee(0x000002), 0x000002); - assertEq(FeeLibrary.getStaticFee(0x0F0003), 0x0F0003); - assertEq(FeeLibrary.getStaticFee(0x001004), 0x001004); - assertEq(FeeLibrary.getStaticFee(0xF00F05), 0x000F05); - assertEq(FeeLibrary.getStaticFee(0x800310), 0x000310); - assertEq(FeeLibrary.getStaticFee(0x111020), 0x011020); - assertEq(FeeLibrary.getStaticFee(0x101020), 0x001020); + function testGetSwapFee() public { + // static + assertEq(SwapFeeLibrary.getSwapFee(0x000001), 0x000001); + assertEq(SwapFeeLibrary.getSwapFee(0x000002), 0x000002); + assertEq(SwapFeeLibrary.getSwapFee(0x0F0003), 0x0F0003); + assertEq(SwapFeeLibrary.getSwapFee(0x001004), 0x001004); + assertEq(SwapFeeLibrary.getSwapFee(0x111020), 0x011020); + assertEq(SwapFeeLibrary.getSwapFee(0x101020), 0x001020); + + // dynamic + assertEq(SwapFeeLibrary.getSwapFee(0xF00F05), 0); + assertEq(SwapFeeLibrary.getSwapFee(0x800310), 0); } function testFuzzIsStaicFeeTooLarge(uint24 self, uint24 maxFee) public { - assertEq(FeeLibrary.getStaticFee(self) > maxFee, FeeLibrary.isStaticFeeTooLarge(self, maxFee)); + assertEq(self.getSwapFee() > maxFee, self.getSwapFee().isSwapFeeTooLarge(maxFee)); } } diff --git a/test/libraries/Hooks/Hooks.t.sol b/test/libraries/Hooks/Hooks.t.sol index 0f172983..e14a860d 100644 --- a/test/libraries/Hooks/Hooks.t.sol +++ b/test/libraries/Hooks/Hooks.t.sol @@ -8,7 +8,7 @@ import {IPoolManager} from "../../../src/interfaces/IPoolManager.sol"; import {Currency} from "../../../src/types/Currency.sol"; import {PoolKey} from "../../../src/types/PoolKey.sol"; import {HooksContract} from "./HooksContract.sol"; -import {FeeLibrary} from "../../../src/libraries/FeeLibrary.sol"; +import {SwapFeeLibrary} from "../../../src/libraries/SwapFeeLibrary.sol"; contract HooksTest is Test { /// @dev trick to convert poolKey to calldata @@ -64,7 +64,7 @@ contract HooksTest is Test { bitmap := and(parameters, 0xFFFF) } - if (bitmap != 0 || FeeLibrary.isDynamicFee(fee)) { + if (bitmap != 0 || SwapFeeLibrary.isDynamicSwapFee(fee)) { vm.expectRevert(Hooks.HookConfigValidationError.selector); } diff --git a/test/pool-bin/BinPoolManager.t.sol b/test/pool-bin/BinPoolManager.t.sol index 1ae2737e..9aa84414 100644 --- a/test/pool-bin/BinPoolManager.t.sol +++ b/test/pool-bin/BinPoolManager.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.24; import "forge-std/Test.sol"; import {GasSnapshot} from "forge-gas-snapshot/GasSnapshot.sol"; import {MockERC20} from "solmate/test/utils/mocks/MockERC20.sol"; -import {FeeLibrary} from "../../src/libraries/FeeLibrary.sol"; +import {SwapFeeLibrary} from "../../src/libraries/SwapFeeLibrary.sol"; import {IVault} from "../../src/interfaces/IVault.sol"; import {IFees} from "../../src/interfaces/IFees.sol"; import {IPoolManager} from "../../src/interfaces/IPoolManager.sol"; @@ -166,7 +166,7 @@ contract BinPoolManagerTest is Test, GasSnapshot, BinTestHelper { } function testInitializeDynamicFeeTooLarge(uint24 dynamicSwapFee) public { - dynamicSwapFee = uint24(bound(dynamicSwapFee, FeeLibrary.TEN_PERCENT_FEE + 1, type(uint24).max)); + dynamicSwapFee = uint24(bound(dynamicSwapFee, SwapFeeLibrary.TEN_PERCENT_FEE + 1, type(uint24).max)); uint16 bitMap = 0x0040; // 0000 0000 0100 0000 (before swap call) BinFeeManagerHook binFeeManagerHook = new BinFeeManagerHook(poolManager); @@ -177,18 +177,19 @@ contract BinPoolManagerTest is Test, GasSnapshot, BinTestHelper { currency1: currency1, hooks: IHooks(address(binFeeManagerHook)), poolManager: IPoolManager(address(poolManager)), - fee: FeeLibrary.DYNAMIC_FEE_FLAG + uint24(3000), // 3000 = 0.3% + fee: SwapFeeLibrary.DYNAMIC_FEE_FLAG + uint24(3000), // 3000 = 0.3% parameters: bytes32(uint256(bitMap)).setBinStep(10) }); binFeeManagerHook.setFee(dynamicSwapFee); + vm.prank(address(binFeeManagerHook)); vm.expectRevert(IFees.FeeTooLarge.selector); - poolManager.updateDynamicSwapFee(key); + poolManager.updateDynamicSwapFee(key, dynamicSwapFee); } function testInitializeSwapFeeTooLarge() public { - uint24 swapFee = FeeLibrary.TEN_PERCENT_FEE + 1; + uint24 swapFee = SwapFeeLibrary.TEN_PERCENT_FEE + 1; key = PoolKey({ currency0: currency0, @@ -714,14 +715,15 @@ contract BinPoolManagerTest is Test, GasSnapshot, BinTestHelper { currency1: currency1, hooks: IHooks(address(binFeeManagerHook)), poolManager: IPoolManager(address(poolManager)), - fee: FeeLibrary.DYNAMIC_FEE_FLAG + uint24(3000), // 3000 = 0.3% + fee: SwapFeeLibrary.DYNAMIC_FEE_FLAG + uint24(3000), // 3000 = 0.3% parameters: bytes32(uint256(bitMap)).setBinStep(10) }); - binFeeManagerHook.setFee(FeeLibrary.TEN_PERCENT_FEE + 1); + binFeeManagerHook.setFee(SwapFeeLibrary.TEN_PERCENT_FEE + 1); vm.expectRevert(IFees.FeeTooLarge.selector); - poolManager.updateDynamicSwapFee(key); + vm.prank(address(binFeeManagerHook)); + poolManager.updateDynamicSwapFee(key, SwapFeeLibrary.TEN_PERCENT_FEE + 1); } function testUpdateDynamicSwapFee_FeeNotDynamic() public { @@ -734,12 +736,12 @@ contract BinPoolManagerTest is Test, GasSnapshot, BinTestHelper { parameters: poolParam }); - vm.expectRevert(IFees.FeeNotDynamic.selector); - poolManager.updateDynamicSwapFee(key); + vm.expectRevert(IPoolManager.UnauthorizedDynamicSwapFeeUpdate.selector); + poolManager.updateDynamicSwapFee(key, 3000); } function testFuzzUpdateDynamicSwapFee(uint24 _swapFee) public { - _swapFee = uint24(bound(_swapFee, 0, FeeLibrary.TEN_PERCENT_FEE)); + _swapFee = uint24(bound(_swapFee, 0, SwapFeeLibrary.TEN_PERCENT_FEE)); uint16 bitMap = 0x0004; // 0000 0000 0000 0100 (before mint call) BinFeeManagerHook binFeeManagerHook = new BinFeeManagerHook(poolManager); @@ -750,7 +752,7 @@ contract BinPoolManagerTest is Test, GasSnapshot, BinTestHelper { currency1: currency1, hooks: IHooks(address(binFeeManagerHook)), poolManager: IPoolManager(address(poolManager)), - fee: FeeLibrary.DYNAMIC_FEE_FLAG + uint24(3000), // 3000 = 0.3% + fee: SwapFeeLibrary.DYNAMIC_FEE_FLAG + uint24(3000), // 3000 = 0.3% parameters: bytes32(uint256(bitMap)).setBinStep(10) }); poolManager.initialize(key, activeId, new bytes(0)); @@ -761,7 +763,8 @@ contract BinPoolManagerTest is Test, GasSnapshot, BinTestHelper { emit DynamicSwapFeeUpdated(key.toId(), _swapFee); snapStart("BinPoolManagerTest#testFuzzUpdateDynamicSwapFee"); - poolManager.updateDynamicSwapFee(key); + vm.prank(address(binFeeManagerHook)); + poolManager.updateDynamicSwapFee(key, _swapFee); snapEnd(); (,, uint24 swapFee) = poolManager.getSlot0(key.toId()); diff --git a/test/pool-bin/helpers/BinFeeManagerHook.sol b/test/pool-bin/helpers/BinFeeManagerHook.sol index 1af0c7e9..eb3b089d 100644 --- a/test/pool-bin/helpers/BinFeeManagerHook.sol +++ b/test/pool-bin/helpers/BinFeeManagerHook.sol @@ -51,7 +51,7 @@ contract BinFeeManagerHook is BaseBinTestHook, IBinDynamicFeeManager { (bool _update, uint24 _fee) = abi.decode(hookData, (bool, uint24)); if (_update) { fee = _fee; - binManager.updateDynamicSwapFee(key); + binManager.updateDynamicSwapFee(key, _fee); } } @@ -67,7 +67,7 @@ contract BinFeeManagerHook is BaseBinTestHook, IBinDynamicFeeManager { (bool _update, uint24 _fee) = abi.decode(hookData, (bool, uint24)); if (_update) { fee = _fee; - binManager.updateDynamicSwapFee(key); + binManager.updateDynamicSwapFee(key, _fee); } } diff --git a/test/pool-bin/libraries/BinHelper.t.sol b/test/pool-bin/libraries/BinHelper.t.sol index 81bb6820..b351f4e2 100644 --- a/test/pool-bin/libraries/BinHelper.t.sol +++ b/test/pool-bin/libraries/BinHelper.t.sol @@ -10,7 +10,7 @@ import {BinHelper} from "../../../src/pool-bin/libraries/BinHelper.sol"; import {BinPoolParametersHelper} from "../../../src/pool-bin/libraries/BinPoolParametersHelper.sol"; import {PriceHelper} from "../../../src/pool-bin/libraries/PriceHelper.sol"; import {FeeHelper} from "../../../src/pool-bin/libraries/FeeHelper.sol"; -import {FeeLibrary} from "../../../src/libraries/FeeLibrary.sol"; +import {SwapFeeLibrary} from "../../../src/libraries/SwapFeeLibrary.sol"; contract BinHelperTest is BinTestHelper { using BinHelper for bytes32; @@ -165,7 +165,7 @@ contract BinHelperTest is BinTestHelper { amountXIn = uint128(bound(amountXIn, 1, type(uint128).max)); amountYIn = uint128(bound(amountYIn, 1, type(uint128).max)); price = uint256(bound(price, 1, type(uint256).max / amountXIn)); - fee = uint24(bound(fee, 0, FeeLibrary.TEN_PERCENT_FEE)); + fee = uint24(bound(fee, 0, SwapFeeLibrary.TEN_PERCENT_FEE)); ///@dev temp fix for "The `vm.assume` cheatcode rejected too many inputs" /// dont see a clear way to rewrite this with bound @@ -229,7 +229,7 @@ contract BinHelperTest is BinTestHelper { uint128 amountIn, uint24 fee ) external { - fee = uint24(bound(fee, 0, FeeLibrary.TEN_PERCENT_FEE)); + fee = uint24(bound(fee, 0, SwapFeeLibrary.TEN_PERCENT_FEE)); uint24 activeId = uint24(uint256(int256(uint256(ID_ONE)) + deltaId)); uint256 price = PriceHelper.getPriceFromId(activeId, DEFAULT_BIN_STEP); @@ -281,7 +281,7 @@ contract BinHelperTest is BinTestHelper { uint128 amountIn, uint24 fee ) external { - fee = uint24(bound(fee, 0, FeeLibrary.TEN_PERCENT_FEE)); + fee = uint24(bound(fee, 0, SwapFeeLibrary.TEN_PERCENT_FEE)); uint24 activeId = uint24(uint256(int256(uint256(ID_ONE)) + deltaId)); uint256 price = PriceHelper.getPriceFromId(activeId, DEFAULT_BIN_STEP); diff --git a/test/pool-bin/libraries/BinPoolFee.t.sol b/test/pool-bin/libraries/BinPoolFee.t.sol index 26e29237..c630ed47 100644 --- a/test/pool-bin/libraries/BinPoolFee.t.sol +++ b/test/pool-bin/libraries/BinPoolFee.t.sol @@ -14,7 +14,6 @@ import {Currency} from "../../../src/types/Currency.sol"; import {PoolKey} from "../../../src/types/PoolKey.sol"; import {BalanceDelta, toBalanceDelta} from "../../../src/types/BalanceDelta.sol"; import {PoolId, PoolIdLibrary} from "../../../src/types/PoolId.sol"; -import {FeeLibrary} from "../../../src/libraries/FeeLibrary.sol"; import {BinPoolManager} from "../../../src/pool-bin/BinPoolManager.sol"; import {BinPool} from "../../../src/pool-bin/libraries/BinPool.sol"; import {PackedUint128Math} from "../../../src/pool-bin/libraries/math/PackedUint128Math.sol"; @@ -22,10 +21,10 @@ import {SafeCast} from "../../../src/pool-bin/libraries/math/SafeCast.sol"; import {LiquidityConfigurations} from "../../../src/pool-bin/libraries/math/LiquidityConfigurations.sol"; import {IBinPoolManager} from "../../../src/pool-bin/interfaces/IBinPoolManager.sol"; import {BinPoolParametersHelper} from "../../../src/pool-bin/libraries/BinPoolParametersHelper.sol"; -import {FeeLibrary} from "../../../src/libraries/FeeLibrary.sol"; +import {SwapFeeLibrary} from "../../../src/libraries/SwapFeeLibrary.sol"; import {BinTestHelper} from "../helpers/BinTestHelper.sol"; import {BinFeeManagerHook} from "../helpers/BinFeeManagerHook.sol"; -import {HOOKS_BEFORE_MINT_OFFSET} from "../../../src/pool-bin/interfaces/IBinHooks.sol"; +import {HOOKS_AFTER_INITIALIZE_OFFSET, HOOKS_BEFORE_MINT_OFFSET} from "../../../src/pool-bin/interfaces/IBinHooks.sol"; /** * @dev tests around fee for mint(), swap() and burn() @@ -127,7 +126,7 @@ contract BinPoolFeeTest is BinTestHelper { } function testFuzz_Mint_WithDynamicFeeTooLarge(uint24 swapFee) external { - swapFee = uint24(bound(swapFee, FeeLibrary.TEN_PERCENT_FEE + 1, type(uint24).max)); + swapFee = uint24(bound(swapFee, SwapFeeLibrary.TEN_PERCENT_FEE + 1, type(uint24).max)); // 0000 0000 0000 0100, beforeMint uint16 bitMap = 0x0004; @@ -138,7 +137,7 @@ contract BinPoolFeeTest is BinTestHelper { currency1: currency1, hooks: IHooks(address(binFeeManagerHook)), poolManager: IPoolManager(address(poolManager)), - fee: FeeLibrary.DYNAMIC_FEE_FLAG + uint24(10_000), // 10_000 = 1% + fee: SwapFeeLibrary.DYNAMIC_FEE_FLAG + uint24(10_000), // 10_000 = 1% parameters: bytes32(uint256(bitMap)).setBinStep(10) }); @@ -151,14 +150,20 @@ contract BinPoolFeeTest is BinTestHelper { } function test_MintCompositionFee_DynamicFee() external { + mockFeeManagerHook.setHooksRegistrationBitmap(uint16(1 << HOOKS_AFTER_INITIALIZE_OFFSET)); key = PoolKey({ currency0: currency0, currency1: currency1, hooks: IHooks(address(mockFeeManagerHook)), poolManager: IPoolManager(address(poolManager)), - fee: FeeLibrary.DYNAMIC_FEE_FLAG + uint24(3000), // 3000 = 0.3% - parameters: poolParam // binStep + /// @dev dynamic swap fee is 0 when pool is initialized, hence 0.3% will be ignored + fee: SwapFeeLibrary.DYNAMIC_FEE_FLAG + uint24(3000), + parameters: BinPoolParametersHelper.setBinStep( + bytes32(uint256(mockFeeManagerHook.getHooksRegistrationBitmap())), 10 + ) }); + + // this could be sync to pool through beforeInitialize hook mockFeeManagerHook.setSwapFee(6_000); // overwrite to 0.6% fee uint24 binId = ID_ONE; // where token price are the same @@ -274,7 +279,7 @@ contract BinPoolFeeTest is BinTestHelper { } function test_Swap_WithDynamicFee(uint24 poolFee) external { - poolFee = uint24(bound(poolFee, 0, FeeLibrary.TEN_PERCENT_FEE - 1)); + poolFee = uint24(bound(poolFee, 0, SwapFeeLibrary.TEN_PERCENT_FEE - 1)); // 0000 0000 0100 0000, beforeSwap uint16 bitMap = 0x0040; @@ -285,7 +290,7 @@ contract BinPoolFeeTest is BinTestHelper { currency1: currency1, hooks: IHooks(address(binFeeManagerHook)), poolManager: IPoolManager(address(poolManager)), - fee: FeeLibrary.DYNAMIC_FEE_FLAG + poolFee, + fee: SwapFeeLibrary.DYNAMIC_FEE_FLAG + poolFee, // parameters: poolParam // binStep parameters: bytes32(uint256(bitMap)).setBinStep(10) }); @@ -297,7 +302,8 @@ contract BinPoolFeeTest is BinTestHelper { // overwrite fee to 2% binFeeManagerHook.setFee(20_000); - poolManager.updateDynamicSwapFee(key); + vm.prank(address(binFeeManagerHook)); + poolManager.updateDynamicSwapFee(key, 20_000); // Call getSwapIn and getSwapOut (, uint128 getSwapOutAmtOut,) = poolManager.getSwapOut(key, true, 1e18); @@ -314,7 +320,7 @@ contract BinPoolFeeTest is BinTestHelper { } function testFuzz_Swap_WithDynamicFeeTooLarge(uint24 swapFee) external { - swapFee = uint24(bound(swapFee, FeeLibrary.TEN_PERCENT_FEE + 1, type(uint24).max)); + swapFee = uint24(bound(swapFee, SwapFeeLibrary.TEN_PERCENT_FEE + 1, type(uint24).max)); // 0000 0000 0100 0000, beforeSwap uint16 bitMap = 0x0040; @@ -325,7 +331,7 @@ contract BinPoolFeeTest is BinTestHelper { currency1: currency1, hooks: IHooks(address(binFeeManagerHook)), poolManager: IPoolManager(address(poolManager)), - fee: FeeLibrary.DYNAMIC_FEE_FLAG + uint24(10_000), // 10_000 = 1% + fee: SwapFeeLibrary.DYNAMIC_FEE_FLAG + uint24(10_000), // 10_000 = 1% parameters: bytes32(uint256(bitMap)).setBinStep(10) }); diff --git a/test/pool-bin/libraries/FeeHelper.t.sol b/test/pool-bin/libraries/FeeHelper.t.sol index 3f6bb126..54336fae 100644 --- a/test/pool-bin/libraries/FeeHelper.t.sol +++ b/test/pool-bin/libraries/FeeHelper.t.sol @@ -4,14 +4,14 @@ pragma solidity ^0.8.24; import "forge-std/Test.sol"; import {FeeHelper} from "../../../src/pool-bin/libraries/FeeHelper.sol"; import {Uint256x256Math} from "../../../src/pool-bin/libraries/math/Uint256x256Math.sol"; -import {FeeLibrary} from "../../../src/libraries/FeeLibrary.sol"; +import {SwapFeeLibrary} from "../../../src/libraries/SwapFeeLibrary.sol"; contract FeeHelperTest is Test { using FeeHelper for uint128; using Uint256x256Math for uint256; function testFuzz_GetFeeAmountFrom(uint128 amountWithFee, uint24 feeBips) external { - feeBips = uint24(bound(feeBips, 0, FeeLibrary.TEN_PERCENT_FEE)); + feeBips = uint24(bound(feeBips, 0, SwapFeeLibrary.TEN_PERCENT_FEE)); uint128 fee = uint128(feeBips) * 1e12; uint256 expectedFeeAmount = (uint256(amountWithFee) * fee + 1e18 - 1) / 1e18; @@ -21,7 +21,7 @@ contract FeeHelperTest is Test { } function testFuzz_GetFeeAmount(uint128 amount, uint24 feeBips) external { - feeBips = uint24(bound(feeBips, 0, FeeLibrary.TEN_PERCENT_FEE)); + feeBips = uint24(bound(feeBips, 0, SwapFeeLibrary.TEN_PERCENT_FEE)); uint128 fee = uint128(feeBips) * 1e12; uint128 denominator = 1e18 - fee; @@ -33,7 +33,7 @@ contract FeeHelperTest is Test { } function testFuzz_GetCompositionFee(uint128 amountWithFee, uint24 feeBips) external { - feeBips = uint24(bound(feeBips, 0, FeeLibrary.TEN_PERCENT_FEE)); + feeBips = uint24(bound(feeBips, 0, SwapFeeLibrary.TEN_PERCENT_FEE)); uint128 fee = uint128(feeBips) * 1e12; uint256 denominator = 1e36; diff --git a/test/pool-cl/CLFees.t.sol b/test/pool-cl/CLFees.t.sol index 6e5ea75b..65cfe83b 100644 --- a/test/pool-cl/CLFees.t.sol +++ b/test/pool-cl/CLFees.t.sol @@ -5,7 +5,7 @@ import {Test} from "forge-std/Test.sol"; import {Vm} from "forge-std/Vm.sol"; import {IHooks} from "../../src/interfaces/IHooks.sol"; import {Hooks} from "../../src/libraries/Hooks.sol"; -import {FeeLibrary} from "../../src/libraries/FeeLibrary.sol"; +import {SwapFeeLibrary} from "../../src/libraries/SwapFeeLibrary.sol"; import {IPoolManager} from "../../src/interfaces/IPoolManager.sol"; import {IFees} from "../../src/interfaces/IFees.sol"; import {ICLPoolManager} from "../../src/pool-cl/interfaces/ICLPoolManager.sol"; diff --git a/test/pool-cl/CLPoolManager.t.sol b/test/pool-cl/CLPoolManager.t.sol index 5c283385..0eb7c499 100644 --- a/test/pool-cl/CLPoolManager.t.sol +++ b/test/pool-cl/CLPoolManager.t.sol @@ -16,7 +16,7 @@ import {PoolId, PoolIdLibrary} from "../../src/types/PoolId.sol"; import {IHooks} from "../../src/interfaces/IHooks.sol"; import {TickMath} from "../../src/pool-cl/libraries/TickMath.sol"; import {IFees} from "../../src/interfaces/IFees.sol"; -import {ICLHooks} from "../../src/pool-cl/interfaces/ICLHooks.sol"; +import {ICLHooks, HOOKS_AFTER_INITIALIZE_OFFSET} from "../../src/pool-cl/interfaces/ICLHooks.sol"; import {Hooks} from "../../src/libraries/Hooks.sol"; import {CLPoolManagerRouter} from "./helpers/CLPoolManagerRouter.sol"; import {FixedPoint96} from "../../src/pool-cl/libraries/FixedPoint96.sol"; @@ -25,7 +25,7 @@ import {CLPosition} from "../../src/pool-cl/libraries/CLPosition.sol"; import {Deployers} from "./helpers/Deployers.sol"; import {TokenFixture, MockERC20} from "../helpers/TokenFixture.sol"; import {MockHooks} from "./helpers/MockHooks.sol"; -import {FeeLibrary} from "../../src/libraries/FeeLibrary.sol"; +import {SwapFeeLibrary} from "../../src/libraries/SwapFeeLibrary.sol"; import {CLPoolParametersHelper} from "../../src/pool-cl/libraries/CLPoolParametersHelper.sol"; import {ParametersHelper} from "../../src/libraries/math/ParametersHelper.sol"; import {BalanceDelta, BalanceDeltaLibrary} from "../../src/types/BalanceDelta.sol"; @@ -42,7 +42,7 @@ contract CLPoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { using CurrencyLibrary for Currency; using CLPoolParametersHelper for bytes32; using ParametersHelper for bytes32; - using FeeLibrary for uint24; + using SwapFeeLibrary for uint24; event Initialize( PoolId indexed id, @@ -139,21 +139,21 @@ contract CLPoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { poolManager.initialize(key, TickMath.MIN_SQRT_RATIO, new bytes(0)); } - // 999999 i.e. 99.9999% overflow + // 1000000 i.e. 100% { PoolKey memory key = PoolKey({ currency0: Currency.wrap(makeAddr("token0")), currency1: Currency.wrap(makeAddr("token1")), hooks: IHooks(address(0)), poolManager: poolManager, - fee: uint24(999999), + fee: uint24(1000000), parameters: bytes32(uint256(0xa0000)) }); poolManager.initialize(key, TickMath.MIN_SQRT_RATIO, new bytes(0)); } - // 1000000 i.e. 100% + 1 overflow + // 1000001 i.e. > 100% { PoolKey memory key = PoolKey({ currency0: Currency.wrap(makeAddr("token0")), @@ -300,10 +300,7 @@ contract CLPoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { key.hooks = IHooks(address(0)); key.poolManager = poolManager; - if (key.fee & FeeLibrary.STATIC_FEE_MASK > FeeLibrary.ONE_HUNDRED_PERCENT_FEE) { - vm.expectRevert(abi.encodeWithSelector(IFees.FeeTooLarge.selector)); - poolManager.initialize(key, sqrtPriceX96, ZERO_BYTES); - } else if (key.parameters.getTickSpacing() > poolManager.MAX_TICK_SPACING()) { + if (key.parameters.getTickSpacing() > poolManager.MAX_TICK_SPACING()) { vm.expectRevert(abi.encodeWithSelector(ICLPoolManager.TickSpacingTooLarge.selector)); poolManager.initialize(key, sqrtPriceX96, ZERO_BYTES); } else if (key.parameters.getTickSpacing() < poolManager.MIN_TICK_SPACING()) { @@ -315,6 +312,9 @@ contract CLPoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { } else if (!_validateHookConfig(key)) { vm.expectRevert(abi.encodeWithSelector(Hooks.HookConfigValidationError.selector)); poolManager.initialize(key, sqrtPriceX96, ZERO_BYTES); + } else if (key.fee & SwapFeeLibrary.STATIC_FEE_MASK > SwapFeeLibrary.ONE_HUNDRED_PERCENT_FEE) { + vm.expectRevert(abi.encodeWithSelector(IFees.FeeTooLarge.selector)); + poolManager.initialize(key, sqrtPriceX96, ZERO_BYTES); } else { vm.expectEmit(true, true, true, true); emit Initialize( @@ -609,15 +609,18 @@ contract CLPoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { } function test_initialize_failsIDynamicFeeTooLarge(uint24 dynamicSwapFee) public { - dynamicSwapFee = uint24(bound(dynamicSwapFee, FeeLibrary.ONE_HUNDRED_PERCENT_FEE + 1, type(uint24).max)); + dynamicSwapFee = uint24(bound(dynamicSwapFee, SwapFeeLibrary.ONE_HUNDRED_PERCENT_FEE + 1, type(uint24).max)); + clFeeManagerHook.setHooksRegistrationBitmap(uint16(1 << HOOKS_AFTER_INITIALIZE_OFFSET)); PoolKey memory key = PoolKey({ currency0: currency0, currency1: currency1, - fee: FeeLibrary.DYNAMIC_FEE_FLAG + uint24(3000), // 3000 = 0.3% + fee: SwapFeeLibrary.DYNAMIC_FEE_FLAG + uint24(3000), // 3000 = 0.3% hooks: IHooks(address(clFeeManagerHook)), poolManager: poolManager, - parameters: bytes32(uint256(10) << 16) + parameters: CLPoolParametersHelper.setTickSpacing( + bytes32(uint256(clFeeManagerHook.getHooksRegistrationBitmap())), 10 + ) }); clFeeManagerHook.setFee(dynamicSwapFee); @@ -1440,7 +1443,7 @@ contract CLPoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { PoolKey memory key = PoolKey({ currency0: currency0, currency1: currency1, - fee: FeeLibrary.DYNAMIC_FEE_FLAG + uint24(3000), // 0.3% + fee: SwapFeeLibrary.DYNAMIC_FEE_FLAG + uint24(3000), // 0.3% hooks: IHooks(address(clFeeManagerHook)), poolManager: poolManager, parameters: bytes32(uint256((60 << 16) | clFeeManagerHook.getHooksRegistrationBitmap())) @@ -2333,16 +2336,17 @@ contract CLPoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { PoolKey memory key = PoolKey({ currency0: currency0, currency1: currency1, - fee: FeeLibrary.DYNAMIC_FEE_FLAG + uint24(3000), // 3000 = 0.3% + fee: SwapFeeLibrary.DYNAMIC_FEE_FLAG + uint24(3000), // 3000 = 0.3% hooks: IHooks(address(clFeeManagerHook)), poolManager: poolManager, parameters: bytes32(uint256(10) << 16) }); - clFeeManagerHook.setFee(FeeLibrary.ONE_HUNDRED_PERCENT_FEE + 1); + clFeeManagerHook.setFee(SwapFeeLibrary.ONE_HUNDRED_PERCENT_FEE + 1); vm.expectRevert(IFees.FeeTooLarge.selector); - poolManager.updateDynamicSwapFee(key); + vm.prank(address(clFeeManagerHook)); + poolManager.updateDynamicSwapFee(key, SwapFeeLibrary.ONE_HUNDRED_PERCENT_FEE + 1); } function testUpdateDynamicSwapFee_FeeNotDynamic() public { @@ -2355,12 +2359,12 @@ contract CLPoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { parameters: bytes32(uint256(10) << 16) }); - vm.expectRevert(IFees.FeeNotDynamic.selector); - poolManager.updateDynamicSwapFee(key); + vm.expectRevert(IPoolManager.UnauthorizedDynamicSwapFeeUpdate.selector); + poolManager.updateDynamicSwapFee(key, 3000); } function testFuzzUpdateDynamicSwapFee(uint24 _swapFee) public { - vm.assume(_swapFee < FeeLibrary.ONE_HUNDRED_PERCENT_FEE); + vm.assume(_swapFee < SwapFeeLibrary.ONE_HUNDRED_PERCENT_FEE); uint16 bitMap = 0x0010; // 0000 0000 0001 0000 (before swap call) clFeeManagerHook.setHooksRegistrationBitmap(bitMap); @@ -2368,7 +2372,7 @@ contract CLPoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { PoolKey memory key = PoolKey({ currency0: currency0, currency1: currency1, - fee: FeeLibrary.DYNAMIC_FEE_FLAG + uint24(3000), // 3000 = 0.3% + fee: SwapFeeLibrary.DYNAMIC_FEE_FLAG + uint24(3000), // 3000 = 0.3% hooks: IHooks(address(clFeeManagerHook)), poolManager: poolManager, parameters: bytes32(uint256((10 << 16) | clFeeManagerHook.getHooksRegistrationBitmap())) @@ -2382,7 +2386,8 @@ contract CLPoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { emit DynamicSwapFeeUpdated(key.toId(), _swapFee); snapStart("CLPoolManagerTest#testFuzzUpdateDynamicSwapFee"); - poolManager.updateDynamicSwapFee(key); + vm.prank(address(clFeeManagerHook)); + poolManager.updateDynamicSwapFee(key, _swapFee); snapEnd(); (,,, uint24 swapFee) = poolManager.getSlot0(key.toId()); @@ -2647,7 +2652,7 @@ contract CLPoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { function _validateHookConfig(PoolKey memory poolKey) internal view returns (bool) { uint16 bitmapInParameters = poolKey.parameters.getHooksRegistrationBitmap(); if (address(poolKey.hooks) == address(0)) { - if (bitmapInParameters == 0 && !poolKey.fee.isDynamicFee()) { + if (bitmapInParameters == 0 && !poolKey.fee.isDynamicSwapFee()) { return true; } return false; diff --git a/test/pool-cl/helpers/CLFeeManagerHook.sol b/test/pool-cl/helpers/CLFeeManagerHook.sol index 60dff289..e312a2c2 100644 --- a/test/pool-cl/helpers/CLFeeManagerHook.sol +++ b/test/pool-cl/helpers/CLFeeManagerHook.sol @@ -5,12 +5,11 @@ import {ICLPoolManager} from "../../../src/pool-cl/interfaces/ICLPoolManager.sol import {ICLHooks} from "../../../src/pool-cl/interfaces/ICLHooks.sol"; import {PoolKey} from "../../../src/types/PoolKey.sol"; import {IHooks} from "../../../src/interfaces/IHooks.sol"; -import {ICLDynamicFeeManager} from "../../../src/pool-cl/interfaces/ICLDynamicFeeManager.sol"; import {PoolId, PoolIdLibrary} from "../../../src/types/PoolId.sol"; import {PoolKey} from "../../../src/types/PoolKey.sol"; import {BaseCLTestHook} from "./BaseCLTestHook.sol"; -contract CLFeeManagerHook is BaseCLTestHook, ICLDynamicFeeManager { +contract CLFeeManagerHook is BaseCLTestHook { using PoolIdLibrary for PoolKey; uint16 bitmap; @@ -37,6 +36,16 @@ contract CLFeeManagerHook is BaseCLTestHook, ICLDynamicFeeManager { return fee; } + /// @dev update swap fee once pool is initialized + function afterInitialize(address, PoolKey calldata key, uint160, int24, bytes calldata) + external + override + returns (bytes4) + { + clManager.updateDynamicSwapFee(key, fee); + return ICLHooks.afterInitialize.selector; + } + function beforeSwap(address, PoolKey calldata key, ICLPoolManager.SwapParams calldata, bytes calldata hookData) external override @@ -46,7 +55,7 @@ contract CLFeeManagerHook is BaseCLTestHook, ICLDynamicFeeManager { (bool _update, uint24 _fee) = abi.decode(hookData, (bool, uint24)); if (_update) { fee = _fee; - clManager.updateDynamicSwapFee(key); + clManager.updateDynamicSwapFee(key, _fee); } } diff --git a/test/pool-cl/helpers/Deployers.sol b/test/pool-cl/helpers/Deployers.sol index a90afa27..f865bcaf 100644 --- a/test/pool-cl/helpers/Deployers.sol +++ b/test/pool-cl/helpers/Deployers.sol @@ -8,7 +8,7 @@ import {IHooks} from "../../../src/interfaces/IHooks.sol"; import {ICLPoolManager} from "../../../src/pool-cl/interfaces/ICLPoolManager.sol"; import {CLPoolManager} from "../../../src/pool-cl/CLPoolManager.sol"; import {PoolId, PoolIdLibrary} from "../../../src/types/PoolId.sol"; -import {FeeLibrary} from "../../../src/libraries/FeeLibrary.sol"; +import {SwapFeeLibrary} from "../../../src/libraries/SwapFeeLibrary.sol"; import {PoolKey} from "../../../src/types/PoolKey.sol"; import {Constants} from "./Constants.sol"; import {SortTokens} from "../../helpers/SortTokens.sol"; @@ -16,7 +16,7 @@ import {Vault} from "../../../src/Vault.sol"; import {IVault} from "../../../src/interfaces/IVault.sol"; contract Deployers { - using FeeLibrary for uint24; + using SwapFeeLibrary for uint24; using PoolIdLibrary for PoolKey; bytes constant ZERO_BYTES = new bytes(0); @@ -58,7 +58,7 @@ contract Deployers { hooks, manager, fee, - fee.isDynamicFee() + fee.isDynamicSwapFee() ? bytes32(uint256((60 << 16) | 0x00ff)) : bytes32(uint256(((fee / 100 * 2) << 16) | 0x00ff)) ); diff --git a/test/pool-cl/libraries/CLPool.t.sol b/test/pool-cl/libraries/CLPool.t.sol index 7af4026b..ec945e5c 100644 --- a/test/pool-cl/libraries/CLPool.t.sol +++ b/test/pool-cl/libraries/CLPool.t.sol @@ -14,7 +14,7 @@ import {FixedPoint96} from "../../../src/pool-cl/libraries/FixedPoint96.sol"; import {SafeCast} from "../../../src/libraries/SafeCast.sol"; import {LiquidityAmounts} from "../helpers/LiquidityAmounts.sol"; import {CLLmPool} from "../helpers/CLLmPool.sol"; -import {FeeLibrary} from "../../../src/libraries/FeeLibrary.sol"; +import {SwapFeeLibrary} from "../../../src/libraries/SwapFeeLibrary.sol"; import {FullMath} from "../../../src/pool-cl/libraries/FullMath.sol"; import {FixedPoint128} from "../../../src/pool-cl/libraries/FixedPoint128.sol"; @@ -46,7 +46,7 @@ contract PoolTest is Test { { // Assumptions tested in PoolManager.t.sol params.tickSpacing = int24(bound(params.tickSpacing, 1, 32767)); - swapFee = uint24(bound(swapFee, 0, FeeLibrary.ONE_HUNDRED_PERCENT_FEE - 1)); + swapFee = uint24(bound(swapFee, 0, SwapFeeLibrary.ONE_HUNDRED_PERCENT_FEE - 1)); testPoolInitialize(sqrtPriceX96, 0, swapFee); diff --git a/test/pool-cl/libraries/CLPoolSwapFee.t.sol b/test/pool-cl/libraries/CLPoolSwapFee.t.sol index 3859fcaf..fa782d5f 100644 --- a/test/pool-cl/libraries/CLPoolSwapFee.t.sol +++ b/test/pool-cl/libraries/CLPoolSwapFee.t.sol @@ -6,7 +6,7 @@ import "forge-std/Test.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {TokenFixture} from "../../helpers/TokenFixture.sol"; import {PoolKey} from "../../../src/types/PoolKey.sol"; -import {FeeLibrary} from "../../../src/libraries/FeeLibrary.sol"; +import {SwapFeeLibrary} from "../../../src/libraries/SwapFeeLibrary.sol"; import {CLFeeManagerHook} from "../helpers/CLFeeManagerHook.sol"; import {Deployers} from "../helpers/Deployers.sol"; import {Vault} from "../../../src/Vault.sol"; @@ -18,7 +18,7 @@ import {CLPoolManagerRouter} from "../helpers/CLPoolManagerRouter.sol"; import {Currency} from "../../../src/types/Currency.sol"; import {PoolId, PoolIdLibrary} from "../../../src/types/PoolId.sol"; import {FixedPoint96} from "../../../src/pool-cl/libraries/FixedPoint96.sol"; -import {HOOKS_BEFORE_SWAP_OFFSET} from "../../../src/pool-cl/interfaces/ICLHooks.sol"; +import {HOOKS_AFTER_INITIALIZE_OFFSET, HOOKS_BEFORE_SWAP_OFFSET} from "../../../src/pool-cl/interfaces/ICLHooks.sol"; import {IHooks} from "../../../src/interfaces/IHooks.sol"; import {Hooks} from "../../../src/libraries/Hooks.sol"; @@ -56,54 +56,50 @@ contract CLPoolSwapFeeTest is Deployers, TokenFixture, Test { hook = new CLFeeManagerHook(poolManager); - hook.setHooksRegistrationBitmap(uint16(1 << HOOKS_BEFORE_SWAP_OFFSET)); + hook.setHooksRegistrationBitmap(uint16((1 << HOOKS_BEFORE_SWAP_OFFSET) | (1 << HOOKS_AFTER_INITIALIZE_OFFSET))); dynamicFeeKey = PoolKey({ currency0: currency0, currency1: currency1, hooks: hook, poolManager: poolManager, - fee: FeeLibrary.DYNAMIC_FEE_FLAG, + fee: SwapFeeLibrary.DYNAMIC_FEE_FLAG, parameters: CLPoolParametersHelper.setTickSpacing(bytes32(uint256(hook.getHooksRegistrationBitmap())), 1) }); + hook.setHooksRegistrationBitmap(uint16(1 << HOOKS_BEFORE_SWAP_OFFSET)); staticFeeKey = PoolKey({ currency0: currency0, currency1: currency1, hooks: hook, poolManager: poolManager, // 50% - fee: FeeLibrary.ONE_HUNDRED_PERCENT_FEE / 2, + fee: SwapFeeLibrary.ONE_HUNDRED_PERCENT_FEE / 2, parameters: CLPoolParametersHelper.setTickSpacing(bytes32(uint256(hook.getHooksRegistrationBitmap())), 1) }); } function testPoolInitializeFailsWithTooLargeFee() public { - // cl pool swap fee is capped at 1_000_000 - hook.setFee(FeeLibrary.ONE_HUNDRED_PERCENT_FEE + 1); - vm.expectRevert(IFees.FeeTooLarge.selector); - poolManager.initialize(dynamicFeeKey, SQRT_RATIO_1_1, ZERO_BYTES); - - { - vm.expectRevert(IFees.FeeTooLarge.selector); - staticFeeKey.fee = FeeLibrary.ONE_HUNDRED_PERCENT_FEE + 1; - poolManager.initialize(staticFeeKey, SQRT_RATIO_1_1, ZERO_BYTES); - } + staticFeeKey.fee = SwapFeeLibrary.ONE_HUNDRED_PERCENT_FEE + 1; + poolManager.initialize(staticFeeKey, SQRT_RATIO_1_1, ZERO_BYTES); } function testUpdateFailsWithTooLargeFee() public { - hook.setFee(FeeLibrary.ONE_HUNDRED_PERCENT_FEE / 2); + hook.setFee(SwapFeeLibrary.ONE_HUNDRED_PERCENT_FEE / 2); + hook.setHooksRegistrationBitmap(uint16((1 << HOOKS_BEFORE_SWAP_OFFSET) | (1 << HOOKS_AFTER_INITIALIZE_OFFSET))); poolManager.initialize(dynamicFeeKey, SQRT_RATIO_1_1, ZERO_BYTES); - hook.setFee(FeeLibrary.ONE_HUNDRED_PERCENT_FEE + 1); + hook.setFee(SwapFeeLibrary.ONE_HUNDRED_PERCENT_FEE + 1); vm.expectRevert(IFees.FeeTooLarge.selector); - poolManager.updateDynamicSwapFee(dynamicFeeKey); + vm.prank(address(dynamicFeeKey.hooks)); + poolManager.updateDynamicSwapFee(dynamicFeeKey, SwapFeeLibrary.ONE_HUNDRED_PERCENT_FEE + 1); } function testSwapWorks() public { - hook.setFee(FeeLibrary.ONE_HUNDRED_PERCENT_FEE / 2); + hook.setFee(SwapFeeLibrary.ONE_HUNDRED_PERCENT_FEE / 2); // starts from price = 1 + hook.setHooksRegistrationBitmap(uint16((1 << HOOKS_BEFORE_SWAP_OFFSET) | (1 << HOOKS_AFTER_INITIALIZE_OFFSET))); poolManager.initialize(dynamicFeeKey, SQRT_RATIO_1_1, ZERO_BYTES); ICLPoolManager.ModifyLiquidityParams memory modifyPositionParams = @@ -163,7 +159,8 @@ contract CLPoolSwapFeeTest is Deployers, TokenFixture, Test { } function testCacheDynamicFeeAndSwap() public { - hook.setFee(FeeLibrary.ONE_HUNDRED_PERCENT_FEE / 2); + hook.setFee(SwapFeeLibrary.ONE_HUNDRED_PERCENT_FEE / 2); + hook.setHooksRegistrationBitmap(uint16((1 << HOOKS_BEFORE_SWAP_OFFSET) | (1 << HOOKS_AFTER_INITIALIZE_OFFSET))); // starts from price = 1 poolManager.initialize(dynamicFeeKey, SQRT_RATIO_1_1, ZERO_BYTES); @@ -182,7 +179,7 @@ contract CLPoolSwapFeeTest is Deployers, TokenFixture, Test { CLPoolManagerRouter.SwapTestSettings memory testSettings = CLPoolManagerRouter.SwapTestSettings({withdrawTokens: true, settleUsingTransfer: true}); - bytes memory data = abi.encode(true, uint24(FeeLibrary.ONE_HUNDRED_PERCENT_FEE - 1)); + bytes memory data = abi.encode(true, uint24(SwapFeeLibrary.ONE_HUNDRED_PERCENT_FEE - 1)); router.swap(dynamicFeeKey, params, testSettings, data); } @@ -192,7 +189,7 @@ contract CLPoolSwapFeeTest is Deployers, TokenFixture, Test { currency1: currency1, hooks: IHooks(address(0)), poolManager: poolManager, - fee: FeeLibrary.DYNAMIC_FEE_FLAG, + fee: SwapFeeLibrary.DYNAMIC_FEE_FLAG, parameters: CLPoolParametersHelper.setTickSpacing(bytes32(uint256(hook.getHooksRegistrationBitmap())), 1) }); diff --git a/test/vault/FakePoolManager.sol b/test/vault/FakePoolManager.sol index 4b60195b..c0ad5bfe 100644 --- a/test/vault/FakePoolManager.sol +++ b/test/vault/FakePoolManager.sol @@ -19,5 +19,5 @@ contract FakePoolManager is IPoolManager { function setProtocolFee(PoolKey memory key) external override {} - function updateDynamicSwapFee(PoolKey memory key) external override {} + function updateDynamicSwapFee(PoolKey memory key, uint24 newDynamicSwapFee) external override {} }