false
true
0

Contract Address Details

0x444444444444C1a66F394025Ac839A535246FCc8

Token
Genius (GENI)
Creator
0xc11914–14a13b at 0x59392d–dad1ce
Balance
0 PLS ( )
Tokens
Fetching tokens...
Transactions
13,676 Transactions
Transfers
0 Transfers
Gas Used
0
Last Balance Update
25857536
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
Contract name:
Genius




Optimization enabled
true
Compiler version
v0.8.4+commit.c7e474f2




Optimization runs
200
EVM Version
default




Verified at
2023-11-28T20:01:10.927630Z

Constructor Arguments

0x000000000000000000000000dca40b6fb95e9c1c4511f7758bc6f3aec7474444000000000000000000000000f2ebcc36ecbbf57bcf7d7531fe24f137afc4c555

Arg [0] (address) : 0xdca40b6fb95e9c1c4511f7758bc6f3aec7474444
Arg [1] (address) : 0xf2ebcc36ecbbf57bcf7d7531fe24f137afc4c555

              

Contract source code

// SPDX-License-Identifier: UNLICENSED
// Genius is NOT LICENSED FOR COPYING.
// Genius (C) 2022. All Rights Reserved.
//
// Telegram: https://t.me/genicrypto
// Twitter: https://twitter.com/genicrypto
// White Paper: https://geni.to/smartcontract
//
// First DAPP: https://start.geni.app
// Community Website: https://thegeniustoken.com
// Development Telegram: https://t.me/genicryptodev
//
// Buy $GENI here:
// * Ethereum: https://geni.to/ethereum
// * Binance: https://geni.to/binance
// * Polygon: https://geni.to/polygon
// * Avalanche: https://geni.to/avalanche
//
// Third-Party Security Reviews:
// * Gleipnir: https://www.gleipnirsecurity.com/_files/ugd/a4dd88_02edf4a4aeef4e6d950db85175488ebb.pdf
// * CertiK: https://www.certik.com/projects/genius

// Sources flattened with hardhat v2.9.9 https://hardhat.org

// File @openzeppelin/contracts/token/ERC20/IERC20.sol@v4.6.0

// License: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

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

    /**
     * @dev 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 `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, 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 `from` to `to` 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 from,
        address to,
        uint256 amount
    ) external returns (bool);
}


// File @openzeppelin/contracts/utils/Counters.sol@v4.6.0

// License: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)



/**
 * @title Counters
 * @author Matt Condon (@shrugs)
 * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
 * of elements in a mapping, issuing ERC721 ids, or counting request ids.
 *
 * Include with `using Counters for Counters.Counter;`
 */
library Counters {
    struct Counter {
        // This variable should never be directly accessed by users of the library: interactions must be restricted to
        // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
        // this feature: see https://github.com/ethereum/solidity/issues/4637
        uint256 _value; // default: 0
    }

    function current(Counter storage counter) internal view returns (uint256) {
        return counter._value;
    }

    function increment(Counter storage counter) internal {
        unchecked {
            counter._value += 1;
        }
    }

    function decrement(Counter storage counter) internal {
        uint256 value = counter._value;
        require(value > 0, "Counter: decrement overflow");
        unchecked {
            counter._value = value - 1;
        }
    }

    function reset(Counter storage counter) internal {
        counter._value = 0;
    }
}


// File @openzeppelin/contracts/security/ReentrancyGuard.sol@v4.6.0

// License: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)



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

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

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

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

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

        _;

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


// File @openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol@v4.6.0

// License: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)



/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}


// File @openzeppelin/contracts/utils/Context.sol@v4.6.0

// License: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)



/**
 * @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 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.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}


// File @openzeppelin/contracts/token/ERC20/ERC20.sol@v4.6.0

// License: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/ERC20.sol)





/**
 * @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 {ERC20PresetMinterPauser}.
 *
 * 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 Contracts guidelines: functions revert
 * instead 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, IERC20Metadata {
    mapping(address => uint256) private _balances;

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

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * The default value of {decimals} is 18. To select a different value for
     * {decimals} you should overload it.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

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

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override 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. This is the value {ERC20} uses, unless this function is
     * overridden;
     *
     * 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 virtual override returns (uint8) {
        return 18;
    }

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

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

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

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

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, 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}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `amount`.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual override returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, amount);
        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 virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, allowance(owner, spender) + 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 virtual returns (bool) {
        address owner = _msgSender();
        uint256 currentAllowance = allowance(owner, spender);
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(owner, spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `sender` to `recipient`.
     *
     * This 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:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     */
    function _transfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(from, to, amount);

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
        }
        _balances[to] += amount;

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, 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:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        _balances[account] += amount;
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(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 virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
        }
        _totalSupply -= amount;

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This 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 virtual {
        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 Updates `owner` s allowance for `spender` based on spent `amount`.
     *
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Might emit an {Approval} event.
     */
    function _spendAllowance(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}
}


// File @openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol@v4.6.0

// License: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)



/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}


// File @openzeppelin/contracts/utils/Strings.sol@v4.6.0

// License: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)



/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _HEX_SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }
}


// File @openzeppelin/contracts/utils/cryptography/ECDSA.sol@v4.6.0

// License: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/ECDSA.sol)



/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        } else if (error == RecoverError.InvalidSignatureV) {
            revert("ECDSA: invalid signature 'v' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        // Check the signature length
        // - case 65: r,s,v signature (standard)
        // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else if (signature.length == 64) {
            bytes32 r;
            bytes32 vs;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            assembly {
                r := mload(add(signature, 0x20))
                vs := mload(add(signature, 0x40))
            }
            return tryRecover(hash, r, vs);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address, RecoverError) {
        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        uint8 v = uint8((uint256(vs) >> 255) + 27);
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS);
        }
        if (v != 27 && v != 28) {
            return (address(0), RecoverError.InvalidSignatureV);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from `s`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
    }
}


// File @openzeppelin/contracts/utils/cryptography/draft-EIP712.sol@v4.6.0

// License: MIT
// OpenZeppelin Contracts v4.4.1 (utils/cryptography/draft-EIP712.sol)



/**
 * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
 *
 * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
 * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
 * they need in their contracts using a combination of `abi.encode` and `keccak256`.
 *
 * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
 * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
 * ({_hashTypedDataV4}).
 *
 * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
 * the chain id to protect against replay attacks on an eventual fork of the chain.
 *
 * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
 * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
 *
 * _Available since v3.4._
 */
abstract contract EIP712 {
    /* solhint-disable var-name-mixedcase */
    // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
    // invalidate the cached domain separator if the chain id changes.
    bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
    uint256 private immutable _CACHED_CHAIN_ID;
    address private immutable _CACHED_THIS;

    bytes32 private immutable _HASHED_NAME;
    bytes32 private immutable _HASHED_VERSION;
    bytes32 private immutable _TYPE_HASH;

    /* solhint-enable var-name-mixedcase */

    /**
     * @dev Initializes the domain separator and parameter caches.
     *
     * The meaning of `name` and `version` is specified in
     * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
     *
     * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
     * - `version`: the current major version of the signing domain.
     *
     * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
     * contract upgrade].
     */
    constructor(string memory name, string memory version) {
        bytes32 hashedName = keccak256(bytes(name));
        bytes32 hashedVersion = keccak256(bytes(version));
        bytes32 typeHash = keccak256(
            "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
        );
        _HASHED_NAME = hashedName;
        _HASHED_VERSION = hashedVersion;
        _CACHED_CHAIN_ID = block.chainid;
        _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
        _CACHED_THIS = address(this);
        _TYPE_HASH = typeHash;
    }

    /**
     * @dev Returns the domain separator for the current chain.
     */
    function _domainSeparatorV4() internal view returns (bytes32) {
        if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) {
            return _CACHED_DOMAIN_SEPARATOR;
        } else {
            return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
        }
    }

    function _buildDomainSeparator(
        bytes32 typeHash,
        bytes32 nameHash,
        bytes32 versionHash
    ) private view returns (bytes32) {
        return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
    }

    /**
     * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
     * function returns the hash of the fully encoded EIP712 message for this domain.
     *
     * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
     *
     * ```solidity
     * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
     *     keccak256("Mail(address to,string contents)"),
     *     mailTo,
     *     keccak256(bytes(mailContents))
     * )));
     * address signer = ECDSA.recover(digest, signature);
     * ```
     */
    function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
        return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
    }
}


// File @openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol@v4.6.0

// License: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/extensions/draft-ERC20Permit.sol)







/**
 * @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * _Available since v3.4._
 */
abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
    using Counters for Counters.Counter;

    mapping(address => Counters.Counter) private _nonces;

    // solhint-disable-next-line var-name-mixedcase
    bytes32 private constant _PERMIT_TYPEHASH =
        keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
    /**
     * @dev In previous versions `_PERMIT_TYPEHASH` was declared as `immutable`.
     * However, to ensure consistency with the upgradeable transpiler, we will continue
     * to reserve a slot.
     * @custom:oz-renamed-from _PERMIT_TYPEHASH
     */
    // solhint-disable-next-line var-name-mixedcase
    bytes32 private _PERMIT_TYPEHASH_DEPRECATED_SLOT;

    /**
     * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
     *
     * It's a good idea to use the same `name` that is defined as the ERC20 token name.
     */
    constructor(string memory name) EIP712(name, "1") {}

    /**
     * @dev See {IERC20Permit-permit}.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual override {
        require(block.timestamp <= deadline, "ERC20Permit: expired deadline");

        bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));

        bytes32 hash = _hashTypedDataV4(structHash);

        address signer = ECDSA.recover(hash, v, r, s);
        require(signer == owner, "ERC20Permit: invalid signature");

        _approve(owner, spender, value);
    }

    /**
     * @dev See {IERC20Permit-nonces}.
     */
    function nonces(address owner) public view virtual override returns (uint256) {
        return _nonces[owner].current();
    }

    /**
     * @dev See {IERC20Permit-DOMAIN_SEPARATOR}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view override returns (bytes32) {
        return _domainSeparatorV4();
    }

    /**
     * @dev "Consume a nonce": return the current value and increment.
     *
     * _Available since v4.1._
     */
    function _useNonce(address owner) internal virtual returns (uint256 current) {
        Counters.Counter storage nonce = _nonces[owner];
        current = nonce.current();
        nonce.increment();
    }
}


// File @openzeppelin/contracts/utils/cryptography/MerkleProof.sol@v4.6.0

// License: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (utils/cryptography/MerkleProof.sol)



/**
 * @dev These functions deal with verification of Merkle Trees proofs.
 *
 * The proofs can be generated using the JavaScript library
 * https://github.com/miguelmota/merkletreejs[merkletreejs].
 * Note: the hashing algorithm should be keccak256 and pair sorting should be enabled.
 *
 * See `test/utils/cryptography/MerkleProof.test.js` for some examples.
 *
 * WARNING: You should avoid using leaf values that are 64 bytes long prior to
 * hashing, or use a hash function other than keccak256 for hashing leaves.
 * This is because the concatenation of a sorted pair of internal nodes in
 * the merkle tree could be reinterpreted as a leaf value.
 */
library MerkleProof {
    /**
     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
     * defined by `root`. For this, a `proof` must be provided, containing
     * sibling hashes on the branch from the leaf to the root of the tree. Each
     * pair of leaves and each pair of pre-images are assumed to be sorted.
     */
    function verify(
        bytes32[] memory proof,
        bytes32 root,
        bytes32 leaf
    ) internal pure returns (bool) {
        return processProof(proof, leaf) == root;
    }

    /**
     * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
     * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
     * hash matches the root of the tree. When processing the proof, the pairs
     * of leafs & pre-images are assumed to be sorted.
     *
     * _Available since v4.4._
     */
    function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            bytes32 proofElement = proof[i];
            if (computedHash <= proofElement) {
                // Hash(current computed hash + current element of the proof)
                computedHash = _efficientHash(computedHash, proofElement);
            } else {
                // Hash(current element of the proof + current computed hash)
                computedHash = _efficientHash(proofElement, computedHash);
            }
        }
        return computedHash;
    }

    function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
        assembly {
            mstore(0x00, a)
            mstore(0x20, b)
            value := keccak256(0x00, 0x40)
        }
    }
}


// File contracts/Utilities.sol

// License: UNLICENSED
// Genius is NOT LICENSED FOR COPYING.
// Genius (C) 2022. All Rights Reserved.


interface IPenalty {
    function setMinersContract(address _minersAddress) external;
    function increasePenaltyCounter(uint256 principal) external;
    function redistribution(bool minerPolicy, uint256 principalPenalties, uint256 rewardPenalties) external
        returns (uint256 oaReceivingAmount, uint256 redistributedPenalties);
    function decMinerPopulation(uint256 genitos) external;
    function incMinerPopulation(uint256 genitos) external;
    function getMaxOrder() external view returns (uint256 maxOrder);
    function counter() external view returns (uint256);
    function calcLemClaimed(Utilities.MinerCache memory miner) external view returns(uint256);
    function endMinerPenalties(Utilities.MinerCache calldata miner, uint256 servedDays,
        uint256 currentGeniusDay, uint256 rewards) external
        returns (Utilities.PenaltyData memory ptData);
    function minerWeight(uint256 weight) external view returns (uint256);
}

interface IGeniusAuction {
    struct AuctionCache {
        uint256 totalBids;
        uint256 firstBid;
        address highestBidder;
        uint256 minerIndex;
        uint256 highestBid;
        address owner;
        bool active;
        uint256 end;
    }

    function getGeniusAuctionState(address owner, uint256 minerIndex)
        external
        returns (AuctionCache memory);

    function cancelAuction(address owner, uint256 minerIndex) external;

    function verifyAuctionNoBid(address owner, uint256 minerIndex)
        external
        returns (bool);

    function setPenaltyAddress(address panlty) external;

    function setMinersContract(address _minersAddress) external;

    function setCalendarContract(address _calendarAddress) external;

    function setGnftContract(address _gnftAddress) external;

}

interface IStabilityPool {

    struct CollateralMiner {
        address collateralToken;
    }

    function getMinerColAddress(address owner, uint256 minerIndex) external returns (address);

    function clearGeniusDebt(
        Utilities.MinerCache calldata miner,
        address minerOwner,
        uint256 minerIndex,
        address beneficiary,
        uint256 currentGeniusDay,
        bool benevolent
    ) external returns (uint256);

    function settleGeniusDebt(address beneficiary, address token,
        uint256 amount, uint256 settlementFeeDays, bool mintNft) external returns (uint256);

    function setOaGrantor(address grantor) external;

    function setOaBeneficiary(address beneficiary) external;

    function genitosRequiredToClear(address collateralAddress, uint256 principal) external returns (uint256);

    function setPenaltyContract(address penaltyContract) external;

    function setMinersContract(address minersContract) external;

    function setGnftContract(address _gnftAddress) external;

    function setAuctionContract(address auctionAddress) external;
}

interface IGeniusCalendar {
    struct GeniusDaySummaryStore {
        uint256 newInflation;
        uint256 redistribution;
        uint256 basicShares;
        uint256 advShares;
    }

    function getDaySummary(uint256 localGeniusDay)
        external
        view
        returns (GeniusDaySummaryStore memory summary);

    function makeGeniusDaySummary(uint256 _summarizeLimit) external;

    function decreaseBurnedSupply(uint256 _amount) external;

    function increaseBurnedSupply(uint256 _amount) external;

    function burnedSupply() external view returns (uint256);

    function calcDayBasicPayout(uint256 _geniusDay)
        external
        view
        returns (uint256);

    function calcDayAdvPayout(uint256 _geniusDay, uint256 _basicPayout)
        external
        view
        returns (uint256);

    function decAdvShares(uint256 _amount) external;

    function decBasicShares(uint256 _amount) external;

    function shareRate() external view returns (uint256);

    function localSummarizeGeniusDay(
        uint256 _summarizeLimit,
        address _summarizer,
        bool mintNft
    ) external returns(uint256 itCount);

    function local10daySummary(uint256 _summarizeLimit, address _summarizer, bool mintNft) external returns(uint256 itCount);

    function local100daySummary(uint256 _summarizeLimit, address _summarizer, bool mintNft) external returns(uint256 itCount);

    function local1000daySummary(uint256 _summarizeLimit, address _summarizer, bool mintNft) external returns(uint256 itCount);

    function incAdvSharesNext(uint256 _amount) external;

    function incBasicSharesNext(uint256 _amount) external;

    function geniusDay() external view returns (uint256);

    function setShareRate(uint256 _shareRate) external;

    function minerTotalPps(uint256 startDay, uint256 lastServedDay, bool minerPolicy) external view returns(uint256);

    function setPenaltyContract(address _penaltyAddress) external;

    function setMinersContract(address _minersAddress) external;

    function setHexodusContract(address _hexodus) external;

    function incDailyPenalties(uint256 _amount) external;

    function setGnftContract(address _gnftAddress) external;

    function summarizeServedDays(address beneficiary, uint256 startDay,
        uint256 promiseDays, bool mintNft) external;
}

interface IMiners {
    function minerStore(address owner, uint256 minerIndex) external view returns(Utilities.MinerCache memory miner);
    function minerStoreLength(address owner) external view returns (uint256 length);
    function getMiners(address owner) external view returns(Utilities.MinerCache[] memory miners);
    function setMinerEnded(address owner, uint256 minerIndex, uint256 ended) external;
    function setMinerStoreLemClaimDay(address owner, uint256 minerIndex, uint256 lemClaimDay) external;
    function setHexodusContract(address _hexodus) external;
    function setGnftContract(address _gnftAddress) external;
    function checkMinerForEnd(
        Utilities.MinerCache memory miner,
        address owner,
        uint256 minerIndex,
        uint256 currentDay,
        uint256 servedDays
    ) external;
}

interface IGnft {
    function mintNft(address to, uint256 nextSalt) external;
}


contract Utilities {

    // Revert Errors
    error NoClaimExists();
    error CannotShutdown();
    error UnauthorizedLostBonusClaiming();

    /** PHI Constants
     * @notice all of the above constants (PHI & GENIUS_RATIO) have 21 decimals of precision
     */
    /* ~ CONSTANTS ~ */
    // PHI = 1.618033988749894848205
    uint256 internal constant PHI = 1618033988749894848204586834;
    // PHI^-2 = 0.38196601125010515179541316563436188227969082019424
    uint256 internal constant PHI_NPOW_2 = 381966011250105151795413165;
    // PHI^-3 = 0.23606797749978969640917366873127623544061835961153
    uint256 internal constant PHI_NPOW_3 = 236067977499789696409173668;
    // PHI^-3.5 = 0.18558516575586807029616916594610619486184991016702
    uint256 internal constant PHI_NPOW_35 = 185585165755868070296169165;
    // PHI^2 = 2.6180339887498948482045868343656381177203091798058
    uint256 internal constant PHI_POW_2 = 2618033988749894848204586834;
    // PHI^PHI = 2.1784575679375991473725457028712458518070433016933
    uint256 internal constant PHI_POW_PHI = 2178457567937599147372545702;

    uint256 internal constant PHI_PRECISION = 1000000000000000000000000000;

    uint256 internal constant GENIUS_PRECISION = 1000000000;

    address internal constant LGENI_OA = 0x66eCa275200015DCD0C2Eaa6E48d4eED3092cDD6;

    uint8 internal constant GENIUS_DECIMALS = 9;

    // Tue Dec 13 2022 20:44:06 GMT+0000
    // Tue Dec 13 2022 13:44:06 GMT-0700 (Mountain Standard Time)
    // Tue Dec 13 2022 14:44:06 PM CST GMT-0600 (Central Standard)
    uint256 public constant LAUNCH_TIMESTAMP = 1670964246;

    // 10 ** 18
    uint256 internal constant SHARE_PRECISION = 1000000000000000000;

    // Penalty Counter Precision: 10 ** 12
    uint256 internal constant PENALTY_COUNTER_PRECISION = 1000000000000;

    // claims root for airdrop
    /** @notice MAKE CONSTANT FOR PRODUCTION */
    bytes32 internal constant MERKLE_ROOT =
        0xcad71776a60b1a4ca80bfa5452bfc50beeb645b7f64e97f5c464ef45a41d548d;

    /* ~ Variables ~ */

    uint256 public advLockedSupply;
    uint256 public basicLockedSupply;

    address public stabilityPoolAddress;
    IStabilityPool stabilityPoolContract;

    address public auctionAddress;
    IGeniusAuction auctionHouse;

    address public calendarAddress;
    IGeniusCalendar calendar;

    address public penaltyAddress;
    IPenalty penaltyContract;

    address public minersAddress;
    IMiners minersContract;

    address public hexodusAddress;
    address public gnftAddress;
    IGnft _gnftContract;

    // Origin Address Wallet
    address public oaGrantor;
    address public oaBeneficiary;

    /** Sacrifice merkle claims tracker */
    mapping(address => bool) public claimed;

    uint256 public oaMintableBalance;

    /* ~ DATA STRUCTS ~ */
    struct MinerCache {
        bool policy;
        bool auctioned;
        bool exodus;
        uint256 startDay;
        uint256 promiseDays;
        uint256 lemClaimDay;
        uint256 rewardShares;
        uint256 penaltyDelta;
        bool nonTransferable;
        uint256 ended;
        uint256 principal; // in genitos (10^9)
        uint256 debtIssueRate;
    }

    struct PenaltyData {
        uint256 eemRewardFee;
        uint256 eemPrincipalFee;
        uint256 eemPenalty;
        uint256 lemRewardFee;
        uint256 lemPrincipalFee;
        uint256 lemPenalty;
    }

    /* ~ EVENTS STRUCTS ~ */
    event Claim(
        address sender,
        address claimant,
        uint256 amount
    );

    event LemRewardsClaim(
        address indexed executorRewardAddress,
        address owner,
        uint256 minerIndex,
        uint256 executorReward
    );

    /**
     * @param  owner        the account that owned the miner at the time of end.
     * @param  minerIndex   the account's index for the miner struct.
     * @param  benevolence  whether this "end" action was for community
     *                      benevolence.
     *
     * @param  principalPayout    The amount of Principal that was returned to
     *                            the owner minus principal penalties.
     * @param  totalMinerRewards  The total amount of rewards--will always be
     *                            the Total PPS multiplied by the Shares.
     * @param  rewardsPayout      The actual amount of rewards paid to the owner
     *                            minus penalties on the rewards.
     * @param  penaltyToMiners    From ending, this amount of penalties was
     *                            redistributed to other Advanced Miners.
     */
    event EndMiner(address indexed owner, uint256 minerIndex, bool benevolence,
        uint256 principalPayout, uint256 totalMinerRewards,
        uint256 rewardsPayout, uint256 penaltyToMiners, Utilities.MinerCache miner);

    event ShutdownMiner(address indexed minerAddress, uint256 minerIndex,
        address indexed executorRewardAddress, uint256 executorReward,
        uint256 performanceRewards, uint256 redistributedPenalties,
        uint256 toOa, uint256 burnedForever, Utilities.MinerCache miner);

    event ChangeOaGrantor(address newOaGrantor, uint256 updated);

    event ChangeOaBeneficiary(address newOaBeneficiary, uint256 updated);
}


