Contract Address Details

0x072D4706f9A383D5608BD14B09b41683cb95fFd7

Contract Name
UniswapV2Router02
Creator
0xd6cf49–0769d0 at 0xd8d4dc–1cbea7
Balance
0 ETN ( )
Tokens
Fetching tokens...
Transactions
9,347 Transactions
Transfers
8,734 Transfers
Gas Used
1,275,171,556
Last Balance Update
4675665
Contract name:
UniswapV2Router02




Optimization enabled
true
Compiler version
v0.6.6+commit.6c089d02




Optimization runs
500
Verified at
2024-03-10T19:52:40.320670Z

Constructor Arguments

000000000000000000000000203d550ed6fa9dab8a4190720cf9f65138abd15b000000000000000000000000138dafbda0ccb3d8e39c19edb0510fc31b7c1c77

Arg [0] (address) : 0x203d550ed6fa9dab8a4190720cf9f65138abd15b
Arg [1] (address) : 0x138dafbda0ccb3d8e39c19edb0510fc31b7c1c77

              

contracts/Router.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.6.6;

interface IUniswapV2Factory {
    event PairCreated(
        address indexed token0,
        address indexed token1,
        address pair,
        uint
    );

    function feeTo() external view returns (address);

    function feeToSetter() external view returns (address);

    function getPair(
        address tokenA,
        address tokenB
    ) external view returns (address pair);

    function allPairs(uint) external view returns (address pair);

    function allPairsLength() external view returns (uint);

    function createPair(
        address tokenA,
        address tokenB
    ) external returns (address pair);

    function setFeeTo(address) external;

    function setFeeToSetter(address) external;
}

interface IUniswapV2Pair {
    event Approval(address indexed owner, address indexed spender, uint value);
    event Transfer(address indexed from, address indexed to, uint value);

    function name() external pure returns (string memory);

    function symbol() external pure returns (string memory);

    function decimals() external pure returns (uint8);

    function totalSupply() external view returns (uint);

    function balanceOf(address owner) external view returns (uint);

    function allowance(
        address owner,
        address spender
    ) external view returns (uint);

    function approve(address spender, uint value) external returns (bool);

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

    function transferFrom(
        address from,
        address to,
        uint value
    ) external returns (bool);

    function DOMAIN_SEPARATOR() external view returns (bytes32);

    function PERMIT_TYPEHASH() external pure returns (bytes32);

    function nonces(address owner) external view returns (uint);

    function permit(
        address owner,
        address spender,
        uint value,
        uint deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

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

    function MINIMUM_LIQUIDITY() external pure returns (uint);

    function factory() external view returns (address);

    function token0() external view returns (address);

    function token1() external view returns (address);

    function getReserves()
        external
        view
        returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);

    function price0CumulativeLast() external view returns (uint);

    function price1CumulativeLast() external view returns (uint);

    function kLast() external view returns (uint);

    function mint(address to) external returns (uint liquidity);

    function burn(address to) external returns (uint amount0, uint amount1);

    function swap(
        uint amount0Out,
        uint amount1Out,
        address to,
        bytes calldata data
    ) external;

    function skim(address to) external;

    function sync() external;

    function initialize(address, address) external;
}

interface IUniswapV2Router01 {
    function factory() external pure returns (address);

    function WETH() external pure returns (address);

    function addLiquidity(
        address tokenA,
        address tokenB,
        uint amountADesired,
        uint amountBDesired,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB, uint liquidity);

    function addLiquidityETH(
        address token,
        uint amountTokenDesired,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    )
        external
        payable
        returns (uint amountToken, uint amountETH, uint liquidity);

    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB);

    function removeLiquidityETH(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountToken, uint 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 returns (uint amountA, uint amountB);

    function removeLiquidityETHWithPermit(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint amountToken, uint amountETH);

    function swapExactTokensForTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);

    function swapTokensForExactTokens(
        uint amountOut,
        uint amountInMax,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);

    function swapExactETHForTokens(
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external payable returns (uint[] memory amounts);

    function swapTokensForExactETH(
        uint amountOut,
        uint amountInMax,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);

    function swapExactTokensForETH(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);

    function swapETHForExactTokens(
        uint amountOut,
        address[] calldata path,
        address to,
        uint deadline
    ) external payable returns (uint[] memory amounts);

    function quote(
        uint amountA,
        uint reserveA,
        uint reserveB
    ) external pure returns (uint amountB);

    function getAmountOut(
        uint amountIn,
        uint reserveIn,
        uint reserveOut
    ) external pure returns (uint amountOut);

    function getAmountIn(
        uint amountOut,
        uint reserveIn,
        uint reserveOut
    ) external pure returns (uint amountIn);

    function getAmountsOut(
        uint amountIn,
        address[] calldata path
    ) external view returns (uint[] memory amounts);

    function getAmountsIn(
        uint amountOut,
        address[] calldata path
    ) external view returns (uint[] memory amounts);
}

interface IUniswapV2Router02 is IUniswapV2Router01 {
    function removeLiquidityETHSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountETH);

    function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint amountETH);

    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;

    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external payable;

    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
}

interface IERC20 {
    event Approval(address indexed owner, address indexed spender, uint value);
    event Transfer(address indexed from, address indexed to, uint value);

    function name() external view returns (string memory);

    function symbol() external view returns (string memory);

    function decimals() external view returns (uint8);

    function totalSupply() external view returns (uint);

    function balanceOf(address owner) external view returns (uint);

    function allowance(
        address owner,
        address spender
    ) external view returns (uint);

    function approve(address spender, uint value) external returns (bool);

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

    function transferFrom(
        address from,
        address to,
        uint value
    ) external returns (bool);
}

interface IWETH {
    function deposit() external payable;

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

