跳到主要内容

07 Position 收益结算

概述

在前一章中,我们已经推导出:

finside=fgfb(il)fa(iu)f_{\text{inside}} = f_g - f_b(i_l) - f_a(i_u)

并且得到了区间内累计手续费的统一表达方式。但是,这里仍然有一个关键问题没有解决:LP 的收益是如何在合约中被记录和结算的?

1. 核心问题:为什么不能实时分配手续费?

在 V2 中:

  • 所有流动性是全局的
  • fee 直接进入池子储备
  • LP 通过 share 自动持有

但在 V3 中:

  • liquidity 是分区间的
  • 不同 LP 在不同时间参与 swap
  • 无法逐笔分配

如果每次 swap 都给每个 LP 分钱 gas 直接爆炸。

2. V3 的核心思想:延迟结算(Lazy Settlement)

V3 并不会在每一笔 swap 时给 LP 分配手续费,而是先累计,后结算。

每个 position 会记录:


feeGrowthInside0LastX128
feeGrowthInside1LastX128

tokensOwed0
tokensOwed1

当一个 LP 创建或更新仓位时,会记录:

fentry=finside at entryf_{\text{entry}} = f_{\text{inside}} \ \text{at entry}

记为:

flastf_{\text{last}}

当前时刻

fnow=当前区间内的 feeGrowthf_{\text{now}} = \text{当前区间内的 feeGrowth}

收益计算

Δf=fnowflast\Delta f = f_{\text{now}} - f_{\text{last}}

从 feeGrowth 到真实收益

fg=fiLif_g = \sum \frac{f_i}{L_i}

它表示单位 liquidity 的收益。因此,LP 实际收益为:

tokensOwed=L(fnowflast)\text{tokensOwed} = L \cdot (f_{\text{now}} - f_{\text{last}})

其中:

  • LL = LP 的 liquidity
  • fnowf_{\text{now}} = 当前区间内累计 fee
  • flastf_{\text{last}} = 上次结算时的快照

核心公式:

tokensOwed+=L(finside, nowfinside, last)\text{tokensOwed} += L \cdot \left( f_{\text{inside, now}} - f_{\text{inside, last}} \right)

3. 什么时候会结算?

V3 不会自动发钱,只有在以下操作时才会触发:

1. mint(开仓)
  • 初始化 position
  • 记录初始快照:
flast=finsidef_{\text{last}} = f_{\text{inside}}
2. increaseLiquidity(加仓)

在加仓前必须先结算:

tokensOwed+=L(finside, nowfinside, last)\text{tokensOwed} += L \cdot \left( f_{\text{inside, now}} - f_{\text{inside, last}} \right)

然后更新:

flast=finsidef_{\text{last}} = f_{\text{inside}}

再增加 liquidity,因为新增加的 liquidity 不应该获得历史收益。

3. decreaseLiquidity(减仓)

同样:

  • 先结算旧收益
  • 再减少 liquidity
4. collect(领取收益)

collect 不计算收益,它只做一件事 transfer(tokensOwed) ,然后 tokensOwed = 0

举个例子🌰:

假设:

LP 提供 liquidity:S = 100 初始:

flast=10f_{\text{last}} = 10

单位:token / liquidity

一段时间后:

fnow=15f_{\text{now}} = 15

那么:

Δf=5\Delta f = 5

表示每单位 liquidity 多赚了 5 token,因此总收益:

tokensOwed=1005=500\text{tokensOwed} = 100 \cdot 5 = 500

注: 在真实 Uniswap V3 合约中:feeGrowthInsideX128 实际是:

feeGrowthInsideX128=f2128\text{feeGrowthInsideX128} = f \cdot 2^{128}

LP 收益计算(链上真实公式):

tokensOwed=Lfinside, nowfinside, last2128\text{tokensOwed} = L \cdot \frac{f_{\text{inside, now}} - f_{\text{inside, last}}}{2^{128}}

注:这里的 f 表示单位 liquidity 的累计收益(fee / liquidity),而不是实际 token 数量。