在上一章中,我们引入了两个新概念:真实储备(real reserves)和虚拟储备(virtual reserves)。一个区间的真实储备是该区间内包含的代币数量—— 个代币 X、 个代币 Y,或者两者皆有。
虚拟储备是指如果该区间是无限曲线的一部分时,它将拥有的代币数量。虚拟储备是必要的,因为在 Uniswap v3 的曲线区间中,兑换(swap)的行为与 Uniswap v2 完全相同——也就是说,就好像这些区间实际上是 Uniswap v2 池中无限曲线的一部分一样。
我们还推导出了基于价格和流动性来计算区间虚拟储备的公式,但我们现在需要的是一个计算区间真实储备的公式。这是本章的主要目标。
Uniswap v3 中的流动性、价格和虚拟储备
在 Uniswap v3 中,流动性和价格的定义如下:
其中 和 是虚拟储备, 是代币 X 的价格(以代币 X 为单位表示)。
正如我们在上一章中所看到的,我们可以反转这些公式,并从 和 推导出虚拟储备 和 ,如下所示:
这并不足以推导出一个区间的真实储备,因为所有具有相同流动性且处于相同价格的区间都将具有相同的虚拟储备——无论它们的边界如何。
这可以从下面的动画中看出来,它展示了在相同价格下具有相同流动性的两个区间。它们的虚拟储备是相同的,但它们的真实储备却不同。
因此,我们不能仅通过价格和流动性来推导真实储备——还必须考虑区间的边界。
区间的真实储备
一个区间的真实储备,是指在该区间内可用于交易的代币数量,直到价格达到该区间的边界为止。
考虑下面图示的情况,此时价格位于区间内(左侧)。此时,池子拥有代币 X 的真实储备 以及代币 Y 的真实储备 。

在上方图像的右侧,一次兑换导致价格移动到上限 tick(upper tick),池子现在只持有代币 Y 的储备——不再持有代币 X。
这表明,一个区间内代币 X 的真实储备 ,等于在将价格移动到上限 tick 的兑换过程中,离开该区间的代币数量。
同样的逻辑也可以应用于代币 Y 的真实储备,但此时对应的是将价格移动到下限 tick 的兑换,如下所示。

的真实储备 ,等于在将价格移动到下限 tick 的兑换过程中,离开该区间的代币数量。
因此,如果我们可以计算出在第一个例子中有多少代币 X 离开了池子,以及在第二个例子中有多少代币 Y 离开了池子,我们就能推导出真实储备的公式。
并且我们可以做到这一点,因为我们知道如何在 Uniswap v2 中执行这些计算——而在一个区间内,Uniswap v3 的行为与 Uniswap v2 完全相同。
推导代币 X 的真实储备公式
在无限曲线的背景下,我们可以轻松计算出在改变价格的兑换中离开池子的代币 X 的数量。假设价格从 开始并移动到 ,其中 是上限 tick 的价格,如下所示。
在左侧,X 的虚拟储备由 表示(价格为 时的虚拟储备)。在右侧,虚拟储备由 表示(价格为 时的虚拟储备)。在 Uniswap v2 中,这些就是实际储备,因此在这个兑换中会有 个代币离开池子。
由于 Uniswap v3 与 v2 具有相同的行为,那么在 Uniswap v3 中也会有 个代币离开池子——这就是该区间中 X 的真实储备 。

因此,
而且我们知道如何根据流动性和价格来计算虚拟储备。请记住 。通过代入,我们得到
由于 小于或等于 , 将始终为正数或零。当 达到上限 tick 时,X 的真实储备将变为零,这符合预期。
当当前价格不在区间内时计算
让我们考虑 不在区间内的情况。
当前价格低于下限 tick
如果当前价格低于下限 tick,该区间的真实储备是下限 tick 和上限 tick 处虚拟储备之间的差值,如下所示。情况之所以如此,是因为我们可以假设 最终会到达 ,而从 到 的兑换不会影响该区间——我们只需要考虑从 到 的兑换。

通过代入 ,我们得到的真实储备为:
当前价格高于上限 tick
如果当前价格高于上限 tick,该区间内没有代币 X 的储备,因此真实储备 为零。
正如你所看到的,代币 X 的真实储备不仅取决于当前价格和流动性,还取决于上限 tick 和下限 tick。
推导代币 Y 的真实储备公式
为了计算一个区间中代币 Y 的真实储备,我们将采用与代币 X 相同的策略。让我们考虑下面的图示。当价格位于 时,我们在区间中有 个代币 Y(即 Y 的真实储备),如左侧所示,并且虚拟储备表示为 。
将价格移动到下限 tick 的兑换将移除该区间内的所有代币 Y。因此,要计算 ,我们只需要算出在 和 之间的兑换中离开该区间的代币 Y 的数量。

