github.com/0xPolygon/supernets2-node@v0.0.0-20230711153321-2fe574524eaa/test/contracts/uniswap/v2/UniswapV2LiquidityMathLibrary.sol (about)

     1  pragma solidity >=0.5.0;
     2  
     3  import './IUniswapV2Pair.sol';
     4  import './IUniswapV2Factory.sol';
     5  import './Babylonian.sol';
     6  import './FullMath.sol';
     7  
     8  import './SafeMath.sol';
     9  import './UniswapV2Library.sol';
    10  
    11  // library containing some math for dealing with the liquidity shares of a pair, e.g. computing their exact value
    12  // in terms of the underlying tokens
    13  library UniswapV2LiquidityMathLibrary {
    14      using SafeMath for uint256;
    15  
    16      // computes the direction and magnitude of the profit-maximizing trade
    17      function computeProfitMaximizingTrade(
    18          uint256 truePriceTokenA,
    19          uint256 truePriceTokenB,
    20          uint256 reserveA,
    21          uint256 reserveB
    22      ) pure internal returns (bool aToB, uint256 amountIn) {
    23          aToB = FullMath.mulDiv(reserveA, truePriceTokenB, reserveB) < truePriceTokenA;
    24  
    25          uint256 invariant = reserveA.mul(reserveB);
    26  
    27          uint256 leftSide = Babylonian.sqrt(
    28              FullMath.mulDiv(
    29                  invariant.mul(1000),
    30                  aToB ? truePriceTokenA : truePriceTokenB,
    31                  (aToB ? truePriceTokenB : truePriceTokenA).mul(997)
    32              )
    33          );
    34          uint256 rightSide = (aToB ? reserveA.mul(1000) : reserveB.mul(1000)) / 997;
    35  
    36          if (leftSide < rightSide) return (false, 0);
    37  
    38          // compute the amount that must be sent to move the price to the profit-maximizing price
    39          amountIn = leftSide.sub(rightSide);
    40      }
    41  
    42      // gets the reserves after an arbitrage moves the price to the profit-maximizing ratio given an externally observed true price
    43      function getReservesAfterArbitrage(
    44          address factory,
    45          address tokenA,
    46          address tokenB,
    47          uint256 truePriceTokenA,
    48          uint256 truePriceTokenB
    49      ) view internal returns (uint256 reserveA, uint256 reserveB) {
    50          // first get reserves before the swap
    51          (reserveA, reserveB) = UniswapV2Library.getReserves(factory, tokenA, tokenB);
    52  
    53          require(reserveA > 0 && reserveB > 0, 'UniswapV2ArbitrageLibrary: ZERO_PAIR_RESERVES');
    54  
    55          // then compute how much to swap to arb to the true price
    56          (bool aToB, uint256 amountIn) = computeProfitMaximizingTrade(truePriceTokenA, truePriceTokenB, reserveA, reserveB);
    57  
    58          if (amountIn == 0) {
    59              return (reserveA, reserveB);
    60          }
    61  
    62          // now affect the trade to the reserves
    63          if (aToB) {
    64              uint amountOut = UniswapV2Library.getAmountOut(amountIn, reserveA, reserveB);
    65              reserveA += amountIn;
    66              reserveB -= amountOut;
    67          } else {
    68              uint amountOut = UniswapV2Library.getAmountOut(amountIn, reserveB, reserveA);
    69              reserveB += amountIn;
    70              reserveA -= amountOut;
    71          }
    72      }
    73  
    74      // computes liquidity value given all the parameters of the pair
    75      function computeLiquidityValue(
    76          uint256 reservesA,
    77          uint256 reservesB,
    78          uint256 totalSupply,
    79          uint256 liquidityAmount,
    80          bool feeOn,
    81          uint kLast
    82      ) internal pure returns (uint256 tokenAAmount, uint256 tokenBAmount) {
    83          if (feeOn && kLast > 0) {
    84              uint rootK = Babylonian.sqrt(reservesA.mul(reservesB));
    85              uint rootKLast = Babylonian.sqrt(kLast);
    86              if (rootK > rootKLast) {
    87                  uint numerator1 = totalSupply;
    88                  uint numerator2 = rootK.sub(rootKLast);
    89                  uint denominator = rootK.mul(5).add(rootKLast);
    90                  uint feeLiquidity = FullMath.mulDiv(numerator1, numerator2, denominator);
    91                  totalSupply = totalSupply.add(feeLiquidity);
    92              }
    93          }
    94          return (reservesA.mul(liquidityAmount) / totalSupply, reservesB.mul(liquidityAmount) / totalSupply);
    95      }
    96  
    97      // get all current parameters from the pair and compute value of a liquidity amount
    98      // **note this is subject to manipulation, e.g. sandwich attacks**. prefer passing a manipulation resistant price to
    99      // #getLiquidityValueAfterArbitrageToPrice
   100      function getLiquidityValue(
   101          address factory,
   102          address tokenA,
   103          address tokenB,
   104          uint256 liquidityAmount
   105      ) internal view returns (uint256 tokenAAmount, uint256 tokenBAmount) {
   106          (uint256 reservesA, uint256 reservesB) = UniswapV2Library.getReserves(factory, tokenA, tokenB);
   107          IUniswapV2Pair pair = IUniswapV2Pair(UniswapV2Library.pairFor(factory, tokenA, tokenB));
   108          bool feeOn = IUniswapV2Factory(factory).feeTo() != address(0);
   109          uint kLast = feeOn ? pair.kLast() : 0;
   110          uint totalSupply = pair.totalSupply();
   111          return computeLiquidityValue(reservesA, reservesB, totalSupply, liquidityAmount, feeOn, kLast);
   112      }
   113  
   114      // given two tokens, tokenA and tokenB, and their "true price", i.e. the observed ratio of value of token A to token B,
   115      // and a liquidity amount, returns the value of the liquidity in terms of tokenA and tokenB
   116      function getLiquidityValueAfterArbitrageToPrice(
   117          address factory,
   118          address tokenA,
   119          address tokenB,
   120          uint256 truePriceTokenA,
   121          uint256 truePriceTokenB,
   122          uint256 liquidityAmount
   123      ) internal view returns (
   124          uint256 tokenAAmount,
   125          uint256 tokenBAmount
   126      ) {
   127          bool feeOn = IUniswapV2Factory(factory).feeTo() != address(0);
   128          IUniswapV2Pair pair = IUniswapV2Pair(UniswapV2Library.pairFor(factory, tokenA, tokenB));
   129          uint kLast = feeOn ? pair.kLast() : 0;
   130          uint totalSupply = pair.totalSupply();
   131  
   132          // this also checks that totalSupply > 0
   133          require(totalSupply >= liquidityAmount && liquidityAmount > 0, 'ComputeLiquidityValue: LIQUIDITY_AMOUNT');
   134  
   135          (uint reservesA, uint reservesB) = getReservesAfterArbitrage(factory, tokenA, tokenB, truePriceTokenA, truePriceTokenB);
   136  
   137          return computeLiquidityValue(reservesA, reservesB, totalSupply, liquidityAmount, feeOn, kLast);
   138      }
   139  }