16.Uniswap V3 LiquidityNet

核心摘要 (Key Takeaways)

  • 智能合约的效率限制:Uniswap v3 智能合约因存储和计算成本的限制,不会记录每个价格区间的具体流动性值。它采用一种更高效的动态计算方法,只在流动性发生变化的 tick 边界点记录关键信息。
  • 核心概念 Liquidity Net:合约追踪流动性的核心是记录在每个被初始化的 tick 上的 Liquidity Net (流动性净变化量)。这是一个有符号的值 (ΔL),表示当价格穿越该 tick 时,池中活跃流动性的变化量
  • 边界点的符号规则:当用户在 [tick_lower, tick_upper] 区间添加流动性 L 时,合约会在 tick_lower 处记录一个正的净变化量 (+L),在 tick_upper 处记录一个负的净变化量 (-L)。移除流动性则符号相反。
  • 流动性的动态计算:在交易(swap)过程中,当价格从一个区间穿越 tick 进入下一个区间时,合约通过一个简单的累加公式实时更新当前的活跃流动性:L_{新区间} = L_{旧区间} + \Delta L_{tick}。这使得合约只需追踪当前区间的流动性,而无需预存所有区间的流动性信息。

1. 背景问题:Uniswap v3 如何高效追踪流动性?

在 Uniswap v3 中,流动性提供者可以在任意自定义的价格区间(由 tick 界定)内提供流动性。这导致了池子的总流动性在不同价格段是阶梯式变化的。

  • 面临的挑战:一笔大的交易可能会跨越多个这样的价格区间。智能合约需要一种方法来准确知道每个区间的活跃流动性 L 是多少,以便进行精确的兑换计算。
  • 需要避免的低效方案:在合约中为每一个 tick 区间(如 tick_0tick_1tick_1tick_2…)单独存储其对应的流动性值。这种方式会消耗巨大的存储空间,并且在计算上非常低效,不符合智能合约设计的经济性原则。
  • 解决方案:合约只在关键节点(即用户添加或移除流动性的 tick 边界)记录信息,并在交易发生时动态地、即时地计算出当前价格区间的流动性。
    • 合约通过 tick bitmap 结构来标记哪些 tick 上存在流动性边界。
    • 而在这些被标记的 tick 上,合约存储的核心数据就是 Liquidity Net

2. 案例前瞻:视频中的动态演示解析

在深入理论之前,视频开头和结尾通过一个动态图表演示了流动性 L 随着价格 tick 变化的实际过程。这个例子完美地展示了我们将要学习的核心机制。

  • 场景:价格 tick 从左到右(从低到高)移动,穿越多个由不同用户设置的流动性区间。
  • 观测到的现象
    1. 初始状态:价格在任何流动性区间之外,当前活跃流动性 L = 0
    2. 穿越第一个 tickL0 变为 253。这说明在该 tick 上的 Liquidity Net+253
    3. 穿越下一个 tickL253 变回 0。这说明该 tick 上的 Liquidity Net-253
    4. 继续移动,穿越新的 tickL0 变为 80 (ΔL = +80)。
    5. 流动性叠加:价格紧接着穿越了两个非常接近的 tick 边界:
      • 第一个边界使得 L80 变为 401。计算可得 ΔL = 401 - 80 = +321
      • 第二个边界使得 L401 变为 519。计算可得 ΔL = 519 - 401 = +118
    6. 流动性减少:随着价格继续升高并穿出某些区间,L 值会相应地阶梯式下降。
  • 结论:这个动态演示直观地证明了,合约并不是直接知道每个区间的 L 值,而是通过在穿越 tick 时,将当前 L 值与该 tick 记录的 Liquidity Net 进行加法运算,从而实时更新 L 值。

3. 核心概念:流动性净变化量 (Liquidity Net)

Liquidity Net (流动性净变化量) 是 Uniswap v3 用于追踪流动性变化的核心数据结构。

  • 定义:当价格从低到高(从左到右)穿越一个 tick 时,池子中活跃流动性净变化量ΔL)。
  • 关键特性
    1. 净变化量 (Net Change):它只记录变化的量,而非该区间的绝对流动性值。
    2. 有符号值 (Signed Value):它可以是正数(增加流动性)或负数(减少流动性)。
  • 工作原理
    • 当一个用户在 [tick_lower, tick_upper] 区间添加了 L 数量的流动性:
      • tick_lower 这个点,当价格从左侧穿越过来时,活跃流动性增加了 L。因此,合约记录 Liquidity Net+L
      • tick_upper 这个点,当价格从左侧穿越出去时,这部分流动性失效,活跃流动性减少了 L。因此,合约记录 Liquidity Net-L

