GLMR Price: $0.020681 (-3.29%)

Contract

0x16E1Cd2370752cc7A4aAd326fe2C1aae95B0c5E2

Overview

GLMR Balance

Moonbeam Chain LogoMoonbeam Chain LogoMoonbeam Chain Logo0 GLMR

GLMR Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Block
From
To
Remove Liquidity...10830782022-05-23 1:14:071346 days ago1653268447IN
0x16E1Cd23...e95B0c5E2
0 GLMR0.01552097101.5
Add Liquidity ET...10784252022-05-22 9:04:481347 days ago1653210288IN
0x16E1Cd23...e95B0c5E2
6.09914989 GLMR0.01274576101.5
Add Liquidity ET...10784192022-05-22 9:03:361347 days ago1653210216IN
0x16E1Cd23...e95B0c5E2
3 GLMR0.38340792101.5
Remove Liquidity8151022022-04-14 3:37:061385 days ago1649907426IN
0x16E1Cd23...e95B0c5E2
0 GLMR0.01495531101.5
Remove Liquidity...8150932022-04-14 3:35:121385 days ago1649907312IN
0x16E1Cd23...e95B0c5E2
0 GLMR0.01570895101.5
Remove Liquidity8150712022-04-14 3:30:481385 days ago1649907048IN
0x16E1Cd23...e95B0c5E2
0 GLMR0.01657413101.5
Swap Exact Token...8120602022-04-13 17:10:421386 days ago1649869842IN
0x16E1Cd23...e95B0c5E2
0 GLMR0.01237315101.5
Swap ETH For Exa...8120562022-04-13 17:09:541386 days ago1649869794IN
0x16E1Cd23...e95B0c5E2
0.00004821 GLMR0.0136217101.5
Swap Exact Token...8120432022-04-13 17:07:181386 days ago1649869638IN
0x16E1Cd23...e95B0c5E2
0 GLMR0.01237315101.5
Swap ETH For Exa...8120402022-04-13 17:06:421386 days ago1649869602IN
0x16E1Cd23...e95B0c5E2
0.00000014 GLMR0.01362069101.5
Swap Exact Token...8120362022-04-13 17:05:481386 days ago1649869548IN
0x16E1Cd23...e95B0c5E2
0 GLMR0.01162773101.5
Swap Exact Token...8120302022-04-13 17:04:361386 days ago1649869476IN
0x16E1Cd23...e95B0c5E2
0 GLMR0.01162773101.5
Swap ETH For Exa...8120262022-04-13 17:03:481386 days ago1649869428IN
0x16E1Cd23...e95B0c5E2
0.00006451 GLMR0.01362069101.5
Swap Exact Token...8120232022-04-13 17:03:121386 days ago1649869392IN
0x16E1Cd23...e95B0c5E2
0 GLMR0.01237315101.5
Swap ETH For Exa...8120182022-04-13 17:02:001386 days ago1649869320IN
0x16E1Cd23...e95B0c5E2
0.0000435 GLMR0.0136217101.5
Swap Exact Token...8120112022-04-13 17:00:301386 days ago1649869230IN
0x16E1Cd23...e95B0c5E2
0 GLMR0.01237315101.5
Swap Exact Token...8120072022-04-13 16:59:361386 days ago1649869176IN
0x16E1Cd23...e95B0c5E2
0 GLMR0.01162773101.5
Swap Exact Token...8120032022-04-13 16:58:481386 days ago1649869128IN
0x16E1Cd23...e95B0c5E2
0 GLMR0.01163017101.5
Swap Exact Token...8120012022-04-13 16:58:241386 days ago1649869104IN
0x16E1Cd23...e95B0c5E2
0 GLMR0.0123695101.5
Swap ETH For Exa...8119832022-04-13 16:54:301386 days ago1649868870IN
0x16E1Cd23...e95B0c5E2
0.00000137 GLMR0.0136217101.5
Swap Tokens For ...8119752022-04-13 16:52:421386 days ago1649868762IN
0x16E1Cd23...e95B0c5E2
0 GLMR0.01215432101.5
Remove Liquidity...8118142022-04-13 16:20:121386 days ago1649866812IN
0x16E1Cd23...e95B0c5E2
0 GLMR0.01570611101.5
Remove Liquidity8118062022-04-13 16:18:251386 days ago1649866705IN
0x16E1Cd23...e95B0c5E2
0 GLMR0.01514938101.5
Remove Liquidity8117702022-04-13 16:11:121386 days ago1649866272IN
0x16E1Cd23...e95B0c5E2
0 GLMR0.01515232101.5
Add Liquidity ET...8117452022-04-13 16:05:421386 days ago1649865942IN
0x16E1Cd23...e95B0c5E2
0.00001 GLMR0.01244643101.5
View all transactions

Latest 25 internal transactions (View All)

Parent Transaction Hash Block From To
10830782022-05-23 1:14:071346 days ago1653268447
0x16E1Cd23...e95B0c5E2
9.09914849 GLMR
10830782022-05-23 1:14:071346 days ago1653268447
0x16E1Cd23...e95B0c5E2
9.09914849 GLMR
10784252022-05-22 9:04:481347 days ago1653210288
0x16E1Cd23...e95B0c5E2
0.00000139 GLMR
10784252022-05-22 9:04:481347 days ago1653210288
0x16E1Cd23...e95B0c5E2
6.0991485 GLMR
10784192022-05-22 9:03:361347 days ago1653210216
0x16E1Cd23...e95B0c5E2
3 GLMR
8150932022-04-14 3:35:121385 days ago1649907312
0x16E1Cd23...e95B0c5E2
0.00017715 GLMR
8150932022-04-14 3:35:121385 days ago1649907312
0x16E1Cd23...e95B0c5E2
0.00017715 GLMR
8120602022-04-13 17:10:421386 days ago1649869842
0x16E1Cd23...e95B0c5E2
0.00003278 GLMR
8120602022-04-13 17:10:421386 days ago1649869842
0x16E1Cd23...e95B0c5E2
0.00003278 GLMR
8120562022-04-13 17:09:541386 days ago1649869794
0x16E1Cd23...e95B0c5E2
0.00000047 GLMR
8120562022-04-13 17:09:541386 days ago1649869794
0x16E1Cd23...e95B0c5E2
0.00004774 GLMR
8120432022-04-13 17:07:181386 days ago1649869638
0x16E1Cd23...e95B0c5E2
0.00000137 GLMR
8120432022-04-13 17:07:181386 days ago1649869638
0x16E1Cd23...e95B0c5E2
0.00000137 GLMR
8120402022-04-13 17:06:421386 days ago1649869602
0x16E1Cd23...e95B0c5E2
0 GLMR
8120402022-04-13 17:06:421386 days ago1649869602
0x16E1Cd23...e95B0c5E2
0.00000014 GLMR
8120262022-04-13 17:03:481386 days ago1649869428
0x16E1Cd23...e95B0c5E2
0.00002121 GLMR
8120262022-04-13 17:03:481386 days ago1649869428
0x16E1Cd23...e95B0c5E2
0.0000433 GLMR
8120232022-04-13 17:03:121386 days ago1649869392
0x16E1Cd23...e95B0c5E2
0.00004304 GLMR
8120232022-04-13 17:03:121386 days ago1649869392
0x16E1Cd23...e95B0c5E2
0.00004304 GLMR
8120182022-04-13 17:02:001386 days ago1649869320
0x16E1Cd23...e95B0c5E2
0.00000021 GLMR
8120182022-04-13 17:02:001386 days ago1649869320
0x16E1Cd23...e95B0c5E2
0.00004328 GLMR
8120112022-04-13 17:00:301386 days ago1649869230
0x16E1Cd23...e95B0c5E2
0.00009046 GLMR
8120112022-04-13 17:00:301386 days ago1649869230
0x16E1Cd23...e95B0c5E2
0.00009046 GLMR
8120012022-04-13 16:58:241386 days ago1649869104
0x16E1Cd23...e95B0c5E2
0 GLMR
8120012022-04-13 16:58:241386 days ago1649869104
0x16E1Cd23...e95B0c5E2
0 GLMR
View All Internal Transactions
Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ImpossibleRouter

Compiler Version
v0.7.6+commit.7338295f

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
// SPDX-License-Identifier: GPL-3
pragma solidity =0.7.6;
pragma experimental ABIEncoderV2;

import './libraries/TransferHelper.sol';
import './libraries/ReentrancyGuard.sol';
import './libraries/ImpossibleLibrary.sol';
import './libraries/SafeMath.sol';

import './interfaces/IImpossibleSwapFactory.sol';
import './interfaces/IImpossibleRouterExtension.sol';
import './interfaces/IImpossibleRouter.sol';
import './interfaces/IERC20.sol';
import './interfaces/IWETH.sol';
import './interfaces/IImpossibleWrappedToken.sol';
import './interfaces/IImpossibleWrapperFactory.sol';

/**
    @title  Router for Impossible Swap V3
    @author Impossible Finance
    @notice This router builds upon basic Uni V2 Router02 by allowing custom
            calculations based on settings in pairs (uni/xybk/custom fees)
    @dev    See documentation at: https://docs.impossible.finance/impossible-swap/overview
    @dev    Very little logical changes made in Router02. Most changes to accomodate xybk are in Library
*/

