12.Uniswap V3 添加流动性案例

核心摘要 (Key Takeaways)

  • Uniswap V3 的核心是集中流动性:与 V2 的无限价格范围不同,V3 允许流动性提供者(LP)将资金集中在特定的价格区间 [Pa, Pb] 内,其底层数学模型可以看作是对 V2 的 x * y = k 模型进行了平移和截断。
  • 价格变动消耗特定 Token:在设定的价格区间内,当价格从当前价 P 下降至 Pa 时,池子会消耗 Y Token;当价格从 P 上升至 Pb 时,池子会消耗 X Token。理解这一点是计算所需流动性的关键。
  • 流动性计算基于虚拟曲线:尽管 V3 的真实曲线是分段的,但其流动性数量 ΔX\Delta XΔY\Delta Y 的计算可以基于 V2 的 xy=L2x \cdot y = L^2P=y/xP = y/x 这两个基础公式,应用在一条“虚拟”的、经过平移的曲线上进行推导。
  • 工程实现中的核心逻辑:在实际添加流动性时,智能合约会根据用户提供的两种 Token (ΔX\Delta XΔY\Delta Y) 分别计算出两个可能的流动性值 L,并最终选择较小的那个作为最终的流动性。这可能导致实际使用的某一种 Token 数量略少于用户最初提供的数量。

一、 Uniswap V3 核心公式与概念回顾

1. V3 流动性公式与曲线

Uniswap V3 引入了集中流动性的概念,允许将流动性提供在指定的价格区间 [Pa, Pb]。其公式可以理解为对 V2 公式的平移。

  • V2/V3 通用基础公式 (应用于虚拟曲线):

    • 恒定乘积公式: xy=L2x \cdot y = L^2 (其中 L 是流动性量)
    • 价格公式: P=yxP = \frac{y}{x}
  • V3 集中流动性公式 (体现真实与虚拟流动性的关系):

    (Xreal+LPb)(Yreal+LPa)=L2 (X_{real} + \frac{L}{\sqrt{P_b}}) \cdot (Y_{real} + L\sqrt{P_a}) = L^2

    这个公式描述了真实流动性(橙色曲线)是如何通过虚拟流动性(绿色曲线)平移而来的。在进行计算时,我们通常会利用更简洁的虚拟曲线公式。

    img

2. 真实流动性与虚拟流动性

  • 真实流动性 (Real Liquidity): 用户在指定价格区间 [Pa, Pb] 内实际存入的 XY Token 数量。
  • 虚拟流动性 (Virtual Liquidity): 为了方便使用 xy=L2x \cdot y = L^2 公式进行计算,我们将真实流动性曲线向左和向下平移,补全成一条完整的双曲线。这部分被“补全”的流动性就是虚拟流动性。
  • 计算的便利性:所有关于 ΔX\Delta XΔY\Delta Y 的计算都是在这条包含了虚拟流动性的完整双曲线(视频中的黄色曲线)上进行的,这样可以简化数学模型。

二、 价格区间内的 Token 消耗机制

这是理解添加多少流动性的核心前提。站在资金池的角度:

  • 价格下降,消耗 Y Token

    • 逻辑:当池内价格从 P 点向 A 点移动(价格下降)时,意味着市场正在用 X Token 换取池中的 Y Token。池中的 Y Token 数量减少,X Token 数量增加。
    • 结论:从价格 P 降到 Pa 的过程,消耗的是池子里的 Y Token
  • 价格上升,消耗 X Token

    • 逻辑:当池内价格从 P 点向 B 点移动(价格上升)时,意味着市场正在用 Y Token 换取池中的 X Token。池中的 X Token 数量减少,Y Token 数量增加。
    • 结论:从价格 P 升到 Pb 的过程,消耗的是池子里的 X Token
  • 案例

    • 假设当前价格点为 P,对应的池内 Token 数量为 (ΔX\Delta X, ΔY\Delta Y)。
    • 当曲线上的点沿着曲线向左下方移动至 A 点时,纵坐标 Y 的值减小,说明 Y Token 被消耗。
    • 当曲线上的点沿着曲线向右上方移动至 B 点时,横坐标 X 的值减小,说明 X Token 被消耗。

