Skip to content

Commit

Permalink
Add fee page
Browse files Browse the repository at this point in the history
  • Loading branch information
cairoeth committed Jan 28, 2025
1 parent 151e411 commit cf164f8
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 81 deletions.
85 changes: 84 additions & 1 deletion docs/modules/ROOT/pages/fee.adoc
Original file line number Diff line number Diff line change
@@ -1,7 +1,90 @@
= 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

xref:api:fee.adoc#BaseDynamicFee[BaseDynamicFee] allows for dynamic setting and application of LP fees. Implementers must override the xref:api:fee.adoc#BaseDynamicFee-_getFee-struct-PoolKey-[`_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 xref:api:fee.adoc#BaseDynamicFee-poke-struct-PoolKey-[`poke`] function. This allows the fee to be dynamically updated based on external data or conditions. However, since xref:api:fee.adoc#BaseDynamicFee-poke-struct-PoolKey-[`poke`] can be called by anyone, implementers must carefully consider whether their xref:api:fee.adoc#BaseDynamicFee-_getFee-struct-PoolKey-[`_getFee`] implementation relies on external states that could be manipulated by adversaries.

[source,solidity]
----
// 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

== After Swap
xref:api:fee.adoc#BaseOverrideFee[BaseOverrideFee] allows for dynamic setting and application of swap fees. Similar to xref:api:fee.adoc#BaseDynamicFee[BaseDynamicFee], implementers must override the xref:api:fee.adoc#BaseOverrideFee-_getFee-struct-PoolKey-[`_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.

[source,solidity]
----
// 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

xref:api:fee.adoc#BaseDynamicAfterFee[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 xref:api:base.adoc#BaseHook-_beforeSwap-address-struct-PoolKey-struct-IPoolManager-SwapParams-bytes-[`_beforeSwap`] phase and storing a target delta. Once the swap is processed by the `PoolManager`, the hook's xref:api:base.adoc#BaseHook-_afterSwap-address-struct-PoolKey-struct-IPoolManager-SwapParams-BalanceDelta-bytes-[`_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 xref:api:base.adoc#BaseHook-_beforeSwap-address-struct-PoolKey-struct-IPoolManager-SwapParams-bytes-[`_beforeSwap`] to ensure consistency.
2 changes: 1 addition & 1 deletion docs/modules/ROOT/pages/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
/**
* @dev A hook that allows the owner to dynamically update the LP fee.
*/
contract DynamicFeeHook is BaseDynamicFee, Ownable {
contract DynamicLPFeeHook is BaseDynamicFee, Ownable {
uint24 public fee;
constructor(IPoolManager _poolManager) BaseDynamicFee(_poolManager) Ownable(msg.sender) {}
Expand Down
79 changes: 0 additions & 79 deletions test/Counter.sol

This file was deleted.

0 comments on commit cf164f8

Please sign in to comment.