账户
0x22...9f01
0x22...9F01

0x22...9F01

US$0.00
此合同的源代码已经过验证!
合同元数据
编译器
0.6.2+commit.bacdbe57
语言
Solidity
合同源代码
文件 1 的 1:EmiRouter.Full.sol
// File: @openzeppelin/contracts/token/ERC20/IERC20.sol

// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

// File: @openzeppelin/contracts/math/SafeMath.sol

// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        uint256 c = a + b;
        if (c < a) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the substraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b > a) return (false, 0);
        return (true, a - b);
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) return (true, 0);
        uint256 c = a * b;
        if (c / a != b) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a / b);
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a % b);
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");
        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a, "SafeMath: subtraction overflow");
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) return 0;
        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");
        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: division by zero");
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: modulo by zero");
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        return a - b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryDiv}.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a % b;
    }
}

// File: contracts/interfaces/IEmiswap.sol

// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.6.0;


interface IEmiswapRegistry {
    function pools(IERC20 token1, IERC20 token2)
        external
        view
        returns (IEmiswap);

    function isPool(address addr) external view returns (bool);

    function deploy(IERC20 tokenA, IERC20 tokenB) external returns (IEmiswap);
    function getAllPools() external view returns (IEmiswap[] memory);
}

interface IEmiswap {
    function fee() external view returns (uint256);

    function tokens(uint256 i) external view returns (IERC20);

    function deposit(
        uint256[] calldata amounts,
        uint256[] calldata minAmounts,
        address referral
    ) external payable returns (uint256 fairSupply);

    function withdraw(uint256 amount, uint256[] calldata minReturns) external;

    function getBalanceForAddition(IERC20 token)
        external
        view
        returns (uint256);

    function getBalanceForRemoval(IERC20 token) external view returns (uint256);

    function getReturn(
        IERC20 fromToken,
        IERC20 destToken,
        uint256 amount
    ) external view returns (uint256, uint256);

    function swap(
        IERC20 fromToken,
        IERC20 destToken,
        uint256 amount,
        uint256 minReturn,
        address to,
        address referral
    ) external payable returns (uint256 returnAmount);

    function initialize(IERC20[] calldata assets) external;
}

// File: contracts/libraries/EmiswapLib.sol

// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.6.0;




