费用
库中提供了与费用相关的基本钩子,允许以灵活的方式调整或覆盖不同操作的费用,例如交换和流动性修改。
动态
BaseDynamicFee 允许动态设置和应用 LP (Liquidity Provider) 费用。实现者必须重写 _getFee
函数,以基于他们选择的逻辑返回以百分之一 Bip 表示的费用值。该费用在初始化后自动应用,并且可以通过调用无需许可的 poke
函数随时刷新。这允许基于外部数据或条件动态更新费用。但是,由于 poke
可以被任何人调用,因此实现者必须仔细考虑他们的_getFee
实现是否依赖于可能被攻击者操纵的外部状态。
// 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 一个允许所有者动态更新 LP 费用的钩子。
*/
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 设置 LP 费用,以百分之一 Bip 为单位。
*/
function setFee(uint24 _fee) external onlyOwner {
fee = _fee;
}
}
构造函数检查池是否配置了动态费用标志,如果没有,则恢复。
覆盖
BaseOverrideFee 允许动态设置和应用交换费用。与 BaseDynamicFee 类似,实现者必须重写 _getFee
函数以返回费用值,该费用值使用覆盖费用标志进行掩码,并在交换之前传递给 PoolManager
。
对于基于时间、基于交易量或基于波动性的费用,这种方法可能很有用,因为这些费用可能频繁波动。由于钩子在执行交换之前运行,因此实现者可以检查当前上下文(如流动性水平或外部价格预言机)以确定每笔交易的适当费用。它也不需要拨动(poke)钩子来刷新费用,因为费用是在每次交换之前动态获取的。
// 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 一个允许所有者动态更新交换费用的钩子。
*/
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 设置交换费用,以百分之一 Bip 为单位。
*/
function setFee(uint24 _fee) external onlyOwner {
fee = _fee;
}
}
交换后
BaseDynamicAfterFee 将调整应用于用户因精确输入交换而将收到的 token。此策略依赖于首先在 _beforeSwap
阶段捕获交换上下文并存储目标增量。一旦 PoolManager
处理了交换,钩子的 _afterSwap
方法会检查精确输入交换,并将实际用户输出与存储的目标增量进行比较。任何正差额都将成为对池的费用捐赠,从而有效地实现了一种动态费用,该费用仅在完成所有交换的内部计算后才最终确定。
实施者应仔细考虑如何缓解攻击者利用“即时”流动性增加来获得这些费用的超额份额的风险。正如合约所指出的那样,目标增量在每次交换后都会被清除,因此建议每次都在 _beforeSwap
中定义或重置它们,以确保一致性。