当前位置: 首页 > news >正文

uniswap v4 合约解析1 pool初始化

当我们创建一个pool时,其入口函数位PoolManager合约的initialize方法:

 代码如下:

    /// @inheritdoc IPoolManagerfunction initialize(PoolKey memory key, uint160 sqrtPriceX96) external noDelegateCall returns (int24 tick) {// see TickBitmap.sol for overflow conditions that can arise from tick spacing being too largeif (key.tickSpacing > MAX_TICK_SPACING) TickSpacingTooLarge.selector.revertWith(key.tickSpacing);if (key.tickSpacing < MIN_TICK_SPACING) TickSpacingTooSmall.selector.revertWith(key.tickSpacing);if (key.currency0 >= key.currency1) {CurrenciesOutOfOrderOrEqual.selector.revertWith(Currency.unwrap(key.currency0), Currency.unwrap(key.currency1));}if (!key.hooks.isValidHookAddress(key.fee)) Hooks.HookAddressNotValid.selector.revertWith(address(key.hooks));uint24 lpFee = key.fee.getInitialLPFee();key.hooks.beforeInitialize(key, sqrtPriceX96);PoolId id = key.toId();tick = _pools[id].initialize(sqrtPriceX96, lpFee);// event is emitted before the afterInitialize call to ensure events are always emitted in order// emit all details of a pool key. poolkeys are not saved in storage and must always be provided by the caller// the key's fee may be a static fee or a sentinel to denote a dynamic fee.emit Initialize(id, key.currency0, key.currency1, key.fee, key.tickSpacing, key.hooks, sqrtPriceX96, tick);key.hooks.afterInitialize(key, sqrtPriceX96, tick);}

校验

第一步是校验

if (key.tickSpacing > MAX_TICK_SPACING) TickSpacingTooLarge.selector.revertWith(key.tickSpacing);
if (key.tickSpacing < MIN_TICK_SPACING) TickSpacingTooSmall.selector.revertWith(key.tickSpacing);

根据MAX_TICK_SPACING和MIN_TICK_SPACING的定义,tickSpacing的范围是[1, 32767]

此提供了足够的灵活性,允许开发者根据需求选择适合的tickSpacing

  • 较小的 tickSpacing刻度间距更密集,允许更精细的价格范围。适合高精度的交易场景,但会增加存储和计算成本。

  • 较大的 tickSpacing刻度间距更稀疏,减少存储和计算成本。适合低精度的交易场景,但可能限制流动性提供者的灵活性。

if (key.currency0 >= key.currency1) {CurrenciesOutOfOrderOrEqual.selector.revertWith(Currency.unwrap(key.currency0), Currency.unwrap(key.currency1));
}

这段代码的作用是对 key.currency0 和 key.currency1 进行验证,确保它们的顺序正确且不相等。如果 key.currency0 大于或等于 key.currency1,则抛出 CurrenciesOutOfOrderOrEqual 错误。

用户在手动调用合约创建交易对时,需要注意poolKey参数里代币地址的大小顺序。

Currency.unwrap(key.currency0)将 Currency 类型的值解包为其底层的原始值address便于输出和调试。

