Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 70 additions & 51 deletions contracts/ApeRouter.sol
Original file line number Diff line number Diff line change
@@ -1,30 +1,44 @@
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
*/

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');
_;
Expand All @@ -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,
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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));
Expand All @@ -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');
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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(
Expand All @@ -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)
);
}
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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)
Expand All @@ -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]);
Expand All @@ -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]);
Expand All @@ -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]);
Expand All @@ -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));
}
}
Expand All @@ -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);
Expand All @@ -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(
Expand All @@ -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));
Expand All @@ -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);
}
}
2 changes: 2 additions & 0 deletions contracts/interfaces/IApeFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
Loading