4. 案例分析:Liquidity Net 的计算与应用

案例 1:添加流动性

  • 场景:一个用户在 tick 区间 [1000, 2000] 内添加了 1000 单位的流动性 L
  • Liquidity Net 记录
    • T = 1000 时,Liquidity Net 增加 1000 ( += 1000 )。
    • T = 2000 时,Liquidity Net 减少 1000 ( -= 1000 )。
  • 解释:使用 +=-= 是因为可能已经有其他用户在该 tick 上设置了流动性边界,合约需要将新操作的净变化量叠加到已有的值上。

案例 2:移除流动性

  • 场景:一个用户从 tick 区间 [1000, 2000] 内移除了 400 单位的流动性 L
  • Liquidity Net 记录
    • T = 1000 时,Liquidity Net 减少 400 ( -= 400 )。因为原本在此处应该增加的流动性变少了。
    • T = 2000 时,Liquidity Net 增加 400 ( += 400 )。因为原本在此处应该移除的流动性变少了,相当于反向增加了这部分流动性。

案例 3:多用户流动性叠加(复杂场景)

这是理解 Liquidity Net 如何协同工作的关键案例。

  • 场景
    • 用户1:在 tick 区间 [1000, 2000] 添加 1000 单位流动性。
    • 用户2:在 tick 区间 [1500, 2500] 添加 2000 单位流动性。
  • Liquidity Net 的统计:合约会在四个关键 tick 上记录净变化量。
    • tick = 1000: ΔL = +1000 (来自用户1的添加)
    • tick = 1500: ΔL = +2000 (来自用户2的添加)
    • tick = 2000: ΔL = -1000 (用户1的流动性在此结束)
    • tick = 2500: ΔL = -2000 (用户2的流动性在此结束)
  • 结果:通过这些净变化量,我们可以推导出每个价格区间的总流动性,如下所示:
    • 区间 (< 1000): L = 0
    • 区间 [1000, 1500): L = 1000
    • 区间 [1500, 2000): L = 1000 + 2000 = 3000
    • 区间 [2000, 2500): L = 3000 - 1000 = 2000
    • 区间 (> 2500): L = 2000 - 2000 = 0

5. 核心机制:流动性的动态更新流程

合约利用 Liquidity Net 在交易过程中动态计算当前活跃的流动性。

  • 计算公式:当价格从左到右(价格升高)穿越一个 tick 时,下一个区间的流动性按以下公式更新: L_{下一个区间} = L_{当前区间} + \Delta L_{穿越的tick} 其中,\Delta L_{穿越的tick} 就是存储在该 tick 上的 Liquidity Net 值。
flowchart TD
    A[价格从低向高变化] --> B{是否穿越tick?}
    B -- 否 --> C[流动性保持不变]
    B -- 是 --> D[获取当前tick的liquidity net值]
    D --> E[更新流动性: L_next = L_current + ΔL]
    E --> F{是否继续穿越下一个tick?}
    F -- 是 --> B
    F -- 否 --> G[结束]
flowchart TD
    A[价格从低向高变化] --> B{是否穿越tick?}
    B -- 否 --> C[流动性保持不变]
    B -- 是 --> D[获取当前tick的liquidity net值]
    D --> E[更新流动性: L_next = L_current + ΔL]
    E --> F{是否继续穿越下一个tick?}
    F -- 是 --> B
    F -- 否 --> G[结束]
flowchart TD
    A[价格从低向高变化] --> B{是否穿越tick?}
    B -- 否 --> C[流动性保持不变]
    B -- 是 --> D[获取当前tick的liquidity net值]
    D --> E[更新流动性: L_next = L_current + ΔL]
    E --> F{是否继续穿越下一个tick?}
    F -- 是 --> B
    F -- 否 --> G[结束]

流程解释

  1. 价格开始从低向高变化
  2. 系统检查是否穿越当前tick
  3. 如果没有穿越,流动性保持不变
  4. 如果穿越,获取当前tick的liquidity net值
  5. 使用公式更新流动性值
  6. 检查是否需要继续穿越下一个tick
  7. 如果需要,回到步骤2;否则结束流程