Fee
Fee-related base hooks are provided in the library to allow for flexible ways to adjust or override fees for different actions, like swaps and liquidity modifications.
Dynamic
BaseDynamicFee allows for dynamic setting and application of LP fees. Implementers must override the _getFee
function to return a fee value expressed in hundredths of a bip, based on their chosen logic. The fee is automatically applied after initialization, and can be refreshed at any time by calling the permissionless poke
function. This allows the fee to be dynamically updated based on external data or conditions. However, since poke
can be called by anyone, implementers must carefully consider whether their _getFee
implementation relies on external states that could be manipulated by adversaries.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import {BaseDynamicFee, IPoolManager, PoolKey} from "src/fee/BaseDynamicFee.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
/**
* @dev A hook that allows the owner to dynamically update the LP fee.
*/
contract DynamicLPFeeHook is BaseDynamicFee, Ownable {
uint24 public fee;
constructor(IPoolManager _poolManager) BaseDynamicFee(_poolManager) Ownable(msg.sender) {}
/**
* @inheritdoc BaseDynamicFee
*/
function _getFee(PoolKey calldata) internal view override returns (uint24) {
return fee;
}
/**
* @notice Sets the LP fee, denominated in hundredths of a bip.
*/
function setFee(uint24 _fee) external onlyOwner {
fee = _fee;
}
}
The constructor checks if the pool is configured with the dynamic fee flag and reverts if not.
Override
BaseOverrideFee allows for dynamic setting and application of swap fees. Similar to BaseDynamicFee, implementers must override the _getFee
function to return a fee value, which is masked with the override fee flag and passed to the PoolManager
before a swap.
This approach can be useful for time-based, volume-based, or volatility-based fees where the fee may fluctuate frequently. Because the hook runs before the swap is executed, an implementer can examine the current context (like liquidity levels or external price oracles) to decide the appropriate fee for each trade. It also doesn’t require poking the hook to refresh the fee, as the fee is dynamically fetched before each swap.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import {BaseOverrideFee, IPoolManager, PoolKey} from "src/fee/BaseOverrideFee.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
/**
* @dev A hook that allows the owner to dynamically update the swap fee.
*/
contract DynamicSwapFeeHook is BaseOverrideFee, Ownable {
uint24 public fee;
constructor(IPoolManager _poolManager) BaseOverrideFee(_poolManager) Ownable(msg.sender) {}
/**
* @inheritdoc BaseOverrideFee
*/
function _getFee(address, PoolKey calldata, IPoolManager.SwapParams calldata, bytes calldata)
internal
view
override
returns (uint24)
{
return fee;
}
/**
* @notice Sets the swap fee, denominated in hundredths of a bip.
*/
function setFee(uint24 _fee) external onlyOwner {
fee = _fee;
}
}
After Swap
BaseDynamicAfterFee applies adjustments to the tokens to be received by a user for exact-input swaps. This strategy relies on first capturing the swap context in the _beforeSwap
phase and storing a target delta. Once the swap is processed by the PoolManager
, the hook’s _afterSwap
method checks for exact-input swaps and compares the actual user output with the stored target delta. Any positive difference becomes a fee donation to the pool, effectively implementing a dynamic fee that is only finalized once all of the swap’s internal calculations are done.
Implementers should carefully consider how to mitigate the risk of attackers exploiting "just-in-time" liquidity additions to gain an outsized share of these fees. As the contract notes, the target deltas are cleared after each swap, so it is recommended to define or reset them each time in _beforeSwap
to ensure consistency.