本文详细解释了Uniswap V3中的ticks概念,ticks作为预定义价格点,在集中流动性管理中起到重要作用。文章深入探讨了ticks的定义、计算方法及其在流动性提供中的应用,并通过多个示例和插图增强理解,适合对DeFi和流动性池有一定了解的读者。
这篇文章解释了在 Uniswap V3 中什么是 ticks。Ticks 实现了集中流动性的气体高效记账,因此让我们先快速回顾一下 集中流动性。
集中流动性意味着流动性不再像 Uniswap V2 那样在价格曲线上保持恒定。流动性提供者可以选择价格曲线中的特定区间来放置他们的流动性。下方的动画展示了 Uniswap V2 和 Uniswap V3 价格曲线之间的区别。
https://img.learnblockchain.cn/2025/03/07/splitcurve3.mp4
例如,在一个 ETH:USDC 池中,如果 ETH 的价格为 USDC 2,000,流动性提供者可能会选择在 USDC 1,800 和 USDC 2,200 之间放置他们的流动性,以便在他们预期资产交易的价格范围内捕获更多费用。在下面的图示中,USDC 1800 和 USDC 2200 之间的区间流动性比其他区间大。
在一个区间内更高的流动性意味着该区间内的 价格影响 会更低。相反,流动性较低则使价格影响更大。
为了支持集中流动性,Uniswap V3 协议需要模型化流动性在价格曲线上的变化。
流动性可以在曲线上变化,但不能在任意点上变化。如果 Uniswap v3 允许在曲线的任意点修改流动性,复杂性和Gas成本将会急剧增加。每当价格稍微变动,协议都必须检查流动性是否改变。
因此,为了显著减少检查流动性是否改变的次数,在 Uniswap V3 中,流动性只能在预先定义的价格处进行调整。
这些预先定义的价格点称为 ticks。下面的图示说明了这个概念,展示了一些 ticks。需要注意的是在 Uniswap V3 中,ticks 的数量远超下方所示。价格曲线被 ticks “切割”(从原点发出的红色射线)。
回顾一下,在 Uniswap V2 和 V3 中,“价格”可以理解为从原点到价格曲线的射线的角度。角度越大,从 x 轴的角度越大,资产 X 相对于资产 Y 的价格越高。因此,角度更大的 ticks 对应于资产 X 的更高价格。
https://img.learnblockchain.cn/2025/03/07/SweepingRayC.mp4
在下面的动画中,青色射线的角度代表资产 X 的价格,而红色射线代表 ticks。资产 X 的价格可以变化并取任何值,而 ticks 则保持不变。
https://img.learnblockchain.cn/2025/03/07/SweepingHyperbola.mp4
重要的是要理解,ticks 代表了将作为标签使用的曲线上的点。让我们把它比作一个有里程标记的道路。虽然汽车可以在道路的任何地方行驶,里程标记都是在特定位置上放置的,通常是在一些可预测的间隔。同样,在 Uniswap v3 中,代币价格可以有任何值,但 ticks 在协议预定义的特定位置上静态定位。
Ticks 充当了流动性可以变化的价格曲线中的参考点。
我们在下面展示了这个概念。ticks 以从原点发出的红色射线表示。在左侧,流动性在 ticks 之间调整,这是 Uniswap V3 所允许的。在右侧,情景展示了试图在不是 ticks 的曲线点上调整流动性,而协议不允许这种调整。
我们将很快展示 Uniswap V3 如何确定 ticks 的位置,但首先,让我们快速回顾一下如何将“价格”理解为“代币 X 相对于 Y 的价格”。
在 Uniswap v3 中,价格始终是指代币 X 相对于代币 Y 的价格。 因此,每当我们写 p 时,它指的是代币 X 的价格,由 $p=p_x=y/x$ 给出。
代币 Y 相对于代币 X 的价格,由 $p_y=x/y$ 给出,可以通过 p 计算为 $p_y=1/p$。因此,协议只需要跟踪 X 中的价格(p)。Y 中的价格(py)可以相应计算。
例如,如果代币 X 的价格为 p=10,那么代币 Y 的价格可以计算为 $p_y=1/10$ 或 $p_y=0.1$。选择使用代币 X 的价格而不是代币 Y 的价格仅是一种约定。
我们说过 ticks 是预定义价格,但这些价格是如何定义的?
它们由以下公式定义:
$$p(i)=1.0001^i$$
其中 i 是一个名为 tick index 的整数,p(i) 是 tick index 所表示的价格。我们将其称为“tick price”。记住,每个 tick 只是一个固定价格的标签。
一些 ticks 的示例:
允许的 tick indexes 范围从 -887,272 到 887,272,这个范围的原因将在下一章解释。
通常将索引 i 也称为 tick,以便在代码库中频繁使用。 从上下文中应该可以清楚地区别 tick index 或 tick price。无论如何,每个 tick index i 与 tick price p(i) 是唯一关联的,反之亦然。
假设我们有一个 USDC:USDT 池,其中代币 X 是 USDC,代币 Y 是 USDT。如果这两个资产具有完全相同的价值,那么价格将正好落在 tick index 0,因为 USDC 相对于 USDT 的价格为 1:1。如下所示(ticks 并非按比例显示)。ticks 用红色射线表示,USDC 与 USDT 的交易价格用黄色虚线表示,终止于黄色点,价格曲线是青色曲线:
现在假设 USDC 相对于 USDT 稍微升值。也就是说,有人需要交易 1.00005 USDT 才能获得 1 USDC。这将使当前价格稍微高于 tick 0,但还不到 tick 1:
这完全没问题——协议并不要求资产的价值必须与某个 tick 匹配。
如果 USDC 继续升值直到 1.0001 USDT 需要被交易才能兑换 1 USDC,那么价格曲线上的点将正好落在 tick 1。这个价格精准落在 tick 1,因为 $p(1)=1.0001^1=1.0001$。
要查看负 ticks、tick 0 和正 ticks 之间的关系,请参见下面标记的 ticks。具体而言,下面的图像使用以下示例价格。
$$ \begin{align} 1.0001^1 &=1.0001 \ 1.0001^{0} &= 1\ 1.0001^{-1} &\approx 0.9999 \end{align} $$
人们常常会混淆代币的价格与 ticks。正如上面所提到的,ticks 是价格曲线上作为参考点的标记,类似于道路上的里程标记。它们也是价格,因为它们是价格曲线上的点,并由公式 $p(i)=1.0001^i$ 定义。
代币的价格是其当前价格:它是价格曲线上在 swaps 过程中变化的一个点。例如,一个代币现在的价格可能是 10,而未来某个时刻可能是 33.2。当价格变化时,它在曲线上移动并跨越 ticks,就像汽车在路上行驶跨越里程标记一样。
偶尔,代币的价格会正好匹配给定 tick 的价格,但通常,代币的价格不会与任何 ticks 匹配。这类似于汽车在道路上停在一个有里程标记的位置上。
代币价格通过 swaps 如何确定将在本书的几章中讨论。应该清楚的是,公式 $p(i)=1.0001^i$ 仅仅是定义 ticks 的一种方式,这些 ticks 充当价格曲线上的标记。它们是流动性可以调整的唯一位置。
根据 Uniswap V3 白皮书,选择 1.0001 的原因是
“这具有每个 tick 与其邻近 ticks 相距 0.01%(1 基点)的理想特性。”
在 ticks 之间使用基点表示价格的相对差异,而不是绝对差异。
例如,绝对差异,比如 0.1 美分,可能根据代币的价格而有很大不同。对于封闭在 100,000 美元的代币,0.1 美分是微不足道的,而对于价值 1 美分的代币来说,它代表了一个显著的变化。
相反,基点之间的差异确保了相同的相对比例。例如,对于价格为 100,000 美元的代币,1 个基点的差异等于 10 美元,而对于价值 1 美分的代币,1 个基点等于 0.0001 美分。尽管 10 美元和 0.000001 美元的绝对值不同,但它们代表了相对于代币价格的相同相对变化。
因此,我们希望邻近的 ticks 之间的差异对于价值 100,000 美元的代币是 10 美元,而的价值 1 美分的代币是 0.000001 美元。
让我们看看一些示例,来查看相邻 ticks 之间 1 基点的变化:
对于 tick 100000 和 100001,我们有以下价格:
$$ \begin{align} 1.0001^{100000} &=\textcolor{red}{2201}5.456048527954 \ 1.0001^{100001}&=\textcolor{red}{2201}7.657594132810\end{align} $$
(前四位数字为了清晰度进行了高亮)。我们可以看到,差异约为 2.201545604853891(大约为 1 基点或 0.01% 的 22015)对于大约 22015 个代币 Y 的代币。
$$ \begin{align} 1.0001^{10}=\textcolor{red}{1.001}000450120021 \ 1.0001^{11}=\textcolor{red}{1.001}100550165033 \end{align} $$
现在差异为 0.000100100045012(0.0001 或大约 1 基点),对应价值约为 1 个代币 Y 的代币。
当我们写 ($p_a$, $p_b$) 时,假设 $p_a$ 和 $p_b$ 是 ticks,我们指的是 $p_a$ 和 $p_b$ 之间的价格范围,不包括边界。此上下文中,$p_a$ 称为 下 tick,而 $p_b$ 称为 上 tick。
当我们写一个 tick 范围为 (−10,10) 时,我们实际上引用的是 $(p_a(−10),p_b(10))$,其中 -10 和 10 是 tick indexes,$p_a(−10)$ 和$p_b(10)$ 是对应的 tick prices。
在下面的插图中,我们看到 pa 和 pb 之间的一个 tick 范围示例。
价格曲线的另一种常见表示方式是作为一条线。价格曲线在 x 和 y 轴上绘制,而在数轴上每一点表示一个价格。
在曲线上,代币 X 的价格增加时向上并向左移动,而在直线上,价格则从左到右增加:
https://img.learnblockchain.cn/2025/03/07/twoTravelingDots.mp4
因此,我们有时会使用以下线图来表示曲线的价格和流动性,其中蓝色区域表示包含在 tick 范围内的流动性。
我们可以在相应的线性图上如下绘制流动性水平:
https://img.learnblockchain.cn/2025/03/07/linePlotAndCurveWithTicks.mp4
下面是一个交互式工具(截图),进一步说明这两种表示方式显示的信息相同。通过移动 k 滑块更改价格区间的流动性,然后单击“扫描价格”。单击扫描价格后,将为两个图表(针对笛卡尔图的红色射线和针对线性图的红色点)显示价格指示器。注意笛卡尔图上的红色射线如何跟踪线性图上的红点。
我们期望读者理解价格曲线的两种表示:一种是在笛卡尔平面(x-y 轴),另一种是作为一条线。这两种图表将在章节中用来解释概念。
在 Uniswap V2 中,x 的值是池中持有的代币 X(储备)的字面量(y 亦然)。然而在 Uniswap V3 中,“储备”是一个更复杂的概念,因为每个曲线段持有不同数量的代币 X 和/或 Y。
仍然有帮助将 x 和 y 轴视为测量“代币”的“数量”,但对这一“数量”有相当多的细微差别,因此我们在后面的章节中再作讨论。我们提到这一点是为了防止将轴错误地解释为代币的价格,因为仅标记一个轴为“x”可能在没有澄清的情况下产生歧义。
Uniswap V3 跟踪 “活动 tick” 或 “当前 tick”或有时仅称为 “tick”。“当前 tick” 是当前价格向下取整到最接近的 tick。 如果价格上升并跨越了一个 tick,那么刚刚跨越的 tick 就成为当前 tick。“跨越”并不要求价格“经过”该 tick。如果价格停在该 tick 上,则该 tick 被视为已跨越。
如果价格下降并跨越了一个 tick, 则它必须跨越先前的当前 tick,因此该 tick 下面的 tick 就成为新的当前 tick。
下面的交互式工具展示了协议如何选择一个 tick 作为当前 tick。移动工具顶部的滑块,查看 ticks(灰色)如何在价格跨越时变为当前 tick(绿色):
slot0
变量持有当前 tick协议在名为 slot0
的 结构体 中存储当前 tick (代码链接)。这个变量是公开的,因此任何人都可以在 Etherscan 上可以直接查询 slot0
获取池的当前 tick。
下面我们展示 ETH:DAI 池在 Base 上的结果:
当前 tick 是 81143。该池为 ETH:DAI,因此价格以以太币相对于 DAI 表示。两个代币都有 18 位小数。计算当前 tick 81143 的价格得到 $p(81143)=1.0001^{81143}≈3340$。当前 tick 是当前价格向下取整到最接近的 tick。因此我们可以假设当前 tick 大约对应于当前价格。所以对于该池,1 ETH 大约值 3340 DAI。
让我们考虑另一个示例,以说明小数位如何影响价格和 tick。下面我们展示 ETH:USDC 池在 Base 上的结果:
此时,tick 为负,值为 -195186,价格可以计算为 $p(−195186)=1.0001^{−195186}≈3.3389×10^{−9}$ 。这里的 tick 为负是由于小数位数的不同,因为 ETH 有 18 位小数,而 USDC 只有 6 位小数。
假设 ETH 的价值为 1000 美元,ETH 的最小单位价值为 $1000/10^{18}$( 或 $1/10^{15}$ ),而 USDC 的最小单位价值为 $1/10^6$。因此,虽然我们假设 1 ETH “价值”超过 1 USDC,但考虑到其最小单位,1 个 USDC 的最小单位的价值却超过了 1 个 ETH 的最小单位。
在这个示例中,为了考虑小数位数的差异(ETH 为 18 位,对应的 USDC 为 6 位),我们需要将价格乘以 $10^{18−6}=10^{12}$。因此,该池的价格变为 $3.3389×10^{−9}×10^{12}≈3338.9$,这与 ETH:DAI 池中的值大致相同。
选择一个低成本的 L2 池并提供流动性。池的列表 在这里。注意你可以和不能提供流动性的地方。
- 原文链接: rareskills.io/post/unisw...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!