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_0到tick_1,tick_1到tick_2…)单独存储其对应的流动性值。这种方式会消耗巨大的存储空间,并且在计算上非常低效,不符合智能合约设计的经济性原则。 - 解决方案:合约只在关键节点(即用户添加或移除流动性的
tick边界)记录信息,并在交易发生时动态地、即时地计算出当前价格区间的流动性。- 合约通过
tick bitmap结构来标记哪些tick上存在流动性边界。 - 而在这些被标记的
tick上,合约存储的核心数据就是Liquidity Net。
- 合约通过
2. 案例前瞻:视频中的动态演示解析
在深入理论之前,视频开头和结尾通过一个动态图表演示了流动性 L 随着价格 tick 变化的实际过程。这个例子完美地展示了我们将要学习的核心机制。
- 场景:价格
tick从左到右(从低到高)移动,穿越多个由不同用户设置的流动性区间。 - 观测到的现象:
- 初始状态:价格在任何流动性区间之外,当前活跃流动性
L = 0。 - 穿越第一个
tick:L从0变为253。这说明在该tick上的Liquidity Net是+253。 - 穿越下一个
tick:L从253变回0。这说明该tick上的Liquidity Net是-253。 - 继续移动,穿越新的
tick:L从0变为80(ΔL = +80)。 - 流动性叠加:价格紧接着穿越了两个非常接近的
tick边界:- 第一个边界使得
L从80变为401。计算可得ΔL = 401 - 80 = +321。 - 第二个边界使得
L从401变为519。计算可得ΔL = 519 - 401 = +118。
- 第一个边界使得
- 流动性减少:随着价格继续升高并穿出某些区间,
L值会相应地阶梯式下降。
- 初始状态:价格在任何流动性区间之外,当前活跃流动性
- 结论:这个动态演示直观地证明了,合约并不是直接知道每个区间的
L值,而是通过在穿越tick时,将当前L值与该tick记录的Liquidity Net进行加法运算,从而实时更新L值。
3. 核心概念:流动性净变化量 (Liquidity Net)
Liquidity Net (流动性净变化量) 是 Uniswap v3 用于追踪流动性变化的核心数据结构。
- 定义:当价格从低到高(从左到右)穿越一个
tick时,池子中活跃流动性的净变化量(ΔL)。 - 关键特性:
- 净变化量 (Net Change):它只记录变化的量,而非该区间的绝对流动性值。
- 有符号值 (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单位流动性。
- 用户1:在
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[结束]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[结束]
流程解释:
- 价格开始从低向高变化
- 系统检查是否穿越当前tick
- 如果没有穿越,流动性保持不变
- 如果穿越,获取当前tick的liquidity net值
- 使用公式更新流动性值
- 检查是否需要继续穿越下一个tick
- 如果需要,回到步骤2;否则结束流程