    function withdraw(uint) external;
}

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, "ElectroSwapRouter: 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,
                    "ElectroSwapRouter: INSUFFICIENT_B_AMOUNT"
                );
                (amountA, amountB) = (amountADesired, amountBOptimal);
            } else {
                uint amountAOptimal = UniswapV2Library.quote(
                    amountBDesired,
                    reserveB,
                    reserveA
                );
                assert(amountAOptimal <= amountADesired);
                require(
                    amountAOptimal >= amountAMin,
                    "ElectroSwapRouter: 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
        payable
        virtual
        override
        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,
            "ElectroSwapRouter: INSUFFICIENT_A_AMOUNT"
        );
        require(
            amountB >= amountBMin,
            "ElectroSwapRouter: 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,
            "ElectroSwapRouter: 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,
            "ElectroSwapRouter: 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
        payable
        virtual
        override
        ensure(deadline)
        returns (uint[] memory amounts)
    {
        require(path[0] == WETH, "ElectroSwapRouter: INVALID_PATH");
        amounts = UniswapV2Library.getAmountsOut(factory, msg.value, path);
        require(
            amounts[amounts.length - 1] >= amountOutMin,
            "ElectroSwapRouter: 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, "ElectroSwapRouter: INVALID_PATH");
        amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);
        require(
            amounts[0] <= amountInMax,
            "ElectroSwapRouter: 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, "ElectroSwapRouter: INVALID_PATH");
        amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path);
        require(
            amounts[amounts.length - 1] >= amountOutMin,
            "ElectroSwapRouter: 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
        payable
        virtual
        override
        ensure(deadline)
        returns (uint[] memory amounts)
    {
        require(path[0] == WETH, "ElectroSwapRouter: INVALID_PATH");
        amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);
        require(
            amounts[0] <= msg.value,
            "ElectroSwapRouter: 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,
            "ElectroSwapRouter: INSUFFICIENT_OUTPUT_AMOUNT"
        );
    }

    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external payable virtual override ensure(deadline) {
        require(path[0] == WETH, "ElectroSwapRouter: 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,
            "ElectroSwapRouter: 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, "ElectroSwapRouter: 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,
            "ElectroSwapRouter: 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);
    }

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

// a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math)

library SafeMath {
    function add(uint x, uint y) internal pure returns (uint z) {
        require((z = x + y) >= x, "ds-math-add-overflow");
    }

    function sub(uint x, uint y) internal pure returns (uint z) {
        require((z = x - y) <= x, "ds-math-sub-underflow");
    }

    function mul(uint x, uint y) internal pure returns (uint z) {
        require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow");
    }
}

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, "ElectroSwapLibrary: IDENTICAL_ADDRESSES");
        (token0, token1) = tokenA < tokenB
            ? (tokenA, tokenB)
            : (tokenB, tokenA);
        require(token0 != address(0), "ElectroSwapLibrary: ZERO_ADDRESS");
    }

    // calculates the CREATE2 address for a pair without making any external calls
    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"bd66f645caa490d6090d3c649129cb143b632b678076973762b574ac1bc9650c" // 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
    function quote(
        uint amountA,
        uint reserveA,
        uint reserveB
    ) internal pure returns (uint amountB) {
        require(amountA > 0, "ElectroSwapLibrary: INSUFFICIENT_AMOUNT");
        require(
            reserveA > 0 && reserveB > 0,
            "ElectroSwapLibrary: INSUFFICIENT_LIQUIDITY"
        );
        amountB = amountA.mul(reserveB) / reserveA;
    }

    // given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset
    function getAmountOut(
        uint amountIn,
        uint reserveIn,
        uint reserveOut
    ) internal pure returns (uint amountOut) {
        require(amountIn > 0, "ElectroSwapLibrary: INSUFFICIENT_INPUT_AMOUNT");
        require(
            reserveIn > 0 && reserveOut > 0,
            "ElectroSwapLibrary: INSUFFICIENT_LIQUIDITY"
        );
        uint amountInWithFee = amountIn.mul(997);
        uint numerator = amountInWithFee.mul(reserveOut);
        uint denominator = reserveIn.mul(1000).add(amountInWithFee);
        amountOut = numerator / denominator;
    }

    // given an output amount of an asset and pair reserves, returns a required input amount of the other asset
    function getAmountIn(
        uint amountOut,
        uint reserveIn,
        uint reserveOut
    ) internal pure returns (uint amountIn) {
        require(amountOut > 0, "ElectroSwapLibrary: INSUFFICIENT_OUTPUT_AMOUNT");
        require(
            reserveIn > 0 && reserveOut > 0,
            "ElectroSwapLibrary: 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
    function getAmountsOut(
        address factory,
        uint amountIn,
        address[] memory path
    ) internal view returns (uint[] memory amounts) {
        require(path.length >= 2, "ElectroSwapLibrary: 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
    function getAmountsIn(
        address factory,
        uint amountOut,
        address[] memory path
    ) internal view returns (uint[] memory amounts) {
        require(path.length >= 2, "ElectroSwapLibrary: 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);
        }
    }
}

// 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, uint 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, uint 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,
        uint 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, uint value) internal {
        (bool success, ) = to.call{value: value}(new bytes(0));
        require(success, "TransferHelper: ETH_TRANSFER_FAILED");
    }
}
        

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_factory","internalType":"address"},{"type":"address","name":"_WETH","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"WETH","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"amountA","internalType":"uint256"},{"type":"uint256","name":"amountB","internalType":"uint256"},{"type":"uint256","name":"liquidity","internalType":"uint256"}],"name":"addLiquidity","inputs":[{"type":"address","name":"tokenA","internalType":"address"},{"type":"address","name":"tokenB","internalType":"address"},{"type":"uint256","name":"amountADesired","internalType":"uint256"},{"type":"uint256","name":"amountBDesired","internalType":"uint256"},{"type":"uint256","name":"amountAMin","internalType":"uint256"},{"type":"uint256","name":"amountBMin","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"uint256","name":"amountToken","internalType":"uint256"},{"type":"uint256","name":"amountETH","internalType":"uint256"},{"type":"uint256","name":"liquidity","internalType":"uint256"}],"name":"addLiquidityETH","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"amountTokenDesired","internalType":"uint256"},{"type":"uint256","name":"amountTokenMin","internalType":"uint256"},{"type":"uint256","name":"amountETHMin","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"factory","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"}],"name":"getAmountIn","inputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"},{"type":"uint256","name":"reserveIn","internalType":"uint256"},{"type":"uint256","name":"reserveOut","internalType":"uint256"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"}],"name":"getAmountOut","inputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"},{"type":"uint256","name":"reserveIn","internalType":"uint256"},{"type":"uint256","name":"reserveOut","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"amounts","internalType":"uint256[]"}],"name":"getAmountsIn","inputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"amounts","internalType":"uint256[]"}],"name":"getAmountsOut","inputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint256","name":"amountB","internalType":"uint256"}],"name":"quote","inputs":[{"type":"uint256","name":"amountA","internalType":"uint256"},{"type":"uint256","name":"reserveA","internalType":"uint256"},{"type":"uint256","name":"reserveB","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"amountA","internalType":"uint256"},{"type":"uint256","name":"amountB","internalType":"uint256"}],"name":"removeLiquidity","inputs":[{"type":"address","name":"tokenA","internalType":"address"},{"type":"address","name":"tokenB","internalType":"address"},{"type":"uint256","name":"liquidity","internalType":"uint256"},{"type":"uint256","name":"amountAMin","internalType":"uint256"},{"type":"uint256","name":"amountBMin","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"amountToken","internalType":"uint256"},{"type":"uint256","name":"amountETH","internalType":"uint256"}],"name":"removeLiquidityETH","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"liquidity","internalType":"uint256"},{"type":"uint256","name":"amountTokenMin","internalType":"uint256"},{"type":"uint256","name":"amountETHMin","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"amountETH","internalType":"uint256"}],"name":"removeLiquidityETHSupportingFeeOnTransferTokens","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"liquidity","internalType":"uint256"},{"type":"uint256","name":"amountTokenMin","internalType":"uint256"},{"type":"uint256","name":"amountETHMin","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"amountToken","internalType":"uint256"},{"type":"uint256","name":"amountETH","internalType":"uint256"}],"name":"removeLiquidityETHWithPermit","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"liquidity","internalType":"uint256"},{"type":"uint256","name":"amountTokenMin","internalType":"uint256"},{"type":"uint256","name":"amountETHMin","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"},{"type":"bool","name":"approveMax","internalType":"bool"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"amountETH","internalType":"uint256"}],"name":"removeLiquidityETHWithPermitSupportingFeeOnTransferTokens","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"liquidity","internalType":"uint256"},{"type":"uint256","name":"amountTokenMin","internalType":"uint256"},{"type":"uint256","name":"amountETHMin","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"},{"type":"bool","name":"approveMax","internalType":"bool"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"amountA","internalType":"uint256"},{"type":"uint256","name":"amountB","internalType":"uint256"}],"name":"removeLiquidityWithPermit","inputs":[{"type":"address","name":"tokenA","internalType":"address"},{"type":"address","name":"tokenB","internalType":"address"},{"type":"uint256","name":"liquidity","internalType":"uint256"},{"type":"uint256","name":"amountAMin","internalType":"uint256"},{"type":"uint256","name":"amountBMin","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"},{"type":"bool","name":"approveMax","internalType":"bool"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"uint256[]","name":"amounts","internalType":"uint256[]"}],"name":"swapETHForExactTokens","inputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"uint256[]","name":"amounts","internalType":"uint256[]"}],"name":"swapExactETHForTokens","inputs":[{"type":"uint256","name":"amountOutMin","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"swapExactETHForTokensSupportingFeeOnTransferTokens","inputs":[{"type":"uint256","name":"amountOutMin","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256[]","name":"amounts","internalType":"uint256[]"}],"name":"swapExactTokensForETH","inputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"},{"type":"uint256","name":"amountOutMin","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"swapExactTokensForETHSupportingFeeOnTransferTokens","inputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"},{"type":"uint256","name":"amountOutMin","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256[]","name":"amounts","internalType":"uint256[]"}],"name":"swapExactTokensForTokens","inputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"},{"type":"uint256","name":"amountOutMin","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"swapExactTokensForTokensSupportingFeeOnTransferTokens","inputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"},{"type":"uint256","name":"amountOutMin","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256[]","name":"amounts","internalType":"uint256[]"}],"name":"swapTokensForExactETH","inputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"},{"type":"uint256","name":"amountInMax","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256[]","name":"amounts","internalType":"uint256[]"}],"name":"swapTokensForExactTokens","inputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"},{"type":"uint256","name":"amountInMax","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"receive","stateMutability":"payable"}]
            

