Contract Address Details

0xbAFcdabe65A03825a131298bE7670c0aEC77B37f

Contract Name
KaiDexRouter
Creator
0x0f5c21–500dc9 at 0xb1bbff–6e8244
Balance
0 KAI ( )
Tokens
Fetching tokens...
Transactions
102,901 Transactions
Transfers
75,633 Transfers
Gas Used
15,389,640,243
Last Balance Update
20038743
Contract name:
KaiDexRouter




Optimization enabled
true
Compiler version
v0.8.4+commit.c7e474f2




Optimization runs
200
EVM Version
default




Verified at
2022-07-12T10:40:11.088050Z

Constructor Arguments

000000000000000000000000c9567a8b6b622cdc8076c6b4432ade0e11f50da1000000000000000000000000af984e23eaa3e7967f3c5e007fbe397d8566d23d

Arg [0] (address) : 0xc9567a8b6b622cdc8076c6b4432ade0e11f50da1
Arg [1] (address) : 0xaf984e23eaa3e7967f3c5e007fbe397d8566d23d

              

Contract source code

// File contracts/exchange/libraries/TransferHelper.sol

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;

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


// File contracts/exchange/interfaces/IKaiDexRouter.sol

pragma solidity ^0.8.0;

interface IKaiDexRouter {
    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 addLiquidityKAI(
        address token,
        uint amountTokenDesired,
        uint amountTokenMin,
        uint amountKAIMin,
        address to,
        uint deadline
    ) external payable returns (uint amountToken, uint amountKAI, 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 removeLiquidityKAI(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountKAIMin,
        address to,
        uint deadline
    ) external returns (uint amountToken, uint amountKAI);
    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 swapExactKAIForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        payable
        returns (uint[] memory amounts);
    function swapTokensForExactKAI(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
        external
        returns (uint[] memory amounts);
    function swapExactTokensForKAI(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        returns (uint[] memory amounts);
    function swapKAIForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
        external
        payable
        returns (uint[] memory amounts);
        
    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 returns (uint256 amountA, uint256 amountB);

    function removeLiquidityKAIWithPermit(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountKAIMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint256 amountToken, uint256 amountKAI);
    
    
    function removeLiquidityKAISupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountKAIMin,
        address to,
        uint deadline
    ) external returns (uint amountKAI);

    function removeLiquidityKAIWithPermitSupportingFeeOnTransferTokens(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountKAIMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint256 amountKAI);
  
    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
    
    function swapExactKAIForTokensSupportingFeeOnTransferTokens(
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external payable;
    function swapExactTokensForKAISupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;

    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/exchange/interfaces/IKaiDexFactory.sol

pragma solidity ^0.8.0;

interface IKaiDexFactory {
    event PairCreated(address indexed token0, address indexed token1, address pair, uint256);

    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(uint256) external view returns (address pair);

    function allPairsLength() external view returns (uint256);

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

    function setFeeTo(address) external;

    function setFeeToSetter(address) external;

}


// File contracts/exchange/libraries/SafeMath.sol

pragma solidity ^0.8.0;

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

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

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

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


// File contracts/exchange/interfaces/IKAIDexPair.sol

pragma solidity ^0.8.0;

interface IKAIDexPair {
    event Approval(address indexed owner, address indexed spender, uint256 value);
    event Transfer(address indexed from, address indexed to, uint256 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 (uint256);

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

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

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

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

    function transferFrom(
        address from,
        address to,
        uint256 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 (uint256);

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

    event Mint(address indexed sender, uint256 amount0, uint256 amount1);
    event Burn(address indexed sender, uint256 amount0, uint256 amount1, address indexed to);
    event Swap(
        address indexed sender,
        uint256 amount0In,
        uint256 amount1In,
        uint256 amount0Out,
        uint256 amount1Out,
        address indexed to
    );
    event Sync(uint112 reserve0, uint112 reserve1);

    function MINIMUM_LIQUIDITY() external pure returns (uint256);

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

    function price1CumulativeLast() external view returns (uint256);

    function kLast() external view returns (uint256);

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

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

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

    function skim(address to) external;

    function sync() external;

    function initialize(address, address) external;
}


// File contracts/exchange/libraries/KaiDexLibrary.sol

pragma solidity ^0.8.0;



library KaiDexLibrary {
    using SafeMath for uint256;

    // 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, "KaiDexLibrary: IDENTICAL_ADDRESSES");
        (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
        require(token0 != address(0), "KaiDexLibrary: 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(
            uint160(uint256(
                keccak256(
                    abi.encodePacked(
                        hex"ff",
                        factory,
                        keccak256(abi.encodePacked(token0, token1)),
                        hex"f264d894da1070a80bc479a9f9b59d7dba3d8977144aba4ba53652b31ab31da1" // init code hash
                    )
                )
            )
            )
        );
    }

    // fetches and sorts the reserves for a pair
    function getReserves(
        address factory,
        address tokenA,
        address tokenB
    ) internal view returns (uint256 reserveA, uint256 reserveB) {
        (address token0, ) = sortTokens(tokenA, tokenB);
        pairFor(factory, tokenA, tokenB);
        (uint256 reserve0, uint256 reserve1, ) = IKAIDexPair(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(
        uint256 amountA,
        uint256 reserveA,
        uint256 reserveB
    ) internal pure returns (uint256 amountB) {
        require(amountA > 0, "KaiDexLibrary: INSUFFICIENT_AMOUNT");
        require(reserveA > 0 && reserveB > 0, "KaiDexLibrary: 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(
        uint256 amountIn,
        uint256 reserveIn,
        uint256 reserveOut
    ) internal pure returns (uint256 amountOut) {
        require(amountIn > 0, "KaiDexLibrary: INSUFFICIENT_INPUT_AMOUNT");
        require(reserveIn > 0 && reserveOut > 0, "KaiDexLibrary: INSUFFICIENT_LIQUIDITY");
        uint256 amountInWithFee = amountIn.mul(997);
        uint256 numerator = amountInWithFee.mul(reserveOut);
        uint256 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(
        uint256 amountOut,
        uint256 reserveIn,
        uint256 reserveOut
    ) internal pure returns (uint256 amountIn) {
        require(amountOut > 0, "KaiDexLibrary: INSUFFICIENT_OUTPUT_AMOUNT");
        require(reserveIn > 0 && reserveOut > 0, "KaiDexLibrary: INSUFFICIENT_LIQUIDITY");
        uint256 numerator = reserveIn.mul(amountOut).mul(1000);
        uint256 denominator = reserveOut.sub(amountOut).mul(997);
        amountIn = (numerator / denominator).add(1);
    }

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


// File contracts/exchange/interfaces/IKRC20.sol

pragma solidity ^0.8.0;

interface IKRC20 {
    event Approval(address indexed owner, address indexed spender, uint256 value);
    event Transfer(address indexed from, address indexed to, uint256 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 (uint256);

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

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

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

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

    function transferFrom(
        address from,
        address to,
        uint256 value
    ) external returns (bool);
    
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}


// File contracts/exchange/interfaces/IWKAI.sol

pragma solidity ^0.8.0;

interface IWKAI {
    function deposit() external payable;

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

    function withdraw(uint256) external;
}


// File contracts/exchange/KaiDexRouter.sol

pragma solidity ^0.8.0;







contract KaiDexRouter is IKaiDexRouter {
    using SafeMath for uint256;

    address public factory;
    address public WKAI;

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

    constructor(address _factory, address _WKAI) public {
        factory = _factory;
        WKAI = _WKAI;
    }

    receive() external payable {
        assert(msg.sender == WKAI); // only accept KAI via fallback from the WKAI 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 (IKaiDexFactory(factory).getPair(tokenA, tokenB) == address(0)) {
            IKaiDexFactory(factory).createPair(tokenA, tokenB);
        }
        (uint256 reserveA, uint256 reserveB) = KaiDexLibrary.getReserves(
            factory,
            tokenA,
            tokenB
        );
        if (reserveA == 0 && reserveB == 0) {
            (amountA, amountB) = (amountADesired, amountBDesired);
        } else {
            uint256 amountBOptimal = KaiDexLibrary.quote(
                amountADesired,
                reserveA,
                reserveB
            );
            if (amountBOptimal <= amountBDesired) {
                require(
                    amountBOptimal >= amountBMin,
                    "KaiDexRouter: INSUFFICIENT_B_AMOUNT"
                );
                (amountA, amountB) = (amountADesired, amountBOptimal);
            } else {
                uint256 amountAOptimal = KaiDexLibrary.quote(
                    amountBDesired,
                    reserveB,
                    reserveA
                );
                assert(amountAOptimal <= amountADesired);
                require(
                    amountAOptimal >= amountAMin,
                    "KaiDexRouter: 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
        override
        ensure(deadline)
        returns (
            uint256 amountA,
            uint256 amountB,
            uint256 liquidity
        )
    {
        (amountA, amountB) = _addLiquidity(
            tokenA,
            tokenB,
            amountADesired,
            amountBDesired,
            amountAMin,
            amountBMin
        );
        address pair = KaiDexLibrary.pairFor(factory, tokenA, tokenB);
        TransferHelper.safeTransferFrom(tokenA, msg.sender, pair, amountA);
        TransferHelper.safeTransferFrom(tokenB, msg.sender, pair, amountB);
        liquidity = IKAIDexPair(pair).mint(to);
    }

    function addLiquidityKAI(
        address token,
        uint256 amountTokenDesired,
        uint256 amountTokenMin,
        uint256 amountKAIMin,
        address to,
        uint256 deadline
    )
        external
        payable
        virtual
        override
        ensure(deadline)
        returns (
            uint256 amountToken,
            uint256 amountKAI,
            uint256 liquidity
        )
    {
        (amountToken, amountKAI) = _addLiquidity(
            token,
            WKAI,
            amountTokenDesired,
            msg.value,
            amountTokenMin,
            amountKAIMin
        );
        address pair = KaiDexLibrary.pairFor(factory, token, WKAI);
        TransferHelper.safeTransferFrom(token, msg.sender, pair, amountToken);
        IWKAI(WKAI).deposit{value: amountKAI}();
        assert(IWKAI(WKAI).transfer(pair, amountKAI));
        liquidity = IKAIDexPair(pair).mint(to);
        // refund dust kai, if any
        if (msg.value > amountKAI)
            TransferHelper.safeTransferKAI(msg.sender, msg.value - amountKAI);
    }

    // **** REMOVE LIQUIDITY ****
    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint256 liquidity,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    )
        public
        virtual
        override
        ensure(deadline)
        returns (uint256 amountA, uint256 amountB)
    {
        address pair = KaiDexLibrary.pairFor(factory, tokenA, tokenB);
        IKAIDexPair(pair).transferFrom(msg.sender, pair, liquidity); // send liquidity to pair
        (uint256 amount0, uint256 amount1) = IKAIDexPair(pair).burn(to);
        (address token0, ) = KaiDexLibrary.sortTokens(tokenA, tokenB);
        (amountA, amountB) = tokenA == token0
            ? (amount0, amount1)
            : (amount1, amount0);
        require(amountA >= amountAMin, "KaiDexRouter: INSUFFICIENT_A_AMOUNT");
        require(amountB >= amountBMin, "KaiDexRouter: INSUFFICIENT_B_AMOUNT");
    }

    function removeLiquidityKAI(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountKAIMin,
        address to,
        uint256 deadline
    )
        public
        virtual
        override
        ensure(deadline)
        returns (uint256 amountToken, uint256 amountKAI)
    {
        (amountToken, amountKAI) = removeLiquidity(
            token,
            WKAI,
            liquidity,
            amountTokenMin,
            amountKAIMin,
            address(this),
            deadline
        );
        TransferHelper.safeTransfer(token, to, amountToken);
        IWKAI(WKAI).withdraw(amountKAI);
        TransferHelper.safeTransferKAI(to, amountKAI);
    }

    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 override returns (uint256 amountA, uint256 amountB) {
        address pair = KaiDexLibrary.pairFor(factory, tokenA, tokenB);
        uint256 value = approveMax ? type(uint256).max : liquidity;
        IKAIDexPair(pair).permit(
            msg.sender,
            address(this),
            value,
            deadline,
            v,
            r,
            s
        );
        (amountA, amountB) = removeLiquidity(
            tokenA,
            tokenB,
            liquidity,
            amountAMin,
            amountBMin,
            to,
            deadline
        );
    }

    function removeLiquidityKAIWithPermit(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountKAIMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    )
        external
        virtual
        override
        returns (uint256 amountToken, uint256 amountKAI)
    {
        address pair = KaiDexLibrary.pairFor(factory, token, WKAI);
        uint256 value = approveMax ? type(uint256).max : liquidity;
        IKAIDexPair(pair).permit(
            msg.sender,
            address(this),
            value,
            deadline,
            v,
            r,
            s
        );
        (amountToken, amountKAI) = removeLiquidityKAI(
            token,
            liquidity,
            amountTokenMin,
            amountKAIMin,
            to,
            deadline
        );
    }

    // **** REMOVE LIQUIDITY (supporting fee-on-transfer tokens) ****
    function removeLiquidityKAISupportingFeeOnTransferTokens(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountKAIMin,
        address to,
        uint256 deadline
    ) public virtual override ensure(deadline) returns (uint256 amountKAI) {
        (, amountKAI) = removeLiquidity(
            token,
            WKAI,
            liquidity,
            amountTokenMin,
            amountKAIMin,
            address(this),
            deadline
        );
        TransferHelper.safeTransfer(
            token,
            to,
            IKRC20(token).balanceOf(address(this))
        );
        IWKAI(WKAI).withdraw(amountKAI);
        TransferHelper.safeTransferKAI(to, amountKAI);
    }

    function removeLiquidityKAIWithPermitSupportingFeeOnTransferTokens(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountKAIMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external virtual override returns (uint256 amountKAI) {
        address pair = KaiDexLibrary.pairFor(factory, token, WKAI);
        uint256 value = approveMax ? type(uint256).max : liquidity;
        IKAIDexPair(pair).permit(
            msg.sender,
            address(this),
            value,
            deadline,
            v,
            r,
            s
        );
        amountKAI = removeLiquidityKAISupportingFeeOnTransferTokens(
            token,
            liquidity,
            amountTokenMin,
            amountKAIMin,
            to,
            deadline
        );
    }

    // **** 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, ) = KaiDexLibrary.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
                ? KaiDexLibrary.pairFor(factory, output, path[i + 2])
                : _to;
            IKAIDexPair(KaiDexLibrary.pairFor(factory, input, output)).swap(
                amount0Out,
                amount1Out,
                to,
                new bytes(0)
            );
        }
    }

    function swapExactTokensForTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    )
        external
        virtual
        override
        ensure(deadline)
        returns (uint256[] memory amounts)
    {
        amounts = KaiDexLibrary.getAmountsOut(factory, amountIn, path);
        require(
            amounts[amounts.length - 1] >= amountOutMin,
            "KaiDexRouter: INSUFFICIENT_OUTPUT_AMOUNT"
        );
        TransferHelper.safeTransferFrom(
            path[0],
            msg.sender,
            KaiDexLibrary.pairFor(factory, path[0], path[1]),
            amounts[0]
        );
        _swap(amounts, path, to);
    }

    function swapTokensForExactTokens(
        uint256 amountOut,
        uint256 amountInMax,
        address[] calldata path,
        address to,
        uint256 deadline
    )
        external
        virtual
        override
        ensure(deadline)
        returns (uint256[] memory amounts)
    {
        amounts = KaiDexLibrary.getAmountsIn(factory, amountOut, path);
        require(
            amounts[0] <= amountInMax,
            "KaiDexRouter: EXCESSIVE_INPUT_AMOUNT"
        );
        TransferHelper.safeTransferFrom(
            path[0],
            msg.sender,
            KaiDexLibrary.pairFor(factory, path[0], path[1]),
            amounts[0]
        );
        _swap(amounts, path, to);
    }

    function swapExactKAIForTokens(
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    )
        external
        payable
        virtual
        override
        ensure(deadline)
        returns (uint256[] memory amounts)
    {
        require(path[0] == WKAI, "KaiDexRouter: INVALID_PATH");
        amounts = KaiDexLibrary.getAmountsOut(factory, msg.value, path);
        require(
            amounts[amounts.length - 1] >= amountOutMin,
            "KaiDexRouter: INSUFFICIENT_OUTPUT_AMOUNT"
        );
        IWKAI(WKAI).deposit{value: amounts[0]}();
        assert(
            IWKAI(WKAI).transfer(
                KaiDexLibrary.pairFor(factory, path[0], path[1]),
                amounts[0]
            )
        );
        _swap(amounts, path, to);
    }

    function swapTokensForExactKAI(
        uint256 amountOut,
        uint256 amountInMax,
        address[] calldata path,
        address to,
        uint256 deadline
    )
        external
        virtual
        override
        ensure(deadline)
        returns (uint256[] memory amounts)
    {
        require(path[path.length - 1] == WKAI, "KaiDexRouter: INVALID_PATH");
        amounts = KaiDexLibrary.getAmountsIn(factory, amountOut, path);
        require(
            amounts[0] <= amountInMax,
            "KaiDexRouter: EXCESSIVE_INPUT_AMOUNT"
        );
        TransferHelper.safeTransferFrom(
            path[0],
            msg.sender,
            KaiDexLibrary.pairFor(factory, path[0], path[1]),
            amounts[0]
        );
        _swap(amounts, path, address(this));
        IWKAI(WKAI).withdraw(amounts[amounts.length - 1]);
        TransferHelper.safeTransferKAI(to, amounts[amounts.length - 1]);
    }

    function swapExactTokensForKAI(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    )
        external
        virtual
        override
        ensure(deadline)
        returns (uint256[] memory amounts)
    {
        require(path[path.length - 1] == WKAI, "KaiDexRouter: INVALID_PATH");
        amounts = KaiDexLibrary.getAmountsOut(factory, amountIn, path);
        require(
            amounts[amounts.length - 1] >= amountOutMin,
            "KaiDexRouter: INSUFFICIENT_OUTPUT_AMOUNT"
        );
        TransferHelper.safeTransferFrom(
            path[0],
            msg.sender,
            KaiDexLibrary.pairFor(factory, path[0], path[1]),
            amounts[0]
        );
        _swap(amounts, path, address(this));
        IWKAI(WKAI).withdraw(amounts[amounts.length - 1]);
        TransferHelper.safeTransferKAI(to, amounts[amounts.length - 1]);
    }

    function swapKAIForExactTokens(
        uint256 amountOut,
        address[] calldata path,
        address to,
        uint256 deadline
    )
        external
        payable
        virtual
        override
        ensure(deadline)
        returns (uint256[] memory amounts)
    {
        require(path[0] == WKAI, "KaiDexRouter: INVALID_PATH");
        amounts = KaiDexLibrary.getAmountsIn(factory, amountOut, path);
        require(
            amounts[0] <= msg.value,
            "KaiDexRouter: EXCESSIVE_INPUT_AMOUNT"
        );
        IWKAI(WKAI).deposit{value: amounts[0]}();
        assert(
            IWKAI(WKAI).transfer(
                KaiDexLibrary.pairFor(factory, path[0], path[1]),
                amounts[0]
            )
        );
        _swap(amounts, path, to);
        // refund dust kai, if any
        if (msg.value > amounts[0])
            TransferHelper.safeTransferKAI(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, ) = KaiDexLibrary.sortTokens(input, output);
            IKAIDexPair pair = IKAIDexPair(
                KaiDexLibrary.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 = IKRC20(input).balanceOf(address(pair)).sub(
                    reserveInput
                );
                amountOutput = KaiDexLibrary.getAmountOut(
                    amountInput,
                    reserveInput,
                    reserveOutput
                );
            }
            (uint256 amount0Out, uint256 amount1Out) = input == token0
                ? (uint256(0), amountOutput)
                : (amountOutput, uint256(0));
            address to = i < path.length - 2
                ? KaiDexLibrary.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 override ensure(deadline) {
        TransferHelper.safeTransferFrom(
            path[0],
            msg.sender,
            KaiDexLibrary.pairFor(factory, path[0], path[1]),
            amountIn
        );
        uint256 balanceBefore = IKRC20(path[path.length - 1]).balanceOf(to);
        _swapSupportingFeeOnTransferTokens(path, to);
        require(
            IKRC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore) >=
                amountOutMin,
            "KaiDexRouter: INSUFFICIENT_OUTPUT_AMOUNT"
        );
    }

    function swapExactKAIForTokensSupportingFeeOnTransferTokens(
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external payable virtual override ensure(deadline) {
        require(path[0] == WKAI, "KaiDexRouter: INVALID_PATH");
        uint256 amountIn = msg.value;
        IWKAI(WKAI).deposit{value: amountIn}();
        assert(
            IWKAI(WKAI).transfer(
                KaiDexLibrary.pairFor(factory, path[0], path[1]),
                amountIn
            )
        );
        uint256 balanceBefore = IKRC20(path[path.length - 1]).balanceOf(to);
        _swapSupportingFeeOnTransferTokens(path, to);
        require(
            IKRC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore) >=
                amountOutMin,
            "KaiDexRouter: INSUFFICIENT_OUTPUT_AMOUNT"
        );
    }

    function swapExactTokensForKAISupportingFeeOnTransferTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external virtual override ensure(deadline) {
        require(path[path.length - 1] == WKAI, "KaiDexRouter: INVALID_PATH");
        TransferHelper.safeTransferFrom(
            path[0],
            msg.sender,
            KaiDexLibrary.pairFor(factory, path[0], path[1]),
            amountIn
        );
        _swapSupportingFeeOnTransferTokens(path, address(this));
        uint256 amountOut = IKRC20(WKAI).balanceOf(address(this));
        require(
            amountOut >= amountOutMin,
            "KaiDexRouter: INSUFFICIENT_OUTPUT_AMOUNT"
        );
        IWKAI(WKAI).withdraw(amountOut);
        TransferHelper.safeTransferKAI(to, amountOut);
    }

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

    function getAmountOut(
        uint256 amountIn,
        uint256 reserveIn,
        uint256 reserveOut
    ) public pure virtual override returns (uint256 amountOut) {
        return KaiDexLibrary.getAmountOut(amountIn, reserveIn, reserveOut);
    }

    function getAmountIn(
        uint256 amountOut,
        uint256 reserveIn,
        uint256 reserveOut
    ) public pure virtual override returns (uint256 amountIn) {
        return KaiDexLibrary.getAmountIn(amountOut, reserveIn, reserveOut);
    }

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

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

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_factory","internalType":"address"},{"type":"address","name":"_WKAI","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"WKAI","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":"amountKAI","internalType":"uint256"},{"type":"uint256","name":"liquidity","internalType":"uint256"}],"name":"addLiquidityKAI","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"amountTokenDesired","internalType":"uint256"},{"type":"uint256","name":"amountTokenMin","internalType":"uint256"},{"type":"uint256","name":"amountKAIMin","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":"amountKAI","internalType":"uint256"}],"name":"removeLiquidityKAI","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"liquidity","internalType":"uint256"},{"type":"uint256","name":"amountTokenMin","internalType":"uint256"},{"type":"uint256","name":"amountKAIMin","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"amountKAI","internalType":"uint256"}],"name":"removeLiquidityKAISupportingFeeOnTransferTokens","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"liquidity","internalType":"uint256"},{"type":"uint256","name":"amountTokenMin","internalType":"uint256"},{"type":"uint256","name":"amountKAIMin","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":"amountKAI","internalType":"uint256"}],"name":"removeLiquidityKAIWithPermit","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"liquidity","internalType":"uint256"},{"type":"uint256","name":"amountTokenMin","internalType":"uint256"},{"type":"uint256","name":"amountKAIMin","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":"amountKAI","internalType":"uint256"}],"name":"removeLiquidityKAIWithPermitSupportingFeeOnTransferTokens","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"liquidity","internalType":"uint256"},{"type":"uint256","name":"amountTokenMin","internalType":"uint256"},{"type":"uint256","name":"amountKAIMin","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":"swapExactKAIForTokens","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":"swapExactKAIForTokensSupportingFeeOnTransferTokens","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":"swapExactTokensForKAI","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":"swapExactTokensForKAISupportingFeeOnTransferTokens","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":"payable","outputs":[{"type":"uint256[]","name":"amounts","internalType":"uint256[]"}],"name":"swapKAIForExactTokens","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":"nonpayable","outputs":[{"type":"uint256[]","name":"amounts","internalType":"uint256[]"}],"name":"swapTokensForExactKAI","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"}]
              

Contract Creation Code

0x60806040523480156200001157600080fd5b50604051620040fd380380620040fd833981016040819052620000349162000083565b600080546001600160a01b039384166001600160a01b03199182161790915560018054929093169116179055620000ba565b80516001600160a01b03811681146200007e57600080fd5b919050565b6000806040838503121562000096578182fd5b620000a18362000066565b9150620000b16020840162000066565b90509250929050565b61403380620000ca6000396000f3fe60806040526004361061014f5760003560e01c80636937ffb6116100b6578063b6a046b31161006f578063b6a046b3146103e9578063baa2abde14610409578063c45a015514610429578063d06ca61f14610449578063d06ebe7d14610469578063e8e337001461049757600080fd5b80636937ffb61461034357806385f8c259146103635780638803dbee146103835780639df02c72146103a3578063ad615dec146103b6578063b62761fa146103d657600080fd5b806338ed17391161010857806338ed17391461026b57806356732d731461028b57806356b2cc2f146102ab5780635c11d795146102cb578063629f4306146102eb578063631722db1461030b57600080fd5b806304d464b114610183578063054d50d4146101965780630c7e815a146101c95780631f00ca74146101fe5780632195995c1461022b57806323da1b271461024b57600080fd5b3661017e576001546001600160a01b0316331461017c57634e487b7160e01b600052600160045260246000fd5b005b600080fd5b61017c610191366004613a3a565b6104b7565b3480156101a257600080fd5b506101b66101b1366004613c06565b6108bc565b6040519081526020015b60405180910390f35b3480156101d557600080fd5b506101e96101e4366004613923565b6108d1565b604080519283526020830191909152016101c0565b34801561020a57600080fd5b5061021e610219366004613a9f565b6109ac565b6040516101c09190613c8e565b34801561023757600080fd5b506101e96102463660046137a2565b6109cf565b34801561025757600080fd5b506101b66102663660046138c6565b610a94565b34801561027757600080fd5b5061021e610286366004613b95565b610bce565b34801561029757600080fd5b506101e96102a63660046138c6565b610da0565b3480156102b757600080fd5b5061021e6102c6366004613b95565b610e64565b3480156102d757600080fd5b5061017c6102e6366004613b95565b6110c9565b3480156102f757600080fd5b506101b6610306366004613923565b6112cc565b34801561031757600080fd5b5060015461032b906001600160a01b031681565b6040516001600160a01b0390911681526020016101c0565b34801561034f57600080fd5b5061021e61035e366004613b95565b61138c565b34801561036f57600080fd5b506101b661037e366004613c06565b6114c3565b34801561038f57600080fd5b5061021e61039e366004613b95565b6114d0565b61021e6103b1366004613a3a565b611587565b3480156103c257600080fd5b506101b66103d1366004613c06565b6118e2565b61021e6103e4366004613a3a565b6118ef565b3480156103f557600080fd5b5061017c610404366004613b95565b611bdf565b34801561041557600080fd5b506101e9610424366004613731565b611ddc565b34801561043557600080fd5b5060005461032b906001600160a01b031681565b34801561045557600080fd5b5061021e610464366004613a9f565b611fad565b61047c6104773660046138c6565b611fc7565b604080519384526020840192909252908201526060016101c0565b3480156104a357600080fd5b5061047c6104b236600461384b565b6121e7565b80428110156104e15760405162461bcd60e51b81526004016104d890613e60565b60405180910390fd5b6001546001600160a01b0316858560008161050c57634e487b7160e01b600052603260045260246000fd5b905060200201602081019061052191906136f2565b6001600160a01b0316146105475760405162461bcd60e51b81526004016104d890613d5a565b60015460408051630d0e30db60e41b8152905134926001600160a01b03169163d0e30db091849160048082019260009290919082900301818588803b15801561058f57600080fd5b505af11580156105a3573d6000803e3d6000fd5b5050600154600080546001600160a01b03928316955063a9059cbb94506106389350909116908a908a90816105e857634e487b7160e01b600052603260045260246000fd5b90506020020160208101906105fd91906136f2565b8a8a600181811061061e57634e487b7160e01b600052603260045260246000fd5b905060200201602081019061063391906136f2565b6122e1565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260248101849052604401602060405180830381600087803b15801561068057600080fd5b505af1158015610694573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106b891906139b8565b6106d257634e487b7160e01b600052600160045260246000fd5b600086866106e1600182613f36565b8181106106fe57634e487b7160e01b600052603260045260246000fd5b905060200201602081019061071391906136f2565b6040516370a0823160e01b81526001600160a01b03878116600483015291909116906370a082319060240160206040518083038186803b15801561075657600080fd5b505afa15801561076a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061078e9190613a22565b90506107ce8787808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508992506123ba915050565b876108948289896107e0600182613f36565b8181106107fd57634e487b7160e01b600052603260045260246000fd5b905060200201602081019061081291906136f2565b6040516370a0823160e01b81526001600160a01b038a8116600483015291909116906370a08231906024015b60206040518083038186803b15801561085657600080fd5b505afa15801561086a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061088e9190613a22565b90612687565b10156108b25760405162461bcd60e51b81526004016104d890613dd4565b5050505050505050565b60006108c98484846126dd565b949350505050565b600080600061090960008054906101000a90046001600160a01b03168e600160009054906101000a90046001600160a01b03166122e1565b9050600087610918578c61091c565b6000195b60405163d505accf60e01b81529091506001600160a01b0383169063d505accf90610957903390309086908f908e908e908e90600401613c4d565b600060405180830381600087803b15801561097157600080fd5b505af1158015610985573d6000803e3d6000fd5b505050506109978e8e8e8e8e8e610da0565b909f909e509c50505050505050505050505050565b6000546060906109c6906001600160a01b031684846127b8565b90505b92915050565b60008054819081906109eb906001600160a01b03168f8f6122e1565b90506000876109fa578c6109fe565b6000195b60405163d505accf60e01b81529091506001600160a01b0383169063d505accf90610a39903390309086908f908e908e908e90600401613c4d565b600060405180830381600087803b158015610a5357600080fd5b505af1158015610a67573d6000803e3d6000fd5b50505050610a7a8f8f8f8f8f8f8f611ddc565b809450819550505050509b509b9950505050505050505050565b60008142811015610ab75760405162461bcd60e51b81526004016104d890613e60565b600154610ad39089906001600160a01b03168989893089611ddc565b6040516370a0823160e01b8152306004820152909350610b5b9150899086906001600160a01b038316906370a082319060240160206040518083038186803b158015610b1e57600080fd5b505afa158015610b32573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b569190613a22565b6129a2565b600154604051632e1a7d4d60e01b8152600481018490526001600160a01b0390911690632e1a7d4d90602401600060405180830381600087803b158015610ba157600080fd5b505af1158015610bb5573d6000803e3d6000fd5b50505050610bc38483612abd565b509695505050505050565b60608142811015610bf15760405162461bcd60e51b81526004016104d890613e60565b6000546040805160208089028281018201909352888252610c3b936001600160a01b0316928c92918b918b91829190850190849080828437600092019190915250612b8692505050565b9150868260018451610c4d9190613f36565b81518110610c6b57634e487b7160e01b600052603260045260246000fd5b60200260200101511015610c915760405162461bcd60e51b81526004016104d890613dd4565b610d6186866000818110610cb557634e487b7160e01b600052603260045260246000fd5b9050602002016020810190610cca91906136f2565b600080543391610d33916001600160a01b0316908b908b9081610cfd57634e487b7160e01b600052603260045260246000fd5b9050602002016020810190610d1291906136f2565b8b8b600181811061061e57634e487b7160e01b600052603260045260246000fd5b85600081518110610d5457634e487b7160e01b600052603260045260246000fd5b6020026020010151612d50565b610bc382878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250899250612e80915050565b6000808242811015610dc45760405162461bcd60e51b81526004016104d890613e60565b600154610de0908a906001600160a01b03168a8a8a308a611ddc565b9093509150610df08986856129a2565b600154604051632e1a7d4d60e01b8152600481018490526001600160a01b0390911690632e1a7d4d90602401600060405180830381600087803b158015610e3657600080fd5b505af1158015610e4a573d6000803e3d6000fd5b50505050610e588583612abd565b50965096945050505050565b60608142811015610e875760405162461bcd60e51b81526004016104d890613e60565b600180546001600160a01b03169087908790610ea39082613f36565b818110610ec057634e487b7160e01b600052603260045260246000fd5b9050602002016020810190610ed591906136f2565b6001600160a01b031614610efb5760405162461bcd60e51b81526004016104d890613d5a565b6000546040805160208089028281018201909352888252610f45936001600160a01b0316928c92918b918b918291908501908490808284376000920191909152506127b892505050565b91508682600081518110610f6957634e487b7160e01b600052603260045260246000fd5b60200260200101511115610f8f5760405162461bcd60e51b81526004016104d890613e1c565b610fb386866000818110610cb557634e487b7160e01b600052603260045260246000fd5b610ff282878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250309250612e80915050565b6001805483516001600160a01b0390911691632e1a7d4d91859161101591613f36565b8151811061103357634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b815260040161105991815260200190565b600060405180830381600087803b15801561107357600080fd5b505af1158015611087573d6000803e3d6000fd5b50505050610bc384836001855161109e9190613f36565b815181106110bc57634e487b7160e01b600052603260045260246000fd5b6020026020010151612abd565b80428110156110ea5760405162461bcd60e51b81526004016104d890613e60565b61115c8585600081811061110e57634e487b7160e01b600052603260045260246000fd5b905060200201602081019061112391906136f2565b600080543391611156916001600160a01b0316908a908a90816105e857634e487b7160e01b600052603260045260246000fd5b8a612d50565b6000858561116b600182613f36565b81811061118857634e487b7160e01b600052603260045260246000fd5b905060200201602081019061119d91906136f2565b6040516370a0823160e01b81526001600160a01b03868116600483015291909116906370a082319060240160206040518083038186803b1580156111e057600080fd5b505afa1580156111f4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112189190613a22565b90506112588686808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508892506123ba915050565b8661089482888861126a600182613f36565b81811061128757634e487b7160e01b600052603260045260246000fd5b905060200201602081019061129c91906136f2565b6040516370a0823160e01b81526001600160a01b03898116600483015291909116906370a082319060240161083e565b6000805460015482916112ed916001600160a01b03918216918f91166122e1565b90506000866112fc578b611300565b6000195b60405163d505accf60e01b81529091506001600160a01b0383169063d505accf9061133b903390309086908e908d908d908d90600401613c4d565b600060405180830381600087803b15801561135557600080fd5b505af1158015611369573d6000803e3d6000fd5b5050505061137b8d8d8d8d8d8d610a94565b9d9c50505050505050505050505050565b606081428110156113af5760405162461bcd60e51b81526004016104d890613e60565b600180546001600160a01b031690879087906113cb9082613f36565b8181106113e857634e487b7160e01b600052603260045260246000fd5b90506020020160208101906113fd91906136f2565b6001600160a01b0316146114235760405162461bcd60e51b81526004016104d890613d5a565b600054604080516020808902828101820190935288825261146d936001600160a01b0316928c92918b918b91829190850190849080828437600092019190915250612b8692505050565b915086826001845161147f9190613f36565b8151811061149d57634e487b7160e01b600052603260045260246000fd5b60200260200101511015610f8f5760405162461bcd60e51b81526004016104d890613dd4565b60006108c984848461306e565b606081428110156114f35760405162461bcd60e51b81526004016104d890613e60565b600054604080516020808902828101820190935288825261153d936001600160a01b0316928c92918b918b918291908501908490808284376000920191909152506127b892505050565b9150868260008151811061156157634e487b7160e01b600052603260045260246000fd5b60200260200101511115610c915760405162461bcd60e51b81526004016104d890613e1c565b606081428110156115aa5760405162461bcd60e51b81526004016104d890613e60565b6001546001600160a01b031686866000816115d557634e487b7160e01b600052603260045260246000fd5b90506020020160208101906115ea91906136f2565b6001600160a01b0316146116105760405162461bcd60e51b81526004016104d890613d5a565b600054604080516020808902828101820190935288825261165a936001600160a01b0316928b92918b918b918291908501908490808284376000920191909152506127b892505050565b9150348260008151811061167e57634e487b7160e01b600052603260045260246000fd5b602002602001015111156116a45760405162461bcd60e51b81526004016104d890613e1c565b60015482516001600160a01b039091169063d0e30db09084906000906116da57634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b15801561170d57600080fd5b505af1158015611721573d6000803e3d6000fd5b5050600154600080546001600160a01b03928316955063a9059cbb94506117669350909116908a908a90816105e857634e487b7160e01b600052603260045260246000fd5b8460008151811061178757634e487b7160e01b600052603260045260246000fd5b60200260200101516040518363ffffffff1660e01b81526004016117c09291906001600160a01b03929092168252602082015260400190565b602060405180830381600087803b1580156117da57600080fd5b505af11580156117ee573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061181291906139b8565b61182c57634e487b7160e01b600052600160045260246000fd5b61186b82878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250899250612e80915050565b8160008151811061188c57634e487b7160e01b600052603260045260246000fd5b60200260200101513411156118d8576118d833836000815181106118c057634e487b7160e01b600052603260045260246000fd5b6020026020010151346118d39190613f36565b612abd565b5095945050505050565b60006108c9848484613144565b606081428110156119125760405162461bcd60e51b81526004016104d890613e60565b6001546001600160a01b0316868660008161193d57634e487b7160e01b600052603260045260246000fd5b905060200201602081019061195291906136f2565b6001600160a01b0316146119785760405162461bcd60e51b81526004016104d890613d5a565b60005460408051602080890282810182019093528882526119c2936001600160a01b0316923492918b918b91829190850190849080828437600092019190915250612b8692505050565b91508682600184516119d49190613f36565b815181106119f257634e487b7160e01b600052603260045260246000fd5b60200260200101511015611a185760405162461bcd60e51b81526004016104d890613dd4565b60015482516001600160a01b039091169063d0e30db0908490600090611a4e57634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b158015611a8157600080fd5b505af1158015611a95573d6000803e3d6000fd5b5050600154600080546001600160a01b03928316955063a9059cbb9450611ada9350909116908a908a90816105e857634e487b7160e01b600052603260045260246000fd5b84600081518110611afb57634e487b7160e01b600052603260045260246000fd5b60200260200101516040518363ffffffff1660e01b8152600401611b349291906001600160a01b03929092168252602082015260400190565b602060405180830381600087803b158015611b4e57600080fd5b505af1158015611b62573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b8691906139b8565b611ba057634e487b7160e01b600052600160045260246000fd5b6118d882878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250899250612e80915050565b8042811015611c005760405162461bcd60e51b81526004016104d890613e60565b600180546001600160a01b03169086908690611c1c9082613f36565b818110611c3957634e487b7160e01b600052603260045260246000fd5b9050602002016020810190611c4e91906136f2565b6001600160a01b031614611c745760405162461bcd60e51b81526004016104d890613d5a565b611c988585600081811061110e57634e487b7160e01b600052603260045260246000fd5b611cd68585808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152503092506123ba915050565b6001546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a082319060240160206040518083038186803b158015611d1a57600080fd5b505afa158015611d2e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d529190613a22565b905086811015611d745760405162461bcd60e51b81526004016104d890613dd4565b600154604051632e1a7d4d60e01b8152600481018390526001600160a01b0390911690632e1a7d4d90602401600060405180830381600087803b158015611dba57600080fd5b505af1158015611dce573d6000803e3d6000fd5b505050506108b28482612abd565b6000808242811015611e005760405162461bcd60e51b81526004016104d890613e60565b60008054611e18906001600160a01b03168c8c6122e1565b6040516323b872dd60e01b81523360048201526001600160a01b03821660248201819052604482018c90529192506323b872dd90606401602060405180830381600087803b158015611e6957600080fd5b505af1158015611e7d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ea191906139b8565b5060405163226bf2d160e21b81526001600160a01b03878116600483015260009182918416906389afcb44906024016040805180830381600087803b158015611ee957600080fd5b505af1158015611efd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f219190613b72565b915091506000611f318e8e6131e1565b509050806001600160a01b03168e6001600160a01b031614611f54578183611f57565b82825b90975095508a871015611f7c5760405162461bcd60e51b81526004016104d890613d17565b89861015611f9c5760405162461bcd60e51b81526004016104d890613d91565b505050505097509795505050505050565b6000546060906109c6906001600160a01b03168484612b86565b60008060008342811015611fed5760405162461bcd60e51b81526004016104d890613e60565b600154612008908b906001600160a01b03168b348c8c6132d6565b600080546001549397509195509161202e916001600160a01b03908116918e91166122e1565b905061203c8b338388612d50565b600160009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0856040518263ffffffff1660e01b81526004016000604051808303818588803b15801561208c57600080fd5b505af11580156120a0573d6000803e3d6000fd5b505060015460405163a9059cbb60e01b81526001600160a01b038681166004830152602482018a9052909116935063a9059cbb92506044019050602060405180830381600087803b1580156120f457600080fd5b505af1158015612108573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061212c91906139b8565b61214657634e487b7160e01b600052600160045260246000fd5b6040516335313c2160e11b81526001600160a01b038881166004830152821690636a62784290602401602060405180830381600087803b15801561218957600080fd5b505af115801561219d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121c19190613a22565b9250833411156121d9576121d9336118d38634613f36565b505096509650969350505050565b6000806000834281101561220d5760405162461bcd60e51b81526004016104d890613e60565b61221b8c8c8c8c8c8c6132d6565b600080549296509094509061223a906001600160a01b03168e8e6122e1565b90506122488d338388612d50565b6122548c338387612d50565b6040516335313c2160e11b81526001600160a01b038881166004830152821690636a62784290602401602060405180830381600087803b15801561229757600080fd5b505af11580156122ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122cf9190613a22565b92505050985098509895505050505050565b60008060006122f085856131e1565b6040516bffffffffffffffffffffffff19606084811b8216602084015283901b16603482015291935091508690604801604051602081830303815290604052805190602001206040516020016123989291906001600160f81b0319815260609290921b6bffffffffffffffffffffffff1916600183015260158201527ff264d894da1070a80bc479a9f9b59d7dba3d8977144aba4ba53652b31ab31da1603582015260550190565b60408051601f1981840301815291905280516020909101209695505050505050565b60005b600183516123cb9190613f36565b811015612682576000808483815181106123f557634e487b7160e01b600052603260045260246000fd5b60200260200101518584600161240b9190613edf565b8151811061242957634e487b7160e01b600052603260045260246000fd5b602002602001015191509150600061244183836131e1565b50600080549192509061245e906001600160a01b031685856122e1565b9050600080600080846001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b15801561249f57600080fd5b505afa1580156124b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124d791906139d4565b506001600160701b031691506001600160701b03169150600080876001600160a01b03168a6001600160a01b031614612511578284612514565b83835b6040516370a0823160e01b81526001600160a01b038a8116600483015292945090925061254f918491908d16906370a082319060240161083e565b955061255c8683836126dd565b945050505050600080856001600160a01b0316886001600160a01b0316146125865782600061258a565b6000835b91509150600060028c5161259e9190613f36565b8a106125aa578a6125f2565b6000546125f2906001600160a01b0316898e6125c78e6002613edf565b815181106125e557634e487b7160e01b600052603260045260246000fd5b60200260200101516122e1565b6040805160008152602081019182905263022c0d9f60e01b9091529091506001600160a01b0387169063022c0d9f906126349086908690869060248101613e8f565b600060405180830381600087803b15801561264e57600080fd5b505af1158015612662573d6000803e3d6000fd5b50505050505050505050505050808061267a90613f90565b9150506123bd565b505050565b6000826126948382613f36565b91508111156109c95760405162461bcd60e51b815260206004820152601560248201527464732d6d6174682d7375622d756e646572666c6f7760581b60448201526064016104d8565b600080841161273f5760405162461bcd60e51b815260206004820152602860248201527f4b61694465784c6962726172793a20494e53554646494349454e545f494e50556044820152671517d05353d5539560c21b60648201526084016104d8565b60008311801561274f5750600082115b61276b5760405162461bcd60e51b81526004016104d890613cd2565b6000612779856103e56134d6565b9050600061278782856134d6565b905060006127a18361279b886103e86134d6565b9061353d565b90506127ad8183613ef7565b979650505050505050565b606060028251101561280c5760405162461bcd60e51b815260206004820152601b60248201527f4b61694465784c6962726172793a20494e56414c49445f50415448000000000060448201526064016104d8565b815167ffffffffffffffff81111561283457634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561285d578160200160208202803683370190505b5090508281600183516128709190613f36565b8151811061288e57634e487b7160e01b600052603260045260246000fd5b6020026020010181815250506000600183516128aa9190613f36565b90505b801561299a5760008061291987866128c6600187613f36565b815181106128e457634e487b7160e01b600052603260045260246000fd5b602002602001015187868151811061290c57634e487b7160e01b600052603260045260246000fd5b6020026020010151613592565b9150915061294f84848151811061294057634e487b7160e01b600052603260045260246000fd5b6020026020010151838361306e565b8461295b600186613f36565b8151811061297957634e487b7160e01b600052603260045260246000fd5b6020026020010181815250505050808061299290613f79565b9150506128ad565b509392505050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b17905291516000928392908716916129fe9190613c31565b6000604051808303816000865af19150503d8060008114612a3b576040519150601f19603f3d011682016040523d82523d6000602084013e612a40565b606091505b5091509150818015612a6a575080511580612a6a575080806020019051810190612a6a91906139b8565b612ab65760405162461bcd60e51b815260206004820152601f60248201527f5472616e7366657248656c7065723a205452414e534645525f4641494c45440060448201526064016104d8565b5050505050565b604080516000808252602082019092526001600160a01b038416908390604051612ae79190613c31565b60006040518083038185875af1925050503d8060008114612b24576040519150601f19603f3d011682016040523d82523d6000602084013e612b29565b606091505b50509050806126825760405162461bcd60e51b815260206004820152602360248201527f5472616e7366657248656c7065723a204b41495f5452414e534645525f46414960448201526213115160ea1b60648201526084016104d8565b6060600282511015612bda5760405162461bcd60e51b815260206004820152601b60248201527f4b61694465784c6962726172793a20494e56414c49445f50415448000000000060448201526064016104d8565b815167ffffffffffffffff811115612c0257634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015612c2b578160200160208202803683370190505b5090508281600081518110612c5057634e487b7160e01b600052603260045260246000fd5b60200260200101818152505060005b60018351612c6d9190613f36565b81101561299a57600080612ccf87868581518110612c9b57634e487b7160e01b600052603260045260246000fd5b602002602001015187866001612cb19190613edf565b8151811061290c57634e487b7160e01b600052603260045260246000fd5b91509150612d05848481518110612cf657634e487b7160e01b600052603260045260246000fd5b602002602001015183836126dd565b84612d11856001613edf565b81518110612d2f57634e487b7160e01b600052603260045260246000fd5b60200260200101818152505050508080612d4890613f90565b915050612c5f565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b1790529151600092839290881691612db49190613c31565b6000604051808303816000865af19150503d8060008114612df1576040519150601f19603f3d011682016040523d82523d6000602084013e612df6565b606091505b5091509150818015612e20575080511580612e20575080806020019051810190612e2091906139b8565b612e785760405162461bcd60e51b8152602060048201526024808201527f5472616e7366657248656c7065723a205452414e534645525f46524f4d5f46416044820152631253115160e21b60648201526084016104d8565b505050505050565b60005b60018351612e919190613f36565b81101561306857600080848381518110612ebb57634e487b7160e01b600052603260045260246000fd5b602002602001015185846001612ed19190613edf565b81518110612eef57634e487b7160e01b600052603260045260246000fd5b6020026020010151915091506000612f0783836131e1565b509050600087612f18866001613edf565b81518110612f3657634e487b7160e01b600052603260045260246000fd5b60200260200101519050600080836001600160a01b0316866001600160a01b031614612f6457826000612f68565b6000835b91509150600060028a51612f7c9190613f36565b8810612f885788612fa5565b600054612fa5906001600160a01b0316878c6125c78c6002613edf565b600054909150612fbf906001600160a01b031688886122e1565b6001600160a01b031663022c0d9f84848460006040519080825280601f01601f191660200182016040528015612ffc576020820181803683370190505b506040518563ffffffff1660e01b815260040161301c9493929190613e8f565b600060405180830381600087803b15801561303657600080fd5b505af115801561304a573d6000803e3d6000fd5b5050505050505050505050808061306090613f90565b915050612e83565b50505050565b60008084116130d15760405162461bcd60e51b815260206004820152602960248201527f4b61694465784c6962726172793a20494e53554646494349454e545f4f555450604482015268155517d05353d5539560ba1b60648201526084016104d8565b6000831180156130e15750600082115b6130fd5760405162461bcd60e51b81526004016104d890613cd2565b60006131156103e861310f86886134d6565b906134d6565b905060006131296103e561310f8689612687565b905061313a600161279b8385613ef7565b9695505050505050565b60008084116131a05760405162461bcd60e51b815260206004820152602260248201527f4b61694465784c6962726172793a20494e53554646494349454e545f414d4f55604482015261139560f21b60648201526084016104d8565b6000831180156131b05750600082115b6131cc5760405162461bcd60e51b81526004016104d890613cd2565b826131d785846134d6565b6108c99190613ef7565b600080826001600160a01b0316846001600160a01b031614156132515760405162461bcd60e51b815260206004820152602260248201527f4b61694465784c6962726172793a204944454e544943414c5f41444452455353604482015261455360f01b60648201526084016104d8565b826001600160a01b0316846001600160a01b031610613271578284613274565b83835b90925090506001600160a01b0382166132cf5760405162461bcd60e51b815260206004820152601b60248201527f4b61694465784c6962726172793a205a45524f5f41444452455353000000000060448201526064016104d8565b9250929050565b6000805460405163e6a4390560e01b81526001600160a01b03898116600483015288811660248301528392839291169063e6a439059060440160206040518083038186803b15801561332757600080fd5b505afa15801561333b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061335f9190613715565b6001600160a01b031614156133f7576000546040516364e329cb60e11b81526001600160a01b038a8116600483015289811660248301529091169063c9c6539690604401602060405180830381600087803b1580156133bd57600080fd5b505af11580156133d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133f59190613715565b505b600080548190613411906001600160a01b03168b8b613592565b91509150816000148015613423575080155b15613433578793508692506134c9565b6000613440898484613144565b905087811161347457858110156134695760405162461bcd60e51b81526004016104d890613d91565b8894509250826134c7565b6000613481898486613144565b9050898111156134a157634e487b7160e01b600052600160045260246000fd5b878110156134c15760405162461bcd60e51b81526004016104d890613d17565b94508793505b505b5050965096945050505050565b60008115806134fa575082826134ec8183613f17565b92506134f89083613ef7565b145b6109c95760405162461bcd60e51b815260206004820152601460248201527364732d6d6174682d6d756c2d6f766572666c6f7760601b60448201526064016104d8565b60008261354a8382613edf565b91508110156109c95760405162461bcd60e51b815260206004820152601460248201527364732d6d6174682d6164642d6f766572666c6f7760601b60448201526064016104d8565b60008060006135a185856131e1565b5090506135af8686866122e1565b506000806135be8888886122e1565b6001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b1580156135f657600080fd5b505afa15801561360a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061362e91906139d4565b506001600160701b031691506001600160701b03169150826001600160a01b0316876001600160a01b031614613665578082613668565b81815b90999098509650505050505050565b803561368281613fd7565b919050565b60008083601f840112613698578081fd5b50813567ffffffffffffffff8111156136af578182fd5b6020830191508360208260051b85010111156132cf57600080fd5b80516001600160701b038116811461368257600080fd5b803560ff8116811461368257600080fd5b600060208284031215613703578081fd5b813561370e81613fd7565b9392505050565b600060208284031215613726578081fd5b815161370e81613fd7565b600080600080600080600060e0888a03121561374b578283fd5b873561375681613fd7565b9650602088013561376681613fd7565b955060408801359450606088013593506080880135925060a088013561378b81613fd7565b8092505060c0880135905092959891949750929550565b60008060008060008060008060008060006101608c8e0312156137c3578384fd5b8b356137ce81613fd7565b9a5060208c01356137de81613fd7565b995060408c0135985060608c0135975060808c0135965060a08c013561380381613fd7565b955060c08c0135945060e08c013561381a81613fef565b93506138296101008d016136e1565b92506101208c013591506101408c013590509295989b509295989b9093969950565b600080600080600080600080610100898b031215613867578384fd5b883561387281613fd7565b9750602089013561388281613fd7565b965060408901359550606089013594506080890135935060a0890135925060c08901356138ae81613fd7565b8092505060e089013590509295985092959890939650565b60008060008060008060c087890312156138de578182fd5b86356138e981613fd7565b9550602087013594506040870135935060608701359250608087013561390e81613fd7565b8092505060a087013590509295509295509295565b6000806000806000806000806000806101408b8d031215613942578384fd5b8a3561394d81613fd7565b995060208b0135985060408b0135975060608b0135965060808b013561397281613fd7565b955060a08b0135945060c08b013561398981613fef565b935061399760e08c016136e1565b92506101008b013591506101208b013590509295989b9194979a5092959850565b6000602082840312156139c9578081fd5b815161370e81613fef565b6000806000606084860312156139e8578081fd5b6139f1846136ca565b92506139ff602085016136ca565b9150604084015163ffffffff81168114613a17578182fd5b809150509250925092565b600060208284031215613a33578081fd5b5051919050565b600080600080600060808688031215613a51578283fd5b85359450602086013567ffffffffffffffff811115613a6e578384fd5b613a7a88828901613687565b9095509350506040860135613a8e81613fd7565b949793965091946060013592915050565b60008060408385031215613ab1578182fd5b8235915060208084013567ffffffffffffffff80821115613ad0578384fd5b818601915086601f830112613ae3578384fd5b813581811115613af557613af5613fc1565b8060051b604051601f19603f83011681018181108582111715613b1a57613b1a613fc1565b604052828152858101935084860182860187018b1015613b38578788fd5b8795505b83861015613b6157613b4d81613677565b855260019590950194938601938601613b3c565b508096505050505050509250929050565b60008060408385031215613b84578182fd5b505080516020909101519092909150565b60008060008060008060a08789031215613bad578384fd5b8635955060208701359450604087013567ffffffffffffffff811115613bd1578485fd5b613bdd89828a01613687565b9095509350506060870135613bf181613fd7565b80925050608087013590509295509295509295565b600080600060608486031215613c1a578081fd5b505081359360208301359350604090920135919050565b60008251613c43818460208701613f4d565b9190910192915050565b6001600160a01b0397881681529590961660208601526040850193909352606084019190915260ff16608083015260a082015260c081019190915260e00190565b6020808252825182820181905260009190848201906040850190845b81811015613cc657835183529284019291840191600101613caa565b50909695505050505050565b60208082526025908201527f4b61694465784c6962726172793a20494e53554646494349454e545f4c495155604082015264494449545960d81b606082015260800190565b60208082526023908201527f4b6169446578526f757465723a20494e53554646494349454e545f415f414d4f60408201526215539560ea1b606082015260800190565b6020808252601a908201527f4b6169446578526f757465723a20494e56414c49445f50415448000000000000604082015260600190565b60208082526023908201527f4b6169446578526f757465723a20494e53554646494349454e545f425f414d4f60408201526215539560ea1b606082015260800190565b60208082526028908201527f4b6169446578526f757465723a20494e53554646494349454e545f4f555450556040820152671517d05353d5539560c21b606082015260800190565b60208082526024908201527f4b6169446578526f757465723a204558434553534956455f494e5055545f414d60408201526313d5539560e21b606082015260800190565b60208082526015908201527412d85a51195e149bdd5d195c8e8811561412549151605a1b604082015260600190565b84815283602082015260018060a01b03831660408201526080606082015260008251806080840152613ec88160a0850160208701613f4d565b601f01601f19169190910160a00195945050505050565b60008219821115613ef257613ef2613fab565b500190565b600082613f1257634e487b7160e01b81526012600452602481fd5b500490565b6000816000190483118215151615613f3157613f31613fab565b500290565b600082821015613f4857613f48613fab565b500390565b60005b83811015613f68578181015183820152602001613f50565b838111156130685750506000910152565b600081613f8857613f88613fab565b506000190190565b6000600019821415613fa457613fa4613fab565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b0381168114613fec57600080fd5b50565b8015158114613fec57600080fdfea2646970667358221220805a0c05e28b179b1aef617ac1335c58028214c58e34fe0761a79fbfc9ab8c0764736f6c63430008040033000000000000000000000000c9567a8b6b622cdc8076c6b4432ade0e11f50da1000000000000000000000000af984e23eaa3e7967f3c5e007fbe397d8566d23d

Deployed ByteCode

0x60806040526004361061014f5760003560e01c80636937ffb6116100b6578063b6a046b31161006f578063b6a046b3146103e9578063baa2abde14610409578063c45a015514610429578063d06ca61f14610449578063d06ebe7d14610469578063e8e337001461049757600080fd5b80636937ffb61461034357806385f8c259146103635780638803dbee146103835780639df02c72146103a3578063ad615dec146103b6578063b62761fa146103d657600080fd5b806338ed17391161010857806338ed17391461026b57806356732d731461028b57806356b2cc2f146102ab5780635c11d795146102cb578063629f4306146102eb578063631722db1461030b57600080fd5b806304d464b114610183578063054d50d4146101965780630c7e815a146101c95780631f00ca74146101fe5780632195995c1461022b57806323da1b271461024b57600080fd5b3661017e576001546001600160a01b0316331461017c57634e487b7160e01b600052600160045260246000fd5b005b600080fd5b61017c610191366004613a3a565b6104b7565b3480156101a257600080fd5b506101b66101b1366004613c06565b6108bc565b6040519081526020015b60405180910390f35b3480156101d557600080fd5b506101e96101e4366004613923565b6108d1565b604080519283526020830191909152016101c0565b34801561020a57600080fd5b5061021e610219366004613a9f565b6109ac565b6040516101c09190613c8e565b34801561023757600080fd5b506101e96102463660046137a2565b6109cf565b34801561025757600080fd5b506101b66102663660046138c6565b610a94565b34801561027757600080fd5b5061021e610286366004613b95565b610bce565b34801561029757600080fd5b506101e96102a63660046138c6565b610da0565b3480156102b757600080fd5b5061021e6102c6366004613b95565b610e64565b3480156102d757600080fd5b5061017c6102e6366004613b95565b6110c9565b3480156102f757600080fd5b506101b6610306366004613923565b6112cc565b34801561031757600080fd5b5060015461032b906001600160a01b031681565b6040516001600160a01b0390911681526020016101c0565b34801561034f57600080fd5b5061021e61035e366004613b95565b61138c565b34801561036f57600080fd5b506101b661037e366004613c06565b6114c3565b34801561038f57600080fd5b5061021e61039e366004613b95565b6114d0565b61021e6103b1366004613a3a565b611587565b3480156103c257600080fd5b506101b66103d1366004613c06565b6118e2565b61021e6103e4366004613a3a565b6118ef565b3480156103f557600080fd5b5061017c610404366004613b95565b611bdf565b34801561041557600080fd5b506101e9610424366004613731565b611ddc565b34801561043557600080fd5b5060005461032b906001600160a01b031681565b34801561045557600080fd5b5061021e610464366004613a9f565b611fad565b61047c6104773660046138c6565b611fc7565b604080519384526020840192909252908201526060016101c0565b3480156104a357600080fd5b5061047c6104b236600461384b565b6121e7565b80428110156104e15760405162461bcd60e51b81526004016104d890613e60565b60405180910390fd5b6001546001600160a01b0316858560008161050c57634e487b7160e01b600052603260045260246000fd5b905060200201602081019061052191906136f2565b6001600160a01b0316146105475760405162461bcd60e51b81526004016104d890613d5a565b60015460408051630d0e30db60e41b8152905134926001600160a01b03169163d0e30db091849160048082019260009290919082900301818588803b15801561058f57600080fd5b505af11580156105a3573d6000803e3d6000fd5b5050600154600080546001600160a01b03928316955063a9059cbb94506106389350909116908a908a90816105e857634e487b7160e01b600052603260045260246000fd5b90506020020160208101906105fd91906136f2565b8a8a600181811061061e57634e487b7160e01b600052603260045260246000fd5b905060200201602081019061063391906136f2565b6122e1565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260248101849052604401602060405180830381600087803b15801561068057600080fd5b505af1158015610694573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106b891906139b8565b6106d257634e487b7160e01b600052600160045260246000fd5b600086866106e1600182613f36565b8181106106fe57634e487b7160e01b600052603260045260246000fd5b905060200201602081019061071391906136f2565b6040516370a0823160e01b81526001600160a01b03878116600483015291909116906370a082319060240160206040518083038186803b15801561075657600080fd5b505afa15801561076a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061078e9190613a22565b90506107ce8787808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508992506123ba915050565b876108948289896107e0600182613f36565b8181106107fd57634e487b7160e01b600052603260045260246000fd5b905060200201602081019061081291906136f2565b6040516370a0823160e01b81526001600160a01b038a8116600483015291909116906370a08231906024015b60206040518083038186803b15801561085657600080fd5b505afa15801561086a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061088e9190613a22565b90612687565b10156108b25760405162461bcd60e51b81526004016104d890613dd4565b5050505050505050565b60006108c98484846126dd565b949350505050565b600080600061090960008054906101000a90046001600160a01b03168e600160009054906101000a90046001600160a01b03166122e1565b9050600087610918578c61091c565b6000195b60405163d505accf60e01b81529091506001600160a01b0383169063d505accf90610957903390309086908f908e908e908e90600401613c4d565b600060405180830381600087803b15801561097157600080fd5b505af1158015610985573d6000803e3d6000fd5b505050506109978e8e8e8e8e8e610da0565b909f909e509c50505050505050505050505050565b6000546060906109c6906001600160a01b031684846127b8565b90505b92915050565b60008054819081906109eb906001600160a01b03168f8f6122e1565b90506000876109fa578c6109fe565b6000195b60405163d505accf60e01b81529091506001600160a01b0383169063d505accf90610a39903390309086908f908e908e908e90600401613c4d565b600060405180830381600087803b158015610a5357600080fd5b505af1158015610a67573d6000803e3d6000fd5b50505050610a7a8f8f8f8f8f8f8f611ddc565b809450819550505050509b509b9950505050505050505050565b60008142811015610ab75760405162461bcd60e51b81526004016104d890613e60565b600154610ad39089906001600160a01b03168989893089611ddc565b6040516370a0823160e01b8152306004820152909350610b5b9150899086906001600160a01b038316906370a082319060240160206040518083038186803b158015610b1e57600080fd5b505afa158015610b32573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b569190613a22565b6129a2565b600154604051632e1a7d4d60e01b8152600481018490526001600160a01b0390911690632e1a7d4d90602401600060405180830381600087803b158015610ba157600080fd5b505af1158015610bb5573d6000803e3d6000fd5b50505050610bc38483612abd565b509695505050505050565b60608142811015610bf15760405162461bcd60e51b81526004016104d890613e60565b6000546040805160208089028281018201909352888252610c3b936001600160a01b0316928c92918b918b91829190850190849080828437600092019190915250612b8692505050565b9150868260018451610c4d9190613f36565b81518110610c6b57634e487b7160e01b600052603260045260246000fd5b60200260200101511015610c915760405162461bcd60e51b81526004016104d890613dd4565b610d6186866000818110610cb557634e487b7160e01b600052603260045260246000fd5b9050602002016020810190610cca91906136f2565b600080543391610d33916001600160a01b0316908b908b9081610cfd57634e487b7160e01b600052603260045260246000fd5b9050602002016020810190610d1291906136f2565b8b8b600181811061061e57634e487b7160e01b600052603260045260246000fd5b85600081518110610d5457634e487b7160e01b600052603260045260246000fd5b6020026020010151612d50565b610bc382878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250899250612e80915050565b6000808242811015610dc45760405162461bcd60e51b81526004016104d890613e60565b600154610de0908a906001600160a01b03168a8a8a308a611ddc565b9093509150610df08986856129a2565b600154604051632e1a7d4d60e01b8152600481018490526001600160a01b0390911690632e1a7d4d90602401600060405180830381600087803b158015610e3657600080fd5b505af1158015610e4a573d6000803e3d6000fd5b50505050610e588583612abd565b50965096945050505050565b60608142811015610e875760405162461bcd60e51b81526004016104d890613e60565b600180546001600160a01b03169087908790610ea39082613f36565b818110610ec057634e487b7160e01b600052603260045260246000fd5b9050602002016020810190610ed591906136f2565b6001600160a01b031614610efb5760405162461bcd60e51b81526004016104d890613d5a565b6000546040805160208089028281018201909352888252610f45936001600160a01b0316928c92918b918b918291908501908490808284376000920191909152506127b892505050565b91508682600081518110610f6957634e487b7160e01b600052603260045260246000fd5b60200260200101511115610f8f5760405162461bcd60e51b81526004016104d890613e1c565b610fb386866000818110610cb557634e487b7160e01b600052603260045260246000fd5b610ff282878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250309250612e80915050565b6001805483516001600160a01b0390911691632e1a7d4d91859161101591613f36565b8151811061103357634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b815260040161105991815260200190565b600060405180830381600087803b15801561107357600080fd5b505af1158015611087573d6000803e3d6000fd5b50505050610bc384836001855161109e9190613f36565b815181106110bc57634e487b7160e01b600052603260045260246000fd5b6020026020010151612abd565b80428110156110ea5760405162461bcd60e51b81526004016104d890613e60565b61115c8585600081811061110e57634e487b7160e01b600052603260045260246000fd5b905060200201602081019061112391906136f2565b600080543391611156916001600160a01b0316908a908a90816105e857634e487b7160e01b600052603260045260246000fd5b8a612d50565b6000858561116b600182613f36565b81811061118857634e487b7160e01b600052603260045260246000fd5b905060200201602081019061119d91906136f2565b6040516370a0823160e01b81526001600160a01b03868116600483015291909116906370a082319060240160206040518083038186803b1580156111e057600080fd5b505afa1580156111f4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112189190613a22565b90506112588686808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508892506123ba915050565b8661089482888861126a600182613f36565b81811061128757634e487b7160e01b600052603260045260246000fd5b905060200201602081019061129c91906136f2565b6040516370a0823160e01b81526001600160a01b03898116600483015291909116906370a082319060240161083e565b6000805460015482916112ed916001600160a01b03918216918f91166122e1565b90506000866112fc578b611300565b6000195b60405163d505accf60e01b81529091506001600160a01b0383169063d505accf9061133b903390309086908e908d908d908d90600401613c4d565b600060405180830381600087803b15801561135557600080fd5b505af1158015611369573d6000803e3d6000fd5b5050505061137b8d8d8d8d8d8d610a94565b9d9c50505050505050505050505050565b606081428110156113af5760405162461bcd60e51b81526004016104d890613e60565b600180546001600160a01b031690879087906113cb9082613f36565b8181106113e857634e487b7160e01b600052603260045260246000fd5b90506020020160208101906113fd91906136f2565b6001600160a01b0316146114235760405162461bcd60e51b81526004016104d890613d5a565b600054604080516020808902828101820190935288825261146d936001600160a01b0316928c92918b918b91829190850190849080828437600092019190915250612b8692505050565b915086826001845161147f9190613f36565b8151811061149d57634e487b7160e01b600052603260045260246000fd5b60200260200101511015610f8f5760405162461bcd60e51b81526004016104d890613dd4565b60006108c984848461306e565b606081428110156114f35760405162461bcd60e51b81526004016104d890613e60565b600054604080516020808902828101820190935288825261153d936001600160a01b0316928c92918b918b918291908501908490808284376000920191909152506127b892505050565b9150868260008151811061156157634e487b7160e01b600052603260045260246000fd5b60200260200101511115610c915760405162461bcd60e51b81526004016104d890613e1c565b606081428110156115aa5760405162461bcd60e51b81526004016104d890613e60565b6001546001600160a01b031686866000816115d557634e487b7160e01b600052603260045260246000fd5b90506020020160208101906115ea91906136f2565b6001600160a01b0316146116105760405162461bcd60e51b81526004016104d890613d5a565b600054604080516020808902828101820190935288825261165a936001600160a01b0316928b92918b918b918291908501908490808284376000920191909152506127b892505050565b9150348260008151811061167e57634e487b7160e01b600052603260045260246000fd5b602002602001015111156116a45760405162461bcd60e51b81526004016104d890613e1c565b60015482516001600160a01b039091169063d0e30db09084906000906116da57634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b15801561170d57600080fd5b505af1158015611721573d6000803e3d6000fd5b5050600154600080546001600160a01b03928316955063a9059cbb94506117669350909116908a908a90816105e857634e487b7160e01b600052603260045260246000fd5b8460008151811061178757634e487b7160e01b600052603260045260246000fd5b60200260200101516040518363ffffffff1660e01b81526004016117c09291906001600160a01b03929092168252602082015260400190565b602060405180830381600087803b1580156117da57600080fd5b505af11580156117ee573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061181291906139b8565b61182c57634e487b7160e01b600052600160045260246000fd5b61186b82878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250899250612e80915050565b8160008151811061188c57634e487b7160e01b600052603260045260246000fd5b60200260200101513411156118d8576118d833836000815181106118c057634e487b7160e01b600052603260045260246000fd5b6020026020010151346118d39190613f36565b612abd565b5095945050505050565b60006108c9848484613144565b606081428110156119125760405162461bcd60e51b81526004016104d890613e60565b6001546001600160a01b0316868660008161193d57634e487b7160e01b600052603260045260246000fd5b905060200201602081019061195291906136f2565b6001600160a01b0316146119785760405162461bcd60e51b81526004016104d890613d5a565b60005460408051602080890282810182019093528882526119c2936001600160a01b0316923492918b918b91829190850190849080828437600092019190915250612b8692505050565b91508682600184516119d49190613f36565b815181106119f257634e487b7160e01b600052603260045260246000fd5b60200260200101511015611a185760405162461bcd60e51b81526004016104d890613dd4565b60015482516001600160a01b039091169063d0e30db0908490600090611a4e57634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b158015611a8157600080fd5b505af1158015611a95573d6000803e3d6000fd5b5050600154600080546001600160a01b03928316955063a9059cbb9450611ada9350909116908a908a90816105e857634e487b7160e01b600052603260045260246000fd5b84600081518110611afb57634e487b7160e01b600052603260045260246000fd5b60200260200101516040518363ffffffff1660e01b8152600401611b349291906001600160a01b03929092168252602082015260400190565b602060405180830381600087803b158015611b4e57600080fd5b505af1158015611b62573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b8691906139b8565b611ba057634e487b7160e01b600052600160045260246000fd5b6118d882878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250899250612e80915050565b8042811015611c005760405162461bcd60e51b81526004016104d890613e60565b600180546001600160a01b03169086908690611c1c9082613f36565b818110611c3957634e487b7160e01b600052603260045260246000fd5b9050602002016020810190611c4e91906136f2565b6001600160a01b031614611c745760405162461bcd60e51b81526004016104d890613d5a565b611c988585600081811061110e57634e487b7160e01b600052603260045260246000fd5b611cd68585808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152503092506123ba915050565b6001546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a082319060240160206040518083038186803b158015611d1a57600080fd5b505afa158015611d2e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d529190613a22565b905086811015611d745760405162461bcd60e51b81526004016104d890613dd4565b600154604051632e1a7d4d60e01b8152600481018390526001600160a01b0390911690632e1a7d4d90602401600060405180830381600087803b158015611dba57600080fd5b505af1158015611dce573d6000803e3d6000fd5b505050506108b28482612abd565b6000808242811015611e005760405162461bcd60e51b81526004016104d890613e60565b60008054611e18906001600160a01b03168c8c6122e1565b6040516323b872dd60e01b81523360048201526001600160a01b03821660248201819052604482018c90529192506323b872dd90606401602060405180830381600087803b158015611e6957600080fd5b505af1158015611e7d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ea191906139b8565b5060405163226bf2d160e21b81526001600160a01b03878116600483015260009182918416906389afcb44906024016040805180830381600087803b158015611ee957600080fd5b505af1158015611efd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f219190613b72565b915091506000611f318e8e6131e1565b509050806001600160a01b03168e6001600160a01b031614611f54578183611f57565b82825b90975095508a871015611f7c5760405162461bcd60e51b81526004016104d890613d17565b89861015611f9c5760405162461bcd60e51b81526004016104d890613d91565b505050505097509795505050505050565b6000546060906109c6906001600160a01b03168484612b86565b60008060008342811015611fed5760405162461bcd60e51b81526004016104d890613e60565b600154612008908b906001600160a01b03168b348c8c6132d6565b600080546001549397509195509161202e916001600160a01b03908116918e91166122e1565b905061203c8b338388612d50565b600160009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0856040518263ffffffff1660e01b81526004016000604051808303818588803b15801561208c57600080fd5b505af11580156120a0573d6000803e3d6000fd5b505060015460405163a9059cbb60e01b81526001600160a01b038681166004830152602482018a9052909116935063a9059cbb92506044019050602060405180830381600087803b1580156120f457600080fd5b505af1158015612108573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061212c91906139b8565b61214657634e487b7160e01b600052600160045260246000fd5b6040516335313c2160e11b81526001600160a01b038881166004830152821690636a62784290602401602060405180830381600087803b15801561218957600080fd5b505af115801561219d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121c19190613a22565b9250833411156121d9576121d9336118d38634613f36565b505096509650969350505050565b6000806000834281101561220d5760405162461bcd60e51b81526004016104d890613e60565b61221b8c8c8c8c8c8c6132d6565b600080549296509094509061223a906001600160a01b03168e8e6122e1565b90506122488d338388612d50565b6122548c338387612d50565b6040516335313c2160e11b81526001600160a01b038881166004830152821690636a62784290602401602060405180830381600087803b15801561229757600080fd5b505af11580156122ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122cf9190613a22565b92505050985098509895505050505050565b60008060006122f085856131e1565b6040516bffffffffffffffffffffffff19606084811b8216602084015283901b16603482015291935091508690604801604051602081830303815290604052805190602001206040516020016123989291906001600160f81b0319815260609290921b6bffffffffffffffffffffffff1916600183015260158201527ff264d894da1070a80bc479a9f9b59d7dba3d8977144aba4ba53652b31ab31da1603582015260550190565b60408051601f1981840301815291905280516020909101209695505050505050565b60005b600183516123cb9190613f36565b811015612682576000808483815181106123f557634e487b7160e01b600052603260045260246000fd5b60200260200101518584600161240b9190613edf565b8151811061242957634e487b7160e01b600052603260045260246000fd5b602002602001015191509150600061244183836131e1565b50600080549192509061245e906001600160a01b031685856122e1565b9050600080600080846001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b15801561249f57600080fd5b505afa1580156124b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124d791906139d4565b506001600160701b031691506001600160701b03169150600080876001600160a01b03168a6001600160a01b031614612511578284612514565b83835b6040516370a0823160e01b81526001600160a01b038a8116600483015292945090925061254f918491908d16906370a082319060240161083e565b955061255c8683836126dd565b945050505050600080856001600160a01b0316886001600160a01b0316146125865782600061258a565b6000835b91509150600060028c5161259e9190613f36565b8a106125aa578a6125f2565b6000546125f2906001600160a01b0316898e6125c78e6002613edf565b815181106125e557634e487b7160e01b600052603260045260246000fd5b60200260200101516122e1565b6040805160008152602081019182905263022c0d9f60e01b9091529091506001600160a01b0387169063022c0d9f906126349086908690869060248101613e8f565b600060405180830381600087803b15801561264e57600080fd5b505af1158015612662573d6000803e3d6000fd5b50505050505050505050505050808061267a90613f90565b9150506123bd565b505050565b6000826126948382613f36565b91508111156109c95760405162461bcd60e51b815260206004820152601560248201527464732d6d6174682d7375622d756e646572666c6f7760581b60448201526064016104d8565b600080841161273f5760405162461bcd60e51b815260206004820152602860248201527f4b61694465784c6962726172793a20494e53554646494349454e545f494e50556044820152671517d05353d5539560c21b60648201526084016104d8565b60008311801561274f5750600082115b61276b5760405162461bcd60e51b81526004016104d890613cd2565b6000612779856103e56134d6565b9050600061278782856134d6565b905060006127a18361279b886103e86134d6565b9061353d565b90506127ad8183613ef7565b979650505050505050565b606060028251101561280c5760405162461bcd60e51b815260206004820152601b60248201527f4b61694465784c6962726172793a20494e56414c49445f50415448000000000060448201526064016104d8565b815167ffffffffffffffff81111561283457634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561285d578160200160208202803683370190505b5090508281600183516128709190613f36565b8151811061288e57634e487b7160e01b600052603260045260246000fd5b6020026020010181815250506000600183516128aa9190613f36565b90505b801561299a5760008061291987866128c6600187613f36565b815181106128e457634e487b7160e01b600052603260045260246000fd5b602002602001015187868151811061290c57634e487b7160e01b600052603260045260246000fd5b6020026020010151613592565b9150915061294f84848151811061294057634e487b7160e01b600052603260045260246000fd5b6020026020010151838361306e565b8461295b600186613f36565b8151811061297957634e487b7160e01b600052603260045260246000fd5b6020026020010181815250505050808061299290613f79565b9150506128ad565b509392505050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b17905291516000928392908716916129fe9190613c31565b6000604051808303816000865af19150503d8060008114612a3b576040519150601f19603f3d011682016040523d82523d6000602084013e612a40565b606091505b5091509150818015612a6a575080511580612a6a575080806020019051810190612a6a91906139b8565b612ab65760405162461bcd60e51b815260206004820152601f60248201527f5472616e7366657248656c7065723a205452414e534645525f4641494c45440060448201526064016104d8565b5050505050565b604080516000808252602082019092526001600160a01b038416908390604051612ae79190613c31565b60006040518083038185875af1925050503d8060008114612b24576040519150601f19603f3d011682016040523d82523d6000602084013e612b29565b606091505b50509050806126825760405162461bcd60e51b815260206004820152602360248201527f5472616e7366657248656c7065723a204b41495f5452414e534645525f46414960448201526213115160ea1b60648201526084016104d8565b6060600282511015612bda5760405162461bcd60e51b815260206004820152601b60248201527f4b61694465784c6962726172793a20494e56414c49445f50415448000000000060448201526064016104d8565b815167ffffffffffffffff811115612c0257634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015612c2b578160200160208202803683370190505b5090508281600081518110612c5057634e487b7160e01b600052603260045260246000fd5b60200260200101818152505060005b60018351612c6d9190613f36565b81101561299a57600080612ccf87868581518110612c9b57634e487b7160e01b600052603260045260246000fd5b602002602001015187866001612cb19190613edf565b8151811061290c57634e487b7160e01b600052603260045260246000fd5b91509150612d05848481518110612cf657634e487b7160e01b600052603260045260246000fd5b602002602001015183836126dd565b84612d11856001613edf565b81518110612d2f57634e487b7160e01b600052603260045260246000fd5b60200260200101818152505050508080612d4890613f90565b915050612c5f565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b1790529151600092839290881691612db49190613c31565b6000604051808303816000865af19150503d8060008114612df1576040519150601f19603f3d011682016040523d82523d6000602084013e612df6565b606091505b5091509150818015612e20575080511580612e20575080806020019051810190612e2091906139b8565b612e785760405162461bcd60e51b8152602060048201526024808201527f5472616e7366657248656c7065723a205452414e534645525f46524f4d5f46416044820152631253115160e21b60648201526084016104d8565b505050505050565b60005b60018351612e919190613f36565b81101561306857600080848381518110612ebb57634e487b7160e01b600052603260045260246000fd5b602002602001015185846001612ed19190613edf565b81518110612eef57634e487b7160e01b600052603260045260246000fd5b6020026020010151915091506000612f0783836131e1565b509050600087612f18866001613edf565b81518110612f3657634e487b7160e01b600052603260045260246000fd5b60200260200101519050600080836001600160a01b0316866001600160a01b031614612f6457826000612f68565b6000835b91509150600060028a51612f7c9190613f36565b8810612f885788612fa5565b600054612fa5906001600160a01b0316878c6125c78c6002613edf565b600054909150612fbf906001600160a01b031688886122e1565b6001600160a01b031663022c0d9f84848460006040519080825280601f01601f191660200182016040528015612ffc576020820181803683370190505b506040518563ffffffff1660e01b815260040161301c9493929190613e8f565b600060405180830381600087803b15801561303657600080fd5b505af115801561304a573d6000803e3d6000fd5b5050505050505050505050808061306090613f90565b915050612e83565b50505050565b60008084116130d15760405162461bcd60e51b815260206004820152602960248201527f4b61694465784c6962726172793a20494e53554646494349454e545f4f555450604482015268155517d05353d5539560ba1b60648201526084016104d8565b6000831180156130e15750600082115b6130fd5760405162461bcd60e51b81526004016104d890613cd2565b60006131156103e861310f86886134d6565b906134d6565b905060006131296103e561310f8689612687565b905061313a600161279b8385613ef7565b9695505050505050565b60008084116131a05760405162461bcd60e51b815260206004820152602260248201527f4b61694465784c6962726172793a20494e53554646494349454e545f414d4f55604482015261139560f21b60648201526084016104d8565b6000831180156131b05750600082115b6131cc5760405162461bcd60e51b81526004016104d890613cd2565b826131d785846134d6565b6108c99190613ef7565b600080826001600160a01b0316846001600160a01b031614156132515760405162461bcd60e51b815260206004820152602260248201527f4b61694465784c6962726172793a204944454e544943414c5f41444452455353604482015261455360f01b60648201526084016104d8565b826001600160a01b0316846001600160a01b031610613271578284613274565b83835b90925090506001600160a01b0382166132cf5760405162461bcd60e51b815260206004820152601b60248201527f4b61694465784c6962726172793a205a45524f5f41444452455353000000000060448201526064016104d8565b9250929050565b6000805460405163e6a4390560e01b81526001600160a01b03898116600483015288811660248301528392839291169063e6a439059060440160206040518083038186803b15801561332757600080fd5b505afa15801561333b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061335f9190613715565b6001600160a01b031614156133f7576000546040516364e329cb60e11b81526001600160a01b038a8116600483015289811660248301529091169063c9c6539690604401602060405180830381600087803b1580156133bd57600080fd5b505af11580156133d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133f59190613715565b505b600080548190613411906001600160a01b03168b8b613592565b91509150816000148015613423575080155b15613433578793508692506134c9565b6000613440898484613144565b905087811161347457858110156134695760405162461bcd60e51b81526004016104d890613d91565b8894509250826134c7565b6000613481898486613144565b9050898111156134a157634e487b7160e01b600052600160045260246000fd5b878110156134c15760405162461bcd60e51b81526004016104d890613d17565b94508793505b505b5050965096945050505050565b60008115806134fa575082826134ec8183613f17565b92506134f89083613ef7565b145b6109c95760405162461bcd60e51b815260206004820152601460248201527364732d6d6174682d6d756c2d6f766572666c6f7760601b60448201526064016104d8565b60008261354a8382613edf565b91508110156109c95760405162461bcd60e51b815260206004820152601460248201527364732d6d6174682d6164642d6f766572666c6f7760601b60448201526064016104d8565b60008060006135a185856131e1565b5090506135af8686866122e1565b506000806135be8888886122e1565b6001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b1580156135f657600080fd5b505afa15801561360a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061362e91906139d4565b506001600160701b031691506001600160701b03169150826001600160a01b0316876001600160a01b031614613665578082613668565b81815b90999098509650505050505050565b803561368281613fd7565b919050565b60008083601f840112613698578081fd5b50813567ffffffffffffffff8111156136af578182fd5b6020830191508360208260051b85010111156132cf57600080fd5b80516001600160701b038116811461368257600080fd5b803560ff8116811461368257600080fd5b600060208284031215613703578081fd5b813561370e81613fd7565b9392505050565b600060208284031215613726578081fd5b815161370e81613fd7565b600080600080600080600060e0888a03121561374b578283fd5b873561375681613fd7565b9650602088013561376681613fd7565b955060408801359450606088013593506080880135925060a088013561378b81613fd7565b8092505060c0880135905092959891949750929550565b60008060008060008060008060008060006101608c8e0312156137c3578384fd5b8b356137ce81613fd7565b9a5060208c01356137de81613fd7565b995060408c0135985060608c0135975060808c0135965060a08c013561380381613fd7565b955060c08c0135945060e08c013561381a81613fef565b93506138296101008d016136e1565b92506101208c013591506101408c013590509295989b509295989b9093969950565b600080600080600080600080610100898b031215613867578384fd5b883561387281613fd7565b9750602089013561388281613fd7565b965060408901359550606089013594506080890135935060a0890135925060c08901356138ae81613fd7565b8092505060e089013590509295985092959890939650565b60008060008060008060c087890312156138de578182fd5b86356138e981613fd7565b9550602087013594506040870135935060608701359250608087013561390e81613fd7565b8092505060a087013590509295509295509295565b6000806000806000806000806000806101408b8d031215613942578384fd5b8a3561394d81613fd7565b995060208b0135985060408b0135975060608b0135965060808b013561397281613fd7565b955060a08b0135945060c08b013561398981613fef565b935061399760e08c016136e1565b92506101008b013591506101208b013590509295989b9194979a5092959850565b6000602082840312156139c9578081fd5b815161370e81613fef565b6000806000606084860312156139e8578081fd5b6139f1846136ca565b92506139ff602085016136ca565b9150604084015163ffffffff81168114613a17578182fd5b809150509250925092565b600060208284031215613a33578081fd5b5051919050565b600080600080600060808688031215613a51578283fd5b85359450602086013567ffffffffffffffff811115613a6e578384fd5b613a7a88828901613687565b9095509350506040860135613a8e81613fd7565b949793965091946060013592915050565b60008060408385031215613ab1578182fd5b8235915060208084013567ffffffffffffffff80821115613ad0578384fd5b818601915086601f830112613ae3578384fd5b813581811115613af557613af5613fc1565b8060051b604051601f19603f83011681018181108582111715613b1a57613b1a613fc1565b604052828152858101935084860182860187018b1015613b38578788fd5b8795505b83861015613b6157613b4d81613677565b855260019590950194938601938601613b3c565b508096505050505050509250929050565b60008060408385031215613b84578182fd5b505080516020909101519092909150565b60008060008060008060a08789031215613bad578384fd5b8635955060208701359450604087013567ffffffffffffffff811115613bd1578485fd5b613bdd89828a01613687565b9095509350506060870135613bf181613fd7565b80925050608087013590509295509295509295565b600080600060608486031215613c1a578081fd5b505081359360208301359350604090920135919050565b60008251613c43818460208701613f4d565b9190910192915050565b6001600160a01b0397881681529590961660208601526040850193909352606084019190915260ff16608083015260a082015260c081019190915260e00190565b6020808252825182820181905260009190848201906040850190845b81811015613cc657835183529284019291840191600101613caa565b50909695505050505050565b60208082526025908201527f4b61694465784c6962726172793a20494e53554646494349454e545f4c495155604082015264494449545960d81b606082015260800190565b60208082526023908201527f4b6169446578526f757465723a20494e53554646494349454e545f415f414d4f60408201526215539560ea1b606082015260800190565b6020808252601a908201527f4b6169446578526f757465723a20494e56414c49445f50415448000000000000604082015260600190565b60208082526023908201527f4b6169446578526f757465723a20494e53554646494349454e545f425f414d4f60408201526215539560ea1b606082015260800190565b60208082526028908201527f4b6169446578526f757465723a20494e53554646494349454e545f4f555450556040820152671517d05353d5539560c21b606082015260800190565b60208082526024908201527f4b6169446578526f757465723a204558434553534956455f494e5055545f414d60408201526313d5539560e21b606082015260800190565b60208082526015908201527412d85a51195e149bdd5d195c8e8811561412549151605a1b604082015260600190565b84815283602082015260018060a01b03831660408201526080606082015260008251806080840152613ec88160a0850160208701613f4d565b601f01601f19169190910160a00195945050505050565b60008219821115613ef257613ef2613fab565b500190565b600082613f1257634e487b7160e01b81526012600452602481fd5b500490565b6000816000190483118215151615613f3157613f31613fab565b500290565b600082821015613f4857613f48613fab565b500390565b60005b83811015613f68578181015183820152602001613f50565b838111156130685750506000910152565b600081613f8857613f88613fab565b506000190190565b6000600019821415613fa457613fa4613fab565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b0381168114613fec57600080fd5b50565b8015158114613fec57600080fdfea2646970667358221220805a0c05e28b179b1aef617ac1335c58028214c58e34fe0761a79fbfc9ab8c0764736f6c63430008040033