contract ImpossibleRouter is IImpossibleRouter, ReentrancyGuard {
    using SafeMath for uint256;

    address public immutable override factory;
    address public immutable override wrapFactory;

    address private utilitySettingAdmin;

    address public override routerExtension; // Can be set by utility setting admin once only
    address public override WETH; // Can be set by utility setting admin once only

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

    /**
     @notice Constructor for IF Router
     @param _pairFactory Address of IF Pair Factory
     @param _wrapFactory Address of IF
     @param _utilitySettingAdmin Admin address allowed to set addresses of utility contracts (once)
    */
    constructor(
        address _pairFactory,
        address _wrapFactory,
        address _utilitySettingAdmin
    ) {
        factory = _pairFactory;
        wrapFactory = _wrapFactory;
        utilitySettingAdmin = _utilitySettingAdmin;
    }

    receive() external payable {
        assert(msg.sender == WETH); // only accept ETH via fallback from the WETH contract
    }

    /**
     @notice Used to set addresses of utility contracts
     @dev Only allows setter to set these addresses once for trustlessness
     @dev Must set both WETH and routerExtension at the same time, else swap will be bricked
     @param _WETH address of WETH contract
     @param _routerExtension address of router interface contract
     */
    function setUtilities(address _WETH, address _routerExtension) public {
        require(WETH == address(0x0) && routerExtension == address(0x0));
        require(msg.sender == utilitySettingAdmin, 'IF: ?');
        WETH = _WETH;
        routerExtension = _routerExtension;
    }

    /**
     @notice Helper function for sending tokens that might need to be wrapped
     @param token The address of the token that might have a wrapper
     @param src The source to take underlying tokens from
     @param dst The destination to send wrapped tokens to
     @param amt The amount of tokens to send (wrapped tokens, not underlying)
    */
    function wrapSafeTransfer(
        address token,
        address src,
        address dst,
        uint256 amt
    ) internal {
        address underlying = IImpossibleWrapperFactory(wrapFactory).wrappedTokensToTokens(token);
        if (underlying == address(0x0)) {
            TransferHelper.safeTransferFrom(token, src, dst, amt);
        } else {
            uint256 underlyingAmt = IImpossibleWrappedToken(token).amtToUnderlyingAmt(amt);
            TransferHelper.safeTransferFrom(underlying, src, address(this), underlyingAmt);
            TransferHelper.safeApprove(underlying, token, underlyingAmt);
            IImpossibleWrappedToken(token).deposit(dst, underlyingAmt);
        }
    }

    /**
     @notice Helper function for sending tokens that might need to be unwrapped
     @param token The address of the token that might be wrapped
     @param dst The destination to send underlying tokens to
     @param amt The amount of wrapped tokens to send (wrapped tokens, not underlying)
    */
    function unwrapSafeTransfer(
        address token,
        address dst,
        uint256 amt
    ) internal {
        address underlying = IImpossibleWrapperFactory(wrapFactory).wrappedTokensToTokens(token);
        if (underlying == address(0x0)) {
            TransferHelper.safeTransfer(token, dst, amt);
        } else {
            IImpossibleWrappedToken(token).withdraw(dst, amt);
        }
    }

    /**
     @notice Swap function - receive maximum output given fixed input
     @dev Openzeppelin reentrancy guards
     @param amountIn The exact input amount`
     @param amountOutMin The minimum output amount allowed for a successful swap
     @param path[] An array of token addresses. Trades are made from arr idx 0 to arr end idx sequentially
     @param to The address that receives the output tokens
     @param deadline The block number after which this transaction is invalid
     @return amounts Array of actual output token amounts received per swap, sequentially.
    */
    function swapExactTokensForTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external virtual override ensure(deadline) nonReentrant returns (uint256[] memory amounts) {
        amounts = ImpossibleLibrary.getAmountsOut(factory, amountIn, path);
        require(amounts[amounts.length - 1] >= amountOutMin, 'ImpossibleRouter: INSUFFICIENT_OUTPUT_AMOUNT');
        wrapSafeTransfer(path[0], msg.sender, ImpossibleLibrary.pairFor(factory, path[0], path[1]), amounts[0]);
        IImpossibleRouterExtension(routerExtension).swap(amounts, path);
        unwrapSafeTransfer(path[path.length - 1], to, amounts[amounts.length - 1]);
    }

    /**
     @notice Swap function - receive desired output amount given a maximum input amount
     @dev Openzeppelin reentrancy guards
     @param amountOut The exact output amount desired
     @param amountInMax The maximum input amount allowed for a successful swap
     @param path[] An array of token addresses. Trades are made from arr idx 0 to arr end idx sequentially
     @param to The address that receives the output tokens
     @param deadline The block number after which this transaction is invalid
     @return amounts Array of actual output token amounts received per swap, sequentially.
    */
    function swapTokensForExactTokens(
        uint256 amountOut,
        uint256 amountInMax,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external virtual override ensure(deadline) nonReentrant returns (uint256[] memory amounts) {
        amounts = ImpossibleLibrary.getAmountsIn(factory, amountOut, path);
        require(amounts[0] <= amountInMax, 'ImpossibleRouter: EXCESSIVE_INPUT_AMOUNT');
        wrapSafeTransfer(path[0], msg.sender, ImpossibleLibrary.pairFor(factory, path[0], path[1]), amounts[0]);
        IImpossibleRouterExtension(routerExtension).swap(amounts, path);
        unwrapSafeTransfer(path[path.length - 1], to, amountOut);
    }

    /**
     @notice Swap function - receive maximum output given fixed input of ETH
     @dev Openzeppelin reentrancy guards
     @param amountOutMin The minimum output amount allowed for a successful swap
     @param path[] An array of token addresses. Trades are made from arr idx 0 to arr end idx sequentially
     @param to The address that receives the output tokens
     @param deadline The block number after which this transaction is invalid
     @return amounts Array of actual output token amounts received per swap, sequentially.
    */
    function swapExactETHForTokens(
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external payable virtual override ensure(deadline) nonReentrant returns (uint256[] memory amounts) {
        require(path[0] == WETH, 'ImpossibleRouter: INVALID_PATH');
        amounts = ImpossibleLibrary.getAmountsOut(factory, msg.value, path);
        require(amounts[amounts.length - 1] >= amountOutMin, 'ImpossibleRouter: INSUFFICIENT_OUTPUT_AMOUNT');
        IWETH(WETH).deposit{value: amounts[0]}();
        assert(IWETH(WETH).transfer(ImpossibleLibrary.pairFor(factory, path[0], path[1]), amounts[0]));
        IImpossibleRouterExtension(routerExtension).swap(amounts, path);
        unwrapSafeTransfer(path[path.length - 1], to, amounts[amounts.length - 1]);
    }

    /**
    @notice Swap function - receive desired ETH output amount given a maximum input amount
     @dev Openzeppelin reentrancy guards
     @param amountOut The exact output amount desired
     @param amountInMax The maximum input amount allowed for a successful swap
     @param path[] An array of token addresses. Trades are made from arr idx 0 to arr end idx sequentially
     @param to The address that receives the output tokens
     @param deadline The block number after which this transaction is invalid
     @return amounts Array of actual output token amounts received per swap, sequentially.
    */
    function swapTokensForExactETH(
        uint256 amountOut,
        uint256 amountInMax,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external virtual override ensure(deadline) nonReentrant returns (uint256[] memory amounts) {
        require(path[path.length - 1] == WETH, 'ImpossibleRouter: INVALID_PATH');
        amounts = ImpossibleLibrary.getAmountsIn(factory, amountOut, path);
        require(amounts[0] <= amountInMax, 'ImpossibleRouter: EXCESSIVE_INPUT_AMOUNT');
        wrapSafeTransfer(path[0], msg.sender, ImpossibleLibrary.pairFor(factory, path[0], path[1]), amounts[0]);
        IImpossibleRouterExtension(routerExtension).swap(amounts, path);
        IWETH(WETH).withdraw(amounts[amounts.length - 1]);
        TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);
    }

    /**
     @notice Swap function - receive maximum ETH output given fixed input of tokens
     @dev Openzeppelin reentrancy guards
     @param amountIn The amount of input tokens
     @param amountOutMin The minimum ETH output amount required for successful swaps
     @param path[] An array of token addresses. Trades are made from arr idx 0 to arr end idx sequentially
     @param to The address that receives the output tokens
     @param deadline The block number after which this transaction is invalid
     @return amounts Array of actual output token amounts received per swap, sequentially.
    */
    function swapExactTokensForETH(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external virtual override ensure(deadline) nonReentrant returns (uint256[] memory amounts) {
        require(path[path.length - 1] == WETH, 'ImpossibleRouter: INVALID_PATH');
        amounts = ImpossibleLibrary.getAmountsOut(factory, amountIn, path);
        require(amounts[amounts.length - 1] >= amountOutMin, 'ImpossibleRouter: INSUFFICIENT_OUTPUT_AMOUNT');
        wrapSafeTransfer(path[0], msg.sender, ImpossibleLibrary.pairFor(factory, path[0], path[1]), amounts[0]);
        IImpossibleRouterExtension(routerExtension).swap(amounts, path);
        IWETH(WETH).withdraw(amounts[amounts.length - 1]);
        TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);
    }

    /**
     @notice Swap function - receive maximum tokens output given fixed ETH input
     @dev Openzeppelin reentrancy guards
     @param amountOut The minimum output amount in tokens required for successful swaps
     @param path[] An array of token addresses. Trades are made from arr idx 0 to arr end idx sequentially
     @param to The address that receives the output tokens
     @param deadline The block number after which this transaction is invalid
     @return amounts Array of actual output token amounts received per swap, sequentially.
    */
    function swapETHForExactTokens(
        uint256 amountOut,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external payable virtual override ensure(deadline) nonReentrant returns (uint256[] memory amounts) {
        require(path[0] == WETH, 'ImpossibleRouter: INVALID_PATH');
        amounts = ImpossibleLibrary.getAmountsIn(factory, amountOut, path);
        require(amounts[0] <= msg.value, 'ImpossibleRouter: EXCESSIVE_INPUT_AMOUNT');
        IWETH(WETH).deposit{value: amounts[0]}();
        assert(IWETH(WETH).transfer(ImpossibleLibrary.pairFor(factory, path[0], path[1]), amounts[0]));
        IImpossibleRouterExtension(routerExtension).swap(amounts, path);
        unwrapSafeTransfer(path[path.length - 1], to, amountOut);
        // refund dust eth, if any
        if (msg.value > amounts[0]) TransferHelper.safeTransferETH(msg.sender, msg.value - amounts[0]);
    }

    /**
     @notice Swap function for fee on transfer tokens, no WETH/WBNB
     @param amountIn The amount of input tokens
     @param amountOutMin The minimum token output amount required for successful swaps
     @param path[] An array of token addresses. Trades are made from arr idx 0 to arr end idx sequentially
     @param to The address that receives the output tokens
     @param deadline The block number after which this transaction is invalid
    */
    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external virtual override ensure(deadline) nonReentrant {
        wrapSafeTransfer(path[0], msg.sender, ImpossibleLibrary.pairFor(factory, path[0], path[1]), amountIn);
        IImpossibleRouterExtension(routerExtension).swapSupportingFeeOnTransferTokens(path);
        uint256 balance = IERC20(path[path.length - 1]).balanceOf(address(this));
        require(balance >= amountOutMin, 'ImpossibleRouter: INSUFFICIENT_OUTPUT_AMOUNT');
        unwrapSafeTransfer(path[path.length - 1], to, balance);
    }

    /**
     @notice Swap function for fee on transfer tokens with WETH/WBNB
     @param amountOutMin The minimum underlying token output amount required for successful swaps
     @param path[] An array of token addresses. Trades are made from arr idx 0 to arr end idx sequentially
     @param to The address that receives the output tokens
     @param deadline The block number after which this transaction is invalid
    */
    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external payable virtual override ensure(deadline) nonReentrant {
        require(path[0] == WETH, 'ImpossibleRouter: INVALID_PATH');
        uint256 amountIn = msg.value;
        IWETH(WETH).deposit{value: amountIn}();
        assert(IWETH(WETH).transfer(ImpossibleLibrary.pairFor(factory, path[0], path[1]), amountIn));
        IImpossibleRouterExtension(routerExtension).swapSupportingFeeOnTransferTokens(path);
        uint256 balance = IERC20(path[path.length - 1]).balanceOf(address(this));
        require(balance >= amountOutMin, 'ImpossibleRouter: INSUFFICIENT_OUTPUT_AMOUNT');
        unwrapSafeTransfer(path[path.length - 1], to, balance);
    }

    /**
     @notice Swap function for fee on transfer tokens, no WETH/WBNB
     @param amountIn The amount of input tokens
     @param amountOutMin The minimum ETH output amount required for successful swaps
     @param path[] An array of token addresses. Trades are made from arr idx 0 to arr end idx sequentially
     @param to The address that receives the output tokens
     @param deadline The block number after which this transaction is invalid
    */
    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external virtual override ensure(deadline) nonReentrant {
        require(path[path.length - 1] == WETH, 'ImpossibleRouter: INVALID_PATH');
        wrapSafeTransfer(path[0], msg.sender, ImpossibleLibrary.pairFor(factory, path[0], path[1]), amountIn);
        IImpossibleRouterExtension(routerExtension).swapSupportingFeeOnTransferTokens(path);
        uint256 amountOut = IERC20(WETH).balanceOf(address(this));
        require(amountOut >= amountOutMin, 'ImpossibleRouter: INSUFFICIENT_OUTPUT_AMOUNT');
        IWETH(WETH).withdraw(amountOut);
        TransferHelper.safeTransferETH(to, amountOut);
    }

    /**
     @notice Function for basic add liquidity functionality
     @dev Openzeppelin reentrancy guards
     @param tokenA The address of underlying tokenA to add
     @param tokenB The address of underlying tokenB to add
     @param amountADesired The desired amount of tokenA to add
     @param amountBDesired The desired amount of tokenB to add
     @param amountAMin The min amount of tokenA to add (amountAMin:amountBDesired sets bounds on ratio)
     @param amountBMin The min amount of tokenB to add (amountADesired:amountBMin sets bounds on ratio)
     @param to The address to mint LP tokens to
     @param deadline The block number after which this transaction is invalid
     @return amountA Amount of tokenA added as liquidity to pair
     @return amountB Actual amount of tokenB added as liquidity to pair
     @return liquidity Actual amount of LP tokens minted
    */
    function addLiquidity(
        address tokenA,
        address tokenB,
        uint256 amountADesired,
        uint256 amountBDesired,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    )
        external
        virtual
        override
        ensure(deadline)
        nonReentrant
        returns (
            uint256 amountA,
            uint256 amountB,
            uint256 liquidity
        )
    {
        (amountA, amountB) = IImpossibleRouterExtension(routerExtension).addLiquidity(
            tokenA,
            tokenB,
            amountADesired,
            amountBDesired,
            amountAMin,
            amountBMin
        );
        address pair = ImpossibleLibrary.pairFor(factory, tokenA, tokenB);
        wrapSafeTransfer(tokenA, msg.sender, pair, amountA);
        wrapSafeTransfer(tokenB, msg.sender, pair, amountB);
        liquidity = IImpossiblePair(pair).mint(to);
    }

    /**
     @notice Function for add liquidity functionality with 1 token being WETH/WBNB
     @dev Openzeppelin reentrancy guards
     @param token The address of the non-ETH underlying token to add
     @param amountTokenDesired The desired amount of non-ETH underlying token to add
     @param amountTokenMin The min amount of non-ETH underlying token to add (amountTokenMin:ETH sent sets bounds on ratio)
     @param amountETHMin The min amount of WETH/WBNB to add (amountTokenDesired:amountETHMin sets bounds on ratio)
     @param to The address to mint LP tokens to
     @param deadline The block number after which this transaction is invalid
     @return amountToken Amount of non-ETH underlying token added as liquidity to pair
     @return amountETH Actual amount of WETH/WBNB added as liquidity to pair
     @return liquidity Actual amount of LP tokens minted
    */
    function addLiquidityETH(
        address token,
        uint256 amountTokenDesired,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    )
        external
        payable
        virtual
        override
        ensure(deadline)
        nonReentrant
        returns (
            uint256 amountToken,
            uint256 amountETH,
            uint256 liquidity
        )
    {
        (amountToken, amountETH) = IImpossibleRouterExtension(routerExtension).addLiquidity(
            token,
            WETH,
            amountTokenDesired,
            msg.value,
            amountTokenMin,
            amountETHMin
        );
        address pair = ImpossibleLibrary.pairFor(factory, token, WETH);
        wrapSafeTransfer(token, msg.sender, pair, amountToken);
        IWETH(WETH).deposit{value: amountETH}();
        assert(IWETH(WETH).transfer(pair, amountETH));
        liquidity = IImpossiblePair(pair).mint(to);
        if (msg.value > amountETH) TransferHelper.safeTransferETH(msg.sender, msg.value - amountETH); // refund dust eth, if any
    }

    /**
     @notice Function for basic remove liquidity functionality
     @dev Openzeppelin reentrancy guards
     @param tokenA The address of underlying tokenA in LP token
     @param tokenB The address of underlying tokenB in LP token
     @param liquidity The amount of LP tokens to burn
     @param amountAMin The min amount of underlying tokenA that has to be received
     @param amountBMin The min amount of underlying tokenB that has to be received
     @param to The address to send underlying tokens to
     @param deadline The block number after which this transaction is invalid
     @return amountA Actual amount of underlying tokenA received
     @return amountB Actual amount of underlying tokenB received
    */
    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint256 liquidity,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    ) public virtual override ensure(deadline) nonReentrant returns (uint256 amountA, uint256 amountB) {
        address pair = ImpossibleLibrary.pairFor(factory, tokenA, tokenB);
        IImpossiblePair(pair).transferFrom(msg.sender, pair, liquidity); // send liquidity to pair
        (amountA, amountB) = IImpossibleRouterExtension(routerExtension).removeLiquidity(
            tokenA,
            tokenB,
            pair,
            amountAMin,
            amountBMin
        );
        unwrapSafeTransfer(tokenA, to, amountA);
        unwrapSafeTransfer(tokenB, to, amountB);
    }

    /**
     @notice Function for remove liquidity functionality with 1 token being WETH/WBNB
     @dev Openzeppelin reentrancy guards
     @param token The address of the non-ETH underlying token to receive
     @param liquidity The amount of LP tokens to burn
     @param amountTokenMin The desired amount of non-ETH underlying token that has to be received
     @param amountETHMin The min amount of ETH that has to be received
     @param to The address to send underlying tokens to
     @param deadline The block number after which this transaction is invalid
     @return amountToken Actual amount of non-ETH underlying token received
     @return amountETH Actual amount of WETH/WBNB received
    */
    function removeLiquidityETH(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    ) public virtual override ensure(deadline) nonReentrant returns (uint256 amountToken, uint256 amountETH) {
        address pair = ImpossibleLibrary.pairFor(factory, token, WETH);
        IImpossiblePair(pair).transferFrom(msg.sender, pair, liquidity); // send liquidity to pair
        (amountToken, amountETH) = IImpossibleRouterExtension(routerExtension).removeLiquidity(
            token,
            WETH,
            pair,
            amountTokenMin,
            amountETHMin
        );
        unwrapSafeTransfer(token, to, amountToken);
        IWETH(WETH).withdraw(amountETH);
        TransferHelper.safeTransferETH(to, amountETH);
    }

    /**
    @notice Function for remove liquidity functionality using EIP712 permit
     @dev Openzeppelin reentrancy guards
     @param tokenA The address of underlying tokenA in LP token
     @param tokenB The address of underlying tokenB in LP token
     @param liquidity The amount of LP tokens to burn
     @param amountAMin The min amount of underlying tokenA that has to be received
     @param amountBMin The min amount of underlying tokenB that has to be received
     @param to The address to send underlying tokens to
     @param deadline The block number after which this transaction is invalid
     @param approveMax How much tokens are approved for transfer (liquidity, or max)
     @param v,r,s Variables that construct a valid EVM signature
     @return amountA Actual amount of underlying tokenA received
     @return amountB Actual amount of underlying tokenB received
    */
    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 = ImpossibleLibrary.pairFor(factory, tokenA, tokenB);
        uint256 value = approveMax ? uint256(-1) : liquidity;
        IImpossiblePair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);
        return removeLiquidity(tokenA, tokenB, liquidity, amountAMin, amountBMin, to, deadline);
    }

    /**
     @notice Function for remove liquidity functionality using EIP712 permit with 1 token being WETH/WBNB
     @param token The address of the non-ETH underlying token to receive
     @param liquidity The amount of LP tokens to burn
     @param amountTokenMin The desired amount of non-ETH underlying token that has to be received
     @param amountETHMin The min amount of ETH that has to be received
     @param to The address to send underlying tokens to
     @param deadline The block number after which this transaction is invalid
     @param approveMax How much tokens are approved for transfer (liquidity, or max)
     @param v,r,s Variables that construct a valid EVM signature
     @return amountToken Actual amount of non-ETH underlying token received
     @return amountETH Actual amount of WETH/WBNB received
    */
    function removeLiquidityETHWithPermit(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external virtual override returns (uint256 amountToken, uint256 amountETH) {
        address pair = ImpossibleLibrary.pairFor(factory, token, WETH);
        uint256 value = approveMax ? uint256(-1) : liquidity;
        IImpossiblePair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);
        (amountToken, amountETH) = removeLiquidityETH(token, liquidity, amountTokenMin, amountETHMin, to, deadline);
    }

    /**
     @notice Function for remove liquidity functionality with 1 token being WETH/WBNB
     @dev This is used when non-WETH/WBNB underlying token is fee-on-transfer: e.g. FEI algo stable v1
     @dev Openzeppelin reentrancy guards
     @param token The address of the non-ETH underlying token to receive
     @param liquidity The amount of LP tokens to burn
     @param amountTokenMin The desired amount of non-ETH underlying token that has to be received
     @param amountETHMin The min amount of ETH that has to be received
     @param to The address to send underlying tokens to
     @param deadline The block number after which this transaction is invalid
     @return amountETH Actual amount of WETH/WBNB received
    */
    function removeLiquidityETHSupportingFeeOnTransferTokens(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    ) public virtual override ensure(deadline) nonReentrant returns (uint256 amountETH) {
        address pair = ImpossibleLibrary.pairFor(factory, token, WETH);
        IImpossiblePair(pair).transferFrom(msg.sender, pair, liquidity); // send liquidity to pair
        (, amountETH) = IImpossibleRouterExtension(routerExtension).removeLiquidity(
            token,
            WETH,
            pair,
            amountTokenMin,
            amountETHMin
        );
        unwrapSafeTransfer(token, to, IERC20(token).balanceOf(address(this)));
        IWETH(WETH).withdraw(amountETH);
        TransferHelper.safeTransferETH(to, amountETH);
    }

    /**
     @notice Function for remove liquidity functionality using EIP712 permit with 1 token being WETH/WBNB
     @dev This is used when non-WETH/WBNB underlying token is fee-on-transfer: e.g. FEI algo stable v1
     @param token The address of the non-ETH underlying token to receive
     @param liquidity The amount of LP tokens to burn
     @param amountTokenMin The desired amount of non-ETH underlying token that has to be received
     @param amountETHMin The min amount of ETH that has to be received
     @param to The address to send underlying tokens to
     @param deadline The block number after which this transaction is invalid
     @param approveMax How much tokens are approved for transfer (liquidity, or max)
     @param v,r,s Variables that construct a valid EVM signature
     @return amountETH Actual amount of WETH/WBNB received
    */
    function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external virtual override returns (uint256 amountETH) {
        address pair = ImpossibleLibrary.pairFor(factory, token, WETH);
        uint256 value = approveMax ? uint256(-1) : liquidity;
        IImpossiblePair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);
        amountETH = removeLiquidityETHSupportingFeeOnTransferTokens(
            token,
            liquidity,
            amountTokenMin,
            amountETHMin,
            to,
            deadline
        );
    }
}

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

pragma solidity >=0.6.0;

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

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

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

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

File 3 of 15 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, 'ReentrancyGuard: reentrant call');

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}

