UniswapV2Router02.sol

pragma solidity =0.6.6;

//导入工厂接口
import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol';
//导入TransferHelper,里面提供一些safe转账方法,以后细看
import '@uniswap/lib/contracts/libraries/TransferHelper.sol';

//导入Router02接口
import './interfaces/IUniswapV2Router02.sol';
//导入Library
import './libraries/UniswapV2Library.sol';
//SafeMath
import './libraries/SafeMath.sol';
//IERC20
import './interfaces/IERC20.sol';
//IWETH,包含deposit、withdraw、transfer
import './interfaces/IWETH.sol';

//继承接口
contract UniswapV2Router02 is IUniswapV2Router02 {
    using SafeMath for uint;

    //immutable表示该值为不可变量
    //override表示覆盖了父函数/变量
    address public immutable override factory;
    address public immutable override WETH;

    //提交一个deadline,函数会验证当前时间是否超过deadline,若未超过,函数继续调用
    modifier ensure(uint deadline) {
        require(deadline >= block.timestamp, 'UniswapV2Router: EXPIRED');
        _;
    }

    //部署合约时,提交一个工厂地址,一个WETH地址,被合约写入状态变量,且不可更改
    constructor(address _factory, address _WETH) public {
        factory = _factory;
        //一个兑换WETH的池子
        WETH = _WETH;
    }

    //当调用者只传ETH,并未调用函数,并未提供信息,receive被触发
    receive() external payable {
        //若向本合约打钱的对象地址为WETH,验证通过
        //assert用于验证内部错误
        //只通过回调接收来自WETH合约的ETH
        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);
        }
        //获取交易对中两种代币的储备量,这种getReserves()里面填参数的形式是从哪里规定的?
        //在Pair中的定义是不填参的
        (uint reserveA, uint reserveB) = UniswapV2Library.getReserves(factory, tokenA, tokenB);
        //如果交易对的代币储备量为0
        if (reserveA == 0 && reserveB == 0) {
            //建议的注入量即为函数调用者自己想注入的量
            (amountA, amountB) = (amountADesired, amountBDesired);
        } 
        //若交易对的代币储备量不为0
        else {
            //通过Library.quote()计算若按照用户的期望量注入A,B的最优注入量应该为多少
            uint amountBOptimal = UniswapV2Library.quote(amountADesired, reserveA, reserveB);
            //如果B的最优注入量<=B的期望注入量,说明用户的B数量足够
            if (amountBOptimal <= amountBDesired) {
                //保证B的最优注入量>=最小注入量
                require(amountBOptimal >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT');
                //将建议的A、B注入量进行设置
                (amountA, amountB) = (amountADesired, amountBOptimal);
            } 
            //若用户的B数量不足
            else {
                //计算A的最优注入量
                uint amountAOptimal = UniswapV2Library.quote(amountBDesired, reserveB, reserveA);
                //保证用户的A币数量足够
                assert(amountAOptimal <= amountADesired);
                //保证A最优注入量>=最小注入量
                require(amountAOptimal >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT');
                //返回建议的A、B注入量
                (amountA, amountB) = (amountAOptimal, amountBDesired);
            }
        }
    }
    //先调用_addLiquidity()计算应该最优注入A、B量,然后真的进行交易,同时转给用户相应的流动性token
    //to参数表示接收流动性代币的地址,deadline表示交易的最迟时间
    //返回的参数增加了一个liquidity,表示注入流动性时,同步返给用户记录流动性的代币的数量
    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) {
        //先调用_addLiquidity()
        (amountA, amountB) = _addLiquidity(tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin);
        //获取交易对的地址
        address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
        //将token从用户手里传入交易对
        TransferHelper.safeTransferFrom(tokenA, msg.sender, pair, amountA);
        TransferHelper.safeTransferFrom(tokenB, msg.sender, pair, amountB);
        //流动性代币数量通过pair.mint()函数计算,执行该函数时,流动性代币已自动发送给to地址
        liquidity = IUniswapV2Pair(pair).mint(to);
    }
    //与addLiquidity()类似,但其中一个代币为ETH,因此只需往该账户打钱,其数量msg.value即为ETHDesired
    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) {
        //先调用_addLiquidity(),计算最优交易量
        (amountToken, amountETH) = _addLiquidity(
            token,
            WETH,
            amountTokenDesired,
            msg.value,
            amountTokenMin,
            amountETHMin
        );
        //获取交易对地址
        address pair = UniswapV2Library.pairFor(factory, token, WETH);
        //把ETH外的另一种token从用户手里转入交易对
        TransferHelper.safeTransferFrom(token, msg.sender, pair, amountToken);
        //把ETH注入WETH兑换池
        IWETH(WETH).deposit{value: amountETH}();
        //验证兑换出的WETH有没有转入交易对
        assert(IWETH(WETH).transfer(pair, amountETH));
        //计算流动性代币数量,并转入用户地址
        liquidity = IUniswapV2Pair(pair).mint(to);
        //退款剩余的eth
        // 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
        //把流动性代币烧掉,把两种代币退还给to,记录下退还的两种代币数量
        (uint amount0, uint amount1) = IUniswapV2Pair(pair).burn(to);
        //对两种代币进行排序,地址小的存入token0
        (address token0,) = UniswapV2Library.sortTokens(tokenA, tokenB);
        //把token和amount对应
        (amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0);
        //保证退还量>=最小交易单位,对于最小交易量的检测应该在退还代币时进行
        require(amountA >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT');
        require(amountB >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT');
    }
    //移除流动性(收回的两种代币中,一种为ETH),返回值为兑换出的token量、ETH量
    function removeLiquidityETH(
        //除ETH外的另一种代币地址
        address token,
        //用户想要兑换的流动性代币数量
        uint liquidity,
        //最小token交易量
        uint amountTokenMin,
        //最小ETH交易量
        uint amountETHMin,
        //用于接收兑换出的代币的地址
        address to,
        //交易最晚时间
        uint deadline
    ) public virtual override ensure(deadline) returns (uint amountToken, uint amountETH) {
        //先调用removeLiquidity(),用流动性代币兑换出token和WETH
        (amountToken, amountETH) = removeLiquidity(
            token,
            WETH,
            liquidity,
            amountTokenMin,
            amountETHMin,
            address(this),
            deadline
        );
        //把兑换出token转账给to
        TransferHelper.safeTransfer(token, to, amountToken);
        //把WETH注入WETH池,撤回ETH
        IWETH(WETH).withdraw(amountETH);
        //把ETH转给to
        TransferHelper.safeTransferETH(to, amountETH);
    }
    //在removeLiquidity()函数的基础上增加了某种审核
    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);
        //若允许最大交易量交易,则value为uint256最大值,否则为用户提交的希望交易的流动性代币数量
        uint value = approveMax ? uint(-1) : liquidity;
        //permit需进一步学习,总之进行了某种授权,使得接下来的操作可行
        IUniswapV2Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);
        //调用removeLiquidity()
        (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);
    }

    //移除流动性时,支持用转账的token支付手续费,没看出此功能是在哪行代码实现的
    // **** 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 {
        //正序循环读入path
        for (uint i; i < path.length - 1; i++) {
            //读入交易对的两种代币地址
            (address input, address output) = (path[i], path[i + 1]);
            //对两种代币进行排序,地址较小的为token0
            (address token0,) = UniswapV2Library.sortTokens(input, output);
            //要兑换出的代币量,即为amounts[]中第二个坑位记录的量
            uint amountOut = amounts[i + 1];
            //进行参数的对齐,细节先不看了
            (uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOut) : (amountOut, uint(0));
            //若遍历已到path的终点,接收代币的地址为to;若未到终点,则接收地址为相应pair池
            address to = i < path.length - 2 ? UniswapV2Library.pairFor(factory, output, path[i + 2]) : _to;
            //获取交易对地址,并通过Pair.swap()执行兑换
            IUniswapV2Pair(UniswapV2Library.pairFor(factory, input, output)).swap(
                amount0Out, amount1Out, to, new bytes(0)
            );
        }
    }
    //以一种确定的token换取另一种token,返回交易链上没种token的成交量数列
    function swapExactTokensForTokens(
        //注入token量
        uint amountIn,
        //取出token最小量
        uint amountOutMin,
        //交易链
        address[] calldata path,
        //收币地址
        address to,
        //交易最晚时间点
        uint deadline
    ) external virtual override ensure(deadline) returns (uint[] memory amounts) {
        //通过Library.getAmountsOut()得到amounts[]
        amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path);
        //保证最后兑换到的token量>=最小交易量
        require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
        //把path[0]代币从用户手里转移到相应的交易对中
        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
    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,) = 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();
            (uint reserveInput, uint reserveOutput) = input == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
            amountInput = IERC20(input).balanceOf(address(pair)).sub(reserveInput);
            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(amount0Out, amount1Out, to, new bytes(0));
        }
    }
    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'
        );
    }
    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;
        IWETH(WETH).deposit{value: amountIn}();
        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'
        );
    }
    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    )
        external
        virtual
        override
        ensure(deadline)
    {
        require(path[path.length - 1] == WETH, 'UniswapV2Router: INVALID_PATH');
        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));
        require(amountOut >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
        IWETH(WETH).withdraw(amountOut);
        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);
    }

    //提交注入token的数量、交易链,返回交易链上的成交量数列
    function getAmountsOut(uint amountIn, address[] memory path)
        public
        view
        virtual
        override
        returns (uint[] memory amounts)
    {   
        //调用了Library.getAmonuntsOut()
        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);
    }
}

 

你可能感兴趣的:(以太坊)