03.Uniswap V2 手续费机制
目录
核心摘要 (Key Takeaways)
- 流动性增长:Uniswap V2 的千三(0.3%)交易手续费会直接重新注入到流动性池中,导致池子中的资产(X和Y)缓慢增长,进而使总流动性
K值不断增大。 - LP 收益机制:流动性提供者(LP)通过持有 LP Token (Share) 来证明其在池中的份额。当池子总流动性
K增长时,LP Token 的总份额不变,因此每个 LP Token 所代表的实际资产价值会提升,LP 在撤出流动性时能获得更多资产。 - 协议手续费 (PROTOCOL_FEE):Uniswap V2 设计了一种机制,允许协议开发者收取一部分手续费(例如千三的 1/6,即万分之五)。这通过增发新的 LP Token (Share) 给协议方来实现,从而让协议方在不提供流动性的前提下,分享流动性增长带来的收益。
- 链上状态:尽管 Uniswap V2 设计了协议手续费机制,但目前该功能并未在主网上开启。通过检查
UniswapV2Factory合约的feeTo地址是否为零,可以验证其开启状态。
1. Uniswap V2 手续费机制概览
1.1 基础概念:流动性与 LP Token
- 恒定乘积做市商 (CPMM):Uniswap V2 基于
X * Y = K的公式进行资产交换,其中X和Y分别代表池中两种资产的数量,K是一个常数(在无手续费和无流动性增减的情况下)。 - 流动性 (L):在 Uniswap V2 中,流动性
L通常指sqrt(K)。- 公式:
- LP Token (Share / LPT):当用户向 Uniswap 池中提供流动性时,会获得相应数量的 LP Token 作为凭证。这些 Token 代表了用户在池中的份额。
S1:在T1时刻,LP Token 的总数量。
1.2 千三手续费 (0.3%)
- 收取方式:每笔交易都会收取 0.3% 的手续费。这笔费用并不是直接从交易对的另一方扣除,而是从进入池子的 Token 中扣除。
- 例如,用户用
DX数量的 X 资产购买 Y 资产,池子实际上只收到(1 - 0.003) * DX = 0.997 * DX。
- 例如,用户用
- 归属:这 0.3% 的手续费直接重新注入到流动性池中,而非直接分配给某个用户或地址。
2. 流动性增长的逻辑
2.1 为什么流动性会增长 (K2 > K1)?
- 手续费的积累:由于 0.3% 的手续费被重新注入到池子中,池子里的
X和Y资产总量会随着交易量的增加而缓慢增长。- 如果用
DX购买 Y,池子收到0.997 * DX。这0.997 * DX增加了池子里的 X 资产。 - 如果用
DY购买 X,池子收到0.997 * DY。这0.997 * DY增加了池子里的 Y 资产。
- 如果用
- 结果:只要有交易发生,池子中的
X和Y资产总量就会增加,从而导致K值(即X * Y)不断增大。- (在不考虑流动性增减的情况下)。
2.2 案例分析:DAI/USDT 池子
为了更好地理解流动性增长和套利机制,我们以 DAI/USDT 稳定币交易对为例:
- 前提:DAI 和 USDT 都是稳定币,价格始终保持 1 美元。
- 初始状态:池中有 100 DAI 和 100 USDT。
- 此时
- 单边交易:假设用户持续用 DAI 购买 USDT。
- 每次交易都会收取 0.3% 的 DAI 手续费并注入池中。
- 池中的 DAI 会增加,USDT 会减少(因为被换出)。
- 池子可能暂时变为:104 DAI 和 98 USDT。
- 套利机器人 (Arbitrage Bots) 的作用:
- 当池子变为 104 DAI 和 98 USDT 时,池子内部的 DAI 价格相对于 USDT 变得更便宜(例如 1 DAI < 1 USDT),而 USDT 价格相对于 DAI 变得更贵。
- 套利机器人会立即发现这个价格差异:它们会用外部市场价格更低的 USDT 来购买池中相对便宜的 DAI,然后将 DAI 卖出换回 USDT,从而赚取差价。
- 套利交易会持续进行,直到池子内部的价格与外部市场价格重新平衡。
- 最终结果:经过交易和套利,池子最终会达到一个新的平衡点,例如:101 DAI 和 101 USDT。
- 此时
- 结论:即使经过套利平衡,最终的流动性
L(或K)仍然会因为手续费的积累而增长(从 100 增长到 101)。
2.3 可视化:流动性增长与套利平衡流程
graph TD
A[用户发起交易] --> B{收取 0.3% 手续费};
B --> C[手续费注入流动性池];
C --> D[池中 X/Y 资产总量增加];
D --> E[池子总流动性 K 增长];
E --> F{池内价格是否偏离市场价?};
F -- 是 --> G[套利机器人介入];
G --> H[套利交易平衡池内价格];
H --> D;
F -- 否 --> I[流动性增长稳定];
I --> J[LP Token 价值提升];
J --> K[LP 撤出时获得更多资产];
graph TD
A[用户发起交易] --> B{收取 0.3% 手续费};
B --> C[手续费注入流动性池];
C --> D[池中 X/Y 资产总量增加];
D --> E[池子总流动性 K 增长];
E --> F{池内价格是否偏离市场价?};
F -- 是 --> G[套利机器人介入];
G --> H[套利交易平衡池内价格];
H --> D;
F -- 否 --> I[流动性增长稳定];
I --> J[LP Token 价值提升];
J --> K[LP 撤出时获得更多资产];
graph TD
A[用户发起交易] --> B{收取 0.3% 手续费};
B --> C[手续费注入流动性池];
C --> D[池中 X/Y 资产总量增加];
D --> E[池子总流动性 K 增长];
E --> F{池内价格是否偏离市场价?};
F -- 是 --> G[套利机器人介入];
G --> H[套利交易平衡池内价格];
H --> D;
F -- 否 --> I[流动性增长稳定];
I --> J[LP Token 价值提升];
J --> K[LP 撤出时获得更多资产];graph TD
A[用户发起交易] --> B{收取 0.3% 手续费};
B --> C[手续费注入流动性池];
C --> D[池中 X/Y 资产总量增加];
D --> E[池子总流动性 K 增长];
E --> F{池内价格是否偏离市场价?};
F -- 是 --> G[套利机器人介入];
G --> H[套利交易平衡池内价格];
H --> D;
F -- 否 --> I[流动性增长稳定];
I --> J[LP Token 价值提升];
J --> K[LP 撤出时获得更多资产];
流程解释:
- 用户进行交易,Uniswap V2 会收取 0.3% 的手续费。
- 这笔手续费会被直接添加到流动性池中,导致池子中的
X和Y资产总量增加。 - 资产总量的增加使得池子的总流动性
K值随之增长。 - 随着
K值的增长,池子内部的资产价格可能会与外部市场价格产生偏差。 - 套利机器人会监测到这种价格偏差,并立即进行交易以平衡价格,从而赚取差价。
- 套利交易本身也会产生手续费,并进一步注入池中,循环促进
K的增长。 - 当池内价格与市场价格平衡后,
K值达到一个因手续费积累而增长的稳定状态。 - 由于
K增长而 LP Token 数量不变,每个 LP Token 对应的资产价值提升。 - 当 LP 决定撤出流动性时,他们将获得比初始投入更多的资产,这就是 LP 从手续费中受益的方式。
3. LP 如何从手续费中受益
3.1 Share 价值的提升
- 基本原理:当流动性池的总流动性
K(或L)增长时,LP Token 的总数量S1保持不变(不考虑新增发或销毁)。 - 结果:这意味着每个 LP Token 所代表的池子份额,以及其背后对应的实际资产数量,都会随之增加。LP 在撤出流动性时,会按照其 LP Token 份额,从更大的资产池中分得更多资产。
- 手续费增加的比例:从
T1到T2时刻,手续费导致流动性增加的比例可以表示为: 其中 是T1时刻的流动性常数, 是T2时刻的流动性常数。
3.2 公平性与实时性
- 公平分配:LP 收益的分配是公平的,完全基于 LP Token 的持有比例。如果 LP 持有池子 1% 的 LP Token,那么他就能获得 1% 的手续费收益。
- 实时性:LP 可以随时加入或退出流动性池。在他们提供流动性的期间,他们对池子的贡献会通过 LP Token 价值的增长,实时地体现在他们的收益中。
4. Uniswap V2 协议手续费 (PROTOCOL_FEE) 机制
4.1 项目方的需求
- 在 Uniswap V1 中,所有手续费都归 LP 所有,协议开发者(Uniswap 团队)自身无法从中直接获利。
- 为了激励协议的持续开发和维护,Uniswap V2 引入了协议手续费机制,允许项目方从协议中分得一杯羹。
4.2 phi () 参数
- 定义: (phi) 代表项目方希望从总手续费中分走的比例。
- 例如,如果项目方想分走 0.3% 手续费中的 1/6,那么 。
- 实际收取的协议费率为 (万分之五)。
4.3 实现方式:增发 Share
- 核心思想:项目方通常不提供流动性,因此不能直接持有 LP Token 来分享收益。为了让项目方获得收益,Uniswap V2 采取了增发新的 LP Token (Share) 给项目方的方式。
- “做大蛋糕,分增量”:项目方增发 Share 的逻辑是,在流动性池因手续费而变大(蛋糕变大)的同时,从这个“变大的部分”中分走一小块,而不是侵占现有 LP 的初始利益。
4.4 数学推导:SM 的计算
假设:
- :
T1时刻 LP 持有的 LP Token 总量。 - :
T1时刻池子的流动性常数。 - :
T2时刻池子的流动性常数(因手续费积累而增长)。 - :项目方在
T2时刻应增发的新 LP Token 数量。 - :项目方希望分走的手续费比例。
协议手续费的计算基于以下公式:
这个公式的含义是:
- 左侧 代表项目方增发的 Share 占总 Share 数量的比例。
- 右侧 代表从
T1到T2时刻,因手续费积累而导致的流动性(L = sqrt(K))的增长比例。 - 是项目方从这个增长比例中分走的份额。
通过对上述公式进行推导,可以求得 SM 的表达式:
推导过程:
- 初始公式:
- 将右侧的 记为 (手续费导致的流动性增长比例)。
- 交叉相乘:
- 展开:
- 将包含 的项移到等式左侧:
- 提取 :
- 解出 :
- 将 代回:
- 为了简化,分子分母同乘以 :
- 进一步整理分母:
- 最终得到 的表达式:
- 为了得到与白皮书和代码实现一致的形式,我们可以将分子分母同除以 :
当 Uniswap V2 协议费比例 时,。
因此,最终用于计算的
SM公式为: 这个公式与 Uniswap V2 白皮书和代码实现中的逻辑完全一致。
4.5 可视化:协议手续费收取流程
graph TD
A[Uniswap V2 团队决定收取协议费] --> B{设置 feeTo 地址 (非零)};
B --> C[用户持续进行交易];
C --> D[手续费累积,流动性 K 增长];
D --> E[调用 mintFee 方法 ,通常由外部触发];
E --> F{根据 K 的增长和 phi 计算 SM};
F --> G[增发 SM 数量的 LP Token];
G --> H[将增发的 LP Token 发送给 feeTo 地址];
H --> I[项目方通过 SM 分享流动性增长收益];
graph TD
A[Uniswap V2 团队决定收取协议费] --> B{设置 feeTo 地址 (非零)};
B --> C[用户持续进行交易];
C --> D[手续费累积,流动性 K 增长];
D --> E[调用 mintFee 方法 ,通常由外部触发];
E --> F{根据 K 的增长和 phi 计算 SM};
F --> G[增发 SM 数量的 LP Token];
G --> H[将增发的 LP Token 发送给 feeTo 地址];
H --> I[项目方通过 SM 分享流动性增长收益];
graph TD
A[Uniswap V2 团队决定收取协议费] --> B{设置 feeTo 地址 (非零)};
B --> C[用户持续进行交易];
C --> D[手续费累积,流动性 K 增长];
D --> E[调用 mintFee 方法 ,通常由外部触发];
E --> F{根据 K 的增长和 phi 计算 SM};
F --> G[增发 SM 数量的 LP Token];
G --> H[将增发的 LP Token 发送给 feeTo 地址];
H --> I[项目方通过 SM 分享流动性增长收益];graph TD
A[Uniswap V2 团队决定收取协议费] --> B{设置 feeTo 地址 (非零)};
B --> C[用户持续进行交易];
C --> D[手续费累积,流动性 K 增长];
D --> E[调用 mintFee 方法 ,通常由外部触发];
E --> F{根据 K 的增长和 phi 计算 SM};
F --> G[增发 SM 数量的 LP Token];
G --> H[将增发的 LP Token 发送给 feeTo 地址];
H --> I[项目方通过 SM 分享流动性增长收益];
流程解释:
- Uniswap V2 团队决定开启协议手续费功能。
- 团队通过设置
UniswapV2Factory合约中的feeTo地址为一个非零地址来激活此功能。 - 用户继续在 Uniswap V2 池中进行交易。
- 交易产生的手续费会不断累积,使得池子的总流动性
K值持续增长。 - 在某个时间点,
mintFee方法会被调用(通常由外部触发,例如在添加/移除流动性时检查并触发)。 mintFee方法会根据当前池子的流动性K值 (K2) 与上次计算协议费时的K值 (K1) 的差额,以及预设的协议费比例phi,计算出应该增发给协议方的 LP Token 数量SM。- 系统增发
SM数量的 LP Token。 - 这些新发行的 LP Token 会被发送到预设的
feeTo地址。 - 项目方通过持有这些
SMLP Token,可以分享流动性池因手续费积累而带来的增长收益。
5. 代码实现与链上验证
5.1 mintFee 方法
function _mintFee(uint112 _reserve0, uint112 _reserve1) private returns (bool feeOn) {
address feeTo = IUniswapV2Factory(factory).feeTo();
feeOn = feeTo != address(0);
uint _kLast = kLast; // gas savings
if (feeOn) {
if (_kLast != 0) {
uint rootK = Math.sqrt(uint(_reserve0).mul(_reserve1));
uint rootKLast = Math.sqrt(_kLast);
if (rootK > rootKLast) {
uint numerator = totalSupply.mul(rootK.sub(rootKLast));
uint denominator = rootK.mul(5).add(rootKLast);
uint liquidity = numerator / denominator;
if (liquidity > 0) _mint(feeTo, liquidity);
}
}
} else if (_kLast != 0) {
kLast = 0;
}
}- 位置:协议手续费的逻辑主要实现在
UniswapV2Pair合约的mintFee方法中。 - 功能:该方法负责计算并增发协议方应得的 LP Token。
- 代码逻辑:
- 计算
K2和K1(当前和上次的流动性常数)。 - 使用上述推导的公式计算
SM(增发数量)。 - 如果
SM > 0,则调用_mint函数增发SM数量的 LP Token,并发送给feeTo地址。 - 代码中
_mintFee的分母是5 * sqrt(K2) + sqrt(K1),这与白皮书的 时的公式一致。
- 计算
5.2 feeTo 开关
- 控制机制:协议手续费的开启与否由
UniswapV2Factory合约中的feeTo地址决定。 - 判断逻辑:
- 如果
feeTo地址为0x00...00(零地址),表示协议手续费未开启。 - 如果
feeTo地址不为0x00...00,表示协议手续费已开启。
- 如果
- 代码中的
feeOn变量:mintFee方法内部会检查feeTo地址是否为零,并设置一个布尔变量feeOn。只有当feeOn为true时,协议手续费的逻辑才会被触发。