ETH Price: $2,806.11 (+1.28%)

Contract

0x30dc4e9E26F24ACEfA8067e71E1Cd4363a05001E
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Create110499222020-10-13 22:11:061593 days ago1602627066IN
Opium.Team: Swaprate Match
0 ETH0.0032589525
Create110499222020-10-13 22:11:061593 days ago1602627066IN
Opium.Team: Swaprate Match
0 ETH0.0032607525
Create110499222020-10-13 22:11:061593 days ago1602627066IN
Opium.Team: Swaprate Match
0 ETH0.0032583525
Create110499222020-10-13 22:11:061593 days ago1602627066IN
Opium.Team: Swaprate Match
0 ETH0.0032610225
Create110499212020-10-13 22:10:561593 days ago1602627056IN
Opium.Team: Swaprate Match
0 ETH0.0032610525
Create110499212020-10-13 22:10:561593 days ago1602627056IN
Opium.Team: Swaprate Match
0 ETH0.0078267225
Create110498902020-10-13 22:03:061593 days ago1602626586IN
Opium.Team: Swaprate Match
0 ETH0.0078267225
Create110498902020-10-13 22:03:061593 days ago1602626586IN
Opium.Team: Swaprate Match
0 ETH0.0032610225
Create110498902020-10-13 22:03:061593 days ago1602626586IN
Opium.Team: Swaprate Match
0 ETH0.0032589525
Create110498902020-10-13 22:03:061593 days ago1602626586IN
Opium.Team: Swaprate Match
0 ETH0.0032607525
Create110498902020-10-13 22:03:061593 days ago1602626586IN
Opium.Team: Swaprate Match
0 ETH0.0032607525
Create110498902020-10-13 22:03:061593 days ago1602626586IN
Opium.Team: Swaprate Match
0 ETH0.0032610525
Create110498902020-10-13 22:03:061593 days ago1602626586IN
Opium.Team: Swaprate Match
0 ETH0.0032583525
Create110498902020-10-13 22:03:061593 days ago1602626586IN
Opium.Team: Swaprate Match
0 ETH0.0032610525
Create110498332020-10-13 21:50:421593 days ago1602625842IN
Opium.Team: Swaprate Match
0 ETH0.0043045533
Create110498062020-10-13 21:44:321593 days ago1602625472IN
Opium.Team: Swaprate Match
0 ETH0.0221879733
Create110498062020-10-13 21:44:321593 days ago1602625472IN
Opium.Team: Swaprate Match
0 ETH0.0221879733
Create110498062020-10-13 21:44:321593 days ago1602625472IN
Opium.Team: Swaprate Match
0 ETH0.0221879733
Create110498052020-10-13 21:44:281593 days ago1602625468IN
Opium.Team: Swaprate Match
0 ETH0.0221879733
Create110498052020-10-13 21:44:281593 days ago1602625468IN
Opium.Team: Swaprate Match
0 ETH0.0221879733
Create110498052020-10-13 21:44:281593 days ago1602625468IN
Opium.Team: Swaprate Match
0 ETH0.0221879733
Create110498052020-10-13 21:44:281593 days ago1602625468IN
Opium.Team: Swaprate Match
0 ETH0.0221879733
Create110498052020-10-13 21:44:281593 days ago1602625468IN
Opium.Team: Swaprate Match
0 ETH0.0221879733
Create110498052020-10-13 21:44:281593 days ago1602625468IN
Opium.Team: Swaprate Match
0 ETH0.0221879733
Create110498052020-10-13 21:44:281593 days ago1602625468IN
Opium.Team: Swaprate Match
0 ETH0.0221879733
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
SwaprateMatch

Compiler Version
v0.5.16+commit.9c3226ce

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, None license

Contract Source Code (Solidity)

/**
 *Submitted for verification at Etherscan.io on 2020-03-24
*/

/**

  Source code of Opium Protocol
  Web https://opium.network
  Telegram https://t.me/opium_network
  Twitter https://twitter.com/opium_network

 */

// File: LICENSE

/**

The software and documentation available in this repository (the "Software") is protected by copyright law and accessible pursuant to the license set forth below. Copyright © 2020 Blockeys BV. All rights reserved.

Permission is hereby granted, free of charge, to any person or organization obtaining the Software (the “Licensee”) to privately study, review, and analyze the Software. Licensee shall not use the Software for any other purpose. Licensee shall not modify, transfer, assign, share, or sub-license the Software or any derivative works of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/


// File: contracts/Lib/LibDerivative.sol

pragma solidity 0.5.16;
pragma experimental ABIEncoderV2;

/// @title Opium.Lib.LibDerivative contract should be inherited by contracts that use Derivative structure and calculate derivativeHash
contract LibDerivative {
    // Opium derivative structure (ticker) definition
    struct Derivative {
        // Margin parameter for syntheticId
        uint256 margin;
        // Maturity of derivative
        uint256 endTime;
        // Additional parameters for syntheticId
        uint256[] params;
        // oracleId of derivative
        address oracleId;
        // Margin token address of derivative
        address token;
        // syntheticId of derivative
        address syntheticId;
    }

    /// @notice Calculates hash of provided Derivative
    /// @param _derivative Derivative Instance of derivative to hash
    /// @return derivativeHash bytes32 Derivative hash
    function getDerivativeHash(Derivative memory _derivative) public pure returns (bytes32 derivativeHash) {
        derivativeHash = keccak256(abi.encodePacked(
            _derivative.margin,
            _derivative.endTime,
            _derivative.params,
            _derivative.oracleId,
            _derivative.token,
            _derivative.syntheticId
        ));
    }
}

// File: openzeppelin-solidity/contracts/token/ERC20/IERC20.sol

pragma solidity ^0.5.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP. Does not include
 * the optional functions; to access them see {ERC20Detailed}.
 */
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: openzeppelin-solidity/contracts/math/SafeMath.sol

pragma solidity ^0.5.0;

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

        return c;
    }

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

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

        return c;
    }

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

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

        return c;
    }

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

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

        return c;
    }

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

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

// File: openzeppelin-solidity/contracts/utils/Address.sol

