当前位置:   article > 正文

Solidity Uniswap V2 Pair中添加流动性

Solidity Uniswap V2 Pair中添加流动性

        添加流动性的功能的用户入口,UniswapV2在UniswapV2Router中实现,它用来计算新的流动性并发行LP-Token,流动性管理简单地视为LP-Token管理。当你为一个pair增加流动性时,合约会创造LP Token;当你移除流动性时,LP-Token就会被销毁。pair合约中的添加流动性函数,是只执行核心操作的低级函数。

        添加流动性函数

        这是添加流动性的底层函数,

  1. function mint() public {
  2.    uint256 balance0 = IERC20(token0).balanceOf(address(this));
  3.    uint256 balance1 = IERC20(token1).balanceOf(address(this));
  4.    uint256 amount0 = balance0 - reserve0;
  5.    uint256 amount1 = balance1 - reserve1;
  6.    uint256 liquidity;
  7.    if (totalSupply == 0) {
  8.       liquidity = ???
  9.       _mint(address(0), MINIMUM_LIQUIDITY);
  10.    } else {
  11.       liquidity = ???
  12.    }
  13.    if (liquidity <= 0) revert InsufficientLiquidityMinted();
  14.    _mint(msg.sender, liquidity);
  15.    _update(balance0, balance1);
  16.    emit Mint(msg.sender, amount0, amount1);
  17. }

        首先,我们需要计算用户新存入的金额(即没有保存到reserve中的)。然后,计算必须发行的 LP-Token的数量,作为对提供流动性的奖励。然后,我们发行 LP-Token并更新reserve,函数 _update 只是将余额保存到reserve中。

        从代码中可以看出,最初存入pool时,根据totalSupply 是否为0 ,流动性的计算方法是不同的。想想看,当池中没有流动性时,我们需要发行多少 LP-Token?

        对于初始 LP-token的数量,Uniswap V2 最终使用了存入金额的几何平均数:

        这样的主要好处是,确保了初始流动性比率不会影响资产池份额的价值。

        然后,我们在计算,当pool里已经有一些流动性时发行LP-Token的情况。

        这里的主要有两个要求,第一是,按照比例,存入的Token;第二是,按照比例,发行LP-Token。

        发行LP-Token的数量,与token的存款数量成正比。但是,在一个pair中,有两个潜在的Token——我们应该在公式中使用哪一个呢?

        我们可以选择其中任何一个,但存款金额的比例与reserve的比例越接近,差异就越小。因此,如果存款金额的比例不同,LP-Token金额也会不同,而且其中一个会比另一个大。

        如果我们选择更大的,那么我们将通过提供流动性来激励价格变化,这将导致价格操纵。如果我们选择较小的一个,我们将惩罚不平衡流动性的存款(流动性提供者将获得更少的LP-Token)。很明显,选择更小的数字更有益。

  1. if (totalSupply == 0) {
  2.    liquidity = Math.sqrt(amount0 * amount1) - MINIMUM_LIQUIDITY;
  3.    _mint(address(0), MINIMUM_LIQUIDITY);
  4. } else {
  5.    liquidity = Math.min(
  6.       (amount0 * totalSupply) / _reserve0,
  7.       (amount1 * totalSupply) / _reserve1
  8.    );
  9. }

        在第一个分支中,我们在提供初始流动性时减去 MINIMUM_LIQUIDITY(即常数 1000,或 1e-15)。这样可以防止有人把一个pool的份额做得太贵,从而把小规模的流动性提供者拒之门外。对于大多数pool来说,1000 wei的 LP-Token可以忽略不计,但如果有人试图让一份pool的代币成本过高(比如 100 美元),他们就必须烧掉 1000 倍的成本(即 100,000 美元)。

使用solidity编写测试

        我将使用 Foundry 测试我们的智能合约,它无需与 JavaScript 打交道。我们使用智能合约测试智能合约。这就相当于,测试智能合约的智能合约。

这就是我们设置pair合约测试所需要的:

  1. contract ZuniswapV2PairTest is Test {
  2.   ERC20Mintable token0;
  3.   ERC20Mintable token1;
  4.   ZuniswapV2Pair pair;
  5.   function setUp() public {
  6.     token0 = new ERC20Mintable("Token A", "TKNA");
  7.     token1 = new ERC20Mintable("Token B", "TKNB");
  8.     pair = new ZuniswapV2Pair(address(token0), address(token1));
  9.     token0.mint(10 ether);
  10.     token1.mint(10 ether);
  11.   }
  12. }

测试一下交易对初始化(提供初始流动性):

  1. function testMintBootstrap() public {
  2.   token0.transfer(address(pair), 1 ether);
  3.   token1.transfer(address(pair), 1 ether);
  4.   pair.mint();
  5.   assertEq(pair.balanceOf(address(this)), 1 ether - 1000);
  6.   assertReserves(1 ether, 1 ether);
  7.   assertEq(pair.totalSupply(), 1 ether);
  8. }

1 个ether的Token0 和 1 个ether的Token1 加入测试池。因此,1 个ether的 LP-Token被发行,我们得到 1 个ether - 1000(减去最小流动性)。pool的reserve和总供应量也会相应改变。

如果向一个已有一定流动性的pool提供平衡的流动性,会发生什么情况呢?让我们来看看:

  1. function testMintWhenTheresLiquidity() public {
  2.   token0.transfer(address(pair), 1 ether);
  3.   token1.transfer(address(pair), 1 ether);
  4.   pair.mint(); // + 1 LP
  5.   token0.transfer(address(pair), 2 ether);
  6.   token1.transfer(address(pair), 2 ether);
  7.   pair.mint(); // + 2 LP
  8.   assertEq(pair.balanceOf(address(this)), 3 ether - 1000);
  9.   assertEq(pair.totalSupply(), 3 ether);
  10.   assertReserves(3 ether, 3 ether);
  11. }

让我们看看提供不平衡流动性时会发生什么:

  1. function testMintUnbalanced() public {
  2.   token0.transfer(address(pair), 1 ether);
  3.   token1.transfer(address(pair), 1 ether);
  4.   pair.mint(); // + 1 LP
  5.   assertEq(pair.balanceOf(address(this)), 1 ether - 1000);
  6.   assertReserves(1 ether, 1 ether);
  7.   token0.transfer(address(pair), 2 ether);
  8.   token1.transfer(address(pair), 1 ether);
  9.   pair.mint(); // + 1 LP
  10.   assertEq(pair.balanceOf(address(this)), 2 ether - 1000);
  11.   assertReserves(3 ether, 2 ether);
  12. }

即使用户提供的Token0流动性多于Token1流动性,他们仍然只能获得1 LP-Token。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/385944
推荐阅读
相关标签
  

闽ICP备14008679号