/// SPDX-License-Identifier: GPL-3
pragma solidity >=0.5.0;

import '../interfaces/IImpossiblePair.sol';
import '../interfaces/IERC20.sol';

import './SafeMath.sol';
import './Math.sol';

library ImpossibleLibrary {
    using SafeMath for uint256;

    /**
     @notice Sorts tokens in ascending order
     @param tokenA The address of token A
     @param tokenB The address of token B
     @return token0 The address of token 0 (lexicographically smaller than addr of token 1)
     @return token1 The address of token 1 (lexicographically larger than addr of token 0)
    */
    function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {
        require(tokenA != tokenB, 'ImpossibleLibrary: IDENTICAL_ADDRESSES');
        (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
        require(token0 != address(0), 'ImpossibleLibrary: ZERO_ADDRESS');
    }

    /**
     @notice Computes the pair contract create2 address deterministically
     @param factory The address of the token factory (pair contract deployer)
     @param tokenA The address of token A
     @param tokenB The address of token B
     @return pair The address of the pair containing token A and B
    */
    function pairFor(
        address factory,
        address tokenA,
        address tokenB
    ) internal pure returns (address pair) {
        (address token0, address token1) = sortTokens(tokenA, tokenB);
        pair = address(
            uint256(
                keccak256(
                    abi.encodePacked(
                        hex'ff',
                        factory,
                        keccak256(abi.encodePacked(token0, token1)),
                        hex'fc84b622ba228c468b74c2d99bfe9454ffac280ac017f05a02feb9f739aeb1e4' // init code hash                    
                    )
                )
            )
        );
    }

    /**
     @notice Obtains the token reserves in the pair contract
     @param factory The address of the token factory (pair contract deployer)
     @param tokenA The address of token A
     @param tokenB The address of token B
     @return reserveA The amount of token A in reserves
     @return reserveB The amount of token B in reserves
     @return pair The address of the pair containing token A and B
    */
    function getReserves(
        address factory,
        address tokenA,
        address tokenB
    )
        internal
        view
        returns (
            uint256 reserveA,
            uint256 reserveB,
            address pair
        )
    {
        (address token0, ) = sortTokens(tokenA, tokenB);
        pair = pairFor(factory, tokenA, tokenB);
        (uint256 reserve0, uint256 reserve1) = IImpossiblePair(pair).getReserves();
        (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
    }

    /**
     @notice Quote returns amountB based on some amountA, in the ratio of reserveA:reserveB
     @param amountA The amount of token A
     @param reserveA The amount of reserveA
     @param reserveB The amount of reserveB
     @return amountB The amount of token B that matches amount A in the ratio of reserves
    */
    function quote(
        uint256 amountA,
        uint256 reserveA,
        uint256 reserveB
    ) internal pure returns (uint256 amountB) {
        require(amountA > 0, 'ImpossibleLibrary: INSUFFICIENT_AMOUNT');
        require(reserveA > 0 && reserveB > 0, 'ImpossibleLibrary: INSUFFICIENT_LIQUIDITY');
        amountB = amountA.mul(reserveB) / reserveA;
    }

    /**
     @notice Internal function to compute the K value for an xybk pair based on token balances and boost
     @dev More details on math at: https://docs.impossible.finance/impossible-swap/swap-math
     @dev Implementation is the same as in pair
     @param boost0 Current boost0 in pair
     @param boost1 Current boost1 in pair
     @param balance0 Current state of balance0 in pair
     @param balance1 Current state of balance1 in pair
     @return k Value of K invariant
    */
    function xybkComputeK(
        uint256 boost0,
        uint256 boost1,
        uint256 balance0,
        uint256 balance1
    ) internal pure returns (uint256 k) {
        uint256 boost = (balance0 > balance1) ? boost0.sub(1) : boost1.sub(1);
        uint256 denom = boost.mul(2).add(1); // 1+2*boost
        uint256 term = boost.mul(balance0.add(balance1)).div(denom.mul(2)); // boost*(x+y)/(2+4*boost)
        k = (Math.sqrt(term**2 + balance0.mul(balance1).div(denom)) + term)**2;
    }

    /**
     @notice Internal helper function for calculating artificial liquidity
     @dev More details on math at: https://docs.impossible.finance/impossible-swap/swap-math
     @param _boost The boost variable on the correct side for the pair contract
     @param _sqrtK The sqrt of the invariant variable K in xybk formula
     @return uint256 The artificial liquidity term
    */
    function calcArtiLiquidityTerm(uint256 _boost, uint256 _sqrtK) internal pure returns (uint256) {
        return (_boost - 1).mul(_sqrtK);
    }

    /**
     @notice Quotes maximum output given exact input amount of tokens and addresses of tokens in pair
     @dev The library function considers custom swap fees/invariants/asymmetric tuning of pairs
     @dev However, library function doesn't consider limits created by hardstops
     @param amountIn The input amount of token A
     @param tokenIn The address of input token
     @param tokenOut The address of output token
     @param factory The address of the factory contract
     @return amountOut The maximum output amount of token B for a valid swap
    */
    function getAmountOut(
        uint256 amountIn,
        address tokenIn,
        address tokenOut,
        address factory
    ) internal view returns (uint256 amountOut) {
        require(amountIn > 0, 'ImpossibleLibrary: INSUFFICIENT_INPUT_AMOUNT');
        uint256 reserveIn;
        uint256 reserveOut;
        uint256 amountInPostFee;
        address pair;
        bool isMatch;
        {
            // Avoid stack too deep
            (address token0, ) = sortTokens(tokenIn, tokenOut);
            isMatch = tokenIn == token0;
            (reserveIn, reserveOut, pair) = getReserves(factory, tokenIn, tokenOut);
        }
        uint256 artiLiqTerm;
        bool isXybk;
        {
            // Avoid stack too deep
            uint256 fee;
            IImpossiblePair.TradeState tradeState;
            (fee, tradeState, isXybk) = IImpossiblePair(pair).getPairSettings();
            amountInPostFee = amountIn.mul(10000 - fee);
            require(
                (tradeState == IImpossiblePair.TradeState.SELL_ALL) ||
                    (tradeState == IImpossiblePair.TradeState.SELL_TOKEN_0 && !isMatch) ||
                    (tradeState == IImpossiblePair.TradeState.SELL_TOKEN_1 && isMatch),
                'ImpossibleLibrary: TRADE_NOT_ALLOWED'
            );
        }

        /// If xybk invariant, set reserveIn/reserveOut to artificial liquidity instead of actual liquidity
        if (isXybk) {
            (uint256 boost0, uint256 boost1) = IImpossiblePair(pair).calcBoost();
            uint256 sqrtK = Math.sqrt(
                xybkComputeK(boost0, boost1, isMatch ? reserveIn : reserveOut, isMatch ? reserveOut : reserveIn)
            );
            /// since balance0=balance1 only at sqrtK, if final balanceIn >= sqrtK means balanceIn >= balanceOut
            /// Use post-fee balances to maintain consistency with pair contract K invariant check
            if (amountInPostFee.add(reserveIn.mul(10000)) >= sqrtK.mul(10000)) {
                /// If tokenIn = token0, balanceIn > sqrtK => balance0>sqrtK, use boost0
                artiLiqTerm = calcArtiLiquidityTerm(isMatch ? boost0 : boost1, sqrtK);
                /// If balance started from <sqrtK and ended at >sqrtK and boosts are different, there'll be different amountIn/Out
                /// Don't need to check in other case for reserveIn < reserveIn.add(x) <= sqrtK since that case doesnt cross midpt
                if (reserveIn < sqrtK && boost0 != boost1) {
                    /// Break into 2 trades => start point -> midpoint (sqrtK, sqrtK), then midpoint -> final point
                    amountOut = reserveOut.sub(sqrtK);
                    amountInPostFee = amountInPostFee.sub((sqrtK.sub(reserveIn)).mul(10000));
                    reserveIn = sqrtK;
                    reserveOut = sqrtK;
                }
            } else {
                /// If tokenIn = token0, balanceIn < sqrtK => balance0<sqrtK, use boost1
                artiLiqTerm = calcArtiLiquidityTerm(isMatch ? boost1 : boost0, sqrtK);
            }
        }
        uint256 numerator = amountInPostFee.mul(reserveOut.add(artiLiqTerm));
        uint256 denominator = (reserveIn.add(artiLiqTerm)).mul(10000).add(amountInPostFee);
        uint256 lastSwapAmountOut = numerator / denominator;
        amountOut = (lastSwapAmountOut > reserveOut) ? reserveOut.add(amountOut) : lastSwapAmountOut.add(amountOut);
    }

    /**
     @notice Quotes minimum input given exact output amount of tokens and addresses of tokens in pair
     @dev The library function considers custom swap fees/invariants/asymmetric tuning of pairs
     @dev However, library function doesn't consider limits created by hardstops
     @param amountOut The desired output amount of token A
     @param tokenIn The address of input token
     @param tokenOut The address of output token
     @param factory The address of the factory contract
     @return amountIn The minimum input amount of token A for a valid swap
    */
    function getAmountIn(
        uint256 amountOut,
        address tokenIn,
        address tokenOut,
        address factory
    ) internal view returns (uint256 amountIn) {
        require(amountOut > 0, 'ImpossibleLibrary: INSUFFICIENT_INPUT_AMOUNT');

        uint256 reserveIn;
        uint256 reserveOut;
        uint256 artiLiqTerm;
        uint256 fee;
        bool isMatch;
        {
            // Avoid stack too deep
            bool isXybk;
            uint256 boost0;
            uint256 boost1;
            {
                // Avoid stack too deep
                (address token0, ) = sortTokens(tokenIn, tokenOut);
                isMatch = tokenIn == token0;
            }
            {
                // Avoid stack too deep
                address pair;
                (reserveIn, reserveOut, pair) = getReserves(factory, tokenIn, tokenOut);
                IImpossiblePair.TradeState tradeState;
                (fee, tradeState, isXybk) = IImpossiblePair(pair).getPairSettings();
                require(
                    (tradeState == IImpossiblePair.TradeState.SELL_ALL) ||
                        (tradeState == IImpossiblePair.TradeState.SELL_TOKEN_0 && !isMatch) ||
                        (tradeState == IImpossiblePair.TradeState.SELL_TOKEN_1 && isMatch),
                    'ImpossibleLibrary: TRADE_NOT_ALLOWED'
                );
                (boost0, boost1) = IImpossiblePair(pair).calcBoost();
            }
            if (isXybk) {
                uint256 sqrtK = Math.sqrt(
                    xybkComputeK(boost0, boost1, isMatch ? reserveIn : reserveOut, isMatch ? reserveOut : reserveIn)
                );
                /// since balance0=balance1 only at sqrtK, if final balanceOut >= sqrtK means balanceOut >= balanceIn
                if (reserveOut.sub(amountOut) >= sqrtK) {
                    /// If tokenIn = token0, balanceOut > sqrtK => balance1>sqrtK, use boost1
                    artiLiqTerm = calcArtiLiquidityTerm(isMatch ? boost1 : boost0, sqrtK);
                } else {
                    /// If tokenIn = token0, balanceOut < sqrtK => balance0>sqrtK, use boost0
                    artiLiqTerm = calcArtiLiquidityTerm(isMatch ? boost0 : boost1, sqrtK);
                    /// If balance started from <sqrtK and ended at >sqrtK and boosts are different, there'll be different amountIn/Out
                    /// Don't need to check in other case for reserveOut > reserveOut.sub(x) >= sqrtK since that case doesnt cross midpt
                    if (reserveOut > sqrtK && boost0 != boost1) {
                        /// Break into 2 trades => start point -> midpoint (sqrtK, sqrtK), then midpoint -> final point
                        amountIn = sqrtK.sub(reserveIn).mul(10000); /// Still need to divide by (10000 - fee). Do with below calculation to prevent early truncation
                        amountOut = amountOut.sub(reserveOut.sub(sqrtK));
                        reserveOut = sqrtK;
                        reserveIn = sqrtK;
                    }
                }
            }
        }
        uint256 numerator = (reserveIn.add(artiLiqTerm)).mul(amountOut).mul(10000);
        uint256 denominator = (reserveOut.add(artiLiqTerm)).sub(amountOut);
        amountIn = (amountIn.add((numerator / denominator)).div(10000 - fee)).add(1);
    }

    /**
     @notice Quotes maximum output given some uncertain input amount of tokens and addresses of tokens in pair
     @dev The library function considers custom swap fees/invariants/asymmetric tuning of pairs
     @dev However, library function doesn't consider limits created by hardstops
     @param tokenIn The address of input token
     @param tokenOut The address of output token
     @param factory The address of the factory contract
     @return uint256 The maximum possible output amount of token A
     @return uint256 The maximum possible output amount of token B
    */
    function getAmountOutFeeOnTransfer(
        address tokenIn,
        address tokenOut,
        address factory
    ) internal view returns (uint256, uint256) {
        uint256 reserveIn;
        uint256 reserveOut;
        address pair;
        bool isMatch;
        {
            // Avoid stack too deep
            (address token0, ) = sortTokens(tokenIn, tokenOut);
            isMatch = tokenIn == token0;
            (reserveIn, reserveOut, pair) = getReserves(factory, tokenIn, tokenOut); /// Should be reserve0/1 but reuse variables to save stack
        }
        uint256 amountOut;
        uint256 artiLiqTerm;
        uint256 amountInPostFee;
        bool isXybk;
        {
            // Avoid stack too deep
            uint256 fee;
            uint256 balanceIn = IERC20(tokenIn).balanceOf(address(pair));
            require(balanceIn > reserveIn, 'ImpossibleLibrary: INSUFFICIENT_INPUT_AMOUNT');
            IImpossiblePair.TradeState tradeState;
            (fee, tradeState, isXybk) = IImpossiblePair(pair).getPairSettings();
            require(
                (tradeState == IImpossiblePair.TradeState.SELL_ALL) ||
                    (tradeState == IImpossiblePair.TradeState.SELL_TOKEN_0 && !isMatch) ||
                    (tradeState == IImpossiblePair.TradeState.SELL_TOKEN_1 && isMatch),
                'ImpossibleLibrary: TRADE_NOT_ALLOWED'
            );
            amountInPostFee = (balanceIn.sub(reserveIn)).mul(10000 - fee);
        }
        /// If xybk invariant, set reserveIn/reserveOut to artificial liquidity instead of actual liquidity
        if (isXybk) {
            (uint256 boost0, uint256 boost1) = IImpossiblePair(pair).calcBoost();
            uint256 sqrtK = Math.sqrt(
                xybkComputeK(boost0, boost1, isMatch ? reserveIn : reserveOut, isMatch ? reserveOut : reserveIn)
            );
            /// since balance0=balance1 only at sqrtK, if final balanceIn >= sqrtK means balanceIn >= balanceOut
            /// Use post-fee balances to maintain consistency with pair contract K invariant check
            if (amountInPostFee.add(reserveIn.mul(10000)) >= sqrtK.mul(10000)) {
                /// If tokenIn = token0, balanceIn > sqrtK => balance0>sqrtK, use boost0
                artiLiqTerm = calcArtiLiquidityTerm(isMatch ? boost0 : boost1, sqrtK);
                /// If balance started from <sqrtK and ended at >sqrtK and boosts are different, there'll be different amountIn/Out
                /// Don't need to check in other case for reserveIn < reserveIn.add(x) <= sqrtK since that case doesnt cross midpt
                if (reserveIn < sqrtK && boost0 != boost1) {
                    /// Break into 2 trades => start point -> midpoint (sqrtK, sqrtK), then midpoint -> final point
                    amountOut = reserveOut.sub(sqrtK);
                    amountInPostFee = amountInPostFee.sub(sqrtK.sub(reserveIn));
                    reserveOut = sqrtK;
                    reserveIn = sqrtK;
                }
            } else {
                /// If tokenIn = token0, balanceIn < sqrtK => balance0<sqrtK, use boost0
                artiLiqTerm = calcArtiLiquidityTerm(isMatch ? boost1 : boost0, sqrtK);
            }
        }
        uint256 numerator = amountInPostFee.mul(reserveOut.add(artiLiqTerm));
        uint256 denominator = (reserveIn.add(artiLiqTerm)).mul(10000).add(amountInPostFee);
        uint256 lastSwapAmountOut = numerator / denominator;
        amountOut = (lastSwapAmountOut > reserveOut) ? reserveOut.add(amountOut) : lastSwapAmountOut.add(amountOut);
        return isMatch ? (uint256(0), amountOut) : (amountOut, uint256(0));
    }

    /**
     @notice Quotes maximum output given exact input amount of tokens and addresses of tokens in trade sequence
     @dev The library function considers custom swap fees/invariants/asymmetric tuning of pairs
     @dev However, library function doesn't consider limits created by hardstops
     @param factory The address of the IF factory
     @param amountIn The input amount of token A
     @param path[] An array of token addresses. Trades are made from arr idx 0 to arr end idx sequentially
     @return amounts The maximum possible output amount of all tokens through sequential swaps
    */
    function getAmountsOut(
        address factory,
        uint256 amountIn,
        address[] memory path
    ) internal view returns (uint256[] memory amounts) {
        require(path.length >= 2, 'ImpossibleLibrary: INVALID_PATH');
        amounts = new uint256[](path.length);
        amounts[0] = amountIn;
        for (uint256 i; i < path.length - 1; i++) {
            amounts[i + 1] = getAmountOut(amounts[i], path[i], path[i + 1], factory);
        }
    }

    /**
     @notice Quotes minimum input given exact output amount of tokens and addresses of tokens in trade sequence
     @dev The library function considers custom swap fees/invariants/asymmetric tuning of pairs
     @dev However, library function doesn't consider limits created by hardstops
     @param factory The address of the IF factory
     @param amountOut The output amount of token A
     @param path[] An array of token addresses. Trades are made from arr idx 0 to arr end idx sequentially
     @return amounts The minimum output amount required of all tokens through sequential swaps
    */
    function getAmountsIn(
        address factory,
        uint256 amountOut,
        address[] memory path
    ) internal view returns (uint256[] memory amounts) {
        require(path.length >= 2, 'ImpossibleLibrary: INVALID_PATH');
        amounts = new uint256[](path.length);
        amounts[amounts.length - 1] = amountOut;
        for (uint256 i = path.length - 1; i > 0; i--) {
            amounts[i - 1] = getAmountIn(amounts[i], path[i - 1], path[i], factory);
        }
    }
}

// SPDX-License-Identifier: GPL-3
pragma solidity =0.7.6;

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

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

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }
}

