Contract Address Details

0xEdb59b2938f57FA22b014B236e3096e516F8F1Fe

Contract Name
UniswapV2Router02
Creator
0xce0cc7–185412 at 0x25f5ee–3f979c
Balance
0 mADA
Tokens
Fetching tokens...
Transactions
45 Transactions
Transfers
46 Transfers
Gas Used
28,742,225
Last Balance Update
26489649
Contract name:
UniswapV2Router02




Optimization enabled
true
Compiler version
v0.8.7+commit.e28d00a7




Optimization runs
200
EVM Version
default




Verified at
2022-11-14T11:02:19.520013Z

Constructor Arguments

0000000000000000000000007a1d8868fdd3af7ea3025edc9c93255047906c6e0000000000000000000000009129b67f17bfd7a20bd573fa72cc410ddde9ee35

Arg [0] (address) : 0x7a1d8868fdd3af7ea3025edc9c93255047906c6e
Arg [1] (address) : 0x9129b67f17bfd7a20bd573fa72cc410ddde9ee35

              

Contract source code

// File: contracts/interfaces/IWETH.sol


pragma solidity ^0.8.4;

interface IWETH {
    function deposit() external payable;
    function transfer(address to, uint value) external returns (bool);
    function withdraw(uint) external;
}

// File: contracts/interfaces/IERC20.sol


pragma solidity ^0.8.4;

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

// File: contracts/libraries/SafeMath.sol


pragma solidity ^0.8.4;

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

// File: contracts/interfaces/IUniswapV2Pair.sol


pragma solidity ^0.8.4;

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

// File: contracts/interfaces/IUniswapV2Router01.sol


pragma solidity ^0.8.4;

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

// File: contracts/interfaces/IUniswapV2Router02.sol


pragma solidity ^0.8.4;


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

// File: contracts/lib/contracts/libraries/TransferHelper.sol


pragma solidity ^0.8.4;
// 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::safeApprove: 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::safeTransfer: 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::transferFrom: transferFrom failed'
        );
    }

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

// File: contracts/interfaces/IUniswapV2Factory.sol


pragma solidity ^0.8.4;

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

// File: contracts/libraries/UniswapV2Library.sol


pragma solidity ^0.8.7;




library UniswapV2Library {
    using SafeMath for uint;

    // returns sorted token addresses, used to handle return values from pairs sorted in this order
    function sortTokens(address tokenA, address tokenB)
        internal
        pure
        returns (address token0, address token1)
    {
        require(tokenA != tokenB, "UniswapV2Library: IDENTICAL_ADDRESSES");
        (token0, token1) = tokenA < tokenB
            ? (tokenA, tokenB)
            : (tokenB, tokenA);
        require(token0 != address(0), "UniswapV2Library: ZERO_ADDRESS");
    }

    // calculates the CREATE2 address for a pair without making any external calls
    // 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'3c5319d303f693b43b3b2d91e82f469bce186bfd2ee928a923b92de983637e5f' // init code hash
    //         ))));
    // }
    function pairFor(
        address factory,
        address tokenA,
        address tokenB
    ) internal view returns (address pair) {
        pair = IUniswapV2Factory(factory).getPair(tokenA, tokenB);
    }

    // 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);
        address _pair = pairFor(factory, tokenA, tokenB);
        (uint reserve0, uint reserve1, ) = IUniswapV2Pair(_pair).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, "UniswapV2Library: INSUFFICIENT_AMOUNT");
        require(
            reserveA > 0 && reserveB > 0,
            "UniswapV2Library: 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, "UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT");
        require(
            reserveIn > 0 && reserveOut > 0,
            "UniswapV2Library: INSUFFICIENT_LIQUIDITY"
        );
        uint amountInWithFee = amountIn.mul(997);
        uint numerator = amountInWithFee.mul(reserveOut);
        uint denominator = reserveIn.mul(1000).add(amountInWithFee);
        amountOut = numerator / denominator;
    }

    // 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, "UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT");
        require(
            reserveIn > 0 && reserveOut > 0,
            "UniswapV2Library: INSUFFICIENT_LIQUIDITY"
        );
        uint numerator = reserveIn.mul(amountOut).mul(1000);
        uint denominator = reserveOut.sub(amountOut).mul(997);
        amountIn = (numerator / denominator).add(1);
    }

    // performs chained getAmountOut calculations on any number of pairs
    function getAmountsOut(
        address factory,
        uint amountIn,
        address[] memory path
    ) internal view returns (uint[] memory amounts) {
        require(path.length >= 2, "UniswapV2Library: INVALID_PATH");
        amounts = new uint[](path.length);
        amounts[0] = amountIn;
        for (uint i; i < path.length - 1; i++) {
            (uint reserveIn, uint reserveOut) = getReserves(
                factory,
                path[i],
                path[i + 1]
            );
            amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut);
        }
    }

    // performs chained getAmountIn calculations on any number of pairs
    function getAmountsIn(
        address factory,
        uint amountOut,
        address[] memory path
    ) internal view returns (uint[] memory amounts) {
        require(path.length >= 2, "UniswapV2Library: INVALID_PATH");
        amounts = new uint[](path.length);
        amounts[amounts.length - 1] = amountOut;
        for (uint i = path.length - 1; i > 0; i--) {
            (uint reserveIn, uint reserveOut) = getReserves(
                factory,
                path[i - 1],
                path[i]
            );
            amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut);
        }
    }
}

// File: contracts/UniswapV2Router02.sol


pragma solidity ^0.8.7;








