Uniswap V2 第4章 添加流动性:LP 铸造与几何平均

讲解 Uniswap V2 如何铸造 LP token:首次用几何平均 √(x·y)、后续按比例取最小值,以及最小流动性锁定的设计。

6 分钟阅读
Uniswap V2 第4章 添加流动性:LP 铸造与几何平均

Uniswap V2 第 4 章:添加流动性(Add Liquidity)

这一章讲怎么把两种代币存进池子换取 LP token。重点搞懂三个数学点:首次铸造为什么用几何平均 √(x·y)后续铸造为什么取两种比例的最小值、以及为什么要永久锁定一小撮 MINIMUM_LIQUIDITY


目录


1. LP token 是什么

你向池子存入两种代币,池子(Pair,本身是个 ERC20)给你铸造 LP token,代表你在池子里的份额。

  • 持有 LP token = 拥有池子里一定比例的两种代币 + 累积的交易手续费。
  • 销毁 LP token(burn)= 按比例取回两种代币(第 5 章)。
  • 因为手续费留在池中让储备增长,每个 LP token 对应的资产会随时间缓慢变多

2. addLiquidity:先把两种币配平

你想存 DAI 和 WETH,但池子有固定的当前比例(比如 3000 DAI : 1 WETH)。如果你存的比例不对,Router 的 addLiquidity自动帮你按当前比例取舍

你给出 amountADesired, amountBDesired(期望存入量)和 amountAMin, amountBMin(下限)
Router 计算:
  - 若按 A 全额存,需要的 B = quote(amountADesired)
  - 若这个 B ≤ amountBDesired,就用 (amountADesired, B)
  - 否则反过来,按 B 全额存,算需要的 A
最终存入的是"按当前比例配平后"的一组数额

quote(amountA) = amountA · reserveB / reserveA 就是按当前储备比例换算。所以多出来的那种币不会被存入(留在你手上),保证你存入时不改变池子比例。


3. 首次铸造:几何平均 √(x·y)

池子第一次被注入流动性时(totalSupply == 0),没有”现有比例”可参考,于是 pair.mint 用:

liquidity = √(amount0 · amount1) − MINIMUM_LIQUIDITY

即存入两种币数量的几何平均数(再减去锁定的最小流动性,见第 7 节)。


4. 为什么用几何平均而不是别的

几何平均 √(x·y) 有一个关键性质:它对”代币单位的选择”不敏感,且正比于恒定乘积的”流动性深度”。

  • 回忆 k = x·y,那么 √k = √(x·y) 正好衡量池子的”规模”。把 LP 数量定为 √(x·y),意味着 LP 数量正比于 √k
  • 好处:当价格不变、只是流动性翻倍时(x、y 同时 ×2),√(x·y) 也正好 ×2,LP 数量翻倍——份额与投入成正比,公平
  • 如果用算术平均或单种币数量,会受代币精度/价格影响,不同池子无法统一衡量。

一句话:几何平均让 “LP 数量 ∝ √k ∝ 池子深度”,是衡量流动性最自然的尺子。


5. 后续铸造:取最小值,防白嫖

池子已有流动性时(totalSupply > 0),按你存入的两种币各自占储备的比例来铸:

liquidity = min(
    amount0 · totalSupply / reserve0,
    amount1 · totalSupply / reserve1
)

为什么取最小值(min)? 防止有人”存得不成比例”还想多拿 LP。

  • 如果你严格按当前比例存,两个式子算出来一样,min 不影响。
  • 如果你某一种币存多了(不成比例),多存的那种币不会让你多拿 LP——min 会以”存得少的那种”为准。多存的部分相当于白送给池子(送给所有 LP)。

这逼着大家按比例存,否则吃亏。Router 的配平(第 2 节)正是为了帮你避免这种亏损。


6. 案例:两次添加流动性算 LP

首次:小明建池,存 100 WETH + 300,000 DAI

liquidity = √(100 · 300,000) − MINIMUM_LIQUIDITY
          = √30,000,000 − 1000 (单位简化)
          ≈ 5,477.2 − 0.001 ≈ 5,477.2 LP(忽略锁定的微量)
