本文详细介绍了Uniswap V2中协议费的收集机制,包括费用计算的数学公式、代码实现以及影响因素。文章还指出了在流动性提供者调用mint
或burn
时收集费用的效率问题,并通过示例和代码片段深入解释了_mintFee
函数的工作方式。
Uniswap V2 旨在将 1/6 的交换手续费收归协议。由于交换手续费为 0.3%,因此 1/6 的手续费为 0.05%。所以每笔交易的 0.05% 将会归协议所有。
尽管这个功能实际上并没有被激活,但我们还是讨论这个功能,毕竟一些分叉可能会使用它。此外,这个计算很容易出错,现在花时间理解它将有助于你在类似计算中捕捉错误。
你需要熟悉 Uniswap V2 Book 中的所有前面的章节,才能跟上后面的内容。
在每笔交易中收取 0.05% 的手续费效率低下,因为这需要额外的代币转移。转移 ERC20 代币需要更新存储,因此将代币转移到两个地址而不是一个会产生更高的成本。
因此,当流动性提供者调用 burn 或 mint 时收取手续费。由于这些操作相对于 [交换代币](swapping tokens) 来说是不频繁的,这将节省 gas 费用。为了收取 mintFee
,合约计算自上次发生以来收集的手续费,并铸造足够的 LP 代币到受益地址,使受益人有权获得 1/6 的手续费。
为避免术语混淆,我们将“费用”称为在交换过程中从交易者那里收取的 0.3%,而“mintFee”则是 0.3% 费用的 1/6。是的,这两者都使用费用这个词并不是很好,但这就是我们必须处理的内容。
流动性是池中代币余额乘积的平方根。这个公式的理由在 Uniswap V2 交换函数文章中有所讨论。一些文献将其称为 $\sqrt{k}$,其中 $k = xy$,$x$ 和 $y$ 是池中的代币余额($x$ 和 $y$ 的储备)。我们用 $\ell$ 来表示流动性,因为它写起来比 $\sqrt{k}$ 短。
为了使这个有效,Uniswap V2 依赖以下两个不变性:
mint()
和 burn()
,池的流动性只能增加。mint()
或 burn()
交易以来流动性增加的情况,池知道收集了多少手续费。这些将是写好的 不变性测试,但目前我们将理所当然地认为它们是真实的。
假设在 $t_1$ 时池中有 10 个 token0
和 10 个 token1
。
经过大量交易和手续费收集后,$t_2$ 时新的池余额为 40 个 token0
和 40 个 token1
。
流动性被测量为两个代币的乘积的平方根,即 $\ell = \sqrt{k}$。在 $t_1$ 时流动性为 10,在 $t_2$ 时流动性为 40。反过来说,$\ell_1 = 10$ 和 $\ell_2 = 40$。我们将对从 10 增长到 40 的部分收取手续费。
在 $t_2$ 时,总共 40 的流动性中有 30 单位是由于交换手续费。
我们想要铸造足够的 LP 代币,即“mintFee”,使得受益人可以获得池中“费用部分”的 1/6。也就是说,他们应该有权获得 5 单位的流动性 (30 / 6),这是来自利润的。
协议不应收取任何“原始流动性”的费用,即 $\ell_1$。协议应仅收取增量的费用,即 $\ell_2 - \ell_1$。
当调用 mint()
或 burn()
时,Uniswap 为协议费用接收方铸造 LP 代币。这会导致稀释,使得 当前供应 的 LP 代币可以赎回原始流动性加上 5/6 的“利润流动性”(来自交换手续费的流动性)。
让我们使用以下符号:
$s$ 为稀释前的 LP 代币供应量。
$\eta$ 为将铸造到协议的 LP 代币数量。它应足以进行 1/6 的利润流动性的赎回。
$\ell_1$ 为原始存款的流动性,即 LP 提供的流动性。
$\ell_2$ 为原始存款的流动性以及由于交换手续费而得到的流动性。
$d$ 为除去协议手续费后应支付给 LP 的流动性金额。也就是说,LP 应获得其原始存款和 5/6 的利润。
$p$ 为协议应获得的流动性金额。这是 5/6 的 $\ell_2 - \ell_1$。
要计算 $\eta$,我们观察到以下不变性必须为真:
$$ \frac{\eta}{p}=\frac{s}{d} $$
换句话说,前总供应量 $s$ 的 LP 代币可以赎回应给 LP 的流动性,而 $\eta$ 的 LP 代币可以赎回应给协议的金额。
下图解决了关于流动性变化时 $\eta$ 的推导问题。
_mintFee()
代码考虑到这个推导,Uniswap V2 的大部分 _mintFee
函数应该是不言自明的。以下是一些符号的变更:
rootK
kLast
totalSupply
feeOn
标志开关,该标志我们尚未讨论我们将进一步深入讨论此功能,但首先我们想指出 kLast
是在哪里更新的。
kLast
更新的位置在上面的代码中,kLast 只有在 feeOn
设为 false
时才会设置。它在 mint 和 burn 完成时设置,但不是在 swap 时设置,因为我们希望测量由于交换而引起的流动性增长,涉及存款和取款事件。从标记的黄色框中可以看到设置 kLast
的位置。
kLast
kLast
_mintFee
代码条件现在我们理解了 kLast 如何更新,我们可以充分解释 _mintFee 函数。
让我们考虑上面代码片段中的可能性,重复以方便理解。
feeOn
为 false
,什么都不会铸造(绿色高亮)feeOn
为 false
,kLast
为零(黄色高亮)feeOn
为 false
,kLast
不是零(黄色高亮)feeOn
为 true
,但流动性没有增长(橙色高亮)feeOn
为 true
,并且流动性有所增长(橙色高亮),因此适用铸造费用(蓝色高亮)在决策树中更容易看到逻辑,因此这就是与 if
语句的分支颜色相同的决策树。
请查看我们的 区块链训练营 以了解我们的课程提供。
原始发布于 2023 年 11 月 14 日
- 原文链接: rareskills.io/post/unisw...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!