// SPDX-License-Identifier: GPL-3
pragma solidity =0.7.6;

interface IImpossibleSwapFactory {
    event PairCreated(address indexed token0, address indexed token1, address pair, uint256);
    event UpdatedGovernance(address governance);

    function feeTo() external view returns (address);

    function governance() 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 setGovernance(address) external;
}

// SPDX-License-Identifier: GPL-3
pragma solidity =0.7.6;

import './IImpossiblePair.sol';

interface IImpossibleRouterExtension {
    function factory() external returns (address factoryAddr);

    function swap(uint256[] memory amounts, address[] memory path) external;

    function swapSupportingFeeOnTransferTokens(address[] memory path) external;

    function addLiquidity(
        address tokenA,
        address tokenB,
        uint256 amountADesired,
        uint256 amountBDesired,
        uint256 amountAMin,
        uint256 amountBMin
    ) external returns (uint256 amountA, uint256 amountB);

    function removeLiquidity(
        address tokenA,
        address tokenB,
        address pair,
        uint256 amountAMin,
        uint256 amountBMin
    ) external returns (uint256 amountA, uint256 amountB);

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

    function getAmountOut(
        uint256 amountIn,
        address tokenIn,
        address tokenOut
    ) external view returns (uint256 amountOut);

    function getAmountIn(
        uint256 amountOut,
        address tokenIn,
        address tokenOut
    ) external view returns (uint256 amountIn);

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

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

// SPDX-License-Identifier: GPL-3
pragma solidity >=0.6.2;

interface IImpossibleRouter {
    function factory() external view returns (address factoryAddr);

    function routerExtension() external view returns (address routerExtensionAddr);

    function wrapFactory() external view returns (address wrapFactoryAddr);

    function WETH() external view returns (address WETHAddr);

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

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

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

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

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

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

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

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

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

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

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

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

    function removeLiquidityETH(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountToken, uint256 amountETH);

    function removeLiquidityWithPermit(
        address tokenA,
        address tokenB,
        uint256 liquidity,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint256 amountA, uint256 amountB);

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

    function removeLiquidityETHSupportingFeeOnTransferTokens(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountETH);

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

// SPDX-License-Identifier: GPL-3
pragma solidity =0.7.6;

interface IERC20 {
    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);
}

// SPDX-License-Identifier: GPL-3
pragma solidity >=0.5.0;

interface IWETH {
    function deposit() external payable;

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

    function withdraw(uint256) external;
}

// SPDX-License-Identifier: GPL-3
pragma solidity =0.7.6;

import './IERC20.sol';

interface IImpossibleWrappedToken is IERC20 {
    function deposit(address, uint256) external returns (uint256);

    function withdraw(address, uint256) external returns (uint256);

    function amtToUnderlyingAmt(uint256) external returns (uint256);
}

// SPDX-License-Identifier: GPL-3
pragma solidity =0.7.6;

interface IImpossibleWrapperFactory {
    event WrapCreated(address, address, uint256, uint256);
    event WrapDeleted(address, address);

    function tokensToWrappedTokens(address) external view returns (address);

    function wrappedTokensToTokens(address) external view returns (address);
}

// SPDX-License-Identifier: GPL-3
pragma solidity =0.7.6;

import './IImpossibleERC20.sol';

interface IImpossiblePair is IImpossibleERC20 {
    enum TradeState {
        SELL_ALL,
        SELL_TOKEN_0,
        SELL_TOKEN_1,
        SELL_NONE
    }

    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(uint256 reserve0, uint256 reserve1);
    event ChangeInvariant(bool _isXybk, uint256 _newBoost0, uint256 _newBoost1);
    event UpdatedTradeFees(uint256 _oldFee, uint256 _newFee);
    event UpdatedDelay(uint256 _oldDelay, uint256 _newDelay);
    event UpdatedTradeState(TradeState _tradeState);
    event UpdatedWithdrawalFeeRatio(uint256 _oldWithdrawalFee, uint256 _newWithdrawalFee);
    event UpdatedBoost(
        uint32 _oldBoost0,
        uint32 _oldBoost1,
        uint32 _newBoost0,
        uint32 _newBoost1,
        uint256 _start,
        uint256 _end
    );

    function MINIMUM_LIQUIDITY() external pure returns (uint256);

    function factory() external view returns (address);

    function token0() external view returns (address); // address of token0

    function token1() external view returns (address); // address of token1

    function getReserves() external view returns (uint256, uint256); // reserves of token0/token1

    function calcBoost() external view returns (uint256, uint256);

    function mint(address) external returns (uint256);

    function burn(address) external returns (uint256, uint256);

    function swap(
        uint256,
        uint256,
        address,
        bytes calldata
    ) external;

    function skim(address to) external;

    function sync() external;

    function getPairSettings()
        external
        view
        returns (
            uint16,
            TradeState,
            bool
        ); // Uses single storage slot, save gas

    function delay() external view returns (uint256); // Amount of time delay required before any change to boost etc, denoted in seconds

    function initialize(
        address,
        address,
        address,
        address
    ) external;
}

// SPDX-License-Identifier: GPL-3
pragma solidity =0.7.6;

// a library for performing various math operations

library Math {
    function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = x < y ? x : y;
    }

    // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
    function sqrt(uint256 y) internal pure returns (uint256 z) {
        if (y > 3) {
            z = y;
            uint256 x = y / 2 + 1;
            while (x < z) {
                z = x;
                x = (y / x + x) / 2;
            }
        } else if (y != 0) {
            z = 1;
        }
    }
}

// SPDX-License-Identifier: GPL-3
pragma solidity =0.7.6;

