ETH Price: $3,064.14 (-5.75%)
 

Overview

Max Total Supply

41.834015801886970582 XT-2

Holders

55

Market

Onchain Market Cap

$0.00

Circulating Supply Market Cap

-

Other Info

Token Contract (WITH 18 Decimals)

Balance
0.000000006726247485 XT-2

Value
$0.00
0xe035b8491afdafe6f51192e44237a662e72308b2
Loading...
Loading
Loading...
Loading
Loading...
Loading

Click here to update the token information / general information
# Exchange Pair Price  24H Volume % Volume

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0xb2b7BeDd...4c73C210d
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
CoFiXPair

Compiler Version
v0.6.12+commit.27d51765

Optimization Enabled:
Yes with 6666 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity)

/**
 *Submitted for verification at Etherscan.io on 2020-10-12
*/

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

/**

Author: CoFiX Core, https://cofix.io
Commit hash: v0.9.5-1-g7141c43
Repository: https://github.com/Computable-Finance/CoFiX
Issues: https://github.com/Computable-Finance/CoFiX/issues

*/

pragma experimental ABIEncoderV2;
pragma solidity 0.6.12;


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

    // function name() external pure returns (string memory);
    // function symbol() external pure returns (string memory);
    function decimals() external pure returns (uint8);
    function totalSupply() external view returns (uint);
    function balanceOf(address owner) external view returns (uint);
    function allowance(address owner, address spender) external view returns (uint);

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

    function DOMAIN_SEPARATOR() external view returns (bytes32);
    function PERMIT_TYPEHASH() external pure returns (bytes32);
    function nonces(address owner) external view returns (uint);

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

// 
interface ICoFiXPair is ICoFiXERC20 {

    struct OraclePrice {
        uint256 ethAmount;
        uint256 erc20Amount;
        uint256 blockNum;
        uint256 K;
        uint256 theta;
    }

    // All pairs: {ETH <-> ERC20 Token}
    event Mint(address indexed sender, uint amount0, uint amount1);
    event Burn(address indexed sender, address outToken, uint outAmount, address indexed to);
    event Swap(
        address indexed sender,
        uint amountIn,
        uint amountOut,
        address outToken,
        address indexed to
    );
    event Sync(uint112 reserve0, uint112 reserve1);

    function MINIMUM_LIQUIDITY() external pure returns (uint);
    function factory() external view returns (address);
    function token0() external view returns (address);
    function token1() external view returns (address);
    function getReserves() external view returns (uint112 reserve0, uint112 reserve1);

    function mint(address to) external payable returns (uint liquidity, uint oracleFeeChange);
    function burn(address outToken, address to) external payable returns (uint amountOut, uint oracleFeeChange);
    function swapWithExact(address outToken, address to) external payable returns (uint amountIn, uint amountOut, uint oracleFeeChange, uint256[4] memory tradeInfo);
    function swapForExact(address outToken, uint amountOutExact, address to) external payable returns (uint amountIn, uint amountOut, uint oracleFeeChange, uint256[4] memory tradeInfo);
    function skim(address to) external;
    function sync() external;

    function initialize(address, address, string memory, string memory) external;

    /// @dev get Net Asset Value Per Share
    /// @param  ethAmount ETH side of Oracle price {ETH <-> ERC20 Token}
    /// @param  erc20Amount Token side of Oracle price {ETH <-> ERC20 Token}
    /// @return navps The Net Asset Value Per Share (liquidity) represents
    function getNAVPerShare(uint256 ethAmount, uint256 erc20Amount) external view returns (uint256 navps);
}

// 
interface ICoFiXFactory {
    // All pairs: {ETH <-> ERC20 Token}
    event PairCreated(address indexed token, address pair, uint256);
    event NewGovernance(address _new);
    event NewController(address _new);
    event NewFeeReceiver(address _new);
    event NewFeeVaultForLP(address token, address feeVault);
    event NewVaultForLP(address _new);
    event NewVaultForTrader(address _new);
    event NewVaultForCNode(address _new);

    /// @dev Create a new token pair for trading
    /// @param  token the address of token to trade
    /// @return pair the address of new token pair
    function createPair(
        address token
        )
        external
        returns (address pair);

    function getPair(address token) external view returns (address pair);
    function allPairs(uint256) external view returns (address pair);
    function allPairsLength() external view returns (uint256);

    function getTradeMiningStatus(address token) external view returns (bool status);
    function setTradeMiningStatus(address token, bool status) external;
    function getFeeVaultForLP(address token) external view returns (address feeVault); // for LPs
    function setFeeVaultForLP(address token, address feeVault) external;

    function setGovernance(address _new) external;
    function setController(address _new) external;
    function setFeeReceiver(address _new) external;
    function setVaultForLP(address _new) external;
    function setVaultForTrader(address _new) external;
    function setVaultForCNode(address _new) external;
    function getController() external view returns (address controller);
    function getFeeReceiver() external view returns (address feeReceiver); // For CoFi Holders
    function getVaultForLP() external view returns (address vaultForLP);
    function getVaultForTrader() external view returns (address vaultForTrader);
    function getVaultForCNode() external view returns (address vaultForCNode);
}

// 
interface ICoFiXController {

    event NewK(address token, int128 K, int128 sigma, uint256 T, uint256 ethAmount, uint256 erc20Amount, uint256 blockNum, uint256 tIdx, uint256 sigmaIdx, int128 K0);
    event NewGovernance(address _new);
    event NewOracle(address _priceOracle);
    event NewKTable(address _kTable);
    event NewTimespan(uint256 _timeSpan);
    event NewKRefreshInterval(uint256 _interval);
    event NewKLimit(int128 maxK0);
    event NewGamma(int128 _gamma);
    event NewTheta(address token, uint32 theta);

    function addCaller(address caller) external;

    function queryOracle(address token, uint8 op, bytes memory data) external payable returns (uint256 k, uint256 ethAmount, uint256 erc20Amount, uint256 blockNum, uint256 theta);
}

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

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

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

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

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

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

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

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

