UniswapV2Router01.sol
pragma solidity =0.6.6;
import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol';
import '@uniswap/lib/contracts/libraries/TransferHelper.sol';
import './libraries/UniswapV2Library.sol';
import './interfaces/IUniswapV2Router01.sol';
import './interfaces/IERC20.sol';
import './interfaces/IWETH.sol';
contract UniswapV2Router01 is IUniswapV2Router01 {
address public immutable override factory;//声明为immutable表示不可修改
address public immutable override WETH;
modifier ensure(uint deadline) {
require(deadline >= block.timestamp, 'UniswapV2Router: EXPIRED');
_;
}
constructor(address _factory, address _WETH) public {
factory = _factory;
WETH = _WETH;
}
receive() external payable {
assert(msg.sender == WETH); // only accept ETH via fallback from the WETH contract
}
/**
* 添加流动性的私有方法,按照原来的比例最大化的添加流动性
* @param tokenA tokenA地址
* @param tokenB tokenB地址
* @param amountADesired 期望数量A
* @param amountBDesired 期望数量B
* @param amountAMin 最小数量A
* @param amountBMin 最小数量B
* @return amountA 最优的数量A
* @return amountB 最优的数量B
*/
function _addLiquidity(
address tokenA,
address tokenB,
uint amountADesired,
uint amountBDesired,
uint amountAMin,
uint amountBMin
) private returns (uint amountA, uint amountB) {
// 如果TokenA,TokenB的配对合约不存在则创建pair
if (IUniswapV2Factory(factory).getPair(tokenA, tokenB) == address(0)) {
IUniswapV2Factory(factory).createPair(tokenA, tokenB);
}
// 从pair合约获取储备量A, 储备量B
(uint reserveA, uint reserveB) = UniswapV2Library.getReserves(factory, tokenA, tokenB);
if (reserveA == 0 && reserveB == 0) {
//如果储备reserveA和reserveB都为0,则返回值(数量A,数量B)=期望值(期望数量A,期望数量B)
(amountA, amountB) = (amountADesired, amountBDesired);
} else {
// 最优数量B = 期望数量A * 储备量B / 储备量A
uint amountBOptimal = UniswapV2Library.quote(amountADesired, reserveA, reserveB);
// 如果最优数量B <= 期望数量B,则数量A,数量B = 期望数量A, 最优数量B
if (amountBOptimal <= amountBDesired) {
require(amountBOptimal >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT');
(amountA, amountB) = (amountADesired, amountBOptimal);
} else {
// 最优数量A = 期望数量A * 储备量A / 储备量B
uint amountAOptimal = UniswapV2Library.quote(amountBDesired, reserveB, reserveA);
assert(amountAOptimal <= amountADesired);
require(amountAOptimal >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT');
// 数量A,数量B = 最优数量A, 期望数量B
(amountA, amountB) = (amountAOptimal, amountBDesired);
}
}
}
/*
* 添加流动性
* @param token token地址
* @param amountTokenDesired 期望数量
* @param amountTokenMin Token最小数量
* @param amountETHMin ETH最小数量
* @param to to地址
* @param deadline 最后期限
* @return amountToken Token数量
* @return amountETH ETH数量
* @return liquidity 流动性数量
*/
function addLiquidity(
address tokenA,
address tokenB,
uint amountADesired,
uint amountBDesired,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
) external override ensure(deadline) returns (uint amountA, uint amountB, uint liquidity) {
// 从_addLiquidity方法中获取最优的数量A和数量B
(amountA, amountB) = _addLiquidity(tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin);
// 根据TokenA,TokenB地址,获取pair合约地址
address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
// 将计算好的AB的tokenA从msg.sender账户中安全发送到pair合约地址
TransferHelper.safeTransferFrom(tokenA, msg.sender, pair, amountA);
TransferHelper.safeTransferFrom(tokenB, msg.sender, pair, amountB);
// 调用pair合约的铸造方法,返回铸造给to的流动性数量
liquidity = IUniswapV2Pair(pair).mint(to);
}
/**
* 添加ETH流动性方法
* @param token token地址
* @param amountTokenDesired Token期望数量
* @param amountTokenMin Token最小数量
* @param amountETHMin ETH最小数量
* @param to to地址
* @param deadline 最后期限
* @return amountToken Token数量
* @return amountETH ETH数量
* @return liquidity 流动性数量
*/
function addLiquidityETH(
address token,
uint amountTokenDesired,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external override payable ensure(deadline) returns (uint amountToken, uint amountETH, uint liquidity) {
// 从_addLiquidity方法中获取最优的数量token和数量eth
(amountToken, amountETH) = _addLiquidity(
token,
WETH,
amountTokenDesired,
msg.value,
amountTokenMin,
amountETHMin
);
// 获取pair地址
address pair = UniswapV2Library.pairFor(factory, token, WETH);
// token和eth发送到pair合约
TransferHelper.safeTransferFrom(token, msg.sender, pair, amountToken);
IWETH(WETH).deposit{value: amountETH}();//存入eth
assert(IWETH(WETH).transfer(pair, amountETH));
// 获得流动性数量
liquidity = IUniswapV2Pair(pair).mint(to);
// 如果收到的主币数量>ETH数量,则返还差值
if (msg.value > amountETH) TransferHelper.safeTransferETH(msg.sender, msg.value - amountETH); // refund dust eth, if any
}
// **** REMOVE LIQUIDITY ****
/**
* 移除流动性
* @param tokenA tokenA地址
* @param tokenB tokenB地址
* @param liquidity 流动性数量
* @param amountAMin 最小数量A
* @param amountBMin 最小数量B
* @param to to地址
* @param deadline 最后期限
* @return amountA 数量A
* @return amountB 数量B
*/
function removeLiquidity(
address tokenA,
address tokenB,
uint liquidity,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
) public override ensure(deadline) returns (uint amountA, uint amountB) {
// 计算出pair地址
address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
// 将流动性数量发送到pair,即发送liquidity,提取本来的token。之前的操作已经在pair合约的mint方法中获得了liquidity
IUniswapV2Pair(pair).transferFrom(msg.sender, pair, liquidity); // send liquidity to pair
// pair合约销毁流动性,并将burn方法中计算出来的tokenA,tokenB的数量发送给to
(uint amount0, uint amount1) = IUniswapV2Pair(pair).burn(to);
// 排序
(address token0,) = UniswapV2Library.sortTokens(tokenA, tokenB);
// 按照排序后的token顺序返回数值AB
(amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0);
// 确保AB的值都大于最小值
require(amountA >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT');
require(amountB >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT');
}
/**
* 移除ETH流动性
* @param token token地址
* @param liquidity 流动性数量
* @param amountTokenMin token最小数量
* @param amountETHMin ETH最小数量
* @param to to地址
* @param deadline 最后期限
* @return amountToken token数量
* @return amountETH ETH数量
*/
function removeLiquidityETH(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) public override ensure(deadline) returns (uint amountToken, uint amountETH) {
// 移除流动性
(amountToken, amountETH) = removeLiquidity(
token,
WETH,
liquidity,
amountTokenMin,
amountETHMin,
address(this),
deadline
);
// token发送到to地址
TransferHelper.safeTransfer(token, to, amountToken);
// 从WETH取出ETH
IWETH(WETH).withdraw(amountETH);
// ETH发送到to地址
TransferHelper.safeTransferETH(to, amountETH);
}
/**
* 带签名移除流动性
* @param tokenA tokenA地址
* @param tokenB tokenB地址
* @param liquidity 流动性数量
* @param amountAMin 最小数量A
* @param amountBMin 最小数量B
* @param to to地址
* @param deadline 最后期限
* @param approveMax 全部批准
* @param v v
* @param r r
* @param s s
* @return amountA 数量A
* @return amountB 数量B
*/
function removeLiquidityWithPermit(
address tokenA,
address tokenB,
uint liquidity,
uint amountAMin,
uint amountBMin,
address to,
uint deadline,
bool approveMax, uint8 v, bytes32 r, bytes32 s
) external override returns (uint amountA, uint amountB) {
// 计算出pair地址
address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
// 如果全部批准,value值等于最大uint256,否则等于流动性
uint value = approveMax ? uint(-1) : liquidity;
// 调用pair合约的许可方法
IUniswapV2Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);
// 移除流动性
(amountA, amountB) = removeLiquidity(tokenA, tokenB, liquidity, amountAMin, amountBMin, to, deadline);
}
/**
* 带签名移除ETH流动性
* @param token token地址
* @param liquidity 流动性数量
* @param amountTokenMin token最小数量
* @param amountETHMin ETH最小数量
* @param to to地址
* @param deadline 最后期限
* @param approveMax 全部批准
* @param v v
* @param r r
* @param s s
* @return amountToken token数量
* @return amountETH ETH数量
与removeLiquidityETH方法类似
*/
function removeLiquidityETHWithPermit(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline,
bool approveMax, uint8 v, bytes32 r, bytes32 s
) external override returns (uint amountToken, uint amountETH) {
address pair = UniswapV2Library.pairFor(factory, token, WETH);
uint value = approveMax ? uint(-1) : liquidity;
IUniswapV2Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);
(amountToken, amountETH) = removeLiquidityETH(token, liquidity, amountTokenMin, amountETHMin, to, deadline);
}
// **** SWAP ****
// requires the initial amount to have already been sent to the first pair
/**
* 私有交换
* @param amounts 数额数组
* @param path 路径
* @param _to to地址
*/
function _swap(uint[] memory amounts, address[] memory path, address _to) private {
// 由于可能需要兑换不存在的交易对,因此由A到B可能需要经过一系列的路径才能达成目标,因此这里才会有循环,而每一次循环都是一次兑换
// 例如:A兑换B,但是不存在AB的交易池,即没有对应的pair合约,但是存在AC、CD、DB这三个交易对,则路径为ACDB
for (uint i; i < path.length - 1; i++) {
// 根据路径找到输入输出地址
(address input, address output) = (path[i], path[i + 1]);
// 排序
(address token0,) = UniswapV2Library.sortTokens(input, output);
// 输出数额 = 数组数额的下一个数值
uint amountOut = amounts[i + 1];
// 经过排序之后得到token0与input进行比较
// 如果token0 = input ,则输出数额0就为0。否则amountOut0 = amountOut;
// 因为一个token对应着一个输入和一个输出,而交换方法就是使用一个token换取另一个token,那么对应着的
// token 有输入input值,则输出out的值就为0;
// 另一个token 则没有输入input值,就有输出out的值
(uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOut) : (amountOut, uint(0));
// 当路径数组的下标 i 遍历到倒数第二个时,所指向的就是最终需要交换出来的_to地址
// 当 i 所指向的不是路径数组中的倒数第二个地址时,
// 那么to地址就是当前输出地址output【path[i+1]】和下一个输出地址path[i+2]计算出来的create2合约地址
address to = i < path.length - 2 ? UniswapV2Library.pairFor(factory, output, path[i + 2]) : _to;
// 调用配对合约的交换方法
IUniswapV2Pair(UniswapV2Library.pairFor(factory, input, output)).swap(amount0Out, amount1Out, to, new bytes(0));
}
}
/**
* 根据精确的token交换尽量多的token
* @param amountIn 精确输入数额
* @param amountOutMin 最小输出数额
* @param path 路径数组
* @param to to地址
* @param deadline 最后期限
* @return amounts[] 数额数组
*/
function swapExactTokensForTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external override ensure(deadline) returns (uint[] memory amounts) {
// 数额 = 计算每个合约对交换扣税之后的输出数额
// 遍历路径数组((输入数额 * 997 * 储备量Out) / (储备量In * 1000 + 输入数额 * 997))
amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path);
// 确认数额数组最后一个元素>=最小输出数额
require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
// 将数量为数额数组[0]的路径[0]的token从调用者账户发送到路径0,1的pair合约
TransferHelper.safeTransferFrom(path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]);
// 交换
_swap(amounts, path, to);
}
function swapTokensForExactTokens(
uint amountOut,
uint amountInMax,
address[] calldata path,
address to,
uint deadline
) external override ensure(deadline) returns (uint[] memory amounts) {
amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);
require(amounts[0] <= amountInMax, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT');
TransferHelper.safeTransferFrom(path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]);
_swap(amounts, path, to);
}
function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
external
override
payable
ensure(deadline)
returns (uint[] memory amounts)
{
require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH');
amounts = UniswapV2Library.getAmountsOut(factory, msg.value, path);
require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
IWETH(WETH).deposit{value: amounts[0]}();
assert(IWETH(WETH).transfer(UniswapV2Library.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)
external
override
ensure(deadline)
returns (uint[] memory amounts)
{
require(path[path.length - 1] == WETH, 'UniswapV2Router: INVALID_PATH');
amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);
require(amounts[0] <= amountInMax, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT');
TransferHelper.safeTransferFrom(path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]);
_swap(amounts, path, address(this));
IWETH(WETH).withdraw(amounts[amounts.length - 1]);
TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);
}
function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
external
override
ensure(deadline)
returns (uint[] memory amounts)
{
require(path[path.length - 1] == WETH, 'UniswapV2Router: INVALID_PATH');
amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path);
require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
TransferHelper.safeTransferFrom(path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]);
_swap(amounts, path, address(this));
IWETH(WETH).withdraw(amounts[amounts.length - 1]);
TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);
}
function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
external
override
payable
ensure(deadline)
returns (uint[] memory amounts)
{
require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH');
amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);
require(amounts[0] <= msg.value, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT');
IWETH(WETH).deposit{value: amounts[0]}();
assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]));
_swap(amounts, path, to);
if (msg.value > amounts[0]) TransferHelper.safeTransferETH(msg.sender, msg.value - amounts[0]); // refund dust eth, if any
}
function quote(uint amountA, uint reserveA, uint reserveB) public pure override returns (uint amountB) {
return UniswapV2Library.quote(amountA, reserveA, reserveB);
}
function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) public pure override returns (uint amountOut) {
return UniswapV2Library.getAmountOut(amountIn, reserveIn, reserveOut);
}
function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) public pure override returns (uint amountIn) {
return UniswapV2Library.getAmountOut(amountOut, reserveIn, reserveOut);
}
function getAmountsOut(uint amountIn, address[] memory path) public view override returns (uint[] memory amounts) {
return UniswapV2Library.getAmountsOut(factory, amountIn, path);
}
function getAmountsIn(uint amountOut, address[] memory path) public view override returns (uint[] memory amounts) {
return UniswapV2Library.getAmountsIn(factory, amountOut, path);
}
}
转载请注明来源