// File contracts/Genius.sol

// License: UNLICENSED
// Genius is NOT LICENSED FOR COPYING.
// Genius (C) 2022. All Rights Reserved.






contract Genius is ERC20, ERC20Permit, Utilities, ReentrancyGuard {

    error ErrorNullAddress();
    error ErrorUnauthorized();
    error ErrorCannotReleaseShares();
    error ErrorCannotReleaseAuctionedShares();
    error ErrorNotLaunchedYet();

    struct ShutdownDataCache {
        uint256 principalToRedistribute;
        uint256 rewardsToRedistribute;
        uint256 txPrincipalRewards;
        uint256 txPerformanceRewards;
    }

    constructor(
        address _oaGrantor,
        address _oaBeneficiary
    ) ERC20("Genius", "GENI") ERC20Permit("Genius") {
        if (_oaGrantor == address(0) || _oaBeneficiary == address(0)) {
            revert ErrorNullAddress();
        }
        oaGrantor = _oaGrantor;
        oaBeneficiary = _oaBeneficiary;
        _mint(address(this), 240000000000000000000);
    }

    /**
     * @notice public facing pure, returns decimal precision value of genius
     */
    function decimals() public pure override returns (uint8) {
        return GENIUS_DECIMALS;
    }

    /**
     * @dev only callable by oaGrantor, set oaBeneficiary address
     */
    function changeOaBeneficiary(address _oaBeneficiary) external {
        if (_oaBeneficiary == address(0)) revert ErrorNullAddress();
        if (msg.sender != oaGrantor) revert ErrorUnauthorized();
        oaBeneficiary = _oaBeneficiary;
        stabilityPoolContract.setOaBeneficiary(_oaBeneficiary);
        emit ChangeOaBeneficiary(_oaBeneficiary, block.timestamp);
    }

    /**
     * @dev only callable by oaGrantor, change oaGrantor
     */
    function changeOaGrantor(address _newOaGrantor) external {
        if (_newOaGrantor == address(0)) revert ErrorNullAddress();
        if (msg.sender != oaGrantor) revert ErrorUnauthorized();
        oaGrantor = _newOaGrantor;
        stabilityPoolContract.setOaGrantor(_newOaGrantor);
        emit ChangeOaGrantor(_newOaGrantor, block.timestamp);
    }

    /**
     * @notice public facing, shielded. Only OA can set the auction.
     * @notice auction house must be set before auction functions are operable
     */
    function setAuctionContract(address _auction) external {
// NOTE: OA Grantor check removed because deployment will include these "set"
// functions.  The actual gate that will prevent the Auction Contract being
// set again is the requirement that auctionAddress is not yet set.
//        require(msg.sender == oaGrantor && auctionAddress == address(0), "u");
        if (_auction == address(0)) revert ErrorNullAddress();
        if (auctionAddress != address(0)) revert ErrorUnauthorized();
        auctionAddress = _auction;
        auctionHouse = IGeniusAuction(_auction);
        stabilityPoolContract.setAuctionContract(_auction);
    }

    /**
     * @dev only callable by oaGrantor, set stability pool address (only callable once)
     */
    function setStabilityPoolAddress(address _stabilityPool) external {
// NOTE: this prevents Stability Pool from being set again AFTER deployment
// by ensuring that the contract address has not already been set.
//        require(msg.sender == oaGrantor && stabilityPoolAddress == address(0), "u");
        if (_stabilityPool == address(0)) revert ErrorNullAddress();
        if (stabilityPoolAddress != address(0)) revert ErrorUnauthorized();
        stabilityPoolAddress = _stabilityPool;
        stabilityPoolContract = IStabilityPool(_stabilityPool);
        // NOTE: to prevent a circular dependency, the OA Grantor will need to
        // call this separately.
        //auctionHouse.setStabilityContract(_stabilityPool);
    }

    /**
     * @dev only callable by oaGrantor, set calendar address and calls setters on calendar and auction (only callable once)
     */
    function setCalendarContract(address _calendar) external {
// NOTE: this prevents Calendar from being set again AFTER deployment by
// ensuring that the contract address has not already been set.
        if (_calendar == address(0)) revert ErrorNullAddress();
        if (calendarAddress != address(0)) revert ErrorUnauthorized();
        calendarAddress = _calendar;
        calendar = IGeniusCalendar(_calendar);
        auctionHouse.setCalendarContract(_calendar);
    }

    /**
     * @dev only callable by oaGrantor, set penalty address and calls setters on calendar, stability pool and auction (only callable once)
     */
    function setPenaltyContract(address _pcAddress) external {
// NOTE: this prevents Penalty from being set again AFTER deployment by
// ensuring that the contract address has not already been set.  The deploy
// scripts manage and ensure that this is set at launch.
        if (_pcAddress == address(0)) revert ErrorNullAddress();
        if (penaltyAddress != address(0)) revert ErrorUnauthorized();
//        require(
//            msg.sender == oaGrantor &&
//            penaltyAddress == address(0) &&
//            calendarAddress != address(0) &&
//            stabilityPoolAddress != address(0) &&
//            auctionAddress != address(0)
//        , "u");

// NOTE: combining these saves 0.111 KB.
//        require(msg.sender == oaGrantor && penaltyAddress == address(0), "u");
//        require(calendarAddress != address(0), "1");
//        require(stabilityPoolAddress != address(0), "2");
//        require(auctionAddress != address(0), "3");
        penaltyContract = IPenalty(_pcAddress);
        penaltyAddress = _pcAddress;
        calendar.setPenaltyContract(_pcAddress);
        stabilityPoolContract.setPenaltyContract(_pcAddress);
        auctionHouse.setPenaltyAddress(_pcAddress);
    }

    /**
     * @dev only callable by oaGrantor, sets miners address and calls setters on auction, calendar and stability pool (only callable once)
     */
    function setMinersContract(address _minersAddress) external {
// NOTE: this prevents Miners from being set again AFTER deployment by
// ensuring that the contract address has not already been set.  The deploy
// scripts manage and ensure that this is set at launch.
//        require(msg.sender == oaGrantor && minersAddress == address(0), "u");
        if (_minersAddress == address(0)) revert ErrorNullAddress();
        if (minersAddress != address(0)) revert ErrorUnauthorized();
        minersAddress = _minersAddress;
        minersContract = IMiners(_minersAddress);
        auctionHouse.setMinersContract(_minersAddress);
        calendar.setMinersContract(_minersAddress);
        stabilityPoolContract.setMinersContract(_minersAddress);
        penaltyContract.setMinersContract(_minersAddress);
    }

    function setHexodusContract(address _hexodus) external {
        if (_hexodus == address(0)) revert ErrorNullAddress();
        if (msg.sender != oaGrantor || hexodusAddress != address(0)) {
            revert ErrorUnauthorized();
        }
        hexodusAddress = _hexodus;
        minersContract.setHexodusContract(_hexodus);
        calendar.setHexodusContract(_hexodus);
    }

    /**
     * @notice set up Genius NFT controller
     * @dev allowed only by OA grantor or deployer
     * @param _gnftAddress address of genius NFT controller
     */
    function setGnftContract(address _gnftAddress) external {
// NOTE: GNFT will be launched after the core Genius contracts, and therefore,
// it is necessary to also gate the setting of GNFT's address by limiting this
// action to the OA Grantor.
        if (_gnftAddress == address(0)) revert ErrorNullAddress();
        if (msg.sender != oaGrantor || gnftAddress != address(0)) {
            revert ErrorUnauthorized();
        }
        gnftAddress = _gnftAddress;
        _gnftContract = IGnft(_gnftAddress);

        auctionHouse.setGnftContract(_gnftAddress);
        calendar.setGnftContract(_gnftAddress);
        minersContract.setGnftContract(_gnftAddress);
        stabilityPoolContract.setGnftContract(_gnftAddress);
    }

    function _currentGeniusDay() internal view returns (uint256) {
        if (block.timestamp < LAUNCH_TIMESTAMP) revert ErrorNotLaunchedYet();
        unchecked {
            return (block.timestamp - LAUNCH_TIMESTAMP) / 1 days;
        }
    }

    /**
     * @dev PUBLIC FACING VIEW, view function that returns the total reserved supply accounting
     */
    function reserveSupply() external view returns (uint256) {
        unchecked {
            return totalSupply() + calendar.burnedSupply() + advLockedSupply + basicLockedSupply;
        }
    }

    /**
     * @dev Claims for initial GENI distribution
     * @param destination is the claimant, based on off chain data
     * @param amount claimant's amount of GENI to distribute
     * @param merkleProof array of hashes up the merkleTree
     * @param mintNft  The EOA's preference of whether or not to spend gas for
     *                 the chance of minting an NFT.
     */
    function claimGenius(
        address destination,
        uint256 amount,
        bytes32[] calldata merkleProof,
        bool mintNft
    ) external nonReentrant {
//        require(canClaim(destination, amount, merkleProof), "I");
        if (!canClaim(destination, amount, merkleProof)) {
            revert NoClaimExists();
        }

        claimed[destination] = true;

        if (destination == msg.sender || _currentGeniusDay() < 181 ||
            (block.timestamp < LAUNCH_TIMESTAMP && destination == LGENI_OA))
        {
            ERC20(address(this)).transfer(destination, amount);
            if (mintNft) {
                _gnftContract.mintNft(destination, 0);
            }
            emit Claim(msg.sender, destination, amount);
            return;
        }

        // Sender may get an NFT if they opted-into minting :)
        if (mintNft && _probability(msg.sender, PHI_NPOW_2, PHI_PRECISION, 0, 0)) {
            _gnftContract.mintNft(msg.sender, 1);
        }

        unchecked {
            // the msg.sender will receive 100 GENI, and the remainder goes to the
            // lazy owner of the claim :)
            ERC20(address(this)).transfer(msg.sender, 100000000000);
            ERC20(address(this)).transfer(destination, amount - 100000000000);
        }
        emit Claim(msg.sender, destination, amount);
    }

    /**
     * @dev helper for validating if an address has GENI to claim
     * @return true if claimant has not already claimed and the data is valid, false otherwise
     */
    function canClaim(
        address destination,
        uint256 amount,
        bytes32[] calldata merkleProof
    ) public view returns (bool) {
        bytes32 node = keccak256(abi.encodePacked(destination, amount));
        return
            !claimed[destination] &&
            MerkleProof.verify(merkleProof, MERKLE_ROOT, node);
    }

    /**
     * @dev only callable by auction contract.  Mints Genius token.
     */
    function mint(address owner, uint256 amount) external {
        if (msg.sender != auctionAddress) revert ErrorUnauthorized();
        _mint(owner, amount);
    }

    /**
     * @dev only callable by stability pool, auction, miners, or hexodus.
     *      Burns Genius token.
     */
    function burn(address owner, uint256 amount) external {
        if (
            msg.sender != stabilityPoolAddress &&
            msg.sender != auctionAddress &&
            msg.sender != minersAddress &&
            msg.sender != hexodusAddress
        ) {
            revert ErrorUnauthorized();
        }
        _burn(owner, amount);
    }


    /**
     * @dev INTERNAL, calculates new share rate based on eem payout
     */
    function _newShareRate(MinerCache memory miner, uint256 neemPayout) internal view returns(uint256 newShareRate) {
        unchecked {
            newShareRate = (neemPayout * PHI_PRECISION +
                _min(
                    neemPayout * PHI_POW_PHI,
                    (neemPayout *
                        _min(
                            4444 * PHI_PRECISION,
                            _ceiling(
                                miner.promiseDays *
                                    (PHI_PRECISION + PHI_NPOW_3),
                                PHI_PRECISION
                            ) - PHI_PRECISION
                        )) /
                        1456
                ) +
                neemPayout * _min(neemPayout * PHI_PRECISION, PHI * 10**17) / 10**18) /
                miner.rewardShares /
                PHI_PRECISION;
            return newShareRate;
        }
    }

    /**
     * @dev manage share rate calculations for end miner functionality
     */
    function _manageSystemShares(
        MinerCache memory miner,
        uint256 currentGeniusDay,
        uint256 eemPenalty,
        uint256 lemPenalty,
        uint256 rewards
    ) internal {
        if (miner.lemClaimDay == 0) {
            if (!miner.policy) {
                calendar.decBasicShares(miner.rewardShares);
            } else {
                calendar.decAdvShares(miner.rewardShares);
            }
        }
        unchecked {
            uint256 neemPayout = miner.principal + rewards -
                (currentGeniusDay < miner.startDay + miner.promiseDays ? eemPenalty : 0);
    //        uint256 neemPayout = _neemPayout(
    //            miner.principal,
    //            rewards,
    //            currentGeniusDay,
    //            miner.startDay + miner.promiseDays,
    //            eemPenalty
    //        ); // NEEMP - Non-Early End Mining Payout

            if (neemPayout >= miner.principal) {
                // calculate new Share Rate
                calendar.setShareRate(_newShareRate(miner, neemPayout));
            }
        }
    }

    /**
     * @dev send collateral miner payouts for ended miners
     */
    function _manageCollateralMinerPayouts(
        address owner,
        uint256 netPrincipalPayout,
        uint256 netRewardsPayout,
        bool benevolence
    ) internal {
        if (!benevolence) {
            // if net principal payout > 0:
            //      mint ( principal + rewards )
            if (netPrincipalPayout > 0) {
                unchecked {
                    _mint(owner, netPrincipalPayout + netRewardsPayout);
                }
            }

            // if net rewards payout > 0:
            //      decrease burned supply ( net rewards payout )
            if (netRewardsPayout > 0) {
                calendar.decreaseBurnedSupply(netRewardsPayout);
            }
        }
    }

    /**
     * @dev send miner payouts foe ended miners
     */
    function _manageMinerPayouts(
        MinerCache memory miner,
        address owner,
        uint256 eemPrincipalFee,
        uint256 lemPrincipalFee,
        uint256 eemRewardFee,
        uint256 lemRewardFee,
        uint256 rewards,
        bool benevolence
    ) internal returns (uint256 netPrincipalPayout, uint256 netRewardsPayout) {
        unchecked {
            uint256 principalPenalties = _max(eemPrincipalFee, lemPrincipalFee + penaltyContract.calcLemClaimed(miner));
            uint256 rewardPenalties = _max(eemRewardFee, lemRewardFee);
            netPrincipalPayout = miner.principal > principalPenalties ? miner.principal - principalPenalties : 0;
            netRewardsPayout = rewards > rewardPenalties ? rewards - rewardPenalties : 0;

            if (miner.lemClaimDay == 0) {
                // NOTE: the Release Shares function is responsible for moving the
                // entire principal out of the Locked Supply--because that principal
                // is no-longer "locked".  However, when LEM Claim Day is not set,
                // then the Release Shares function was not called, and the miner's
                // principal must be completely removed from the locked supply.

                if (miner.policy) {
                    // NOTE: the Principal Penalties will STILL be removed from
                    // the locked supply.  In other words, the entire principal
                    // must be removed.  The Lucid Chart specs were wrong;
                    // remove the entire supply.
                    advLockedSupply -= miner.principal;
                } else {
                    // NOTE: the entire principal should be removed from any of
                    // the Locked supplies because the principal is no-longer
                    // "locked".  Of course, any principal that does not get
                    // "minted" to the EOA or OA will need to be added to the
                    // Burned Supply.
                    basicLockedSupply -= miner.principal;
                }

                if (netRewardsPayout > 0 && !benevolence) {
                    calendar.decreaseBurnedSupply(netRewardsPayout);
                }

                // NOTE: if this is Benevolence, then is the entire principal must
                // be moved over to the Burned Supply.
                if (benevolence) {
                    calendar.increaseBurnedSupply(miner.principal);
                }
                else {
                    // But when the miner was not ended benevolently, then we will
                    // increase the burned supply by only the principal penalties,
                    // which *will not* include the LEM Claim Reward.  That reward
                    // will not be included in the 'principalPenalties' calculation
                    // because the lemClaimDay property is 0 :)
                    calendar.increaseBurnedSupply(principalPenalties);
                }
            } else {
                // WARNING: in this scope, the shares were released by the Release
                // Shares function, and therefore, all principal (minus the LEM
                // Claim Reward) was already moved over to the Burned Supply.

                // THEREFORE: we will not increase the Burned Supply here.  We will
                // only decrease the burned supply by the amount of principal and
                // rewards that get removed from the Burned Supply.

                // NOTE: When a non-collateral miner ends, if the shares were
                // released, like in this conditional scope, then the entire
                // principal was already unlocked and moved over to the burned
                // supply.  Therefore, we need to remove any principal (and rewards)
                // that will be minted out of the Burned Supply.
                calendar.decreaseBurnedSupply(netPrincipalPayout + netRewardsPayout);
            }

            // NOTE: all "penalized principal" must move over to the Burned Supply,
            // except for the LEM Claim Reward, because that was given to the EOA
            // that called the Release Shares function.  Furthermore, the payout
            // accounting is handled automatically by _mint -- Total Supply will be
            // increased, and the amount increased was already removed, or not added
            // to, the Burned Supply in the scope, above.

            if (!benevolence && netPrincipalPayout + netRewardsPayout > 0) {
                _mint(owner, netPrincipalPayout + netRewardsPayout);
            }
        }
    }

    /**
     * @dev extended logic for endMiner() function
     * @param  owner  the owner of the miner -- it is the responsibility of the
     *                external functions that call this function to provide end
     *                user security.  Only the msg sender can end a miner.
     * @param  minerIndex   miner index
     * @param  rewards      all performance rewards earned during this miner's
     *                      serving period.
     * @param  benevolence  is this miner ending for the sake of benevolence?
     *                      then anything minted will be burned.
     * @param  mintNft      end user opt-in to spend gas and possibly get an NFT
     */
    function _endMinerDeep(address owner, uint256 minerIndex, uint256 rewards, bool benevolence, bool mintNft) internal
        returns (
            uint256 netPrincipalPayout, uint256 netRewardsPayout, uint256 penaltyToMiners
        ) {
        MinerCache memory miner = minersContract.minerStore(owner, minerIndex);
        unchecked {
            uint256 currentGeniusDay = _currentGeniusDay();
            uint256 servedDays = (
                currentGeniusDay < (miner.startDay + miner.promiseDays)
                    ? currentGeniusDay
                    : (miner.startDay + miner.promiseDays)
                ) - miner.startDay;

            // EM_03
            PenaltyData memory ptData = penaltyContract.endMinerPenalties(miner, servedDays, currentGeniusDay, rewards);

            // EM_04
            if (servedDays < miner.promiseDays) {
                // @dev miner is ending early.
                penaltyToMiners = _redistribution(miner, rewards, ptData.eemPrincipalFee, ptData.eemRewardFee);

                if (mintNft) {
                    if (_probability(owner, PHI / (miner.policy ? 10 : 100),
                        PHI_PRECISION, miner.principal, 0))
                    {
                        _gnftContract.mintNft(owner, 1);
                    }
                    else if (benevolence &&
                        _probability(owner, PHI_PRECISION, PHI_PRECISION, miner.principal, 2))
                    {
                        _gnftContract.mintNft(owner, 1);
                    }
                }
            } else {
                if (currentGeniusDay > miner.startDay + miner.promiseDays + 7) {
                    // @dev miner ended late and will serve late penalties.
                    penaltyToMiners = _redistribution(miner, rewards, ptData.lemPrincipalFee, ptData.lemRewardFee);

                    if (mintNft) {
                        bool minted = false;
                        if (benevolence) {
                            if (_probability(owner, PHI / 10, PHI_PRECISION, miner.principal, 10)) {
                                _gnftContract.mintNft(owner, 11);
                                minted = true;
                            }
                        }

                        if (!minted) {
                            if (_probability(owner, PHI / (miner.policy ? 10 : 100),
                                PHI_PRECISION, miner.principal, 0))
                            {
                                _gnftContract.mintNft(owner, 1);
                            }
                        }
                    }
                } else {
                    // @dev miner ended on time, as promised by the EOA.

                    // if the EOA opted-in to mint an NFT...
                    if (mintNft) {
                        // if this miner had 90 or more promise days...
                        if (miner.promiseDays > 89) {
                            // NOTE: if someone created a BASIC miner w/ 89 Promise
                            // Days on Day 0, then they get a free day and free NFT
                            // ...lucky!! :D
                            if (_probability(owner, PHI_PRECISION, PHI_PRECISION,
                                miner.principal, 0))
                            {
                                _gnftContract.mintNft(owner, 1);
                            }

                            // ALSO NOTE: if this end is a result of benevolence,
                            // then there will be another round of chance.
                            if (benevolence) {
                                if (_probability(owner, PHI_PRECISION, PHI_PRECISION,
                                    miner.principal, 3))
                                {
                                    _gnftContract.mintNft(owner, 4);
                                }
                            }
                        }
                        else if (_probability(owner, PHI / 100, PHI_PRECISION,
                            miner.principal, 0))
                        {
                            // Promise days will be < 90, and therefore this must be a
                            // Basic Miner.
                            _gnftContract.mintNft(owner, 1);
                        }
                        else if (benevolence && _probability(
                            owner, PHI_PRECISION, PHI_PRECISION, miner.principal, 20))
                        {
                            // if this condition is met, then this is a basic miner
                            // that was Proof Of Benevolence'd.
                            _gnftContract.mintNft(owner, 1);
                        }
                    }
                }
            }

            // EM_05 Manage System Shares
            _manageSystemShares(
                miner,
                currentGeniusDay,
                ptData.eemPenalty,
                ptData.lemPenalty,
                rewards
            );

            if (miner.debtIssueRate > 0) {
                // EM_06B Manage Collateral Payouts
                (netPrincipalPayout, netRewardsPayout) = _manageCollateralPayouts(
                    miner, owner, minerIndex, rewards,
                    ptData.lemPrincipalFee, ptData.lemRewardFee, benevolence);
            } else {
                // EM_06A Manage Payouts
                // uint256 principalPenalties = _max(eemPrincipalFee, lemPrincipalFee + lemClaimed);
                // address localOwner = owner;
                uint256 rewards = rewards;
                bool benevolence = benevolence;
                (netPrincipalPayout, netRewardsPayout) = _manageMinerPayouts(
                    miner,
                    owner,
                    ptData.eemPrincipalFee,
                    ptData.lemPrincipalFee,
                    ptData.eemRewardFee,
                    ptData.lemRewardFee,
                    rewards,
                    benevolence
                );
            }
        }
    }

    function _manageCollateralPayouts(
        MinerCache memory _miner,
        address _owner,
        uint256 _minerIndex,
        uint256 _rewards,
        uint256 _principalPenalties,
        uint256 _rewardPenalties,
        bool _benevolence
    ) internal returns (uint256 netPrincipalPayout, uint256 netRewardsPayout) {

        unchecked {
            uint256 rewardPenalties = _min(_rewards, _principalPenalties + _rewardPenalties);
            uint256 currentGeniusDay = _currentGeniusDay();

            // EM_06B
            if (_miner.lemClaimDay == 0) {
                advLockedSupply -= _miner.principal;
            }

            /*
                Utilities.MinerCache calldata miner,
                address minerOwner,
                uint256 minerIndex,
                address beneficiary,
                uint256 currentGeniusDay,
                bool benevolent
            */
            address owner = _owner;
            uint256 minerSettlementAmount = stabilityPoolContract.clearGeniusDebt(
                _miner, owner, _minerIndex, owner,
                currentGeniusDay, _benevolence);
    //        netPrincipalPayout = availablePrincipal > minerSettlementAmount ? availablePrincipal - minerSettlementAmount : 0;
            netPrincipalPayout = _miner.principal > minerSettlementAmount ?
                _miner.principal - minerSettlementAmount : 0;

            // NOTE: _rewardPenalties is what was passed as a parameter, it is NOT
            // the recalculated local variable.  Use the local variable that was a
            // recalculation of the Reward Penalties.
            netRewardsPayout = _rewards - rewardPenalties;
            _manageCollateralMinerPayouts(owner, netPrincipalPayout, netRewardsPayout, _benevolence);
        }
    }

    /**
     * @dev handle miner end functionalities
     * @param  minerIndex   index of the miner
     * @param  benevolence  is the miner benevolenced?
     * @param  mintNft      end user opt-in to spend gas and possibly get an NFT
     */
    function endMiner(
        uint256 minerIndex,
        bool benevolence,
        bool mintNft
    ) external nonReentrant {
        MinerCache memory miner = minersContract.minerStore(msg.sender, minerIndex);
        unchecked {
            uint256 currentGeniusDay = _currentGeniusDay();

            // Promise End Day = miner.startDay + miner.promiseDays
            // Served Days = MIN(Current Genius Day, Promise End Day) - miner.startDay
            // NOTE: if the miner index is invalid, then the "servedDays" value will
            // result in 0.
            uint256 servedDays = (
                currentGeniusDay < (miner.startDay + miner.promiseDays)
                    ? currentGeniusDay
                    : (miner.startDay + miner.promiseDays)
            ) - miner.startDay;

            // Start EM_01
            minersContract.checkMinerForEnd(
                miner,
                msg.sender,
                minerIndex,
                currentGeniusDay,
                servedDays
            );

            // Start Phase EM_02 Summarize Served Days
            //uint256 lastServedDay = miner.startDay + servedDays - 1;
            uint256 lastSummarizedDay = calendar.geniusDay() - 1;

            // if the miner is ending early OR the miner is ending beyond the grace period...
            if (servedDays < miner.promiseDays || miner.startDay + miner.promiseDays + 7 <= currentGeniusDay) {
                //calendar.localSummarizeGeniusDay(_currentGeniusDay() - lastServedDay, msg.sender);
                //calendar.localSummarizeGeniusDay(_currentGeniusDay() - (miner.startDay + servedDays - 1), msg.sender);

                calendar.localSummarizeGeniusDay(0, msg.sender, mintNft);
                calendar.local10daySummary(0, msg.sender, mintNft);
                calendar.local100daySummary(0, msg.sender, mintNft);
                calendar.local1000daySummary(0, msg.sender, mintNft);
            }
            else {
                // NOTE: when the miner ends "on-time", we won't catch-up and
                // summarize every single day, 10-day, 100-day, and 1,000-day
                // period summary.  Instead, since the owner of the miner was a
                // "good end user" and did the "good thing" by ending on-time, we
                // will only summarize the days and periods that enclose the days
                // served.
                //
                // Therefore, we must do this before we calculate the Total PPS and
                // rewards:
                //
                // 1) summarize all single-day summaries: only summarize the served
                //    days that have not yet been summarized.
                //if (lastServedDay > lastSummarizedDay) {
                uint256 lastServedDay = miner.startDay + servedDays - 1;
                if (lastServedDay > lastSummarizedDay) {
                    //calendar.localSummarizeGeniusDay(lastServedDay - lastSummarizedDay, msg.sender);
                    calendar.localSummarizeGeniusDay(
                        lastServedDay - lastSummarizedDay,
                        msg.sender,
                        mintNft
                    );
                }

                // 2) Summarize all full 10-day periods within the SERVED DAYS.
                //    Therefore, if the miner served all days (including) 100-415
                //    and we realize that the only 10-day summaries that exist are
                //    for all days prior to 390, then we must create the 10-day
                //    summary for 390-399 (index 39) and 400-409 (index 40).
                //    Summary 410-419 (index 41) WILL NOT BE CREATED because the
                //    miner did not serve the entire period of days 410-419; that
                //    miner only served days 410-415.  We also won't summarize the
                //    index 41 to save the end user gas :)  And because the end user
                //    does not need index 41 to calculate their Total PPS.
                    // Dai Proving the calculation of index and full period
                    // Summaring example and index and period change
                    // startDay = 0, servedDays = 10, lastServedDay = 9 =>
                    //      10summary index 0, period 1
                    //      100summary index 0, period 1
                    //      1000summary index 0, period 1
                    // startDay = 0, servedDays = 100, lastServedDay = 99 =>
                    //      10summary index 9, period 10
                    //      100summary index 9, period 0
                    //      1000summary index 9, period 0
                    // ... ...
                    // start day = 100, servedDays = 316 lastServedDay = startDay + servedDays - 1 = 415 =>
                    //      10summary index 41, period 42
                    //      100summary index 4, period 5
                    //      1000summary index 0, period 0
                    // Generalizing....
                    // maxXIndex = (miner.startDay +servedDays - 1) / X-days;
                    // maxXPeriod = max10Index + 1 = (miner.startDay +servedDays - 1) / X-days + 1;
                    // actualXPeriod = maxXPeriod - 1 = (miner.startDay +servedDays - 1) / X-days + 1 - 1 = (miner.startDay +servedDays - 1) / X-days
                    calendar.local10daySummary(lastServedDay / 10, msg.sender, mintNft);

                // 3) Just like with the 10-day periods, we must summarize all
                //    100-day periods that the miner needs to calculate the Total
                //    PPS / rewards, properly.
                    calendar.local100daySummary(lastServedDay / 100, msg.sender, mintNft);

                // 4) And then, finally, we must make sure that all 1,000-day
                //    periods within the SERVED DAYS are summarized.
                    calendar.local1000daySummary(lastServedDay / 1000, msg.sender, mintNft);
            }

            //calendar.minerTotalPps(miner.startDay, lastServedDay, miner.policy)
            uint256 totalMinerPps = calendar.minerTotalPps(miner.startDay, (miner.startDay + servedDays - 1), miner.policy);
            uint256 rewards = miner.rewardShares * totalMinerPps / SHARE_PRECISION;

            // End Phase EM_02
            (
                uint256 netPrincipalPayout,
                uint256 netRewardsPayout,
                uint256 penaltyToMiners
            )
            = _endMinerDeep(msg.sender, minerIndex, rewards, benevolence, mintNft);
            MinerCache memory miner2 = miner;
            emit EndMiner(msg.sender, minerIndex, benevolence,
                netPrincipalPayout, rewards,
                netRewardsPayout, penaltyToMiners, miner2);
        }
    }

    /**
     * @dev calculates late end mining penalties
     */
    function _penaltiesLem(
        uint256 geniusDay,
        uint256 promiseEndDay,
        uint256 principal,
        uint256 penalties,
        bool policy
    ) internal pure returns (uint256) {
        unchecked {
            uint256 lateDays = geniusDay - promiseEndDay - 7;
            uint256 min;

            if (!policy) {
                uint256 ceil = (principal * PHI * 100) / (7 * PHI_PRECISION);
                ceil =
                    ((ceil + GENIUS_PRECISION) - GENIUS_PRECISION) *
                    GENIUS_PRECISION;
                min = ceil > principal ? principal : ceil;
            } else {
                uint256 ceil = (principal * PHI_POW_2 * 100) / (7 * PHI_PRECISION);
                ceil =
                    ((ceil + GENIUS_PRECISION) - GENIUS_PRECISION) *
                    GENIUS_PRECISION;
                min = ceil > principal ? principal : ceil;
            }
            return (penalties / 1000) - (lateDays * min);
        }
    }

    /**
     * @dev mint amount stored in oaMintableBalance to the oaBeneficiary
     */
    function claimLostMintBonus() external nonReentrant {
// NOTE: converting this to a revert saves ... only 2 bytes :(
//        require(
//            msg.sender == oaGrantor &&
//            _currentGeniusDay() > 31 &&
//            oaMintableBalance > 0
//        , "u");
        if (msg.sender != oaGrantor ||
            _currentGeniusDay() < 32 ||
            oaMintableBalance == 0)
        {
            revert UnauthorizedLostBonusClaiming();
        }

// NOTE: combining these saves 0.039 KB
//        require(msg.sender == oaGrantor, "u");
//        require(_currentGeniusDay() > 31 && oaMintableBalance > 0, "o");
        _mint(oaBeneficiary, oaMintableBalance);
        oaMintableBalance = 0;
    }

    /**
     * @dev only callable by miners, increase oaMintableBalance
     */
    function incOaMintableBalance(uint256 bonusLostForever) external {
        if (msg.sender != minersAddress) revert ErrorUnauthorized();
        oaMintableBalance += bonusLostForever;
    }

    /**
     * @dev PUBLIC FACING, executes releaseShares core functionality (releases shares from pool)
     * @param  owner    owner of the miner that has the shares to be released.
     * @param  minerId  the INDEX of the owner's miner
     * @param  mintNft  end user opt-in to spend gas and possibly get an NFT
     */
    function releaseShares(address owner, uint256 minerId, bool mintNft) external nonReentrant {
        /*
            Inspect the lucid chart spec carefully, and be sure to update the flow and implementation of the RED TEXT AREAS.
            https://lucid.app/lucidchart/f1a1439e-e956-4a2c-be2f-e5dba66cc6a5/edit?view_items=0ccNMa7ilC91&invitationId=inv_5b23c2b2-e19b-434c-b6a4-9fcc5f90496c#

            For accounting references, check out the "Release Shares Burning Principal" sub-sheet.
            https://docs.google.com/spreadsheets/d/16JXDzM2PwEOQD-324uwTPVYQbhWbUWnavtKcusaWjzs/edit?usp=sharing
         */
        // Require: Miner Ended is False
        MinerCache memory miner = minersContract.minerStore(owner, minerId);
        unchecked {
    // NOTE: combining the 3 requires below saves 0.078 KB.
    //        require(miner.ended == 0, "f");

            // Require: Miner LEM Claim Day == 0
    //        require(miner.lemClaimDay == 0, "L");

            uint256 promiseEndDay = miner.startDay + miner.promiseDays;
            uint256 cgd = _currentGeniusDay();

            // Require: CGD >= Promise End Day
    //        require(cgd >= promiseEndDay, "E");
            if (miner.auctioned && miner.nonTransferable) {
                revert ErrorCannotReleaseAuctionedShares();
            }
            if (
                miner.ended > 0 ||
                miner.lemClaimDay > 0 ||
                cgd < promiseEndDay ||
                miner.promiseDays == 0
            ) {
                revert ErrorCannotReleaseShares();
            }

            // Set the Miner LEM Claim Day to CGD
            minersContract.setMinerStoreLemClaimDay(owner, minerId, cgd);

            // Calculate Daily Late Fee
            uint256 ceil;
            if (miner.policy) {
                // Need to consider PHI precision
                ceil = _ceiling(miner.principal * PHI_POW_2 / 7 / 100, PHI_PRECISION) / PHI_PRECISION;
            } else {
                // Need to consider PHI precision
                ceil = _ceiling(miner.principal * PHI / 7 / 100, PHI_PRECISION) / PHI_PRECISION;
            }

            // Calculate the LEM Release Reward

            // lemReleaseReward = MIN(principal, lateDayCount * dailyLateFee)
            // NOTE: dailyLateFee = MIN(principal, CEILING(calculation here))
            uint256 lemReleaseReward = _min(miner.principal, (cgd > promiseEndDay + 7 ? cgd - promiseEndDay - 7 : 0)
                * _min(miner.principal, ceil));

            // Check if the miner is under auction and the auction has zero bids.
            if (miner.auctioned) {
                if (auctionHouse.verifyAuctionNoBid(owner, minerId)) {
                    auctionHouse.cancelAuction(owner, minerId);
                }
            }

            // Enforce that at least the first day of the miner was summarized.
            // This prevents a possible underflow when removing the shares from the
            // calendar's share pool.
            uint256 gDay = calendar.geniusDay();
            if (gDay < miner.startDay + 1) {
                calendar.localSummarizeGeniusDay(miner.startDay + 1 - gDay, msg.sender, mintNft);
            }

            if (miner.policy) {
                advLockedSupply -= miner.principal;
                calendar.decAdvShares(miner.rewardShares);
            } else {
                basicLockedSupply -= miner.principal;
                calendar.decBasicShares(miner.rewardShares);
            }

            uint256 minerSettlementAmount;
            if (miner.debtIssueRate > 0) {
                if (lemReleaseReward > 0) {
                    address colToken = stabilityPoolContract.getMinerColAddress(owner, minerId);
                    minerSettlementAmount =
                        stabilityPoolContract.settleGeniusDebt(msg.sender, colToken, lemReleaseReward, cgd - miner.startDay, false);
                    lemReleaseReward = _min(minerSettlementAmount, lemReleaseReward);
                }
            } else {
                calendar.increaseBurnedSupply(miner.principal > lemReleaseReward ? miner.principal - lemReleaseReward : 0);
                if (lemReleaseReward > 0) {
                    _mint(msg.sender, lemReleaseReward);
                }
            }

            if (mintNft && _probability(msg.sender, PHI_NPOW_3, PHI_PRECISION, 0, 0)) {
                _gnftContract.mintNft(msg.sender, 1);
            }

            // By checking the event param, we can have people know that if there is no debt to settle, they actually get no reward.
            emit LemRewardsClaim(msg.sender, owner, minerId, lemReleaseReward);
        }
    }

    /**
     * @dev round up or ceil a number with the precision specified.
     * @param a number to be rounded up or ceiled
     * @param m precision (10^x, where x >= 0) of ceiling the number
     * @return ceiled value
     */
    function _ceiling(uint256 a, uint256 m) internal pure returns (uint256) {
        //return ((a + m - 1) / m) * m;
        unchecked {
            return (a / m + (a % m == 0 ? 0 : 1)) * m;
        }
    }

    /**
     * @dev compare two numbers and return smaller one.
     * @param a number a
     * @param b number b
     * @return smaller value
     */
    function _min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev compare two numbers and return greater one.
     * @param a number a
     * @param b number b
     * @return greater value
     */
    function _max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev PUBLIC FACING, executes shutdownMiner core functionality
     * @param owner owner of the miner
     * @param minerId ID(index) of the miner
     */
    function shutdownMiner(address owner, uint256 minerId, bool mintNft)
        external nonReentrant
    {
        MinerCache memory miner = minersContract.minerStore(owner, minerId);
        unchecked {
            // First Late Day = Start Day + Promise Days + 7
            uint256 forcedShutdownDay = miner.policy ?
                miner.startDay + miner.promiseDays + 275 :
                miner.startDay + miner.promiseDays + 440;

            if (
                _currentGeniusDay() < forcedShutdownDay ||
                miner.ended > 0 ||
                miner.auctioned ||
                miner.promiseDays == 0
            ) {
                revert CannotShutdown();
            }

            ShutdownDataCache memory shutdownData =
                _shutdownMiner(miner, owner, minerId, forcedShutdownDay, mintNft);

            if (mintNft && _probability(msg.sender, PHI_NPOW_3, PHI_PRECISION, 0, 0)) {
                _gnftContract.mintNft(msg.sender, 1);
            }

            _mintTokenToOa(owner, minerId, shutdownData.principalToRedistribute,
                shutdownData.rewardsToRedistribute, shutdownData.txPrincipalRewards,
                shutdownData.txPerformanceRewards, miner);
        }
    }

    function _shutdownMiner(
        MinerCache memory miner,
        address owner,
        uint256 minerId,
        uint256 forcedShutdownDay,
        bool mintNft
    ) private returns (ShutdownDataCache memory shutdownData) {
        // Set Miner "ended" to the current time
        minersContract.setMinerEnded(owner, minerId, block.timestamp);
        miner.ended = block.timestamp;
        penaltyContract.decMinerPopulation(miner.principal);
        uint256 lemClaimed = penaltyContract.calcLemClaimed(miner);

        // Before we calculate the rewards, we need to be sure that all
        // SERVED DAYS are Summarized.
        //
        // Then we must make sure all 10-day periods within the SERVED DAYS
        // are also summarized.
        //
        // Then we must make sure all 100-day periods within the SERVED DAYS
        // are summarized.
        //
        // And then, finally, we must make sure that all 1,000-day periods
        // within the SERVED DAYS are summarized.
        calendar.summarizeServedDays(msg.sender, miner.startDay,
            miner.promiseDays, mintNft);
        unchecked {
            uint256 netPrincipal = miner.principal > lemClaimed ? miner.principal - lemClaimed : 0;
            uint256 rewards = miner.rewardShares *
                calendar.minerTotalPps(miner.startDay, miner.startDay + miner.promiseDays - 1, miner.policy)
                / SHARE_PRECISION;
            uint256 netPayout = netPrincipal + rewards;

            // Start Phase EM_04
            _manageShutdownMinerShares(miner, forcedShutdownDay, rewards);
            // End Phase EM_04

            uint256 maxTxRewards = netPayout * PHI_NPOW_3 / PHI_PRECISION;
            shutdownData.txPrincipalRewards = _min(netPrincipal, maxTxRewards);

            if (miner.debtIssueRate > 0) {
                /*
                    Utilities.MinerCache calldata miner,
                    address minerOwner,
                    uint256 minerIndex,
                    address beneficiary,
                    uint256 currentGeniusDay,
                    bool benevolent
                */

                MinerCache memory miner2  = miner;
                bool mintNft2 = mintNft;
                address owner2 = owner;
                uint256 minerId2 = minerId;

                uint256 txSettledRewards = stabilityPoolContract.settleGeniusDebt(
                    msg.sender,
                    stabilityPoolContract.getMinerColAddress(owner2, minerId2),
                    shutdownData.txPrincipalRewards,
                    _currentGeniusDay() - miner2.startDay,
                    mintNft2
                );

                uint256 minerDebtCleared = stabilityPoolContract.clearGeniusDebt(
                    miner2, owner2, minerId2, address(0),
                    _currentGeniusDay(), true);

                shutdownData.txPerformanceRewards = maxTxRewards > txSettledRewards ? maxTxRewards - txSettledRewards : 0;
                shutdownData.txPrincipalRewards -= txSettledRewards;
                // shutdownData.principalToRedistribute = netPrincipal - txSettledRewards - minerDebtCleared -
                // (shutdownData.txPrincipalRewards - txSettledRewards)
                // = netPrincipal - minerDebtCleared - shutdownData.txPrincipalRewards
                // it might be underflow
                uint256 totalSub = txSettledRewards + minerDebtCleared + shutdownData.txPrincipalRewards;
                if (netPrincipal > totalSub) {
                    shutdownData.principalToRedistribute = netPrincipal - totalSub;
                    calendar.increaseBurnedSupply(shutdownData.principalToRedistribute);
                } else {
                    shutdownData.principalToRedistribute = 0;
                }
                shutdownData.rewardsToRedistribute = rewards > shutdownData.txPerformanceRewards ? rewards - shutdownData.txPerformanceRewards : 0;

                if (miner2.lemClaimDay == 0) {
                    if (miner2.policy) {
                        advLockedSupply -= miner2.principal;
                    } else {
                        basicLockedSupply -= miner2.principal;
                    }
                }
            } else { // Debt Issue Rate == 0
                shutdownData.txPerformanceRewards = maxTxRewards > netPrincipal ? maxTxRewards - netPrincipal : 0;
                shutdownData.principalToRedistribute = netPrincipal > shutdownData.txPrincipalRewards ? netPrincipal - shutdownData.txPrincipalRewards : 0;
                shutdownData.rewardsToRedistribute = rewards > shutdownData.txPerformanceRewards ? rewards - shutdownData.txPerformanceRewards : 0;

                if (miner.lemClaimDay == 0)  {
                    calendar.increaseBurnedSupply(miner.principal);
                    if (miner.policy) {
                        advLockedSupply -= miner.principal;
                    } else {
                        basicLockedSupply -= miner.principal;
                    }
                }
            }
        }

    }

    function _mintTokenToOa(
        address owner,
        uint256 minerId,
        uint256 principalToRedistribute,
        uint256 rewardsToRedistribute,
        uint256 txPrincipalRewards,
        uint256 txPerformanceRewards,
        MinerCache memory miner
    ) private {
        unchecked {
            uint256 totalPenalties = principalToRedistribute + rewardsToRedistribute;
            uint256 redistributedPenalties = totalPenalties * PHI_PRECISION / PHI;
            calendar.incDailyPenalties(redistributedPenalties);
            uint256 toOa = totalPenalties - redistributedPenalties - (totalPenalties * PHI_NPOW_3 / PHI_PRECISION);
            uint256 resurrection = toOa + txPrincipalRewards + txPerformanceRewards;

            if (resurrection > 0) {
                calendar.decreaseBurnedSupply(resurrection);
            }

            if (toOa > 0) {
                _mint(oaBeneficiary, toOa);
            }

            // we will not check if txPerf + txPrinc is > 0 because if this is the
            // case, then the miner was likely a stale, "dust" miner with not much
            // principal and likely zero earnings.
            _mint(msg.sender, txPerformanceRewards + txPrincipalRewards);

            emit ShutdownMiner(owner, minerId, msg.sender,
                txPrincipalRewards, txPerformanceRewards,
                redistributedPenalties, toOa, (totalPenalties * PHI_NPOW_3 / PHI_PRECISION),
                miner
            );
        }
    }

    /**
     * @dev INTERNAL, helper function used in shutdownMiner()
     */
    function _manageShutdownMinerShares(
        MinerCache memory miner, uint256 forcedShutdownDay, uint256 rewards
    ) internal {
        if (miner.lemClaimDay == 0) {
            if (!miner.policy) {
                calendar.decBasicShares(miner.rewardShares);
            } else {
                calendar.decAdvShares(miner.rewardShares);
            }
        }
        unchecked {
            calendar.setShareRate(_newShareRate(miner, miner.principal + rewards));
        }
    }

    /**
     * @dev only callable by calendar, mint summary rewards calculated by
     * summarize functions in calendar.
     */
    function mintSummaryReward(address _to, uint256 _amount) external {
        if (msg.sender != calendarAddress) {
            revert ErrorUnauthorized();
        }
        _mint(_to, _amount);
    }

    /**
     * @dev only callable by Penalty contract, advLockedSupply accounting.
     */
    function decAdvLockedSupply(uint256 _amount) external {
        if (msg.sender != penaltyAddress) revert ErrorUnauthorized();
        unchecked { advLockedSupply -= _amount; }
    }

    /**
     * @dev only callable by Penalty contract, basicLockedSupply accounting.
     */
    function decBasicLockedSupply(uint256 _amount) external {
        if (msg.sender != penaltyAddress) revert ErrorUnauthorized();
        unchecked { basicLockedSupply -= _amount; }
    }

    /**
     * @dev only callable by Miners contract, advLockedSupply accounting.
     */
    function incAdvLockedSupply(uint256 _amount) external {
        if (msg.sender != minersAddress) revert ErrorUnauthorized();
        unchecked { advLockedSupply += _amount; }
    }

    /**
     * @dev only callable by Miners contract, basicLockedSupply accounting.
     */
    function incBasicLockedSupply(uint256 _amount) external {
        if (msg.sender != minersAddress) revert ErrorUnauthorized();
        unchecked { basicLockedSupply += _amount; }
    }

    /**
     * @dev only called by the End Miner functionality; this redistributes
     * fees incurrred for ending the miner.
     * @param rewards the performance earnings of the miner.
     * @param principalPenalties penalties applied to the principal.
     * @param rewardPenalties penalties applied to the rewards.
     * @return redistributedPenalties the amount of penalties that will be
     * redistributed to Advanced Miners that are currently active.
     */
    function _redistribution(
        MinerCache memory miner,
        uint256 rewards,
        uint256 principalPenalties,
        uint256 rewardPenalties
    ) internal returns (uint256 redistributedPenalties) {
        unchecked {
            uint256 totalPenalties;
            if (miner.debtIssueRate > 0) {
                totalPenalties = _min(rewards, principalPenalties + rewardPenalties);
                rewardPenalties = totalPenalties;
                principalPenalties = 0;
            } else {
                if (miner.lemClaimDay == 0) {
                    if (miner.policy) {
                        advLockedSupply -= principalPenalties;
                    } else {
                        basicLockedSupply -= principalPenalties;
                    }
                    calendar.increaseBurnedSupply(principalPenalties);
                }
                totalPenalties = principalPenalties + rewardPenalties;
            }

            redistributedPenalties = totalPenalties * PHI_PRECISION / PHI;
            calendar.incDailyPenalties(redistributedPenalties);
            uint256 oaReceivingAmount = totalPenalties - redistributedPenalties
                - totalPenalties * PHI_NPOW_3 / PHI_PRECISION;

            _mint(oaBeneficiary, oaReceivingAmount);
            calendar.decreaseBurnedSupply(oaReceivingAmount);
        }
    }

    /**
     * @dev   Calculates a secure-ish random 256-bit number for GENFTs.
     *        These are the motivations and purposes behind each parameter to
     *        calculate the random number:
     *
     *        1. salt: each function that initially invokes the first
     *           _probability / _random functions will originate with its own
     *           unique 'salt'.  This is to ensure that when multiple functions
     *           are called by the EOA within the same transaction, the EOA will
     *           have equally-random chances to yield a completely different
     *           GENFT.  The block timestamp is added to salt to make it more
     *           difficult for an end user to predict which GENFT they'll mint.
     *
     *        2. blockhash: the only EOAs that can reasonably use this to their
     *           advantage without adding significant costs for the transaction,
     *           such as the capital required to create a miner with a weight of
     *           1 or greater, are EOAs that run the function to "claim" their
     *           sacrifie tokens and EOAs that run the function to summarize
     *           a Genius Calendar period.  That is because these functions have
     *           a 100% chance to mint a GENFT.
     *
     *           However, the "claim" function can only be run once per EOA that
     *           participated in the Genius Sacrifice Event.  Therefore, this
     *           will not be useful for the EOA, even if they have the ability
     *           to influence the block hash.  See: https://sacrifice.to
     *
     *           In regards to the Calendar summarize functions, the EOA cannot
     *           waste time figuring out their best chances because if they are
     *           not the first EOA to run the function, then they lose the
     *           ability to run the function for the day/period.
     *
     *           For every other function, the EOA is prevented from spamming
     *           these functions not only from the blockchain's gas fee, but
     *           spam is additional prevented because every other function
     *           has one of the following qualities:
     *              a. It is a "first-come, first-to-benefit" function, e.g. the
     *                 functions to claimAuction, releaseShares, etc.
     *              b. The function is necessary for "cleaning up" or updating
     *                 Genius' environment, active shares, etc., and therefore,
     *                 the EOA should be rewarded as they wish.
     *              c. The EOA had to have input something of value to the
     *                 network, i.e. they had to put up a significant, non-dust
     *                 amount of GENI capital, which ultimately benefitted the
     *                 Genius end users.
     *
     *           Therefore, if it is worth it for the EOA to exert the position-
     *           ing and effort to influence random numbers for their purpose,
     *           then this action is also not guaranteed, and its repeated
     *           action is designed to benefit the Genius end user.  Since the
     *           purpose of GENFTs is purely as collectibles and *not* for
     *           significant financial value, it is perfectly acceptable for
     *           EOAs to "game" the possibilities of yielding the GENFT that
     *           they desire.
     *
     *        3. account: used so that different EOAs running the same GENFT
     *           minting functions within the same block will not generate the
     *           same GENFTs.  Likewise, if different accounts are unpacking
     *           booster/ultimate packs within the same transaction, this will
     *           ensure that the end users do not unpack the same GENFTs.
     *
     *        Finally, it should be noted that the GENFT controller prevents
     *        EOAs from minting GENFTs with the same randomization salt or
     *        unpacking to mint multiple GENFTs within the same block.  This is
     *        done to prevent the end user from duplicating multiple copies of
     *        the same GENFTs.
     *
     * @param account address used to generate a random number
     * @param salt  when multiple random numbers are necessary, this is used
     *                to add some randomness.  This is important because within
     *                a single transaction, the random number will be exactly
     *                the same without this _salt.
     */
    function _random(address account, uint256 salt) internal view returns (uint256) {
        unchecked {
            return uint256(
                keccak256(
                    abi.encodePacked(
                        salt + block.timestamp,
                        blockhash(block.number),
                        account
                    )
                )
            );
        }
    }

    /**
     * @dev     You tell the function the "probability" that something will
     *          happen, and this function tells you if it happened :)
     * @param   account address used for the calculation
     * @param   chances How many chances of successes will there be out of the entire...
     * @param   totals ...range of precision totals?
     * @param   weight will the weight be based on?  Use "0" for no weight.
     * @param   nonce "nonce" so that if this function is called multiple times
     *          within a transaction, the random number will be different each
     *          time this function is invoked.
     * @return  Whether the probability test was succcessful :)
     */
    function _probability(address account, uint256 chances, uint256 totals, uint256 weight, uint256 nonce)
        internal view returns (bool)
    {
        unchecked {
            // STEP 1: increase the weight of the chances if necessary.
            if (weight > 0) {
                chances = chances * penaltyContract.minerWeight(weight)
                    / PENALTY_COUNTER_PRECISION;
            }

            if (chances >= totals) {
                return true;
            }

            // NOTE: beyond this point, chances < totals, and therefore, it is not
            // possible for chances to be equal to or greater than totals, resulting
            // in an overflow.

            // STEP 2: Find a random number between 0 and the totals.
            uint256 random = _random(account, nonce) % totals;

            // STEP 3: Is the random number within the probability range of chance?
            // The minimum that (totals - chances) will be is 1, so therefore, if
            // the random number is 0, then the return value will be false.  In the
            // situation where this is a "1 in X" chance, "random" must be 0 in
            // order for the logical expression (below) to be true.
            return random >= totals - chances;
        }
    }

}
        

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_oaGrantor","internalType":"address"},{"type":"address","name":"_oaBeneficiary","internalType":"address"}]},{"type":"error","name":"CannotShutdown","inputs":[]},{"type":"error","name":"ErrorCannotReleaseAuctionedShares","inputs":[]},{"type":"error","name":"ErrorCannotReleaseShares","inputs":[]},{"type":"error","name":"ErrorNotLaunchedYet","inputs":[]},{"type":"error","name":"ErrorNullAddress","inputs":[]},{"type":"error","name":"ErrorUnauthorized","inputs":[]},{"type":"error","name":"NoClaimExists","inputs":[]},{"type":"error","name":"UnauthorizedLostBonusClaiming","inputs":[]},{"type":"event","name":"Approval","inputs":[{"type":"address","name":"owner","internalType":"address","indexed":true},{"type":"address","name":"spender","internalType":"address","indexed":true},{"type":"uint256","name":"value","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"ChangeOaBeneficiary","inputs":[{"type":"address","name":"newOaBeneficiary","internalType":"address","indexed":false},{"type":"uint256","name":"updated","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"ChangeOaGrantor","inputs":[{"type":"address","name":"newOaGrantor","internalType":"address","indexed":false},{"type":"uint256","name":"updated","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Claim","inputs":[{"type":"address","name":"sender","internalType":"address","indexed":false},{"type":"address","name":"claimant","internalType":"address","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"EndMiner","inputs":[{"type":"address","name":"owner","internalType":"address","indexed":true},{"type":"uint256","name":"minerIndex","internalType":"uint256","indexed":false},{"type":"bool","name":"benevolence","internalType":"bool","indexed":false},{"type":"uint256","name":"principalPayout","internalType":"uint256","indexed":false},{"type":"uint256","name":"totalMinerRewards","internalType":"uint256","indexed":false},{"type":"uint256","name":"rewardsPayout","internalType":"uint256","indexed":false},{"type":"uint256","name":"penaltyToMiners","internalType":"uint256","indexed":false},{"type":"tuple","name":"miner","internalType":"struct Utilities.MinerCache","indexed":false,"components":[{"type":"bool","name":"policy","internalType":"bool"},{"type":"bool","name":"auctioned","internalType":"bool"},{"type":"bool","name":"exodus","internalType":"bool"},{"type":"uint256","name":"startDay","internalType":"uint256"},{"type":"uint256","name":"promiseDays","internalType":"uint256"},{"type":"uint256","name":"lemClaimDay","internalType":"uint256"},{"type":"uint256","name":"rewardShares","internalType":"uint256"},{"type":"uint256","name":"penaltyDelta","internalType":"uint256"},{"type":"bool","name":"nonTransferable","internalType":"bool"},{"type":"uint256","name":"ended","internalType":"uint256"},{"type":"uint256","name":"principal","internalType":"uint256"},{"type":"uint256","name":"debtIssueRate","internalType":"uint256"}]}],"anonymous":false},{"type":"event","name":"LemRewardsClaim","inputs":[{"type":"address","name":"executorRewardAddress","internalType":"address","indexed":true},{"type":"address","name":"owner","internalType":"address","indexed":false},{"type":"uint256","name":"minerIndex","internalType":"uint256","indexed":false},{"type":"uint256","name":"executorReward","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"ShutdownMiner","inputs":[{"type":"address","name":"minerAddress","internalType":"address","indexed":true},{"type":"uint256","name":"minerIndex","internalType":"uint256","indexed":false},{"type":"address","name":"executorRewardAddress","internalType":"address","indexed":true},{"type":"uint256","name":"executorReward","internalType":"uint256","indexed":false},{"type":"uint256","name":"performanceRewards","internalType":"uint256","indexed":false},{"type":"uint256","name":"redistributedPenalties","internalType":"uint256","indexed":false},{"type":"uint256","name":"toOa","internalType":"uint256","indexed":false},{"type":"uint256","name":"burnedForever","internalType":"uint256","indexed":false},{"type":"tuple","name":"miner","internalType":"struct Utilities.MinerCache","indexed":false,"components":[{"type":"bool","name":"policy","internalType":"bool"},{"type":"bool","name":"auctioned","internalType":"bool"},{"type":"bool","name":"exodus","internalType":"bool"},{"type":"uint256","name":"startDay","internalType":"uint256"},{"type":"uint256","name":"promiseDays","internalType":"uint256"},{"type":"uint256","name":"lemClaimDay","internalType":"uint256"},{"type":"uint256","name":"rewardShares","internalType":"uint256"},{"type":"uint256","name":"penaltyDelta","internalType":"uint256"},{"type":"bool","name":"nonTransferable","internalType":"bool"},{"type":"uint256","name":"ended","internalType":"uint256"},{"type":"uint256","name":"principal","internalType":"uint256"},{"type":"uint256","name":"debtIssueRate","internalType":"uint256"}]}],"anonymous":false},{"type":"event","name":"Transfer","inputs":[{"type":"address","name":"from","internalType":"address","indexed":true},{"type":"address","name":"to","internalType":"address","indexed":true},{"type":"uint256","name":"value","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"DOMAIN_SEPARATOR","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"LAUNCH_TIMESTAMP","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"advLockedSupply","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"allowance","inputs":[{"type":"address","name":"owner","internalType":"address"},{"type":"address","name":"spender","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"approve","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"auctionAddress","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"balanceOf","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"basicLockedSupply","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"burn","inputs":[{"type":"address","name":"owner","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"calendarAddress","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"canClaim","inputs":[{"type":"address","name":"destination","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"bytes32[]","name":"merkleProof","internalType":"bytes32[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"changeOaBeneficiary","inputs":[{"type":"address","name":"_oaBeneficiary","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"changeOaGrantor","inputs":[{"type":"address","name":"_newOaGrantor","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"claimGenius","inputs":[{"type":"address","name":"destination","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"bytes32[]","name":"merkleProof","internalType":"bytes32[]"},{"type":"bool","name":"mintNft","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"claimLostMintBonus","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"claimed","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"decAdvLockedSupply","inputs":[{"type":"uint256","name":"_amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"decBasicLockedSupply","inputs":[{"type":"uint256","name":"_amount","internalType":"uint256"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint8","name":"","internalType":"uint8"}],"name":"decimals","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"decreaseAllowance","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"subtractedValue","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"endMiner","inputs":[{"type":"uint256","name":"minerIndex","internalType":"uint256"},{"type":"bool","name":"benevolence","internalType":"bool"},{"type":"bool","name":"mintNft","internalType":"bool"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"gnftAddress","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"hexodusAddress","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"incAdvLockedSupply","inputs":[{"type":"uint256","name":"_amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"incBasicLockedSupply","inputs":[{"type":"uint256","name":"_amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"incOaMintableBalance","inputs":[{"type":"uint256","name":"bonusLostForever","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"increaseAllowance","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"addedValue","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"minersAddress","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"mint","inputs":[{"type":"address","name":"owner","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"mintSummaryReward","inputs":[{"type":"address","name":"_to","internalType":"address"},{"type":"uint256","name":"_amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"name","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"nonces","inputs":[{"type":"address","name":"owner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"oaBeneficiary","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"oaGrantor","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"oaMintableBalance","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"penaltyAddress","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"permit","inputs":[{"type":"address","name":"owner","internalType":"address"},{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"value","internalType":"uint256"},{"type":"uint256","name":"deadline","internalType":"uint256"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"releaseShares","inputs":[{"type":"address","name":"owner","internalType":"address"},{"type":"uint256","name":"minerId","internalType":"uint256"},{"type":"bool","name":"mintNft","internalType":"bool"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"reserveSupply","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setAuctionContract","inputs":[{"type":"address","name":"_auction","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setCalendarContract","inputs":[{"type":"address","name":"_calendar","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setGnftContract","inputs":[{"type":"address","name":"_gnftAddress","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setHexodusContract","inputs":[{"type":"address","name":"_hexodus","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setMinersContract","inputs":[{"type":"address","name":"_minersAddress","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setPenaltyContract","inputs":[{"type":"address","name":"_pcAddress","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setStabilityPoolAddress","inputs":[{"type":"address","name":"_stabilityPool","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"shutdownMiner","inputs":[{"type":"address","name":"owner","internalType":"address"},{"type":"uint256","name":"minerId","internalType":"uint256"},{"type":"bool","name":"mintNft","internalType":"bool"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"stabilityPoolAddress","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"symbol","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalSupply","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transfer","inputs":[{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transferFrom","inputs":[{"type":"address","name":"from","internalType":"address"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]}]
              

Contract Creation Code

Verify & Publish
0x6101406040523480156200001257600080fd5b50604051620062a1380380620062a18339810160408190526200003591620003ab565b6040518060400160405280600681526020016547656e69757360d01b81525080604051806040016040528060018152602001603160f81b8152506040518060400160405280600681526020016547656e69757360d01b8152506040518060400160405280600481526020016347454e4960e01b8152508160039080519060200190620000c3929190620002e8565b508051620000d9906004906020840190620002e8565b5050825160208085019190912083518483012060e08290526101008190524660a0818152604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81880181905281830187905260608201869052608082019490945230818401528151808203909301835260c0019052805194019390932091935091906080523060601b60c0526101205250506001601a555050506001600160a01b03821615806200019457506001600160a01b038116155b15620001b3576040516342bec4df60e01b815260040160405180910390fd5b601680546001600160a01b038085166001600160a01b0319928316179092556017805492841692909116919091179055620001f830680d02ab486cedc0000062000200565b505062000444565b6001600160a01b0382166200025b5760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015260640160405180910390fd5b80600260008282546200026f9190620003e2565b90915550506001600160a01b038216600090815260208190526040812080548392906200029e908490620003e2565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b828054620002f69062000407565b90600052602060002090601f0160209004810192826200031a576000855562000365565b82601f106200033557805160ff191683800117855562000365565b8280016001018555821562000365579182015b828111156200036557825182559160200191906001019062000348565b506200037392915062000377565b5090565b5b8082111562000373576000815560010162000378565b80516001600160a01b0381168114620003a657600080fd5b919050565b60008060408385031215620003be578182fd5b620003c9836200038e565b9150620003d9602084016200038e565b90509250929050565b600082198211156200040257634e487b7160e01b81526011600452602481fd5b500190565b600181811c908216806200041c57607f821691505b602082108114156200043e57634e487b7160e01b600052602260045260246000fd5b50919050565b60805160a05160c05160601c60e0516101005161012051615e0a6200049760003960006134a9015260006134f8015260006134d30152600061342c01526000613456015260006134800152615e0a6000f3fe608060405234801561001057600080fd5b506004361061030c5760003560e01c806360f1edf71161019d578063a9059cbb116100e9578063d505accf116100a2578063dfcad7ea1161007c578063dfcad7ea146106ad578063e09f0817146106c0578063e954f41d146106d3578063e9c4239b146106de57600080fd5b8063d505accf14610674578063dc38bdb514610687578063dd62ed3e1461069a57600080fd5b8063a9059cbb146105f2578063ad09d65814610605578063b074d2f614610618578063c884ef831461062b578063cb01454b1461064e578063d11d01db1461066157600080fd5b806376df7c321161015657806395d89b411161013057806395d89b41146105b157806396cc563f146105b95780639dc29fac146105cc578063a457c2d7146105df57600080fd5b806376df7c32146105785780637ecebe001461058b578063873f0a331461059e57600080fd5b806360f1edf7146104f057806360f6c4d61461050357806363fcd08a146105165780636f72995f1461052957806370a082311461053c57806372fb9c551461056557600080fd5b806323b872dd1161025c578063379b2c1311610215578063445c101f116101ef578063445c101f146104ae5780634bd0cd42146104b75780635476ea9e146104ca57806357ec85dd146104dd57600080fd5b8063379b2c1314610475578063395093511461048857806340c10f191461049b57600080fd5b806323b872dd1461041c578063269858651461042f5780632c265c88146104425780632ceaaf281461044b578063313ce5671461045e5780633644e5151461046d57600080fd5b80630c6eae62116102c9578063197162be116102a3578063197162be146103e55780631ad03156146103f85780631bf803391461040b578063238bf8e01461041457600080fd5b80630c6eae62146103b757806315df1fca146103ca57806318160ddd146103dd57600080fd5b8063023924c71461031157806303d41eb61461032657806306fdde0314610341578063095ea7b314610356578063096ff58a146103795780630b622ab21461038c575b600080fd5b61032461031f36600461562d565b6106f1565b005b61032e6107c7565b6040519081526020015b60405180910390f35b610349610860565b6040516103389190615b18565b610369610364366004615752565b6108f2565b6040519015158152602001610338565b61032461038736600461562d565b61090c565b60095461039f906001600160a01b031681565b6040516001600160a01b039091168152602001610338565b6103246103c536600461562d565b610ae6565b60115461039f906001600160a01b031681565b60025461032e565b6103246103f33660046159e0565b610bea565b600d5461039f906001600160a01b031681565b61032e60075481565b610324610c21565b61036961042a36600461569d565b610cc1565b61032461043d3660046157d7565b610ce5565b61032e60085481565b60165461039f906001600160a01b031681565b60405160098152602001610338565b61032e6110c4565b60135461039f906001600160a01b031681565b610369610496366004615752565b6110d3565b6103246104a9366004615752565b6110f5565b61032e60195481565b6103246104c536600461562d565b61112e565b600b5461039f906001600160a01b031681565b6103246104eb366004615a10565b6112f0565b6103246104fe36600461562d565b611a95565b61032461051136600461562d565b611b8e565b60175461039f906001600160a01b031681565b61032461053736600461562d565b611c33565b61032e61054a36600461562d565b6001600160a01b031660009081526020819052604090205490565b6103246105733660046159e0565b611d44565b6103246105863660046159e0565b611d7a565b61032e61059936600461562d565b611dbf565b60145461039f906001600160a01b031681565b610349611ddd565b6103246105c736600461562d565b611dec565b6103246105da366004615752565b611f4f565b6103696105ed366004615752565b611fcb565b610369610600366004615752565b612046565b61032461061336600461562d565b612054565b600f5461039f906001600160a01b031681565b61036961063936600461562d565b60186020526000908152604090205460ff1681565b61032461065c3660046159e0565b6120d1565b61032461066f366004615849565b612107565b6103246106823660046156dd565b61230b565b61036961069536600461577d565b61246f565b61032e6106a8366004615665565b612541565b6103246106bb366004615752565b61256c565b6103246106ce3660046159e0565b612597565b61032e636398e41681565b6103246106ec366004615849565b6125ce565b6001600160a01b038116610718576040516342bec4df60e01b815260040160405180910390fd5b600b546001600160a01b03161561074257604051631d0b184b60e21b815260040160405180910390fd5b600b80546001600160a01b038381166001600160a01b03199283168117909355600c80549092168317909155600a5460405163023924c760e01b81526004810193909352169063023924c7906024015b600060405180830381600087803b1580156107ac57600080fd5b505af11580156107c0573d6000803e3d6000fd5b5050505050565b6000600854600754600e60009054906101000a90046001600160a01b03166001600160a01b03166355d0a1d06040518163ffffffff1660e01b815260040160206040518083038186803b15801561081d57600080fd5b505afa158015610831573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061085591906159f8565b600254010101905090565b60606003805461086f90615d4b565b80601f016020809104026020016040519081016040528092919081815260200182805461089b90615d4b565b80156108e85780601f106108bd576101008083540402835291602001916108e8565b820191906000526020600020905b8154815290600101906020018083116108cb57829003601f168201915b5050505050905090565b600033610900818585612ea5565b60019150505b92915050565b6001600160a01b038116610933576040516342bec4df60e01b815260040160405180910390fd5b6016546001600160a01b03163314158061095757506014546001600160a01b031615155b1561097557604051631d0b184b60e21b815260040160405180910390fd5b601480546001600160a01b038381166001600160a01b03199283168117909355601580549092168317909155600c546040516304b7fac560e11b81526004810193909352169063096ff58a90602401600060405180830381600087803b1580156109de57600080fd5b505af11580156109f2573d6000803e3d6000fd5b5050600e546040516304b7fac560e11b81526001600160a01b038581166004830152909116925063096ff58a9150602401600060405180830381600087803b158015610a3d57600080fd5b505af1158015610a51573d6000803e3d6000fd5b50506012546040516304b7fac560e11b81526001600160a01b038581166004830152909116925063096ff58a9150602401600060405180830381600087803b158015610a9c57600080fd5b505af1158015610ab0573d6000803e3d6000fd5b5050600a546040516304b7fac560e11b81526001600160a01b038581166004830152909116925063096ff58a9150602401610792565b6001600160a01b038116610b0d576040516342bec4df60e01b815260040160405180910390fd5b6016546001600160a01b03163314610b3857604051631d0b184b60e21b815260040160405180910390fd5b601780546001600160a01b0319166001600160a01b03838116918217909255600a54604051637827d56160e01b8152600481019290925290911690637827d56190602401600060405180830381600087803b158015610b9657600080fd5b505af1158015610baa573d6000803e3d6000fd5b505050507fdd475012ca9bbf511d7eb2d3c4f7709ade7f0e3910b847b5dcda58b16684d7518142604051610bdf929190615ade565b60405180910390a150565b600f546001600160a01b03163314610c1557604051631d0b184b60e21b815260040160405180910390fd5b60078054919091039055565b6002601a541415610c4d5760405162461bcd60e51b8152600401610c4490615b6b565b60405180910390fd5b6002601a556016546001600160a01b031633141580610c7357506020610c71612fca565b105b80610c7e5750601954155b15610c9c576040516390172c2b60e01b815260040160405180910390fd5b601754601954610cb5916001600160a01b031690613002565b60006019556001601a55565b600033610ccf8582856130e1565b610cda85858561315b565b506001949350505050565b6002601a541415610d085760405162461bcd60e51b8152600401610c4490615b6b565b6002601a55610d198585858561246f565b610d3657604051633d9a544f60e21b815260040160405180910390fd5b6001600160a01b0385166000818152601860205260409020805460ff19166001179055331480610d6d575060b5610d6b612fca565b105b80610da25750636398e41642108015610da257506001600160a01b0385167366eca275200015dcd0c2eaa6e48d4eed3092cdd6145b15610ed95760405163a9059cbb60e01b8152309063a9059cbb90610dcc9088908890600401615ade565b602060405180830381600087803b158015610de657600080fd5b505af1158015610dfa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e1e919061588a565b508015610e8b5760155460405163d1194e2b60e01b81526001600160a01b039091169063d1194e2b90610e58908890600090600401615ade565b600060405180830381600087803b158015610e7257600080fd5b505af1158015610e86573d6000803e3d6000fd5b505050505b604080513381526001600160a01b03871660208201529081018590527f70eb43c4a8ae8c40502dcf22436c509c28d6ff421cf07c491be56984bd9870689060600160405180910390a16110b8565b808015610f065750610f06336b013bf472f5ed1d56574ec0ad676765c793fa10079d601b1b600080613329565b15610f715760155460405163d1194e2b60e01b81526001600160a01b039091169063d1194e2b90610f3e903390600190600401615ade565b600060405180830381600087803b158015610f5857600080fd5b505af1158015610f6c573d6000803e3d6000fd5b505050505b60405163a9059cbb60e01b8152309063a9059cbb90610f9b90339064174876e80090600401615ade565b602060405180830381600087803b158015610fb557600080fd5b505af1158015610fc9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fed919061588a565b5060405163a9059cbb60e01b8152309063a9059cbb9061101b90889064174876e7ff19890190600401615ade565b602060405180830381600087803b15801561103557600080fd5b505af1158015611049573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061106d919061588a565b50604080513381526001600160a01b03871660208201529081018590527f70eb43c4a8ae8c40502dcf22436c509c28d6ff421cf07c491be56984bd9870689060600160405180910390a15b50506001601a55505050565b60006110ce61341f565b905090565b6000336109008185856110e68383612541565b6110f09190615d1c565b612ea5565b600b546001600160a01b0316331461112057604051631d0b184b60e21b815260040160405180910390fd5b61112a8282613002565b5050565b6001600160a01b038116611155576040516342bec4df60e01b815260040160405180910390fd5b6011546001600160a01b03161561117f57604051631d0b184b60e21b815260040160405180910390fd5b601180546001600160a01b038381166001600160a01b03199283168117909355601280549092168317909155600c546040516325e866a160e11b815260048101939093521690634bd0cd4290602401600060405180830381600087803b1580156111e857600080fd5b505af11580156111fc573d6000803e3d6000fd5b5050600e546040516325e866a160e11b81526001600160a01b0385811660048301529091169250634bd0cd429150602401600060405180830381600087803b15801561124757600080fd5b505af115801561125b573d6000803e3d6000fd5b5050600a546040516325e866a160e11b81526001600160a01b0385811660048301529091169250634bd0cd429150602401600060405180830381600087803b1580156112a657600080fd5b505af11580156112ba573d6000803e3d6000fd5b50506010546040516325e866a160e11b81526001600160a01b0385811660048301529091169250634bd0cd429150602401610792565b6002601a5414156113135760405162461bcd60e51b8152600401610c4490615b6b565b6002601a55601254604051631f11d95360e01b81526000916001600160a01b031690631f11d9539061134b9033908890600401615ade565b6101806040518083038186803b15801561136457600080fd5b505afa158015611378573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061139c91906158a6565b905060006113a8612fca565b60608301516080840151919250600091810183106113d05783608001518460600151016113d2565b825b60125460405163f139dfb760e01b81529290910392506001600160a01b03169063f139dfb79061140e90869033908b9088908890600401615bfc565b600060405180830381600087803b15801561142857600080fd5b505af115801561143c573d6000803e3d6000fd5b5050505060006001600e60009054906101000a90046001600160a01b03166001600160a01b0316630f90e0076040518163ffffffff1660e01b815260040160206040518083038186803b15801561149257600080fd5b505afa1580156114a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114ca91906159f8565b03905083608001518210806114ec575082846080015185606001510160070111155b1561171657600e546040516348d62c6d60e11b81526001600160a01b03909116906391ac58da906115269060009033908a90600401615af7565b602060405180830381600087803b15801561154057600080fd5b505af1158015611554573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061157891906159f8565b50600e54604051633b30762f60e11b81526001600160a01b0390911690637660ec5e906115ae9060009033908a90600401615af7565b602060405180830381600087803b1580156115c857600080fd5b505af11580156115dc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061160091906159f8565b50600e5460405163ab18a02760e01b81526001600160a01b039091169063ab18a027906116369060009033908a90600401615af7565b602060405180830381600087803b15801561165057600080fd5b505af1158015611664573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061168891906159f8565b50600e54604051637c6d493160e11b81526001600160a01b039091169063f8da9262906116be9060009033908a90600401615af7565b602060405180830381600087803b1580156116d857600080fd5b505af11580156116ec573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061171091906159f8565b5061194e565b6060840151820160001901818111156117b357600e546040516348d62c6d60e11b81526001600160a01b03909116906391ac58da9061175f908585039033908b90600401615af7565b602060405180830381600087803b15801561177957600080fd5b505af115801561178d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117b191906159f8565b505b600e546001600160a01b0316637660ec5e600a830433896040518463ffffffff1660e01b81526004016117e893929190615af7565b602060405180830381600087803b15801561180257600080fd5b505af1158015611816573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061183a91906159f8565b50600e546001600160a01b031663ab18a0276064830433896040518463ffffffff1660e01b815260040161187093929190615af7565b602060405180830381600087803b15801561188a57600080fd5b505af115801561189e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118c291906159f8565b50600e546001600160a01b031663f8da92626103e8830433896040518463ffffffff1660e01b81526004016118f993929190615af7565b602060405180830381600087803b15801561191357600080fd5b505af1158015611927573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061194b91906159f8565b50505b600e5460608501518551604051634c29019760e11b815260048101839052918501600019016024830152151560448201526000916001600160a01b031690639852032e9060640160206040518083038186803b1580156119ad57600080fd5b505afa1580156119c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119e591906159f8565b90506000670de0b6b3a7640000828760c001510281611a1457634e487b7160e01b600052601260045260246000fd5b0490506000806000611a29338d868e8e613546565b9250925092506000899050336001600160a01b03167fe16ef2c5ff376e78191b54a217db16e2ca4eca7cb451d282f7dd8a75c492df498e8e8789888888604051611a799796959493929190615c68565b60405180910390a250506001601a555050505050505050505050565b6001600160a01b038116611abc576040516342bec4df60e01b815260040160405180910390fd5b6016546001600160a01b03163314611ae757604051631d0b184b60e21b815260040160405180910390fd5b601680546001600160a01b0319166001600160a01b03838116918217909255600a54604051637e98d42960e11b815260048101929092529091169063fd31a85290602401600060405180830381600087803b158015611b4557600080fd5b505af1158015611b59573d6000803e3d6000fd5b505050507fd535651a5590e21c663adfebb2eb6431447c10dccf3eee90c4c4d8bf7983c14a8142604051610bdf929190615ade565b6001600160a01b038116611bb5576040516342bec4df60e01b815260040160405180910390fd5b600d546001600160a01b031615611bdf57604051631d0b184b60e21b815260040160405180910390fd5b600d80546001600160a01b038381166001600160a01b03199283168117909355600e80549092168317909155600c5460405163307b626b60e11b8152600481019390935216906360f6c4d690602401610792565b6001600160a01b038116611c5a576040516342bec4df60e01b815260040160405180910390fd5b6016546001600160a01b031633141580611c7e57506013546001600160a01b031615155b15611c9c57604051631d0b184b60e21b815260040160405180910390fd5b601380546001600160a01b0319166001600160a01b03838116918217909255601254604051636f72995f60e01b8152600481019290925290911690636f72995f90602401600060405180830381600087803b158015611cfa57600080fd5b505af1158015611d0e573d6000803e3d6000fd5b5050600e54604051636f72995f60e01b81526001600160a01b0385811660048301529091169250636f72995f9150602401610792565b6011546001600160a01b03163314611d6f57604051631d0b184b60e21b815260040160405180910390fd5b600780549091019055565b6011546001600160a01b03163314611da557604051631d0b184b60e21b815260040160405180910390fd5b8060196000828254611db79190615d1c565b909155505050565b6001600160a01b038116600090815260056020526040812054610906565b60606004805461086f90615d4b565b6001600160a01b038116611e13576040516342bec4df60e01b815260040160405180910390fd5b600f546001600160a01b031615611e3d57604051631d0b184b60e21b815260040160405180910390fd5b601080546001600160a01b038381166001600160a01b03199283168117909355600f80549092168317909155600e546040516396cc563f60e01b8152600481019390935216906396cc563f90602401600060405180830381600087803b158015611ea657600080fd5b505af1158015611eba573d6000803e3d6000fd5b5050600a546040516396cc563f60e01b81526001600160a01b03858116600483015290911692506396cc563f9150602401600060405180830381600087803b158015611f0557600080fd5b505af1158015611f19573d6000803e3d6000fd5b5050600c54604051632e8098c560e01b81526001600160a01b0385811660048301529091169250632e8098c59150602401610792565b6009546001600160a01b03163314801590611f755750600b546001600160a01b03163314155b8015611f8c57506011546001600160a01b03163314155b8015611fa357506013546001600160a01b03163314155b15611fc157604051631d0b184b60e21b815260040160405180910390fd5b61112a8282613c21565b60003381611fd98286612541565b9050838110156120395760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610c44565b610cda8286868403612ea5565b60003361090081858561315b565b6001600160a01b03811661207b576040516342bec4df60e01b815260040160405180910390fd5b6009546001600160a01b0316156120a557604051631d0b184b60e21b815260040160405180910390fd5b600980546001600160a01b039092166001600160a01b03199283168117909155600a8054909216179055565b6011546001600160a01b031633146120fc57604051631d0b184b60e21b815260040160405180910390fd5b600880549091019055565b6002601a54141561212a5760405162461bcd60e51b8152600401610c4490615b6b565b6002601a55601254604051631f11d95360e01b81526000916001600160a01b031690631f11d953906121629087908790600401615ade565b6101806040518083038186803b15801561217b57600080fd5b505afa15801561218f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121b391906158a6565b9050600081600001516121d45781608001518260600151016101b8016121e4565b8160800151826060015101610113015b9050806121ef612fca565b108061220057506000826101200151115b8061220c575081602001515b8061221957506080820151155b156122375760405163270b039d60e21b815260040160405180910390fd5b60006122468387878588613d67565b90508380156122745750612274336ac34556b3f6459039627ea4676765c793fa10079d601b1b600080613329565b156122df5760155460405163d1194e2b60e01b81526001600160a01b039091169063d1194e2b906122ac903390600190600401615ade565b600060405180830381600087803b1580156122c657600080fd5b505af11580156122da573d6000803e3d6000fd5b505050505b6122fe868683600001518460200151856040015186606001518961447c565b50506001601a5550505050565b8342111561235b5760405162461bcd60e51b815260206004820152601d60248201527f45524332305065726d69743a206578706972656420646561646c696e650000006044820152606401610c44565b60007f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c988888861238a8c614643565b6040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810186905260e00160405160208183030381529060405280519060200120905060006123e58261466b565b905060006123f5828787876146b9565b9050896001600160a01b0316816001600160a01b0316146124585760405162461bcd60e51b815260206004820152601e60248201527f45524332305065726d69743a20696e76616c6964207369676e617475726500006044820152606401610c44565b6124638a8a8a612ea5565b50505050505050505050565b6040516bffffffffffffffffffffffff19606086901b16602082015260348101849052600090819060540160408051601f1981840301815291815281516020928301206001600160a01b0389166000908152601890935291205490915060ff1615801561253757506125378484808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152507fcad71776a60b1a4ca80bfa5452bfc50beeb645b7f64e97f5c464ef45a41d548d92508591506146e19050565b9695505050505050565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b600d546001600160a01b0316331461112057604051631d0b184b60e21b815260040160405180910390fd5b600f546001600160a01b031633146125c257604051631d0b184b60e21b815260040160405180910390fd5b60088054919091039055565b6002601a5414156125f15760405162461bcd60e51b8152600401610c4490615b6b565b6002601a55601254604051631f11d95360e01b81526000916001600160a01b031690631f11d953906126299087908790600401615ade565b6101806040518083038186803b15801561264257600080fd5b505afa158015612656573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061267a91906158a6565b60808101516060820151919250016000612692612fca565b9050826020015180156126a757508261010001515b156126c557604051635ad8888f60e01b815260040160405180910390fd5b600083610120015111806126dd575060008360a00151115b806126e757508181105b806126f457506080830151155b15612712576040516301ddf13b60e61b815260040160405180910390fd5b601254604051632fd96bfd60e11b81526001600160a01b038881166004830152602482018890526044820184905290911690635fb2d7fa90606401600060405180830381600087803b15801561276757600080fd5b505af115801561277b573d6000803e3d6000fd5b50505050600083600001511561281e57676765c793fa10079d601b1b6127fb606460076b08759642e984636060b13f5288610140015102816127cd57634e487b7160e01b600052601260045260246000fd5b04816127e957634e487b7160e01b600052601260045260246000fd5b04676765c793fa10079d601b1b6146f7565b8161281657634e487b7160e01b600052601260045260246000fd5b04905061287f565b676765c793fa10079d601b1b612860606460076b053a680649b3e32378b13f5288610140015102816127cd57634e487b7160e01b600052601260045260246000fd5b8161287b57634e487b7160e01b600052601260045260246000fd5b0490505b6101408401516000906128b4906128968185614751565b8660070186116128a75760006128ae565b6007878703035b02614751565b90508460200151156129ae57600c546040516303f9f6f560e61b81526001600160a01b039091169063fe7dbd40906128f2908b908b90600401615ade565b602060405180830381600087803b15801561290c57600080fd5b505af1158015612920573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612944919061588a565b156129ae57600c546040516342cdcbff60e11b81526001600160a01b039091169063859b97fe9061297b908b908b90600401615ade565b600060405180830381600087803b15801561299557600080fd5b505af11580156129a9573d6000803e3d6000fd5b505050505b600e5460408051630f90e00760e01b815290516000926001600160a01b031691630f90e007916004808301926020929190829003018186803b1580156129f357600080fd5b505afa158015612a07573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a2b91906159f8565b90508560600151600101811015612acf57600e5460608701516040516348d62c6d60e11b81526001600160a01b03909216916391ac58da91612a7b91908590036001019033908c90600401615af7565b602060405180830381600087803b158015612a9557600080fd5b505af1158015612aa9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612acd91906159f8565b505b855115612b545761014086015160078054919091039055600e5460c08701516040516310d6833b60e31b81526001600160a01b03909216916386b419d891612b1d9160040190815260200190565b600060405180830381600087803b158015612b3757600080fd5b505af1158015612b4b573d6000803e3d6000fd5b50505050612bce565b61014086015160088054919091039055600e5460c0870151604051633f9d44fd60e01b81526001600160a01b0390921691633f9d44fd91612b9b9160040190815260200190565b600060405180830381600087803b158015612bb557600080fd5b505af1158015612bc9573d6000803e3d6000fd5b505050505b61016086015160009015612d1d578215612d1857600a546040516317858ab160e31b81526000916001600160a01b03169063bc2c558890612c15908e908e90600401615ade565b602060405180830381600087803b158015612c2f57600080fd5b505af1158015612c43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c679190615649565b600a5460608a0151604051631898636d60e31b81523360048201526001600160a01b03808516602483015260448201899052918a03606482015260006084820152929350169063c4c31b689060a401602060405180830381600087803b158015612cd057600080fd5b505af1158015612ce4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d0891906159f8565b9150612d148285614751565b9350505b612db1565b600e546101408801516001600160a01b0390911690631ce2ed86908510612d45576000612d4e565b84896101400151035b6040518263ffffffff1660e01b8152600401612d6c91815260200190565b600060405180830381600087803b158015612d8657600080fd5b505af1158015612d9a573d6000803e3d6000fd5b505050506000831115612db157612db13384613002565b878015612ddd5750612ddd336ac34556b3f6459039627ea4676765c793fa10079d601b1b600080613329565b15612e485760155460405163d1194e2b60e01b81526001600160a01b039091169063d1194e2b90612e15903390600190600401615ade565b600060405180830381600087803b158015612e2f57600080fd5b505af1158015612e43573d6000803e3d6000fd5b505050505b604080516001600160a01b038c168152602081018b905290810184905233907f0d6b33a90fd9ff440fc1c724f3f3d492e7c2fc1c629b5a8aacf5c76c2ecbcdbe9060600160405180910390a250506001601a555050505050505050565b6001600160a01b038316612f075760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610c44565b6001600160a01b038216612f685760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610c44565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6000636398e416421015612ff157604051633c109e6160e11b815260040160405180910390fd5b506201518042636398e41519010490565b6001600160a01b0382166130585760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610c44565b806002600082825461306a9190615d1c565b90915550506001600160a01b03821660009081526020819052604081208054839290613097908490615d1c565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b60006130ed8484612541565b9050600019811461315557818110156131485760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401610c44565b6131558484848403612ea5565b50505050565b6001600160a01b0383166131bf5760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610c44565b6001600160a01b0382166132215760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610c44565b6001600160a01b038316600090815260208190526040902054818110156132995760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610c44565b6001600160a01b038085166000908152602081905260408082208585039055918516815290812080548492906132d0908490615d1c565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161331c91815260200190565b60405180910390a3613155565b600082156133d357601054604051631f8ee6fb60e11b81526004810185905264e8d4a51000916001600160a01b031690633f1dcdf69060240160206040518083038186803b15801561337a57600080fd5b505afa15801561338e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133b291906159f8565b8602816133cf57634e487b7160e01b600052601260045260246000fd5b0494505b8385106133e257506001613416565b6000846133ef8885614769565b8161340a57634e487b7160e01b600052601260045260246000fd5b87870391900610159150505b95945050505050565b6000306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614801561347857507f000000000000000000000000000000000000000000000000000000000000000046145b156134a257507f000000000000000000000000000000000000000000000000000000000000000090565b50604080517f00000000000000000000000000000000000000000000000000000000000000006020808301919091527f0000000000000000000000000000000000000000000000000000000000000000828401527f000000000000000000000000000000000000000000000000000000000000000060608301524660808301523060a0808401919091528351808403909101815260c0909201909252805191012090565b601254604051631f11d95360e01b81526000918291829182916001600160a01b0390911690631f11d95390613581908c908c90600401615ade565b6101806040518083038186803b15801561359a57600080fd5b505afa1580156135ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135d291906158a6565b905060006135de612fca565b6060830151608084015191925060009181018310613606578360800151846060015101613608565b825b0390506000601060009054906101000a90046001600160a01b03166001600160a01b031663e4c2a4d68584868e6040518563ffffffff1660e01b81526004016136549493929190615c3b565b60c060405180830381600087803b15801561366e57600080fd5b505af1158015613682573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136a6919061595b565b905083608001518210156137fe576136c8848b836020015184600001516147c5565b945087156137f95761372c8c85600001516136e45760646136e7565b600a5b60ff166b053a680649b3e32378b13f528161371257634e487b7160e01b600052601260045260246000fd5b04676765c793fa10079d601b1b8761014001516000613329565b1561379b5760155460405163d1194e2b60e01b81526001600160a01b039091169063d1194e2b90613764908f90600190600401615ade565b600060405180830381600087803b15801561377e57600080fd5b505af1158015613792573d6000803e3d6000fd5b50505050613b9f565b8880156137c157506137c18c676765c793fa10079d601b1b808761014001516002613329565b156137f95760155460405163d1194e2b60e01b81526001600160a01b039091169063d1194e2b90613764908f90600190600401615ade565b613b9f565b83608001518460600151016007018311156139c757613827848b836080015184606001516147c5565b945087156137f957600089156138e4576138648d600a6b053a680649b3e32378b13f5204676765c793fa10079d601b1b886101400151600a613329565b156138e457601560009054906101000a90046001600160a01b03166001600160a01b031663d1194e2b8e600b6040518363ffffffff1660e01b81526004016138ad929190615ade565b600060405180830381600087803b1580156138c757600080fd5b505af11580156138db573d6000803e3d6000fd5b50505050600190505b806139c1576139458d86600001516138fd576064613900565b600a5b60ff166b053a680649b3e32378b13f528161392b57634e487b7160e01b600052601260045260246000fd5b04676765c793fa10079d601b1b8861014001516000613329565b156139c157601560009054906101000a90046001600160a01b03166001600160a01b031663d1194e2b8e60016040518363ffffffff1660e01b815260040161398e929190615ade565b600060405180830381600087803b1580156139a857600080fd5b505af11580156139bc573d6000803e3d6000fd5b505050505b50613b9f565b8715613b9f57605984608001511115613abe576139f88c676765c793fa10079d601b1b808761014001516000613329565b15613a635760155460405163d1194e2b60e01b81526001600160a01b039091169063d1194e2b90613a30908f90600190600401615ade565b600060405180830381600087803b158015613a4a57600080fd5b505af1158015613a5e573d6000803e3d6000fd5b505050505b88156137f957613a878c676765c793fa10079d601b1b808761014001516003613329565b156137f95760155460405163d1194e2b60e01b81526001600160a01b039091169063d1194e2b90613764908f906004908101615ade565b613ad68c60646b053a680649b3e32378b13f52613712565b15613b0e5760155460405163d1194e2b60e01b81526001600160a01b039091169063d1194e2b90613764908f90600190600401615ade565b888015613b345750613b348c676765c793fa10079d601b1b808761014001516014613329565b15613b9f5760155460405163d1194e2b60e01b81526001600160a01b039091169063d1194e2b90613b6c908f90600190600401615ade565b600060405180830381600087803b158015613b8657600080fd5b505af1158015613b9a573d6000803e3d6000fd5b505050505b613bb4848483604001518460a001518e6149b6565b61016084015115613be057613bd6848d8d8d856080015186606001518f614b3f565b9097509550613c12565b60008a905060008a9050613c0a868f85602001518660800151876000015188606001518888614c61565b909950975050505b50505050955095509592505050565b6001600160a01b038216613c815760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401610c44565b6001600160a01b03821660009081526020819052604090205481811015613cf55760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608401610c44565b6001600160a01b0383166000908152602081905260408120838303905560028054849290613d24908490615d34565b90915550506040518281526000906001600160a01b038516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602001612fbd565b613d926040518060800160405280600081526020016000815260200160008152602001600081525090565b6012546040516365892b4960e01b81526001600160a01b03878116600483015260248201879052426044830152909116906365892b4990606401600060405180830381600087803b158015613de657600080fd5b505af1158015613dfa573d6000803e3d6000fd5b50504261012089015250506010546101408701516040516336a34f2760e01b81526001600160a01b03909216916336a34f2791613e3d9160040190815260200190565b600060405180830381600087803b158015613e5757600080fd5b505af1158015613e6b573d6000803e3d6000fd5b50506010546040516303e2f69160e11b8152600093506001600160a01b0390911691506307c5ed2290613ea2908a90600401615ba2565b60206040518083038186803b158015613eba57600080fd5b505afa158015613ece573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ef291906159f8565b600e54606089015160808a0151604051635d070d7f60e11b81523360048201526024810192909252604482015285151560648201529192506001600160a01b03169063ba0e1afe90608401600060405180830381600087803b158015613f5757600080fd5b505af1158015613f6b573d6000803e3d6000fd5b5050505060008188610140015111613f84576000613f8d565b81886101400151035b600e5460608a015160808b01518b51604051634c29019760e11b815260048101849052919092016000190160248201529015156044820152919250600091670de0b6b3a7640000916001600160a01b031690639852032e9060640160206040518083038186803b15801561400057600080fd5b505afa158015614014573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061403891906159f8565b8a60c00151028161405957634e487b7160e01b600052601260045260246000fd5b04905081810161406a8a8884614f26565b676765c793fa10079d601b1b6ac34556b3f6459039627ea482020461408f8482614751565b60408701526101608b01511561437957600a546040516317858ab160e31b81528c9189918d918d916000916001600160a01b03169063c4c31b68903390839063bc2c5588906140e49089908990600401615ade565b602060405180830381600087803b1580156140fe57600080fd5b505af1158015614112573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141369190615649565b8e604001518960600151614148612fca565b6040516001600160e01b031960e088901b1681526001600160a01b039586166004820152949093166024850152604484019190915290036064820152861515608482015260a401602060405180830381600087803b1580156141a957600080fd5b505af11580156141bd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141e191906159f8565b600a549091506000906001600160a01b03166354f4758f87868685614204612fca565b60016040518763ffffffff1660e01b815260040161422796959493929190615bb1565b602060405180830381600087803b15801561424157600080fd5b505af1158015614255573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061427991906159f8565b905081871161428957600061428d565b8187035b60608d015260408c0180518390039081905281830101808b111561431357808b03808e52600e54604051630e7176c360e11b815260048101929092526001600160a01b031690631ce2ed8690602401600060405180830381600087803b1580156142f657600080fd5b505af115801561430a573d6000803e3d6000fd5b50505050614318565b60008d525b8c606001518a1161432a576000614332565b8c606001518a035b60208e015260a087015161436d5786511561435c576101408701516007805491909103905561436d565b610140870151600880549190910390555b5050505050505061446e565b83811161438757600061438b565b8381035b6060870152604086015184116143a25760006143aa565b856040015184035b8652606086015183116143be5760006143c6565b856060015183035b602087015260a08b015161446e57600e546101408c0151604051630e7176c360e11b81526001600160a01b0390921691631ce2ed869161440c9160040190815260200190565b600060405180830381600087803b15801561442657600080fd5b505af115801561443a573d6000803e3d6000fd5b505050508a600001511561445d576101408b01516007805491909103905561446e565b6101408b0151600880549190910390555b505050505095945050505050565b84840160006b053a680649b3e32378b13f52676765c793fa10079d601b1b8302600e546040516339dbe92160e21b8152929091046004830181905292506001600160a01b03169063e76fa48490602401600060405180830381600087803b1580156144e657600080fd5b505af11580156144fa573d6000803e3d6000fd5b505050506000676765c793fa10079d601b1b6ac34556b3f6459039627ea484028161453557634e487b7160e01b600052601260045260246000fd5b04828403039050858101850180156145a657600e5460405163c0c8835760e01b8152600481018390526001600160a01b039091169063c0c8835790602401600060405180830381600087803b15801561458d57600080fd5b505af11580156145a1573d6000803e3d6000fd5b505050505b81156145c2576017546145c2906001600160a01b031683613002565b6145ce33888801613002565b336001600160a01b038c167f3d7c224e9ad9e12f013ba8a086e40c61608308a8810a12b8fa970887f0feb6aa8c8a8a8888676765c793fa10079d601b1b6ac34556b3f6459039627ea48d02048d60405161462e9796959493929190615cad565b60405180910390a35050505050505050505050565b6001600160a01b03811660009081526005602052604090208054600181018255905b50919050565b600061090661467861341f565b8360405161190160f01b6020820152602281018390526042810182905260009060620160405160208183030381529060405280519060200120905092915050565b60008060006146ca8787878761508a565b915091506146d781615177565b5095945050505050565b6000826146ee858461537b565b14949350505050565b60008182848161471757634e487b7160e01b600052601260045260246000fd5b0615614724576001614727565b60005b60ff1683858161474757634e487b7160e01b600052601260045260246000fd5b0401029392505050565b60008183106147605781614762565b825b9392505050565b60004282014340846040516020016147a693929190928352602083019190915260601b6bffffffffffffffffffffffff1916604082015260540190565b60408051601f1981840301815291905280516020909101209392505050565b600080600086610160015111156147f0576147e285848601614751565b90508092506000935061487c565b60a08601516148775785511561480e57600780548590039055614818565b6008805485900390555b600e54604051630e7176c360e11b8152600481018690526001600160a01b0390911690631ce2ed8690602401600060405180830381600087803b15801561485e57600080fd5b505af1158015614872573d6000803e3d6000fd5b505050505b508282015b600e546040516339dbe92160e21b81526b053a680649b3e32378b13f52676765c793fa10079d601b1b8402046004820181905293506001600160a01b039091169063e76fa48490602401600060405180830381600087803b1580156148e057600080fd5b505af11580156148f4573d6000803e3d6000fd5b505050506000676765c793fa10079d601b1b6ac34556b3f6459039627ea483028161492f57634e487b7160e01b600052601260045260246000fd5b60175491900484840303915061494e906001600160a01b031682613002565b600e5460405163c0c8835760e01b8152600481018390526001600160a01b039091169063c0c8835790602401600060405180830381600087803b15801561499457600080fd5b505af11580156149a8573d6000803e3d6000fd5b505050505050949350505050565b60a0850151614a9d578451614a3357600e5460c0860151604051633f9d44fd60e01b81526001600160a01b0390921691633f9d44fd916149fc9160040190815260200190565b600060405180830381600087803b158015614a1657600080fd5b505af1158015614a2a573d6000803e3d6000fd5b50505050614a9d565b600e5460c08601516040516310d6833b60e31b81526001600160a01b03909216916386b419d891614a6a9160040190815260200190565b600060405180830381600087803b158015614a8457600080fd5b505af1158015614a98573d6000803e3d6000fd5b505050505b600085608001518660600151018510614ab7576000614ab9565b835b82876101400151010390508561014001518110614b3757600e546001600160a01b03166365407202614aeb88846153fd565b6040518263ffffffff1660e01b8152600401614b0991815260200190565b600060405180830381600087803b158015614b2357600080fd5b505af1158015612463573d6000803e3d6000fd5b505050505050565b6000806000614b5087868801614751565b90506000614b5c612fca565b90508a60a0015160001415614b7c576101408b0151600780549190910390555b60008a90506000600a60009054906101000a90046001600160a01b03166001600160a01b03166354f4758f8e848e86888d6040518763ffffffff1660e01b8152600401614bce96959493929190615bb1565b602060405180830381600087803b158015614be857600080fd5b505af1158015614bfc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614c2091906159f8565b9050808d610140015111614c35576000614c3e565b808d6101400151035b9550838a039450614c518287878a615543565b5050505097509795505050505050565b6000806000614d0289601060009054906101000a90046001600160a01b03166001600160a01b03166307c5ed228e6040518263ffffffff1660e01b8152600401614cab9190615ba2565b60206040518083038186803b158015614cc357600080fd5b505afa158015614cd7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614cfb91906159f8565b8a016155c4565b90506000614d1088886155c4565b9050818c610140015111614d25576000614d2e565b818c6101400151035b9350808611614d3e576000614d42565b8086035b92508b60a0015160001415614e96578b5115614d6d576101408c015160078054919091039055614d7e565b6101408c0151600880549190910390555b600083118015614d8c575084155b15614df057600e5460405163c0c8835760e01b8152600481018590526001600160a01b039091169063c0c8835790602401600060405180830381600087803b158015614dd757600080fd5b505af1158015614deb573d6000803e3d6000fd5b505050505b8415614e6557600e546101408d0151604051630e7176c360e11b81526001600160a01b0390921691631ce2ed8691614e2e9160040190815260200190565b600060405180830381600087803b158015614e4857600080fd5b505af1158015614e5c573d6000803e3d6000fd5b50505050614ef6565b600e54604051630e7176c360e11b8152600481018490526001600160a01b0390911690631ce2ed8690602401614e2e565b600e5460405163c0c8835760e01b815284860160048201526001600160a01b039091169063c0c8835790602401600060405180830381600087803b158015614edd57600080fd5b505af1158015614ef1573d6000803e3d6000fd5b505050505b84158015614f0657506000838501115b15614f1757614f178b848601613002565b50509850989650505050505050565b60a083015161500d578251614fa357600e5460c0840151604051633f9d44fd60e01b81526001600160a01b0390921691633f9d44fd91614f6c9160040190815260200190565b600060405180830381600087803b158015614f8657600080fd5b505af1158015614f9a573d6000803e3d6000fd5b5050505061500d565b600e5460c08401516040516310d6833b60e31b81526001600160a01b03909216916386b419d891614fda9160040190815260200190565b600060405180830381600087803b158015614ff457600080fd5b505af1158015615008573d6000803e3d6000fd5b505050505b600e546101408401516001600160a01b039091169063654072029061503590869085016153fd565b6040518263ffffffff1660e01b815260040161505391815260200190565b600060405180830381600087803b15801561506d57600080fd5b505af1158015615081573d6000803e3d6000fd5b50505050505050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311156150c1575060009050600361516e565b8460ff16601b141580156150d957508460ff16601c14155b156150ea575060009050600461516e565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa15801561513e573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166151675760006001925092505061516e565b9150600090505b94509492505050565b600081600481111561519957634e487b7160e01b600052602160045260246000fd5b14156151a25750565b60018160048111156151c457634e487b7160e01b600052602160045260246000fd5b14156152125760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401610c44565b600281600481111561523457634e487b7160e01b600052602160045260246000fd5b14156152825760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610c44565b60038160048111156152a457634e487b7160e01b600052602160045260246000fd5b14156152fd5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608401610c44565b600481600481111561531f57634e487b7160e01b600052602160045260246000fd5b14156153785760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b6064820152608401610c44565b50565b600081815b84518110156153f55760008582815181106153ab57634e487b7160e01b600052603260045260246000fd5b602002602001015190508083116153d157600083815260208290526040902092506153e2565b600081815260208490526040902092505b50806153ed81615d80565b915050615380565b509392505050565b6000676765c793fa10079d601b1b8360c00151670de0b6b3a7640000615446676765c793fa10079d601b1b86026b053a680649b3e32378b13f5267016345785d8a000002614751565b85028161546357634e487b7160e01b600052601260045260246000fd5b046154f46b0709fa55642a614ae4f586a686026105b06154d1676765c793fa10079d601b1b61115c02676765c793fa10079d601b1b6154cb6ac34556b3f6459039627ea4676765c793fa10079d601b1b018d6080015102676765c793fa10079d601b1b6146f7565b03614751565b8802816154ee57634e487b7160e01b600052601260045260246000fd5b04614751565b676765c793fa10079d601b1b860201018161551f57634e487b7160e01b600052601260045260246000fd5b048161553b57634e487b7160e01b600052601260045260246000fd5b049392505050565b8061315557821561555a5761555a84838501613002565b811561315557600e5460405163c0c8835760e01b8152600481018490526001600160a01b039091169063c0c8835790602401600060405180830381600087803b1580156155a657600080fd5b505af11580156155ba573d6000803e3d6000fd5b5050505050505050565b60008183116147605781614762565b60008083601f8401126155e4578182fd5b50813567ffffffffffffffff8111156155fb578182fd5b6020830191508360208260051b850101111561561657600080fd5b9250929050565b805161562881615dc6565b919050565b60006020828403121561563e578081fd5b813561476281615db1565b60006020828403121561565a578081fd5b815161476281615db1565b60008060408385031215615677578081fd5b823561568281615db1565b9150602083013561569281615db1565b809150509250929050565b6000806000606084860312156156b1578081fd5b83356156bc81615db1565b925060208401356156cc81615db1565b929592945050506040919091013590565b600080600080600080600060e0888a0312156156f7578283fd5b873561570281615db1565b9650602088013561571281615db1565b95506040880135945060608801359350608088013560ff81168114615735578384fd5b9699959850939692959460a0840135945060c09093013592915050565b60008060408385031215615764578182fd5b823561576f81615db1565b946020939093013593505050565b60008060008060608587031215615792578384fd5b843561579d81615db1565b935060208501359250604085013567ffffffffffffffff8111156157bf578283fd5b6157cb878288016155d3565b95989497509550505050565b6000806000806000608086880312156157ee578081fd5b85356157f981615db1565b945060208601359350604086013567ffffffffffffffff81111561581b578182fd5b615827888289016155d3565b909450925050606086013561583b81615dc6565b809150509295509295909350565b60008060006060848603121561585d578081fd5b833561586881615db1565b925060208401359150604084013561587f81615dc6565b809150509250925092565b60006020828403121561589b578081fd5b815161476281615dc6565b600061018082840312156158b8578081fd5b6158c0615ce4565b6158c98361561d565b81526158d76020840161561d565b60208201526158e86040840161561d565b6040820152606083015160608201526080830151608082015260a083015160a082015260c083015160c082015260e083015160e082015261010061592d81850161561d565b9082015261012083810151908201526101408084015190820152610160928301519281019290925250919050565b600060c0828403121561596c578081fd5b60405160c0810181811067ffffffffffffffff8211171561599b57634e487b7160e01b83526041600452602483fd5b8060405250825181526020830151602082015260408301516040820152606083015160608201526080830151608082015260a083015160a08201528091505092915050565b6000602082840312156159f1578081fd5b5035919050565b600060208284031215615a09578081fd5b5051919050565b600080600060608486031215615a24578081fd5b833592506020840135615a3681615dc6565b9150604084013561587f81615dc6565b8051151582526020810151615a5f602084018215159052565b506040810151615a73604084018215159052565b50606081015160608301526080810151608083015260a081015160a083015260c081015160c083015260e081015160e083015261010080820151615aba8285018215159052565b50506101208181015190830152610140808201519083015261016090810151910152565b6001600160a01b03929092168252602082015260400190565b9283526001600160a01b039190911660208301521515604082015260600190565b6000602080835283518082850152825b81811015615b4457858101830151858201604001528201615b28565b81811115615b555783604083870101525b50601f01601f1916929092016040019392505050565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b61018081016109068284615a46565b6102208101615bc08289615a46565b6001600160a01b039687166101808301526101a0820195909552929094166101c08301526101e082015291151561020090920191909152919050565b6102008101615c0b8288615a46565b6001600160a01b03959095166101808201526101a08101939093526101c08301919091526101e090910152919050565b6101e08101615c4a8287615a46565b84610180830152836101a0830152826101c083015295945050505050565b60006102408201905088825287151560208301528660408301528560608301528460808301528360a0830152615ca160c0830184615a46565b98975050505050505050565b6000610240820190508882528760208301528660408301528560608301528460808301528360a0830152615ca160c0830184615a46565b604051610180810167ffffffffffffffff81118282101715615d1657634e487b7160e01b600052604160045260246000fd5b60405290565b60008219821115615d2f57615d2f615d9b565b500190565b600082821015615d4657615d46615d9b565b500390565b600181811c90821680615d5f57607f821691505b6020821081141561466557634e487b7160e01b600052602260045260246000fd5b6000600019821415615d9457615d94615d9b565b5060010190565b634e487b7160e01b600052601160045260246000fd5b6001600160a01b038116811461537857600080fd5b801515811461537857600080fdfea2646970667358221220cf7fec9b990305171b472ea11addeeab57dabb8532cd6cde4b8907984b8c951864736f6c63430008040033000000000000000000000000dca40b6fb95e9c1c4511f7758bc6f3aec7474444000000000000000000000000f2ebcc36ecbbf57bcf7d7531fe24f137afc4c555

Deployed ByteCode

0x608060405234801561001057600080fd5b506004361061030c5760003560e01c806360f1edf71161019d578063a9059cbb116100e9578063d505accf116100a2578063dfcad7ea1161007c578063dfcad7ea146106ad578063e09f0817146106c0578063e954f41d146106d3578063e9c4239b146106de57600080fd5b8063d505accf14610674578063dc38bdb514610687578063dd62ed3e1461069a57600080fd5b8063a9059cbb146105f2578063ad09d65814610605578063b074d2f614610618578063c884ef831461062b578063cb01454b1461064e578063d11d01db1461066157600080fd5b806376df7c321161015657806395d89b411161013057806395d89b41146105b157806396cc563f146105b95780639dc29fac146105cc578063a457c2d7146105df57600080fd5b806376df7c32146105785780637ecebe001461058b578063873f0a331461059e57600080fd5b806360f1edf7146104f057806360f6c4d61461050357806363fcd08a146105165780636f72995f1461052957806370a082311461053c57806372fb9c551461056557600080fd5b806323b872dd1161025c578063379b2c1311610215578063445c101f116101ef578063445c101f146104ae5780634bd0cd42146104b75780635476ea9e146104ca57806357ec85dd146104dd57600080fd5b8063379b2c1314610475578063395093511461048857806340c10f191461049b57600080fd5b806323b872dd1461041c578063269858651461042f5780632c265c88146104425780632ceaaf281461044b578063313ce5671461045e5780633644e5151461046d57600080fd5b80630c6eae62116102c9578063197162be116102a3578063197162be146103e55780631ad03156146103f85780631bf803391461040b578063238bf8e01461041457600080fd5b80630c6eae62146103b757806315df1fca146103ca57806318160ddd146103dd57600080fd5b8063023924c71461031157806303d41eb61461032657806306fdde0314610341578063095ea7b314610356578063096ff58a146103795780630b622ab21461038c575b600080fd5b61032461031f36600461562d565b6106f1565b005b61032e6107c7565b6040519081526020015b60405180910390f35b610349610860565b6040516103389190615b18565b610369610364366004615752565b6108f2565b6040519015158152602001610338565b61032461038736600461562d565b61090c565b60095461039f906001600160a01b031681565b6040516001600160a01b039091168152602001610338565b6103246103c536600461562d565b610ae6565b60115461039f906001600160a01b031681565b60025461032e565b6103246103f33660046159e0565b610bea565b600d5461039f906001600160a01b031681565b61032e60075481565b610324610c21565b61036961042a36600461569d565b610cc1565b61032461043d3660046157d7565b610ce5565b61032e60085481565b60165461039f906001600160a01b031681565b60405160098152602001610338565b61032e6110c4565b60135461039f906001600160a01b031681565b610369610496366004615752565b6110d3565b6103246104a9366004615752565b6110f5565b61032e60195481565b6103246104c536600461562d565b61112e565b600b5461039f906001600160a01b031681565b6103246104eb366004615a10565b6112f0565b6103246104fe36600461562d565b611a95565b61032461051136600461562d565b611b8e565b60175461039f906001600160a01b031681565b61032461053736600461562d565b611c33565b61032e61054a36600461562d565b6001600160a01b031660009081526020819052604090205490565b6103246105733660046159e0565b611d44565b6103246105863660046159e0565b611d7a565b61032e61059936600461562d565b611dbf565b60145461039f906001600160a01b031681565b610349611ddd565b6103246105c736600461562d565b611dec565b6103246105da366004615752565b611f4f565b6103696105ed366004615752565b611fcb565b610369610600366004615752565b612046565b61032461061336600461562d565b612054565b600f5461039f906001600160a01b031681565b61036961063936600461562d565b60186020526000908152604090205460ff1681565b61032461065c3660046159e0565b6120d1565b61032461066f366004615849565b612107565b6103246106823660046156dd565b61230b565b61036961069536600461577d565b61246f565b61032e6106a8366004615665565b612541565b6103246106bb366004615752565b61256c565b6103246106ce3660046159e0565b612597565b61032e636398e41681565b6103246106ec366004615849565b6125ce565b6001600160a01b038116610718576040516342bec4df60e01b815260040160405180910390fd5b600b546001600160a01b03161561074257604051631d0b184b60e21b815260040160405180910390fd5b600b80546001600160a01b038381166001600160a01b03199283168117909355600c80549092168317909155600a5460405163023924c760e01b81526004810193909352169063023924c7906024015b600060405180830381600087803b1580156107ac57600080fd5b505af11580156107c0573d6000803e3d6000fd5b5050505050565b6000600854600754600e60009054906101000a90046001600160a01b03166001600160a01b03166355d0a1d06040518163ffffffff1660e01b815260040160206040518083038186803b15801561081d57600080fd5b505afa158015610831573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061085591906159f8565b600254010101905090565b60606003805461086f90615d4b565b80601f016020809104026020016040519081016040528092919081815260200182805461089b90615d4b565b80156108e85780601f106108bd576101008083540402835291602001916108e8565b820191906000526020600020905b8154815290600101906020018083116108cb57829003601f168201915b5050505050905090565b600033610900818585612ea5565b60019150505b92915050565b6001600160a01b038116610933576040516342bec4df60e01b815260040160405180910390fd5b6016546001600160a01b03163314158061095757506014546001600160a01b031615155b1561097557604051631d0b184b60e21b815260040160405180910390fd5b601480546001600160a01b038381166001600160a01b03199283168117909355601580549092168317909155600c546040516304b7fac560e11b81526004810193909352169063096ff58a90602401600060405180830381600087803b1580156109de57600080fd5b505af11580156109f2573d6000803e3d6000fd5b5050600e546040516304b7fac560e11b81526001600160a01b038581166004830152909116925063096ff58a9150602401600060405180830381600087803b158015610a3d57600080fd5b505af1158015610a51573d6000803e3d6000fd5b50506012546040516304b7fac560e11b81526001600160a01b038581166004830152909116925063096ff58a9150602401600060405180830381600087803b158015610a9c57600080fd5b505af1158015610ab0573d6000803e3d6000fd5b5050600a546040516304b7fac560e11b81526001600160a01b038581166004830152909116925063096ff58a9150602401610792565b6001600160a01b038116610b0d576040516342bec4df60e01b815260040160405180910390fd5b6016546001600160a01b03163314610b3857604051631d0b184b60e21b815260040160405180910390fd5b601780546001600160a01b0319166001600160a01b03838116918217909255600a54604051637827d56160e01b8152600481019290925290911690637827d56190602401600060405180830381600087803b158015610b9657600080fd5b505af1158015610baa573d6000803e3d6000fd5b505050507fdd475012ca9bbf511d7eb2d3c4f7709ade7f0e3910b847b5dcda58b16684d7518142604051610bdf929190615ade565b60405180910390a150565b600f546001600160a01b03163314610c1557604051631d0b184b60e21b815260040160405180910390fd5b60078054919091039055565b6002601a541415610c4d5760405162461bcd60e51b8152600401610c4490615b6b565b60405180910390fd5b6002601a556016546001600160a01b031633141580610c7357506020610c71612fca565b105b80610c7e5750601954155b15610c9c576040516390172c2b60e01b815260040160405180910390fd5b601754601954610cb5916001600160a01b031690613002565b60006019556001601a55565b600033610ccf8582856130e1565b610cda85858561315b565b506001949350505050565b6002601a541415610d085760405162461bcd60e51b8152600401610c4490615b6b565b6002601a55610d198585858561246f565b610d3657604051633d9a544f60e21b815260040160405180910390fd5b6001600160a01b0385166000818152601860205260409020805460ff19166001179055331480610d6d575060b5610d6b612fca565b105b80610da25750636398e41642108015610da257506001600160a01b0385167366eca275200015dcd0c2eaa6e48d4eed3092cdd6145b15610ed95760405163a9059cbb60e01b8152309063a9059cbb90610dcc9088908890600401615ade565b602060405180830381600087803b158015610de657600080fd5b505af1158015610dfa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e1e919061588a565b508015610e8b5760155460405163d1194e2b60e01b81526001600160a01b039091169063d1194e2b90610e58908890600090600401615ade565b600060405180830381600087803b158015610e7257600080fd5b505af1158015610e86573d6000803e3d6000fd5b505050505b604080513381526001600160a01b03871660208201529081018590527f70eb43c4a8ae8c40502dcf22436c509c28d6ff421cf07c491be56984bd9870689060600160405180910390a16110b8565b808015610f065750610f06336b013bf472f5ed1d56574ec0ad676765c793fa10079d601b1b600080613329565b15610f715760155460405163d1194e2b60e01b81526001600160a01b039091169063d1194e2b90610f3e903390600190600401615ade565b600060405180830381600087803b158015610f5857600080fd5b505af1158015610f6c573d6000803e3d6000fd5b505050505b60405163a9059cbb60e01b8152309063a9059cbb90610f9b90339064174876e80090600401615ade565b602060405180830381600087803b158015610fb557600080fd5b505af1158015610fc9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fed919061588a565b5060405163a9059cbb60e01b8152309063a9059cbb9061101b90889064174876e7ff19890190600401615ade565b602060405180830381600087803b15801561103557600080fd5b505af1158015611049573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061106d919061588a565b50604080513381526001600160a01b03871660208201529081018590527f70eb43c4a8ae8c40502dcf22436c509c28d6ff421cf07c491be56984bd9870689060600160405180910390a15b50506001601a55505050565b60006110ce61341f565b905090565b6000336109008185856110e68383612541565b6110f09190615d1c565b612ea5565b600b546001600160a01b0316331461112057604051631d0b184b60e21b815260040160405180910390fd5b61112a8282613002565b5050565b6001600160a01b038116611155576040516342bec4df60e01b815260040160405180910390fd5b6011546001600160a01b03161561117f57604051631d0b184b60e21b815260040160405180910390fd5b601180546001600160a01b038381166001600160a01b03199283168117909355601280549092168317909155600c546040516325e866a160e11b815260048101939093521690634bd0cd4290602401600060405180830381600087803b1580156111e857600080fd5b505af11580156111fc573d6000803e3d6000fd5b5050600e546040516325e866a160e11b81526001600160a01b0385811660048301529091169250634bd0cd429150602401600060405180830381600087803b15801561124757600080fd5b505af115801561125b573d6000803e3d6000fd5b5050600a546040516325e866a160e11b81526001600160a01b0385811660048301529091169250634bd0cd429150602401600060405180830381600087803b1580156112a657600080fd5b505af11580156112ba573d6000803e3d6000fd5b50506010546040516325e866a160e11b81526001600160a01b0385811660048301529091169250634bd0cd429150602401610792565b6002601a5414156113135760405162461bcd60e51b8152600401610c4490615b6b565b6002601a55601254604051631f11d95360e01b81526000916001600160a01b031690631f11d9539061134b9033908890600401615ade565b6101806040518083038186803b15801561136457600080fd5b505afa158015611378573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061139c91906158a6565b905060006113a8612fca565b60608301516080840151919250600091810183106113d05783608001518460600151016113d2565b825b60125460405163f139dfb760e01b81529290910392506001600160a01b03169063f139dfb79061140e90869033908b9088908890600401615bfc565b600060405180830381600087803b15801561142857600080fd5b505af115801561143c573d6000803e3d6000fd5b5050505060006001600e60009054906101000a90046001600160a01b03166001600160a01b0316630f90e0076040518163ffffffff1660e01b815260040160206040518083038186803b15801561149257600080fd5b505afa1580156114a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114ca91906159f8565b03905083608001518210806114ec575082846080015185606001510160070111155b1561171657600e546040516348d62c6d60e11b81526001600160a01b03909116906391ac58da906115269060009033908a90600401615af7565b602060405180830381600087803b15801561154057600080fd5b505af1158015611554573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061157891906159f8565b50600e54604051633b30762f60e11b81526001600160a01b0390911690637660ec5e906115ae9060009033908a90600401615af7565b602060405180830381600087803b1580156115c857600080fd5b505af11580156115dc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061160091906159f8565b50600e5460405163ab18a02760e01b81526001600160a01b039091169063ab18a027906116369060009033908a90600401615af7565b602060405180830381600087803b15801561165057600080fd5b505af1158015611664573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061168891906159f8565b50600e54604051637c6d493160e11b81526001600160a01b039091169063f8da9262906116be9060009033908a90600401615af7565b602060405180830381600087803b1580156116d857600080fd5b505af11580156116ec573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061171091906159f8565b5061194e565b6060840151820160001901818111156117b357600e546040516348d62c6d60e11b81526001600160a01b03909116906391ac58da9061175f908585039033908b90600401615af7565b602060405180830381600087803b15801561177957600080fd5b505af115801561178d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117b191906159f8565b505b600e546001600160a01b0316637660ec5e600a830433896040518463ffffffff1660e01b81526004016117e893929190615af7565b602060405180830381600087803b15801561180257600080fd5b505af1158015611816573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061183a91906159f8565b50600e546001600160a01b031663ab18a0276064830433896040518463ffffffff1660e01b815260040161187093929190615af7565b602060405180830381600087803b15801561188a57600080fd5b505af115801561189e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118c291906159f8565b50600e546001600160a01b031663f8da92626103e8830433896040518463ffffffff1660e01b81526004016118f993929190615af7565b602060405180830381600087803b15801561191357600080fd5b505af1158015611927573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061194b91906159f8565b50505b600e5460608501518551604051634c29019760e11b815260048101839052918501600019016024830152151560448201526000916001600160a01b031690639852032e9060640160206040518083038186803b1580156119ad57600080fd5b505afa1580156119c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119e591906159f8565b90506000670de0b6b3a7640000828760c001510281611a1457634e487b7160e01b600052601260045260246000fd5b0490506000806000611a29338d868e8e613546565b9250925092506000899050336001600160a01b03167fe16ef2c5ff376e78191b54a217db16e2ca4eca7cb451d282f7dd8a75c492df498e8e8789888888604051611a799796959493929190615c68565b60405180910390a250506001601a555050505050505050505050565b6001600160a01b038116611abc576040516342bec4df60e01b815260040160405180910390fd5b6016546001600160a01b03163314611ae757604051631d0b184b60e21b815260040160405180910390fd5b601680546001600160a01b0319166001600160a01b03838116918217909255600a54604051637e98d42960e11b815260048101929092529091169063fd31a85290602401600060405180830381600087803b158015611b4557600080fd5b505af1158015611b59573d6000803e3d6000fd5b505050507fd535651a5590e21c663adfebb2eb6431447c10dccf3eee90c4c4d8bf7983c14a8142604051610bdf929190615ade565b6001600160a01b038116611bb5576040516342bec4df60e01b815260040160405180910390fd5b600d546001600160a01b031615611bdf57604051631d0b184b60e21b815260040160405180910390fd5b600d80546001600160a01b038381166001600160a01b03199283168117909355600e80549092168317909155600c5460405163307b626b60e11b8152600481019390935216906360f6c4d690602401610792565b6001600160a01b038116611c5a576040516342bec4df60e01b815260040160405180910390fd5b6016546001600160a01b031633141580611c7e57506013546001600160a01b031615155b15611c9c57604051631d0b184b60e21b815260040160405180910390fd5b601380546001600160a01b0319166001600160a01b03838116918217909255601254604051636f72995f60e01b8152600481019290925290911690636f72995f90602401600060405180830381600087803b158015611cfa57600080fd5b505af1158015611d0e573d6000803e3d6000fd5b5050600e54604051636f72995f60e01b81526001600160a01b0385811660048301529091169250636f72995f9150602401610792565b6011546001600160a01b03163314611d6f57604051631d0b184b60e21b815260040160405180910390fd5b600780549091019055565b6011546001600160a01b03163314611da557604051631d0b184b60e21b815260040160405180910390fd5b8060196000828254611db79190615d1c565b909155505050565b6001600160a01b038116600090815260056020526040812054610906565b60606004805461086f90615d4b565b6001600160a01b038116611e13576040516342bec4df60e01b815260040160405180910390fd5b600f546001600160a01b031615611e3d57604051631d0b184b60e21b815260040160405180910390fd5b601080546001600160a01b038381166001600160a01b03199283168117909355600f80549092168317909155600e546040516396cc563f60e01b8152600481019390935216906396cc563f90602401600060405180830381600087803b158015611ea657600080fd5b505af1158015611eba573d6000803e3d6000fd5b5050600a546040516396cc563f60e01b81526001600160a01b03858116600483015290911692506396cc563f9150602401600060405180830381600087803b158015611f0557600080fd5b505af1158015611f19573d6000803e3d6000fd5b5050600c54604051632e8098c560e01b81526001600160a01b0385811660048301529091169250632e8098c59150602401610792565b6009546001600160a01b03163314801590611f755750600b546001600160a01b03163314155b8015611f8c57506011546001600160a01b03163314155b8015611fa357506013546001600160a01b03163314155b15611fc157604051631d0b184b60e21b815260040160405180910390fd5b61112a8282613c21565b60003381611fd98286612541565b9050838110156120395760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610c44565b610cda8286868403612ea5565b60003361090081858561315b565b6001600160a01b03811661207b576040516342bec4df60e01b815260040160405180910390fd5b6009546001600160a01b0316156120a557604051631d0b184b60e21b815260040160405180910390fd5b600980546001600160a01b039092166001600160a01b03199283168117909155600a8054909216179055565b6011546001600160a01b031633146120fc57604051631d0b184b60e21b815260040160405180910390fd5b600880549091019055565b6002601a54141561212a5760405162461bcd60e51b8152600401610c4490615b6b565b6002601a55601254604051631f11d95360e01b81526000916001600160a01b031690631f11d953906121629087908790600401615ade565b6101806040518083038186803b15801561217b57600080fd5b505afa15801561218f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121b391906158a6565b9050600081600001516121d45781608001518260600151016101b8016121e4565b8160800151826060015101610113015b9050806121ef612fca565b108061220057506000826101200151115b8061220c575081602001515b8061221957506080820151155b156122375760405163270b039d60e21b815260040160405180910390fd5b60006122468387878588613d67565b90508380156122745750612274336ac34556b3f6459039627ea4676765c793fa10079d601b1b600080613329565b156122df5760155460405163d1194e2b60e01b81526001600160a01b039091169063d1194e2b906122ac903390600190600401615ade565b600060405180830381600087803b1580156122c657600080fd5b505af11580156122da573d6000803e3d6000fd5b505050505b6122fe868683600001518460200151856040015186606001518961447c565b50506001601a5550505050565b8342111561235b5760405162461bcd60e51b815260206004820152601d60248201527f45524332305065726d69743a206578706972656420646561646c696e650000006044820152606401610c44565b60007f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c988888861238a8c614643565b6040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810186905260e00160405160208183030381529060405280519060200120905060006123e58261466b565b905060006123f5828787876146b9565b9050896001600160a01b0316816001600160a01b0316146124585760405162461bcd60e51b815260206004820152601e60248201527f45524332305065726d69743a20696e76616c6964207369676e617475726500006044820152606401610c44565b6124638a8a8a612ea5565b50505050505050505050565b6040516bffffffffffffffffffffffff19606086901b16602082015260348101849052600090819060540160408051601f1981840301815291815281516020928301206001600160a01b0389166000908152601890935291205490915060ff1615801561253757506125378484808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152507fcad71776a60b1a4ca80bfa5452bfc50beeb645b7f64e97f5c464ef45a41d548d92508591506146e19050565b9695505050505050565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b600d546001600160a01b0316331461112057604051631d0b184b60e21b815260040160405180910390fd5b600f546001600160a01b031633146125c257604051631d0b184b60e21b815260040160405180910390fd5b60088054919091039055565b6002601a5414156125f15760405162461bcd60e51b8152600401610c4490615b6b565b6002601a55601254604051631f11d95360e01b81526000916001600160a01b031690631f11d953906126299087908790600401615ade565b6101806040518083038186803b15801561264257600080fd5b505afa158015612656573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061267a91906158a6565b60808101516060820151919250016000612692612fca565b9050826020015180156126a757508261010001515b156126c557604051635ad8888f60e01b815260040160405180910390fd5b600083610120015111806126dd575060008360a00151115b806126e757508181105b806126f457506080830151155b15612712576040516301ddf13b60e61b815260040160405180910390fd5b601254604051632fd96bfd60e11b81526001600160a01b038881166004830152602482018890526044820184905290911690635fb2d7fa90606401600060405180830381600087803b15801561276757600080fd5b505af115801561277b573d6000803e3d6000fd5b50505050600083600001511561281e57676765c793fa10079d601b1b6127fb606460076b08759642e984636060b13f5288610140015102816127cd57634e487b7160e01b600052601260045260246000fd5b04816127e957634e487b7160e01b600052601260045260246000fd5b04676765c793fa10079d601b1b6146f7565b8161281657634e487b7160e01b600052601260045260246000fd5b04905061287f565b676765c793fa10079d601b1b612860606460076b053a680649b3e32378b13f5288610140015102816127cd57634e487b7160e01b600052601260045260246000fd5b8161287b57634e487b7160e01b600052601260045260246000fd5b0490505b6101408401516000906128b4906128968185614751565b8660070186116128a75760006128ae565b6007878703035b02614751565b90508460200151156129ae57600c546040516303f9f6f560e61b81526001600160a01b039091169063fe7dbd40906128f2908b908b90600401615ade565b602060405180830381600087803b15801561290c57600080fd5b505af1158015612920573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612944919061588a565b156129ae57600c546040516342cdcbff60e11b81526001600160a01b039091169063859b97fe9061297b908b908b90600401615ade565b600060405180830381600087803b15801561299557600080fd5b505af11580156129a9573d6000803e3d6000fd5b505050505b600e5460408051630f90e00760e01b815290516000926001600160a01b031691630f90e007916004808301926020929190829003018186803b1580156129f357600080fd5b505afa158015612a07573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a2b91906159f8565b90508560600151600101811015612acf57600e5460608701516040516348d62c6d60e11b81526001600160a01b03909216916391ac58da91612a7b91908590036001019033908c90600401615af7565b602060405180830381600087803b158015612a9557600080fd5b505af1158015612aa9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612acd91906159f8565b505b855115612b545761014086015160078054919091039055600e5460c08701516040516310d6833b60e31b81526001600160a01b03909216916386b419d891612b1d9160040190815260200190565b600060405180830381600087803b158015612b3757600080fd5b505af1158015612b4b573d6000803e3d6000fd5b50505050612bce565b61014086015160088054919091039055600e5460c0870151604051633f9d44fd60e01b81526001600160a01b0390921691633f9d44fd91612b9b9160040190815260200190565b600060405180830381600087803b158015612bb557600080fd5b505af1158015612bc9573d6000803e3d6000fd5b505050505b61016086015160009015612d1d578215612d1857600a546040516317858ab160e31b81526000916001600160a01b03169063bc2c558890612c15908e908e90600401615ade565b602060405180830381600087803b158015612c2f57600080fd5b505af1158015612c43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c679190615649565b600a5460608a0151604051631898636d60e31b81523360048201526001600160a01b03808516602483015260448201899052918a03606482015260006084820152929350169063c4c31b689060a401602060405180830381600087803b158015612cd057600080fd5b505af1158015612ce4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d0891906159f8565b9150612d148285614751565b9350505b612db1565b600e546101408801516001600160a01b0390911690631ce2ed86908510612d45576000612d4e565b84896101400151035b6040518263ffffffff1660e01b8152600401612d6c91815260200190565b600060405180830381600087803b158015612d8657600080fd5b505af1158015612d9a573d6000803e3d6000fd5b505050506000831115612db157612db13384613002565b878015612ddd5750612ddd336ac34556b3f6459039627ea4676765c793fa10079d601b1b600080613329565b15612e485760155460405163d1194e2b60e01b81526001600160a01b039091169063d1194e2b90612e15903390600190600401615ade565b600060405180830381600087803b158015612e2f57600080fd5b505af1158015612e43573d6000803e3d6000fd5b505050505b604080516001600160a01b038c168152602081018b905290810184905233907f0d6b33a90fd9ff440fc1c724f3f3d492e7c2fc1c629b5a8aacf5c76c2ecbcdbe9060600160405180910390a250506001601a555050505050505050565b6001600160a01b038316612f075760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610c44565b6001600160a01b038216612f685760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610c44565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6000636398e416421015612ff157604051633c109e6160e11b815260040160405180910390fd5b506201518042636398e41519010490565b6001600160a01b0382166130585760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610c44565b806002600082825461306a9190615d1c565b90915550506001600160a01b03821660009081526020819052604081208054839290613097908490615d1c565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b60006130ed8484612541565b9050600019811461315557818110156131485760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401610c44565b6131558484848403612ea5565b50505050565b6001600160a01b0383166131bf5760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610c44565b6001600160a01b0382166132215760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610c44565b6001600160a01b038316600090815260208190526040902054818110156132995760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610c44565b6001600160a01b038085166000908152602081905260408082208585039055918516815290812080548492906132d0908490615d1c565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161331c91815260200190565b60405180910390a3613155565b600082156133d357601054604051631f8ee6fb60e11b81526004810185905264e8d4a51000916001600160a01b031690633f1dcdf69060240160206040518083038186803b15801561337a57600080fd5b505afa15801561338e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133b291906159f8565b8602816133cf57634e487b7160e01b600052601260045260246000fd5b0494505b8385106133e257506001613416565b6000846133ef8885614769565b8161340a57634e487b7160e01b600052601260045260246000fd5b87870391900610159150505b95945050505050565b6000306001600160a01b037f000000000000000000000000444444444444c1a66f394025ac839a535246fcc81614801561347857507f000000000000000000000000000000000000000000000000000000000000000146145b156134a257507fd951a38113188a785d6753c8c4b58034c16acb79f12f2984aaf2450160fe2feb90565b50604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6020808301919091527f0a66c77ba3b0497bc9ceb34460a870cd59906d00eaa03ff7f53a58db8b994326828401527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608301524660808301523060a0808401919091528351808403909101815260c0909201909252805191012090565b601254604051631f11d95360e01b81526000918291829182916001600160a01b0390911690631f11d95390613581908c908c90600401615ade565b6101806040518083038186803b15801561359a57600080fd5b505afa1580156135ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135d291906158a6565b905060006135de612fca565b6060830151608084015191925060009181018310613606578360800151846060015101613608565b825b0390506000601060009054906101000a90046001600160a01b03166001600160a01b031663e4c2a4d68584868e6040518563ffffffff1660e01b81526004016136549493929190615c3b565b60c060405180830381600087803b15801561366e57600080fd5b505af1158015613682573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136a6919061595b565b905083608001518210156137fe576136c8848b836020015184600001516147c5565b945087156137f95761372c8c85600001516136e45760646136e7565b600a5b60ff166b053a680649b3e32378b13f528161371257634e487b7160e01b600052601260045260246000fd5b04676765c793fa10079d601b1b8761014001516000613329565b1561379b5760155460405163d1194e2b60e01b81526001600160a01b039091169063d1194e2b90613764908f90600190600401615ade565b600060405180830381600087803b15801561377e57600080fd5b505af1158015613792573d6000803e3d6000fd5b50505050613b9f565b8880156137c157506137c18c676765c793fa10079d601b1b808761014001516002613329565b156137f95760155460405163d1194e2b60e01b81526001600160a01b039091169063d1194e2b90613764908f90600190600401615ade565b613b9f565b83608001518460600151016007018311156139c757613827848b836080015184606001516147c5565b945087156137f957600089156138e4576138648d600a6b053a680649b3e32378b13f5204676765c793fa10079d601b1b886101400151600a613329565b156138e457601560009054906101000a90046001600160a01b03166001600160a01b031663d1194e2b8e600b6040518363ffffffff1660e01b81526004016138ad929190615ade565b600060405180830381600087803b1580156138c757600080fd5b505af11580156138db573d6000803e3d6000fd5b50505050600190505b806139c1576139458d86600001516138fd576064613900565b600a5b60ff166b053a680649b3e32378b13f528161392b57634e487b7160e01b600052601260045260246000fd5b04676765c793fa10079d601b1b8861014001516000613329565b156139c157601560009054906101000a90046001600160a01b03166001600160a01b031663d1194e2b8e60016040518363ffffffff1660e01b815260040161398e929190615ade565b600060405180830381600087803b1580156139a857600080fd5b505af11580156139bc573d6000803e3d6000fd5b505050505b50613b9f565b8715613b9f57605984608001511115613abe576139f88c676765c793fa10079d601b1b808761014001516000613329565b15613a635760155460405163d1194e2b60e01b81526001600160a01b039091169063d1194e2b90613a30908f90600190600401615ade565b600060405180830381600087803b158015613a4a57600080fd5b505af1158015613a5e573d6000803e3d6000fd5b505050505b88156137f957613a878c676765c793fa10079d601b1b808761014001516003613329565b156137f95760155460405163d1194e2b60e01b81526001600160a01b039091169063d1194e2b90613764908f906004908101615ade565b613ad68c60646b053a680649b3e32378b13f52613712565b15613b0e5760155460405163d1194e2b60e01b81526001600160a01b039091169063d1194e2b90613764908f90600190600401615ade565b888015613b345750613b348c676765c793fa10079d601b1b808761014001516014613329565b15613b9f5760155460405163d1194e2b60e01b81526001600160a01b039091169063d1194e2b90613b6c908f90600190600401615ade565b600060405180830381600087803b158015613b8657600080fd5b505af1158015613b9a573d6000803e3d6000fd5b505050505b613bb4848483604001518460a001518e6149b6565b61016084015115613be057613bd6848d8d8d856080015186606001518f614b3f565b9097509550613c12565b60008a905060008a9050613c0a868f85602001518660800151876000015188606001518888614c61565b909950975050505b50505050955095509592505050565b6001600160a01b038216613c815760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401610c44565b6001600160a01b03821660009081526020819052604090205481811015613cf55760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608401610c44565b6001600160a01b0383166000908152602081905260408120838303905560028054849290613d24908490615d34565b90915550506040518281526000906001600160a01b038516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602001612fbd565b613d926040518060800160405280600081526020016000815260200160008152602001600081525090565b6012546040516365892b4960e01b81526001600160a01b03878116600483015260248201879052426044830152909116906365892b4990606401600060405180830381600087803b158015613de657600080fd5b505af1158015613dfa573d6000803e3d6000fd5b50504261012089015250506010546101408701516040516336a34f2760e01b81526001600160a01b03909216916336a34f2791613e3d9160040190815260200190565b600060405180830381600087803b158015613e5757600080fd5b505af1158015613e6b573d6000803e3d6000fd5b50506010546040516303e2f69160e11b8152600093506001600160a01b0390911691506307c5ed2290613ea2908a90600401615ba2565b60206040518083038186803b158015613eba57600080fd5b505afa158015613ece573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ef291906159f8565b600e54606089015160808a0151604051635d070d7f60e11b81523360048201526024810192909252604482015285151560648201529192506001600160a01b03169063ba0e1afe90608401600060405180830381600087803b158015613f5757600080fd5b505af1158015613f6b573d6000803e3d6000fd5b5050505060008188610140015111613f84576000613f8d565b81886101400151035b600e5460608a015160808b01518b51604051634c29019760e11b815260048101849052919092016000190160248201529015156044820152919250600091670de0b6b3a7640000916001600160a01b031690639852032e9060640160206040518083038186803b15801561400057600080fd5b505afa158015614014573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061403891906159f8565b8a60c00151028161405957634e487b7160e01b600052601260045260246000fd5b04905081810161406a8a8884614f26565b676765c793fa10079d601b1b6ac34556b3f6459039627ea482020461408f8482614751565b60408701526101608b01511561437957600a546040516317858ab160e31b81528c9189918d918d916000916001600160a01b03169063c4c31b68903390839063bc2c5588906140e49089908990600401615ade565b602060405180830381600087803b1580156140fe57600080fd5b505af1158015614112573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141369190615649565b8e604001518960600151614148612fca565b6040516001600160e01b031960e088901b1681526001600160a01b039586166004820152949093166024850152604484019190915290036064820152861515608482015260a401602060405180830381600087803b1580156141a957600080fd5b505af11580156141bd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141e191906159f8565b600a549091506000906001600160a01b03166354f4758f87868685614204612fca565b60016040518763ffffffff1660e01b815260040161422796959493929190615bb1565b602060405180830381600087803b15801561424157600080fd5b505af1158015614255573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061427991906159f8565b905081871161428957600061428d565b8187035b60608d015260408c0180518390039081905281830101808b111561431357808b03808e52600e54604051630e7176c360e11b815260048101929092526001600160a01b031690631ce2ed8690602401600060405180830381600087803b1580156142f657600080fd5b505af115801561430a573d6000803e3d6000fd5b50505050614318565b60008d525b8c606001518a1161432a576000614332565b8c606001518a035b60208e015260a087015161436d5786511561435c576101408701516007805491909103905561436d565b610140870151600880549190910390555b5050505050505061446e565b83811161438757600061438b565b8381035b6060870152604086015184116143a25760006143aa565b856040015184035b8652606086015183116143be5760006143c6565b856060015183035b602087015260a08b015161446e57600e546101408c0151604051630e7176c360e11b81526001600160a01b0390921691631ce2ed869161440c9160040190815260200190565b600060405180830381600087803b15801561442657600080fd5b505af115801561443a573d6000803e3d6000fd5b505050508a600001511561445d576101408b01516007805491909103905561446e565b6101408b0151600880549190910390555b505050505095945050505050565b84840160006b053a680649b3e32378b13f52676765c793fa10079d601b1b8302600e546040516339dbe92160e21b8152929091046004830181905292506001600160a01b03169063e76fa48490602401600060405180830381600087803b1580156144e657600080fd5b505af11580156144fa573d6000803e3d6000fd5b505050506000676765c793fa10079d601b1b6ac34556b3f6459039627ea484028161453557634e487b7160e01b600052601260045260246000fd5b04828403039050858101850180156145a657600e5460405163c0c8835760e01b8152600481018390526001600160a01b039091169063c0c8835790602401600060405180830381600087803b15801561458d57600080fd5b505af11580156145a1573d6000803e3d6000fd5b505050505b81156145c2576017546145c2906001600160a01b031683613002565b6145ce33888801613002565b336001600160a01b038c167f3d7c224e9ad9e12f013ba8a086e40c61608308a8810a12b8fa970887f0feb6aa8c8a8a8888676765c793fa10079d601b1b6ac34556b3f6459039627ea48d02048d60405161462e9796959493929190615cad565b60405180910390a35050505050505050505050565b6001600160a01b03811660009081526005602052604090208054600181018255905b50919050565b600061090661467861341f565b8360405161190160f01b6020820152602281018390526042810182905260009060620160405160208183030381529060405280519060200120905092915050565b60008060006146ca8787878761508a565b915091506146d781615177565b5095945050505050565b6000826146ee858461537b565b14949350505050565b60008182848161471757634e487b7160e01b600052601260045260246000fd5b0615614724576001614727565b60005b60ff1683858161474757634e487b7160e01b600052601260045260246000fd5b0401029392505050565b60008183106147605781614762565b825b9392505050565b60004282014340846040516020016147a693929190928352602083019190915260601b6bffffffffffffffffffffffff1916604082015260540190565b60408051601f1981840301815291905280516020909101209392505050565b600080600086610160015111156147f0576147e285848601614751565b90508092506000935061487c565b60a08601516148775785511561480e57600780548590039055614818565b6008805485900390555b600e54604051630e7176c360e11b8152600481018690526001600160a01b0390911690631ce2ed8690602401600060405180830381600087803b15801561485e57600080fd5b505af1158015614872573d6000803e3d6000fd5b505050505b508282015b600e546040516339dbe92160e21b81526b053a680649b3e32378b13f52676765c793fa10079d601b1b8402046004820181905293506001600160a01b039091169063e76fa48490602401600060405180830381600087803b1580156148e057600080fd5b505af11580156148f4573d6000803e3d6000fd5b505050506000676765c793fa10079d601b1b6ac34556b3f6459039627ea483028161492f57634e487b7160e01b600052601260045260246000fd5b60175491900484840303915061494e906001600160a01b031682613002565b600e5460405163c0c8835760e01b8152600481018390526001600160a01b039091169063c0c8835790602401600060405180830381600087803b15801561499457600080fd5b505af11580156149a8573d6000803e3d6000fd5b505050505050949350505050565b60a0850151614a9d578451614a3357600e5460c0860151604051633f9d44fd60e01b81526001600160a01b0390921691633f9d44fd916149fc9160040190815260200190565b600060405180830381600087803b158015614a1657600080fd5b505af1158015614a2a573d6000803e3d6000fd5b50505050614a9d565b600e5460c08601516040516310d6833b60e31b81526001600160a01b03909216916386b419d891614a6a9160040190815260200190565b600060405180830381600087803b158015614a8457600080fd5b505af1158015614a98573d6000803e3d6000fd5b505050505b600085608001518660600151018510614ab7576000614ab9565b835b82876101400151010390508561014001518110614b3757600e546001600160a01b03166365407202614aeb88846153fd565b6040518263ffffffff1660e01b8152600401614b0991815260200190565b600060405180830381600087803b158015614b2357600080fd5b505af1158015612463573d6000803e3d6000fd5b505050505050565b6000806000614b5087868801614751565b90506000614b5c612fca565b90508a60a0015160001415614b7c576101408b0151600780549190910390555b60008a90506000600a60009054906101000a90046001600160a01b03166001600160a01b03166354f4758f8e848e86888d6040518763ffffffff1660e01b8152600401614bce96959493929190615bb1565b602060405180830381600087803b158015614be857600080fd5b505af1158015614bfc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614c2091906159f8565b9050808d610140015111614c35576000614c3e565b808d6101400151035b9550838a039450614c518287878a615543565b5050505097509795505050505050565b6000806000614d0289601060009054906101000a90046001600160a01b03166001600160a01b03166307c5ed228e6040518263ffffffff1660e01b8152600401614cab9190615ba2565b60206040518083038186803b158015614cc357600080fd5b505afa158015614cd7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614cfb91906159f8565b8a016155c4565b90506000614d1088886155c4565b9050818c610140015111614d25576000614d2e565b818c6101400151035b9350808611614d3e576000614d42565b8086035b92508b60a0015160001415614e96578b5115614d6d576101408c015160078054919091039055614d7e565b6101408c0151600880549190910390555b600083118015614d8c575084155b15614df057600e5460405163c0c8835760e01b8152600481018590526001600160a01b039091169063c0c8835790602401600060405180830381600087803b158015614dd757600080fd5b505af1158015614deb573d6000803e3d6000fd5b505050505b8415614e6557600e546101408d0151604051630e7176c360e11b81526001600160a01b0390921691631ce2ed8691614e2e9160040190815260200190565b600060405180830381600087803b158015614e4857600080fd5b505af1158015614e5c573d6000803e3d6000fd5b50505050614ef6565b600e54604051630e7176c360e11b8152600481018490526001600160a01b0390911690631ce2ed8690602401614e2e565b600e5460405163c0c8835760e01b815284860160048201526001600160a01b039091169063c0c8835790602401600060405180830381600087803b158015614edd57600080fd5b505af1158015614ef1573d6000803e3d6000fd5b505050505b84158015614f0657506000838501115b15614f1757614f178b848601613002565b50509850989650505050505050565b60a083015161500d578251614fa357600e5460c0840151604051633f9d44fd60e01b81526001600160a01b0390921691633f9d44fd91614f6c9160040190815260200190565b600060405180830381600087803b158015614f8657600080fd5b505af1158015614f9a573d6000803e3d6000fd5b5050505061500d565b600e5460c08401516040516310d6833b60e31b81526001600160a01b03909216916386b419d891614fda9160040190815260200190565b600060405180830381600087803b158015614ff457600080fd5b505af1158015615008573d6000803e3d6000fd5b505050505b600e546101408401516001600160a01b039091169063654072029061503590869085016153fd565b6040518263ffffffff1660e01b815260040161505391815260200190565b600060405180830381600087803b15801561506d57600080fd5b505af1158015615081573d6000803e3d6000fd5b50505050505050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311156150c1575060009050600361516e565b8460ff16601b141580156150d957508460ff16601c14155b156150ea575060009050600461516e565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa15801561513e573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166151675760006001925092505061516e565b9150600090505b94509492505050565b600081600481111561519957634e487b7160e01b600052602160045260246000fd5b14156151a25750565b60018160048111156151c457634e487b7160e01b600052602160045260246000fd5b14156152125760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401610c44565b600281600481111561523457634e487b7160e01b600052602160045260246000fd5b14156152825760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610c44565b60038160048111156152a457634e487b7160e01b600052602160045260246000fd5b14156152fd5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608401610c44565b600481600481111561531f57634e487b7160e01b600052602160045260246000fd5b14156153785760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b6064820152608401610c44565b50565b600081815b84518110156153f55760008582815181106153ab57634e487b7160e01b600052603260045260246000fd5b602002602001015190508083116153d157600083815260208290526040902092506153e2565b600081815260208490526040902092505b50806153ed81615d80565b915050615380565b509392505050565b6000676765c793fa10079d601b1b8360c00151670de0b6b3a7640000615446676765c793fa10079d601b1b86026b053a680649b3e32378b13f5267016345785d8a000002614751565b85028161546357634e487b7160e01b600052601260045260246000fd5b046154f46b0709fa55642a614ae4f586a686026105b06154d1676765c793fa10079d601b1b61115c02676765c793fa10079d601b1b6154cb6ac34556b3f6459039627ea4676765c793fa10079d601b1b018d6080015102676765c793fa10079d601b1b6146f7565b03614751565b8802816154ee57634e487b7160e01b600052601260045260246000fd5b04614751565b676765c793fa10079d601b1b860201018161551f57634e487b7160e01b600052601260045260246000fd5b048161553b57634e487b7160e01b600052601260045260246000fd5b049392505050565b8061315557821561555a5761555a84838501613002565b811561315557600e5460405163c0c8835760e01b8152600481018490526001600160a01b039091169063c0c8835790602401600060405180830381600087803b1580156155a657600080fd5b505af11580156155ba573d6000803e3d6000fd5b5050505050505050565b60008183116147605781614762565b60008083601f8401126155e4578182fd5b50813567ffffffffffffffff8111156155fb578182fd5b6020830191508360208260051b850101111561561657600080fd5b9250929050565b805161562881615dc6565b919050565b60006020828403121561563e578081fd5b813561476281615db1565b60006020828403121561565a578081fd5b815161476281615db1565b60008060408385031215615677578081fd5b823561568281615db1565b9150602083013561569281615db1565b809150509250929050565b6000806000606084860312156156b1578081fd5b83356156bc81615db1565b925060208401356156cc81615db1565b929592945050506040919091013590565b600080600080600080600060e0888a0312156156f7578283fd5b873561570281615db1565b9650602088013561571281615db1565b95506040880135945060608801359350608088013560ff81168114615735578384fd5b9699959850939692959460a0840135945060c09093013592915050565b60008060408385031215615764578182fd5b823561576f81615db1565b946020939093013593505050565b60008060008060608587031215615792578384fd5b843561579d81615db1565b935060208501359250604085013567ffffffffffffffff8111156157bf578283fd5b6157cb878288016155d3565b95989497509550505050565b6000806000806000608086880312156157ee578081fd5b85356157f981615db1565b945060208601359350604086013567ffffffffffffffff81111561581b578182fd5b615827888289016155d3565b909450925050606086013561583b81615dc6565b809150509295509295909350565b60008060006060848603121561585d578081fd5b833561586881615db1565b925060208401359150604084013561587f81615dc6565b809150509250925092565b60006020828403121561589b578081fd5b815161476281615dc6565b600061018082840312156158b8578081fd5b6158c0615ce4565b6158c98361561d565b81526158d76020840161561d565b60208201526158e86040840161561d565b6040820152606083015160608201526080830151608082015260a083015160a082015260c083015160c082015260e083015160e082015261010061592d81850161561d565b9082015261012083810151908201526101408084015190820152610160928301519281019290925250919050565b600060c0828403121561596c578081fd5b60405160c0810181811067ffffffffffffffff8211171561599b57634e487b7160e01b83526041600452602483fd5b8060405250825181526020830151602082015260408301516040820152606083015160608201526080830151608082015260a083015160a08201528091505092915050565b6000602082840312156159f1578081fd5b5035919050565b600060208284031215615a09578081fd5b5051919050565b600080600060608486031215615a24578081fd5b833592506020840135615a3681615dc6565b9150604084013561587f81615dc6565b8051151582526020810151615a5f602084018215159052565b506040810151615a73604084018215159052565b50606081015160608301526080810151608083015260a081015160a083015260c081015160c083015260e081015160e083015261010080820151615aba8285018215159052565b50506101208181015190830152610140808201519083015261016090810151910152565b6001600160a01b03929092168252602082015260400190565b9283526001600160a01b039190911660208301521515604082015260600190565b6000602080835283518082850152825b81811015615b4457858101830151858201604001528201615b28565b81811115615b555783604083870101525b50601f01601f1916929092016040019392505050565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b61018081016109068284615a46565b6102208101615bc08289615a46565b6001600160a01b039687166101808301526101a0820195909552929094166101c08301526101e082015291151561020090920191909152919050565b6102008101615c0b8288615a46565b6001600160a01b03959095166101808201526101a08101939093526101c08301919091526101e090910152919050565b6101e08101615c4a8287615a46565b84610180830152836101a0830152826101c083015295945050505050565b60006102408201905088825287151560208301528660408301528560608301528460808301528360a0830152615ca160c0830184615a46565b98975050505050505050565b6000610240820190508882528760208301528660408301528560608301528460808301528360a0830152615ca160c0830184615a46565b604051610180810167ffffffffffffffff81118282101715615d1657634e487b7160e01b600052604160045260246000fd5b60405290565b60008219821115615d2f57615d2f615d9b565b500190565b600082821015615d4657615d46615d9b565b500390565b600181811c90821680615d5f57607f821691505b6020821081141561466557634e487b7160e01b600052602260045260246000fd5b6000600019821415615d9457615d94615d9b565b5060010190565b634e487b7160e01b600052601160045260246000fd5b6001600160a01b038116811461537857600080fd5b801515811461537857600080fdfea2646970667358221220cf7fec9b990305171b472ea11addeeab57dabb8532cd6cde4b8907984b8c951864736f6c63430008040033