// 
/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");

        return c;
    }

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

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

        return c;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");

        return c;
    }

    /**
     * @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;
    }

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

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

// 
// ERC20 token implementation, inherited by CoFiXPair contract, no owner or governance
contract CoFiXERC20 is ICoFiXERC20 {
    using SafeMath for uint;

    string public constant nameForDomain = 'CoFiX Pool Token';
    uint8 public override constant decimals = 18;
    uint  public override totalSupply;
    mapping(address => uint) public override balanceOf;
    mapping(address => mapping(address => uint)) public override allowance;

    bytes32 public override DOMAIN_SEPARATOR;
    // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
    bytes32 public override constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
    mapping(address => uint) public override nonces;

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

    constructor() public {
        uint chainId;
        assembly {
            chainId := chainid()
        }
        DOMAIN_SEPARATOR = keccak256(
            abi.encode(
                keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
                keccak256(bytes(nameForDomain)),
                keccak256(bytes('1')),
                chainId,
                address(this)
            )
        );
    }

    function _mint(address to, uint value) internal {
        totalSupply = totalSupply.add(value);
        balanceOf[to] = balanceOf[to].add(value);
        emit Transfer(address(0), to, value);
    }

    function _burn(address from, uint value) internal {
        balanceOf[from] = balanceOf[from].sub(value);
        totalSupply = totalSupply.sub(value);
        emit Transfer(from, address(0), value);
    }

    function _approve(address owner, address spender, uint value) private {
        allowance[owner][spender] = value;
        emit Approval(owner, spender, value);
    }

    function _transfer(address from, address to, uint value) private {
        balanceOf[from] = balanceOf[from].sub(value);
        balanceOf[to] = balanceOf[to].add(value);
        emit Transfer(from, to, value);
    }

    function approve(address spender, uint value) external override returns (bool) {
        _approve(msg.sender, spender, value);
        return true;
    }

    function transfer(address to, uint value) external override returns (bool) {
        _transfer(msg.sender, to, value);
        return true;
    }

    function transferFrom(address from, address to, uint value) external override returns (bool) {
        if (allowance[from][msg.sender] != uint(-1)) {
            allowance[from][msg.sender] = allowance[from][msg.sender].sub(value);
        }
        _transfer(from, to, value);
        return true;
    }

    function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external override {
        require(deadline >= block.timestamp, 'CERC20: EXPIRED');
        bytes32 digest = keccak256(
            abi.encodePacked(
                '\x19\x01',
                DOMAIN_SEPARATOR,
                keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
            )
        );
        address recoveredAddress = ecrecover(digest, v, r, s);
        require(recoveredAddress != address(0) && recoveredAddress == owner, 'CERC20: INVALID_SIGNATURE');
        _approve(owner, spender, value);
    }
}

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

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

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

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

// 
// Pair contract for each trading pair, storing assets and handling settlement
// No owner or governance
contract CoFiXPair is ICoFiXPair, CoFiXERC20 {
    using SafeMath for uint;

    enum CoFiX_OP { QUERY, MINT, BURN, SWAP_WITH_EXACT, SWAP_FOR_EXACT } // operations in CoFiX

    uint public override constant MINIMUM_LIQUIDITY = 10**9; // it's negligible because we calc liquidity in ETH
    bytes4 private constant SELECTOR = bytes4(keccak256(bytes("transfer(address,uint256)")));

    uint256 constant public K_BASE = 1E8; // K
    uint256 constant public NAVPS_BASE = 1E18; // NAVPS (Net Asset Value Per Share), need accuracy
    uint256 constant public THETA_BASE = 1E8; // theta

    string public name;
    string public symbol;

    address public override immutable factory;
    address public override token0; // WETH token
    address public override token1; // any ERC20 token

    uint112 private reserve0;           // uses single storage slot, accessible via getReserves
    uint112 private reserve1;           // uses single storage slot, accessible via getReserves

    uint private unlocked = 1;

    event Mint(address indexed sender, uint amount0, uint amount1);
    event Burn(address indexed sender, address outToken, uint outAmount, address indexed to);
    event Swap(
        address indexed sender,
        uint amountIn,
        uint amountOut,
        address outToken,
        address indexed to
    );
    event Sync(uint112 reserve0, uint112 reserve1);

    modifier lock() {
        require(unlocked == 1, "CPair: LOCKED");
        unlocked = 0;
        _;
        unlocked = 1;
    }

    constructor() public {
        factory = msg.sender;
    }

    receive() external payable {}

    // called once by the factory at time of deployment
    function initialize(address _token0, address _token1, string memory _name, string memory _symbol) external override {
        require(msg.sender == factory, "CPair: FORBIDDEN"); // sufficient check
        token0 = _token0;
        token1 = _token1;
        name = _name;
        symbol = _symbol;
    }

    function getReserves() public override view returns (uint112 _reserve0, uint112 _reserve1) {
        _reserve0 = reserve0;
        _reserve1 = reserve1;
    }

    function _safeTransfer(address token, address to, uint value) private {
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), "CPair: TRANSFER_FAILED");
    }

    // update reserves
    function _update(uint balance0, uint balance1) private {
        require(balance0 <= uint112(-1) && balance1 <= uint112(-1), "CPair: OVERFLOW");
        reserve0 = uint112(balance0);
        reserve1 = uint112(balance1);
        emit Sync(reserve0, reserve1);
    }

    // this low-level function should be called from a contract which performs important safety checks
    function mint(address to) external payable override lock returns (uint liquidity, uint oracleFeeChange) {
        address _token0 = token0;                                // gas savings
        address _token1 = token1;                                // gas savings
        (uint112 _reserve0, uint112 _reserve1) = getReserves(); // gas savings
        uint balance0 = IERC20(_token0).balanceOf(address(this));
        uint balance1 = IERC20(_token1).balanceOf(address(this));
        uint amount0 = balance0.sub(_reserve0);
        uint amount1 = balance1.sub(_reserve1);

        uint256 _ethBalanceBefore = address(this).balance;
        { // scope for ethAmount/erc20Amount/blockNum to avoid stack too deep error
            bytes memory data = abi.encode(msg.sender, to, amount0, amount1);
            // query price
            OraclePrice memory _op;
            (_op.K, _op.ethAmount, _op.erc20Amount, _op.blockNum, _op.theta) = _queryOracle(_token1, CoFiX_OP.MINT, data);
            uint256 navps = calcNAVPerShareForMint(_reserve0, _reserve1, _op);
            if (totalSupply == 0) {
                liquidity = calcLiquidity(amount0, amount1, navps, _op).sub(MINIMUM_LIQUIDITY);
                _mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens
            } else {
                liquidity = calcLiquidity(amount0, amount1, navps, _op);
            }
        }
        oracleFeeChange = msg.value.sub(_ethBalanceBefore.sub(address(this).balance));

        require(liquidity > 0, "CPair: SHORT_LIQUIDITY_MINTED");
        _mint(to, liquidity);

        _update(balance0, balance1);
        if (oracleFeeChange > 0) TransferHelper.safeTransferETH(msg.sender, oracleFeeChange);

        emit Mint(msg.sender, amount0, amount1);
    }

    // this low-level function should be called from a contract which performs important safety checks
    function burn(address outToken, address to) external payable override lock returns (uint amountOut, uint oracleFeeChange) {
        address _token0 = token0;                                // gas savings
        address _token1 = token1;                                // gas savings
        uint balance0 = IERC20(_token0).balanceOf(address(this));
        uint balance1 = IERC20(_token1).balanceOf(address(this));
        uint liquidity = balanceOf[address(this)];

        uint256 _ethBalanceBefore = address(this).balance;
        uint256 fee;
        {
            bytes memory data = abi.encode(msg.sender, outToken, to, liquidity);
            // query price
            OraclePrice memory _op;
            (_op.K, _op.ethAmount, _op.erc20Amount, _op.blockNum, _op.theta) = _queryOracle(_token1, CoFiX_OP.BURN, data);
            if (outToken == _token0) {
                (amountOut, fee) = calcOutToken0ForBurn(liquidity, _op); // navps calculated
            } else if (outToken == _token1) {
                (amountOut, fee) = calcOutToken1ForBurn(liquidity, _op); // navps calculated
            }  else {
                revert("CPair: wrong outToken");
            }
        }
        oracleFeeChange = msg.value.sub(_ethBalanceBefore.sub(address(this).balance));

        require(amountOut > 0, "CPair: SHORT_LIQUIDITY_BURNED");
        _burn(address(this), liquidity);
        _safeTransfer(outToken, to, amountOut);
        if (fee > 0) {
            if (ICoFiXFactory(factory).getTradeMiningStatus(_token1)) {
                // only transfer fee to protocol feeReceiver when trade mining is enabled for this trading pair
                _safeSendFeeForCoFiHolder(_token0, fee);
            } else {
                _safeSendFeeForLP(_token0, _token1, fee);
            }
        }
        balance0 = IERC20(_token0).balanceOf(address(this));
        balance1 = IERC20(_token1).balanceOf(address(this));

        _update(balance0, balance1);
        if (oracleFeeChange > 0) TransferHelper.safeTransferETH(msg.sender, oracleFeeChange);

        emit Burn(msg.sender, outToken, amountOut, to);
    }


    // this low-level function should be called from a contract which performs important safety checks
    function swapWithExact(address outToken, address to)
        external
        payable override lock
        returns (uint amountIn, uint amountOut, uint oracleFeeChange, uint256[4] memory tradeInfo)
    {
        // tradeInfo[0]: thetaFee, tradeInfo[1]: x, tradeInfo[2]: y, tradeInfo[3]: navps
        address _token0 = token0;
        address _token1 = token1;
        uint256 balance0 = IERC20(_token0).balanceOf(address(this));
        uint256 balance1 = IERC20(_token1).balanceOf(address(this));

        // uint256 fee;
        { // scope for ethAmount/erc20Amount/blockNum to avoid stack too deep error
            uint256 _ethBalanceBefore = address(this).balance;
            (uint112 _reserve0, uint112 _reserve1) = getReserves(); // gas savings
            // calc amountIn
            if (outToken == _token1) {
                amountIn = balance0.sub(_reserve0);
            } else if (outToken == _token0) {
                amountIn = balance1.sub(_reserve1);
            } else {
                revert("CPair: wrong outToken");
            }
            require(amountIn > 0, "CPair: wrong amountIn");
            bytes memory data = abi.encode(msg.sender, outToken, to, amountIn);
            // query price
            OraclePrice memory _op;
            (_op.K, _op.ethAmount, _op.erc20Amount, _op.blockNum, _op.theta) = _queryOracle(_token1, CoFiX_OP.SWAP_WITH_EXACT, data);
            if (outToken == _token1) {
                (amountOut, tradeInfo[0]) = calcOutToken1(amountIn, _op);
                tradeInfo[1] = _reserve0; // swap token0 for token1 out
                tradeInfo[2] = uint256(_reserve1).mul(_op.ethAmount).div(_op.erc20Amount); // _reserve1 value as _reserve0
            } else if (outToken == _token0) {
                (amountOut, tradeInfo[0]) = calcOutToken0(amountIn, _op);
                tradeInfo[1] = uint256(_reserve1).mul(_op.ethAmount).div(_op.erc20Amount); // _reserve1 value as _reserve0
                tradeInfo[2] = _reserve0; // swap token1 for token0 out
            }
            oracleFeeChange = msg.value.sub(_ethBalanceBefore.sub(address(this).balance));
            tradeInfo[3] = calcNAVPerShare(_reserve0, _reserve1, _op.ethAmount, _op.erc20Amount);
        }
        
        require(to != _token0 && to != _token1, "CPair: INVALID_TO");

        _safeTransfer(outToken, to, amountOut); // optimistically transfer tokens
        if (tradeInfo[0] > 0) {
            if (ICoFiXFactory(factory).getTradeMiningStatus(_token1)) {
                // only transfer fee to protocol feeReceiver when trade mining is enabled for this trading pair
                _safeSendFeeForCoFiHolder(_token0, tradeInfo[0]);
            } else {
                _safeSendFeeForLP(_token0, _token1, tradeInfo[0]);
                tradeInfo[0] = 0; // so router won't go into the trade mining logic (reduce one more call gas cost)
            }
        }
        balance0 = IERC20(_token0).balanceOf(address(this));
        balance1 = IERC20(_token1).balanceOf(address(this));

        _update(balance0, balance1);
        if (oracleFeeChange > 0) TransferHelper.safeTransferETH(msg.sender, oracleFeeChange);

        emit Swap(msg.sender, amountIn, amountOut, outToken, to);
    }

    // this low-level function should be called from a contract which performs important safety checks
    function swapForExact(address outToken, uint amountOutExact, address to)
        external
        payable override lock
        returns (uint amountIn, uint amountOut, uint oracleFeeChange, uint256[4] memory tradeInfo)
    {
        // tradeInfo[0]: thetaFee, tradeInfo[1]: x, tradeInfo[2]: y, tradeInfo[3]: navps
        address _token0 = token0;
        address _token1 = token1;
        OraclePrice memory _op;

        // uint256 fee;

        { // scope for ethAmount/erc20Amount/blockNum to avoid stack too deep error
            uint256 _ethBalanceBefore = address(this).balance;
            bytes memory data = abi.encode(msg.sender, outToken, amountOutExact, to);
            // query price
            (_op.K, _op.ethAmount, _op.erc20Amount, _op.blockNum, _op.theta) = _queryOracle(_token1, CoFiX_OP.SWAP_FOR_EXACT, data);
            oracleFeeChange = msg.value.sub(_ethBalanceBefore.sub(address(this).balance));
        }

        { // calc and check amountIn, also outToken
            uint256 balance0 = IERC20(_token0).balanceOf(address(this));
            uint256 balance1 = IERC20(_token1).balanceOf(address(this));
            (uint112 _reserve0, uint112 _reserve1) = getReserves(); // gas savings
     
            if (outToken == _token1) {
                amountIn = balance0.sub(_reserve0);
                tradeInfo[1] = _reserve0; // swap token0 for token1 out
                tradeInfo[2] = uint256(_reserve1).mul(_op.ethAmount).div(_op.erc20Amount); // _reserve1 value as _reserve0
            } else if (outToken == _token0) {
                amountIn = balance1.sub(_reserve1);
                tradeInfo[1] = uint256(_reserve1).mul(_op.ethAmount).div(_op.erc20Amount); // _reserve1 value as _reserve0
                tradeInfo[2] = _reserve0; // swap token1 for token0 out
            } else {
                revert("CPair: wrong outToken");
            }
            require(amountIn > 0, "CPair: wrong amountIn");
            tradeInfo[3] = calcNAVPerShare(_reserve0, _reserve1, _op.ethAmount, _op.erc20Amount);
        }

        { // split with branch upbove to make code more clear
            uint _amountInNeeded;
            uint _amountInLeft;
            if (outToken == _token1) {
                (_amountInNeeded, tradeInfo[0]) = calcInNeededToken0(amountOutExact, _op);
                _amountInLeft = amountIn.sub(_amountInNeeded);
                if (_amountInLeft > 0) {
                    _safeTransfer(_token0, to, _amountInLeft); // send back the amount0 token change
                }
            } else if (outToken == _token0) {
                (_amountInNeeded, tradeInfo[0]) = calcInNeededToken1(amountOutExact, _op);
                _amountInLeft = amountIn.sub(_amountInNeeded);
                if (_amountInLeft > 0) {
                    _safeTransfer(_token1, to, _amountInLeft); // send back the amount1 token change
                }
            }
            require(_amountInNeeded <= amountIn, "CPair: insufficient amountIn");
            require(_amountInNeeded > 0, "CPair: wrong amountIn needed");
        }
        
        {
            require(to != _token0 && to != _token1, "CPair: INVALID_TO");

            amountOut = amountOutExact;
            _safeTransfer(outToken, to, amountOut); // optimistically transfer tokens
            if (tradeInfo[0] > 0) {
                if (ICoFiXFactory(factory).getTradeMiningStatus(_token1)) {
                    // only transfer fee to protocol feeReceiver when trade mining is enabled for this trading pair
                    _safeSendFeeForCoFiHolder(_token0, tradeInfo[0]);
                } else {
                    _safeSendFeeForLP(_token0, _token1, tradeInfo[0]);
                    tradeInfo[0] = 0; // so router won't go into the trade mining logic (reduce one more call gas cost)
                }
            }
            uint256 balance0 = IERC20(_token0).balanceOf(address(this));
            uint256 balance1 = IERC20(_token1).balanceOf(address(this));

            _update(balance0, balance1);
            if (oracleFeeChange > 0) TransferHelper.safeTransferETH(msg.sender, oracleFeeChange);
        }

        emit Swap(msg.sender, amountIn, amountOut, outToken, to);
    }

    // force balances to match reserves
    function skim(address to) external override lock {
        address _token0 = token0; // gas savings
        address _token1 = token1; // gas savings
        _safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)).sub(reserve0));
        _safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)).sub(reserve1));
    }

    // force reserves to match balances
    function sync() external override lock {
        _update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)));
    }

    // calc Net Asset Value Per Share for mint
    // use it in this contract, for optimized gas usage
    function calcNAVPerShareForMint(uint256 balance0, uint256 balance1, OraclePrice memory _op) public view returns (uint256 navps) {
        uint _totalSupply = totalSupply;
        if (_totalSupply == 0) {
            navps = NAVPS_BASE;
        } else {
            /*
            N_{p} &= (A_{u}/P_{s}^{'} + A_{e})/S \\\\
                  &= (A_{u}/(P * (1 - K)) + A_{e})/S \\\\
                  &= (\frac{A_{u}}{\frac{erc20Amount}{ethAmount} * \frac{(k_{BASE} - k)}{(k_{BASE})}} + A_{e})/S \\\\
                  &= (\frac{A_{u}*ethAmount*k_{BASE}}{erc20Amount*(k_{BASE} - k)}+ A_{e}) / S \\\\
                  &= (A_{u}*ethAmount*k_{BASE}+ A_{e}*erc20Amount*(k_{BASE} - k)) / S / (erc20Amount*(k_{BASE} - k)) \\\\
            N_{p} &= NAVPS_{BASE}*(A_{u}*ethAmount*k_{BASE}+ A_{e}*erc20Amount*(k_{BASE} - k)) / S / (erc20Amount*(k_{BASE} - k)) \\\\
            // navps = NAVPS_BASE * ( (balance1*_op.ethAmount*K_BASE) + (balance0*_op.erc20Amount*(K_BASE-_op.K)) ) / _totalSupply / _op.erc20Amount / (K_BASE-_op.K);
            */
            uint256 kbaseSubK = K_BASE.sub(_op.K);
            uint256 balance1MulEthKbase = balance1.mul(_op.ethAmount).mul(K_BASE);
            uint256 balance0MulErcKbsk = balance0.mul(_op.erc20Amount).mul(kbaseSubK);
            navps = NAVPS_BASE.mul( (balance1MulEthKbase).add(balance0MulErcKbsk) ).div(_totalSupply).div(_op.erc20Amount).div(kbaseSubK);
        }
    }

    // calc Net Asset Value Per Share for burn
    // use it in this contract, for optimized gas usage
    function calcNAVPerShareForBurn(uint256 balance0, uint256 balance1, OraclePrice memory _op) public view returns (uint256 navps) {
        uint _totalSupply = totalSupply;
        if (_totalSupply == 0) {
            navps = NAVPS_BASE;
        } else {
            /*
            N_{p}^{'} &= (A_{u}/P_{b}^{'} + A_{e})/S \\\\
                      &= (A_{u}/(P * (1 + K)) + A_{e})/S \\\\
                      &= (\frac{A_{u}}{\frac{erc20Amount}{ethAmount} * \frac{(k_{BASE} + k)}{(k_{BASE})}} + A_{e})/S \\\\
                      &= (\frac{A_{u}*ethAmount*k_{BASE}}{erc20Amount*(k_{BASE} + k)}+ A_{e}) / S \\\\
                      &= (A_{u}*ethAmount*k_{BASE}+ A_{e}*erc20Amount*(k_{BASE} + k)) / S / (erc20Amount*(k_{BASE} + k)) \\\\
            N_{p}^{'} &= NAVPS_{BASE}*(A_{u}*ethAmount*k_{BASE}+ A_{e}*erc20Amount*(k_{BASE} + k)) / S / (erc20Amount*(k_{BASE} + k)) \\\\
            // navps = NAVPS_BASE * ( (balance1*_op.ethAmount*K_BASE) + (balance0*_op.erc20Amount*(K_BASE+_op.K)) ) / _totalSupply / _op.erc20Amount / (K_BASE+_op.K);
            */
            uint256 kbaseAddK = K_BASE.add(_op.K);
            uint256 balance1MulEthKbase = balance1.mul(_op.ethAmount).mul(K_BASE);
            uint256 balance0MulErcKbsk = balance0.mul(_op.erc20Amount).mul(kbaseAddK);
            navps = NAVPS_BASE.mul( (balance1MulEthKbase).add(balance0MulErcKbsk) ).div(_totalSupply).div(_op.erc20Amount).div(kbaseAddK);
        }
    }

    // calc Net Asset Value Per Share (no K)
    // use it in this contract, for optimized gas usage
    function calcNAVPerShare(uint256 balance0, uint256 balance1, uint256 ethAmount, uint256 erc20Amount) public view returns (uint256 navps) {
        uint _totalSupply = totalSupply;
        if (_totalSupply == 0) {
            navps = NAVPS_BASE;
        } else {
            /*
            N_{p}^{'} &= (A_{u}/P + A_{e})/S \\\\
                      &= (\frac{A_{u}}{\frac{erc20Amount}{ethAmount}} + A_{e})/S \\\\
                      &= (\frac{A_{u}*ethAmount}{erc20Amount}+ A_{e}) / S \\\\
                      &= (A_{u}*ethAmount+ A_{e}*erc20Amount) / S / (erc20Amount) \\\\
            N_{p}^{'} &= NAVPS_{BASE}*(A_{u}*ethAmount+ A_{e}*erc20Amount) / S / (erc20Amount) \\\\
            // navps = NAVPS_BASE * ( (balance1*_op.ethAmount) + (balance0*_op.erc20Amount) ) / _totalSupply / _op.erc20Amount;
            */
            uint256 balance1MulEth = balance1.mul(ethAmount);
            uint256 balance0MulErc = balance0.mul(erc20Amount);
            navps = NAVPS_BASE.mul( (balance1MulEth).add(balance0MulErc) ).div(_totalSupply).div(erc20Amount);
        }
    }

    // use it in this contract, for optimized gas usage
    function calcLiquidity(uint256 amount0, uint256 amount1, uint256 navps, OraclePrice memory _op) public pure returns (uint256 liquidity) {
        /*
        s_{1} &= a / (N_{p} / NAVPS_{BASE}) \\\\
              &= a * NAVPS_{BASE} / N_{p} \\\\
        s_{2} &= b / P_{b}^{'} / (N_{p} / NAVPS_{BASE}) \\\\
              &= b / (N_{p} / NAVPS_{BASE}) / P_{b}^{'} \\\\
              &= b * NAVPS_{BASE} / N_{p} / P_{b}^{'} \\\\
              &= b * NAVPS_{BASE} / N_{p} / (\frac{erc20Amount}{ethAmount} * \frac{(k_{BASE} + k)}{(k_{BASE})}) \\\\
              &= b * NAVPS_{BASE} * ethAmount * k_{BASE} / N_{p} / (erc20Amount * (k_{BASE} + k))
        s &= s_1 + s_2 \\\\
          &= a * NAVPS_{BASE} / N_{p} + b * NAVPS_{BASE} / N_{p} / P_{b}^{'} \\\\
          &= a * NAVPS_{BASE} / N_{p} + b * NAVPS_{BASE} * ethAmount * k_{BASE} / N_{p} / (erc20Amount * (k_{BASE} + k)) \\\\
        // liquidity = (amount0 * NAVPS_BASE / navps) + (amount1 * NAVPS_BASE * _op.ethAmount * K_BASE / navps / _op.erc20Amount / (K_BASE + _op.K));
        */
        uint256 amnt0MulNbaseDivN = amount0.mul(NAVPS_BASE).div(navps);
        uint256 amnt1MulNbaseEthKbase = amount1.mul(NAVPS_BASE).mul(_op.ethAmount).mul(K_BASE);
        liquidity = ( amnt0MulNbaseDivN ).add( amnt1MulNbaseEthKbase.div(navps).div(_op.erc20Amount).div(K_BASE.add(_op.K)) );
    }

    // get Net Asset Value Per Share for mint
    // only for read, could cost more gas if use it directly in contract
    function getNAVPerShareForMint(OraclePrice memory _op) public view returns (uint256 navps) {
        return calcNAVPerShareForMint(reserve0, reserve1, _op);
    }

    // get Net Asset Value Per Share for burn
    // only for read, could cost more gas if use it directly in contract
    function getNAVPerShareForBurn(OraclePrice memory _op) external view returns (uint256 navps) {
        return calcNAVPerShareForBurn(reserve0, reserve1, _op);
    }

    // get Net Asset Value Per Share
    // only for read, could cost more gas if use it directly in contract
    function getNAVPerShare(uint256 ethAmount, uint256 erc20Amount) external override view returns (uint256 navps) {
        return calcNAVPerShare(reserve0, reserve1, ethAmount, erc20Amount);
    }

    // get estimated liquidity amount (it represents the amount of pool tokens will be minted if someone provide liquidity to the pool)
    // only for read, could cost more gas if use it directly in contract
    function getLiquidity(uint256 amount0, uint256 amount1, OraclePrice memory _op) external view returns (uint256 liquidity) {
        uint256 navps = getNAVPerShareForMint(_op);
        return calcLiquidity(amount0, amount1, navps, _op);
    }

    // calc amountOut for token0 (WETH) when send liquidity token to pool for burning
    function calcOutToken0ForBurn(uint256 liquidity, OraclePrice memory _op) public view returns (uint256 amountOut, uint256 fee) {
        /*
        e &= c * (N_{p}^{'} / NAVPS_{BASE}) * (THETA_{BASE} - \theta)/THETA_{BASE} \\\\
          &= c * \frac{N_{p}^{'}}{NAVPS_{BASE}} * \frac{THETA_{BASE} - \theta}{THETA_{BASE}} \\\\
          &= c * N_{p}^{'} * (THETA_{BASE} - \theta) / NAVPS_{BASE} / THETA_{BASE} \\\\
        // amountOut = liquidity * navps * (THETA_BASE - _op.theta) / NAVPS_BASE / THETA_BASE;
        */
        uint256 navps = calcNAVPerShareForBurn(reserve0, reserve1, _op);
        amountOut = liquidity.mul(navps).mul(THETA_BASE.sub(_op.theta)).div(NAVPS_BASE).div(THETA_BASE);
        if (_op.theta != 0) {
            // fee = liquidity * navps * (_op.theta) / NAVPS_BASE / THETA_BASE;
            fee = liquidity.mul(navps).mul(_op.theta).div(NAVPS_BASE).div(THETA_BASE);
        }
        return (amountOut, fee);
    }


    // calc amountOut for token1 (ERC20 token) when send liquidity token to pool for burning
    function calcOutToken1ForBurn(uint256 liquidity, OraclePrice memory _op) public view returns (uint256 amountOut, uint256 fee) {
        /*
        u &= c * (N_{p}^{'} / NAVPS_{BASE}) * P_{s}^{'} * (THETA_{BASE} - \theta)/THETA_{BASE} \\\\
          &= c * \frac{N_{p}^{'}}{NAVPS_{BASE}} * \frac{erc20Amount}{ethAmount} * \frac{(k_{BASE} - k)}{(k_{BASE})} * \frac{THETA_{BASE} - \theta}{THETA_{BASE}} \\\\
          &= \frac{c * N_{p}^{'} * erc20Amount * (k_{BASE} - k) * (THETA_{BASE} - \theta)}{NAVPS_{BASE}*ethAmount*k_{BASE}*THETA_{BASE}}
        // amountOut = liquidity * navps * _op.erc20Amount * (K_BASE - _op.K) * (THETA_BASE - _op.theta) / NAVPS_BASE / _op.ethAmount / K_BASE / THETA_BASE;
        */
        uint256 navps = calcNAVPerShareForBurn(reserve0, reserve1, _op);
        uint256 liqMulMany = liquidity.mul(navps).mul(_op.erc20Amount).mul(K_BASE.sub(_op.K)).mul(THETA_BASE.sub(_op.theta));
        amountOut = liqMulMany.div(NAVPS_BASE).div(_op.ethAmount).div(K_BASE).div(THETA_BASE);
        if (_op.theta != 0) {
            // fee = liquidity * navps * (_op.theta) / NAVPS_BASE / THETA_BASE;
            fee = liquidity.mul(navps).mul(_op.theta).div(NAVPS_BASE).div(THETA_BASE);
        }
        return (amountOut, fee);
    }

    // get estimated amountOut for token0 (WETH) when swapWithExact
    function calcOutToken0(uint256 amountIn, OraclePrice memory _op) public pure returns (uint256 amountOut, uint256 fee) {
        /*
        x &= (a/P_{b}^{'})*\frac{THETA_{BASE} - \theta}{THETA_{BASE}} \\\\
          &= a / (\frac{erc20Amount}{ethAmount} * \frac{(k_{BASE} + k)}{(k_{BASE})}) * \frac{THETA_{BASE} - \theta}{THETA_{BASE}} \\\\
          &= \frac{a*ethAmount*k_{BASE}}{erc20Amount*(k_{BASE} + k)} * \frac{THETA_{BASE} - \theta}{THETA_{BASE}} \\\\
          &= \frac{a*ethAmount*k_{BASE}*(THETA_{BASE} - \theta)}{erc20Amount*(k_{BASE} + k)*THETA_{BASE}} \\\\
        // amountOut = amountIn * _op.ethAmount * K_BASE * (THETA_BASE - _op.theta) / _op.erc20Amount / (K_BASE + _op.K) / THETA_BASE;
        */
        amountOut = amountIn.mul(_op.ethAmount).mul(K_BASE).mul(THETA_BASE.sub(_op.theta)).div(_op.erc20Amount).div(K_BASE.add(_op.K)).div(THETA_BASE);
        if (_op.theta != 0) {
            // fee = amountIn * _op.ethAmount * K_BASE * (_op.theta) / _op.erc20Amount / (K_BASE + _op.K) / THETA_BASE;
            fee = amountIn.mul(_op.ethAmount).mul(K_BASE).mul(_op.theta).div(_op.erc20Amount).div(K_BASE.add(_op.K)).div(THETA_BASE);
        }
        return (amountOut, fee);
    }

    // get estimated amountOut for token1 (ERC20 token) when swapWithExact
    function calcOutToken1(uint256 amountIn, OraclePrice memory _op) public pure returns (uint256 amountOut, uint256 fee) {
        /*
        y &= b*P_{s}^{'}*\frac{THETA_{BASE} - \theta}{THETA_{BASE}} \\\\
          &= b * \frac{erc20Amount}{ethAmount} * \frac{(k_{BASE} - k)}{(k_{BASE})} * \frac{THETA_{BASE} - \theta}{THETA_{BASE}} \\\\
          &= \frac{b*erc20Amount*(k_{BASE} - k)*(THETA_{BASE} - \theta)}{ethAmount*k_{BASE}*THETA_{BASE}} \\\\
        // amountOut = amountIn * _op.erc20Amount * (K_BASE - _op.K) * (THETA_BASE - _op.theta) / _op.ethAmount / K_BASE / THETA_BASE;
        */
        amountOut = amountIn.mul(_op.erc20Amount).mul(K_BASE.sub(_op.K)).mul(THETA_BASE.sub(_op.theta)).div(_op.ethAmount).div(K_BASE).div(THETA_BASE);
        if (_op.theta != 0) {
            // fee = amountIn * _op.theta / THETA_BASE;
            fee = amountIn.mul(_op.theta).div(THETA_BASE);
        }
        return (amountOut, fee);
    }

    // get estimate amountInNeeded for token0 (WETH) when swapForExact
    function calcInNeededToken0(uint256 amountOut, OraclePrice memory _op) public pure returns (uint256 amountInNeeded, uint256 fee) {
        // inverse of calcOutToken1
        // amountOut = amountIn.mul(_op.erc20Amount).mul(K_BASE.sub(_op.K)).mul(THETA_BASE.sub(_op.theta)).div(_op.ethAmount).div(K_BASE).div(THETA_BASE);
        amountInNeeded = amountOut.mul(_op.ethAmount).mul(K_BASE).mul(THETA_BASE).div(_op.erc20Amount).div(K_BASE.sub(_op.K)).div(THETA_BASE.sub(_op.theta));
        if (_op.theta != 0) {
            // fee = amountIn * _op.theta / THETA_BASE;
            fee = amountInNeeded.mul(_op.theta).div(THETA_BASE);
        }
        return (amountInNeeded, fee);
    }

    // get estimate amountInNeeded for token1 (ERC20 token) when swapForExact
    function calcInNeededToken1(uint256 amountOut, OraclePrice memory _op) public pure returns (uint256 amountInNeeded, uint256 fee) {
        // inverse of calcOutToken0
        // amountOut = amountIn.mul(_op.ethAmount).mul(K_BASE).mul(THETA_BASE.sub(_op.theta)).div(_op.erc20Amount).div(K_BASE.add(_op.K)).div(THETA_BASE);
        amountInNeeded = amountOut.mul(_op.erc20Amount).mul(K_BASE.add(_op.K)).mul(THETA_BASE).div(_op.ethAmount).div(K_BASE).div(THETA_BASE.sub(_op.theta));
        if (_op.theta != 0) {
            // fee = amountIn * _op.ethAmount * K_BASE * (_op.theta) / _op.erc20Amount / (K_BASE + _op.K) / THETA_BASE;
            fee = amountInNeeded.mul(_op.ethAmount).mul(K_BASE).mul(_op.theta).div(_op.erc20Amount).div(K_BASE.add(_op.K)).div(THETA_BASE);
        }
        return (amountInNeeded, fee);
    }

    function _queryOracle(address token, CoFiX_OP op, bytes memory data) internal returns (uint256, uint256, uint256, uint256, uint256) {
        return ICoFiXController(ICoFiXFactory(factory).getController()).queryOracle{value: msg.value}(token, uint8(op), data);
    }

    // Safe WETH transfer function, just in case not having enough WETH. CoFi holder will earn these fees.
    function _safeSendFeeForCoFiHolder(address _token0, uint256 _fee) internal {
        address feeReceiver = ICoFiXFactory(factory).getFeeReceiver();
        if (feeReceiver == address(0)) {
            return; // if feeReceiver not set, theta fee keeps in pair pool
        }
        _safeSendFee(_token0, feeReceiver, _fee); // transfer fee to protocol fee reward pool for CoFi holders
    }

    // Safe WETH transfer function, just in case not having enough WETH. LP will earn these fees.
    function _safeSendFeeForLP(address _token0, address _token1, uint256 _fee) internal {
        address feeVault = ICoFiXFactory(factory).getFeeVaultForLP(_token1);
        if (feeVault == address(0)) {
            return; // if fee vault not set, theta fee keeps in pair pool
        }
        _safeSendFee(_token0, feeVault, _fee); // transfer fee to protocol fee reward pool for LP
    }

    function _safeSendFee(address _token0, address _receiver, uint256 _fee) internal {
        uint256 wethBal = IERC20(_token0).balanceOf(address(this));
        if (_fee > wethBal) {
            _fee = wethBal;
        }
        if (_fee > 0) _safeTransfer(_token0, _receiver, _fee); 
    }
}