interface IImpossibleERC20 {
    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);

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

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_pairFactory","type":"address"},{"internalType":"address","name":"_wrapFactory","type":"address"},{"internalType":"address","name":"_utilitySettingAdmin","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"amountADesired","type":"uint256"},{"internalType":"uint256","name":"amountBDesired","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"addLiquidity","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amountTokenDesired","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"addLiquidityETH","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountETH","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidity","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidityETH","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidityETHSupportingFeeOnTransferTokens","outputs":[{"internalType":"uint256","name":"amountETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bool","name":"approveMax","type":"bool"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"removeLiquidityETHWithPermit","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bool","name":"approveMax","type":"bool"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"removeLiquidityETHWithPermitSupportingFeeOnTransferTokens","outputs":[{"internalType":"uint256","name":"amountETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bool","name":"approveMax","type":"bool"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"removeLiquidityWithPermit","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"routerExtension","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_WETH","type":"address"},{"internalType":"address","name":"_routerExtension","type":"address"}],"name":"setUtilities","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapETHForExactTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactETHForTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactETHForTokensSupportingFeeOnTransferTokens","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForETH","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForETHSupportingFeeOnTransferTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForTokensSupportingFeeOnTransferTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMax","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapTokensForExactETH","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMax","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapTokensForExactTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"wrapFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

60c06040523480156200001157600080fd5b5060405162004a8238038062004a82833981016040819052620000349162000096565b60016000819055606093841b6001600160601b03199081166080529290931b90911660a05281546001600160a01b0319166001600160a01b03909116179055620000df565b80516001600160a01b03811681146200009157600080fd5b919050565b600080600060608486031215620000ab578283fd5b620000b68462000079565b9250620000c66020850162000079565b9150620000d66040850162000079565b90509250925092565b60805160601c60a05160601c61491062000172600039806104085280612a625280612dda5250806104ad528061074b528061080852806109a85280610ae95280610d035280610daf5280610f1852806113e452806114fd52806116b1528061186f5280611bf85280611e675280611fce528061200052806121db52806123c552806126eb528061280152506149106000f3fe6080604052600436106101395760003560e01c80638803dbee116100ab578063c45a01551161006f578063c45a01551461035c578063ded9382a14610371578063e8e3370014610391578063f305d719146103c0578063fa7de13b146103d3578063fb3bdb41146103f357610154565b80638803dbee146102d4578063ad5c4648146102f4578063af2979eb14610309578063b6f9de9514610329578063baa2abde1461033c57610154565b80634a25d94a116100fd5780634a25d94a1461021f578063590c61f01461023f5780635b0d5984146102545780635c11d79514610281578063791ac947146102a15780637ff36ab5146102c157610154565b8063017e2d551461015957806302751cec1461018457806318cbafe5146101b25780632195995c146101df57806338ed1739146101ff57610154565b36610154576003546001600160a01b0316331461015257fe5b005b600080fd5b34801561016557600080fd5b5061016e610406565b60405161017b91906144b8565b60405180910390f35b34801561019057600080fd5b506101a461019f366004614222565b61042a565b60405161017b92919061473d565b3480156101be57600080fd5b506101d26101cd3660046143d0565b610681565b60405161017b91906145d0565b3480156101eb57600080fd5b506101a46101fa3660046140fe565b61099e565b34801561020b57600080fd5b506101d261021a3660046143d0565b610a73565b34801561022b57600080fd5b506101d261023a3660046143d0565b610c39565b34801561024b57600080fd5b5061016e610d92565b34801561026057600080fd5b5061027461026f36600461427f565b610da1565b60405161017b9190614734565b34801561028d57600080fd5b5061015261029c3660046143d0565b610e7e565b3480156102ad57600080fd5b506101526102bc3660046143d0565b6110d4565b6101d26102cf366004614348565b61131c565b3480156102e057600080fd5b506101d26102ef3660046143d0565b61163b565b34801561030057600080fd5b5061016e6117e7565b34801561031557600080fd5b50610274610324366004614222565b6117f6565b610152610337366004614348565b611abd565b34801561034857600080fd5b506101a461035736600461408d565b611df1565b34801561036857600080fd5b5061016e611fcc565b34801561037d57600080fd5b506101a461038c36600461427f565b611ff0565b34801561039d57600080fd5b506103b16103ac3660046141a7565b6120d3565b60405161017b9392919061474b565b6103b16103ce366004614222565b6122b3565b3480156103df57600080fd5b506101526103ee366004614055565b6125a0565b6101d2610401366004614348565b612623565b7f000000000000000000000000000000000000000000000000000000000000000081565b60008082428110156104575760405162461bcd60e51b815260040161044e906146fd565b60405180910390fd5b6002600054141561049d576040805162461bcd60e51b815260206004820152601f60248201526000805160206147b4833981519152604482015290519081900360640190fd5b600260009081556003546104dd907f0000000000000000000000000000000000000000000000000000000000000000908c906001600160a01b0316612988565b6040516323b872dd60e01b81529091506001600160a01b038216906323b872dd9061051090339085908e9060040161450d565b602060405180830381600087803b15801561052a57600080fd5b505af115801561053e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105629190614314565b5060025460035460405163e54dc2d760e01b81526001600160a01b039283169263e54dc2d7926105a0928f929091169086908e908e90600401614531565b6040805180830381600087803b1580156105b957600080fd5b505af11580156105cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105f191906143ad565b90945092506106018a8786612a48565b600354604051632e1a7d4d60e01b81526001600160a01b0390911690632e1a7d4d90610631908690600401614734565b600060405180830381600087803b15801561064b57600080fd5b505af115801561065f573d6000803e3d6000fd5b5050505061066d8684612b8f565b505060016000559097909650945050505050565b606081428110156106a45760405162461bcd60e51b815260040161044e906146fd565b600260005414156106ea576040805162461bcd60e51b815260206004820152601f60248201526000805160206147b4833981519152604482015290519081900360640190fd5b60026000556003546001600160a01b03168686600019810181811061070b57fe5b9050602002016020810190610720919061401d565b6001600160a01b0316146107465760405162461bcd60e51b815260040161044e906146c6565b6107a47f000000000000000000000000000000000000000000000000000000000000000089888880806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250612c8792505050565b915086826001845103815181106107b757fe5b602002602001015110156107dd5760405162461bcd60e51b815260040161044e9061467a565b61088a868660008181106107ed57fe5b9050602002016020810190610802919061401d565b336108707f00000000000000000000000000000000000000000000000000000000000000008a8a600081811061083457fe5b9050602002016020810190610849919061401d565b8b8b600181811061085657fe5b905060200201602081019061086b919061401d565b612988565b8560008151811061087d57fe5b6020026020010151612dc0565b600254604051631a70b66160e21b81526001600160a01b03909116906369c2d984906108be9085908a908a906004016145e3565b600060405180830381600087803b1580156108d857600080fd5b505af11580156108ec573d6000803e3d6000fd5b505060035484516001600160a01b039091169250632e1a7d4d91508490600019810190811061091757fe5b60200260200101516040518263ffffffff1660e01b815260040161093b9190614734565b600060405180830381600087803b15801561095557600080fd5b505af1158015610969573d6000803e3d6000fd5b5050505061098e848360018551038151811061098157fe5b6020026020010151612b8f565b5060016000559695505050505050565b60008060006109ce7f00000000000000000000000000000000000000000000000000000000000000008f8f612988565b90506000876109dd578c6109e1565b6000195b60405163d505accf60e01b81529091506001600160a01b0383169063d505accf90610a1c903390309086908f908e908e908e906004016144cc565b600060405180830381600087803b158015610a3657600080fd5b505af1158015610a4a573d6000803e3d6000fd5b50505050610a5d8f8f8f8f8f8f8f611df1565b9350935050509b509b9950505050505050505050565b60608142811015610a965760405162461bcd60e51b815260040161044e906146fd565b60026000541415610adc576040805162461bcd60e51b815260206004820152601f60248201526000805160206147b4833981519152604482015290519081900360640190fd5b6002600081905550610b427f000000000000000000000000000000000000000000000000000000000000000089888880806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250612c8792505050565b91508682600184510381518110610b5557fe5b60200260200101511015610b7b5760405162461bcd60e51b815260040161044e9061467a565b610b8b868660008181106107ed57fe5b600254604051631a70b66160e21b81526001600160a01b03909116906369c2d98490610bbf9085908a908a906004016145e3565b600060405180830381600087803b158015610bd957600080fd5b505af1158015610bed573d6000803e3d6000fd5b5050505061098e868660018989905003818110610c0657fe5b9050602002016020810190610c1b919061401d565b8584600186510381518110610c2c57fe5b6020026020010151612a48565b60608142811015610c5c5760405162461bcd60e51b815260040161044e906146fd565b60026000541415610ca2576040805162461bcd60e51b815260206004820152601f60248201526000805160206147b4833981519152604482015290519081900360640190fd5b60026000556003546001600160a01b031686866000198101818110610cc357fe5b9050602002016020810190610cd8919061401d565b6001600160a01b031614610cfe5760405162461bcd60e51b815260040161044e906146c6565b610d5c7f000000000000000000000000000000000000000000000000000000000000000089888880806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250612fa392505050565b91508682600081518110610d6c57fe5b602002602001015111156107dd5760405162461bcd60e51b815260040161044e90614632565b6002546001600160a01b031681565b6003546000908190610ddf907f0000000000000000000000000000000000000000000000000000000000000000908e906001600160a01b0316612988565b9050600086610dee578b610df2565b6000195b60405163d505accf60e01b81529091506001600160a01b0383169063d505accf90610e2d903390309086908e908d908d908d906004016144cc565b600060405180830381600087803b158015610e4757600080fd5b505af1158015610e5b573d6000803e3d6000fd5b50505050610e6d8d8d8d8d8d8d6117f6565b9d9c50505050505050505050505050565b8042811015610e9f5760405162461bcd60e51b815260040161044e906146fd565b60026000541415610ee5576040805162461bcd60e51b815260206004820152601f60248201526000805160206147b4833981519152604482015290519081900360640190fd5b6002600081905550610f6c85856000818110610efd57fe5b9050602002016020810190610f12919061401d565b33610f667f000000000000000000000000000000000000000000000000000000000000000089896000818110610f4457fe5b9050602002016020810190610f59919061401d565b8a8a600181811061085657fe5b8a612dc0565b600254604051636fb0847760e01b81526001600160a01b0390911690636fb0847790610f9e90889088906004016145b4565b600060405180830381600087803b158015610fb857600080fd5b505af1158015610fcc573d6000803e3d6000fd5b505050506000858560018888905003818110610fe457fe5b9050602002016020810190610ff9919061401d565b6001600160a01b03166370a08231306040518263ffffffff1660e01b815260040161102491906144b8565b60206040518083038186803b15801561103c57600080fd5b505afa158015611050573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110749190614330565b9050868110156110965760405162461bcd60e51b815260040161044e9061467a565b6110c5868660001981018181106110a957fe5b90506020020160208101906110be919061401d565b8583612a48565b50506001600055505050505050565b80428110156110f55760405162461bcd60e51b815260040161044e906146fd565b6002600054141561113b576040805162461bcd60e51b815260206004820152601f60248201526000805160206147b4833981519152604482015290519081900360640190fd5b60026000556003546001600160a01b03168585600019810181811061115c57fe5b9050602002016020810190611171919061401d565b6001600160a01b0316146111975760405162461bcd60e51b815260040161044e906146c6565b6111a785856000818110610efd57fe5b600254604051636fb0847760e01b81526001600160a01b0390911690636fb08477906111d990889088906004016145b4565b600060405180830381600087803b1580156111f357600080fd5b505af1158015611207573d6000803e3d6000fd5b50506003546040516370a0823160e01b8152600093506001600160a01b0390911691506370a082319061123e9030906004016144b8565b60206040518083038186803b15801561125657600080fd5b505afa15801561126a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061128e9190614330565b9050868110156112b05760405162461bcd60e51b815260040161044e9061467a565b600354604051632e1a7d4d60e01b81526001600160a01b0390911690632e1a7d4d906112e0908490600401614734565b600060405180830381600087803b1580156112fa57600080fd5b505af115801561130e573d6000803e3d6000fd5b505050506110c58482612b8f565b6060814281101561133f5760405162461bcd60e51b815260040161044e906146fd565b60026000541415611385576040805162461bcd60e51b815260206004820152601f60248201526000805160206147b4833981519152604482015290519081900360640190fd5b600260009081556003546001600160a01b03169087908790816113a457fe5b90506020020160208101906113b9919061401d565b6001600160a01b0316146113df5760405162461bcd60e51b815260040161044e906146c6565b61143d7f000000000000000000000000000000000000000000000000000000000000000034888880806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250612c8792505050565b9150868260018451038151811061145057fe5b602002602001015110156114765760405162461bcd60e51b815260040161044e9061467a565b60035482516001600160a01b039091169063d0e30db090849060009061149857fe5b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b1580156114cb57600080fd5b505af11580156114df573d6000803e3d6000fd5b50506003546001600160a01b0316925063a9059cbb915061152790507f00000000000000000000000000000000000000000000000000000000000000008989600081610f4457fe5b8460008151811061153457fe5b60200260200101516040518363ffffffff1660e01b815260040161155992919061459b565b602060405180830381600087803b15801561157357600080fd5b505af1158015611587573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115ab9190614314565b6115b157fe5b600254604051631a70b66160e21b81526001600160a01b03909116906369c2d984906115e59085908a908a906004016145e3565b600060405180830381600087803b1580156115ff57600080fd5b505af1158015611613573d6000803e3d6000fd5b5050505061162c868660018989905003818110610c0657fe5b50600160005595945050505050565b6060814281101561165e5760405162461bcd60e51b815260040161044e906146fd565b600260005414156116a4576040805162461bcd60e51b815260206004820152601f60248201526000805160206147b4833981519152604482015290519081900360640190fd5b600260008190555061170a7f000000000000000000000000000000000000000000000000000000000000000089888880806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250612fa392505050565b9150868260008151811061171a57fe5b602002602001015111156117405760405162461bcd60e51b815260040161044e90614632565b611750868660008181106107ed57fe5b600254604051631a70b66160e21b81526001600160a01b03909116906369c2d984906117849085908a908a906004016145e3565b600060405180830381600087803b15801561179e57600080fd5b505af11580156117b2573d6000803e3d6000fd5b5050505061098e8686600189899050038181106117cb57fe5b90506020020160208101906117e0919061401d565b858a612a48565b6003546001600160a01b031681565b600081428110156118195760405162461bcd60e51b815260040161044e906146fd565b6002600054141561185f576040805162461bcd60e51b815260206004820152601f60248201526000805160206147b4833981519152604482015290519081900360640190fd5b6002600090815560035461189f907f0000000000000000000000000000000000000000000000000000000000000000908b906001600160a01b0316612988565b6040516323b872dd60e01b81529091506001600160a01b038216906323b872dd906118d290339085908d9060040161450d565b602060405180830381600087803b1580156118ec57600080fd5b505af1158015611900573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119249190614314565b5060025460035460405163e54dc2d760e01b81526001600160a01b039283169263e54dc2d792611962928e929091169086908d908d90600401614531565b6040805180830381600087803b15801561197b57600080fd5b505af115801561198f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119b391906143ad565b6040516370a0823160e01b8152909450611a4091508a9087906001600160a01b038316906370a08231906119eb9030906004016144b8565b60206040518083038186803b158015611a0357600080fd5b505afa158015611a17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a3b9190614330565b612a48565b600354604051632e1a7d4d60e01b81526001600160a01b0390911690632e1a7d4d90611a70908690600401614734565b600060405180830381600087803b158015611a8a57600080fd5b505af1158015611a9e573d6000803e3d6000fd5b50505050611aac8584612b8f565b505060016000559695505050505050565b8042811015611ade5760405162461bcd60e51b815260040161044e906146fd565b60026000541415611b24576040805162461bcd60e51b815260206004820152601f60248201526000805160206147b4833981519152604482015290519081900360640190fd5b600260009081556003546001600160a01b0316908690869081611b4357fe5b9050602002016020810190611b58919061401d565b6001600160a01b031614611b7e5760405162461bcd60e51b815260040161044e906146c6565b60035460408051630d0e30db60e41b8152905134926001600160a01b03169163d0e30db091849160048082019260009290919082900301818588803b158015611bc657600080fd5b505af1158015611bda573d6000803e3d6000fd5b50506003546001600160a01b0316925063a9059cbb9150611c2290507f00000000000000000000000000000000000000000000000000000000000000008989600081610f4457fe5b836040518363ffffffff1660e01b8152600401611c4092919061459b565b602060405180830381600087803b158015611c5a57600080fd5b505af1158015611c6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c929190614314565b611c9857fe5b600254604051636fb0847760e01b81526001600160a01b0390911690636fb0847790611cca90899089906004016145b4565b600060405180830381600087803b158015611ce457600080fd5b505af1158015611cf8573d6000803e3d6000fd5b505050506000868660018989905003818110611d1057fe5b9050602002016020810190611d25919061401d565b6001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401611d5091906144b8565b60206040518083038186803b158015611d6857600080fd5b505afa158015611d7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611da09190614330565b905087811015611dc25760405162461bcd60e51b815260040161044e9061467a565b6110c587876000198101818110611dd557fe5b9050602002016020810190611dea919061401d565b8683612a48565b6000808242811015611e155760405162461bcd60e51b815260040161044e906146fd565b60026000541415611e5b576040805162461bcd60e51b815260206004820152601f60248201526000805160206147b4833981519152604482015290519081900360640190fd5b60026000908155611e8d7f00000000000000000000000000000000000000000000000000000000000000008c8c612988565b6040516323b872dd60e01b81529091506001600160a01b038216906323b872dd90611ec090339085908e9060040161450d565b602060405180830381600087803b158015611eda57600080fd5b505af1158015611eee573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f129190614314565b5060025460405163e54dc2d760e01b81526001600160a01b039091169063e54dc2d790611f4b908e908e9086908e908e90600401614531565b6040805180830381600087803b158015611f6457600080fd5b505af1158015611f78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f9c91906143ad565b9094509250611fac8b8786612a48565b611fb78a8785612a48565b50506001600055909890975095505050505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b60035460009081908190612030907f0000000000000000000000000000000000000000000000000000000000000000908f906001600160a01b0316612988565b905060008761203f578c612043565b6000195b60405163d505accf60e01b81529091506001600160a01b0383169063d505accf9061207e903390309086908f908e908e908e906004016144cc565b600060405180830381600087803b15801561209857600080fd5b505af11580156120ac573d6000803e3d6000fd5b505050506120be8e8e8e8e8e8e61042a565b909f909e509c50505050505050505050505050565b600080600083428110156120f95760405162461bcd60e51b815260040161044e906146fd565b6002600054141561213f576040805162461bcd60e51b815260206004820152601f60248201526000805160206147b4833981519152604482015290519081900360640190fd5b6002600081905554604051633351733f60e01b81526001600160a01b0390911690633351733f9061217e908f908f908f908f908f908f90600401614565565b6040805180830381600087803b15801561219757600080fd5b505af11580156121ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121cf91906143ad565b909450925060006122017f00000000000000000000000000000000000000000000000000000000000000008e8e612988565b905061220f8d338388612dc0565b61221b8c338387612dc0565b6040516335313c2160e11b81526001600160a01b03821690636a62784290612247908a906004016144b8565b602060405180830381600087803b15801561226157600080fd5b505af1158015612275573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122999190614330565b925050600160008190555050985098509895505050505050565b600080600083428110156122d95760405162461bcd60e51b815260040161044e906146fd565b6002600054141561231f576040805162461bcd60e51b815260206004820152601f60248201526000805160206147b4833981519152604482015290519081900360640190fd5b6002600081905554600354604051633351733f60e01b81526001600160a01b0392831692633351733f92612363928f92909116908e9034908f908f90600401614565565b6040805180830381600087803b15801561237c57600080fd5b505af1158015612390573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123b491906143ad565b60035491955093506000906123f5907f0000000000000000000000000000000000000000000000000000000000000000908d906001600160a01b0316612988565b90506124038b338388612dc0565b600360009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0856040518263ffffffff1660e01b81526004016000604051808303818588803b15801561245357600080fd5b505af1158015612467573d6000803e3d6000fd5b505060035460405163a9059cbb60e01b81526001600160a01b03909116935063a9059cbb925061249e91508490889060040161459b565b602060405180830381600087803b1580156124b857600080fd5b505af11580156124cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124f09190614314565b6124f657fe5b6040516335313c2160e11b81526001600160a01b03821690636a62784290612522908a906004016144b8565b602060405180830381600087803b15801561253c57600080fd5b505af1158015612550573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125749190614330565b92508334111561258a5761258a33853403612b8f565b5060016000819055505096509650969350505050565b6003546001600160a01b03161580156125c257506002546001600160a01b0316155b6125cb57600080fd5b6001546001600160a01b031633146125f55760405162461bcd60e51b815260040161044e90614613565b600380546001600160a01b039384166001600160a01b03199182161790915560028054929093169116179055565b606081428110156126465760405162461bcd60e51b815260040161044e906146fd565b6002600054141561268c576040805162461bcd60e51b815260206004820152601f60248201526000805160206147b4833981519152604482015290519081900360640190fd5b600260009081556003546001600160a01b03169087908790816126ab57fe5b90506020020160208101906126c0919061401d565b6001600160a01b0316146126e65760405162461bcd60e51b815260040161044e906146c6565b6127447f000000000000000000000000000000000000000000000000000000000000000088888880806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250612fa392505050565b9150348260008151811061275457fe5b6020026020010151111561277a5760405162461bcd60e51b815260040161044e90614632565b60035482516001600160a01b039091169063d0e30db090849060009061279c57fe5b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b1580156127cf57600080fd5b505af11580156127e3573d6000803e3d6000fd5b50506003546001600160a01b0316925063a9059cbb915061282b90507f00000000000000000000000000000000000000000000000000000000000000008989600081610f4457fe5b8460008151811061283857fe5b60200260200101516040518363ffffffff1660e01b815260040161285d92919061459b565b602060405180830381600087803b15801561287757600080fd5b505af115801561288b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128af9190614314565b6128b557fe5b600254604051631a70b66160e21b81526001600160a01b03909116906369c2d984906128e99085908a908a906004016145e3565b600060405180830381600087803b15801561290357600080fd5b505af1158015612917573d6000803e3d6000fd5b5050505061294c86866001898990500381811061293057fe5b9050602002016020810190612945919061401d565b8589612a48565b8160008151811061295957fe5b602002602001015134111561162c5761162c338360008151811061297957fe5b60200260200101513403612b8f565b600080600061299785856130d5565b604080516bffffffffffffffffffffffff19606094851b811660208084019190915293851b81166034830152825160288184030181526048830184528051908501206001600160f81b031960688401529a90941b9093166069840152607d8301989098527ffc84b622ba228c468b74c2d99bfe9454ffac280ac017f05a02feb9f739aeb1e4609d808401919091528851808403909101815260bd909201909752805196019590952095945050505050565b604051632a4d95e760e21b81526000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063a936579c90612a979087906004016144b8565b60206040518083038186803b158015612aaf57600080fd5b505afa158015612ac3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ae79190614039565b90506001600160a01b038116612b0757612b028484846131b3565b612b89565b60405163f3fef3a360e01b81526001600160a01b0385169063f3fef3a390612b35908690869060040161459b565b602060405180830381600087803b158015612b4f57600080fd5b505af1158015612b63573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b879190614330565b505b50505050565b604080516000808252602082019092526001600160a01b0384169083906040518082805190602001908083835b60208310612bdb5780518252601f199092019160209182019101612bbc565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114612c3d576040519150601f19603f3d011682016040523d82523d6000602084013e612c42565b606091505b5050905080612c825760405162461bcd60e51b81526004018080602001828103825260348152602001806148056034913960400191505060405180910390fd5b505050565b6060600282511015612ce0576040805162461bcd60e51b815260206004820152601f60248201527f496d706f737369626c654c6962726172793a20494e56414c49445f5041544800604482015290519081900360640190fd5b815167ffffffffffffffff81118015612cf857600080fd5b50604051908082528060200260200182016040528015612d22578160200160208202803683370190505b5090508281600081518110612d3357fe5b60200260200101818152505060005b6001835103811015612db857612d96828281518110612d5d57fe5b6020026020010151848381518110612d7157fe5b6020026020010151858460010181518110612d8857fe5b6020026020010151886132ff565b828260010181518110612da557fe5b6020908102919091010152600101612d42565b509392505050565b604051632a4d95e760e21b81526000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063a936579c90612e0f9088906004016144b8565b60206040518083038186803b158015612e2757600080fd5b505afa158015612e3b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e5f9190614039565b90506001600160a01b038116612e8057612e7b85858585613668565b612b87565b604051635ac402df60e11b81526000906001600160a01b0387169063b58805be90612eaf908690600401614734565b602060405180830381600087803b158015612ec957600080fd5b505af1158015612edd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f019190614330565b9050612f0f82863084613668565b612f1a8287836137c4565b6040516311f9fbc960e21b81526001600160a01b038716906347e7ef2490612f48908790859060040161459b565b602060405180830381600087803b158015612f6257600080fd5b505af1158015612f76573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f9a9190614330565b50505050505050565b6060600282511015612ffc576040805162461bcd60e51b815260206004820152601f60248201527f496d706f737369626c654c6962726172793a20494e56414c49445f5041544800604482015290519081900360640190fd5b815167ffffffffffffffff8111801561301457600080fd5b5060405190808252806020026020018201604052801561303e578160200160208202803683370190505b509050828160018351038151811061305257fe5b60209081029190910101528151600019015b8015612db8576130b282828151811061307957fe5b602002602001015184600184038151811061309057fe5b60200260200101518584815181106130a457fe5b602002602001015188613910565b8260018303815181106130c157fe5b602090810291909101015260001901613064565b600080826001600160a01b0316846001600160a01b031614156131295760405162461bcd60e51b81526004018080602001828103825260268152602001806148b56026913960400191505060405180910390fd5b826001600160a01b0316846001600160a01b03161061314957828461314c565b83835b90925090506001600160a01b0382166131ac576040805162461bcd60e51b815260206004820152601f60248201527f496d706f737369626c654c6962726172793a205a45524f5f4144445245535300604482015290519081900360640190fd5b9250929050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b1781529251825160009485949389169392918291908083835b6020831061322f5780518252601f199092019160209182019101613210565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114613291576040519150601f19603f3d011682016040523d82523d6000602084013e613296565b606091505b50915091508180156132c45750805115806132c457508080602001905160208110156132c157600080fd5b50515b612b875760405162461bcd60e51b815260040180806020018281038252602d815260200180614888602d913960400191505060405180910390fd5b600080851161333f5760405162461bcd60e51b815260040180806020018281038252602c815260200180614788602c913960400191505060405180910390fd5b6000806000806000806133528a8a6130d5565b509050806001600160a01b03168a6001600160a01b0316149150613377888b8b613c2b565b80955081975082985050505050600080600080856001600160a01b031663e94e3d8d6040518163ffffffff1660e01b815260040160606040518083038186803b1580156133c357600080fd5b505afa1580156133d7573d6000803e3d6000fd5b505050506040513d60608110156133ed57600080fd5b5080516020820151604090920151945061ffff16925090506134148e612710849003613ced565b9650600081600381111561342457fe5b14806134445750600181600381111561343957fe5b148015613444575084155b806134625750600281600381111561345857fe5b1480156134625750845b61349d5760405162461bcd60e51b81526004018080602001828103825260248152602001806148396024913960400191505060405180910390fd5b505080156135f157600080856001600160a01b031663b6b9f61b6040518163ffffffff1660e01b8152600401604080518083038186803b1580156134e057600080fd5b505afa1580156134f4573d6000803e3d6000fd5b505050506040513d604081101561350a57600080fd5b5080516020909101519092509050600061354661354184848961352d578c61352f565b8d5b8a61353a578e61353c565b8d5b613d56565b613de3565b905061355481612710613ced565b61356a6135638c612710613ced565b8a90613e35565b106135d5576135858661357d578261357f565b835b82613e84565b9450808a1080156135965750818314155b156135d0576135a58982613e9b565b9a506135c76135c06127106135ba848e613e9b565b90613ced565b8990613e9b565b97508099508098505b6135ed565b6135ea866135e3578361357f565b8282613e84565b94505b5050505b60006136076136008885613e35565b8790613ced565b905060006136258761361f6127106135ba8d89613e35565b90613e35565b9050600081838161363257fe5b04905088811161364b57613646818c613e35565b613655565b613655898c613e35565b9f9e505050505050505050505050505050565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b178152925182516000948594938a169392918291908083835b602083106136ec5780518252601f1990920191602091820191016136cd565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d806000811461374e576040519150601f19603f3d011682016040523d82523d6000602084013e613753565b606091505b5091509150818015613781575080511580613781575080806020019051602081101561377e57600080fd5b50515b6137bc5760405162461bcd60e51b81526004018080602001828103825260318152602001806147d46031913960400191505060405180910390fd5b505050505050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663095ea7b360e01b1781529251825160009485949389169392918291908083835b602083106138405780518252601f199092019160209182019101613821565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146138a2576040519150601f19603f3d011682016040523d82523d6000602084013e6138a7565b606091505b50915091508180156138d55750805115806138d557508080602001905160208110156138d257600080fd5b50515b612b875760405162461bcd60e51b815260040180806020018281038252602b81526020018061485d602b913960400191505060405180910390fd5b60008085116139505760405162461bcd60e51b815260040180806020018281038252602c815260200180614788602c913960400191505060405180910390fd5b60008060008060008060008060006139688d8d6130d5565b506001600160a01b038e81169116149450600090506139888b8e8e613c2b565b809350819a50829b505050506000816001600160a01b031663e94e3d8d6040518163ffffffff1660e01b815260040160606040518083038186803b1580156139cf57600080fd5b505afa1580156139e3573d6000803e3d6000fd5b505050506040513d60608110156139f957600080fd5b508051602082015160409092015161ffff9091169850955090506000816003811115613a2157fe5b1480613a4157506001816003811115613a3657fe5b148015613a41575085155b80613a5f57506002816003811115613a5557fe5b148015613a5f5750855b613a9a5760405162461bcd60e51b81526004018080602001828103825260248152602001806148396024913960400191505060405180910390fd5b816001600160a01b031663b6b9f61b6040518163ffffffff1660e01b8152600401604080518083038186803b158015613ad257600080fd5b505afa158015613ae6573d6000803e3d6000fd5b505050506040513d6040811015613afc57600080fd5b50805160209091015190945092505083159050613bc0576000613b3b613541848488613b28578b613b2a565b8c5b89613b35578d61353c565b8c613d56565b905080613b518f8a613e9b90919063ffffffff16565b10613b6b57613b64856135e3578361357f565b9650613bbe565b613b798561357d578261357f565b96508088118015613b8a5750818314155b15613bbe57613b9f6127106135ba838c613e9b565b9950613bb5613bae8983613e9b565b8f90613e9b565b9d508097508098505b505b5060009150613bd990506127106135ba8d818a89613e35565b90506000613bf18c613beb8888613e35565b90613e9b565b9050613c1b600161361f8661271003613c15858781613c0c57fe5b8e919004613e35565b90613eeb565b9c9b505050505050505050505050565b600080600080613c3b86866130d5565b509050613c49878787612988565b9150600080836001600160a01b0316630902f1ac6040518163ffffffff1660e01b8152600401604080518083038186803b158015613c8657600080fd5b505afa158015613c9a573d6000803e3d6000fd5b505050506040513d6040811015613cb057600080fd5b50805160209091015190925090506001600160a01b0388811690841614613cd8578082613cdb565b81815b909a9099509397509295505050505050565b6000811580613d0857505080820282828281613d0557fe5b04145b613d50576040805162461bcd60e51b815260206004820152601460248201527364732d6d6174682d6d756c2d6f766572666c6f7760601b604482015290519081900360640190fd5b92915050565b600080828411613d7057613d6b856001613e9b565b613d7b565b613d7b866001613e9b565b90506000613d8f600161361f846002613ced565b90506000613db5613da1836002613ced565b613c15613dae8989613e35565b8690613ced565b9050600281613dd5613dcb85613c158b8b613ced565b6002850a01613de3565b010a98975050505050505050565b60006003821115613e26575080600160028204015b81811015613e2057809150600281828581613e0f57fe5b040181613e1857fe5b049050613df8565b50613e30565b8115613e30575060015b919050565b80820182811015613d50576040805162461bcd60e51b815260206004820152601460248201527364732d6d6174682d6164642d6f766572666c6f7760601b604482015290519081900360640190fd5b6000613e94600019840183613ced565b9392505050565b80820382811115613d50576040805162461bcd60e51b815260206004820152601560248201527464732d6d6174682d7375622d756e646572666c6f7760581b604482015290519081900360640190fd5b6000613e9483836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f00000000000081525060008183613fb45760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015613f79578181015183820152602001613f61565b50505050905090810190601f168015613fa65780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b506000838581613fc057fe5b0495945050505050565b60008083601f840112613fdb578182fd5b50813567ffffffffffffffff811115613ff2578182fd5b60208301915083602080830285010111156131ac57600080fd5b803560ff81168114613e3057600080fd5b60006020828403121561402e578081fd5b8135613e9481614761565b60006020828403121561404a578081fd5b8151613e9481614761565b60008060408385031215614067578081fd5b823561407281614761565b9150602083013561408281614761565b809150509250929050565b600080600080600080600060e0888a0312156140a7578283fd5b87356140b281614761565b965060208801356140c281614761565b955060408801359450606088013593506080880135925060a08801356140e781614761565b8092505060c0880135905092959891949750929550565b60008060008060008060008060008060006101608c8e03121561411f578384fd5b8b3561412a81614761565b9a5060208c013561413a81614761565b995060408c0135985060608c0135975060808c0135965060a08c013561415f81614761565b955060c08c0135945060e08c013561417681614779565b93506141856101008d0161400c565b92506101208c013591506101408c013590509295989b509295989b9093969950565b600080600080600080600080610100898b0312156141c3578384fd5b88356141ce81614761565b975060208901356141de81614761565b965060408901359550606089013594506080890135935060a0890135925060c089013561420a81614761565b8092505060e089013590509295985092959890939650565b60008060008060008060c0878903121561423a578182fd5b863561424581614761565b9550602087013594506040870135935060608701359250608087013561426a81614761565b8092505060a087013590509295509295509295565b6000806000806000806000806000806101408b8d03121561429e578384fd5b8a356142a981614761565b995060208b0135985060408b0135975060608b0135965060808b01356142ce81614761565b955060a08b0135945060c08b01356142e581614779565b93506142f360e08c0161400c565b92506101008b013591506101208b013590509295989b9194979a5092959850565b600060208284031215614325578081fd5b8151613e9481614779565b600060208284031215614341578081fd5b5051919050565b60008060008060006080868803121561435f578283fd5b85359450602086013567ffffffffffffffff81111561437c578384fd5b61438888828901613fca565b909550935050604086013561439c81614761565b949793965091946060013592915050565b600080604083850312156143bf578182fd5b505080516020909101519092909150565b60008060008060008060a087890312156143e8578384fd5b8635955060208701359450604087013567ffffffffffffffff81111561440c578485fd5b61441889828a01613fca565b909550935050606087013561442c81614761565b80925050608087013590509295509295509295565b60008284526020808501945082825b8581101561447e57813561446381614761565b6001600160a01b031687529582019590820190600101614450565b509495945050505050565b6000815180845260208085019450808401835b8381101561447e5781518752958201959082019060010161449c565b6001600160a01b0391909116815260200190565b6001600160a01b0397881681529590961660208601526040850193909352606084019190915260ff16608083015260a082015260c081019190915260e00190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03958616815293851660208501529190931660408301526060820192909252608081019190915260a00190565b6001600160a01b03968716815294909516602085015260408401929092526060830152608082015260a081019190915260c00190565b6001600160a01b03929092168252602082015260400190565b6000602082526145c8602083018486614441565b949350505050565b600060208252613e946020830184614489565b6000604082526145f66040830186614489565b8281036020840152614609818587614441565b9695505050505050565b60208082526005908201526449463a203f60d81b604082015260600190565b60208082526028908201527f496d706f737369626c65526f757465723a204558434553534956455f494e50556040820152671517d05353d5539560c21b606082015260800190565b6020808252602c908201527f496d706f737369626c65526f757465723a20494e53554646494349454e545f4f60408201526b155514155517d05353d5539560a21b606082015260800190565b6020808252601e908201527f496d706f737369626c65526f757465723a20494e56414c49445f504154480000604082015260600190565b60208082526019908201527f496d706f737369626c65526f757465723a204558504952454400000000000000604082015260600190565b90815260200190565b918252602082015260400190565b9283526020830191909152604082015260600190565b6001600160a01b038116811461477657600080fd5b50565b801515811461477657600080fdfe496d706f737369626c654c6962726172793a20494e53554646494349454e545f494e5055545f414d4f554e545265656e7472616e637947756172643a207265656e7472616e742063616c6c005472616e7366657248656c7065723a3a7472616e7366657246726f6d3a207472616e7366657246726f6d206661696c65645472616e7366657248656c7065723a3a736166655472616e736665724554483a20455448207472616e73666572206661696c6564496d706f737369626c654c6962726172793a2054524144455f4e4f545f414c4c4f5745445472616e7366657248656c7065723a3a73616665417070726f76653a20617070726f7665206661696c65645472616e7366657248656c7065723a3a736166655472616e736665723a207472616e73666572206661696c6564496d706f737369626c654c6962726172793a204944454e544943414c5f414444524553534553a26469706673582212201d47bb6aa4c4e5a635ca01716e6be81c6af6f3e867a84fc2495526c9765ca44264736f6c6343000706003300000000000000000000000045603612891b6406a06854813e18443fc8ec7c7300000000000000000000000045a3a315277fbc1bce0611c4398b32e0317fd7c10000000000000000000000003c9584426432eb851e5689230d5cfc50659103d5

Deployed Bytecode

0x6080604052600436106101395760003560e01c80638803dbee116100ab578063c45a01551161006f578063c45a01551461035c578063ded9382a14610371578063e8e3370014610391578063f305d719146103c0578063fa7de13b146103d3578063fb3bdb41146103f357610154565b80638803dbee146102d4578063ad5c4648146102f4578063af2979eb14610309578063b6f9de9514610329578063baa2abde1461033c57610154565b80634a25d94a116100fd5780634a25d94a1461021f578063590c61f01461023f5780635b0d5984146102545780635c11d79514610281578063791ac947146102a15780637ff36ab5146102c157610154565b8063017e2d551461015957806302751cec1461018457806318cbafe5146101b25780632195995c146101df57806338ed1739146101ff57610154565b36610154576003546001600160a01b0316331461015257fe5b005b600080fd5b34801561016557600080fd5b5061016e610406565b60405161017b91906144b8565b60405180910390f35b34801561019057600080fd5b506101a461019f366004614222565b61042a565b60405161017b92919061473d565b3480156101be57600080fd5b506101d26101cd3660046143d0565b610681565b60405161017b91906145d0565b3480156101eb57600080fd5b506101a46101fa3660046140fe565b61099e565b34801561020b57600080fd5b506101d261021a3660046143d0565b610a73565b34801561022b57600080fd5b506101d261023a3660046143d0565b610c39565b34801561024b57600080fd5b5061016e610d92565b34801561026057600080fd5b5061027461026f36600461427f565b610da1565b60405161017b9190614734565b34801561028d57600080fd5b5061015261029c3660046143d0565b610e7e565b3480156102ad57600080fd5b506101526102bc3660046143d0565b6110d4565b6101d26102cf366004614348565b61131c565b3480156102e057600080fd5b506101d26102ef3660046143d0565b61163b565b34801561030057600080fd5b5061016e6117e7565b34801561031557600080fd5b50610274610324366004614222565b6117f6565b610152610337366004614348565b611abd565b34801561034857600080fd5b506101a461035736600461408d565b611df1565b34801561036857600080fd5b5061016e611fcc565b34801561037d57600080fd5b506101a461038c36600461427f565b611ff0565b34801561039d57600080fd5b506103b16103ac3660046141a7565b6120d3565b60405161017b9392919061474b565b6103b16103ce366004614222565b6122b3565b3480156103df57600080fd5b506101526103ee366004614055565b6125a0565b6101d2610401366004614348565b612623565b7f00000000000000000000000045a3a315277fbc1bce0611c4398b32e0317fd7c181565b60008082428110156104575760405162461bcd60e51b815260040161044e906146fd565b60405180910390fd5b6002600054141561049d576040805162461bcd60e51b815260206004820152601f60248201526000805160206147b4833981519152604482015290519081900360640190fd5b600260009081556003546104dd907f00000000000000000000000045603612891b6406a06854813e18443fc8ec7c73908c906001600160a01b0316612988565b6040516323b872dd60e01b81529091506001600160a01b038216906323b872dd9061051090339085908e9060040161450d565b602060405180830381600087803b15801561052a57600080fd5b505af115801561053e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105629190614314565b5060025460035460405163e54dc2d760e01b81526001600160a01b039283169263e54dc2d7926105a0928f929091169086908e908e90600401614531565b6040805180830381600087803b1580156105b957600080fd5b505af11580156105cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105f191906143ad565b90945092506106018a8786612a48565b600354604051632e1a7d4d60e01b81526001600160a01b0390911690632e1a7d4d90610631908690600401614734565b600060405180830381600087803b15801561064b57600080fd5b505af115801561065f573d6000803e3d6000fd5b5050505061066d8684612b8f565b505060016000559097909650945050505050565b606081428110156106a45760405162461bcd60e51b815260040161044e906146fd565b600260005414156106ea576040805162461bcd60e51b815260206004820152601f60248201526000805160206147b4833981519152604482015290519081900360640190fd5b60026000556003546001600160a01b03168686600019810181811061070b57fe5b9050602002016020810190610720919061401d565b6001600160a01b0316146107465760405162461bcd60e51b815260040161044e906146c6565b6107a47f00000000000000000000000045603612891b6406a06854813e18443fc8ec7c7389888880806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250612c8792505050565b915086826001845103815181106107b757fe5b602002602001015110156107dd5760405162461bcd60e51b815260040161044e9061467a565b61088a868660008181106107ed57fe5b9050602002016020810190610802919061401d565b336108707f00000000000000000000000045603612891b6406a06854813e18443fc8ec7c738a8a600081811061083457fe5b9050602002016020810190610849919061401d565b8b8b600181811061085657fe5b905060200201602081019061086b919061401d565b612988565b8560008151811061087d57fe5b6020026020010151612dc0565b600254604051631a70b66160e21b81526001600160a01b03909116906369c2d984906108be9085908a908a906004016145e3565b600060405180830381600087803b1580156108d857600080fd5b505af11580156108ec573d6000803e3d6000fd5b505060035484516001600160a01b039091169250632e1a7d4d91508490600019810190811061091757fe5b60200260200101516040518263ffffffff1660e01b815260040161093b9190614734565b600060405180830381600087803b15801561095557600080fd5b505af1158015610969573d6000803e3d6000fd5b5050505061098e848360018551038151811061098157fe5b6020026020010151612b8f565b5060016000559695505050505050565b60008060006109ce7f00000000000000000000000045603612891b6406a06854813e18443fc8ec7c738f8f612988565b90506000876109dd578c6109e1565b6000195b60405163d505accf60e01b81529091506001600160a01b0383169063d505accf90610a1c903390309086908f908e908e908e906004016144cc565b600060405180830381600087803b158015610a3657600080fd5b505af1158015610a4a573d6000803e3d6000fd5b50505050610a5d8f8f8f8f8f8f8f611df1565b9350935050509b509b9950505050505050505050565b60608142811015610a965760405162461bcd60e51b815260040161044e906146fd565b60026000541415610adc576040805162461bcd60e51b815260206004820152601f60248201526000805160206147b4833981519152604482015290519081900360640190fd5b6002600081905550610b427f00000000000000000000000045603612891b6406a06854813e18443fc8ec7c7389888880806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250612c8792505050565b91508682600184510381518110610b5557fe5b60200260200101511015610b7b5760405162461bcd60e51b815260040161044e9061467a565b610b8b868660008181106107ed57fe5b600254604051631a70b66160e21b81526001600160a01b03909116906369c2d98490610bbf9085908a908a906004016145e3565b600060405180830381600087803b158015610bd957600080fd5b505af1158015610bed573d6000803e3d6000fd5b5050505061098e868660018989905003818110610c0657fe5b9050602002016020810190610c1b919061401d565b8584600186510381518110610c2c57fe5b6020026020010151612a48565b60608142811015610c5c5760405162461bcd60e51b815260040161044e906146fd565b60026000541415610ca2576040805162461bcd60e51b815260206004820152601f60248201526000805160206147b4833981519152604482015290519081900360640190fd5b60026000556003546001600160a01b031686866000198101818110610cc357fe5b9050602002016020810190610cd8919061401d565b6001600160a01b031614610cfe5760405162461bcd60e51b815260040161044e906146c6565b610d5c7f00000000000000000000000045603612891b6406a06854813e18443fc8ec7c7389888880806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250612fa392505050565b91508682600081518110610d6c57fe5b602002602001015111156107dd5760405162461bcd60e51b815260040161044e90614632565b6002546001600160a01b031681565b6003546000908190610ddf907f00000000000000000000000045603612891b6406a06854813e18443fc8ec7c73908e906001600160a01b0316612988565b9050600086610dee578b610df2565b6000195b60405163d505accf60e01b81529091506001600160a01b0383169063d505accf90610e2d903390309086908e908d908d908d906004016144cc565b600060405180830381600087803b158015610e4757600080fd5b505af1158015610e5b573d6000803e3d6000fd5b50505050610e6d8d8d8d8d8d8d6117f6565b9d9c50505050505050505050505050565b8042811015610e9f5760405162461bcd60e51b815260040161044e906146fd565b60026000541415610ee5576040805162461bcd60e51b815260206004820152601f60248201526000805160206147b4833981519152604482015290519081900360640190fd5b6002600081905550610f6c85856000818110610efd57fe5b9050602002016020810190610f12919061401d565b33610f667f00000000000000000000000045603612891b6406a06854813e18443fc8ec7c7389896000818110610f4457fe5b9050602002016020810190610f59919061401d565b8a8a600181811061085657fe5b8a612dc0565b600254604051636fb0847760e01b81526001600160a01b0390911690636fb0847790610f9e90889088906004016145b4565b600060405180830381600087803b158015610fb857600080fd5b505af1158015610fcc573d6000803e3d6000fd5b505050506000858560018888905003818110610fe457fe5b9050602002016020810190610ff9919061401d565b6001600160a01b03166370a08231306040518263ffffffff1660e01b815260040161102491906144b8565b60206040518083038186803b15801561103c57600080fd5b505afa158015611050573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110749190614330565b9050868110156110965760405162461bcd60e51b815260040161044e9061467a565b6110c5868660001981018181106110a957fe5b90506020020160208101906110be919061401d565b8583612a48565b50506001600055505050505050565b80428110156110f55760405162461bcd60e51b815260040161044e906146fd565b6002600054141561113b576040805162461bcd60e51b815260206004820152601f60248201526000805160206147b4833981519152604482015290519081900360640190fd5b60026000556003546001600160a01b03168585600019810181811061115c57fe5b9050602002016020810190611171919061401d565b6001600160a01b0316146111975760405162461bcd60e51b815260040161044e906146c6565b6111a785856000818110610efd57fe5b600254604051636fb0847760e01b81526001600160a01b0390911690636fb08477906111d990889088906004016145b4565b600060405180830381600087803b1580156111f357600080fd5b505af1158015611207573d6000803e3d6000fd5b50506003546040516370a0823160e01b8152600093506001600160a01b0390911691506370a082319061123e9030906004016144b8565b60206040518083038186803b15801561125657600080fd5b505afa15801561126a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061128e9190614330565b9050868110156112b05760405162461bcd60e51b815260040161044e9061467a565b600354604051632e1a7d4d60e01b81526001600160a01b0390911690632e1a7d4d906112e0908490600401614734565b600060405180830381600087803b1580156112fa57600080fd5b505af115801561130e573d6000803e3d6000fd5b505050506110c58482612b8f565b6060814281101561133f5760405162461bcd60e51b815260040161044e906146fd565b60026000541415611385576040805162461bcd60e51b815260206004820152601f60248201526000805160206147b4833981519152604482015290519081900360640190fd5b600260009081556003546001600160a01b03169087908790816113a457fe5b90506020020160208101906113b9919061401d565b6001600160a01b0316146113df5760405162461bcd60e51b815260040161044e906146c6565b61143d7f00000000000000000000000045603612891b6406a06854813e18443fc8ec7c7334888880806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250612c8792505050565b9150868260018451038151811061145057fe5b602002602001015110156114765760405162461bcd60e51b815260040161044e9061467a565b60035482516001600160a01b039091169063d0e30db090849060009061149857fe5b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b1580156114cb57600080fd5b505af11580156114df573d6000803e3d6000fd5b50506003546001600160a01b0316925063a9059cbb915061152790507f00000000000000000000000045603612891b6406a06854813e18443fc8ec7c738989600081610f4457fe5b8460008151811061153457fe5b60200260200101516040518363ffffffff1660e01b815260040161155992919061459b565b602060405180830381600087803b15801561157357600080fd5b505af1158015611587573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115ab9190614314565b6115b157fe5b600254604051631a70b66160e21b81526001600160a01b03909116906369c2d984906115e59085908a908a906004016145e3565b600060405180830381600087803b1580156115ff57600080fd5b505af1158015611613573d6000803e3d6000fd5b5050505061162c868660018989905003818110610c0657fe5b50600160005595945050505050565b6060814281101561165e5760405162461bcd60e51b815260040161044e906146fd565b600260005414156116a4576040805162461bcd60e51b815260206004820152601f60248201526000805160206147b4833981519152604482015290519081900360640190fd5b600260008190555061170a7f00000000000000000000000045603612891b6406a06854813e18443fc8ec7c7389888880806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250612fa392505050565b9150868260008151811061171a57fe5b602002602001015111156117405760405162461bcd60e51b815260040161044e90614632565b611750868660008181106107ed57fe5b600254604051631a70b66160e21b81526001600160a01b03909116906369c2d984906117849085908a908a906004016145e3565b600060405180830381600087803b15801561179e57600080fd5b505af11580156117b2573d6000803e3d6000fd5b5050505061098e8686600189899050038181106117cb57fe5b90506020020160208101906117e0919061401d565b858a612a48565b6003546001600160a01b031681565b600081428110156118195760405162461bcd60e51b815260040161044e906146fd565b6002600054141561185f576040805162461bcd60e51b815260206004820152601f60248201526000805160206147b4833981519152604482015290519081900360640190fd5b6002600090815560035461189f907f00000000000000000000000045603612891b6406a06854813e18443fc8ec7c73908b906001600160a01b0316612988565b6040516323b872dd60e01b81529091506001600160a01b038216906323b872dd906118d290339085908d9060040161450d565b602060405180830381600087803b1580156118ec57600080fd5b505af1158015611900573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119249190614314565b5060025460035460405163e54dc2d760e01b81526001600160a01b039283169263e54dc2d792611962928e929091169086908d908d90600401614531565b6040805180830381600087803b15801561197b57600080fd5b505af115801561198f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119b391906143ad565b6040516370a0823160e01b8152909450611a4091508a9087906001600160a01b038316906370a08231906119eb9030906004016144b8565b60206040518083038186803b158015611a0357600080fd5b505afa158015611a17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a3b9190614330565b612a48565b600354604051632e1a7d4d60e01b81526001600160a01b0390911690632e1a7d4d90611a70908690600401614734565b600060405180830381600087803b158015611a8a57600080fd5b505af1158015611a9e573d6000803e3d6000fd5b50505050611aac8584612b8f565b505060016000559695505050505050565b8042811015611ade5760405162461bcd60e51b815260040161044e906146fd565b60026000541415611b24576040805162461bcd60e51b815260206004820152601f60248201526000805160206147b4833981519152604482015290519081900360640190fd5b600260009081556003546001600160a01b0316908690869081611b4357fe5b9050602002016020810190611b58919061401d565b6001600160a01b031614611b7e5760405162461bcd60e51b815260040161044e906146c6565b60035460408051630d0e30db60e41b8152905134926001600160a01b03169163d0e30db091849160048082019260009290919082900301818588803b158015611bc657600080fd5b505af1158015611bda573d6000803e3d6000fd5b50506003546001600160a01b0316925063a9059cbb9150611c2290507f00000000000000000000000045603612891b6406a06854813e18443fc8ec7c738989600081610f4457fe5b836040518363ffffffff1660e01b8152600401611c4092919061459b565b602060405180830381600087803b158015611c5a57600080fd5b505af1158015611c6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c929190614314565b611c9857fe5b600254604051636fb0847760e01b81526001600160a01b0390911690636fb0847790611cca90899089906004016145b4565b600060405180830381600087803b158015611ce457600080fd5b505af1158015611cf8573d6000803e3d6000fd5b505050506000868660018989905003818110611d1057fe5b9050602002016020810190611d25919061401d565b6001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401611d5091906144b8565b60206040518083038186803b158015611d6857600080fd5b505afa158015611d7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611da09190614330565b905087811015611dc25760405162461bcd60e51b815260040161044e9061467a565b6110c587876000198101818110611dd557fe5b9050602002016020810190611dea919061401d565b8683612a48565b6000808242811015611e155760405162461bcd60e51b815260040161044e906146fd565b60026000541415611e5b576040805162461bcd60e51b815260206004820152601f60248201526000805160206147b4833981519152604482015290519081900360640190fd5b60026000908155611e8d7f00000000000000000000000045603612891b6406a06854813e18443fc8ec7c738c8c612988565b6040516323b872dd60e01b81529091506001600160a01b038216906323b872dd90611ec090339085908e9060040161450d565b602060405180830381600087803b158015611eda57600080fd5b505af1158015611eee573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f129190614314565b5060025460405163e54dc2d760e01b81526001600160a01b039091169063e54dc2d790611f4b908e908e9086908e908e90600401614531565b6040805180830381600087803b158015611f6457600080fd5b505af1158015611f78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f9c91906143ad565b9094509250611fac8b8786612a48565b611fb78a8785612a48565b50506001600055909890975095505050505050565b7f00000000000000000000000045603612891b6406a06854813e18443fc8ec7c7381565b60035460009081908190612030907f00000000000000000000000045603612891b6406a06854813e18443fc8ec7c73908f906001600160a01b0316612988565b905060008761203f578c612043565b6000195b60405163d505accf60e01b81529091506001600160a01b0383169063d505accf9061207e903390309086908f908e908e908e906004016144cc565b600060405180830381600087803b15801561209857600080fd5b505af11580156120ac573d6000803e3d6000fd5b505050506120be8e8e8e8e8e8e61042a565b909f909e509c50505050505050505050505050565b600080600083428110156120f95760405162461bcd60e51b815260040161044e906146fd565b6002600054141561213f576040805162461bcd60e51b815260206004820152601f60248201526000805160206147b4833981519152604482015290519081900360640190fd5b6002600081905554604051633351733f60e01b81526001600160a01b0390911690633351733f9061217e908f908f908f908f908f908f90600401614565565b6040805180830381600087803b15801561219757600080fd5b505af11580156121ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121cf91906143ad565b909450925060006122017f00000000000000000000000045603612891b6406a06854813e18443fc8ec7c738e8e612988565b905061220f8d338388612dc0565b61221b8c338387612dc0565b6040516335313c2160e11b81526001600160a01b03821690636a62784290612247908a906004016144b8565b602060405180830381600087803b15801561226157600080fd5b505af1158015612275573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122999190614330565b925050600160008190555050985098509895505050505050565b600080600083428110156122d95760405162461bcd60e51b815260040161044e906146fd565b6002600054141561231f576040805162461bcd60e51b815260206004820152601f60248201526000805160206147b4833981519152604482015290519081900360640190fd5b6002600081905554600354604051633351733f60e01b81526001600160a01b0392831692633351733f92612363928f92909116908e9034908f908f90600401614565565b6040805180830381600087803b15801561237c57600080fd5b505af1158015612390573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123b491906143ad565b60035491955093506000906123f5907f00000000000000000000000045603612891b6406a06854813e18443fc8ec7c73908d906001600160a01b0316612988565b90506124038b338388612dc0565b600360009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0856040518263ffffffff1660e01b81526004016000604051808303818588803b15801561245357600080fd5b505af1158015612467573d6000803e3d6000fd5b505060035460405163a9059cbb60e01b81526001600160a01b03909116935063a9059cbb925061249e91508490889060040161459b565b602060405180830381600087803b1580156124b857600080fd5b505af11580156124cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124f09190614314565b6124f657fe5b6040516335313c2160e11b81526001600160a01b03821690636a62784290612522908a906004016144b8565b602060405180830381600087803b15801561253c57600080fd5b505af1158015612550573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125749190614330565b92508334111561258a5761258a33853403612b8f565b5060016000819055505096509650969350505050565b6003546001600160a01b03161580156125c257506002546001600160a01b0316155b6125cb57600080fd5b6001546001600160a01b031633146125f55760405162461bcd60e51b815260040161044e90614613565b600380546001600160a01b039384166001600160a01b03199182161790915560028054929093169116179055565b606081428110156126465760405162461bcd60e51b815260040161044e906146fd565b6002600054141561268c576040805162461bcd60e51b815260206004820152601f60248201526000805160206147b4833981519152604482015290519081900360640190fd5b600260009081556003546001600160a01b03169087908790816126ab57fe5b90506020020160208101906126c0919061401d565b6001600160a01b0316146126e65760405162461bcd60e51b815260040161044e906146c6565b6127447f00000000000000000000000045603612891b6406a06854813e18443fc8ec7c7388888880806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250612fa392505050565b9150348260008151811061275457fe5b6020026020010151111561277a5760405162461bcd60e51b815260040161044e90614632565b60035482516001600160a01b039091169063d0e30db090849060009061279c57fe5b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b1580156127cf57600080fd5b505af11580156127e3573d6000803e3d6000fd5b50506003546001600160a01b0316925063a9059cbb915061282b90507f00000000000000000000000045603612891b6406a06854813e18443fc8ec7c738989600081610f4457fe5b8460008151811061283857fe5b60200260200101516040518363ffffffff1660e01b815260040161285d92919061459b565b602060405180830381600087803b15801561287757600080fd5b505af115801561288b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128af9190614314565b6128b557fe5b600254604051631a70b66160e21b81526001600160a01b03909116906369c2d984906128e99085908a908a906004016145e3565b600060405180830381600087803b15801561290357600080fd5b505af1158015612917573d6000803e3d6000fd5b5050505061294c86866001898990500381811061293057fe5b9050602002016020810190612945919061401d565b8589612a48565b8160008151811061295957fe5b602002602001015134111561162c5761162c338360008151811061297957fe5b60200260200101513403612b8f565b600080600061299785856130d5565b604080516bffffffffffffffffffffffff19606094851b811660208084019190915293851b81166034830152825160288184030181526048830184528051908501206001600160f81b031960688401529a90941b9093166069840152607d8301989098527ffc84b622ba228c468b74c2d99bfe9454ffac280ac017f05a02feb9f739aeb1e4609d808401919091528851808403909101815260bd909201909752805196019590952095945050505050565b604051632a4d95e760e21b81526000906001600160a01b037f00000000000000000000000045a3a315277fbc1bce0611c4398b32e0317fd7c1169063a936579c90612a979087906004016144b8565b60206040518083038186803b158015612aaf57600080fd5b505afa158015612ac3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ae79190614039565b90506001600160a01b038116612b0757612b028484846131b3565b612b89565b60405163f3fef3a360e01b81526001600160a01b0385169063f3fef3a390612b35908690869060040161459b565b602060405180830381600087803b158015612b4f57600080fd5b505af1158015612b63573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b879190614330565b505b50505050565b604080516000808252602082019092526001600160a01b0384169083906040518082805190602001908083835b60208310612bdb5780518252601f199092019160209182019101612bbc565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114612c3d576040519150601f19603f3d011682016040523d82523d6000602084013e612c42565b606091505b5050905080612c825760405162461bcd60e51b81526004018080602001828103825260348152602001806148056034913960400191505060405180910390fd5b505050565b6060600282511015612ce0576040805162461bcd60e51b815260206004820152601f60248201527f496d706f737369626c654c6962726172793a20494e56414c49445f5041544800604482015290519081900360640190fd5b815167ffffffffffffffff81118015612cf857600080fd5b50604051908082528060200260200182016040528015612d22578160200160208202803683370190505b5090508281600081518110612d3357fe5b60200260200101818152505060005b6001835103811015612db857612d96828281518110612d5d57fe5b6020026020010151848381518110612d7157fe5b6020026020010151858460010181518110612d8857fe5b6020026020010151886132ff565b828260010181518110612da557fe5b6020908102919091010152600101612d42565b509392505050565b604051632a4d95e760e21b81526000906001600160a01b037f00000000000000000000000045a3a315277fbc1bce0611c4398b32e0317fd7c1169063a936579c90612e0f9088906004016144b8565b60206040518083038186803b158015612e2757600080fd5b505afa158015612e3b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e5f9190614039565b90506001600160a01b038116612e8057612e7b85858585613668565b612b87565b604051635ac402df60e11b81526000906001600160a01b0387169063b58805be90612eaf908690600401614734565b602060405180830381600087803b158015612ec957600080fd5b505af1158015612edd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f019190614330565b9050612f0f82863084613668565b612f1a8287836137c4565b6040516311f9fbc960e21b81526001600160a01b038716906347e7ef2490612f48908790859060040161459b565b602060405180830381600087803b158015612f6257600080fd5b505af1158015612f76573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f9a9190614330565b50505050505050565b6060600282511015612ffc576040805162461bcd60e51b815260206004820152601f60248201527f496d706f737369626c654c6962726172793a20494e56414c49445f5041544800604482015290519081900360640190fd5b815167ffffffffffffffff8111801561301457600080fd5b5060405190808252806020026020018201604052801561303e578160200160208202803683370190505b509050828160018351038151811061305257fe5b60209081029190910101528151600019015b8015612db8576130b282828151811061307957fe5b602002602001015184600184038151811061309057fe5b60200260200101518584815181106130a457fe5b602002602001015188613910565b8260018303815181106130c157fe5b602090810291909101015260001901613064565b600080826001600160a01b0316846001600160a01b031614156131295760405162461bcd60e51b81526004018080602001828103825260268152602001806148b56026913960400191505060405180910390fd5b826001600160a01b0316846001600160a01b03161061314957828461314c565b83835b90925090506001600160a01b0382166131ac576040805162461bcd60e51b815260206004820152601f60248201527f496d706f737369626c654c6962726172793a205a45524f5f4144445245535300604482015290519081900360640190fd5b9250929050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b1781529251825160009485949389169392918291908083835b6020831061322f5780518252601f199092019160209182019101613210565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114613291576040519150601f19603f3d011682016040523d82523d6000602084013e613296565b606091505b50915091508180156132c45750805115806132c457508080602001905160208110156132c157600080fd5b50515b612b875760405162461bcd60e51b815260040180806020018281038252602d815260200180614888602d913960400191505060405180910390fd5b600080851161333f5760405162461bcd60e51b815260040180806020018281038252602c815260200180614788602c913960400191505060405180910390fd5b6000806000806000806133528a8a6130d5565b509050806001600160a01b03168a6001600160a01b0316149150613377888b8b613c2b565b80955081975082985050505050600080600080856001600160a01b031663e94e3d8d6040518163ffffffff1660e01b815260040160606040518083038186803b1580156133c357600080fd5b505afa1580156133d7573d6000803e3d6000fd5b505050506040513d60608110156133ed57600080fd5b5080516020820151604090920151945061ffff16925090506134148e612710849003613ced565b9650600081600381111561342457fe5b14806134445750600181600381111561343957fe5b148015613444575084155b806134625750600281600381111561345857fe5b1480156134625750845b61349d5760405162461bcd60e51b81526004018080602001828103825260248152602001806148396024913960400191505060405180910390fd5b505080156135f157600080856001600160a01b031663b6b9f61b6040518163ffffffff1660e01b8152600401604080518083038186803b1580156134e057600080fd5b505afa1580156134f4573d6000803e3d6000fd5b505050506040513d604081101561350a57600080fd5b5080516020909101519092509050600061354661354184848961352d578c61352f565b8d5b8a61353a578e61353c565b8d5b613d56565b613de3565b905061355481612710613ced565b61356a6135638c612710613ced565b8a90613e35565b106135d5576135858661357d578261357f565b835b82613e84565b9450808a1080156135965750818314155b156135d0576135a58982613e9b565b9a506135c76135c06127106135ba848e613e9b565b90613ced565b8990613e9b565b97508099508098505b6135ed565b6135ea866135e3578361357f565b8282613e84565b94505b5050505b60006136076136008885613e35565b8790613ced565b905060006136258761361f6127106135ba8d89613e35565b90613e35565b9050600081838161363257fe5b04905088811161364b57613646818c613e35565b613655565b613655898c613e35565b9f9e505050505050505050505050505050565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b178152925182516000948594938a169392918291908083835b602083106136ec5780518252601f1990920191602091820191016136cd565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d806000811461374e576040519150601f19603f3d011682016040523d82523d6000602084013e613753565b606091505b5091509150818015613781575080511580613781575080806020019051602081101561377e57600080fd5b50515b6137bc5760405162461bcd60e51b81526004018080602001828103825260318152602001806147d46031913960400191505060405180910390fd5b505050505050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663095ea7b360e01b1781529251825160009485949389169392918291908083835b602083106138405780518252601f199092019160209182019101613821565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146138a2576040519150601f19603f3d011682016040523d82523d6000602084013e6138a7565b606091505b50915091508180156138d55750805115806138d557508080602001905160208110156138d257600080fd5b50515b612b875760405162461bcd60e51b815260040180806020018281038252602b81526020018061485d602b913960400191505060405180910390fd5b60008085116139505760405162461bcd60e51b815260040180806020018281038252602c815260200180614788602c913960400191505060405180910390fd5b60008060008060008060008060006139688d8d6130d5565b506001600160a01b038e81169116149450600090506139888b8e8e613c2b565b809350819a50829b505050506000816001600160a01b031663e94e3d8d6040518163ffffffff1660e01b815260040160606040518083038186803b1580156139cf57600080fd5b505afa1580156139e3573d6000803e3d6000fd5b505050506040513d60608110156139f957600080fd5b508051602082015160409092015161ffff9091169850955090506000816003811115613a2157fe5b1480613a4157506001816003811115613a3657fe5b148015613a41575085155b80613a5f57506002816003811115613a5557fe5b148015613a5f5750855b613a9a5760405162461bcd60e51b81526004018080602001828103825260248152602001806148396024913960400191505060405180910390fd5b816001600160a01b031663b6b9f61b6040518163ffffffff1660e01b8152600401604080518083038186803b158015613ad257600080fd5b505afa158015613ae6573d6000803e3d6000fd5b505050506040513d6040811015613afc57600080fd5b50805160209091015190945092505083159050613bc0576000613b3b613541848488613b28578b613b2a565b8c5b89613b35578d61353c565b8c613d56565b905080613b518f8a613e9b90919063ffffffff16565b10613b6b57613b64856135e3578361357f565b9650613bbe565b613b798561357d578261357f565b96508088118015613b8a5750818314155b15613bbe57613b9f6127106135ba838c613e9b565b9950613bb5613bae8983613e9b565b8f90613e9b565b9d508097508098505b505b5060009150613bd990506127106135ba8d818a89613e35565b90506000613bf18c613beb8888613e35565b90613e9b565b9050613c1b600161361f8661271003613c15858781613c0c57fe5b8e919004613e35565b90613eeb565b9c9b505050505050505050505050565b600080600080613c3b86866130d5565b509050613c49878787612988565b9150600080836001600160a01b0316630902f1ac6040518163ffffffff1660e01b8152600401604080518083038186803b158015613c8657600080fd5b505afa158015613c9a573d6000803e3d6000fd5b505050506040513d6040811015613cb057600080fd5b50805160209091015190925090506001600160a01b0388811690841614613cd8578082613cdb565b81815b909a9099509397509295505050505050565b6000811580613d0857505080820282828281613d0557fe5b04145b613d50576040805162461bcd60e51b815260206004820152601460248201527364732d6d6174682d6d756c2d6f766572666c6f7760601b604482015290519081900360640190fd5b92915050565b600080828411613d7057613d6b856001613e9b565b613d7b565b613d7b866001613e9b565b90506000613d8f600161361f846002613ced565b90506000613db5613da1836002613ced565b613c15613dae8989613e35565b8690613ced565b9050600281613dd5613dcb85613c158b8b613ced565b6002850a01613de3565b010a98975050505050505050565b60006003821115613e26575080600160028204015b81811015613e2057809150600281828581613e0f57fe5b040181613e1857fe5b049050613df8565b50613e30565b8115613e30575060015b919050565b80820182811015613d50576040805162461bcd60e51b815260206004820152601460248201527364732d6d6174682d6164642d6f766572666c6f7760601b604482015290519081900360640190fd5b6000613e94600019840183613ced565b9392505050565b80820382811115613d50576040805162461bcd60e51b815260206004820152601560248201527464732d6d6174682d7375622d756e646572666c6f7760581b604482015290519081900360640190fd5b6000613e9483836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f00000000000081525060008183613fb45760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015613f79578181015183820152602001613f61565b50505050905090810190601f168015613fa65780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b506000838581613fc057fe5b0495945050505050565b60008083601f840112613fdb578182fd5b50813567ffffffffffffffff811115613ff2578182fd5b60208301915083602080830285010111156131ac57600080fd5b803560ff81168114613e3057600080fd5b60006020828403121561402e578081fd5b8135613e9481614761565b60006020828403121561404a578081fd5b8151613e9481614761565b60008060408385031215614067578081fd5b823561407281614761565b9150602083013561408281614761565b809150509250929050565b600080600080600080600060e0888a0312156140a7578283fd5b87356140b281614761565b965060208801356140c281614761565b955060408801359450606088013593506080880135925060a08801356140e781614761565b8092505060c0880135905092959891949750929550565b60008060008060008060008060008060006101608c8e03121561411f578384fd5b8b3561412a81614761565b9a5060208c013561413a81614761565b995060408c0135985060608c0135975060808c0135965060a08c013561415f81614761565b955060c08c0135945060e08c013561417681614779565b93506141856101008d0161400c565b92506101208c013591506101408c013590509295989b509295989b9093969950565b600080600080600080600080610100898b0312156141c3578384fd5b88356141ce81614761565b975060208901356141de81614761565b965060408901359550606089013594506080890135935060a0890135925060c089013561420a81614761565b8092505060e089013590509295985092959890939650565b60008060008060008060c0878903121561423a578182fd5b863561424581614761565b9550602087013594506040870135935060608701359250608087013561426a81614761565b8092505060a087013590509295509295509295565b6000806000806000806000806000806101408b8d03121561429e578384fd5b8a356142a981614761565b995060208b0135985060408b0135975060608b0135965060808b01356142ce81614761565b955060a08b0135945060c08b01356142e581614779565b93506142f360e08c0161400c565b92506101008b013591506101208b013590509295989b9194979a5092959850565b600060208284031215614325578081fd5b8151613e9481614779565b600060208284031215614341578081fd5b5051919050565b60008060008060006080868803121561435f578283fd5b85359450602086013567ffffffffffffffff81111561437c578384fd5b61438888828901613fca565b909550935050604086013561439c81614761565b949793965091946060013592915050565b600080604083850312156143bf578182fd5b505080516020909101519092909150565b60008060008060008060a087890312156143e8578384fd5b8635955060208701359450604087013567ffffffffffffffff81111561440c578485fd5b61441889828a01613fca565b909550935050606087013561442c81614761565b80925050608087013590509295509295509295565b60008284526020808501945082825b8581101561447e57813561446381614761565b6001600160a01b031687529582019590820190600101614450565b509495945050505050565b6000815180845260208085019450808401835b8381101561447e5781518752958201959082019060010161449c565b6001600160a01b0391909116815260200190565b6001600160a01b0397881681529590961660208601526040850193909352606084019190915260ff16608083015260a082015260c081019190915260e00190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03958616815293851660208501529190931660408301526060820192909252608081019190915260a00190565b6001600160a01b03968716815294909516602085015260408401929092526060830152608082015260a081019190915260c00190565b6001600160a01b03929092168252602082015260400190565b6000602082526145c8602083018486614441565b949350505050565b600060208252613e946020830184614489565b6000604082526145f66040830186614489565b8281036020840152614609818587614441565b9695505050505050565b60208082526005908201526449463a203f60d81b604082015260600190565b60208082526028908201527f496d706f737369626c65526f757465723a204558434553534956455f494e50556040820152671517d05353d5539560c21b606082015260800190565b6020808252602c908201527f496d706f737369626c65526f757465723a20494e53554646494349454e545f4f60408201526b155514155517d05353d5539560a21b606082015260800190565b6020808252601e908201527f496d706f737369626c65526f757465723a20494e56414c49445f504154480000604082015260600190565b60208082526019908201527f496d706f737369626c65526f757465723a204558504952454400000000000000604082015260600190565b90815260200190565b918252602082015260400190565b9283526020830191909152604082015260600190565b6001600160a01b038116811461477657600080fd5b50565b801515811461477657600080fdfe496d706f737369626c654c6962726172793a20494e53554646494349454e545f494e5055545f414d4f554e545265656e7472616e637947756172643a207265656e7472616e742063616c6c005472616e7366657248656c7065723a3a7472616e7366657246726f6d3a207472616e7366657246726f6d206661696c65645472616e7366657248656c7065723a3a736166655472616e736665724554483a20455448207472616e73666572206661696c6564496d706f737369626c654c6962726172793a2054524144455f4e4f545f414c4c4f5745445472616e7366657248656c7065723a3a73616665417070726f76653a20617070726f7665206661696c65645472616e7366657248656c7065723a3a736166655472616e736665723a207472616e73666572206661696c6564496d706f737369626c654c6962726172793a204944454e544943414c5f414444524553534553a26469706673582212201d47bb6aa4c4e5a635ca01716e6be81c6af6f3e867a84fc2495526c9765ca44264736f6c63430007060033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

00000000000000000000000045603612891b6406a06854813e18443fc8ec7c7300000000000000000000000045a3a315277fbc1bce0611c4398b32e0317fd7c10000000000000000000000003c9584426432eb851e5689230d5cfc50659103d5

-----Decoded View---------------
Arg [0] : _pairFactory (address): 0x45603612891b6406A06854813e18443fC8ec7C73
Arg [1] : _wrapFactory (address): 0x45a3a315277Fbc1BCe0611c4398b32E0317Fd7c1
Arg [2] : _utilitySettingAdmin (address): 0x3C9584426432eb851e5689230d5cFc50659103D5

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 00000000000000000000000045603612891b6406a06854813e18443fc8ec7c73
Arg [1] : 00000000000000000000000045a3a315277fbc1bce0611c4398b32e0317fd7c1
Arg [2] : 0000000000000000000000003c9584426432eb851e5689230d5cfc50659103d5


Block Transaction Gas Used Reward
view all blocks collator

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.