在前面的章节中,我们介绍了 ticks 的概念,它将价格曲线离散化。tick 是由公式 定义的价格,其中 被称为 tick index。
tick index 是在 范围内的整数,因此在价格曲线上从 到 共有 1,774,545 个 tick。
tick 是曲线上流动性可能发生变化的点。例如,流动性提供者可以在两个 tick 之间添加流动性,但不能在两个非 tick 的任意点之间添加流动性。
在本章中我们将看到,这 1,774,545 个 tick 并非全部都能在流动性池(pool)中使用,具体可使用的 tick 取决于创建该池时所做的选择。
Tick spacing
每个流动性池都定义了一个名为 tick spacing 的值,它决定了两个连续可用 tick 之间的距离。
例如,如果池的 tick spacing 设置为 10,则只有 10 的倍数的 tick index 是可用的,如 -20、-10、0、10、20 等。如果 tick spacing 设置为 60,则只允许使用 60 的倍数,如 -120、-60、0、60、120 等,如下图所示。在这两种情况下,像 55 这样的 tick 都不能用作提供流动性的边界。

在流动性池中定义 tick spacing 的变量名为 tickSpacing,它是在创建池时设置的。事实上,tickSpacing 与池的手续费相关联,每个手续费等级(fee tier)都决定了一个相应的 tick spacing。
波动率、手续费等级与 tick spacing 之间的关系
Tick spacing 与波动率
Uniswap V3 支持不同的 tick spacing,以适应不同资产对的波动率差异。在 swap 中跨越一个 tick 会产生 gas 成本,因此在典型的交易中应尽可能减少跨越 tick 的频率。
波动性较大的资产对受益于更宽的 tick spacing,以减少过多的 tick 跨越。然而,如果间距太宽,流动性提供者就无法在他们预期的资产对市场价值所在的价格区域周围精确地部署流动性。
请看下面的动画,我们展示了两种情况。在第一种涉及高波动率资产对的情况下,价格变化可能非常显著。因此,如果我们试图限制 swap 只跨越两个可用的 tick,那么可用 tick 之间的距离就必须很大。在第二种涉及更稳定资产对的情况下,tick spacing 可以更小。
波动率与手续费
此外,还要考虑流动性提供者面临的无常损失(impermanent loss)风险。高波动性资产往往会导致更高的无常损失,而较稳定的资产往往会导致较少的无常损失。例如,稳定币交易对几乎没有无常损失的风险,而 memecoin 交易对则具有极高的风险。
因此,LP 在为高波动性资产提供流动性时,会要求更高的手续费以补偿无常损失。同样地,交易者也能容忍在波动性资产上支付更高的手续费,因为交易这些资产的潜在回报要大得多。
这表明资产对的波动率、tick spacing 和手续费之间的关系应如下所述:对于具有高无常损失风险的高波动率资产对,tick spacing 和手续费都应该更高;而对于具有低无常损失风险的较稳定资产对,两者都可以更低。
由用户来决定哪些资产对更稳定或更具波动性。协议所定义的只是 tick spacing 和手续费等级之间的关系。
Uniswap V3 的 Factory 合约提供了一些默认值,但 Uniswap 治理机制可以添加额外的 tick-spacing 与手续费组合。这就是下一节的主题。
代码中的手续费与 tick spacing
手续费与 tick spacing 之间的关系包含在 UniswapV3Factory.sol 合约的 feeAmountTickSpacing 映射中(如下图中的黄框所示)。Factory 合约负责创建新的流动性池。
手续费与 tick spacing 之间的初始关系是在部署 Factory 合约时定义的,如下图中构造函数所示(绿框所示)。

当前手续费与 tick spacing 之间的关系如下表所示。手续费与 tick spacing 之间没有严格的“数学”关系。由协议治理来决定特定 tick spacing 对应的最佳手续费。
| fee tier | fee tier (basis points) | tick spacing |
|---|---|---|
| 0.01% | 1 | 1 |
| 0.05% | 5 | 10 |
| 0.3% | 30 | 60 |
| 1% | 100 | 200 |
我们可以看到,0.01% 的手续费对应 tick spacing 为 1,而 0.3% 的手续费对应 tick spacing 为 60。
基点 (Basis points)
手续费以基点(basis points)为单位来衡量。1 个基点是百分之一的 1/100,即 0.01%。例如,5 个基点就是 5 * 0.01%,即 0.05%。
手续费等级可被更新
从手续费到 tick spacing 的映射可以使用 enableFeeAmount 函数进行更新,该函数只能由治理机制调用。

1 个基点等级(即 0.01% 的手续费)并不在构造函数中,而是 Uniswap 治理在 2022 年 3 月 5 日添加的。你可以在 Tally 数据看板上查看到该笔交易。
可用的手续费等级在不同的链上可能会有所不同。例如,在 L2 Base 上,可以使用 2、3 和 4 个基点的手续费等级。这项新增设置由治理机制于 2024 年 9 月 16 日执行。
使用 Factory 合约创建新的流动性池
要创建一个新池,用户必须传入两种代币的地址以及期望的手续费等级。随后 tick spacing 将通过 feeAmountTickSpacing 映射计算得出。
一个流动性池由这三个参数唯一确定。因此,可以为相同的代币对创建不同手续费等级的池。在这种情况下,将由市场来决定这些池中哪一个会成为“受欢迎”的池。
Factory 合约对流动性池的部署
当 Factory 合约部署流动性池时,会向其传递 fee 和 tickSpacing,并将这些值设置为 public immutable 变量。

请注意,对于谁能调用 createPool 并没有限制 —— 只要该代币对和对应手续费等级的池尚未被创建,它就是无须许可的。
这两个 immutable 变量(fee 和 tickSpacing)在 UniswapV3Pool.sol 中都是 public 的。事实上,传递给池构造函数的所有参数都会存储在 public immutable 变量中:

示例
让我们使用 USDC/ETH Pool 作为一个贯穿前后的示例。我们可以看到手续费是 5 个基点,且其 tick spacing 为 10:

这就是该池的前端如何知道手续费等级为 0.05% 的方式。

总结
- 并非池中的所有 tick 都能使用——只允许使用 tick spacing 倍数的 tick。
- tick spacing 和手续费之间的关系在 Factory 的映射中设置。治理机制可以添加更多的 tick spacing 和手续费选项。
- 频繁跨越 tick 会导致更高的 gas 成本,正如我们在学习 swap 时将会了解到的。因此,具有较高价格波动率和/或较低流动性的池应该使用更大的 tick spacing,以最小化可用 tick 被跨越的频率。
- 另一方面,对于具有较低价格波动率和/或高流动性交易对的池,tick spacing 可以更小,因为流动性提供者会对在何处集中他们的流动性有更清晰的预判。
练习题
浏览 Uniswap V3 Pools。
- 主网上 USDC/USDT 的 tick spacing 是多少?由于可能存在同一个交易对但手续费不同的多个池,请寻找过去一天内交易量至少为一百万美金的池。
- 包含 memecoin 的交易对的 tick spacing 是多少?