if (!key.hooks.isValidHookAddress(key.fee)) Hooks.HookAddressNotValid.selector.revertWith(address(key.hooks));

 这里主要是对pool中包含的钩子地址进行校验和交易费用进行校验,代码如下:

    /// @notice Ensures that the hook address includes at least one hook flag or dynamic fees, or is the 0 address/// @param self The hook to verify/// @param fee The fee of the pool the hook is used with/// @return bool True if the hook address is validfunction isValidHookAddress(IHooks self, uint24 fee) internal pure returns (bool) {// The hook can only have a flag to return a hook delta on an action if it also has the corresponding action flagif (!self.hasPermission(BEFORE_SWAP_FLAG) && self.hasPermission(BEFORE_SWAP_RETURNS_DELTA_FLAG)) return false;if (!self.hasPermission(AFTER_SWAP_FLAG) && self.hasPermission(AFTER_SWAP_RETURNS_DELTA_FLAG)) return false;if (!self.hasPermission(AFTER_ADD_LIQUIDITY_FLAG) && self.hasPermission(AFTER_ADD_LIQUIDITY_RETURNS_DELTA_FLAG)){return false;}if (!self.hasPermission(AFTER_REMOVE_LIQUIDITY_FLAG)&& self.hasPermission(AFTER_REMOVE_LIQUIDITY_RETURNS_DELTA_FLAG)) return false;// If there is no hook contract set, then fee cannot be dynamic// If a hook contract is set, it must have at least 1 flag set, or have a dynamic feereturn address(self) == address(0)? !fee.isDynamicFee(): (uint160(address(self)) & ALL_HOOK_MASK > 0 || fee.isDynamicFee());}

代码比较简单,但是需要先了解一下hooks地址的标志位

这里需要介绍一下动态费用,我们知道uniswap v3支持的交易费用为0.01%0.05%0.3% 和 1%,而v4在此基础上则增加了动态费用,支持根据交易情况在钩子里动态调整交易费用,也就是说当我们把交易费用设置为0x800000(0x代表16进制),转换成2进制为0b100000000000000000000000,则代表当前的交易费需要通过钩子调整,所以如用户设置了动态费用但是没有设置任何hooks,则校验失败。

uint24 lpFee = key.fee.getInitialLPFee();

这里是获取用户的设置的交易费,如果是动态费用则返回0,同时这里也包含了一步校验,如果费用超过100%,会抛出异常。

   /// @notice An lp fee of exactly 0b1000000... signals a dynamic fee pool. This isn't a valid static fee as it is > MAX_LP_FEEuint24 public constant DYNAMIC_FEE_FLAG = 0x800000;/// @notice the second bit of the fee returned by beforeSwap is used to signal if the stored LP fee should be overridden in this swap// only dynamic-fee pools can return a fee via the beforeSwap hookuint24 public constant OVERRIDE_FEE_FLAG = 0x400000;/// @notice mask to remove the override fee flag from a fee returned by the beforeSwaphookuint24 public constant REMOVE_OVERRIDE_MASK = 0xBFFFFF;/// @notice the lp fee is represented in hundredths of a bip, so the max is 100%uint24 public constant MAX_LP_FEE = 1000000;    function isDynamicFee(uint24 self) internal pure returns (bool) {return self == DYNAMIC_FEE_FLAG;}/// @notice returns true if an LP fee is valid, aka not above the maximum permitted fee/// @param self The fee to check/// @return bool True of the fee is validfunction isValid(uint24 self) internal pure returns (bool) {return self <= MAX_LP_FEE;}/// @notice validates whether an LP fee is larger than the maximum, and reverts if invalid/// @param self The fee to validatefunction validate(uint24 self) internal pure {if (!self.isValid()) LPFeeTooLarge.selector.revertWith(self);}/// @notice gets and validates the initial LP fee for a pool. Dynamic fee pools have an initial fee of 0./// @dev if a dynamic fee pool wants a non-0 initial fee, it should call `updateDynamicLPFee` in the afterInitialize hook/// @param self The fee to get the initial LP from/// @return initialFee 0 if the fee is dynamic, otherwise the fee (if valid)function getInitialLPFee(uint24 self) internal pure returns (uint24) {// the initial fee for a dynamic fee pool is 0if (self.isDynamicFee()) return 0;self.validate();return self;}

初始化

接下来就是正式创建交易对

key.hooks.beforeInitialize(key, sqrtPriceX96);PoolId id = key.toId();tick = _pools[id].initialize(sqrtPriceX96, lpFee);// event is emitted before the afterInitialize call to ensure events are always emitted in order
// emit all details of a pool key. poolkeys are not saved in storage and must always be provided by the caller
// the key's fee may be a static fee or a sentinel to denote a dynamic fee.
emit Initialize(id, key.currency0, key.currency1, key.fee, key.tickSpacing, key.hooks, sqrtPriceX96, tick);key.hooks.afterInitialize(key, sqrtPriceX96, tick);