三、 流动性数量计算

三、 流动性数量 (\Delta X 和 \Delta Y) 计算

1. 基础公式推导

从两个基础公式 xy=L2x \cdot y = L^2P=y/xP = y/x 出发,可以推导出在任意价格 P 点,对应的 xy 的数量(在虚拟曲线上)。

  • 推导过程:

    1. y=Pxy = Px 代入 xy=L2x \cdot y = L^2
    2. 得到 x(Px)=L2    Px2=L2x \cdot (Px) = L^2 \implies Px^2 = L^2
    3. 解得 x2=L2/P    x=L/Px^2 = L^2/P \implies x = L / \sqrt{P}
    4. 再将 x 代入 y=Pxy = Px,得到 y=P(L/P)=LPy = P \cdot (L / \sqrt{P}) = L \cdot \sqrt{P}
  • 结论:

    X=LP X = \frac{L}{\sqrt{P}}

    Y=LP Y = L \cdot \sqrt{P}

2. 计算 \Delta X 和 \Delta Y

利用上述公式,我们可以计算出在特定价格范围内支持交易所需的 Token 数量。

  • 计算 ΔY\Delta Y (支持价格从 P 降到 Pa):

    • 这是 Y Token 的消耗量,等于 P 点的 Y 值减去 Pa 点的 Y 值。 ΔY=YPYPa=LPLPa=L(PPa) \Delta Y = Y_P - Y_{Pa} = L \cdot \sqrt{P} - L \cdot \sqrt{P_a} = L \cdot (\sqrt{P} - \sqrt{P_a})
  • 计算 ΔX\Delta X (支持价格从 P 升到 Pb):

    • 这是 X Token 的消耗量,等于 P 点的 X 值减去 Pb 点的 X 值。(注意:因为 P 越大,X 越小,所以是用低价对应的 X 减去高价对应的 XΔX=XPXPb=LPLPb=L(1P1Pb) \Delta X = X_P - X_{Pb} = \frac{L}{\sqrt{P}} - \frac{L}{\sqrt{P_b}} = L \cdot (\frac{1}{\sqrt{P}} - \frac{1}{\sqrt{P_b}})

四、 案例分析:添加流动性实践

案例 1: 理论计算题回顾

  • 问题: 用户有 ΔX\Delta X = 2 ETH,当前价格 P = 2000,目标价格区间是 [1500, 2500],求解需要多少 ΔY\Delta Y
  • 分析: 这是一个上述公式的直接应用。
    1. 已知 ΔX\Delta X, P, Pa=1500, Pb=2500
    2. 使用 ΔX\Delta X 的公式 ΔX=L(1P1Pb)\Delta X = L \cdot (\frac{1}{\sqrt{P}} - \frac{1}{\sqrt{P_b}}) 先反解出流动性 L
    3. 将求出的 L 代入 ΔY\Delta Y 的公式 ΔY=L(PPa)\Delta Y = L \cdot (\sqrt{P} - \sqrt{P_a}) 即可计算出所需的 ΔY\Delta Y

案例 2: 结合代码实现的完整流程

这是一个更贴近工程实践的案例,展示了从价格到最终流动性添加的完整步骤。

  • 场景设定:
    • 交易对: ETH/USDC
    • 当前价格 (P): 5000
    • 价格区间: [4545 (Pa), 5500 (Pb)]
    • 计划投入: 1 ETH 和 5000 USDC
流程图:添加流动性的工程实现步骤
flowchart TD
    A[用户提供Token和价格区间] --> B
    B[步骤一 价格转换为Tick索引] --> C
    C[步骤二 Tick索引转换为SqrtPriceX96格式] --> D
    D[步骤三 根据ΔX计算流动性L_x] --> F
    C --> E[步骤四 根据ΔY计算流动性L_y] --> F
    F[步骤五 选择较小的L作为最终流动性] --> G
    G[步骤六 使用最终L反推实际Token用量] --> H
    H[步骤七 完成流动性添加]
flowchart TD
    A[用户提供Token和价格区间] --> B
    B[步骤一 价格转换为Tick索引] --> C
    C[步骤二 Tick索引转换为SqrtPriceX96格式] --> D
    D[步骤三 根据ΔX计算流动性L_x] --> F
    C --> E[步骤四 根据ΔY计算流动性L_y] --> F
    F[步骤五 选择较小的L作为最终流动性] --> G
    G[步骤六 使用最终L反推实际Token用量] --> H
    H[步骤七 完成流动性添加]
flowchart TD
    A[用户提供Token和价格区间] --> B
    B[步骤一 价格转换为Tick索引] --> C
    C[步骤二 Tick索引转换为SqrtPriceX96格式] --> D
    D[步骤三 根据ΔX计算流动性L_x] --> F
    C --> E[步骤四 根据ΔY计算流动性L_y] --> F
    F[步骤五 选择较小的L作为最终流动性] --> G
    G[步骤六 使用最终L反推实际Token用量] --> H
    H[步骤七 完成流动性添加]

流程解释 上述图表展示了在 Uniswap V3 中添加流动性的实际后台计算流程。它始于用户的输入(两种代币数量和价格区间),经过一系列的格式转换(价格到Tick,再到合约内部使用的SqrtPriceX96)。核心步骤是合约会并行计算两种代币分别能提供的流动性L_xL_y,然后选择两者中较小的一个作为最终的有效流动性值。最后,根据这个最终的L值,反向计算出需要投入的精确代币数量,完成添加。

具体步骤详解:
  1. 从价格到 Tick

    • 概念: 在 V3 中,价格不是连续的,而是离散的点,每个点由一个整数索引 tick 表示。
    • 公式: P(i)=1.0001ii=log1.0001(P) P(i) = 1.0001^i \quad \Leftrightarrow \quad i = \log_{1.0001}(P)
    • 案例结果:
      • P = 5000 -> tick = 85176
      • Pa = 4545 -> tick = 84222
      • Pb = 5500 -> tick = 86129
    • 案例中的验证: 视频中提到,1.000185176 次方约等于 4999.9,由于计算中向下取整,所以非常接近 5000
  2. 从价格到 SqrtPriceX96

    • 概念: 为了计算精度和效率,智能合约内部不直接存储价格 P,而是存储其平方根并乘以 2^96定点数格式。
    • 公式: sqrtPriceX96=P296 \text{sqrtPriceX96} = \lfloor \sqrt{P} \cdot 2^{96} \rfloor
  3. 计算流动性 L

    • 逻辑转换: 前述理论公式计算的是从当前价P到边界所需的ΔX\Delta XΔY\Delta Y。在实际工程中,合约是根据用户提供的ΔX\Delta XΔY\Delta Y,来计算它们能为整个价格区间 [Pa, Pb] 提供多少流动性L
    • 公式:
      • ΔX\Delta X 计算: Lx=ΔX1Pa1PbL_x = \frac{\Delta X}{\frac{1}{\sqrt{P_a}} - \frac{1}{\sqrt{P_b}}}
      • ΔY\Delta Y 计算: Ly=ΔYPbPaL_y = \frac{\Delta Y}{\sqrt{P_b} - \sqrt{P_a}}
    • 核心决策: 最终采用的流动性是两者中的最小值L=min(Lx,Ly)L = min(L_x, L_y)
    • 案例结果: 计算出的最终 L 值为 1517...
  4. 反推实际使用的 Token 数量

    • 原因: 因为选择了最小的 L 值,意味着其中一种 Token 并没有被完全使用,只会使用与那个较小的L值相匹配的数量。
    • 过程: 使用最终确定的 L 值,再通过公式反向计算出与这个 L 精确匹配的 ΔX\Delta XΔY\Delta Y
    • 案例结果:
      • 初始提供: 1 ETH 和 5000 USDC。
      • 最终实际使用: 0.998976 ETH5000 USDC
      • 结论: 在这个价格区间和当前价格下,5000 USDC 是限制因素,它所能提供的流动性(L_y)小于 1 ETH 所能提供的流动性(L_x)。因此,合约只使用了足以匹配 5000 USDC 的 ETH 数量。