pragma solidity ^0.5.5;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * This test is non-exhaustive, and there may be false-negatives: during the
     * execution of a contract's constructor, its address will be reported as
     * not containing a contract.
     *
     * IMPORTANT: It is unsafe to assume that an address for which this
     * function returns false is an externally-owned account (EOA) and not a
     * contract.
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies in extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
        // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
        // for accounts without code, i.e. `keccak256('')`
        bytes32 codehash;
        bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
        // solhint-disable-next-line no-inline-assembly
        assembly { codehash := extcodehash(account) }
        return (codehash != 0x0 && codehash != accountHash);
    }

    /**
     * @dev Converts an `address` into `address payable`. Note that this is
     * simply a type cast: the actual underlying value is not changed.
     *
     * _Available since v2.4.0._
     */
    function toPayable(address account) internal pure returns (address payable) {
        return address(uint160(account));
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     *
     * _Available since v2.4.0._
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        // solhint-disable-next-line avoid-call-value
        (bool success, ) = recipient.call.value(amount)("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }
}

// File: openzeppelin-solidity/contracts/token/ERC20/SafeERC20.sol

pragma solidity ^0.5.0;




/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using SafeMath for uint256;
    using Address for address;

    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        // solhint-disable-next-line max-line-length
        require((value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).add(value);
        callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
        callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves.

        // A Solidity high level call has three parts:
        //  1. The target address is checked to verify it contains contract code
        //  2. The call itself is made, and success asserted
        //  3. The return value is decoded, which in turn checks the size of the returned data.
        // solhint-disable-next-line max-line-length
        require(address(token).isContract(), "SafeERC20: call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = address(token).call(data);
        require(success, "SafeERC20: low-level call failed");

        if (returndata.length > 0) { // Return data is optional
            // solhint-disable-next-line max-line-length
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

// File: openzeppelin-solidity/contracts/utils/ReentrancyGuard.sol

pragma solidity ^0.5.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 */
contract ReentrancyGuard {
    // counter to allow mutex lock with only one SSTORE operation
    uint256 private _guardCounter;

    constructor () internal {
        // The counter starts at one to prevent changing it from zero to a non-zero
        // value, which is a more expensive operation.
        _guardCounter = 1;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _guardCounter += 1;
        uint256 localCounter = _guardCounter;
        _;
        require(localCounter == _guardCounter, "ReentrancyGuard: reentrant call");
    }
}

// File: erc721o/contracts/Libs/LibPosition.sol

pragma solidity ^0.5.4;

library LibPosition {
  function getLongTokenId(bytes32 _hash) public pure returns (uint256 tokenId) {
    tokenId = uint256(keccak256(abi.encodePacked(_hash, "LONG")));
  }

  function getShortTokenId(bytes32 _hash) public pure returns (uint256 tokenId) {
    tokenId = uint256(keccak256(abi.encodePacked(_hash, "SHORT")));
  }
}

// File: contracts/Interface/IDerivativeLogic.sol

pragma solidity 0.5.16;


/// @title Opium.Interface.IDerivativeLogic contract is an interface that every syntheticId should implement
contract IDerivativeLogic is LibDerivative {
    /// @notice Validates ticker
    /// @param _derivative Derivative Instance of derivative to validate
    /// @return Returns boolean whether ticker is valid
    function validateInput(Derivative memory _derivative) public view returns (bool);

    /// @notice Calculates margin required for derivative creation
    /// @param _derivative Derivative Instance of derivative
    /// @return buyerMargin uint256 Margin needed from buyer (LONG position)
    /// @return sellerMargin uint256 Margin needed from seller (SHORT position)
    function getMargin(Derivative memory _derivative) public view returns (uint256 buyerMargin, uint256 sellerMargin);

    /// @notice Calculates payout for derivative execution
    /// @param _derivative Derivative Instance of derivative
    /// @param _result uint256 Data retrieved from oracleId on the maturity
    /// @return buyerPayout uint256 Payout in ratio for buyer (LONG position holder)
    /// @return sellerPayout uint256 Payout in ratio for seller (SHORT position holder)
    function getExecutionPayout(Derivative memory _derivative, uint256 _result)	public view returns (uint256 buyerPayout, uint256 sellerPayout);

    /// @notice Returns syntheticId author address for Opium commissions
    /// @return authorAddress address The address of syntheticId address
    function getAuthorAddress() public view returns (address authorAddress);

    /// @notice Returns syntheticId author commission in base of COMMISSION_BASE
    /// @return commission uint256 Author commission
    function getAuthorCommission() public view returns (uint256 commission);

    /// @notice Returns whether thirdparty could execute on derivative's owner's behalf
    /// @param _derivativeOwner address Derivative owner address
    /// @return Returns boolean whether _derivativeOwner allowed third party execution
    function thirdpartyExecutionAllowed(address _derivativeOwner) public view returns (bool);

    /// @notice Returns whether syntheticId implements pool logic
    /// @return Returns whether syntheticId implements pool logic
    function isPool() public view returns (bool);

    /// @notice Sets whether thirds parties are allowed or not to execute derivative's on msg.sender's behalf
    /// @param _allow bool Flag for execution allowance
    function allowThirdpartyExecution(bool _allow) public;

    // Event with syntheticId metadata JSON string (for DIB.ONE derivative explorer)
    event MetadataSet(string metadata);
}

// File: contracts/Errors/CoreErrors.sol

pragma solidity 0.5.16;

contract CoreErrors {
    string constant internal ERROR_CORE_NOT_POOL = "CORE:NOT_POOL";
    string constant internal ERROR_CORE_CANT_BE_POOL = "CORE:CANT_BE_POOL";

    string constant internal ERROR_CORE_TICKER_WAS_CANCELLED = "CORE:TICKER_WAS_CANCELLED";
    string constant internal ERROR_CORE_SYNTHETIC_VALIDATION_ERROR = "CORE:SYNTHETIC_VALIDATION_ERROR";
    string constant internal ERROR_CORE_NOT_ENOUGH_TOKEN_ALLOWANCE = "CORE:NOT_ENOUGH_TOKEN_ALLOWANCE";

    string constant internal ERROR_CORE_TOKEN_IDS_AND_QUANTITIES_LENGTH_DOES_NOT_MATCH = "CORE:TOKEN_IDS_AND_QUANTITIES_LENGTH_DOES_NOT_MATCH";
    string constant internal ERROR_CORE_TOKEN_IDS_AND_DERIVATIVES_LENGTH_DOES_NOT_MATCH = "CORE:TOKEN_IDS_AND_DERIVATIVES_LENGTH_DOES_NOT_MATCH";

    string constant internal ERROR_CORE_EXECUTION_BEFORE_MATURITY_NOT_ALLOWED = "CORE:EXECUTION_BEFORE_MATURITY_NOT_ALLOWED";
    string constant internal ERROR_CORE_SYNTHETIC_EXECUTION_WAS_NOT_ALLOWED = "CORE:SYNTHETIC_EXECUTION_WAS_NOT_ALLOWED";
    string constant internal ERROR_CORE_INSUFFICIENT_POOL_BALANCE = "CORE:INSUFFICIENT_POOL_BALANCE";

    string constant internal ERROR_CORE_CANT_CANCEL_DUMMY_ORACLE_ID = "CORE:CANT_CANCEL_DUMMY_ORACLE_ID";
    string constant internal ERROR_CORE_CANCELLATION_IS_NOT_ALLOWED = "CORE:CANCELLATION_IS_NOT_ALLOWED";

    string constant internal ERROR_CORE_UNKNOWN_POSITION_TYPE = "CORE:UNKNOWN_POSITION_TYPE";
}

// File: contracts/Errors/RegistryErrors.sol

pragma solidity 0.5.16;

contract RegistryErrors {
    string constant internal ERROR_REGISTRY_ONLY_INITIALIZER = "REGISTRY:ONLY_INITIALIZER";
    string constant internal ERROR_REGISTRY_ONLY_OPIUM_ADDRESS_ALLOWED = "REGISTRY:ONLY_OPIUM_ADDRESS_ALLOWED";

    string constant internal ERROR_REGISTRY_CANT_BE_ZERO_ADDRESS = "REGISTRY:CANT_BE_ZERO_ADDRESS";

    string constant internal ERROR_REGISTRY_ALREADY_SET = "REGISTRY:ALREADY_SET";
}

// File: contracts/Registry.sol

pragma solidity 0.5.16;


/// @title Opium.Registry contract keeps addresses of deployed Opium contracts set to allow them route and communicate to each other
contract Registry is RegistryErrors {

    // Address of Opium.TokenMinter contract
    address private minter;

    // Address of Opium.Core contract
    address private core;

    // Address of Opium.OracleAggregator contract
    address private oracleAggregator;

    // Address of Opium.SyntheticAggregator contract
    address private syntheticAggregator;

    // Address of Opium.TokenSpender contract
    address private tokenSpender;

    // Address of Opium commission receiver
    address private opiumAddress;

    // Address of Opium contract set deployer
    address public initializer;

    /// @notice This modifier restricts access to functions, which could be called only by initializer
    modifier onlyInitializer() {
        require(msg.sender == initializer, ERROR_REGISTRY_ONLY_INITIALIZER);
        _;
    }

    /// @notice Sets initializer
    constructor() public {
        initializer = msg.sender;
    }

    // SETTERS

    /// @notice Sets Opium.TokenMinter, Opium.Core, Opium.OracleAggregator, Opium.SyntheticAggregator, Opium.TokenSpender, Opium commission receiver addresses and allows to do it only once
    /// @param _minter address Address of Opium.TokenMinter
    /// @param _core address Address of Opium.Core
    /// @param _oracleAggregator address Address of Opium.OracleAggregator
    /// @param _syntheticAggregator address Address of Opium.SyntheticAggregator
    /// @param _tokenSpender address Address of Opium.TokenSpender
    /// @param _opiumAddress address Address of Opium commission receiver
    function init(
        address _minter,
        address _core,
        address _oracleAggregator,
        address _syntheticAggregator,
        address _tokenSpender,
        address _opiumAddress
    ) external onlyInitializer {
        require(
            minter == address(0) &&
            core == address(0) &&
            oracleAggregator == address(0) &&
            syntheticAggregator == address(0) &&
            tokenSpender == address(0) &&
            opiumAddress == address(0),
            ERROR_REGISTRY_ALREADY_SET
        );

        require(
            _minter != address(0) &&
            _core != address(0) &&
            _oracleAggregator != address(0) &&
            _syntheticAggregator != address(0) &&
            _tokenSpender != address(0) &&
            _opiumAddress != address(0),
            ERROR_REGISTRY_CANT_BE_ZERO_ADDRESS
        );

        minter = _minter;
        core = _core;
        oracleAggregator = _oracleAggregator;
        syntheticAggregator = _syntheticAggregator;
        tokenSpender = _tokenSpender;
        opiumAddress = _opiumAddress;
    }

    /// @notice Allows opium commission receiver address to change itself
    /// @param _opiumAddress address New opium commission receiver address
    function changeOpiumAddress(address _opiumAddress) external {
        require(opiumAddress == msg.sender, ERROR_REGISTRY_ONLY_OPIUM_ADDRESS_ALLOWED);
        require(_opiumAddress != address(0), ERROR_REGISTRY_CANT_BE_ZERO_ADDRESS);
        opiumAddress = _opiumAddress;
    }

    // GETTERS

    /// @notice Returns address of Opium.TokenMinter
    /// @param result address Address of Opium.TokenMinter
    function getMinter() external view returns (address result) {
        return minter;
    }

    /// @notice Returns address of Opium.Core
    /// @param result address Address of Opium.Core
    function getCore() external view returns (address result) {
        return core;
    }

    /// @notice Returns address of Opium.OracleAggregator
    /// @param result address Address of Opium.OracleAggregator
    function getOracleAggregator() external view returns (address result) {
        return oracleAggregator;
    }

    /// @notice Returns address of Opium.SyntheticAggregator
    /// @param result address Address of Opium.SyntheticAggregator
    function getSyntheticAggregator() external view returns (address result) {
        return syntheticAggregator;
    }

    /// @notice Returns address of Opium.TokenSpender
    /// @param result address Address of Opium.TokenSpender
    function getTokenSpender() external view returns (address result) {
        return tokenSpender;
    }

    /// @notice Returns address of Opium commission receiver
    /// @param result address Address of Opium commission receiver
    function getOpiumAddress() external view returns (address result) {
        return opiumAddress;
    }
}

// File: contracts/Errors/UsingRegistryErrors.sol

pragma solidity 0.5.16;

contract UsingRegistryErrors {
    string constant internal ERROR_USING_REGISTRY_ONLY_CORE_ALLOWED = "USING_REGISTRY:ONLY_CORE_ALLOWED";
}

// File: contracts/Lib/UsingRegistry.sol

pragma solidity 0.5.16;



/// @title Opium.Lib.UsingRegistry contract should be inherited by contracts, that are going to use Opium.Registry
contract UsingRegistry is UsingRegistryErrors {
    // Emitted when registry instance is set
    event RegistrySet(address registry);

    // Instance of Opium.Registry contract
    Registry internal registry;

    /// @notice This modifier restricts access to functions, which could be called only by Opium.Core
    modifier onlyCore() {
        require(msg.sender == registry.getCore(), ERROR_USING_REGISTRY_ONLY_CORE_ALLOWED);
        _;
    }

    /// @notice Defines registry instance and emits appropriate event
    constructor(address _registry) public {
        registry = Registry(_registry);
        emit RegistrySet(_registry);
    }

    /// @notice Getter for registry variable
    /// @return address Address of registry set in current contract
    function getRegistry() external view returns (address) {
        return address(registry);
    }
}

// File: contracts/Lib/LibCommission.sol

pragma solidity 0.5.16;

/// @title Opium.Lib.LibCommission contract defines constants for Opium commissions
contract LibCommission {
    // Represents 100% base for commissions calculation
    uint256 constant public COMMISSION_BASE = 10000;

    // Represents 100% base for Opium commission
    uint256 constant public OPIUM_COMMISSION_BASE = 10;

    // Represents which part of `syntheticId` author commissions goes to opium
    uint256 constant public OPIUM_COMMISSION_PART = 1;
}

// File: openzeppelin-solidity/contracts/token/ERC721/IERC721Receiver.sol

pragma solidity ^0.5.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
contract IERC721Receiver {
    /**
     * @notice Handle the receipt of an NFT
     * @dev The ERC721 smart contract calls this function on the recipient
     * after a {IERC721-safeTransferFrom}. This function MUST return the function selector,
     * otherwise the caller will revert the transaction. The selector to be
     * returned can be obtained as `this.onERC721Received.selector`. This
     * function MAY throw to revert and reject the transfer.
     * Note: the ERC721 contract address is always the message sender.
     * @param operator The address which called `safeTransferFrom` function
     * @param from The address which previously owned the token
     * @param tokenId The NFT identifier which is being transferred
     * @param data Additional data with no specified format
     * @return bytes4 `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
     */
    function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data)
    public returns (bytes4);
}

// File: erc721o/contracts/Libs/UintArray.sol

pragma solidity ^0.5.4;

library UintArray {
  function indexOf(uint256[] memory A, uint256 a) internal pure returns (uint256, bool) {
    uint256 length = A.length;
    for (uint256 i = 0; i < length; i++) {
      if (A[i] == a) {
        return (i, true);
      }
    }
    return (0, false);
  }

  function contains(uint256[] memory A, uint256 a) internal pure returns (bool) {
    (, bool isIn) = indexOf(A, a);
    return isIn;
  }

  function difference(uint256[] memory A, uint256[] memory B) internal pure returns (uint256[] memory, uint256[] memory) {
    uint256 length = A.length;
    bool[] memory includeMap = new bool[](length);
    uint256 count = 0;
    // First count the new length because can't push for in-memory arrays
    for (uint256 i = 0; i < length; i++) {
      uint256 e = A[i];
      if (!contains(B, e)) {
        includeMap[i] = true;
        count++;
      }
    }
    uint256[] memory newUints = new uint256[](count);
    uint256[] memory newUintsIdxs = new uint256[](count);
    uint256 j = 0;
    for (uint256 i = 0; i < length; i++) {
      if (includeMap[i]) {
        newUints[j] = A[i];
        newUintsIdxs[j] = i;
        j++;
      }
    }
    return (newUints, newUintsIdxs);
  }

  function intersect(uint256[] memory A, uint256[] memory B) internal pure returns (uint256[] memory, uint256[] memory, uint256[] memory) {
    uint256 length = A.length;
    bool[] memory includeMap = new bool[](length);
    uint256 newLength = 0;
    for (uint256 i = 0; i < length; i++) {
      if (contains(B, A[i])) {
        includeMap[i] = true;
        newLength++;
      }
    }
    uint256[] memory newUints = new uint256[](newLength);
    uint256[] memory newUintsAIdxs = new uint256[](newLength);
    uint256[] memory newUintsBIdxs = new uint256[](newLength);
    uint256 j = 0;
    for (uint256 i = 0; i < length; i++) {
      if (includeMap[i]) {
        newUints[j] = A[i];
        newUintsAIdxs[j] = i;
        (newUintsBIdxs[j], ) = indexOf(B, A[i]);
        j++;
      }
    }
    return (newUints, newUintsAIdxs, newUintsBIdxs);
  }

  function isUnique(uint256[] memory A) internal pure returns (bool) {
    uint256 length = A.length;

    for (uint256 i = 0; i < length; i++) {
      (uint256 idx, bool isIn) = indexOf(A, A[i]);

      if (isIn && idx < i) {
        return false;
      }
    }

    return true;
  }
}

// File: openzeppelin-solidity/contracts/introspection/IERC165.sol

pragma solidity ^0.5.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

// File: openzeppelin-solidity/contracts/introspection/ERC165.sol

pragma solidity ^0.5.0;


/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts may inherit from this and call {_registerInterface} to declare
 * their support of an interface.
 */
contract ERC165 is IERC165 {
    /*
     * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
     */
    bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;

    /**
     * @dev Mapping of interface ids to whether or not it's supported.
     */
    mapping(bytes4 => bool) private _supportedInterfaces;

    constructor () internal {
        // Derived contracts need only register support for their own interfaces,
        // we register support for ERC165 itself here
        _registerInterface(_INTERFACE_ID_ERC165);
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     *
     * Time complexity O(1), guaranteed to always use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool) {
        return _supportedInterfaces[interfaceId];
    }

    /**
     * @dev Registers the contract as an implementer of the interface defined by
     * `interfaceId`. Support of the actual ERC165 interface is automatic and
     * registering its interface id is not required.
     *
     * See {IERC165-supportsInterface}.
     *
     * Requirements:
     *
     * - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
     */
    function _registerInterface(bytes4 interfaceId) internal {
        require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
        _supportedInterfaces[interfaceId] = true;
    }
}

// File: openzeppelin-solidity/contracts/token/ERC721/IERC721.sol

pragma solidity ^0.5.0;


/**
 * @dev Required interface of an ERC721 compliant contract.
 */
contract IERC721 is IERC165 {
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of NFTs in `owner`'s account.
     */
    function balanceOf(address owner) public view returns (uint256 balance);

    /**
     * @dev Returns the owner of the NFT specified by `tokenId`.
     */
    function ownerOf(uint256 tokenId) public view returns (address owner);

    /**
     * @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to
     * another (`to`).
     *
     *
     *
     * Requirements:
     * - `from`, `to` cannot be zero.
     * - `tokenId` must be owned by `from`.
     * - If the caller is not `from`, it must be have been allowed to move this
     * NFT by either {approve} or {setApprovalForAll}.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) public;
    /**
     * @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to
     * another (`to`).
     *
     * Requirements:
     * - If the caller is not `from`, it must be approved to move this NFT by
     * either {approve} or {setApprovalForAll}.
     */
    function transferFrom(address from, address to, uint256 tokenId) public;
    function approve(address to, uint256 tokenId) public;
    function getApproved(uint256 tokenId) public view returns (address operator);

    function setApprovalForAll(address operator, bool _approved) public;
    function isApprovedForAll(address owner, address operator) public view returns (bool);


    function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public;
}

// File: erc721o/contracts/Interfaces/IERC721O.sol

pragma solidity ^0.5.4;

contract IERC721O {
  function name() external view returns (string memory);
  function symbol() external view returns (string memory);

  function implementsERC721O() public pure returns (bool);
  function ownerOf(uint256 _tokenId) public view returns (address _owner);
  function balanceOf(address owner) public view returns (uint256);
  function balanceOf(address owner, uint256 tokenId) public view returns (uint256);
  function tokensOwned(address owner) public view returns (uint256[] memory, uint256[] memory);

  function transfer(address to, uint256 tokenId, uint256 quantity) public;
  function transferFrom(address from, address to, uint256 tokenId, uint256 quantity) public;

  // Fungible Safe Transfer From
  function safeTransferFrom(address from, address to, uint256 tokenId, uint256 _amount) public;
  function safeTransferFrom(address from, address to, uint256 tokenId, uint256 _amount, bytes memory data) public;

  // Batch Safe Transfer From
  function safeBatchTransferFrom(address _from, address _to, uint256[] memory tokenIds, uint256[] memory _amounts, bytes memory _data) public;

  // Required Events
  event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
  event TransferWithQuantity(address indexed from, address indexed to, uint256 indexed tokenId, uint256 quantity);
  event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
  event BatchTransfer(address indexed from, address indexed to, uint256[] tokenTypes, uint256[] amounts);
  event Composition(uint256 portfolioId, uint256[] tokenIds, uint256[] tokenRatio);
}

// File: erc721o/contracts/Interfaces/IERC721OReceiver.sol

pragma solidity ^0.5.4;

/**
 * @title ERC721O token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 *  from ERC721O contracts.
 */
contract IERC721OReceiver {
  /**
    * @dev Magic value to be returned upon successful reception of an amount of ERC721O tokens
    *  ERC721O_RECEIVED = `bytes4(keccak256("onERC721OReceived(address,address,uint256,uint256,bytes)"))` = 0xf891ffe0
    *  ERC721O_BATCH_RECEIVED = `bytes4(keccak256("onERC721OBatchReceived(address,address,uint256[],uint256[],bytes)"))` = 0xd0e17c0b
    */
  bytes4 constant internal ERC721O_RECEIVED = 0xf891ffe0;
  bytes4 constant internal ERC721O_BATCH_RECEIVED = 0xd0e17c0b;

  function onERC721OReceived(
    address _operator,
    address _from,
    uint256 tokenId,
    uint256 amount,
    bytes memory data
  ) public returns(bytes4);

  function onERC721OBatchReceived(
    address _operator,
    address _from,
    uint256[] memory _types,
    uint256[] memory _amounts,
    bytes memory _data
  ) public returns (bytes4);
}

// File: erc721o/contracts/Libs/ObjectsLib.sol

pragma solidity ^0.5.4;


library ObjectLib {
  // Libraries
  using SafeMath for uint256;

  enum Operations { ADD, SUB, REPLACE }

  // Constants regarding bin or chunk sizes for balance packing
  uint256 constant TYPES_BITS_SIZE   = 32;                     // Max size of each object
  uint256 constant TYPES_PER_UINT256 = 256 / TYPES_BITS_SIZE; // Number of types per uint256

  //
  // Objects and Tokens Functions
  //

  /**
  * @dev Return the bin number and index within that bin where ID is
  * @param _tokenId Object type
  * @return (Bin number, ID's index within that bin)
  */
  function getTokenBinIndex(uint256 _tokenId) internal pure returns (uint256 bin, uint256 index) {
    bin = _tokenId * TYPES_BITS_SIZE / 256;
    index = _tokenId % TYPES_PER_UINT256;
    return (bin, index);
  }


  /**
  * @dev update the balance of a type provided in _binBalances
  * @param _binBalances Uint256 containing the balances of objects
  * @param _index Index of the object in the provided bin
  * @param _amount Value to update the type balance
  * @param _operation Which operation to conduct :
  *     Operations.REPLACE : Replace type balance with _amount
  *     Operations.ADD     : ADD _amount to type balance
  *     Operations.SUB     : Substract _amount from type balance
  */
  function updateTokenBalance(
    uint256 _binBalances,
    uint256 _index,
    uint256 _amount,
    Operations _operation) internal pure returns (uint256 newBinBalance)
  {
    uint256 objectBalance;
    if (_operation == Operations.ADD) {
      objectBalance = getValueInBin(_binBalances, _index);
      newBinBalance = writeValueInBin(_binBalances, _index, objectBalance.add(_amount));
    } else if (_operation == Operations.SUB) {
      objectBalance = getValueInBin(_binBalances, _index);
      newBinBalance = writeValueInBin(_binBalances, _index, objectBalance.sub(_amount));
    } else if (_operation == Operations.REPLACE) {
      newBinBalance = writeValueInBin(_binBalances, _index, _amount);
    } else {
      revert("Invalid operation"); // Bad operation
    }

    return newBinBalance;
  }

  /*
  * @dev return value in _binValue at position _index
  * @param _binValue uint256 containing the balances of TYPES_PER_UINT256 types
  * @param _index index at which to retrieve value
  * @return Value at given _index in _bin
  */
  function getValueInBin(uint256 _binValue, uint256 _index) internal pure returns (uint256) {

    // Mask to retrieve data for a given binData
    uint256 mask = (uint256(1) << TYPES_BITS_SIZE) - 1;

    // Shift amount
    uint256 rightShift = 256 - TYPES_BITS_SIZE * (_index + 1);
    return (_binValue >> rightShift) & mask;
  }

  /**
  * @dev return the updated _binValue after writing _amount at _index
  * @param _binValue uint256 containing the balances of TYPES_PER_UINT256 types
  * @param _index Index at which to retrieve value
  * @param _amount Value to store at _index in _bin
  * @return Value at given _index in _bin
  */
  function writeValueInBin(uint256 _binValue, uint256 _index, uint256 _amount) internal pure returns (uint256) {
    require(_amount < 2**TYPES_BITS_SIZE, "Amount to write in bin is too large");

    // Mask to retrieve data for a given binData
    uint256 mask = (uint256(1) << TYPES_BITS_SIZE) - 1;

    // Shift amount
    uint256 leftShift = 256 - TYPES_BITS_SIZE * (_index + 1);
    return (_binValue & ~(mask << leftShift) ) | (_amount << leftShift);
  }

}

// File: erc721o/contracts/ERC721OBase.sol

pragma solidity ^0.5.4;






contract ERC721OBase is IERC721O, ERC165, IERC721 {
  // Libraries
  using ObjectLib for ObjectLib.Operations;
  using ObjectLib for uint256;

  // Array with all tokenIds
  uint256[] internal allTokens;

  // Packed balances
  mapping(address => mapping(uint256 => uint256)) internal packedTokenBalance;

  // Operators
  mapping(address => mapping(address => bool)) internal operators;

  // Keeps aprovals for tokens from owner to approved address
  // tokenApprovals[tokenId][owner] = approved
  mapping (uint256 => mapping (address => address)) internal tokenApprovals;

  // Token Id state
  mapping(uint256 => uint256) internal tokenTypes;

  uint256 constant internal INVALID = 0;
  uint256 constant internal POSITION = 1;
  uint256 constant internal PORTFOLIO = 2;

  // Interface constants
  bytes4 internal constant INTERFACE_ID_ERC721O = 0x12345678;

  // EIP712 constants
  bytes32 public DOMAIN_SEPARATOR;
  bytes32 public PERMIT_TYPEHASH;

  // mapping holds nonces for approval permissions
  // nonces[holder] => nonce
  mapping (address => uint) public nonces;

  modifier isOperatorOrOwner(address _from) {
    require((msg.sender == _from) || operators[_from][msg.sender], "msg.sender is neither _from nor operator");
    _;
  }

  constructor() public {
    _registerInterface(INTERFACE_ID_ERC721O);

    // Calculate EIP712 constants
    DOMAIN_SEPARATOR = keccak256(abi.encode(
      keccak256("EIP712Domain(string name,string version,address verifyingContract)"),
      keccak256(bytes("ERC721o")),
      keccak256(bytes("1")),
      address(this)
    ));
    PERMIT_TYPEHASH = keccak256("Permit(address holder,address spender,uint256 nonce,uint256 expiry,bool allowed)");
  }

  function implementsERC721O() public pure returns (bool) {
    return true;
  }

  /**
   * @dev Returns whether the specified token exists
   * @param _tokenId uint256 ID of the token to query the existence of
   * @return whether the token exists
   */
  function exists(uint256 _tokenId) public view returns (bool) {
    return tokenTypes[_tokenId] != INVALID;
  }

  /**
   * @dev return the _tokenId type' balance of _address
   * @param _address Address to query balance of
   * @param _tokenId type to query balance of
   * @return Amount of objects of a given type ID
   */
  function balanceOf(address _address, uint256 _tokenId) public view returns (uint256) {
    (uint256 bin, uint256 index) = _tokenId.getTokenBinIndex();
    return packedTokenBalance[_address][bin].getValueInBin(index);
  }

  /**
   * @dev Gets the total amount of tokens stored by the contract
   * @return uint256 representing the total amount of tokens
   */
  function totalSupply() public view returns (uint256) {
    return allTokens.length;
  }

  /**
   * @dev Gets Iterate through the list of existing tokens and return the indexes
   *        and balances of the tokens owner by the user
   * @param _owner The adddress we are checking
   * @return indexes The tokenIds
   * @return balances The balances of each token
   */
  function tokensOwned(address _owner) public view returns (uint256[] memory indexes, uint256[] memory balances) {
    uint256 numTokens = totalSupply();
    uint256[] memory tokenIndexes = new uint256[](numTokens);
    uint256[] memory tempTokens = new uint256[](numTokens);

    uint256 count;
    for (uint256 i = 0; i < numTokens; i++) {
      uint256 tokenId = allTokens[i];
      if (balanceOf(_owner, tokenId) > 0) {
        tempTokens[count] = balanceOf(_owner, tokenId);
        tokenIndexes[count] = tokenId;
        count++;
      }
    }

    // copy over the data to a correct size array
    uint256[] memory _ownedTokens = new uint256[](count);
    uint256[] memory _ownedTokensIndexes = new uint256[](count);

    for (uint256 i = 0; i < count; i++) {
      _ownedTokens[i] = tempTokens[i];
      _ownedTokensIndexes[i] = tokenIndexes[i];
    }

    return (_ownedTokensIndexes, _ownedTokens);
  }

  /**
   * @dev Will set _operator operator status to true or false
   * @param _operator Address to changes operator status.
   * @param _approved  _operator's new operator status (true or false)
   */
  function setApprovalForAll(address _operator, bool _approved) public {
    // Update operator status
    operators[msg.sender][_operator] = _approved;
    emit ApprovalForAll(msg.sender, _operator, _approved);
  }

  /// @notice Approve for all by signature
  function permit(address _holder, address _spender, uint256 _nonce, uint256 _expiry, bool _allowed, bytes calldata _signature) external {
    // Calculate hash
    bytes32 digest =
      keccak256(abi.encodePacked(
        "\x19\x01",
        DOMAIN_SEPARATOR,
        keccak256(abi.encode(
          PERMIT_TYPEHASH,
          _holder,
          _spender,
          _nonce,
          _expiry,
          _allowed
        ))
    ));

    // Divide the signature in r, s and v variables
    // ecrecover takes the signature parameters, and the only way to get them
    // currently is to use assembly.
    // solium-disable-next-line security/no-inline-assembly
    bytes32 r;
    bytes32 s;
    uint8 v;

    bytes memory signature = _signature;

    assembly {
      r := mload(add(signature, 32))
      s := mload(add(signature, 64))
      v := byte(0, mload(add(signature, 96)))
    }

    // Version of signature should be 27 or 28, but 0 and 1 are also possible versions
    if (v < 27) {
      v += 27;
    }

    address recoveredAddress;

    // If the version is correct return the signer address
    if (v != 27 && v != 28) {
      recoveredAddress = address(0);
    } else {
      // solium-disable-next-line arg-overflow
      recoveredAddress = ecrecover(digest, v, r, s);
    }

    require(_holder != address(0), "Holder can't be zero address");
    require(_holder == recoveredAddress, "Signer address is invalid");
    require(_expiry == 0 || now <= _expiry, "Permission expired");
    require(_nonce == nonces[_holder]++, "Nonce is invalid");

    // Update operator status
    operators[_holder][_spender] = _allowed;
    emit ApprovalForAll(_holder, _spender, _allowed);
  }

  /**
   * @dev Approves another address to transfer the given token ID
   * The zero address indicates there is no approved address.
   * There can only be one approved address per token at a given time.
   * Can only be called by the token owner or an approved operator.
   * @param _to address to be approved for the given token ID
   * @param _tokenId uint256 ID of the token to be approved
   */
  function approve(address _to, uint256 _tokenId) public {
    require(_to != msg.sender, "Can't approve to yourself");
    tokenApprovals[_tokenId][msg.sender] = _to;
    emit Approval(msg.sender, _to, _tokenId);
  }

  /**
   * @dev Gets the approved address for a token ID, or zero if no address set
   * @param _tokenId uint256 ID of the token to query the approval of
   * @return address currently approved for the given token ID
   */
  function getApproved(uint256 _tokenId, address _tokenOwner) public view returns (address) {
    return tokenApprovals[_tokenId][_tokenOwner];
  }

  /**
   * @dev Function that verifies whether _operator is an authorized operator of _tokenHolder.
   * @param _operator The address of the operator to query status of
   * @param _owner Address of the tokenHolder
   * @return A uint256 specifying the amount of tokens still available for the spender.
   */
  function isApprovedForAll(address _owner, address _operator) public view returns (bool isOperator) {
    return operators[_owner][_operator];
  }

  function isApprovedOrOwner(
    address _spender,
    address _owner,
    uint256 _tokenId
  ) public view returns (bool) {
    return (
      _spender == _owner ||
      getApproved(_tokenId, _owner) == _spender ||
      isApprovedForAll(_owner, _spender)
    );
  }

  function _updateTokenBalance(
    address _from,
    uint256 _tokenId,
    uint256 _amount,
    ObjectLib.Operations op
  ) internal {
    (uint256 bin, uint256 index) = _tokenId.getTokenBinIndex();
    packedTokenBalance[_from][bin] = packedTokenBalance[_from][bin].updateTokenBalance(
      index, _amount, op
    );
  }
}

// File: erc721o/contracts/ERC721OTransferable.sol

pragma solidity ^0.5.4;




contract ERC721OTransferable is ERC721OBase, ReentrancyGuard {
  // Libraries
  using Address for address;

  // safeTransfer constants
  bytes4 internal constant ERC721O_RECEIVED = 0xf891ffe0;
  bytes4 internal constant ERC721O_BATCH_RECEIVED = 0xd0e17c0b;

  function batchTransferFrom(address _from, address _to, uint256[] memory _tokenIds, uint256[] memory _amounts) public {
    // Batch Transfering
    _batchTransferFrom(_from, _to, _tokenIds, _amounts);
  }

  /**
    * @dev transfer objects from different tokenIds to specified address
    * @param _from The address to BatchTransfer objects from.
    * @param _to The address to batchTransfer objects to.
    * @param _tokenIds Array of tokenIds to update balance of
    * @param _amounts Array of amount of object per type to be transferred.
    * @param _data Data to pass to onERC721OReceived() function if recipient is contract
    * Note:  Arrays should be sorted so that all tokenIds in a same bin are adjacent (more efficient).
    */
  function safeBatchTransferFrom(
    address _from,
    address _to,
    uint256[] memory _tokenIds,
    uint256[] memory _amounts,
    bytes memory _data
  ) public nonReentrant {
    // Batch Transfering
    _batchTransferFrom(_from, _to, _tokenIds, _amounts);

    // Pass data if recipient is contract
    if (_to.isContract()) {
      bytes4 retval = IERC721OReceiver(_to).onERC721OBatchReceived(
        msg.sender, _from, _tokenIds, _amounts, _data
      );
      require(retval == ERC721O_BATCH_RECEIVED);
    }
  }

  function safeBatchTransferFrom(
    address _from,
    address _to,
    uint256[] memory _tokenIds,
    uint256[] memory _amounts
  ) public {
    safeBatchTransferFrom(_from, _to, _tokenIds, _amounts, "");
  }

  function transfer(address _to, uint256 _tokenId, uint256 _amount) public {
    _transferFrom(msg.sender, _to, _tokenId, _amount);
  }

  function transferFrom(address _from, address _to, uint256 _tokenId, uint256 _amount) public {
    _transferFrom(_from, _to, _tokenId, _amount);
  }

  function safeTransferFrom(address _from, address _to, uint256 _tokenId, uint256 _amount) public {
    safeTransferFrom(_from, _to, _tokenId, _amount, "");
  }

  function safeTransferFrom(address _from, address _to, uint256 _tokenId, uint256 _amount, bytes memory _data) public nonReentrant {
    _transferFrom(_from, _to, _tokenId, _amount);
    require(
      _checkAndCallSafeTransfer(_from, _to, _tokenId, _amount, _data),
      "Sent to a contract which is not an ERC721O receiver"
    );
  }

  /**
    * @dev transfer objects from different tokenIds to specified address
    * @param _from The address to BatchTransfer objects from.
    * @param _to The address to batchTransfer objects to.
    * @param _tokenIds Array of tokenIds to update balance of
    * @param _amounts Array of amount of object per type to be transferred.
    * Note:  Arrays should be sorted so that all tokenIds in a same bin are adjacent (more efficient).
    */
  function _batchTransferFrom(
    address _from,
    address _to,
    uint256[] memory _tokenIds,
    uint256[] memory _amounts
  ) internal isOperatorOrOwner(_from) {
    // Requirements
    require(_tokenIds.length == _amounts.length, "Inconsistent array length between args");
    require(_to != address(0), "Invalid recipient");

    // Load first bin and index where the object balance exists
    (uint256 bin, uint256 index) = ObjectLib.getTokenBinIndex(_tokenIds[0]);

    // Balance for current bin in memory (initialized with first transfer)
    // Written with bad library syntax instead of as below to bypass stack limit error
    uint256 balFrom = ObjectLib.updateTokenBalance(
      packedTokenBalance[_from][bin], index, _amounts[0], ObjectLib.Operations.SUB
    );
    uint256 balTo = ObjectLib.updateTokenBalance(
      packedTokenBalance[_to][bin], index, _amounts[0], ObjectLib.Operations.ADD
    );

    emit Transfer(_from, _to, _tokenIds[0]);
    emit TransferWithQuantity(_from, _to, _tokenIds[0], _amounts[0]);

    // Number of transfers to execute
    uint256 nTransfer = _tokenIds.length;

    // Last bin updated
    uint256 lastBin = bin;

    for (uint256 i = 1; i < nTransfer; i++) {
      (bin, index) = _tokenIds[i].getTokenBinIndex();

      // If new bin
      if (bin != lastBin) {
        // Update storage balance of previous bin
        packedTokenBalance[_from][lastBin] = balFrom;
        packedTokenBalance[_to][lastBin] = balTo;

        // Load current bin balance in memory
        balFrom = packedTokenBalance[_from][bin];
        balTo = packedTokenBalance[_to][bin];

        // Bin will be the most recent bin
        lastBin = bin;
      }

      // Update memory balance
      balFrom = balFrom.updateTokenBalance(index, _amounts[i], ObjectLib.Operations.SUB);
      balTo = balTo.updateTokenBalance(index, _amounts[i], ObjectLib.Operations.ADD);

      emit Transfer(_from, _to, _tokenIds[i]);
      emit TransferWithQuantity(_from, _to, _tokenIds[i], _amounts[i]);
    }

    // Update storage of the last bin visited
    packedTokenBalance[_from][bin] = balFrom;
    packedTokenBalance[_to][bin] = balTo;

    // Emit batchTransfer event
    emit BatchTransfer(_from, _to, _tokenIds, _amounts);
  }

  function _transferFrom(address _from, address _to, uint256 _tokenId, uint256 _amount) internal {
    require(isApprovedOrOwner(msg.sender, _from, _tokenId), "Not approved");
    require(_amount <= balanceOf(_from, _tokenId), "Quantity greater than from balance");
    require(_to != address(0), "Invalid to address");

    _updateTokenBalance(_from, _tokenId, _amount, ObjectLib.Operations.SUB);
    _updateTokenBalance(_to, _tokenId, _amount, ObjectLib.Operations.ADD);
    emit Transfer(_from, _to, _tokenId);
    emit TransferWithQuantity(_from, _to, _tokenId, _amount);
  }

  function _checkAndCallSafeTransfer(
    address _from,
    address _to,
    uint256 _tokenId,
    uint256 _amount,
    bytes memory _data
  ) internal returns (bool) {
    if (!_to.isContract()) {
      return true;
    }

    bytes4 retval = IERC721OReceiver(_to).onERC721OReceived(msg.sender, _from, _tokenId, _amount, _data);
    return(retval == ERC721O_RECEIVED);
  }
}

// File: erc721o/contracts/ERC721OMintable.sol

pragma solidity ^0.5.4;



contract ERC721OMintable is ERC721OTransferable {
  // Libraries
  using LibPosition for bytes32;

  // Internal functions
  function _mint(uint256 _tokenId, address _to, uint256 _supply) internal {
    // If the token doesn't exist, add it to the tokens array
    if (!exists(_tokenId)) {
      tokenTypes[_tokenId] = POSITION;
      allTokens.push(_tokenId);
    }

    _updateTokenBalance(_to, _tokenId, _supply, ObjectLib.Operations.ADD);
    emit Transfer(address(0), _to, _tokenId);
    emit TransferWithQuantity(address(0), _to, _tokenId, _supply);
  }

  function _burn(address _tokenOwner, uint256 _tokenId, uint256 _quantity) internal {
    uint256 ownerBalance = balanceOf(_tokenOwner, _tokenId);
    require(ownerBalance >= _quantity, "TOKEN_MINTER:NOT_ENOUGH_POSITIONS");

    _updateTokenBalance(_tokenOwner, _tokenId, _quantity, ObjectLib.Operations.SUB);
    emit Transfer(_tokenOwner, address(0), _tokenId);
    emit TransferWithQuantity(_tokenOwner, address(0), _tokenId, _quantity);
  }

  function _mint(address _buyer, address _seller, bytes32 _derivativeHash, uint256 _quantity) internal {
    _mintLong(_buyer, _derivativeHash, _quantity);
    _mintShort(_seller, _derivativeHash, _quantity);
  }

  function _mintLong(address _buyer, bytes32 _derivativeHash, uint256 _quantity) internal {
    uint256 longTokenId = _derivativeHash.getLongTokenId();
    _mint(longTokenId, _buyer, _quantity);
  }

  function _mintShort(address _seller, bytes32 _derivativeHash, uint256 _quantity) internal {
    uint256 shortTokenId = _derivativeHash.getShortTokenId();
    _mint(shortTokenId, _seller, _quantity);
  }

  function _registerPortfolio(uint256 _portfolioId, uint256[] memory _tokenIds, uint256[] memory _tokenRatio) internal {
    if (!exists(_portfolioId)) {
      tokenTypes[_portfolioId] = PORTFOLIO;
      emit Composition(_portfolioId, _tokenIds, _tokenRatio);
    }
  }
}

// File: erc721o/contracts/ERC721OComposable.sol

pragma solidity ^0.5.4;



contract ERC721OComposable is ERC721OMintable {
  // Libraries
  using UintArray for uint256[];

  function compose(uint256[] memory _tokenIds, uint256[] memory _tokenRatio, uint256 _quantity) public {
    require(_tokenIds.length == _tokenRatio.length, "TOKEN_MINTER:TOKEN_IDS_AND_RATIO_LENGTH_DOES_NOT_MATCH");
    require(_quantity > 0, "TOKEN_MINTER:WRONG_QUANTITY");
    require(_tokenIds.length > 0, "TOKEN_MINTER:WRONG_QUANTITY");
    require(_tokenIds.isUnique(), "TOKEN_MINTER:TOKEN_IDS_NOT_UNIQUE");

    for (uint256 i = 0; i < _tokenIds.length; i++) {
      _burn(msg.sender, _tokenIds[i], _tokenRatio[i] * _quantity);
    }

    uint256 portfolioId = uint256(keccak256(abi.encodePacked(
      _tokenIds,
      _tokenRatio
    )));

    _registerPortfolio(portfolioId, _tokenIds, _tokenRatio);
    _mint(portfolioId, msg.sender, _quantity);
  }

  function decompose(uint256 _portfolioId, uint256[] memory _tokenIds, uint256[] memory _tokenRatio, uint256 _quantity) public {
    require(_tokenIds.length == _tokenRatio.length, "TOKEN_MINTER:TOKEN_IDS_AND_RATIO_LENGTH_DOES_NOT_MATCH");
    require(_quantity > 0, "TOKEN_MINTER:WRONG_QUANTITY");
    require(_tokenIds.length > 0, "TOKEN_MINTER:WRONG_QUANTITY");
    require(_tokenIds.isUnique(), "TOKEN_MINTER:TOKEN_IDS_NOT_UNIQUE");

    uint256 portfolioId = uint256(keccak256(abi.encodePacked(
      _tokenIds,
      _tokenRatio
    )));

    require(portfolioId == _portfolioId, "TOKEN_MINTER:WRONG_PORTFOLIO_ID");
    _burn(msg.sender, _portfolioId, _quantity);

    for (uint256 i = 0; i < _tokenIds.length; i++) {
      _mint(_tokenIds[i], msg.sender, _tokenRatio[i] * _quantity);
    }
  }

  function recompose(
    uint256 _portfolioId,
    uint256[] memory _initialTokenIds,
    uint256[] memory _initialTokenRatio,
    uint256[] memory _finalTokenIds,
    uint256[] memory _finalTokenRatio,
    uint256 _quantity
  ) public {
    require(_initialTokenIds.length == _initialTokenRatio.length, "TOKEN_MINTER:INITIAL_TOKEN_IDS_AND_RATIO_LENGTH_DOES_NOT_MATCH");
    require(_finalTokenIds.length == _finalTokenRatio.length, "TOKEN_MINTER:FINAL_TOKEN_IDS_AND_RATIO_LENGTH_DOES_NOT_MATCH");
    require(_quantity > 0, "TOKEN_MINTER:WRONG_QUANTITY");
    require(_initialTokenIds.length > 0, "TOKEN_MINTER:WRONG_QUANTITY");
    require(_finalTokenIds.length > 0, "TOKEN_MINTER:WRONG_QUANTITY");
    require(_initialTokenIds.isUnique(), "TOKEN_MINTER:TOKEN_IDS_NOT_UNIQUE");
    require(_finalTokenIds.isUnique(), "TOKEN_MINTER:TOKEN_IDS_NOT_UNIQUE");

    uint256 oldPortfolioId = uint256(keccak256(abi.encodePacked(
      _initialTokenIds,
      _initialTokenRatio
    )));

    require(oldPortfolioId == _portfolioId, "TOKEN_MINTER:WRONG_PORTFOLIO_ID");
    _burn(msg.sender, _portfolioId, _quantity);

    _removedIds(_initialTokenIds, _initialTokenRatio, _finalTokenIds, _finalTokenRatio, _quantity);
    _addedIds(_initialTokenIds, _initialTokenRatio, _finalTokenIds, _finalTokenRatio, _quantity);
    _keptIds(_initialTokenIds, _initialTokenRatio, _finalTokenIds, _finalTokenRatio, _quantity);

    uint256 newPortfolioId = uint256(keccak256(abi.encodePacked(
      _finalTokenIds,
      _finalTokenRatio
    )));

    _registerPortfolio(newPortfolioId, _finalTokenIds, _finalTokenRatio);
    _mint(newPortfolioId, msg.sender, _quantity);
  }

  function _removedIds(
    uint256[] memory _initialTokenIds,
    uint256[] memory _initialTokenRatio,
    uint256[] memory _finalTokenIds,
    uint256[] memory _finalTokenRatio,
    uint256 _quantity
  ) private {
    (uint256[] memory removedIds, uint256[] memory removedIdsIdxs) = _initialTokenIds.difference(_finalTokenIds);

    for (uint256 i = 0; i < removedIds.length; i++) {
      uint256 index = removedIdsIdxs[i];
      _mint(_initialTokenIds[index], msg.sender, _initialTokenRatio[index] * _quantity);
    }

    _finalTokenRatio;
  }

  function _addedIds(
      uint256[] memory _initialTokenIds,
      uint256[] memory _initialTokenRatio,
      uint256[] memory _finalTokenIds,
      uint256[] memory _finalTokenRatio,
      uint256 _quantity
  ) private {
    (uint256[] memory addedIds, uint256[] memory addedIdsIdxs) = _finalTokenIds.difference(_initialTokenIds);

    for (uint256 i = 0; i < addedIds.length; i++) {
      uint256 index = addedIdsIdxs[i];
      _burn(msg.sender, _finalTokenIds[index], _finalTokenRatio[index] * _quantity);
    }

    _initialTokenRatio;
  }

  function _keptIds(
      uint256[] memory _initialTokenIds,
      uint256[] memory _initialTokenRatio,
      uint256[] memory _finalTokenIds,
      uint256[] memory _finalTokenRatio,
      uint256 _quantity
  ) private {
    (uint256[] memory keptIds, uint256[] memory keptInitialIdxs, uint256[] memory keptFinalIdxs) = _initialTokenIds.intersect(_finalTokenIds);

    for (uint256 i = 0; i < keptIds.length; i++) {
      uint256 initialIndex = keptInitialIdxs[i];
      uint256 finalIndex = keptFinalIdxs[i];

      if (_initialTokenRatio[initialIndex] > _finalTokenRatio[finalIndex]) {
        uint256 diff = _initialTokenRatio[initialIndex] - _finalTokenRatio[finalIndex];
        _mint(_initialTokenIds[initialIndex], msg.sender, diff * _quantity);
      } else if (_initialTokenRatio[initialIndex] < _finalTokenRatio[finalIndex]) {
        uint256 diff = _finalTokenRatio[finalIndex] - _initialTokenRatio[initialIndex];
        _burn(msg.sender, _initialTokenIds[initialIndex], diff * _quantity);
      }
    }
  }
}

// File: erc721o/contracts/Libs/UintsLib.sol

pragma solidity ^0.5.4;

library UintsLib {
  function uint2str(uint _i) internal pure returns (string memory _uintAsString) {
    if (_i == 0) {
      return "0";
    }

    uint j = _i;
    uint len;
    while (j != 0) {
      len++;
      j /= 10;
    }

    bytes memory bstr = new bytes(len);
    uint k = len - 1;
    while (_i != 0) {
      bstr[k--] = byte(uint8(48 + _i % 10));
      _i /= 10;
    }

    return string(bstr);
  }
}

// File: erc721o/contracts/ERC721OBackwardCompatible.sol

pragma solidity ^0.5.4;




contract ERC721OBackwardCompatible is ERC721OComposable {
  using UintsLib for uint256;

  // Interface constants
  bytes4 internal constant INTERFACE_ID_ERC721 = 0x80ac58cd;
  bytes4 internal constant INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63;
  bytes4 internal constant INTERFACE_ID_ERC721_METADATA = 0x5b5e139f;

  // Reciever constants
  bytes4 internal constant ERC721_RECEIVED = 0x150b7a02;

  // Metadata URI
  string internal baseTokenURI;

  constructor(string memory _baseTokenURI) public ERC721OBase() {
    baseTokenURI = _baseTokenURI;
    _registerInterface(INTERFACE_ID_ERC721);
    _registerInterface(INTERFACE_ID_ERC721_ENUMERABLE);
    _registerInterface(INTERFACE_ID_ERC721_METADATA);
  }

  // ERC721 compatibility
  function implementsERC721() public pure returns (bool) {
    return true;
  }

  /**
    * @dev Gets the owner of a given NFT
    * @param _tokenId uint256 representing the unique token identifier
    * @return address the owner of the token
    */
  function ownerOf(uint256 _tokenId) public view returns (address) {
    if (exists(_tokenId)) {
      return address(this);
    }

    return address(0);
  }

  /**
   *  @dev Gets the number of tokens owned by the address we are checking
   *  @param _owner The adddress we are checking
   *  @return balance The unique amount of tokens owned
   */
  function balanceOf(address _owner) public view returns (uint256 balance) {
    (, uint256[] memory tokens) = tokensOwned(_owner);
    return tokens.length;
  }

  // ERC721 - Enumerable compatibility
  /**
   * @dev Gets the token ID at a given index of all the tokens in this contract
   * Reverts if the index is greater or equal to the total number of tokens
   * @param _index uint256 representing the index to be accessed of the tokens list
   * @return uint256 token ID at the given index of the tokens list
   */
  function tokenByIndex(uint256 _index) public view returns (uint256) {
    require(_index < totalSupply());
    return allTokens[_index];
  }

  function tokenOfOwnerByIndex(address _owner, uint256 _index) public view returns (uint256 _tokenId) {
    (, uint256[] memory tokens) = tokensOwned(_owner);
    require(_index < tokens.length);
    return tokens[_index];
  }

  // ERC721 - Metadata compatibility
  function tokenURI(uint256 _tokenId) public view returns (string memory tokenUri) {
    require(exists(_tokenId), "Token doesn't exist");
    return string(abi.encodePacked(
      baseTokenURI,
      _tokenId.uint2str(),
      ".json"
    ));
  }

  /**
   * @dev Gets the approved address for a token ID, or zero if no address set
   * @param _tokenId uint256 ID of the token to query the approval of
   * @return address currently approved for the given token ID
   */
  function getApproved(uint256 _tokenId) public view returns (address) {
    if (exists(_tokenId)) {
      return address(this);
    }

    return address(0);
  }

  function safeTransferFrom(address _from, address _to, uint256 _tokenId) public {
    safeTransferFrom(_from, _to, _tokenId, "");
  }

  function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes memory _data) public nonReentrant {
    _transferFrom(_from, _to, _tokenId, 1);
    require(
      _checkAndCallSafeTransfer(_from, _to, _tokenId, _data),
      "Sent to a contract which is not an ERC721 receiver"
    );
  }

  function transferFrom(address _from, address _to, uint256 _tokenId) public {
    _transferFrom(_from, _to, _tokenId, 1);
  }

  /**
   * @dev Internal function to invoke `onERC721Received` on a target address
   * The call is not executed if the target address is not a contract
   * @param _from address representing the previous owner of the given token ID
   * @param _to target address that will receive the tokens
   * @param _tokenId uint256 ID of the token to be transferred
   * @param _data bytes optional data to send along with the call
   * @return whether the call correctly returned the expected magic value
   */
  function _checkAndCallSafeTransfer(
    address _from,
    address _to,
    uint256 _tokenId,
    bytes memory _data
  ) internal returns (bool) {
    if (!_to.isContract()) {
      return true;
    }
    bytes4 retval = IERC721Receiver(_to).onERC721Received(
        msg.sender, _from, _tokenId, _data
    );
    return (retval == ERC721_RECEIVED);
  }
}

// File: contracts/TokenMinter.sol

pragma solidity 0.5.16;



/// @title Opium.TokenMinter contract implements ERC721O token standard for minting, burning and transferring position tokens
contract TokenMinter is ERC721OBackwardCompatible, UsingRegistry {
    /// @notice Calls constructors of super-contracts
    /// @param _baseTokenURI string URI for token explorers
    /// @param _registry address Address of Opium.registry
    constructor(string memory _baseTokenURI, address _registry) public ERC721OBackwardCompatible(_baseTokenURI) UsingRegistry(_registry) {}

    /// @notice Mints LONG and SHORT position tokens
    /// @param _buyer address Address of LONG position receiver
    /// @param _seller address Address of SHORT position receiver
    /// @param _derivativeHash bytes32 Hash of derivative (ticker) of position
    /// @param _quantity uint256 Quantity of positions to mint
    function mint(address _buyer, address _seller, bytes32 _derivativeHash, uint256 _quantity) external onlyCore {
        _mint(_buyer, _seller, _derivativeHash, _quantity);
    }

    /// @notice Mints only LONG position tokens for "pooled" derivatives
    /// @param _buyer address Address of LONG position receiver
    /// @param _derivativeHash bytes32 Hash of derivative (ticker) of position
    /// @param _quantity uint256 Quantity of positions to mint
    function mint(address _buyer, bytes32 _derivativeHash, uint256 _quantity) external onlyCore {
        _mintLong(_buyer, _derivativeHash, _quantity);
    }

    /// @notice Burns position tokens
    /// @param _tokenOwner address Address of tokens owner
    /// @param _tokenId uint256 tokenId of positions to burn
    /// @param _quantity uint256 Quantity of positions to burn
    function burn(address _tokenOwner, uint256 _tokenId, uint256 _quantity) external onlyCore {
        _burn(_tokenOwner, _tokenId, _quantity);
    }

    /// @notice ERC721 interface compatible function for position token name retrieving
    /// @return Returns name of token
    function name() external view returns (string memory) {
        return "Opium Network Position Token";
    }

    /// @notice ERC721 interface compatible function for position token symbol retrieving
    /// @return Returns symbol of token
    function symbol() external view returns (string memory) {
        return "ONP";
    }

    /// VIEW FUNCTIONS

    /// @notice Checks whether _spender is approved to spend tokens on _owners behalf or owner itself
    /// @param _spender address Address of spender
    /// @param _owner address Address of owner
    /// @param _tokenId address tokenId of interest
    /// @return Returns whether _spender is approved to spend tokens
    function isApprovedOrOwner(
        address _spender,
        address _owner,
        uint256 _tokenId
    ) public view returns (bool) {
        return (
        _spender == _owner ||
        getApproved(_tokenId, _owner) == _spender ||
        isApprovedForAll(_owner, _spender) ||
        isOpiumSpender(_spender)
        );
    }

    /// @notice Checks whether _spender is Opium.TokenSpender
    /// @return Returns whether _spender is Opium.TokenSpender
    function isOpiumSpender(address _spender) public view returns (bool) {
        return _spender == registry.getTokenSpender();
    }
}

// File: contracts/Errors/OracleAggregatorErrors.sol

pragma solidity 0.5.16;

contract OracleAggregatorErrors {
    string constant internal ERROR_ORACLE_AGGREGATOR_NOT_ENOUGH_ETHER = "ORACLE_AGGREGATOR:NOT_ENOUGH_ETHER";

    string constant internal ERROR_ORACLE_AGGREGATOR_QUERY_WAS_ALREADY_MADE = "ORACLE_AGGREGATOR:QUERY_WAS_ALREADY_MADE";

    string constant internal ERROR_ORACLE_AGGREGATOR_DATA_DOESNT_EXIST = "ORACLE_AGGREGATOR:DATA_DOESNT_EXIST";

    string constant internal ERROR_ORACLE_AGGREGATOR_DATA_ALREADY_EXIST = "ORACLE_AGGREGATOR:DATA_ALREADY_EXIST";
}

// File: contracts/Interface/IOracleId.sol

pragma solidity 0.5.16;

/// @title Opium.Interface.IOracleId contract is an interface that every oracleId should implement
interface IOracleId {
    /// @notice Requests data from `oracleId` one time
    /// @param timestamp uint256 Timestamp at which data are needed
    function fetchData(uint256 timestamp) external payable;

    /// @notice Requests data from `oracleId` multiple times
    /// @param timestamp uint256 Timestamp at which data are needed for the first time
    /// @param period uint256 Period in seconds between multiple timestamps
    /// @param times uint256 How many timestamps are requested
    function recursivelyFetchData(uint256 timestamp, uint256 period, uint256 times) external payable;

    /// @notice Requests and returns price in ETH for one request. This function could be called as `view` function. Oraclize API for price calculations restricts making this function as view.
    /// @return fetchPrice uint256 Price of one data request in ETH
    function calculateFetchPrice() external returns (uint256 fetchPrice);

    // Event with oracleId metadata JSON string (for DIB.ONE derivative explorer)
    event MetadataSet(string metadata);
}

// File: contracts/OracleAggregator.sol

pragma solidity 0.5.16;





/// @title Opium.OracleAggregator contract requests and caches the data from `oracleId`s and provides them to the Core for positions execution
contract OracleAggregator is OracleAggregatorErrors, ReentrancyGuard {
    using SafeMath for uint256;

    // Storage for the `oracleId` results
    // dataCache[oracleId][timestamp] => data
    mapping (address => mapping(uint256 => uint256)) public dataCache;

    // Flags whether data were provided
    // dataExist[oracleId][timestamp] => bool
    mapping (address => mapping(uint256 => bool)) public dataExist;

    // Flags whether data were requested
    // dataRequested[oracleId][timestamp] => bool
    mapping (address => mapping(uint256 => bool)) public dataRequested;

    // MODIFIERS

    /// @notice Checks whether enough ETH were provided withing data request to proceed
    /// @param oracleId address Address of the `oracleId` smart contract
    /// @param times uint256 How many times the `oracleId` is being requested
    modifier enoughEtherProvided(address oracleId, uint256 times) {
        // Calling Opium.IOracleId function to get the data fetch price per one request
        uint256 oneTimePrice = calculateFetchPrice(oracleId);

        // Checking if enough ether was provided for `times` amount of requests
        require(msg.value >= oneTimePrice.mul(times), ERROR_ORACLE_AGGREGATOR_NOT_ENOUGH_ETHER);
        _;
    }

    // PUBLIC FUNCTIONS

    /// @notice Requests data from `oracleId` one time
    /// @param oracleId address Address of the `oracleId` smart contract
    /// @param timestamp uint256 Timestamp at which data are needed
    function fetchData(address oracleId, uint256 timestamp) public payable nonReentrant enoughEtherProvided(oracleId, 1) {
        // Check if was not requested before and mark as requested
        _registerQuery(oracleId, timestamp);

        // Call the `oracleId` contract and transfer ETH
        IOracleId(oracleId).fetchData.value(msg.value)(timestamp);
    }

    /// @notice Requests data from `oracleId` multiple times
    /// @param oracleId address Address of the `oracleId` smart contract
    /// @param timestamp uint256 Timestamp at which data are needed for the first time
    /// @param period uint256 Period in seconds between multiple timestamps
    /// @param times uint256 How many timestamps are requested
    function recursivelyFetchData(address oracleId, uint256 timestamp, uint256 period, uint256 times) public payable nonReentrant enoughEtherProvided(oracleId, times) {
        // Check if was not requested before and mark as requested in loop for each timestamp
        for (uint256 i = 0; i < times; i++) {
            _registerQuery(oracleId, timestamp + period * i);
        }

        // Call the `oracleId` contract and transfer ETH
        IOracleId(oracleId).recursivelyFetchData.value(msg.value)(timestamp, period, times);
    }

    /// @notice Receives and caches data from `msg.sender`
    /// @param timestamp uint256 Timestamp of data
    /// @param data uint256 Data itself
    function __callback(uint256 timestamp, uint256 data) public {
        // Don't allow to push data twice
        require(!dataExist[msg.sender][timestamp], ERROR_ORACLE_AGGREGATOR_DATA_ALREADY_EXIST);

        // Saving data
        dataCache[msg.sender][timestamp] = data;

        // Flagging that data were received
        dataExist[msg.sender][timestamp] = true;
    }

    /// @notice Requests and returns price in ETH for one request. This function could be called as `view` function. Oraclize API for price calculations restricts making this function as view.
    /// @param oracleId address Address of the `oracleId` smart contract
    /// @return fetchPrice uint256 Price of one data request in ETH
    function calculateFetchPrice(address oracleId) public returns(uint256 fetchPrice) {
        fetchPrice = IOracleId(oracleId).calculateFetchPrice();
    }

    // PRIVATE FUNCTIONS

    /// @notice Checks if data was not requested and provided before and marks as requested
    /// @param oracleId address Address of the `oracleId` smart contract
    /// @param timestamp uint256 Timestamp at which data are requested
    function _registerQuery(address oracleId, uint256 timestamp) private {
        // Check if data was not requested and provided yet
        require(!dataRequested[oracleId][timestamp] && !dataExist[oracleId][timestamp], ERROR_ORACLE_AGGREGATOR_QUERY_WAS_ALREADY_MADE);

        // Mark as requested
        dataRequested[oracleId][timestamp] = true;
    }

    // VIEW FUNCTIONS

    /// @notice Returns cached data if they exist, or reverts with an error
    /// @param oracleId address Address of the `oracleId` smart contract
    /// @param timestamp uint256 Timestamp at which data were requested
    /// @return dataResult uint256 Cached data provided by `oracleId`
    function getData(address oracleId, uint256 timestamp) public view returns(uint256 dataResult) {
        // Check if Opium.OracleAggregator has data
        require(hasData(oracleId, timestamp), ERROR_ORACLE_AGGREGATOR_DATA_DOESNT_EXIST);

        // Return cached data
        dataResult = dataCache[oracleId][timestamp];
    }

    /// @notice Getter for dataExist mapping
    /// @param oracleId address Address of the `oracleId` smart contract
    /// @param timestamp uint256 Timestamp at which data were requested
    /// @param result bool Returns whether data were provided already
    function hasData(address oracleId, uint256 timestamp) public view returns(bool result) {
        return dataExist[oracleId][timestamp];
    }
}

// File: contracts/Errors/SyntheticAggregatorErrors.sol

pragma solidity 0.5.16;

contract SyntheticAggregatorErrors {
    string constant internal ERROR_SYNTHETIC_AGGREGATOR_DERIVATIVE_HASH_NOT_MATCH = "SYNTHETIC_AGGREGATOR:DERIVATIVE_HASH_NOT_MATCH";
    string constant internal ERROR_SYNTHETIC_AGGREGATOR_WRONG_MARGIN = "SYNTHETIC_AGGREGATOR:WRONG_MARGIN";
    string constant internal ERROR_SYNTHETIC_AGGREGATOR_COMMISSION_TOO_BIG = "SYNTHETIC_AGGREGATOR:COMMISSION_TOO_BIG";
}

// File: contracts/SyntheticAggregator.sol

pragma solidity 0.5.16;







/// @notice Opium.SyntheticAggregator contract initialized, identifies and caches syntheticId sensitive data
contract SyntheticAggregator is SyntheticAggregatorErrors, LibDerivative, LibCommission, ReentrancyGuard {
    // Emitted when new ticker is initialized
    event Create(Derivative derivative, bytes32 derivativeHash);

    // Enum for types of syntheticId
    // Invalid - syntheticId is not initialized yet
    // NotPool - syntheticId with p2p logic
    // Pool - syntheticId with pooled logic
    enum SyntheticTypes { Invalid, NotPool, Pool }

    // Cache of buyer margin by ticker
    // buyerMarginByHash[derivativeHash] = buyerMargin
    mapping (bytes32 => uint256) public buyerMarginByHash;

    // Cache of seller margin by ticker
    // sellerMarginByHash[derivativeHash] = sellerMargin
    mapping (bytes32 => uint256) public sellerMarginByHash;

    // Cache of type by ticker
    // typeByHash[derivativeHash] = type
    mapping (bytes32 => SyntheticTypes) public typeByHash;

    // Cache of commission by ticker
    // commissionByHash[derivativeHash] = commission
    mapping (bytes32 => uint256) public commissionByHash;

    // Cache of author addresses by ticker
    // authorAddressByHash[derivativeHash] = authorAddress
    mapping (bytes32 => address) public authorAddressByHash;

    // PUBLIC FUNCTIONS

    /// @notice Initializes ticker, if was not initialized and returns `syntheticId` author commission from cache
    /// @param _derivativeHash bytes32 Hash of derivative
    /// @param _derivative Derivative Derivative itself
    /// @return commission uint256 Synthetic author commission
    function getAuthorCommission(bytes32 _derivativeHash, Derivative memory _derivative) public nonReentrant returns (uint256 commission) {
        // Initialize derivative if wasn't initialized before
        _initDerivative(_derivativeHash, _derivative);
        commission = commissionByHash[_derivativeHash];
    }

    /// @notice Initializes ticker, if was not initialized and returns `syntheticId` author address from cache
    /// @param _derivativeHash bytes32 Hash of derivative
    /// @param _derivative Derivative Derivative itself
    /// @return authorAddress address Synthetic author address
    function getAuthorAddress(bytes32 _derivativeHash, Derivative memory _derivative) public nonReentrant returns (address authorAddress) {
        // Initialize derivative if wasn't initialized before
        _initDerivative(_derivativeHash, _derivative);
        authorAddress = authorAddressByHash[_derivativeHash];
    }

    /// @notice Initializes ticker, if was not initialized and returns buyer and seller margin from cache
    /// @param _derivativeHash bytes32 Hash of derivative
    /// @param _derivative Derivative Derivative itself
    /// @return buyerMargin uint256 Margin of buyer
    /// @return sellerMargin uint256 Margin of seller
    function getMargin(bytes32 _derivativeHash, Derivative memory _derivative) public nonReentrant returns (uint256 buyerMargin, uint256 sellerMargin) {
        // If it's a pool, just return margin from syntheticId contract
        if (_isPool(_derivativeHash, _derivative)) {
            return IDerivativeLogic(_derivative.syntheticId).getMargin(_derivative);
        }

        // Initialize derivative if wasn't initialized before
        _initDerivative(_derivativeHash, _derivative);

        // Check if margins for _derivativeHash were already cached
        buyerMargin = buyerMarginByHash[_derivativeHash];
        sellerMargin = sellerMarginByHash[_derivativeHash];
    }

    /// @notice Checks whether `syntheticId` implements pooled logic
    /// @param _derivativeHash bytes32 Hash of derivative
    /// @param _derivative Derivative Derivative itself
    /// @return result bool Returns whether synthetic implements pooled logic
    function isPool(bytes32 _derivativeHash, Derivative memory _derivative) public nonReentrant returns (bool result) {
        result = _isPool(_derivativeHash, _derivative);
    }

    // PRIVATE FUNCTIONS

    /// @notice Initializes ticker, if was not initialized and returns whether `syntheticId` implements pooled logic
    /// @param _derivativeHash bytes32 Hash of derivative
    /// @param _derivative Derivative Derivative itself
    /// @return result bool Returns whether synthetic implements pooled logic
    function _isPool(bytes32 _derivativeHash, Derivative memory _derivative) private returns (bool result) {
        // Initialize derivative if wasn't initialized before
        _initDerivative(_derivativeHash, _derivative);
        result = typeByHash[_derivativeHash] == SyntheticTypes.Pool;
    }

    /// @notice Initializes ticker: caches syntheticId type, margin, author address and commission
    /// @param _derivativeHash bytes32 Hash of derivative
    /// @param _derivative Derivative Derivative itself
    function _initDerivative(bytes32 _derivativeHash, Derivative memory _derivative) private {
        // Check if type for _derivativeHash was already cached
        SyntheticTypes syntheticType = typeByHash[_derivativeHash];

        // Type could not be Invalid, thus this condition says us that type was not cached before
        if (syntheticType != SyntheticTypes.Invalid) {
            return;
        }

        // For security reasons we calculate hash of provided _derivative
        bytes32 derivativeHash = getDerivativeHash(_derivative);
        require(derivativeHash == _derivativeHash, ERROR_SYNTHETIC_AGGREGATOR_DERIVATIVE_HASH_NOT_MATCH);

        // POOL
        // Get isPool from SyntheticId
        bool result = IDerivativeLogic(_derivative.syntheticId).isPool();
        // Cache type returned from synthetic
        typeByHash[derivativeHash] = result ? SyntheticTypes.Pool : SyntheticTypes.NotPool;

        // MARGIN
        // Get margin from SyntheticId
        (uint256 buyerMargin, uint256 sellerMargin) = IDerivativeLogic(_derivative.syntheticId).getMargin(_derivative);
        // We are not allowing both margins to be equal to 0
        require(buyerMargin != 0 || sellerMargin != 0, ERROR_SYNTHETIC_AGGREGATOR_WRONG_MARGIN);
        // Cache margins returned from synthetic
        buyerMarginByHash[derivativeHash] = buyerMargin;
        sellerMarginByHash[derivativeHash] = sellerMargin;

        // AUTHOR ADDRESS
        // Cache author address returned from synthetic
        authorAddressByHash[derivativeHash] = IDerivativeLogic(_derivative.syntheticId).getAuthorAddress();

        // AUTHOR COMMISSION
        // Get commission from syntheticId
        uint256 commission = IDerivativeLogic(_derivative.syntheticId).getAuthorCommission();
        // Check if commission is not set > 100%
        require(commission <= COMMISSION_BASE, ERROR_SYNTHETIC_AGGREGATOR_COMMISSION_TOO_BIG);
        // Cache commission
        commissionByHash[derivativeHash] = commission;

        // If we are here, this basically means this ticker was not used before, so we emit an event for Dapps developers about new ticker (derivative) and it's hash
        emit Create(_derivative, derivativeHash);
    }
}

// File: contracts/Lib/Whitelisted.sol

pragma solidity 0.5.16;

/// @title Opium.Lib.Whitelisted contract implements whitelist with modifier to restrict access to only whitelisted addresses
contract Whitelisted {
    // Whitelist array
    address[] internal whitelist;

    /// @notice This modifier restricts access to functions, which could be called only by whitelisted addresses
    modifier onlyWhitelisted() {
        // Allowance flag
        bool allowed = false;

        // Going through whitelisted addresses array
        uint256 whitelistLength = whitelist.length;
        for (uint256 i = 0; i < whitelistLength; i++) {
            // If `msg.sender` is met within whitelisted addresses, raise the flag and exit the loop
            if (whitelist[i] == msg.sender) {
                allowed = true;
                break;
            }
        }

        // Check if flag was raised
        require(allowed, "Only whitelisted allowed");
        _;
    }

    /// @notice Getter for whitelisted addresses array
    /// @return Array of whitelisted addresses
    function getWhitelist() public view returns (address[] memory) {
        return whitelist;
    }
}

// File: contracts/Lib/WhitelistedWithGovernance.sol

pragma solidity 0.5.16;


/// @title Opium.Lib.WhitelistedWithGovernance contract implements Opium.Lib.Whitelisted and adds governance for whitelist controlling
contract WhitelistedWithGovernance is Whitelisted {
    // Emitted when new governor is set
    event GovernorSet(address governor);

    // Emitted when new whitelist is proposed
    event Proposed(address[] whitelist);
    // Emitted when proposed whitelist is committed (set)
    event Committed(address[] whitelist);

    // Proposal life timelock interval
    uint256 public timeLockInterval;

    // Governor address
    address public governor;

    // Timestamp of last proposal
    uint256 public proposalTime;

    // Proposed whitelist
    address[] public proposedWhitelist;

    /// @notice This modifier restricts access to functions, which could be called only by governor
    modifier onlyGovernor() {
        require(msg.sender == governor, "Only governor allowed");
        _;
    }

    /// @notice Contract constructor
    /// @param _timeLockInterval uint256 Initial value for timelock interval
    /// @param _governor address Initial value for governor
    constructor(uint256 _timeLockInterval, address _governor) public {
        timeLockInterval = _timeLockInterval;
        governor = _governor;
        emit GovernorSet(governor);
    }

    /// @notice Calling this function governor could propose new whitelist addresses array. Also it allows to initialize first whitelist if it was not initialized yet.
    function proposeWhitelist(address[] memory _whitelist) public onlyGovernor {
        // Restrict empty proposals
        require(_whitelist.length != 0, "Can't be empty");

        // Consider empty whitelist as not initialized, as proposing of empty whitelists is not allowed
        // If whitelist has never been initialized, we set whitelist right away without proposal
        if (whitelist.length == 0) {
            whitelist = _whitelist;
            emit Committed(_whitelist);

        // Otherwise save current time as timestamp of proposal, save proposed whitelist and emit event
        } else {
            proposalTime = now;
            proposedWhitelist = _whitelist;
            emit Proposed(_whitelist);
        }
    }

    /// @notice Calling this function governor commits proposed whitelist if timelock interval of proposal was passed
    function commitWhitelist() public onlyGovernor {
        // Check if proposal was made
        require(proposalTime != 0, "Didn't proposed yet");

        // Check if timelock interval was passed
        require((proposalTime + timeLockInterval) < now, "Can't commit yet");

        // Set new whitelist and emit event
        whitelist = proposedWhitelist;
        emit Committed(whitelist);

        // Reset proposal time lock
        proposalTime = 0;
    }

    /// @notice This function allows governor to transfer governance to a new governor and emits event
    /// @param _governor address Address of new governor
    function setGovernor(address _governor) public onlyGovernor {
        require(_governor != address(0), "Can't set zero address");
        governor = _governor;
        emit GovernorSet(governor);
    }
}

// File: contracts/Lib/WhitelistedWithGovernanceAndChangableTimelock.sol

pragma solidity 0.5.16;


/// @notice Opium.Lib.WhitelistedWithGovernanceAndChangableTimelock contract implements Opium.Lib.WhitelistedWithGovernance and adds possibility for governor to change timelock interval within timelock interval
contract WhitelistedWithGovernanceAndChangableTimelock is WhitelistedWithGovernance {
    // Emitted when new timelock is proposed
    event Proposed(uint256 timelock);
    // Emitted when new timelock is committed (set)
    event Committed(uint256 timelock);

    // Timestamp of last timelock proposal
    uint256 public timeLockProposalTime;
    // Proposed timelock
    uint256 public proposedTimeLock;

    /// @notice Calling this function governor could propose new timelock
    /// @param _timelock uint256 New timelock value
    function proposeTimelock(uint256 _timelock) public onlyGovernor {
        timeLockProposalTime = now;
        proposedTimeLock = _timelock;
        emit Proposed(_timelock);
    }

    /// @notice Calling this function governor could commit previously proposed new timelock if timelock interval of proposal was passed
    function commitTimelock() public onlyGovernor {
        // Check if proposal was made
        require(timeLockProposalTime != 0, "Didn't proposed yet");
        // Check if timelock interval was passed
        require((timeLockProposalTime + timeLockInterval) < now, "Can't commit yet");

        // Set new timelock and emit event
        timeLockInterval = proposedTimeLock;
        emit Committed(proposedTimeLock);

        // Reset timelock time lock
        timeLockProposalTime = 0;
    }
}

// File: contracts/TokenSpender.sol

pragma solidity 0.5.16;





/// @title Opium.TokenSpender contract holds users ERC20 approvals and allows whitelisted contracts to use tokens
contract TokenSpender is WhitelistedWithGovernanceAndChangableTimelock {
    using SafeERC20 for IERC20;

    // Initial timelock period
    uint256 public constant WHITELIST_TIMELOCK = 1 hours;

    /// @notice Calls constructors of super-contracts
    /// @param _governor address Address of governor, who is allowed to adjust whitelist
    constructor(address _governor) public WhitelistedWithGovernance(WHITELIST_TIMELOCK, _governor) {}

    /// @notice Using this function whitelisted contracts could call ERC20 transfers
    /// @param token IERC20 Instance of token
    /// @param from address Address from which tokens are transferred
    /// @param to address Address of tokens receiver
    /// @param amount uint256 Amount of tokens to be transferred
    function claimTokens(IERC20 token, address from, address to, uint256 amount) external onlyWhitelisted {
        token.safeTransferFrom(from, to, amount);
    }

    /// @notice Using this function whitelisted contracts could call ERC721O transfers
    /// @param token IERC721O Instance of token
    /// @param from address Address from which tokens are transferred
    /// @param to address Address of tokens receiver
    /// @param tokenId uint256 Token ID to be transferred
    /// @param amount uint256 Amount of tokens to be transferred
    function claimPositions(IERC721O token, address from, address to, uint256 tokenId, uint256 amount) external onlyWhitelisted {
        token.safeTransferFrom(from, to, tokenId, amount);
    }
}

// File: contracts/Core.sol

pragma solidity 0.5.16;
















/// @title Opium.Core contract creates positions, holds and distributes margin at the maturity
contract Core is LibDerivative, LibCommission, UsingRegistry, CoreErrors, ReentrancyGuard {
    using SafeMath for uint256;
    using LibPosition for bytes32;
    using SafeERC20 for IERC20;

    // Emitted when Core creates new position
    event Created(address buyer, address seller, bytes32 derivativeHash, uint256 quantity);
    // Emitted when Core executes positions
    event Executed(address tokenOwner, uint256 tokenId, uint256 quantity);
    // Emitted when Core cancels ticker for the first time
    event Canceled(bytes32 derivativeHash);

    // Period of time after which ticker could be canceled if no data was provided to the `oracleId`
    uint256 public constant NO_DATA_CANCELLATION_PERIOD = 2 weeks;

    // Vaults for pools
    // This mapping holds balances of pooled positions
    // poolVaults[syntheticAddress][tokenAddress] => availableBalance
    mapping (address => mapping(address => uint256)) public poolVaults;

    // Vaults for fees
    // This mapping holds balances of fee recipients
    // feesVaults[feeRecipientAddress][tokenAddress] => availableBalance
    mapping (address => mapping(address => uint256)) public feesVaults;

    // Hashes of cancelled tickers
    mapping (bytes32 => bool) public cancelled;

    /// @notice Calls Core.Lib.UsingRegistry constructor
    constructor(address _registry) public UsingRegistry(_registry) {}

    // PUBLIC FUNCTIONS

    /// @notice This function allows fee recipients to withdraw their fees
    /// @param _tokenAddress address Address of an ERC20 token to withdraw
    function withdrawFee(address _tokenAddress) public nonReentrant {
        uint256 balance = feesVaults[msg.sender][_tokenAddress];
        feesVaults[msg.sender][_tokenAddress] = 0;
        IERC20(_tokenAddress).safeTransfer(msg.sender, balance);
    }

    /// @notice Creates derivative contracts (positions)
    /// @param _derivative Derivative Derivative definition
    /// @param _quantity uint256 Quantity of derivatives to be created
    /// @param _addresses address[2] Addresses of buyer and seller
    /// [0] - buyer address
    /// [1] - seller address - if seller is set to `address(0)`, consider as pooled position
    function create(Derivative memory _derivative, uint256 _quantity, address[2] memory _addresses) public nonReentrant {
        if (_addresses[1] == address(0)) {
            _createPooled(_derivative, _quantity, _addresses[0]);
        } else {
            _create(_derivative, _quantity, _addresses);
        }
    }

    /// @notice Executes several positions of `msg.sender` with same `tokenId`
    /// @param _tokenId uint256 `tokenId` of positions that needs to be executed
    /// @param _quantity uint256 Quantity of positions to execute
    /// @param _derivative Derivative Derivative definition
    function execute(uint256 _tokenId, uint256 _quantity, Derivative memory _derivative) public nonReentrant {
        uint256[] memory tokenIds = new uint256[](1);
        uint256[] memory quantities = new uint256[](1);
        Derivative[] memory derivatives = new Derivative[](1);

        tokenIds[0] = _tokenId;
        quantities[0] = _quantity;
        derivatives[0] = _derivative;

        _execute(msg.sender, tokenIds, quantities, derivatives);
    }

    /// @notice Executes several positions of `_tokenOwner` with same `tokenId`
    /// @param _tokenOwner address Address of the owner of positions
    /// @param _tokenId uint256 `tokenId` of positions that needs to be executed
    /// @param _quantity uint256 Quantity of positions to execute
    /// @param _derivative Derivative Derivative definition
    function execute(address _tokenOwner, uint256 _tokenId, uint256 _quantity, Derivative memory _derivative) public nonReentrant {
        uint256[] memory tokenIds = new uint256[](1);
        uint256[] memory quantities = new uint256[](1);
        Derivative[] memory derivatives = new Derivative[](1);

        tokenIds[0] = _tokenId;
        quantities[0] = _quantity;
        derivatives[0] = _derivative;

        _execute(_tokenOwner, tokenIds, quantities, derivatives);
    }

    /// @notice Executes several positions of `msg.sender` with different `tokenId`s
    /// @param _tokenIds uint256[] `tokenId`s of positions that needs to be executed
    /// @param _quantities uint256[] Quantity of positions to execute for each `tokenId`
    /// @param _derivatives Derivative[] Derivative definitions for each `tokenId`
    function execute(uint256[] memory _tokenIds, uint256[] memory _quantities, Derivative[] memory _derivatives) public nonReentrant {
        _execute(msg.sender, _tokenIds, _quantities, _derivatives);
    }

    /// @notice Executes several positions of `_tokenOwner` with different `tokenId`s
    /// @param _tokenOwner address Address of the owner of positions
    /// @param _tokenIds uint256[] `tokenId`s of positions that needs to be executed
    /// @param _quantities uint256[] Quantity of positions to execute for each `tokenId`
    /// @param _derivatives Derivative[] Derivative definitions for each `tokenId`
    function execute(address _tokenOwner, uint256[] memory _tokenIds, uint256[] memory _quantities, Derivative[] memory _derivatives) public nonReentrant {
        _execute(_tokenOwner, _tokenIds, _quantities, _derivatives);
    }

    /// @notice Cancels tickers, burns positions and returns margins to positions owners in case no data were provided within `NO_DATA_CANCELLATION_PERIOD`
    /// @param _tokenId uint256 `tokenId` of positions that needs to be canceled
    /// @param _quantity uint256 Quantity of positions to cancel
    /// @param _derivative Derivative Derivative definition
    function cancel(uint256 _tokenId, uint256 _quantity, Derivative memory _derivative) public nonReentrant {
        uint256[] memory tokenIds = new uint256[](1);
        uint256[] memory quantities = new uint256[](1);
        Derivative[] memory derivatives = new Derivative[](1);

        tokenIds[0] = _tokenId;
        quantities[0] = _quantity;
        derivatives[0] = _derivative;

        _cancel(tokenIds, quantities, derivatives);
    }

    /// @notice Cancels tickers, burns positions and returns margins to positions owners in case no data were provided within `NO_DATA_CANCELLATION_PERIOD`
    /// @param _tokenIds uint256[] `tokenId` of positions that needs to be canceled
    /// @param _quantities uint256[] Quantity of positions to cancel for each `tokenId`
    /// @param _derivatives Derivative[] Derivative definitions for each `tokenId`
    function cancel(uint256[] memory _tokenIds, uint256[] memory _quantities, Derivative[] memory _derivatives) public nonReentrant {
        _cancel(_tokenIds, _quantities, _derivatives);
    }

    // PRIVATE FUNCTIONS

    struct CreatePooledLocalVars {
        SyntheticAggregator syntheticAggregator;
        IDerivativeLogic derivativeLogic;
        IERC20 marginToken;
        TokenSpender tokenSpender;
        TokenMinter tokenMinter;
    }

    /// @notice This function creates pooled positions
    /// @param _derivative Derivative Derivative definition
    /// @param _quantity uint256 Quantity of positions to create
    /// @param _address address Address of position receiver
    function _createPooled(Derivative memory _derivative, uint256 _quantity, address _address) private {
        // Local variables
        CreatePooledLocalVars memory vars;

        // Create instance of Opium.SyntheticAggregator
        // Create instance of Opium.IDerivativeLogic
        // Create instance of margin token
        // Create instance of Opium.TokenSpender
        // Create instance of Opium.TokenMinter
        vars.syntheticAggregator = SyntheticAggregator(registry.getSyntheticAggregator());
        vars.derivativeLogic = IDerivativeLogic(_derivative.syntheticId);
        vars.marginToken = IERC20(_derivative.token);
        vars.tokenSpender = TokenSpender(registry.getTokenSpender());
        vars.tokenMinter = TokenMinter(registry.getMinter());

        // Generate hash for derivative
        bytes32 derivativeHash = getDerivativeHash(_derivative);

        // Check with Opium.SyntheticAggregator if syntheticId is a pool
        require(vars.syntheticAggregator.isPool(derivativeHash, _derivative), ERROR_CORE_NOT_POOL);

        // Check if ticker was canceled
        require(!cancelled[derivativeHash], ERROR_CORE_TICKER_WAS_CANCELLED);

        // Validate input data against Derivative logic (`syntheticId`)
        require(vars.derivativeLogic.validateInput(_derivative), ERROR_CORE_SYNTHETIC_VALIDATION_ERROR);

        // Get cached margin required according to logic from Opium.SyntheticAggregator
        (uint256 margin, ) = vars.syntheticAggregator.getMargin(derivativeHash, _derivative);

        // Check ERC20 tokens allowance: margin * quantity
        // `msg.sender` must provide margin for position creation
        require(vars.marginToken.allowance(msg.sender, address(vars.tokenSpender)) >= margin.mul(_quantity), ERROR_CORE_NOT_ENOUGH_TOKEN_ALLOWANCE);

    	// Take ERC20 tokens from msg.sender, should never revert in correct ERC20 implementation
        vars.tokenSpender.claimTokens(vars.marginToken, msg.sender, address(this), margin.mul(_quantity));

        // Since it's a pooled position, we add transferred margin to pool balance
        poolVaults[_derivative.syntheticId][_derivative.token] = poolVaults[_derivative.syntheticId][_derivative.token].add(margin.mul(_quantity));

        // Mint LONG position tokens
        vars.tokenMinter.mint(_address, derivativeHash, _quantity);

        emit Created(_address, address(0), derivativeHash, _quantity);
    }

    struct CreateLocalVars {
        SyntheticAggregator syntheticAggregator;
        IDerivativeLogic derivativeLogic;
        IERC20 marginToken;
        TokenSpender tokenSpender;
        TokenMinter tokenMinter;
    }

    /// @notice This function creates p2p positions
    /// @param _derivative Derivative Derivative definition
    /// @param _quantity uint256 Quantity of positions to create
    /// @param _addresses address[2] Addresses of buyer and seller
    /// [0] - buyer address
    /// [1] - seller address
    function _create(Derivative memory _derivative, uint256 _quantity, address[2] memory _addresses) private {
        // Local variables
        CreateLocalVars memory vars;

        // Create instance of Opium.SyntheticAggregator
        // Create instance of Opium.IDerivativeLogic
        // Create instance of margin token
        // Create instance of Opium.TokenSpender
        // Create instance of Opium.TokenMinter
        vars.syntheticAggregator = SyntheticAggregator(registry.getSyntheticAggregator());
        vars.derivativeLogic = IDerivativeLogic(_derivative.syntheticId);
        vars.marginToken = IERC20(_derivative.token);
        vars.tokenSpender = TokenSpender(registry.getTokenSpender());
        vars.tokenMinter = TokenMinter(registry.getMinter());

        // Generate hash for derivative
        bytes32 derivativeHash = getDerivativeHash(_derivative);

        // Check with Opium.SyntheticAggregator if syntheticId is not a pool
        require(!vars.syntheticAggregator.isPool(derivativeHash, _derivative), ERROR_CORE_CANT_BE_POOL);

        // Check if ticker was canceled
        require(!cancelled[derivativeHash], ERROR_CORE_TICKER_WAS_CANCELLED);

        // Validate input data against Derivative logic (`syntheticId`)
        require(vars.derivativeLogic.validateInput(_derivative), ERROR_CORE_SYNTHETIC_VALIDATION_ERROR);

        uint256[2] memory margins;
        // Get cached margin required according to logic from Opium.SyntheticAggregator
        // margins[0] - buyerMargin
        // margins[1] - sellerMargin
        (margins[0], margins[1]) = vars.syntheticAggregator.getMargin(derivativeHash, _derivative);

        // Check ERC20 tokens allowance: (margins[0] + margins[1]) * quantity
        // `msg.sender` must provide margin for position creation
        require(vars.marginToken.allowance(msg.sender, address(vars.tokenSpender)) >= margins[0].add(margins[1]).mul(_quantity), ERROR_CORE_NOT_ENOUGH_TOKEN_ALLOWANCE);

    	// Take ERC20 tokens from msg.sender, should never revert in correct ERC20 implementation
        vars.tokenSpender.claimTokens(vars.marginToken, msg.sender, address(this), margins[0].add(margins[1]).mul(_quantity));

        // Mint LONG and SHORT positions tokens
        vars.tokenMinter.mint(_addresses[0], _addresses[1], derivativeHash, _quantity);

        emit Created(_addresses[0], _addresses[1], derivativeHash, _quantity);
    }

    struct ExecuteAndCancelLocalVars {
        TokenMinter tokenMinter;
        OracleAggregator oracleAggregator;
        SyntheticAggregator syntheticAggregator;
    }

    /// @notice Executes several positions of `_tokenOwner` with different `tokenId`s
    /// @param _tokenOwner address Address of the owner of positions
    /// @param _tokenIds uint256[] `tokenId`s of positions that needs to be executed
    /// @param _quantities uint256[] Quantity of positions to execute for each `tokenId`
    /// @param _derivatives Derivative[] Derivative definitions for each `tokenId`
    function _execute(address _tokenOwner, uint256[] memory _tokenIds, uint256[] memory _quantities, Derivative[] memory _derivatives) private {
        require(_tokenIds.length == _quantities.length, ERROR_CORE_TOKEN_IDS_AND_QUANTITIES_LENGTH_DOES_NOT_MATCH);
        require(_tokenIds.length == _derivatives.length, ERROR_CORE_TOKEN_IDS_AND_DERIVATIVES_LENGTH_DOES_NOT_MATCH);

        // Local variables
        ExecuteAndCancelLocalVars memory vars;

        // Create instance of Opium.TokenMinter
        // Create instance of Opium.OracleAggregator
        // Create instance of Opium.SyntheticAggregator
        vars.tokenMinter = TokenMinter(registry.getMinter());
        vars.oracleAggregator = OracleAggregator(registry.getOracleAggregator());
        vars.syntheticAggregator = SyntheticAggregator(registry.getSyntheticAggregator());

        for (uint256 i; i < _tokenIds.length; i++) {
            // Check if execution is performed after endTime
            require(now > _derivatives[i].endTime, ERROR_CORE_EXECUTION_BEFORE_MATURITY_NOT_ALLOWED);

            // Checking whether execution is performed by `_tokenOwner` or `_tokenOwner` allowed third party executions on it's behalf
            require(
                _tokenOwner == msg.sender ||
                IDerivativeLogic(_derivatives[i].syntheticId).thirdpartyExecutionAllowed(_tokenOwner),
                ERROR_CORE_SYNTHETIC_EXECUTION_WAS_NOT_ALLOWED
            );

            // Returns payout for all positions
            uint256 payout = _getPayout(_derivatives[i], _tokenIds[i], _quantities[i], vars);

            // Transfer payout
            if (payout > 0) {
                IERC20(_derivatives[i].token).safeTransfer(_tokenOwner, payout);
            }

            // Burn executed position tokens
            vars.tokenMinter.burn(_tokenOwner, _tokenIds[i], _quantities[i]);

            emit Executed(_tokenOwner, _tokenIds[i], _quantities[i]);
        }
    }

    /// @notice Cancels tickers, burns positions and returns margins to positions owners in case no data were provided within `NO_DATA_CANCELLATION_PERIOD`
    /// @param _tokenIds uint256[] `tokenId` of positions that needs to be canceled
    /// @param _quantities uint256[] Quantity of positions to cancel for each `tokenId`
    /// @param _derivatives Derivative[] Derivative definitions for each `tokenId`
    function _cancel(uint256[] memory _tokenIds, uint256[] memory _quantities, Derivative[] memory _derivatives) private {
        require(_tokenIds.length == _quantities.length, ERROR_CORE_TOKEN_IDS_AND_QUANTITIES_LENGTH_DOES_NOT_MATCH);
        require(_tokenIds.length == _derivatives.length, ERROR_CORE_TOKEN_IDS_AND_DERIVATIVES_LENGTH_DOES_NOT_MATCH);

        // Local variables
        ExecuteAndCancelLocalVars memory vars;

        // Create instance of Opium.TokenMinter
        // Create instance of Opium.OracleAggregator
        // Create instance of Opium.SyntheticAggregator
        vars.tokenMinter = TokenMinter(registry.getMinter());
        vars.oracleAggregator = OracleAggregator(registry.getOracleAggregator());
        vars.syntheticAggregator = SyntheticAggregator(registry.getSyntheticAggregator());

        for (uint256 i; i < _tokenIds.length; i++) {
            // Don't allow to cancel tickers with "dummy" oracleIds
            require(_derivatives[i].oracleId != address(0), ERROR_CORE_CANT_CANCEL_DUMMY_ORACLE_ID);

            // Check if cancellation is called after `NO_DATA_CANCELLATION_PERIOD` and `oracleId` didn't provided data
            require(
                _derivatives[i].endTime + NO_DATA_CANCELLATION_PERIOD <= now &&
                !vars.oracleAggregator.hasData(_derivatives[i].oracleId, _derivatives[i].endTime),
                ERROR_CORE_CANCELLATION_IS_NOT_ALLOWED
            );

            // Generate hash for derivative
            bytes32 derivativeHash = getDerivativeHash(_derivatives[i]);

            // Emit `Canceled` event only once and mark ticker as canceled
            if (!cancelled[derivativeHash]) {
                cancelled[derivativeHash] = true;
                emit Canceled(derivativeHash);
            }

            uint256[2] memory margins;
            // Get cached margin required according to logic from Opium.SyntheticAggregator
            // margins[0] - buyerMargin
            // margins[1] - sellerMargin
            (margins[0], margins[1]) = vars.syntheticAggregator.getMargin(derivativeHash, _derivatives[i]);

            uint256 payout;
            // Check if `_tokenId` is an ID of LONG position
            if (derivativeHash.getLongTokenId() == _tokenIds[i]) {
                // Set payout to buyerPayout
                payout = margins[0];

            // Check if `_tokenId` is an ID of SHORT position
            } else if (derivativeHash.getShortTokenId() == _tokenIds[i]) {
                // Set payout to sellerPayout
                payout = margins[1];
            } else {
                // Either portfolioId, hack or bug
                revert(ERROR_CORE_UNKNOWN_POSITION_TYPE);
            }

            // Transfer payout * _quantities[i]
            if (payout > 0) {
                IERC20(_derivatives[i].token).safeTransfer(msg.sender, payout.mul(_quantities[i]));
            }

            // Burn canceled position tokens
            vars.tokenMinter.burn(msg.sender, _tokenIds[i], _quantities[i]);
        }
    }

    /// @notice Calculates payout for position and gets fees
    /// @param _derivative Derivative Derivative definition
    /// @param _tokenId uint256 `tokenId` of positions
    /// @param _quantity uint256 Quantity of positions
    /// @param _vars ExecuteAndCancelLocalVars Helping local variables
    /// @return payout uint256 Payout for all tokens
    function _getPayout(Derivative memory _derivative, uint256 _tokenId, uint256 _quantity, ExecuteAndCancelLocalVars memory _vars) private returns (uint256 payout) {
        // Trying to getData from Opium.OracleAggregator, could be reverted
        // Opium allows to use "dummy" oracleIds, in this case data is set to `0`
        uint256 data;
        if (_derivative.oracleId != address(0)) {
            data = _vars.oracleAggregator.getData(_derivative.oracleId, _derivative.endTime);
        } else {
            data = 0;
        }

        uint256[2] memory payoutRatio;
        // Get payout ratio from Derivative logic
        // payoutRatio[0] - buyerPayout
        // payoutRatio[1] - sellerPayout
        (payoutRatio[0], payoutRatio[1]) = IDerivativeLogic(_derivative.syntheticId).getExecutionPayout(_derivative, data);

        // Generate hash for derivative
        bytes32 derivativeHash = getDerivativeHash(_derivative);

        // Check if ticker was canceled
        require(!cancelled[derivativeHash], ERROR_CORE_TICKER_WAS_CANCELLED);

        uint256[2] memory margins;
        // Get cached total margin required from Opium.SyntheticAggregator
        // margins[0] - buyerMargin
        // margins[1] - sellerMargin
        (margins[0], margins[1]) = _vars.syntheticAggregator.getMargin(derivativeHash, _derivative);

        uint256[2] memory payouts;
        // Calculate payouts from ratio
        // payouts[0] -> buyerPayout = (buyerMargin + sellerMargin) * buyerPayoutRatio / (buyerPayoutRatio + sellerPayoutRatio)
        // payouts[1] -> sellerPayout = (buyerMargin + sellerMargin) * sellerPayoutRatio / (buyerPayoutRatio + sellerPayoutRatio)
        payouts[0] = margins[0].add(margins[1]).mul(payoutRatio[0]).div(payoutRatio[0].add(payoutRatio[1]));
        payouts[1] = margins[0].add(margins[1]).mul(payoutRatio[1]).div(payoutRatio[0].add(payoutRatio[1]));

        // Check if `_tokenId` is an ID of LONG position
        if (derivativeHash.getLongTokenId() == _tokenId) {
            // Check if it's a pooled position
            if (_vars.syntheticAggregator.isPool(derivativeHash, _derivative)) {
                // Pooled position payoutRatio is considered as full payout, not as payoutRatio
                payout = payoutRatio[0];

                // Multiply payout by quantity
                payout = payout.mul(_quantity);

                // Check sufficiency of syntheticId balance in poolVaults
                require(
                    poolVaults[_derivative.syntheticId][_derivative.token] >= payout
                    ,
                    ERROR_CORE_INSUFFICIENT_POOL_BALANCE
                );

                // Subtract paid out margin from poolVault
                poolVaults[_derivative.syntheticId][_derivative.token] = poolVaults[_derivative.syntheticId][_derivative.token].sub(payout);
            } else {
                // Set payout to buyerPayout
                payout = payouts[0];

                // Multiply payout by quantity
                payout = payout.mul(_quantity);
            }

            // Take fees only from profit makers
            // Check: payout > buyerMargin * quantity
            if (payout > margins[0].mul(_quantity)) {
                // Get Opium and `syntheticId` author fees and subtract it from payout
                payout = payout.sub(_getFees(_vars.syntheticAggregator, derivativeHash, _derivative, payout - margins[0].mul(_quantity)));
            }

        // Check if `_tokenId` is an ID of SHORT position
        } else if (derivativeHash.getShortTokenId() == _tokenId) {
            // Set payout to sellerPayout
            payout = payouts[1];

            // Multiply payout by quantity
            payout = payout.mul(_quantity);

            // Take fees only from profit makers
            // Check: payout > sellerMargin * quantity
            if (payout > margins[1].mul(_quantity)) {
                // Get Opium fees and subtract it from payout
                payout = payout.sub(_getFees(_vars.syntheticAggregator, derivativeHash, _derivative, payout - margins[1].mul(_quantity)));
            }
        } else {
            // Either portfolioId, hack or bug
            revert(ERROR_CORE_UNKNOWN_POSITION_TYPE);
        }
    }

    /// @notice Calculates `syntheticId` author and opium fees from profit makers
    /// @param _syntheticAggregator SyntheticAggregator Instance of Opium.SyntheticAggregator
    /// @param _derivativeHash bytes32 Derivative hash
    /// @param _derivative Derivative Derivative definition
    /// @param _profit uint256 payout of one position
    /// @return fee uint256 Opium and `syntheticId` author fee
    function _getFees(SyntheticAggregator _syntheticAggregator, bytes32 _derivativeHash, Derivative memory _derivative, uint256 _profit) private returns (uint256 fee) {
        // Get cached `syntheticId` author address from Opium.SyntheticAggregator
        address authorAddress = _syntheticAggregator.getAuthorAddress(_derivativeHash, _derivative);
        // Get cached `syntheticId` fee percentage from Opium.SyntheticAggregator
        uint256 commission = _syntheticAggregator.getAuthorCommission(_derivativeHash, _derivative);

        // Calculate fee
        // fee = profit * commission / COMMISSION_BASE
        fee = _profit.mul(commission).div(COMMISSION_BASE);

        // If commission is zero, finish
        if (fee == 0) {
            return 0;
        }

        // Calculate opium fee
        // opiumFee = fee * OPIUM_COMMISSION_PART / OPIUM_COMMISSION_BASE
        uint256 opiumFee = fee.mul(OPIUM_COMMISSION_PART).div(OPIUM_COMMISSION_BASE);

        // Calculate author fee
        // authorFee = fee - opiumFee
        uint256 authorFee = fee.sub(opiumFee);

        // Get opium address
        address opiumAddress = registry.getOpiumAddress();

        // Update feeVault for Opium team
        // feesVault[opium][token] += opiumFee
        feesVaults[opiumAddress][_derivative.token] = feesVaults[opiumAddress][_derivative.token].add(opiumFee);

        // Update feeVault for `syntheticId` author
        // feeVault[author][token] += authorFee
        feesVaults[authorAddress][_derivative.token] = feesVaults[authorAddress][_derivative.token].add(authorFee);
    }
}

// File: contracts/Errors/MatchingErrors.sol

pragma solidity 0.5.16;

contract MatchingErrors {
    string constant internal ERROR_MATCH_CANCELLATION_NOT_ALLOWED = "MATCH:CANCELLATION_NOT_ALLOWED";
    string constant internal ERROR_MATCH_ALREADY_CANCELED = "MATCH:ALREADY_CANCELED";
    string constant internal ERROR_MATCH_ORDER_WAS_CANCELED = "MATCH:ORDER_WAS_CANCELED";

    string constant internal ERROR_MATCH_TAKER_ADDRESS_WRONG = "MATCH:TAKER_ADDRESS_WRONG";
    string constant internal ERROR_MATCH_ORDER_IS_EXPIRED = "MATCH:ORDER_IS_EXPIRED";
    string constant internal ERROR_MATCH_SENDER_ADDRESS_WRONG = "MATCH:SENDER_ADDRESS_WRONG";
    string constant internal ERROR_MATCH_SIGNATURE_NOT_VERIFIED = "MATCH:SIGNATURE_NOT_VERIFIED";
    string constant internal ERROR_MATCH_NOT_ENOUGH_ALLOWED_FEES = "MATCH:NOT_ENOUGH_ALLOWED_FEES";
}

// File: contracts/Lib/LibEIP712.sol

pragma solidity 0.5.16;

/// @title Opium.Lib.LibEIP712 contract implements the domain of EIP712 for meta transactions
contract LibEIP712 {
    // EIP712Domain structure
    // name - protocol name
    // version - protocol version
    // verifyingContract - signed message verifying contract
    struct EIP712Domain {
        string  name;
        string  version;
        address verifyingContract;
    }

    // Calculate typehash of ERC712Domain
    bytes32 constant internal EIP712DOMAIN_TYPEHASH = keccak256(abi.encodePacked(
        "EIP712Domain(",
        "string name,",
        "string version,",
        "address verifyingContract",
        ")"
    ));

    // solhint-disable-next-line var-name-mixedcase
    bytes32 internal DOMAIN_SEPARATOR;

    // Calculate domain separator at creation
    constructor () public {
        DOMAIN_SEPARATOR = keccak256(abi.encode(
            EIP712DOMAIN_TYPEHASH,
            keccak256("Opium Network"),
            keccak256("1"),
            address(this)
        ));
    }

    /// @notice Hashes EIP712Message
    /// @param hashStruct bytes32 Hash of structured message
    /// @return result bytes32 Hash of EIP712Message
    function hashEIP712Message(bytes32 hashStruct) internal view returns (bytes32 result) {
        bytes32 domainSeparator = DOMAIN_SEPARATOR;

        assembly {
            // Load free memory pointer
            let memPtr := mload(64)

            mstore(memPtr, 0x1901000000000000000000000000000000000000000000000000000000000000)  // EIP191 header
            mstore(add(memPtr, 2), domainSeparator)                                            // EIP712 domain hash
            mstore(add(memPtr, 34), hashStruct)                                                 // Hash of struct

            // Compute hash
            result := keccak256(memPtr, 66)
        }
        return result;
    }
}

// File: contracts/Matching/SwaprateMatch/LibSwaprateOrder.sol

pragma solidity 0.5.16;


/// @title Opium.Matching.SwaprateMatch.LibSwaprateOrder contract implements EIP712 signed SwaprateOrder for Opium.Matching.SwaprateMatch
contract LibSwaprateOrder is LibEIP712 {
    /**
        Structure of order
        Description should be considered from the order signer (maker) perspective

        syntheticId - address of derivative syntheticId
        oracleId - address of derivative oracleId
        token - address of derivative margin token

        makerMarginAddress - address of token that maker is willing to pay with
        takerMarginAddress - address of token that maker is willing to receive

        makerAddress - address of maker
        takerAddress - address of counterparty (taker). If zero address, then taker could be anyone

        senderAddress - address which is allowed to settle the order on-chain. If zero address, then anyone could settle

        relayerAddress - address of the relayer fee recipient
        affiliateAddress - address of the affiliate fee recipient

        feeTokenAddress - address of token which is used for fees

        endTime - timestamp of derivative maturity

        quantity - quantity of positions maker wants to receive
        partialFill - whether maker allows partial fill of it's order

        param0...param9 - additional params to pass it to syntheticId

        relayerFee - amount of fee in feeToken that should be paid to relayer
        affiliateFee - amount of fee in feeToken that should be paid to affiliate

        nonce - unique order ID

        signature - Signature of EIP712 message. Not used in hash, but then set for order processing purposes

     */
    struct SwaprateOrder {
        address syntheticId;
        address oracleId;
        address token;

        address makerAddress;
        address takerAddress;

        address senderAddress;

        address relayerAddress;
        address affiliateAddress;

        address feeTokenAddress;

        uint256 endTime;

        uint256 quantity;
        uint256 partialFill;

        uint256 param0;
        uint256 param1;
        uint256 param2;
        uint256 param3;
        uint256 param4;
        uint256 param5;
        uint256 param6;
        uint256 param7;
        uint256 param8;
        uint256 param9;

        uint256 relayerFee;
        uint256 affiliateFee;

        uint256 nonce;

        // Not used in hash
        bytes signature;
    }

    // Calculate typehash of Order
    bytes32 constant internal EIP712_ORDER_TYPEHASH = keccak256(abi.encodePacked(
        "Order(",
        "address syntheticId,",
        "address oracleId,",
        "address token,",

        "address makerAddress,",
        "address takerAddress,",

        "address senderAddress,",

        "address relayerAddress,",
        "address affiliateAddress,",

        "address feeTokenAddress,",

        "uint256 endTime,",

        "uint256 quantity,",
        "uint256 partialFill,",

        "uint256 param0,",
        "uint256 param1,",
        "uint256 param2,",
        "uint256 param3,",
        "uint256 param4,",
        "uint256 param5,",
        "uint256 param6,",
        "uint256 param7,",
        "uint256 param8,",
        "uint256 param9,",

        "uint256 relayerFee,",
        "uint256 affiliateFee,",

        "uint256 nonce",
        ")"
    ));

    /// @notice Hashes the order
    /// @param _order SwaprateOrder Order to hash
    /// @return hash bytes32 Order hash
    function hashOrder(SwaprateOrder memory _order) public pure returns (bytes32 hash) {
        hash = keccak256(
            abi.encodePacked(
                abi.encodePacked(
                    EIP712_ORDER_TYPEHASH,
                    uint256(_order.syntheticId),
                    uint256(_order.oracleId),
                    uint256(_order.token),

                    uint256(_order.makerAddress),
                    uint256(_order.takerAddress),

                    uint256(_order.senderAddress),

                    uint256(_order.relayerAddress),
                    uint256(_order.affiliateAddress),

                    uint256(_order.feeTokenAddress)
                ),
                abi.encodePacked(
                    _order.endTime,
                    _order.quantity,
                    _order.partialFill
                ),
                abi.encodePacked(
                    _order.param0,
                    _order.param1,
                    _order.param2,
                    _order.param3,
                    _order.param4
                ),
                abi.encodePacked(
                    _order.param5,
                    _order.param6,
                    _order.param7,
                    _order.param8,
                    _order.param9
                ),
                abi.encodePacked(
                    _order.relayerFee,
                    _order.affiliateFee,

                    _order.nonce
                )
            )
        );
    }

    /// @notice Verifies order signature
    /// @param _hash bytes32 Hash of the order
    /// @param _signature bytes Signature of the order
    /// @param _address address Address of the order signer
    /// @return bool Returns whether `_signature` is valid and was created by `_address`
    function verifySignature(bytes32 _hash, bytes memory _signature, address _address) internal view returns (bool) {
        require(_signature.length == 65, "ORDER:INVALID_SIGNATURE_LENGTH");

        bytes32 digest = hashEIP712Message(_hash);
        address recovered = retrieveAddress(digest, _signature);
        return _address == recovered;
    }

    /// @notice Helping function to recover signer address
    /// @param _hash bytes32 Hash for signature
    /// @param _signature bytes Signature
    /// @return address Returns address of signature creator
    function retrieveAddress(bytes32 _hash, bytes memory _signature) private pure returns (address) {
        bytes32 r;
        bytes32 s;
        uint8 v;

        // Divide the signature in r, s and v variables
        // ecrecover takes the signature parameters, and the only way to get them
        // currently is to use assembly.
        // solium-disable-next-line security/no-inline-assembly
        assembly {
            r := mload(add(_signature, 32))
            s := mload(add(_signature, 64))
            v := byte(0, mload(add(_signature, 96)))
        }

        // Version of signature should be 27 or 28, but 0 and 1 are also possible versions
        if (v < 27) {
            v += 27;
        }

        // If the version is correct return the signer address
        if (v != 27 && v != 28) {
            return (address(0));
        } else {
            // solium-disable-next-line arg-overflow
            return ecrecover(_hash, v, r, s);
        }
    }
}

// File: contracts/Matching/SwaprateMatch/SwaprateMatchBase.sol

pragma solidity 0.5.16;












/// @title Opium.Matching.SwaprateMatchBase contract implements logic for order validation and cancelation
contract SwaprateMatchBase is MatchingErrors, LibSwaprateOrder, UsingRegistry, ReentrancyGuard {
    using SafeMath for uint256;
    using LibPosition for bytes32;
    using SafeERC20 for IERC20;

    // Emmitted when order was canceled
    event Canceled(bytes32 orderHash);

    // Canceled orders
    // This mapping holds hashes of canceled orders
    // canceled[orderHash] => canceled
    mapping (bytes32 => bool) public canceled;

    // Verified orders
    // This mapping holds hashes of verified orders to verify only once
    // verified[orderHash] => verified
    mapping (bytes32 => bool) public verified;

    // Vaults for fees
    // This mapping holds balances of relayers and affiliates fees to withdraw
    // balances[feeRecipientAddress][tokenAddress] => balances
    mapping (address => mapping (address => uint256)) public balances;

    // Keeps whether fee was already taken
    mapping (bytes32 => bool) public feeTaken;

    /// @notice Calling this function maker of the order could cancel it on-chain
    /// @param _order SwaprateOrder
    function cancel(SwaprateOrder memory _order) public {
        require(msg.sender == _order.makerAddress, ERROR_MATCH_CANCELLATION_NOT_ALLOWED);
        bytes32 orderHash = hashOrder(_order);
        require(!canceled[orderHash], ERROR_MATCH_ALREADY_CANCELED);
        canceled[orderHash] = true;

        emit Canceled(orderHash);
    }

    /// @notice Function to withdraw fees from orders for relayer and affiliates
    /// @param _token IERC20 Instance of token to withdraw
    function withdraw(IERC20 _token) public nonReentrant {
        uint256 balance = balances[msg.sender][address(_token)];
        balances[msg.sender][address(_token)] = 0;
        _token.safeTransfer(msg.sender, balance);
    }

    /// @notice This function checks whether order was canceled
    /// @param _hash bytes32 Hash of the order
    function validateNotCanceled(bytes32 _hash) internal view {
        require(!canceled[_hash], ERROR_MATCH_ORDER_WAS_CANCELED);
    }

    /// @notice This function validates takerAddress of _leftOrder. It should match either with _rightOrder.makerAddress or be set to zero address
    /// @param _leftOrder SwaprateOrder Left order
    /// @param _rightOrder SwaprateOrder Right order
    function validateTakerAddress(SwaprateOrder memory _leftOrder, SwaprateOrder memory _rightOrder) pure internal {
        require(
            _leftOrder.takerAddress == address(0) ||
            _leftOrder.takerAddress == _rightOrder.makerAddress,
            ERROR_MATCH_TAKER_ADDRESS_WRONG
        );
    }

    /// @notice This function validates whether sender address equals to `msg.sender` or set to zero address
    /// @param _order SwaprateOrder
    function validateSenderAddress(SwaprateOrder memory _order) internal view {
        require(
            _order.senderAddress == address(0) ||
            _order.senderAddress == msg.sender,
            ERROR_MATCH_SENDER_ADDRESS_WRONG
        );
    }

    /// @notice This function validates order signature if not validated before
    /// @param orderHash bytes32 Hash of the order
    /// @param _order SwaprateOrder
    function validateSignature(bytes32 orderHash, SwaprateOrder memory _order) internal {
        if (verified[orderHash]) {
            return;
        }

        bool result = verifySignature(orderHash, _order.signature, _order.makerAddress);

        require(result, ERROR_MATCH_SIGNATURE_NOT_VERIFIED);

        verified[orderHash] = true;
    }

    /// @notice This function is responsible for taking relayer and affiliate fees, if they were not taken already
    /// @param _orderHash bytes32 Hash of the order
    /// @param _order Order Order itself
    function takeFees(bytes32 _orderHash, SwaprateOrder memory _order) internal {
        // Check if fee was already taken
        if (feeTaken[_orderHash]) {
            return;
        }

        // Check if feeTokenAddress is not set to zero address
        if (_order.feeTokenAddress == address(0)) {
            return;
        }

        // Calculate total amount of fees needs to be transfered
        uint256 fees = _order.relayerFee.add(_order.affiliateFee);

        // If total amount of fees is non-zero
        if (fees == 0) {
            return;
        }

        // Create instance of fee token
        IERC20 feeToken = IERC20(_order.feeTokenAddress);

        // Create instance of TokenSpender
        TokenSpender tokenSpender = TokenSpender(registry.getTokenSpender());

        // Check if user has enough token approval to pay the fees
        require(feeToken.allowance(_order.makerAddress, address(tokenSpender)) >= fees, ERROR_MATCH_NOT_ENOUGH_ALLOWED_FEES);
        // Transfer fee
        tokenSpender.claimTokens(feeToken, _order.makerAddress, address(this), fees);

        // Get opium address
        address opiumAddress = registry.getOpiumAddress();

        // Add commission to relayer balance, or to opium balance if relayer is not set
        if (_order.relayerAddress != address(0)) {
            balances[_order.relayerAddress][_order.feeTokenAddress] = balances[_order.relayerAddress][_order.feeTokenAddress].add(_order.relayerFee);
        } else {
            balances[opiumAddress][_order.feeTokenAddress] = balances[opiumAddress][_order.feeTokenAddress].add(_order.relayerFee);
        }

        // Add commission to affiliate balance, or to opium balance if affiliate is not set
        if (_order.affiliateAddress != address(0)) {
            balances[_order.affiliateAddress][_order.feeTokenAddress] = balances[_order.affiliateAddress][_order.feeTokenAddress].add(_order.affiliateFee);
        } else {
            balances[opiumAddress][_order.feeTokenAddress] = balances[opiumAddress][_order.feeTokenAddress].add(_order.affiliateFee);
        }

        // Mark the fee of token as taken
        feeTaken[_orderHash] = true;
    }

    /// @notice Helper to get minimal of two integers
    /// @param _a uint256 First integer
    /// @param _b uint256 Second integer
    /// @return uint256 Minimal integer
    function min(uint256 _a, uint256 _b) internal pure returns (uint256) {
        return _a < _b ? _a : _b;
    }
}

// File: contracts/Matching/SwaprateMatch/SwaprateMatch.sol

pragma solidity 0.5.16;





/// @title Opium.Matching.SwaprateMatch contract implements create() function to settle a pair of orders and create derivatives for order makers
contract SwaprateMatch is SwaprateMatchBase, LibDerivative {

    // Orders filled quantity
    // This mapping holds orders filled quantity
    // filled[orderHash] => filled
    mapping (bytes32 => uint256) public filled;

    /// @notice Calls constructors of super-contracts
    /// @param _registry address Address of Opium.registry
    constructor (address _registry) public UsingRegistry(_registry) {}

    /// @notice This function receives left and right orders, derivative related to it
    /// @param _leftOrder Order
    /// @param _rightOrder Order
    /// @param _derivative Derivative Data of derivative for validation and calculation purposes
    function create(SwaprateOrder memory _leftOrder, SwaprateOrder memory _rightOrder, Derivative memory _derivative) public nonReentrant {
        // New deals must not offer tokenIds
        require(
            _leftOrder.syntheticId == _rightOrder.syntheticId,
            "MATCH:NOT_CREATION"
        );

        // Check if it's not pool
        require(!IDerivativeLogic(_derivative.syntheticId).isPool(), "MATCH:CANT_BE_POOL");

        // Validate taker if set
        validateTakerAddress(_leftOrder, _rightOrder);
        validateTakerAddress(_rightOrder, _leftOrder);

        // Validate sender if set
        validateSenderAddress(_leftOrder);
        validateSenderAddress(_rightOrder);

        // Validate if was canceled
        // orderHashes[0] - leftOrderHash
        // orderHashes[1] - rightOrderHash
        bytes32[2] memory orderHashes;
        orderHashes[0] = hashOrder(_leftOrder);
        validateNotCanceled(orderHashes[0]);
        validateSignature(orderHashes[0], _leftOrder);

        orderHashes[1] = hashOrder(_rightOrder);
        validateNotCanceled(orderHashes[1]);
        validateSignature(orderHashes[1], _rightOrder);

        // Calculate derivative hash and get margin
        // margins[0] - leftMargin
        // margins[1] - rightMargin
        (uint256[2] memory margins, ) = _calculateDerivativeAndGetMargin(_derivative);

        // Calculate and validate availabilities of orders and fill them
        uint256 fillable = _checkFillability(orderHashes[0], _leftOrder, orderHashes[1], _rightOrder);

        // Validate derivative parameters with orders
        _verifyDerivative(_leftOrder, _rightOrder, _derivative);

        // Take fees
        takeFees(orderHashes[0], _leftOrder);
        takeFees(orderHashes[1], _rightOrder);

        // Send margin to Core
        _distributeFunds(_leftOrder, _rightOrder, _derivative, margins, fillable);

        // Settle contracts
        Core(registry.getCore()).create(_derivative, fillable, [_leftOrder.makerAddress, _rightOrder.makerAddress]);
    }

    // PRIVATE FUNCTIONS

    /// @notice Calculates derivative hash and gets margin
    /// @param _derivative Derivative
    /// @return margins uint256[2] left and right margin
    /// @return derivativeHash bytes32 Hash of the derivative
    function _calculateDerivativeAndGetMargin(Derivative memory _derivative) private returns (uint256[2] memory margins, bytes32 derivativeHash) {
        // Calculate derivative related data for validation
        derivativeHash = getDerivativeHash(_derivative);

        // Get cached total margin required according to logic
        // margins[0] - leftMargin
        // margins[1] - rightMargin
        (margins[0], margins[1]) = SyntheticAggregator(registry.getSyntheticAggregator()).getMargin(derivativeHash, _derivative);
    }

    /// @notice Calculate and validate availabilities of orders and fill them
    /// @param _leftOrderHash bytes32
    /// @param _leftOrder SwaprateOrder
    /// @param _rightOrderHash bytes32
    /// @param _rightOrder SwaprateOrder
    /// @return fillable uint256
    function _checkFillability(bytes32 _leftOrderHash, SwaprateOrder memory _leftOrder, bytes32 _rightOrderHash, SwaprateOrder memory _rightOrder) private returns (uint256 fillable) {
        // Calculate availabilities of orders
        uint256 leftAvailable = _leftOrder.quantity.sub(filled[_leftOrderHash]);
        uint256 rightAvailable = _rightOrder.quantity.sub(filled[_rightOrderHash]);

        require(leftAvailable != 0 && rightAvailable !=0, "MATCH:NO_AVAILABLE");

        // We could only fill minimum available of both counterparties
        fillable = min(leftAvailable, rightAvailable);

        // Check fillable with order conditions about partial fill requirements
        if (_leftOrder.partialFill == 0 && _rightOrder.partialFill == 0) {
            require(_leftOrder.quantity == _rightOrder.quantity, "MATCH:FULL_FILL_NOT_POSSIBLE");
        } else if (_leftOrder.partialFill == 0 && _rightOrder.partialFill == 1) {
            require(_leftOrder.quantity <= rightAvailable, "MATCH:FULL_FILL_NOT_POSSIBLE");
        } else if (_leftOrder.partialFill == 1 && _rightOrder.partialFill == 0) {
            require(leftAvailable >= _rightOrder.quantity, "MATCH:FULL_FILL_NOT_POSSIBLE");
        }

        // Update filled
        filled[_leftOrderHash] = filled[_leftOrderHash].add(fillable);
        filled[_rightOrderHash] = filled[_rightOrderHash].add(fillable);
    }

    /// @notice Validate derivative parameters with orders
    /// @param _leftOrder SwaprateOrder
    /// @param _rightOrder SwaprateOrder
    /// @param _derivative Derivative
    function _verifyDerivative(SwaprateOrder memory _leftOrder, SwaprateOrder memory _rightOrder, Derivative memory _derivative) private pure {
        string memory orderError = "MATCH:DERIVATIVE_PARAM_IS_WRONG";

        // Validate derivative endTime
        require(
            _derivative.endTime == _leftOrder.endTime &&
            _derivative.endTime == _rightOrder.endTime,
            orderError
        );

        // Validate derivative syntheticId
        require(
            _derivative.syntheticId == _leftOrder.syntheticId &&
            _derivative.syntheticId == _rightOrder.syntheticId,
            orderError
        );

        // Validate derivative oracleId
        require(
            _derivative.oracleId == _leftOrder.oracleId &&
            _derivative.oracleId == _rightOrder.oracleId,
            orderError
        );

        // Validate derivative token
        require(
            _derivative.token == _leftOrder.token &&
            _derivative.token == _rightOrder.token,
            orderError
        );

        // Validate derivative params
        require(_derivative.params.length >= 20, "MATCH:DERIVATIVE_PARAMS_LENGTH_IS_WRONG");

        // Validate left order params
        require(_leftOrder.param0 == _derivative.params[0], orderError);
        require(_leftOrder.param1 == _derivative.params[1], orderError);
        require(_leftOrder.param2 == _derivative.params[2], orderError);
        require(_leftOrder.param3 == _derivative.params[3], orderError);
        require(_leftOrder.param4 == _derivative.params[4], orderError);
        require(_leftOrder.param5 == _derivative.params[5], orderError);
        require(_leftOrder.param6 == _derivative.params[6], orderError);
        require(_leftOrder.param7 == _derivative.params[7], orderError);
        require(_leftOrder.param8 == _derivative.params[8], orderError);
        require(_leftOrder.param9 == _derivative.params[9], orderError);

        // Validate right order params
        require(_rightOrder.param0 == _derivative.params[10], orderError);
        require(_rightOrder.param1 == _derivative.params[11], orderError);
        require(_rightOrder.param2 == _derivative.params[12], orderError);
        require(_rightOrder.param3 == _derivative.params[13], orderError);
        require(_rightOrder.param4 == _derivative.params[14], orderError);
        require(_rightOrder.param5 == _derivative.params[15], orderError);
        require(_rightOrder.param6 == _derivative.params[16], orderError);
        require(_rightOrder.param7 == _derivative.params[17], orderError);
        require(_rightOrder.param8 == _derivative.params[18], orderError);
        require(_rightOrder.param9 == _derivative.params[19], orderError);
    }

    /// @notice Distributes funds to core
    /// @param _leftOrder SwaprateOrder
    /// @param _rightOrder SwaprateOrder
    /// @param _derivative Derivative
    /// @param margins uint256[2] left and right margin
    /// @param _fillable uint256 How many positions are fillable
    function _distributeFunds(SwaprateOrder memory _leftOrder, SwaprateOrder memory _rightOrder, Derivative memory _derivative, uint256[2] memory margins, uint256 _fillable) private {
        IERC20 marginToken = IERC20(_derivative.token);
        TokenSpender tokenSpender = TokenSpender(registry.getTokenSpender());

        // Transfer margin from left to Match and send to Core
        if (margins[0] != 0) {
            // Check allowance for margins
            require(marginToken.allowance(_leftOrder.makerAddress, address(tokenSpender)) >= margins[0].mul(_fillable), "MATCH:NOT_ENOUGH_ALLOWED_MARGIN");

            // Transfer margins from buyer to Match
            tokenSpender.claimTokens(marginToken, _leftOrder.makerAddress, address(this), margins[0].mul(_fillable));
        }

        // Transfer margin from right to Match and send to Core
        if (margins[1] != 0) {
            // Check allowance for premiums + margin
            require(marginToken.allowance(_rightOrder.makerAddress, address(tokenSpender)) >= margins[1].mul(_fillable), "MATCH:NOT_ENOUGH_ALLOWED_MARGIN");

            // Transfer margins from seller to Match
            tokenSpender.claimTokens(marginToken, _rightOrder.makerAddress, address(this), margins[1].mul(_fillable));
        }

        if (margins[0].add(margins[1]) != 0) {
            // Approve margin to Core for derivative creation
            require(marginToken.approve(address(tokenSpender), margins[0].add(margins[1]).mul(_fillable)), "MATCH:COULDNT_APPROVE_MARGIN_FOR_CORE");
        }
    }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_registry","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"orderHash","type":"bytes32"}],"name":"Canceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"registry","type":"address"}],"name":"RegistrySet","type":"event"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"balances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"components":[{"internalType":"address","name":"syntheticId","type":"address"},{"internalType":"address","name":"oracleId","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"makerAddress","type":"address"},{"internalType":"address","name":"takerAddress","type":"address"},{"internalType":"address","name":"senderAddress","type":"address"},{"internalType":"address","name":"relayerAddress","type":"address"},{"internalType":"address","name":"affiliateAddress","type":"address"},{"internalType":"address","name":"feeTokenAddress","type":"address"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"uint256","name":"partialFill","type":"uint256"},{"internalType":"uint256","name":"param0","type":"uint256"},{"internalType":"uint256","name":"param1","type":"uint256"},{"internalType":"uint256","name":"param2","type":"uint256"},{"internalType":"uint256","name":"param3","type":"uint256"},{"internalType":"uint256","name":"param4","type":"uint256"},{"internalType":"uint256","name":"param5","type":"uint256"},{"internalType":"uint256","name":"param6","type":"uint256"},{"internalType":"uint256","name":"param7","type":"uint256"},{"internalType":"uint256","name":"param8","type":"uint256"},{"internalType":"uint256","name":"param9","type":"uint256"},{"internalType":"uint256","name":"relayerFee","type":"uint256"},{"internalType":"uint256","name":"affiliateFee","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct LibSwaprateOrder.SwaprateOrder","name":"_order","type":"tuple"}],"name":"cancel","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"canceled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"components":[{"internalType":"address","name":"syntheticId","type":"address"},{"internalType":"address","name":"oracleId","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"makerAddress","type":"address"},{"internalType":"address","name":"takerAddress","type":"address"},{"internalType":"address","name":"senderAddress","type":"address"},{"internalType":"address","name":"relayerAddress","type":"address"},{"internalType":"address","name":"affiliateAddress","type":"address"},{"internalType":"address","name":"feeTokenAddress","type":"address"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"uint256","name":"partialFill","type":"uint256"},{"internalType":"uint256","name":"param0","type":"uint256"},{"internalType":"uint256","name":"param1","type":"uint256"},{"internalType":"uint256","name":"param2","type":"uint256"},{"internalType":"uint256","name":"param3","type":"uint256"},{"internalType":"uint256","name":"param4","type":"uint256"},{"internalType":"uint256","name":"param5","type":"uint256"},{"internalType":"uint256","name":"param6","type":"uint256"},{"internalType":"uint256","name":"param7","type":"uint256"},{"internalType":"uint256","name":"param8","type":"uint256"},{"internalType":"uint256","name":"param9","type":"uint256"},{"internalType":"uint256","name":"relayerFee","type":"uint256"},{"internalType":"uint256","name":"affiliateFee","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct LibSwaprateOrder.SwaprateOrder","name":"_leftOrder","type":"tuple"},{"components":[{"internalType":"address","name":"syntheticId","type":"address"},{"internalType":"address","name":"oracleId","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"makerAddress","type":"address"},{"internalType":"address","name":"takerAddress","type":"address"},{"internalType":"address","name":"senderAddress","type":"address"},{"internalType":"address","name":"relayerAddress","type":"address"},{"internalType":"address","name":"affiliateAddress","type":"address"},{"internalType":"address","name":"feeTokenAddress","type":"address"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"uint256","name":"partialFill","type":"uint256"},{"internalType":"uint256","name":"param0","type":"uint256"},{"internalType":"uint256","name":"param1","type":"uint256"},{"internalType":"uint256","name":"param2","type":"uint256"},{"internalType":"uint256","name":"param3","type":"uint256"},{"internalType":"uint256","name":"param4","type":"uint256"},{"internalType":"uint256","name":"param5","type":"uint256"},{"internalType":"uint256","name":"param6","type":"uint256"},{"internalType":"uint256","name":"param7","type":"uint256"},{"internalType":"uint256","name":"param8","type":"uint256"},{"internalType":"uint256","name":"param9","type":"uint256"},{"internalType":"uint256","name":"relayerFee","type":"uint256"},{"internalType":"uint256","name":"affiliateFee","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct LibSwaprateOrder.SwaprateOrder","name":"_rightOrder","type":"tuple"},{"components":[{"internalType":"uint256","name":"margin","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256[]","name":"params","type":"uint256[]"},{"internalType":"address","name":"oracleId","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"syntheticId","type":"address"}],"internalType":"struct LibDerivative.Derivative","name":"_derivative","type":"tuple"}],"name":"create","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"feeTaken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"filled","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"components":[{"internalType":"uint256","name":"margin","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256[]","name":"params","type":"uint256[]"},{"internalType":"address","name":"oracleId","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"syntheticId","type":"address"}],"internalType":"struct LibDerivative.Derivative","name":"_derivative","type":"tuple"}],"name":"getDerivativeHash","outputs":[{"internalType":"bytes32","name":"derivativeHash","type":"bytes32"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[],"name":"getRegistry","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"components":[{"internalType":"address","name":"syntheticId","type":"address"},{"internalType":"address","name":"oracleId","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"makerAddress","type":"address"},{"internalType":"address","name":"takerAddress","type":"address"},{"internalType":"address","name":"senderAddress","type":"address"},{"internalType":"address","name":"relayerAddress","type":"address"},{"internalType":"address","name":"affiliateAddress","type":"address"},{"internalType":"address","name":"feeTokenAddress","type":"address"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"uint256","name":"partialFill","type":"uint256"},{"internalType":"uint256","name":"param0","type":"uint256"},{"internalType":"uint256","name":"param1","type":"uint256"},{"internalType":"uint256","name":"param2","type":"uint256"},{"internalType":"uint256","name":"param3","type":"uint256"},{"internalType":"uint256","name":"param4","type":"uint256"},{"internalType":"uint256","name":"param5","type":"uint256"},{"internalType":"uint256","name":"param6","type":"uint256"},{"internalType":"uint256","name":"param7","type":"uint256"},{"internalType":"uint256","name":"param8","type":"uint256"},{"internalType":"uint256","name":"param9","type":"uint256"},{"internalType":"uint256","name":"relayerFee","type":"uint256"},{"internalType":"uint256","name":"affiliateFee","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct LibSwaprateOrder.SwaprateOrder","name":"_order","type":"tuple"}],"name":"hashOrder","outputs":[{"internalType":"bytes32","name":"hash","type":"bytes32"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"verified","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]

60806040523480156200001157600080fd5b5060405162003b7138038062003b71833981016040819052620000349162000129565b806040516020016200004690620002a1565b604051602081830303815290604052805190602001206040516200006a90620002e2565b6040518091039020604051620000809062000294565b6040519081900381206200009b9392913090602001620002ff565b60408051808303601f19018152908290528051602090910120600055600180546001600160a01b0319166001600160a01b0384161790557f27fe5f0c1c3b1ed427cc63d0f05759ffdecf9aec9e18d31ef366fc8a6cb5dc3b9062000101908390620002ef565b60405180910390a15050600160025562000380565b8051620001238162000366565b92915050565b6000602082840312156200013c57600080fd5b60006200014a848462000116565b949350505050565b6200015d816200034a565b82525050565b6200015d8162000357565b60006200017d600c8362000345565b6b1cdd1c9a5b99c81b985b594b60a21b8152600c0192915050565b6000620001a760198362000345565b7f6164647265737320766572696679696e67436f6e747261637400000000000000815260190192915050565b6000620001e260018362000345565b602960f81b815260010192915050565b600062000201600f8362000345565b6e1cdd1c9a5b99c81d995c9cda5bdb8b608a1b8152600f0192915050565b60006200022e60018362000345565b603160f81b815260010192915050565b60006200024d600d8362000345565b6c08a92a06e626488dedac2d2dc5609b1b8152600d0192915050565b600062000278600d8362000345565b6c4f7069756d204e6574776f726b60981b8152600d0192915050565b600062000123826200021f565b6000620002ae826200023e565b9150620002bb826200016e565b9150620002c882620001f2565b9150620002d58262000198565b91506200012382620001d3565b6000620001238262000269565b6020810162000123828462000152565b608081016200030f828762000163565b6200031e602083018662000163565b6200032d604083018562000163565b6200033c606083018462000152565b95945050505050565b919050565b600062000123826200035a565b90565b6001600160a01b031690565b62000371816200034a565b81146200037d57600080fd5b50565b6137e180620003906000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c80635ab1bd53116100715780635ab1bd5314610132578063c23f001f14610147578063c59e43e51461015a578063e6cd370e1461016d578063eb95512214610180578063fdd2f04714610193576100a9565b80630411e552146100ae5780631c439266146100d7578063288cdc91146100ea5780632d5bb2791461010a57806351cff8d91461011d575b600080fd5b6100c16100bc366004612553565b6101a6565b6040516100ce9190613495565b60405180910390f35b6100c16100e5366004612553565b6101bb565b6100fd6100f8366004612553565b6101d0565b6040516100ce91906134a3565b6100fd6101183660046125c4565b6101e2565b61013061012b366004612571565b6103c1565b005b61013a610430565b6040516100ce9190613451565b6100fd6101553660046124fb565b61043f565b6100c1610168366004612553565b61045c565b61013061017b3660046125f9565b610471565b6100fd61018e36600461258f565b61074b565b6101306101a13660046125c4565b61077b565b60066020526000908152604090205460ff1681565b60036020526000908152604090205460ff1681565b60076020526000908152604090205481565b60006040516020016101f390613232565b6040516020818303038152906040528051906020012082600001516001600160a01b031683602001516001600160a01b031684604001516001600160a01b031685606001516001600160a01b031686608001516001600160a01b03168760a001516001600160a01b03168860c001516001600160a01b03168960e001516001600160a01b03168a61010001516001600160a01b03166040516020016102a19a99989796959493929190613131565b6040516020818303038152906040528261012001518361014001518461016001516040516020016102d4939291906133c1565b60408051601f19818403018152908290526101808501516101a08601516101c08701516101e0880151610200890151949561031295906020016133f8565b60408051601f19818403018152908290526102208601516102408701516102608801516102808901516102a08a0151949561035095906020016133f8565b604051602081830303815290604052856102c00151866102e00151876103000151604051602001610383939291906133c1565b60408051601f19818403018152908290526103a495949392916020016131eb565b604051602081830303815290604052805190602001209050919050565b60028054600101908190553360008181526005602090815260408083206001600160a01b03871680855292528220805492905590916104019190836108af565b50600254811461042c5760405162461bcd60e51b815260040161042390613608565b60405180910390fd5b5050565b6001546001600160a01b031690565b600560209081526000928352604080842090915290825290205481565b60046020526000908152604090205460ff1681565b6002805460010190819055825184516001600160a01b039081169116146104aa5760405162461bcd60e51b815260040161042390613548565b8160a001516001600160a01b031663e2e1c6db6040518163ffffffff1660e01b815260040160206040518083038186803b1580156104e757600080fd5b505afa1580156104fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061051f9190810190612535565b1561053c5760405162461bcd60e51b8152600401610423906135c8565b610546848461090d565b610550838561090d565b61055984610994565b61056283610994565b61056a6120a4565b610573856101e2565b81526105868160005b6020020151610a0e565b80516105929086610a76565b61059b846101e2565b60208201526105ab81600161057c565b60208101516105ba9085610a76565b6105c26120a4565b6105cb84610b1e565b50825160208401519192506000916105e69190899089610c45565b90506105f3878787610dff565b82516105ff90886114cb565b602083015161060e90876114cb565b61061b8787878585611997565b600160009054906101000a90046001600160a01b03166001600160a01b031663da4fbe526040518163ffffffff1660e01b815260040160206040518083038186803b15801561066957600080fd5b505afa15801561067d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506106a191908101906124dd565b6040805180820182526060808b01516001600160a01b039081168352908a0151811660208301529151631af5968960e31b8152929091169163d7acb448916106ef9189918691600401613628565b600060405180830381600087803b15801561070957600080fd5b505af115801561071d573d6000803e3d6000fd5b5050505050505060025481146107455760405162461bcd60e51b815260040161042390613608565b50505050565b80516020808301516040808501516060860151608087015160a088015193516000976103a497909695910161335b565b80606001516001600160a01b0316336001600160a01b0316146040518060400160405280601e81526020017f4d415443483a43414e43454c4c4154494f4e5f4e4f545f414c4c4f5745440000815250906107e85760405162461bcd60e51b81526004016104239190613537565b5060006107f4826101e2565b600081815260036020908152604091829020548251808401909352601683527513505510d20e9053149150511657d0d05390d153115160521b918301919091529192509060ff16156108595760405162461bcd60e51b81526004016104239190613537565b5060008181526003602052604090819020805460ff19166001179055517f134fdd648feeaf30251f0157f9624ef8608ff9a042aad6d13e73f35d21d3f88d906108a39083906134a3565b60405180910390a15050565b60405161090890849063a9059cbb60e01b906108d1908690869060240161347a565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152611d6e565b505050565b60808201516001600160a01b03161580610940575080606001516001600160a01b031682608001516001600160a01b0316145b6040518060400160405280601981526020017f4d415443483a54414b45525f414444524553535f57524f4e4700000000000000815250906109085760405162461bcd60e51b81526004016104239190613537565b60a08101516001600160a01b031615806109ba575060a08101516001600160a01b031633145b6040518060400160405280601a81526020017f4d415443483a53454e4445525f414444524553535f57524f4e470000000000008152509061042c5760405162461bcd60e51b81526004016104239190613537565b600081815260036020908152604091829020548251808401909352601883527f4d415443483a4f524445525f5741535f43414e43454c454400000000000000009183019190915260ff161561042c5760405162461bcd60e51b81526004016104239190613537565b60008281526004602052604090205460ff1615610a925761042c565b6000610aa8838361032001518460600151611e53565b9050806040518060400160405280601c81526020017f4d415443483a5349474e41545552455f4e4f545f56455249464945440000000081525090610aff5760405162461bcd60e51b81526004016104239190613537565b50506000828152600460205260409020805460ff191660011790555050565b610b266120a4565b6000610b318361074b565b9050600160009054906101000a90046001600160a01b03166001600160a01b0316638177644c6040518163ffffffff1660e01b815260040160206040518083038186803b158015610b8157600080fd5b505afa158015610b95573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610bb991908101906124dd565b6001600160a01b03166399f685ee82856040518363ffffffff1660e01b8152600401610be69291906134b1565b6040805180830381600087803b158015610bff57600080fd5b505af1158015610c13573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c3791908101906126ab565b602084015282529092909150565b6000848152600760205260408120546101408501518291610c6c919063ffffffff611ea916565b6000858152600760205260408120546101408601519293509091610c959163ffffffff611ea916565b90508115801590610ca557508015155b610cc15760405162461bcd60e51b815260040161042390613568565b610ccb8282611ef4565b92508561016001516000148015610ce55750610160840151155b15610d185783610140015186610140015114610d135760405162461bcd60e51b8152600401610423906135e8565b610d9c565b610160860151158015610d3057508361016001516001145b15610d5a57808661014001511115610d135760405162461bcd60e51b8152600401610423906135e8565b8561016001516001148015610d725750610160840151155b15610d9c57836101400151821015610d9c5760405162461bcd60e51b8152600401610423906135e8565b600087815260076020526040902054610dbb908463ffffffff611f0a16565b600088815260076020526040808220929092558681522054610de3908463ffffffff611f0a16565b6000958652600760205260409095209490945550949350505050565b60606040518060400160405280601f81526020017f4d415443483a444552495641544956455f504152414d5f49535f57524f4e470081525090508361012001518260200151148015610e5957508261012001518260200151145b8190610e785760405162461bcd60e51b81526004016104239190613537565b5083600001516001600160a01b03168260a001516001600160a01b0316148015610ebb575082600001516001600160a01b03168260a001516001600160a01b0316145b8190610eda5760405162461bcd60e51b81526004016104239190613537565b5083602001516001600160a01b031682606001516001600160a01b0316148015610f1d575082602001516001600160a01b031682606001516001600160a01b0316145b8190610f3c5760405162461bcd60e51b81526004016104239190613537565b5083604001516001600160a01b031682608001516001600160a01b0316148015610f7f575082604001516001600160a01b031682608001516001600160a01b0316145b8190610f9e5760405162461bcd60e51b81526004016104239190613537565b5060148260400151511015610fc55760405162461bcd60e51b8152600401610423906135b8565b8160400151600081518110610fd657fe5b60200260200101518461018001511481906110045760405162461bcd60e51b81526004016104239190613537565b50816040015160018151811061101657fe5b6020026020010151846101a001511481906110445760405162461bcd60e51b81526004016104239190613537565b50816040015160028151811061105657fe5b6020026020010151846101c001511481906110845760405162461bcd60e51b81526004016104239190613537565b50816040015160038151811061109657fe5b6020026020010151846101e001511481906110c45760405162461bcd60e51b81526004016104239190613537565b5081604001516004815181106110d657fe5b60200260200101518461020001511481906111045760405162461bcd60e51b81526004016104239190613537565b50816040015160058151811061111657fe5b60200260200101518461022001511481906111445760405162461bcd60e51b81526004016104239190613537565b50816040015160068151811061115657fe5b60200260200101518461024001511481906111845760405162461bcd60e51b81526004016104239190613537565b50816040015160078151811061119657fe5b60200260200101518461026001511481906111c45760405162461bcd60e51b81526004016104239190613537565b5081604001516008815181106111d657fe5b60200260200101518461028001511481906112045760405162461bcd60e51b81526004016104239190613537565b50816040015160098151811061121657fe5b6020026020010151846102a001511481906112445760405162461bcd60e51b81526004016104239190613537565b508160400151600a8151811061125657fe5b60200260200101518361018001511481906112845760405162461bcd60e51b81526004016104239190613537565b508160400151600b8151811061129657fe5b6020026020010151836101a001511481906112c45760405162461bcd60e51b81526004016104239190613537565b508160400151600c815181106112d657fe5b6020026020010151836101c001511481906113045760405162461bcd60e51b81526004016104239190613537565b508160400151600d8151811061131657fe5b6020026020010151836101e001511481906113445760405162461bcd60e51b81526004016104239190613537565b508160400151600e8151811061135657fe5b60200260200101518361020001511481906113845760405162461bcd60e51b81526004016104239190613537565b508160400151600f8151811061139657fe5b60200260200101518361022001511481906113c45760405162461bcd60e51b81526004016104239190613537565b5081604001516010815181106113d657fe5b60200260200101518361024001511481906114045760405162461bcd60e51b81526004016104239190613537565b50816040015160118151811061141657fe5b60200260200101518361026001511481906114445760405162461bcd60e51b81526004016104239190613537565b50816040015160128151811061145657fe5b60200260200101518361028001511481906114845760405162461bcd60e51b81526004016104239190613537565b50816040015160138151811061149657fe5b6020026020010151836102a001511481906114c45760405162461bcd60e51b81526004016104239190613537565b5050505050565b60008281526006602052604090205460ff16156114e75761042c565b6101008101516001600160a01b03166114ff5761042c565b600061151e826102e00151836102c00151611f0a90919063ffffffff16565b90508061152b575061042c565b6101008201516001546040805163beb5bd8160e01b815290516000926001600160a01b03169163beb5bd81916004808301926020929190829003018186803b15801561157657600080fd5b505afa15801561158a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506115ae91908101906124dd565b905082826001600160a01b031663dd62ed3e8660600151846040518363ffffffff1660e01b81526004016115e392919061345f565b60206040518083038186803b1580156115fb57600080fd5b505afa15801561160f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611633919081019061268d565b10156040518060400160405280601d81526020017f4d415443483a4e4f545f454e4f5547485f414c4c4f5745445f46454553000000815250906116895760405162461bcd60e51b81526004016104239190613537565b50606084015160405163052f523360e11b81526001600160a01b03831691630a5ea466916116bf9186913090899060040161350f565b600060405180830381600087803b1580156116d957600080fd5b505af11580156116ed573d6000803e3d6000fd5b505050506000600160009054906101000a90046001600160a01b03166001600160a01b031663ec45d8ae6040518163ffffffff1660e01b815260040160206040518083038186803b15801561174157600080fd5b505afa158015611755573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061177991908101906124dd565b60c08601519091506001600160a01b03161561180b576102c085015160c08601516001600160a01b0390811660009081526005602090815260408083206101008b0151909416835292905220546117d59163ffffffff611f0a16565b60c08601516001600160a01b0390811660009081526005602090815260408083206101008b015190941683529290522055611879565b6102c08501516001600160a01b0380831660009081526005602090815260408083206101008b01519094168352929052205461184c9163ffffffff611f0a16565b6001600160a01b0380831660009081526005602090815260408083206101008b0151909416835292905220555b60e08501516001600160a01b031615611908576102e085015160e08601516001600160a01b0390811660009081526005602090815260408083206101008b0151909416835292905220546118d29163ffffffff611f0a16565b60e08601516001600160a01b0390811660009081526005602090815260408083206101008b015190941683529290522055611976565b6102e08501516001600160a01b0380831660009081526005602090815260408083206101008b0151909416835292905220546119499163ffffffff611f0a16565b6001600160a01b0380831660009081526005602090815260408083206101008b0151909416835292905220555b5050506000838152600660205260409020805460ff19166001179055505050565b60808301516001546040805163beb5bd8160e01b815290516000926001600160a01b03169163beb5bd81916004808301926020929190829003018186803b1580156119e157600080fd5b505afa1580156119f5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611a1991908101906124dd565b845190915015611b5557611a3c838560005b60200201519063ffffffff611f2f16565b6060880151604051636eb1769f60e11b81526001600160a01b0385169163dd62ed3e91611a6e9190869060040161345f565b60206040518083038186803b158015611a8657600080fd5b505afa158015611a9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611abe919081019061268d565b1015611adc5760405162461bcd60e51b815260040161042390613598565b806001600160a01b0316630a5ea46683896060015130611b03888a600060028110611a2b57fe5b6040518563ffffffff1660e01b8152600401611b22949392919061350f565b600060405180830381600087803b158015611b3c57600080fd5b505af1158015611b50573d6000803e3d6000fd5b505050505b602084015115611c8457611b6b83856001611a2b565b6060870151604051636eb1769f60e11b81526001600160a01b0385169163dd62ed3e91611b9d9190869060040161345f565b60206040518083038186803b158015611bb557600080fd5b505afa158015611bc9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611bed919081019061268d565b1015611c0b5760405162461bcd60e51b815260040161042390613598565b806001600160a01b0316630a5ea46683886060015130611c32888a600160028110611a2b57fe5b6040518563ffffffff1660e01b8152600401611c51949392919061350f565b600060405180830381600087803b158015611c6b57600080fd5b505af1158015611c7f573d6000803e3d6000fd5b505050505b6020840151611ca2908560005b60200201519063ffffffff611f0a16565b15611d65576001600160a01b03821663095ea7b382611cda86611cce89600160200201518a6000611c91565b9063ffffffff611f2f16565b6040518363ffffffff1660e01b8152600401611cf792919061347a565b602060405180830381600087803b158015611d1157600080fd5b505af1158015611d25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611d499190810190612535565b611d655760405162461bcd60e51b8152600401610423906135d8565b50505050505050565b611d80826001600160a01b0316611f69565b611d9c5760405162461bcd60e51b815260040161042390613618565b60006060836001600160a01b031683604051611db891906131df565b6000604051808303816000865af19150503d8060008114611df5576040519150601f19603f3d011682016040523d82523d6000602084013e611dfa565b606091505b509150915081611e1c5760405162461bcd60e51b815260040161042390613588565b8051156107455780806020019051611e379190810190612535565b6107455760405162461bcd60e51b8152600401610423906135f8565b60008251604114611e765760405162461bcd60e51b815260040161042390613558565b6000611e8185611fa5565b90506000611e8f8286611fca565b6001600160a01b03858116911614925050505b9392505050565b6000611eeb83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250612078565b90505b92915050565b6000818310611f035781611eeb565b5090919050565b600082820183811015611eeb5760405162461bcd60e51b815260040161042390613578565b600082611f3e57506000611eee565b82820282848281611f4b57fe5b0414611eeb5760405162461bcd60e51b8152600401610423906135a8565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4708115801590611f9d5750808214155b949350505050565b60005460405161190160f01b8152600281019190915260228101919091526042902090565b6020810151604082015160608301516000929190831a601b811015611fed57601b015b8060ff16601b1415801561200557508060ff16601c14155b156120165760009350505050611eee565b6001868285856040516000815260200160405260405161203994939291906134d1565b6020604051602081039080840390855afa15801561205b573d6000803e3d6000fd5b505050602060405103519350505050611eee565b50505092915050565b6000818484111561209c5760405162461bcd60e51b81526004016104239190613537565b505050900390565b60405180604001604052806002906020820280388339509192915050565b8035611eee8161376c565b8051611eee8161376c565b600082601f8301126120e957600080fd5b81356120fc6120f78261367c565b613655565b9150818183526020840193506020810190508385602084028201111561212157600080fd5b60005b8381101561214d57816121378882612162565b8452506020928301929190910190600101612124565b5050505092915050565b8051611eee81613783565b8035611eee8161378c565b600082601f83011261217e57600080fd5b813561218c6120f78261369d565b915080825260208301602083018583830111156121a857600080fd5b61206f838284613713565b8035611eee81613795565b600060c082840312156121d057600080fd5b6121da60c0613655565b905060006121e88484612162565b82525060206121f984848301612162565b602083015250604082013567ffffffffffffffff81111561221957600080fd5b612225848285016120d8565b6040830152506060612239848285016120c2565b606083015250608061224d848285016120c2565b60808301525060a0612261848285016120c2565b60a08301525092915050565b6000610340828403121561228057600080fd5b61228b610340613655565b9050600061229984846120c2565b82525060206122aa848483016120c2565b60208301525060406122be848285016120c2565b60408301525060606122d2848285016120c2565b60608301525060806122e6848285016120c2565b60808301525060a06122fa848285016120c2565b60a08301525060c061230e848285016120c2565b60c08301525060e0612322848285016120c2565b60e083015250610100612337848285016120c2565b6101008301525061012061234d84828501612162565b6101208301525061014061236384828501612162565b6101408301525061016061237984828501612162565b6101608301525061018061238f84828501612162565b610180830152506101a06123a584828501612162565b6101a0830152506101c06123bb84828501612162565b6101c0830152506101e06123d184828501612162565b6101e0830152506102006123e784828501612162565b610200830152506102206123fd84828501612162565b6102208301525061024061241384828501612162565b6102408301525061026061242984828501612162565b6102608301525061028061243f84828501612162565b610280830152506102a061245584828501612162565b6102a0830152506102c061246b84828501612162565b6102c0830152506102e061248184828501612162565b6102e08301525061030061249784828501612162565b6103008301525061032082013567ffffffffffffffff8111156124b957600080fd5b6124c58482850161216d565b6103208301525092915050565b8051611eee8161378c565b6000602082840312156124ef57600080fd5b6000611f9d84846120cd565b6000806040838503121561250e57600080fd5b600061251a85856120c2565b925050602061252b858286016120c2565b9150509250929050565b60006020828403121561254757600080fd5b6000611f9d8484612157565b60006020828403121561256557600080fd5b6000611f9d8484612162565b60006020828403121561258357600080fd5b6000611f9d84846121b3565b6000602082840312156125a157600080fd5b813567ffffffffffffffff8111156125b857600080fd5b611f9d848285016121be565b6000602082840312156125d657600080fd5b813567ffffffffffffffff8111156125ed57600080fd5b611f9d8482850161226d565b60008060006060848603121561260e57600080fd5b833567ffffffffffffffff81111561262557600080fd5b6126318682870161226d565b935050602084013567ffffffffffffffff81111561264e57600080fd5b61265a8682870161226d565b925050604084013567ffffffffffffffff81111561267757600080fd5b612683868287016121be565b9150509250925092565b60006020828403121561269f57600080fd5b6000611f9d84846124d2565b600080604083850312156126be57600080fd5b60006126ca85856124d2565b925050602061252b858286016124d2565b60006126e783836126fb565b505060200190565b60006126e7838361281f565b612704816136e6565b82525050565b612704612716826136e6565b61374b565b612724816136ce565b61272e81846136d8565b9250612739826136c5565b8060005b8381101561276757815161275187826126db565b965061275c836136c8565b92505060010161273d565b505050505050565b600061277a826136d4565b61278481856136dd565b935061278f836136c8565b8060005b838110156127bd5781516127a788826126ef565b97506127b2836136c8565b925050600101612793565b509495945050505050565b60006127d3826136d4565b6127dd81856136d8565b93506127e8836136c8565b8060005b838110156127bd57815161280088826126ef565b975061280b836136c8565b9250506001016127ec565b612704816136f1565b612704816136c5565b612704612834826136c5565b6136c5565b6000612844826136d4565b61284e81856136d8565b935061285e81856020860161371f565b9290920192915050565b612704816136f6565b600061287c826136d4565b61288681856136dd565b935061289681856020860161371f565b61289f8161375c565b9093019392505050565b60006128b6600f836136d8565b6e1d5a5b9d0c8d4d881c185c985b4d8b608a1b8152600f0192915050565b60006128e16012836136dd565b7126a0aa21a41d2727aa2fa1a922a0aa24a7a760711b815260200192915050565b600061290f601e836136dd565b7f4f524445523a494e56414c49445f5349474e41545552455f4c454e4754480000815260200192915050565b60006129486011836136d8565b701d5a5b9d0c8d4d881c5d585b9d1a5d1e4b607a1b815260110192915050565b60006129756016836136d8565b751859191c995cdcc81cd95b99195c9059191c995cdccb60521b815260160192915050565b60006129a7600f836136d8565b6e1d5a5b9d0c8d4d881c185c985b4c8b608a1b8152600f0192915050565b60006129d26015836136d8565b741d5a5b9d0c8d4d881859999a5b1a585d195199594b605a1b815260150192915050565b6000612a036006836136d8565b6509ee4c8cae4560d31b815260060192915050565b6000612a256012836136dd565b714d415443483a4e4f5f415641494c41424c4560701b815260200192915050565b6000612a53600f836136d8565b6e1d5a5b9d0c8d4d881c185c985b4dcb608a1b8152600f0192915050565b6000612a7e601b836136dd565b7f536166654d6174683a206164646974696f6e206f766572666c6f770000000000815260200192915050565b6000612ab76015836136d8565b741859191c995cdcc81b585ad95c9059191c995cdccb605a1b815260150192915050565b6000612ae86020836136dd565b7f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815260200192915050565b6000612b21600d836136d8565b6c75696e74323536206e6f6e636560981b8152600d0192915050565b6000612b4a6001836136d8565b602960f81b815260010192915050565b6000612b676018836136d8565b7f6164647265737320666565546f6b656e416464726573732c0000000000000000815260180192915050565b6000612ba0600f836136d8565b6e1d5a5b9d0c8d4d881c185c985b4e4b608a1b8152600f0192915050565b6000612bcb6010836136d8565b6f1d5a5b9d0c8d4d88195b99151a5b594b60821b815260100192915050565b6000612bf7600f836136d8565b6e1d5a5b9d0c8d4d881c185c985b4ccb608a1b8152600f0192915050565b6000612c226017836136d8565b7f616464726573732072656c61796572416464726573732c000000000000000000815260170192915050565b6000612c5b6019836136d8565b7f6164647265737320616666696c69617465416464726573732c00000000000000815260190192915050565b6000612c94601f836136dd565b7f4d415443483a4e4f545f454e4f5547485f414c4c4f5745445f4d415247494e00815260200192915050565b6000612ccd600f836136d8565b6e1d5a5b9d0c8d4d881c185c985b4c4b608a1b8152600f0192915050565b6000612cf86011836136d8565b701859191c995cdcc81bdc9858db1952590b607a1b815260110192915050565b6000612d256021836136dd565b7f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f8152607760f81b602082015260400192915050565b6000612d686027836136dd565b7f4d415443483a444552495641544956455f504152414d535f4c454e4754485f49815266535f57524f4e4760c81b602082015260400192915050565b6000612db1600f836136d8565b6e1d5a5b9d0c8d4d881c185c985b4d4b608a1b8152600f0192915050565b6000612ddc6014836136d8565b731d5a5b9d0c8d4d881c185c9d1a585b119a5b1b0b60621b815260140192915050565b6000612e0c6012836136dd565b7113505510d20e90d0539517d09157d413d3d360721b815260200192915050565b6000612e3a600f836136d8565b6e1d5a5b9d0c8d4d881c185c985b4c0b608a1b8152600f0192915050565b6000612e656025836136dd565b7f4d415443483a434f554c444e545f415050524f56455f4d415247494e5f464f528152645f434f524560d81b602082015260400192915050565b6000612eac600f836136d8565b6e1d5a5b9d0c8d4d881c185c985b4e0b608a1b8152600f0192915050565b6000612ed76014836136d8565b731859191c995cdcc81cde5b9d1a195d1a58d2590b60621b815260140192915050565b6000612f07600f836136d8565b6e1d5a5b9d0c8d4d881c185c985b4d0b608a1b8152600f0192915050565b6000612f32601c836136dd565b7f4d415443483a46554c4c5f46494c4c5f4e4f545f504f535349424c4500000000815260200192915050565b6000612f6b602a836136dd565b7f5361666545524332303a204552433230206f7065726174696f6e20646964206e8152691bdd081cdd58d8d9595960b21b602082015260400192915050565b6000612fb76013836136d8565b721d5a5b9d0c8d4d881c995b185e595c9199594b606a1b815260130192915050565b6000612fe6601f836136dd565b7f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00815260200192915050565b600061301f6015836136d8565b741859191c995cdcc81d185ad95c9059191c995cdccb605a1b815260150192915050565b6000613050601f836136dd565b7f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400815260200192915050565b6000613089600e836136d8565b6d1859191c995cdcc81d1bdad95b8b60921b8152600e0192915050565b805160009060c08401906130ba858261281f565b5060208301516130cd602086018261281f565b50604083015184820360408601526130e5828261276f565b91505060608301516130fa60608601826126fb565b50608083015161310d60808601826126fb565b5060a083015161312060a08601826126fb565b509392505050565b6127048161370d565b600061313d828d612828565b60208201915061314d828c612828565b60208201915061315d828b612828565b60208201915061316d828a612828565b60208201915061317d8289612828565b60208201915061318d8288612828565b60208201915061319d8287612828565b6020820191506131ad8286612828565b6020820191506131bd8285612828565b6020820191506131cd8284612828565b506020019a9950505050505050505050565b6000611ea28284612839565b60006131f78288612839565b91506132038287612839565b915061320f8286612839565b915061321b8285612839565b91506132278284612839565b979650505050505050565b600061323d826129f6565b915061324882612eca565b915061325382612ceb565b915061325e8261307c565b915061326982612aaa565b915061327482613012565b915061327f82612968565b915061328a82612c15565b915061329582612c4e565b91506132a082612b5a565b91506132ab82612bbe565b91506132b68261293b565b91506132c182612dcf565b91506132cc82612e2d565b91506132d782612cc0565b91506132e28261299a565b91506132ed82612bea565b91506132f882612efa565b915061330382612da4565b915061330e826128a9565b915061331982612a46565b915061332482612e9f565b915061332f82612b93565b915061333a82612faa565b9150613345826129c5565b915061335082612b14565b9150611eee82612b3d565b60006133678289612828565b6020820191506133778288612828565b60208201915061338782876127c8565b9150613393828661270a565b6014820191506133a3828561270a565b6014820191506133b3828461270a565b506014019695505050505050565b60006133cd8286612828565b6020820191506133dd8285612828565b6020820191506133ed8284612828565b506020019392505050565b60006134048288612828565b6020820191506134148287612828565b6020820191506134248286612828565b6020820191506134348285612828565b6020820191506134448284612828565b5060200195945050505050565b60208101611eee82846126fb565b6040810161346d82856126fb565b611ea260208301846126fb565b6040810161348882856126fb565b611ea2602083018461281f565b60208101611eee8284612816565b60208101611eee828461281f565b604081016134bf828561281f565b8181036020830152611f9d81846130a6565b608081016134df828761281f565b6134ec6020830186613128565b6134f9604083018561281f565b613506606083018461281f565b95945050505050565b6080810161351d8287612868565b61352a60208301866126fb565b6134f960408301856126fb565b60208082528101611eeb8184612871565b60208082528101611eee816128d4565b60208082528101611eee81612902565b60208082528101611eee81612a18565b60208082528101611eee81612a71565b60208082528101611eee81612adb565b60208082528101611eee81612c87565b60208082528101611eee81612d18565b60208082528101611eee81612d5b565b60208082528101611eee81612dff565b60208082528101611eee81612e58565b60208082528101611eee81612f25565b60208082528101611eee81612f5e565b60208082528101611eee81612fd9565b60208082528101611eee81613043565b6080808252810161363981866130a6565b9050613648602083018561281f565b611f9d604083018461271b565b60405181810167ffffffffffffffff8111828210171561367457600080fd5b604052919050565b600067ffffffffffffffff82111561369357600080fd5b5060209081020190565b600067ffffffffffffffff8211156136b457600080fd5b506020601f91909101601f19160190565b90565b60200190565b50600290565b5190565b919050565b90815260200190565b6000611eee82613701565b151590565b6000611eee826136e6565b6001600160a01b031690565b60ff1690565b82818337506000910152565b60005b8381101561373a578181015183820152602001613722565b838111156107455750506000910152565b6000611eee826000611eee82613766565b601f01601f191690565b60601b90565b613775816136e6565b811461378057600080fd5b50565b613775816136f1565b613775816136c5565b613775816136f656fea365627a7a723158205954dbcb54a17d8a58594685ce1fb91fab7c5ef8ceaf900cfe50bbac7ab7b91c6c6578706572696d656e74616cf564736f6c63430005100040000000000000000000000000c955f3c0d5a87710996d13b1f9aa3a77552d7a7e

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106100a95760003560e01c80635ab1bd53116100715780635ab1bd5314610132578063c23f001f14610147578063c59e43e51461015a578063e6cd370e1461016d578063eb95512214610180578063fdd2f04714610193576100a9565b80630411e552146100ae5780631c439266146100d7578063288cdc91146100ea5780632d5bb2791461010a57806351cff8d91461011d575b600080fd5b6100c16100bc366004612553565b6101a6565b6040516100ce9190613495565b60405180910390f35b6100c16100e5366004612553565b6101bb565b6100fd6100f8366004612553565b6101d0565b6040516100ce91906134a3565b6100fd6101183660046125c4565b6101e2565b61013061012b366004612571565b6103c1565b005b61013a610430565b6040516100ce9190613451565b6100fd6101553660046124fb565b61043f565b6100c1610168366004612553565b61045c565b61013061017b3660046125f9565b610471565b6100fd61018e36600461258f565b61074b565b6101306101a13660046125c4565b61077b565b60066020526000908152604090205460ff1681565b60036020526000908152604090205460ff1681565b60076020526000908152604090205481565b60006040516020016101f390613232565b6040516020818303038152906040528051906020012082600001516001600160a01b031683602001516001600160a01b031684604001516001600160a01b031685606001516001600160a01b031686608001516001600160a01b03168760a001516001600160a01b03168860c001516001600160a01b03168960e001516001600160a01b03168a61010001516001600160a01b03166040516020016102a19a99989796959493929190613131565b6040516020818303038152906040528261012001518361014001518461016001516040516020016102d4939291906133c1565b60408051601f19818403018152908290526101808501516101a08601516101c08701516101e0880151610200890151949561031295906020016133f8565b60408051601f19818403018152908290526102208601516102408701516102608801516102808901516102a08a0151949561035095906020016133f8565b604051602081830303815290604052856102c00151866102e00151876103000151604051602001610383939291906133c1565b60408051601f19818403018152908290526103a495949392916020016131eb565b604051602081830303815290604052805190602001209050919050565b60028054600101908190553360008181526005602090815260408083206001600160a01b03871680855292528220805492905590916104019190836108af565b50600254811461042c5760405162461bcd60e51b815260040161042390613608565b60405180910390fd5b5050565b6001546001600160a01b031690565b600560209081526000928352604080842090915290825290205481565b60046020526000908152604090205460ff1681565b6002805460010190819055825184516001600160a01b039081169116146104aa5760405162461bcd60e51b815260040161042390613548565b8160a001516001600160a01b031663e2e1c6db6040518163ffffffff1660e01b815260040160206040518083038186803b1580156104e757600080fd5b505afa1580156104fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061051f9190810190612535565b1561053c5760405162461bcd60e51b8152600401610423906135c8565b610546848461090d565b610550838561090d565b61055984610994565b61056283610994565b61056a6120a4565b610573856101e2565b81526105868160005b6020020151610a0e565b80516105929086610a76565b61059b846101e2565b60208201526105ab81600161057c565b60208101516105ba9085610a76565b6105c26120a4565b6105cb84610b1e565b50825160208401519192506000916105e69190899089610c45565b90506105f3878787610dff565b82516105ff90886114cb565b602083015161060e90876114cb565b61061b8787878585611997565b600160009054906101000a90046001600160a01b03166001600160a01b031663da4fbe526040518163ffffffff1660e01b815260040160206040518083038186803b15801561066957600080fd5b505afa15801561067d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506106a191908101906124dd565b6040805180820182526060808b01516001600160a01b039081168352908a0151811660208301529151631af5968960e31b8152929091169163d7acb448916106ef9189918691600401613628565b600060405180830381600087803b15801561070957600080fd5b505af115801561071d573d6000803e3d6000fd5b5050505050505060025481146107455760405162461bcd60e51b815260040161042390613608565b50505050565b80516020808301516040808501516060860151608087015160a088015193516000976103a497909695910161335b565b80606001516001600160a01b0316336001600160a01b0316146040518060400160405280601e81526020017f4d415443483a43414e43454c4c4154494f4e5f4e4f545f414c4c4f5745440000815250906107e85760405162461bcd60e51b81526004016104239190613537565b5060006107f4826101e2565b600081815260036020908152604091829020548251808401909352601683527513505510d20e9053149150511657d0d05390d153115160521b918301919091529192509060ff16156108595760405162461bcd60e51b81526004016104239190613537565b5060008181526003602052604090819020805460ff19166001179055517f134fdd648feeaf30251f0157f9624ef8608ff9a042aad6d13e73f35d21d3f88d906108a39083906134a3565b60405180910390a15050565b60405161090890849063a9059cbb60e01b906108d1908690869060240161347a565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152611d6e565b505050565b60808201516001600160a01b03161580610940575080606001516001600160a01b031682608001516001600160a01b0316145b6040518060400160405280601981526020017f4d415443483a54414b45525f414444524553535f57524f4e4700000000000000815250906109085760405162461bcd60e51b81526004016104239190613537565b60a08101516001600160a01b031615806109ba575060a08101516001600160a01b031633145b6040518060400160405280601a81526020017f4d415443483a53454e4445525f414444524553535f57524f4e470000000000008152509061042c5760405162461bcd60e51b81526004016104239190613537565b600081815260036020908152604091829020548251808401909352601883527f4d415443483a4f524445525f5741535f43414e43454c454400000000000000009183019190915260ff161561042c5760405162461bcd60e51b81526004016104239190613537565b60008281526004602052604090205460ff1615610a925761042c565b6000610aa8838361032001518460600151611e53565b9050806040518060400160405280601c81526020017f4d415443483a5349474e41545552455f4e4f545f56455249464945440000000081525090610aff5760405162461bcd60e51b81526004016104239190613537565b50506000828152600460205260409020805460ff191660011790555050565b610b266120a4565b6000610b318361074b565b9050600160009054906101000a90046001600160a01b03166001600160a01b0316638177644c6040518163ffffffff1660e01b815260040160206040518083038186803b158015610b8157600080fd5b505afa158015610b95573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610bb991908101906124dd565b6001600160a01b03166399f685ee82856040518363ffffffff1660e01b8152600401610be69291906134b1565b6040805180830381600087803b158015610bff57600080fd5b505af1158015610c13573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c3791908101906126ab565b602084015282529092909150565b6000848152600760205260408120546101408501518291610c6c919063ffffffff611ea916565b6000858152600760205260408120546101408601519293509091610c959163ffffffff611ea916565b90508115801590610ca557508015155b610cc15760405162461bcd60e51b815260040161042390613568565b610ccb8282611ef4565b92508561016001516000148015610ce55750610160840151155b15610d185783610140015186610140015114610d135760405162461bcd60e51b8152600401610423906135e8565b610d9c565b610160860151158015610d3057508361016001516001145b15610d5a57808661014001511115610d135760405162461bcd60e51b8152600401610423906135e8565b8561016001516001148015610d725750610160840151155b15610d9c57836101400151821015610d9c5760405162461bcd60e51b8152600401610423906135e8565b600087815260076020526040902054610dbb908463ffffffff611f0a16565b600088815260076020526040808220929092558681522054610de3908463ffffffff611f0a16565b6000958652600760205260409095209490945550949350505050565b60606040518060400160405280601f81526020017f4d415443483a444552495641544956455f504152414d5f49535f57524f4e470081525090508361012001518260200151148015610e5957508261012001518260200151145b8190610e785760405162461bcd60e51b81526004016104239190613537565b5083600001516001600160a01b03168260a001516001600160a01b0316148015610ebb575082600001516001600160a01b03168260a001516001600160a01b0316145b8190610eda5760405162461bcd60e51b81526004016104239190613537565b5083602001516001600160a01b031682606001516001600160a01b0316148015610f1d575082602001516001600160a01b031682606001516001600160a01b0316145b8190610f3c5760405162461bcd60e51b81526004016104239190613537565b5083604001516001600160a01b031682608001516001600160a01b0316148015610f7f575082604001516001600160a01b031682608001516001600160a01b0316145b8190610f9e5760405162461bcd60e51b81526004016104239190613537565b5060148260400151511015610fc55760405162461bcd60e51b8152600401610423906135b8565b8160400151600081518110610fd657fe5b60200260200101518461018001511481906110045760405162461bcd60e51b81526004016104239190613537565b50816040015160018151811061101657fe5b6020026020010151846101a001511481906110445760405162461bcd60e51b81526004016104239190613537565b50816040015160028151811061105657fe5b6020026020010151846101c001511481906110845760405162461bcd60e51b81526004016104239190613537565b50816040015160038151811061109657fe5b6020026020010151846101e001511481906110c45760405162461bcd60e51b81526004016104239190613537565b5081604001516004815181106110d657fe5b60200260200101518461020001511481906111045760405162461bcd60e51b81526004016104239190613537565b50816040015160058151811061111657fe5b60200260200101518461022001511481906111445760405162461bcd60e51b81526004016104239190613537565b50816040015160068151811061115657fe5b60200260200101518461024001511481906111845760405162461bcd60e51b81526004016104239190613537565b50816040015160078151811061119657fe5b60200260200101518461026001511481906111c45760405162461bcd60e51b81526004016104239190613537565b5081604001516008815181106111d657fe5b60200260200101518461028001511481906112045760405162461bcd60e51b81526004016104239190613537565b50816040015160098151811061121657fe5b6020026020010151846102a001511481906112445760405162461bcd60e51b81526004016104239190613537565b508160400151600a8151811061125657fe5b60200260200101518361018001511481906112845760405162461bcd60e51b81526004016104239190613537565b508160400151600b8151811061129657fe5b6020026020010151836101a001511481906112c45760405162461bcd60e51b81526004016104239190613537565b508160400151600c815181106112d657fe5b6020026020010151836101c001511481906113045760405162461bcd60e51b81526004016104239190613537565b508160400151600d8151811061131657fe5b6020026020010151836101e001511481906113445760405162461bcd60e51b81526004016104239190613537565b508160400151600e8151811061135657fe5b60200260200101518361020001511481906113845760405162461bcd60e51b81526004016104239190613537565b508160400151600f8151811061139657fe5b60200260200101518361022001511481906113c45760405162461bcd60e51b81526004016104239190613537565b5081604001516010815181106113d657fe5b60200260200101518361024001511481906114045760405162461bcd60e51b81526004016104239190613537565b50816040015160118151811061141657fe5b60200260200101518361026001511481906114445760405162461bcd60e51b81526004016104239190613537565b50816040015160128151811061145657fe5b60200260200101518361028001511481906114845760405162461bcd60e51b81526004016104239190613537565b50816040015160138151811061149657fe5b6020026020010151836102a001511481906114c45760405162461bcd60e51b81526004016104239190613537565b5050505050565b60008281526006602052604090205460ff16156114e75761042c565b6101008101516001600160a01b03166114ff5761042c565b600061151e826102e00151836102c00151611f0a90919063ffffffff16565b90508061152b575061042c565b6101008201516001546040805163beb5bd8160e01b815290516000926001600160a01b03169163beb5bd81916004808301926020929190829003018186803b15801561157657600080fd5b505afa15801561158a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506115ae91908101906124dd565b905082826001600160a01b031663dd62ed3e8660600151846040518363ffffffff1660e01b81526004016115e392919061345f565b60206040518083038186803b1580156115fb57600080fd5b505afa15801561160f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611633919081019061268d565b10156040518060400160405280601d81526020017f4d415443483a4e4f545f454e4f5547485f414c4c4f5745445f46454553000000815250906116895760405162461bcd60e51b81526004016104239190613537565b50606084015160405163052f523360e11b81526001600160a01b03831691630a5ea466916116bf9186913090899060040161350f565b600060405180830381600087803b1580156116d957600080fd5b505af11580156116ed573d6000803e3d6000fd5b505050506000600160009054906101000a90046001600160a01b03166001600160a01b031663ec45d8ae6040518163ffffffff1660e01b815260040160206040518083038186803b15801561174157600080fd5b505afa158015611755573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061177991908101906124dd565b60c08601519091506001600160a01b03161561180b576102c085015160c08601516001600160a01b0390811660009081526005602090815260408083206101008b0151909416835292905220546117d59163ffffffff611f0a16565b60c08601516001600160a01b0390811660009081526005602090815260408083206101008b015190941683529290522055611879565b6102c08501516001600160a01b0380831660009081526005602090815260408083206101008b01519094168352929052205461184c9163ffffffff611f0a16565b6001600160a01b0380831660009081526005602090815260408083206101008b0151909416835292905220555b60e08501516001600160a01b031615611908576102e085015160e08601516001600160a01b0390811660009081526005602090815260408083206101008b0151909416835292905220546118d29163ffffffff611f0a16565b60e08601516001600160a01b0390811660009081526005602090815260408083206101008b015190941683529290522055611976565b6102e08501516001600160a01b0380831660009081526005602090815260408083206101008b0151909416835292905220546119499163ffffffff611f0a16565b6001600160a01b0380831660009081526005602090815260408083206101008b0151909416835292905220555b5050506000838152600660205260409020805460ff19166001179055505050565b60808301516001546040805163beb5bd8160e01b815290516000926001600160a01b03169163beb5bd81916004808301926020929190829003018186803b1580156119e157600080fd5b505afa1580156119f5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611a1991908101906124dd565b845190915015611b5557611a3c838560005b60200201519063ffffffff611f2f16565b6060880151604051636eb1769f60e11b81526001600160a01b0385169163dd62ed3e91611a6e9190869060040161345f565b60206040518083038186803b158015611a8657600080fd5b505afa158015611a9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611abe919081019061268d565b1015611adc5760405162461bcd60e51b815260040161042390613598565b806001600160a01b0316630a5ea46683896060015130611b03888a600060028110611a2b57fe5b6040518563ffffffff1660e01b8152600401611b22949392919061350f565b600060405180830381600087803b158015611b3c57600080fd5b505af1158015611b50573d6000803e3d6000fd5b505050505b602084015115611c8457611b6b83856001611a2b565b6060870151604051636eb1769f60e11b81526001600160a01b0385169163dd62ed3e91611b9d9190869060040161345f565b60206040518083038186803b158015611bb557600080fd5b505afa158015611bc9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611bed919081019061268d565b1015611c0b5760405162461bcd60e51b815260040161042390613598565b806001600160a01b0316630a5ea46683886060015130611c32888a600160028110611a2b57fe5b6040518563ffffffff1660e01b8152600401611c51949392919061350f565b600060405180830381600087803b158015611c6b57600080fd5b505af1158015611c7f573d6000803e3d6000fd5b505050505b6020840151611ca2908560005b60200201519063ffffffff611f0a16565b15611d65576001600160a01b03821663095ea7b382611cda86611cce89600160200201518a6000611c91565b9063ffffffff611f2f16565b6040518363ffffffff1660e01b8152600401611cf792919061347a565b602060405180830381600087803b158015611d1157600080fd5b505af1158015611d25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611d499190810190612535565b611d655760405162461bcd60e51b8152600401610423906135d8565b50505050505050565b611d80826001600160a01b0316611f69565b611d9c5760405162461bcd60e51b815260040161042390613618565b60006060836001600160a01b031683604051611db891906131df565b6000604051808303816000865af19150503d8060008114611df5576040519150601f19603f3d011682016040523d82523d6000602084013e611dfa565b606091505b509150915081611e1c5760405162461bcd60e51b815260040161042390613588565b8051156107455780806020019051611e379190810190612535565b6107455760405162461bcd60e51b8152600401610423906135f8565b60008251604114611e765760405162461bcd60e51b815260040161042390613558565b6000611e8185611fa5565b90506000611e8f8286611fca565b6001600160a01b03858116911614925050505b9392505050565b6000611eeb83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250612078565b90505b92915050565b6000818310611f035781611eeb565b5090919050565b600082820183811015611eeb5760405162461bcd60e51b815260040161042390613578565b600082611f3e57506000611eee565b82820282848281611f4b57fe5b0414611eeb5760405162461bcd60e51b8152600401610423906135a8565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4708115801590611f9d5750808214155b949350505050565b60005460405161190160f01b8152600281019190915260228101919091526042902090565b6020810151604082015160608301516000929190831a601b811015611fed57601b015b8060ff16601b1415801561200557508060ff16601c14155b156120165760009350505050611eee565b6001868285856040516000815260200160405260405161203994939291906134d1565b6020604051602081039080840390855afa15801561205b573d6000803e3d6000fd5b505050602060405103519350505050611eee565b50505092915050565b6000818484111561209c5760405162461bcd60e51b81526004016104239190613537565b505050900390565b60405180604001604052806002906020820280388339509192915050565b8035611eee8161376c565b8051611eee8161376c565b600082601f8301126120e957600080fd5b81356120fc6120f78261367c565b613655565b9150818183526020840193506020810190508385602084028201111561212157600080fd5b60005b8381101561214d57816121378882612162565b8452506020928301929190910190600101612124565b5050505092915050565b8051611eee81613783565b8035611eee8161378c565b600082601f83011261217e57600080fd5b813561218c6120f78261369d565b915080825260208301602083018583830111156121a857600080fd5b61206f838284613713565b8035611eee81613795565b600060c082840312156121d057600080fd5b6121da60c0613655565b905060006121e88484612162565b82525060206121f984848301612162565b602083015250604082013567ffffffffffffffff81111561221957600080fd5b612225848285016120d8565b6040830152506060612239848285016120c2565b606083015250608061224d848285016120c2565b60808301525060a0612261848285016120c2565b60a08301525092915050565b6000610340828403121561228057600080fd5b61228b610340613655565b9050600061229984846120c2565b82525060206122aa848483016120c2565b60208301525060406122be848285016120c2565b60408301525060606122d2848285016120c2565b60608301525060806122e6848285016120c2565b60808301525060a06122fa848285016120c2565b60a08301525060c061230e848285016120c2565b60c08301525060e0612322848285016120c2565b60e083015250610100612337848285016120c2565b6101008301525061012061234d84828501612162565b6101208301525061014061236384828501612162565b6101408301525061016061237984828501612162565b6101608301525061018061238f84828501612162565b610180830152506101a06123a584828501612162565b6101a0830152506101c06123bb84828501612162565b6101c0830152506101e06123d184828501612162565b6101e0830152506102006123e784828501612162565b610200830152506102206123fd84828501612162565b6102208301525061024061241384828501612162565b6102408301525061026061242984828501612162565b6102608301525061028061243f84828501612162565b610280830152506102a061245584828501612162565b6102a0830152506102c061246b84828501612162565b6102c0830152506102e061248184828501612162565b6102e08301525061030061249784828501612162565b6103008301525061032082013567ffffffffffffffff8111156124b957600080fd5b6124c58482850161216d565b6103208301525092915050565b8051611eee8161378c565b6000602082840312156124ef57600080fd5b6000611f9d84846120cd565b6000806040838503121561250e57600080fd5b600061251a85856120c2565b925050602061252b858286016120c2565b9150509250929050565b60006020828403121561254757600080fd5b6000611f9d8484612157565b60006020828403121561256557600080fd5b6000611f9d8484612162565b60006020828403121561258357600080fd5b6000611f9d84846121b3565b6000602082840312156125a157600080fd5b813567ffffffffffffffff8111156125b857600080fd5b611f9d848285016121be565b6000602082840312156125d657600080fd5b813567ffffffffffffffff8111156125ed57600080fd5b611f9d8482850161226d565b60008060006060848603121561260e57600080fd5b833567ffffffffffffffff81111561262557600080fd5b6126318682870161226d565b935050602084013567ffffffffffffffff81111561264e57600080fd5b61265a8682870161226d565b925050604084013567ffffffffffffffff81111561267757600080fd5b612683868287016121be565b9150509250925092565b60006020828403121561269f57600080fd5b6000611f9d84846124d2565b600080604083850312156126be57600080fd5b60006126ca85856124d2565b925050602061252b858286016124d2565b60006126e783836126fb565b505060200190565b60006126e7838361281f565b612704816136e6565b82525050565b612704612716826136e6565b61374b565b612724816136ce565b61272e81846136d8565b9250612739826136c5565b8060005b8381101561276757815161275187826126db565b965061275c836136c8565b92505060010161273d565b505050505050565b600061277a826136d4565b61278481856136dd565b935061278f836136c8565b8060005b838110156127bd5781516127a788826126ef565b97506127b2836136c8565b925050600101612793565b509495945050505050565b60006127d3826136d4565b6127dd81856136d8565b93506127e8836136c8565b8060005b838110156127bd57815161280088826126ef565b975061280b836136c8565b9250506001016127ec565b612704816136f1565b612704816136c5565b612704612834826136c5565b6136c5565b6000612844826136d4565b61284e81856136d8565b935061285e81856020860161371f565b9290920192915050565b612704816136f6565b600061287c826136d4565b61288681856136dd565b935061289681856020860161371f565b61289f8161375c565b9093019392505050565b60006128b6600f836136d8565b6e1d5a5b9d0c8d4d881c185c985b4d8b608a1b8152600f0192915050565b60006128e16012836136dd565b7126a0aa21a41d2727aa2fa1a922a0aa24a7a760711b815260200192915050565b600061290f601e836136dd565b7f4f524445523a494e56414c49445f5349474e41545552455f4c454e4754480000815260200192915050565b60006129486011836136d8565b701d5a5b9d0c8d4d881c5d585b9d1a5d1e4b607a1b815260110192915050565b60006129756016836136d8565b751859191c995cdcc81cd95b99195c9059191c995cdccb60521b815260160192915050565b60006129a7600f836136d8565b6e1d5a5b9d0c8d4d881c185c985b4c8b608a1b8152600f0192915050565b60006129d26015836136d8565b741d5a5b9d0c8d4d881859999a5b1a585d195199594b605a1b815260150192915050565b6000612a036006836136d8565b6509ee4c8cae4560d31b815260060192915050565b6000612a256012836136dd565b714d415443483a4e4f5f415641494c41424c4560701b815260200192915050565b6000612a53600f836136d8565b6e1d5a5b9d0c8d4d881c185c985b4dcb608a1b8152600f0192915050565b6000612a7e601b836136dd565b7f536166654d6174683a206164646974696f6e206f766572666c6f770000000000815260200192915050565b6000612ab76015836136d8565b741859191c995cdcc81b585ad95c9059191c995cdccb605a1b815260150192915050565b6000612ae86020836136dd565b7f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815260200192915050565b6000612b21600d836136d8565b6c75696e74323536206e6f6e636560981b8152600d0192915050565b6000612b4a6001836136d8565b602960f81b815260010192915050565b6000612b676018836136d8565b7f6164647265737320666565546f6b656e416464726573732c0000000000000000815260180192915050565b6000612ba0600f836136d8565b6e1d5a5b9d0c8d4d881c185c985b4e4b608a1b8152600f0192915050565b6000612bcb6010836136d8565b6f1d5a5b9d0c8d4d88195b99151a5b594b60821b815260100192915050565b6000612bf7600f836136d8565b6e1d5a5b9d0c8d4d881c185c985b4ccb608a1b8152600f0192915050565b6000612c226017836136d8565b7f616464726573732072656c61796572416464726573732c000000000000000000815260170192915050565b6000612c5b6019836136d8565b7f6164647265737320616666696c69617465416464726573732c00000000000000815260190192915050565b6000612c94601f836136dd565b7f4d415443483a4e4f545f454e4f5547485f414c4c4f5745445f4d415247494e00815260200192915050565b6000612ccd600f836136d8565b6e1d5a5b9d0c8d4d881c185c985b4c4b608a1b8152600f0192915050565b6000612cf86011836136d8565b701859191c995cdcc81bdc9858db1952590b607a1b815260110192915050565b6000612d256021836136dd565b7f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f8152607760f81b602082015260400192915050565b6000612d686027836136dd565b7f4d415443483a444552495641544956455f504152414d535f4c454e4754485f49815266535f57524f4e4760c81b602082015260400192915050565b6000612db1600f836136d8565b6e1d5a5b9d0c8d4d881c185c985b4d4b608a1b8152600f0192915050565b6000612ddc6014836136d8565b731d5a5b9d0c8d4d881c185c9d1a585b119a5b1b0b60621b815260140192915050565b6000612e0c6012836136dd565b7113505510d20e90d0539517d09157d413d3d360721b815260200192915050565b6000612e3a600f836136d8565b6e1d5a5b9d0c8d4d881c185c985b4c0b608a1b8152600f0192915050565b6000612e656025836136dd565b7f4d415443483a434f554c444e545f415050524f56455f4d415247494e5f464f528152645f434f524560d81b602082015260400192915050565b6000612eac600f836136d8565b6e1d5a5b9d0c8d4d881c185c985b4e0b608a1b8152600f0192915050565b6000612ed76014836136d8565b731859191c995cdcc81cde5b9d1a195d1a58d2590b60621b815260140192915050565b6000612f07600f836136d8565b6e1d5a5b9d0c8d4d881c185c985b4d0b608a1b8152600f0192915050565b6000612f32601c836136dd565b7f4d415443483a46554c4c5f46494c4c5f4e4f545f504f535349424c4500000000815260200192915050565b6000612f6b602a836136dd565b7f5361666545524332303a204552433230206f7065726174696f6e20646964206e8152691bdd081cdd58d8d9595960b21b602082015260400192915050565b6000612fb76013836136d8565b721d5a5b9d0c8d4d881c995b185e595c9199594b606a1b815260130192915050565b6000612fe6601f836136dd565b7f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00815260200192915050565b600061301f6015836136d8565b741859191c995cdcc81d185ad95c9059191c995cdccb605a1b815260150192915050565b6000613050601f836136dd565b7f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400815260200192915050565b6000613089600e836136d8565b6d1859191c995cdcc81d1bdad95b8b60921b8152600e0192915050565b805160009060c08401906130ba858261281f565b5060208301516130cd602086018261281f565b50604083015184820360408601526130e5828261276f565b91505060608301516130fa60608601826126fb565b50608083015161310d60808601826126fb565b5060a083015161312060a08601826126fb565b509392505050565b6127048161370d565b600061313d828d612828565b60208201915061314d828c612828565b60208201915061315d828b612828565b60208201915061316d828a612828565b60208201915061317d8289612828565b60208201915061318d8288612828565b60208201915061319d8287612828565b6020820191506131ad8286612828565b6020820191506131bd8285612828565b6020820191506131cd8284612828565b506020019a9950505050505050505050565b6000611ea28284612839565b60006131f78288612839565b91506132038287612839565b915061320f8286612839565b915061321b8285612839565b91506132278284612839565b979650505050505050565b600061323d826129f6565b915061324882612eca565b915061325382612ceb565b915061325e8261307c565b915061326982612aaa565b915061327482613012565b915061327f82612968565b915061328a82612c15565b915061329582612c4e565b91506132a082612b5a565b91506132ab82612bbe565b91506132b68261293b565b91506132c182612dcf565b91506132cc82612e2d565b91506132d782612cc0565b91506132e28261299a565b91506132ed82612bea565b91506132f882612efa565b915061330382612da4565b915061330e826128a9565b915061331982612a46565b915061332482612e9f565b915061332f82612b93565b915061333a82612faa565b9150613345826129c5565b915061335082612b14565b9150611eee82612b3d565b60006133678289612828565b6020820191506133778288612828565b60208201915061338782876127c8565b9150613393828661270a565b6014820191506133a3828561270a565b6014820191506133b3828461270a565b506014019695505050505050565b60006133cd8286612828565b6020820191506133dd8285612828565b6020820191506133ed8284612828565b506020019392505050565b60006134048288612828565b6020820191506134148287612828565b6020820191506134248286612828565b6020820191506134348285612828565b6020820191506134448284612828565b5060200195945050505050565b60208101611eee82846126fb565b6040810161346d82856126fb565b611ea260208301846126fb565b6040810161348882856126fb565b611ea2602083018461281f565b60208101611eee8284612816565b60208101611eee828461281f565b604081016134bf828561281f565b8181036020830152611f9d81846130a6565b608081016134df828761281f565b6134ec6020830186613128565b6134f9604083018561281f565b613506606083018461281f565b95945050505050565b6080810161351d8287612868565b61352a60208301866126fb565b6134f960408301856126fb565b60208082528101611eeb8184612871565b60208082528101611eee816128d4565b60208082528101611eee81612902565b60208082528101611eee81612a18565b60208082528101611eee81612a71565b60208082528101611eee81612adb565b60208082528101611eee81612c87565b60208082528101611eee81612d18565b60208082528101611eee81612d5b565b60208082528101611eee81612dff565b60208082528101611eee81612e58565b60208082528101611eee81612f25565b60208082528101611eee81612f5e565b60208082528101611eee81612fd9565b60208082528101611eee81613043565b6080808252810161363981866130a6565b9050613648602083018561281f565b611f9d604083018461271b565b60405181810167ffffffffffffffff8111828210171561367457600080fd5b604052919050565b600067ffffffffffffffff82111561369357600080fd5b5060209081020190565b600067ffffffffffffffff8211156136b457600080fd5b506020601f91909101601f19160190565b90565b60200190565b50600290565b5190565b919050565b90815260200190565b6000611eee82613701565b151590565b6000611eee826136e6565b6001600160a01b031690565b60ff1690565b82818337506000910152565b60005b8381101561373a578181015183820152602001613722565b838111156107455750506000910152565b6000611eee826000611eee82613766565b601f01601f191690565b60601b90565b613775816136e6565b811461378057600080fd5b50565b613775816136f1565b613775816136c5565b613775816136f656fea365627a7a723158205954dbcb54a17d8a58594685ce1fb91fab7c5ef8ceaf900cfe50bbac7ab7b91c6c6578706572696d656e74616cf564736f6c63430005100040

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

000000000000000000000000c955f3c0d5a87710996d13b1f9aa3a77552d7a7e

-----Decoded View---------------
Arg [0] : _registry (address): 0xC955F3c0d5a87710996D13B1f9AA3A77552D7a7E

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000c955f3c0d5a87710996d13b1f9aa3a77552d7a7e


Deployed Bytecode Sourcemap

143370:10122:0:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;143370:10122:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;137703:41;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;137180;;;;;;;;;:::i;143555:42::-;;;;;;;;;:::i;:::-;;;;;;;;133110:1549;;;;;;;;;:::i;138366:230::-;;;;;;;;;:::i;:::-;;30683:98;;;:::i;:::-;;;;;;;;137585:65;;;;;;;;;:::i;137367:41::-;;;;;;;;;:::i;144048:2097::-;;;;;;;;;:::i;2187:382::-;;;;;;;;;:::i;137873:343::-;;;;;;;;;:::i;137703:41::-;;;;;;;;;;;;;;;:::o;137180:::-;;;;;;;;;;;;;;;:::o;143555:42::-;;;;;;;;;;;;;:::o;133110:1549::-;133179:12;132132:842;;;;;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;132132:842:0;;;132122:853;;;;;;133361:6;:18;;;-1:-1:-1;;;;;133353:27:0;133411:6;:15;;;-1:-1:-1;;;;;133403:24:0;133458:6;:12;;;-1:-1:-1;;;;;133450:21:0;133504:6;:19;;;-1:-1:-1;;;;;133496:28:0;133555:6;:19;;;-1:-1:-1;;;;;133547:28:0;133608:6;:20;;;-1:-1:-1;;;;;133600:29:0;133662:6;:21;;;-1:-1:-1;;;;;133654:30:0;133715:6;:23;;;-1:-1:-1;;;;;133707:32:0;133772:6;:22;;;-1:-1:-1;;;;;133764:31:0;133270:544;;;;;;;;;;;;;;;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;133270:544:0;;;133872:6;:14;;;133909:6;:15;;;133947:6;:18;;;133833:151;;;;;;;;;;;;;;;-1:-1:-1;;26:21;;;22:32;6:49;;133833:151:0;;;;134042:13;;;;134078;;;;134114;;;;134150;;;;134186;;;;133833:151;;134003:215;;134186:13;49:4:-1;134003:215:0;;;;;;;-1:-1:-1;;26:21;;;22:32;6:49;;134003:215:0;;;;134276:13;;;;134312;;;;134348;;;;134384;;;;134420;;;;134003:215;;134237;;134420:13;49:4:-1;134237:215:0;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;134237:215:0;;;134510:6;:17;;;134550:6;:19;;;134594:6;:12;;;134471:154;;;;;;;;;;;;;;;-1:-1:-1;;26:21;;;22:32;6:49;;134471:154:0;;;;133235:1405;;;;;;49:4:-1;133235:1405:0;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;133235:1405:0;;;133211:1440;;;;;;133204:1447;;133110:1549;;;:::o;138366:230::-;19346:13;:18;;19363:1;19346:18;;;;;138457:10;-1:-1:-1;138448:20:0;;;:8;:20;;;;;;;;-1:-1:-1;;;;;138448:37:0;;;;;;;;;;;138496:41;;;138448:37;;138548:40;;138448:37;;138548:19;:40::i;:::-;19422:1;19458:13;;19442:12;:29;19434:73;;;;-1:-1:-1;;;19434:73:0;;;;;;;;;;;;;;;;;138366:230;;:::o;30683:98::-;30764:8;;-1:-1:-1;;;;;30764:8:0;30683:98;:::o;137585:65::-;;;;;;;;;;;;;;;;;;;;;;;;:::o;137367:41::-;;;;;;;;;;;;;;;:::o;144048:2097::-;19346:13;:18;;19363:1;19346:18;;;;;144287:23;;144261:22;;-1:-1:-1;;;;;144261:49:0;;;;;;144239:117;;;;-1:-1:-1;;;144239:117:0;;;;;;;;;144430:11;:23;;;-1:-1:-1;;;;;144413:48:0;;:50;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;144413:50:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;144413:50:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;144413:50:0;;;;;;;;;144412:51;144404:82;;;;-1:-1:-1;;;144404:82:0;;;;;;;;;144533:45;144554:10;144566:11;144533:20;:45::i;:::-;144589;144610:11;144623:10;144589:20;:45::i;:::-;144682:33;144704:10;144682:21;:33::i;:::-;144726:34;144748:11;144726:21;:34::i;:::-;144897:29;;:::i;:::-;144954:21;144964:10;144954:9;:21::i;:::-;144937:38;;144986:35;144937:11;144949:1;145006:14;;;;;144986:19;:35::i;:::-;145050:14;;145032:45;;145066:10;145032:17;:45::i;:::-;145107:22;145117:11;145107:9;:22::i;:::-;145090:14;;;:39;145140:35;145090:11;145102:1;145160:14;;145140:35;145204:14;;;;145186:46;;145220:11;145186:17;:46::i;:::-;145372:25;;:::i;:::-;145403:45;145436:11;145403:32;:45::i;:::-;-1:-1:-1;145572:14:0;;;145600;;;145371:77;;-1:-1:-1;145535:16:0;;145554:74;;145572:14;145588:10;;145616:11;145554:17;:74::i;:::-;145535:93;;145696:55;145714:10;145726:11;145739;145696:17;:55::i;:::-;145795:14;;145786:36;;145811:10;145786:8;:36::i;:::-;145842:14;;;;145833:37;;145858:11;145833:8;:37::i;:::-;145915:73;145932:10;145944:11;145957;145970:7;145979:8;145915:16;:73::i;:::-;146035:8;;;;;;;;;-1:-1:-1;;;;;146035:8:0;-1:-1:-1;;;;;146035:16:0;;:18;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;146035:18:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;146035:18:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;146035:18:0;;;;;;;;;146030:107;;;;;;;;146086:23;;;;;-1:-1:-1;;;;;146030:107:0;;;;;146111:24;;;;146030:107;;;;;;;;-1:-1:-1;;;146030:107:0;;:31;;;;;;;:107;;146062:11;;146075:8;;146030:107;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;146030:107:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;146030:107:0;;;;19422:1;;;19458:13;;19442:12;:29;19434:73;;;;-1:-1:-1;;;19434:73:0;;;;;;;;;144048:2097;;;;:::o;2187:382::-;2359:18;;2392:19;;;;;2426:18;;;;;2459:20;;;;2494:17;;;;2526:23;;;;2328:232;;2266:22;;2328:232;;2359:18;;2392:19;2526:23;2328:232;;;137873:343;137958:6;:19;;;-1:-1:-1;;;;;137944:33:0;:10;-1:-1:-1;;;;;137944:33:0;;137979:36;;;;;;;;;;;;;;;;;137936:80;;;;;-1:-1:-1;;;137936:80:0;;;;;;;;;;;138027:17;138047;138057:6;138047:9;:17::i;:::-;138084:19;;;;:8;:19;;;;;;;;;;138105:28;;;;;;;;;;;-1:-1:-1;;;138105:28:0;;;;;;;138027:37;;-1:-1:-1;138105:28:0;138084:19;;138083:20;138075:59;;;;-1:-1:-1;;;138075:59:0;;;;;;;;;;-1:-1:-1;138145:19:0;;;;:8;:19;;;;;;;:26;;-1:-1:-1;;138145:26:0;138167:4;138145:26;;;138189:19;;;;;138154:9;;138189:19;;;;;;;;;;137873:343;;:::o;14788:176::-;14897:58;;14871:85;;14890:5;;-1:-1:-1;;;14920:23:0;14897:58;;14945:2;;14949:5;;14897:58;;;;;;;;-1:-1:-1;;26:21;;;22:32;6:49;;14897:58:0;;;49:4:-1;25:18;;61:17;;-1:-1;;;;;182:15;-1:-1;;;;;;14897:58:0;;;179:29:-1;;;;160:49;;;14871:18:0;:85::i;:::-;14788:176;;;:::o;139113:314::-;139257:23;;;;-1:-1:-1;;;;;139257:37:0;;;:105;;;139338:11;:24;;;-1:-1:-1;;;;;139311:51:0;:10;:23;;;-1:-1:-1;;;;;139311:51:0;;139257:105;139377:31;;;;;;;;;;;;;;;;;139235:184;;;;;-1:-1:-1;;;139235:184:0;;;;;;;;;139582:258;139689:20;;;;-1:-1:-1;;;;;139689:34:0;;;:85;;-1:-1:-1;139740:20:0;;;;-1:-1:-1;;;;;139740:34:0;139764:10;139740:34;139689:85;139789:32;;;;;;;;;;;;;;;;;139667:165;;;;;-1:-1:-1;;;139667:165:0;;;;;;;;;138717:134;138795:15;;;;:8;:15;;;;;;;;;;138812:30;;;;;;;;;;;;;;;;;;;138795:15;;138794:16;138786:57;;;;-1:-1:-1;;;138786:57:0;;;;;;;;;140018:355;140117:19;;;;:8;:19;;;;;;;;140113:58;;;140153:7;;140113:58;140183:11;140197:65;140213:9;140224:6;:16;;;140242:6;:19;;;140197:15;:65::i;:::-;140183:79;;140283:6;140291:34;;;;;;;;;;;;;;;;;140275:51;;;;;-1:-1:-1;;;140275:51:0;;;;;;;;;;-1:-1:-1;;140339:19:0;;;;:8;:19;;;;;:26;;-1:-1:-1;;140339:26:0;140361:4;140339:26;;;140018:355;;:::o;146401:538::-;146491:25;;:::i;:::-;146518:22;146631:30;146649:11;146631:17;:30::i;:::-;146614:47;;146858:8;;;;;;;;;-1:-1:-1;;;;;146858:8:0;-1:-1:-1;;;;;146858:31:0;;:33;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;146858:33:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;146858:33:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;146858:33:0;;;;;;;;;-1:-1:-1;;;;;146838:64:0;;146903:14;146919:11;146838:93;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;146838:93:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;146838:93:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;146838:93:0;;;;;;;;;146812:10;146824;;146811:120;;;146812:7;;146401:538;;-1:-1:-1;146401:538:0:o;147222:1409::-;147382:16;147506:22;;;:6;:22;;;;;;147482:19;;;;147382:16;;147482:47;;:19;:47;:23;:47;:::i;:::-;147540:22;147590:23;;;:6;:23;;;;;;147565:20;;;;147458:71;;-1:-1:-1;147540:22:0;;147565:49;;;:24;:49;:::i;:::-;147540:74;-1:-1:-1;147635:18:0;;;;;:40;;-1:-1:-1;147657:18:0;;;147635:40;147627:71;;;;-1:-1:-1;;;147627:71:0;;;;;;;;;147794:34;147798:13;147813:14;147794:3;:34::i;:::-;147783:45;;147926:10;:22;;;147952:1;147926:27;:59;;;;-1:-1:-1;147957:23:0;;;;:28;147926:59;147922:528;;;148033:11;:20;;;148010:10;:19;;;:43;148002:84;;;;-1:-1:-1;;;148002:84:0;;;;;;;;;147922:528;;;148108:22;;;;:27;:59;;;;;148139:11;:23;;;148166:1;148139:28;148108:59;148104:346;;;148215:14;148192:10;:19;;;:37;;148184:78;;;;-1:-1:-1;;;148184:78:0;;;;;;;;148104:346;148284:10;:22;;;148310:1;148284:27;:59;;;;-1:-1:-1;148315:23:0;;;;:28;148284:59;148280:170;;;148385:11;:20;;;148368:13;:37;;148360:78;;;;-1:-1:-1;;;148360:78:0;;;;;;;;;148513:22;;;;:6;:22;;;;;;:36;;148540:8;148513:36;:26;:36;:::i;:::-;148488:22;;;;:6;:22;;;;;;:61;;;;148586:23;;;;;:37;;148614:8;148586:37;:27;:37;:::i;:::-;148560:23;;;;:6;:23;;;;;;:63;;;;-1:-1:-1;147222:1409:0;;-1:-1:-1;;;;147222:1409:0:o;148821:2794::-;148970:24;:60;;;;;;;;;;;;;;;;;;;149128:10;:18;;;149105:11;:19;;;:41;:100;;;;;149186:11;:19;;;149163:11;:19;;;:42;149105:100;149220:10;149083:158;;;;;-1:-1:-1;;;149083:158:0;;;;;;;;;;;149347:10;:22;;;-1:-1:-1;;;;;149320:49:0;:11;:23;;;-1:-1:-1;;;;;149320:49:0;;:116;;;;;149413:11;:23;;;-1:-1:-1;;;;;149386:50:0;:11;:23;;;-1:-1:-1;;;;;149386:50:0;;149320:116;149451:10;149298:174;;;;;-1:-1:-1;;;149298:174:0;;;;;;;;;;;149572:10;:19;;;-1:-1:-1;;;;;149548:43:0;:11;:20;;;-1:-1:-1;;;;;149548:43:0;;:104;;;;;149632:11;:20;;;-1:-1:-1;;;;;149608:44:0;:11;:20;;;-1:-1:-1;;;;;149608:44:0;;149548:104;149667:10;149526:162;;;;;-1:-1:-1;;;149526:162:0;;;;;;;;;;;149782:10;:16;;;-1:-1:-1;;;;;149761:37:0;:11;:17;;;-1:-1:-1;;;;;149761:37:0;;:92;;;;;149836:11;:17;;;-1:-1:-1;;;;;149815:38:0;:11;:17;;;-1:-1:-1;;;;;149815:38:0;;149761:92;149868:10;149739:150;;;;;-1:-1:-1;;;149739:150:0;;;;;;;;;;;149978:2;149949:11;:18;;;:25;:31;;149941:83;;;;-1:-1:-1;;;149941:83:0;;;;;;;;;150105:11;:18;;;150124:1;150105:21;;;;;;;;;;;;;;150084:10;:17;;;:42;150128:10;150076:63;;;;;-1:-1:-1;;;150076:63:0;;;;;;;;;;;150179:11;:18;;;150198:1;150179:21;;;;;;;;;;;;;;150158:10;:17;;;:42;150202:10;150150:63;;;;;-1:-1:-1;;;150150:63:0;;;;;;;;;;;150253:11;:18;;;150272:1;150253:21;;;;;;;;;;;;;;150232:10;:17;;;:42;150276:10;150224:63;;;;;-1:-1:-1;;;150224:63:0;;;;;;;;;;;150327:11;:18;;;150346:1;150327:21;;;;;;;;;;;;;;150306:10;:17;;;:42;150350:10;150298:63;;;;;-1:-1:-1;;;150298:63:0;;;;;;;;;;;150401:11;:18;;;150420:1;150401:21;;;;;;;;;;;;;;150380:10;:17;;;:42;150424:10;150372:63;;;;;-1:-1:-1;;;150372:63:0;;;;;;;;;;;150475:11;:18;;;150494:1;150475:21;;;;;;;;;;;;;;150454:10;:17;;;:42;150498:10;150446:63;;;;;-1:-1:-1;;;150446:63:0;;;;;;;;;;;150549:11;:18;;;150568:1;150549:21;;;;;;;;;;;;;;150528:10;:17;;;:42;150572:10;150520:63;;;;;-1:-1:-1;;;150520:63:0;;;;;;;;;;;150623:11;:18;;;150642:1;150623:21;;;;;;;;;;;;;;150602:10;:17;;;:42;150646:10;150594:63;;;;;-1:-1:-1;;;150594:63:0;;;;;;;;;;;150697:11;:18;;;150716:1;150697:21;;;;;;;;;;;;;;150676:10;:17;;;:42;150720:10;150668:63;;;;;-1:-1:-1;;;150668:63:0;;;;;;;;;;;150771:11;:18;;;150790:1;150771:21;;;;;;;;;;;;;;150750:10;:17;;;:42;150794:10;150742:63;;;;;-1:-1:-1;;;150742:63:0;;;;;;;;;;;150888:11;:18;;;150907:2;150888:22;;;;;;;;;;;;;;150866:11;:18;;;:44;150912:10;150858:65;;;;;-1:-1:-1;;;150858:65:0;;;;;;;;;;;150964:11;:18;;;150983:2;150964:22;;;;;;;;;;;;;;150942:11;:18;;;:44;150988:10;150934:65;;;;;-1:-1:-1;;;150934:65:0;;;;;;;;;;;151040:11;:18;;;151059:2;151040:22;;;;;;;;;;;;;;151018:11;:18;;;:44;151064:10;151010:65;;;;;-1:-1:-1;;;151010:65:0;;;;;;;;;;;151116:11;:18;;;151135:2;151116:22;;;;;;;;;;;;;;151094:11;:18;;;:44;151140:10;151086:65;;;;;-1:-1:-1;;;151086:65:0;;;;;;;;;;;151192:11;:18;;;151211:2;151192:22;;;;;;;;;;;;;;151170:11;:18;;;:44;151216:10;151162:65;;;;;-1:-1:-1;;;151162:65:0;;;;;;;;;;;151268:11;:18;;;151287:2;151268:22;;;;;;;;;;;;;;151246:11;:18;;;:44;151292:10;151238:65;;;;;-1:-1:-1;;;151238:65:0;;;;;;;;;;;151344:11;:18;;;151363:2;151344:22;;;;;;;;;;;;;;151322:11;:18;;;:44;151368:10;151314:65;;;;;-1:-1:-1;;;151314:65:0;;;;;;;;;;;151420:11;:18;;;151439:2;151420:22;;;;;;;;;;;;;;151398:11;:18;;;:44;151444:10;151390:65;;;;;-1:-1:-1;;;151390:65:0;;;;;;;;;;;151496:11;:18;;;151515:2;151496:22;;;;;;;;;;;;;;151474:11;:18;;;:44;151520:10;151466:65;;;;;-1:-1:-1;;;151466:65:0;;;;;;;;;;;151572:11;:18;;;151591:2;151572:22;;;;;;;;;;;;;;151550:11;:18;;;:44;151596:10;151542:65;;;;;-1:-1:-1;;;151542:65:0;;;;;;;;;;;148821:2794;;;;:::o;140592:2228::-;140726:20;;;;:8;:20;;;;;;;;140722:59;;;140763:7;;140722:59;140861:22;;;;-1:-1:-1;;;;;140861:36:0;140857:75;;140914:7;;140857:75;141010:12;141025:42;141047:6;:19;;;141025:6;:17;;;:21;;:42;;;;:::i;:::-;141010:57;-1:-1:-1;141132:9:0;141128:48;;141158:7;;;141128:48;141254:22;;;;141375:8;;:26;;;-1:-1:-1;;;141375:26:0;;;;141229:15;;-1:-1:-1;;;;;141375:8:0;;:24;;:26;;;;;;;;;;;;;;:8;:26;;;5:2:-1;;;;30:1;27;20:12;5:2;141375:26:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;141375:26:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;141375:26:0;;;;;;;;;141334:68;;141557:4;141491:8;-1:-1:-1;;;;;141491:18:0;;141510:6;:19;;;141539:12;141491:62;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;141491:62:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;141491:62:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;141491:62:0;;;;;;;;;:70;;141563:35;;;;;;;;;;;;;;;;;141483:116;;;;;-1:-1:-1;;;141483:116:0;;;;;;;;;;-1:-1:-1;141670:19:0;;;;141635:76;;-1:-1:-1;;;141635:76:0;;-1:-1:-1;;;;;141635:24:0;;;;;:76;;141660:8;;141699:4;;141706;;141635:76;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;141635:76:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;141635:76:0;;;;141754:20;141777:8;;;;;;;;;-1:-1:-1;;;;;141777:8:0;-1:-1:-1;;;;;141777:24:0;;:26;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;141777:26:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;141777:26:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;141777:26:0;;;;;;;;;141909:21;;;;141754:49;;-1:-1:-1;;;;;;141909:35:0;;141905:355;;142079:17;;;;142028:21;;;;-1:-1:-1;;;;;142019:31:0;;;;;;;:8;:31;;;;;;;;142051:22;;;;142019:55;;;;;;;;;;:78;;;:59;:78;:::i;:::-;141970:21;;;;-1:-1:-1;;;;;141961:31:0;;;;;;;:8;:31;;;;;;;;141993:22;;;;141961:55;;;;;;;;;:136;141905:355;;;142230:17;;;;-1:-1:-1;;;;;142179:22:0;;;;;;;:8;:22;;;;;;;;142202;;;;142179:46;;;;;;;;;;:69;;;:50;:69;:::i;:::-;-1:-1:-1;;;;;142130:22:0;;;;;;;:8;:22;;;;;;;;142153;;;;142130:46;;;;;;;;;:118;141905:355;142369:23;;;;-1:-1:-1;;;;;142369:37:0;;142365:365;;142545:19;;;;142492:23;;;;-1:-1:-1;;;;;142483:33:0;;;;;;;:8;:33;;;;;;;;142517:22;;;;142483:57;;;;;;;;;;:82;;;:61;:82;:::i;:::-;142432:23;;;;-1:-1:-1;;;;;142423:33:0;;;;;;;:8;:33;;;;;;;;142457:22;;;;142423:57;;;;;;;;;:142;142365:365;;;142698:19;;;;-1:-1:-1;;;;;142647:22:0;;;;;;;:8;:22;;;;;;;;142670;;;;142647:46;;;;;;;;;;:71;;;:50;:71;:::i;:::-;-1:-1:-1;;;;;142598:22:0;;;;;;;:8;:22;;;;;;;;142621;;;;142598:46;;;;;;;;;:120;142365:365;-1:-1:-1;;;142785:20:0;;;;:8;:20;;;;;:27;;-1:-1:-1;;142785:27:0;142808:4;142785:27;;;-1:-1:-1;140592:2228:0;;:::o;151911:1578::-;152128:17;;;;152198:8;;:26;;;-1:-1:-1;;;152198:26:0;;;;152100:18;;-1:-1:-1;;;;;152198:8:0;;:24;;:26;;;;;;;;;;;;;;:8;:26;;;5:2:-1;;;;30:1;27;20:12;5:2;152198:26:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;152198:26:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;152198:26:0;;;;;;;;;152306:10;;152157:68;;-1:-1:-1;152306:15:0;152302:408;;152463:25;152478:9;152463:7;152471:1;152463:10;;;;;;:25;:14;:25;:::i;:::-;152412:23;;;;152390:69;;-1:-1:-1;;;152390:69:0;;-1:-1:-1;;;;;152390:21:0;;;;;:69;;152412:23;152445:12;;152390:69;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;152390:69:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;152390:69:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;152390:69:0;;;;;;;;;:98;;152382:142;;;;-1:-1:-1;;;152382:142:0;;;;;;;;;152594:12;-1:-1:-1;;;;;152594:24:0;;152619:11;152632:10;:23;;;152665:4;152672:25;152687:9;152672:7;152680:1;152672:10;;;;;;:25;152594:104;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;152594:104:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;152594:104:0;;;;152302:408;152791:10;;;;:15;152787:421;;152959:25;152974:9;152959:7;152967:1;152959:10;;:25;152907:24;;;;152885:70;;-1:-1:-1;;;152885:70:0;;-1:-1:-1;;;;;152885:21:0;;;;;:70;;152907:24;152941:12;;152885:70;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;152885:70:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;152885:70:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;152885:70:0;;;;;;;;;:99;;152877:143;;;;-1:-1:-1;;;152877:143:0;;;;;;;;;153091:12;-1:-1:-1;;;;;153091:24:0;;153116:11;153129;:24;;;153163:4;153170:25;153185:9;153170:7;153178:1;153170:10;;;;;;:25;153091:105;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;153091:105:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;153091:105:0;;;;152787:421;153239:10;;;;153224:26;;153239:7;153232:1;153224:10;;;;;;:26;:14;:26;:::i;:::-;:31;153220:262;;-1:-1:-1;;;;;153343:19:0;;;153371:12;153386:41;153417:9;153386:26;153401:7;153409:1;153401:10;;;;153386:7;153394:1;153386:10;;:26;:30;:41;:30;:41;:::i;:::-;153343:85;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;153343:85:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;153343:85:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;153343:85:0;;;;;;;;;153335:135;;;;-1:-1:-1;;;153335:135:0;;;;;;;;;151911:1578;;;;;;;:::o;16827:1114::-;17431:27;17439:5;-1:-1:-1;;;;;17431:25:0;;:27::i;:::-;17423:71;;;;-1:-1:-1;;;17423:71:0;;;;;;;;;17568:12;17582:23;17617:5;-1:-1:-1;;;;;17609:19:0;17629:4;17609:25;;;;;;;;;;;;;;;;;;;;;;;14:1:-1;21;16:31;;;;75:4;69:11;64:16;;144:4;140:9;133:4;115:16;111:27;107:43;104:1;100:51;94:4;87:65;169:16;166:1;159:27;225:16;222:1;215:4;212:1;208:12;193:49;7:242;;16:31;36:4;31:9;;7:242;;17567:67:0;;;;17653:7;17645:52;;;;-1:-1:-1;;;17645:52:0;;;;;;;;;17714:17;;:21;17710:224;;17856:10;17845:30;;;;;;;;;;;;;;17837:85;;;;-1:-1:-1;;;17837:85:0;;;;;;;;134964:356;135070:4;135095:10;:17;135116:2;135095:23;135087:66;;;;-1:-1:-1;;;135087:66:0;;;;;;;;;135166:14;135183:24;135201:5;135183:17;:24::i;:::-;135166:41;;135218:17;135238:35;135254:6;135262:10;135238:15;:35::i;:::-;-1:-1:-1;;;;;135291:21:0;;;;;;;-1:-1:-1;;;134964:356:0;;;;;;:::o;6841:136::-;6899:7;6926:43;6930:1;6933;6926:43;;;;;;;;;;;;;;;;;:3;:43::i;:::-;6919:50;;6841:136;;;;;:::o;143007:112::-;143067:7;143099:2;143094;:7;:17;;143109:2;143094:17;;;-1:-1:-1;143104:2:0;;143007:112;-1:-1:-1;143007:112:0:o;6385:181::-;6443:7;6475:5;;;6499:6;;;;6491:46;;;;-1:-1:-1;;;6491:46:0;;;;;;;;7757:471;7815:7;8060:6;8056:47;;-1:-1:-1;8090:1:0;8083:8;;8056:47;8127:5;;;8131:1;8127;:5;:1;8151:5;;;;;:10;8143:56;;;;-1:-1:-1;;;8143:56:0;;;;;;;;11618:810;11678:4;12337:20;;12180:66;12377:15;;;;;:42;;;12408:11;12396:8;:23;;12377:42;12369:51;11618:810;-1:-1:-1;;;;11618:810:0:o;128733:707::-;128803:14;128856:16;128970:2;128964:9;-1:-1:-1;;;128989:82:0;;129122:1;129110:14;;129103:39;;;;129240:2;129228:15;;129221:35;;;;129395:2;129377:21;;;128733:707::o;135542:1001::-;136007:2;135991:19;;135985:26;136052:2;136036:19;;136030:26;136105:2;136089:19;;136083:26;135629:7;;135985:26;136030;136075:35;;136233:2;136229:6;;136225:46;;;136257:2;136252:7;136225:46;136351:1;:7;;136356:2;136351:7;;:18;;;;;136362:1;:7;;136367:2;136362:7;;136351:18;136347:189;;;136402:1;136386:19;;;;;;;136347:189;136499:25;136509:5;136516:1;136519;136522;136499:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;136499:25:0;;;;;;;;136492:32;;;;;;;136347:189;135542:1001;;;;;;;:::o;7314:192::-;7400:7;7436:12;7428:6;;;;7420:29;;;;-1:-1:-1;;;7420:29:0;;;;;;;;;;-1:-1:-1;;;7472:5:0;;;7314:192::o;143370:10122::-;;;;;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;-1:-1;143370:10122:0;;;-1:-1:-1;;143370:10122:0:o;5:130:-1:-;72:20;;97:33;72:20;97:33;;142:134;220:13;;238:33;220:13;238:33;;301:699;;414:3;407:4;399:6;395:17;391:27;381:2;;432:1;429;422:12;381:2;469:6;456:20;491:76;506:60;559:6;506:60;;;491:76;;;482:85;;584:5;609:6;602:5;595:21;639:4;631:6;627:17;617:27;;661:4;656:3;652:14;645:21;;714:6;761:3;753:4;745:6;741:17;736:3;732:27;729:36;726:2;;;778:1;775;768:12;726:2;803:1;788:206;813:6;810:1;807:13;788:206;;;871:3;893:37;926:3;914:10;893:37;;;881:50;;-1:-1;954:4;945:14;;;;973;;;;;835:1;828:9;788:206;;;792:14;374:626;;;;;;;;1008:128;1083:13;;1101:30;1083:13;1101:30;;1143:130;1210:20;;1235:33;1210:20;1235:33;;1281:432;;1378:3;1371:4;1363:6;1359:17;1355:27;1345:2;;1396:1;1393;1386:12;1345:2;1433:6;1420:20;1455:60;1470:44;1507:6;1470:44;;1455:60;1446:69;;1535:6;1528:5;1521:21;1571:4;1563:6;1559:17;1604:4;1597:5;1593:16;1639:3;1630:6;1625:3;1621:16;1618:25;1615:2;;;1656:1;1653;1646:12;1615:2;1666:41;1700:6;1695:3;1690;1666:41;;1721:158;1802:20;;1827:47;1802:20;1827:47;;1924:1172;;2039:4;2027:9;2022:3;2018:19;2014:30;2011:2;;;2057:1;2054;2047:12;2011:2;2075:20;2090:4;2075:20;;;2066:29;-1:-1;2147:1;2179:49;2224:3;2204:9;2179:49;;;2154:75;;-1:-1;2293:2;2326:49;2371:3;2347:22;;;2326:49;;;2319:4;2312:5;2308:16;2301:75;2250:137;2467:2;2456:9;2452:18;2439:32;2491:18;2483:6;2480:30;2477:2;;;2523:1;2520;2513:12;2477:2;2558:70;2624:3;2615:6;2604:9;2600:22;2558:70;;;2551:4;2544:5;2540:16;2533:96;2397:243;2694:2;2727:49;2772:3;2763:6;2752:9;2748:22;2727:49;;;2720:4;2713:5;2709:16;2702:75;2650:138;2839:3;2873:49;2918:3;2909:6;2898:9;2894:22;2873:49;;;2866:4;2859:5;2855:16;2848:75;2798:136;2991:3;3025:49;3070:3;3061:6;3050:9;3046:22;3025:49;;;3018:4;3011:5;3007:16;3000:75;2944:142;2005:1091;;;;;3147:4206;;3267:6;3255:9;3250:3;3246:19;3242:32;3239:2;;;3287:1;3284;3277:12;3239:2;3305:22;3320:6;3305:22;;;3296:31;-1:-1;3384:1;3416:49;3461:3;3441:9;3416:49;;;3391:75;;-1:-1;3531:2;3564:49;3609:3;3585:22;;;3564:49;;;3557:4;3550:5;3546:16;3539:75;3487:138;3676:2;3709:49;3754:3;3745:6;3734:9;3730:22;3709:49;;;3702:4;3695:5;3691:16;3684:75;3635:135;3828:2;3861:49;3906:3;3897:6;3886:9;3882:22;3861:49;;;3854:4;3847:5;3843:16;3836:75;3780:142;3980:3;4014:49;4059:3;4050:6;4039:9;4035:22;4014:49;;;4007:4;4000:5;3996:16;3989:75;3932:143;4134:3;4168:49;4213:3;4204:6;4193:9;4189:22;4168:49;;;4161:4;4154:5;4150:16;4143:75;4085:144;4289:3;4323:49;4368:3;4359:6;4348:9;4344:22;4323:49;;;4316:4;4309:5;4305:16;4298:75;4239:145;4446:3;4480:49;4525:3;4516:6;4505:9;4501:22;4480:49;;;4473:4;4466:5;4462:16;4455:75;4394:147;4602:3;4638:49;4683:3;4674:6;4663:9;4659:22;4638:49;;;4629:6;4622:5;4618:18;4611:77;4551:148;4752:3;4788:49;4833:3;4824:6;4813:9;4809:22;4788:49;;;4779:6;4772:5;4768:18;4761:77;4709:140;4903:3;4939:49;4984:3;4975:6;4964:9;4960:22;4939:49;;;4930:6;4923:5;4919:18;4912:77;4859:141;5057:3;5093:49;5138:3;5129:6;5118:9;5114:22;5093:49;;;5084:6;5077:5;5073:18;5066:77;5010:144;5206:3;5242:49;5287:3;5278:6;5267:9;5263:22;5242:49;;;5233:6;5226:5;5222:18;5215:77;5164:139;5355:3;5391:49;5436:3;5427:6;5416:9;5412:22;5391:49;;;5382:6;5375:5;5371:18;5364:77;5313:139;5504:3;5540:49;5585:3;5576:6;5565:9;5561:22;5540:49;;;5531:6;5524:5;5520:18;5513:77;5462:139;5653:3;5689:49;5734:3;5725:6;5714:9;5710:22;5689:49;;;5680:6;5673:5;5669:18;5662:77;5611:139;5802:3;5838:49;5883:3;5874:6;5863:9;5859:22;5838:49;;;5829:6;5822:5;5818:18;5811:77;5760:139;5951:3;5987:49;6032:3;6023:6;6012:9;6008:22;5987:49;;;5978:6;5971:5;5967:18;5960:77;5909:139;6100:3;6136:49;6181:3;6172:6;6161:9;6157:22;6136:49;;;6127:6;6120:5;6116:18;6109:77;6058:139;6249:3;6285:49;6330:3;6321:6;6310:9;6306:22;6285:49;;;6276:6;6269:5;6265:18;6258:77;6207:139;6398:3;6434:49;6479:3;6470:6;6459:9;6455:22;6434:49;;;6425:6;6418:5;6414:18;6407:77;6356:139;6547:3;6583:49;6628:3;6619:6;6608:9;6604:22;6583:49;;;6574:6;6567:5;6563:18;6556:77;6505:139;6700:3;6736:49;6781:3;6772:6;6761:9;6757:22;6736:49;;;6727:6;6720:5;6716:18;6709:77;6654:143;6855:3;6891:49;6936:3;6927:6;6916:9;6912:22;6891:49;;;6882:6;6875:5;6871:18;6864:77;6807:145;7003:3;7039:49;7084:3;7075:6;7064:9;7060:22;7039:49;;;7030:6;7023:5;7019:18;7012:77;6962:138;7183:3;7172:9;7168:19;7155:33;7208:18;7200:6;7197:30;7194:2;;;7240:1;7237;7230:12;7194:2;7277:54;7327:3;7318:6;7307:9;7303:22;7277:54;;;7268:6;7261:5;7257:18;7250:82;7110:233;3233:4120;;;;;7497:134;7575:13;;7593:33;7575:13;7593:33;;7638:263;;7753:2;7741:9;7732:7;7728:23;7724:32;7721:2;;;7769:1;7766;7759:12;7721:2;7804:1;7821:64;7877:7;7857:9;7821:64;;7908:366;;;8029:2;8017:9;8008:7;8004:23;8000:32;7997:2;;;8045:1;8042;8035:12;7997:2;8080:1;8097:53;8142:7;8122:9;8097:53;;;8087:63;;8059:97;8187:2;8205:53;8250:7;8241:6;8230:9;8226:22;8205:53;;;8195:63;;8166:98;7991:283;;;;;;8281:257;;8393:2;8381:9;8372:7;8368:23;8364:32;8361:2;;;8409:1;8406;8399:12;8361:2;8444:1;8461:61;8514:7;8494:9;8461:61;;8545:241;;8649:2;8637:9;8628:7;8624:23;8620:32;8617:2;;;8665:1;8662;8655:12;8617:2;8700:1;8717:53;8762:7;8742:9;8717:53;;8793:269;;8911:2;8899:9;8890:7;8886:23;8882:32;8879:2;;;8927:1;8924;8917:12;8879:2;8962:1;8979:67;9038:7;9018:9;8979:67;;9069:379;;9199:2;9187:9;9178:7;9174:23;9170:32;9167:2;;;9215:1;9212;9205:12;9167:2;9250:31;;9301:18;9290:30;;9287:2;;;9333:1;9330;9323:12;9287:2;9353:79;9424:7;9415:6;9404:9;9400:22;9353:79;;9455:389;;9590:2;9578:9;9569:7;9565:23;9561:32;9558:2;;;9606:1;9603;9596:12;9558:2;9641:31;;9692:18;9681:30;;9678:2;;;9724:1;9721;9714:12;9678:2;9744:84;9820:7;9811:6;9800:9;9796:22;9744:84;;9851:925;;;;10077:2;10065:9;10056:7;10052:23;10048:32;10045:2;;;10093:1;10090;10083:12;10045:2;10128:31;;10179:18;10168:30;;10165:2;;;10211:1;10208;10201:12;10165:2;10231:84;10307:7;10298:6;10287:9;10283:22;10231:84;;;10221:94;;10107:214;10380:2;10369:9;10365:18;10352:32;10404:18;10396:6;10393:30;10390:2;;;10436:1;10433;10426:12;10390:2;10456:84;10532:7;10523:6;10512:9;10508:22;10456:84;;;10446:94;;10331:215;10605:2;10594:9;10590:18;10577:32;10629:18;10621:6;10618:30;10615:2;;;10661:1;10658;10651:12;10615:2;10681:79;10752:7;10743:6;10732:9;10728:22;10681:79;;;10671:89;;10556:210;10039:737;;;;;;10783:263;;10898:2;10886:9;10877:7;10873:23;10869:32;10866:2;;;10914:1;10911;10904:12;10866:2;10949:1;10966:64;11022:7;11002:9;10966:64;;11053:399;;;11185:2;11173:9;11164:7;11160:23;11156:32;11153:2;;;11201:1;11198;11191:12;11153:2;11236:1;11253:64;11309:7;11289:9;11253:64;;;11243:74;;11215:108;11354:2;11372:64;11428:7;11419:6;11408:9;11404:22;11372:64;;11460:173;;11547:46;11589:3;11581:6;11547:46;;;-1:-1;;11622:4;11613:14;;11540:93;11642:173;;11729:46;11771:3;11763:6;11729:46;;12021:103;12094:24;12112:5;12094:24;;;12089:3;12082:37;12076:48;;;12251:152;12352:45;12372:24;12390:5;12372:24;;;12352:45;;12443:660;12576:52;12622:5;12576:52;;;12641:84;12718:6;12713:3;12641:84;;;12634:91;;12746:54;12794:5;12746:54;;;12820:7;12848:1;12833:258;12858:6;12855:1;12852:13;12833:258;;;12925:6;12919:13;12946:63;13005:3;12990:13;12946:63;;;12939:70;;13026:58;13077:6;13026:58;;;13016:68;-1:-1;;12880:1;12873:9;12833:258;;;12837:14;12555:548;;;;;;13142:654;;13273:50;13317:5;13273:50;;;13336:76;13405:6;13400:3;13336:76;;;13329:83;;13433:52;13479:5;13433:52;;;13505:7;13533:1;13518:256;13543:6;13540:1;13537:13;13518:256;;;13610:6;13604:13;13631:63;13690:3;13675:13;13631:63;;;13624:70;;13711:56;13760:6;13711:56;;;13701:66;-1:-1;;13565:1;13558:9;13518:256;;;-1:-1;13787:3;;13252:544;-1:-1;;;;;13252:544;13835:718;;13994:50;14038:5;13994:50;;;14057:104;14154:6;14149:3;14057:104;;;14050:111;;14182:52;14228:5;14182:52;;;14254:7;14282:1;14267:264;14292:6;14289:1;14286:13;14267:264;;;14359:6;14353:13;14380:71;14447:3;14432:13;14380:71;;;14373:78;;14468:56;14517:6;14468:56;;;14458:66;-1:-1;;14314:1;14307:9;14267:264;;14561:104;14638:21;14653:5;14638:21;;14672:113;14755:24;14773:5;14755:24;;14792:152;14893:45;14913:24;14931:5;14913:24;;;14893:45;;14951:356;;15079:38;15111:5;15079:38;;;15129:88;15210:6;15205:3;15129:88;;;15122:95;;15222:52;15267:6;15262:3;15255:4;15248:5;15244:16;15222:52;;;15286:16;;;;;15059:248;-1:-1;;15059:248;15314:154;15411:51;15456:5;15411:51;;15475:347;;15587:39;15620:5;15587:39;;;15638:71;15702:6;15697:3;15638:71;;;15631:78;;15714:52;15759:6;15754:3;15747:4;15740:5;15736:16;15714:52;;;15787:29;15809:6;15787:29;;;15778:39;;;;15567:255;-1:-1;;;15567:255;16176:351;;16354:85;16436:2;16431:3;16354:85;;;-1:-1;;;16452:38;;16518:2;16509:12;;16340:187;-1:-1;;16340:187;16536:318;;16696:67;16760:2;16755:3;16696:67;;;-1:-1;;;16776:41;;16845:2;16836:12;;16682:172;-1:-1;;16682:172;16863:330;;17023:67;17087:2;17082:3;17023:67;;;17123:32;17103:53;;17184:2;17175:12;;17009:184;-1:-1;;17009:184;17202:353;;17380:85;17462:2;17457:3;17380:85;;;-1:-1;;;17478:40;;17546:2;17537:12;;17366:189;-1:-1;;17366:189;17564:358;;17742:85;17824:2;17819:3;17742:85;;;-1:-1;;;17840:45;;17913:2;17904:12;;17728:194;-1:-1;;17728:194;17931:351;;18109:85;18191:2;18186:3;18109:85;;;-1:-1;;;18207:38;;18273:2;18264:12;;18095:187;-1:-1;;18095:187;18291:357;;18469:85;18551:2;18546:3;18469:85;;;-1:-1;;;18567:44;;18639:2;18630:12;;18455:193;-1:-1;;18455:193;18657:340;;18835:84;18917:1;18912:3;18835:84;;;-1:-1;;;18932:29;;18989:1;18980:11;;18821:176;-1:-1;;18821:176;19006:318;;19166:67;19230:2;19225:3;19166:67;;;-1:-1;;;19246:41;;19315:2;19306:12;;19152:172;-1:-1;;19152:172;19333:351;;19511:85;19593:2;19588:3;19511:85;;;-1:-1;;;19609:38;;19675:2;19666:12;;19497:187;-1:-1;;19497:187;19693:327;;19853:67;19917:2;19912:3;19853:67;;;19953:29;19933:50;;20011:2;20002:12;;19839:181;-1:-1;;19839:181;20029:357;;20207:85;20289:2;20284:3;20207:85;;;-1:-1;;;20305:44;;20377:2;20368:12;;20193:193;-1:-1;;20193:193;20395:332;;20555:67;20619:2;20614:3;20555:67;;;20655:34;20635:55;;20718:2;20709:12;;20541:186;-1:-1;;20541:186;20736:349;;20914:85;20996:2;20991:3;20914:85;;;-1:-1;;;21012:36;;21076:2;21067:12;;20900:185;-1:-1;;20900:185;21094:335;;21272:84;21354:1;21349:3;21272:84;;;-1:-1;;;21369:24;;21421:1;21412:11;;21258:171;-1:-1;;21258:171;21438:360;;21616:85;21698:2;21693:3;21616:85;;;21734:26;21714:47;;21789:2;21780:12;;21602:196;-1:-1;;21602:196;21807:351;;21985:85;22067:2;22062:3;21985:85;;;-1:-1;;;22083:38;;22149:2;22140:12;;21971:187;-1:-1;;21971:187;22167:352;;22345:85;22427:2;22422:3;22345:85;;;-1:-1;;;22443:39;;22510:2;22501:12;;22331:188;-1:-1;;22331:188;22528:351;;22706:85;22788:2;22783:3;22706:85;;;-1:-1;;;22804:38;;22870:2;22861:12;;22692:187;-1:-1;;22692:187;22888:359;;23066:85;23148:2;23143:3;23066:85;;;23184:25;23164:46;;23238:2;23229:12;;23052:195;-1:-1;;23052:195;23256:361;;23434:85;23516:2;23511:3;23434:85;;;23552:27;23532:48;;23608:2;23599:12;;23420:197;-1:-1;;23420:197;23626:331;;23786:67;23850:2;23845:3;23786:67;;;23886:33;23866:54;;23948:2;23939:12;;23772:185;-1:-1;;23772:185;23966:351;;24144:85;24226:2;24221:3;24144:85;;;-1:-1;;;24242:38;;24308:2;24299:12;;24130:187;-1:-1;;24130:187;24326:353;;24504:85;24586:2;24581:3;24504:85;;;-1:-1;;;24602:40;;24670:2;24661:12;;24490:189;-1:-1;;24490:189;24688:370;;24848:67;24912:2;24907:3;24848:67;;;24948:34;24928:55;;-1:-1;;;25012:2;25003:12;;24996:25;25049:2;25040:12;;24834:224;-1:-1;;24834:224;25067:376;;25227:67;25291:2;25286:3;25227:67;;;25327:34;25307:55;;-1:-1;;;25391:2;25382:12;;25375:31;25434:2;25425:12;;25213:230;-1:-1;;25213:230;25452:351;;25630:85;25712:2;25707:3;25630:85;;;-1:-1;;;25728:38;;25794:2;25785:12;;25616:187;-1:-1;;25616:187;25812:356;;25990:85;26072:2;26067:3;25990:85;;;-1:-1;;;26088:43;;26159:2;26150:12;;25976:192;-1:-1;;25976:192;26177:318;;26337:67;26401:2;26396:3;26337:67;;;-1:-1;;;26417:41;;26486:2;26477:12;;26323:172;-1:-1;;26323:172;26504:351;;26682:85;26764:2;26759:3;26682:85;;;-1:-1;;;26780:38;;26846:2;26837:12;;26668:187;-1:-1;;26668:187;26864:374;;27024:67;27088:2;27083:3;27024:67;;;27124:34;27104:55;;-1:-1;;;27188:2;27179:12;;27172:29;27229:2;27220:12;;27010:228;-1:-1;;27010:228;27247:351;;27425:85;27507:2;27502:3;27425:85;;;-1:-1;;;27523:38;;27589:2;27580:12;;27411:187;-1:-1;;27411:187;27607:356;;27785:85;27867:2;27862:3;27785:85;;;-1:-1;;;27883:43;;27954:2;27945:12;;27771:192;-1:-1;;27771:192;27972:351;;28150:85;28232:2;28227:3;28150:85;;;-1:-1;;;28248:38;;28314:2;28305:12;;28136:187;-1:-1;;28136:187;28332:328;;28492:67;28556:2;28551:3;28492:67;;;28592:30;28572:51;;28651:2;28642:12;;28478:182;-1:-1;;28478:182;28669:379;;28829:67;28893:2;28888:3;28829:67;;;28929:34;28909:55;;-1:-1;;;28993:2;28984:12;;28977:34;29039:2;29030:12;;28815:233;-1:-1;;28815:233;29057:355;;29235:85;29317:2;29312:3;29235:85;;;-1:-1;;;29333:42;;29403:2;29394:12;;29221:191;-1:-1;;29221:191;29421:331;;29581:67;29645:2;29640:3;29581:67;;;29681:33;29661:54;;29743:2;29734:12;;29567:185;-1:-1;;29567:185;29761:357;;29939:85;30021:2;30016:3;29939:85;;;-1:-1;;;30037:44;;30109:2;30100:12;;29925:193;-1:-1;;29925:193;30127:331;;30287:67;30351:2;30346:3;30287:67;;;30387:33;30367:54;;30449:2;30440:12;;30273:185;-1:-1;;30273:185;30467:350;;30645:85;30727:2;30722:3;30645:85;;;-1:-1;;;30743:37;;30808:2;30799:12;;30631:186;-1:-1;;30631:186;30898:1234;31119:23;;30898:1234;;31051:4;31042:14;;;31148:63;31046:3;31119:23;31148:63;;;31071:146;31293:4;31286:5;31282:16;31276:23;31305:63;31362:4;31357:3;31353:14;31339:12;31305:63;;;31227:147;31449:4;31442:5;31438:16;31432:23;31501:3;31495:4;31491:14;31484:4;31479:3;31475:14;31468:38;31521:99;31615:4;31601:12;31521:99;;;31513:107;;31384:248;31709:4;31702:5;31698:16;31692:23;31721:63;31778:4;31773:3;31769:14;31755:12;31721:63;;;31642:148;31864:4;31857:5;31853:16;31847:23;31876:63;31933:4;31928:3;31924:14;31910:12;31876:63;;;31800:145;32025:4;32018:5;32014:16;32008:23;32037:63;32094:4;32089:3;32085:14;32071:12;32037:63;;;-1:-1;32123:4;31024:1108;-1:-1;;;31024:1108;32646:107;32725:22;32741:5;32725:22;;32760:1495;;33131:75;33202:3;33193:6;33131:75;;;33228:2;33223:3;33219:12;33212:19;;33242:75;33313:3;33304:6;33242:75;;;33339:2;33334:3;33330:12;33323:19;;33353:75;33424:3;33415:6;33353:75;;;33450:2;33445:3;33441:12;33434:19;;33464:75;33535:3;33526:6;33464:75;;;33561:2;33556:3;33552:12;33545:19;;33575:75;33646:3;33637:6;33575:75;;;33672:2;33667:3;33663:12;33656:19;;33686:75;33757:3;33748:6;33686:75;;;33783:2;33778:3;33774:12;33767:19;;33797:75;33868:3;33859:6;33797:75;;;33894:2;33889:3;33885:12;33878:19;;33908:75;33979:3;33970:6;33908:75;;;34005:2;34000:3;33996:12;33989:19;;34019:75;34090:3;34081:6;34019:75;;;34116:2;34111:3;34107:12;34100:19;;34130:75;34201:3;34192:6;34130:75;;;-1:-1;34227:2;34218:12;;33119:1136;-1:-1;;;;;;;;;;33119:1136;34262:262;;34406:93;34495:3;34486:6;34406:93;;34531:890;;34859:93;34948:3;34939:6;34859:93;;;34852:100;;34970:93;35059:3;35050:6;34970:93;;;34963:100;;35081:93;35170:3;35161:6;35081:93;;;35074:100;;35192:93;35281:3;35272:6;35192:93;;;35185:100;;35303:93;35392:3;35383:6;35303:93;;;35296:100;34840:581;-1:-1;;;;;;;34840:581;35428:7314;;38253:148;38397:3;38253:148;;;38246:155;;38419:148;38563:3;38419:148;;;38412:155;;38585:148;38729:3;38585:148;;;38578:155;;38751:148;38895:3;38751:148;;;38744:155;;38917:148;39061:3;38917:148;;;38910:155;;39083:148;39227:3;39083:148;;;39076:155;;39249:148;39393:3;39249:148;;;39242:155;;39415:148;39559:3;39415:148;;;39408:155;;39581:148;39725:3;39581:148;;;39574:155;;39747:148;39891:3;39747:148;;;39740:155;;39913:148;40057:3;39913:148;;;39906:155;;40079:148;40223:3;40079:148;;;40072:155;;40245:148;40389:3;40245:148;;;40238:155;;40411:148;40555:3;40411:148;;;40404:155;;40577:148;40721:3;40577:148;;;40570:155;;40743:148;40887:3;40743:148;;;40736:155;;40909:148;41053:3;40909:148;;;40902:155;;41075:148;41219:3;41075:148;;;41068:155;;41241:148;41385:3;41241:148;;;41234:155;;41407:148;41551:3;41407:148;;;41400:155;;41573:148;41717:3;41573:148;;;41566:155;;41739:148;41883:3;41739:148;;;41732:155;;41905:148;42049:3;41905:148;;;41898:155;;42071:148;42215:3;42071:148;;;42064:155;;42237:148;42381:3;42237:148;;;42230:155;;42403:148;42547:3;42403:148;;;42396:155;;42569:148;42713:3;42569:148;;42749:1013;;43054:75;43125:3;43116:6;43054:75;;;43151:2;43146:3;43142:12;43135:19;;43165:75;43236:3;43227:6;43165:75;;;43262:2;43257:3;43253:12;43246:19;;43283:121;43400:3;43391:6;43283:121;;;43276:128;;43415:75;43486:3;43477:6;43415:75;;;43512:2;43507:3;43503:12;43496:19;;43526:75;43597:3;43588:6;43526:75;;;43623:2;43618:3;43614:12;43607:19;;43637:75;43708:3;43699:6;43637:75;;;-1:-1;43734:2;43725:12;;43042:720;-1:-1;;;;;;43042:720;43769:522;;43944:75;44015:3;44006:6;43944:75;;;44041:2;44036:3;44032:12;44025:19;;44055:75;44126:3;44117:6;44055:75;;;44152:2;44147:3;44143:12;44136:19;;44166:75;44237:3;44228:6;44166:75;;;-1:-1;44263:2;44254:12;;43932:359;-1:-1;;;43932:359;44298:800;;44529:75;44600:3;44591:6;44529:75;;;44626:2;44621:3;44617:12;44610:19;;44640:75;44711:3;44702:6;44640:75;;;44737:2;44732:3;44728:12;44721:19;;44751:75;44822:3;44813:6;44751:75;;;44848:2;44843:3;44839:12;44832:19;;44862:75;44933:3;44924:6;44862:75;;;44959:2;44954:3;44950:12;44943:19;;44973:75;45044:3;45035:6;44973:75;;;-1:-1;45070:2;45061:12;;44517:581;-1:-1;;;;;44517:581;45105:213;45223:2;45208:18;;45237:71;45212:9;45281:6;45237:71;;45325:324;45471:2;45456:18;;45485:71;45460:9;45529:6;45485:71;;;45567:72;45635:2;45624:9;45620:18;45611:6;45567:72;;45656:324;45802:2;45787:18;;45816:71;45791:9;45860:6;45816:71;;;45898:72;45966:2;45955:9;45951:18;45942:6;45898:72;;45987:201;46099:2;46084:18;;46113:65;46088:9;46151:6;46113:65;;46195:213;46313:2;46298:18;;46327:71;46302:9;46371:6;46327:71;;46415:476;46613:2;46598:18;;46627:71;46602:9;46671:6;46627:71;;;46746:9;46740:4;46736:20;46731:2;46720:9;46716:18;46709:48;46771:110;46876:4;46867:6;46771:110;;46898:539;47096:3;47081:19;;47111:71;47085:9;47155:6;47111:71;;;47193:68;47257:2;47246:9;47242:18;47233:6;47193:68;;;47272:72;47340:2;47329:9;47325:18;47316:6;47272:72;;;47355;47423:2;47412:9;47408:18;47399:6;47355:72;;;47067:370;;;;;;;;47444:575;47660:3;47645:19;;47675:85;47649:9;47733:6;47675:85;;;47771:72;47839:2;47828:9;47824:18;47815:6;47771:72;;;47854;47922:2;47911:9;47907:18;47898:6;47854:72;;48026:293;48160:2;48174:47;;;48145:18;;48235:74;48145:18;48295:6;48235:74;;48634:407;48825:2;48839:47;;;48810:18;;48900:131;48810:18;48900:131;;49048:407;49239:2;49253:47;;;49224:18;;49314:131;49224:18;49314:131;;49462:407;49653:2;49667:47;;;49638:18;;49728:131;49638:18;49728:131;;49876:407;50067:2;50081:47;;;50052:18;;50142:131;50052:18;50142:131;;50290:407;50481:2;50495:47;;;50466:18;;50556:131;50466:18;50556:131;;50704:407;50895:2;50909:47;;;50880:18;;50970:131;50880:18;50970:131;;51118:407;51309:2;51323:47;;;51294:18;;51384:131;51294:18;51384:131;;51532:407;51723:2;51737:47;;;51708:18;;51798:131;51708:18;51798:131;;51946:407;52137:2;52151:47;;;52122:18;;52212:131;52122:18;52212:131;;52360:407;52551:2;52565:47;;;52536:18;;52626:131;52536:18;52626:131;;52774:407;52965:2;52979:47;;;52950:18;;53040:131;52950:18;53040:131;;53188:407;53379:2;53393:47;;;53364:18;;53454:131;53364:18;53454:131;;53602:407;53793:2;53807:47;;;53778:18;;53868:131;53778:18;53868:131;;54016:407;54207:2;54221:47;;;54192:18;;54282:131;54192:18;54282:131;;54430:680;54702:3;54717:47;;;54687:19;;54778:110;54687:19;54874:6;54778:110;;;54770:118;;54899:72;54967:2;54956:9;54952:18;54943:6;54899:72;;;54982:118;55096:2;55085:9;55081:18;55072:6;54982:118;;55337:256;55399:2;55393:9;55425:17;;;55500:18;55485:34;;55521:22;;;55482:62;55479:2;;;55557:1;55554;55547:12;55479:2;55573;55566:22;55377:216;;-1:-1;55377:216;55600:300;;55755:18;55747:6;55744:30;55741:2;;;55787:1;55784;55777:12;55741:2;-1:-1;55822:4;55810:17;;;55875:15;;55678:222;55907:317;;56046:18;56038:6;56035:30;56032:2;;;56078:1;56075;56068:12;56032:2;-1:-1;56209:4;56145;56122:17;;;;-1:-1;;56118:33;56199:15;;55969:255;56231:97;56315:3;56301:27;56335:147;56455:4;56446:14;;56403:79;56489:108;-1:-1;56583:4;;56561:36;56604:133;56703:12;;56674:63;57351:140;57482:3;57460:31;-1:-1;57460:31;57500:168;57608:19;;;57657:4;57648:14;;57601:67;58324:91;;58386:24;58404:5;58386:24;;58422:85;58488:13;58481:21;;58464:43;58593:105;;58669:24;58687:5;58669:24;;58705:121;-1:-1;;;;;58767:54;;58750:76;58912:81;58983:4;58972:16;;58955:38;59286:145;59367:6;59362:3;59357;59344:30;-1:-1;59423:1;59405:16;;59398:27;59337:94;59440:268;59505:1;59512:101;59526:6;59523:1;59520:13;59512:101;;;59593:11;;;59587:18;59574:11;;;59567:39;59548:2;59541:10;59512:101;;;59628:6;59625:1;59622:13;59619:2;;;-1:-1;;59693:1;59675:16;;59668:27;59489:219;59716:95;;59780:26;59800:5;59899:89;59963:20;59977:5;59963:20;;60076:97;60164:2;60144:14;-1:-1;;60140:28;;60124:49;60181:94;60255:2;60251:14;;60223:52;60283:117;60352:24;60370:5;60352:24;;;60345:5;60342:35;60332:2;;60391:1;60388;60381:12;60332:2;60326:74;;60407:111;60473:21;60488:5;60473:21;;60525:117;60594:24;60612:5;60594:24;;60649:145;60732:38;60764:5;60732:38;

Swarm Source

bzzr://5954dbcb54a17d8a58594685ce1fb91fab7c5ef8ceaf900cfe50bbac7ab7b91c

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

OVERVIEW

Settles SwapRate orders matched off-chain and distributes funds to Core.

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ 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.