创建交易对前后需要执行相应的钩子beforeInitialize和afterInitialize

tick = _pools[id].initialize(sqrtPriceX96, lpFee);这部分是真正初始化交易对。

_pools是一个映射(mapping),在 Solidity 中,映射的默认行为是:如果访问一个未初始化的键(id),它会返回该键对应值类型的默认值

根据_pools的定义

mapping(PoolId id => Pool.State) internal _pools;

如果 id 对应的池(Pool.State)从未被初始化,_pools[id] 会返回一个默认值(即 Pool.State 的默认状态)。

    struct State {Slot0 slot0;uint256 feeGrowthGlobal0X128;uint256 feeGrowthGlobal1X128;uint128 liquidity;mapping(int24 tick => TickInfo) ticks;mapping(int16 wordPos => uint256) tickBitmap;mapping(bytes32 positionKey => Position.State) positions;}

相应的基本类型和结构体(slot0)中的基本类型都会初始化为0

pool库通过using Pool for State将 Pool 库中的函数与 State 结构体相关联,所以_pools[id].initialize(sqrtPriceX96, lpFee);会直接调用poolinitialize方法。

    function initialize(State storage self, uint160 sqrtPriceX96, uint24 lpFee) internal returns (int24 tick) {if (self.slot0.sqrtPriceX96() != 0) PoolAlreadyInitialized.selector.revertWith();tick = TickMath.getTickAtSqrtPrice(sqrtPriceX96);// the initial protocolFee is 0 so doesn't need to be setself.slot0 = Slot0.wrap(bytes32(0)).setSqrtPriceX96(sqrtPriceX96).setTick(tick).setLpFee(lpFee);}
  • 第一行是校验
  • 第二行是通过初始的价格获取相应的tick,
  • 第三行设置当前pool的价格和tick。

第二行代码的实现是最复杂的,详见:uniswap getTickAtSqrtPrice 方法解析

相关文章:

  • VTK 数据结构和算法类介绍
  • pyqt写一个单片机配置界面
  • 基于YOLOv的目标检测训练数据构建方法研究—图像采集、标注、划分与增强一体化流程设计
  • java单元测试代码
  • Python中的JSON库,详细介绍与代码示例
  • 《RESTful API版本控制的哲学思辨:稳定性与创新性的终极平衡》
  • Node.js 是什么?
  • 深入理解 TensorFlow 的模型保存与加载机制(SavedModel vs H5)
  • 蓝桥杯单片机国赛模板——基于柳离风模板
  • 列日-巴斯通-列日:与VELO Senso TT+见证精彩时刻
  • java类=null的回收
  • PostgreSQL 的 pg_ls_waldir 函数
  • Scala day6(Class,field,Single Object)
  • 【Flask】ORM模型以及数据库迁移的两种方法(flask-migrate、Alembic)
  • 学习路线(嵌入式软件)
  • 【C/C++】无锁编程——compare_exchange_strong
  • LeetCode 热题 100 46. 全排列
  • 为React组件库引入自动化测试:从零到完善的实践之路
  • 【CF】Day51——Codeforces Round 963 (Div. 2) CD
  • 【AI学习】DeepSeek-R1是如何训练的?
  • 专家解读《人源类器官研究伦理指引》:构建类器官研究全过程伦理治理框架
  • 特朗普要征100%关税,好莱坞这批境外摄制新片能躲过吗?
  • 岳伟华任北京大学第六医院院长,陆林院士卸任
  • 市场驱动的系统改造:丹麦零弃风经验研究
  • 港股5月迎开门红,恒生科指涨3%,欧股开盘全线上扬
  • 山西太原一小区发生爆炸,太原:进一步深刻汲取教训