本文是 UniswapV2 深入解析系列的第三篇文章,深入讲解流动性移除的工作原理、LP 代币销毁机制,以及不平衡流动性提供的惩罚效应。
本文是 UniswapV2 深入解析系列的第三篇文章,深入讲解流动性移除的工作原理、LP 代币销毁机制,以及不平衡流动性提供的惩罚效应。
流动性移除是流动性提供的逆向操作,正如代币销毁与代币铸造互为逆向操作。当流动性提供者希望取回其提供的代币时,需要将持有的 LP 代币销毁,以换取池子中相应比例的底层代币。
流动性移除遵循严格的比例计算原则:
返还代币数量公式:
返还代币数量 = 储备代币数量 × (持有LP代币数量 / LP代币总供应量)
数学表达式:
Amount_token = Reserve_token × (Balance_LP / TotalSupply_LP)
核心原理:
UniswapV2 的流动性管理采用对称设计:
这种设计使得流动性管理变得简洁明了,核心合约只需专注于底层的代币操作逻辑。
/**
* @notice 销毁 LP 代币,移除流动性
* @dev 将对应比例的两种代币发送给指定地址
* @param to 接收代币的地址
* @return amount0 返还的 token0 数量
* @return amount1 返还的 token1 数量
*/
function burn(address to) external returns (uint256 amount0, uint256 amount1) {
address _token0 = token0; // 节省 gas
address _token1 = token1; // 节省 gas
uint256 balance0 = IERC20(_token0).balanceOf(address(this));
uint256 balance1 = IERC20(_token1).balanceOf(address(this));
uint256 liquidity = balanceOf(address(this));
uint256 _totalSupply = totalSupply(); // 节省 gas
// 使用余额确保按比例分配,防止捐赠攻击
amount0 = (liquidity * balance0) / _totalSupply;
amount1 = (liquidity * balance1) / _totalSupply;
if (amount0 <= 0 || amount1 <= 0) revert InsufficientLiquidityBurned();
// 销毁 LP 代币
_burn(address(this), liquidity);
// 转账代币给用户
_safeTransfer(_token0, to, amount0);
_safeTransfer(_token1, to, amount1);
// 更新余额
balance0 = IERC20(_token0).balanceOf(address(this));
balance1 = IERC20(_token1).balanceOf(address(this));
// 更新储备金
_update(balance0, balance1);
emit Burn(msg.sender, amount0, amount1, to);
}
关键设计决策:使用当前余额 balance0/balance1
而非储备金 reserve0/reserve1
进行计算。
原因分析:
_safeTransfer(token0, to, amount0);
_safeTransfer(token1, to, amount1);
安全考虑:
// 1. 计算返还数量
// 2. 销毁 LP 代币
// 3. 转账代币
// 4. 更新储备金
// 5. 发出事件
设计原理:
注意:原文提到"UniswapV2 不支持部分流动性移除",这实际上是不准确的。该函数销毁用户的全部 LP 代币,但用户可以通过先转移部分 LP 代币到其他地址来实现部分移除的效果。
当流动性提供者提供不平衡的流动性时,由于取最小值计算策略,会导致:
假设池子当前状态:
用户提供不平衡流动性:
LP代币计算:
liquidity0 = (200 × 100) / 100 = 200
liquidity1 = (100 × 100) / 100 = 100
liquidity = min(200, 100) = 100 // 取最小值
结果分析:
/**
* @notice 测试基本的流动性移除功能
*/
function testBurn() public {
// 提供初始流动性
token0.transfer(address(pair), 1 ether);
token1.transfer(address(pair), 1 ether);
pair.mint();
// 移除所有流动性
pair.burn();
// 验证测试结果
assertEq(pair.balanceOf(address(this)), 0); // LP代币余额为0
assertReserves(1000, 1000); // 只剩最小流动性
assertEq(pair.totalSupply(), 1000); // 总供应只剩最小流动性
assertEq(token0.balanceOf(address(this)), 10 ether - 1000); // 返还代币数量
assertEq(token1.balanceOf(address(this)), 10 ether - 1000);
}
测试要点:
/**
* @notice 测试不平衡流动性提供的惩罚效应
*/
function testBurnUnbalanced() public {
// 第一次:平衡流动性提供
token0.transfer(address(pair), 1 ether);
token1.transfer(address(pair), 1 ether);
pair.mint();
// 第二次:不平衡流动性提供
token0.transfer(address(pair), 2 ether); // 多提供token0
token1.transfer(address(pair), 1 ether);
pair.mint(); // 只获得1个LP代币
// 移除所有流动性
pair.burn();
// 验证惩罚效果
assertEq(pair.balanceOf(address(this)), 0);
assertReserves(1500, 1000); // 最小流动性按比例分布
assertEq(pair.totalSupply(), 1000);
// 关键:损失了500 wei的token0
assertEq(token0.balanceOf(address(this)), 10 ether - 1500);
assertEq(token1.balanceOf(address(this)), 10 ether - 1000);
}
惩罚分析:
/**
* @notice 测试多用户环境下的不平衡流动性惩罚
*/
function testBurnUnbalancedDifferentUsers() public {
// 测试用户提供初始平衡流动性
testUser.provideLiquidity(
address(pair),
address(token0),
address(token1),
1 ether,
1 ether
);
// 验证初始状态
assertEq(pair.balanceOf(address(this)), 0);
assertEq(pair.balanceOf(address(testUser)), 1 ether - 1000);
assertEq(pair.totalSupply(), 1 ether);
// 当前用户提供不平衡流动性
token0.transfer(address(pair), 2 ether);
token1.transfer(address(pair), 1 ether);
pair.mint(); // 只获得1个LP代币
assertEq(pair.balanceOf(address(this)), 1); // 注意:只有1个LP代币
// 移除自己的流动性
pair.burn();
// 验证显著的惩罚效果
assertEq(pair.balanceOf(address(this)), 0);
assertReserves(1.5 ether, 1 ether);
assertEq(pair.totalSupply(), 1 ether);
// 关键:损失了0.5 ether的token0(25%的损失!)
assertEq(token0.balanceOf(address(this)), 10 ether - 0.5 ether);
assertEq(token1.balanceOf(address(this)), 10 ether);
}
单用户场景:
多用户场景:
思考题:损失的0.5 ether token0最终由谁获得?
答案分析:
详细推理:
假设testUser后续移除流动性:
testUser的token0收益 = (1 ether - 1000) × 1.5 ether / 1 ether
= 约1.5 ether - 1500
相比正常情况下的1 ether收益,testUser额外获得了约0.5 ether。
比例化设计
惩罚机制
安全设计
通过本文的深入分析,我们全面理解了 UniswapV2 流动性移除机制的设计原理和实现细节。这种机制通过精巧的数学设计,既保证了流动性提供者的公平待遇,又通过惩罚机制维护了池子的价格稳定性。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!