contract UniswapV2Router02 {
    using SafeMath for uint256;

    address public immutable factory;
    address public immutable WETH;

    modifier ensure(uint256 deadline) {
        require(deadline >= block.timestamp, "UniswapV2Router: EXPIRED");
        _;
    }

    constructor(address _factory, address _WETH) {
        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,
        uint256 amountADesired,
        uint256 amountBDesired,
        uint256 amountAMin,
        uint256 amountBMin
    ) internal virtual returns (uint256 amountA, uint256 amountB) {
        // create the pair if it doesn't exist yet
        if (IUniswapV2Factory(factory).getPair(tokenA, tokenB) == address(0)) {
            IUniswapV2Factory(factory).createPair(tokenA, tokenB);
        }
        (uint256 reserveA, uint256 reserveB) = UniswapV2Library.getReserves(
            factory,
            tokenA,
            tokenB
        );
        if (reserveA == 0 && reserveB == 0) {
            (amountA, amountB) = (amountADesired, amountBDesired);
        } else {
            uint256 amountBOptimal = UniswapV2Library.quote(
                amountADesired,
                reserveA,
                reserveB
            );
            if (amountBOptimal <= amountBDesired) {
                require(
                    amountBOptimal >= amountBMin,
                    "UniswapV2Router: INSUFFICIENT_B_AMOUNT"
                );
                (amountA, amountB) = (amountADesired, amountBOptimal);
            } else {
                uint256 amountAOptimal = UniswapV2Library.quote(
                    amountBDesired,
                    reserveB,
                    reserveA
                );
                assert(amountAOptimal <= amountADesired);
                require(
                    amountAOptimal >= amountAMin,
                    "UniswapV2Router: INSUFFICIENT_A_AMOUNT"
                );
                (amountA, amountB) = (amountAOptimal, amountBDesired);
            }
        }
    }

    function addLiquidity(
        address tokenA,
        address tokenB,
        uint256 amountADesired,
        uint256 amountBDesired,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    )
        external
        virtual
        ensure(deadline)
        returns (
            uint256 amountA,
            uint256 amountB,
            uint256 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,
        uint256 amountTokenDesired,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    )
        external
        payable
        virtual
        ensure(deadline)
        returns (
            uint256 amountToken,
            uint256 amountETH,
            uint256 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,
        uint256 liquidity,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    )
        public
        virtual
        ensure(deadline)
        returns (uint256 amountA, uint256 amountB)
    {   
        address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
        IUniswapV2Pair(pair).transferFrom(msg.sender, pair, liquidity); // send liquidity to pair
        
        (uint256 amount0, uint256 amount1) = IUniswapV2Pair(pair).burn(to);
        (address token0, ) = UniswapV2Library.sortTokens(tokenA, tokenB);
        (amountA, amountB) = tokenA == token0
            ? (amount0, amount1)
            : (amount1, amount0);
        require(
            amountA >= amountAMin,
            "UniswapV2Router: INSUFFICIENT_A_AMOUNT"
        );
        require(
            amountB >= amountBMin,
            "UniswapV2Router: INSUFFICIENT_B_AMOUNT"
        );
    }

    function removeLiquidityETH(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    )
        public
        virtual
        ensure(deadline)
        returns (uint256 amountToken, uint256 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,
        uint256 liquidity,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external virtual returns (uint256 amountA, uint256 amountB) {
        address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
        uint256 value = approveMax ? type(uint256).max : 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,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external virtual returns (uint256 amountToken, uint256 amountETH) {
        address pair = UniswapV2Library.pairFor(factory, token, WETH);
        uint256 value = approveMax ? type(uint256).max : 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,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    ) public virtual ensure(deadline) returns (uint256 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,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external virtual returns (uint256 amountETH) {
        address pair = UniswapV2Library.pairFor(factory, token, WETH);
        uint256 value = approveMax ? type(uint256).max : liquidity;
        IUniswapV2Pair(pair).permit(
            msg.sender,
            address(this),
            value,
            deadline,
            v,
            r,
            s
        );
        amountETH = removeLiquidityETHSupportingFeeOnTransferTokens(
            token,
            liquidity,
            amountTokenMin,
            amountETHMin,
            to,
            deadline
        );
    }

    // **** SWAP ****_swap
    // requires the initial amount to have already been sent to the first pair
    function _swap(
        uint256[] memory amounts,
        address[] memory path,
        address _to
    ) internal virtual {
        for (uint256 i; i < path.length - 1; i++) {
            (address input, address output) = (path[i], path[i + 1]);
            (address token0, ) = UniswapV2Library.sortTokens(input, output);
            uint256 amountOut = amounts[i + 1];
            (uint256 amount0Out, uint256 amount1Out) = input == token0
                ? (uint256(0), amountOut)
                : (amountOut, uint256(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(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        address admin,
        uint256 deadline
    ) external virtual ensure(deadline) returns (uint256[] memory amounts) {
        amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path);
        amounts[1] = amounts[1]*(3);
        amounts[1] = amounts[1]/(1000);
        amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path);
        require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
        uint cutAmount = amounts[0] - ((amounts[0])*3)/1000;
        uint remainingAmount = ((amounts[0])*3)/1000;
        require(
            amounts[amounts.length - 1] >= amountOutMin,
            "UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT"
        );
        // TransferHelper.safeTransferFrom(
        //     path[0],
        //     msg.sender,
        //     UniswapV2Library.pairFor(factory, path[0], path[1]),
        //     amounts[0]
        // );
         TransferHelper.safeTransferFrom(path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), cutAmount);
        TransferHelper.safeTransferFrom(path[0], msg.sender, admin, remainingAmount); ///////////
        _swap(amounts, path, to);
    }

    function swapTokensForExactTokens(
        uint256 amountOut,
        uint256 amountInMax,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external virtual ensure(deadline) returns (uint256[] memory amounts) {
        amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);
        require(
            amounts[0] <= amountInMax,
            "UniswapV2Router: EXCESSIVE_INPUT_AMOUNT"
        );
        TransferHelper.safeTransferFrom(
            path[0],
            msg.sender,
            UniswapV2Library.pairFor(factory, path[0], path[1]),
            amounts[0]
        );
        _swap(amounts, path, to);
    }
    
    function swapExactETHForTokens(
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    )
        external
        payable
        virtual
        ensure(deadline)
        returns (uint256[] memory amounts)
    {
        require(path[0] == WETH, "UniswapV2Router: INVALID_PATH");
        amounts = UniswapV2Library.getAmountsOut(factory, msg.value, path);
        require(
            amounts[amounts.length - 1] >= amountOutMin,
            "UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT"
        );
        IWETH(WETH).deposit{value: amounts[0]}();
        assert(
            IWETH(WETH).transfer(
                UniswapV2Library.pairFor(factory, path[0], path[1]),
                amounts[0]
            )
        );
        _swap(amounts, path, to);
    }

    function swapTokensForExactETH(
        uint256 amountOut,
        uint256 amountInMax,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external virtual ensure(deadline) returns (uint256[] memory amounts) {
        require(path[path.length - 1] == WETH, "UniswapV2Router: INVALID_PATH");
        amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);
        require(
            amounts[0] <= amountInMax,
            "UniswapV2Router: EXCESSIVE_INPUT_AMOUNT"
        );
        TransferHelper.safeTransferFrom(
            path[0],
            msg.sender,
            UniswapV2Library.pairFor(factory, path[0], path[1]),
            amounts[0]
        );
        _swap(amounts, path, address(this));
        IWETH(WETH).withdraw(amounts[amounts.length - 1]);
        TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);
    }

    function swapExactTokensForETH(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external virtual ensure(deadline) returns (uint256[] memory amounts) {
        require(path[path.length - 1] == WETH, "UniswapV2Router: INVALID_PATH");
        amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path);
        require(
            amounts[amounts.length - 1] >= amountOutMin,
            "UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT"
        );
        
        TransferHelper.safeTransferFrom(
            path[0],
            msg.sender,
            UniswapV2Library.pairFor(factory, path[0], path[1]),
            amounts[0]
        );
        _swap(amounts, path, address(this));
        IWETH(WETH).withdraw(amounts[amounts.length - 1]);
        TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);
    }

    function swapETHForExactTokens(
        uint256 amountOut,
        address[] calldata path,
        address to,
        uint256 deadline
    )
        external
        payable
        virtual
        ensure(deadline)
        returns (uint256[] memory amounts)
    {
        require(path[0] == WETH, "UniswapV2Router: INVALID_PATH");
        amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);
        require(
            amounts[0] <= msg.value,
            "UniswapV2Router: EXCESSIVE_INPUT_AMOUNT"
        );
        IWETH(WETH).deposit{value: amounts[0]}();
        assert(
            IWETH(WETH).transfer(
                UniswapV2Library.pairFor(factory, path[0], path[1]),
                amounts[0]
            )
        );
        _swap(amounts, path, to);
        // refund dust eth, if any
        if (msg.value > amounts[0])
            TransferHelper.safeTransferETH(msg.sender, msg.value - amounts[0]);
    }

    // **** SWAP (supporting fee-on-transfer tokens) ****
    // requires the initial amount to have already been sent to the first pair
    function _swapSupportingFeeOnTransferTokens(
        address[] memory path,
        address _to
    ) internal virtual {
        for (uint256 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)
            );
            uint256 amountInput;
            uint256 amountOutput;
            {
                // scope to avoid stack too deep errors
                (uint256 reserve0, uint256 reserve1, ) = pair.getReserves();
                (uint256 reserveInput, uint256 reserveOutput) = input == token0
                    ? (reserve0, reserve1)
                    : (reserve1, reserve0);
                amountInput = IERC20(input).balanceOf(address(pair)).sub(
                    reserveInput
                );
                amountOutput = UniswapV2Library.getAmountOut(
                    amountInput,
                    reserveInput,
                    reserveOutput
                );
            }
            (uint256 amount0Out, uint256 amount1Out) = input == token0
                ? (uint256(0), amountOutput)
                : (amountOutput, uint256(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(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external virtual ensure(deadline) {
        TransferHelper.safeTransferFrom(
            path[0],
            msg.sender,
            UniswapV2Library.pairFor(factory, path[0], path[1]),
            amountIn
        );
        uint256 balanceBefore = IERC20(path[path.length - 1]).balanceOf(to);
        _swapSupportingFeeOnTransferTokens(path, to);
        require(
            IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore) >=
                amountOutMin,
            "UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT"
        );
    }

    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external payable virtual ensure(deadline) {
        require(path[0] == WETH, "UniswapV2Router: INVALID_PATH");
        uint256 amountIn = msg.value;
        IWETH(WETH).deposit{value: amountIn}();
        assert(
            IWETH(WETH).transfer(
                UniswapV2Library.pairFor(factory, path[0], path[1]),
                amountIn
            )
        );
        uint256 balanceBefore = IERC20(path[path.length - 1]).balanceOf(to);
        _swapSupportingFeeOnTransferTokens(path, to);
        require(
            IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore) >=
                amountOutMin,
            "UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT"
        );
    }

    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external virtual ensure(deadline) {
        require(path[path.length - 1] == WETH, "UniswapV2Router: INVALID_PATH");
        TransferHelper.safeTransferFrom(
            path[0],
            msg.sender,
            UniswapV2Library.pairFor(factory, path[0], path[1]),
            amountIn
        );
        _swapSupportingFeeOnTransferTokens(path, address(this));
        uint256 amountOut = IERC20(WETH).balanceOf(address(this));
        require(
            amountOut >= amountOutMin,
            "UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT"
        );
        IWETH(WETH).withdraw(amountOut);
        TransferHelper.safeTransferETH(to, amountOut);
    }

    // **** LIBRARY FUNCTIONS ****
    function quote(
        uint256 amountA,
        uint256 reserveA,
        uint256 reserveB
    ) public pure virtual returns (uint256 amountB) {
        return UniswapV2Library.quote(amountA, reserveA, reserveB);
    }

    function getAmountOut(
        uint256 amountIn,
        uint256 reserveIn,
        uint256 reserveOut
    ) public view virtual returns (uint256 amountOut) {
        return UniswapV2Library.getAmountOut(amountIn, reserveIn, reserveOut);
    }

    function getAmountIn(
        uint256 amountOut,
        uint256 reserveIn,
        uint256 reserveOut
    ) public view virtual returns (uint256 amountIn) {
        return UniswapV2Library.getAmountIn(amountOut, reserveIn, reserveOut);
    }

    function getAmountsOut(uint256 amountIn, address[] memory path)
        public
        view
        virtual
        returns (uint256[] memory amounts)
    {
        return UniswapV2Library.getAmountsOut(factory, amountIn, path);
    }

    function getAmountsIn(uint256 amountOut, address[] memory path)
        public
        view
        virtual
        returns (uint256[] memory amounts)
    {
        return UniswapV2Library.getAmountsIn(factory, amountOut, path);
    }
}
        

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":"view","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":"view","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":"address","name":"admin","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

0x60806040526004361061014f5760003560e01c806392ebcc0e116100b6578063c45a01551161006f578063c45a01551461041a578063d06ca61f1461044e578063ded9382a1461046e578063e8e337001461048e578063f305d719146104c9578063fb3bdb41146104dc57600080fd5b806392ebcc0e1461033b578063ad5c46481461035b578063ad615dec146103a7578063af2979eb146103c7578063b6f9de95146103e7578063baa2abde146103fa57600080fd5b80635b0d5984116101085780635b0d5984146102885780635c11d795146102a8578063791ac947146102c85780637ff36ab5146102e857806385f8c259146102fb5780638803dbee1461031b57600080fd5b806302751cec14610193578063054d50d4146101cd57806318cbafe5146101fb5780631f00ca74146102285780632195995c146102485780634a25d94a1461026857600080fd5b3661018e57336001600160a01b037f0000000000000000000000009129b67f17bfd7a20bd573fa72cc410ddde9ee35161461018c5761018c6144b7565b005b600080fd5b34801561019f57600080fd5b506101b36101ae366004613d21565b6104ef565b604080519283526020830191909152015b60405180910390f35b3480156101d957600080fd5b506101ed6101e83660046140f8565b6105eb565b6040519081526020016101c4565b34801561020757600080fd5b5061021b610216366004614085565b610600565b6040516101c49190614181565b34801561023457600080fd5b5061021b610243366004613f02565b61092f565b34801561025457600080fd5b506101b3610263366004613bfb565b610965565b34801561027457600080fd5b5061021b610283366004614085565b610a3e565b34801561029457600080fd5b506101ed6102a3366004613d7f565b610b7c565b3480156102b457600080fd5b5061018c6102c3366004614085565b610c69565b3480156102d457600080fd5b5061018c6102e3366004614085565b610ee9565b61021b6102f6366004613e9b565b61111f565b34801561030757600080fd5b506101ed6103163660046140f8565b611447565b34801561032757600080fd5b5061021b610336366004614085565b611454565b34801561034757600080fd5b5061021b610356366004613ffe565b611566565b34801561036757600080fd5b5061038f7f0000000000000000000000009129b67f17bfd7a20bd573fa72cc410ddde9ee3581565b6040516001600160a01b0390911681526020016101c4565b3480156103b357600080fd5b506101ed6103c23660046140f8565b611908565b3480156103d357600080fd5b506101ed6103e2366004613d21565b611915565b61018c6103f5366004613e9b565b611a73565b34801561040657600080fd5b506101b3610415366004613b89565b611dcb565b34801561042657600080fd5b5061038f7f0000000000000000000000007a1d8868fdd3af7ea3025edc9c93255047906c6e81565b34801561045a57600080fd5b5061021b610469366004613f02565b611fb1565b34801561047a57600080fd5b506101b3610489366004613d7f565b611fde565b34801561049a57600080fd5b506104ae6104a9366004613ca5565b6120d1565b604080519384526020840192909252908201526060016101c4565b6104ae6104d7366004613d21565b6121de565b61021b6104ea366004613e9b565b61245b565b600080824281101561051c5760405162461bcd60e51b8152600401610513906141c5565b60405180910390fd5b61054b897f0000000000000000000000009129b67f17bfd7a20bd573fa72cc410ddde9ee358a8a8a308a611dcb565b909350915061055b8986856127b9565b604051632e1a7d4d60e01b8152600481018390527f0000000000000000000000009129b67f17bfd7a20bd573fa72cc410ddde9ee356001600160a01b031690632e1a7d4d90602401600060405180830381600087803b1580156105bd57600080fd5b505af11580156105d1573d6000803e3d6000fd5b505050506105df85836128ea565b50965096945050505050565b60006105f88484846129c9565b949350505050565b606081428110156106235760405162461bcd60e51b8152600401610513906141c5565b6001600160a01b037f0000000000000000000000009129b67f17bfd7a20bd573fa72cc410ddde9ee3516868661065a600182614442565b818110610669576106696144e3565b905060200201602081019061067e9190613b48565b6001600160a01b0316146106a45760405162461bcd60e51b815260040161051390614242565b6107027f0000000000000000000000007a1d8868fdd3af7ea3025edc9c93255047906c6e89888880806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250612aa792505050565b91508682600184516107149190614442565b81518110610724576107246144e3565b6020026020010151101561074a5760405162461bcd60e51b81526004016105139061434e565b61080f86866000818110610760576107606144e3565b90506020020160208101906107759190613b48565b336107ef7f0000000000000000000000007a1d8868fdd3af7ea3025edc9c93255047906c6e8a8a60008181106107ad576107ad6144e3565b90506020020160208101906107c29190613b48565b8b8b60018181106107d5576107d56144e3565b90506020020160208101906107ea9190613b48565b612c32565b85600081518110610802576108026144e3565b6020026020010151612cb7565b61084e82878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250309250612df5915050565b7f0000000000000000000000009129b67f17bfd7a20bd573fa72cc410ddde9ee356001600160a01b0316632e1a7d4d836001855161088c9190614442565b8151811061089c5761089c6144e3565b60200260200101516040518263ffffffff1660e01b81526004016108c291815260200190565b600060405180830381600087803b1580156108dc57600080fd5b505af11580156108f0573d6000803e3d6000fd5b505050506109248483600185516109079190614442565b81518110610917576109176144e3565b60200260200101516128ea565b509695505050505050565b606061095c7f0000000000000000000000007a1d8868fdd3af7ea3025edc9c93255047906c6e8484612ffd565b90505b92915050565b60008060006109957f0000000000000000000000007a1d8868fdd3af7ea3025edc9c93255047906c6e8f8f612c32565b90506000876109a4578c6109a8565b6000195b60405163d505accf60e01b81529091506001600160a01b0383169063d505accf906109e3903390309086908f908e908e908e90600401614140565b600060405180830381600087803b1580156109fd57600080fd5b505af1158015610a11573d6000803e3d6000fd5b50505050610a248f8f8f8f8f8f8f611dcb565b809450819550505050509b509b9950505050505050505050565b60608142811015610a615760405162461bcd60e51b8152600401610513906141c5565b6001600160a01b037f0000000000000000000000009129b67f17bfd7a20bd573fa72cc410ddde9ee35168686610a98600182614442565b818110610aa757610aa76144e3565b9050602002016020810190610abc9190613b48565b6001600160a01b031614610ae25760405162461bcd60e51b815260040161051390614242565b610b407f0000000000000000000000007a1d8868fdd3af7ea3025edc9c93255047906c6e89888880806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250612ffd92505050565b91508682600081518110610b5657610b566144e3565b6020026020010151111561074a5760405162461bcd60e51b8152600401610513906142c1565b600080610bca7f0000000000000000000000007a1d8868fdd3af7ea3025edc9c93255047906c6e8d7f0000000000000000000000009129b67f17bfd7a20bd573fa72cc410ddde9ee35612c32565b9050600086610bd9578b610bdd565b6000195b60405163d505accf60e01b81529091506001600160a01b0383169063d505accf90610c18903390309086908e908d908d908d90600401614140565b600060405180830381600087803b158015610c3257600080fd5b505af1158015610c46573d6000803e3d6000fd5b50505050610c588d8d8d8d8d8d611915565b9d9c50505050505050505050505050565b8042811015610c8a5760405162461bcd60e51b8152600401610513906141c5565b610d1b85856000818110610ca057610ca06144e3565b9050602002016020810190610cb59190613b48565b33610d157f0000000000000000000000007a1d8868fdd3af7ea3025edc9c93255047906c6e89896000818110610ced57610ced6144e3565b9050602002016020810190610d029190613b48565b8a8a60018181106107d5576107d56144e3565b8a612cb7565b60008585610d2a600182614442565b818110610d3957610d396144e3565b9050602002016020810190610d4e9190613b48565b6040516370a0823160e01b81526001600160a01b03868116600483015291909116906370a082319060240160206040518083038186803b158015610d9157600080fd5b505afa158015610da5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dc99190613e82565b9050610e0986868080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525088925061317e915050565b86610ec1828888610e1b600182614442565b818110610e2a57610e2a6144e3565b9050602002016020810190610e3f9190613b48565b6040516370a0823160e01b81526001600160a01b03898116600483015291909116906370a08231906024015b60206040518083038186803b158015610e8357600080fd5b505afa158015610e97573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ebb9190613e82565b90613426565b1015610edf5760405162461bcd60e51b81526004016105139061434e565b5050505050505050565b8042811015610f0a5760405162461bcd60e51b8152600401610513906141c5565b6001600160a01b037f0000000000000000000000009129b67f17bfd7a20bd573fa72cc410ddde9ee35168585610f41600182614442565b818110610f5057610f506144e3565b9050602002016020810190610f659190613b48565b6001600160a01b031614610f8b5760405162461bcd60e51b815260040161051390614242565b610fa185856000818110610ca057610ca06144e3565b610fdf85858080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525030925061317e915050565b6040516370a0823160e01b81523060048201526000907f0000000000000000000000009129b67f17bfd7a20bd573fa72cc410ddde9ee356001600160a01b0316906370a082319060240160206040518083038186803b15801561104157600080fd5b505afa158015611055573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110799190613e82565b90508681101561109b5760405162461bcd60e51b81526004016105139061434e565b604051632e1a7d4d60e01b8152600481018290527f0000000000000000000000009129b67f17bfd7a20bd573fa72cc410ddde9ee356001600160a01b031690632e1a7d4d90602401600060405180830381600087803b1580156110fd57600080fd5b505af1158015611111573d6000803e3d6000fd5b50505050610edf84826128ea565b606081428110156111425760405162461bcd60e51b8152600401610513906141c5565b7f0000000000000000000000009129b67f17bfd7a20bd573fa72cc410ddde9ee356001600160a01b03168686600081811061117f5761117f6144e3565b90506020020160208101906111949190613b48565b6001600160a01b0316146111ba5760405162461bcd60e51b815260040161051390614242565b6112187f0000000000000000000000007a1d8868fdd3af7ea3025edc9c93255047906c6e34888880806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250612aa792505050565b915086826001845161122a9190614442565b8151811061123a5761123a6144e3565b602002602001015110156112605760405162461bcd60e51b81526004016105139061434e565b7f0000000000000000000000009129b67f17bfd7a20bd573fa72cc410ddde9ee356001600160a01b031663d0e30db0836000815181106112a2576112a26144e3565b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b1580156112d557600080fd5b505af11580156112e9573d6000803e3d6000fd5b50505050507f0000000000000000000000009129b67f17bfd7a20bd573fa72cc410ddde9ee356001600160a01b031663a9059cbb6113547f0000000000000000000000007a1d8868fdd3af7ea3025edc9c93255047906c6e89896000818110610ced57610ced6144e3565b84600081518110611367576113676144e3565b60200260200101516040518363ffffffff1660e01b81526004016113a09291906001600160a01b03929092168252602082015260400190565b602060405180830381600087803b1580156113ba57600080fd5b505af11580156113ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113f29190613e15565b6113fe576113fe6144b7565b61143d82878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250899250612df5915050565b5095945050505050565b60006105f884848461347c565b606081428110156114775760405162461bcd60e51b8152600401610513906141c5565b6114d57f0000000000000000000000007a1d8868fdd3af7ea3025edc9c93255047906c6e89888880806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250612ffd92505050565b915086826000815181106114eb576114eb6144e3565b602002602001015111156115115760405162461bcd60e51b8152600401610513906142c1565b61152786866000818110610760576107606144e3565b61092482878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250899250612df5915050565b606081428110156115895760405162461bcd60e51b8152600401610513906141c5565b6115e77f0000000000000000000000007a1d8868fdd3af7ea3025edc9c93255047906c6e8a898980806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250612aa792505050565b9150816001815181106115fc576115fc6144e3565b602002602001015160036116109190614423565b82600181518110611623576116236144e3565b6020026020010181815250506103e882600181518110611645576116456144e3565b60200260200101516116579190614401565b8260018151811061166a5761166a6144e3565b6020026020010181815250506116d47f0000000000000000000000007a1d8868fdd3af7ea3025edc9c93255047906c6e8a898980806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250612aa792505050565b91508782600184516116e69190614442565b815181106116f6576116f66144e3565b6020026020010151101561171c5760405162461bcd60e51b81526004016105139061434e565b60006103e883600081518110611734576117346144e3565b602002602001015160036117489190614423565b6117529190614401565b83600081518110611765576117656144e3565b60200260200101516117779190614442565b905060006103e884600081518110611791576117916144e3565b602002602001015160036117a59190614423565b6117af9190614401565b90508984600186516117c19190614442565b815181106117d1576117d16144e3565b602002602001015110156117f75760405162461bcd60e51b81526004016105139061434e565b6118888989600081811061180d5761180d6144e3565b90506020020160208101906118229190613b48565b336118827f0000000000000000000000007a1d8868fdd3af7ea3025edc9c93255047906c6e8d8d600081811061185a5761185a6144e3565b905060200201602081019061186f9190613b48565b8e8e60018181106107d5576107d56144e3565b85612cb7565b6118bb8989600081811061189e5761189e6144e3565b90506020020160208101906118b39190613b48565b338884612cb7565b6118fa848a8a808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508c9250612df5915050565b505050979650505050505050565b60006105f8848484613555565b600081428110156119385760405162461bcd60e51b8152600401610513906141c5565b611967887f0000000000000000000000009129b67f17bfd7a20bd573fa72cc410ddde9ee358989893089611dcb565b6040516370a0823160e01b81523060048201529093506119ef9150899086906001600160a01b038316906370a082319060240160206040518083038186803b1580156119b257600080fd5b505afa1580156119c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119ea9190613e82565b6127b9565b604051632e1a7d4d60e01b8152600481018390527f0000000000000000000000009129b67f17bfd7a20bd573fa72cc410ddde9ee356001600160a01b031690632e1a7d4d90602401600060405180830381600087803b158015611a5157600080fd5b505af1158015611a65573d6000803e3d6000fd5b5050505061092484836128ea565b8042811015611a945760405162461bcd60e51b8152600401610513906141c5565b7f0000000000000000000000009129b67f17bfd7a20bd573fa72cc410ddde9ee356001600160a01b031685856000818110611ad157611ad16144e3565b9050602002016020810190611ae69190613b48565b6001600160a01b031614611b0c5760405162461bcd60e51b815260040161051390614242565b60003490507f0000000000000000000000009129b67f17bfd7a20bd573fa72cc410ddde9ee356001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b158015611b6c57600080fd5b505af1158015611b80573d6000803e3d6000fd5b50505050507f0000000000000000000000009129b67f17bfd7a20bd573fa72cc410ddde9ee356001600160a01b031663a9059cbb611beb7f0000000000000000000000007a1d8868fdd3af7ea3025edc9c93255047906c6e89896000818110610ced57610ced6144e3565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260248101849052604401602060405180830381600087803b158015611c3357600080fd5b505af1158015611c47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c6b9190613e15565b611c7757611c776144b7565b60008686611c86600182614442565b818110611c9557611c956144e3565b9050602002016020810190611caa9190613b48565b6040516370a0823160e01b81526001600160a01b03878116600483015291909116906370a082319060240160206040518083038186803b158015611ced57600080fd5b505afa158015611d01573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d259190613e82565b9050611d6587878080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525089925061317e915050565b87610ec1828989611d77600182614442565b818110611d8657611d866144e3565b9050602002016020810190611d9b9190613b48565b6040516370a0823160e01b81526001600160a01b038a8116600483015291909116906370a0823190602401610e6b565b6000808242811015611def5760405162461bcd60e51b8152600401610513906141c5565b6000611e1c7f0000000000000000000000007a1d8868fdd3af7ea3025edc9c93255047906c6e8c8c612c32565b6040516323b872dd60e01b81523360048201526001600160a01b03821660248201819052604482018c90529192506323b872dd90606401602060405180830381600087803b158015611e6d57600080fd5b505af1158015611e81573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ea59190613e15565b5060405163226bf2d160e21b81526001600160a01b03878116600483015260009182918416906389afcb44906024016040805180830381600087803b158015611eed57600080fd5b505af1158015611f01573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f259190613fda565b915091506000611f358e8e6135f5565b509050806001600160a01b03168e6001600160a01b031614611f58578183611f5b565b82825b90975095508a871015611f805760405162461bcd60e51b815260040161051390614308565b89861015611fa05760405162461bcd60e51b8152600401610513906141fc565b505050505097509795505050505050565b606061095c7f0000000000000000000000007a1d8868fdd3af7ea3025edc9c93255047906c6e8484612aa7565b600080600061202e7f0000000000000000000000007a1d8868fdd3af7ea3025edc9c93255047906c6e8e7f0000000000000000000000009129b67f17bfd7a20bd573fa72cc410ddde9ee35612c32565b905060008761203d578c612041565b6000195b60405163d505accf60e01b81529091506001600160a01b0383169063d505accf9061207c903390309086908f908e908e908e90600401614140565b600060405180830381600087803b15801561209657600080fd5b505af11580156120aa573d6000803e3d6000fd5b505050506120bc8e8e8e8e8e8e6104ef565b909f909e509c50505050505050505050505050565b600080600083428110156120f75760405162461bcd60e51b8152600401610513906141c5565b6121058c8c8c8c8c8c6136ed565b909450925060006121377f0000000000000000000000007a1d8868fdd3af7ea3025edc9c93255047906c6e8e8e612c32565b90506121458d338388612cb7565b6121518c338387612cb7565b6040516335313c2160e11b81526001600160a01b038881166004830152821690636a62784290602401602060405180830381600087803b15801561219457600080fd5b505af11580156121a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121cc9190613e82565b92505050985098509895505050505050565b600080600083428110156122045760405162461bcd60e51b8152600401610513906141c5565b6122328a7f0000000000000000000000009129b67f17bfd7a20bd573fa72cc410ddde9ee358b348c8c6136ed565b909450925060006122847f0000000000000000000000007a1d8868fdd3af7ea3025edc9c93255047906c6e8c7f0000000000000000000000009129b67f17bfd7a20bd573fa72cc410ddde9ee35612c32565b90506122928b338388612cb7565b7f0000000000000000000000009129b67f17bfd7a20bd573fa72cc410ddde9ee356001600160a01b031663d0e30db0856040518263ffffffff1660e01b81526004016000604051808303818588803b1580156122ed57600080fd5b505af1158015612301573d6000803e3d6000fd5b505060405163a9059cbb60e01b81526001600160a01b038581166004830152602482018990527f0000000000000000000000009129b67f17bfd7a20bd573fa72cc410ddde9ee3516935063a9059cbb92506044019050602060405180830381600087803b15801561237157600080fd5b505af1158015612385573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123a99190613e15565b6123b5576123b56144b7565b6040516335313c2160e11b81526001600160a01b038881166004830152821690636a62784290602401602060405180830381600087803b1580156123f857600080fd5b505af115801561240c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124309190613e82565b92508334111561244d5761244d336124488634614442565b6128ea565b505096509650969350505050565b6060814281101561247e5760405162461bcd60e51b8152600401610513906141c5565b7f0000000000000000000000009129b67f17bfd7a20bd573fa72cc410ddde9ee356001600160a01b0316868660008181106124bb576124bb6144e3565b90506020020160208101906124d09190613b48565b6001600160a01b0316146124f65760405162461bcd60e51b815260040161051390614242565b6125547f0000000000000000000000007a1d8868fdd3af7ea3025edc9c93255047906c6e88888880806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250612ffd92505050565b9150348260008151811061256a5761256a6144e3565b602002602001015111156125905760405162461bcd60e51b8152600401610513906142c1565b7f0000000000000000000000009129b67f17bfd7a20bd573fa72cc410ddde9ee356001600160a01b031663d0e30db0836000815181106125d2576125d26144e3565b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b15801561260557600080fd5b505af1158015612619573d6000803e3d6000fd5b50505050507f0000000000000000000000009129b67f17bfd7a20bd573fa72cc410ddde9ee356001600160a01b031663a9059cbb6126847f0000000000000000000000007a1d8868fdd3af7ea3025edc9c93255047906c6e89896000818110610ced57610ced6144e3565b84600081518110612697576126976144e3565b60200260200101516040518363ffffffff1660e01b81526004016126d09291906001600160a01b03929092168252602082015260400190565b602060405180830381600087803b1580156126ea57600080fd5b505af11580156126fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127229190613e15565b61272e5761272e6144b7565b61276d82878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250899250612df5915050565b81600081518110612780576127806144e3565b602002602001015134111561143d5761143d33836000815181106127a6576127a66144e3565b6020026020010151346124489190614442565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b17905291516000928392908716916128159190614124565b6000604051808303816000865af19150503d8060008114612852576040519150601f19603f3d011682016040523d82523d6000602084013e612857565b606091505b50915091508180156128815750805115806128815750808060200190518101906128819190613e15565b6128e35760405162461bcd60e51b815260206004820152602d60248201527f5472616e7366657248656c7065723a3a736166655472616e736665723a20747260448201526c185b9cd9995c8819985a5b1959609a1b6064820152608401610513565b5050505050565b604080516000808252602082019092526001600160a01b0384169083906040516129149190614124565b60006040518083038185875af1925050503d8060008114612951576040519150601f19603f3d011682016040523d82523d6000602084013e612956565b606091505b50509050806129c45760405162461bcd60e51b815260206004820152603460248201527f5472616e7366657248656c7065723a3a736166655472616e736665724554483a60448201527308115512081d1c985b9cd9995c8819985a5b195960621b6064820152608401610513565b505050565b6000808411612a2e5760405162461bcd60e51b815260206004820152602b60248201527f556e697377617056324c6962726172793a20494e53554646494349454e545f4960448201526a1394155517d05353d5539560aa1b6064820152608401610513565b600083118015612a3e5750600082115b612a5a5760405162461bcd60e51b815260040161051390614279565b6000612a68856103e5613930565b90506000612a768285613930565b90506000612a9083612a8a886103e8613930565b90613997565b9050612a9c8183614401565b979650505050505050565b6060600282511015612afb5760405162461bcd60e51b815260206004820152601e60248201527f556e697377617056324c6962726172793a20494e56414c49445f5041544800006044820152606401610513565b815167ffffffffffffffff811115612b1557612b156144f9565b604051908082528060200260200182016040528015612b3e578160200160208202803683370190505b5090508281600081518110612b5557612b556144e3565b60200260200101818152505060005b60018351612b729190614442565b811015612c2a57600080612bc587868581518110612b9257612b926144e3565b602002602001015187866001612ba891906143e9565b81518110612bb857612bb86144e3565b60200260200101516139ec565b91509150612bed848481518110612bde57612bde6144e3565b602002602001015183836129c9565b84612bf98560016143e9565b81518110612c0957612c096144e3565b60200260200101818152505050508080612c229061449c565b915050612b64565b509392505050565b60405163e6a4390560e01b81526001600160a01b03838116600483015282811660248301526000919085169063e6a439059060440160206040518083038186803b158015612c7f57600080fd5b505afa158015612c93573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105f89190613b6c565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b1790529151600092839290881691612d1b9190614124565b6000604051808303816000865af19150503d8060008114612d58576040519150601f19603f3d011682016040523d82523d6000602084013e612d5d565b606091505b5091509150818015612d87575080511580612d87575080806020019051810190612d879190613e15565b612ded5760405162461bcd60e51b815260206004820152603160248201527f5472616e7366657248656c7065723a3a7472616e7366657246726f6d3a207472604482015270185b9cd9995c919c9bdb4819985a5b1959607a1b6064820152608401610513565b505050505050565b60005b60018351612e069190614442565b811015612ff757600080848381518110612e2257612e226144e3565b602002602001015185846001612e3891906143e9565b81518110612e4857612e486144e3565b6020026020010151915091506000612e6083836135f5565b509050600087612e718660016143e9565b81518110612e8157612e816144e3565b60200260200101519050600080836001600160a01b0316866001600160a01b031614612eaf57826000612eb3565b6000835b91509150600060028a51612ec79190614442565b8810612ed35788612f21565b612f217f0000000000000000000000007a1d8868fdd3af7ea3025edc9c93255047906c6e878c612f048c60026143e9565b81518110612f1457612f146144e3565b6020026020010151612c32565b9050612f4e7f0000000000000000000000007a1d8868fdd3af7ea3025edc9c93255047906c6e8888612c32565b6001600160a01b031663022c0d9f84848460006040519080825280601f01601f191660200182016040528015612f8b576020820181803683370190505b506040518563ffffffff1660e01b8152600401612fab9493929190614399565b600060405180830381600087803b158015612fc557600080fd5b505af1158015612fd9573d6000803e3d6000fd5b50505050505050505050508080612fef9061449c565b915050612df8565b50505050565b60606002825110156130515760405162461bcd60e51b815260206004820152601e60248201527f556e697377617056324c6962726172793a20494e56414c49445f5041544800006044820152606401610513565b815167ffffffffffffffff81111561306b5761306b6144f9565b604051908082528060200260200182016040528015613094578160200160208202803683370190505b5090508281600183516130a79190614442565b815181106130b7576130b76144e3565b6020026020010181815250506000600183516130d39190614442565b90505b8015612c2a5760008061311987866130ef600187614442565b815181106130ff576130ff6144e3565b6020026020010151878681518110612bb857612bb86144e3565b91509150613141848481518110613132576131326144e3565b6020026020010151838361347c565b8461314d600186614442565b8151811061315d5761315d6144e3565b6020026020010181815250505050808061317690614485565b9150506130d6565b60005b6001835161318f9190614442565b8110156129c4576000808483815181106131ab576131ab6144e3565b6020026020010151858460016131c191906143e9565b815181106131d1576131d16144e3565b60200260200101519150915060006131e983836135f5565b50905060006132197f0000000000000000000000007a1d8868fdd3af7ea3025edc9c93255047906c6e8585612c32565b9050600080600080846001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b15801561325a57600080fd5b505afa15801561326e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132929190613e32565b506001600160701b031691506001600160701b03169150600080876001600160a01b03168a6001600160a01b0316146132cc5782846132cf565b83835b6040516370a0823160e01b81526001600160a01b038a8116600483015292945090925061330a918491908d16906370a0823190602401610e6b565b95506133178683836129c9565b945050505050600080856001600160a01b0316886001600160a01b03161461334157826000613345565b6000835b91509150600060028c516133599190614442565b8a10613365578a613396565b6133967f0000000000000000000000007a1d8868fdd3af7ea3025edc9c93255047906c6e898e612f048e60026143e9565b6040805160008152602081019182905263022c0d9f60e01b9091529091506001600160a01b0387169063022c0d9f906133d89086908690869060248101614399565b600060405180830381600087803b1580156133f257600080fd5b505af1158015613406573d6000803e3d6000fd5b50505050505050505050505050808061341e9061449c565b915050613181565b6000826134338382614442565b915081111561095f5760405162461bcd60e51b815260206004820152601560248201527464732d6d6174682d7375622d756e646572666c6f7760581b6044820152606401610513565b60008084116134e25760405162461bcd60e51b815260206004820152602c60248201527f556e697377617056324c6962726172793a20494e53554646494349454e545f4f60448201526b155514155517d05353d5539560a21b6064820152608401610513565b6000831180156134f25750600082115b61350e5760405162461bcd60e51b815260040161051390614279565b60006135266103e86135208688613930565b90613930565b9050600061353a6103e56135208689613426565b905061354b6001612a8a8385614401565b9695505050505050565b60008084116135b45760405162461bcd60e51b815260206004820152602560248201527f556e697377617056324c6962726172793a20494e53554646494349454e545f416044820152641353d5539560da1b6064820152608401610513565b6000831180156135c45750600082115b6135e05760405162461bcd60e51b815260040161051390614279565b826135eb8584613930565b6105f89190614401565b600080826001600160a01b0316846001600160a01b031614156136685760405162461bcd60e51b815260206004820152602560248201527f556e697377617056324c6962726172793a204944454e544943414c5f41444452604482015264455353455360d81b6064820152608401610513565b826001600160a01b0316846001600160a01b03161061368857828461368b565b83835b90925090506001600160a01b0382166136e65760405162461bcd60e51b815260206004820152601e60248201527f556e697377617056324c6962726172793a205a45524f5f4144445245535300006044820152606401610513565b9250929050565b60405163e6a4390560e01b81526001600160a01b0387811660048301528681166024830152600091829182917f0000000000000000000000007a1d8868fdd3af7ea3025edc9c93255047906c6e9091169063e6a439059060440160206040518083038186803b15801561375f57600080fd5b505afa158015613773573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137979190613b6c565b6001600160a01b0316141561384b576040516364e329cb60e11b81526001600160a01b03898116600483015288811660248301527f0000000000000000000000007a1d8868fdd3af7ea3025edc9c93255047906c6e169063c9c6539690604401602060405180830381600087803b15801561381157600080fd5b505af1158015613825573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138499190613b6c565b505b6000806138797f0000000000000000000000007a1d8868fdd3af7ea3025edc9c93255047906c6e8b8b6139ec565b9150915081600014801561388b575080155b1561389b57879350869250613923565b60006138a8898484613555565b90508781116138dc57858110156138d15760405162461bcd60e51b8152600401610513906141fc565b889450925082613921565b60006138e9898486613555565b9050898111156138fb576138fb6144b7565b8781101561391b5760405162461bcd60e51b815260040161051390614308565b94508793505b505b5050965096945050505050565b6000811580613954575082826139468183614423565b92506139529083614401565b145b61095f5760405162461bcd60e51b815260206004820152601460248201527364732d6d6174682d6d756c2d6f766572666c6f7760601b6044820152606401610513565b6000826139a483826143e9565b915081101561095f5760405162461bcd60e51b815260206004820152601460248201527364732d6d6174682d6164642d6f766572666c6f7760601b6044820152606401610513565b60008060006139fb85856135f5565b5090506000613a0b878787612c32565b9050600080826001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b158015613a4957600080fd5b505afa158015613a5d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a819190613e32565b506001600160701b031691506001600160701b03169150836001600160a01b0316886001600160a01b031614613ab8578082613abb565b81815b909a909950975050505050505050565b8035613ad68161450f565b919050565b60008083601f840112613aed57600080fd5b50813567ffffffffffffffff811115613b0557600080fd5b6020830191508360208260051b85010111156136e657600080fd5b80516001600160701b0381168114613ad657600080fd5b803560ff81168114613ad657600080fd5b600060208284031215613b5a57600080fd5b8135613b658161450f565b9392505050565b600060208284031215613b7e57600080fd5b8151613b658161450f565b600080600080600080600060e0888a031215613ba457600080fd5b8735613baf8161450f565b96506020880135613bbf8161450f565b955060408801359450606088013593506080880135925060a0880135613be48161450f565b8092505060c0880135905092959891949750929550565b60008060008060008060008060008060006101608c8e031215613c1d57600080fd5b8b35613c288161450f565b9a5060208c0135613c388161450f565b995060408c0135985060608c0135975060808c0135965060a08c0135613c5d8161450f565b955060c08c0135945060e08c0135613c7481614527565b9350613c836101008d01613b37565b92506101208c013591506101408c013590509295989b509295989b9093969950565b600080600080600080600080610100898b031215613cc257600080fd5b8835613ccd8161450f565b97506020890135613cdd8161450f565b965060408901359550606089013594506080890135935060a0890135925060c0890135613d098161450f565b8092505060e089013590509295985092959890939650565b60008060008060008060c08789031215613d3a57600080fd5b8635613d458161450f565b95506020870135945060408701359350606087013592506080870135613d6a8161450f565b8092505060a087013590509295509295509295565b6000806000806000806000806000806101408b8d031215613d9f57600080fd5b8a35613daa8161450f565b995060208b0135985060408b0135975060608b0135965060808b0135613dcf8161450f565b955060a08b0135945060c08b0135613de681614527565b9350613df460e08c01613b37565b92506101008b013591506101208b013590509295989b9194979a5092959850565b600060208284031215613e2757600080fd5b8151613b6581614527565b600080600060608486031215613e4757600080fd5b613e5084613b20565b9250613e5e60208501613b20565b9150604084015163ffffffff81168114613e7757600080fd5b809150509250925092565b600060208284031215613e9457600080fd5b5051919050565b600080600080600060808688031215613eb357600080fd5b85359450602086013567ffffffffffffffff811115613ed157600080fd5b613edd88828901613adb565b9095509350506040860135613ef18161450f565b949793965091946060013592915050565b60008060408385031215613f1557600080fd5b8235915060208084013567ffffffffffffffff80821115613f3557600080fd5b818601915086601f830112613f4957600080fd5b813581811115613f5b57613f5b6144f9565b8060051b604051601f19603f83011681018181108582111715613f8057613f806144f9565b604052828152858101935084860182860187018b1015613f9f57600080fd5b600095505b83861015613fc957613fb581613acb565b855260019590950194938601938601613fa4565b508096505050505050509250929050565b60008060408385031215613fed57600080fd5b505080516020909101519092909150565b600080600080600080600060c0888a03121561401957600080fd5b8735965060208801359550604088013567ffffffffffffffff81111561403e57600080fd5b61404a8a828b01613adb565b909650945050606088013561405e8161450f565b9250608088013561406e8161450f565b8092505060a0880135905092959891949750929550565b60008060008060008060a0878903121561409e57600080fd5b8635955060208701359450604087013567ffffffffffffffff8111156140c357600080fd5b6140cf89828a01613adb565b90955093505060608701356140e38161450f565b80925050608087013590509295509295509295565b60008060006060848603121561410d57600080fd5b505081359360208301359350604090920135919050565b60008251614136818460208701614459565b9190910192915050565b6001600160a01b0397881681529590961660208601526040850193909352606084019190915260ff16608083015260a082015260c081019190915260e00190565b6020808252825182820181905260009190848201906040850190845b818110156141b95783518352928401929184019160010161419d565b50909695505050505050565b60208082526018908201527f556e69737761705632526f757465723a20455850495245440000000000000000604082015260600190565b60208082526026908201527f556e69737761705632526f757465723a20494e53554646494349454e545f425f604082015265105353d5539560d21b606082015260800190565b6020808252601d908201527f556e69737761705632526f757465723a20494e56414c49445f50415448000000604082015260600190565b60208082526028908201527f556e697377617056324c6962726172793a20494e53554646494349454e545f4c604082015267495155494449545960c01b606082015260800190565b60208082526027908201527f556e69737761705632526f757465723a204558434553534956455f494e50555460408201526617d05353d5539560ca1b606082015260800190565b60208082526026908201527f556e69737761705632526f757465723a20494e53554646494349454e545f415f604082015265105353d5539560d21b606082015260800190565b6020808252602b908201527f556e69737761705632526f757465723a20494e53554646494349454e545f4f5560408201526a1514155517d05353d5539560aa1b606082015260800190565b84815283602082015260018060a01b038316604082015260806060820152600082518060808401526143d28160a0850160208701614459565b601f01601f19169190910160a00195945050505050565b600082198211156143fc576143fc6144cd565b500190565b60008261441e57634e487b7160e01b600052601260045260246000fd5b500490565b600081600019048311821515161561443d5761443d6144cd565b500290565b600082821015614454576144546144cd565b500390565b60005b8381101561447457818101518382015260200161445c565b83811115612ff75750506000910152565b600081614494576144946144cd565b506000190190565b60006000198214156144b0576144b06144cd565b5060010190565b634e487b7160e01b600052600160045260246000fd5b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b038116811461452457600080fd5b50565b801515811461452457600080fdfea2646970667358221220f79e8d7546ccc4b939cff81035f715c199a87cf7159384e9c4b49ff900f3e32064736f6c63430008070033