Deployed ByteCode

0x60806040526004361061018f5760003560e01c80638803dbee116100d6578063c45a01551161007f578063e8e3370011610059578063e8e3370014610b8d578063f305d71914610c0d578063fb3bdb4114610c53576101c8565b8063c45a015514610a50578063d06ca61f14610a65578063ded9382a14610b1a576101c8565b8063af2979eb116100b0578063af2979eb1461091c578063b6f9de951461096f578063baa2abde146109f3576101c8565b80638803dbee1461081f578063ad5c4648146108b5578063ad615dec146108e6576101c8565b80634a25d94a11610138578063791ac94711610112578063791ac947146106cf5780637ff36ab51461076557806385f8c259146107e9576101c8565b80634a25d94a146105305780635b0d5984146105c65780635c11d79514610639576101c8565b80631f00ca74116101695780631f00ca74146103675780632195995c1461041c57806338ed17391461049a576101c8565b806302751cec146101cd578063054d50d41461023957806318cbafe514610281576101c8565b366101c857336001600160a01b037f000000000000000000000000138dafbda0ccb3d8e39c19edb0510fc31b7c1c7716146101c657fe5b005b600080fd5b3480156101d957600080fd5b50610220600480360360c08110156101f057600080fd5b506001600160a01b0381358116916020810135916040820135916060810135916080820135169060a00135610cd7565b6040805192835260208301919091528051918290030190f35b34801561024557600080fd5b5061026f6004803603606081101561025c57600080fd5b5080359060208101359060400135610df1565b60408051918252519081900360200190f35b34801561028d57600080fd5b50610317600480360360a08110156102a457600080fd5b813591602081013591810190606081016040820135600160201b8111156102ca57600080fd5b8201836020820111156102dc57600080fd5b803590602001918460208302840111600160201b831117156102fd57600080fd5b91935091506001600160a01b038135169060200135610e06565b60408051602080825283518183015283519192839290830191858101910280838360005b8381101561035357818101518382015260200161033b565b505050509050019250505060405180910390f35b34801561037357600080fd5b506103176004803603604081101561038a57600080fd5b81359190810190604081016020820135600160201b8111156103ab57600080fd5b8201836020820111156103bd57600080fd5b803590602001918460208302840111600160201b831117156103de57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550611145945050505050565b34801561042857600080fd5b50610220600480360361016081101561044057600080fd5b506001600160a01b038135811691602081013582169160408201359160608101359160808201359160a08101359091169060c08101359060e081013515159060ff610100820135169061012081013590610140013561117b565b3480156104a657600080fd5b50610317600480360360a08110156104bd57600080fd5b813591602081013591810190606081016040820135600160201b8111156104e357600080fd5b8201836020820111156104f557600080fd5b803590602001918460208302840111600160201b8311171561051657600080fd5b91935091506001600160a01b038135169060200135611275565b34801561053c57600080fd5b50610317600480360360a081101561055357600080fd5b813591602081013591810190606081016040820135600160201b81111561057957600080fd5b82018360208201111561058b57600080fd5b803590602001918460208302840111600160201b831117156105ac57600080fd5b91935091506001600160a01b0381351690602001356113c0565b3480156105d257600080fd5b5061026f60048036036101408110156105ea57600080fd5b506001600160a01b0381358116916020810135916040820135916060810135916080820135169060a08101359060c081013515159060ff60e0820135169061010081013590610120013561155e565b34801561064557600080fd5b506101c6600480360360a081101561065c57600080fd5b813591602081013591810190606081016040820135600160201b81111561068257600080fd5b82018360208201111561069457600080fd5b803590602001918460208302840111600160201b831117156106b557600080fd5b91935091506001600160a01b03813516906020013561166c565b3480156106db57600080fd5b506101c6600480360360a08110156106f257600080fd5b813591602081013591810190606081016040820135600160201b81111561071857600080fd5b82018360208201111561072a57600080fd5b803590602001918460208302840111600160201b8311171561074b57600080fd5b91935091506001600160a01b038135169060200135611901565b6103176004803603608081101561077b57600080fd5b81359190810190604081016020820135600160201b81111561079c57600080fd5b8201836020820111156107ae57600080fd5b803590602001918460208302840111600160201b831117156107cf57600080fd5b91935091506001600160a01b038135169060200135611b97565b3480156107f557600080fd5b5061026f6004803603606081101561080c57600080fd5b5080359060208101359060400135611efc565b34801561082b57600080fd5b50610317600480360360a081101561084257600080fd5b813591602081013591810190606081016040820135600160201b81111561086857600080fd5b82018360208201111561087a57600080fd5b803590602001918460208302840111600160201b8311171561089b57600080fd5b91935091506001600160a01b038135169060200135611f09565b3480156108c157600080fd5b506108ca612002565b604080516001600160a01b039092168252519081900360200190f35b3480156108f257600080fd5b5061026f6004803603606081101561090957600080fd5b5080359060208101359060400135612026565b34801561092857600080fd5b5061026f600480360360c081101561093f57600080fd5b506001600160a01b0381358116916020810135916040820135916060810135916080820135169060a00135612033565b6101c66004803603608081101561098557600080fd5b81359190810190604081016020820135600160201b8111156109a657600080fd5b8201836020820111156109b857600080fd5b803590602001918460208302840111600160201b831117156109d957600080fd5b91935091506001600160a01b0381351690602001356121b4565b3480156109ff57600080fd5b50610220600480360360e0811015610a1657600080fd5b506001600160a01b038135811691602081013582169160408201359160608101359160808201359160a08101359091169060c00135612552565b348015610a5c57600080fd5b506108ca612796565b348015610a7157600080fd5b5061031760048036036040811015610a8857600080fd5b81359190810190604081016020820135600160201b811115610aa957600080fd5b820183602082011115610abb57600080fd5b803590602001918460208302840111600160201b83111715610adc57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506127ba945050505050565b348015610b2657600080fd5b506102206004803603610140811015610b3e57600080fd5b506001600160a01b0381358116916020810135916040820135916060810135916080820135169060a08101359060c081013515159060ff60e082013516906101008101359061012001356127e7565b348015610b9957600080fd5b50610bef6004803603610100811015610bb157600080fd5b506001600160a01b038135811691602081013582169160408201359160608101359160808201359160a08101359160c0820135169060e001356128fb565b60408051938452602084019290925282820152519081900360600190f35b610bef600480360360c0811015610c2357600080fd5b506001600160a01b0381358116916020810135916040820135916060810135916080820135169060a00135612a37565b61031760048036036080811015610c6957600080fd5b81359190810190604081016020820135600160201b811115610c8a57600080fd5b820183602082011115610c9c57600080fd5b803590602001918460208302840111600160201b83111715610cbd57600080fd5b91935091506001600160a01b038135169060200135612cdc565b6000808242811015610d1e576040805162461bcd60e51b815260206004820152601a60248201526000805160206144ac833981519152604482015290519081900360640190fd5b610d4d897f000000000000000000000000138dafbda0ccb3d8e39c19edb0510fc31b7c1c778a8a8a308a612552565b9093509150610d5d898685613070565b7f000000000000000000000000138dafbda0ccb3d8e39c19edb0510fc31b7c1c776001600160a01b0316632e1a7d4d836040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015610dc357600080fd5b505af1158015610dd7573d6000803e3d6000fd5b50505050610de585836131da565b50965096945050505050565b6000610dfe8484846132d2565b949350505050565b60608142811015610e4c576040805162461bcd60e51b815260206004820152601a60248201526000805160206144ac833981519152604482015290519081900360640190fd5b6001600160a01b037f000000000000000000000000138dafbda0ccb3d8e39c19edb0510fc31b7c1c771686866000198101818110610e8657fe5b905060200201356001600160a01b03166001600160a01b031614610ef1576040805162461bcd60e51b815260206004820152601f60248201527f456c656374726f53776170526f757465723a20494e56414c49445f5041544800604482015290519081900360640190fd5b610f4f7f000000000000000000000000203d550ed6fa9dab8a4190720cf9f65138abd15b898888808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152506133c292505050565b91508682600184510381518110610f6257fe5b60200260200101511015610fa75760405162461bcd60e51b815260040180806020018281038252602d815260200180614572602d913960400191505060405180910390fd5b61104586866000818110610fb757fe5b905060200201356001600160a01b03163361102b7f000000000000000000000000203d550ed6fa9dab8a4190720cf9f65138abd15b8a8a6000818110610ff957fe5b905060200201356001600160a01b03168b8b600181811061101657fe5b905060200201356001600160a01b031661350e565b8560008151811061103857fe5b60200260200101516135e6565b61108482878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250309250613743915050565b7f000000000000000000000000138dafbda0ccb3d8e39c19edb0510fc31b7c1c776001600160a01b0316632e1a7d4d836001855103815181106110c357fe5b60200260200101516040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561110157600080fd5b505af1158015611115573d6000803e3d6000fd5b5050505061113a848360018551038151811061112d57fe5b60200260200101516131da565b509695505050505050565b60606111727f000000000000000000000000203d550ed6fa9dab8a4190720cf9f65138abd15b8484613989565b90505b92915050565b60008060006111ab7f000000000000000000000000203d550ed6fa9dab8a4190720cf9f65138abd15b8f8f61350e565b90506000876111ba578c6111be565b6000195b6040805163d505accf60e01b815233600482015230602482015260448101839052606481018c905260ff8a16608482015260a4810189905260c4810188905290519192506001600160a01b0384169163d505accf9160e48082019260009290919082900301818387803b15801561123457600080fd5b505af1158015611248573d6000803e3d6000fd5b5050505061125b8f8f8f8f8f8f8f612552565b809450819550505050509b509b9950505050505050505050565b606081428110156112bb576040805162461bcd60e51b815260206004820152601a60248201526000805160206144ac833981519152604482015290519081900360640190fd5b6113197f000000000000000000000000203d550ed6fa9dab8a4190720cf9f65138abd15b898888808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152506133c292505050565b9150868260018451038151811061132c57fe5b602002602001015110156113715760405162461bcd60e51b815260040180806020018281038252602d815260200180614572602d913960400191505060405180910390fd5b61138186866000818110610fb757fe5b61113a82878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250899250613743915050565b60608142811015611406576040805162461bcd60e51b815260206004820152601a60248201526000805160206144ac833981519152604482015290519081900360640190fd5b6001600160a01b037f000000000000000000000000138dafbda0ccb3d8e39c19edb0510fc31b7c1c77168686600019810181811061144057fe5b905060200201356001600160a01b03166001600160a01b0316146114ab576040805162461bcd60e51b815260206004820152601f60248201527f456c656374726f53776170526f757465723a20494e56414c49445f5041544800604482015290519081900360640190fd5b6115097f000000000000000000000000203d550ed6fa9dab8a4190720cf9f65138abd15b8988888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061398992505050565b9150868260008151811061151957fe5b60200260200101511115610fa75760405162461bcd60e51b81526004018080602001828103825260298152602001806145216029913960400191505060405180910390fd5b6000806115ac7f000000000000000000000000203d550ed6fa9dab8a4190720cf9f65138abd15b8d7f000000000000000000000000138dafbda0ccb3d8e39c19edb0510fc31b7c1c7761350e565b90506000866115bb578b6115bf565b6000195b6040805163d505accf60e01b815233600482015230602482015260448101839052606481018b905260ff8916608482015260a4810188905260c4810187905290519192506001600160a01b0384169163d505accf9160e48082019260009290919082900301818387803b15801561163557600080fd5b505af1158015611649573d6000803e3d6000fd5b5050505061165b8d8d8d8d8d8d612033565b9d9c50505050505050505050505050565b80428110156116b0576040805162461bcd60e51b815260206004820152601a60248201526000805160206144ac833981519152604482015290519081900360640190fd5b611725858560008181106116c057fe5b905060200201356001600160a01b03163361171f7f000000000000000000000000203d550ed6fa9dab8a4190720cf9f65138abd15b8989600081811061170257fe5b905060200201356001600160a01b03168a8a600181811061101657fe5b8a6135e6565b60008585600019810181811061173757fe5b905060200201356001600160a01b03166001600160a01b03166370a08231856040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b15801561179c57600080fd5b505afa1580156117b0573d6000803e3d6000fd5b505050506040513d60208110156117c657600080fd5b50516040805160208881028281018201909352888252929350611808929091899189918291850190849080828437600092019190915250889250613ac1915050565b866118ba828888600019810181811061181d57fe5b905060200201356001600160a01b03166001600160a01b03166370a08231886040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b15801561188257600080fd5b505afa158015611896573d6000803e3d6000fd5b505050506040513d60208110156118ac57600080fd5b50519063ffffffff613dd316565b10156118f75760405162461bcd60e51b815260040180806020018281038252602d815260200180614572602d913960400191505060405180910390fd5b5050505050505050565b8042811015611945576040805162461bcd60e51b815260206004820152601a60248201526000805160206144ac833981519152604482015290519081900360640190fd5b6001600160a01b037f000000000000000000000000138dafbda0ccb3d8e39c19edb0510fc31b7c1c77168585600019810181811061197f57fe5b905060200201356001600160a01b03166001600160a01b0316146119ea576040805162461bcd60e51b815260206004820152601f60248201527f456c656374726f53776170526f757465723a20494e56414c49445f5041544800604482015290519081900360640190fd5b6119fa858560008181106116c057fe5b611a38858580806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250309250613ac1915050565b604080516370a0823160e01b815230600482015290516000916001600160a01b037f000000000000000000000000138dafbda0ccb3d8e39c19edb0510fc31b7c1c7716916370a0823191602480820192602092909190829003018186803b158015611aa257600080fd5b505afa158015611ab6573d6000803e3d6000fd5b505050506040513d6020811015611acc57600080fd5b5051905086811015611b0f5760405162461bcd60e51b815260040180806020018281038252602d815260200180614572602d913960400191505060405180910390fd5b7f000000000000000000000000138dafbda0ccb3d8e39c19edb0510fc31b7c1c776001600160a01b0316632e1a7d4d826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015611b7557600080fd5b505af1158015611b89573d6000803e3d6000fd5b505050506118f784826131da565b60608142811015611bdd576040805162461bcd60e51b815260206004820152601a60248201526000805160206144ac833981519152604482015290519081900360640190fd5b7f000000000000000000000000138dafbda0ccb3d8e39c19edb0510fc31b7c1c776001600160a01b031686866000818110611c1457fe5b905060200201356001600160a01b03166001600160a01b031614611c7f576040805162461bcd60e51b815260206004820152601f60248201527f456c656374726f53776170526f757465723a20494e56414c49445f5041544800604482015290519081900360640190fd5b611cdd7f000000000000000000000000203d550ed6fa9dab8a4190720cf9f65138abd15b348888808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152506133c292505050565b91508682600184510381518110611cf057fe5b60200260200101511015611d355760405162461bcd60e51b815260040180806020018281038252602d815260200180614572602d913960400191505060405180910390fd5b7f000000000000000000000000138dafbda0ccb3d8e39c19edb0510fc31b7c1c776001600160a01b031663d0e30db083600081518110611d7157fe5b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b158015611da457600080fd5b505af1158015611db8573d6000803e3d6000fd5b50505050507f000000000000000000000000138dafbda0ccb3d8e39c19edb0510fc31b7c1c776001600160a01b031663a9059cbb611e1d7f000000000000000000000000203d550ed6fa9dab8a4190720cf9f65138abd15b8989600081811061170257fe5b84600081518110611e2a57fe5b60200260200101516040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050602060405180830381600087803b158015611e8157600080fd5b505af1158015611e95573d6000803e3d6000fd5b505050506040513d6020811015611eab57600080fd5b5051611eb357fe5b611ef282878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250899250613743915050565b5095945050505050565b6000610dfe848484613e2b565b60608142811015611f4f576040805162461bcd60e51b815260206004820152601a60248201526000805160206144ac833981519152604482015290519081900360640190fd5b611fad7f000000000000000000000000203d550ed6fa9dab8a4190720cf9f65138abd15b8988888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061398992505050565b91508682600081518110611fbd57fe5b602002602001015111156113715760405162461bcd60e51b81526004018080602001828103825260298152602001806145216029913960400191505060405180910390fd5b7f000000000000000000000000138dafbda0ccb3d8e39c19edb0510fc31b7c1c7781565b6000610dfe848484613f1b565b60008142811015612079576040805162461bcd60e51b815260206004820152601a60248201526000805160206144ac833981519152604482015290519081900360640190fd5b6120a8887f000000000000000000000000138dafbda0ccb3d8e39c19edb0510fc31b7c1c778989893089612552565b604080516370a0823160e01b8152306004820152905191945061212c92508a9187916001600160a01b038416916370a0823191602480820192602092909190829003018186803b1580156120fb57600080fd5b505afa15801561210f573d6000803e3d6000fd5b505050506040513d602081101561212557600080fd5b5051613070565b7f000000000000000000000000138dafbda0ccb3d8e39c19edb0510fc31b7c1c776001600160a01b0316632e1a7d4d836040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561219257600080fd5b505af11580156121a6573d6000803e3d6000fd5b5050505061113a84836131da565b80428110156121f8576040805162461bcd60e51b815260206004820152601a60248201526000805160206144ac833981519152604482015290519081900360640190fd5b7f000000000000000000000000138dafbda0ccb3d8e39c19edb0510fc31b7c1c776001600160a01b03168585600081811061222f57fe5b905060200201356001600160a01b03166001600160a01b03161461229a576040805162461bcd60e51b815260206004820152601f60248201527f456c656374726f53776170526f757465723a20494e56414c49445f5041544800604482015290519081900360640190fd5b60003490507f000000000000000000000000138dafbda0ccb3d8e39c19edb0510fc31b7c1c776001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b1580156122fa57600080fd5b505af115801561230e573d6000803e3d6000fd5b50505050507f000000000000000000000000138dafbda0ccb3d8e39c19edb0510fc31b7c1c776001600160a01b031663a9059cbb6123737f000000000000000000000000203d550ed6fa9dab8a4190720cf9f65138abd15b8989600081811061170257fe5b836040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050602060405180830381600087803b1580156123c357600080fd5b505af11580156123d7573d6000803e3d6000fd5b505050506040513d60208110156123ed57600080fd5b50516123f557fe5b60008686600019810181811061240757fe5b905060200201356001600160a01b03166001600160a01b03166370a08231866040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b15801561246c57600080fd5b505afa158015612480573d6000803e3d6000fd5b505050506040513d602081101561249657600080fd5b505160408051602089810282810182019093528982529293506124d89290918a918a918291850190849080828437600092019190915250899250613ac1915050565b876118ba82898960001981018181106124ed57fe5b905060200201356001600160a01b03166001600160a01b03166370a08231896040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b15801561188257600080fd5b6000808242811015612599576040805162461bcd60e51b815260206004820152601a60248201526000805160206144ac833981519152604482015290519081900360640190fd5b60006125c67f000000000000000000000000203d550ed6fa9dab8a4190720cf9f65138abd15b8c8c61350e565b604080516323b872dd60e01b81523360048201526001600160a01b03831660248201819052604482018d9052915192935090916323b872dd916064808201926020929091908290030181600087803b15801561262157600080fd5b505af1158015612635573d6000803e3d6000fd5b505050506040513d602081101561264b57600080fd5b50506040805163226bf2d160e21b81526001600160a01b03888116600483015282516000938493928616926389afcb44926024808301939282900301818787803b15801561269857600080fd5b505af11580156126ac573d6000803e3d6000fd5b505050506040513d60408110156126c257600080fd5b508051602090910151909250905060006126dc8e8e613fc7565b509050806001600160a01b03168e6001600160a01b0316146126ff578183612702565b82825b90975095508a8710156127465760405162461bcd60e51b81526004018080602001828103825260288152602001806144cc6028913960400191505060405180910390fd5b898610156127855760405162461bcd60e51b815260040180806020018281038252602881526020018061454a6028913960400191505060405180910390fd5b505050505097509795505050505050565b7f000000000000000000000000203d550ed6fa9dab8a4190720cf9f65138abd15b81565b60606111727f000000000000000000000000203d550ed6fa9dab8a4190720cf9f65138abd15b84846133c2565b60008060006128377f000000000000000000000000203d550ed6fa9dab8a4190720cf9f65138abd15b8e7f000000000000000000000000138dafbda0ccb3d8e39c19edb0510fc31b7c1c7761350e565b9050600087612846578c61284a565b6000195b6040805163d505accf60e01b815233600482015230602482015260448101839052606481018c905260ff8a16608482015260a4810189905260c4810188905290519192506001600160a01b0384169163d505accf9160e48082019260009290919082900301818387803b1580156128c057600080fd5b505af11580156128d4573d6000803e3d6000fd5b505050506128e68e8e8e8e8e8e610cd7565b909f909e509c50505050505050505050505050565b60008060008342811015612944576040805162461bcd60e51b815260206004820152601a60248201526000805160206144ac833981519152604482015290519081900360640190fd5b6129528c8c8c8c8c8c6140a5565b909450925060006129847f000000000000000000000000203d550ed6fa9dab8a4190720cf9f65138abd15b8e8e61350e565b90506129928d3383886135e6565b61299e8c3383876135e6565b806001600160a01b0316636a627842886040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b03168152602001915050602060405180830381600087803b1580156129f657600080fd5b505af1158015612a0a573d6000803e3d6000fd5b505050506040513d6020811015612a2057600080fd5b5051949d939c50939a509198505050505050505050565b60008060008342811015612a80576040805162461bcd60e51b815260206004820152601a60248201526000805160206144ac833981519152604482015290519081900360640190fd5b612aae8a7f000000000000000000000000138dafbda0ccb3d8e39c19edb0510fc31b7c1c778b348c8c6140a5565b90945092506000612b007f000000000000000000000000203d550ed6fa9dab8a4190720cf9f65138abd15b8c7f000000000000000000000000138dafbda0ccb3d8e39c19edb0510fc31b7c1c7761350e565b9050612b0e8b3383886135e6565b7f000000000000000000000000138dafbda0ccb3d8e39c19edb0510fc31b7c1c776001600160a01b031663d0e30db0856040518263ffffffff1660e01b81526004016000604051808303818588803b158015612b6957600080fd5b505af1158015612b7d573d6000803e3d6000fd5b50505050507f000000000000000000000000138dafbda0ccb3d8e39c19edb0510fc31b7c1c776001600160a01b031663a9059cbb82866040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050602060405180830381600087803b158015612c0257600080fd5b505af1158015612c16573d6000803e3d6000fd5b505050506040513d6020811015612c2c57600080fd5b5051612c3457fe5b806001600160a01b0316636a627842886040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b03168152602001915050602060405180830381600087803b158015612c8c57600080fd5b505af1158015612ca0573d6000803e3d6000fd5b505050506040513d6020811015612cb657600080fd5b5051925034841015612cce57612cce338534036131da565b505096509650969350505050565b60608142811015612d22576040805162461bcd60e51b815260206004820152601a60248201526000805160206144ac833981519152604482015290519081900360640190fd5b7f000000000000000000000000138dafbda0ccb3d8e39c19edb0510fc31b7c1c776001600160a01b031686866000818110612d5957fe5b905060200201356001600160a01b03166001600160a01b031614612dc4576040805162461bcd60e51b815260206004820152601f60248201527f456c656374726f53776170526f757465723a20494e56414c49445f5041544800604482015290519081900360640190fd5b612e227f000000000000000000000000203d550ed6fa9dab8a4190720cf9f65138abd15b8888888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061398992505050565b91503482600081518110612e3257fe5b60200260200101511115612e775760405162461bcd60e51b81526004018080602001828103825260298152602001806145216029913960400191505060405180910390fd5b7f000000000000000000000000138dafbda0ccb3d8e39c19edb0510fc31b7c1c776001600160a01b031663d0e30db083600081518110612eb357fe5b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b158015612ee657600080fd5b505af1158015612efa573d6000803e3d6000fd5b50505050507f000000000000000000000000138dafbda0ccb3d8e39c19edb0510fc31b7c1c776001600160a01b031663a9059cbb612f5f7f000000000000000000000000203d550ed6fa9dab8a4190720cf9f65138abd15b8989600081811061170257fe5b84600081518110612f6c57fe5b60200260200101516040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050602060405180830381600087803b158015612fc357600080fd5b505af1158015612fd7573d6000803e3d6000fd5b505050506040513d6020811015612fed57600080fd5b5051612ff557fe5b61303482878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250899250613743915050565b8160008151811061304157fe5b6020026020010151341115611ef257611ef2338360008151811061306157fe5b602002602001015134036131da565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b178152925182516000946060949389169392918291908083835b602083106130ed5780518252601f1990920191602091820191016130ce565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d806000811461314f576040519150601f19603f3d011682016040523d82523d6000602084013e613154565b606091505b5091509150818015613182575080511580613182575080806020019051602081101561317f57600080fd5b50515b6131d3576040805162461bcd60e51b815260206004820152601f60248201527f5472616e7366657248656c7065723a205452414e534645525f4641494c454400604482015290519081900360640190fd5b5050505050565b604080516000808252602082019092526001600160a01b0384169083906040518082805190602001908083835b602083106132265780518252601f199092019160209182019101613207565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114613288576040519150601f19603f3d011682016040523d82523d6000602084013e61328d565b606091505b50509050806132cd5760405162461bcd60e51b81526004018080602001828103825260238152602001806146176023913960400191505060405180910390fd5b505050565b60008084116133125760405162461bcd60e51b815260040180806020018281038252602d8152602001806144f4602d913960400191505060405180910390fd5b6000831180156133225750600082115b61335d5760405162461bcd60e51b815260040180806020018281038252602a8152602001806145c6602a913960400191505060405180910390fd5b6000613371856103e563ffffffff61431916565b90506000613385828563ffffffff61431916565b905060006133ab8361339f886103e863ffffffff61431916565b9063ffffffff61438516565b90508082816133b657fe5b04979650505050505050565b606060028251101561341b576040805162461bcd60e51b815260206004820181905260248201527f456c656374726f537761704c6962726172793a20494e56414c49445f50415448604482015290519081900360640190fd5b815167ffffffffffffffff8111801561343357600080fd5b5060405190808252806020026020018201604052801561345d578160200160208202803683370190505b509050828160008151811061346e57fe5b60200260200101818152505060005b6001835103811015613506576000806134c08786858151811061349c57fe5b60200260200101518786600101815181106134b357fe5b60200260200101516143dd565b915091506134e28484815181106134d357fe5b602002602001015183836132d2565b8484600101815181106134f157fe5b6020908102919091010152505060010161347d565b509392505050565b600080600061351d8585613fc7565b604080516bffffffffffffffffffffffff19606094851b811660208084019190915293851b81166034830152825160288184030181526048830184528051908501207fff0000000000000000000000000000000000000000000000000000000000000060688401529a90941b9093166069840152607d8301989098527fbd66f645caa490d6090d3c649129cb143b632b678076973762b574ac1bc9650c609d808401919091528851808403909101815260bd909201909752805196019590952095945050505050565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b17815292518251600094606094938a169392918291908083835b6020831061366b5780518252601f19909201916020918201910161364c565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146136cd576040519150601f19603f3d011682016040523d82523d6000602084013e6136d2565b606091505b509150915081801561370057508051158061370057508080602001905160208110156136fd57600080fd5b50515b61373b5760405162461bcd60e51b81526004018080602001828103825260248152602001806146686024913960400191505060405180910390fd5b505050505050565b60005b60018351038110156139835760008084838151811061376157fe5b602002602001015185846001018151811061377857fe5b60200260200101519150915060006137908383613fc7565b50905060008785600101815181106137a457fe5b60200260200101519050600080836001600160a01b0316866001600160a01b0316146137d2578260006137d6565b6000835b91509150600060028a510388106137ed578861382e565b61382e7f000000000000000000000000203d550ed6fa9dab8a4190720cf9f65138abd15b878c8b6002018151811061382157fe5b602002602001015161350e565b905061385b7f000000000000000000000000203d550ed6fa9dab8a4190720cf9f65138abd15b888861350e565b6001600160a01b031663022c0d9f84848460006040519080825280601f01601f191660200182016040528015613898576020820181803683370190505b506040518563ffffffff1660e01b815260040180858152602001848152602001836001600160a01b03166001600160a01b0316815260200180602001828103825283818151815260200191508051906020019080838360005b838110156139095781810151838201526020016138f1565b50505050905090810190601f1680156139365780820380516001836020036101000a031916815260200191505b5095505050505050600060405180830381600087803b15801561395857600080fd5b505af115801561396c573d6000803e3d6000fd5b505060019099019850613746975050505050505050565b50505050565b60606002825110156139e2576040805162461bcd60e51b815260206004820181905260248201527f456c656374726f537761704c6962726172793a20494e56414c49445f50415448604482015290519081900360640190fd5b815167ffffffffffffffff811180156139fa57600080fd5b50604051908082528060200260200182016040528015613a24578160200160208202803683370190505b5090508281600183510381518110613a3857fe5b60209081029190910101528151600019015b801561350657600080613a7a87866001860381518110613a6657fe5b60200260200101518786815181106134b357fe5b91509150613a9c848481518110613a8d57fe5b60200260200101518383613e2b565b846001850381518110613aab57fe5b6020908102919091010152505060001901613a4a565b60005b60018351038110156132cd57600080848381518110613adf57fe5b6020026020010151858460010181518110613af657fe5b6020026020010151915091506000613b0e8383613fc7565b5090506000613b3e7f000000000000000000000000203d550ed6fa9dab8a4190720cf9f65138abd15b858561350e565b9050600080600080846001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b158015613b7f57600080fd5b505afa158015613b93573d6000803e3d6000fd5b505050506040513d6060811015613ba957600080fd5b5080516020909101516dffffffffffffffffffffffffffff91821693501690506000806001600160a01b038a811690891614613be6578284613be9565b83835b91509150613c47828b6001600160a01b03166370a082318a6040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b15801561188257600080fd5b9550613c548683836132d2565b945050505050600080856001600160a01b0316886001600160a01b031614613c7e57826000613c82565b6000835b91509150600060028c51038a10613c99578a613ccd565b613ccd7f000000000000000000000000203d550ed6fa9dab8a4190720cf9f65138abd15b898e8d6002018151811061382157fe5b604080516000808252602082019283905263022c0d9f60e01b835260248201878152604483018790526001600160a01b038086166064850152608060848501908152845160a48601819052969750908c169563022c0d9f958a958a958a9591949193919260c486019290918190849084905b83811015613d57578181015183820152602001613d3f565b50505050905090810190601f168015613d845780820380516001836020036101000a031916815260200191505b5095505050505050600060405180830381600087803b158015613da657600080fd5b505af1158015613dba573d6000803e3d6000fd5b50506001909b019a50613ac49950505050505050505050565b80820382811115611175576040805162461bcd60e51b815260206004820152601560248201527f64732d6d6174682d7375622d756e646572666c6f770000000000000000000000604482015290519081900360640190fd5b6000808411613e6b5760405162461bcd60e51b815260040180806020018281038252602e81526020018061463a602e913960400191505060405180910390fd5b600083118015613e7b5750600082115b613eb65760405162461bcd60e51b815260040180806020018281038252602a8152602001806145c6602a913960400191505060405180910390fd5b6000613eda6103e8613ece868863ffffffff61431916565b9063ffffffff61431916565b90506000613ef46103e5613ece868963ffffffff613dd316565b9050613f116001828481613f0457fe5b049063ffffffff61438516565b9695505050505050565b6000808411613f5b5760405162461bcd60e51b81526004018080602001828103825260278152602001806145f06027913960400191505060405180910390fd5b600083118015613f6b5750600082115b613fa65760405162461bcd60e51b815260040180806020018281038252602a8152602001806145c6602a913960400191505060405180910390fd5b82613fb7858463ffffffff61431916565b81613fbe57fe5b04949350505050565b600080826001600160a01b0316846001600160a01b0316141561401b5760405162461bcd60e51b815260040180806020018281038252602781526020018061459f6027913960400191505060405180910390fd5b826001600160a01b0316846001600160a01b03161061403b57828461403e565b83835b90925090506001600160a01b03821661409e576040805162461bcd60e51b815260206004820181905260248201527f456c656374726f537761704c6962726172793a205a45524f5f41444452455353604482015290519081900360640190fd5b9250929050565b6040805163e6a4390560e01b81526001600160a01b03888116600483015287811660248301529151600092839283927f000000000000000000000000203d550ed6fa9dab8a4190720cf9f65138abd15b9092169163e6a4390591604480820192602092909190829003018186803b15801561411f57600080fd5b505afa158015614133573d6000803e3d6000fd5b505050506040513d602081101561414957600080fd5b50516001600160a01b031614156141fc57604080516364e329cb60e11b81526001600160a01b038a81166004830152898116602483015291517f000000000000000000000000203d550ed6fa9dab8a4190720cf9f65138abd15b9092169163c9c65396916044808201926020929091908290030181600087803b1580156141cf57600080fd5b505af11580156141e3573d6000803e3d6000fd5b505050506040513d60208110156141f957600080fd5b50505b60008061422a7f000000000000000000000000203d550ed6fa9dab8a4190720cf9f65138abd15b8b8b6143dd565b9150915081600014801561423c575080155b1561424c5787935086925061430c565b6000614259898484613f1b565b90508781116142ac57858110156142a15760405162461bcd60e51b815260040180806020018281038252602881526020018061454a6028913960400191505060405180910390fd5b88945092508261430a565b60006142b9898486613f1b565b9050898111156142c557fe5b878110156143045760405162461bcd60e51b81526004018080602001828103825260288152602001806144cc6028913960400191505060405180910390fd5b94508793505b505b5050965096945050505050565b60008115806143345750508082028282828161433157fe5b04145b611175576040805162461bcd60e51b815260206004820152601460248201527f64732d6d6174682d6d756c2d6f766572666c6f77000000000000000000000000604482015290519081900360640190fd5b80820182811015611175576040805162461bcd60e51b815260206004820152601460248201527f64732d6d6174682d6164642d6f766572666c6f77000000000000000000000000604482015290519081900360640190fd5b60008060006143ec8585613fc7565b5090506000806143fd88888861350e565b6001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b15801561443557600080fd5b505afa158015614449573d6000803e3d6000fd5b505050506040513d606081101561445f57600080fd5b5080516020909101516dffffffffffffffffffffffffffff91821693501690506001600160a01b038781169084161461449957808261449c565b81815b9099909850965050505050505056fe456c656374726f53776170526f757465723a2045585049524544000000000000456c656374726f53776170526f757465723a20494e53554646494349454e545f415f414d4f554e54456c656374726f537761704c6962726172793a20494e53554646494349454e545f494e5055545f414d4f554e54456c656374726f53776170526f757465723a204558434553534956455f494e5055545f414d4f554e54456c656374726f53776170526f757465723a20494e53554646494349454e545f425f414d4f554e54456c656374726f53776170526f757465723a20494e53554646494349454e545f4f55545055545f414d4f554e54456c656374726f537761704c6962726172793a204944454e544943414c5f414444524553534553456c656374726f537761704c6962726172793a20494e53554646494349454e545f4c4951554944495459456c656374726f537761704c6962726172793a20494e53554646494349454e545f414d4f554e545472616e7366657248656c7065723a204554485f5452414e534645525f4641494c4544456c656374726f537761704c6962726172793a20494e53554646494349454e545f4f55545055545f414d4f554e545472616e7366657248656c7065723a205452414e534645525f46524f4d5f4641494c4544a26469706673582212209b434c8b8ad3770f83faa820bcecfb1f074d5d979deb3ee69dbfe5cc0a32368364736f6c63430006060033