library EmiswapLib {
    using SafeMath for uint256;
    uint256 public constant FEE_DENOMINATOR = 1e18;

    function previewSwapExactTokenForToken(
        address factory,
        address tokenFrom,
        address tokenTo,
        uint256 ammountFrom
    ) internal view returns (uint256 ammountTo) {
        IEmiswap pairContract =
            IEmiswapRegistry(factory).pools(IERC20(tokenFrom), IERC20(tokenTo));

        if (pairContract != IEmiswap(0)) {
            (,ammountTo) = pairContract.getReturn(
                IERC20(tokenFrom),
                IERC20(tokenTo),
                ammountFrom
            );
        }
    }

    /**************************************************************************************
     * get preview result of virtual swap by route of tokens
     **************************************************************************************/
    function previewSwapbyRoute(
        address factory,
        address[] memory path,
        uint256 ammountFrom
    ) internal view returns (uint256 ammountTo) {
        for (uint256 i = 0; i < path.length - 1; i++) {
            if (path.length >= 2) {
                ammountTo = previewSwapExactTokenForToken(
                    factory,
                    path[i],
                    path[i + 1],
                    ammountFrom
                );

                if (i == (path.length - 2)) {
                    return (ammountTo);
                } else {
                    ammountFrom = ammountTo;
                }
            }
        }
    }

    function fee(address factory) internal view returns (uint256) {
        return IEmiswap(factory).fee();
    }

    // given an output amount of an asset and pair reserves, returns a required input amount of the other asset
    function getAmountIn(
        address factory,
        uint256 amountOut,
        uint256 reserveIn,
        uint256 reserveOut
    ) internal view returns (uint256 amountIn) {
        require(amountOut > 0, "EmiswapLibrary: INSUFFICIENT_OUTPUT_AMOUNT");
        require(
            reserveIn > 0 && reserveOut > 0,
            "EmiswapLibrary: INSUFFICIENT_LIQUIDITY"
        );
        uint256 numerator = reserveIn.mul(amountOut).mul(1000);
        uint256 denominator =
            reserveOut.sub(amountOut).mul(
                uint256(1000000000000000000).sub(fee(factory)).div(1e15)
            ); // 997
        amountIn = (numerator / denominator).add(1);
    }

    // given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset
    function getAmountOut(
        address factory,
        uint256 amountIn,
        uint256 reserveIn,
        uint256 reserveOut
    ) internal view returns (uint256 amountOut) {
        if (amountIn == 0 || reserveIn == 0 || reserveOut == 0) {
            return (0);
        }

        uint256 amountInWithFee =
            amountIn.mul(
                uint256(1000000000000000000).sub(fee(factory)).div(1e15)
            ); //997
        uint256 numerator = amountInWithFee.mul(reserveOut);
        uint256 denominator = reserveIn.mul(1000).add(amountInWithFee);
        amountOut = (denominator == 0 ? 0 : amountOut =
            numerator /
            denominator);
    }

    // performs chained getAmountIn calculations on any number of pairs
    function getAmountsIn(
        address factory,
        uint256 amountOut,
        address[] memory path
    ) internal view returns (uint256[] memory amounts) {
        require(path.length >= 2, "EmiswapLibrary: INVALID_PATH");
        amounts = new uint256[](path.length);
        amounts[amounts.length - 1] = amountOut;
        for (uint256 i = path.length - 1; i > 0; i--) {
            IEmiswap pairContract =
                IEmiswapRegistry(factory).pools(
                    IERC20(IERC20(path[i])),
                    IERC20(path[i - 1])
                );

            uint256 reserveIn;
            uint256 reserveOut;

            if (address(pairContract) != address(0)) {
                reserveIn = IEmiswap(pairContract).getBalanceForAddition(
                    IERC20(path[i - 1])
                );
                reserveOut = IEmiswap(pairContract).getBalanceForRemoval(
                    IERC20(path[i])
                );
            }

            amounts[i - 1] = getAmountIn(
                factory,
                amounts[i],
                reserveIn,
                reserveOut
            );
        }
    }

    // performs chained getAmountOut calculations on any number of pairs
    function getAmountsOut(
        address factory,
        uint256 amountIn,
        address[] memory path
    ) internal view returns (uint256[] memory amounts) {
        require(path.length >= 2, "EmiswapLibrary: INVALID_PATH");
        amounts = new uint256[](path.length);
        amounts[0] = amountIn;
        for (uint256 i = 0; i < path.length - 1; i++) {
            IEmiswap pairContract =
                IEmiswapRegistry(factory).pools(
                    IERC20(IERC20(path[i])),
                    IERC20(path[i + 1])
                );

            uint256 reserveIn;
            uint256 reserveOut;

            if (address(pairContract) != address(0)) {
                reserveIn = IEmiswap(pairContract).getBalanceForAddition(
                    IERC20(path[i])
                );
                reserveOut = IEmiswap(pairContract).getBalanceForRemoval(
                    IERC20(path[i + 1])
                );
            }

            amounts[i + 1] = getAmountOut(
                factory,
                amounts[i],
                reserveIn,
                reserveOut
            );
        }
    }

    // given some amount of an asset and pair reserves, returns an equivalent amount of the other asset
    function quote(
        uint256 amountA,
        uint256 reserveA,
        uint256 reserveB
    ) internal pure returns (uint256 amountB) {
        require(amountA > 0, "EmiswapLibrary: INSUFFICIENT_AMOUNT");
        require(
            reserveA > 0 && reserveB > 0,
            "EmiswapLibrary: INSUFFICIENT_LIQUIDITY"
        );
        amountB = amountA.mul(reserveB) / reserveA;
    }
}

// File: contracts/libraries/TransferHelper.sol

// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity >=0.6.0;

// helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
library TransferHelper {
    function safeApprove(
        address token,
        address to,
        uint256 value
    ) internal {
        // bytes4(keccak256(bytes('approve(address,uint256)')));
        (bool success, bytes memory data) =
            token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
        require(
            success && (data.length == 0 || abi.decode(data, (bool))),
            "TransferHelper: APPROVE_FAILED"
        );
    }

    function safeTransfer(
        address token,
        address to,
        uint256 value
    ) internal {
        // bytes4(keccak256(bytes('transfer(address,uint256)')));
        (bool success, bytes memory data) =
            token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
        require(
            success && (data.length == 0 || abi.decode(data, (bool))),
            "TransferHelper: TRANSFER_FAILED"
        );
    }

    function safeTransferFrom(
        address token,
        address from,
        address to,
        uint256 value
    ) internal {
        // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
        (bool success, bytes memory data) =
            token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
        require(
            success && (data.length == 0 || abi.decode(data, (bool))),
            "TransferHelper: TRANSFER_FROM_FAILED"
        );
    }

    function safeTransferETH(address to, uint256 value) internal {
        (bool success, ) = to.call{value: value}(new bytes(0));
        require(success, "TransferHelper: ETH_TRANSFER_FAILED");
    }
}

// File: contracts/interfaces/IWETH.sol

// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.6.0;

interface IWETH {
    function deposit() external payable;

    function transfer(address to, uint256 value) external returns (bool);

    function withdraw(uint256) external;
}

// File: contracts/EmiRouter.sol

// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.6.2;
pragma experimental ABIEncoderV2;







