以普通用户的视角来看,对比 Uniswap v2,Uniswap v3 在手续费方面做了如下改动:
添加流动性时,手续费可以有 3个级别供选择:0.05%, 0.3% 和1%,未来可以通过治理加入更多可选的手续费率Uniswap v2 中手续费会在收取后自动复投称为 LP 的一部分,即每次手续费都自动变成流动性加入池子中,而Uniswap v3 中收取的手续费不会自动复投(主要是为了方便合约的计算),需要手动取出手续费不同手续费级别,在添加流动性时,价格可选值的最小粒度也不一样(这个是因为 tick spacing 的影响),一般来说,手续费越低,价格可选值越精细,因此官方推荐价格波动小的交易对使用低费率(例如稳定币交易对)
以开发者的视角来看,Uniswap v3 的手续费计算相对会比较复杂, 因为它需要针对每一个position来进行单独的计算,为了方便计算,在代码中会将手续费相关的元数据记录在position的边界 tick 上(这些 tick 上还存储了 $\Delta L$ 等元数据)。

手续费的计算和存储

在之前的文章中说过,一个交易对池的流动性,是由不同的流动性组合而成,每一个流动性的提供者都可以设置独立的价格范围区间,这个被称为positon. 当我们计算交易的手续费时,我们需要计算如下值:
每一个position收取的手续费(token0, token1 需要分别单独计算)用户如果提取了手续费,需要记录用户已提取的数值
v3 中有以下几个关于手续费的变量:
交易池中手续费的费率值,这里记录的值时以 1000000 为基数的值,例如当手续费为 0.03% 时,费率值为 300全局状态变量feeGrowthGlobal0X128和feeGrowthGlobal1X128,分别表示 token0 和token1 所累计的手续费总额,使用了Q128.128浮点数来记录对于每个 tick,记录了feeGrowthOutside0X128和feeGrowthOutside1X128,这两个变量记录了发生在此 tick 「外侧」的手续费总额,那么什么「外侧」呢,后文会详细说明对于每个position,记录了此position内的手续费总额feeGrowthInside0LastX128和feeGrowthInside1LastX128,这个值不需要每次都更新,它只会在position发生变动,或者用户提取手续费时更新
需要注意的时,上面这些手续费状态变量都是每一份 LP 所对应的手续费,在计算真正的手续费时,需要使用 LP 数相乘来得出实际手续费数额,又因为 LP 数在不同价格可能时不同的(因为流动性深度不同),所以在计算手续费时只能针对position进行计算(同一个position内LP 总量不变)。

计算过程

我们用 $f_g$ 表示代币池收取的手续费总额,对于一个 tick,其索引为 $i$,使用 $f_o(i)$ 表示此 tick 「外侧」的手续费总额,使用 $f_b(i)$ 表示低于此 tick 价格发生的交易的手续费总额,使用 $f_a(i)$ 表示高于此 tick 价格发生的交易的手续费总额。
如上图所示,对于 tick(i),有$f_g = f_a(i) + f_b(i)$

$f_o$ 的含义

$f_o$ 表示的是发生在此 tick $i$「外侧」的所有交易手续费总额。那么什么是「外侧」呢,外侧指的是与当前价格所对应的 tick 相对于 tick i 的相反侧。
$f_o(i)$ 与tick $i_c$ 之间的关系:
当$i_c