totalSupply ≈ 5,477.2

后续:小红按比例存 10 WETH + 30,000 DAI(正好 1/10 比例)

按 WETH:10 · 5477.2 / 100 = 547.72
按 DAI :30,000 · 5477.2 / 300,000 = 547.72
liquidity = min(547.72, 547.72) = 547.72 LP

小红存了池子规模的 10%,正好拿到约 10% 的 LP(547.72 / (5477.2+547.72) ≈ 9.1%,因为池子在她存入后变大了)。

如果小红不按比例:存 10 WETH + 40,000 DAI

按 WETH:10 · 5477.2 / 100 = 547.72
按 DAI :40,000 · 5477.2 / 300,000 = 730.3
liquidity = min(547.72, 730.3) = 547.72 LP

她多存了 10,000 DAI,却还是只拿 547.72 LP——多存的 DAI 白白送给了池子。所以一定要按比例存(用 Router 自动配平)。


7. 最小流动性锁定(MINIMUM_LIQUIDITY)

首次铸造时减去并永久锁定 MINIMUM_LIQUIDITY = 1000(铸给零地址,谁都取不回)。

为什么?防一种攻击:

  • 如果不锁,攻击者可以先存极少量建池、拿到极少 LP,再直接给池子转入大量代币,把单个 LP token 的价值抬到极高,导致后来者因取整(除法向下取整)几乎拿不到 LP,被”挤出”。
  • 锁定 1000 个最小单位 LP,让池子的 LP 总量永远有一个不可忽略的基数,使上述”通胀单份价值”的攻击在经济上不划算。

代价只是首个 LP 损失价值约 1000 个最小单位的份额,几乎可忽略,却堵住了一类攻击。


8. 本章小结

  1. 存两种币换 LP token(Pair 本身是 ERC20);持有 LP = 池子份额 + 累积手续费。
  2. Router 的 addLiquidity按当前储备比例自动配平,多余的币不存入,避免你吃亏。
  3. 首次铸造 liquidity = √(amount0·amount1) − MINIMUM_LIQUIDITY,几何平均让 LP ∝ √k ∝ 池子深度。
  4. 后续铸造 liquidity = min(amount0·ts/reserve0, amount1·ts/reserve1),取最小值逼你按比例存。
  5. MINIMUM_LIQUIDITY=1000 永久锁定,防”通胀单份 LP 价值”的挤出攻击。

9. 动手练习

对应课程的 Add Liquidity 练习:向 DAI/WETH 池添加流动性。

练习:addLiquidity

主网分叉,Router = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D

interface IUniswapV2Router02 {
    function addLiquidity(
        address tokenA, address tokenB,
        uint256 amountADesired, uint256 amountBDesired,
        uint256 amountAMin, uint256 amountBMin,
        address to, uint256 deadline
    ) external returns (uint256 amountA, uint256 amountB, uint256 liquidity);
}

思路:

  1. setUp:给 user 充值 100 WETH(weth.deposit)和 100 万 DAI(deal),都 approve 给 Router。
  2. 调用 addLiquidity(DAI, WETH, 1_000_000e18, 100e18, 1, 1, user, block.timestamp)
  3. 打印返回的 amountA(实际存入 DAI)、amountB(实际存入 WETH)、liquidity(铸到的 LP)。
  4. 观察配平:因为池子有固定比例,amountA/amountB 通常不会正好是你给的 100 万/100——有一种会被按比例缩减,多的留在你手上。打印你的 DAI/WETH 剩余余额验证。
  5. 断言 pair.balanceOf(user) > 0

进阶

  • 故意传一个严重偏离当前比例的 amountADesired/amountBDesired,观察 Router 如何配平、哪种币没被全额存入。
  • getReserves 算出当前比例,预测会存入多少,再和实际对比。

运行

forge test --evm-version cancun --fork-url $FORK_URL \
  --match-path test/UniswapV2Liquidity.t.sol -vvv

下一章(第 5 章 Remove Liquidity)讲:销毁 LP 怎么按比例取回两种币,以及 burn 的数学。

💬 评论