ETH Price: $3,396.10 (+2.03%)

Contract

0x95D52139A62Df9abDc9db3996A810C1d319eCd23
 

Overview

ETH Balance

0.77650002 ETH

Eth Value

$2,637.07 (@ $3,396.10/ETH)

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Redeem Vault Bal...207542202024-09-15 6:27:59104 days ago1726381679IN
Fake_Phishing286638
0 ETH0.000087471.4
Approve205514912024-08-17 23:01:23133 days ago1723935683IN
Fake_Phishing286638
0 ETH0.000026841.10740616
Redeem Vault Bal...187552412023-12-10 10:33:47384 days ago1702204427IN
Fake_Phishing286638
0 ETH0.0015671523.8010912
Approve174434112023-06-09 14:58:11568 days ago1686322691IN
Fake_Phishing286638
0 ETH0.0005872124.21996876
Redeem Vault Bal...170112692023-04-09 14:15:59629 days ago1681049759IN
Fake_Phishing286638
0 ETH0.0013892321.09885578
Transfer167147392023-02-26 20:19:23671 days ago1677442763IN
Fake_Phishing286638
0 ETH0.0011194323.88834747
Redeem Vault Bal...165290272023-01-31 19:54:11697 days ago1675194851IN
Fake_Phishing286638
0 ETH0.0021718432.9847044
Redeem Vault Bal...142187722022-02-16 18:28:021046 days ago1645036082IN
Fake_Phishing286638
0 ETH0.0047927272.78914037
Redeem Vault Bal...139229332022-01-02 0:58:131091 days ago1641085093IN
Fake_Phishing286638
0 ETH0.006192894.0526768
Approve138500692021-12-21 18:10:031103 days ago1640110203IN
Fake_Phishing286638
0 ETH0.0016518168.13018784
Redeem Vault Bal...138026202021-12-14 9:59:301110 days ago1639475970IN
Fake_Phishing286638
0 ETH0.0025672438.98974892
Redeem Vault Bal...133600212021-10-05 16:06:211180 days ago1633449981IN
Fake_Phishing286638
0 ETH0.00870337132.18169151
Redeem Vault Bal...125172822021-05-27 15:57:331311 days ago1622131053IN
Fake_Phishing286638
0 ETH0.0017336838.15
Redeem Vault Bal...123082152021-04-25 7:57:561343 days ago1619337476IN
Fake_Phishing286638
0 ETH0.001679837
Redeem Vault Bal...121662282021-04-03 11:02:291365 days ago1617447749IN
Fake_Phishing286638
0 ETH0.003276104
Redeem Vault Bal...121045272021-03-24 23:15:241375 days ago1616627724IN
Fake_Phishing286638
0 ETH0.005292168.00000145
Redeem Vault Bal...119461622021-02-28 13:42:281399 days ago1614519748IN
Fake_Phishing286638
0 ETH0.0020506565.1
Redeem Vault Bal...119112182021-02-23 4:47:531404 days ago1614055673IN
Fake_Phishing286638
0 ETH0.008757278
Redeem Vault Bal...116938962021-01-20 18:12:311438 days ago1611166351IN
Fake_Phishing286638
0 ETH0.00182758
Redeem Vault Bal...116403852021-01-12 13:30:371446 days ago1610458237IN
Fake_Phishing286638
0 ETH0.00270986
Redeem Vault Bal...116403852021-01-12 13:30:371446 days ago1610458237IN
Fake_Phishing286638
0 ETH0.00270986
Redeem Vault Bal...116388252021-01-12 7:33:411446 days ago1610436821IN
Fake_Phishing286638
0 ETH0.001606551.00000145
Redeem Vault Bal...116214962021-01-09 16:00:031449 days ago1610208003IN
Fake_Phishing286638
0 ETH0.003213102
Redeem Vault Bal...116210982021-01-09 14:27:281449 days ago1610202448IN
Fake_Phishing286638
0 ETH0.00233174
Redeem Vault Bal...116061532021-01-07 7:18:311451 days ago1610003911IN
Fake_Phishing286638
0 ETH0.0019057560.5
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block
From
To
207542202024-09-15 6:27:59104 days ago1726381679
Fake_Phishing286638
0.25 ETH
187552412023-12-10 10:33:47384 days ago1702204427
Fake_Phishing286638
0.5 ETH
170112692023-04-09 14:15:59629 days ago1681049759
Fake_Phishing286638
0.04 ETH
165290272023-01-31 19:54:11697 days ago1675194851
Fake_Phishing286638
0.01 ETH
142187722022-02-16 18:28:021046 days ago1645036082
Fake_Phishing286638
0.05 ETH
139229332022-01-02 0:58:131091 days ago1641085093
Fake_Phishing286638
0.01 ETH
138026202021-12-14 9:59:301110 days ago1639475970
Fake_Phishing286638
0.007 ETH
133600212021-10-05 16:06:211180 days ago1633449981
Fake_Phishing286638
0.1 ETH
125172822021-05-27 15:57:331311 days ago1622131053
Fake_Phishing286638
0.02 ETH
123082152021-04-25 7:57:561343 days ago1619337476
Fake_Phishing286638
7.754 ETH
121662282021-04-03 11:02:291365 days ago1617447749
Fake_Phishing286638
0.1 ETH
121045272021-03-24 23:15:241375 days ago1616627724
Fake_Phishing286638
0.3 ETH
119461622021-02-28 13:42:281399 days ago1614519748
Fake_Phishing286638
0.05 ETH
119112182021-02-23 4:47:531404 days ago1614055673
Fake_Phishing286638
0.5 ETH
116938962021-01-20 18:12:311438 days ago1611166351
Fake_Phishing286638
0.146 ETH
116403852021-01-12 13:30:371446 days ago1610458237
Fake_Phishing286638
0.42 ETH
116403852021-01-12 13:30:371446 days ago1610458237
Fake_Phishing286638
0.3 ETH
116388252021-01-12 7:33:411446 days ago1610436821
Fake_Phishing286638
0.5 ETH
116214962021-01-09 16:00:031449 days ago1610208003
Fake_Phishing286638
0.01 ETH
116210982021-01-09 14:27:281449 days ago1610202448
Fake_Phishing286638
1 ETH
116061532021-01-07 7:18:311451 days ago1610003911
Fake_Phishing286638
0.05 ETH
116006972021-01-06 11:16:591452 days ago1609931819
Fake_Phishing286638
1 ETH
115972122021-01-05 22:16:161453 days ago1609884976
Fake_Phishing286638
4 ETH
115882272021-01-04 13:26:581454 days ago1609766818
Fake_Phishing286638
4 ETH
115868452021-01-04 8:10:581454 days ago1609747858
Fake_Phishing286638
0.15 ETH
View All Internal Transactions
Loading...
Loading

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

Contract Name:
oToken

Compiler Version
v0.5.10+commit.5a6ea5b1

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, Unlicense license

Contract Source Code (Solidity)

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

// File: @openzeppelin/contracts/GSN/Context.sol

pragma solidity ^0.5.0;

