pragma solidity >=0.5.0; import '@aetherswap/v1-core/contracts/interfaces/IAetherswapV1Pair.sol'; import '@aetherswap/v1-core/contracts/interfaces/IAetherswapV1Factory.sol'; import '@uniswap/lib/contracts/libraries/Babylonian.sol'; import '@uniswap/lib/contracts/libraries/FullMath.sol'; import './SafeMath.sol'; import './AetherswapV1Library.sol'; // library containing some math for dealing with the liquidity shares of a pair, e.g. computing their exact value // in terms of the underlying tokens library AetherswapV1LiquidityMathLibrary { using SafeMath for uint256; // computes the direction and magnitude of the profit-maximizing trade function computeProfitMaximizingTrade( uint256 truePriceTokenA, uint256 truePriceTokenB, uint256 reserveA, uint256 reserveB ) pure internal returns (bool aToB, uint256 amountIn) { aToB = FullMath.mulDiv(reserveA, truePriceTokenB, reserveB) < truePriceTokenA; uint256 invariant = reserveA.mul(reserveB); uint256 leftSide = Babylonian.sqrt( FullMath.mulDiv( invariant.mul(1000), aToB ? truePriceTokenA : truePriceTokenB, (aToB ? truePriceTokenB : truePriceTokenA).mul(997) ) ); uint256 rightSide = (aToB ? reserveA.mul(1000) : reserveB.mul(1000)) / 997; if (leftSide < rightSide) return (false, 0); // compute the amount that must be sent to move the price to the profit-maximizing price amountIn = leftSide.sub(rightSide); } // gets the reserves after an arbitrage moves the price to the profit-maximizing ratio given an externally observed true price function getReservesAfterArbitrage( address factory, address tokenA, address tokenB, uint256 truePriceTokenA, uint256 truePriceTokenB ) view internal returns (uint256 reserveA, uint256 reserveB) { // first get reserves before the swap (reserveA, reserveB) = AetherswapV1Library.getReserves(factory, tokenA, tokenB); require(reserveA > 0 && reserveB > 0, 'AetherswapV1ArbitrageLibrary: ZERO_PAIR_RESERVES'); // then compute how much to swap to arb to the true price (bool aToB, uint256 amountIn) = computeProfitMaximizingTrade(truePriceTokenA, truePriceTokenB, reserveA, reserveB); if (amountIn == 0) { return (reserveA, reserveB); } // now affect the trade to the reserves if (aToB) { uint amountOut = AetherswapV1Library.getAmountOut(amountIn, reserveA, reserveB); reserveA += amountIn; reserveB -= amountOut; } else { uint amountOut = AetherswapV1Library.getAmountOut(amountIn, reserveB, reserveA); reserveB += amountIn; reserveA -= amountOut; } } // computes liquidity value given all the parameters of the pair function computeLiquidityValue( uint256 reservesA, uint256 reservesB, uint256 totalSupply, uint256 liquidityAmount, bool feeOn, uint kLast ) internal pure returns (uint256 tokenAAmount, uint256 tokenBAmount) { if (feeOn && kLast > 0) { uint rootK = Babylonian.sqrt(reservesA.mul(reservesB)); uint rootKLast = Babylonian.sqrt(kLast); if (rootK > rootKLast) { uint numerator1 = totalSupply; uint numerator2 = rootK.sub(rootKLast); uint denominator = rootK.mul(5).add(rootKLast); uint feeLiquidity = FullMath.mulDiv(numerator1, numerator2, denominator); totalSupply = totalSupply.add(feeLiquidity); } } return (reservesA.mul(liquidityAmount) / totalSupply, reservesB.mul(liquidityAmount) / totalSupply); } // get all current parameters from the pair and compute value of a liquidity amount // **note this is subject to manipulation, e.g. sandwich attacks**. prefer passing a manipulation resistant price to // #getLiquidityValueAfterArbitrageToPrice function getLiquidityValue( address factory, address tokenA, address tokenB, uint256 liquidityAmount ) internal view returns (uint256 tokenAAmount, uint256 tokenBAmount) { (uint256 reservesA, uint256 reservesB) = AetherswapV1Library.getReserves(factory, tokenA, tokenB); IAetherswapV1Pair pair = IAetherswapV1Pair(AetherswapV1Library.pairFor(factory, tokenA, tokenB)); bool feeOn = IAetherswapV1Factory(factory).feeTo() != address(0); uint kLast = feeOn ? pair.kLast() : 0; uint totalSupply = pair.totalSupply(); return computeLiquidityValue(reservesA, reservesB, totalSupply, liquidityAmount, feeOn, kLast); } // given two tokens, tokenA and tokenB, and their "true price", i.e. the observed ratio of value of token A to token B, // and a liquidity amount, returns the value of the liquidity in terms of tokenA and tokenB function getLiquidityValueAfterArbitrageToPrice( address factory, address tokenA, address tokenB, uint256 truePriceTokenA, uint256 truePriceTokenB, uint256 liquidityAmount ) internal view returns ( uint256 tokenAAmount, uint256 tokenBAmount ) { bool feeOn = IAetherswapV1Factory(factory).feeTo() != address(0); IAetherswapV1Pair pair = IAetherswapV1Pair(AetherswapV1Library.pairFor(factory, tokenA, tokenB)); uint kLast = feeOn ? pair.kLast() : 0; uint totalSupply = pair.totalSupply(); // this also checks that totalSupply > 0 require(totalSupply >= liquidityAmount && liquidityAmount > 0, 'ComputeLiquidityValue: LIQUIDITY_AMOUNT'); (uint reservesA, uint reservesB) = getReservesAfterArbitrage(factory, tokenA, tokenB, truePriceTokenA, truePriceTokenB); return computeLiquidityValue(reservesA, reservesB, totalSupply, liquidityAmount, feeOn, kLast); } }