Uniswap V3中的价格波动限制

本文详细介绍了Uniswap v3中的tick索引的最小值和最大值,以及它们与价格存储的关系。文章解释了如何计算与最大价格2128对应的tick值,并探讨了使用int24来存储tick索引的原因,同时指出了在代码库中硬编码的最小和最大平方根比率值。

最小的 tick 在 Uniswap v3 中是 -887,272,最大的 tick 是 887,272。本章解释了这一范围背后的理由,它基于找到与协议中可以存储的最高价格相对应的 tick。

价格限制

在上一章中,我们看到协议将代币价格的平方根存储为 Q64.96 类型的固定点数字。这种类型的变量的最大整数值为 264。因此,它可以存储的最高价格为 2128。

这意味着协议无法处理超过 2128 的价格。换句话说,在 Uniswap v3 中,代币永远无法达到超过 2128 的实际价格。如果不遵守此限制,代币可能会达到协议无法存储的价格值。

因此,最高 tick 必须是对应于价格 2128 的 tick,以与最高价格保持一致。

最高 tick 索引

要计算对应于价格 2128 的 tick,让我们记住价格与 tick 之间的关系是由以下公式给出的:

$$p(i)=1.0001^i$$

通过对两边取以 1.0001 为底的对数,可以反转这个关系。

$$ \begin{align} \log{1.0001}(p(i)) &= \log{1.0001}(1.0001^i) \ \log_{1.0001}(p(i)) &= i \end{align} $$

因为对于任何底 b,有 $log_b(b^x)=x$。

上述公式允许我们根据价格 p(i) 计算 tick 索引 i。

现在,我们需要确定相对于最高可能代币价格 2128 的 tick 索引。

在上面的公式中使用 p(i)=2128,我们得到:

$i=log_{1.0001}(2^{128})=887272$

这个计算可以用 Python 进行如下操作:

from math import log
log(2**128,1.0001) # log_1.0001(2**128) = 887272 

因此,tick 索引 887,272 是协议使用的最高索引,因为大于 887,272 的 ticks 对应于超过 sqrtPriceX96 变量可以存储的最大值的价格。

最低 tick 索引

最低 tick 索引设置为 -887,272,这是最高可能 tick 的负数。

这种对称性是可取的,因为代币 X 相对于代币 Y 的价格是代币 Y 相对于代币 X 的价格的倒数。因此,限制代币价格的最小值为 2^-128 是可取的,这对应于 tick -887272。

代码库中的最小和最大值

最小和最大 ticks 索引在 Uniswap v3 TickMath 库中被硬编码为 MIN_TICKMAX_TICK

sqrtPriceX96 变量可以假定的最小值和最大值也分别被硬编码为 MIN_SQRT_RATIOMAX_SQRT_RATIO。这是可以在下面的截图中看到的,这些值将在后面的章节中计算。

Uniswap V3 TickMath 库中最小 tick 和最大 tick 的截图

Ticks 与价格平方根

在章节 在 Uniswap v3 中介绍 ticks 中,我们看到 ticks 是由以下公式定义的,

$$ p(i)=1.0001^i $$

其中 i 是 tick 索引。

可以使用价格的平方根而不是价格本身,并为给定 tick 索引计算价格的平方根。

为此,只需对上面的公式取平方根:

$$ \sqrt{p}= \sqrt{1.0001^i} = 1.0001^{\frac{i}{2}} $$

例如,要计算 tick 100 的 p,我们有 $\sqrt{p}{100}= 1.0001^{\frac{100}{2}} = 1.0001^{50} = 1.0050122696230506$。从这个信息,如果我们想要获得 p100(tick 100 的价格),我们只需平方它:$p{100} = \left(\sqrt{p}_{100} \right)^2$。

请注意,我们忽略了负平方根,仅保留了正平方根,因为价格不能为负。因此,我们可以始终通过平方平方根价格明确获取价格。

协议中允许的 Q64.96 的最高和最低平方根价格

MIN_SQRT_RATIOMAX_SQRT_RATIO 的值可以通过以下公式计算:

最低 tick 索引为 -887,272,所以允许的最低平方根价格为 $\sqrt{p}_{−887272}$。这可以计算为:

$$ \sqrt{{p}}_{−887272} = 1.0001^{\frac{-887272}{2}} = 1.0001^{-443636} $$

要将此值转换为固定点 Q64.96,需要将其乘以 296。因此,

$$ \text{MIN_SQRT_RATIO} = 1.0001^{-443636} \times 2^{96} \approx 4295128738.152353 $$

出现的一个问题是:我们应该向上舍入还是向下舍入这个值?假设我们向下舍入,这意味着变量 sqrtPriceX96 的最低可能值是 4295128738。换句话说,sqrtPriceX96 有可能达到 4295128738 的值。

这个值 4295128738 稍微低于 tick -887272(请记住,与 tick -887272 相关的值是 4295128738.152353)。因此,如果价格达到 4295128738,当前的 tick 将是向下舍入的最近 tick。这意味着它将是 -887273。 however, tick -887273 是不允许的,因为我们将 tick -887272 设置为最小。

因此,我们得出结论,向上舍入是必要的。也就是说,sqrtPriceX96 变量可以假定的最小值是 4295128739,因为它已被硬编码到代码库中。

最小平方根比率变量截图

这个计算可以用 Python 进行如下操作:

math.ceil(1.0001**(-887272/2)*(2**96)) # 4295128739

MAX_SQRT_RATIO 的计算是类似的,但这次我们使用最高可能的 tick 索引 887,272:

$$ \begin{align} \text{MAX_SQRT_RATIO} &=\sqrt{p(887272)} \cdot 2^{96} \ &= 1.0001^{\frac{887272}{2}} \cdot 2^{96} \ &=1461446703485210103287273052203988822378723970342 \end{align} $$

这个计算可以用 Python 执行,但会受到精度损失。在后面的章节中,我们将看到 Solidity 是如何精确执行这种计算,而没有精度损失的。

为什么使用 int24 作为 tick 索引

存储 887,272 所需的位数为$\log_2(887,272)\approx20$。由于我们也有负 ticks,我们需要存储两倍的 ticks。为了保存正数和它们的负值,我们的 tick 变量需要支持 21 位。

由于 Solidity 只支持倍数为 8 的 int 尺寸,这样所需的最小 int 尺寸是 int24。因此,Uniswap V3 使用 int24 以存储 tick 索引(代码链接),如下所示。

slot0 中的 tick 变量

总结

  • Tick 索引 i 的范围为-887,272 到 887,272。这些 ticks 分别代表协议中代币所能假设的最低和最高价格 p(i)=1.0001 和 p(i)=1.0001887272。
  • MIN_SQRT_RATIOMAX_SQRT_RATIO 的值表示允许的最小和最大平方根价格,格式为 Q64.96,正如协议中所定义。这些值在代码库中是硬编码的。
  • 原文链接: rareskills.io/post/unisw...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
RareSkills
RareSkills
https://www.rareskills.io/