该兑换会将虚拟储备从 改变为 ,因此在这种情况下的真实储备计算为 。回想代币 Y 的虚拟储备由 给出,我们得出
由于 大于或等于 , 将始终为正数或零。当 达到下限 tick 时,Y 的真实储备变为零,这符合预期。
当当前价格不在区间内时计算
让我们考虑 不在区间内的两种情况。
当前价格高于上限 tick
如果价格大于上限 tick,代币 Y 的真实储备 如下所示,其中 是上限 tick 时 Y 的虚拟储备, 是下限 tick 时 Y 的虚拟储备。
要计算 ,我们需要考虑从上限 tick 到下限 tick 的兑换,这会将虚拟储备从 带到 。

因此,代币 Y 的真实储备为:
当前价格低于下限 tick
如果价格低于下限 tick,那么代币 Y 将没有真实储备,因此 为零。
计算任意两个价格之间的真实储备
给定一个区间内的任意两个价格,我们总是可以计算出它们之间的真实储备——换句话说,就是从一个价格移动到另一个价格所需的兑换出代币的数量。其推理过程与上方计算区间真实储备的过程相同,只是我们不再局限于将区间的端点作为交易的停止点。
两个价格之间的真实储备同样取决于当前价格是大于等于最高价、小于等于最低价,还是介于两者之间。
考虑计算价格 和 之间的真实储备,其中 ,当前价格为 ,且这些价格之间的流动性为 。我们需要考虑的三种情况如下所示。
- 当前价格低于下限 tick
- 当前价格高于上限 tick
- 当前价格介于上限和下限 tick 之间

公式将如下所示:
1. 当前价格低于下限 tick
在这种情况下,区间的所有真实储备均为代币 X。
2. 当前价格高于上限 tick
在这种情况下,区间的所有真实储备均为代币 Y。
3. 当前价格介于上限和下限 tick 之间
在这种情况下,区间同时拥有代币 X 和 Y。
在给定流动性 的情况下,协议使用这些公式来计算两个价格之间存在的代币 X 或 Y 的数量。
在兑换中使用上述公式
在 Uniswap v2 中,协议不需要担心兑换后的最终价格——最终价格是没有边界的,因为流动性是恒定的,且价格曲线是无限的。
在 Uniswap v3 中,区间是有限的,因此具有边界。当在一个区间内发生兑换时,协议必须计算该区间拥有多少真实储备,以确定用户想要交易的数量是可以完全在该区间内获得,还是只能部分获得。为此,协议会计算当前价格与区间边界之间的代币数量——如果用户买入代币 X,则为上限 tick;如果用户卖出代币 X,则为下限 tick。
在后续章节中,我们将详细研究 Uniswap v3 中兑换的工作原理。
我们上面推导的公式可以在 SqrtPriceMath.sol 库中找到。具体而言,可以在 getAmount0Delta 和 getAmount1Delta 函数中找到这些公式,它们的函数签名连同其 NatSpec 注释如下所示。
getAmount0Delta
此函数计算并返回两个价格(一个较低价格和一个较高价格)之间的代币 X 数量。文本中的红框展示了我们上面推导出的代币 X 的真实储备公式:。
该函数接收较低价格(sqrtRatioAX96)、较高价格(sqrtRatioBX96)、区间的流动性,以及一个指示金额是否应向上取整的标志位。

getAmount1Delta
此函数计算并返回两个价格(较低价格和较高价格)之间的代币 Y 数量。同样,文本中的红框展示了我们上面推导出的公式,只是现在对应的是代币 Y 的真实储备:。

总结
- 区间的真实储备不仅取决于流动性和当前价格,还取决于上限和下限 tick。
- 给定两个价格 和 ,且 ,在一个流动性为 的区间内,如果当前价格小于或等于 ,则这两个价格之间代币 X 的真实储备为 。
- 给定两个价格 和 ,且 ,在一个流动性为 的区间内,如果当前价格大于或等于 ,则这两个价格之间代币 Y 的真实储备为 。
练习题
- 假设当前价格位于 tick 0,并且在一个界定于 tick -10 到 10 的区间内,其流动性为 。该区间的代币 X 和 Y 的真实储备分别是多少?
- 与问题 (1) 的条件相同,但此时当前价格高于 tick 10。
- 与问题 (1) 的条件相同,但此时当前价格低于 tick -10。