最近,看到一个比较厉害的机器人,每次夹人可以获取0.2个ETH,利润还是十分的诱人。
兴趣既然萌生了,那么也去研究了解一下。
那么,首先还是要读一下去中心化交易所的源码(uniswap)。
uniswap底层源码分三个模块,Factory,Pair,Router。
Factory存放在多个Pair,Router是通过调用Factory,Factory再来找到Pair,更加详细的交易逻辑,还得看底层源码。
Factory
pragma solidity =0.5.16;
import './interfaces/IUniswapV2Factory.sol';
import './UniswapV2Pair.sol';
contract UniswapV2Factory is IUniswapV2Factory {
address public feeTo;
address public feeToSetter;
mapping(address => mapping(address => address)) public getPair;
address[] public allPairs;
event PairCreated(address indexed token0, address indexed token1, address pair, uint);
// 初始化就设定好谁是设定手续费接收的人的设定者
constructor(address _feeToSetter) public {
feeToSetter = _feeToSetter;
}
// 获取一共有多少个交易对
function allPairsLength() external view returns (uint) {
return allPairs.length;
}
// 创建交易对函数
// 创建交易对只是创建一个 交易对地址 ,还没有往里面添加代币数量
function createPair(address tokenA, address tokenB) external returns (address pair) {
//必须是两个不一样的ERC20合约地址
require(tokenA != tokenB, 'UniswapV2: IDENTICAL_ADDRESSES');
// 让tokenA和tokenB的地址从小到大排列
(address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
//token地址不能是0
require(token0 != address(0), 'UniswapV2: ZERO_ADDRESS');
//必须是uniswap中未创建过的pair
require(getPair[token0][token1] == address(0), 'UniswapV2: PAIR_EXISTS'); // single check is sufficient
//获取模板合约UniswapV2Pair的creationCode
bytes memory bytecode = type(UniswapV2Pair).creationCode;
//以两个token的地址作为种子生产salt
bytes32 salt = keccak256(abi.encodePacked(token0, token1));
//直接调用汇编创建合约
assembly {
pair := create2(0, add(bytecode, 32), mload(bytecode), salt)
}
//初始化刚刚创建的合约
IUniswapV2Pair(pair).initialize(token0, token1);
// 交易对映射填充
//记录刚刚创建的合约对应的pair
getPair[token0][token1] = pair;
getPair[token1][token0] = pair;
allPairs.push(pair);
emit PairCreated(token0, token1, pair, allPairs.length);
}
// 设置接收手续费的人,只能设置者能设置
// 用于设置feeTo地址,只有feeToSetter才可以设置。
function setFeeTo(address _feeTo) external {
require(msg.sender == feeToSetter, 'UniswapV2: FORBIDDEN');
feeTo = _feeTo;
}
// 设置接收手续费的人的设置者,只能上一个设置者进行设置,也就是设置权利转交
// 用于设置feeToSetter地址,必须是现任feeToSetter才可以设置。
function setFeeToSetter(address _feeToSetter) external {
require(msg.sender == feeToSetter, 'UniswapV2: FORBIDDEN');
feeToSetter = _feeToSetter;
}
}
Pair
pragma solidity =0.5.16;
import './interfaces/IUniswapV2Pair.sol';
import './UniswapV2ERC20.sol';
import './libraries/Math.sol';
import './libraries/UQ112x112.sol';
import './interfaces/IERC20.sol';
import './interfaces/IUniswapV2Factory.sol';
import './interfaces/IUniswapV2Callee.sol';
contract UniswapV2Pair is IUniswapV2Pair, UniswapV2ERC20 {
using SafeMath for uint;
using UQ112x112 for uint224;
// 最低流动性
uint public constant MINIMUM_LIQUIDITY = 10**3;
// 获取transfer方法的bytecode前四个字节
bytes4 private constant SELECTOR = bytes4(keccak256(bytes('transfer(address,uint256)')));
address public factory;
address public token0;
address public token1;
uint112 private reserve0; // uses single storage slot, accessible via getReserves == 使用单个存储槽,可通过 getReserves 访问
uint112 private reserve1; // uses single storage slot, accessible via getReserves
uint32 private blockTimestampLast; // uses single storage slot, accessible via getReserves
uint public price0CumulativeLast; // 最后价格累计的 0 价格 ?
uint public price1CumulativeLast;
// 紧接最近一次流动性事件之后
uint public kLast; // reserve0 * reserve1, as of immediately after the most recent liquidity event
uint private unlocked = 1;
// 防止递归迭代出现问题,所以要上锁
//一个锁,使用该modifier的函数在unlocked==1时才可以进入,
//第一个调用者进入后,会将unlocked置为0,此使第二个调用者无法再进入
//执行完_部分的代码后,才会再将unlocked置1,重新将锁打开
modifier lock() {
require(unlocked == 1, 'UniswapV2: LOCKED');
unlocked = 0;
_;
unlocked = 1;
}
// 获取储备: 返回: _reserve0, _reserve1, _blockTimestampLast
// 用于获取两个token在池子中的数量和最后更新的时间
function getReserves() public view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast) {
_reserve0 = reserve0;
_reserve1 = reserve1;
// 时间戳
_blockTimestampLast = blockTimestampLast;
}
// 转账,安全校验
function _safeTransfer(address token, address to, uint value) private {
// 调用transfer方法,把地址token中的value个coin转账给to
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR, to, value));
// 检查返回值,必须成功否则报错
require(success && (data.length == 0 || abi.decode(data, (bool))), 'UniswapV2: TRANSFER_FAILED');
}
event Mint(address indexed sender, uint amount0, uint amount1);
event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
event Swap(address indexed sender,uint amount0In,uint amount1In,uint amount0Out,uint amount1Out,address indexed to);
event Sync(uint112 reserve0, uint112 reserve1);
//部署此合约时将msg.sender设置为factory,后续初始化时会用到这个值
constructor() public {
factory = msg.sender;
}
// called once by the factory at time of deployment
// 在UniswapV2Factory.sol的createPair中调用过
function initialize(address _token0, address _token1) external {
require(msg.sender == factory, 'UniswapV2: FORBIDDEN'); // sufficient check
token0 = _token0;
token1 = _token1;
}
// update reserves and, on the first call per block, price accumulators
// 更新储备,并在每个区块的第一次调用时更新价格累加器
/**
更新变量:
blockTimestampLast
reserve0
reserve1
price0CumulativeLast
price1CumulativeLast
*/
// 这个函数是用来更新价格oracle的,计算累计价格
function _update(uint balance0, uint balance1, uint112 _reserve0, uint112 _reserve1) private {
// 溢出校验
require(balance0 <= uint112(-1) && balance1 <= uint112(-1), 'UniswapV2: OVERFLOW');
uint32 blockTimestamp = uint32(block.timestamp % 2**32);
uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
// 计算时间加权的累计价格,256位中,前112位用来存整数,后112位用来存小数,多的32位用来存溢出的值
if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
// * never overflows, and + overflow is desired
price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;
price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;
}
// 更新reserve值
reserve0 = uint112(balance0);
reserve1 = uint112(balance1);
blockTimestampLast = blockTimestamp;
emit Sync(reserve0, reserve1);
}
// if fee is on, mint liquidity equivalent to 1/6th of the growth in sqrt(k)
// 如果收费,增发流动性相当于 sqrt(k) 增长的 1/6
function _mintFee(uint112 _reserve0, uint112 _reserve1) private returns (bool feeOn) {
// 获取接收手续费的地址
address feeTo = IUniswapV2Factory(factory).feeTo();
// 手续费接收者不为0地址
feeOn = feeTo != address(0);
uint _kLast = kLast; // gas savings
// 手续费接收者不为0地址
if (feeOn) {
if (_kLast != 0) {
uint rootK = Math.sqrt(uint(_reserve0).mul(_reserve1));
uint rootKLast = Math.sqrt(_kLast);
if (rootK > rootKLast) {
uint numerator = totalSupply.mul(rootK.sub(rootKLast));
uint denominator = rootK.mul(5).add(rootKLast);
uint liquidity = numerator / denominator;
if (liquidity > 0) _mint(feeTo, liquidity);
}
}
}
// 手续费接收者为0, 并且kLast 不为 0
else if (_kLast != 0) {
kLast = 0;
}
}
// this low-level function should be called from a contract which performs important safety checks
// 这个低级函数应该从执行重要安全检查的合约中调用
function mint(address to) external lock returns (uint liquidity) {
(uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
// 合约里两种token的当前的balance
uint balance0 = IERC20(token0).balanceOf(address(this));
uint balance1 = IERC20(token1).balanceOf(address(this));
// 获得当前balance和上一次缓存的余额的差值
// 因为balance是动态变化的,reserve是静态变化的
uint amount0 = balance0.sub(_reserve0);
uint amount1 = balance1.sub(_reserve1);
// 计算手续费
bool feeOn = _mintFee(_reserve0, _reserve1);
// gas 节省,必须在此处定义,因为 totalSupply 可以在 _mintFee 中更新
// totalSupply 是 pair 的凭证
uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
if (_totalSupply == 0) {
// 第一次铸币,也就是第一次注入流动性,值为根号k减去MINIMUM_LIQUIDITY,防止数据溢出
liquidity = Math.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY);
// 把MINIMUM_LIQUIDITY赋给地址0,永久锁住
_mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens
} else {
// 计算增量的token占总池子的比例,作为新铸币的数量
// 木桶法则,按最少的来,按当前投入的占池子总的比例增发
liquidity = Math.min(amount0.mul(_totalSupply) / _reserve0, amount1.mul(_totalSupply) / _reserve1);
}
require(liquidity > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_MINTED');
// 铸币,修改to的token数量及totalsupply
// 给to地址发凭证,同时pair合约的totalSupply增发同等的凭证
_mint(to, liquidity);
// 更新时间加权平均价格
_update(balance0, balance1, _reserve0, _reserve1);
if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
emit Mint(msg.sender, amount0, amount1);
}
// this low-level function should be called from a contract which performs important safety checks
function burn(address to) external lock returns (uint amount0, uint amount1) {
(uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
address _token0 = token0; // gas savings
address _token1 = token1; // gas savings
uint balance0 = IERC20(_token0).balanceOf(address(this));
uint balance1 = IERC20(_token1).balanceOf(address(this));
uint liquidity = balanceOf[address(this)];
bool feeOn = _mintFee(_reserve0, _reserve1);
uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
// 计算返回的 amount0/1
amount0 = liquidity.mul(balance0) / _totalSupply; // using balances ensures pro-rata distribution
amount1 = liquidity.mul(balance1) / _totalSupply; // using balances ensures pro-rata distribution
require(amount0 > 0 && amount1 > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_BURNED');
_burn(address(this), liquidity);
// _token0/1 给 to 转 amount0/1
_safeTransfer(_token0, to, amount0);
_safeTransfer(_token1, to, amount1);
// 获取转账后的balance
balance0 = IERC20(_token0).balanceOf(address(this));
balance1 = IERC20(_token1).balanceOf(address(this));
// 更新 reserve0, reserve1 和 时间戳
_update(balance0, balance1, _reserve0, _reserve1);
if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
emit Burn(msg.sender, amount0, amount1, to);
}
// this low-level function should be called from a contract which performs important safety checks
// 交易函数
// 可以是 token0 --> token1,
// 也可以是 token1 --> token0
// 但 参数中:amount0Out 和 amount1Out 中有一个值是0
function swap(
uint amount0Out,
uint amount1Out,
address to,
bytes calldata data
) external lock
{
require(amount0Out > 0 || amount1Out > 0, 'UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT');
(uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
require(amount0Out < _reserve0 && amount1Out < _reserve1, 'UniswapV2: INSUFFICIENT_LIQUIDITY');
uint balance0;
uint balance1;
{ // scope for _token{0,1}, avoids stack too deep errors
address _token0 = token0;
address _token1 = token1;
require(to != _token0 && to != _token1, 'UniswapV2: INVALID_TO');
// 划转操作
if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens
if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens
if (data.length > 0) IUniswapV2Callee(to).uniswapV2Call(msg.sender, amount0Out, amount1Out, data);
balance0 = IERC20(_token0).balanceOf(address(this));
balance1 = IERC20(_token1).balanceOf(address(this));
}
uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;
uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;
require(amount0In > 0 || amount1In > 0, 'UniswapV2: INSUFFICIENT_INPUT_AMOUNT');
{ // scope for reserve{0,1}Adjusted, avoids stack too deep errors
// 防止数据溢出校验
uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3));
uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3));
require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), 'UniswapV2: K');
}
// 更新
_update(balance0, balance1, _reserve0, _reserve1);
emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
}
// force balances to match reserves
// 强制balance以匹配储备
function skim(address to) external lock {
address _token0 = token0; // gas savings
address _token1 = token1; // gas savings
_safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)).sub(reserve0));
_safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)).sub(reserve1));
}
// force reserves to match balances
// 强制储备以匹配balance
function sync() external lock {
_update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)), reserve0, reserve1);
}
}
Router v1
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;
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
}
// **** ADD LIQUIDITY ****
// 返回值:填充 A,B 数量
// 添加流动池
// 这个函数是为了计算往池子里填充 A,B 数量
function _addLiquidity(
address tokenA,// A地址
address tokenB,// B地址
uint amountADesired, // A的填充量
uint amountBDesired, // B的填充量
uint amountAMin, // A的填充量最小值
uint amountBMin // B的填充量最小值
) private returns (uint amountA, uint amountB) {
// 如果不存在这个交易对,那么新建一个
if (IUniswapV2Factory(factory).getPair(tokenA, tokenB) == address(0)) {
IUniswapV2Factory(factory).createPair(tokenA, tokenB);
}
// 获取当前池子A,B的储备量
(uint reserveA, uint reserveB) = UniswapV2Library.getReserves(factory, tokenA, tokenB);
if (reserveA == 0 && reserveB == 0) {
// 新建的池子,直接填充
(amountA, amountB) = (amountADesired, amountBDesired);
}
else {
// 如果两个储备量不为0,需要根据当前的价格/比例去新增流动性
// 按A的比例填充 B的数量
// AA/BB = A/B --> AA = BB*(A/B)
uint amountBOptimal = UniswapV2Library.quote(amountADesired, reserveA, reserveB);
// amountBMin <= amountBOptimal <= amountBDesired
if (amountBOptimal <= amountBDesired) {
require(amountBOptimal >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT');
(amountA, amountB) = (amountADesired, amountBOptimal);
}
else {
// 按B的比例填充 A的数量
// BB/AA = B/A --> BB = AA*(B/A)
uint amountAOptimal = UniswapV2Library.quote(amountBDesired, reserveB, reserveA);
assert(amountAOptimal <= amountADesired);
require(amountAOptimal >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT');
(amountA, amountB) = (amountAOptimal, amountBDesired);
}
}
}
// 返回值:A,B 数量及得到的凭证数量
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) {
// 返回值:A,B 数量及得到的凭证数量
// 调用_addLiquidity,计算需要打入的 A,B 数量
(amountA, amountB) = _addLiquidity(tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin);
// 获取 pair 的地址
address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
// msg.sender 往 pair 打入 amount 的 A 或者 B
TransferHelper.safeTransferFrom(tokenA, msg.sender, pair, amountA);
TransferHelper.safeTransferFrom(tokenB, msg.sender, pair, amountB);
// pair 给 to 发 liquidity 数量的凭证, 并且 pair 增发 liquidity 的 lp
liquidity = IUniswapV2Pair(pair).mint(to);
}
// 添加池子,返回值是:token需要填充的数量,ETH需要填充的数量,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) {
(amountToken, amountETH) = _addLiquidity(
token,
WETH,
amountTokenDesired,
msg.value,
amountTokenMin,
amountETHMin
);
// 获取 pair 地址
address pair = UniswapV2Library.pairFor(factory, token, WETH);
// 给pair转代币数量
TransferHelper.safeTransferFrom(token, msg.sender, pair, amountToken);
// 调用weth的兑换方法,通过eth换weth,eth是公链币,weth是代币,但是它们兑换比例是 1:1
IWETH(WETH).deposit{value: amountETH}();
assert(IWETH(WETH).transfer(pair, amountETH));
liquidity = IUniswapV2Pair(pair).mint(to);
//如果传入的eth数量,大于实际所需的eth数量, 将剩余的eth返还给用户
if (msg.value > amountETH) TransferHelper.safeTransferETH(msg.sender, msg.value - amountETH);
}
// **** REMOVE LIQUIDITY ****
// 移除流动性,该方法需要先将lp代币授权给路由合约,才能代扣lp
// 返回值:amountA,amountB
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) {
address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
// 把msg.sender的lp还回去pair地址
IUniswapV2Pair(pair).transferFrom(msg.sender, pair, liquidity);
// 调用pair的burn方法, 内部会将两个币的数量转给to, 返回值就是两个代币的输出数量
(uint amount0, uint amount1) = IUniswapV2Pair(pair).burn(to);
// 通过排序确认两个amountA/B
(address token0,) = UniswapV2Library.sortTokens(tokenA, tokenB);
(amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0);
require(amountA >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT');
require(amountB >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT');
}
// 移除流动性(其中一个返还币是ETH),该方法需要先将lp代币授权给路由合约,才能代扣lp
function removeLiquidityETH(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) public override ensure(deadline) returns (uint amountToken, uint amountETH) {
// 调用上面的removeLiquidity方法,传入的是WETH
(amountToken, amountETH) = removeLiquidity(
token,
WETH,
liquidity,
amountTokenMin,
amountETHMin,
address(this),// 接收代币的是路由地址
deadline
);
// 将代币转给to
TransferHelper.safeTransfer(token, to, amountToken);
// 将weth转换成eth
IWETH(WETH).withdraw(amountETH);
// 将Eth转给to
TransferHelper.safeTransferETH(to, amountETH);
}
/*
permit在前端的使用场景,就是移除流动性的时候, 有个授权实际没有发送交易,只是要求签名,签名会得到参数中的v/r/s
在实际调用该移除的方法传进来,内部验签,确认是该用户,就将移除的lp的数量,授权给路由,可以代扣lp
*/
// 返回值:amountA, amountB
function removeLiquidityWithPermit(
address tokenA,
address tokenB,
uint liquidity,
uint amountAMin,
uint amountBMin,
address to,
uint deadline,
bool approveMax, uint8 v, bytes32 r, bytes32 s // v,r,s 验签,通过就授权给路由
) external override returns (uint amountA, uint amountB) {
// 获取到pair,调用pair的permit(内部实际就是授权给路由)
address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
uint value = approveMax ? uint(-1) : liquidity;
IUniswapV2Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);
// 最终还是调用上面的removeLiquidity方法
(amountA, amountB) = removeLiquidity(tokenA, tokenB, liquidity, amountAMin, amountBMin, to, deadline);
}
// WithPermit,移除时,其中一个返回eth
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) {
// 获取到pair,调用pair的permit(内部实际就是授权给路由)
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);
// 最终还是调用上面的removeLiquidityETH方法
(amountToken, amountETH) = removeLiquidityETH(token, liquidity, amountTokenMin, amountETHMin, to, deadline);
}
// **** SWAP ****
// requires the initial amount to have already been sent to the first pair
// 交易方法
// 需要先将amounts[0]的金额【已经】转到第一个pair地址(即path[0]+path[1]组成的pair)
function _swap(
uint[] memory amounts,
address[] memory path,
address _to
) private
{
for (uint i; i < path.length - 1; i++) {
(address input, address output) = (path[i], path[i + 1]);
/**
因为Factory里,tokenA和tokenB的地址从小到大排列。
创建Pair的时候,也是token0 和 token1的地址也是从小到大排列。
Pair内的swap函数的参数列表中(amount0Out 和 amount1Out) 是和 token0 和 token1 对应
所以需要排序,确定 amountOut 对应的是 token0 还是 token1
*/
(address token0,) = UniswapV2Library.sortTokens(input, output);
uint amountOut = amounts[i + 1];
// output 地址对应的 amountOut
// amount0Out, amount1Out 对应的值是 (0或amountOut)
(uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOut) : (amountOut, uint(0));
address to = i < path.length - 2 ? UniswapV2Library.pairFor(factory, output, path[i + 2]) : _to;
// IUniswapV2Pair 调用 swap函数
IUniswapV2Pair(UniswapV2Library.pairFor(factory, input, output)).swap(amount0Out, amount1Out, to, new bytes(0));
}
}
// 输入精确的token,换取另一个token(输出量不确定)
function swapExactTokensForTokens(
uint amountIn,// 输入金额
uint amountOutMin,// 最小输入金额
address[] calldata path,//交易路径
address to,// 接收地址
uint deadline
) external override ensure(deadline) returns (uint[] memory amounts) {
// 获取AmountsOut列表
amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path);
// 需要满足计算得来最终输出量大于等于最小输出金额
require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
// 先将amounts[0]入金额转入第一个pair
// msg.sender 给 UniswapV2Library.pairFor(factory, path[0], path[1]) 转数量 amounts[0] 的 path[0]
TransferHelper.safeTransferFrom(path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]);
// 调用内部_swap方法
_swap(amounts, path, to);
}
/**
输入不确定数量A,换取精确输出的B
(例:精确输出1个token,正常100u可以换1个token,
由于发交易后其他人先交易过,导致价格变了,
可能95或者105可以买1个token,95肯定交易通过,
如果amountInMax是102,该交易就无法成交,回退)
*/
function swapTokensForExactTokens(
uint amountOut,
uint amountInMax,
address[] calldata path,
address to,
uint deadline
) external override ensure(deadline) returns (uint[] memory amounts) {
// 获取AmountsIn列表
amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);
// 同swapExactTokensForTokens
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);
}
/**
输入精确的eth换取不定量的token,对应swapExactTokensForTokens,
不过输入的是eth,换成weth就一样了
*/
function swapExactETHForTokens(
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
)external override payable ensure(deadline) returns (uint[] memory amounts)
{
// 要求path[0]是weth地址
require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH');
amounts = UniswapV2Library.getAmountsOut(factory, msg.value, path);
require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
// ETH 换取准确的 WETH
IWETH(WETH).deposit{value: amounts[0]}();
assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]));
_swap(amounts, path, to);
}
/**
输入不定量的A,换取精确的输出ETH,对应swapTokensForExactTokens,
只是内部将weth转成eth再给用户
*/
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));
// ETH转换
IWETH(WETH).withdraw(amounts[amounts.length - 1]);
TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);
}
// 输入精确的A换取不定量的eth swapExactTokensForTokens
// 只是输出是eth
function swapExactTokensForETH(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external override ensure(deadline) returns (uint[] memory amounts)
{
// path最后一个输出地址是weth
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]);
}
/**
输入不定量的ETH换取精确的token输出,对应swapTokensForExactTokens,
只是输入的是eth
*/
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');
// 将ETH兑换成WETH
IWETH(WETH).deposit{value: amounts[0]}();
assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]));
_swap(amounts, path, to);
// 对出的ETH退还
if (msg.value > amounts[0]) TransferHelper.safeTransferETH(msg.sender, msg.value - amounts[0]); // refund dust eth, if any
}
// 给定一定数量的资产和配对准备金,返回等量的其他资产
// BB/AA = B/A --> BB = AA*(B/A)
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);
}
}
Router v2
pragma solidity =0.6.6;
import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol';
import '@uniswap/lib/contracts/libraries/TransferHelper.sol';
import './interfaces/IUniswapV2Router02.sol';
import './libraries/UniswapV2Library.sol';
import './libraries/SafeMath.sol';
import './interfaces/IERC20.sol';
import './interfaces/IWETH.sol';
contract UniswapV2Router02 is IUniswapV2Router02 {
using SafeMath for uint;
address public immutable override factory;
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
}
// **** ADD LIQUIDITY ****
function _addLiquidity(
address tokenA,
address tokenB,
uint amountADesired,
uint amountBDesired,
uint amountAMin,
uint amountBMin
) internal virtual returns (uint amountA, uint amountB) {
// create the pair if it doesn't exist yet
if (IUniswapV2Factory(factory).getPair(tokenA, tokenB) == address(0)) {
IUniswapV2Factory(factory).createPair(tokenA, tokenB);
}
(uint reserveA, uint reserveB) = UniswapV2Library.getReserves(factory, tokenA, tokenB);
if (reserveA == 0 && reserveB == 0) {
(amountA, amountB) = (amountADesired, amountBDesired);
} else {
uint amountBOptimal = UniswapV2Library.quote(amountADesired, reserveA, reserveB);
if (amountBOptimal <= amountBDesired) {
require(amountBOptimal >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT');
(amountA, amountB) = (amountADesired, amountBOptimal);
} else {
uint amountAOptimal = UniswapV2Library.quote(amountBDesired, reserveB, reserveA);
assert(amountAOptimal <= amountADesired);
require(amountAOptimal >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT');
(amountA, amountB) = (amountAOptimal, amountBDesired);
}
}
}
function addLiquidity(
address tokenA,
address tokenB,
uint amountADesired,
uint amountBDesired,
uint amountAMin,
uint amountBMin,
address to,
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 = UniswapV2Library.pairFor(factory, tokenA, tokenB);
TransferHelper.safeTransferFrom(tokenA, msg.sender, pair, amountA);
TransferHelper.safeTransferFrom(tokenB, msg.sender, pair, amountB);
liquidity = IUniswapV2Pair(pair).mint(to);
}
function addLiquidityETH(
address token,
uint amountTokenDesired,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external virtual override payable ensure(deadline) returns (uint amountToken, uint amountETH, uint liquidity) {
(amountToken, amountETH) = _addLiquidity(
token,
WETH,
amountTokenDesired,
msg.value,
amountTokenMin,
amountETHMin
);
address pair = UniswapV2Library.pairFor(factory, token, WETH);
TransferHelper.safeTransferFrom(token, msg.sender, pair, amountToken);
IWETH(WETH).deposit{value: amountETH}();
assert(IWETH(WETH).transfer(pair, amountETH));
liquidity = IUniswapV2Pair(pair).mint(to);
// refund dust eth, if any
if (msg.value > amountETH) TransferHelper.safeTransferETH(msg.sender, msg.value - amountETH);
}
// **** REMOVE LIQUIDITY ****
function removeLiquidity(
address tokenA,
address tokenB,
uint liquidity,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
) public virtual override ensure(deadline) returns (uint amountA, uint amountB) {
address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
IUniswapV2Pair(pair).transferFrom(msg.sender, pair, liquidity); // send liquidity to pair
(uint amount0, uint amount1) = IUniswapV2Pair(pair).burn(to);
(address token0,) = UniswapV2Library.sortTokens(tokenA, tokenB);
(amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0);
require(amountA >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT');
require(amountB >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT');
}
function removeLiquidityETH(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) public virtual override ensure(deadline) returns (uint amountToken, uint amountETH) {
(amountToken, amountETH) = removeLiquidity(
token,
WETH,
liquidity,
amountTokenMin,
amountETHMin,
address(this),
deadline
);
TransferHelper.safeTransfer(token, to, amountToken);
IWETH(WETH).withdraw(amountETH);
TransferHelper.safeTransferETH(to, amountETH);
}
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 virtual override returns (uint amountA, uint amountB) {
address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
uint value = approveMax ? uint(-1) : liquidity;
IUniswapV2Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);
(amountA, amountB) = removeLiquidity(tokenA, tokenB, liquidity, amountAMin, amountBMin, to, deadline);
}
function removeLiquidityETHWithPermit(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline,
bool approveMax, uint8 v, bytes32 r, bytes32 s
) external virtual 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);
}
// **** REMOVE LIQUIDITY (supporting fee-on-transfer tokens) ****
function removeLiquidityETHSupportingFeeOnTransferTokens(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) public virtual override ensure(deadline) returns (uint amountETH) {
(, amountETH) = removeLiquidity(
token,
WETH,
liquidity,
amountTokenMin,
amountETHMin,
address(this),
deadline
);
TransferHelper.safeTransfer(token, to, IERC20(token).balanceOf(address(this)));
IWETH(WETH).withdraw(amountETH);
TransferHelper.safeTransferETH(to, amountETH);
}
function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline,
bool approveMax, uint8 v, bytes32 r, bytes32 s
) external virtual override returns (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);
amountETH = removeLiquidityETHSupportingFeeOnTransferTokens(
token, liquidity, amountTokenMin, amountETHMin, to, deadline
);
}
// **** SWAP ****
// requires the initial amount to have already been sent to the first pair
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,) = UniswapV2Library.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 ? UniswapV2Library.pairFor(factory, output, path[i + 2]) : _to;
IUniswapV2Pair(UniswapV2Library.pairFor(factory, input, output)).swap(
amount0Out, amount1Out, to, new bytes(0)
);
}
}
function swapExactTokensForTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external virtual override ensure(deadline) returns (uint[] memory amounts) {
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, to);
}
function swapTokensForExactTokens(
uint amountOut,
uint amountInMax,
address[] calldata path,
address to,
uint deadline
) external virtual 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
virtual
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
virtual
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
virtual
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
virtual
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);
// refund dust eth, if any
if (msg.value > amounts[0]) TransferHelper.safeTransferETH(msg.sender, msg.value - amounts[0]);
}
// **** SWAP (supporting fee-on-transfer tokens) ****
// requires the initial amount to have already been sent to the first pair
// 交易方法,支持转账扣手续费的代币
// 需要先将amounts[0]的金额已经转到第一个pair地址(即path[0]+path[1]组成的pair)
function _swapSupportingFeeOnTransferTokens(address[] memory path, address _to) internal virtual {
for (uint i; i < path.length - 1; i++) {
// 获取进/出token地址
(address input, address output) = (path[i], path[i + 1]);
(address token0,) = UniswapV2Library.sortTokens(input, output);
IUniswapV2Pair pair = IUniswapV2Pair(UniswapV2Library.pairFor(factory, input, output));
uint amountInput;
uint amountOutput;
{ // scope to avoid stack too deep errors
// 避免堆栈太深错误,用{}括部分临时变量
// 获取两个币的储备量
(uint reserve0, uint reserve1,) = pair.getReserves();
// 根据input,token0 得出 inToken的储备量,outToken的储备量
(uint reserveInput, uint reserveOutput) = input == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
/**
查询交易对的inToken余额,减掉最后记录的储备量,
就是交易对实际获取到的inToken数量
(和_swap的区别就在这里,不是使用计算来的amounts[0]作为输入,
而是通过查询pair余额再减去最后更新的储备量得到实际pair到账额!)
*/
amountInput = IERC20(input).balanceOf(address(pair)).sub(reserveInput);
// 通过实际得到的input量,计算实际会输出的output数量
amountOutput = UniswapV2Library.getAmountOut(amountInput, reserveInput, reserveOutput);
}
(uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOutput) : (amountOutput, uint(0));
address to = i < path.length - 2 ? UniswapV2Library.pairFor(factory, output, path[i + 2]) : _to;
/*
调用pair的 swap方法,其中一个out是0,另一个是要转出的金额,
内部是转出输出量,并校验交易是否正确,更新储备量
*/
pair.swap(amount0Out, amount1Out, to, new bytes(0));
}
}
// 输入精确的token,换取另一个token,支持转账时扣手续费的token
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external virtual override ensure(deadline) {
TransferHelper.safeTransferFrom(
path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn
);
uint balanceBefore = IERC20(path[path.length - 1]).balanceOf(to);
// 调用内部交易方法
_swapSupportingFeeOnTransferTokens(path, to);
// 通过查询余额的方式,校验交易前后的余额差,大于等于最小输出!
require(
IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore) >= amountOutMin,
'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT'
);
}
// 输入精确eth换取另一个token, 支持转账时扣手续费的token
function swapExactETHForTokensSupportingFeeOnTransferTokens(
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external virtual override payable ensure(deadline)
{
require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH');
uint amountIn = msg.value;
// 将eth转成weth
IWETH(WETH).deposit{value: amountIn}();
// 并转给第一个pair地址
assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn));
uint balanceBefore = IERC20(path[path.length - 1]).balanceOf(to);
// 调用内部交易方法
_swapSupportingFeeOnTransferTokens(path, to);
// 通过查询余额的方式,校验交易前后的余额差,大于等于最小输出!
require(
IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore) >= amountOutMin,
'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT'
);
}
//输入精确token换取输出eth,支持转账时扣手续费的token
function swapExactTokensForETHSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external virtual override ensure(deadline)
{
// 确保最后是输出WETH
require(path[path.length - 1] == WETH, 'UniswapV2Router: INVALID_PATH');
// 给第一个pair转 amountIn 的 path[0]
TransferHelper.safeTransferFrom(
path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn
);
// 调用内部交易方法
_swapSupportingFeeOnTransferTokens(path, address(this));
uint amountOut = IERC20(WETH).balanceOf(address(this));
// 校验amountOut >= amountOutMin
require(amountOut >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
// 将weth转换成eth
IWETH(WETH).withdraw(amountOut);
// 将eth转给to
TransferHelper.safeTransferETH(to, amountOut);
}
// **** LIBRARY FUNCTIONS ****
function quote(uint amountA, uint reserveA, uint reserveB) public pure virtual override returns (uint amountB) {
return UniswapV2Library.quote(amountA, reserveA, reserveB);
}
function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut)
public
pure
virtual
override
returns (uint amountOut)
{
return UniswapV2Library.getAmountOut(amountIn, reserveIn, reserveOut);
}
function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut)
public
pure
virtual
override
returns (uint amountIn)
{
return UniswapV2Library.getAmountIn(amountOut, reserveIn, reserveOut);
}
function getAmountsOut(uint amountIn, address[] memory path)
public
view
virtual
override
returns (uint[] memory amounts)
{
return UniswapV2Library.getAmountsOut(factory, amountIn, path);
}
function getAmountsIn(uint amountOut, address[] memory path)
public
view
virtual
override
returns (uint[] memory amounts)
{
return UniswapV2Library.getAmountsIn(factory, amountOut, path);
}
}
Library
pragma solidity >=0.5.0;
import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol';
import "./SafeMath.sol";
library UniswapV2Library {
using SafeMath for uint;
// 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, 'UniswapV2Library: IDENTICAL_ADDRESSES');
(token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
require(token0 != address(0), 'UniswapV2Library: ZERO_ADDRESS');
}
// calculates the CREATE2 address for a pair without making any external calls
// 在不进行任何外部调用的情况下计算一对的 CREATE2 地址
function pairFor(address factory, address tokenA, address tokenB) internal pure returns (address pair) {
(address token0, address token1) = sortTokens(tokenA, tokenB);
pair = address(uint(keccak256(abi.encodePacked(
hex'ff',
factory,
keccak256(abi.encodePacked(token0, token1)),
hex'96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f' // init code 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);
(uint reserve0, uint reserve1,) = IUniswapV2Pair(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
// 给定一定数量的资产和配对准备金,返回等量的其他资产
// BB/AA = B/A --> BB = AA*(B/A)
function quote(uint amountA, uint reserveA, uint reserveB) internal pure returns (uint amountB) {
require(amountA > 0, 'UniswapV2Library: INSUFFICIENT_AMOUNT');
require(reserveA > 0 && reserveB > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
amountB = amountA.mul(reserveB) / reserveA;
}
// 给定资产的输入量和对储备,返回另一资产的最大输出量
// 考虑手续费
// (x+0.997a)(y-b) = xy
// b = (0.997ay)/(x+0.997a)
// b = (997ay)/(1000x + 997a)
function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) internal pure returns (uint amountOut) {
require(amountIn > 0, 'UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT');
require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
uint amountInWithFee = amountIn.mul(997);
uint numerator = amountInWithFee.mul(reserveOut);
uint denominator = reserveIn.mul(1000).add(amountInWithFee);
amountOut = numerator / denominator;
}
// 给定资产的输出量和对准备金,返回其他资产所需的输入量
// (x+a)(y-b) = xy
// a = bx/(y-b)
// 考虑手续费:
// 实际 a` = a * 1000/997
function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) internal pure returns (uint amountIn) {
require(amountOut > 0, 'UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT');
require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
uint numerator = reserveIn.mul(amountOut).mul(1000);
uint denominator = reserveOut.sub(amountOut).mul(997);
// 加一是防止数据溢出
amountIn = (numerator / denominator).add(1);
}
// performs chained getAmountOut calculations on any number of pairs
// 对任意数量的对执行链式 getAmountOut 计算
// 输入a,得到n
// 路径:A -> B -> C -> ... N
// 数量:a -> b -> c -> ... n
// 假如 N = A, n=a`
// a` > a 则存在套利空间,如何实现利润最大化还需考虑
function getAmountsOut(address factory, uint amountIn, address[] memory path) internal view returns (uint[] memory amounts) {
require(path.length >= 2, 'UniswapV2Library: 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
// 对任意数量的对执行链式 getAmountIn 计算
// 为了得到n,输入a
// 路径:N ... C -> B -> A
// 数量:n ... c -> b -> a
// 假如 N = A, n=a`
// a` > a 则存在套利空间,如何实现利润最大化还需考虑
function getAmountsIn(address factory, uint amountOut, address[] memory path) internal view returns (uint[] memory amounts) {
require(path.length >= 2, 'UniswapV2Library: 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);
}
}
}