/*
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with GSN meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
contract Context {
    // Empty internal constructor, to prevent people from mistakenly deploying
    // an instance of this contract, which should be used via inheritance.
    constructor () internal { }
    // solhint-disable-previous-line no-empty-blocks

    function _msgSender() internal view returns (address payable) {
        return msg.sender;
    }

    function _msgData() internal view returns (bytes memory) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
}

// File: @openzeppelin/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/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/contracts/token/ERC20/ERC20.sol

pragma solidity ^0.5.0;




/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20Mintable}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin guidelines: functions revert instead
 * of returning `false` on failure. This behavior is nonetheless conventional
 * and does not conflict with the expectations of ERC20 applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20 {
    using SafeMath for uint256;

    mapping (address => uint256) private _balances;

    mapping (address => mapping (address => uint256)) private _allowances;

    uint256 private _totalSupply;

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address recipient, uint256 amount) public returns (bool) {
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20};
     *
     * Requirements:
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     * - the caller must have allowance for `sender`'s tokens of at least
     * `amount`.
     */
    function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
        _transfer(sender, recipient, amount);
        _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
        return true;
    }

    /**
     * @dev Moves tokens `amount` from `sender` to `recipient`.
     *
     * This is internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `sender` cannot be the zero address.
     * - `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     */
    function _transfer(address sender, address recipient, uint256 amount) internal {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
        _balances[recipient] = _balances[recipient].add(amount);
        emit Transfer(sender, recipient, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements
     *
     * - `to` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal {
        require(account != address(0), "ERC20: mint to the zero address");

        _totalSupply = _totalSupply.add(amount);
        _balances[account] = _balances[account].add(amount);
        emit Transfer(address(0), account, amount);
    }

     /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal {
        require(account != address(0), "ERC20: burn from the zero address");

        _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
        _totalSupply = _totalSupply.sub(amount);
        emit Transfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
     *
     * This is internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(address owner, address spender, uint256 amount) internal {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`.`amount` is then deducted
     * from the caller's allowance.
     *
     * See {_burn} and {_approve}.
     */
    function _burnFrom(address account, uint256 amount) internal {
        _burn(account, amount);
        _approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance"));
    }
}

// File: contracts/lib/CompoundOracleInterface.sol

pragma solidity ^0.5.0;
// AT MAINNET ADDRESS: 0x02557a5E05DeFeFFD4cAe6D83eA3d173B272c904

contract CompoundOracleInterface {
    // returns asset:eth -- to get USDC:eth, have to do 10**24/result,


    constructor() public {
    }

    /**
  * @notice retrieves price of an asset
  * @dev function to get price for an asset
  * @param asset Asset for which to get the price
  * @return uint mantissa of asset price (scaled by 1e18) or zero if unset or contract paused
  */
    function getPrice(address asset) public view returns (uint);
    function getUnderlyingPrice(ERC20 cToken) public view returns (uint);
    // function getPrice(address asset) public view returns (uint) {
    //     return 527557000000000;
    // }

}

// File: contracts/lib/UniswapExchangeInterface.sol

pragma solidity 0.5.10;


// Solidity Interface
contract UniswapExchangeInterface {
    // Address of ERC20 token sold on this exchange
    function tokenAddress() external view returns (address token);
    // Address of Uniswap Factory
    function factoryAddress() external view returns (address factory);
    // Provide Liquidity
    function addLiquidity(uint256 min_liquidity, uint256 max_tokens, uint256 deadline) external payable returns (uint256);
    function removeLiquidity(uint256 amount, uint256 min_eth, uint256 min_tokens, uint256 deadline) external returns (uint256, uint256);
    // Get Prices
    function getEthToTokenInputPrice(uint256 eth_sold) external view returns (uint256 tokens_bought);
    function getEthToTokenOutputPrice(uint256 tokens_bought) external view returns (uint256 eth_sold);
    function getTokenToEthInputPrice(uint256 tokens_sold) external view returns (uint256 eth_bought);
    function getTokenToEthOutputPrice(uint256 eth_bought) external view returns (uint256 tokens_sold);
    // Trade ETH to ERC20
    function ethToTokenSwapInput(uint256 min_tokens, uint256 deadline) external payable returns (uint256  tokens_bought);
    function ethToTokenTransferInput(uint256 min_tokens, uint256 deadline, address recipient) external payable returns (uint256  tokens_bought);
    function ethToTokenSwapOutput(uint256 tokens_bought, uint256 deadline) external payable returns (uint256  eth_sold);
    function ethToTokenTransferOutput(uint256 tokens_bought, uint256 deadline, address recipient) external payable returns (uint256  eth_sold);
    // Trade ERC20 to ETH
    function tokenToEthSwapInput(uint256 tokens_sold, uint256 min_eth, uint256 deadline) external returns (uint256  eth_bought);
    function tokenToEthTransferInput(uint256 tokens_sold, uint256 min_eth, uint256 deadline, address recipient) external returns (uint256  eth_bought);
    function tokenToEthSwapOutput(uint256 eth_bought, uint256 max_tokens, uint256 deadline) external returns (uint256  tokens_sold);
    function tokenToEthTransferOutput(uint256 eth_bought, uint256 max_tokens, uint256 deadline, address recipient) external returns (uint256  tokens_sold);
    // Trade ERC20 to ERC20
    function tokenToTokenSwapInput(uint256 tokens_sold, uint256 min_tokens_bought, uint256 min_eth_bought, uint256 deadline, address token_addr) external returns (uint256  tokens_bought);
    function tokenToTokenTransferInput(uint256 tokens_sold, uint256 min_tokens_bought, uint256 min_eth_bought, uint256 deadline, address recipient, address token_addr) external returns (uint256  tokens_bought);
    function tokenToTokenSwapOutput(uint256 tokens_bought, uint256 max_tokens_sold, uint256 max_eth_sold, uint256 deadline, address token_addr) external returns (uint256  tokens_sold);
    function tokenToTokenTransferOutput(uint256 tokens_bought, uint256 max_tokens_sold, uint256 max_eth_sold, uint256 deadline, address recipient, address token_addr) external returns (uint256  tokens_sold);
    // Trade ERC20 to Custom Pool
    function tokenToExchangeSwapInput(uint256 tokens_sold, uint256 min_tokens_bought, uint256 min_eth_bought, uint256 deadline, address exchange_addr) external returns (uint256  tokens_bought);
    function tokenToExchangeTransferInput(uint256 tokens_sold, uint256 min_tokens_bought, uint256 min_eth_bought, uint256 deadline, address recipient, address exchange_addr) external returns (uint256  tokens_bought);
    function tokenToExchangeSwapOutput(uint256 tokens_bought, uint256 max_tokens_sold, uint256 max_eth_sold, uint256 deadline, address exchange_addr) external returns (uint256  tokens_sold);
    function tokenToExchangeTransferOutput(uint256 tokens_bought, uint256 max_tokens_sold, uint256 max_eth_sold, uint256 deadline, address recipient, address exchange_addr) external returns (uint256  tokens_sold);
    // ERC20 comaptibility for liquidity tokens
    bytes32 public name;
    bytes32 public symbol;
    uint256 public decimals;
    function transfer(address _to, uint256 _value) external returns (bool);
    function transferFrom(address _from, address _to, uint256 value) external returns (bool);
    function approve(address _spender, uint256 _value) external returns (bool);
    function allowance(address _owner, address _spender) external view returns (uint256);
    function balanceOf(address _owner) external view returns (uint256);
    function totalSupply() external view returns (uint256);
    // Never use
    function setup(address token_addr) external;
}

// File: contracts/lib/UniswapFactoryInterface.sol

pragma solidity 0.5.10;


// Solidity Interface
contract UniswapFactoryInterface {
    // Public Variables
    address public exchangeTemplate;
    uint256 public tokenCount;
    // // Create Exchange
    function createExchange(address token) external returns (address exchange);
    // Get Exchange and Token Info
    function getExchange(address token) external view returns (address exchange);
    function getToken(address exchange) external view returns (address token);
    function getTokenWithId(uint256 tokenId) external view returns (address token);
    // Never use
    function initializeFactory(address template) external;
    // function createExchange(address token) external returns (address exchange) {
    //     return 0x06D014475F84Bb45b9cdeD1Cf3A1b8FE3FbAf128;
    // }
    // // Get Exchange and Token Info
    // function getExchange(address token) external view returns (address exchange){
    //     return 0x06D014475F84Bb45b9cdeD1Cf3A1b8FE3FbAf128;
    // }
    // function getToken(address exchange) external view returns (address token) {
    //     return 0x06D014475F84Bb45b9cdeD1Cf3A1b8FE3FbAf128;
    // }
    // function getTokenWithId(uint256 tokenId) external view returns (address token) {
    //     return 0x06D014475F84Bb45b9cdeD1Cf3A1b8FE3FbAf128;
    // }
}

// File: contracts/OptionsUtils.sol

pragma solidity 0.5.10;





contract OptionsUtils {
    // defauls are for mainnet
    UniswapFactoryInterface public UNISWAP_FACTORY;

    CompoundOracleInterface public COMPOUND_ORACLE;

    constructor(address _uniswapFactory, address _compoundOracle) public {
        UNISWAP_FACTORY = UniswapFactoryInterface(_uniswapFactory);
        COMPOUND_ORACLE = CompoundOracleInterface(_compoundOracle);
    }

    // TODO: for now gets Uniswap, later update to get other exchanges
    function getExchange(address _token)
        public
        view
        returns (UniswapExchangeInterface)
    {
        if (address(UNISWAP_FACTORY.getExchange(_token)) == address(0)) {
            revert("No payout exchange");
        }

        UniswapExchangeInterface exchange = UniswapExchangeInterface(
            UNISWAP_FACTORY.getExchange(_token)
        );

        return exchange;
    }

    function isETH(IERC20 _ierc20) public pure returns (bool) {
        return _ierc20 == IERC20(0);
    }
}

// File: contracts/OptionsExchange.sol

pragma solidity 0.5.10;






contract OptionsExchange {
    uint256 constant LARGE_BLOCK_SIZE = 1651753129000;
    uint256 constant LARGE_APPROVAL_NUMBER = 10**30;

    UniswapFactoryInterface public UNISWAP_FACTORY;

    constructor(address _uniswapFactory) public {
        UNISWAP_FACTORY = UniswapFactoryInterface(_uniswapFactory);
    }

    /*** Events ***/
    event SellOTokens(
        address seller,
        address payable receiver,
        address oTokenAddress,
        address payoutTokenAddress,
        uint256 oTokensToSell,
        uint256 payoutTokensReceived
    );
    event BuyOTokens(
        address buyer,
        address payable receiver,
        address oTokenAddress,
        address paymentTokenAddress,
        uint256 oTokensToBuy,
        uint256 premiumPaid
    );

    /**
    * @notice This function sells oTokens on Uniswap and sends back payoutTokens to the receiver
    * @param receiver The address to send the payout tokens back to
    * @param oTokenAddress The address of the oToken to sell
    * @param payoutTokenAddress The address of the token to receive the premiums in
    * @param oTokensToSell The number of oTokens to sell
    */
    function sellOTokens(
        address payable receiver,
        address oTokenAddress,
        address payoutTokenAddress,
        uint256 oTokensToSell
    ) public {
        // @note: first need to bootstrap the uniswap exchange to get the address.
        IERC20 oToken = IERC20(oTokenAddress);
        IERC20 payoutToken = IERC20(payoutTokenAddress);
        oToken.transferFrom(msg.sender, address(this), oTokensToSell);
        uint256 payoutTokensReceived = uniswapSellOToken(
            oToken,
            payoutToken,
            oTokensToSell,
            receiver
        );

        emit SellOTokens(
            msg.sender,
            receiver,
            oTokenAddress,
            payoutTokenAddress,
            oTokensToSell,
            payoutTokensReceived
        );
    }

    /**
    * @notice This function buys oTokens on Uniswap and using paymentTokens from the receiver
    * @param receiver The address to send the oTokens back to
    * @param oTokenAddress The address of the oToken to buy
    * @param paymentTokenAddress The address of the token to pay the premiums in
    * @param oTokensToBuy The number of oTokens to buy
    */
    function buyOTokens(
        address payable receiver,
        address oTokenAddress,
        address paymentTokenAddress,
        uint256 oTokensToBuy
    ) public payable {
        IERC20 oToken = IERC20(oTokenAddress);
        IERC20 paymentToken = IERC20(paymentTokenAddress);
        uniswapBuyOToken(paymentToken, oToken, oTokensToBuy, receiver);
    }

    /**
    * @notice This function calculates the amount of premiums that the seller
    * will receive if they sold oTokens on Uniswap
    * @param oTokenAddress The address of the oToken to sell
    * @param payoutTokenAddress The address of the token to receive the premiums in
    * @param oTokensToSell The number of oTokens to sell
    */
    function premiumReceived(
        address oTokenAddress,
        address payoutTokenAddress,
        uint256 oTokensToSell
    ) public view returns (uint256) {
        // get the amount of ETH that will be paid out if oTokensToSell is sold.
        UniswapExchangeInterface oTokenExchange = getExchange(oTokenAddress);
        uint256 ethReceived = oTokenExchange.getTokenToEthInputPrice(
            oTokensToSell
        );

        if (!isETH(IERC20(payoutTokenAddress))) {
            // get the amount of payout tokens that will be received if the ethRecieved is sold.
            UniswapExchangeInterface payoutExchange = getExchange(
                payoutTokenAddress
            );
            return payoutExchange.getEthToTokenInputPrice(ethReceived);
        }
        return ethReceived;

    }

    /**
    * @notice This function calculates the premiums to be paid if a buyer wants to
    * buy oTokens on Uniswap
    * @param oTokenAddress The address of the oToken to buy
    * @param paymentTokenAddress The address of the token to pay the premiums in
    * @param oTokensToBuy The number of oTokens to buy
    */
    function premiumToPay(
        address oTokenAddress,
        address paymentTokenAddress,
        uint256 oTokensToBuy
    ) public view returns (uint256) {
        // get the amount of ETH that needs to be paid for oTokensToBuy.
        UniswapExchangeInterface oTokenExchange = getExchange(oTokenAddress);
        uint256 ethToPay = oTokenExchange.getEthToTokenOutputPrice(
            oTokensToBuy
        );

        if (!isETH(IERC20(paymentTokenAddress))) {
            // get the amount of paymentTokens that needs to be paid to get the desired ethToPay.
            UniswapExchangeInterface paymentTokenExchange = getExchange(
                paymentTokenAddress
            );
            return paymentTokenExchange.getTokenToEthOutputPrice(ethToPay);
        }

        return ethToPay;
    }

    function uniswapSellOToken(
        IERC20 oToken,
        IERC20 payoutToken,
        uint256 _amt,
        address payable _transferTo
    ) internal returns (uint256) {
        require(!isETH(oToken), "Can only sell oTokens");
        UniswapExchangeInterface exchange = getExchange(address(oToken));

        if (isETH(payoutToken)) {
            //Token to ETH
            oToken.approve(address(exchange), _amt);
            return
                exchange.tokenToEthTransferInput(
                    _amt,
                    1,
                    LARGE_BLOCK_SIZE,
                    _transferTo
                );
        } else {
            //Token to Token
            oToken.approve(address(exchange), _amt);
            return
                exchange.tokenToTokenTransferInput(
                    _amt,
                    1,
                    1,
                    LARGE_BLOCK_SIZE,
                    _transferTo,
                    address(payoutToken)
                );
        }
    }

    function uniswapBuyOToken(
        IERC20 paymentToken,
        IERC20 oToken,
        uint256 _amt,
        address payable _transferTo
    ) public returns (uint256) {
        require(!isETH(oToken), "Can only buy oTokens");

        if (!isETH(paymentToken)) {
            UniswapExchangeInterface exchange = getExchange(
                address(paymentToken)
            );

            uint256 paymentTokensToTransfer = premiumToPay(
                address(oToken),
                address(paymentToken),
                _amt
            );
            paymentToken.transferFrom(
                msg.sender,
                address(this),
                paymentTokensToTransfer
            );

            // Token to Token
            paymentToken.approve(address(exchange), LARGE_APPROVAL_NUMBER);

            emit BuyOTokens(
                msg.sender,
                _transferTo,
                address(oToken),
                address(paymentToken),
                _amt,
                paymentTokensToTransfer
            );

            return
                exchange.tokenToTokenTransferInput(
                    paymentTokensToTransfer,
                    1,
                    1,
                    LARGE_BLOCK_SIZE,
                    _transferTo,
                    address(oToken)
                );
        } else {
            // ETH to Token
            UniswapExchangeInterface exchange = UniswapExchangeInterface(
                UNISWAP_FACTORY.getExchange(address(oToken))
            );

            uint256 ethToTransfer = exchange.getEthToTokenOutputPrice(_amt);

            emit BuyOTokens(
                msg.sender,
                _transferTo,
                address(oToken),
                address(paymentToken),
                _amt,
                ethToTransfer
            );

            return
                exchange.ethToTokenTransferOutput.value(ethToTransfer)(
                    _amt,
                    LARGE_BLOCK_SIZE,
                    _transferTo
                );
        }
    }

    function getExchange(address _token)
        internal
        view
        returns (UniswapExchangeInterface)
    {
        UniswapExchangeInterface exchange = UniswapExchangeInterface(
            UNISWAP_FACTORY.getExchange(_token)
        );

        if (address(exchange) == address(0)) {
            revert("No payout exchange");
        }

        return exchange;
    }

    function isETH(IERC20 _ierc20) internal pure returns (bool) {
        return _ierc20 == IERC20(0);
    }

    function() external payable {
        // to get ether from uniswap exchanges
    }

}

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

pragma solidity ^0.5.0;


/**
 * @dev Optional functions from the ERC20 standard.
 */
contract ERC20Detailed is IERC20 {
    string private _name;
    string private _symbol;
    uint8 private _decimals;

    /**
     * @dev Sets the values for `name`, `symbol`, and `decimals`. All three of
     * these values are immutable: they can only be set once during
     * construction.
     */
    constructor (string memory name, string memory symbol, uint8 decimals) public {
        _name = name;
        _symbol = symbol;
        _decimals = decimals;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5,05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view returns (uint8) {
        return _decimals;
    }
}

// File: @openzeppelin/contracts/ownership/Ownable.sol

pragma solidity ^0.5.0;

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor () internal {
        _owner = _msgSender();
        emit OwnershipTransferred(address(0), _owner);
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(isOwner(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Returns true if the caller is the current owner.
     */
    function isOwner() public view returns (bool) {
        return _msgSender() == _owner;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public onlyOwner {
        emit OwnershipTransferred(_owner, address(0));
        _owner = address(0);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public onlyOwner {
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     */
    function _transferOwnership(address newOwner) internal {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
}

// File: contracts/OptionsContract.sol

pragma solidity 0.5.10;











/**
 * @title Opyn's Options Contract
 * @author Opyn
 */
contract OptionsContract is Ownable, ERC20 {
    using SafeMath for uint256;

    /* represents floting point numbers, where number = value * 10 ** exponent
    i.e 0.1 = 10 * 10 ** -3 */
    struct Number {
        uint256 value;
        int32 exponent;
    }

    // Keeps track of the weighted collateral and weighted debt for each vault.
    struct Vault {
        uint256 collateral;
        uint256 oTokensIssued;
        uint256 underlying;
        bool owned;
    }

    OptionsExchange public optionsExchange;

    mapping(address => Vault) internal vaults;

    address payable[] internal vaultOwners;

    // 10 is 0.01 i.e. 1% incentive.
    Number public liquidationIncentive = Number(10, -3);

    // 100 is egs. 0.1 i.e. 10%.
    Number public transactionFee = Number(0, -3);

    /* 500 is 0.5. Max amount that a Vault can be liquidated by i.e.
    max collateral that can be taken in one function call */
    Number public liquidationFactor = Number(500, -3);

    /* 16 means 1.6. The minimum ratio of a Vault's collateral to insurance promised.
    The ratio is calculated as below:
    vault.collateral / (Vault.oTokensIssued * strikePrice) */
    Number public minCollateralizationRatio = Number(16, -1);

    // The amount of insurance promised per oToken
    Number public strikePrice;

    // The amount of underlying that 1 oToken protects.
    Number public oTokenExchangeRate;

    /* UNIX time.
    Exercise period starts at `(expiry - windowSize)` and ends at `expiry` */
    uint256 internal windowSize;

    /* The total fees accumulated in the contract any time liquidate or exercise is called */
    uint256 internal totalFee;

    // The time of expiry of the options contract
    uint256 public expiry;

    // The precision of the collateral
    int32 public collateralExp = -18;

    // The precision of the underlying
    int32 public underlyingExp = -18;

    // The collateral asset
    IERC20 public collateral;

    // The asset being protected by the insurance
    IERC20 public underlying;

    // The asset in which insurance is denominated in.
    IERC20 public strike;

    // The Oracle used for the contract
    CompoundOracleInterface public COMPOUND_ORACLE;

    // The name of  the contract
    string public name;

    // The symbol of  the contract
    string public symbol;

    // The number of decimals of the contract
    uint8 public decimals;

    /**
    * @param _collateral The collateral asset
    * @param _collExp The precision of the collateral (-18 if ETH)
    * @param _underlying The asset that is being protected
    * @param _underlyingExp The precision of the underlying asset
    * @param _oTokenExchangeExp The precision of the `amount of underlying` that 1 oToken protects
    * @param _strikePrice The amount of strike asset that will be paid out per oToken
    * @param _strikeExp The precision of the strike price.
    * @param _strike The asset in which the insurance is calculated
    * @param _expiry The time at which the insurance expires
    * @param _optionsExchange The contract which interfaces with the exchange + oracle
    * @param _oracleAddress The address of the oracle
    * @param _windowSize UNIX time. Exercise window is from `expiry - _windowSize` to `expiry`.
    */
    constructor(
        IERC20 _collateral,
        int32 _collExp,
        IERC20 _underlying,
        int32 _underlyingExp,
        int32 _oTokenExchangeExp,
        uint256 _strikePrice,
        int32 _strikeExp,
        IERC20 _strike,
        uint256 _expiry,
        OptionsExchange _optionsExchange,
        address _oracleAddress,
        uint256 _windowSize
    ) public {
        require(block.timestamp < _expiry, "Can't deploy an expired contract");
        require(
            _windowSize <= _expiry,
            "Exercise window can't be longer than the contract's lifespan"
        );
        require(
            isWithinExponentRange(_collExp),
            "collateral exponent not within expected range"
        );
        require(
            isWithinExponentRange(_underlyingExp),
            "underlying exponent not within expected range"
        );
        require(
            isWithinExponentRange(_strikeExp),
            "strike price exponent not within expected range"
        );
        require(
            isWithinExponentRange(_oTokenExchangeExp),
            "oToken exchange rate exponent not within expected range"
        );

        collateral = _collateral;
        collateralExp = _collExp;

        underlying = _underlying;
        underlyingExp = _underlyingExp;
        oTokenExchangeRate = Number(1, _oTokenExchangeExp);

        strikePrice = Number(_strikePrice, _strikeExp);
        strike = _strike;

        expiry = _expiry;
        COMPOUND_ORACLE = CompoundOracleInterface(_oracleAddress);
        optionsExchange = _optionsExchange;
        windowSize = _windowSize;
    }

    /*** Events ***/
    event VaultOpened(address payable vaultOwner);
    event ETHCollateralAdded(
        address payable vaultOwner,
        uint256 amount,
        address payer
    );
    event ERC20CollateralAdded(
        address payable vaultOwner,
        uint256 amount,
        address payer
    );
    event IssuedOTokens(
        address issuedTo,
        uint256 oTokensIssued,
        address payable vaultOwner
    );
    event Liquidate(
        uint256 amtCollateralToPay,
        address payable vaultOwner,
        address payable liquidator
    );
    event Exercise(
        uint256 amtUnderlyingToPay,
        uint256 amtCollateralToPay,
        address payable exerciser,
        address payable vaultExercisedFrom
    );
    event RedeemVaultBalance(
        uint256 amtCollateralRedeemed,
        uint256 amtUnderlyingRedeemed,
        address payable vaultOwner
    );
    event BurnOTokens(address payable vaultOwner, uint256 oTokensBurned);
    event RemoveCollateral(uint256 amtRemoved, address payable vaultOwner);
    event UpdateParameters(
        uint256 liquidationIncentive,
        uint256 liquidationFactor,
        uint256 transactionFee,
        uint256 minCollateralizationRatio,
        address owner
    );
    event TransferFee(address payable to, uint256 fees);
    event RemoveUnderlying(
        uint256 amountUnderlying,
        address payable vaultOwner
    );

    /**
     * @dev Throws if called Options contract is expired.
     */
    modifier notExpired() {
        require(!hasExpired(), "Options contract expired");
        _;
    }

    /**
     * @notice This function gets the array of vaultOwners
     */
    function getVaultOwners() public view returns (address payable[] memory) {
        address payable[] memory owners;
        uint256 index = 0;
        for (uint256 i = 0; i < vaultOwners.length; i++) {
            if (hasVault(vaultOwners[i])) {
                owners[index] = vaultOwners[i];
                index++;
            }
        }

        return owners;
    }

    /**
     * @notice Can only be called by owner. Used to update the fees, minminCollateralizationRatio, etc
     * @param _liquidationIncentive The incentive paid to liquidator. 10 is 0.01 i.e. 1% incentive.
     * @param _liquidationFactor Max amount that a Vault can be liquidated by. 500 is 0.5.
     * @param _transactionFee The fees paid to our protocol every time a execution happens. 100 is egs. 0.1 i.e. 10%.
     * @param _minCollateralizationRatio The minimum ratio of a Vault's collateral to insurance promised. 16 means 1.6.
     */
    function updateParameters(
        uint256 _liquidationIncentive,
        uint256 _liquidationFactor,
        uint256 _transactionFee,
        uint256 _minCollateralizationRatio
    ) public onlyOwner {
        require(
            _liquidationIncentive <= 200,
            "Can't have >20% liquidation incentive"
        );
        require(
            _liquidationFactor <= 1000,
            "Can't liquidate more than 100% of the vault"
        );
        require(_transactionFee <= 100, "Can't have transaction fee > 10%");
        require(
            _minCollateralizationRatio >= 10,
            "Can't have minCollateralizationRatio < 1"
        );

        liquidationIncentive.value = _liquidationIncentive;
        liquidationFactor.value = _liquidationFactor;
        transactionFee.value = _transactionFee;
        minCollateralizationRatio.value = _minCollateralizationRatio;

        emit UpdateParameters(
            _liquidationIncentive,
            _liquidationFactor,
            _transactionFee,
            _minCollateralizationRatio,
            owner()
        );
    }

    /**
     * @notice Can only be called by owner. Used to set the name, symbol and decimals of the contract
     * @param _name The name of the contract
     * @param _symbol The symbol of the contract
     */
    function setDetails(string memory _name, string memory _symbol)
        public
        onlyOwner
    {
        name = _name;
        symbol = _symbol;
        decimals = uint8(-1 * oTokenExchangeRate.exponent);
        require(
            decimals >= 0,
            "1 oToken cannot protect less than the smallest unit of the asset"
        );
    }

    /**
     * @notice Can only be called by owner. Used to take out the protocol fees from the contract.
     * @param _address The address to send the fee to.
     */
    function transferFee(address payable _address) public onlyOwner {
        uint256 fees = totalFee;
        totalFee = 0;
        transferCollateral(_address, fees);

        emit TransferFee(_address, fees);
    }

    /**
     * @notice Checks if a `owner` has already created a Vault
     * @param owner The address of the supposed owner
     * @return true or false
     */
    function hasVault(address payable owner) public view returns (bool) {
        return vaults[owner].owned;
    }

    /**
     * @notice Creates a new empty Vault and sets the owner of the vault to be the msg.sender.
     */
    function openVault() public notExpired returns (bool) {
        require(!hasVault(msg.sender), "Vault already created");

        vaults[msg.sender] = Vault(0, 0, 0, true);
        vaultOwners.push(msg.sender);

        emit VaultOpened(msg.sender);
        return true;
    }

    /**
     * @notice If the collateral type is ETH, anyone can call this function any time before
     * expiry to increase the amount of collateral in a Vault. Will fail if ETH is not the
     * collateral asset.
     * Remember that adding ETH collateral even if no oTokens have been created can put the owner at a
     * risk of losing the collateral if an exercise event happens.
     * Ensure that you issue and immediately sell oTokens to allow the owner to earn premiums.
     * (Either call the createAndSell function in the oToken contract or batch the
     * addERC20Collateral, issueOTokens and sell transactions and ensure they happen atomically to protect
     * the end user).
     * @param vaultOwner the index of the Vault to which collateral will be added.
     */
    function addETHCollateral(address payable vaultOwner)
        public
        payable
        notExpired
        returns (uint256)
    {
        require(isETH(collateral), "ETH is not the specified collateral type");
        require(hasVault(vaultOwner), "Vault does not exist");

        emit ETHCollateralAdded(vaultOwner, msg.value, msg.sender);
        return _addCollateral(vaultOwner, msg.value);
    }

    /**
     * @notice If the collateral type is any ERC20, anyone can call this function any time before
     * expiry to increase the amount of collateral in a Vault. Can only transfer in the collateral asset.
     * Will fail if ETH is the collateral asset.
     * The user has to allow the contract to handle their ERC20 tokens on his behalf before these
     * functions are called.
     * Remember that adding ERC20 collateral even if no oTokens have been created can put the owner at a
     * risk of losing the collateral. Ensure that you issue and immediately sell the oTokens!
     * (Either call the createAndSell function in the oToken contract or batch the
     * addERC20Collateral, issueOTokens and sell transactions and ensure they happen atomically to protect
     * the end user).
     * @param vaultOwner the index of the Vault to which collateral will be added.
     * @param amt the amount of collateral to be transferred in.
     */
    function addERC20Collateral(address payable vaultOwner, uint256 amt)
        public
        notExpired
        returns (uint256)
    {
        require(
            collateral.transferFrom(msg.sender, address(this), amt),
            "Could not transfer in collateral tokens"
        );
        require(hasVault(vaultOwner), "Vault does not exist");

        emit ERC20CollateralAdded(vaultOwner, amt, msg.sender);
        return _addCollateral(vaultOwner, amt);
    }

    /**
     * @notice Returns the amount of underlying to be transferred during an exercise call
     */
    function underlyingRequiredToExercise(uint256 oTokensToExercise)
        public
        view
        returns (uint256)
    {
        uint64 underlyingPerOTokenExp = uint64(
            oTokenExchangeRate.exponent - underlyingExp
        );
        return oTokensToExercise.mul(10**underlyingPerOTokenExp);
    }

    /**
     * @notice Returns true if exercise can be called
     */
    function isExerciseWindow() public view returns (bool) {
        return ((block.timestamp >= expiry.sub(windowSize)) &&
            (block.timestamp < expiry));
    }

    /**
     * @notice Returns true if the oToken contract has expired
     */
    function hasExpired() public view returns (bool) {
        return (block.timestamp >= expiry);
    }

    /**
     * @notice Called by anyone holding the oTokens and underlying during the
     * exercise window i.e. from `expiry - windowSize` time to `expiry` time. The caller
     * transfers in their oTokens and corresponding amount of underlying and gets
     * `strikePrice * oTokens` amount of collateral out. The collateral paid out is taken from
     * the each vault owner starting with the first and iterating until the oTokens to exercise
     * are found.
     * NOTE: This uses a for loop and hence could run out of gas if the array passed in is too big!
     * @param oTokensToExercise the number of oTokens being exercised.
     * @param vaultsToExerciseFrom the array of vaults to exercise from.
     */
    function exercise(
        uint256 oTokensToExercise,
        address payable[] memory vaultsToExerciseFrom
    ) public payable {
        for (uint256 i = 0; i < vaultsToExerciseFrom.length; i++) {
            address payable vaultOwner = vaultsToExerciseFrom[i];
            require(
                hasVault(vaultOwner),
                "Cannot exercise from a vault that doesn't exist"
            );
            Vault storage vault = vaults[vaultOwner];
            if (oTokensToExercise == 0) {
                return;
            } else if (vault.oTokensIssued >= oTokensToExercise) {
                _exercise(oTokensToExercise, vaultOwner);
                return;
            } else {
                oTokensToExercise = oTokensToExercise.sub(vault.oTokensIssued);
                _exercise(vault.oTokensIssued, vaultOwner);
            }
        }
        require(
            oTokensToExercise == 0,
            "Specified vaults have insufficient collateral"
        );
    }

    /**
     * @notice This function allows the vault owner to remove their share of underlying after an exercise
     */
    function removeUnderlying() public {
        require(hasVault(msg.sender), "Vault does not exist");
        Vault storage vault = vaults[msg.sender];

        require(vault.underlying > 0, "No underlying balance");

        uint256 underlyingToTransfer = vault.underlying;
        vault.underlying = 0;

        transferUnderlying(msg.sender, underlyingToTransfer);
        emit RemoveUnderlying(underlyingToTransfer, msg.sender);

    }

    /**
     * @notice This function is called to issue the option tokens. Remember that issuing oTokens even if they
     * haven't been sold can put the owner at a risk of not making premiums on the oTokens. Ensure that you
     * issue and immidiately sell the oTokens! (Either call the createAndSell function in the oToken contract
     * of batch the issueOTokens transaction with a sell transaction and ensure it happens atomically).
     * @dev The owner of a Vault should only be able to have a max of
     * repo.collateral * collateralToStrike / (minminCollateralizationRatio * strikePrice) tokens issued.
     * @param oTokensToIssue The number of o tokens to issue
     * @param receiver The address to send the oTokens to
     */
    function issueOTokens(uint256 oTokensToIssue, address receiver)
        public
        notExpired
    {
        //check that we're properly collateralized to mint this number, then call _mint(address account, uint256 amount)
        require(hasVault(msg.sender), "Vault does not exist");

        Vault storage vault = vaults[msg.sender];

        // checks that the vault is sufficiently collateralized
        uint256 newOTokensBalance = vault.oTokensIssued.add(oTokensToIssue);
        require(isSafe(vault.collateral, newOTokensBalance), "unsafe to mint");

        // issue the oTokens
        vault.oTokensIssued = newOTokensBalance;
        _mint(receiver, oTokensToIssue);

        emit IssuedOTokens(receiver, oTokensToIssue, msg.sender);
        return;
    }

    /**
     * @notice Returns the vault for a given address
     * @param vaultOwner the owner of the Vault to return
     */
    function getVault(address payable vaultOwner)
        public
        view
        returns (uint256, uint256, uint256, bool)
    {
        Vault storage vault = vaults[vaultOwner];
        return (
            vault.collateral,
            vault.oTokensIssued,
            vault.underlying,
            vault.owned
        );
    }

    /**
     * @notice Returns true if the given ERC20 is ETH.
     * @param _ierc20 the ERC20 asset.
     */
    function isETH(IERC20 _ierc20) public pure returns (bool) {
        return _ierc20 == IERC20(0);
    }

    /**
     * @notice allows the owner to burn their oTokens to increase the collateralization ratio of
     * their vault.
     * @param amtToBurn number of oTokens to burn
     * @dev only want to call this function before expiry. After expiry, no benefit to calling it.
     */
    function burnOTokens(uint256 amtToBurn) public notExpired {
        require(hasVault(msg.sender), "Vault does not exist");

        Vault storage vault = vaults[msg.sender];

        vault.oTokensIssued = vault.oTokensIssued.sub(amtToBurn);
        _burn(msg.sender, amtToBurn);

        emit BurnOTokens(msg.sender, amtToBurn);
    }

    /**
     * @notice allows the owner to remove excess collateral from the vault before expiry. Removing collateral lowers
     * the collateralization ratio of the vault.
     * @param amtToRemove Amount of collateral to remove in 10^-18.
     */
    function removeCollateral(uint256 amtToRemove) public notExpired {
        require(amtToRemove > 0, "Cannot remove 0 collateral");
        require(hasVault(msg.sender), "Vault does not exist");

        Vault storage vault = vaults[msg.sender];
        require(
            amtToRemove <= getCollateral(msg.sender),
            "Can't remove more collateral than owned"
        );

        // check that vault will remain safe after removing collateral
        uint256 newCollateralBalance = vault.collateral.sub(amtToRemove);

        require(
            isSafe(newCollateralBalance, vault.oTokensIssued),
            "Vault is unsafe"
        );

        // remove the collateral
        vault.collateral = newCollateralBalance;
        transferCollateral(msg.sender, amtToRemove);

        emit RemoveCollateral(amtToRemove, msg.sender);
    }

    /**
     * @notice after expiry, each vault holder can get back their proportional share of collateral
     * from vaults that they own.
     * @dev The owner gets all of their collateral back if no exercise event took their collateral.
     */
    function redeemVaultBalance() public {
        require(hasExpired(), "Can't collect collateral until expiry");
        require(hasVault(msg.sender), "Vault does not exist");

        // pay out owner their share
        Vault storage vault = vaults[msg.sender];

        // To deal with lower precision
        uint256 collateralToTransfer = vault.collateral;
        uint256 underlyingToTransfer = vault.underlying;

        vault.collateral = 0;
        vault.oTokensIssued = 0;
        vault.underlying = 0;

        transferCollateral(msg.sender, collateralToTransfer);
        transferUnderlying(msg.sender, underlyingToTransfer);

        emit RedeemVaultBalance(
            collateralToTransfer,
            underlyingToTransfer,
            msg.sender
        );
    }

    /**
     * This function returns the maximum amount of collateral liquidatable if the given vault is unsafe
     * @param vaultOwner The index of the vault to be liquidated
     */
    function maxOTokensLiquidatable(address payable vaultOwner)
        public
        view
        returns (uint256)
    {
        if (isUnsafe(vaultOwner)) {
            Vault storage vault = vaults[vaultOwner];
            uint256 maxCollateralLiquidatable = vault
                .collateral
                .mul(liquidationFactor.value)
                .div(10**uint256(-liquidationFactor.exponent));

            uint256 one = 10**uint256(-liquidationIncentive.exponent);
            Number memory liqIncentive = Number(
                liquidationIncentive.value.add(one),
                liquidationIncentive.exponent
            );
            return calculateOTokens(maxCollateralLiquidatable, liqIncentive);
        } else {
            return 0;
        }
    }

    /**
     * @notice This function can be called by anyone who notices a vault is undercollateralized.
     * The caller gets a reward for reducing the amount of oTokens in circulation.
     * @dev Liquidator comes with _oTokens. They get _oTokens * strikePrice * (incentive + fee)
     * amount of collateral out. They can liquidate a max of liquidationFactor * vault.collateral out
     * in one function call i.e. partial liquidations.
     * @param vaultOwner The index of the vault to be liquidated
     * @param oTokensToLiquidate The number of oTokens being taken out of circulation
     */
    function liquidate(address payable vaultOwner, uint256 oTokensToLiquidate)
        public
        notExpired
    {
        require(hasVault(vaultOwner), "Vault does not exist");

        Vault storage vault = vaults[vaultOwner];

        // cannot liquidate a safe vault.
        require(isUnsafe(vaultOwner), "Vault is safe");

        // Owner can't liquidate themselves
        require(msg.sender != vaultOwner, "Owner can't liquidate themselves");

        uint256 amtCollateral = calculateCollateralToPay(
            oTokensToLiquidate,
            Number(1, 0)
        );
        uint256 amtIncentive = calculateCollateralToPay(
            oTokensToLiquidate,
            liquidationIncentive
        );
        uint256 amtCollateralToPay = amtCollateral.add(amtIncentive);

        // calculate the maximum amount of collateral that can be liquidated
        uint256 maxCollateralLiquidatable = vault.collateral.mul(
            liquidationFactor.value
        );

        if (liquidationFactor.exponent > 0) {
            maxCollateralLiquidatable = maxCollateralLiquidatable.mul(
                10**uint256(liquidationFactor.exponent)
            );
        } else {
            maxCollateralLiquidatable = maxCollateralLiquidatable.div(
                10**uint256(-1 * liquidationFactor.exponent)
            );
        }

        require(
            amtCollateralToPay <= maxCollateralLiquidatable,
            "Can only liquidate liquidation factor at any given time"
        );

        // deduct the collateral and oTokensIssued
        vault.collateral = vault.collateral.sub(amtCollateralToPay);
        vault.oTokensIssued = vault.oTokensIssued.sub(oTokensToLiquidate);

        // transfer the collateral and burn the _oTokens
        _burn(msg.sender, oTokensToLiquidate);
        transferCollateral(msg.sender, amtCollateralToPay);

        emit Liquidate(amtCollateralToPay, vaultOwner, msg.sender);
    }

    /**
     * @notice checks if a vault is unsafe. If so, it can be liquidated
     * @param vaultOwner The number of the vault to check
     * @return true or false
     */
    function isUnsafe(address payable vaultOwner) public view returns (bool) {
        bool stillUnsafe = !isSafe(
            getCollateral(vaultOwner),
            getOTokensIssued(vaultOwner)
        );
        return stillUnsafe;
    }

    /**
     * @notice This function returns if an -30 <= exponent <= 30
     */
    function isWithinExponentRange(int32 val) internal pure returns (bool) {
        return ((val <= 30) && (val >= -30));
    }

    /**
     * @notice This function calculates and returns the amount of collateral in the vault
    */
    function getCollateral(address payable vaultOwner)
        internal
        view
        returns (uint256)
    {
        Vault storage vault = vaults[vaultOwner];
        return vault.collateral;
    }

    /**
     * @notice This function calculates and returns the amount of puts issued by the Vault
    */
    function getOTokensIssued(address payable vaultOwner)
        internal
        view
        returns (uint256)
    {
        Vault storage vault = vaults[vaultOwner];
        return vault.oTokensIssued;
    }

    /**
     * @notice Called by anyone holding the oTokens and underlying during the
     * exercise window i.e. from `expiry - windowSize` time to `expiry` time. The caller
     * transfers in their oTokens and corresponding amount of underlying and gets
     * `strikePrice * oTokens` amount of collateral out. The collateral paid out is taken from
     * the specified vault holder. At the end of the expiry window, the vault holder can redeem their balance
     * of collateral. The vault owner can withdraw their underlying at any time.
     * The user has to allow the contract to handle their oTokens and underlying on his behalf before these functions are called.
     * @param oTokensToExercise the number of oTokens being exercised.
     * @param vaultToExerciseFrom the address of the vaultOwner to take collateral from.
     * @dev oTokenExchangeRate is the number of underlying tokens that 1 oToken protects.
     */
    function _exercise(
        uint256 oTokensToExercise,
        address payable vaultToExerciseFrom
    ) internal {
        // 1. before exercise window: revert
        require(
            isExerciseWindow(),
            "Can't exercise outside of the exercise window"
        );

        require(hasVault(vaultToExerciseFrom), "Vault does not exist");

        Vault storage vault = vaults[vaultToExerciseFrom];
        require(oTokensToExercise > 0, "Can't exercise 0 oTokens");
        // Check correct amount of oTokens passed in)
        require(
            oTokensToExercise <= vault.oTokensIssued,
            "Can't exercise more oTokens than the owner has"
        );
        // Ensure person calling has enough oTokens
        require(
            balanceOf(msg.sender) >= oTokensToExercise,
            "Not enough oTokens"
        );

        // 1. Check sufficient underlying
        // 1.1 update underlying balances
        uint256 amtUnderlyingToPay = underlyingRequiredToExercise(
            oTokensToExercise
        );
        vault.underlying = vault.underlying.add(amtUnderlyingToPay);

        // 2. Calculate Collateral to pay
        // 2.1 Payout enough collateral to get (strikePrice * oTokens) amount of collateral
        uint256 amtCollateralToPay = calculateCollateralToPay(
            oTokensToExercise,
            Number(1, 0)
        );

        // 2.2 Take a small fee on every exercise
        uint256 amtFee = calculateCollateralToPay(
            oTokensToExercise,
            transactionFee
        );
        totalFee = totalFee.add(amtFee);

        uint256 totalCollateralToPay = amtCollateralToPay.add(amtFee);
        require(
            totalCollateralToPay <= vault.collateral,
            "Vault underwater, can't exercise"
        );

        // 3. Update collateral + oToken balances
        vault.collateral = vault.collateral.sub(totalCollateralToPay);
        vault.oTokensIssued = vault.oTokensIssued.sub(oTokensToExercise);

        // 4. Transfer in underlying, burn oTokens + pay out collateral
        // 4.1 Transfer in underlying
        if (isETH(underlying)) {
            require(msg.value == amtUnderlyingToPay, "Incorrect msg.value");
        } else {
            require(
                underlying.transferFrom(
                    msg.sender,
                    address(this),
                    amtUnderlyingToPay
                ),
                "Could not transfer in tokens"
            );
        }
        // 4.2 burn oTokens
        _burn(msg.sender, oTokensToExercise);

        // 4.3 Pay out collateral
        transferCollateral(msg.sender, amtCollateralToPay);

        emit Exercise(
            amtUnderlyingToPay,
            amtCollateralToPay,
            msg.sender,
            vaultToExerciseFrom
        );

    }

    /**
     * @notice adds `_amt` collateral to `vaultOwner` and returns the new balance of the vault
     * @param vaultOwner the index of the vault
     * @param amt the amount of collateral to add
     */
    function _addCollateral(address payable vaultOwner, uint256 amt)
        internal
        notExpired
        returns (uint256)
    {
        Vault storage vault = vaults[vaultOwner];
        vault.collateral = vault.collateral.add(amt);

        return vault.collateral;
    }

    /**
     * @notice checks if a hypothetical vault is safe with the given collateralAmt and oTokensIssued
     * @param collateralAmt The amount of collateral the hypothetical vault has
     * @param oTokensIssued The amount of oTokens generated by the hypothetical vault
     * @return true or false
     */
    function isSafe(uint256 collateralAmt, uint256 oTokensIssued)
        internal
        view
        returns (bool)
    {
        // get price from Oracle
        uint256 collateralToEthPrice = getPrice(address(collateral));
        uint256 strikeToEthPrice = getPrice(address(strike));

        // check `oTokensIssued * minCollateralizationRatio * strikePrice <= collAmt * collateralToStrikePrice`
        uint256 leftSideVal = oTokensIssued
            .mul(minCollateralizationRatio.value)
            .mul(strikePrice.value);
        int32 leftSideExp = minCollateralizationRatio.exponent +
            strikePrice.exponent;

        uint256 rightSideVal = (collateralAmt.mul(collateralToEthPrice)).div(
            strikeToEthPrice
        );
        int32 rightSideExp = collateralExp;

        uint256 exp = 0;
        bool stillSafe = false;

        if (rightSideExp < leftSideExp) {
            exp = uint256(leftSideExp - rightSideExp);
            stillSafe = leftSideVal.mul(10**exp) <= rightSideVal;
        } else {
            exp = uint256(rightSideExp - leftSideExp);
            stillSafe = leftSideVal <= rightSideVal.mul(10**exp);
        }

        return stillSafe;
    }

    /**
     * This function returns the maximum amount of oTokens that can safely be issued against the specified amount of collateral.
     * @param collateralAmt The amount of collateral against which oTokens will be issued.
     */
    function maxOTokensIssuable(uint256 collateralAmt)
        public
        view
        returns (uint256)
    {
        return calculateOTokens(collateralAmt, minCollateralizationRatio);

    }

    /**
     * @notice This function is used to calculate the amount of tokens that can be issued.
     * @dev The amount of oTokens is determined by:
     * oTokensIssued  <= collateralAmt * collateralToStrikePrice / (proportion * strikePrice)
     * @param collateralAmt The amount of collateral
     * @param proportion The proportion of the collateral to pay out. If 100% of collateral
     * should be paid out, pass in Number(1, 0). The proportion might be less than 100% if
     * you are calculating fees.
     */
    function calculateOTokens(uint256 collateralAmt, Number memory proportion)
        internal
        view
        returns (uint256)
    {
        uint256 collateralToEthPrice = getPrice(address(collateral));
        uint256 strikeToEthPrice = getPrice(address(strike));

        // oTokensIssued  <= collAmt * collateralToStrikePrice / (proportion * strikePrice)
        uint256 denomVal = proportion.value.mul(strikePrice.value);
        int32 denomExp = proportion.exponent + strikePrice.exponent;

        uint256 numeratorVal = (collateralAmt.mul(collateralToEthPrice)).div(
            strikeToEthPrice
        );
        int32 numeratorExp = collateralExp;

        uint256 exp = 0;
        uint256 numOptions = 0;

        if (numeratorExp < denomExp) {
            exp = uint256(denomExp - numeratorExp);
            numOptions = numeratorVal.div(denomVal.mul(10**exp));
        } else {
            exp = uint256(numeratorExp - denomExp);
            numOptions = numeratorVal.mul(10**exp).div(denomVal);
        }

        return numOptions;
    }

    /**
     * @notice This function calculates the amount of collateral to be paid out.
     * @dev The amount of collateral to paid out is determined by:
     * (proportion * strikePrice * strikeToCollateralPrice * oTokens) amount of collateral.
     * @param _oTokens The number of oTokens.
     * @param proportion The proportion of the collateral to pay out. If 100% of collateral
     * should be paid out, pass in Number(1, 0). The proportion might be less than 100% if
     * you are calculating fees.
     */
    function calculateCollateralToPay(
        uint256 _oTokens,
        Number memory proportion
    ) internal view returns (uint256) {
        // Get price from oracle
        uint256 collateralToEthPrice = getPrice(address(collateral));
        uint256 strikeToEthPrice = getPrice(address(strike));

        // calculate how much should be paid out
        uint256 amtCollateralToPayInEthNum = _oTokens
            .mul(strikePrice.value)
            .mul(proportion.value)
            .mul(strikeToEthPrice);
        int32 amtCollateralToPayExp = strikePrice.exponent +
            proportion.exponent -
            collateralExp;
        uint256 amtCollateralToPay = 0;
        if (amtCollateralToPayExp > 0) {
            uint32 exp = uint32(amtCollateralToPayExp);
            amtCollateralToPay = amtCollateralToPayInEthNum.mul(10**exp).div(
                collateralToEthPrice
            );
        } else {
            uint32 exp = uint32(-1 * amtCollateralToPayExp);
            amtCollateralToPay = (amtCollateralToPayInEthNum.div(10**exp)).div(
                collateralToEthPrice
            );
        }

        return amtCollateralToPay;

    }

    /**
     * @notice This function transfers `amt` collateral to `_addr`
     * @param _addr The address to send the collateral to
     * @param _amt The amount of the collateral to pay out.
     */
    function transferCollateral(address payable _addr, uint256 _amt) internal {
        if (isETH(collateral)) {
            _addr.transfer(_amt);
        } else {
            collateral.transfer(_addr, _amt);
        }
    }

    /**
     * @notice This function transfers `amt` underlying to `_addr`
     * @param _addr The address to send the underlying to
     * @param _amt The amount of the underlying to pay out.
     */
    function transferUnderlying(address payable _addr, uint256 _amt) internal {
        if (isETH(underlying)) {
            _addr.transfer(_amt);
        } else {
            underlying.transfer(_addr, _amt);
        }
    }

    /**
     * @notice This function gets the price ETH (wei) to asset price.
     * @param asset The address of the asset to get the price of
     */
    function getPrice(address asset) internal view returns (uint256) {
        if (address(collateral) == address(strike)) {
            return 1;
        } else if (asset == address(0)) {
            return (10**18);
        } else {
            return COMPOUND_ORACLE.getPrice(asset);
        }
    }
}

// File: contracts/oToken.sol

pragma solidity 0.5.10;


/**
 * @title Opyn's Options Contract
 * @author Opyn
 */

contract oToken is OptionsContract {
    /**
    * @param _collateral The collateral asset
    * @param _collExp The precision of the collateral (-18 if ETH)
    * @param _underlying The asset that is being protected
    * @param _underlyingExp The precision of the underlying asset
    * @param _oTokenExchangeExp The precision of the `amount of underlying` that 1 oToken protects
    * @param _strikePrice The amount of strike asset that will be paid out
    * @param _strikeExp The precision of the strike asset (-18 if ETH)
    * @param _strike The asset in which the insurance is calculated
    * @param _expiry The time at which the insurance expires
    * @param _optionsExchange The contract which interfaces with the exchange + oracle
    * @param _oracleAddress The address of the oracle
    * @param _windowSize UNIX time. Exercise window is from `expiry - _windowSize` to `expiry`.
    */
    constructor(
        IERC20 _collateral,
        int32 _collExp,
        IERC20 _underlying,
        int32 _underlyingExp,
        int32 _oTokenExchangeExp,
        uint256 _strikePrice,
        int32 _strikeExp,
        IERC20 _strike,
        uint256 _expiry,
        OptionsExchange _optionsExchange,
        address _oracleAddress,
        uint256 _windowSize
    )
        public
        OptionsContract(
            _collateral,
            _collExp,
            _underlying,
            _underlyingExp,
            _oTokenExchangeExp,
            _strikePrice,
            _strikeExp,
            _strike,
            _expiry,
            _optionsExchange,
            _oracleAddress,
            _windowSize
        )
    {}

    /**
     * @notice opens a Vault, adds ETH collateral, and mints new oTokens in one step
     * Remember that creating oTokens can put the owner at a risk of losing the collateral
     * if an exercise event happens.
     * The sell function provides the owner a chance to earn premiums.
     * Ensure that you create and immediately sell oTokens atmoically.
     * @param amtToCreate number of oTokens to create
     * @param receiver address to send the Options to
     */
    function createETHCollateralOption(uint256 amtToCreate, address receiver)
        external
        payable
    {
        openVault();
        addETHCollateralOption(amtToCreate, receiver);
    }

    /**
     * @notice adds ETH collateral, and mints new oTokens in one step to an existing Vault
     * Remember that creating oTokens can put the owner at a risk of losing the collateral
     * if an exercise event happens.
     * The sell function provides the owner a chance to earn premiums.
     * Ensure that you create and immediately sell oTokens atmoically.
     * @param amtToCreate number of oTokens to create
     * @param receiver address to send the Options to
     */
    function addETHCollateralOption(uint256 amtToCreate, address receiver)
        public
        payable
    {
        addETHCollateral(msg.sender);
        issueOTokens(amtToCreate, receiver);
    }

    /**
     * @notice opens a Vault, adds ETH collateral, mints new oTokens and sell in one step
     * @param amtToCreate number of oTokens to create
     * @param receiver address to receive the premiums
     */
    function createAndSellETHCollateralOption(
        uint256 amtToCreate,
        address payable receiver
    ) external payable {
        openVault();
        addETHCollateralOption(amtToCreate, address(this));
        this.approve(address(optionsExchange), amtToCreate);
        optionsExchange.sellOTokens(
            receiver,
            address(this),
            address(0),
            amtToCreate
        );
    }

    /**
     * @notice adds ETH collateral to an existing Vault, and mints new oTokens and sells the oTokens in one step
     * @param amtToCreate number of oTokens to create
     * @param receiver address to send the Options to
     */
    function addAndSellETHCollateralOption(
        uint256 amtToCreate,
        address payable receiver
    ) public payable {
        addETHCollateral(msg.sender);
        issueOTokens(amtToCreate, address(this));
        this.approve(address(optionsExchange), amtToCreate);
        optionsExchange.sellOTokens(
            receiver,
            address(this),
            address(0),
            amtToCreate
        );
    }

    /**
     * @notice opens a Vault, adds ERC20 collateral, and mints new oTokens in one step
     * Remember that creating oTokens can put the owner at a risk of losing the collateral
     * if an exercise event happens.
     * The sell function provides the owner a chance to earn premiums.
     * Ensure that you create and immediately sell oTokens atmoically.
     * @param amtToCreate number of oTokens to create
     * @param amtCollateral amount of collateral added
     * @param receiver address to send the Options to
     */
    function createERC20CollateralOption(
        uint256 amtToCreate,
        uint256 amtCollateral,
        address receiver
    ) external {
        openVault();
        addERC20CollateralOption(amtToCreate, amtCollateral, receiver);
    }

    /**
     * @notice adds ERC20 collateral, and mints new oTokens in one step
     * Remember that creating oTokens can put the owner at a risk of losing the collateral
     * if an exercise event happens.
     * The sell function provides the owner a chance to earn premiums.
     * Ensure that you create and immediately sell oTokens atmoically.
     * @param amtToCreate number of oTokens to create
     * @param amtCollateral amount of collateral added
     * @param receiver address to send the Options to
     */
    function addERC20CollateralOption(
        uint256 amtToCreate,
        uint256 amtCollateral,
        address receiver
    ) public {
        addERC20Collateral(msg.sender, amtCollateral);
        issueOTokens(amtToCreate, receiver);
    }

    /**
     * @notice opens a Vault, adds ERC20 collateral, mints new oTokens and sells the oTokens in one step
     * @param amtToCreate number of oTokens to create
     * @param amtCollateral amount of collateral added
     * @param receiver address to send the Options to
     */
    function createAndSellERC20CollateralOption(
        uint256 amtToCreate,
        uint256 amtCollateral,
        address payable receiver
    ) external {
        openVault();
        addERC20CollateralOption(amtToCreate, amtCollateral, address(this));
        this.approve(address(optionsExchange), amtToCreate);
        optionsExchange.sellOTokens(
            receiver,
            address(this),
            address(0),
            amtToCreate
        );
    }

    /**
     * @notice adds ERC20 collateral, mints new oTokens and sells the oTokens in one step
     * @param amtToCreate number of oTokens to create
     * @param amtCollateral amount of collateral added
     * @param receiver address to send the Options to
     */
    function addAndSellERC20CollateralOption(
        uint256 amtToCreate,
        uint256 amtCollateral,
        address payable receiver
    ) public {
        addERC20Collateral(msg.sender, amtCollateral);
        issueOTokens(amtToCreate, address(this));
        this.approve(address(optionsExchange), amtToCreate);
        optionsExchange.sellOTokens(
            receiver,
            address(this),
            address(0),
            amtToCreate
        );
    }
}

Contract Security Audit

Contract ABI

[{"constant":false,"inputs":[{"name":"vaultOwner","type":"address"},{"name":"amt","type":"uint256"}],"name":"addERC20Collateral","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getVaultOwners","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"amount","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"owner","type":"address"}],"name":"hasVault","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isExerciseWindow","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"vaultOwner","type":"address"}],"name":"getVault","outputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"oTokensToIssue","type":"uint256"},{"name":"receiver","type":"address"}],"name":"issueOTokens","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"sender","type":"address"},{"name":"recipient","type":"address"},{"name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"amtToCreate","type":"uint256"},{"name":"amtCollateral","type":"uint256"},{"name":"receiver","type":"address"}],"name":"addAndSellERC20CollateralOption","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"amtToRemove","type":"uint256"}],"name":"removeCollateral","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"liquidationFactor","outputs":[{"name":"value","type":"uint256"},{"name":"exponent","type":"int32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"amtToCreate","type":"uint256"},{"name":"receiver","type":"address"}],"name":"createAndSellETHCollateralOption","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"optionsExchange","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"amtToCreate","type":"uint256"},{"name":"amtCollateral","type":"uint256"},{"name":"receiver","type":"address"}],"name":"createERC20CollateralOption","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"oTokensToExercise","type":"uint256"},{"name":"vaultsToExerciseFrom","type":"address[]"}],"name":"exercise","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"amtToCreate","type":"uint256"},{"name":"amtCollateral","type":"uint256"},{"name":"receiver","type":"address"}],"name":"addERC20CollateralOption","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"collateralAmt","type":"uint256"}],"name":"maxOTokensIssuable","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"underlying","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"oTokensToExercise","type":"uint256"}],"name":"underlyingRequiredToExercise","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"renounceOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"openVault","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"COMPOUND_ORACLE","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"liquidationIncentive","outputs":[{"name":"value","type":"uint256"},{"name":"exponent","type":"int32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isOwner","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"hasExpired","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"vaultOwner","type":"address"}],"name":"addETHCollateral","outputs":[{"name":"","type":"uint256"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"transactionFee","outputs":[{"name":"value","type":"uint256"},{"name":"exponent","type":"int32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"recipient","type":"address"},{"name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"strike","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"underlyingExp","outputs":[{"name":"","type":"int32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"collateralExp","outputs":[{"name":"","type":"int32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"oTokenExchangeRate","outputs":[{"name":"value","type":"uint256"},{"name":"exponent","type":"int32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"redeemVaultBalance","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"string"},{"name":"_symbol","type":"string"}],"name":"setDetails","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"amtToCreate","type":"uint256"},{"name":"receiver","type":"address"}],"name":"addETHCollateralOption","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"minCollateralizationRatio","outputs":[{"name":"value","type":"uint256"},{"name":"exponent","type":"int32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"vaultOwner","type":"address"},{"name":"oTokensToLiquidate","type":"uint256"}],"name":"liquidate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"strikePrice","outputs":[{"name":"value","type":"uint256"},{"name":"exponent","type":"int32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"amtToCreate","type":"uint256"},{"name":"amtCollateral","type":"uint256"},{"name":"receiver","type":"address"}],"name":"createAndSellERC20CollateralOption","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"vaultOwner","type":"address"}],"name":"isUnsafe","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"amtToCreate","type":"uint256"},{"name":"receiver","type":"address"}],"name":"addAndSellETHCollateralOption","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"collateral","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"owner","type":"address"},{"name":"spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"vaultOwner","type":"address"}],"name":"maxOTokensLiquidatable","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"expiry","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_address","type":"address"}],"name":"transferFee","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"amtToBurn","type":"uint256"}],"name":"burnOTokens","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"amtToCreate","type":"uint256"},{"name":"receiver","type":"address"}],"name":"createETHCollateralOption","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"_liquidationIncentive","type":"uint256"},{"name":"_liquidationFactor","type":"uint256"},{"name":"_transactionFee","type":"uint256"},{"name":"_minCollateralizationRatio","type":"uint256"}],"name":"updateParameters","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_ierc20","type":"address"}],"name":"isETH","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[],"name":"removeUnderlying","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_collateral","type":"address"},{"name":"_collExp","type":"int32"},{"name":"_underlying","type":"address"},{"name":"_underlyingExp","type":"int32"},{"name":"_oTokenExchangeExp","type":"int32"},{"name":"_strikePrice","type":"uint256"},{"name":"_strikeExp","type":"int32"},{"name":"_strike","type":"address"},{"name":"_expiry","type":"uint256"},{"name":"_optionsExchange","type":"address"},{"name":"_oracleAddress","type":"address"},{"name":"_windowSize","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"vaultOwner","type":"address"}],"name":"VaultOpened","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"vaultOwner","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"payer","type":"address"}],"name":"ETHCollateralAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"vaultOwner","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"payer","type":"address"}],"name":"ERC20CollateralAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"issuedTo","type":"address"},{"indexed":false,"name":"oTokensIssued","type":"uint256"},{"indexed":false,"name":"vaultOwner","type":"address"}],"name":"IssuedOTokens","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"amtCollateralToPay","type":"uint256"},{"indexed":false,"name":"vaultOwner","type":"address"},{"indexed":false,"name":"liquidator","type":"address"}],"name":"Liquidate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"amtUnderlyingToPay","type":"uint256"},{"indexed":false,"name":"amtCollateralToPay","type":"uint256"},{"indexed":false,"name":"exerciser","type":"address"},{"indexed":false,"name":"vaultExercisedFrom","type":"address"}],"name":"Exercise","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"amtCollateralRedeemed","type":"uint256"},{"indexed":false,"name":"amtUnderlyingRedeemed","type":"uint256"},{"indexed":false,"name":"vaultOwner","type":"address"}],"name":"RedeemVaultBalance","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"vaultOwner","type":"address"},{"indexed":false,"name":"oTokensBurned","type":"uint256"}],"name":"BurnOTokens","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"amtRemoved","type":"uint256"},{"indexed":false,"name":"vaultOwner","type":"address"}],"name":"RemoveCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"liquidationIncentive","type":"uint256"},{"indexed":false,"name":"liquidationFactor","type":"uint256"},{"indexed":false,"name":"transactionFee","type":"uint256"},{"indexed":false,"name":"minCollateralizationRatio","type":"uint256"},{"indexed":false,"name":"owner","type":"address"}],"name":"UpdateParameters","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"to","type":"address"},{"indexed":false,"name":"fees","type":"uint256"}],"name":"TransferFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"amountUnderlying","type":"uint256"},{"indexed":false,"name":"vaultOwner","type":"address"}],"name":"RemoveUnderlying","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"}]

Deployed Bytecode

0x6080604052600436106103975760003560e01c806390e64d13116101dc578063c52987cf11610102578063e184c9be116100a0578063ee1eab4f1161006f578063ee1eab4f14610ed3578063f2fde38b14610f0f578063f70a250814610f42578063faa2041f14610f7557610397565b8063e184c9be14610e35578063ea8c4bcf14610e4a578063eaa376b514610e7d578063ed1f41c314610ea757610397565b8063cfbea789116100dc578063cfbea78914610d86578063d8dfeb4514610db2578063dd62ed3e14610dc7578063dec44c0b14610e0257610397565b8063c52987cf14610cff578063c56749ce14610d14578063cdb4b5c214610d5357610397565b8063b2c2b13f1161017a578063b7b090ee11610149578063b7b090ee14610b4f578063b96661ba14610c85578063ba1be55414610cb1578063bcbaf48714610cc657610397565b8063b2c2b13f14610ae2578063b6e61c0814610b10578063b736554014610b25578063b76fdb6c14610b3a57610397565b80639ed3edf0116101b65780639ed3edf014610a46578063a457c2d714610a5b578063a9059cbb14610a94578063ad8f500814610acd57610397565b806390e64d13146109f657806395d89b4114610a0b5780639ce0725114610a2057610397565b806339509351116102c15780636fd865f91161025f5780638a5e8cc71161022e5780638a5e8cc7146109a25780638c765e94146109b75780638da5cb5b146109cc5780638f32d59b146109e157610397565b80636fd865f91461091b57806370a0823114610945578063715018a61461097857806386f547121461098d57610397565b806358b36dac1161029b57806358b36dac146107f55780635ca7c8a61461089d578063686c1e21146108dc5780636f307dc31461090657610397565b8063395093511461074c5780633bd33f621461078557806352f89fe3146107b657610397565b806318160ddd116103395780633226052d116103085780633226052d146106845780633237c158146106c3578063352ade55146106ed5780633667429f1461072057610397565b806318160ddd146105c65780631a0e21bd146105db57806323b872dd14610616578063313ce5671461065957610397565b8063095ea7b311610375578063095ea7b3146104d65780630d453efb146105235780630d6cd8aa146105565780630eb9af381461056b57610397565b806301b4a3c11461039c578063060ab2ea146103e757806306fdde031461044c575b600080fd5b3480156103a857600080fd5b506103d5600480360360408110156103bf57600080fd5b506001600160a01b038135169060200135610f8a565b60408051918252519081900360200190f35b3480156103f357600080fd5b506103fc61113e565b60408051602080825283518183015283519192839290830191858101910280838360005b83811015610438578181015183820152602001610420565b505050509050019250505060405180910390f35b34801561045857600080fd5b506104616111e5565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561049b578181015183820152602001610483565b50505050905090810190601f1680156104c85780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156104e257600080fd5b5061050f600480360360408110156104f957600080fd5b506001600160a01b038135169060200135611273565b604080519115158252519081900360200190f35b34801561052f57600080fd5b5061050f6004803603602081101561054657600080fd5b50356001600160a01b0316611290565b34801561056257600080fd5b5061050f6112b5565b34801561057757600080fd5b5061059e6004803603602081101561058e57600080fd5b50356001600160a01b03166112e3565b6040805194855260208501939093528383019190915215156060830152519081900360800190f35b3480156105d257600080fd5b506103d5611317565b3480156105e757600080fd5b50610614600480360360408110156105fe57600080fd5b50803590602001356001600160a01b031661131d565b005b34801561062257600080fd5b5061050f6004803603606081101561063957600080fd5b506001600160a01b03813581169160208101359091169060400135611486565b34801561066557600080fd5b5061066e611513565b6040805160ff9092168252519081900360200190f35b34801561069057600080fd5b50610614600480360360608110156106a757600080fd5b50803590602081013590604001356001600160a01b031661151c565b3480156106cf57600080fd5b50610614600480360360208110156106e657600080fd5b5035611634565b3480156106f957600080fd5b50610702611824565b60408051928352600391820b90910b60208301528051918290030190f35b6106146004803603604081101561073657600080fd5b50803590602001356001600160a01b0316611830565b34801561075857600080fd5b5061050f6004803603604081101561076f57600080fd5b506001600160a01b038135169060200135611945565b34801561079157600080fd5b5061079a611999565b604080516001600160a01b039092168252519081900360200190f35b3480156107c257600080fd5b50610614600480360360608110156107d957600080fd5b50803590602081013590604001356001600160a01b03166119a8565b6106146004803603604081101561080b57600080fd5b81359190810190604081016020820135600160201b81111561082c57600080fd5b82018360208201111561083e57600080fd5b803590602001918460208302840111600160201b8311171561085f57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506119c1945050505050565b3480156108a957600080fd5b50610614600480360360608110156108c057600080fd5b50803590602081013590604001356001600160a01b0316611ad9565b3480156108e857600080fd5b506103d5600480360360208110156108ff57600080fd5b5035611aee565b34801561091257600080fd5b5061079a611b1c565b34801561092757600080fd5b506103d56004803603602081101561093e57600080fd5b5035611b2b565b34801561095157600080fd5b506103d56004803603602081101561096857600080fd5b50356001600160a01b0316611b6f565b34801561098457600080fd5b50610614611b8a565b34801561099957600080fd5b5061050f611c1b565b3480156109ae57600080fd5b5061079a611d88565b3480156109c357600080fd5b50610702611d97565b3480156109d857600080fd5b5061079a611da3565b3480156109ed57600080fd5b5061050f611db2565b348015610a0257600080fd5b5061050f611dd6565b348015610a1757600080fd5b50610461611ddf565b6103d560048036036020811015610a3657600080fd5b50356001600160a01b0316611e3a565b348015610a5257600080fd5b50610702611f74565b348015610a6757600080fd5b5061050f60048036036040811015610a7e57600080fd5b506001600160a01b038135169060200135611f80565b348015610aa057600080fd5b5061050f60048036036040811015610ab757600080fd5b506001600160a01b038135169060200135611fee565b348015610ad957600080fd5b5061079a612002565b348015610aee57600080fd5b50610af7612011565b60408051600392830b90920b8252519081900360200190f35b348015610b1c57600080fd5b50610af7612021565b348015610b3157600080fd5b5061070261202a565b348015610b4657600080fd5b50610614612036565b348015610b5b57600080fd5b5061061460048036036040811015610b7257600080fd5b810190602081018135600160201b811115610b8c57600080fd5b820183602082011115610b9e57600080fd5b803590602001918460018302840111600160201b83111715610bbf57600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295949360208101935035915050600160201b811115610c1157600080fd5b820183602082011115610c2357600080fd5b803590602001918460018302840111600160201b83111715610c4457600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550612142945050505050565b61061460048036036040811015610c9b57600080fd5b50803590602001356001600160a01b03166121d3565b348015610cbd57600080fd5b506107026121e7565b348015610cd257600080fd5b5061061460048036036040811015610ce957600080fd5b506001600160a01b0381351690602001356121f3565b348015610d0b57600080fd5b506107026124f7565b348015610d2057600080fd5b5061061460048036036060811015610d3757600080fd5b50803590602081013590604001356001600160a01b0316612503565b348015610d5f57600080fd5b5061050f60048036036020811015610d7657600080fd5b50356001600160a01b0316612517565b61061460048036036040811015610d9c57600080fd5b50803590602001356001600160a01b031661253c565b348015610dbe57600080fd5b5061079a612550565b348015610dd357600080fd5b506103d560048036036040811015610dea57600080fd5b506001600160a01b0381358116916020013516612566565b348015610e0e57600080fd5b506103d560048036036020811015610e2557600080fd5b50356001600160a01b0316612591565b348015610e4157600080fd5b506103d561265c565b348015610e5657600080fd5b5061061460048036036020811015610e6d57600080fd5b50356001600160a01b0316612662565b348015610e8957600080fd5b5061061460048036036020811015610ea057600080fd5b5035612704565b61061460048036036040811015610ebd57600080fd5b50803590602001356001600160a01b0316612806565b348015610edf57600080fd5b5061061460048036036080811015610ef657600080fd5b5080359060208101359060408101359060600135612819565b348015610f1b57600080fd5b5061061460048036036020811015610f3257600080fd5b50356001600160a01b03166129f1565b348015610f4e57600080fd5b5061050f60048036036020811015610f6557600080fd5b50356001600160a01b0316612a44565b348015610f8157600080fd5b50610614612a51565b6000610f94611dd6565b15610fd4576040805162461bcd60e51b81526020600482015260186024820152600080516020614105833981519152604482015290519081900360640190fd5b601654604080516323b872dd60e01b8152336004820152306024820152604481018590529051600160401b9092046001600160a01b0316916323b872dd916064808201926020929091908290030181600087803b15801561103457600080fd5b505af1158015611048573d6000803e3d6000fd5b505050506040513d602081101561105e57600080fd5b505161109b5760405162461bcd60e51b8152600401808060200182810382526027815260200180613e8b6027913960400191505060405180910390fd5b6110a483611290565b6110e3576040805162461bcd60e51b81526020600482015260146024820152600080516020613eb2833981519152604482015290519081900360640190fd5b604080516001600160a01b038516815260208101849052338183015290517f2199418ea9428ed3ff7d460860e1edaf5831452fa4ea0f8d1a60d63c603487829181900360600190a16111358383612b49565b90505b92915050565b6060806000805b6006548110156111dc576111796006828154811061115f57fe5b6000918252602090912001546001600160a01b0316611290565b156111d4576006818154811061118b57fe5b9060005260206000200160009054906101000a90046001600160a01b03168383815181106111b557fe5b6001600160a01b03909216602092830291909101909101526001909101905b600101611145565b50909150505b90565b601a805460408051602060026001851615610100026000190190941693909304601f8101849004840282018401909252818152929183018282801561126b5780601f106112405761010080835404028352916020019161126b565b820191906000526020600020905b81548152906001019060200180831161124e57829003601f168201915b505050505081565b6000611287611280612bc8565b8484612bcc565b50600192915050565b6001600160a01b03811660009081526005602052604090206003015460ff165b919050565b60006112ce601354601554612cb890919063ffffffff16565b42101580156112de575060155442105b905090565b6001600160a01b03166000908152600560205260409020805460018201546002830154600390930154919390929160ff1690565b60035490565b611325611dd6565b15611365576040805162461bcd60e51b81526020600482015260186024820152600080516020614105833981519152604482015290519081900360640190fd5b61136e33611290565b6113ad576040805162461bcd60e51b81526020600482015260146024820152600080516020613eb2833981519152604482015290519081900360640190fd5b33600090815260056020526040812060018101549091906113d4908563ffffffff612cfa16565b90506113e4826000015482612d54565b611426576040805162461bcd60e51b815260206004820152600e60248201526d1d5b9cd85999481d1bc81b5a5b9d60921b604482015290519081900360640190fd5b600182018190556114378385612e59565b604080516001600160a01b038516815260208101869052338183015290517f5e5aaabf04e3760968ffb551bdf9708f4dbf95d53ad98539e91a56b125e88f089181900360600190a150505b5050565b6000611493848484612f4b565b6115098461149f612bc8565b61150485604051806060016040528060288152602001613ff6602891396001600160a01b038a166000908152600260205260408120906114dd612bc8565b6001600160a01b03168152602081019190915260400160002054919063ffffffff6130a916565b612bcc565b5060019392505050565b601c5460ff1681565b6115263383610f8a565b50611531833061131d565b600480546040805163095ea7b360e01b81526001600160a01b0390921692820192909252602481018590529051309163095ea7b39160448083019260209291908290030181600087803b15801561158757600080fd5b505af115801561159b573d6000803e3d6000fd5b505050506040513d60208110156115b157600080fd5b50506004805460408051637dafae5960e01b81526001600160a01b03858116948201949094523060248201526000604482018190526064820188905291519390921692637dafae5992608480820193929182900301818387803b15801561161757600080fd5b505af115801561162b573d6000803e3d6000fd5b50505050505050565b61163c611dd6565b1561167c576040805162461bcd60e51b81526020600482015260186024820152600080516020614105833981519152604482015290519081900360640190fd5b600081116116d1576040805162461bcd60e51b815260206004820152601a60248201527f43616e6e6f742072656d6f7665203020636f6c6c61746572616c000000000000604482015290519081900360640190fd5b6116da33611290565b611719576040805162461bcd60e51b81526020600482015260146024820152600080516020613eb2833981519152604482015290519081900360640190fd5b3360008181526005602052604090209061173290613140565b8211156117705760405162461bcd60e51b8152600401808060200182810382526027815260200180613fa26027913960400191505060405180910390fd5b8054600090611785908463ffffffff612cb816565b9050611795818360010154612d54565b6117d8576040805162461bcd60e51b815260206004820152600f60248201526e5661756c7420697320756e7361666560881b604482015290519081900360640190fd5b8082556117e5338461315b565b6040805184815233602082015281517f5a945309b3c58e9bb259128c2a530a6579dc75ac1d7d61b3db4c0b8305a16821929181900390910190a1505050565b600b54600c5460030b82565b611838611c1b565b5061184382306121d3565b600480546040805163095ea7b360e01b81526001600160a01b0390921692820192909252602481018490529051309163095ea7b39160448083019260209291908290030181600087803b15801561189957600080fd5b505af11580156118ad573d6000803e3d6000fd5b505050506040513d60208110156118c357600080fd5b50506004805460408051637dafae5960e01b81526001600160a01b03858116948201949094523060248201526000604482018190526064820187905291519390921692637dafae5992608480820193929182900301818387803b15801561192957600080fd5b505af115801561193d573d6000803e3d6000fd5b505050505050565b6000611287611952612bc8565b846115048560026000611963612bc8565b6001600160a01b03908116825260208083019390935260409182016000908120918c16815292529020549063ffffffff612cfa16565b6004546001600160a01b031681565b6119b0611c1b565b506119bc838383611ad9565b505050565b60005b8151811015611a9b5760008282815181106119db57fe5b602002602001015190506119ee81611290565b611a295760405162461bcd60e51b815260040180806020018281038252602f81526020018061405f602f913960400191505060405180910390fd5b6001600160a01b038116600090815260056020526040902084611a4e57505050611482565b84816001015410611a6b57611a638583613246565b505050611482565b6001810154611a8190869063ffffffff612cb816565b9450611a91816001015483613246565b50506001016119c4565b5081156114825760405162461bcd60e51b815260040180806020018281038252602d815260200180613f1d602d913960400191505060405180910390fd5b611ae33383610f8a565b506119bc838261131d565b60408051808201909152600d548152600e54600390810b810b900b60208201526000906111389083906136a9565b6017546001600160a01b031681565b601654601254600091600160201b9004600390810b91810b91909103900b611b688367ffffffffffffffff808416600a0a1663ffffffff6137a416565b9392505050565b6001600160a01b031660009081526001602052604090205490565b611b92611db2565b611bd1576040805162461bcd60e51b8152602060048201819052602482015260008051602061401e833981519152604482015290519081900360640190fd5b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b6000611c25611dd6565b15611c65576040805162461bcd60e51b81526020600482015260186024820152600080516020614105833981519152604482015290519081900360640190fd5b611c6e33611290565b15611cb8576040805162461bcd60e51b815260206004820152601560248201527415985d5b1d08185b1c9958591e4818dc99585d1959605a1b604482015290519081900360640190fd5b60408051608081018252600080825260208083018281528385018381526001606086018181523380875260058652888720975188559351878301559151600287015590516003909501805460ff1916951515959095179094556006805494850181559092527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f90920180546001600160a01b03191682179055825190815291517f66a872561db77eb92ef3079a44a5af00c68c3a09e0976814a95bd91721f57c2f9281900390910190a150600190565b6019546001600160a01b031681565b60075460085460030b82565b6000546001600160a01b031690565b600080546001600160a01b0316611dc7612bc8565b6001600160a01b031614905090565b60155442101590565b601b805460408051602060026001851615610100026000190190941693909304601f8101849004840282018401909252818152929183018282801561126b5780601f106112405761010080835404028352916020019161126b565b6000611e44611dd6565b15611e84576040805162461bcd60e51b81526020600482015260186024820152600080516020614105833981519152604482015290519081900360640190fd5b601654611ea090600160401b90046001600160a01b0316612a44565b611edb5760405162461bcd60e51b8152600401808060200182810382526028815260200180613df66028913960400191505060405180910390fd5b611ee482611290565b611f23576040805162461bcd60e51b81526020600482015260146024820152600080516020613eb2833981519152604482015290519081900360640190fd5b604080516001600160a01b0384168152346020820152338183015290517ff24ce6016de57e90501829715846e26ac283a0aabfc160647e0ae8b05e0f433d9181900360600190a16111388234612b49565b600954600a5460030b82565b6000611287611f8d612bc8565b84611504856040518060600160405280602581526020016141256025913960026000611fb7612bc8565b6001600160a01b03908116825260208083019390935260409182016000908120918d1681529252902054919063ffffffff6130a916565b6000611287611ffb612bc8565b8484612f4b565b6018546001600160a01b031681565b601654600160201b900460030b81565b60165460030b81565b60115460125460030b82565b61203e611dd6565b6120795760405162461bcd60e51b8152600401808060200182810382526025815260200180613e666025913960400191505060405180910390fd5b61208233611290565b6120c1576040805162461bcd60e51b81526020600482015260146024820152600080516020613eb2833981519152604482015290519081900360640190fd5b33600081815260056020526040812080546002820180548484556001840185905593905590929091906120f4908361315b565b6120fe33826137fd565b6040805183815260208101839052338183015290517fe481532a3f7d078365ca0145442ed0a0a3e0443f3c0bae0c29cff131112678389181900360600190a1505050565b61214a611db2565b612189576040805162461bcd60e51b8152602060048201819052602482015260008051602061401e833981519152604482015290519081900360640190fd5b815161219c90601a906020850190613cae565b5080516121b090601b906020840190613cae565b50601254601c805460ff191660039290920b60000360ff16919091179055611482565b6121dc33611e3a565b50611482828261131d565b600d54600e5460030b82565b6121fb611dd6565b1561223b576040805162461bcd60e51b81526020600482015260186024820152600080516020614105833981519152604482015290519081900360640190fd5b61224482611290565b612283576040805162461bcd60e51b81526020600482015260146024820152600080516020613eb2833981519152604482015290519081900360640190fd5b6001600160a01b03821660009081526005602052604090206122a483612517565b6122e5576040805162461bcd60e51b815260206004820152600d60248201526c5661756c74206973207361666560981b604482015290519081900360640190fd5b336001600160a01b0384161415612343576040805162461bcd60e51b815260206004820181905260248201527f4f776e65722063616e2774206c6971756964617465207468656d73656c766573604482015290519081900360640190fd5b600061236883604051806040016040528060018152602001600060030b8152506138a3565b604080518082019091526007548152600854600390810b810b900b60208201529091506000906123999085906138a3565b905060006123ad838363ffffffff612cfa16565b600b5485549192506000916123c79163ffffffff6137a416565b600c549091506000600391820b90910b131561240257600c546123fb908290600390810b900b600a0a63ffffffff6137a416565b9050612426565b600c54612423908290600390810b600003900b600a0a63ffffffff61398516565b90505b808211156124655760405162461bcd60e51b8152600401808060200182810382526037815260200180613f4a6037913960400191505060405180910390fd5b8454612477908363ffffffff612cb816565b8555600185015461248e908763ffffffff612cb816565b600186015561249d33876139c7565b6124a7338361315b565b604080518381526001600160a01b0389166020820152338183015290517fcab8e1abb9f8235c6db895cf185336dc9461aecf477b98c1be83687ee549e66a9181900360600190a150505050505050565b600f5460105460030b82565b61250b611c1b565b50611531838330611ad9565b60008061253461252684613140565b61252f85613ac3565b612d54565b159392505050565b61254533611e3a565b50611843823061131d565b601654600160401b90046001600160a01b031681565b6001600160a01b03918216600090815260026020908152604080832093909416825291909152205490565b600061259c82612517565b15612654576001600160a01b0382166000908152600560205260408120600c54600b5482549293926125f192600390810b8503900b600a0a916125e5919063ffffffff6137a416565b9063ffffffff61398516565b600854909150600390810b600003900b600a0a61260c613d2c565b60408051808201909152600754819061262b908563ffffffff612cfa16565b8152600854600390810b900b602090910152905061264983826136a9565b9450505050506112b0565b5060006112b0565b60155481565b61266a611db2565b6126a9576040805162461bcd60e51b8152602060048201819052602482015260008051602061401e833981519152604482015290519081900360640190fd5b6014805460009091556126bc828261315b565b604080516001600160a01b03841681526020810183905281517f88b171bb78d3ac5e1caa8e729dddce4e1322e84c80c093ebbe52507b62c77d98929181900390910190a15050565b61270c611dd6565b1561274c576040805162461bcd60e51b81526020600482015260186024820152600080516020614105833981519152604482015290519081900360640190fd5b61275533611290565b612794576040805162461bcd60e51b81526020600482015260146024820152600080516020613eb2833981519152604482015290519081900360640190fd5b33600090815260056020526040902060018101546127b8908363ffffffff612cb816565b60018201556127c733836139c7565b604080513381526020810184905281517fdf8cebdea6ef1fd20576b80bc951377c0e61e2a8169153a1f836673ccce80e62929181900390910190a15050565b61280e611c1b565b5061148282826121d3565b612821611db2565b612860576040805162461bcd60e51b8152602060048201819052602482015260008051602061401e833981519152604482015290519081900360640190fd5b60c88411156128a05760405162461bcd60e51b8152600401808060200182810382526025815260200180613ef86025913960400191505060405180910390fd5b6103e88311156128e15760405162461bcd60e51b815260040180806020018281038252602b815260200180613da9602b913960400191505060405180910390fd5b6064821115612937576040805162461bcd60e51b815260206004820181905260248201527f43616e27742068617665207472616e73616374696f6e20666565203e20313025604482015290519081900360640190fd5b600a8110156129775760405162461bcd60e51b8152600401808060200182810382526028815260200180613d816028913960400191505060405180910390fd5b6007849055600b8390556009829055600d8190557f3450d20c21ea671871fed271900cc8ff03badafa9b6fe2ff7f86991950e86b6b848484846129b8611da3565b6040805195865260208601949094528484019290925260608401526001600160a01b03166080830152519081900360a00190a150505050565b6129f9611db2565b612a38576040805162461bcd60e51b8152602060048201819052602482015260008051602061401e833981519152604482015290519081900360640190fd5b612a4181613ae1565b50565b6001600160a01b03161590565b612a5a33611290565b612a99576040805162461bcd60e51b81526020600482015260146024820152600080516020613eb2833981519152604482015290519081900360640190fd5b3360009081526005602052604090206002810154612af6576040805162461bcd60e51b81526020600482015260156024820152744e6f20756e6465726c79696e672062616c616e636560581b604482015290519081900360640190fd5b6002810180546000909155612b0b33826137fd565b6040805182815233602082015281517fea0bff65fa9380b944e9a761f9c6a665ad2d31e74706a52773ddb45c8a57c83d929181900390910190a15050565b6000612b53611dd6565b15612b93576040805162461bcd60e51b81526020600482015260186024820152600080516020614105833981519152604482015290519081900360640190fd5b6001600160a01b03831660009081526005602052604090208054612bbd908463ffffffff612cfa16565b908190559392505050565b3390565b6001600160a01b038316612c115760405162461bcd60e51b81526004018080602001828103825260248152602001806140b36024913960400191505060405180910390fd5b6001600160a01b038216612c565760405162461bcd60e51b8152600401808060200182810382526022815260200180613e446022913960400191505060405180910390fd5b6001600160a01b03808416600081815260026020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b600061113583836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506130a9565b600082820183811015611135576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b600080612d75601660089054906101000a90046001600160a01b0316613b81565b601854909150600090612d90906001600160a01b0316613b81565b600f54600d54919250600091612dbe9190612db290889063ffffffff6137a416565b9063ffffffff6137a416565b601054600e54919250600391820b910b016000612de5846125e58a8863ffffffff6137a416565b601654909150600390810b90600090819085810b9084900b1215612e295782850360030b915083612e2087600a85900a63ffffffff6137a416565b11159050612e4b565b84830360030b9150612e4584600a84900a63ffffffff6137a416565b86111590505b9a9950505050505050505050565b6001600160a01b038216612eb4576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b600354612ec7908263ffffffff612cfa16565b6003556001600160a01b038216600090815260016020526040902054612ef3908263ffffffff612cfa16565b6001600160a01b03831660008181526001602090815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b6001600160a01b038316612f905760405162461bcd60e51b815260040180806020018281038252602581526020018061408e6025913960400191505060405180910390fd5b6001600160a01b038216612fd55760405162461bcd60e51b8152600401808060200182810382526023815260200180613d5e6023913960400191505060405180910390fd5b61301881604051806060016040528060268152602001613ed2602691396001600160a01b038616600090815260016020526040902054919063ffffffff6130a916565b6001600160a01b03808516600090815260016020526040808220939093559084168152205461304d908263ffffffff612cfa16565b6001600160a01b0380841660008181526001602090815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b600081848411156131385760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156130fd5781810151838201526020016130e5565b50505050905090810190601f16801561312a5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b6001600160a01b031660009081526005602052604090205490565b60165461317790600160401b90046001600160a01b0316612a44565b156131b8576040516001600160a01b0383169082156108fc029083906000818181858888f193505050501580156131b2573d6000803e3d6000fd5b50611482565b6016546040805163a9059cbb60e01b81526001600160a01b038581166004830152602482018590529151600160401b9093049091169163a9059cbb916044808201926020929091908290030181600087803b15801561321657600080fd5b505af115801561322a573d6000803e3d6000fd5b505050506040513d602081101561324057600080fd5b50505050565b61324e6112b5565b6132895760405162461bcd60e51b815260040180806020018281038252602d815260200180613fc9602d913960400191505060405180910390fd5b61329281611290565b6132d1576040805162461bcd60e51b81526020600482015260146024820152600080516020613eb2833981519152604482015290519081900360640190fd5b6001600160a01b03811660009081526005602052604090208261333b576040805162461bcd60e51b815260206004820152601860248201527f43616e27742065786572636973652030206f546f6b656e730000000000000000604482015290519081900360640190fd5b806001015483111561337e5760405162461bcd60e51b815260040180806020018281038252602e8152602001806140d7602e913960400191505060405180910390fd5b8261338833611b6f565b10156133d0576040805162461bcd60e51b81526020600482015260126024820152714e6f7420656e6f756768206f546f6b656e7360701b604482015290519081900360640190fd5b60006133db84611b2b565b60028301549091506133f3908263ffffffff612cfa16565b600283015560408051808201909152600181526000602082018190529061341b9086906138a3565b604080518082019091526009548152600a54600390810b810b900b602082015290915060009061344c9087906138a3565b601454909150613462908263ffffffff612cfa16565b6014556000613477838363ffffffff612cfa16565b85549091508111156134d0576040805162461bcd60e51b815260206004820181905260248201527f5661756c7420756e64657277617465722c2063616e2774206578657263697365604482015290519081900360640190fd5b84546134e2908263ffffffff612cb816565b855560018501546134f9908863ffffffff612cb816565b6001860155601754613513906001600160a01b0316612a44565b1561356757833414613562576040805162461bcd60e51b8152602060048201526013602482015272496e636f7272656374206d73672e76616c756560681b604482015290519081900360640190fd5b61363e565b601754604080516323b872dd60e01b81523360048201523060248201526044810187905290516001600160a01b03909216916323b872dd916064808201926020929091908290030181600087803b1580156135c157600080fd5b505af11580156135d5573d6000803e3d6000fd5b505050506040513d60208110156135eb57600080fd5b505161363e576040805162461bcd60e51b815260206004820152601c60248201527f436f756c64206e6f74207472616e7366657220696e20746f6b656e7300000000604482015290519081900360640190fd5b61364833886139c7565b613652338461315b565b604080518581526020810185905233818301526001600160a01b038816606082015290517ffa7bab37479e50a9b24a9412b879d400de9bcaa1e3a2b343e90bb370d85bbaa79181900360800190a150505050505050565b6000806136ca601660089054906101000a90046001600160a01b0316613b81565b6018549091506000906136e5906001600160a01b0316613b81565b600f5485519192506000916136ff9163ffffffff6137a416565b601054602087015191925060030b016000613724846125e58a8863ffffffff6137a416565b601654909150600390810b90600090819085810b9084900b12156137755782850360030b915061376e61376187600a85900a63ffffffff6137a416565b859063ffffffff61398516565b9050612e4b565b84830360030b9150613795866125e586600a86900a63ffffffff6137a416565b9b9a5050505050505050505050565b6000826137b357506000611138565b828202828482816137c057fe5b04146111355760405162461bcd60e51b8152600401808060200182810382526021815260200180613f816021913960400191505060405180910390fd5b601754613812906001600160a01b0316612a44565b1561384d576040516001600160a01b0383169082156108fc029083906000818181858888f193505050501580156131b2573d6000803e3d6000fd5b6017546040805163a9059cbb60e01b81526001600160a01b038581166004830152602482018590529151919092169163a9059cbb9160448083019260209291908290030181600087803b15801561321657600080fd5b6000806138c4601660089054906101000a90046001600160a01b0316613b81565b6018549091506000906138df906001600160a01b0316613b81565b9050600061390782612db28760000151612db2600f600001548b6137a490919063ffffffff16565b6016546020870151601054929350600391820b92820b01919091039060009082900b811215613955578161394d866125e58663ffffffff808616600a0a8116906137a416565b91505061397a565b6000829003613976866125e58663ffffffff808616600a0a81169061398516565b9150505b979650505050505050565b600061113583836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250613c49565b6001600160a01b038216613a0c5760405162461bcd60e51b815260040180806020018281038252602181526020018061403e6021913960400191505060405180910390fd5b613a4f81604051806060016040528060228152602001613dd4602291396001600160a01b038516600090815260016020526040902054919063ffffffff6130a916565b6001600160a01b038316600090815260016020526040902055600354613a7b908263ffffffff612cb816565b6003556040805182815290516000916001600160a01b038516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9181900360200190a35050565b6001600160a01b031660009081526005602052604090206001015490565b6001600160a01b038116613b265760405162461bcd60e51b8152600401808060200182810382526026815260200180613e1e6026913960400191505060405180910390fd5b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b6018546016546000916001600160a01b03908116600160401b909204161415613bac575060016112b0565b6001600160a01b038216613bc95750670de0b6b3a76400006112b0565b601954604080516341976e0960e01b81526001600160a01b038581166004830152915191909216916341976e09916024808301926020929190829003018186803b158015613c1657600080fd5b505afa158015613c2a573d6000803e3d6000fd5b505050506040513d6020811015613c4057600080fd5b505190506112b0565b60008183613c985760405162461bcd60e51b81526020600482018181528351602484015283519092839260449091019190850190808383600083156130fd5781810151838201526020016130e5565b506000838581613ca457fe5b0495945050505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10613cef57805160ff1916838001178555613d1c565b82800160010185558215613d1c579182015b82811115613d1c578251825591602001919060010190613d01565b50613d28929150613d43565b5090565b604080518082019091526000808252602082015290565b6111e291905b80821115613d285760008155600101613d4956fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737343616e27742068617665206d696e436f6c6c61746572616c697a6174696f6e526174696f203c203143616e2774206c6971756964617465206d6f7265207468616e2031303025206f6620746865207661756c7445524332303a206275726e20616d6f756e7420657863656564732062616c616e6365455448206973206e6f74207468652073706563696669656420636f6c6c61746572616c20747970654f776e61626c653a206e6577206f776e657220697320746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f206164647265737343616e277420636f6c6c65637420636f6c6c61746572616c20756e74696c20657870697279436f756c64206e6f74207472616e7366657220696e20636f6c6c61746572616c20746f6b656e735661756c7420646f6573206e6f7420657869737400000000000000000000000045524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636543616e27742068617665203e323025206c69717569646174696f6e20696e63656e74697665537065636966696564207661756c7473206861766520696e73756666696369656e7420636f6c6c61746572616c43616e206f6e6c79206c6971756964617465206c69717569646174696f6e20666163746f7220617420616e7920676976656e2074696d65536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7743616e27742072656d6f7665206d6f726520636f6c6c61746572616c207468616e206f776e656443616e2774206578657263697365206f757473696465206f66207468652065786572636973652077696e646f7745524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e63654f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657245524332303a206275726e2066726f6d20746865207a65726f206164647265737343616e6e6f742065786572636973652066726f6d2061207661756c74207468617420646f65736e277420657869737445524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737343616e2774206578657263697365206d6f7265206f546f6b656e73207468616e20746865206f776e6572206861734f7074696f6e7320636f6e74726163742065787069726564000000000000000045524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa265627a7a7230582024408987c6dcfcda4c0e73ff0354366a3d057faa494c86e0ef89208047b75fda64736f6c634300050a0032

Deployed Bytecode Sourcemap

76622:7558:0:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;51764:480;;8:9:-1;5:2;;;30:1;27;20:12;5:2;51764:480:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;51764:480:0;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;45813:383;;8:9:-1;5:2;;;30:1;27;20:12;5:2;45813:383:0;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:100:-1;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;45813:383:0;;;;;;;;;;;;;;;;;41356:18;;8:9:-1;5:2;;;30:1;27;20:12;5:2;41356:18:0;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:100:-1;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;41356:18:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;12136:152;;8:9:-1;5:2;;;30:1;27;20:12;5:2;12136:152:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;12136:152:0;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;49046:113;;8:9:-1;5:2;;;30:1;27;20:12;5:2;49046:113:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;49046:113:0;-1:-1:-1;;;;;49046:113:0;;:::i;52762:169::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;52762:169:0;;;:::i;57143:342::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;57143:342:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;57143:342:0;-1:-1:-1;;;;;57143:342:0;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;11157:91;;8:9:-1;5:2;;;30:1;27;20:12;5:2;11157:91:0;;;:::i;56216:788::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;56216:788:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;56216:788:0;;;;;;-1:-1:-1;;;;;56216:788:0;;:::i;:::-;;12760:304;;8:9:-1;5:2;;;30:1;27;20:12;5:2;12760:304:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;12760:304:0;;;;;;;;;;;;;;;;;:::i;41495:21::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;41495:21:0;;;:::i;:::-;;;;;;;;;;;;;;;;;;;83697:480;;8:9:-1;5:2;;;30:1;27;20:12;5:2;83697:480:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;83697:480:0;;;;;;;;;;;-1:-1:-1;;;;;83697:480:0;;:::i;58613:870::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;58613:870:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;58613:870:0;;:::i;39993:49::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;39993:49:0;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;79931:435;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;79931:435:0;;;;;;-1:-1:-1;;;;;79931:435:0;;:::i;13473:210::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;13473:210:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;13473:210:0;;;;;;;;:::i;39532:38::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;39532:38:0;;;:::i;:::-;;;;-1:-1:-1;;;;;39532:38:0;;;;;;;;;;;;;;81607:245;;8:9:-1;5:2;;;30:1;27;20:12;5:2;81607:245:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;81607:245:0;;;;;;;;;;;-1:-1:-1;;;;;81607:245:0;;:::i;53860:1013::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;53860:1013:0;;;;;;;;;;;;;;-1:-1:-1;;;5:28;;2:2;;;46:1;43;36:12;2:2;53860:1013:0;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;53860:1013:0;;;;;;101:9:-1;95:2;81:12;77:21;67:8;63:36;60:51;-1:-1;;;25:12;22:29;11:108;8:2;;;132:1;129;122:12;8:2;53860:1013:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;74:27;;;;-1:-1;53860:1013:0;;-1:-1:-1;53860:1013:0;;-1:-1:-1;;;;;53860:1013:0:i;82391:247::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;82391:247:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;82391:247:0;;;;;;;;;;;-1:-1:-1;;;;;82391:247:0;;:::i;71588:199::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;71588:199:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;71588:199:0;;:::i;41108:24::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;41108:24:0;;;:::i;52361:320::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;52361:320:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;52361:320:0;;:::i;11311:110::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;11311:110:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;11311:110:0;-1:-1:-1;;;;;11311:110:0;;:::i;38138:140::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;38138:140:0;;;:::i;49281:284::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;49281:284:0;;;:::i;41267:46::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;41267:46:0;;;:::i;39714:51::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;39714:51:0;;;:::i;37327:79::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;37327:79:0;;;:::i;37693:94::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;37693:94:0;;;:::i;53021:102::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;53021:102:0;;;:::i;41419:20::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;41419:20:0;;;:::i;50369:418::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;50369:418:0;-1:-1:-1;;;;;50369:418:0;;:::i;39808:44::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;39808:44:0;;;:::i;14186:261::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;14186:261:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;14186:261:0;;;;;;;;:::i;11634:158::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;11634:158:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;11634:158:0;;;;;;;;:::i;41197:20::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;41197:20:0;;;:::i;40954:32::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;40954:32:0;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;40873;;8:9:-1;5:2;;;30:1;27;20:12;5:2;40873:32:0;;;:::i;40448:::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;40448:32:0;;;:::i;59745:800::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;59745:800:0;;;:::i;48110:361::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;48110:361:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;48110:361:0;;;;;;;;-1:-1:-1;;;5:28;;2:2;;;46:1;43;36:12;2:2;48110:361:0;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;48110:361:0;;;;;;100:9:-1;95:1;81:12;77:20;67:8;63:35;60:50;-1:-1;;;25:12;22:29;11:107;8:2;;;131:1;128;121:12;8:2;48110:361:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;74:27;;;;-1:-1;48110:361:0;;;;;;;;-1:-1:-1;48110:361:0;;-1:-1:-1;;;;;5:28;;2:2;;;46:1;43;36:12;2:2;48110:361:0;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;48110:361:0;;;;;;100:9:-1;95:1;81:12;77:20;67:8;63:35;60:50;-1:-1;;;25:12;22:29;11:107;8:2;;;131:1;128;121:12;8:2;48110:361:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;74:27;;;;-1:-1;48110:361:0;;-1:-1:-1;48110:361:0;;-1:-1:-1;;;;;48110:361:0:i;79501:202::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;79501:202:0;;;;;;-1:-1:-1;;;;;79501:202:0;;:::i;40240:56::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;40240:56:0;;;:::i;62149:1984::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;62149:1984:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;62149:1984:0;;;;;;;;:::i;40357:25::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;40357:25:0;;;:::i;82936:478::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;82936:478:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;82936:478:0;;;;;;;;;;;-1:-1:-1;;;;;82936:478:0;;:::i;64321:241::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;64321:241:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;64321:241:0;-1:-1:-1;;;;;64321:241:0;;:::i;80616:437::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;80616:437:0;;;;;;-1:-1:-1;;;;;80616:437:0;;:::i;41024:24::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;41024:24:0;;;:::i;11855:134::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;11855:134:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;11855:134:0;;;;;;;;;;:::i;60742:790::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;60742:790:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;60742:790:0;-1:-1:-1;;;;;60742:790:0;;:::i;40803:21::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;40803:21:0;;;:::i;48652:219::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;48652:219:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;48652:219:0;-1:-1:-1;;;;;48652:219:0;;:::i;58007:343::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;58007:343:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;58007:343:0;;:::i;78799:200::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;78799:200:0;;;;;;-1:-1:-1;;;;;78799:200:0;;:::i;46759:1126::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;46759:1126:0;;;;;;13:3:-1;8;5:12;2:2;;;30:1;27;20:12;2:2;-1:-1;46759:1126:0;;;;;;;;;;;;;;;;;:::i;38433:109::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;38433:109:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;38433:109:0;-1:-1:-1;;;;;38433:109:0;;:::i;57607:104::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;57607:104:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;57607:104:0;-1:-1:-1;;;;;57607:104:0;;:::i;55006:449::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;55006:449:0;;;:::i;51764:480::-;51887:7;45666:12;:10;:12::i;:::-;45665:13;45657:50;;;;;-1:-1:-1;;;45657:50:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;45657:50:0;;;;;;;;;;;;;;;51934:10;;:55;;;-1:-1:-1;;;51934:55:0;;51958:10;51934:55;;;;51978:4;51934:55;;;;;;;;;;;;-1:-1:-1;;;51934:10:0;;;-1:-1:-1;;;;;51934:10:0;;:23;;:55;;;;;;;;;;;;;;;-1:-1:-1;51934:10:0;:55;;;5:2:-1;;;;30:1;27;20:12;5:2;51934:55:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;51934:55:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;51934:55:0;51912:144;;;;-1:-1:-1;;;51912:144:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;52075:20;52084:10;52075:8;:20::i;:::-;52067:53;;;;;-1:-1:-1;;;52067:53:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;52067:53:0;;;;;;;;;;;;;;;52138:49;;;-1:-1:-1;;;;;52138:49:0;;;;;;;;;;52176:10;52138:49;;;;;;;;;;;;;;;52205:31;52220:10;52232:3;52205:14;:31::i;:::-;52198:38;;45718:1;51764:480;;;;:::o;45813:383::-;45860:24;;45939:13;;45967:196;45991:11;:18;45987:22;;45967:196;;;46035:24;46044:11;46056:1;46044:14;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;46044:14:0;46035:8;:24::i;:::-;46031:121;;;46096:11;46108:1;46096:14;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;46096:14:0;46080:6;46087:5;46080:13;;;;;;;;-1:-1:-1;;;;;46080:30:0;;;:13;;;;;;;;;;;:30;46129:7;;;;;46031:121;46011:3;;45967:196;;;-1:-1:-1;46182:6:0;;-1:-1:-1;;45813:383:0;;:::o;41356:18::-;;;;;;;;;;;;;;;-1:-1:-1;;41356:18:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;12136:152::-;12202:4;12219:39;12228:12;:10;:12::i;:::-;12242:7;12251:6;12219:8;:39::i;:::-;-1:-1:-1;12276:4:0;12136:152;;;;:::o;49046:113::-;-1:-1:-1;;;;;49132:13:0;;49108:4;49132:13;;;:6;:13;;;;;:19;;;;;49046:113;;;;:::o;52762:169::-;52811:4;52856:22;52867:10;;52856:6;;:10;;:22;;;;:::i;:::-;52837:15;:41;;52836:86;;;;;52915:6;;52897:15;:24;52836:86;52828:95;;52762:169;:::o;57143:342::-;-1:-1:-1;;;;;57308:18:0;57237:7;57308:18;;;:6;:18;;;;;57359:16;;57390:19;;;;57424:16;;;;57455:11;;;;;57359:16;;57390:19;;57424:16;57455:11;;;57143:342::o;11157:91::-;11228:12;;11157:91;:::o;56216:788::-;45666:12;:10;:12::i;:::-;45665:13;45657:50;;;;;-1:-1:-1;;;45657:50:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;45657:50:0;;;;;;;;;;;;;;;56462:20;56471:10;56462:8;:20::i;:::-;56454:53;;;;;-1:-1:-1;;;56454:53:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;56454:53:0;;;;;;;;;;;;;;;56549:10;56520:19;56542:18;;;:6;:18;;;;;56666:19;;;;56542:18;;56520:19;56666:39;;56690:14;56666:39;:23;:39;:::i;:::-;56638:67;;56724:43;56731:5;:16;;;56749:17;56724:6;:43::i;:::-;56716:70;;;;;-1:-1:-1;;;56716:70:0;;;;;;;;;;;;-1:-1:-1;;;56716:70:0;;;;;;;;;;;;;;;56829:19;;;:39;;;56879:31;56885:8;56895:14;56879:5;:31::i;:::-;56928:51;;;-1:-1:-1;;;;;56928:51:0;;;;;;;;;;56968:10;56928:51;;;;;;;;;;;;;;;56990:7;;45718:1;56216:788;;:::o;12760:304::-;12849:4;12866:36;12876:6;12884:9;12895:6;12866:9;:36::i;:::-;12913:121;12922:6;12930:12;:10;:12::i;:::-;12944:89;12982:6;12944:89;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;12944:19:0;;;;;;:11;:19;;;;;;12964:12;:10;:12::i;:::-;-1:-1:-1;;;;;12944:33:0;;;;;;;;;;;;-1:-1:-1;12944:33:0;;;:89;;:37;:89;:::i;:::-;12913:8;:121::i;:::-;-1:-1:-1;13052:4:0;12760:304;;;;;:::o;41495:21::-;;;;;;:::o;83697:480::-;83860:45;83879:10;83891:13;83860:18;:45::i;:::-;;83916:40;83929:11;83950:4;83916:12;:40::i;:::-;83988:15;;;83967:51;;;-1:-1:-1;;;83967:51:0;;-1:-1:-1;;;;;83988:15:0;;;83967:51;;;;;;;;;;;;;;;:4;;:12;;:51;;;;;;;;;;;;;;-1:-1:-1;83967:4:0;:51;;;5:2:-1;;;;30:1;27;20:12;5:2;83967:51:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;83967:51:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;84029:15:0;;;:140;;;-1:-1:-1;;;84029:140:0;;-1:-1:-1;;;;;84029:140:0;;;;;;;;;;84102:4;84029:140;;;;:15;:140;;;;;;;;;;;;;;:15;;;;;:27;;:140;;;;;:15;:140;;;;;;:15;;:140;;;5:2:-1;;;;30:1;27;20:12;5:2;84029:140:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;84029:140:0;;;;83697:480;;;:::o;58613:870::-;45666:12;:10;:12::i;:::-;45665:13;45657:50;;;;;-1:-1:-1;;;45657:50:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;45657:50:0;;;;;;;;;;;;;;;58711:1;58697:11;:15;58689:54;;;;;-1:-1:-1;;;58689:54:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;58762:20;58771:10;58762:8;:20::i;:::-;58754:53;;;;;-1:-1:-1;;;58754:53:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;58754:53:0;;;;;;;;;;;;;;;58849:10;58820:19;58842:18;;;:6;:18;;;;;;58908:25;;:13;:25::i;:::-;58893:11;:40;;58871:129;;;;-1:-1:-1;;;58871:129:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;59116:16;;59085:28;;59116:33;;59137:11;59116:33;:20;:33;:::i;:::-;59085:64;;59184:49;59191:20;59213:5;:19;;;59184:6;:49::i;:::-;59162:114;;;;;-1:-1:-1;;;59162:114:0;;;;;;;;;;;;-1:-1:-1;;;59162:114:0;;;;;;;;;;;;;;;59323:39;;;59373:43;59392:10;59404:11;59373:18;:43::i;:::-;59434:41;;;;;;59464:10;59434:41;;;;;;;;;;;;;;;;;45718:1;;58613:870;:::o;39993:49::-;;;;;;;;:::o;79931:435::-;80073:11;:9;:11::i;:::-;;80095:50;80118:11;80139:4;80095:22;:50::i;:::-;80177:15;;;80156:51;;;-1:-1:-1;;;80156:51:0;;-1:-1:-1;;;;;80177:15:0;;;80156:51;;;;;;;;;;;;;;;:4;;:12;;:51;;;;;;;;;;;;;;-1:-1:-1;80156:4:0;:51;;;5:2:-1;;;;30:1;27;20:12;5:2;80156:51:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;80156:51:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;80218:15:0;;;:140;;;-1:-1:-1;;;80218:140:0;;-1:-1:-1;;;;;80218:140:0;;;;;;;;;;80291:4;80218:140;;;;:15;:140;;;;;;;;;;;;;;:15;;;;;:27;;:140;;;;;:15;:140;;;;;;:15;;:140;;;5:2:-1;;;;30:1;27;20:12;5:2;80218:140:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;80218:140:0;;;;79931:435;;:::o;13473:210::-;13553:4;13570:83;13579:12;:10;:12::i;:::-;13593:7;13602:50;13641:10;13602:11;:25;13614:12;:10;:12::i;:::-;-1:-1:-1;;;;;13602:25:0;;;;;;;;;;;;;;;;;-1:-1:-1;13602:25:0;;;:34;;;;;;;;;;;:50;:38;:50;:::i;39532:38::-;;;-1:-1:-1;;;;;39532:38:0;;:::o;81607:245::-;81760:11;:9;:11::i;:::-;;81782:62;81807:11;81820:13;81835:8;81782:24;:62::i;:::-;81607:245;;;:::o;53860:1013::-;54008:9;54003:735;54027:20;:27;54023:1;:31;54003:735;;;54076:26;54105:20;54126:1;54105:23;;;;;;;;;;;;;;54076:52;;54169:20;54178:10;54169:8;:20::i;:::-;54143:129;;;;-1:-1:-1;;;54143:129:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;54309:18:0;;54287:19;54309:18;;;:6;:18;;;;;54346:22;54342:385;;54389:7;;;;;54342:385;54444:17;54421:5;:19;;;:40;54417:310;;54482:40;54492:17;54511:10;54482:9;:40::i;:::-;54541:7;;;;;54417:310;54630:19;;;;54608:42;;:17;;:42;:21;:42;:::i;:::-;54588:62;;54669:42;54679:5;:19;;;54700:10;54669:9;:42::i;:::-;-1:-1:-1;;54056:3:0;;54003:735;;;-1:-1:-1;54770:22:0;;54748:117;;;;-1:-1:-1;;;54748:117:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;82391:247;82539:45;82558:10;82570:13;82539:18;:45::i;:::-;;82595:35;82608:11;82621:8;82595:12;:35::i;71588:199::-;71719:58;;;;;;;;;71751:25;71719:58;;;;;;;;;;;;;;;;;71687:7;;71719:58;;71736:13;;71719:16;:58::i;41108:24::-;;;-1:-1:-1;;;;;41108:24:0;;:::o;52361:320::-;52582:13;;52552:27;;52474:7;;-1:-1:-1;;;52582:13:0;;;;;;52552:27;;;:43;;;;52531:75;;52624:49;:17;52646:26;;;;:2;:26;52624:49;;:21;:49;:::i;:::-;52617:56;52361:320;-1:-1:-1;;;52361:320:0:o;11311:110::-;-1:-1:-1;;;;;11395:18:0;11368:7;11395:18;;;:9;:18;;;;;;;11311:110::o;38138:140::-;37539:9;:7;:9::i;:::-;37531:54;;;;;-1:-1:-1;;;37531:54:0;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;37531:54:0;;;;;;;;;;;;;;;38237:1;38221:6;;38200:40;;-1:-1:-1;;;;;38221:6:0;;;;38200:40;;38237:1;;38200:40;38268:1;38251:19;;-1:-1:-1;;;;;;38251:19:0;;;38138:140::o;49281:284::-;49329:4;45666:12;:10;:12::i;:::-;45665:13;45657:50;;;;;-1:-1:-1;;;45657:50:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;45657:50:0;;;;;;;;;;;;;;;49355:20;49364:10;49355:8;:20::i;:::-;49354:21;49346:55;;;;;-1:-1:-1;;;49346:55:0;;;;;;;;;;;;-1:-1:-1;;;49346:55:0;;;;;;;;;;;;;;;49435:20;;;;;;;;-1:-1:-1;49435:20:0;;;;;;;;;;;;;;;;49450:4;49435:20;;;;;;49421:10;49414:18;;;:6;:18;;;;;:41;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;49414:41:0;;;;;;;;;;;49466:11;27:10:-1;;23:18;;;45:23;;49466:28:0;;;;;;;;;-1:-1:-1;;;;;;49466:28:0;;;;;49512:23;;;;;;;;;;;;;;;;;-1:-1:-1;49553:4:0;49281:284;:::o;41267:46::-;;;-1:-1:-1;;;;;41267:46:0;;:::o;39714:51::-;;;;;;;;:::o;37327:79::-;37365:7;37392:6;-1:-1:-1;;;;;37392:6:0;37327:79;:::o;37693:94::-;37733:4;37773:6;;-1:-1:-1;;;;;37773:6:0;37757:12;:10;:12::i;:::-;-1:-1:-1;;;;;37757:22:0;;37750:29;;37693:94;:::o;53021:102::-;53108:6;;53089:15;:25;;53021:102;:::o;41419:20::-;;;;;;;;;;;;;;;-1:-1:-1;;41419:20:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;50369:418;50494:7;45666:12;:10;:12::i;:::-;45665:13;45657:50;;;;;-1:-1:-1;;;45657:50:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;45657:50:0;;;;;;;;;;;;;;;50533:10;;50527:17;;-1:-1:-1;;;50533:10:0;;-1:-1:-1;;;;;50533:10:0;50527:5;:17::i;:::-;50519:70;;;;-1:-1:-1;;;50519:70:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;50608:20;50617:10;50608:8;:20::i;:::-;50600:53;;;;;-1:-1:-1;;;50600:53:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;50600:53:0;;;;;;;;;;;;;;;50671;;;-1:-1:-1;;;;;50671:53:0;;;;50702:9;50671:53;;;;50713:10;50671:53;;;;;;;;;;;;;;;50742:37;50757:10;50769:9;50742:14;:37::i;39808:44::-;;;;;;;;:::o;14186:261::-;14271:4;14288:129;14297:12;:10;:12::i;:::-;14311:7;14320:96;14359:15;14320:96;;;;;;;;;;;;;;;;;:11;:25;14332:12;:10;:12::i;:::-;-1:-1:-1;;;;;14320:25:0;;;;;;;;;;;;;;;;;-1:-1:-1;14320:25:0;;;:34;;;;;;;;;;;:96;;:38;:96;:::i;11634:158::-;11703:4;11720:42;11730:12;:10;:12::i;:::-;11744:9;11755:6;11720:9;:42::i;41197:20::-;;;-1:-1:-1;;;;;41197:20:0;;:::o;40954:32::-;;;-1:-1:-1;;;40954:32:0;;;;;:::o;40873:::-;;;;;;:::o;40448:::-;;;;;;;;:::o;59745:800::-;59801:12;:10;:12::i;:::-;59793:62;;;;-1:-1:-1;;;59793:62:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;59874:20;59883:10;59874:8;:20::i;:::-;59866:53;;;;;-1:-1:-1;;;59866:53:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;59866:53:0;;;;;;;;;;;;;;;59999:10;59970:19;59992:18;;;:6;:18;;;;;60095:16;;60153;;;;;60182:20;;;-1:-1:-1;60213:19:0;;:23;;;60247:20;;;59992:18;;60095:16;;60153;60280:52;;60095:16;60280:18;:52::i;:::-;60343;60362:10;60374:20;60343:18;:52::i;:::-;60413:124;;;;;;;;;;;;60516:10;60413:124;;;;;;;;;;;;;;;59745:800;;;:::o;48110:361::-;37539:9;:7;:9::i;:::-;37531:54;;;;;-1:-1:-1;;;37531:54:0;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;37531:54:0;;;;;;;;;;;;;;;48225:12;;;;:4;;:12;;;;;:::i;:::-;-1:-1:-1;48248:16:0;;;;:6;;:16;;;;;:::i;:::-;-1:-1:-1;48297:27:0;;48275:8;:50;;-1:-1:-1;;48275:50:0;48297:27;;;;;;48292:32;48275:50;;;;;;;;48336:127;;79501:202;79621:28;79638:10;79621:16;:28::i;:::-;;79660:35;79673:11;79686:8;79660:12;:35::i;40240:56::-;;;;;;;;:::o;62149:1984::-;45666:12;:10;:12::i;:::-;45665:13;45657:50;;;;;-1:-1:-1;;;45657:50:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;45657:50:0;;;;;;;;;;;;;;;62284:20;62293:10;62284:8;:20::i;:::-;62276:53;;;;;-1:-1:-1;;;62276:53:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;62276:53:0;;;;;;;;;;;;;;;-1:-1:-1;;;;;62364:18:0;;62342:19;62364:18;;;:6;:18;;;;;62446:20;62371:10;62446:8;:20::i;:::-;62438:46;;;;;-1:-1:-1;;;62438:46:0;;;;;;;;;;;;-1:-1:-1;;;62438:46:0;;;;;;;;;;;;;;;62550:10;-1:-1:-1;;;;;62550:24:0;;;;62542:69;;;;;-1:-1:-1;;;62542:69:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;62624:21;62648:95;62687:18;62720:12;;;;;;;;62727:1;62720:12;;;;62730:1;62720:12;;;;;62648:24;:95::i;:::-;62777:103;;;;;;;;;62849:20;62777:103;;;;;;;;;;;;;;;;;62624:119;;-1:-1:-1;62754:20:0;;62777:103;;62816:18;;62777:24;:103::i;:::-;62754:126;-1:-1:-1;62891:26:0;62920:31;:13;62754:126;62920:31;:17;:31;:::i;:::-;63113:17;:23;63078:16;;62891:60;;-1:-1:-1;63042:33:0;;63078:69;;;:20;:69;:::i;:::-;63164:26;;63042:105;;-1:-1:-1;63193:1:0;63164:26;;;;:30;;;;63160:361;;;63299:26;;63239:102;;:25;;63299:26;;;;63291:35;;63287:2;:39;63239:102;:29;:102;:::i;:::-;63211:130;;63160:361;;;63467:26;;63402:107;;:25;;63467:26;;;;;63462:31;63454:40;;63450:2;:44;63402:107;:29;:107;:::i;:::-;63374:135;;63160:361;63577:25;63555:18;:47;;63533:152;;;;-1:-1:-1;;;63533:152:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;63769:16;;:40;;63790:18;63769:40;:20;:40;:::i;:::-;63750:59;;63842:19;;;;:43;;63866:18;63842:43;:23;:43;:::i;:::-;63820:19;;;:65;63956:37;63962:10;63974:18;63956:5;:37::i;:::-;64004:50;64023:10;64035:18;64004;:50::i;:::-;64072:53;;;;;;-1:-1:-1;;;;;64072:53:0;;;;;;64114:10;64072:53;;;;;;;;;;;;;;;45718:1;;;;;62149:1984;;:::o;40357:25::-;;;;;;;;:::o;82936:478::-;83104:11;:9;:11::i;:::-;;83126:67;83151:11;83164:13;83187:4;83126:24;:67::i;64321:241::-;64388:4;64405:16;64425:100;64446:25;64460:10;64446:13;:25::i;:::-;64486:28;64503:10;64486:16;:28::i;:::-;64425:6;:100::i;:::-;64424:101;;64321:241;-1:-1:-1;;;64321:241:0:o;80616:437::-;80753:28;80770:10;80753:16;:28::i;:::-;;80792:40;80805:11;80826:4;80792:12;:40::i;41024:24::-;;;-1:-1:-1;;;41024:24:0;;-1:-1:-1;;;;;41024:24:0;;:::o;11855:134::-;-1:-1:-1;;;;;11954:18:0;;;11927:7;11954:18;;;:11;:18;;;;;;;;:27;;;;;;;;;;;;;11855:134::o;60742:790::-;60850:7;60879:20;60888:10;60879:8;:20::i;:::-;60875:650;;;-1:-1:-1;;;;;60938:18:0;;60916:19;60938:18;;;:6;:18;;;;;61124:26;;:17;61064:23;61007:34;;60938:18;;60916:19;61007:145;;61124:26;;;;61123:27;;61115:36;;61111:2;:40;;61007:81;;:34;:81;:56;:81;:::i;:::-;:103;:145;:103;:145;:::i;:::-;61196:29;;60971:181;;-1:-1:-1;61196:29:0;;;;61169:11;61195:30;61187:39;;61183:2;:43;61241:26;;:::i;:::-;61270:123;;;;;;;;;61295:20;:26;61270:123;;61295:35;;61326:3;61295:35;:30;:35;:::i;:::-;61270:123;;61349:29;;;;;;61270:123;;;;;;;61241:152;-1:-1:-1;61415:57:0;61432:25;61241:152;61415:16;:57::i;:::-;61408:64;;;;;;;;60875:650;-1:-1:-1;61512:1:0;61505:8;;40803:21;;;;:::o;48652:219::-;37539:9;:7;:9::i;:::-;37531:54;;;;;-1:-1:-1;;;37531:54:0;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;37531:54:0;;;;;;;;;;;;;;;48742:8;;;48727:12;48761;;;48784:34;48803:8;48742;48784:18;:34::i;:::-;48836:27;;;-1:-1:-1;;;;;48836:27:0;;;;;;;;;;;;;;;;;;;;;;;37596:1;48652:219;:::o;58007:343::-;45666:12;:10;:12::i;:::-;45665:13;45657:50;;;;;-1:-1:-1;;;45657:50:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;45657:50:0;;;;;;;;;;;;;;;58084:20;58093:10;58084:8;:20::i;:::-;58076:53;;;;;-1:-1:-1;;;58076:53:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;58076:53:0;;;;;;;;;;;;;;;58171:10;58142:19;58164:18;;;:6;:18;;;;;58217:19;;;;:34;;58241:9;58217:34;:23;:34;:::i;:::-;58195:19;;;:56;58262:28;58268:10;58280:9;58262:5;:28::i;:::-;58308:34;;;58320:10;58308:34;;;;;;;;;;;;;;;;;;;;;45718:1;58007:343;:::o;78799:200::-;78924:11;:9;:11::i;:::-;;78946:45;78969:11;78982:8;78946:22;:45::i;46759:1126::-;37539:9;:7;:9::i;:::-;37531:54;;;;;-1:-1:-1;;;37531:54:0;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;37531:54:0;;;;;;;;;;;;;;;47023:3;46998:21;:28;;46976:115;;;;-1:-1:-1;;;46976:115:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;47146:4;47124:18;:26;;47102:119;;;;-1:-1:-1;;;47102:119:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;47259:3;47240:15;:22;;47232:67;;;;;-1:-1:-1;;;47232:67:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;47362:2;47332:26;:32;;47310:122;;;;-1:-1:-1;;;47310:122:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;47445:20;:50;;;47506:17;:44;;;47561:14;:38;;;47610:25;:60;;;47688:189;47474:21;47532:18;47584:15;47644:26;47859:7;:5;:7::i;:::-;47688:189;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;47688:189:0;;;;;;;;;;;;;;46759:1126;;;;:::o;38433:109::-;37539:9;:7;:9::i;:::-;37531:54;;;;;-1:-1:-1;;;37531:54:0;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;37531:54:0;;;;;;;;;;;;;;;38506:28;38525:8;38506:18;:28::i;:::-;38433:109;:::o;57607:104::-;-1:-1:-1;;;;;57683:20:0;;;57607:104::o;55006:449::-;55060:20;55069:10;55060:8;:20::i;:::-;55052:53;;;;;-1:-1:-1;;;55052:53:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;55052:53:0;;;;;;;;;;;;;;;55145:10;55116:19;55138:18;;;:6;:18;;;;;55177:16;;;;55169:54;;;;;-1:-1:-1;;;55169:54:0;;;;;;;;;;;;-1:-1:-1;;;55169:54:0;;;;;;;;;;;;;;;55267:16;;;;;55236:28;55294:20;;;55327:52;55346:10;55267:16;55327:18;:52::i;:::-;55395:50;;;;;;55434:10;55395:50;;;;;;;;;;;;;;;;;55006:449;;:::o;69502:285::-;69623:7;45666:12;:10;:12::i;:::-;45665:13;45657:50;;;;;-1:-1:-1;;;45657:50:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;45657:50:0;;;;;;;;;;;;;;;-1:-1:-1;;;;;69670:18:0;;69648:19;69670:18;;;:6;:18;;;;;69718:16;;:25;;69739:3;69718:25;:20;:25;:::i;:::-;69699:44;;;;;69502:285;-1:-1:-1;;;69502:285:0:o;858:98::-;938:10;858:98;:::o;17118:338::-;-1:-1:-1;;;;;17212:19:0;;17204:68;;;;-1:-1:-1;;;17204:68:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;17291:21:0;;17283:68;;;;-1:-1:-1;;;17283:68:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;17364:18:0;;;;;;;:11;:18;;;;;;;;:27;;;;;;;;;;;;;:36;;;17416:32;;;;;;;;;;;;;;;;;17118:338;;;:::o;5445:136::-;5503:7;5530:43;5534:1;5537;5530:43;;;;;;;;;;;;;;;;;:3;:43::i;4989:181::-;5047:7;5079:5;;;5103:6;;;;5095:46;;;;;-1:-1:-1;;;5095:46:0;;;;;;;;;;;;;;;;;;;;;;;;;;;70113:1227;70225:4;70281:28;70312:29;70329:10;;;;;;;;;-1:-1:-1;;;;;70329:10:0;70312:8;:29::i;:::-;70396:6;;70281:60;;-1:-1:-1;70352:24:0;;70379:25;;-1:-1:-1;;;;;70396:6:0;70379:8;:25::i;:::-;70635:11;:17;70584:25;:31;70352:52;;-1:-1:-1;70530:19:0;;70552:101;;70635:17;70552:64;;:13;;:64;:31;:64;:::i;:::-;:82;:101;:82;:101;:::i;:::-;70734:20;;70684:34;;70530:123;;-1:-1:-1;70734:20:0;70684:34;;;70734:20;;70684:70;70664:17;70790:87;70850:16;70791:39;:13;70809:20;70791:39;:17;:39;:::i;70790:87::-;70909:13;;70767:110;;-1:-1:-1;70909:13:0;;;;;70888:18;;;;71000:26;;;;;;;;70996:308;;;71057:26;;;71049:35;;;-1:-1:-1;71139:12:0;71111:24;:11;71127:2;:7;;;71111:24;:15;:24;:::i;:::-;:40;;71099:52;;70996:308;;;71198:26;;;71190:35;;;-1:-1:-1;71267:25:0;:12;71284:2;:7;;;71267:25;:16;:25;:::i;:::-;71252:11;:40;;71240:52;;70996:308;71323:9;70113:1227;-1:-1:-1;;;;;;;;;;70113:1227:0:o;15689:308::-;-1:-1:-1;;;;;15765:21:0;;15757:65;;;;;-1:-1:-1;;;15757:65:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;15850:12;;:24;;15867:6;15850:24;:16;:24;:::i;:::-;15835:12;:39;-1:-1:-1;;;;;15906:18:0;;;;;;:9;:18;;;;;;:30;;15929:6;15906:30;:22;:30;:::i;:::-;-1:-1:-1;;;;;15885:18:0;;;;;;:9;:18;;;;;;;;:51;;;;15952:37;;;;;;;15885:18;;;;15952:37;;;;;;;;;;15689:308;;:::o;14937:471::-;-1:-1:-1;;;;;15035:20:0;;15027:70;;;;-1:-1:-1;;;15027:70:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;15116:23:0;;15108:71;;;;-1:-1:-1;;;15108:71:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;15212;15234:6;15212:71;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;15212:17:0;;;;;;:9;:17;;;;;;;:71;;:21;:71;:::i;:::-;-1:-1:-1;;;;;15192:17:0;;;;;;;:9;:17;;;;;;:91;;;;15317:20;;;;;;;:32;;15342:6;15317:32;:24;:32;:::i;:::-;-1:-1:-1;;;;;15294:20:0;;;;;;;:9;:20;;;;;;;;;:55;;;;15365:35;;;;;;;15294:20;;15365:35;;;;;;;;;;;;;14937:471;;;:::o;5918:192::-;6004:7;6040:12;6032:6;;;;6024:29;;;;-1:-1:-1;;;6024:29:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;6024:29:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;6076:5:0;;;5918:192::o;64896:208::-;-1:-1:-1;;;;;65044:18:0;64997:7;65044:18;;;:6;:18;;;;;65080:16;;64896:208::o;75352:227::-;75447:10;;75441:17;;-1:-1:-1;;;75447:10:0;;-1:-1:-1;;;;;75447:10:0;75441:5;:17::i;:::-;75437:135;;;75475:20;;-1:-1:-1;;;;;75475:14:0;;;:20;;;;;75490:4;;75475:20;;;;75490:4;75475:14;:20;;;;;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;75475:20:0;75437:135;;;75528:10;;:32;;;-1:-1:-1;;;75528:32:0;;-1:-1:-1;;;;;75528:32:0;;;;;;;;;;;;;;;-1:-1:-1;;;75528:10:0;;;;;;;:19;;:32;;;;;;;;;;;;;;;-1:-1:-1;75528:10:0;:32;;;5:2:-1;;;;30:1;27;20:12;5:2;75528:32:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;75528:32:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;75352:227:0;;:::o;66386:2894::-;66582:18;:16;:18::i;:::-;66560:113;;;;-1:-1:-1;;;66560:113:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;66694:29;66703:19;66694:8;:29::i;:::-;66686:62;;;;;-1:-1:-1;;;66686:62:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;66686:62:0;;;;;;;;;;;;;;;-1:-1:-1;;;;;66783:27:0;;66761:19;66783:27;;;:6;:27;;;;;66829:21;66821:58;;;;;-1:-1:-1;;;66821:58:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;66988:5;:19;;;66967:17;:40;;66945:136;;;;-1:-1:-1;;;66945:136:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;67192:17;67167:21;67177:10;67167:9;:21::i;:::-;:42;;67145:110;;;;;-1:-1:-1;;;67145:110:0;;;;;;;;;;;;-1:-1:-1;;;67145:110:0;;;;;;;;;;;;;;;67354:26;67383:71;67426:17;67383:28;:71::i;:::-;67484:16;;;;67354:100;;-1:-1:-1;67484:40:0;;67354:100;67484:40;:20;:40;:::i;:::-;67465:16;;;:59;67773:12;;;;;;;;;67780:1;67773:12;;67673:26;67773:12;;;;;;67673:26;67702:94;;67741:17;;67702:24;:94::i;:::-;67877:96;;;;;;;;;67948:14;67877:96;;;;;;;;;;;;;;;;;67673:123;;-1:-1:-1;67860:14:0;;67877:96;;67916:17;;67877:24;:96::i;:::-;67995:8;;67860:113;;-1:-1:-1;67995:20:0;;67860:113;67995:20;:12;:20;:::i;:::-;67984:8;:31;68028:28;68059:30;:18;68082:6;68059:30;:22;:30;:::i;:::-;68146:16;;68028:61;;-1:-1:-1;68122:40:0;;;68100:122;;;;;-1:-1:-1;;;68100:122:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;68305:16;;:42;;68326:20;68305:42;:20;:42;:::i;:::-;68286:61;;68380:19;;;;:42;;68404:17;68380:42;:23;:42;:::i;:::-;68358:19;;;:64;68557:10;;68551:17;;-1:-1:-1;;;;;68557:10:0;68551:5;:17::i;:::-;68547:388;;;68606:18;68593:9;:31;68585:63;;;;;-1:-1:-1;;;68585:63:0;;;;;;;;;;;;-1:-1:-1;;;68585:63:0;;;;;;;;;;;;;;;68547:388;;;68707:10;;:152;;;-1:-1:-1;;;68707:152:0;;68753:10;68707:152;;;;68794:4;68707:152;;;;;;;;;;;;-1:-1:-1;;;;;68707:10:0;;;;:23;;:152;;;;;;;;;;;;;;;:10;;:152;;;5:2:-1;;;;30:1;27;20:12;5:2;68707:152:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;68707:152:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;68707:152:0;68681:242;;;;;-1:-1:-1;;;68681:242:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;68974:36;68980:10;68992:17;68974:5;:36::i;:::-;69058:50;69077:10;69089:18;69058;:50::i;:::-;69126:144;;;;;;;;;;;;69215:10;69126:144;;;;-1:-1:-1;;;;;69126:144:0;;;;;;;;;;;;;;;;;66386:2894;;;;;;;:::o;72326:1085::-;72451:7;72476:28;72507:29;72524:10;;;;;;;;;-1:-1:-1;;;;;72524:10:0;72507:8;:29::i;:::-;72591:6;;72476:60;;-1:-1:-1;72547:24:0;;72574:25;;-1:-1:-1;;;;;72591:6:0;72574:8;:25::i;:::-;72745:11;:17;72724:16;;72547:52;;-1:-1:-1;72705:16:0;;72724:39;;;:20;:39;:::i;:::-;72813:20;;72791:19;;;;72705:58;;-1:-1:-1;72813:20:0;;72791:42;72774:14;72869:87;72929:16;72870:39;:13;72888:20;72870:39;:17;:39;:::i;72869:87::-;72988:13;;72846:110;;-1:-1:-1;72988:13:0;;;;;72967:18;;;;73079:23;;;;;;;;73075:299;;;73133:23;;;73125:32;;;-1:-1:-1;73185:39:0;73202:21;:8;73215:2;:7;;;73202:21;:12;:21;:::i;:::-;73185:12;;:39;:16;:39;:::i;:::-;73172:52;;73075:299;;;73271:23;;;73263:32;;;-1:-1:-1;73323:39:0;73353:8;73323:25;:12;73340:2;:7;;;73323:25;:16;:25;:::i;:39::-;73310:52;72326:1085;-1:-1:-1;;;;;;;;;;;72326:1085:0:o;6361:471::-;6419:7;6664:6;6660:47;;-1:-1:-1;6694:1:0;6687:8;;6660:47;6731:5;;;6735:1;6731;:5;:1;6755:5;;;;;:10;6747:56;;;;-1:-1:-1;;;6747:56:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;75793:227;75888:10;;75882:17;;-1:-1:-1;;;;;75888:10:0;75882:5;:17::i;:::-;75878:135;;;75916:20;;-1:-1:-1;;;;;75916:14:0;;;:20;;;;;75931:4;;75916:20;;;;75931:4;75916:14;:20;;;;;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;75878:135:0;75969:10;;:32;;;-1:-1:-1;;;75969:32:0;;-1:-1:-1;;;;;75969:32:0;;;;;;;;;;;;;;;:10;;;;;:19;;:32;;;;;;;;;;;;;;:10;;:32;;;5:2:-1;;;;30:1;27;20:12;73946:1192:0;74072:7;74126:28;74157:29;74174:10;;;;;;;;;-1:-1:-1;;;;;74174:10:0;74157:8;:29::i;:::-;74241:6;;74126:60;;-1:-1:-1;74197:24:0;;74224:25;;-1:-1:-1;;;;;74241:6:0;74224:8;:25::i;:::-;74197:52;;74312:34;74349:117;74449:16;74349:81;74413:10;:16;;;74349:45;74376:11;:17;;;74349:8;:26;;:45;;;;:::i;:117::-;74578:13;;74543:19;;;;74507:20;;74312:154;;-1:-1:-1;74578:13:0;;;;74507:20;;;:55;:84;;;;;74477:27;;74647:25;;;;-1:-1:-1;74643:448:0;;;74709:21;74767:97;74829:20;74767:39;:26;74798:7;;;;:2;:7;74767:39;;;:30;:39;:::i;:97::-;74746:118;;74643:448;;;;74897:10;74917:26;;;74980:99;75044:20;74981:39;:26;75012:7;;;;:2;:7;74981:39;;;:30;:39;:::i;74980:99::-;74959:120;;74643:448;;75110:18;73946:1192;-1:-1:-1;;;;;;;73946:1192:0:o;7300:132::-;7358:7;7385:39;7389:1;7392;7385:39;;;;;;;;;;;;;;;;;:3;:39::i;16330:348::-;-1:-1:-1;;;;;16406:21:0;;16398:67;;;;-1:-1:-1;;;16398:67:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;16499:68;16522:6;16499:68;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;16499:18:0;;;;;;:9;:18;;;;;;;:68;;:22;:68;:::i;:::-;-1:-1:-1;;;;;16478:18:0;;;;;;:9;:18;;;;;:89;16593:12;;:24;;16610:6;16593:24;:16;:24;:::i;:::-;16578:12;:39;16633:37;;;;;;;;16659:1;;-1:-1:-1;;;;;16633:37:0;;;;;;;;;;;;16330:348;;:::o;65221:214::-;-1:-1:-1;;;;;65372:18:0;65325:7;65372:18;;;:6;:18;;;;;65408:19;;;;65221:214::o;38648:229::-;-1:-1:-1;;;;;38722:22:0;;38714:73;;;;-1:-1:-1;;;38714:73:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;38824:6;;;38803:38;;-1:-1:-1;;;;;38803:38:0;;;;38824:6;;;38803:38;;;38852:6;:17;;-1:-1:-1;;;;;;38852:17:0;-1:-1:-1;;;;;38852:17:0;;;;;;;;;;38648:229::o;76183:306::-;76294:6;;76271:10;;76239:7;;-1:-1:-1;;;;;76294:6:0;;;-1:-1:-1;;;76271:10:0;;;;76263:38;76259:223;;;-1:-1:-1;76325:1:0;76318:8;;76259:223;-1:-1:-1;;;;;76348:19:0;;76344:138;;-1:-1:-1;76392:6:0;76384:15;;76344:138;76439:15;;:31;;;-1:-1:-1;;;76439:31:0;;-1:-1:-1;;;;;76439:31:0;;;;;;;;;:15;;;;;:24;;:31;;;;;;;;;;;;;;:15;:31;;;5:2:-1;;;;30:1;27;20:12;5:2;76439:31:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;76439:31:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;76439:31:0;;-1:-1:-1;76432:38:0;;7962:345;8048:7;8150:12;8143:5;8135:28;;;;-1:-1:-1;;;8135:28:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27:10:-1;;8:100;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;8135:28:0;;8174:9;8190:1;8186;:5;;;;;;;7962:345;-1:-1:-1;;;;;7962:345:0:o;76622:7558::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;76622:7558:0;;;-1:-1:-1;76622:7558:0;:::i;:::-;;;:::o;:::-;;;;;;;;;;-1:-1:-1;76622:7558:0;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;

Swarm Source

bzzr://24408987c6dcfcda4c0e73ff0354366a3d057faa494c86e0ef89208047b75fda

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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