流动性提供机制:添加流动性(Mint)、移除流动性(Burn)、流动性代币(NFT)表示
这一章,我们来聊聊Uniswap V3最核心的实操环节——怎么往池子里放钱,怎么把钱拿回来,以及你放进去的份额到底长什么样。
说实话,我第一次接触V3的流动性机制时,被那个NFT搞懵了。V2多简单啊,给你一个ERC20代币就完事了。V3倒好,直接给你发个NFT。后来我才明白,这背后其实藏着V3最精妙的设计。
Mint:添加流动性的底层逻辑
添加流动性,在V3里叫Mint。为什么叫Mint?因为每次添加流动性,系统都会铸造一个NFT给你。这个NFT就是你流动性的凭证。
先看核心函数签名:
function mint(
address recipient,
int24 tickLower,
int24 tickUpper,
uint128 amount,
bytes calldata data
) external returns (uint256 amount0, uint256 amount1);
参数看着多,其实就三件事:
- recipient:谁拿这个NFT
- tickLower / tickUpper:你选的价格区间
- amount:你要提供多少流动性(注意,这是流动性数量,不是代币数量)
这里有个坑。amount参数是流动性数量,不是你想存多少USDC或ETH。流动性数量和代币数量之间有个换算关系,我当年第一次写测试时直接传了个1000进去,结果交易对数量对不上,debug了半天。
核心公式:流动性 L = Δx * √P 或 L = Δy / √P,取决于你提供的哪种代币
实际调用时,合约会通过回调函数让你把代币转进去。流程是这样的:
- 你调用mint
- 合约计算需要的token0和token1数量
- 合约回调你的回调函数,让你转账
- 合约铸造NFT给你
嗯,这里要注意,回调函数里必须把代币转给池子合约,否则交易会回滚。我见过有人回调函数写错了,gas费白花了。
Burn:移除流动性的正确姿势
移除流动性叫Burn。为什么叫Burn?因为要把你的NFT销毁掉,换成代币还给你。
function burn(
uint256 tokenId,
int24 tickLower,
int24 tickUpper,
uint128 amount
) external returns (uint256 amount0, uint256 amount1);
参数和mint基本对称。你告诉合约:我要销毁这个NFT里的多少流动性。
Burn之后,代币不会直接到你手里。合约会记录一个待领取的余额。你需要再调用collect函数才能真正拿到代币。
重要提醒:Burn和Collect是两步操作。只Burn不Collect,钱就卡在合约里了。我有个朋友在测试网上就犯过这个错,以为Burn完就完事了。
为什么设计成两步?因为这样更灵活。你可以先Burn掉流动性,等gas费低的时候再Collect。或者你可以把多个位置的Collect合并成一笔交易。
NFT:流动性的数字化身
V3用NFT来表示流动性头寸,这个设计我觉得很妙。每个NFT代表一个独特的流动性位置,包含:
| 字段 | 含义 |
|---|---|
| tokenId | 唯一标识 |
| tickLower | 区间下限 |
| tickUpper | 区间上限 |
| liquidity | 流动性数量 |
| feeGrowthInside | 已赚取的手续费 |
你想想看,V2的LP代币是同质化的,所有流动性都一样。但V3不一样,每个人的价格区间、流动性深度都不同,用NFT来区分再合适不过了。
NFT的元数据还包含了价格区间信息,你可以在浏览器上直接看到这个头寸的详细信息。我个人觉得这个设计比V2优雅多了。
实战中的避坑指南
我总结几个实际开发中容易踩的坑:
- 价格区间别设太窄:我曾经设了个0.1%的区间,结果一天就被震荡出去了,手续费没赚到,还亏了gas费
- 注意tick的精度:tick是整数,但价格是连续的。计算区间时要用TickMath库
- NFT的owner可以转移:你可以把NFT卖给其他人,对方就拥有了这个流动性位置
- 手续费是单独领取的:Burn流动性时不会自动领取手续费,需要额外调用collect
小技巧:如果你要批量管理多个流动性位置,可以考虑用Position Manager合约。它帮你统一管理NFT,省得一个个操作。
核心逻辑流程图
下面这张图展示了Mint、Burn、Collect三者之间的关系:
从图上可以看得很清楚:Mint产生NFT,Burn销毁NFT,Collect从池子里拿钱。这三步构成了完整的流动性生命周期。
说实话,理解了这套机制,Uniswap V3的核心你就掌握了七成。剩下的三成是手续费计算、价格预言机这些细节。但流动性管理是基础,基础打牢了,后面都好说。
我在实际项目中,经常看到有人把Mint和AddLiquidity混为一谈。其实Mint是V3的术语,AddLiquidity是V2的。虽然做的事情类似,但底层逻辑完全不同。V3的Mint更灵活,但也更复杂。你得多花点时间理解tick和价格区间的概念。
最后说一句,如果你要写合约跟V3交互,建议直接用Uniswap官方提供的NonfungiblePositionManager。它封装好了Mint、Burn、Collect这些操作,省得你自己跟池子合约直接打交道。我一开始就是自己写,结果被回调函数折磨得够呛。