pragma solidity =0.6.6; import "../core/interfaces/IAstraFactory.sol"; import "./libraries/TransferHelper.sol"; import "./libraries/AstraLibrary.sol"; import "./interfaces/IAstraRouter01.sol"; import "./interfaces/IERC20.sol"; import "./interfaces/ISAMB.sol"; contract AstraRouter01 is IAstraRouter01 { address public immutable override factory; address public immutable override SAMB; modifier ensure(uint256 deadline) { require(deadline >= block.timestamp, "AstraRouter: EXPIRED"); _; } constructor(address _factory, address _SAMB) public { factory = _factory; SAMB = _SAMB; } receive() external payable { assert(msg.sender == SAMB); // only accept AMB via fallback from the SAMB contract } // **** ADD LIQUIDITY **** function _addLiquidity( address tokenA, address tokenB, uint256 amountADesired, uint256 amountBDesired, uint256 amountAMin, uint256 amountBMin ) private returns (uint256 amountA, uint256 amountB) { // create the pair if it doesn't exist yet if (IAstraFactory(factory).getPair(tokenA, tokenB) == address(0)) { IAstraFactory(factory).createPair(tokenA, tokenB); } (uint256 reserveA, uint256 reserveB) = AstraLibrary.getReserves( factory, tokenA, tokenB ); if (reserveA == 0 && reserveB == 0) { (amountA, amountB) = (amountADesired, amountBDesired); } else { uint256 amountBOptimal = AstraLibrary.quote( amountADesired, reserveA, reserveB ); if (amountBOptimal <= amountBDesired) { require( amountBOptimal >= amountBMin, "AstraRouter: INSUFFICIENT_B_AMOUNT" ); (amountA, amountB) = (amountADesired, amountBOptimal); } else { uint256 amountAOptimal = AstraLibrary.quote( amountBDesired, reserveB, reserveA ); assert(amountAOptimal <= amountADesired); require( amountAOptimal >= amountAMin, "AstraRouter: INSUFFICIENT_A_AMOUNT" ); (amountA, amountB) = (amountAOptimal, amountBDesired); } } } function addLiquidity( address tokenA, address tokenB, uint256 amountADesired, uint256 amountBDesired, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline ) external override ensure(deadline) returns ( uint256 amountA, uint256 amountB, uint256 liquidity ) { (amountA, amountB) = _addLiquidity( tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin ); address pair = AstraLibrary.pairFor(factory, tokenA, tokenB); TransferHelper.safeTransferFrom(tokenA, msg.sender, pair, amountA); TransferHelper.safeTransferFrom(tokenB, msg.sender, pair, amountB); liquidity = IAstraPair(pair).mint(to); } function addLiquidityAMB( address token, uint256 amountTokenDesired, uint256 amountTokenMin, uint256 amountAMBMin, address to, uint256 deadline ) external payable override ensure(deadline) returns ( uint256 amountToken, uint256 amountAMB, uint256 liquidity ) { (amountToken, amountAMB) = _addLiquidity( token, SAMB, amountTokenDesired, msg.value, amountTokenMin, amountAMBMin ); address pair = AstraLibrary.pairFor(factory, token, SAMB); TransferHelper.safeTransferFrom(token, msg.sender, pair, amountToken); ISAMB(SAMB).deposit{value: amountAMB}(); assert(ISAMB(SAMB).transfer(pair, amountAMB)); liquidity = IAstraPair(pair).mint(to); if (msg.value > amountAMB) TransferHelper.safeTransferAMB(msg.sender, msg.value - amountAMB); // refund dust amb, if any } // **** REMOVE LIQUIDITY **** function removeLiquidity( address tokenA, address tokenB, uint256 liquidity, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline ) public override ensure(deadline) returns (uint256 amountA, uint256 amountB) { address pair = AstraLibrary.pairFor(factory, tokenA, tokenB); IAstraPair(pair).transferFrom(msg.sender, pair, liquidity); // send liquidity to pair (uint256 amount0, uint256 amount1) = IAstraPair(pair).burn(to); (address token0, ) = AstraLibrary.sortTokens(tokenA, tokenB); (amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0); require( amountA >= amountAMin, "AstraRouter: INSUFFICIENT_A_AMOUNT" ); require( amountB >= amountBMin, "AstraRouter: INSUFFICIENT_B_AMOUNT" ); } function removeLiquidityAMB( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountAMBMin, address to, uint256 deadline ) public override ensure(deadline) returns (uint256 amountToken, uint256 amountAMB) { (amountToken, amountAMB) = removeLiquidity( token, SAMB, liquidity, amountTokenMin, amountAMBMin, address(this), deadline ); TransferHelper.safeTransfer(token, to, amountToken); ISAMB(SAMB).withdraw(amountAMB); TransferHelper.safeTransferAMB(to, amountAMB); } function removeLiquidityWithPermit( address tokenA, address tokenB, uint256 liquidity, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external override returns (uint256 amountA, uint256 amountB) { address pair = AstraLibrary.pairFor(factory, tokenA, tokenB); uint256 value = approveMax ? uint256(-1) : liquidity; IAstraPair(pair).permit( msg.sender, address(this), value, deadline, v, r, s ); (amountA, amountB) = removeLiquidity( tokenA, tokenB, liquidity, amountAMin, amountBMin, to, deadline ); } function removeLiquidityAMBWithPermit( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountAMBMin, address to, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external override returns (uint256 amountToken, uint256 amountAMB) { address pair = AstraLibrary.pairFor(factory, token, SAMB); uint256 value = approveMax ? uint256(-1) : liquidity; IAstraPair(pair).permit( msg.sender, address(this), value, deadline, v, r, s ); (amountToken, amountAMB) = removeLiquidityAMB( token, liquidity, amountTokenMin, amountAMBMin, to, deadline ); } // **** SWAP **** // requires the initial amount to have already been sent to the first pair function _swap( uint256[] memory amounts, address[] memory path, address _to ) private { for (uint256 i; i < path.length - 1; i++) { (address input, address output) = (path[i], path[i + 1]); (address token0, ) = AstraLibrary.sortTokens(input, output); uint256 amountOut = amounts[i + 1]; (uint256 amount0Out, uint256 amount1Out) = input == token0 ? (uint256(0), amountOut) : (amountOut, uint256(0)); address to = i < path.length - 2 ? AstraLibrary.pairFor(factory, output, path[i + 2]) : _to; IAstraPair(AstraLibrary.pairFor(factory, input, output)) .swap(amount0Out, amount1Out, to, new bytes(0)); } } function swapExactTokensForTokens( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external override ensure(deadline) returns (uint256[] memory amounts) { amounts = AstraLibrary.getAmountsOut(factory, amountIn, path); require( amounts[amounts.length - 1] >= amountOutMin, "AstraRouter: INSUFFICIENT_OUTPUT_AMOUNT" ); TransferHelper.safeTransferFrom( path[0], msg.sender, AstraLibrary.pairFor(factory, path[0], path[1]), amounts[0] ); _swap(amounts, path, to); } function swapTokensForExactTokens( uint256 amountOut, uint256 amountInMax, address[] calldata path, address to, uint256 deadline ) external override ensure(deadline) returns (uint256[] memory amounts) { amounts = AstraLibrary.getAmountsIn(factory, amountOut, path); require( amounts[0] <= amountInMax, "AstraRouter: EXCESSIVE_INPUT_AMOUNT" ); TransferHelper.safeTransferFrom( path[0], msg.sender, AstraLibrary.pairFor(factory, path[0], path[1]), amounts[0] ); _swap(amounts, path, to); } function swapExactAMBForTokens( uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external payable override ensure(deadline) returns (uint256[] memory amounts) { require(path[0] == SAMB, "AstraRouter: INVALID_PATH"); amounts = AstraLibrary.getAmountsOut(factory, msg.value, path); require( amounts[amounts.length - 1] >= amountOutMin, "AstraRouter: INSUFFICIENT_OUTPUT_AMOUNT" ); ISAMB(SAMB).deposit{value: amounts[0]}(); assert( ISAMB(SAMB).transfer( AstraLibrary.pairFor(factory, path[0], path[1]), amounts[0] ) ); _swap(amounts, path, to); } function swapTokensForExactAMB( uint256 amountOut, uint256 amountInMax, address[] calldata path, address to, uint256 deadline ) external override ensure(deadline) returns (uint256[] memory amounts) { require(path[path.length - 1] == SAMB, "AstraRouter: INVALID_PATH"); amounts = AstraLibrary.getAmountsIn(factory, amountOut, path); require( amounts[0] <= amountInMax, "AstraRouter: EXCESSIVE_INPUT_AMOUNT" ); TransferHelper.safeTransferFrom( path[0], msg.sender, AstraLibrary.pairFor(factory, path[0], path[1]), amounts[0] ); _swap(amounts, path, address(this)); ISAMB(SAMB).withdraw(amounts[amounts.length - 1]); TransferHelper.safeTransferAMB(to, amounts[amounts.length - 1]); } function swapExactTokensForAMB( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external override ensure(deadline) returns (uint256[] memory amounts) { require(path[path.length - 1] == SAMB, "AstraRouter: INVALID_PATH"); amounts = AstraLibrary.getAmountsOut(factory, amountIn, path); require( amounts[amounts.length - 1] >= amountOutMin, "AstraRouter: INSUFFICIENT_OUTPUT_AMOUNT" ); TransferHelper.safeTransferFrom( path[0], msg.sender, AstraLibrary.pairFor(factory, path[0], path[1]), amounts[0] ); _swap(amounts, path, address(this)); ISAMB(SAMB).withdraw(amounts[amounts.length - 1]); TransferHelper.safeTransferAMB(to, amounts[amounts.length - 1]); } function swapAMBForExactTokens( uint256 amountOut, address[] calldata path, address to, uint256 deadline ) external payable override ensure(deadline) returns (uint256[] memory amounts) { require(path[0] == SAMB, "AstraRouter: INVALID_PATH"); amounts = AstraLibrary.getAmountsIn(factory, amountOut, path); require( amounts[0] <= msg.value, "AstraRouter: EXCESSIVE_INPUT_AMOUNT" ); ISAMB(SAMB).deposit{value: amounts[0]}(); assert( ISAMB(SAMB).transfer( AstraLibrary.pairFor(factory, path[0], path[1]), amounts[0] ) ); _swap(amounts, path, to); if (msg.value > amounts[0]) TransferHelper.safeTransferAMB(msg.sender, msg.value - amounts[0]); // refund dust amb, if any } function quote( uint256 amountA, uint256 reserveA, uint256 reserveB ) public pure override returns (uint256 amountB) { return AstraLibrary.quote(amountA, reserveA, reserveB); } function getAmountOut( uint256 amountIn, uint256 reserveIn, uint256 reserveOut ) public pure override returns (uint256 amountOut) { return AstraLibrary.getAmountOut(amountIn, reserveIn, reserveOut); } function getAmountIn( uint256 amountOut, uint256 reserveIn, uint256 reserveOut ) public pure override returns (uint256 amountIn) { return AstraLibrary.getAmountOut(amountOut, reserveIn, reserveOut); } function getAmountsOut(uint256 amountIn, address[] memory path) public view override returns (uint256[] memory amounts) { return AstraLibrary.getAmountsOut(factory, amountIn, path); } function getAmountsIn(uint256 amountOut, address[] memory path) public view override returns (uint256[] memory amounts) { return AstraLibrary.getAmountsIn(factory, amountOut, path); } }