// UNI & CoFi Rocks

Contract Security Audit

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"address","name":"outToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"outAmount","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"Burn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOut","type":"uint256"},{"indexed":false,"internalType":"address","name":"outToken","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint112","name":"reserve0","type":"uint112"},{"indexed":false,"internalType":"uint112","name":"reserve1","type":"uint112"}],"name":"Sync","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"K_BASE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINIMUM_LIQUIDITY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NAVPS_BASE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"THETA_BASE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"outToken","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"burn","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"oracleFeeChange","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"components":[{"internalType":"uint256","name":"ethAmount","type":"uint256"},{"internalType":"uint256","name":"erc20Amount","type":"uint256"},{"internalType":"uint256","name":"blockNum","type":"uint256"},{"internalType":"uint256","name":"K","type":"uint256"},{"internalType":"uint256","name":"theta","type":"uint256"}],"internalType":"struct ICoFiXPair.OraclePrice","name":"_op","type":"tuple"}],"name":"calcInNeededToken0","outputs":[{"internalType":"uint256","name":"amountInNeeded","type":"uint256"},{"internalType":"uint256","name":"fee","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"components":[{"internalType":"uint256","name":"ethAmount","type":"uint256"},{"internalType":"uint256","name":"erc20Amount","type":"uint256"},{"internalType":"uint256","name":"blockNum","type":"uint256"},{"internalType":"uint256","name":"K","type":"uint256"},{"internalType":"uint256","name":"theta","type":"uint256"}],"internalType":"struct ICoFiXPair.OraclePrice","name":"_op","type":"tuple"}],"name":"calcInNeededToken1","outputs":[{"internalType":"uint256","name":"amountInNeeded","type":"uint256"},{"internalType":"uint256","name":"fee","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"},{"internalType":"uint256","name":"navps","type":"uint256"},{"components":[{"internalType":"uint256","name":"ethAmount","type":"uint256"},{"internalType":"uint256","name":"erc20Amount","type":"uint256"},{"internalType":"uint256","name":"blockNum","type":"uint256"},{"internalType":"uint256","name":"K","type":"uint256"},{"internalType":"uint256","name":"theta","type":"uint256"}],"internalType":"struct ICoFiXPair.OraclePrice","name":"_op","type":"tuple"}],"name":"calcLiquidity","outputs":[{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"balance0","type":"uint256"},{"internalType":"uint256","name":"balance1","type":"uint256"},{"internalType":"uint256","name":"ethAmount","type":"uint256"},{"internalType":"uint256","name":"erc20Amount","type":"uint256"}],"name":"calcNAVPerShare","outputs":[{"internalType":"uint256","name":"navps","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"balance0","type":"uint256"},{"internalType":"uint256","name":"balance1","type":"uint256"},{"components":[{"internalType":"uint256","name":"ethAmount","type":"uint256"},{"internalType":"uint256","name":"erc20Amount","type":"uint256"},{"internalType":"uint256","name":"blockNum","type":"uint256"},{"internalType":"uint256","name":"K","type":"uint256"},{"internalType":"uint256","name":"theta","type":"uint256"}],"internalType":"struct ICoFiXPair.OraclePrice","name":"_op","type":"tuple"}],"name":"calcNAVPerShareForBurn","outputs":[{"internalType":"uint256","name":"navps","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"balance0","type":"uint256"},{"internalType":"uint256","name":"balance1","type":"uint256"},{"components":[{"internalType":"uint256","name":"ethAmount","type":"uint256"},{"internalType":"uint256","name":"erc20Amount","type":"uint256"},{"internalType":"uint256","name":"blockNum","type":"uint256"},{"internalType":"uint256","name":"K","type":"uint256"},{"internalType":"uint256","name":"theta","type":"uint256"}],"internalType":"struct ICoFiXPair.OraclePrice","name":"_op","type":"tuple"}],"name":"calcNAVPerShareForMint","outputs":[{"internalType":"uint256","name":"navps","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"components":[{"internalType":"uint256","name":"ethAmount","type":"uint256"},{"internalType":"uint256","name":"erc20Amount","type":"uint256"},{"internalType":"uint256","name":"blockNum","type":"uint256"},{"internalType":"uint256","name":"K","type":"uint256"},{"internalType":"uint256","name":"theta","type":"uint256"}],"internalType":"struct ICoFiXPair.OraclePrice","name":"_op","type":"tuple"}],"name":"calcOutToken0","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"fee","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"liquidity","type":"uint256"},{"components":[{"internalType":"uint256","name":"ethAmount","type":"uint256"},{"internalType":"uint256","name":"erc20Amount","type":"uint256"},{"internalType":"uint256","name":"blockNum","type":"uint256"},{"internalType":"uint256","name":"K","type":"uint256"},{"internalType":"uint256","name":"theta","type":"uint256"}],"internalType":"struct ICoFiXPair.OraclePrice","name":"_op","type":"tuple"}],"name":"calcOutToken0ForBurn","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"fee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"components":[{"internalType":"uint256","name":"ethAmount","type":"uint256"},{"internalType":"uint256","name":"erc20Amount","type":"uint256"},{"internalType":"uint256","name":"blockNum","type":"uint256"},{"internalType":"uint256","name":"K","type":"uint256"},{"internalType":"uint256","name":"theta","type":"uint256"}],"internalType":"struct ICoFiXPair.OraclePrice","name":"_op","type":"tuple"}],"name":"calcOutToken1","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"fee","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"liquidity","type":"uint256"},{"components":[{"internalType":"uint256","name":"ethAmount","type":"uint256"},{"internalType":"uint256","name":"erc20Amount","type":"uint256"},{"internalType":"uint256","name":"blockNum","type":"uint256"},{"internalType":"uint256","name":"K","type":"uint256"},{"internalType":"uint256","name":"theta","type":"uint256"}],"internalType":"struct ICoFiXPair.OraclePrice","name":"_op","type":"tuple"}],"name":"calcOutToken1ForBurn","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"fee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"},{"components":[{"internalType":"uint256","name":"ethAmount","type":"uint256"},{"internalType":"uint256","name":"erc20Amount","type":"uint256"},{"internalType":"uint256","name":"blockNum","type":"uint256"},{"internalType":"uint256","name":"K","type":"uint256"},{"internalType":"uint256","name":"theta","type":"uint256"}],"internalType":"struct ICoFiXPair.OraclePrice","name":"_op","type":"tuple"}],"name":"getLiquidity","outputs":[{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"ethAmount","type":"uint256"},{"internalType":"uint256","name":"erc20Amount","type":"uint256"}],"name":"getNAVPerShare","outputs":[{"internalType":"uint256","name":"navps","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"ethAmount","type":"uint256"},{"internalType":"uint256","name":"erc20Amount","type":"uint256"},{"internalType":"uint256","name":"blockNum","type":"uint256"},{"internalType":"uint256","name":"K","type":"uint256"},{"internalType":"uint256","name":"theta","type":"uint256"}],"internalType":"struct ICoFiXPair.OraclePrice","name":"_op","type":"tuple"}],"name":"getNAVPerShareForBurn","outputs":[{"internalType":"uint256","name":"navps","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"ethAmount","type":"uint256"},{"internalType":"uint256","name":"erc20Amount","type":"uint256"},{"internalType":"uint256","name":"blockNum","type":"uint256"},{"internalType":"uint256","name":"K","type":"uint256"},{"internalType":"uint256","name":"theta","type":"uint256"}],"internalType":"struct ICoFiXPair.OraclePrice","name":"_op","type":"tuple"}],"name":"getNAVPerShareForMint","outputs":[{"internalType":"uint256","name":"navps","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReserves","outputs":[{"internalType":"uint112","name":"_reserve0","type":"uint112"},{"internalType":"uint112","name":"_reserve1","type":"uint112"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token0","type":"address"},{"internalType":"address","name":"_token1","type":"address"},{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"mint","outputs":[{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"oracleFeeChange","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nameForDomain","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"skim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"outToken","type":"address"},{"internalType":"uint256","name":"amountOutExact","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"swapForExact","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"oracleFeeChange","type":"uint256"},{"internalType":"uint256[4]","name":"tradeInfo","type":"uint256[4]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"outToken","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"swapWithExact","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"oracleFeeChange","type":"uint256"},{"internalType":"uint256[4]","name":"tradeInfo","type":"uint256[4]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sync","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"token0","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token1","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

60a06040526001600a553480156200001657600080fd5b50604080518082018252601081526f21b7a334ac102837b7b6102a37b5b2b760811b6020918201528151808301835260018152603160f81b9082015290514691620000cc917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f917f70f1d8d3a2b874a116643bbbdb7e2937ce56e0359f9c3070aac2ec77a8354ccc917fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6918691309101620000f5565b60408051601f198184030181529190528051602090910120600355503360601b60805262000121565b9485526020850193909352604084019190915260608301526001600160a01b0316608082015260a00190565b60805160601c61410362000162600039806108db5280610d115280611a0052806120a652806125ae5280612c885280612fbd52806130a652506141036000f3fe6080604052600436106102e05760003560e01c806395d89b4111610184578063bc25cf77116100d6578063d505accf1161008a578063e5eac39011610064578063e5eac39014610776578063eb77289914610796578063fff6cae9146107b6576102e7565b8063d505accf14610716578063dd2e0a3114610736578063dd62ed3e14610756576102e7565b8063bf36aac2116100bb578063bf36aac2146106cc578063c45a0155146106ec578063d21220a714610701576102e7565b8063bc25cf771461068c578063be5b32f8146106ac576102e7565b8063a661dbda11610138578063b2874f8711610112578063b2874f8714610637578063b72bda4a14610657578063ba9a7a5614610677576102e7565b8063a661dbda14610602578063a9059cbb14610617578063aaf07e9214610465576102e7565b80639c673c52116101695780639c673c52146105ac5780639f44296a146105cc578063a12db6cc146105ef576102e7565b806395d89b411461057757806399f45e481461058c576102e7565b8063313ce5671161023d5780634f6ec2dd116101f15780636a627842116101cb5780636a6278421461052457806370a08231146105375780637ecebe0014610557576102e7565b80634f6ec2dd146104cf5780635ad6bd30146104e4578063663eda1f14610504576102e7565b80633644e515116102225780633644e5151461047a57806339818b661461048f57806349a23da0146104af576102e7565b8063313ce567146104435780633298613114610465576102e7565b80632016a0d21161029457806324334be81161027957806324334be8146103ed57806327fc84a31461040d57806330adf81f1461042e576102e7565b80632016a0d2146103ab57806323b872dd146103cd576102e7565b8063095ea7b3116102c5578063095ea7b31461033a5780630dfe16811461036757806318160ddd14610389576102e7565b806306fdde03146102ec5780630902f1ac14610317576102e7565b366102e757005b600080fd5b3480156102f857600080fd5b506103016107cb565b60405161030e9190613bed565b60405180910390f35b34801561032357600080fd5b5061032c610877565b60405161030e929190613fbc565b34801561034657600080fd5b5061035a61035536600461384b565b6108a4565b60405161030e9190613b87565b34801561037357600080fd5b5061037c6108bb565b60405161030e9190613ab1565b34801561039557600080fd5b5061039e6108ca565b60405161030e9190613b92565b3480156103b757600080fd5b506103cb6103c6366004613710565b6108d0565b005b3480156103d957600080fd5b5061035a6103e8366004613796565b610997565b3480156103f957600080fd5b5061039e61040836600461392d565b610a49565b61042061041b3660046136d8565b610a87565b60405161030e929190613fdd565b34801561043a57600080fd5b5061039e610f2f565b34801561044f57600080fd5b50610458610f53565b60405161030e9190614054565b34801561047157600080fd5b5061039e610f58565b34801561048657600080fd5b5061039e610f60565b34801561049b57600080fd5b5061039e6104aa3660046138d7565b610f66565b3480156104bb57600080fd5b5061039e6104ca36600461394e565b610f98565b3480156104db57600080fd5b5061039e611061565b3480156104f057600080fd5b506104206104ff36600461390a565b61106d565b34801561051057600080fd5b5061039e61051f366004613983565b611112565b6104206105323660046136a0565b6111a0565b34801561054357600080fd5b5061039e6105523660046136a0565b6114a0565b34801561056357600080fd5b5061039e6105723660046136a0565b6114b2565b34801561058357600080fd5b506103016114c4565b34801561059857600080fd5b5061039e6105a73660046139c3565b61153d565b3480156105b857600080fd5b506104206105c736600461390a565b611593565b6105df6105da3660046136d8565b611627565b60405161030e949392919061400a565b6105df6105fd366004613876565b611c33565b34801561060e57600080fd5b506103016122d3565b34801561062357600080fd5b5061035a61063236600461384b565b61230c565b34801561064357600080fd5b5061039e61065236600461394e565b612319565b34801561066357600080fd5b5061039e61067236600461394e565b61233c565b34801561068357600080fd5b5061039e612372565b34801561069857600080fd5b506103cb6106a73660046136a0565b61237a565b3480156106b857600080fd5b5061039e6106c73660046138d7565b6124bc565b3480156106d857600080fd5b506104206106e736600461390a565b6124f2565b3480156106f857600080fd5b5061037c6125ac565b34801561070d57600080fd5b5061037c6125d0565b34801561072257600080fd5b506103cb6107313660046137d6565b6125df565b34801561074257600080fd5b5061042061075136600461390a565b61274e565b34801561076257600080fd5b5061039e6107713660046136d8565b612857565b34801561078257600080fd5b5061042061079136600461390a565b612874565b3480156107a257600080fd5b506104206107b136600461390a565b61293b565b3480156107c257600080fd5b506103cb6129fb565b6005805460408051602060026001851615610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190941693909304601f8101849004840282018401909252818152929183018282801561086f5780601f106108445761010080835404028352916020019161086f565b820191906000526020600020905b81548152906001019060200180831161085257829003601f168201915b505050505081565b6009546dffffffffffffffffffffffffffff808216926e0100000000000000000000000000009092041690565b60006108b1338484612b30565b5060015b92915050565b6007546001600160a01b031681565b60005481565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146109215760405162461bcd60e51b815260040161091890613e83565b60405180910390fd5b600780546001600160a01b038087167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316179092556008805492861692909116919091179055815161097c906005906020850190613508565b508051610990906006906020840190613508565b5050505050565b6001600160a01b03831660009081526002602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff14610a34576001600160a01b0384166000908152600260209081526040808320338452909152902054610a0f9083612b98565b6001600160a01b03851660009081526002602090815260408083203384529091529020555b610a3f848484612bda565b5060019392505050565b600954600090610a80906dffffffffffffffffffffffffffff808216916e010000000000000000000000000000900416858561153d565b9392505050565b600080600a54600114610aac5760405162461bcd60e51b815260040161091890613c00565b6000600a8190556007546008546040516370a0823160e01b81526001600160a01b0392831693919092169183906370a0823190610aed903090600401613ab1565b60206040518083038186803b158015610b0557600080fd5b505afa158015610b19573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b3d91906138f2565b90506000826001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401610b6d9190613ab1565b60206040518083038186803b158015610b8557600080fd5b505afa158015610b99573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bbd91906138f2565b30600090815260016020908152604080832054905193945092479291606091610bee9133918f918f91899101613ac5565b6040516020818303038152906040529050610c07613586565b610c1388600284612c7e565b608086015260408501526020840152825260608201526001600160a01b038d8116908a161415610c5157610c47858261293b565b909b509250610c8d565b876001600160a01b03168d6001600160a01b03161415610c7557610c47858261274e565b60405162461bcd60e51b815260040161091890613dde565b50610ca49050610c9d8347612b98565b3490612b98565b975060008911610cc65760405162461bcd60e51b815260040161091890613e15565b610cd03084612db8565b610cdb8b8b8b612e4e565b8015610db5576040517f80feb3980000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906380feb39890610d46908990600401613ab1565b60206040518083038186803b158015610d5e57600080fd5b505afa158015610d72573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d9691906138b7565b15610daa57610da58782612fb9565b610db5565b610db5878783613073565b6040516370a0823160e01b81526001600160a01b038816906370a0823190610de1903090600401613ab1565b60206040518083038186803b158015610df957600080fd5b505afa158015610e0d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e3191906138f2565b6040516370a0823160e01b81529095506001600160a01b038716906370a0823190610e60903090600401613ab1565b60206040518083038186803b158015610e7857600080fd5b505afa158015610e8c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eb091906138f2565b9350610ebc8585613152565b8715610ecc57610ecc3389613258565b896001600160a01b0316336001600160a01b03167f3dd1df88dc92e2788892542d81f999d720a44b4c127065d45c128f4f59fdc3738d8c604051610f11929190613b43565b60405180910390a3505050505050506001600a819055509250929050565b7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b601281565b6305f5e10081565b60035481565b6009546000906108b5906dffffffffffffffffffffffffffff808216916e010000000000000000000000000000900416845b6000805480610fb157670de0b6b3a76400009150611059565b6000610fce84606001516305f5e100612b9890919063ffffffff16565b90506000610ff76305f5e100610ff18760000151896132e590919063ffffffff16565b906132e5565b9050600061101683610ff188602001518b6132e590919063ffffffff16565b90506110538361104d886020015161104d8861104d61103e888a61331f90919063ffffffff16565b670de0b6b3a7640000906132e5565b90613344565b94505050505b509392505050565b670de0b6b3a764000081565b6000806110db61108e84608001516305f5e100612b9890919063ffffffff16565b61104d6110ac86606001516305f5e100612b9890919063ffffffff16565b61104d876020015161104d6305f5e100610ff16305f5e100610ff18d600001518f6132e590919063ffffffff16565b9150826080015160001461110b576111086305f5e10061104d8560800151856132e590919063ffffffff16565b90505b9250929050565b60008061112b8461104d88670de0b6b3a76400006132e5565b9050600061115a6305f5e100610ff18660000151610ff1670de0b6b3a76400008b6132e590919063ffffffff16565b905061119561118e61117d86606001516305f5e10061331f90919063ffffffff16565b602087015161104d9081868b613344565b839061331f565b979650505050505050565b600080600a546001146111c55760405162461bcd60e51b815260040161091890613c00565b6000600a8190556007546008546001600160a01b0391821692911690806111ea610877565b915091506000846001600160a01b03166370a08231306040518263ffffffff1660e01b815260040161121c9190613ab1565b60206040518083038186803b15801561123457600080fd5b505afa158015611248573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061126c91906138f2565b90506000846001600160a01b03166370a08231306040518263ffffffff1660e01b815260040161129c9190613ab1565b60206040518083038186803b1580156112b457600080fd5b505afa1580156112c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112ec91906138f2565b9050600061130a836dffffffffffffffffffffffffffff8716612b98565b90506000611328836dffffffffffffffffffffffffffff8716612b98565b905060004790506060338d85856040516020016113489493929190613b1a565b6040516020818303038152906040529050611361613586565b61136d8a600184612c7e565b6080860152604085015260208401528252606082015260006113a36dffffffffffffffffffffffffffff808c16908b1684610f98565b9050600054600014156113e0576113ca633b9aca006113c488888587611112565b90612b98565b9d506113db6000633b9aca00613386565b6113ef565b6113ec86868385611112565b9d505b506114019150610c9d90508247612b98565b995060008b116114235760405162461bcd60e51b815260040161091890613e4c565b61142d8c8c613386565b6114378585613152565b891561144757611447338b613258565b336001600160a01b03167f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f8484604051611482929190613fdd565b60405180910390a25050505050505050506001600a81905550915091565b60016020526000908152604090205481565b60046020526000908152604090205481565b6006805460408051602060026001851615610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190941693909304601f8101849004840282018401909252818152929183018282801561086f5780601f106108445761010080835404028352916020019161086f565b600080548061155657670de0b6b3a7640000915061158a565b600061156286866132e5565b9050600061157088866132e5565b90506115858561104d858161103e878761331f565b935050505b50949350505050565b6000806115fa6305f5e10061104d6305f5e10061104d876000015161104d6115cc8a608001516305f5e100612b9890919063ffffffff16565b610ff16115ea8c606001516305f5e100612b9890919063ffffffff16565b60208d0151610ff1908f906132e5565b9150826080015160001461110b576111086305f5e10061104d8560800151876132e590919063ffffffff16565b60008060006116346135b5565b600a546001146116565760405162461bcd60e51b815260040161091890613c00565b6000600a8190556007546008546040516370a0823160e01b81526001600160a01b0392831693919092169183906370a0823190611697903090600401613ab1565b60206040518083038186803b1580156116af57600080fd5b505afa1580156116c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116e791906138f2565b90506000826001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016117179190613ab1565b60206040518083038186803b15801561172f57600080fd5b505afa158015611743573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061176791906138f2565b905047600080611775610877565b91509150856001600160a01b03168d6001600160a01b031614156117b4576117ad856dffffffffffffffffffffffffffff8416612b98565b9a506117e8565b866001600160a01b03168d6001600160a01b03161415610c75576117ad846dffffffffffffffffffffffffffff8316612b98565b60008b116118085760405162461bcd60e51b815260040161091890613f85565b6060338e8e8e6040516020016118219493929190613ac5565b604051602081830303815290604052905061183a613586565b61184688600384612c7e565b608086015260408501526020840152825260608201526001600160a01b038f811690891614156118bc5761187a8d82611593565b8b526dffffffffffffffffffffffffffff8581166020808e01919091528301518351929e506118b292909161104d91908716906132e5565b60408b0152611927565b886001600160a01b03168f6001600160a01b03161415611927576118e08d82612874565b8b5260208201518251919d5061190b9161104d906dffffffffffffffffffffffffffff8716906132e5565b60208b01526dffffffffffffffffffffffffffff841660408b01525b611934610c9d8647612b98565b9a5061196a846dffffffffffffffffffffffffffff16846dffffffffffffffffffffffffffff168360000151846020015161153d565b60608b015250505050506001600160a01b03848116908a16148015906119a25750826001600160a01b0316896001600160a01b031614155b6119be5760405162461bcd60e51b815260040161091890613cdc565b6119c98a8a89612e4e565b845115611ab9576040517f80feb3980000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906380feb39890611a35908690600401613ab1565b60206040518083038186803b158015611a4d57600080fd5b505afa158015611a61573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a8591906138b7565b15611aa157611a9c848660005b6020020151612fb9565b611ab9565b611ab484848760005b6020020151613073565b600085525b6040516370a0823160e01b81526001600160a01b038516906370a0823190611ae5903090600401613ab1565b60206040518083038186803b158015611afd57600080fd5b505afa158015611b11573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b3591906138f2565b6040516370a0823160e01b81529092506001600160a01b038416906370a0823190611b64903090600401613ab1565b60206040518083038186803b158015611b7c57600080fd5b505afa158015611b90573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bb491906138f2565b9050611bc08282613152565b8515611bd057611bd03387613258565b886001600160a01b0316336001600160a01b03167f053d794b2310b8d186a24ae24a65ee066983a52a6efa6bd3df09a7601a3cb4f38a8a8e604051611c1793929190613feb565b60405180910390a350506001600a555093969295509093509150565b6000806000611c406135b5565b600a54600114611c625760405162461bcd60e51b815260040161091890613c00565b6000600a556007546008546001600160a01b039182169116611c82613586565b6040514790606090611c9e9033908e908e908e90602001613aef565b6040516020818303038152906040529050611cbb84600483612c7e565b60808801526040870152602086015284526060840152611cde610c9d8347612b98565b965050506000836001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401611d109190613ab1565b60206040518083038186803b158015611d2857600080fd5b505afa158015611d3c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d6091906138f2565b90506000836001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401611d909190613ab1565b60206040518083038186803b158015611da857600080fd5b505afa158015611dbc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611de091906138f2565b9050600080611ded610877565b91509150856001600160a01b03168e6001600160a01b03161415611e6957611e25846dffffffffffffffffffffffffffff8416612b98565b6dffffffffffffffffffffffffffff8381166020808c01919091528701518751929d50611e5b92909161104d91908516906132e5565b8860025b6020020152611ee4565b866001600160a01b03168e6001600160a01b03161415610c7557611e9d836dffffffffffffffffffffffffffff8316612b98565b60208601518651919c50611ec69161104d906dffffffffffffffffffffffffffff8516906132e5565b60208901526dffffffffffffffffffffffffffff8216886002611e5f565b60008b11611f045760405162461bcd60e51b815260040161091890613f85565b611f38826dffffffffffffffffffffffffffff16826dffffffffffffffffffffffffffff168760000151886020015161153d565b60608901525060009250829150506001600160a01b03848116908d161415611f8a57611f648b8461106d565b87529150611f728983612b98565b90508015611f8557611f85858b83612e4e565b611fcf565b846001600160a01b03168c6001600160a01b03161415611fcf57611fae8b846124f2565b87529150611fbc8983612b98565b90508015611fcf57611fcf848b83612e4e565b88821115611fef5760405162461bcd60e51b815260040161091890613ca5565b6000821161200f5760405162461bcd60e51b815260040161091890613d70565b5050826001600160a01b0316886001600160a01b0316141580156120455750816001600160a01b0316886001600160a01b031614155b6120615760405162461bcd60e51b815260040161091890613cdc565b88955061206f8a8988612e4e565b835115612153576040517f80feb3980000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906380feb398906120db908590600401613ab1565b60206040518083038186803b1580156120f357600080fd5b505afa158015612107573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061212b91906138b7565b156121415761213c83856000611a92565b612153565b61214e8383866000611aaa565b600084525b6040516370a0823160e01b81526000906001600160a01b038516906370a0823190612182903090600401613ab1565b60206040518083038186803b15801561219a57600080fd5b505afa1580156121ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121d291906138f2565b90506000836001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016122029190613ab1565b60206040518083038186803b15801561221a57600080fd5b505afa15801561222e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061225291906138f2565b905061225e8282613152565b861561226e5761226e3388613258565b5050876001600160a01b0316336001600160a01b03167f053d794b2310b8d186a24ae24a65ee066983a52a6efa6bd3df09a7601a3cb4f389898e6040516122b793929190613feb565b60405180910390a35050506001600a8190555093509350935093565b6040518060400160405280601081526020017f436f46695820506f6f6c20546f6b656e0000000000000000000000000000000081525081565b60006108b1338484612bda565b60008061232583610f66565b905061233385858386611112565b95945050505050565b600080548061235557670de0b6b3a76400009150611059565b6000610fce84606001516305f5e10061331f90919063ffffffff16565b633b9aca0081565b600a5460011461239c5760405162461bcd60e51b815260040161091890613c00565b6000600a556007546008546009546040516370a0823160e01b81526001600160a01b039384169390921691612451918491869161244c916dffffffffffffffffffffffffffff9091169084906370a08231906123fc903090600401613ab1565b60206040518083038186803b15801561241457600080fd5b505afa158015612428573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113c491906138f2565b612e4e565b6124b2818461244c6009600e9054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff16856001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016123fc9190613ab1565b50506001600a5550565b6009546000906108b5906dffffffffffffffffffffffffffff808216916e0100000000000000000000000000009004168461233c565b60008061254961251384608001516305f5e100612b9890919063ffffffff16565b61104d6305f5e10061104d876000015161104d6305f5e100610ff16115ea8c606001516305f5e10061331f90919063ffffffff16565b9150826080015160001461110b576111086305f5e10061104d61257d86606001516305f5e10061331f90919063ffffffff16565b61104d876020015161104d8960800151610ff16305f5e100610ff18d600001518d6132e590919063ffffffff16565b7f000000000000000000000000000000000000000000000000000000000000000081565b6008546001600160a01b031681565b428410156125ff5760405162461bcd60e51b815260040161091890613da7565b6003546001600160a01b03881660009081526004602090815260408083208054600181019091559051929392612660927f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9928d928d928d92918d9101613b9b565b60405160208183030381529060405280519060200120604051602001612687929190613a7b565b6040516020818303038152906040528051906020012090506000600182868686604051600081526020016040526040516126c49493929190613bcf565b6020604051602081039080840390855afa1580156126e6573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381161580159061271c5750886001600160a01b0316816001600160a01b0316145b6127385760405162461bcd60e51b815260040161091890613c37565b612743898989612b30565b505050505050505050565b60095460009081908190612788906dffffffffffffffffffffffffffff808216916e0100000000000000000000000000009004168661233c565b905060006127d96127aa86608001516305f5e100612b9890919063ffffffff16565b610ff16127c888606001516305f5e100612b9890919063ffffffff16565b6020890151610ff190818c896132e5565b905061280e6305f5e10061104d6305f5e10061104d896000015161104d670de0b6b3a76400008861334490919063ffffffff16565b9350846080015160001461284e5761284b6305f5e10061104d670de0b6b3a764000061104d8960800151610ff1888d6132e590919063ffffffff16565b92505b50509250929050565b600260209081526000928352604080842090915290825290205481565b6000806128d86305f5e10061104d61289d86606001516305f5e10061331f90919063ffffffff16565b61104d876020015161104d6128c38a608001516305f5e100612b9890919063ffffffff16565b8a51610ff1906305f5e1009082908f906132e5565b9150826080015160001461110b576111086305f5e10061104d61290c86606001516305f5e10061331f90919063ffffffff16565b61104d876020015161104d8960800151610ff16305f5e100610ff18d600001518f6132e590919063ffffffff16565b60095460009081908190612975906dffffffffffffffffffffffffffff808216916e0100000000000000000000000000009004168661233c565b90506129b36305f5e10061104d670de0b6b3a764000061104d6129a989608001516305f5e100612b9890919063ffffffff16565b610ff18b886132e5565b925083608001516000146129f3576129f06305f5e10061104d670de0b6b3a764000061104d8860800151610ff1878c6132e590919063ffffffff16565b91505b509250929050565b600a54600114612a1d5760405162461bcd60e51b815260040161091890613c00565b6000600a556007546040516370a0823160e01b8152612b29916001600160a01b0316906370a0823190612a54903090600401613ab1565b60206040518083038186803b158015612a6c57600080fd5b505afa158015612a80573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612aa491906138f2565b6008546040516370a0823160e01b81526001600160a01b03909116906370a0823190612ad4903090600401613ab1565b60206040518083038186803b158015612aec57600080fd5b505afa158015612b00573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b2491906138f2565b613152565b6001600a55565b6001600160a01b0380841660008181526002602090815260408083209487168084529490915290819020849055517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590612b8b908590613b92565b60405180910390a3505050565b6000610a8083836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250613407565b6001600160a01b038316600090815260016020526040902054612bfd9082612b98565b6001600160a01b038085166000908152600160205260408082209390935590841681522054612c2c908261331f565b6001600160a01b0380841660008181526001602052604090819020939093559151908516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90612b8b908590613b92565b60008060008060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316633018205f6040518163ffffffff1660e01b815260040160206040518083038186803b158015612cdf57600080fd5b505afa158015612cf3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d1791906136bc565b6001600160a01b0316638dba9329348a8a6004811115612d3357fe5b8a6040518563ffffffff1660e01b8152600401612d5293929190613b5c565b60a0604051808303818588803b158015612d6b57600080fd5b505af1158015612d7f573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190612da491906139f4565b939c929b5090995097509095509350505050565b6001600160a01b038216600090815260016020526040902054612ddb9082612b98565b6001600160a01b03831660009081526001602052604081209190915554612e029082612b98565b60009081556040516001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90612e42908590613b92565b60405180910390a35050565b604080518082018252601981527f7472616e7366657228616464726573732c75696e743235362900000000000000602090910152516000906060906001600160a01b038616907fa9059cbb2ab09eb219583f4a59a5d0623ade346d962bcd4e46b11da047c9049b90612ec69087908790602401613b43565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051612f319190613a5f565b6000604051808303816000865af19150503d8060008114612f6e576040519150601f19603f3d011682016040523d82523d6000602084013e612f73565b606091505b5091509150818015612f9d575080511580612f9d575080806020019051810190612f9d91906138b7565b6109905760405162461bcd60e51b815260040161091890613f17565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663e8a353926040518163ffffffff1660e01b815260040160206040518083038186803b15801561301457600080fd5b505afa158015613028573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061304c91906136bc565b90506001600160a01b038116613062575061306f565b61306d838284613433565b505b5050565b6040517f72b8cbcc0000000000000000000000000000000000000000000000000000000081526000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906372b8cbcc906130db908690600401613ab1565b60206040518083038186803b1580156130f357600080fd5b505afa158015613107573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061312b91906136bc565b90506001600160a01b038116613141575061306d565b61314c848284613433565b50505050565b6dffffffffffffffffffffffffffff821180159061317e57506dffffffffffffffffffffffffffff8111155b61319a5760405162461bcd60e51b815260040161091890613f4e565b600980547fffffffffffffffffffffffffffffffffffff0000000000000000000000000000166dffffffffffffffffffffffffffff848116919091177fffffffff0000000000000000000000000000ffffffffffffffffffffffffffff166e010000000000000000000000000000848316810291909117928390556040517f1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad19361324c93818116939091041690613fbc565b60405180910390a15050565b604080516000808252602082019092526001600160a01b0384169083906040516132829190613a5f565b60006040518083038185875af1925050503d80600081146132bf576040519150601f19603f3d011682016040523d82523d6000602084013e6132c4565b606091505b505090508061306d5760405162461bcd60e51b815260040161091890613eba565b6000826132f4575060006108b5565b8282028284828161330157fe5b0414610a805760405162461bcd60e51b815260040161091890613d13565b600082820183811015610a805760405162461bcd60e51b815260040161091890613c6e565b6000610a8083836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f0000000000008152506134d1565b600054613393908261331f565b60009081556001600160a01b0383168152600160205260409020546133b8908261331f565b6001600160a01b0383166000818152600160205260408082209390935591519091907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90612e42908590613b92565b6000818484111561342b5760405162461bcd60e51b81526004016109189190613bed565b505050900390565b6040516370a0823160e01b81526000906001600160a01b038516906370a0823190613462903090600401613ab1565b60206040518083038186803b15801561347a57600080fd5b505afa15801561348e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134b291906138f2565b9050808211156134c0578091505b811561314c5761314c848484612e4e565b600081836134f25760405162461bcd60e51b81526004016109189190613bed565b5060008385816134fe57fe5b0495945050505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061354957805160ff1916838001178555613576565b82800160010185558215613576579182015b8281111561357657825182559160200191906001019061355b565b506135829291506135d3565b5090565b6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b60405180608001604052806004906020820280368337509192915050565b5b8082111561358257600081556001016135d4565b600082601f8301126135f8578081fd5b813567ffffffffffffffff81111561360e578182fd5b6136216020601f19601f84011601614062565b915080825283602082850101111561363857600080fd5b8060208401602084013760009082016020015292915050565b600060a08284031215613662578081fd5b61366c60a0614062565b9050813581526020820135602082015260408201356040820152606082013560608201526080820135608082015292915050565b6000602082840312156136b1578081fd5b8135610a80816140b5565b6000602082840312156136cd578081fd5b8151610a80816140b5565b600080604083850312156136ea578081fd5b82356136f5816140b5565b91506020830135613705816140b5565b809150509250929050565b60008060008060808587031215613725578182fd5b8435613730816140b5565b93506020850135613740816140b5565b9250604085013567ffffffffffffffff8082111561375c578384fd5b613768888389016135e8565b9350606087013591508082111561377d578283fd5b5061378a878288016135e8565b91505092959194509250565b6000806000606084860312156137aa578283fd5b83356137b5816140b5565b925060208401356137c5816140b5565b929592945050506040919091013590565b600080600080600080600060e0888a0312156137f0578283fd5b87356137fb816140b5565b9650602088013561380b816140b5565b95506040880135945060608801359350608088013560ff8116811461382e578384fd5b9699959850939692959460a0840135945060c09093013592915050565b6000806040838503121561385d578182fd5b8235613868816140b5565b946020939093013593505050565b60008060006060848603121561388a578283fd5b8335613895816140b5565b92506020840135915060408401356138ac816140b5565b809150509250925092565b6000602082840312156138c8578081fd5b81518015158114610a80578182fd5b600060a082840312156138e8578081fd5b610a808383613651565b600060208284031215613903578081fd5b5051919050565b60008060c0838503121561391c578182fd5b823591506111088460208501613651565b6000806040838503121561393f578182fd5b50508035926020909101359150565b600080600060e08486031215613962578081fd5b833592506020840135915061397a8560408601613651565b90509250925092565b6000806000806101008587031215613999578182fd5b8435935060208501359250604085013591506139b88660608701613651565b905092959194509250565b600080600080608085870312156139d8578182fd5b5050823594602084013594506040840135936060013592509050565b600080600080600060a08688031215613a0b578283fd5b5050835160208501516040860151606087015160809097015192989197509594509092509050565b60008151808452613a4b816020860160208601614089565b601f01601f19169290920160200192915050565b60008251613a71818460208701614089565b9190910192915050565b7f190100000000000000000000000000000000000000000000000000000000000081526002810192909252602282015260420190565b6001600160a01b0391909116815260200190565b6001600160a01b039485168152928416602084015292166040820152606081019190915260800190565b6001600160a01b03948516815292841660208401526040830191909152909116606082015260800190565b6001600160a01b0394851681529290931660208301526040820152606081019190915260800190565b6001600160a01b03929092168252602082015260400190565b60006001600160a01b038516825260ff84166020830152606060408301526123336060830184613a33565b901515815260200190565b90815260200190565b9586526001600160a01b0394851660208701529290931660408501526060840152608083019190915260a082015260c00190565b93845260ff9290921660208401526040830152606082015260800190565b600060208252610a806020830184613a33565b6020808252600d908201527f43506169723a204c4f434b454400000000000000000000000000000000000000604082015260600190565b60208082526019908201527f4345524332303a20494e56414c49445f5349474e415455524500000000000000604082015260600190565b6020808252601b908201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604082015260600190565b6020808252601c908201527f43506169723a20696e73756666696369656e7420616d6f756e74496e00000000604082015260600190565b60208082526011908201527f43506169723a20494e56414c49445f544f000000000000000000000000000000604082015260600190565b60208082526021908201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f60408201527f7700000000000000000000000000000000000000000000000000000000000000606082015260800190565b6020808252601c908201527f43506169723a2077726f6e6720616d6f756e74496e206e656564656400000000604082015260600190565b6020808252600f908201527f4345524332303a20455850495245440000000000000000000000000000000000604082015260600190565b60208082526015908201527f43506169723a2077726f6e67206f7574546f6b656e0000000000000000000000604082015260600190565b6020808252601d908201527f43506169723a2053484f52545f4c49515549444954595f4255524e4544000000604082015260600190565b6020808252601d908201527f43506169723a2053484f52545f4c49515549444954595f4d494e544544000000604082015260600190565b60208082526010908201527f43506169723a20464f5242494444454e00000000000000000000000000000000604082015260600190565b60208082526023908201527f5472616e7366657248656c7065723a204554485f5452414e534645525f46414960408201527f4c45440000000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526016908201527f43506169723a205452414e534645525f4641494c454400000000000000000000604082015260600190565b6020808252600f908201527f43506169723a204f564552464c4f570000000000000000000000000000000000604082015260600190565b60208082526015908201527f43506169723a2077726f6e6720616d6f756e74496e0000000000000000000000604082015260600190565b6dffffffffffffffffffffffffffff92831681529116602082015260400190565b918252602082015260400190565b92835260208301919091526001600160a01b0316604082015260600190565b600060e08201905085825260208581840152846040840152606083018460005b60048110156140475781518352918301919083019060010161402a565b5050505095945050505050565b60ff91909116815260200190565b60405181810167ffffffffffffffff8111828210171561408157600080fd5b604052919050565b60005b838110156140a457818101518382015260200161408c565b8381111561314c5750506000910152565b6001600160a01b03811681146140ca57600080fd5b5056fea26469706673582212204d9c04fecc367b0b983af1eb4467f748ca149b973eebf1723301659a88c0b77264736f6c634300060c0033

Deployed Bytecode

0x6080604052600436106102e05760003560e01c806395d89b4111610184578063bc25cf77116100d6578063d505accf1161008a578063e5eac39011610064578063e5eac39014610776578063eb77289914610796578063fff6cae9146107b6576102e7565b8063d505accf14610716578063dd2e0a3114610736578063dd62ed3e14610756576102e7565b8063bf36aac2116100bb578063bf36aac2146106cc578063c45a0155146106ec578063d21220a714610701576102e7565b8063bc25cf771461068c578063be5b32f8146106ac576102e7565b8063a661dbda11610138578063b2874f8711610112578063b2874f8714610637578063b72bda4a14610657578063ba9a7a5614610677576102e7565b8063a661dbda14610602578063a9059cbb14610617578063aaf07e9214610465576102e7565b80639c673c52116101695780639c673c52146105ac5780639f44296a146105cc578063a12db6cc146105ef576102e7565b806395d89b411461057757806399f45e481461058c576102e7565b8063313ce5671161023d5780634f6ec2dd116101f15780636a627842116101cb5780636a6278421461052457806370a08231146105375780637ecebe0014610557576102e7565b80634f6ec2dd146104cf5780635ad6bd30146104e4578063663eda1f14610504576102e7565b80633644e515116102225780633644e5151461047a57806339818b661461048f57806349a23da0146104af576102e7565b8063313ce567146104435780633298613114610465576102e7565b80632016a0d21161029457806324334be81161027957806324334be8146103ed57806327fc84a31461040d57806330adf81f1461042e576102e7565b80632016a0d2146103ab57806323b872dd146103cd576102e7565b8063095ea7b3116102c5578063095ea7b31461033a5780630dfe16811461036757806318160ddd14610389576102e7565b806306fdde03146102ec5780630902f1ac14610317576102e7565b366102e757005b600080fd5b3480156102f857600080fd5b506103016107cb565b60405161030e9190613bed565b60405180910390f35b34801561032357600080fd5b5061032c610877565b60405161030e929190613fbc565b34801561034657600080fd5b5061035a61035536600461384b565b6108a4565b60405161030e9190613b87565b34801561037357600080fd5b5061037c6108bb565b60405161030e9190613ab1565b34801561039557600080fd5b5061039e6108ca565b60405161030e9190613b92565b3480156103b757600080fd5b506103cb6103c6366004613710565b6108d0565b005b3480156103d957600080fd5b5061035a6103e8366004613796565b610997565b3480156103f957600080fd5b5061039e61040836600461392d565b610a49565b61042061041b3660046136d8565b610a87565b60405161030e929190613fdd565b34801561043a57600080fd5b5061039e610f2f565b34801561044f57600080fd5b50610458610f53565b60405161030e9190614054565b34801561047157600080fd5b5061039e610f58565b34801561048657600080fd5b5061039e610f60565b34801561049b57600080fd5b5061039e6104aa3660046138d7565b610f66565b3480156104bb57600080fd5b5061039e6104ca36600461394e565b610f98565b3480156104db57600080fd5b5061039e611061565b3480156104f057600080fd5b506104206104ff36600461390a565b61106d565b34801561051057600080fd5b5061039e61051f366004613983565b611112565b6104206105323660046136a0565b6111a0565b34801561054357600080fd5b5061039e6105523660046136a0565b6114a0565b34801561056357600080fd5b5061039e6105723660046136a0565b6114b2565b34801561058357600080fd5b506103016114c4565b34801561059857600080fd5b5061039e6105a73660046139c3565b61153d565b3480156105b857600080fd5b506104206105c736600461390a565b611593565b6105df6105da3660046136d8565b611627565b60405161030e949392919061400a565b6105df6105fd366004613876565b611c33565b34801561060e57600080fd5b506103016122d3565b34801561062357600080fd5b5061035a61063236600461384b565b61230c565b34801561064357600080fd5b5061039e61065236600461394e565b612319565b34801561066357600080fd5b5061039e61067236600461394e565b61233c565b34801561068357600080fd5b5061039e612372565b34801561069857600080fd5b506103cb6106a73660046136a0565b61237a565b3480156106b857600080fd5b5061039e6106c73660046138d7565b6124bc565b3480156106d857600080fd5b506104206106e736600461390a565b6124f2565b3480156106f857600080fd5b5061037c6125ac565b34801561070d57600080fd5b5061037c6125d0565b34801561072257600080fd5b506103cb6107313660046137d6565b6125df565b34801561074257600080fd5b5061042061075136600461390a565b61274e565b34801561076257600080fd5b5061039e6107713660046136d8565b612857565b34801561078257600080fd5b5061042061079136600461390a565b612874565b3480156107a257600080fd5b506104206107b136600461390a565b61293b565b3480156107c257600080fd5b506103cb6129fb565b6005805460408051602060026001851615610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190941693909304601f8101849004840282018401909252818152929183018282801561086f5780601f106108445761010080835404028352916020019161086f565b820191906000526020600020905b81548152906001019060200180831161085257829003601f168201915b505050505081565b6009546dffffffffffffffffffffffffffff808216926e0100000000000000000000000000009092041690565b60006108b1338484612b30565b5060015b92915050565b6007546001600160a01b031681565b60005481565b336001600160a01b037f00000000000000000000000066c64ecc3a6014733325a8f2ebee46b4ca3ed55016146109215760405162461bcd60e51b815260040161091890613e83565b60405180910390fd5b600780546001600160a01b038087167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316179092556008805492861692909116919091179055815161097c906005906020850190613508565b508051610990906006906020840190613508565b5050505050565b6001600160a01b03831660009081526002602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff14610a34576001600160a01b0384166000908152600260209081526040808320338452909152902054610a0f9083612b98565b6001600160a01b03851660009081526002602090815260408083203384529091529020555b610a3f848484612bda565b5060019392505050565b600954600090610a80906dffffffffffffffffffffffffffff808216916e010000000000000000000000000000900416858561153d565b9392505050565b600080600a54600114610aac5760405162461bcd60e51b815260040161091890613c00565b6000600a8190556007546008546040516370a0823160e01b81526001600160a01b0392831693919092169183906370a0823190610aed903090600401613ab1565b60206040518083038186803b158015610b0557600080fd5b505afa158015610b19573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b3d91906138f2565b90506000826001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401610b6d9190613ab1565b60206040518083038186803b158015610b8557600080fd5b505afa158015610b99573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bbd91906138f2565b30600090815260016020908152604080832054905193945092479291606091610bee9133918f918f91899101613ac5565b6040516020818303038152906040529050610c07613586565b610c1388600284612c7e565b608086015260408501526020840152825260608201526001600160a01b038d8116908a161415610c5157610c47858261293b565b909b509250610c8d565b876001600160a01b03168d6001600160a01b03161415610c7557610c47858261274e565b60405162461bcd60e51b815260040161091890613dde565b50610ca49050610c9d8347612b98565b3490612b98565b975060008911610cc65760405162461bcd60e51b815260040161091890613e15565b610cd03084612db8565b610cdb8b8b8b612e4e565b8015610db5576040517f80feb3980000000000000000000000000000000000000000000000000000000081526001600160a01b037f00000000000000000000000066c64ecc3a6014733325a8f2ebee46b4ca3ed55016906380feb39890610d46908990600401613ab1565b60206040518083038186803b158015610d5e57600080fd5b505afa158015610d72573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d9691906138b7565b15610daa57610da58782612fb9565b610db5565b610db5878783613073565b6040516370a0823160e01b81526001600160a01b038816906370a0823190610de1903090600401613ab1565b60206040518083038186803b158015610df957600080fd5b505afa158015610e0d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e3191906138f2565b6040516370a0823160e01b81529095506001600160a01b038716906370a0823190610e60903090600401613ab1565b60206040518083038186803b158015610e7857600080fd5b505afa158015610e8c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eb091906138f2565b9350610ebc8585613152565b8715610ecc57610ecc3389613258565b896001600160a01b0316336001600160a01b03167f3dd1df88dc92e2788892542d81f999d720a44b4c127065d45c128f4f59fdc3738d8c604051610f11929190613b43565b60405180910390a3505050505050506001600a819055509250929050565b7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b601281565b6305f5e10081565b60035481565b6009546000906108b5906dffffffffffffffffffffffffffff808216916e010000000000000000000000000000900416845b6000805480610fb157670de0b6b3a76400009150611059565b6000610fce84606001516305f5e100612b9890919063ffffffff16565b90506000610ff76305f5e100610ff18760000151896132e590919063ffffffff16565b906132e5565b9050600061101683610ff188602001518b6132e590919063ffffffff16565b90506110538361104d886020015161104d8861104d61103e888a61331f90919063ffffffff16565b670de0b6b3a7640000906132e5565b90613344565b94505050505b509392505050565b670de0b6b3a764000081565b6000806110db61108e84608001516305f5e100612b9890919063ffffffff16565b61104d6110ac86606001516305f5e100612b9890919063ffffffff16565b61104d876020015161104d6305f5e100610ff16305f5e100610ff18d600001518f6132e590919063ffffffff16565b9150826080015160001461110b576111086305f5e10061104d8560800151856132e590919063ffffffff16565b90505b9250929050565b60008061112b8461104d88670de0b6b3a76400006132e5565b9050600061115a6305f5e100610ff18660000151610ff1670de0b6b3a76400008b6132e590919063ffffffff16565b905061119561118e61117d86606001516305f5e10061331f90919063ffffffff16565b602087015161104d9081868b613344565b839061331f565b979650505050505050565b600080600a546001146111c55760405162461bcd60e51b815260040161091890613c00565b6000600a8190556007546008546001600160a01b0391821692911690806111ea610877565b915091506000846001600160a01b03166370a08231306040518263ffffffff1660e01b815260040161121c9190613ab1565b60206040518083038186803b15801561123457600080fd5b505afa158015611248573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061126c91906138f2565b90506000846001600160a01b03166370a08231306040518263ffffffff1660e01b815260040161129c9190613ab1565b60206040518083038186803b1580156112b457600080fd5b505afa1580156112c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112ec91906138f2565b9050600061130a836dffffffffffffffffffffffffffff8716612b98565b90506000611328836dffffffffffffffffffffffffffff8716612b98565b905060004790506060338d85856040516020016113489493929190613b1a565b6040516020818303038152906040529050611361613586565b61136d8a600184612c7e565b6080860152604085015260208401528252606082015260006113a36dffffffffffffffffffffffffffff808c16908b1684610f98565b9050600054600014156113e0576113ca633b9aca006113c488888587611112565b90612b98565b9d506113db6000633b9aca00613386565b6113ef565b6113ec86868385611112565b9d505b506114019150610c9d90508247612b98565b995060008b116114235760405162461bcd60e51b815260040161091890613e4c565b61142d8c8c613386565b6114378585613152565b891561144757611447338b613258565b336001600160a01b03167f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f8484604051611482929190613fdd565b60405180910390a25050505050505050506001600a81905550915091565b60016020526000908152604090205481565b60046020526000908152604090205481565b6006805460408051602060026001851615610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190941693909304601f8101849004840282018401909252818152929183018282801561086f5780601f106108445761010080835404028352916020019161086f565b600080548061155657670de0b6b3a7640000915061158a565b600061156286866132e5565b9050600061157088866132e5565b90506115858561104d858161103e878761331f565b935050505b50949350505050565b6000806115fa6305f5e10061104d6305f5e10061104d876000015161104d6115cc8a608001516305f5e100612b9890919063ffffffff16565b610ff16115ea8c606001516305f5e100612b9890919063ffffffff16565b60208d0151610ff1908f906132e5565b9150826080015160001461110b576111086305f5e10061104d8560800151876132e590919063ffffffff16565b60008060006116346135b5565b600a546001146116565760405162461bcd60e51b815260040161091890613c00565b6000600a8190556007546008546040516370a0823160e01b81526001600160a01b0392831693919092169183906370a0823190611697903090600401613ab1565b60206040518083038186803b1580156116af57600080fd5b505afa1580156116c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116e791906138f2565b90506000826001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016117179190613ab1565b60206040518083038186803b15801561172f57600080fd5b505afa158015611743573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061176791906138f2565b905047600080611775610877565b91509150856001600160a01b03168d6001600160a01b031614156117b4576117ad856dffffffffffffffffffffffffffff8416612b98565b9a506117e8565b866001600160a01b03168d6001600160a01b03161415610c75576117ad846dffffffffffffffffffffffffffff8316612b98565b60008b116118085760405162461bcd60e51b815260040161091890613f85565b6060338e8e8e6040516020016118219493929190613ac5565b604051602081830303815290604052905061183a613586565b61184688600384612c7e565b608086015260408501526020840152825260608201526001600160a01b038f811690891614156118bc5761187a8d82611593565b8b526dffffffffffffffffffffffffffff8581166020808e01919091528301518351929e506118b292909161104d91908716906132e5565b60408b0152611927565b886001600160a01b03168f6001600160a01b03161415611927576118e08d82612874565b8b5260208201518251919d5061190b9161104d906dffffffffffffffffffffffffffff8716906132e5565b60208b01526dffffffffffffffffffffffffffff841660408b01525b611934610c9d8647612b98565b9a5061196a846dffffffffffffffffffffffffffff16846dffffffffffffffffffffffffffff168360000151846020015161153d565b60608b015250505050506001600160a01b03848116908a16148015906119a25750826001600160a01b0316896001600160a01b031614155b6119be5760405162461bcd60e51b815260040161091890613cdc565b6119c98a8a89612e4e565b845115611ab9576040517f80feb3980000000000000000000000000000000000000000000000000000000081526001600160a01b037f00000000000000000000000066c64ecc3a6014733325a8f2ebee46b4ca3ed55016906380feb39890611a35908690600401613ab1565b60206040518083038186803b158015611a4d57600080fd5b505afa158015611a61573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a8591906138b7565b15611aa157611a9c848660005b6020020151612fb9565b611ab9565b611ab484848760005b6020020151613073565b600085525b6040516370a0823160e01b81526001600160a01b038516906370a0823190611ae5903090600401613ab1565b60206040518083038186803b158015611afd57600080fd5b505afa158015611b11573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b3591906138f2565b6040516370a0823160e01b81529092506001600160a01b038416906370a0823190611b64903090600401613ab1565b60206040518083038186803b158015611b7c57600080fd5b505afa158015611b90573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bb491906138f2565b9050611bc08282613152565b8515611bd057611bd03387613258565b886001600160a01b0316336001600160a01b03167f053d794b2310b8d186a24ae24a65ee066983a52a6efa6bd3df09a7601a3cb4f38a8a8e604051611c1793929190613feb565b60405180910390a350506001600a555093969295509093509150565b6000806000611c406135b5565b600a54600114611c625760405162461bcd60e51b815260040161091890613c00565b6000600a556007546008546001600160a01b039182169116611c82613586565b6040514790606090611c9e9033908e908e908e90602001613aef565b6040516020818303038152906040529050611cbb84600483612c7e565b60808801526040870152602086015284526060840152611cde610c9d8347612b98565b965050506000836001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401611d109190613ab1565b60206040518083038186803b158015611d2857600080fd5b505afa158015611d3c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d6091906138f2565b90506000836001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401611d909190613ab1565b60206040518083038186803b158015611da857600080fd5b505afa158015611dbc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611de091906138f2565b9050600080611ded610877565b91509150856001600160a01b03168e6001600160a01b03161415611e6957611e25846dffffffffffffffffffffffffffff8416612b98565b6dffffffffffffffffffffffffffff8381166020808c01919091528701518751929d50611e5b92909161104d91908516906132e5565b8860025b6020020152611ee4565b866001600160a01b03168e6001600160a01b03161415610c7557611e9d836dffffffffffffffffffffffffffff8316612b98565b60208601518651919c50611ec69161104d906dffffffffffffffffffffffffffff8516906132e5565b60208901526dffffffffffffffffffffffffffff8216886002611e5f565b60008b11611f045760405162461bcd60e51b815260040161091890613f85565b611f38826dffffffffffffffffffffffffffff16826dffffffffffffffffffffffffffff168760000151886020015161153d565b60608901525060009250829150506001600160a01b03848116908d161415611f8a57611f648b8461106d565b87529150611f728983612b98565b90508015611f8557611f85858b83612e4e565b611fcf565b846001600160a01b03168c6001600160a01b03161415611fcf57611fae8b846124f2565b87529150611fbc8983612b98565b90508015611fcf57611fcf848b83612e4e565b88821115611fef5760405162461bcd60e51b815260040161091890613ca5565b6000821161200f5760405162461bcd60e51b815260040161091890613d70565b5050826001600160a01b0316886001600160a01b0316141580156120455750816001600160a01b0316886001600160a01b031614155b6120615760405162461bcd60e51b815260040161091890613cdc565b88955061206f8a8988612e4e565b835115612153576040517f80feb3980000000000000000000000000000000000000000000000000000000081526001600160a01b037f00000000000000000000000066c64ecc3a6014733325a8f2ebee46b4ca3ed55016906380feb398906120db908590600401613ab1565b60206040518083038186803b1580156120f357600080fd5b505afa158015612107573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061212b91906138b7565b156121415761213c83856000611a92565b612153565b61214e8383866000611aaa565b600084525b6040516370a0823160e01b81526000906001600160a01b038516906370a0823190612182903090600401613ab1565b60206040518083038186803b15801561219a57600080fd5b505afa1580156121ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121d291906138f2565b90506000836001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016122029190613ab1565b60206040518083038186803b15801561221a57600080fd5b505afa15801561222e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061225291906138f2565b905061225e8282613152565b861561226e5761226e3388613258565b5050876001600160a01b0316336001600160a01b03167f053d794b2310b8d186a24ae24a65ee066983a52a6efa6bd3df09a7601a3cb4f389898e6040516122b793929190613feb565b60405180910390a35050506001600a8190555093509350935093565b6040518060400160405280601081526020017f436f46695820506f6f6c20546f6b656e0000000000000000000000000000000081525081565b60006108b1338484612bda565b60008061232583610f66565b905061233385858386611112565b95945050505050565b600080548061235557670de0b6b3a76400009150611059565b6000610fce84606001516305f5e10061331f90919063ffffffff16565b633b9aca0081565b600a5460011461239c5760405162461bcd60e51b815260040161091890613c00565b6000600a556007546008546009546040516370a0823160e01b81526001600160a01b039384169390921691612451918491869161244c916dffffffffffffffffffffffffffff9091169084906370a08231906123fc903090600401613ab1565b60206040518083038186803b15801561241457600080fd5b505afa158015612428573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113c491906138f2565b612e4e565b6124b2818461244c6009600e9054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff16856001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016123fc9190613ab1565b50506001600a5550565b6009546000906108b5906dffffffffffffffffffffffffffff808216916e0100000000000000000000000000009004168461233c565b60008061254961251384608001516305f5e100612b9890919063ffffffff16565b61104d6305f5e10061104d876000015161104d6305f5e100610ff16115ea8c606001516305f5e10061331f90919063ffffffff16565b9150826080015160001461110b576111086305f5e10061104d61257d86606001516305f5e10061331f90919063ffffffff16565b61104d876020015161104d8960800151610ff16305f5e100610ff18d600001518d6132e590919063ffffffff16565b7f00000000000000000000000066c64ecc3a6014733325a8f2ebee46b4ca3ed55081565b6008546001600160a01b031681565b428410156125ff5760405162461bcd60e51b815260040161091890613da7565b6003546001600160a01b03881660009081526004602090815260408083208054600181019091559051929392612660927f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9928d928d928d92918d9101613b9b565b60405160208183030381529060405280519060200120604051602001612687929190613a7b565b6040516020818303038152906040528051906020012090506000600182868686604051600081526020016040526040516126c49493929190613bcf565b6020604051602081039080840390855afa1580156126e6573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381161580159061271c5750886001600160a01b0316816001600160a01b0316145b6127385760405162461bcd60e51b815260040161091890613c37565b612743898989612b30565b505050505050505050565b60095460009081908190612788906dffffffffffffffffffffffffffff808216916e0100000000000000000000000000009004168661233c565b905060006127d96127aa86608001516305f5e100612b9890919063ffffffff16565b610ff16127c888606001516305f5e100612b9890919063ffffffff16565b6020890151610ff190818c896132e5565b905061280e6305f5e10061104d6305f5e10061104d896000015161104d670de0b6b3a76400008861334490919063ffffffff16565b9350846080015160001461284e5761284b6305f5e10061104d670de0b6b3a764000061104d8960800151610ff1888d6132e590919063ffffffff16565b92505b50509250929050565b600260209081526000928352604080842090915290825290205481565b6000806128d86305f5e10061104d61289d86606001516305f5e10061331f90919063ffffffff16565b61104d876020015161104d6128c38a608001516305f5e100612b9890919063ffffffff16565b8a51610ff1906305f5e1009082908f906132e5565b9150826080015160001461110b576111086305f5e10061104d61290c86606001516305f5e10061331f90919063ffffffff16565b61104d876020015161104d8960800151610ff16305f5e100610ff18d600001518f6132e590919063ffffffff16565b60095460009081908190612975906dffffffffffffffffffffffffffff808216916e0100000000000000000000000000009004168661233c565b90506129b36305f5e10061104d670de0b6b3a764000061104d6129a989608001516305f5e100612b9890919063ffffffff16565b610ff18b886132e5565b925083608001516000146129f3576129f06305f5e10061104d670de0b6b3a764000061104d8860800151610ff1878c6132e590919063ffffffff16565b91505b509250929050565b600a54600114612a1d5760405162461bcd60e51b815260040161091890613c00565b6000600a556007546040516370a0823160e01b8152612b29916001600160a01b0316906370a0823190612a54903090600401613ab1565b60206040518083038186803b158015612a6c57600080fd5b505afa158015612a80573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612aa491906138f2565b6008546040516370a0823160e01b81526001600160a01b03909116906370a0823190612ad4903090600401613ab1565b60206040518083038186803b158015612aec57600080fd5b505afa158015612b00573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b2491906138f2565b613152565b6001600a55565b6001600160a01b0380841660008181526002602090815260408083209487168084529490915290819020849055517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590612b8b908590613b92565b60405180910390a3505050565b6000610a8083836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250613407565b6001600160a01b038316600090815260016020526040902054612bfd9082612b98565b6001600160a01b038085166000908152600160205260408082209390935590841681522054612c2c908261331f565b6001600160a01b0380841660008181526001602052604090819020939093559151908516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90612b8b908590613b92565b60008060008060007f00000000000000000000000066c64ecc3a6014733325a8f2ebee46b4ca3ed5506001600160a01b0316633018205f6040518163ffffffff1660e01b815260040160206040518083038186803b158015612cdf57600080fd5b505afa158015612cf3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d1791906136bc565b6001600160a01b0316638dba9329348a8a6004811115612d3357fe5b8a6040518563ffffffff1660e01b8152600401612d5293929190613b5c565b60a0604051808303818588803b158015612d6b57600080fd5b505af1158015612d7f573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190612da491906139f4565b939c929b5090995097509095509350505050565b6001600160a01b038216600090815260016020526040902054612ddb9082612b98565b6001600160a01b03831660009081526001602052604081209190915554612e029082612b98565b60009081556040516001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90612e42908590613b92565b60405180910390a35050565b604080518082018252601981527f7472616e7366657228616464726573732c75696e743235362900000000000000602090910152516000906060906001600160a01b038616907fa9059cbb2ab09eb219583f4a59a5d0623ade346d962bcd4e46b11da047c9049b90612ec69087908790602401613b43565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051612f319190613a5f565b6000604051808303816000865af19150503d8060008114612f6e576040519150601f19603f3d011682016040523d82523d6000602084013e612f73565b606091505b5091509150818015612f9d575080511580612f9d575080806020019051810190612f9d91906138b7565b6109905760405162461bcd60e51b815260040161091890613f17565b60007f00000000000000000000000066c64ecc3a6014733325a8f2ebee46b4ca3ed5506001600160a01b031663e8a353926040518163ffffffff1660e01b815260040160206040518083038186803b15801561301457600080fd5b505afa158015613028573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061304c91906136bc565b90506001600160a01b038116613062575061306f565b61306d838284613433565b505b5050565b6040517f72b8cbcc0000000000000000000000000000000000000000000000000000000081526000906001600160a01b037f00000000000000000000000066c64ecc3a6014733325a8f2ebee46b4ca3ed55016906372b8cbcc906130db908690600401613ab1565b60206040518083038186803b1580156130f357600080fd5b505afa158015613107573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061312b91906136bc565b90506001600160a01b038116613141575061306d565b61314c848284613433565b50505050565b6dffffffffffffffffffffffffffff821180159061317e57506dffffffffffffffffffffffffffff8111155b61319a5760405162461bcd60e51b815260040161091890613f4e565b600980547fffffffffffffffffffffffffffffffffffff0000000000000000000000000000166dffffffffffffffffffffffffffff848116919091177fffffffff0000000000000000000000000000ffffffffffffffffffffffffffff166e010000000000000000000000000000848316810291909117928390556040517f1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad19361324c93818116939091041690613fbc565b60405180910390a15050565b604080516000808252602082019092526001600160a01b0384169083906040516132829190613a5f565b60006040518083038185875af1925050503d80600081146132bf576040519150601f19603f3d011682016040523d82523d6000602084013e6132c4565b606091505b505090508061306d5760405162461bcd60e51b815260040161091890613eba565b6000826132f4575060006108b5565b8282028284828161330157fe5b0414610a805760405162461bcd60e51b815260040161091890613d13565b600082820183811015610a805760405162461bcd60e51b815260040161091890613c6e565b6000610a8083836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f0000000000008152506134d1565b600054613393908261331f565b60009081556001600160a01b0383168152600160205260409020546133b8908261331f565b6001600160a01b0383166000818152600160205260408082209390935591519091907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90612e42908590613b92565b6000818484111561342b5760405162461bcd60e51b81526004016109189190613bed565b505050900390565b6040516370a0823160e01b81526000906001600160a01b038516906370a0823190613462903090600401613ab1565b60206040518083038186803b15801561347a57600080fd5b505afa15801561348e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134b291906138f2565b9050808211156134c0578091505b811561314c5761314c848484612e4e565b600081836134f25760405162461bcd60e51b81526004016109189190613bed565b5060008385816134fe57fe5b0495945050505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061354957805160ff1916838001178555613576565b82800160010185558215613576579182015b8281111561357657825182559160200191906001019061355b565b506135829291506135d3565b5090565b6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b60405180608001604052806004906020820280368337509192915050565b5b8082111561358257600081556001016135d4565b600082601f8301126135f8578081fd5b813567ffffffffffffffff81111561360e578182fd5b6136216020601f19601f84011601614062565b915080825283602082850101111561363857600080fd5b8060208401602084013760009082016020015292915050565b600060a08284031215613662578081fd5b61366c60a0614062565b9050813581526020820135602082015260408201356040820152606082013560608201526080820135608082015292915050565b6000602082840312156136b1578081fd5b8135610a80816140b5565b6000602082840312156136cd578081fd5b8151610a80816140b5565b600080604083850312156136ea578081fd5b82356136f5816140b5565b91506020830135613705816140b5565b809150509250929050565b60008060008060808587031215613725578182fd5b8435613730816140b5565b93506020850135613740816140b5565b9250604085013567ffffffffffffffff8082111561375c578384fd5b613768888389016135e8565b9350606087013591508082111561377d578283fd5b5061378a878288016135e8565b91505092959194509250565b6000806000606084860312156137aa578283fd5b83356137b5816140b5565b925060208401356137c5816140b5565b929592945050506040919091013590565b600080600080600080600060e0888a0312156137f0578283fd5b87356137fb816140b5565b9650602088013561380b816140b5565b95506040880135945060608801359350608088013560ff8116811461382e578384fd5b9699959850939692959460a0840135945060c09093013592915050565b6000806040838503121561385d578182fd5b8235613868816140b5565b946020939093013593505050565b60008060006060848603121561388a578283fd5b8335613895816140b5565b92506020840135915060408401356138ac816140b5565b809150509250925092565b6000602082840312156138c8578081fd5b81518015158114610a80578182fd5b600060a082840312156138e8578081fd5b610a808383613651565b600060208284031215613903578081fd5b5051919050565b60008060c0838503121561391c578182fd5b823591506111088460208501613651565b6000806040838503121561393f578182fd5b50508035926020909101359150565b600080600060e08486031215613962578081fd5b833592506020840135915061397a8560408601613651565b90509250925092565b6000806000806101008587031215613999578182fd5b8435935060208501359250604085013591506139b88660608701613651565b905092959194509250565b600080600080608085870312156139d8578182fd5b5050823594602084013594506040840135936060013592509050565b600080600080600060a08688031215613a0b578283fd5b5050835160208501516040860151606087015160809097015192989197509594509092509050565b60008151808452613a4b816020860160208601614089565b601f01601f19169290920160200192915050565b60008251613a71818460208701614089565b9190910192915050565b7f190100000000000000000000000000000000000000000000000000000000000081526002810192909252602282015260420190565b6001600160a01b0391909116815260200190565b6001600160a01b039485168152928416602084015292166040820152606081019190915260800190565b6001600160a01b03948516815292841660208401526040830191909152909116606082015260800190565b6001600160a01b0394851681529290931660208301526040820152606081019190915260800190565b6001600160a01b03929092168252602082015260400190565b60006001600160a01b038516825260ff84166020830152606060408301526123336060830184613a33565b901515815260200190565b90815260200190565b9586526001600160a01b0394851660208701529290931660408501526060840152608083019190915260a082015260c00190565b93845260ff9290921660208401526040830152606082015260800190565b600060208252610a806020830184613a33565b6020808252600d908201527f43506169723a204c4f434b454400000000000000000000000000000000000000604082015260600190565b60208082526019908201527f4345524332303a20494e56414c49445f5349474e415455524500000000000000604082015260600190565b6020808252601b908201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604082015260600190565b6020808252601c908201527f43506169723a20696e73756666696369656e7420616d6f756e74496e00000000604082015260600190565b60208082526011908201527f43506169723a20494e56414c49445f544f000000000000000000000000000000604082015260600190565b60208082526021908201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f60408201527f7700000000000000000000000000000000000000000000000000000000000000606082015260800190565b6020808252601c908201527f43506169723a2077726f6e6720616d6f756e74496e206e656564656400000000604082015260600190565b6020808252600f908201527f4345524332303a20455850495245440000000000000000000000000000000000604082015260600190565b60208082526015908201527f43506169723a2077726f6e67206f7574546f6b656e0000000000000000000000604082015260600190565b6020808252601d908201527f43506169723a2053484f52545f4c49515549444954595f4255524e4544000000604082015260600190565b6020808252601d908201527f43506169723a2053484f52545f4c49515549444954595f4d494e544544000000604082015260600190565b60208082526010908201527f43506169723a20464f5242494444454e00000000000000000000000000000000604082015260600190565b60208082526023908201527f5472616e7366657248656c7065723a204554485f5452414e534645525f46414960408201527f4c45440000000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526016908201527f43506169723a205452414e534645525f4641494c454400000000000000000000604082015260600190565b6020808252600f908201527f43506169723a204f564552464c4f570000000000000000000000000000000000604082015260600190565b60208082526015908201527f43506169723a2077726f6e6720616d6f756e74496e0000000000000000000000604082015260600190565b6dffffffffffffffffffffffffffff92831681529116602082015260400190565b918252602082015260400190565b92835260208301919091526001600160a01b0316604082015260600190565b600060e08201905085825260208581840152846040840152606083018460005b60048110156140475781518352918301919083019060010161402a565b5050505095945050505050565b60ff91909116815260200190565b60405181810167ffffffffffffffff8111828210171561408157600080fd5b604052919050565b60005b838110156140a457818101518382015260200161408c565b8381111561314c5750506000910152565b6001600160a01b03811681146140ca57600080fd5b5056fea26469706673582212204d9c04fecc367b0b983af1eb4467f748ca149b973eebf1723301659a88c0b77264736f6c634300060c0033

Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.