移除流动性与提供流动性是截然相反的一个动作。从Pool中移除流动性意味着烧销毁LP-Token,以换取pair中相应数量的Token。返回的token数量与提供的流动性是这样计算的:
简而言之,返回的Token数量,与持有的LP-Token数量与LP-Token总供应量成正比。你的LP-Token份额越大,你在burn后从reserve获得的份额就越大。
这就是我们实现burn函数所需要知道的:
当有人向池提供流动性时,他们会得到 LP-token作为回报。当流动性被移除时,这些 LP-token会被兑换成池流动性并被销毁。让用户向合约发送一定数量的 LP-token,在合约中计算出用户可以获取的token后,销毁 LP-token。
function burn(address to) public returns (uint256 amount0, uint256 amount1) {
uint256 balance0 = IERC20(token0).balanceOf(address(this));
uint256 balance1 = IERC20(token1).balanceOf(address(this));
uint256 liquidity = balanceOf[address(this)];
uint256 amount0 = (liquidity * balance0) / totalSupply;
uint256 amount1 = (liquidity * balance1) / totalSupply;
if (amount0 <= 0 || amount1 <= 0) revert InsufficientLiquidityBurned();
_burn(address(this), liquidity);
bool success0 = IERC20(token0).transfer(to, amount0);
if (!success0)
revert TransferFailed();
bool success1 = IERC20(token1).transfer(to, amount1);
if (!success1)
revert TransferFailed();
balance0 = IERC20(token0).balanceOf(address(this));
balance1 = IERC20(token1).balanceOf(address(this));
(uint112 reserve0_, uint112 reserve1_, ) = getReserves();
_update(balance0, balance1, reserve0_, reserve1_);
emit Burn(msg.sender, amount0, amount1);
}
接下来测试一下:
function testBurn() public {
token0.transfer(address(pair), 1 ether);
token1.transfer(address(pair), 1 ether);
pair.mint(address(this));
uint256 liquidity = pair.balanceOf(address(this));
pair.transfer(address(pair), liquidity);
pair.burn(address(this));
assertEq(pair.balanceOf(address(this)), 0);
assertReserves(1000, 1000);
assertEq(pair.totalSupply(), 1000);
assertEq(token0.balanceOf(address(this)), 10 ether - 1000);
assertEq(token1.balanceOf(address(this)), 10 ether - 1000);
}
我们可以看到,除了发送到零地址的最低流动性外,Pool又回到了未初始化的状态。
现在,让我们看看在提供不平衡的流动性后会发生什么:
function testBurnUnbalanced() public {
token0.transfer(address(pair), 1 ether);
token1.transfer(address(pair), 1 ether);
pair.mint(address(this));
token0.transfer(address(pair), 2 ether);
token1.transfer(address(pair), 1 ether);
pair.mint(address(this)); // + 1 LP
uint256 liquidity = pair.balanceOf(address(this));
pair.transfer(address(pair), liquidity);
pair.burn(address(this));
assertEq(pair.balanceOf(address(this)), 0);
assertReserves(1500, 1000);
assertEq(pair.totalSupply(), 1000);
assertEq(token0.balanceOf(address(this)), 10 ether - 1500);
assertEq(token1.balanceOf(address(this)), 10 ether - 1000);
}
我们在这里看到的是,我们损失了 500 wei的Token 0!这就是上文提到的对价格操纵的惩罚。但这个数额小得离谱,看起来一点都不重要。这是因为我们当前的用户是唯一的流动性提供者。如果我们向另一个用户初始化的池提供了不平衡的流动性,会怎么样呢?让我们来看看:
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(address(this)); // + 1 LP
uint256 liquidity = pair.balanceOf(address(this));
pair.transfer(address(pair), liquidity);
pair.burn(address(this));
// this user is penalized for providing unbalanced liquidity
assertEq(pair.balanceOf(address(this)), 0);
assertReserves(1.5 ether, 1 ether);
assertEq(pair.totalSupply(), 1 ether);
assertEq(token0.balanceOf(address(this)), 10 ether - 0.5 ether);
assertEq(token1.balanceOf(address(this)), 10 ether);
testUser.removeLiquidity(address(pair));
// testUser receives the amount collected from this user
assertEq(pair.balanceOf(address(testUser)), 0);
assertReserves(1500, 1000);
assertEq(pair.totalSupply(), 1000);
assertEq(
token0.balanceOf(address(testUser)),
10 ether + 0.5 ether - 1500
);
assertEq(token1.balanceOf(address(testUser)), 10 ether - 1000);
}
现在看起来完全不同了!我们现在损失了 0.5 ether 的 token0,相当于我们存入资金的 1/4 。这可是一笔不小的数目!
想一想,是谁最终得到了这 0.5 个以太币:合约还是用户?