Uniswap Part Ⅳ | 交易手续费
交易手续费
计算原理
以普通用户的视角来看,对比 Uniswap v2,Uniswap v3 在手续费方面做了如下改动:
- 添加流动性时,手续费可以有 3个级别供选择:0.05%, 0.3% 和 1%,未来可以通过治理加入更多可选的手续费率
- Uniswap v2 中手续费会在收取后自动复投称为 LP 的一部分,即每次手续费都自动变成流动性加入池子中,而 Uniswap v3 中收取的手续费不会自动复投(主要是为了方便合约的计算),需要手动取出手续费
- 不同手续费级别,在添加流动性时,价格可选值的最小粒度也不一样(这个是因为 tick spacing 的影响),一般来说,手续费越低,价格可选值越精细,因此官方推荐价格波动小的交易对使用低费率(例如稳定币交易对)
以开发者的视角来看,Uniswap v3 的手续费计算相对会比较复杂, 因为它需要针对每一个 position
来进行单独的计算,为了方便计算,在代码中会将手续费相关的元数据记录在 position
的边界 tick 上(这些 tick 上还存储了 ΔLΔL 等元数据)。
当我们计算交易的手续费时,我们需要计算如下值:
- 每一个
position
收取的手续费(token0, token1 需要分别单独计算) - 用户如果提取了手续费,需要记录用户已提取的数值
v3 中有以下几个关于手续费的变量:
- 交易池中手续费的费率值,这里记录的值时以 1000000 为基数的值,例如当手续费为 0.03% 时,费率值为 300
- 全局状态变量
feeGrowthGlobal0X128
和feeGrowthGlobal1X128
,分别表示 token0 和 token1 所累计的手续费总额,使用了Q128.128
浮点数来记录 - 对于每个 tick,记录了
feeGrowthOutside0X128
和feeGrowthOutside1X128
,这两个变量记录了发生在此 tick 「外侧」的手续费总额,外侧指的是与当前价格所对应的 tick 相对于 tick i 的相反侧。 - 对于每个
position
,记录了此position
内的手续费总额feeGrowthInside0LastX128
和feeGrowthInside1LastX128
,这个值不需要每次都更新,它只会在position
发生变动,或者用户提取手续费时更新
需要注意的时,上面这些手续费状态变量都是每一份 LP 所对应的手续费,在计算真正的手续费时,需要使用 LP 数相乘来得出实际手续费数额,又因为 LP 数在不同价格可能时不同的(因为流动性深度不同),所以在计算手续费时只能针对 position
进行计算(同一个 position
内 LP 总量不变)。
代码实现
手续费更新的代码在前面有提到过
这里看一下手续费的提取
core中手续费的提取是以 position
为单位进行提取的。使用 UniswapV3Pool.collect
提取手续费:
1 |
|
但是这里 posiiton
中的手续费可能并不是最新的(手续费总数只会在 position
的流动性更新时更新)。因此在提取手续费前,需要主动触发一次手续费的更新,这些操作已经在 uniswap-v3-periphery
仓库中进行了封装。
1 |
|
这个函数就是先用 pool.burn
函数来触发 pool 中 position
内手续费总额的更新,使其更新为当前的最新值。调用时传入参数的 Liquidity 为 0,表示只是用来触发手续费总额的更新,并没有进行流动性的更新。更新完成后,再调用 pool.collect
提取手续费。
Uniswap Part Ⅳ | 交易手续费
http://sissice.github.io/2022/09/29/uniswap-v3-4/