diff --git a/contracts/ApeRouter.sol b/contracts/ApeRouter.sol index ee5a32f..7dca881 100644 --- a/contracts/ApeRouter.sol +++ b/contracts/ApeRouter.sol @@ -1,12 +1,25 @@ pragma solidity =0.6.6; /* - * ApeSwapFinance - * App: https://apeswap.finance - * Medium: https://ape-swap.medium.com - * Twitter: https://twitter.com/ape_swap + ______ ______ + / \ / \ +| ▓▓▓▓▓▓\ ______ ______ | ▓▓▓▓▓▓\__ __ __ ______ ______ +| ▓▓__| ▓▓/ \ / \| ▓▓___\▓▓ \ | \ | \| \ / \ +| ▓▓ ▓▓ ▓▓▓▓▓▓\ ▓▓▓▓▓▓\\▓▓ \| ▓▓ | ▓▓ | ▓▓ \▓▓▓▓▓▓\ ▓▓▓▓▓▓\ +| ▓▓▓▓▓▓▓▓ ▓▓ | ▓▓ ▓▓ ▓▓_\▓▓▓▓▓▓\ ▓▓ | ▓▓ | ▓▓/ ▓▓ ▓▓ | ▓▓ +| ▓▓ | ▓▓ ▓▓__/ ▓▓ ▓▓▓▓▓▓▓▓ \__| ▓▓ ▓▓_/ ▓▓_/ ▓▓ ▓▓▓▓▓▓▓ ▓▓__/ ▓▓ +| ▓▓ | ▓▓ ▓▓ ▓▓\▓▓ \\▓▓ ▓▓\▓▓ ▓▓ ▓▓\▓▓ ▓▓ ▓▓ ▓▓ + \▓▓ \▓▓ ▓▓▓▓▓▓▓ \▓▓▓▓▓▓▓ \▓▓▓▓▓▓ \▓▓▓▓▓\▓▓▓▓ \▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓ + | ▓▓ | ▓▓ + | ▓▓ | ▓▓ + \▓▓ \▓▓ + * App: https://ApeSwap.finance + * Medium: https://ape-swap.medium.com + * Twitter: https://twitter.com/ape_swap * Telegram: https://t.me/ape_swap * Announcements: https://t.me/ape_swap_news + * Reddit: https://reddit.com/r/ApeSwap + * Instagram: https://instagram.com/ApeSwap.finance * GitHub: https://github.com/ApeSwapFinance */ @@ -14,17 +27,18 @@ import './interfaces/IApeFactory.sol'; import '@uniswap/lib/contracts/libraries/TransferHelper.sol'; import './interfaces/IApeRouter02.sol'; -import './libraries/ApeLibrary.sol'; +import './libraries/ApePairMath.sol'; import './libraries/SafeMath.sol'; import './interfaces/IERC20.sol'; import './interfaces/IWETH.sol'; -contract ApeRouter is IApeRouter02 { +contract ApeRouter is ApePairMath, IApeRouter02 { using SafeMath for uint; address public immutable override factory; address public immutable override WETH; - + bytes32 private _INIT_CODE_PAIR_HASH; + modifier ensure(uint deadline) { require(deadline >= block.timestamp, 'ApeRouter: EXPIRED'); _; @@ -33,12 +47,17 @@ contract ApeRouter is IApeRouter02 { constructor(address _factory, address _WETH) public { factory = _factory; WETH = _WETH; + _INIT_CODE_PAIR_HASH = IApeFactory(_factory).INIT_CODE_PAIR_HASH(); } receive() external payable { assert(msg.sender == WETH); // only accept ETH via fallback from the WETH contract } + function INIT_CODE_PAIR_HASH() public view override returns (bytes32) { + return _INIT_CODE_PAIR_HASH; + } + // **** ADD LIQUIDITY **** function _addLiquidity( address tokenA, @@ -52,16 +71,16 @@ contract ApeRouter is IApeRouter02 { if (IApeFactory(factory).getPair(tokenA, tokenB) == address(0)) { IApeFactory(factory).createPair(tokenA, tokenB); } - (uint reserveA, uint reserveB) = ApeLibrary.getReserves(factory, tokenA, tokenB); + (uint reserveA, uint reserveB) = _getReserves(factory, tokenA, tokenB); if (reserveA == 0 && reserveB == 0) { (amountA, amountB) = (amountADesired, amountBDesired); } else { - uint amountBOptimal = ApeLibrary.quote(amountADesired, reserveA, reserveB); + uint amountBOptimal = _quote(amountADesired, reserveA, reserveB); if (amountBOptimal <= amountBDesired) { require(amountBOptimal >= amountBMin, 'ApeRouter: INSUFFICIENT_B_AMOUNT'); (amountA, amountB) = (amountADesired, amountBOptimal); } else { - uint amountAOptimal = ApeLibrary.quote(amountBDesired, reserveB, reserveA); + uint amountAOptimal = _quote(amountBDesired, reserveB, reserveA); assert(amountAOptimal <= amountADesired); require(amountAOptimal >= amountAMin, 'ApeRouter: INSUFFICIENT_A_AMOUNT'); (amountA, amountB) = (amountAOptimal, amountBDesired); @@ -79,7 +98,7 @@ contract ApeRouter is IApeRouter02 { uint deadline ) external virtual override ensure(deadline) returns (uint amountA, uint amountB, uint liquidity) { (amountA, amountB) = _addLiquidity(tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin); - address pair = ApeLibrary.pairFor(factory, tokenA, tokenB); + address pair = _pairFor(factory, tokenA, tokenB); TransferHelper.safeTransferFrom(tokenA, msg.sender, pair, amountA); TransferHelper.safeTransferFrom(tokenB, msg.sender, pair, amountB); liquidity = IApePair(pair).mint(to); @@ -100,7 +119,7 @@ contract ApeRouter is IApeRouter02 { amountTokenMin, amountETHMin ); - address pair = ApeLibrary.pairFor(factory, token, WETH); + address pair = _pairFor(factory, token, WETH); TransferHelper.safeTransferFrom(token, msg.sender, pair, amountToken); IWETH(WETH).deposit{value: amountETH}(); assert(IWETH(WETH).transfer(pair, amountETH)); @@ -119,10 +138,10 @@ contract ApeRouter is IApeRouter02 { address to, uint deadline ) public virtual override ensure(deadline) returns (uint amountA, uint amountB) { - address pair = ApeLibrary.pairFor(factory, tokenA, tokenB); + address pair = _pairFor(factory, tokenA, tokenB); TransferHelper.safeTransferFrom(pair, msg.sender, pair, liquidity); // send liquidity to pair (uint amount0, uint amount1) = IApePair(pair).burn(to); - (address token0,) = ApeLibrary.sortTokens(tokenA, tokenB); + (address token0,) = _sortTokens(tokenA, tokenB); (amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0); require(amountA >= amountAMin, 'ApeRouter: INSUFFICIENT_A_AMOUNT'); require(amountB >= amountBMin, 'ApeRouter: INSUFFICIENT_B_AMOUNT'); @@ -158,7 +177,7 @@ contract ApeRouter is IApeRouter02 { uint deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external virtual override returns (uint amountA, uint amountB) { - address pair = ApeLibrary.pairFor(factory, tokenA, tokenB); + address pair = _pairFor(factory, tokenA, tokenB); uint value = approveMax ? uint(-1) : liquidity; IApePair(pair).permit(msg.sender, address(this), value, deadline, v, r, s); (amountA, amountB) = removeLiquidity(tokenA, tokenB, liquidity, amountAMin, amountBMin, to, deadline); @@ -172,7 +191,7 @@ contract ApeRouter is IApeRouter02 { uint deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external virtual override returns (uint amountToken, uint amountETH) { - address pair = ApeLibrary.pairFor(factory, token, WETH); + address pair = _pairFor(factory, token, WETH); uint value = approveMax ? uint(-1) : liquidity; IApePair(pair).permit(msg.sender, address(this), value, deadline, v, r, s); (amountToken, amountETH) = removeLiquidityETH(token, liquidity, amountTokenMin, amountETHMin, to, deadline); @@ -209,7 +228,7 @@ contract ApeRouter is IApeRouter02 { uint deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external virtual override returns (uint amountETH) { - address pair = ApeLibrary.pairFor(factory, token, WETH); + address pair = _pairFor(factory, token, WETH); uint value = approveMax ? uint(-1) : liquidity; IApePair(pair).permit(msg.sender, address(this), value, deadline, v, r, s); amountETH = removeLiquidityETHSupportingFeeOnTransferTokens( @@ -222,11 +241,11 @@ contract ApeRouter is IApeRouter02 { function _swap(uint[] memory amounts, address[] memory path, address _to) internal virtual { for (uint i; i < path.length - 1; i++) { (address input, address output) = (path[i], path[i + 1]); - (address token0,) = ApeLibrary.sortTokens(input, output); + (address token0,) = _sortTokens(input, output); uint amountOut = amounts[i + 1]; (uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOut) : (amountOut, uint(0)); - address to = i < path.length - 2 ? ApeLibrary.pairFor(factory, output, path[i + 2]) : _to; - IApePair(ApeLibrary.pairFor(factory, input, output)).swap( + address to = i < path.length - 2 ? _pairFor(factory, output, path[i + 2]) : _to; + IApePair(_pairFor(factory, input, output)).swap( amount0Out, amount1Out, to, new bytes(0) ); } @@ -238,10 +257,10 @@ contract ApeRouter is IApeRouter02 { address to, uint deadline ) external virtual override ensure(deadline) returns (uint[] memory amounts) { - amounts = ApeLibrary.getAmountsOut(factory, amountIn, path); + amounts = _getAmountsOut(factory, amountIn, path); require(amounts[amounts.length - 1] >= amountOutMin, 'ApeRouter: INSUFFICIENT_OUTPUT_AMOUNT'); TransferHelper.safeTransferFrom( - path[0], msg.sender, ApeLibrary.pairFor(factory, path[0], path[1]), amounts[0] + path[0], msg.sender, _pairFor(factory, path[0], path[1]), amounts[0] ); _swap(amounts, path, to); } @@ -252,10 +271,10 @@ contract ApeRouter is IApeRouter02 { address to, uint deadline ) external virtual override ensure(deadline) returns (uint[] memory amounts) { - amounts = ApeLibrary.getAmountsIn(factory, amountOut, path); + amounts = _getAmountsIn(factory, amountOut, path); require(amounts[0] <= amountInMax, 'ApeRouter: EXCESSIVE_INPUT_AMOUNT'); TransferHelper.safeTransferFrom( - path[0], msg.sender, ApeLibrary.pairFor(factory, path[0], path[1]), amounts[0] + path[0], msg.sender, _pairFor(factory, path[0], path[1]), amounts[0] ); _swap(amounts, path, to); } @@ -268,10 +287,10 @@ contract ApeRouter is IApeRouter02 { returns (uint[] memory amounts) { require(path[0] == WETH, 'ApeRouter: INVALID_PATH'); - amounts = ApeLibrary.getAmountsOut(factory, msg.value, path); + amounts = _getAmountsOut(factory, msg.value, path); require(amounts[amounts.length - 1] >= amountOutMin, 'ApeRouter: INSUFFICIENT_OUTPUT_AMOUNT'); IWETH(WETH).deposit{value: amounts[0]}(); - assert(IWETH(WETH).transfer(ApeLibrary.pairFor(factory, path[0], path[1]), amounts[0])); + assert(IWETH(WETH).transfer(_pairFor(factory, path[0], path[1]), amounts[0])); _swap(amounts, path, to); } function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline) @@ -282,10 +301,10 @@ contract ApeRouter is IApeRouter02 { returns (uint[] memory amounts) { require(path[path.length - 1] == WETH, 'ApeRouter: INVALID_PATH'); - amounts = ApeLibrary.getAmountsIn(factory, amountOut, path); + amounts = _getAmountsIn(factory, amountOut, path); require(amounts[0] <= amountInMax, 'ApeRouter: EXCESSIVE_INPUT_AMOUNT'); TransferHelper.safeTransferFrom( - path[0], msg.sender, ApeLibrary.pairFor(factory, path[0], path[1]), amounts[0] + path[0], msg.sender, _pairFor(factory, path[0], path[1]), amounts[0] ); _swap(amounts, path, address(this)); IWETH(WETH).withdraw(amounts[amounts.length - 1]); @@ -299,10 +318,10 @@ contract ApeRouter is IApeRouter02 { returns (uint[] memory amounts) { require(path[path.length - 1] == WETH, 'ApeRouter: INVALID_PATH'); - amounts = ApeLibrary.getAmountsOut(factory, amountIn, path); + amounts = _getAmountsOut(factory, amountIn, path); require(amounts[amounts.length - 1] >= amountOutMin, 'ApeRouter: INSUFFICIENT_OUTPUT_AMOUNT'); TransferHelper.safeTransferFrom( - path[0], msg.sender, ApeLibrary.pairFor(factory, path[0], path[1]), amounts[0] + path[0], msg.sender, _pairFor(factory, path[0], path[1]), amounts[0] ); _swap(amounts, path, address(this)); IWETH(WETH).withdraw(amounts[amounts.length - 1]); @@ -317,10 +336,10 @@ contract ApeRouter is IApeRouter02 { returns (uint[] memory amounts) { require(path[0] == WETH, 'ApeRouter: INVALID_PATH'); - amounts = ApeLibrary.getAmountsIn(factory, amountOut, path); + amounts = _getAmountsIn(factory, amountOut, path); require(amounts[0] <= msg.value, 'ApeRouter: EXCESSIVE_INPUT_AMOUNT'); IWETH(WETH).deposit{value: amounts[0]}(); - assert(IWETH(WETH).transfer(ApeLibrary.pairFor(factory, path[0], path[1]), amounts[0])); + assert(IWETH(WETH).transfer(_pairFor(factory, path[0], path[1]), amounts[0])); _swap(amounts, path, to); // refund dust eth, if any if (msg.value > amounts[0]) TransferHelper.safeTransferETH(msg.sender, msg.value - amounts[0]); @@ -331,18 +350,18 @@ contract ApeRouter is IApeRouter02 { function _swapSupportingFeeOnTransferTokens(address[] memory path, address _to) internal virtual { for (uint i; i < path.length - 1; i++) { (address input, address output) = (path[i], path[i + 1]); - (address token0,) = ApeLibrary.sortTokens(input, output); - IApePair pair = IApePair(ApeLibrary.pairFor(factory, input, output)); + (address token0,) = _sortTokens(input, output); + IApePair pair = IApePair(_pairFor(factory, input, output)); uint amountInput; uint amountOutput; { // scope to avoid stack too deep errors (uint reserve0, uint reserve1,) = pair.getReserves(); (uint reserveInput, uint reserveOutput) = input == token0 ? (reserve0, reserve1) : (reserve1, reserve0); amountInput = IERC20(input).balanceOf(address(pair)).sub(reserveInput); - amountOutput = ApeLibrary.getAmountOut(amountInput, reserveInput, reserveOutput); + amountOutput = _getAmountOut(amountInput, reserveInput, reserveOutput); } (uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOutput) : (amountOutput, uint(0)); - address to = i < path.length - 2 ? ApeLibrary.pairFor(factory, output, path[i + 2]) : _to; + address to = i < path.length - 2 ? _pairFor(factory, output, path[i + 2]) : _to; pair.swap(amount0Out, amount1Out, to, new bytes(0)); } } @@ -354,7 +373,7 @@ contract ApeRouter is IApeRouter02 { uint deadline ) external virtual override ensure(deadline) { TransferHelper.safeTransferFrom( - path[0], msg.sender, ApeLibrary.pairFor(factory, path[0], path[1]), amountIn + path[0], msg.sender, _pairFor(factory, path[0], path[1]), amountIn ); uint balanceBefore = IERC20(path[path.length - 1]).balanceOf(to); _swapSupportingFeeOnTransferTokens(path, to); @@ -378,7 +397,7 @@ contract ApeRouter is IApeRouter02 { require(path[0] == WETH, 'ApeRouter: INVALID_PATH'); uint amountIn = msg.value; IWETH(WETH).deposit{value: amountIn}(); - assert(IWETH(WETH).transfer(ApeLibrary.pairFor(factory, path[0], path[1]), amountIn)); + assert(IWETH(WETH).transfer(_pairFor(factory, path[0], path[1]), amountIn)); uint balanceBefore = IERC20(path[path.length - 1]).balanceOf(to); _swapSupportingFeeOnTransferTokens(path, to); require( @@ -400,7 +419,7 @@ contract ApeRouter is IApeRouter02 { { require(path[path.length - 1] == WETH, 'ApeRouter: INVALID_PATH'); TransferHelper.safeTransferFrom( - path[0], msg.sender, ApeLibrary.pairFor(factory, path[0], path[1]), amountIn + path[0], msg.sender, _pairFor(factory, path[0], path[1]), amountIn ); _swapSupportingFeeOnTransferTokens(path, address(this)); uint amountOut = IERC20(WETH).balanceOf(address(this)); @@ -410,47 +429,47 @@ contract ApeRouter is IApeRouter02 { } // **** LIBRARY FUNCTIONS **** - function quote(uint amountA, uint reserveA, uint reserveB) public pure virtual override returns (uint amountB) { - return ApeLibrary.quote(amountA, reserveA, reserveB); + function quote(uint amountA, uint reserveA, uint reserveB) external pure virtual override returns (uint amountB) { + return _quote(amountA, reserveA, reserveB); } function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) - public + external pure virtual override returns (uint amountOut) { - return ApeLibrary.getAmountOut(amountIn, reserveIn, reserveOut); + return _getAmountOut(amountIn, reserveIn, reserveOut); } function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) - public + external pure virtual override returns (uint amountIn) { - return ApeLibrary.getAmountIn(amountOut, reserveIn, reserveOut); + return _getAmountIn(amountOut, reserveIn, reserveOut); } - function getAmountsOut(uint amountIn, address[] memory path) - public + function getAmountsOut(uint amountIn, address[] calldata path) + external view virtual override returns (uint[] memory amounts) { - return ApeLibrary.getAmountsOut(factory, amountIn, path); + return _getAmountsOut(factory, amountIn, path); } - function getAmountsIn(uint amountOut, address[] memory path) - public + function getAmountsIn(uint amountOut, address[] calldata path) + external view virtual override returns (uint[] memory amounts) { - return ApeLibrary.getAmountsIn(factory, amountOut, path); + return _getAmountsIn(factory, amountOut, path); } } diff --git a/contracts/interfaces/IApeFactory.sol b/contracts/interfaces/IApeFactory.sol index 3d79091..332b69b 100644 --- a/contracts/interfaces/IApeFactory.sol +++ b/contracts/interfaces/IApeFactory.sol @@ -3,6 +3,8 @@ pragma solidity >=0.6.6; interface IApeFactory { event PairCreated(address indexed token0, address indexed token1, address pair, uint); + function INIT_CODE_PAIR_HASH() external view returns (bytes32); + function feeTo() external view returns (address); function feeToSetter() external view returns (address); diff --git a/contracts/libraries/ApeLibrary.sol b/contracts/libraries/ApeLibrary.sol index 1a09659..e6fe318 100644 --- a/contracts/libraries/ApeLibrary.sol +++ b/contracts/libraries/ApeLibrary.sol @@ -1,12 +1,25 @@ pragma solidity >=0.5.0; /* - * ApeSwapFinance - * App: https://apeswap.finance + ______ ______ + / \ / \ +| ▓▓▓▓▓▓\ ______ ______ | ▓▓▓▓▓▓\__ __ __ ______ ______ +| ▓▓__| ▓▓/ \ / \| ▓▓___\▓▓ \ | \ | \| \ / \ +| ▓▓ ▓▓ ▓▓▓▓▓▓\ ▓▓▓▓▓▓\\▓▓ \| ▓▓ | ▓▓ | ▓▓ \▓▓▓▓▓▓\ ▓▓▓▓▓▓\ +| ▓▓▓▓▓▓▓▓ ▓▓ | ▓▓ ▓▓ ▓▓_\▓▓▓▓▓▓\ ▓▓ | ▓▓ | ▓▓/ ▓▓ ▓▓ | ▓▓ +| ▓▓ | ▓▓ ▓▓__/ ▓▓ ▓▓▓▓▓▓▓▓ \__| ▓▓ ▓▓_/ ▓▓_/ ▓▓ ▓▓▓▓▓▓▓ ▓▓__/ ▓▓ +| ▓▓ | ▓▓ ▓▓ ▓▓\▓▓ \\▓▓ ▓▓\▓▓ ▓▓ ▓▓\▓▓ ▓▓ ▓▓ ▓▓ + \▓▓ \▓▓ ▓▓▓▓▓▓▓ \▓▓▓▓▓▓▓ \▓▓▓▓▓▓ \▓▓▓▓▓\▓▓▓▓ \▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓ + | ▓▓ | ▓▓ + | ▓▓ | ▓▓ + \▓▓ \▓▓ + * App: https://ApeSwap.finance * Medium: https://ape-swap.medium.com - * Twitter: https://twitter.com/ape_swap + * Twitter: https://twitter.com/ape_swap * Telegram: https://t.me/ape_swap * Announcements: https://t.me/ape_swap_news + * Reddit: https://reddit.com/r/ApeSwap + * Instagram: https://instagram.com/ApeSwap.finance * GitHub: https://github.com/ApeSwapFinance */ diff --git a/contracts/libraries/ApePairMath.sol b/contracts/libraries/ApePairMath.sol new file mode 100644 index 0000000..c9094bc --- /dev/null +++ b/contracts/libraries/ApePairMath.sol @@ -0,0 +1,107 @@ +pragma solidity >=0.5.0; + +/* + ______ ______ + / \ / \ +| ▓▓▓▓▓▓\ ______ ______ | ▓▓▓▓▓▓\__ __ __ ______ ______ +| ▓▓__| ▓▓/ \ / \| ▓▓___\▓▓ \ | \ | \| \ / \ +| ▓▓ ▓▓ ▓▓▓▓▓▓\ ▓▓▓▓▓▓\\▓▓ \| ▓▓ | ▓▓ | ▓▓ \▓▓▓▓▓▓\ ▓▓▓▓▓▓\ +| ▓▓▓▓▓▓▓▓ ▓▓ | ▓▓ ▓▓ ▓▓_\▓▓▓▓▓▓\ ▓▓ | ▓▓ | ▓▓/ ▓▓ ▓▓ | ▓▓ +| ▓▓ | ▓▓ ▓▓__/ ▓▓ ▓▓▓▓▓▓▓▓ \__| ▓▓ ▓▓_/ ▓▓_/ ▓▓ ▓▓▓▓▓▓▓ ▓▓__/ ▓▓ +| ▓▓ | ▓▓ ▓▓ ▓▓\▓▓ \\▓▓ ▓▓\▓▓ ▓▓ ▓▓\▓▓ ▓▓ ▓▓ ▓▓ + \▓▓ \▓▓ ▓▓▓▓▓▓▓ \▓▓▓▓▓▓▓ \▓▓▓▓▓▓ \▓▓▓▓▓\▓▓▓▓ \▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓ + | ▓▓ | ▓▓ + | ▓▓ | ▓▓ + \▓▓ \▓▓ + * App: https://ApeSwap.finance + * Medium: https://ape-swap.medium.com + * Twitter: https://twitter.com/ape_swap + * Telegram: https://t.me/ape_swap + * Announcements: https://t.me/ape_swap_news + * Reddit: https://reddit.com/r/ApeSwap + * Instagram: https://instagram.com/ApeSwap.finance + * GitHub: https://github.com/ApeSwapFinance + */ + +import '../interfaces/IApePair.sol'; +import "./SafeMath.sol"; + +abstract contract ApePairMath { + using SafeMath for uint; + + function INIT_CODE_PAIR_HASH() public view virtual returns (bytes32); + + // returns sorted token addresses, used to handle return values from pairs sorted in this order + function _sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) { + require(tokenA != tokenB, 'ApePairMath: IDENTICAL_ADDRESSES'); + (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); + require(token0 != address(0), 'ApePairMath: ZERO_ADDRESS'); + } + + // calculates the CREATE2 address for a pair without making any external calls + function _pairFor(address factory, address tokenA, address tokenB) internal view returns (address pair) { + (address token0, address token1) = _sortTokens(tokenA, tokenB); + pair = address(uint(keccak256(abi.encodePacked( + hex'ff', + factory, + keccak256(abi.encodePacked(token0, token1)), + INIT_CODE_PAIR_HASH() + )))); + } + + // fetches and sorts the reserves for a pair + function _getReserves(address factory, address tokenA, address tokenB) internal view returns (uint reserveA, uint reserveB) { + (address token0,) = _sortTokens(tokenA, tokenB); + _pairFor(factory, tokenA, tokenB); + (uint reserve0, uint reserve1,) = IApePair(_pairFor(factory, tokenA, tokenB)).getReserves(); + (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0); + } + + // given some amount of an asset and pair reserves, returns an equivalent amount of the other asset + function _quote(uint amountA, uint reserveA, uint reserveB) internal pure returns (uint amountB) { + require(amountA > 0, 'ApePairMath: INSUFFICIENT_AMOUNT'); + require(reserveA > 0 && reserveB > 0, 'ApePairMath: INSUFFICIENT_LIQUIDITY'); + amountB = amountA.mul(reserveB) / reserveA; + } + + // given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset + function _getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) internal pure returns (uint amountOut) { + require(amountIn > 0, 'ApePairMath: INSUFFICIENT_INPUT_AMOUNT'); + require(reserveIn > 0 && reserveOut > 0, 'ApePairMath: INSUFFICIENT_LIQUIDITY'); + uint amountInWithFee = amountIn.mul(998); + uint numerator = amountInWithFee.mul(reserveOut); + uint denominator = reserveIn.mul(1000).add(amountInWithFee); + amountOut = numerator / denominator; + } + + // given an output amount of an asset and pair reserves, returns a required input amount of the other asset + function _getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) internal pure returns (uint amountIn) { + require(amountOut > 0, 'ApePairMath: INSUFFICIENT_OUTPUT_AMOUNT'); + require(reserveIn > 0 && reserveOut > 0, 'ApePairMath: INSUFFICIENT_LIQUIDITY'); + uint numerator = reserveIn.mul(amountOut).mul(1000); + uint denominator = reserveOut.sub(amountOut).mul(998); + amountIn = (numerator / denominator).add(1); + } + + // performs chained getAmountOut calculations on any number of pairs + function _getAmountsOut(address factory, uint amountIn, address[] memory path) internal view returns (uint[] memory amounts) { + require(path.length >= 2, 'ApePairMath: INVALID_PATH'); + amounts = new uint[](path.length); + amounts[0] = amountIn; + for (uint i; i < path.length - 1; i++) { + (uint reserveIn, uint reserveOut) = _getReserves(factory, path[i], path[i + 1]); + amounts[i + 1] = _getAmountOut(amounts[i], reserveIn, reserveOut); + } + } + + // performs chained getAmountIn calculations on any number of pairs + function _getAmountsIn(address factory, uint amountOut, address[] memory path) internal view returns (uint[] memory amounts) { + require(path.length >= 2, 'ApePairMath: INVALID_PATH'); + amounts = new uint[](path.length); + amounts[amounts.length - 1] = amountOut; + for (uint i = path.length - 1; i > 0; i--) { + (uint reserveIn, uint reserveOut) = _getReserves(factory, path[i - 1], path[i]); + amounts[i - 1] = _getAmountIn(amounts[i], reserveIn, reserveOut); + } + } +}