ETH Price: $3,939.03 (+5.90%)

Contract

0x54c75fEC898d34f40960B2dA65Adc1e46aB84f65
 
Transaction Hash
Method
Block
From
To
Claim Balance115439702020-12-28 18:12:401437 days ago1609179160IN
0x54c75fEC...46aB84f65
0 ETH0.0019388550
Add Liquidity115439702020-12-28 18:12:401437 days ago1609179160IN
0x54c75fEC...46aB84f65
0 ETH0.004654550
Transfer Store O...115435972020-12-28 16:46:041437 days ago1609173964IN
0x54c75fEC...46aB84f65
0 ETH0.00874255186
Add Liquidity115432042020-12-28 15:16:331437 days ago1609168593IN
0x54c75fEC...46aB84f65
0 ETH0.0444116673
Add Liquidity115430882020-12-28 14:51:481437 days ago1609167108IN
0x54c75fEC...46aB84f65
0 ETH0.09368672244
Add Liquidity115428212020-12-28 13:49:511437 days ago1609163391IN
0x54c75fEC...46aB84f65
0 ETH0.07748106201.8125
Add Liquidity115425572020-12-28 12:51:151437 days ago1609159875IN
0x54c75fEC...46aB84f65
0 ETH0.12865123205
Add Liquidity115423772020-12-28 12:14:071438 days ago1609157647IN
0x54c75fEC...46aB84f65
0 ETH0.05066665127
Add Liquidity115423242020-12-28 12:03:181438 days ago1609156998IN
0x54c75fEC...46aB84f65
0 ETH0.0533442185
Add Liquidity115421652020-12-28 11:30:591438 days ago1609155059IN
0x54c75fEC...46aB84f65
0 ETH0.0457871470
Claim Balance115421282020-12-28 11:22:181438 days ago1609154538IN
0x54c75fEC...46aB84f65
0 ETH0.00446075111
Add Liquidity115421182020-12-28 11:20:361438 days ago1609154436IN
0x54c75fEC...46aB84f65
0 ETH0.05490485143
Claim Balance115417562020-12-28 9:59:521438 days ago1609149592IN
0x54c75fEC...46aB84f65
0 ETH0.0022504756
Remove Liquidity115416832020-12-28 9:45:081438 days ago1609148708IN
0x54c75fEC...46aB84f65
0 ETH0.0185768870
Remove Liquidity115414452020-12-28 8:54:181438 days ago1609145658IN
0x54c75fEC...46aB84f65
0 ETH0.0156983660.1
Remove Liquidity115414182020-12-28 8:48:311438 days ago1609145311IN
0x54c75fEC...46aB84f65
0 ETH0.0164709763
Remove Liquidity115413072020-12-28 8:27:341438 days ago1609144054IN
0x54c75fEC...46aB84f65
0 ETH0.0116961746.2
Add Liquidity115411062020-12-28 7:45:531438 days ago1609141553IN
0x54c75fEC...46aB84f65
0 ETH0.06807434108
Add Liquidity115408842020-12-28 6:57:221438 days ago1609138642IN
0x54c75fEC...46aB84f65
0 ETH0.0238009962
Remove Liquidity115408102020-12-28 6:43:081438 days ago1609137788IN
0x54c75fEC...46aB84f65
0 ETH0.0107610638
Add Liquidity115404982020-12-28 5:33:191438 days ago1609133599IN
0x54c75fEC...46aB84f65
0 ETH0.0147718436
Remove Liquidity115404732020-12-28 5:29:171438 days ago1609133357IN
0x54c75fEC...46aB84f65
0 ETH0.0166519553.92
Add Liquidity115403762020-12-28 5:06:161438 days ago1609131976IN
0x54c75fEC...46aB84f65
0 ETH0.0375140751.00000145
Remove Liquidity115403512020-12-28 5:01:001438 days ago1609131660IN
0x54c75fEC...46aB84f65
0 ETH0.0126804851.00000145
Add Liquidity115403142020-12-28 4:53:461438 days ago1609131226IN
0x54c75fEC...46aB84f65
0 ETH0.0281337673
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
115343062020-12-27 6:52:151439 days ago1609051935
0x54c75fEC...46aB84f65
22.61086941 ETH
115343062020-12-27 6:52:151439 days ago1609051935
0x54c75fEC...46aB84f65
22.61086941 ETH
115268452020-12-26 3:32:581440 days ago1608953578
0x54c75fEC...46aB84f65
2 ETH
115265002020-12-26 2:20:551440 days ago1608949255
0x54c75fEC...46aB84f65
4 ETH
115256942020-12-25 23:16:071440 days ago1608938167
0x54c75fEC...46aB84f65
16.95 ETH
115250722020-12-25 20:49:141440 days ago1608929354
0x54c75fEC...46aB84f65
2.01424929 ETH
115250722020-12-25 20:49:141440 days ago1608929354
0x54c75fEC...46aB84f65
2.01424929 ETH
115243512020-12-25 18:13:391440 days ago1608920019
0x54c75fEC...46aB84f65
9.8 ETH
115224712020-12-25 11:15:201441 days ago1608894920
0x54c75fEC...46aB84f65
0.7 ETH
115215662020-12-25 8:03:211441 days ago1608883401
0x54c75fEC...46aB84f65
16.15731661 ETH
115215402020-12-25 7:56:001441 days ago1608882960
0x54c75fEC...46aB84f65
7 ETH
115208882020-12-25 5:29:021441 days ago1608874142
0x54c75fEC...46aB84f65
20 ETH
115208382020-12-25 5:18:321441 days ago1608873512
0x54c75fEC...46aB84f65
25 ETH
115190072020-12-24 22:24:251441 days ago1608848665
0x54c75fEC...46aB84f65
10 ETH
115169822020-12-24 15:00:341441 days ago1608822034
0x54c75fEC...46aB84f65
5 ETH
115164432020-12-24 13:01:321441 days ago1608814892
0x54c75fEC...46aB84f65
0.9 ETH
115161352020-12-24 11:50:241442 days ago1608810624
0x54c75fEC...46aB84f65
5 ETH
115155182020-12-24 9:34:141442 days ago1608802454
0x54c75fEC...46aB84f65
105 ETH
115153432020-12-24 8:53:441442 days ago1608800024
0x54c75fEC...46aB84f65
4 ETH
115151852020-12-24 8:20:401442 days ago1608798040
0x54c75fEC...46aB84f65
185.6940737 ETH
115151852020-12-24 8:20:401442 days ago1608798040
0x54c75fEC...46aB84f65
185.6940737 ETH
115148072020-12-24 6:59:511442 days ago1608793191
0x54c75fEC...46aB84f65
69 ETH
115145132020-12-24 5:51:291442 days ago1608789089
0x54c75fEC...46aB84f65
99.8 ETH
115142792020-12-24 4:58:491442 days ago1608785929
0x54c75fEC...46aB84f65
3.01733675 ETH
115142792020-12-24 4:58:491442 days ago1608785929
0x54c75fEC...46aB84f65
3.01733675 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
LiquidityProtection

Compiler Version
v0.6.12+commit.27d51765

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity)

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

// File: @openzeppelin/contracts/token/ERC20/IERC20.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.6.0;

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

// File: @bancor/token-governance/contracts/IClaimable.sol


pragma solidity 0.6.12;

/// @title Claimable contract interface
interface IClaimable {
    function owner() external view returns (address);

    function transferOwnership(address newOwner) external;

    function acceptOwnership() external;
}

// File: @bancor/token-governance/contracts/IMintableToken.sol


pragma solidity 0.6.12;



/// @title Mintable Token interface
interface IMintableToken is IERC20, IClaimable {
    function issue(address to, uint256 amount) external;

    function destroy(address from, uint256 amount) external;
}

// File: @bancor/token-governance/contracts/ITokenGovernance.sol


pragma solidity 0.6.12;


/// @title The interface for mintable/burnable token governance.
interface ITokenGovernance {
    // The address of the mintable ERC20 token.
    function token() external view returns (IMintableToken);

    /// @dev Mints new tokens.
    ///
    /// @param to Account to receive the new amount.
    /// @param amount Amount to increase the supply by.
    ///
    function mint(address to, uint256 amount) external;

    /// @dev Burns tokens from the caller.
    ///
    /// @param amount Amount to decrease the supply by.
    ///
    function burn(uint256 amount) external;
}

// File: solidity/contracts/utility/interfaces/ICheckpointStore.sol


pragma solidity 0.6.12;

/**
 * @dev Checkpoint store contract interface
 */
interface ICheckpointStore {
    function addCheckpoint(address _address) external;

    function addPastCheckpoint(address _address, uint256 _time) external;

    function addPastCheckpoints(address[] calldata _addresses, uint256[] calldata _times) external;

    function checkpoint(address _address) external view returns (uint256);
}

// File: solidity/contracts/utility/ReentrancyGuard.sol


pragma solidity 0.6.12;

/**
 * @dev This contract provides protection against calling a function
 * (directly or indirectly) from within itself.
 */
contract ReentrancyGuard {
    uint256 private constant UNLOCKED = 1;
    uint256 private constant LOCKED = 2;

    // LOCKED while protected code is being executed, UNLOCKED otherwise
    uint256 private state = UNLOCKED;

    /**
     * @dev ensures instantiation only by sub-contracts
     */
    constructor() internal {}

    // protects a function against reentrancy attacks
    modifier protected() {
        _protected();
        state = LOCKED;
        _;
        state = UNLOCKED;
    }

    // error message binary size optimization
    function _protected() internal view {
        require(state == UNLOCKED, "ERR_REENTRANCY");
    }
}

// File: solidity/contracts/utility/interfaces/IOwned.sol


pragma solidity 0.6.12;

/*
    Owned contract interface
*/
interface IOwned {
    // this function isn't since the compiler emits automatically generated getter functions as external
    function owner() external view returns (address);

    function transferOwnership(address _newOwner) external;

    function acceptOwnership() external;
}

// File: solidity/contracts/utility/Owned.sol


pragma solidity 0.6.12;


/**
 * @dev This contract provides support and utilities for contract ownership.
 */
contract Owned is IOwned {
    address public override owner;
    address public newOwner;

    /**
     * @dev triggered when the owner is updated
     *
     * @param _prevOwner previous owner
     * @param _newOwner  new owner
     */
    event OwnerUpdate(address indexed _prevOwner, address indexed _newOwner);

    /**
     * @dev initializes a new Owned instance
     */
    constructor() public {
        owner = msg.sender;
    }

    // allows execution by the owner only
    modifier ownerOnly {
        _ownerOnly();
        _;
    }

    // error message binary size optimization
    function _ownerOnly() internal view {
        require(msg.sender == owner, "ERR_ACCESS_DENIED");
    }

    /**
     * @dev allows transferring the contract ownership
     * the new owner still needs to accept the transfer
     * can only be called by the contract owner
     *
     * @param _newOwner    new contract owner
     */
    function transferOwnership(address _newOwner) public override ownerOnly {
        require(_newOwner != owner, "ERR_SAME_OWNER");
        newOwner = _newOwner;
    }

    /**
     * @dev used by a new owner to accept an ownership transfer
     */
    function acceptOwnership() public override {
        require(msg.sender == newOwner, "ERR_ACCESS_DENIED");
        emit OwnerUpdate(owner, newOwner);
        owner = newOwner;
        newOwner = address(0);
    }
}

// File: solidity/contracts/utility/SafeMath.sol


pragma solidity 0.6.12;

/**
 * @dev This library supports basic math operations with overflow/underflow protection.
 */
library SafeMath {
    /**
     * @dev returns the sum of _x and _y, reverts if the calculation overflows
     *
     * @param _x   value 1
     * @param _y   value 2
     *
     * @return sum
     */
    function add(uint256 _x, uint256 _y) internal pure returns (uint256) {
        uint256 z = _x + _y;
        require(z >= _x, "ERR_OVERFLOW");
        return z;
    }

    /**
     * @dev returns the difference of _x minus _y, reverts if the calculation underflows
     *
     * @param _x   minuend
     * @param _y   subtrahend
     *
     * @return difference
     */
    function sub(uint256 _x, uint256 _y) internal pure returns (uint256) {
        require(_x >= _y, "ERR_UNDERFLOW");
        return _x - _y;
    }

    /**
     * @dev returns the product of multiplying _x by _y, reverts if the calculation overflows
     *
     * @param _x   factor 1
     * @param _y   factor 2
     *
     * @return product
     */
    function mul(uint256 _x, uint256 _y) internal pure returns (uint256) {
        // gas optimization
        if (_x == 0) return 0;

        uint256 z = _x * _y;
        require(z / _x == _y, "ERR_OVERFLOW");
        return z;
    }

    /**
     * @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
     *
     * @param _x   dividend
     * @param _y   divisor
     *
     * @return quotient
     */
    function div(uint256 _x, uint256 _y) internal pure returns (uint256) {
        require(_y > 0, "ERR_DIVIDE_BY_ZERO");
        uint256 c = _x / _y;
        return c;
    }
}

// File: solidity/contracts/utility/Math.sol


pragma solidity 0.6.12;

/**
 * @dev This library provides a set of complex math operations.
 */
library Math {
    /**
     * @dev returns the largest integer smaller than or equal to the square root of a positive integer
     *
     * @param _num a positive integer
     *
     * @return the largest integer smaller than or equal to the square root of the positive integer
     */
    function floorSqrt(uint256 _num) internal pure returns (uint256) {
        uint256 x = _num / 2 + 1;
        uint256 y = (x + _num / x) / 2;
        while (x > y) {
            x = y;
            y = (x + _num / x) / 2;
        }
        return x;
    }

    /**
     * @dev returns the smallest integer larger than or equal to the square root of a positive integer
     *
     * @param _num a positive integer
     *
     * @return the smallest integer larger than or equal to the square root of the positive integer
     */
    function ceilSqrt(uint256 _num) internal pure returns (uint256) {
        uint256 x = floorSqrt(_num);
        return x * x == _num ? x : x + 1;
    }

    /**
     * @dev computes a reduced-scalar ratio
     *
     * @param _n   ratio numerator
     * @param _d   ratio denominator
     * @param _max maximum desired scalar
     *
     * @return ratio's numerator and denominator
     */
    function reducedRatio(
        uint256 _n,
        uint256 _d,
        uint256 _max
    ) internal pure returns (uint256, uint256) {
        (uint256 n, uint256 d) = (_n, _d);
        if (n > _max || d > _max) {
            (n, d) = normalizedRatio(n, d, _max);
        }
        if (n != d) {
            return (n, d);
        }
        return (1, 1);
    }

    /**
     * @dev computes "scale * a / (a + b)" and "scale * b / (a + b)".
     */
    function normalizedRatio(
        uint256 _a,
        uint256 _b,
        uint256 _scale
    ) internal pure returns (uint256, uint256) {
        if (_a <= _b) {
            return accurateRatio(_a, _b, _scale);
        }
        (uint256 y, uint256 x) = accurateRatio(_b, _a, _scale);
        return (x, y);
    }

    /**
     * @dev computes "scale * a / (a + b)" and "scale * b / (a + b)", assuming that "a <= b".
     */
    function accurateRatio(
        uint256 _a,
        uint256 _b,
        uint256 _scale
    ) internal pure returns (uint256, uint256) {
        uint256 maxVal = uint256(-1) / _scale;
        if (_a > maxVal) {
            uint256 c = _a / (maxVal + 1) + 1;
            _a /= c; // we can now safely compute `_a * _scale`
            _b /= c;
        }
        if (_a != _b) {
            uint256 n = _a * _scale;
            uint256 d = _a + _b; // can overflow
            if (d >= _a) { // no overflow in `_a + _b`
                uint256 x = roundDiv(n, d); // we can now safely compute `_scale - x`
                uint256 y = _scale - x;
                return (x, y);
            }
            if (n < _b - (_b - _a) / 2) {
                return (0, _scale); // `_a * _scale < (_a + _b) / 2 < MAX_UINT256 < _a + _b`
            }
            return (1, _scale - 1); // `(_a + _b) / 2 < _a * _scale < MAX_UINT256 < _a + _b`
        }
        return (_scale / 2, _scale / 2); // allow reduction to `(1, 1)` in the calling function
    }

    /**
     * @dev computes the nearest integer to a given quotient without overflowing or underflowing.
     */
    function roundDiv(uint256 _n, uint256 _d) internal pure returns (uint256) {
        return _n / _d + (_n % _d) / (_d - _d / 2);
    }

    /**
     * @dev returns the average number of decimal digits in a given list of positive integers
     *
     * @param _values  list of positive integers
     *
     * @return the average number of decimal digits in the given list of positive integers
     */
    function geometricMean(uint256[] memory _values) internal pure returns (uint256) {
        uint256 numOfDigits = 0;
        uint256 length = _values.length;
        for (uint256 i = 0; i < length; i++) {
            numOfDigits += decimalLength(_values[i]);
        }
        return uint256(10)**(roundDivUnsafe(numOfDigits, length) - 1);
    }

    /**
     * @dev returns the number of decimal digits in a given positive integer
     *
     * @param _x   positive integer
     *
     * @return the number of decimal digits in the given positive integer
     */
    function decimalLength(uint256 _x) internal pure returns (uint256) {
        uint256 y = 0;
        for (uint256 x = _x; x > 0; x /= 10) {
            y++;
        }
        return y;
    }

    /**
     * @dev returns the nearest integer to a given quotient
     * the computation is overflow-safe assuming that the input is sufficiently small
     *
     * @param _n   quotient numerator
     * @param _d   quotient denominator
     *
     * @return the nearest integer to the given quotient
     */
    function roundDivUnsafe(uint256 _n, uint256 _d) internal pure returns (uint256) {
        return (_n + _d / 2) / _d;
    }

    /**
     * @dev returns the larger of two values
     *
     * @param _val1 the first value
     * @param _val2 the second value
     */
    function max(uint256 _val1, uint256 _val2) internal pure returns (uint256) {
        return _val1 > _val2 ? _val1 : _val2;
    }
}

// File: solidity/contracts/token/interfaces/IERC20Token.sol


pragma solidity 0.6.12;

/*
    ERC20 Standard Token interface
*/
interface IERC20Token {
    function name() external view returns (string memory);

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

    function decimals() external view returns (uint8);

    function totalSupply() external view returns (uint256);

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

    function allowance(address _owner, address _spender) external view returns (uint256);

    function transfer(address _to, uint256 _value) external returns (bool);

    function transferFrom(
        address _from,
        address _to,
        uint256 _value
    ) external returns (bool);

    function approve(address _spender, uint256 _value) external returns (bool);
}

// File: solidity/contracts/utility/TokenHandler.sol


pragma solidity 0.6.12;