contract EmiRouter {
    using SafeMath for uint256;

    address public factory;
    address public WETH;

    struct PoolData {
        IEmiswap pool;
        uint256 balanceA;
        uint256 balanceB;
    }

    event Log(uint256 a, uint256 b);

    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
    }

    // **** Pool Info ****

    function tokenToIERC(IERC20 _token) public view returns (IERC20) {
        return (address(_token) == address(0) ? IERC20(WETH) : _token);
    }

    function getPoolDataList(
        IERC20[] memory tokenAList,
        IERC20[] memory tokenBList
    ) public view returns (PoolData[] memory dataList) {
        if (tokenAList.length > 0 && tokenAList.length == tokenBList.length) {
            dataList = new PoolData[](tokenAList.length);
            for (uint256 i = 0; i < tokenAList.length; i++) {
                if (
                    address(
                        IEmiswapRegistry(address(factory)).pools(
                            tokenToIERC(tokenAList[i]),
                            tokenToIERC(tokenBList[i])
                        )
                    ) != address(0)
                ) {
                    dataList[i].pool = IEmiswapRegistry(address(factory)).pools(
                        tokenToIERC(tokenAList[i]),
                        tokenToIERC(tokenBList[i])
                    );
                    dataList[i].balanceA = IEmiswap(address(dataList[i].pool))
                        .getBalanceForAddition(tokenToIERC(tokenAList[i]));
                    dataList[i].balanceB = IEmiswap(address(dataList[i].pool))
                        .getBalanceForAddition(tokenToIERC(tokenBList[i]));
                }
            }
        } else {
            dataList = new PoolData[](1);
        }
    }

    function getReservesByPool(address pool)
        public
        view
        returns (uint256 _reserve0, uint256 _reserve1)
    {
        _reserve0 = IEmiswap(pool).getBalanceForAddition(
            IEmiswap(pool).tokens(0)
        );
        _reserve1 = IEmiswap(pool).getBalanceForAddition(
            IEmiswap(pool).tokens(1)
        );
    }

    function getReserves(IERC20 token0, IERC20 token1)
        public
        view
        returns (
            uint256 _reserve0,
            uint256 _reserve1,
            address poolAddresss
        )
    {
        if (
            address(
                IEmiswapRegistry(address(factory)).pools(
                    tokenToIERC(token0),
                    tokenToIERC(token1)
                )
            ) != address(0)
        ) {
            _reserve0 = IEmiswapRegistry(address(factory))
                .pools(tokenToIERC(token0), tokenToIERC(token1))
                .getBalanceForAddition(tokenToIERC(token0));
            _reserve1 = IEmiswapRegistry(address(factory))
                .pools(tokenToIERC(token0), tokenToIERC(token1))
                .getBalanceForAddition(tokenToIERC(token1));
            poolAddresss = address(
                IEmiswapRegistry(address(factory)).pools(
                    tokenToIERC(token0),
                    tokenToIERC(token1)
                )
            );
        }
    }

    function getExpectedReturn(
        IERC20 fromToken,
        IERC20 destToken,
        uint256 amount
    )
        public
        view
        returns (uint256 returnAmount, uint256[] memory distribution)
    {
        address[] memory path;
        path = new address[](2);
        path[0] = address(tokenToIERC(fromToken));
        path[1] = address(tokenToIERC(destToken));

        returnAmount = getAmountsOut(amount, path)[1];
        uint256[] memory _distribution;
        _distribution = new uint256[](34);
        _distribution[12] = 1;
        distribution = _distribution;
    }

    function resetAllowance(address token, address pairContract) public {
        if (IERC20(token).allowance(address(this), pairContract) > 0) {
            TransferHelper.safeApprove(token, pairContract, 0);
        }
    }


    // **** Liquidity ****
    /**
     * @param tokenA address of first token in pair
     * @param tokenB address of second token in pair
     * @return LP balance
     */
    function getLiquidity(address tokenA, address tokenB)
        external
        view
        returns (uint256)
    {
        return (
            IERC20(
                address(
                    IEmiswapRegistry(factory).pools(
                        IERC20(tokenA),
                        IERC20(tokenB)
                    )
                )
            )
                .balanceOf(msg.sender)
        );
    }

    // **** ADD LIQUIDITY ****
    function _addLiquidity(
        address tokenA,
        address tokenB,
        uint256 amountADesired,
        uint256 amountBDesired,
        uint256 amountAMin,
        uint256 amountBMin
    ) internal returns (uint256 amountA, uint256 amountB) {
        IERC20 ERC20tokenA = IERC20(tokenA);
        IERC20 ERC20tokenB = IERC20(tokenB);
        IEmiswap pairContract =
            IEmiswapRegistry(factory).pools(ERC20tokenA, ERC20tokenB);
        // create the pair if it doesn't exist yet
        if (pairContract == IEmiswap(0)) {
            pairContract = IEmiswapRegistry(factory).deploy(
                ERC20tokenA,
                ERC20tokenB
            );
        }

        uint256 reserveA = pairContract.getBalanceForAddition(ERC20tokenA);
        uint256 reserveB = pairContract.getBalanceForRemoval(ERC20tokenB);

        if (reserveA == 0 && reserveB == 0) {
            (amountA, amountB) = (amountADesired, amountBDesired);
        } else {
            uint256 amountBOptimal =
                EmiswapLib.quote(amountADesired, reserveA, reserveB);
            if (amountBOptimal <= amountBDesired) {
                require(
                    amountBOptimal >= amountBMin,
                    "EmiRouter:INSUFFICIENT_B_AMOUNT"
                );
                (amountA, amountB) = (amountADesired, amountBOptimal);
            } else {
                uint256 amountAOptimal =
                    EmiswapLib.quote(amountBDesired, reserveB, reserveA);
                assert(amountAOptimal <= amountADesired);
                require(
                    amountAOptimal >= amountAMin,
                    "EmiRouter:INSUFFICIENT_A_AMOUNT"
                );
                (amountA, amountB) = (amountAOptimal, amountBDesired);
            }
        }
    }

    /**
     * @param tokenA address of first token in pair
     * @param tokenB address of second token in pair
     * @param amountADesired desired amount of first token
     * @param amountBDesired desired amount of second token
     * @param amountAMin minimum amount of first token
     * @param amountBMin minimum amount of second token
     * @param ref referral address
     * @return amountA added liquidity of first token
     * @return amountB added liquidity of second token
     * @return liquidity
     */

    function addLiquidity(
        address tokenA,
        address tokenB,
        uint256 amountADesired,
        uint256 amountBDesired,
        uint256 amountAMin,
        uint256 amountBMin,
        address ref
    )
        external
        returns (
            uint256 amountA,
            uint256 amountB,
            uint256 liquidity
        )
    {
        (amountA, amountB) = _addLiquidity(
            tokenA,
            tokenB,
            amountADesired,
            amountBDesired,
            amountAMin,
            amountBMin
        );
        IEmiswap pairContract =
            IEmiswapRegistry(factory).pools(IERC20(tokenA), IERC20(tokenB));

        TransferHelper.safeTransferFrom(
            tokenA,
            msg.sender,
            address(this),
            amountA
        );
        TransferHelper.safeTransferFrom(
            tokenB,
            msg.sender,
            address(this),
            amountB
        );

        resetAllowance(tokenA, address(pairContract));
        resetAllowance(tokenB, address(pairContract));
        TransferHelper.safeApprove(tokenA, address(pairContract), amountA);
        TransferHelper.safeApprove(tokenB, address(pairContract), amountB);

        uint256[] memory amounts;
        amounts = new uint256[](2);
        uint256[] memory minAmounts;
        minAmounts = new uint256[](2);

        if (tokenA < tokenB) {
            amounts[0] = amountA;
            amounts[1] = amountB;
            minAmounts[0] = amountAMin;
            minAmounts[1] = amountBMin;
        } else {
            amounts[0] = amountB;
            amounts[1] = amountA;
            minAmounts[0] = amountBMin;
            minAmounts[1] = amountAMin;
        }

        //emit Log(amounts[0], amounts[1]);
        liquidity = IEmiswap(pairContract).deposit(amounts, minAmounts, ref);

        TransferHelper.safeTransfer(
            address(pairContract),
            msg.sender,
            liquidity
        );
    }

    /**
     * @param token address of token
     * @param amountTokenDesired desired amount of token
     * @param amountTokenMin minimum amount of token
     * @param amountETHMin minimum amount of ETH
     * @param ref referral address
     * @return amountToken added liquidity of token
     * @return amountETH added liquidity of ETH
     * @return liquidity
     */
    function addLiquidityETH(
        address token,
        uint256 amountTokenDesired,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address ref
    )
        external
        payable
        returns (
            uint256 amountToken,
            uint256 amountETH,
            uint256 liquidity
        )
    {
        (amountToken, amountETH) = _addLiquidity(
            token,
            WETH,
            amountTokenDesired,
            msg.value,
            amountTokenMin,
            amountETHMin
        );
        IEmiswap pairContract =
            IEmiswapRegistry(factory).pools(IERC20(token), IERC20(WETH));
        TransferHelper.safeTransferFrom(
            token,
            msg.sender,
            address(this),
            amountToken
        );
        // set allowance to 0
        resetAllowance(token, address(pairContract));        
        TransferHelper.safeApprove(token, address(pairContract), amountToken);
        IWETH(WETH).deposit{value: amountETH}();
        resetAllowance(WETH, address(pairContract));
        TransferHelper.safeApprove(WETH, address(pairContract), amountETH);

        uint256[] memory amounts;
        amounts = new uint256[](2);
        uint256[] memory minAmounts;
        minAmounts = new uint256[](2);

        if (token < WETH) {
            amounts[0] = amountToken;
            amounts[1] = amountETH;
            minAmounts[0] = amountTokenMin;
            minAmounts[1] = amountETHMin;
        } else {
            amounts[0] = amountETH;
            amounts[1] = amountToken;
            minAmounts[0] = amountETHMin;
            minAmounts[1] = amountTokenMin;
        }
        liquidity = IEmiswap(pairContract).deposit(amounts, minAmounts, ref);
        TransferHelper.safeTransfer(
            address(pairContract),
            msg.sender,
            liquidity
        );
    }

    // **** REMOVE LIQUIDITY ****
    /**
     * @param tokenA address of first token in pair
     * @param tokenB address of second token in pair
     * @param liquidity LP token
     * @param amountAMin minimum amount of first token
     * @param amountBMin minimum amount of second token
     */
    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint256 liquidity,
        uint256 amountAMin,
        uint256 amountBMin
    ) public {
        IEmiswap pairContract =
            IEmiswapRegistry(factory).pools(IERC20(tokenA), IERC20(tokenB));
        TransferHelper.safeTransferFrom(
            address(pairContract),
            msg.sender,
            address(this),
            liquidity
        ); // send liquidity to this

        uint256[] memory minReturns;
        minReturns = new uint256[](2);

        if (tokenA < tokenB) {
            minReturns[0] = amountAMin;
            minReturns[1] = amountBMin;
        } else {
            minReturns[0] = amountBMin;
            minReturns[1] = amountAMin;
        }
        uint256 tokenAbalance = IERC20(tokenA).balanceOf(address(this));
        uint256 tokenBbalance = IERC20(tokenB).balanceOf(address(this));

        pairContract.withdraw(liquidity, minReturns);

        tokenAbalance = IERC20(tokenA).balanceOf(address(this)).sub(
            tokenAbalance
        );
        tokenBbalance = IERC20(tokenB).balanceOf(address(this)).sub(
            tokenBbalance
        );

        TransferHelper.safeTransfer(tokenA, msg.sender, tokenAbalance);
        TransferHelper.safeTransfer(tokenB, msg.sender, tokenBbalance);
    }

    /**
     * @param token address of token
     * @param liquidity LP token amount
     * @param amountTokenMin minimum amount of token
     * @param amountETHMin minimum amount of ETH
     */
    function removeLiquidityETH(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin
    ) public {
        IEmiswap pairContract =
            IEmiswapRegistry(factory).pools(IERC20(token), IERC20(WETH));
        TransferHelper.safeTransferFrom(
            address(pairContract),
            msg.sender,
            address(this),
            liquidity
        ); // send liquidity to this

        uint256[] memory minReturns;
        minReturns = new uint256[](2);

        if (token < WETH) {
            minReturns[0] = amountTokenMin;
            minReturns[1] = amountETHMin;
        } else {
            minReturns[0] = amountETHMin;
            minReturns[1] = amountTokenMin;
        }

        uint256 tokenbalance = IERC20(token).balanceOf(address(this));
        uint256 WETHbalance = IERC20(WETH).balanceOf(address(this));

        pairContract.withdraw(liquidity, minReturns);

        tokenbalance = IERC20(token).balanceOf(address(this)).sub(tokenbalance);
        WETHbalance = IERC20(WETH).balanceOf(address(this)).sub(WETHbalance);

        TransferHelper.safeTransfer(token, msg.sender, tokenbalance);

        // convert WETH and send back raw ETH
        IWETH(WETH).withdraw(WETHbalance);
        TransferHelper.safeTransferETH(msg.sender, WETHbalance);
    }

    // **** SWAP ****

    function _swap_(
        address tokenFrom,
        address tokenTo,
        uint256 ammountFrom,
        address to,
        address ref
    ) internal returns (uint256 ammountTo) {
        IEmiswap pairContract =
            IEmiswapRegistry(factory).pools(IERC20(tokenFrom), IERC20(tokenTo));

        (, uint256 amt1) = pairContract.getReturn(
                IERC20(tokenFrom),
                IERC20(tokenTo),
                ammountFrom
            );
        if (amt1 > 0) {
            resetAllowance(tokenFrom, address(pairContract));
            TransferHelper.safeApprove(
                tokenFrom,
                address(pairContract),
                ammountFrom
            );            
            ammountTo = pairContract.swap(
                IERC20(tokenFrom),
                IERC20(tokenTo),
                ammountFrom,
                0,
                to,
                ref
            );
        }
    }

    function _swapbyRoute(
        address[] memory path,
        uint256 ammountFrom,
        address to,
        address ref
    ) internal returns (uint256 ammountTo) {
        for (uint256 i = 0; i < path.length - 1; i++) {
            if (path.length >= 2) {
                uint256 _ammountTo =
                    _swap_(
                        path[i],
                        path[i + 1],
                        ammountFrom,
                        (i == (path.length - 2) ? to : address(this)),
                        ref
                    );
                if (i == (path.length - 2)) {
                    return (_ammountTo);
                } else {
                    ammountFrom = _ammountTo;
                }
            }
        }
    }

    /**
     * @param amountIn exact in value of source token
     * @param amountOutMin minimum amount value of result token
     * @param path array of token addresses, represent the path for swaps
     * @param to send result token to
     * @param ref referral
     * @return amounts result amount
     */

    function swapExactTokensForTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        address ref
    ) external returns (uint256[] memory amounts) {
        amounts = getAmountsOut(amountIn, path);
        require(
            amounts[amounts.length - 1] >= amountOutMin,
            "EmiRouter:INSUFFICIENT_OUTPUT_AMOUNT"
        );

        TransferHelper.safeTransferFrom(
            path[0],
            msg.sender,
            address(this),
            amountIn
        );
        _swapbyRoute(path, amountIn, to, ref);
    }

    /**
     * @param amountOut exact in value of result token
     * @param amountInMax maximum amount value of source token
     * @param path array of token addresses, represent the path for swaps
     * @param to send result token to
     * @param ref referral
     * @return amounts result amount values
     */

    function swapTokensForExactTokens(
        uint256 amountOut,
        uint256 amountInMax,
        address[] calldata path,
        address to,
        address ref
    ) external returns (uint256[] memory amounts) {
        amounts = getAmountsIn(amountOut, path);
        require(
            amounts[0] <= amountInMax,
            "EmiRouter:EXCESSIVE_INPUT_AMOUNT"
        );

        TransferHelper.safeTransferFrom(
            path[0],
            msg.sender,
            address(this),
            amounts[0]
        );
        _swapbyRoute(path, amounts[0], to, ref);
    }

    /**
     * @param amountOutMin minimum amount value of result token
     * @param path array of token addresses, represent the path for swaps
     * @param to send result token to
     * @param ref referral
     * @return amounts result token amount values
     */

    function swapExactETHForTokens(
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        address ref
    ) external payable returns (uint256[] memory amounts) {
        require(path[0] == WETH, "EmiRouter:INVALID_PATH");
        amounts = getAmountsOut(msg.value, path);
        require(
            amounts[amounts.length - 1] >= amountOutMin,
            "EmiRouter:INSUFFICIENT_OUTPUT_AMOUNT"
        );
        IWETH(WETH).deposit{value: amounts[0]}();
        _swapbyRoute(path, amounts[0], to, ref);
    }

    /**
     * @param amountOut amount value of result ETH
     * @param amountInMax maximum amount of source token
     * @param path array of token addresses, represent the path for swaps, (WETH for ETH)
     * @param to send result token to
     * @param ref referral
     * @return amounts result token amount values
     */

    function swapTokensForExactETH(
        uint256 amountOut,
        uint256 amountInMax,
        address[] calldata path,
        address to,
        address ref
    ) external returns (uint256[] memory amounts) {
        require(path[path.length - 1] == WETH, "EmiRouter:INVALID_PATH");
        amounts = getAmountsIn(amountOut, path);
        require(amounts[0] <= amountInMax, "EmiRouter:EXCESSIVE_AMOUNT");

        TransferHelper.safeTransferFrom(
            path[0],
            msg.sender,
            address(this),
            amounts[0]
        );

        uint256 result = _swapbyRoute(path, amounts[0], address(this), ref);

        IWETH(WETH).withdraw(result);
        TransferHelper.safeTransferETH(to, result);
    }

    /**
     * @param amountIn amount value of source token
     * @param path array of token addresses, represent the path for swaps, (WETH for ETH)
     * @param to send result token to
     * @param ref referral
     */

    function swapExactTokensForETH(
        uint256 amountIn,
        address[] calldata path,
        address to,
        address ref
    ) external {
        require(path[path.length - 1] == WETH, "EmiRouter:INVALID_PATH");
        TransferHelper.safeTransferFrom(
            path[0],
            msg.sender,
            address(this),
            amountIn
        );

        uint256 result = _swapbyRoute(path, amountIn, address(this), ref);

        IWETH(WETH).withdraw(result);
        TransferHelper.safeTransferETH(to, result);
    }

    /**
     * @param amountOut amount of result tokens
     * @param path array of token addresses, represent the path for swaps, (WETH for ETH)
     * @param to send result token to
     * @param ref referral
     * @return amounts result token amount values
     */

    function swapETHForExactTokens(
        uint256 amountOut,
        address[] calldata path,
        address to,
        address ref
    ) external payable returns (uint256[] memory amounts) {
        require(path[0] == WETH, "EmiRouter:INVALID_PATH");
        amounts = getAmountsIn(amountOut, path);
        require(
            amounts[0] <= msg.value,
            "EmiRouter:EXCESSIVE_INPUT_AMOUNT"
        );

        IWETH(WETH).deposit{value: amounts[0]}();

        _swapbyRoute(path, amounts[0], to, ref);
    }

    // **** LIBRARY FUNCTIONS ****
    /**
     * @param amountIn amount of source token
     * @param path array of token addresses, represent the path for swaps, (WETH for ETH)
     * @return amounts result token amount values
     */
    function getAmountsOut(uint256 amountIn, address[] memory path)
        public
        view
        returns (uint256[] memory amounts)
    {
        return EmiswapLib.getAmountsOut(factory, amountIn, path);
    }

    /**
     * @param amountOut amount of result token
     * @param path array of token addresses, represent the path for swaps, (WETH for ETH)
     * @return amounts result token amount values
     */
    function getAmountsIn(uint256 amountOut, address[] memory path)
        public
        view
        returns (uint256[] memory amounts)
    {
        return EmiswapLib.getAmountsIn(factory, amountOut, path);
    }
}
设置
{
  "compilationTarget": {
    "EmiRouter.Full.sol": "EmiRouter"
  },
  "evmVersion": "istanbul",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "remappings": []
}
ABI
[{"inputs":[{"internalType":"address","name":"_factory","type":"address"},{"internalType":"address","name":"_wEth","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"a","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"b","type":"uint256"}],"name":"Log","type":"event"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"amountADesired","type":"uint256"},{"internalType":"uint256","name":"amountBDesired","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"address","name":"ref","type":"address"}],"name":"addLiquidity","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amountTokenDesired","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"ref","type":"address"}],"name":"addLiquidityETH","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountETH","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"}],"name":"getAmountsIn","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"}],"name":"getAmountsOut","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"destToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"getExpectedReturn","outputs":[{"internalType":"uint256","name":"returnAmount","type":"uint256"},{"internalType":"uint256[]","name":"distribution","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"}],"name":"getLiquidity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20[]","name":"tokenAList","type":"address[]"},{"internalType":"contract IERC20[]","name":"tokenBList","type":"address[]"}],"name":"getPoolDataList","outputs":[{"components":[{"internalType":"contract IEmiswap","name":"pool","type":"address"},{"internalType":"uint256","name":"balanceA","type":"uint256"},{"internalType":"uint256","name":"balanceB","type":"uint256"}],"internalType":"struct EmiRouter.PoolData[]","name":"dataList","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token0","type":"address"},{"internalType":"contract IERC20","name":"token1","type":"address"}],"name":"getReserves","outputs":[{"internalType":"uint256","name":"_reserve0","type":"uint256"},{"internalType":"uint256","name":"_reserve1","type":"uint256"},{"internalType":"address","name":"poolAddresss","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"}],"name":"getReservesByPool","outputs":[{"internalType":"uint256","name":"_reserve0","type":"uint256"},{"internalType":"uint256","name":"_reserve1","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"}],"name":"removeLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"}],"name":"removeLiquidityETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"pairContract","type":"address"}],"name":"resetAllowance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"ref","type":"address"}],"name":"swapETHForExactTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"ref","type":"address"}],"name":"swapExactETHForTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"ref","type":"address"}],"name":"swapExactTokensForETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"ref","type":"address"}],"name":"swapExactTokensForTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMax","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"ref","type":"address"}],"name":"swapTokensForExactETH","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMax","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"ref","type":"address"}],"name":"swapTokensForExactTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"}],"name":"tokenToIERC","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]