uniswapV2(4)-周边源码解析

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);
    }
}

转载请注明来源

×

喜欢就点赞,疼爱就打赏