contract TokenHandler {
    bytes4 private constant APPROVE_FUNC_SELECTOR = bytes4(keccak256("approve(address,uint256)"));
    bytes4 private constant TRANSFER_FUNC_SELECTOR = bytes4(keccak256("transfer(address,uint256)"));
    bytes4 private constant TRANSFER_FROM_FUNC_SELECTOR = bytes4(keccak256("transferFrom(address,address,uint256)"));

    /**
     * @dev executes the ERC20 token's `approve` function and reverts upon failure
     * the main purpose of this function is to prevent a non standard ERC20 token
     * from failing silently
     *
     * @param _token   ERC20 token address
     * @param _spender approved address
     * @param _value   allowance amount
     */
    function safeApprove(
        IERC20Token _token,
        address _spender,
        uint256 _value
    ) internal {
        (bool success, bytes memory data) = address(_token).call(
            abi.encodeWithSelector(APPROVE_FUNC_SELECTOR, _spender, _value)
        );
        require(success && (data.length == 0 || abi.decode(data, (bool))), "ERR_APPROVE_FAILED");
    }

    /**
     * @dev executes the ERC20 token's `transfer` function and reverts upon failure
     * the main purpose of this function is to prevent a non standard ERC20 token
     * from failing silently
     *
     * @param _token   ERC20 token address
     * @param _to      target address
     * @param _value   transfer amount
     */
    function safeTransfer(
        IERC20Token _token,
        address _to,
        uint256 _value
    ) internal {
        (bool success, bytes memory data) = address(_token).call(
            abi.encodeWithSelector(TRANSFER_FUNC_SELECTOR, _to, _value)
        );
        require(success && (data.length == 0 || abi.decode(data, (bool))), "ERR_TRANSFER_FAILED");
    }

    /**
     * @dev executes the ERC20 token's `transferFrom` function and reverts upon failure
     * the main purpose of this function is to prevent a non standard ERC20 token
     * from failing silently
     *
     * @param _token   ERC20 token address
     * @param _from    source address
     * @param _to      target address
     * @param _value   transfer amount
     */
    function safeTransferFrom(
        IERC20Token _token,
        address _from,
        address _to,
        uint256 _value
    ) internal {
        (bool success, bytes memory data) = address(_token).call(
            abi.encodeWithSelector(TRANSFER_FROM_FUNC_SELECTOR, _from, _to, _value)
        );
        require(success && (data.length == 0 || abi.decode(data, (bool))), "ERR_TRANSFER_FROM_FAILED");
    }
}

// File: solidity/contracts/utility/Types.sol


pragma solidity 0.6.12;

/**
 * @dev This contract provides types which can be used by various contracts.
 */

struct Fraction {
    uint256 n; // numerator
    uint256 d; // denominator
}

// File: solidity/contracts/utility/Time.sol


pragma solidity 0.6.12;

/*
    Time implementing contract
*/
contract Time {
    /**
     * @dev returns the current time
     */
    function time() internal view virtual returns (uint256) {
        return block.timestamp;
    }
}

// File: solidity/contracts/utility/Utils.sol


pragma solidity 0.6.12;

/**
 * @dev Utilities & Common Modifiers
 */
contract Utils {
    // verifies that a value is greater than zero
    modifier greaterThanZero(uint256 _value) {
        _greaterThanZero(_value);
        _;
    }

    // error message binary size optimization
    function _greaterThanZero(uint256 _value) internal pure {
        require(_value > 0, "ERR_ZERO_VALUE");
    }

    // validates an address - currently only checks that it isn't null
    modifier validAddress(address _address) {
        _validAddress(_address);
        _;
    }

    // error message binary size optimization
    function _validAddress(address _address) internal pure {
        require(_address != address(0), "ERR_INVALID_ADDRESS");
    }

    // verifies that the address is different than this contract address
    modifier notThis(address _address) {
        _notThis(_address);
        _;
    }

    // error message binary size optimization
    function _notThis(address _address) internal view {
        require(_address != address(this), "ERR_ADDRESS_IS_SELF");
    }
}

// File: solidity/contracts/converter/interfaces/IConverterAnchor.sol


pragma solidity 0.6.12;


/*
    Converter Anchor interface
*/
interface IConverterAnchor is IOwned {

}

// File: solidity/contracts/token/interfaces/IDSToken.sol


pragma solidity 0.6.12;




/*
    DSToken interface
*/
interface IDSToken is IConverterAnchor, IERC20Token {
    function issue(address _to, uint256 _amount) external;

    function destroy(address _from, uint256 _amount) external;
}

// File: solidity/contracts/liquidity-protection/interfaces/ILiquidityProtectionStore.sol


pragma solidity 0.6.12;





/*
    Liquidity Protection Store interface
*/
interface ILiquidityProtectionStore is IOwned {
    function withdrawTokens(
        IERC20Token _token,
        address _to,
        uint256 _amount
    ) external;

    function protectedLiquidity(uint256 _id)
        external
        view
        returns (
            address,
            IDSToken,
            IERC20Token,
            uint256,
            uint256,
            uint256,
            uint256,
            uint256
        );

    function addProtectedLiquidity(
        address _provider,
        IDSToken _poolToken,
        IERC20Token _reserveToken,
        uint256 _poolAmount,
        uint256 _reserveAmount,
        uint256 _reserveRateN,
        uint256 _reserveRateD,
        uint256 _timestamp
    ) external returns (uint256);

    function updateProtectedLiquidityAmounts(
        uint256 _id,
        uint256 _poolNewAmount,
        uint256 _reserveNewAmount
    ) external;

    function removeProtectedLiquidity(uint256 _id) external;

    function lockedBalance(address _provider, uint256 _index) external view returns (uint256, uint256);

    function lockedBalanceRange(
        address _provider,
        uint256 _startIndex,
        uint256 _endIndex
    ) external view returns (uint256[] memory, uint256[] memory);

    function addLockedBalance(
        address _provider,
        uint256 _reserveAmount,
        uint256 _expirationTime
    ) external returns (uint256);

    function removeLockedBalance(address _provider, uint256 _index) external;

    function systemBalance(IERC20Token _poolToken) external view returns (uint256);

    function incSystemBalance(IERC20Token _poolToken, uint256 _poolAmount) external;

    function decSystemBalance(IERC20Token _poolToken, uint256 _poolAmount) external;
}

// File: solidity/contracts/liquidity-protection/interfaces/ILiquidityProtectionSettings.sol


pragma solidity 0.6.12;


/*
    Liquidity Protection Store Settings interface
*/
interface ILiquidityProtectionSettings {
    function addPoolToWhitelist(IConverterAnchor _poolAnchor) external;

    function removePoolFromWhitelist(IConverterAnchor _poolAnchor) external;

    function isPoolWhitelisted(IConverterAnchor _poolAnchor) external view returns (bool);

    function isPoolSupported(IConverterAnchor _poolAnchor) external view returns (bool);

    function minNetworkTokenLiquidityForMinting() external view returns (uint256);

    function defaultNetworkTokenMintingLimit() external view returns (uint256);

    function networkTokenMintingLimits(IConverterAnchor _poolAnchor) external view returns (uint256);

    function networkTokensMinted(IConverterAnchor _poolAnchor) external view returns (uint256);

    function incNetworkTokensMinted(IConverterAnchor _poolAnchor, uint256 _amount) external;

    function decNetworkTokensMinted(IConverterAnchor _poolAnchor, uint256 _amount) external;

    function minProtectionDelay() external view returns (uint256);

    function maxProtectionDelay() external view returns (uint256);

    function setProtectionDelays(uint256 _minProtectionDelay, uint256 _maxProtectionDelay) external;

    function minNetworkCompensation() external view returns (uint256);

    function setMinNetworkCompensation(uint256 _minCompensation) external;

    function lockDuration() external view returns (uint256);

    function setLockDuration(uint256 _lockDuration) external;

    function averageRateMaxDeviation() external view returns (uint32);

    function setAverageRateMaxDeviation(uint32 _averageRateMaxDeviation) external;
}

// File: solidity/contracts/converter/interfaces/IConverter.sol


pragma solidity 0.6.12;




/*
    Converter interface
*/
interface IConverter is IOwned {
    function converterType() external pure returns (uint16);

    function anchor() external view returns (IConverterAnchor);

    function isActive() external view returns (bool);

    function targetAmountAndFee(
        IERC20Token _sourceToken,
        IERC20Token _targetToken,
        uint256 _amount
    ) external view returns (uint256, uint256);

    function convert(
        IERC20Token _sourceToken,
        IERC20Token _targetToken,
        uint256 _amount,
        address _trader,
        address payable _beneficiary
    ) external payable returns (uint256);

    function conversionFee() external view returns (uint32);

    function maxConversionFee() external view returns (uint32);

    function reserveBalance(IERC20Token _reserveToken) external view returns (uint256);

    receive() external payable;

    function transferAnchorOwnership(address _newOwner) external;

    function acceptAnchorOwnership() external;

    function setConversionFee(uint32 _conversionFee) external;

    function withdrawTokens(
        IERC20Token _token,
        address _to,
        uint256 _amount
    ) external;

    function withdrawETH(address payable _to) external;

    function addReserve(IERC20Token _token, uint32 _ratio) external;

    // deprecated, backward compatibility
    function token() external view returns (IConverterAnchor);

    function transferTokenOwnership(address _newOwner) external;

    function acceptTokenOwnership() external;

    function connectors(IERC20Token _address)
        external
        view
        returns (
            uint256,
            uint32,
            bool,
            bool,
            bool
        );

    function getConnectorBalance(IERC20Token _connectorToken) external view returns (uint256);

    function connectorTokens(uint256 _index) external view returns (IERC20Token);

    function connectorTokenCount() external view returns (uint16);

    /**
     * @dev triggered when the converter is activated
     *
     * @param _type        converter type
     * @param _anchor      converter anchor
     * @param _activated   true if the converter was activated, false if it was deactivated
     */
    event Activation(uint16 indexed _type, IConverterAnchor indexed _anchor, bool indexed _activated);

    /**
     * @dev triggered when a conversion between two tokens occurs
     *
     * @param _fromToken       source ERC20 token
     * @param _toToken         target ERC20 token
     * @param _trader          wallet that initiated the trade
     * @param _amount          input amount in units of the source token
     * @param _return          output amount minus conversion fee in units of the target token
     * @param _conversionFee   conversion fee in units of the target token
     */
    event Conversion(
        IERC20Token indexed _fromToken,
        IERC20Token indexed _toToken,
        address indexed _trader,
        uint256 _amount,
        uint256 _return,
        int256 _conversionFee
    );

    /**
     * @dev triggered when the rate between two tokens in the converter changes
     * note that the event might be dispatched for rate updates between any two tokens in the converter
     *
     * @param  _token1 address of the first token
     * @param  _token2 address of the second token
     * @param  _rateN  rate of 1 unit of `_token1` in `_token2` (numerator)
     * @param  _rateD  rate of 1 unit of `_token1` in `_token2` (denominator)
     */
    event TokenRateUpdate(IERC20Token indexed _token1, IERC20Token indexed _token2, uint256 _rateN, uint256 _rateD);

    /**
     * @dev triggered when the conversion fee is updated
     *
     * @param  _prevFee    previous fee percentage, represented in ppm
     * @param  _newFee     new fee percentage, represented in ppm
     */
    event ConversionFeeUpdate(uint32 _prevFee, uint32 _newFee);
}

// File: solidity/contracts/converter/interfaces/IConverterRegistry.sol


pragma solidity 0.6.12;



interface IConverterRegistry {
    function getAnchorCount() external view returns (uint256);

    function getAnchors() external view returns (address[] memory);

    function getAnchor(uint256 _index) external view returns (IConverterAnchor);

    function isAnchor(address _value) external view returns (bool);

    function getLiquidityPoolCount() external view returns (uint256);

    function getLiquidityPools() external view returns (address[] memory);

    function getLiquidityPool(uint256 _index) external view returns (IConverterAnchor);

    function isLiquidityPool(address _value) external view returns (bool);

    function getConvertibleTokenCount() external view returns (uint256);

    function getConvertibleTokens() external view returns (address[] memory);

    function getConvertibleToken(uint256 _index) external view returns (IERC20Token);

    function isConvertibleToken(address _value) external view returns (bool);

    function getConvertibleTokenAnchorCount(IERC20Token _convertibleToken) external view returns (uint256);

    function getConvertibleTokenAnchors(IERC20Token _convertibleToken) external view returns (address[] memory);

    function getConvertibleTokenAnchor(IERC20Token _convertibleToken, uint256 _index)
        external
        view
        returns (IConverterAnchor);

    function isConvertibleTokenAnchor(IERC20Token _convertibleToken, address _value) external view returns (bool);
}

// File: solidity/contracts/liquidity-protection/LiquidityProtection.sol


pragma solidity 0.6.12;



















interface ILiquidityPoolConverter is IConverter {
    function addLiquidity(
        IERC20Token[] memory _reserveTokens,
        uint256[] memory _reserveAmounts,
        uint256 _minReturn
    ) external payable;

    function removeLiquidity(
        uint256 _amount,
        IERC20Token[] memory _reserveTokens,
        uint256[] memory _reserveMinReturnAmounts
    ) external;

    function recentAverageRate(IERC20Token _reserveToken) external view returns (uint256, uint256);
}

/**
 * @dev This contract implements the liquidity protection mechanism.
 */
contract LiquidityProtection is TokenHandler, Utils, Owned, ReentrancyGuard, Time {
    using SafeMath for uint256;
    using Math for *;

    struct ProtectedLiquidity {
        address provider; // liquidity provider
        IDSToken poolToken; // pool token address
        IERC20Token reserveToken; // reserve token address
        uint256 poolAmount; // pool token amount
        uint256 reserveAmount; // reserve token amount
        uint256 reserveRateN; // rate of 1 protected reserve token in units of the other reserve token (numerator)
        uint256 reserveRateD; // rate of 1 protected reserve token in units of the other reserve token (denominator)
        uint256 timestamp; // timestamp
    }

    // various rates between the two reserve tokens. the rate is of 1 unit of the protected reserve token in units of the other reserve token
    struct PackedRates {
        uint128 addSpotRateN; // spot rate of 1 A in units of B when liquidity was added (numerator)
        uint128 addSpotRateD; // spot rate of 1 A in units of B when liquidity was added (denominator)
        uint128 removeSpotRateN; // spot rate of 1 A in units of B when liquidity is removed (numerator)
        uint128 removeSpotRateD; // spot rate of 1 A in units of B when liquidity is removed (denominator)
        uint128 removeAverageRateN; // average rate of 1 A in units of B when liquidity is removed (numerator)
        uint128 removeAverageRateD; // average rate of 1 A in units of B when liquidity is removed (denominator)
    }

    IERC20Token internal constant ETH_RESERVE_ADDRESS = IERC20Token(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
    uint32 internal constant PPM_RESOLUTION = 1000000;
    uint256 internal constant MAX_UINT128 = 2**128 - 1;
    uint256 internal constant MAX_UINT256 = uint256(-1);

    ILiquidityProtectionSettings public immutable settings;
    ILiquidityProtectionStore public immutable store;
    IERC20Token public immutable networkToken;
    ITokenGovernance public immutable networkTokenGovernance;
    IERC20Token public immutable govToken;
    ITokenGovernance public immutable govTokenGovernance;
    ICheckpointStore public immutable lastRemoveCheckpointStore;

    // true if the contract is currently adding/removing liquidity from a converter, used for accepting ETH
    bool private updatingLiquidity = false;

    /**
     * @dev initializes a new LiquidityProtection contract
     *
     * @param _settings liquidity protection settings
     * @param _store liquidity protection store
     * @param _networkTokenGovernance network token governance
     * @param _govTokenGovernance governance token governance
     * @param _lastRemoveCheckpointStore last liquidity removal/unprotection checkpoints store
     */
    constructor(
        ILiquidityProtectionSettings _settings,
        ILiquidityProtectionStore _store,
        ITokenGovernance _networkTokenGovernance,
        ITokenGovernance _govTokenGovernance,
        ICheckpointStore _lastRemoveCheckpointStore
    )
        public
        validAddress(address(_settings))
        validAddress(address(_store))
        validAddress(address(_networkTokenGovernance))
        validAddress(address(_govTokenGovernance))
        notThis(address(_settings))
        notThis(address(_store))
        notThis(address(_networkTokenGovernance))
        notThis(address(_govTokenGovernance))
    {
        settings = _settings;
        store = _store;

        networkTokenGovernance = _networkTokenGovernance;
        networkToken = IERC20Token(address(_networkTokenGovernance.token()));
        govTokenGovernance = _govTokenGovernance;
        govToken = IERC20Token(address(_govTokenGovernance.token()));

        lastRemoveCheckpointStore = _lastRemoveCheckpointStore;
    }

    // ensures that the contract is currently removing liquidity from a converter
    modifier updatingLiquidityOnly() {
        _updatingLiquidityOnly();
        _;
    }

    // error message binary size optimization
    function _updatingLiquidityOnly() internal view {
        require(updatingLiquidity, "ERR_NOT_UPDATING_LIQUIDITY");
    }

    // ensures that the portion is valid
    modifier validPortion(uint32 _portion) {
        _validPortion(_portion);
        _;
    }

    // error message binary size optimization
    function _validPortion(uint32 _portion) internal pure {
        require(_portion > 0 && _portion <= PPM_RESOLUTION, "ERR_INVALID_PORTION");
    }

    // ensures that the pool is supported
    modifier poolSupported(IConverterAnchor _poolAnchor) {
        _poolSupported(_poolAnchor);
        _;
    }

    // error message binary size optimization
    function _poolSupported(IConverterAnchor _poolAnchor) internal view {
        require(settings.isPoolSupported(_poolAnchor), "ERR_POOL_NOT_SUPPORTED");
    }

    // ensures that the pool is whitelisted
    modifier poolWhitelisted(IConverterAnchor _poolAnchor) {
        _poolWhitelisted(_poolAnchor);
        _;
    }

    // error message binary size optimization
    function _poolWhitelisted(IConverterAnchor _poolAnchor) internal view {
        require(settings.isPoolWhitelisted(_poolAnchor), "ERR_POOL_NOT_WHITELISTED");
    }

    /**
     * @dev accept ETH
     * used when removing liquidity from ETH converters
     */
    receive() external payable updatingLiquidityOnly() {}

    /**
     * @dev transfers the ownership of the store
     * can only be called by the contract owner
     *
     * @param _newOwner    the new owner of the store
     */
    function transferStoreOwnership(address _newOwner) external ownerOnly {
        store.transferOwnership(_newOwner);
    }

    /**
     * @dev accepts the ownership of the store
     * can only be called by the contract owner
     */
    function acceptStoreOwnership() external ownerOnly {
        store.acceptOwnership();
    }

    /**
     * @dev adds protection to existing pool tokens
     * also mints new governance tokens for the caller
     *
     * @param _poolAnchor  anchor of the pool
     * @param _amount      amount of pool tokens to protect
     */
    function protectLiquidity(IConverterAnchor _poolAnchor, uint256 _amount)
        external
        protected
        poolSupported(_poolAnchor)
        poolWhitelisted(_poolAnchor)
        greaterThanZero(_amount)
    {
        // get the converter
        IConverter converter = IConverter(payable(ownedBy(_poolAnchor)));

        // save a local copy of `networkToken`
        IERC20Token networkTokenLocal = networkToken;

        // protect both reserves
        IDSToken poolToken = IDSToken(address(_poolAnchor));
        protectLiquidity(poolToken, converter, networkTokenLocal, 0, _amount / 2);
        protectLiquidity(poolToken, converter, networkTokenLocal, 1, _amount - _amount / 2);

        // transfer the pool tokens from the caller directly to the store
        safeTransferFrom(poolToken, msg.sender, address(store), _amount);
    }

    /**
     * @dev cancels the protection and returns the pool tokens to the caller
     * also burns governance tokens from the caller
     * must be called with the indices of both the base token and the network token protections
     *
     * @param _id1 id in the caller's list of protected liquidity
     * @param _id2 matching id in the caller's list of protected liquidity
     */
    function unprotectLiquidity(uint256 _id1, uint256 _id2) external protected {
        require(_id1 != _id2, "ERR_SAME_ID");

        ProtectedLiquidity memory liquidity1 = protectedLiquidity(_id1, msg.sender);
        ProtectedLiquidity memory liquidity2 = protectedLiquidity(_id2, msg.sender);

        // save a local copy of `networkToken`
        IERC20Token networkTokenLocal = networkToken;

        // verify that the two protected liquidities were added together (using `protect`)
        require(
            liquidity1.poolToken == liquidity2.poolToken &&
                liquidity1.reserveToken != liquidity2.reserveToken &&
                (liquidity1.reserveToken == networkTokenLocal || liquidity2.reserveToken == networkTokenLocal) &&
                liquidity1.timestamp == liquidity2.timestamp &&
                liquidity1.poolAmount <= liquidity2.poolAmount.add(1) &&
                liquidity2.poolAmount <= liquidity1.poolAmount.add(1),
            "ERR_PROTECTIONS_MISMATCH"
        );

        // verify that the two protected liquidities are not removed on the same block in which they were added
        require(liquidity1.timestamp < time(), "ERR_TOO_EARLY");

        // burn the governance tokens from the caller. we need to transfer the tokens to the contract itself, since only
        // token holders can burn their tokens
        uint256 amount =
            liquidity1.reserveToken == networkTokenLocal ? liquidity1.reserveAmount : liquidity2.reserveAmount;
        safeTransferFrom(govToken, msg.sender, address(this), amount);
        govTokenGovernance.burn(amount);

        // update last liquidity removal checkpoint
        lastRemoveCheckpointStore.addCheckpoint(msg.sender);

        // remove the two protected liquidities from the provider
        store.removeProtectedLiquidity(_id1);
        store.removeProtectedLiquidity(_id2);

        // transfer the pool tokens back to the caller
        store.withdrawTokens(liquidity1.poolToken, msg.sender, liquidity1.poolAmount.add(liquidity2.poolAmount));
    }

    /**
     * @dev adds protected liquidity to a pool for a specific recipient
     * also mints new governance tokens for the caller if the caller adds network tokens
     *
     * @param _owner       protected liquidity owner
     * @param _poolAnchor      anchor of the pool
     * @param _reserveToken    reserve token to add to the pool
     * @param _amount          amount of tokens to add to the pool
     * @return new protected liquidity id
     */
    function addLiquidityFor(
        address _owner,
        IConverterAnchor _poolAnchor,
        IERC20Token _reserveToken,
        uint256 _amount
    )
        external
        payable
        protected
        validAddress(_owner)
        poolSupported(_poolAnchor)
        poolWhitelisted(_poolAnchor)
        greaterThanZero(_amount)
        returns (uint256)
    {
        return addLiquidity(_owner, _poolAnchor, _reserveToken, _amount);
    }

    /**
     * @dev adds protected liquidity to a pool
     * also mints new governance tokens for the caller if the caller adds network tokens
     *
     * @param _poolAnchor      anchor of the pool
     * @param _reserveToken    reserve token to add to the pool
     * @param _amount          amount of tokens to add to the pool
     * @return new protected liquidity id
     */
    function addLiquidity(
        IConverterAnchor _poolAnchor,
        IERC20Token _reserveToken,
        uint256 _amount
    )
        external
        payable
        protected
        poolSupported(_poolAnchor)
        poolWhitelisted(_poolAnchor)
        greaterThanZero(_amount)
        returns (uint256)
    {
        return addLiquidity(msg.sender, _poolAnchor, _reserveToken, _amount);
    }

    /**
     * @dev adds protected liquidity to a pool for a specific recipient
     * also mints new governance tokens for the caller if the caller adds network tokens
     *
     * @param _owner       protected liquidity owner
     * @param _poolAnchor      anchor of the pool
     * @param _reserveToken    reserve token to add to the pool
     * @param _amount          amount of tokens to add to the pool
     * @return new protected liquidity id
     */
    function addLiquidity(
        address _owner,
        IConverterAnchor _poolAnchor,
        IERC20Token _reserveToken,
        uint256 _amount
    ) private returns (uint256) {
        // save a local copy of `networkToken`
        IERC20Token networkTokenLocal = networkToken;

        if (_reserveToken == networkTokenLocal) {
            require(msg.value == 0, "ERR_ETH_AMOUNT_MISMATCH");
            return addNetworkTokenLiquidity(_owner, _poolAnchor, networkTokenLocal, _amount);
        }

        // verify that ETH was passed with the call if needed
        uint256 val = _reserveToken == ETH_RESERVE_ADDRESS ? _amount : 0;
        require(msg.value == val, "ERR_ETH_AMOUNT_MISMATCH");
        return addBaseTokenLiquidity(_owner, _poolAnchor, _reserveToken, networkTokenLocal, _amount);
    }

    /**
     * @dev adds protected network token liquidity to a pool
     * also mints new governance tokens for the caller
     *
     * @param _owner    protected liquidity owner
     * @param _poolAnchor   anchor of the pool
     * @param _networkToken the network reserve token of the pool
     * @param _amount       amount of tokens to add to the pool
     * @return new protected liquidity id
     */
    function addNetworkTokenLiquidity(
        address _owner,
        IConverterAnchor _poolAnchor,
        IERC20Token _networkToken,
        uint256 _amount
    ) internal returns (uint256) {
        IDSToken poolToken = IDSToken(address(_poolAnchor));

        // get the rate between the pool token and the reserve
        Fraction memory poolRate = poolTokenRate(poolToken, _networkToken);

        // calculate the amount of pool tokens based on the amount of reserve tokens
        uint256 poolTokenAmount = _amount.mul(poolRate.d).div(poolRate.n);

        // remove the pool tokens from the system's ownership (will revert if not enough tokens are available)
        store.decSystemBalance(poolToken, poolTokenAmount);

        // add protected liquidity for the recipient
        uint256 id = addProtectedLiquidity(_owner, poolToken, _networkToken, poolTokenAmount, _amount);

        // burns the network tokens from the caller. we need to transfer the tokens to the contract itself, since only
        // token holders can burn their tokens
        safeTransferFrom(_networkToken, msg.sender, address(this), _amount);
        networkTokenGovernance.burn(_amount);
        settings.decNetworkTokensMinted(_poolAnchor, _amount);

        // mint governance tokens to the recipient
        govTokenGovernance.mint(_owner, _amount);

        return id;
    }

    /**
     * @dev adds protected base token liquidity to a pool
     *
     * @param _owner    protected liquidity owner
     * @param _poolAnchor   anchor of the pool
     * @param _baseToken    the base reserve token of the pool
     * @param _networkToken the network reserve token of the pool
     * @param _amount       amount of tokens to add to the pool
     * @return new protected liquidity id
     */
    function addBaseTokenLiquidity(
        address _owner,
        IConverterAnchor _poolAnchor,
        IERC20Token _baseToken,
        IERC20Token _networkToken,
        uint256 _amount
    ) internal returns (uint256) {
        IDSToken poolToken = IDSToken(address(_poolAnchor));

        // get the reserve balances
        ILiquidityPoolConverter converter = ILiquidityPoolConverter(payable(ownedBy(_poolAnchor)));
        (uint256 reserveBalanceBase, uint256 reserveBalanceNetwork) =
            converterReserveBalances(converter, _baseToken, _networkToken);

        require(reserveBalanceNetwork >= settings.minNetworkTokenLiquidityForMinting(), "ERR_NOT_ENOUGH_LIQUIDITY");

        // calculate and mint the required amount of network tokens for adding liquidity
        uint256 newNetworkLiquidityAmount = _amount.mul(reserveBalanceNetwork).div(reserveBalanceBase);

        // verify network token minting limit
        uint256 mintingLimit = settings.networkTokenMintingLimits(_poolAnchor);
        if (mintingLimit == 0) {
            mintingLimit = settings.defaultNetworkTokenMintingLimit();
        }

        uint256 newNetworkTokensMinted = settings.networkTokensMinted(_poolAnchor).add(newNetworkLiquidityAmount);
        require(newNetworkTokensMinted <= mintingLimit, "ERR_MAX_AMOUNT_REACHED");

        // issue new network tokens to the system
        networkTokenGovernance.mint(address(this), newNetworkLiquidityAmount);
        settings.incNetworkTokensMinted(_poolAnchor, newNetworkLiquidityAmount);

        // transfer the base tokens from the caller and approve the converter
        ensureAllowance(_networkToken, address(converter), newNetworkLiquidityAmount);
        if (_baseToken != ETH_RESERVE_ADDRESS) {
            safeTransferFrom(_baseToken, msg.sender, address(this), _amount);
            ensureAllowance(_baseToken, address(converter), _amount);
        }

        // add liquidity
        addLiquidity(converter, _baseToken, _networkToken, _amount, newNetworkLiquidityAmount, msg.value);

        // transfer the new pool tokens to the store
        uint256 poolTokenAmount = poolToken.balanceOf(address(this));
        safeTransfer(poolToken, address(store), poolTokenAmount);

        // the system splits the pool tokens with the caller
        // increase the system's pool token balance and add protected liquidity for the caller
        store.incSystemBalance(poolToken, poolTokenAmount - poolTokenAmount / 2); // account for rounding errors
        return addProtectedLiquidity(_owner, poolToken, _baseToken, poolTokenAmount / 2, _amount);
    }

    /**
     * @dev returns the expected/actual amounts the provider will receive for removing liquidity
     * it's also possible to provide the remove liquidity time to get an estimation
     * for the return at that given point
     *
     * @param _id              protected liquidity id
     * @param _portion         portion of liquidity to remove, in PPM
     * @param _removeTimestamp time at which the liquidity is removed
     * @return expected return amount in the reserve token
     * @return actual return amount in the reserve token
     * @return compensation in the network token
     */
    function removeLiquidityReturn(
        uint256 _id,
        uint32 _portion,
        uint256 _removeTimestamp
    )
        external
        view
        validPortion(_portion)
        returns (
            uint256,
            uint256,
            uint256
        )
    {
        ProtectedLiquidity memory liquidity = protectedLiquidity(_id);

        // verify input
        require(liquidity.provider != address(0), "ERR_INVALID_ID");
        require(_removeTimestamp >= liquidity.timestamp, "ERR_INVALID_TIMESTAMP");

        // calculate the portion of the liquidity to remove
        if (_portion != PPM_RESOLUTION) {
            liquidity.poolAmount = liquidity.poolAmount.mul(_portion) / PPM_RESOLUTION;
            liquidity.reserveAmount = liquidity.reserveAmount.mul(_portion) / PPM_RESOLUTION;
        }

        // get the various rates between the reserves upon adding liquidity and now
        PackedRates memory packedRates =
            packRates(
                liquidity.poolToken,
                liquidity.reserveToken,
                liquidity.reserveRateN,
                liquidity.reserveRateD,
                false
            );

        uint256 targetAmount =
            removeLiquidityTargetAmount(
                liquidity.poolToken,
                liquidity.reserveToken,
                liquidity.poolAmount,
                liquidity.reserveAmount,
                packedRates,
                liquidity.timestamp,
                _removeTimestamp
            );

        // for network token, the return amount is identical to the target amount
        if (liquidity.reserveToken == networkToken) {
            return (targetAmount, targetAmount, 0);
        }

        // handle base token return

        // calculate the amount of pool tokens required for liquidation
        // note that the amount is doubled since it's not possible to liquidate one reserve only
        Fraction memory poolRate = poolTokenRate(liquidity.poolToken, liquidity.reserveToken);
        uint256 poolAmount = targetAmount.mul(poolRate.d).div(poolRate.n / 2);

        // limit the amount of pool tokens by the amount the system/caller holds
        uint256 availableBalance = store.systemBalance(liquidity.poolToken).add(liquidity.poolAmount);
        poolAmount = poolAmount > availableBalance ? availableBalance : poolAmount;

        // calculate the base token amount received by liquidating the pool tokens
        // note that the amount is divided by 2 since the pool amount represents both reserves
        uint256 baseAmount = poolAmount.mul(poolRate.n / 2).div(poolRate.d);
        uint256 networkAmount = getNetworkCompensation(targetAmount, baseAmount, packedRates);

        return (targetAmount, baseAmount, networkAmount);
    }

    /**
     * @dev removes protected liquidity from a pool
     * also burns governance tokens from the caller if the caller removes network tokens
     *
     * @param _id      id in the caller's list of protected liquidity
     * @param _portion portion of liquidity to remove, in PPM
     */
    function removeLiquidity(uint256 _id, uint32 _portion) external validPortion(_portion) protected {
        ProtectedLiquidity memory liquidity = protectedLiquidity(_id, msg.sender);

        // save a local copy of `networkToken`
        IERC20Token networkTokenLocal = networkToken;

        // verify that the pool is whitelisted
        _poolWhitelisted(liquidity.poolToken);

        // verify that the protected liquidity is not removed on the same block in which it was added
        require(liquidity.timestamp < time(), "ERR_TOO_EARLY");

        if (_portion == PPM_RESOLUTION) {
            // remove the protected liquidity from the provider
            store.removeProtectedLiquidity(_id);
        } else {
            // remove a portion of the protected liquidity from the provider
            uint256 fullPoolAmount = liquidity.poolAmount;
            uint256 fullReserveAmount = liquidity.reserveAmount;
            liquidity.poolAmount = liquidity.poolAmount.mul(_portion) / PPM_RESOLUTION;
            liquidity.reserveAmount = liquidity.reserveAmount.mul(_portion) / PPM_RESOLUTION;

            store.updateProtectedLiquidityAmounts(
                _id,
                fullPoolAmount - liquidity.poolAmount,
                fullReserveAmount - liquidity.reserveAmount
            );
        }

        // update last liquidity removal checkpoint
        lastRemoveCheckpointStore.addCheckpoint(msg.sender);

        // add the pool tokens to the system
        store.incSystemBalance(liquidity.poolToken, liquidity.poolAmount);

        // if removing network token liquidity, burn the governance tokens from the caller. we need to transfer the
        // tokens to the contract itself, since only token holders can burn their tokens
        if (liquidity.reserveToken == networkTokenLocal) {
            safeTransferFrom(govToken, msg.sender, address(this), liquidity.reserveAmount);
            govTokenGovernance.burn(liquidity.reserveAmount);
        }

        // get the various rates between the reserves upon adding liquidity and now
        PackedRates memory packedRates =
            packRates(
                liquidity.poolToken,
                liquidity.reserveToken,
                liquidity.reserveRateN,
                liquidity.reserveRateD,
                true
            );

        // get the target token amount
        uint256 targetAmount =
            removeLiquidityTargetAmount(
                liquidity.poolToken,
                liquidity.reserveToken,
                liquidity.poolAmount,
                liquidity.reserveAmount,
                packedRates,
                liquidity.timestamp,
                time()
            );

        // remove network token liquidity
        if (liquidity.reserveToken == networkTokenLocal) {
            // mint network tokens for the caller and lock them
            networkTokenGovernance.mint(address(store), targetAmount);
            settings.incNetworkTokensMinted(liquidity.poolToken, targetAmount);
            lockTokens(msg.sender, targetAmount);
            return;
        }

        // remove base token liquidity

        // calculate the amount of pool tokens required for liquidation
        // note that the amount is doubled since it's not possible to liquidate one reserve only
        Fraction memory poolRate = poolTokenRate(liquidity.poolToken, liquidity.reserveToken);
        uint256 poolAmount = targetAmount.mul(poolRate.d).div(poolRate.n / 2);

        // limit the amount of pool tokens by the amount the system holds
        uint256 systemBalance = store.systemBalance(liquidity.poolToken);
        poolAmount = poolAmount > systemBalance ? systemBalance : poolAmount;

        // withdraw the pool tokens from the store
        store.decSystemBalance(liquidity.poolToken, poolAmount);
        store.withdrawTokens(liquidity.poolToken, address(this), poolAmount);

        // remove liquidity
        removeLiquidity(liquidity.poolToken, poolAmount, liquidity.reserveToken, networkTokenLocal);

        // transfer the base tokens to the caller
        uint256 baseBalance;
        if (liquidity.reserveToken == ETH_RESERVE_ADDRESS) {
            baseBalance = address(this).balance;
            msg.sender.transfer(baseBalance);
        } else {
            baseBalance = liquidity.reserveToken.balanceOf(address(this));
            safeTransfer(liquidity.reserveToken, msg.sender, baseBalance);
        }

        // compensate the caller with network tokens if still needed
        uint256 delta = getNetworkCompensation(targetAmount, baseBalance, packedRates);
        if (delta > 0) {
            // check if there's enough network token balance, otherwise mint more
            uint256 networkBalance = networkTokenLocal.balanceOf(address(this));
            if (networkBalance < delta) {
                networkTokenGovernance.mint(address(this), delta - networkBalance);
            }

            // lock network tokens for the caller
            safeTransfer(networkTokenLocal, address(store), delta);
            lockTokens(msg.sender, delta);
        }

        // if the contract still holds network tokens, burn them
        uint256 networkBalance = networkTokenLocal.balanceOf(address(this));
        if (networkBalance > 0) {
            networkTokenGovernance.burn(networkBalance);
            settings.decNetworkTokensMinted(liquidity.poolToken, networkBalance);
        }
    }

    /**
     * @dev returns the amount the provider will receive for removing liquidity
     * it's also possible to provide the remove liquidity rate & time to get an estimation
     * for the return at that given point
     *
     * @param _poolToken       pool token
     * @param _reserveToken    reserve token
     * @param _poolAmount      pool token amount when the liquidity was added
     * @param _reserveAmount   reserve token amount that was added
     * @param _packedRates     see `struct PackedRates`
     * @param _addTimestamp    time at which the liquidity was added
     * @param _removeTimestamp time at which the liquidity is removed
     * @return amount received for removing liquidity
     */
    function removeLiquidityTargetAmount(
        IDSToken _poolToken,
        IERC20Token _reserveToken,
        uint256 _poolAmount,
        uint256 _reserveAmount,
        PackedRates memory _packedRates,
        uint256 _addTimestamp,
        uint256 _removeTimestamp
    ) internal view returns (uint256) {
        // get the rate between the pool token and the reserve token
        Fraction memory poolRate = poolTokenRate(_poolToken, _reserveToken);

        // get the rate between the reserves upon adding liquidity and now
        Fraction memory addSpotRate = Fraction({ n: _packedRates.addSpotRateN, d: _packedRates.addSpotRateD });
        Fraction memory removeSpotRate = Fraction({ n: _packedRates.removeSpotRateN, d: _packedRates.removeSpotRateD });
        Fraction memory removeAverageRate =
            Fraction({ n: _packedRates.removeAverageRateN, d: _packedRates.removeAverageRateD });

        // calculate the protected amount of reserve tokens plus accumulated fee before compensation
        uint256 total = protectedAmountPlusFee(_poolAmount, poolRate, addSpotRate, removeSpotRate);

        // calculate the impermanent loss
        Fraction memory loss = impLoss(addSpotRate, removeAverageRate);

        // calculate the protection level
        Fraction memory level = protectionLevel(_addTimestamp, _removeTimestamp);

        // calculate the compensation amount
        return compensationAmount(_reserveAmount, Math.max(_reserveAmount, total), loss, level);
    }

    /**
     * @dev allows the caller to claim network token balance that is no longer locked
     * note that the function can revert if the range is too large
     *
     * @param _startIndex  start index in the caller's list of locked balances
     * @param _endIndex    end index in the caller's list of locked balances (exclusive)
     */
    function claimBalance(uint256 _startIndex, uint256 _endIndex) external protected {
        // get the locked balances from the store
        (uint256[] memory amounts, uint256[] memory expirationTimes) =
            store.lockedBalanceRange(msg.sender, _startIndex, _endIndex);

        uint256 totalAmount = 0;
        uint256 length = amounts.length;
        assert(length == expirationTimes.length);

        // reverse iteration since we're removing from the list
        for (uint256 i = length; i > 0; i--) {
            uint256 index = i - 1;
            if (expirationTimes[index] > time()) {
                continue;
            }

            // remove the locked balance item
            store.removeLockedBalance(msg.sender, _startIndex + index);
            totalAmount = totalAmount.add(amounts[index]);
        }

        if (totalAmount > 0) {
            // transfer the tokens to the caller in a single call
            store.withdrawTokens(networkToken, msg.sender, totalAmount);
        }
    }

    /**
     * @dev returns the ROI for removing liquidity in the current state after providing liquidity with the given args
     * the function assumes full protection is in effect
     * return value is in PPM and can be larger than PPM_RESOLUTION for positive ROI, 1M = 0% ROI
     *
     * @param _poolToken       pool token
     * @param _reserveToken    reserve token
     * @param _reserveAmount   reserve token amount that was added
     * @param _poolRateN       rate of 1 pool token in reserve token units when the liquidity was added (numerator)
     * @param _poolRateD       rate of 1 pool token in reserve token units when the liquidity was added (denominator)
     * @param _reserveRateN    rate of 1 reserve token in the other reserve token units when the liquidity was added (numerator)
     * @param _reserveRateD    rate of 1 reserve token in the other reserve token units when the liquidity was added (denominator)
     * @return ROI in PPM
     */
    function poolROI(
        IDSToken _poolToken,
        IERC20Token _reserveToken,
        uint256 _reserveAmount,
        uint256 _poolRateN,
        uint256 _poolRateD,
        uint256 _reserveRateN,
        uint256 _reserveRateD
    ) external view returns (uint256) {
        // calculate the amount of pool tokens based on the amount of reserve tokens
        uint256 poolAmount = _reserveAmount.mul(_poolRateD).div(_poolRateN);

        // get the various rates between the reserves upon adding liquidity and now
        PackedRates memory packedRates = packRates(_poolToken, _reserveToken, _reserveRateN, _reserveRateD, false);

        // get the current return
        uint256 protectedReturn =
            removeLiquidityTargetAmount(
                _poolToken,
                _reserveToken,
                poolAmount,
                _reserveAmount,
                packedRates,
                time().sub(settings.maxProtectionDelay()),
                time()
            );

        // calculate the ROI as the ratio between the current fully protected return and the initial amount
        return protectedReturn.mul(PPM_RESOLUTION).div(_reserveAmount);
    }

    /**
     * @dev utility to protect existing liquidity
     * also mints new governance tokens for the caller when protecting the network token reserve
     *
     * @param _poolAnchor      pool anchor
     * @param _converter       pool converter
     * @param _networkToken    the network reserve token of the pool
     * @param _reserveIndex    index of the reserve to protect
     * @param _poolAmount      amount of pool tokens to protect
     */
    function protectLiquidity(
        IDSToken _poolAnchor,
        IConverter _converter,
        IERC20Token _networkToken,
        uint256 _reserveIndex,
        uint256 _poolAmount
    ) internal {
        // get the reserves token
        IERC20Token reserveToken = _converter.connectorTokens(_reserveIndex);

        // get the pool token rate
        IDSToken poolToken = IDSToken(address(_poolAnchor));
        Fraction memory poolRate = poolTokenRate(poolToken, reserveToken);

        // calculate the reserve balance based on the amount provided and the pool token rate
        uint256 reserveAmount = _poolAmount.mul(poolRate.n).div(poolRate.d);

        // protect the liquidity
        addProtectedLiquidity(msg.sender, poolToken, reserveToken, _poolAmount, reserveAmount);

        // for network token liquidity, mint governance tokens to the caller
        if (reserveToken == _networkToken) {
            govTokenGovernance.mint(msg.sender, reserveAmount);
        }
    }

    /**
     * @dev adds protected liquidity for the caller to the store
     *
     * @param _provider        protected liquidity provider
     * @param _poolToken       pool token
     * @param _reserveToken    reserve token
     * @param _poolAmount      amount of pool tokens to protect
     * @param _reserveAmount   amount of reserve tokens to protect
     * @return new protected liquidity id
     */
    function addProtectedLiquidity(
        address _provider,
        IDSToken _poolToken,
        IERC20Token _reserveToken,
        uint256 _poolAmount,
        uint256 _reserveAmount
    ) internal returns (uint256) {
        Fraction memory rate = reserveTokenAverageRate(_poolToken, _reserveToken, true);
        return
            store.addProtectedLiquidity(
                _provider,
                _poolToken,
                _reserveToken,
                _poolAmount,
                _reserveAmount,
                rate.n,
                rate.d,
                time()
            );
    }

    /**
     * @dev locks network tokens for the provider and emits the tokens locked event
     *
     * @param _provider    tokens provider
     * @param _amount      amount of network tokens
     */
    function lockTokens(address _provider, uint256 _amount) internal {
        uint256 expirationTime = time().add(settings.lockDuration());
        store.addLockedBalance(_provider, _amount, expirationTime);
    }

    /**
     * @dev returns the rate of 1 pool token in reserve token units
     *
     * @param _poolToken       pool token
     * @param _reserveToken    reserve token
     */
    function poolTokenRate(IDSToken _poolToken, IERC20Token _reserveToken) internal view virtual returns (Fraction memory) {
        // get the pool token supply
        uint256 poolTokenSupply = _poolToken.totalSupply();

        // get the reserve balance
        IConverter converter = IConverter(payable(ownedBy(_poolToken)));
        uint256 reserveBalance = converter.getConnectorBalance(_reserveToken);

        // for standard pools, 50% of the pool supply value equals the value of each reserve
        return Fraction({ n: reserveBalance.mul(2), d: poolTokenSupply });
    }

    /**
     * @dev returns the average rate of 1 reserve token in the other reserve token units
     *
     * @param _poolToken            pool token
     * @param _reserveToken         reserve token
     * @param _validateAverageRate  true to validate the average rate; false otherwise
     */
    function reserveTokenAverageRate(
        IDSToken _poolToken,
        IERC20Token _reserveToken,
        bool _validateAverageRate
    ) internal view returns (Fraction memory) {
        (, , uint256 averageRateN, uint256 averageRateD) =
            reserveTokenRates(_poolToken, _reserveToken, _validateAverageRate);
        return Fraction(averageRateN, averageRateD);
    }

    /**
     * @dev returns the spot rate and average rate of 1 reserve token in the other reserve token units
     *
     * @param _poolToken            pool token
     * @param _reserveToken         reserve token
     * @param _validateAverageRate  true to validate the average rate; false otherwise
     */
    function reserveTokenRates(
        IDSToken _poolToken,
        IERC20Token _reserveToken,
        bool _validateAverageRate
    )
        internal
        view
        returns (
            uint256,
            uint256,
            uint256,
            uint256
        )
    {
        ILiquidityPoolConverter converter = ILiquidityPoolConverter(payable(ownedBy(_poolToken)));

        IERC20Token otherReserve = converter.connectorTokens(0);
        if (otherReserve == _reserveToken) {
            otherReserve = converter.connectorTokens(1);
        }

        (uint256 spotRateN, uint256 spotRateD) = converterReserveBalances(converter, otherReserve, _reserveToken);
        (uint256 averageRateN, uint256 averageRateD) = converter.recentAverageRate(_reserveToken);

        require(
            !_validateAverageRate ||
                averageRateInRange(
                    spotRateN,
                    spotRateD,
                    averageRateN,
                    averageRateD,
                    settings.averageRateMaxDeviation()
                ),
            "ERR_INVALID_RATE"
        );

        return (spotRateN, spotRateD, averageRateN, averageRateD);
    }

    /**
     * @dev returns the various rates between the reserves
     *
     * @param _poolToken            pool token
     * @param _reserveToken         reserve token
     * @param _addSpotRateN         add spot rate numerator
     * @param _addSpotRateD         add spot rate denominator
     * @param _validateAverageRate  true to validate the average rate; false otherwise
     * @return see `struct PackedRates`
     */
    function packRates(
        IDSToken _poolToken,
        IERC20Token _reserveToken,
        uint256 _addSpotRateN,
        uint256 _addSpotRateD,
        bool _validateAverageRate
    ) internal view returns (PackedRates memory) {
        (uint256 removeSpotRateN, uint256 removeSpotRateD, uint256 removeAverageRateN, uint256 removeAverageRateD) =
            reserveTokenRates(_poolToken, _reserveToken, _validateAverageRate);

        require(
            (_addSpotRateN <= MAX_UINT128 && _addSpotRateD <= MAX_UINT128) &&
                (removeSpotRateN <= MAX_UINT128 && removeSpotRateD <= MAX_UINT128) &&
                (removeAverageRateN <= MAX_UINT128 && removeAverageRateD <= MAX_UINT128),
            "ERR_INVALID_RATE"
        );

        return
            PackedRates({
                addSpotRateN: uint128(_addSpotRateN),
                addSpotRateD: uint128(_addSpotRateD),
                removeSpotRateN: uint128(removeSpotRateN),
                removeSpotRateD: uint128(removeSpotRateD),
                removeAverageRateN: uint128(removeAverageRateN),
                removeAverageRateD: uint128(removeAverageRateD)
            });
    }

    /**
     * @dev returns whether or not the deviation of the average rate from the spot rate is within range
     * for example, if the maximum permitted deviation is 5%, then return `95/100 <= average/spot <= 100/95`
     *
     * @param _spotRateN       spot rate numerator
     * @param _spotRateD       spot rate denominator
     * @param _averageRateN    average rate numerator
     * @param _averageRateD    average rate denominator
     * @param _maxDeviation    the maximum permitted deviation of the average rate from the spot rate
     */
    function averageRateInRange(
        uint256 _spotRateN,
        uint256 _spotRateD,
        uint256 _averageRateN,
        uint256 _averageRateD,
        uint32 _maxDeviation
    ) internal pure returns (bool) {
        uint256 min =
            _spotRateN.mul(_averageRateD).mul(PPM_RESOLUTION - _maxDeviation).mul(PPM_RESOLUTION - _maxDeviation);
        uint256 mid = _spotRateD.mul(_averageRateN).mul(PPM_RESOLUTION - _maxDeviation).mul(PPM_RESOLUTION);
        uint256 max = _spotRateN.mul(_averageRateD).mul(PPM_RESOLUTION).mul(PPM_RESOLUTION);
        return min <= mid && mid <= max;
    }

    /**
     * @dev utility to add liquidity to a converter
     *
     * @param _converter       converter
     * @param _reserveToken1   reserve token 1
     * @param _reserveToken2   reserve token 2
     * @param _reserveAmount1  reserve amount 1
     * @param _reserveAmount2  reserve amount 2
     * @param _value           ETH amount to add
     */
    function addLiquidity(
        ILiquidityPoolConverter _converter,
        IERC20Token _reserveToken1,
        IERC20Token _reserveToken2,
        uint256 _reserveAmount1,
        uint256 _reserveAmount2,
        uint256 _value
    ) internal {
        // ensure that the contract can receive ETH
        updatingLiquidity = true;

        IERC20Token[] memory reserveTokens = new IERC20Token[](2);
        uint256[] memory amounts = new uint256[](2);
        reserveTokens[0] = _reserveToken1;
        reserveTokens[1] = _reserveToken2;
        amounts[0] = _reserveAmount1;
        amounts[1] = _reserveAmount2;
        _converter.addLiquidity{ value: _value }(reserveTokens, amounts, 1);

        // ensure that the contract can receive ETH
        updatingLiquidity = false;
    }

    /**
     * @dev utility to remove liquidity from a converter
     *
     * @param _poolToken       pool token of the converter
     * @param _poolAmount      amount of pool tokens to remove
     * @param _reserveToken1   reserve token 1
     * @param _reserveToken2   reserve token 2
     */
    function removeLiquidity(
        IDSToken _poolToken,
        uint256 _poolAmount,
        IERC20Token _reserveToken1,
        IERC20Token _reserveToken2
    ) internal {
        ILiquidityPoolConverter converter = ILiquidityPoolConverter(payable(ownedBy(_poolToken)));

        // ensure that the contract can receive ETH
        updatingLiquidity = true;

        IERC20Token[] memory reserveTokens = new IERC20Token[](2);
        uint256[] memory minReturns = new uint256[](2);
        reserveTokens[0] = _reserveToken1;
        reserveTokens[1] = _reserveToken2;
        minReturns[0] = 1;
        minReturns[1] = 1;
        converter.removeLiquidity(_poolAmount, reserveTokens, minReturns);

        // ensure that the contract can receive ETH
        updatingLiquidity = false;
    }

    /**
     * @dev returns a protected liquidity from the store
     *
     * @param _id  protected liquidity id
     * @return protected liquidity
     */
    function protectedLiquidity(uint256 _id) internal view returns (ProtectedLiquidity memory) {
        ProtectedLiquidity memory liquidity;
        (
            liquidity.provider,
            liquidity.poolToken,
            liquidity.reserveToken,
            liquidity.poolAmount,
            liquidity.reserveAmount,
            liquidity.reserveRateN,
            liquidity.reserveRateD,
            liquidity.timestamp
        ) = store.protectedLiquidity(_id);

        return liquidity;
    }

    /**
     * @dev returns a protected liquidity from the store
     *
     * @param _id          protected liquidity id
     * @param _provider    authorized provider
     * @return protected liquidity
     */
    function protectedLiquidity(uint256 _id, address _provider) internal view returns (ProtectedLiquidity memory) {
        ProtectedLiquidity memory liquidity = protectedLiquidity(_id);
        require(liquidity.provider == _provider, "ERR_ACCESS_DENIED");
        return liquidity;
    }

    /**
     * @dev returns the protected amount of reserve tokens plus accumulated fee before compensation
     *
     * @param _poolAmount      pool token amount when the liquidity was added
     * @param _poolRate        rate of 1 pool token in the related reserve token units
     * @param _addRate         rate of 1 reserve token in the other reserve token units when the liquidity was added
     * @param _removeRate      rate of 1 reserve token in the other reserve token units when the liquidity is removed
     * @return protected amount of reserve tokens plus accumulated fee = sqrt(_removeRate / _addRate) * _poolRate * _poolAmount
     */
    function protectedAmountPlusFee(
        uint256 _poolAmount,
        Fraction memory _poolRate,
        Fraction memory _addRate,
        Fraction memory _removeRate
    ) internal pure returns (uint256) {
        uint256 n = Math.ceilSqrt(_addRate.d.mul(_removeRate.n)).mul(_poolRate.n);
        uint256 d = Math.floorSqrt(_addRate.n.mul(_removeRate.d)).mul(_poolRate.d);

        uint256 x = n * _poolAmount;
        if (x / n == _poolAmount) {
            return x / d;
        }

        (uint256 hi, uint256 lo) = n > _poolAmount ? (n, _poolAmount) : (_poolAmount, n);
        (uint256 p, uint256 q) = Math.reducedRatio(hi, d, MAX_UINT256 / lo);
        uint256 min = (hi / d).mul(lo);

        if (q > 0) {
            return Math.max(min, p * lo / q);
        }
        return min;
    }

    /**
     * @dev returns the impermanent loss incurred due to the change in rates between the reserve tokens
     *
     * @param _prevRate    previous rate between the reserves
     * @param _newRate     new rate between the reserves
     * @return impermanent loss (as a ratio)
     */
    function impLoss(Fraction memory _prevRate, Fraction memory _newRate) internal pure returns (Fraction memory) {
        uint256 ratioN = _newRate.n.mul(_prevRate.d);
        uint256 ratioD = _newRate.d.mul(_prevRate.n);

        uint256 prod = ratioN * ratioD;
        uint256 root = prod / ratioN == ratioD ? Math.floorSqrt(prod) : Math.floorSqrt(ratioN) * Math.floorSqrt(ratioD);
        uint256 sum = ratioN.add(ratioD);

        // the arithmetic below is safe because `x + y >= sqrt(x * y) * 2`
        if (sum % 2 == 0) {
            sum /= 2;
            return Fraction({ n: sum - root, d: sum });
        }
        return Fraction({ n: sum - root * 2, d: sum });
    }

    /**
     * @dev returns the protection level based on the timestamp and protection delays
     *
     * @param _addTimestamp    time at which the liquidity was added
     * @param _removeTimestamp time at which the liquidity is removed
     * @return protection level (as a ratio)
     */
    function protectionLevel(uint256 _addTimestamp, uint256 _removeTimestamp) internal view returns (Fraction memory) {
        uint256 timeElapsed = _removeTimestamp.sub(_addTimestamp);
        uint256 minProtectionDelay = settings.minProtectionDelay();
        uint256 maxProtectionDelay = settings.maxProtectionDelay();
        if (timeElapsed < minProtectionDelay) {
            return Fraction({ n: 0, d: 1 });
        }

        if (timeElapsed >= maxProtectionDelay) {
            return Fraction({ n: 1, d: 1 });
        }

        return Fraction({ n: timeElapsed, d: maxProtectionDelay });
    }

    /**
     * @dev returns the compensation amount based on the impermanent loss and the protection level
     *
     * @param _amount  protected amount in units of the reserve token
     * @param _total   amount plus fee in units of the reserve token
     * @param _loss    protection level (as a ratio between 0 and 1)
     * @param _level   impermanent loss (as a ratio between 0 and 1)
     * @return compensation amount
     */
    function compensationAmount(
        uint256 _amount,
        uint256 _total,
        Fraction memory _loss,
        Fraction memory _level
    ) internal pure returns (uint256) {
        uint256 levelN = _level.n.mul(_amount);
        uint256 levelD = _level.d;
        uint256 maxVal = Math.max(Math.max(levelN, levelD), _total);
        (uint256 lossN, uint256 lossD) = Math.reducedRatio(_loss.n, _loss.d, MAX_UINT256 / maxVal);
        return _total.mul(lossD.sub(lossN)).div(lossD).add(lossN.mul(levelN).div(lossD.mul(levelD)));
    }

    function getNetworkCompensation(
        uint256 _targetAmount,
        uint256 _baseAmount,
        PackedRates memory _packedRates
    ) internal view returns (uint256) {
        if (_targetAmount <= _baseAmount) {
            return 0;
        }

        // calculate the delta in network tokens
        uint256 delta =
            (_targetAmount - _baseAmount).mul(_packedRates.removeAverageRateN).div(_packedRates.removeAverageRateD);

        // the delta might be very small due to precision loss
        // in which case no compensation will take place (gas optimization)
        if (delta >= settings.minNetworkCompensation()) {
            return delta;
        }

        return 0;
    }

    /**
     * @dev utility, checks whether allowance for the given spender exists and approves one if it doesn't.
     * note that we use the non standard erc-20 interface in which `approve` has no return value so that
     * this function will work for both standard and non standard tokens
     *
     * @param _token   token to check the allowance in
     * @param _spender approved address
     * @param _value   allowance amount
     */
    function ensureAllowance(
        IERC20Token _token,
        address _spender,
        uint256 _value
    ) private {
        uint256 allowance = _token.allowance(address(this), _spender);
        if (allowance < _value) {
            if (allowance > 0) safeApprove(_token, _spender, 0);
            safeApprove(_token, _spender, _value);
        }
    }

    // utility to get the reserve balances
    function converterReserveBalances(
        IConverter _converter,
        IERC20Token _reserveToken1,
        IERC20Token _reserveToken2
    ) private view returns (uint256, uint256) {
        return (_converter.getConnectorBalance(_reserveToken1), _converter.getConnectorBalance(_reserveToken2));
    }

    // utility to get the owner
    function ownedBy(IOwned _owned) private view returns (address) {
        return _owned.owner();
    }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"contract ILiquidityProtectionSettings","name":"_settings","type":"address"},{"internalType":"contract ILiquidityProtectionStore","name":"_store","type":"address"},{"internalType":"contract ITokenGovernance","name":"_networkTokenGovernance","type":"address"},{"internalType":"contract ITokenGovernance","name":"_govTokenGovernance","type":"address"},{"internalType":"contract ICheckpointStore","name":"_lastRemoveCheckpointStore","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_prevOwner","type":"address"},{"indexed":true,"internalType":"address","name":"_newOwner","type":"address"}],"name":"OwnerUpdate","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"acceptStoreOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IConverterAnchor","name":"_poolAnchor","type":"address"},{"internalType":"contract IERC20Token","name":"_reserveToken","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"addLiquidity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"contract IConverterAnchor","name":"_poolAnchor","type":"address"},{"internalType":"contract IERC20Token","name":"_reserveToken","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"addLiquidityFor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_startIndex","type":"uint256"},{"internalType":"uint256","name":"_endIndex","type":"uint256"}],"name":"claimBalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"govToken","outputs":[{"internalType":"contract IERC20Token","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"govTokenGovernance","outputs":[{"internalType":"contract ITokenGovernance","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastRemoveCheckpointStore","outputs":[{"internalType":"contract ICheckpointStore","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"networkToken","outputs":[{"internalType":"contract IERC20Token","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"networkTokenGovernance","outputs":[{"internalType":"contract ITokenGovernance","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"newOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IDSToken","name":"_poolToken","type":"address"},{"internalType":"contract IERC20Token","name":"_reserveToken","type":"address"},{"internalType":"uint256","name":"_reserveAmount","type":"uint256"},{"internalType":"uint256","name":"_poolRateN","type":"uint256"},{"internalType":"uint256","name":"_poolRateD","type":"uint256"},{"internalType":"uint256","name":"_reserveRateN","type":"uint256"},{"internalType":"uint256","name":"_reserveRateD","type":"uint256"}],"name":"poolROI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IConverterAnchor","name":"_poolAnchor","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"protectLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint32","name":"_portion","type":"uint32"}],"name":"removeLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint32","name":"_portion","type":"uint32"},{"internalType":"uint256","name":"_removeTimestamp","type":"uint256"}],"name":"removeLiquidityReturn","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"settings","outputs":[{"internalType":"contract ILiquidityProtectionSettings","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"store","outputs":[{"internalType":"contract ILiquidityProtectionStore","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferStoreOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id1","type":"uint256"},{"internalType":"uint256","name":"_id2","type":"uint256"}],"name":"unprotectLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

61016060405260016002556003805460ff191690553480156200002157600080fd5b506040516200531538038062005315833981810160405260a08110156200004757600080fd5b508051602082015160408301516060840151608090940151600080546001600160a01b031916331790559293919290919084620000848162000225565b84620000908162000225565b846200009c8162000225565b84620000a88162000225565b88620000b48162000284565b88620000c08162000284565b88620000cc8162000284565b88620000d88162000284565b6001600160601b031960608e811b82166080528d811b821660a0528c901b1660e05260408051637e062a3560e11b815290516001600160a01b038d169163fc0c546a916004808301926020929190829003018186803b1580156200013b57600080fd5b505afa15801562000150573d6000803e3d6000fd5b505050506040513d60208110156200016757600080fd5b50516001600160601b0319606091821b811660c052908b901b166101205260408051637e062a3560e11b815290516001600160a01b038c169163fc0c546a916004808301926020929190829003018186803b158015620001c657600080fd5b505afa158015620001db573d6000803e3d6000fd5b505050506040513d6020811015620001f257600080fd5b50516001600160601b0319606091821b81166101005299901b9098166101405250620002e39a5050505050505050505050565b6001600160a01b03811662000281576040805162461bcd60e51b815260206004820152601360248201527f4552525f494e56414c49445f4144445245535300000000000000000000000000604482015290519081900360640190fd5b50565b6001600160a01b03811630141562000281576040805162461bcd60e51b815260206004820152601360248201527f4552525f414444524553535f49535f53454c4600000000000000000000000000604482015290519081900360640190fd5b60805160601c60a05160601c60c05160601c60e05160601c6101005160601c6101205160601c6101405160601c614ea26200047360003980610f505280611a6c52806120115250806110965280611c7a5280611f7752806126585280613fd452508061050e52806110695280611f4e525080610532528061118d52806116c252806117de5280613ec7528061438e52508061058d528061091e52806109bd5280610b565280610d2d5280611d8352806134e75250806105d8528061064b528061085452806108ef5280610bfe5280610dbd5280610ece5280610fb152806111bc528061131552806113c55280611458528061172852806119c45280611a485280611a98528061207452806120f2528061217052806129045280613005528061362e5280613e1b5280614553528061458252508061123c528061185c5280611b5a5280611c9e52806122dc52806123c25280612e535280612f7052806138e55280613bb75280613c5a5280613f455280614091528061417e528061421e52806142ac52806143ef5250614ea26000f3fe60806040526004361061012e5760003560e01c8063975057e7116100ab578063d4ee1d901161006f578063d4ee1d90146103db578063d79dabae146103f0578063e06174e414610405578063e4a767261461041a578063f04ef41f14610450578063f2fde38b146104805761013d565b8063975057e7146102d5578063c21fe133146102ea578063c2250a99146102ff578063c83df66314610332578063caee4c8f1461039f5761013d565b80636d533e9b116100f25780636d533e9b14610206578063782ed90c1461026057806379ba50971461029657806389d94b46146102ab5780638da5cb5b146102c05761013d565b806305268cff146101425780630529fa3d1461017357806315ee207d14610188578063630d8c63146101c15780636ca95a4e146101f15761013d565b3661013d5761013b6104b3565b005b600080fd5b34801561014e57600080fd5b5061015761050c565b604080516001600160a01b039092168252519081900360200190f35b34801561017f57600080fd5b50610157610530565b34801561019457600080fd5b5061013b600480360360408110156101ab57600080fd5b506001600160a01b038135169060200135610554565b3480156101cd57600080fd5b5061013b600480360360408110156101e457600080fd5b508035906020013561060c565b3480156101fd57600080fd5b506101576109bb565b34801561021257600080fd5b506102426004803603606081101561022957600080fd5b5080359063ffffffff60208201351690604001356109df565b60408051938452602084019290925282820152519081900360600190f35b34801561026c57600080fd5b5061013b6004803603604081101561028357600080fd5b508035906020013563ffffffff16610cff565b3480156102a257600080fd5b5061013b611903565b3480156102b757600080fd5b5061013b6119ba565b3480156102cc57600080fd5b50610157611a37565b3480156102e157600080fd5b50610157611a46565b3480156102f657600080fd5b50610157611a6a565b34801561030b57600080fd5b5061013b6004803603602081101561032257600080fd5b50356001600160a01b0316611a8e565b34801561033e57600080fd5b5061038d600480360360e081101561035557600080fd5b506001600160a01b03813581169160208101359091169060408101359060608101359060808101359060a08101359060c00135611b20565b60408051918252519081900360200190f35b61038d600480360360808110156103b557600080fd5b506001600160a01b03813581169160208101358216916040820135169060600135611c15565b3480156103e757600080fd5b50610157611c69565b3480156103fc57600080fd5b50610157611c78565b34801561041157600080fd5b50610157611c9c565b61038d6004803603606081101561043057600080fd5b506001600160a01b03813581169160208101359091169060400135611cc0565b34801561045c57600080fd5b5061013b6004803603604081101561047357600080fd5b5080359060200135611d08565b34801561048c57600080fd5b5061013b600480360360208110156104a357600080fd5b50356001600160a01b0316612214565b60035460ff1661050a576040805162461bcd60e51b815260206004820152601a60248201527f4552525f4e4f545f5550444154494e475f4c4951554944495459000000000000604482015290519081900360640190fd5b565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b61055c612292565b600280558161056a816122da565b82610574816123c0565b8261057e816124aa565b6000610589866124f0565b90507f0000000000000000000000000000000000000000000000000000000000000000866105be818484600060028c0461255d565b6105d1818484600160028c048c0361255d565b6105fd81337f00000000000000000000000000000000000000000000000000000000000000008a6126c3565b50506001600255505050505050565b610614612292565b6002805560408051637a1036f560e11b81523360048201526024810184905260448101839052905160609182916001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169163f4206dea916064808301926000929190829003018186803b15801561069157600080fd5b505afa1580156106a5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160409081528110156106ce57600080fd5b81019080805160405193929190846401000000008211156106ee57600080fd5b90830190602082018581111561070357600080fd5b825186602082028301116401000000008211171561072057600080fd5b82525081516020918201928201910280838360005b8381101561074d578181015183820152602001610735565b505050509050016040526020018051604051939291908464010000000082111561077657600080fd5b90830190602082018581111561078b57600080fd5b82518660208202830111640100000000821117156107a857600080fd5b82525081516020918201928201910280838360005b838110156107d55781810151838201526020016107bd565b505050509050016040525050509150915060008083519050825181146107f757fe5b805b80156108e657600019810161080c612836565b85828151811061081857fe5b6020026020010151111561082c57506108dd565b604080516390e0661b60e01b8152336004820152898301602482015290516001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916390e0661b91604480830192600092919082900301818387803b15801561089b57600080fd5b505af11580156108af573d6000803e3d6000fd5b505050506108d98682815181106108c257fe5b60200260200101518561283a90919063ffffffff16565b9350505b600019016107f9565b5081156109ae577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316635e35359e7f000000000000000000000000000000000000000000000000000000000000000033856040518463ffffffff1660e01b815260040180846001600160a01b03168152602001836001600160a01b031681526020018281526020019350505050600060405180830381600087803b15801561099557600080fd5b505af11580156109a9573d6000803e3d6000fd5b505050505b5050600160025550505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000806000846109ee8161288c565b6109f6614dbd565b6109ff886128f2565b80519091506001600160a01b0316610a4f576040805162461bcd60e51b815260206004820152600e60248201526d11549497d253959053125117d25160921b604482015290519081900360640190fd5b8060e00151861015610aa0576040805162461bcd60e51b815260206004820152601560248201527404552525f494e56414c49445f54494d455354414d5605c1b604482015290519081900360640190fd5b63ffffffff8716620f424014610b05576060810151620f424090610acd9063ffffffff8a8116906129fc16565b81610ad457fe5b0460608201526080810151620f424090610af79063ffffffff8a8116906129fc16565b81610afe57fe5b0460808201525b610b0d614e1d565b610b2b826020015183604001518460a001518560c001516000612a5a565b90506000610b528360200151846040015185606001518660800151868860e001518e612b6f565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031683604001516001600160a01b03161415610ba357955085945060009350610cf5915050565b610bab614e52565b610bbd84602001518560400151612c85565b90506000610bf06002836000015181610bd257fe5b04610bea8460200151866129fc90919063ffffffff16565b90612daa565b90506000610c9f86606001517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316635121220c89602001516040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015610c6d57600080fd5b505afa158015610c81573d6000803e3d6000fd5b505050506040513d6020811015610c9757600080fd5b50519061283a565b9050808211610cae5781610cb0565b805b91506000610cd68460200151610bea6002876000015181610ccd57fe5b879190046129fc565b90506000610ce5868389612e09565b959b509099509397505050505050505b5093509350939050565b80610d098161288c565b610d11612292565b60028055610d1d614dbd565b610d278433612ef0565b905060007f00000000000000000000000000000000000000000000000000000000000000009050610d5b82602001516123c0565b610d63612836565b8260e0015110610daa576040805162461bcd60e51b815260206004820152600d60248201526c4552525f544f4f5f4541524c5960981b604482015290519081900360640190fd5b63ffffffff8416620f42401415610e3e577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636f366b71866040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015610e2157600080fd5b505af1158015610e35573d6000803e3d6000fd5b50505050610f30565b60608201516080830151620f4240610e5f8363ffffffff898116906129fc16565b81610e6657fe5b0460608501526080840151620f424090610e899063ffffffff898116906129fc16565b81610e9057fe5b046080850181905260608501516040805163161139bd60e31b8152600481018b90529185036024830152918303604482015290516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169163b089cde891606480830192600092919082900301818387803b158015610f1557600080fd5b505af1158015610f29573d6000803e3d6000fd5b5050505050505b60408051631d092adf60e01b815233600482015290516001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691631d092adf91602480830192600092919082900301818387803b158015610f9757600080fd5b505af1158015610fab573d6000803e3d6000fd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663332100fa836020015184606001516040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561102e57600080fd5b505af1158015611042573d6000803e3d6000fd5b50505050806001600160a01b031682604001516001600160a01b03161415611117576110947f0000000000000000000000000000000000000000000000000000000000000000333085608001516126c3565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166342966c6883608001516040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b1580156110fe57600080fd5b505af1158015611112573d6000803e3d6000fd5b505050505b61111f614e1d565b61113d836020015184604001518560a001518660c001516001612a5a565b9050600061116b8460200151856040015186606001518760800151868960e00151611166612836565b612b6f565b9050826001600160a01b031684604001516001600160a01b031614156112e0577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166340c10f197f0000000000000000000000000000000000000000000000000000000000000000836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561122257600080fd5b505af1158015611236573d6000803e3d6000fd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663deacd84e8560200151836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b1580156112b557600080fd5b505af11580156112c9573d6000803e3d6000fd5b505050506112d73382612f69565b505050506118f9565b6112e8614e52565b6112fa85602001518660400151612c85565b9050600061130f6002836000015181610bd257fe5b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316635121220c88602001516040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561138457600080fd5b505afa158015611398573d6000803e3d6000fd5b505050506040513d60208110156113ae57600080fd5b505190508082116113bf57816113c1565b805b91507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166319c6a5e48860200151846040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561143e57600080fd5b505af1158015611452573d6000803e3d6000fd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316635e35359e886020015130856040518463ffffffff1660e01b815260040180846001600160a01b03168152602001836001600160a01b031681526020018281526020019350505050600060405180830381600087803b1580156114e257600080fd5b505af11580156114f6573d6000803e3d6000fd5b5050505061150e8760200151838960400151896130ac565b60408701516000906001600160a01b031673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee141561157057506040514790339082156108fc029083906000818181858888f1935050505015801561156a573d6000803e3d6000fd5b50611600565b87604001516001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156115c157600080fd5b505afa1580156115d5573d6000803e3d6000fd5b505050506040513d60208110156115eb57600080fd5b50516040890151909150611600903383613299565b600061160d868389612e09565b90508015611759576000886001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561166457600080fd5b505afa158015611678573d6000803e3d6000fd5b505050506040513d602081101561168e57600080fd5b505190508181101561172257604080516340c10f1960e01b8152306004820152828403602482015290516001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916340c10f1991604480830192600092919082900301818387803b15801561170957600080fd5b505af115801561171d573d6000803e3d6000fd5b505050505b61174d897f000000000000000000000000000000000000000000000000000000000000000084613299565b6117573383612f69565b505b6000886001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156117a857600080fd5b505afa1580156117bc573d6000803e3d6000fd5b505050506040513d60208110156117d257600080fd5b5051905080156118ee577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166342966c68826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561184257600080fd5b505af1158015611856573d6000803e3d6000fd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663802fa3ba8b60200151836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b1580156118d557600080fd5b505af11580156118e9573d6000803e3d6000fd5b505050505b505050505050505050505b5050600160025550565b6001546001600160a01b03163314611956576040805162461bcd60e51b815260206004820152601160248201527011549497d050d0d154d4d7d11153925151607a1b604482015290519081900360640190fd5b600154600080546040516001600160a01b0393841693909116917f343765429aea5a34b3ff6a3785a98a5abb2597aca87bfbb58632c173d585373a91a360018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b6119c26133f2565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166379ba50976040518163ffffffff1660e01b8152600401600060405180830381600087803b158015611a1d57600080fd5b505af1158015611a31573d6000803e3d6000fd5b50505050565b6000546001600160a01b031681565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b611a966133f2565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f2fde38b826040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050600060405180830381600087803b158015611b0557600080fd5b505af1158015611b19573d6000803e3d6000fd5b5050505050565b600080611b3186610bea89886129fc565b9050611b3b614e1d565b611b498a8a87876000612a5a565b90506000611bf38b8b858c86611beb7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ce3f3adb6040518163ffffffff1660e01b815260040160206040518083038186803b158015611bb157600080fd5b505afa158015611bc5573d6000803e3d6000fd5b505050506040513d6020811015611bdb57600080fd5b5051611be5612836565b90613445565b611166612836565b9050611c0689610bea83620f42406129fc565b9b9a5050505050505050505050565b6000611c1f612292565b6002805584611c2d81613492565b84611c37816122da565b85611c41816123c0565b84611c4b816124aa565b611c57898989896134e3565b60016002559998505050505050505050565b6001546001600160a01b031681565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000611cca612292565b6002805583611cd8816122da565b84611ce2816123c0565b83611cec816124aa565b611cf8338888886134e3565b6001600255979650505050505050565b611d10612292565b6002805581811415611d57576040805162461bcd60e51b815260206004820152600b60248201526a11549497d4d0535157d25160aa1b604482015290519081900360640190fd5b611d5f614dbd565b611d698333612ef0565b9050611d73614dbd565b611d7d8333612ef0565b905060007f0000000000000000000000000000000000000000000000000000000000000000905081602001516001600160a01b031683602001516001600160a01b0316148015611de7575081604001516001600160a01b031683604001516001600160a01b031614155b8015611e275750806001600160a01b031683604001516001600160a01b03161480611e275750806001600160a01b031682604001516001600160a01b0316145b8015611e3a57508160e001518360e00151145b8015611e5957506060820151611e5190600161283a565b836060015111155b8015611e7857506060830151611e7090600161283a565b826060015111155b611ec9576040805162461bcd60e51b815260206004820152601860248201527f4552525f50524f54454354494f4e535f4d49534d415443480000000000000000604482015290519081900360640190fd5b611ed1612836565b8360e0015110611f18576040805162461bcd60e51b815260206004820152600d60248201526c4552525f544f4f5f4541524c5960981b604482015290519081900360640190fd5b6000816001600160a01b031684604001516001600160a01b031614611f41578260800151611f47565b83608001515b9050611f757f00000000000000000000000000000000000000000000000000000000000000003330846126c3565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166342966c68826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015611fdb57600080fd5b505af1158015611fef573d6000803e3d6000fd5b505060408051631d092adf60e01b815233600482015290516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169350631d092adf9250602480830192600092919082900301818387803b15801561205a57600080fd5b505af115801561206e573d6000803e3d6000fd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636f366b71876040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b1580156120d857600080fd5b505af11580156120ec573d6000803e3d6000fd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636f366b71866040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561215657600080fd5b505af115801561216a573d6000803e3d6000fd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316635e35359e8560200151336121be8760600151896060015161283a90919063ffffffff16565b6040518463ffffffff1660e01b815260040180846001600160a01b03168152602001836001600160a01b031681526020018281526020019350505050600060405180830381600087803b15801561099557600080fd5b61221c6133f2565b6000546001600160a01b0382811691161415612270576040805162461bcd60e51b815260206004820152600e60248201526d22a9292fa9a0a6a2afa7aba722a960911b604482015290519081900360640190fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b60016002541461050a576040805162461bcd60e51b815260206004820152600e60248201526d4552525f5245454e5452414e435960901b604482015290519081900360640190fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d4f63148826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561234757600080fd5b505afa15801561235b573d6000803e3d6000fd5b505050506040513d602081101561237157600080fd5b50516123bd576040805162461bcd60e51b815260206004820152601660248201527511549497d413d3d317d393d517d4d5541413d495115160521b604482015290519081900360640190fd5b50565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316632b26a982826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561242d57600080fd5b505afa158015612441573d6000803e3d6000fd5b505050506040513d602081101561245757600080fd5b50516123bd576040805162461bcd60e51b815260206004820152601860248201527f4552525f504f4f4c5f4e4f545f57484954454c49535445440000000000000000604482015290519081900360640190fd5b600081116123bd576040805162461bcd60e51b815260206004820152600e60248201526d4552525f5a45524f5f56414c554560901b604482015290519081900360640190fd5b6000816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561252b57600080fd5b505afa15801561253f573d6000803e3d6000fd5b505050506040513d602081101561255557600080fd5b505192915050565b6000846001600160a01b03166319b64015846040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156125a357600080fd5b505afa1580156125b7573d6000803e3d6000fd5b505050506040513d60208110156125cd57600080fd5b50519050856125da614e52565b6125e48284612c85565b905060006126078260200151610bea8460000151886129fc90919063ffffffff16565b90506126163384868885613614565b50866001600160a01b0316846001600160a01b031614156126b857604080516340c10f1960e01b81523360048201526024810183905290516001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916340c10f1991604480830192600092919082900301818387803b15801561269f57600080fd5b505af11580156126b3573d6000803e3d6000fd5b505050505b505050505050505050565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b17815292518251600094606094938a169392918291908083835b602083106127485780518252601f199092019160209182019101612729565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146127aa576040519150601f19603f3d011682016040523d82523d6000602084013e6127af565b606091505b50915091508180156127dd5750805115806127dd57508080602001905160208110156127da57600080fd5b50515b61282e576040805162461bcd60e51b815260206004820152601860248201527f4552525f5452414e534645525f46524f4d5f4641494c45440000000000000000604482015290519081900360640190fd5b505050505050565b4290565b600082820183811015612883576040805162461bcd60e51b815260206004820152600c60248201526b4552525f4f564552464c4f5760a01b604482015290519081900360640190fd5b90505b92915050565b60008163ffffffff161180156128ab5750620f424063ffffffff821611155b6123bd576040805162461bcd60e51b815260206004820152601360248201527222a9292fa4a72b20a624a22fa827a92a24a7a760691b604482015290519081900360640190fd5b6128fa614dbd565b612902614dbd565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316635290ffbb846040518263ffffffff1660e01b8152600401808281526020019150506101006040518083038186803b15801561296757600080fd5b505afa15801561297b573d6000803e3d6000fd5b505050506040513d61010081101561299257600080fd5b50805160208083015160408085015160608087015160808089015160a0808b015160c0808d015160e09d8e01519d8f019d909d528d019b909b528b01999099528901979097528701959095526001600160a01b039485169086015283169084015216815292915050565b600082612a0b57506000612886565b82820282848281612a1857fe5b0414612883576040805162461bcd60e51b815260206004820152600c60248201526b4552525f4f564552464c4f5760a01b604482015290519081900360640190fd5b612a62614e1d565b600080600080612a738a8a8861372b565b93509350935093506001600160801b038811158015612a9957506001600160801b038711155b8015612abe57506001600160801b038411158015612abe57506001600160801b038311155b8015612ae357506001600160801b038211158015612ae357506001600160801b038111155b612b27576040805162461bcd60e51b815260206004820152601060248201526f4552525f494e56414c49445f5241544560801b604482015290519081900360640190fd5b6040805160c0810182526001600160801b03998a16815297891660208901529388169387019390935290861660608601528516608085015290931660a0830152509392505050565b6000612b79614e52565b612b838989612c85565b9050612b8d614e52565b604051806040016040528087600001516001600160801b0316815260200187602001516001600160801b03168152509050612bc6614e52565b50604080518082018252908701516001600160801b0390811682526060880151166020820152612bf4614e52565b604051806040016040528089608001516001600160801b031681526020018960a001516001600160801b031681525090506000612c338b8686866139c4565b9050612c3d614e52565b612c478584613acf565b9050612c51614e52565b612c5b8a8a613b9d565b9050612c728c612c6b8e86613d39565b8484613d4f565b9f9e505050505050505050505050505050565b612c8d614e52565b6000836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015612cc857600080fd5b505afa158015612cdc573d6000803e3d6000fd5b505050506040513d6020811015612cf257600080fd5b505190506000612d01856124f0565b90506000816001600160a01b031663d8959512866040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015612d5257600080fd5b505afa158015612d66573d6000803e3d6000fd5b505050506040513d6020811015612d7c57600080fd5b50516040805180820190915290915080612d978360026129fc565b8152602001939093525090949350505050565b6000808211612df5576040805162461bcd60e51b81526020600482015260126024820152714552525f4449564944455f42595f5a45524f60701b604482015290519081900360640190fd5b6000828481612e0057fe5b04949350505050565b6000828411612e1a57506000612ee9565b6000612e4f8360a001516001600160801b0316610bea85608001516001600160801b03168789036129fc90919063ffffffff16565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a80c76ff6040518163ffffffff1660e01b815260040160206040518083038186803b158015612eaa57600080fd5b505afa158015612ebe573d6000803e3d6000fd5b505050506040513d6020811015612ed457600080fd5b50518110612ee3579050612ee9565b60009150505b9392505050565b612ef8614dbd565b612f00614dbd565b612f09846128f2565b9050826001600160a01b031681600001516001600160a01b031614612883576040805162461bcd60e51b815260206004820152601160248201527011549497d050d0d154d4d7d11153925151607a1b604482015290519081900360640190fd5b60006130017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663045544436040518163ffffffff1660e01b815260040160206040518083038186803b158015612fc757600080fd5b505afa158015612fdb573d6000803e3d6000fd5b505050506040513d6020811015612ff157600080fd5b5051612ffb612836565b9061283a565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663dbae3a5d8484846040518463ffffffff1660e01b815260040180846001600160a01b031681526020018381526020018281526020019350505050602060405180830381600087803b15801561308257600080fd5b505af1158015613096573d6000803e3d6000fd5b505050506040513d6020811015611b1957600080fd5b60006130b7856124f0565b6003805460ff1916600117905560408051600280825260608083018452939450909160208301908036833750506040805160028082526060808301845294955090925090602083019080368337019050509050848260008151811061311857fe5b60200260200101906001600160a01b031690816001600160a01b031681525050838260018151811061314657fe5b60200260200101906001600160a01b031690816001600160a01b03168152505060018160008151811061317557fe5b60200260200101818152505060018160018151811061319057fe5b602002602001018181525050826001600160a01b031663b127c0a58784846040518463ffffffff1660e01b8152600401808481526020018060200180602001838103835285818151815260200191508051906020019060200280838360005b838110156132075781810151838201526020016131ef565b50505050905001838103825284818151815260200191508051906020019060200280838360005b8381101561324657818101518382015260200161322e565b5050505090500195505050505050600060405180830381600087803b15801561326e57600080fd5b505af1158015613282573d6000803e3d6000fd5b50506003805460ff19169055505050505050505050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b178152925182516000946060949389169392918291908083835b602083106133165780518252601f1990920191602091820191016132f7565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114613378576040519150601f19603f3d011682016040523d82523d6000602084013e61337d565b606091505b50915091508180156133ab5750805115806133ab57508080602001905160208110156133a857600080fd5b50515b611b19576040805162461bcd60e51b815260206004820152601360248201527211549497d514905394d1915497d19052531151606a1b604482015290519081900360640190fd5b6000546001600160a01b0316331461050a576040805162461bcd60e51b815260206004820152601160248201527011549497d050d0d154d4d7d11153925151607a1b604482015290519081900360640190fd5b60008183101561348c576040805162461bcd60e51b815260206004820152600d60248201526c4552525f554e444552464c4f5760981b604482015290519081900360640190fd5b50900390565b6001600160a01b0381166123bd576040805162461bcd60e51b81526020600482015260136024820152724552525f494e56414c49445f4144445245535360681b604482015290519081900360640190fd5b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03848116908216141561357b573415613567576040805162461bcd60e51b815260206004820152601760248201527608aa4a4be8aa890be829a9eaa9ca8be9a92a69a82a8869604b1b604482015290519081900360640190fd5b61357386868386613ddf565b91505061360c565b60006001600160a01b03851673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee146135a85760006135aa565b835b90508034146135fa576040805162461bcd60e51b815260206004820152601760248201527608aa4a4be8aa890be829a9eaa9ca8be9a92a69a82a8869604b1b604482015290519081900360640190fd5b613607878787858861406e565b925050505b949350505050565b600061361e614e52565b61362a86866001614635565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166361d5f087888888888887600001518860200151613672612836565b6040518963ffffffff1660e01b815260040180896001600160a01b03168152602001886001600160a01b03168152602001876001600160a01b0316815260200186815260200185815260200184815260200183815260200182815260200198505050505050505050602060405180830381600087803b1580156136f457600080fd5b505af1158015613708573d6000803e3d6000fd5b505050506040513d602081101561371e57600080fd5b5051979650505050505050565b600080600080600061373c886124f0565b90506000816001600160a01b03166319b6401560006040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561378557600080fd5b505afa158015613799573d6000803e3d6000fd5b505050506040513d60208110156137af57600080fd5b505190506001600160a01b03808216908916141561383b57816001600160a01b03166319b6401560016040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561380c57600080fd5b505afa158015613820573d6000803e3d6000fd5b505050506040513d602081101561383657600080fd5b505190505b60008061384984848c614669565b91509150600080856001600160a01b0316631f0181bc8d6040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050604080518083038186803b15801561389c57600080fd5b505afa1580156138b0573d6000803e3d6000fd5b505050506040513d60408110156138c657600080fd5b50805160209091015190925090508a158061396d575061396d848484847f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166324a088686040518163ffffffff1660e01b815260040160206040518083038186803b15801561393c57600080fd5b505afa158015613950573d6000803e3d6000fd5b505050506040513d602081101561396657600080fd5b5051614769565b6139b1576040805162461bcd60e51b815260206004820152601060248201526f4552525f494e56414c49445f5241544560801b604482015290519081900360640190fd5b929c919b50995090975095505050505050565b82518151602084015160009283926139ef926139e9916139e491906129fc565b614806565b906129fc565b90506000613a1e86602001516139e9613a19876020015189600001516129fc90919063ffffffff16565b614826565b905081870287838281613a2d57fe5b041415613a4857818181613a3d57fe5b04935050505061360c565b600080898511613a59578985613a5c565b848a5b91509150600080613a7984878560001981613a7357fe5b0461487c565b915091506000613a9384888781613a8c57fe5b04906129fc565b90508115613abf57613ab0818386860281613aaa57fe5b04613d39565b9850505050505050505061360c565b9c9b505050505050505050505050565b613ad7614e52565b60208301518251600091613aeb91906129fc565b84516020850151919250600091613b01916129fc565b9050818102600082848381613b1257fe5b0414613b3057613b2183614826565b613b2a85614826565b02613b39565b613b3982614826565b90506000613b47858561283a565b905060028106613b7a57600281049050604051806040016040528083830381526020018281525095505050505050612886565b604080518082019091526002909202810382526020820152935050505092915050565b613ba5614e52565b6000613bb18385613445565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316632c560f896040518163ffffffff1660e01b815260040160206040518083038186803b158015613c0e57600080fd5b505afa158015613c22573d6000803e3d6000fd5b505050506040513d6020811015613c3857600080fd5b50516040805163ce3f3adb60e01b815290519192506000916001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169163ce3f3adb916004808301926020929190829003018186803b158015613ca057600080fd5b505afa158015613cb4573d6000803e3d6000fd5b505050506040513d6020811015613cca57600080fd5b5051905081831015613cf75760405180604001604052806000815260200160018152509350505050612886565b808310613d1f5760405180604001604052806001815260200160018152509350505050612886565b604080518082019091529283526020830152509392505050565b6000818311613d485781612883565b5090919050565b80516000908190613d6090876129fc565b60208401519091506000613d7d613d778484613d39565b88613d39565b9050600080613d9a886000015189602001518560001981613a7357fe5b9092509050613dd1613db9613daf83876129fc565b610bea85896129fc565b612ffb83610bea613dca8288613445565b8e906129fc565b9a9950505050505050505050565b600083613dea614e52565b613df48286612c85565b90506000613e178260000151610bea8460200151886129fc90919063ffffffff16565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166319c6a5e484836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b158015613e9057600080fd5b505af1158015613ea4573d6000803e3d6000fd5b505050506000613eb7898589858a613614565b9050613ec5873330896126c3565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166342966c68876040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015613f2b57600080fd5b505af1158015613f3f573d6000803e3d6000fd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663802fa3ba89886040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b158015613fba57600080fd5b505af1158015613fce573d6000803e3d6000fd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166340c10f198a886040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561404957600080fd5b505af115801561405d573d6000803e3d6000fd5b50929b9a5050505050505050505050565b6000848161407b826124f0565b905060008061408b838989614669565b915091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166312588d0e6040518163ffffffff1660e01b815260040160206040518083038186803b1580156140e857600080fd5b505afa1580156140fc573d6000803e3d6000fd5b505050506040513d602081101561411257600080fd5b5051811015614168576040805162461bcd60e51b815260206004820152601860248201527f4552525f4e4f545f454e4f5547485f4c49515549444954590000000000000000604482015290519081900360640190fd5b600061417883610bea89856129fc565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663943fd08a8c6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156141e957600080fd5b505afa1580156141fd573d6000803e3d6000fd5b505050506040513d602081101561421357600080fd5b50519050806142a4577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b97b55ce6040518163ffffffff1660e01b815260040160206040518083038186803b15801561427557600080fd5b505afa158015614289573d6000803e3d6000fd5b505050506040513d602081101561429f57600080fd5b505190505b6000614317837f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663350ed8e78f6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015610c6d57600080fd5b905081811115614367576040805162461bcd60e51b815260206004820152601660248201527511549497d3505617d05353d5539517d4915050d2115160521b604482015290519081900360640190fd5b604080516340c10f1960e01b81523060048201526024810185905290516001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916340c10f1991604480830192600092919082900301818387803b1580156143d557600080fd5b505af11580156143e9573d6000803e3d6000fd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663deacd84e8d856040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561446457600080fd5b505af1158015614478573d6000803e3d6000fd5b505050506144878a87856148c4565b6001600160a01b038b1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee146144c2576144b78b33308c6126c3565b6144c28b878b6148c4565b6144d0868c8c8c8734614968565b6000876001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561451f57600080fd5b505afa158015614533573d6000803e3d6000fd5b505050506040513d602081101561454957600080fd5b50519050614578887f000000000000000000000000000000000000000000000000000000000000000083613299565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663332100fa896002840484036040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b1580156145f457600080fd5b505af1158015614608573d6000803e3d6000fd5b505050506146238e898e6002858161461c57fe5b048e613614565b9e9d5050505050505050505050505050565b61463d614e52565b60008061464b86868661372b565b60408051808201909152918252602082015298975050505050505050565b600080846001600160a01b031663d8959512856040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156146b957600080fd5b505afa1580156146cd573d6000803e3d6000fd5b505050506040513d60208110156146e357600080fd5b505160408051636c4aca8960e11b81526001600160a01b03868116600483015291519188169163d895951291602480820192602092909190829003018186803b15801561472f57600080fd5b505afa158015614743573d6000803e3d6000fd5b505050506040513d602081101561475957600080fd5b505190925090505b935093915050565b60008061479d83620f42400363ffffffff166139e985620f42400363ffffffff166139e9888c6129fc90919063ffffffff16565b905060006147d0620f424063ffffffff166139e986620f42400363ffffffff166139e98a8c6129fc90919063ffffffff16565b905060006147e7620f42406139e981818d8b6129fc565b90508183111580156147f95750808211155b9998505050505050505050565b60008061481283614826565b905082818202146128865780600101612ee9565b6000806002830460010190506000600282858161483f57fe5b0483018161484957fe5b0490505b8082111561487557809150600282858161486357fe5b0483018161486d57fe5b04905061484d565b5092915050565b60008084848482118061488e57508481115b156148a45761489e828287614b47565b90925090505b8082146148b5579092509050614761565b50600196879650945050505050565b60408051636eb1769f60e11b81523060048201526001600160a01b038481166024830152915160009286169163dd62ed3e916044808301926020929190829003018186803b15801561491557600080fd5b505afa158015614929573d6000803e3d6000fd5b505050506040513d602081101561493f57600080fd5b5051905081811015611a3157801561495d5761495d84846000614b80565b611a31848484614b80565b6003805460ff1916600117905560408051600280825260608083018452926020830190803683375050604080516002808252606080830184529495509092509060208301908036833701905050905086826000815181106149c557fe5b60200260200101906001600160a01b031690816001600160a01b03168152505085826001815181106149f357fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508481600081518110614a2157fe5b6020026020010181815250508381600181518110614a3b57fe5b602002602001018181525050876001600160a01b0316637d8916bd84848460016040518563ffffffff1660e01b8152600401808060200180602001848152602001838103835286818151815260200191508051906020019060200280838360005b83811015614ab4578181015183820152602001614a9c565b50505050905001838103825285818151815260200191508051906020019060200280838360005b83811015614af3578181015183820152602001614adb565b50505050905001955050505050506000604051808303818588803b158015614b1a57600080fd5b505af1158015614b2e573d6000803e3d6000fd5b50506003805460ff191690555050505050505050505050565b600080838511614b6557614b5c858585614cd8565b91509150614761565b600080614b73868887614cd8565b9890975095505050505050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663095ea7b360e01b178152925182516000946060949389169392918291908083835b60208310614bfd5780518252601f199092019160209182019101614bde565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114614c5f576040519150601f19603f3d011682016040523d82523d6000602084013e614c64565b606091505b5091509150818015614c92575080511580614c925750808060200190516020811015614c8f57600080fd5b50515b611b19576040805162461bcd60e51b815260206004820152601260248201527111549497d054141493d59157d1905253115160721b604482015290519081900360640190fd5b60008060008360001981614ce857fe5b04905080861115614d21576000816001018781614d0157fe5b046001019050808781614d1057fe5b049650808681614d1c57fe5b049550505b848614614d8157858402858701878110614d52576000614d418383614d91565b955050508385039250614761915050565b6002888803048703821015614d705760008694509450505050614761565b600180870394509450505050614761565b5050600290910493849350915050565b6000600282048203828481614da257fe5b0681614daa57fe5b04828481614db457fe5b04019392505050565b60405180610100016040528060006001600160a01b0316815260200160006001600160a01b0316815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a081019190915290565b60405180604001604052806000815260200160008152509056fea26469706673582212200b0235110436240d15a64b82ece6b9e600dccbb988885efc5c5e63d964f6343d64736f6c634300060c0033000000000000000000000000d444ec18952c7caf09636f21807683dacc1d7da9000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb55000000000000000000000000a489c2b5b36835a327851ab917a80562b5afc2440000000000000000000000000887ae1251e180d7d453aedebee26e1639f20113000000000000000000000000f8a2fb650e25a26ce839d64be8a0abbcb0b87b32

Deployed Bytecode

0x60806040526004361061012e5760003560e01c8063975057e7116100ab578063d4ee1d901161006f578063d4ee1d90146103db578063d79dabae146103f0578063e06174e414610405578063e4a767261461041a578063f04ef41f14610450578063f2fde38b146104805761013d565b8063975057e7146102d5578063c21fe133146102ea578063c2250a99146102ff578063c83df66314610332578063caee4c8f1461039f5761013d565b80636d533e9b116100f25780636d533e9b14610206578063782ed90c1461026057806379ba50971461029657806389d94b46146102ab5780638da5cb5b146102c05761013d565b806305268cff146101425780630529fa3d1461017357806315ee207d14610188578063630d8c63146101c15780636ca95a4e146101f15761013d565b3661013d5761013b6104b3565b005b600080fd5b34801561014e57600080fd5b5061015761050c565b604080516001600160a01b039092168252519081900360200190f35b34801561017f57600080fd5b50610157610530565b34801561019457600080fd5b5061013b600480360360408110156101ab57600080fd5b506001600160a01b038135169060200135610554565b3480156101cd57600080fd5b5061013b600480360360408110156101e457600080fd5b508035906020013561060c565b3480156101fd57600080fd5b506101576109bb565b34801561021257600080fd5b506102426004803603606081101561022957600080fd5b5080359063ffffffff60208201351690604001356109df565b60408051938452602084019290925282820152519081900360600190f35b34801561026c57600080fd5b5061013b6004803603604081101561028357600080fd5b508035906020013563ffffffff16610cff565b3480156102a257600080fd5b5061013b611903565b3480156102b757600080fd5b5061013b6119ba565b3480156102cc57600080fd5b50610157611a37565b3480156102e157600080fd5b50610157611a46565b3480156102f657600080fd5b50610157611a6a565b34801561030b57600080fd5b5061013b6004803603602081101561032257600080fd5b50356001600160a01b0316611a8e565b34801561033e57600080fd5b5061038d600480360360e081101561035557600080fd5b506001600160a01b03813581169160208101359091169060408101359060608101359060808101359060a08101359060c00135611b20565b60408051918252519081900360200190f35b61038d600480360360808110156103b557600080fd5b506001600160a01b03813581169160208101358216916040820135169060600135611c15565b3480156103e757600080fd5b50610157611c69565b3480156103fc57600080fd5b50610157611c78565b34801561041157600080fd5b50610157611c9c565b61038d6004803603606081101561043057600080fd5b506001600160a01b03813581169160208101359091169060400135611cc0565b34801561045c57600080fd5b5061013b6004803603604081101561047357600080fd5b5080359060200135611d08565b34801561048c57600080fd5b5061013b600480360360208110156104a357600080fd5b50356001600160a01b0316612214565b60035460ff1661050a576040805162461bcd60e51b815260206004820152601a60248201527f4552525f4e4f545f5550444154494e475f4c4951554944495459000000000000604482015290519081900360640190fd5b565b7f00000000000000000000000048fb253446873234f2febbf9bdeaa72d9d387f9481565b7f000000000000000000000000a489c2b5b36835a327851ab917a80562b5afc24481565b61055c612292565b600280558161056a816122da565b82610574816123c0565b8261057e816124aa565b6000610589866124f0565b90507f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c866105be818484600060028c0461255d565b6105d1818484600160028c048c0361255d565b6105fd81337f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb558a6126c3565b50506001600255505050505050565b610614612292565b6002805560408051637a1036f560e11b81523360048201526024810184905260448101839052905160609182916001600160a01b037f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb55169163f4206dea916064808301926000929190829003018186803b15801561069157600080fd5b505afa1580156106a5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160409081528110156106ce57600080fd5b81019080805160405193929190846401000000008211156106ee57600080fd5b90830190602082018581111561070357600080fd5b825186602082028301116401000000008211171561072057600080fd5b82525081516020918201928201910280838360005b8381101561074d578181015183820152602001610735565b505050509050016040526020018051604051939291908464010000000082111561077657600080fd5b90830190602082018581111561078b57600080fd5b82518660208202830111640100000000821117156107a857600080fd5b82525081516020918201928201910280838360005b838110156107d55781810151838201526020016107bd565b505050509050016040525050509150915060008083519050825181146107f757fe5b805b80156108e657600019810161080c612836565b85828151811061081857fe5b6020026020010151111561082c57506108dd565b604080516390e0661b60e01b8152336004820152898301602482015290516001600160a01b037f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb5516916390e0661b91604480830192600092919082900301818387803b15801561089b57600080fd5b505af11580156108af573d6000803e3d6000fd5b505050506108d98682815181106108c257fe5b60200260200101518561283a90919063ffffffff16565b9350505b600019016107f9565b5081156109ae577f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb556001600160a01b0316635e35359e7f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c33856040518463ffffffff1660e01b815260040180846001600160a01b03168152602001836001600160a01b031681526020018281526020019350505050600060405180830381600087803b15801561099557600080fd5b505af11580156109a9573d6000803e3d6000fd5b505050505b5050600160025550505050565b7f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c81565b6000806000846109ee8161288c565b6109f6614dbd565b6109ff886128f2565b80519091506001600160a01b0316610a4f576040805162461bcd60e51b815260206004820152600e60248201526d11549497d253959053125117d25160921b604482015290519081900360640190fd5b8060e00151861015610aa0576040805162461bcd60e51b815260206004820152601560248201527404552525f494e56414c49445f54494d455354414d5605c1b604482015290519081900360640190fd5b63ffffffff8716620f424014610b05576060810151620f424090610acd9063ffffffff8a8116906129fc16565b81610ad457fe5b0460608201526080810151620f424090610af79063ffffffff8a8116906129fc16565b81610afe57fe5b0460808201525b610b0d614e1d565b610b2b826020015183604001518460a001518560c001516000612a5a565b90506000610b528360200151846040015185606001518660800151868860e001518e612b6f565b90507f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c6001600160a01b031683604001516001600160a01b03161415610ba357955085945060009350610cf5915050565b610bab614e52565b610bbd84602001518560400151612c85565b90506000610bf06002836000015181610bd257fe5b04610bea8460200151866129fc90919063ffffffff16565b90612daa565b90506000610c9f86606001517f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb556001600160a01b0316635121220c89602001516040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015610c6d57600080fd5b505afa158015610c81573d6000803e3d6000fd5b505050506040513d6020811015610c9757600080fd5b50519061283a565b9050808211610cae5781610cb0565b805b91506000610cd68460200151610bea6002876000015181610ccd57fe5b879190046129fc565b90506000610ce5868389612e09565b959b509099509397505050505050505b5093509350939050565b80610d098161288c565b610d11612292565b60028055610d1d614dbd565b610d278433612ef0565b905060007f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c9050610d5b82602001516123c0565b610d63612836565b8260e0015110610daa576040805162461bcd60e51b815260206004820152600d60248201526c4552525f544f4f5f4541524c5960981b604482015290519081900360640190fd5b63ffffffff8416620f42401415610e3e577f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb556001600160a01b0316636f366b71866040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015610e2157600080fd5b505af1158015610e35573d6000803e3d6000fd5b50505050610f30565b60608201516080830151620f4240610e5f8363ffffffff898116906129fc16565b81610e6657fe5b0460608501526080840151620f424090610e899063ffffffff898116906129fc16565b81610e9057fe5b046080850181905260608501516040805163161139bd60e31b8152600481018b90529185036024830152918303604482015290516001600160a01b037f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb55169163b089cde891606480830192600092919082900301818387803b158015610f1557600080fd5b505af1158015610f29573d6000803e3d6000fd5b5050505050505b60408051631d092adf60e01b815233600482015290516001600160a01b037f000000000000000000000000f8a2fb650e25a26ce839d64be8a0abbcb0b87b321691631d092adf91602480830192600092919082900301818387803b158015610f9757600080fd5b505af1158015610fab573d6000803e3d6000fd5b505050507f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb556001600160a01b031663332100fa836020015184606001516040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561102e57600080fd5b505af1158015611042573d6000803e3d6000fd5b50505050806001600160a01b031682604001516001600160a01b03161415611117576110947f00000000000000000000000048fb253446873234f2febbf9bdeaa72d9d387f94333085608001516126c3565b7f0000000000000000000000000887ae1251e180d7d453aedebee26e1639f201136001600160a01b03166342966c6883608001516040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b1580156110fe57600080fd5b505af1158015611112573d6000803e3d6000fd5b505050505b61111f614e1d565b61113d836020015184604001518560a001518660c001516001612a5a565b9050600061116b8460200151856040015186606001518760800151868960e00151611166612836565b612b6f565b9050826001600160a01b031684604001516001600160a01b031614156112e0577f000000000000000000000000a489c2b5b36835a327851ab917a80562b5afc2446001600160a01b03166340c10f197f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb55836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561122257600080fd5b505af1158015611236573d6000803e3d6000fd5b505050507f000000000000000000000000d444ec18952c7caf09636f21807683dacc1d7da96001600160a01b031663deacd84e8560200151836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b1580156112b557600080fd5b505af11580156112c9573d6000803e3d6000fd5b505050506112d73382612f69565b505050506118f9565b6112e8614e52565b6112fa85602001518660400151612c85565b9050600061130f6002836000015181610bd257fe5b905060007f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb556001600160a01b0316635121220c88602001516040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561138457600080fd5b505afa158015611398573d6000803e3d6000fd5b505050506040513d60208110156113ae57600080fd5b505190508082116113bf57816113c1565b805b91507f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb556001600160a01b03166319c6a5e48860200151846040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561143e57600080fd5b505af1158015611452573d6000803e3d6000fd5b505050507f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb556001600160a01b0316635e35359e886020015130856040518463ffffffff1660e01b815260040180846001600160a01b03168152602001836001600160a01b031681526020018281526020019350505050600060405180830381600087803b1580156114e257600080fd5b505af11580156114f6573d6000803e3d6000fd5b5050505061150e8760200151838960400151896130ac565b60408701516000906001600160a01b031673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee141561157057506040514790339082156108fc029083906000818181858888f1935050505015801561156a573d6000803e3d6000fd5b50611600565b87604001516001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156115c157600080fd5b505afa1580156115d5573d6000803e3d6000fd5b505050506040513d60208110156115eb57600080fd5b50516040890151909150611600903383613299565b600061160d868389612e09565b90508015611759576000886001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561166457600080fd5b505afa158015611678573d6000803e3d6000fd5b505050506040513d602081101561168e57600080fd5b505190508181101561172257604080516340c10f1960e01b8152306004820152828403602482015290516001600160a01b037f000000000000000000000000a489c2b5b36835a327851ab917a80562b5afc24416916340c10f1991604480830192600092919082900301818387803b15801561170957600080fd5b505af115801561171d573d6000803e3d6000fd5b505050505b61174d897f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb5584613299565b6117573383612f69565b505b6000886001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156117a857600080fd5b505afa1580156117bc573d6000803e3d6000fd5b505050506040513d60208110156117d257600080fd5b5051905080156118ee577f000000000000000000000000a489c2b5b36835a327851ab917a80562b5afc2446001600160a01b03166342966c68826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561184257600080fd5b505af1158015611856573d6000803e3d6000fd5b505050507f000000000000000000000000d444ec18952c7caf09636f21807683dacc1d7da96001600160a01b031663802fa3ba8b60200151836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b1580156118d557600080fd5b505af11580156118e9573d6000803e3d6000fd5b505050505b505050505050505050505b5050600160025550565b6001546001600160a01b03163314611956576040805162461bcd60e51b815260206004820152601160248201527011549497d050d0d154d4d7d11153925151607a1b604482015290519081900360640190fd5b600154600080546040516001600160a01b0393841693909116917f343765429aea5a34b3ff6a3785a98a5abb2597aca87bfbb58632c173d585373a91a360018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b6119c26133f2565b7f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb556001600160a01b03166379ba50976040518163ffffffff1660e01b8152600401600060405180830381600087803b158015611a1d57600080fd5b505af1158015611a31573d6000803e3d6000fd5b50505050565b6000546001600160a01b031681565b7f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb5581565b7f000000000000000000000000f8a2fb650e25a26ce839d64be8a0abbcb0b87b3281565b611a966133f2565b7f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb556001600160a01b031663f2fde38b826040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050600060405180830381600087803b158015611b0557600080fd5b505af1158015611b19573d6000803e3d6000fd5b5050505050565b600080611b3186610bea89886129fc565b9050611b3b614e1d565b611b498a8a87876000612a5a565b90506000611bf38b8b858c86611beb7f000000000000000000000000d444ec18952c7caf09636f21807683dacc1d7da96001600160a01b031663ce3f3adb6040518163ffffffff1660e01b815260040160206040518083038186803b158015611bb157600080fd5b505afa158015611bc5573d6000803e3d6000fd5b505050506040513d6020811015611bdb57600080fd5b5051611be5612836565b90613445565b611166612836565b9050611c0689610bea83620f42406129fc565b9b9a5050505050505050505050565b6000611c1f612292565b6002805584611c2d81613492565b84611c37816122da565b85611c41816123c0565b84611c4b816124aa565b611c57898989896134e3565b60016002559998505050505050505050565b6001546001600160a01b031681565b7f0000000000000000000000000887ae1251e180d7d453aedebee26e1639f2011381565b7f000000000000000000000000d444ec18952c7caf09636f21807683dacc1d7da981565b6000611cca612292565b6002805583611cd8816122da565b84611ce2816123c0565b83611cec816124aa565b611cf8338888886134e3565b6001600255979650505050505050565b611d10612292565b6002805581811415611d57576040805162461bcd60e51b815260206004820152600b60248201526a11549497d4d0535157d25160aa1b604482015290519081900360640190fd5b611d5f614dbd565b611d698333612ef0565b9050611d73614dbd565b611d7d8333612ef0565b905060007f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c905081602001516001600160a01b031683602001516001600160a01b0316148015611de7575081604001516001600160a01b031683604001516001600160a01b031614155b8015611e275750806001600160a01b031683604001516001600160a01b03161480611e275750806001600160a01b031682604001516001600160a01b0316145b8015611e3a57508160e001518360e00151145b8015611e5957506060820151611e5190600161283a565b836060015111155b8015611e7857506060830151611e7090600161283a565b826060015111155b611ec9576040805162461bcd60e51b815260206004820152601860248201527f4552525f50524f54454354494f4e535f4d49534d415443480000000000000000604482015290519081900360640190fd5b611ed1612836565b8360e0015110611f18576040805162461bcd60e51b815260206004820152600d60248201526c4552525f544f4f5f4541524c5960981b604482015290519081900360640190fd5b6000816001600160a01b031684604001516001600160a01b031614611f41578260800151611f47565b83608001515b9050611f757f00000000000000000000000048fb253446873234f2febbf9bdeaa72d9d387f943330846126c3565b7f0000000000000000000000000887ae1251e180d7d453aedebee26e1639f201136001600160a01b03166342966c68826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015611fdb57600080fd5b505af1158015611fef573d6000803e3d6000fd5b505060408051631d092adf60e01b815233600482015290516001600160a01b037f000000000000000000000000f8a2fb650e25a26ce839d64be8a0abbcb0b87b32169350631d092adf9250602480830192600092919082900301818387803b15801561205a57600080fd5b505af115801561206e573d6000803e3d6000fd5b505050507f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb556001600160a01b0316636f366b71876040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b1580156120d857600080fd5b505af11580156120ec573d6000803e3d6000fd5b505050507f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb556001600160a01b0316636f366b71866040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561215657600080fd5b505af115801561216a573d6000803e3d6000fd5b505050507f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb556001600160a01b0316635e35359e8560200151336121be8760600151896060015161283a90919063ffffffff16565b6040518463ffffffff1660e01b815260040180846001600160a01b03168152602001836001600160a01b031681526020018281526020019350505050600060405180830381600087803b15801561099557600080fd5b61221c6133f2565b6000546001600160a01b0382811691161415612270576040805162461bcd60e51b815260206004820152600e60248201526d22a9292fa9a0a6a2afa7aba722a960911b604482015290519081900360640190fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b60016002541461050a576040805162461bcd60e51b815260206004820152600e60248201526d4552525f5245454e5452414e435960901b604482015290519081900360640190fd5b7f000000000000000000000000d444ec18952c7caf09636f21807683dacc1d7da96001600160a01b031663d4f63148826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561234757600080fd5b505afa15801561235b573d6000803e3d6000fd5b505050506040513d602081101561237157600080fd5b50516123bd576040805162461bcd60e51b815260206004820152601660248201527511549497d413d3d317d393d517d4d5541413d495115160521b604482015290519081900360640190fd5b50565b7f000000000000000000000000d444ec18952c7caf09636f21807683dacc1d7da96001600160a01b0316632b26a982826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561242d57600080fd5b505afa158015612441573d6000803e3d6000fd5b505050506040513d602081101561245757600080fd5b50516123bd576040805162461bcd60e51b815260206004820152601860248201527f4552525f504f4f4c5f4e4f545f57484954454c49535445440000000000000000604482015290519081900360640190fd5b600081116123bd576040805162461bcd60e51b815260206004820152600e60248201526d4552525f5a45524f5f56414c554560901b604482015290519081900360640190fd5b6000816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561252b57600080fd5b505afa15801561253f573d6000803e3d6000fd5b505050506040513d602081101561255557600080fd5b505192915050565b6000846001600160a01b03166319b64015846040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156125a357600080fd5b505afa1580156125b7573d6000803e3d6000fd5b505050506040513d60208110156125cd57600080fd5b50519050856125da614e52565b6125e48284612c85565b905060006126078260200151610bea8460000151886129fc90919063ffffffff16565b90506126163384868885613614565b50866001600160a01b0316846001600160a01b031614156126b857604080516340c10f1960e01b81523360048201526024810183905290516001600160a01b037f0000000000000000000000000887ae1251e180d7d453aedebee26e1639f2011316916340c10f1991604480830192600092919082900301818387803b15801561269f57600080fd5b505af11580156126b3573d6000803e3d6000fd5b505050505b505050505050505050565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b17815292518251600094606094938a169392918291908083835b602083106127485780518252601f199092019160209182019101612729565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146127aa576040519150601f19603f3d011682016040523d82523d6000602084013e6127af565b606091505b50915091508180156127dd5750805115806127dd57508080602001905160208110156127da57600080fd5b50515b61282e576040805162461bcd60e51b815260206004820152601860248201527f4552525f5452414e534645525f46524f4d5f4641494c45440000000000000000604482015290519081900360640190fd5b505050505050565b4290565b600082820183811015612883576040805162461bcd60e51b815260206004820152600c60248201526b4552525f4f564552464c4f5760a01b604482015290519081900360640190fd5b90505b92915050565b60008163ffffffff161180156128ab5750620f424063ffffffff821611155b6123bd576040805162461bcd60e51b815260206004820152601360248201527222a9292fa4a72b20a624a22fa827a92a24a7a760691b604482015290519081900360640190fd5b6128fa614dbd565b612902614dbd565b7f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb556001600160a01b0316635290ffbb846040518263ffffffff1660e01b8152600401808281526020019150506101006040518083038186803b15801561296757600080fd5b505afa15801561297b573d6000803e3d6000fd5b505050506040513d61010081101561299257600080fd5b50805160208083015160408085015160608087015160808089015160a0808b015160c0808d015160e09d8e01519d8f019d909d528d019b909b528b01999099528901979097528701959095526001600160a01b039485169086015283169084015216815292915050565b600082612a0b57506000612886565b82820282848281612a1857fe5b0414612883576040805162461bcd60e51b815260206004820152600c60248201526b4552525f4f564552464c4f5760a01b604482015290519081900360640190fd5b612a62614e1d565b600080600080612a738a8a8861372b565b93509350935093506001600160801b038811158015612a9957506001600160801b038711155b8015612abe57506001600160801b038411158015612abe57506001600160801b038311155b8015612ae357506001600160801b038211158015612ae357506001600160801b038111155b612b27576040805162461bcd60e51b815260206004820152601060248201526f4552525f494e56414c49445f5241544560801b604482015290519081900360640190fd5b6040805160c0810182526001600160801b03998a16815297891660208901529388169387019390935290861660608601528516608085015290931660a0830152509392505050565b6000612b79614e52565b612b838989612c85565b9050612b8d614e52565b604051806040016040528087600001516001600160801b0316815260200187602001516001600160801b03168152509050612bc6614e52565b50604080518082018252908701516001600160801b0390811682526060880151166020820152612bf4614e52565b604051806040016040528089608001516001600160801b031681526020018960a001516001600160801b031681525090506000612c338b8686866139c4565b9050612c3d614e52565b612c478584613acf565b9050612c51614e52565b612c5b8a8a613b9d565b9050612c728c612c6b8e86613d39565b8484613d4f565b9f9e505050505050505050505050505050565b612c8d614e52565b6000836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015612cc857600080fd5b505afa158015612cdc573d6000803e3d6000fd5b505050506040513d6020811015612cf257600080fd5b505190506000612d01856124f0565b90506000816001600160a01b031663d8959512866040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015612d5257600080fd5b505afa158015612d66573d6000803e3d6000fd5b505050506040513d6020811015612d7c57600080fd5b50516040805180820190915290915080612d978360026129fc565b8152602001939093525090949350505050565b6000808211612df5576040805162461bcd60e51b81526020600482015260126024820152714552525f4449564944455f42595f5a45524f60701b604482015290519081900360640190fd5b6000828481612e0057fe5b04949350505050565b6000828411612e1a57506000612ee9565b6000612e4f8360a001516001600160801b0316610bea85608001516001600160801b03168789036129fc90919063ffffffff16565b90507f000000000000000000000000d444ec18952c7caf09636f21807683dacc1d7da96001600160a01b031663a80c76ff6040518163ffffffff1660e01b815260040160206040518083038186803b158015612eaa57600080fd5b505afa158015612ebe573d6000803e3d6000fd5b505050506040513d6020811015612ed457600080fd5b50518110612ee3579050612ee9565b60009150505b9392505050565b612ef8614dbd565b612f00614dbd565b612f09846128f2565b9050826001600160a01b031681600001516001600160a01b031614612883576040805162461bcd60e51b815260206004820152601160248201527011549497d050d0d154d4d7d11153925151607a1b604482015290519081900360640190fd5b60006130017f000000000000000000000000d444ec18952c7caf09636f21807683dacc1d7da96001600160a01b031663045544436040518163ffffffff1660e01b815260040160206040518083038186803b158015612fc757600080fd5b505afa158015612fdb573d6000803e3d6000fd5b505050506040513d6020811015612ff157600080fd5b5051612ffb612836565b9061283a565b90507f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb556001600160a01b031663dbae3a5d8484846040518463ffffffff1660e01b815260040180846001600160a01b031681526020018381526020018281526020019350505050602060405180830381600087803b15801561308257600080fd5b505af1158015613096573d6000803e3d6000fd5b505050506040513d6020811015611b1957600080fd5b60006130b7856124f0565b6003805460ff1916600117905560408051600280825260608083018452939450909160208301908036833750506040805160028082526060808301845294955090925090602083019080368337019050509050848260008151811061311857fe5b60200260200101906001600160a01b031690816001600160a01b031681525050838260018151811061314657fe5b60200260200101906001600160a01b031690816001600160a01b03168152505060018160008151811061317557fe5b60200260200101818152505060018160018151811061319057fe5b602002602001018181525050826001600160a01b031663b127c0a58784846040518463ffffffff1660e01b8152600401808481526020018060200180602001838103835285818151815260200191508051906020019060200280838360005b838110156132075781810151838201526020016131ef565b50505050905001838103825284818151815260200191508051906020019060200280838360005b8381101561324657818101518382015260200161322e565b5050505090500195505050505050600060405180830381600087803b15801561326e57600080fd5b505af1158015613282573d6000803e3d6000fd5b50506003805460ff19169055505050505050505050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b178152925182516000946060949389169392918291908083835b602083106133165780518252601f1990920191602091820191016132f7565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114613378576040519150601f19603f3d011682016040523d82523d6000602084013e61337d565b606091505b50915091508180156133ab5750805115806133ab57508080602001905160208110156133a857600080fd5b50515b611b19576040805162461bcd60e51b815260206004820152601360248201527211549497d514905394d1915497d19052531151606a1b604482015290519081900360640190fd5b6000546001600160a01b0316331461050a576040805162461bcd60e51b815260206004820152601160248201527011549497d050d0d154d4d7d11153925151607a1b604482015290519081900360640190fd5b60008183101561348c576040805162461bcd60e51b815260206004820152600d60248201526c4552525f554e444552464c4f5760981b604482015290519081900360640190fd5b50900390565b6001600160a01b0381166123bd576040805162461bcd60e51b81526020600482015260136024820152724552525f494e56414c49445f4144445245535360681b604482015290519081900360640190fd5b60007f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c6001600160a01b03848116908216141561357b573415613567576040805162461bcd60e51b815260206004820152601760248201527608aa4a4be8aa890be829a9eaa9ca8be9a92a69a82a8869604b1b604482015290519081900360640190fd5b61357386868386613ddf565b91505061360c565b60006001600160a01b03851673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee146135a85760006135aa565b835b90508034146135fa576040805162461bcd60e51b815260206004820152601760248201527608aa4a4be8aa890be829a9eaa9ca8be9a92a69a82a8869604b1b604482015290519081900360640190fd5b613607878787858861406e565b925050505b949350505050565b600061361e614e52565b61362a86866001614635565b90507f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb556001600160a01b03166361d5f087888888888887600001518860200151613672612836565b6040518963ffffffff1660e01b815260040180896001600160a01b03168152602001886001600160a01b03168152602001876001600160a01b0316815260200186815260200185815260200184815260200183815260200182815260200198505050505050505050602060405180830381600087803b1580156136f457600080fd5b505af1158015613708573d6000803e3d6000fd5b505050506040513d602081101561371e57600080fd5b5051979650505050505050565b600080600080600061373c886124f0565b90506000816001600160a01b03166319b6401560006040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561378557600080fd5b505afa158015613799573d6000803e3d6000fd5b505050506040513d60208110156137af57600080fd5b505190506001600160a01b03808216908916141561383b57816001600160a01b03166319b6401560016040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561380c57600080fd5b505afa158015613820573d6000803e3d6000fd5b505050506040513d602081101561383657600080fd5b505190505b60008061384984848c614669565b91509150600080856001600160a01b0316631f0181bc8d6040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050604080518083038186803b15801561389c57600080fd5b505afa1580156138b0573d6000803e3d6000fd5b505050506040513d60408110156138c657600080fd5b50805160209091015190925090508a158061396d575061396d848484847f000000000000000000000000d444ec18952c7caf09636f21807683dacc1d7da96001600160a01b03166324a088686040518163ffffffff1660e01b815260040160206040518083038186803b15801561393c57600080fd5b505afa158015613950573d6000803e3d6000fd5b505050506040513d602081101561396657600080fd5b5051614769565b6139b1576040805162461bcd60e51b815260206004820152601060248201526f4552525f494e56414c49445f5241544560801b604482015290519081900360640190fd5b929c919b50995090975095505050505050565b82518151602084015160009283926139ef926139e9916139e491906129fc565b614806565b906129fc565b90506000613a1e86602001516139e9613a19876020015189600001516129fc90919063ffffffff16565b614826565b905081870287838281613a2d57fe5b041415613a4857818181613a3d57fe5b04935050505061360c565b600080898511613a59578985613a5c565b848a5b91509150600080613a7984878560001981613a7357fe5b0461487c565b915091506000613a9384888781613a8c57fe5b04906129fc565b90508115613abf57613ab0818386860281613aaa57fe5b04613d39565b9850505050505050505061360c565b9c9b505050505050505050505050565b613ad7614e52565b60208301518251600091613aeb91906129fc565b84516020850151919250600091613b01916129fc565b9050818102600082848381613b1257fe5b0414613b3057613b2183614826565b613b2a85614826565b02613b39565b613b3982614826565b90506000613b47858561283a565b905060028106613b7a57600281049050604051806040016040528083830381526020018281525095505050505050612886565b604080518082019091526002909202810382526020820152935050505092915050565b613ba5614e52565b6000613bb18385613445565b905060007f000000000000000000000000d444ec18952c7caf09636f21807683dacc1d7da96001600160a01b0316632c560f896040518163ffffffff1660e01b815260040160206040518083038186803b158015613c0e57600080fd5b505afa158015613c22573d6000803e3d6000fd5b505050506040513d6020811015613c3857600080fd5b50516040805163ce3f3adb60e01b815290519192506000916001600160a01b037f000000000000000000000000d444ec18952c7caf09636f21807683dacc1d7da9169163ce3f3adb916004808301926020929190829003018186803b158015613ca057600080fd5b505afa158015613cb4573d6000803e3d6000fd5b505050506040513d6020811015613cca57600080fd5b5051905081831015613cf75760405180604001604052806000815260200160018152509350505050612886565b808310613d1f5760405180604001604052806001815260200160018152509350505050612886565b604080518082019091529283526020830152509392505050565b6000818311613d485781612883565b5090919050565b80516000908190613d6090876129fc565b60208401519091506000613d7d613d778484613d39565b88613d39565b9050600080613d9a886000015189602001518560001981613a7357fe5b9092509050613dd1613db9613daf83876129fc565b610bea85896129fc565b612ffb83610bea613dca8288613445565b8e906129fc565b9a9950505050505050505050565b600083613dea614e52565b613df48286612c85565b90506000613e178260000151610bea8460200151886129fc90919063ffffffff16565b90507f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb556001600160a01b03166319c6a5e484836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b158015613e9057600080fd5b505af1158015613ea4573d6000803e3d6000fd5b505050506000613eb7898589858a613614565b9050613ec5873330896126c3565b7f000000000000000000000000a489c2b5b36835a327851ab917a80562b5afc2446001600160a01b03166342966c68876040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015613f2b57600080fd5b505af1158015613f3f573d6000803e3d6000fd5b505050507f000000000000000000000000d444ec18952c7caf09636f21807683dacc1d7da96001600160a01b031663802fa3ba89886040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b158015613fba57600080fd5b505af1158015613fce573d6000803e3d6000fd5b505050507f0000000000000000000000000887ae1251e180d7d453aedebee26e1639f201136001600160a01b03166340c10f198a886040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561404957600080fd5b505af115801561405d573d6000803e3d6000fd5b50929b9a5050505050505050505050565b6000848161407b826124f0565b905060008061408b838989614669565b915091507f000000000000000000000000d444ec18952c7caf09636f21807683dacc1d7da96001600160a01b03166312588d0e6040518163ffffffff1660e01b815260040160206040518083038186803b1580156140e857600080fd5b505afa1580156140fc573d6000803e3d6000fd5b505050506040513d602081101561411257600080fd5b5051811015614168576040805162461bcd60e51b815260206004820152601860248201527f4552525f4e4f545f454e4f5547485f4c49515549444954590000000000000000604482015290519081900360640190fd5b600061417883610bea89856129fc565b905060007f000000000000000000000000d444ec18952c7caf09636f21807683dacc1d7da96001600160a01b031663943fd08a8c6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156141e957600080fd5b505afa1580156141fd573d6000803e3d6000fd5b505050506040513d602081101561421357600080fd5b50519050806142a4577f000000000000000000000000d444ec18952c7caf09636f21807683dacc1d7da96001600160a01b031663b97b55ce6040518163ffffffff1660e01b815260040160206040518083038186803b15801561427557600080fd5b505afa158015614289573d6000803e3d6000fd5b505050506040513d602081101561429f57600080fd5b505190505b6000614317837f000000000000000000000000d444ec18952c7caf09636f21807683dacc1d7da96001600160a01b031663350ed8e78f6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015610c6d57600080fd5b905081811115614367576040805162461bcd60e51b815260206004820152601660248201527511549497d3505617d05353d5539517d4915050d2115160521b604482015290519081900360640190fd5b604080516340c10f1960e01b81523060048201526024810185905290516001600160a01b037f000000000000000000000000a489c2b5b36835a327851ab917a80562b5afc24416916340c10f1991604480830192600092919082900301818387803b1580156143d557600080fd5b505af11580156143e9573d6000803e3d6000fd5b505050507f000000000000000000000000d444ec18952c7caf09636f21807683dacc1d7da96001600160a01b031663deacd84e8d856040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561446457600080fd5b505af1158015614478573d6000803e3d6000fd5b505050506144878a87856148c4565b6001600160a01b038b1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee146144c2576144b78b33308c6126c3565b6144c28b878b6148c4565b6144d0868c8c8c8734614968565b6000876001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561451f57600080fd5b505afa158015614533573d6000803e3d6000fd5b505050506040513d602081101561454957600080fd5b50519050614578887f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb5583613299565b6001600160a01b037f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb551663332100fa896002840484036040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b1580156145f457600080fd5b505af1158015614608573d6000803e3d6000fd5b505050506146238e898e6002858161461c57fe5b048e613614565b9e9d5050505050505050505050505050565b61463d614e52565b60008061464b86868661372b565b60408051808201909152918252602082015298975050505050505050565b600080846001600160a01b031663d8959512856040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156146b957600080fd5b505afa1580156146cd573d6000803e3d6000fd5b505050506040513d60208110156146e357600080fd5b505160408051636c4aca8960e11b81526001600160a01b03868116600483015291519188169163d895951291602480820192602092909190829003018186803b15801561472f57600080fd5b505afa158015614743573d6000803e3d6000fd5b505050506040513d602081101561475957600080fd5b505190925090505b935093915050565b60008061479d83620f42400363ffffffff166139e985620f42400363ffffffff166139e9888c6129fc90919063ffffffff16565b905060006147d0620f424063ffffffff166139e986620f42400363ffffffff166139e98a8c6129fc90919063ffffffff16565b905060006147e7620f42406139e981818d8b6129fc565b90508183111580156147f95750808211155b9998505050505050505050565b60008061481283614826565b905082818202146128865780600101612ee9565b6000806002830460010190506000600282858161483f57fe5b0483018161484957fe5b0490505b8082111561487557809150600282858161486357fe5b0483018161486d57fe5b04905061484d565b5092915050565b60008084848482118061488e57508481115b156148a45761489e828287614b47565b90925090505b8082146148b5579092509050614761565b50600196879650945050505050565b60408051636eb1769f60e11b81523060048201526001600160a01b038481166024830152915160009286169163dd62ed3e916044808301926020929190829003018186803b15801561491557600080fd5b505afa158015614929573d6000803e3d6000fd5b505050506040513d602081101561493f57600080fd5b5051905081811015611a3157801561495d5761495d84846000614b80565b611a31848484614b80565b6003805460ff1916600117905560408051600280825260608083018452926020830190803683375050604080516002808252606080830184529495509092509060208301908036833701905050905086826000815181106149c557fe5b60200260200101906001600160a01b031690816001600160a01b03168152505085826001815181106149f357fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508481600081518110614a2157fe5b6020026020010181815250508381600181518110614a3b57fe5b602002602001018181525050876001600160a01b0316637d8916bd84848460016040518563ffffffff1660e01b8152600401808060200180602001848152602001838103835286818151815260200191508051906020019060200280838360005b83811015614ab4578181015183820152602001614a9c565b50505050905001838103825285818151815260200191508051906020019060200280838360005b83811015614af3578181015183820152602001614adb565b50505050905001955050505050506000604051808303818588803b158015614b1a57600080fd5b505af1158015614b2e573d6000803e3d6000fd5b50506003805460ff191690555050505050505050505050565b600080838511614b6557614b5c858585614cd8565b91509150614761565b600080614b73868887614cd8565b9890975095505050505050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663095ea7b360e01b178152925182516000946060949389169392918291908083835b60208310614bfd5780518252601f199092019160209182019101614bde565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114614c5f576040519150601f19603f3d011682016040523d82523d6000602084013e614c64565b606091505b5091509150818015614c92575080511580614c925750808060200190516020811015614c8f57600080fd5b50515b611b19576040805162461bcd60e51b815260206004820152601260248201527111549497d054141493d59157d1905253115160721b604482015290519081900360640190fd5b60008060008360001981614ce857fe5b04905080861115614d21576000816001018781614d0157fe5b046001019050808781614d1057fe5b049650808681614d1c57fe5b049550505b848614614d8157858402858701878110614d52576000614d418383614d91565b955050508385039250614761915050565b6002888803048703821015614d705760008694509450505050614761565b600180870394509450505050614761565b5050600290910493849350915050565b6000600282048203828481614da257fe5b0681614daa57fe5b04828481614db457fe5b04019392505050565b60405180610100016040528060006001600160a01b0316815260200160006001600160a01b0316815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a081019190915290565b60405180604001604052806000815260200160008152509056fea26469706673582212200b0235110436240d15a64b82ece6b9e600dccbb988885efc5c5e63d964f6343d64736f6c634300060c0033

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

000000000000000000000000d444ec18952c7caf09636f21807683dacc1d7da9000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb55000000000000000000000000a489c2b5b36835a327851ab917a80562b5afc2440000000000000000000000000887ae1251e180d7d453aedebee26e1639f20113000000000000000000000000f8a2fb650e25a26ce839d64be8a0abbcb0b87b32

-----Decoded View---------------
Arg [0] : _settings (address): 0xd444ec18952c7cAf09636f21807683DaCC1d7dA9
Arg [1] : _store (address): 0xf5FAB5DBD2f3bf675dE4cB76517d4767013cfB55
Arg [2] : _networkTokenGovernance (address): 0xa489C2b5b36835A327851Ab917A80562B5AFC244
Arg [3] : _govTokenGovernance (address): 0x0887ae1251E180d7D453aeDEBee26e1639f20113
Arg [4] : _lastRemoveCheckpointStore (address): 0xF8a2FB650e25a26CE839D64bE8a0aBbCb0b87B32

-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 000000000000000000000000d444ec18952c7caf09636f21807683dacc1d7da9
Arg [1] : 000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb55
Arg [2] : 000000000000000000000000a489c2b5b36835a327851ab917a80562b5afc244
Arg [3] : 0000000000000000000000000887ae1251e180d7d453aedebee26e1639f20113
Arg [4] : 000000000000000000000000f8a2fb650e25a26ce839d64be8a0abbcb0b87b32


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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