ETH Price: $2,938.90 (-0.68%)

Token

CRV WEEKLY PUTS SSOV V3 2 (CRV-WEEKLY-PUTS-SSOV-V3-2)

Overview

Max Total Supply

1 CRV-WEEKLY-PUTS-SSOV-V3-2

Holders

1

Market

Volume (24H)

N/A

Min Price (24H)

N/A

Max Price (24H)

N/A
Balance
1 CRV-WEEKLY-PUTS-SSOV-V3-2
0xc07b5c9f38f97eea948034831d98ccdf7070b55c
Loading...
Loading
Loading...
Loading
Loading...
Loading

Click here to update the token information / general information
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.

Contract Source Code Verified (Exact Match)

Contract Name:
CrvWeeklyPutsSsovV3

Compiler Version
v0.8.9+commit.e5eed63a

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, Unlicense license
File 1 of 50 : CrvWeeklyPutsSsovV3.sol
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

// Contracts
import {SsovV3} from "../core/SsovV3.sol";

/// @title CRV Weekly Puts SSOV V3 contract
contract CrvWeeklyPutsSsovV3 is SsovV3 {
    constructor()
        SsovV3(
            "CRV WEEKLY PUTS SSOV V3 2",
            "CRV-WEEKLY-PUTS-SSOV-V3-2",
            "CRV",
            0x7f90122BF0700F9E7e1F688fe926940E8839F353,
            true
        )
    {}
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol)

pragma solidity ^0.8.0;

import "./IAccessControlEnumerableUpgradeable.sol";
import "./AccessControlUpgradeable.sol";
import "../utils/structs/EnumerableSetUpgradeable.sol";
import "../proxy/utils/Initializable.sol";

/**
 * @dev Extension of {AccessControl} that allows enumerating the members of each role.
 */
abstract contract AccessControlEnumerableUpgradeable is Initializable, IAccessControlEnumerableUpgradeable, AccessControlUpgradeable {
    function __AccessControlEnumerable_init() internal onlyInitializing {
    }

    function __AccessControlEnumerable_init_unchained() internal onlyInitializing {
    }
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;

    mapping(bytes32 => EnumerableSetUpgradeable.AddressSet) private _roleMembers;

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControlEnumerableUpgradeable).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns one of the accounts that have `role`. `index` must be a
     * value between 0 and {getRoleMemberCount}, non-inclusive.
     *
     * Role bearers are not sorted in any particular way, and their ordering may
     * change at any point.
     *
     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
     * you perform all queries on the same block. See the following
     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
     * for more information.
     */
    function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) {
        return _roleMembers[role].at(index);
    }

    /**
     * @dev Returns the number of accounts that have `role`. Can be used
     * together with {getRoleMember} to enumerate all bearers of a role.
     */
    function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) {
        return _roleMembers[role].length();
    }

    /**
     * @dev Overload {_grantRole} to track enumerable memberships
     */
    function _grantRole(bytes32 role, address account) internal virtual override {
        super._grantRole(role, account);
        _roleMembers[role].add(account);
    }

    /**
     * @dev Overload {_revokeRole} to track enumerable memberships
     */
    function _revokeRole(bytes32 role, address account) internal virtual override {
        super._revokeRole(role, account);
        _roleMembers[role].remove(account);
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControl.sol)

pragma solidity ^0.8.0;

import "./IAccessControlUpgradeable.sol";
import "../utils/ContextUpgradeable.sol";
import "../utils/StringsUpgradeable.sol";
import "../utils/introspection/ERC165Upgradeable.sol";
import "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it.
 */
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControlUpgradeable, ERC165Upgradeable {
    function __AccessControl_init() internal onlyInitializing {
    }

    function __AccessControl_init_unchained() internal onlyInitializing {
    }
    struct RoleData {
        mapping(address => bool) members;
        bytes32 adminRole;
    }

    mapping(bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with a standardized message including the required role.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     *
     * _Available since v4.1._
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role, _msgSender());
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControlUpgradeable).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
        return _roles[role].members[account];
    }

    /**
     * @dev Revert with a standard message if `account` is missing `role`.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert(
                string(
                    abi.encodePacked(
                        "AccessControl: account ",
                        StringsUpgradeable.toHexString(uint160(account), 20),
                        " is missing role ",
                        StringsUpgradeable.toHexString(uint256(role), 32)
                    )
                )
            );
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) public virtual override {
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");

        _revokeRole(role, account);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     *
     * NOTE: This function is deprecated in favor of {_grantRole}.
     */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * Internal function without access restriction.
     */
    function _grantRole(bytes32 role, address account) internal virtual {
        if (!hasRole(role, account)) {
            _roles[role].members[account] = true;
            emit RoleGranted(role, account, _msgSender());
        }
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * Internal function without access restriction.
     */
    function _revokeRole(bytes32 role, address account) internal virtual {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, _msgSender());
        }
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol)

pragma solidity ^0.8.0;

import "./IAccessControlUpgradeable.sol";

/**
 * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
 */
interface IAccessControlEnumerableUpgradeable is IAccessControlUpgradeable {
    /**
     * @dev Returns one of the accounts that have `role`. `index` must be a
     * value between 0 and {getRoleMemberCount}, non-inclusive.
     *
     * Role bearers are not sorted in any particular way, and their ordering may
     * change at any point.
     *
     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
     * you perform all queries on the same block. See the following
     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
     * for more information.
     */
    function getRoleMember(bytes32 role, uint256 index) external view returns (address);

    /**
     * @dev Returns the number of accounts that have `role`. Can be used
     * together with {getRoleMember} to enumerate all bearers of a role.
     */
    function getRoleMemberCount(bytes32 role) external view returns (uint256);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)

pragma solidity ^0.8.0;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControlUpgradeable {
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}

File 6 of 50 : Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.0;

import "../../utils/AddressUpgradeable.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To initialize the implementation contract, you can either invoke the
 * initializer manually, or you can include a constructor to automatically mark it as initialized when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() initializer {}
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     */
    bool private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Modifier to protect an initializer function from being invoked twice.
     */
    modifier initializer() {
        // If the contract is initializing we ignore whether _initialized is set in order to support multiple
        // inheritance patterns, but we only do this in the context of a constructor, because in other contexts the
        // contract may have been reentered.
        require(_initializing ? _isConstructor() : !_initialized, "Initializable: contract is already initialized");

        bool isTopLevelCall = !_initializing;
        if (isTopLevelCall) {
            _initializing = true;
            _initialized = true;
        }

        _;

        if (isTopLevelCall) {
            _initializing = false;
        }
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} modifier, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    function _isConstructor() private view returns (bool) {
        return !AddressUpgradeable.isContract(address(this));
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    function __Pausable_init() internal onlyInitializing {
        __Pausable_init_unchained();
    }

    function __Pausable_init_unchained() internal onlyInitializing {
        _paused = false;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        require(!paused(), "Pausable: paused");
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        require(paused(), "Pausable: not paused");
        _;
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.0;

import "./IERC20Upgradeable.sol";
import "./extensions/IERC20MetadataUpgradeable.sol";
import "../../utils/ContextUpgradeable.sol";
import "../../proxy/utils/Initializable.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 ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable, IERC20MetadataUpgradeable {
    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.
     */
    function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing {
        __ERC20_init_unchained(name_, symbol_);
    }

    function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
        _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, _allowances[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 = _allowances[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 Spend `amount` form the allowance of `owner` toward `spender`.
     *
     * 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 {}

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[45] private __gap;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20Upgradeable {
    /**
     * @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);

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20Burnable.sol)

pragma solidity ^0.8.0;

import "../ERC20Upgradeable.sol";
import "../../../utils/ContextUpgradeable.sol";
import "../../../proxy/utils/Initializable.sol";

/**
 * @dev Extension of {ERC20} that allows token holders to destroy both their own
 * tokens and those that they have an allowance for, in a way that can be
 * recognized off-chain (via event analysis).
 */
abstract contract ERC20BurnableUpgradeable is Initializable, ContextUpgradeable, ERC20Upgradeable {
    function __ERC20Burnable_init() internal onlyInitializing {
    }

    function __ERC20Burnable_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev Destroys `amount` tokens from the caller.
     *
     * See {ERC20-_burn}.
     */
    function burn(uint256 amount) public virtual {
        _burn(_msgSender(), amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, deducting from the caller's
     * allowance.
     *
     * See {ERC20-_burn} and {ERC20-allowance}.
     *
     * Requirements:
     *
     * - the caller must have allowance for ``accounts``'s tokens of at least
     * `amount`.
     */
    function burnFrom(address account, uint256 amount) public virtual {
        _spendAllowance(account, _msgSender(), amount);
        _burn(account, amount);
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/ERC20Pausable.sol)

pragma solidity ^0.8.0;

import "../ERC20Upgradeable.sol";
import "../../../security/PausableUpgradeable.sol";
import "../../../proxy/utils/Initializable.sol";

/**
 * @dev ERC20 token with pausable token transfers, minting and burning.
 *
 * Useful for scenarios such as preventing trades until the end of an evaluation
 * period, or having an emergency switch for freezing all token transfers in the
 * event of a large bug.
 */
abstract contract ERC20PausableUpgradeable is Initializable, ERC20Upgradeable, PausableUpgradeable {
    function __ERC20Pausable_init() internal onlyInitializing {
        __Pausable_init_unchained();
    }

    function __ERC20Pausable_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {ERC20-_beforeTokenTransfer}.
     *
     * Requirements:
     *
     * - the contract must not be paused.
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual override {
        super._beforeTokenTransfer(from, to, amount);

        require(!paused(), "ERC20Pausable: token transfer while paused");
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

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

pragma solidity ^0.8.0;

import "../IERC20Upgradeable.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20MetadataUpgradeable is IERC20Upgradeable {
    /**
     * @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);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/presets/ERC20PresetMinterPauser.sol)

pragma solidity ^0.8.0;

import "../ERC20Upgradeable.sol";
import "../extensions/ERC20BurnableUpgradeable.sol";
import "../extensions/ERC20PausableUpgradeable.sol";
import "../../../access/AccessControlEnumerableUpgradeable.sol";
import "../../../utils/ContextUpgradeable.sol";
import "../../../proxy/utils/Initializable.sol";

/**
 * @dev {ERC20} token, including:
 *
 *  - ability for holders to burn (destroy) their tokens
 *  - a minter role that allows for token minting (creation)
 *  - a pauser role that allows to stop all token transfers
 *
 * This contract uses {AccessControl} to lock permissioned functions using the
 * different roles - head to its documentation for details.
 *
 * The account that deploys the contract will be granted the minter and pauser
 * roles, as well as the default admin role, which will let it grant both minter
 * and pauser roles to other accounts.
 *
 * _Deprecated in favor of https://wizard.openzeppelin.com/[Contracts Wizard]._
 */
contract ERC20PresetMinterPauserUpgradeable is Initializable, ContextUpgradeable, AccessControlEnumerableUpgradeable, ERC20BurnableUpgradeable, ERC20PausableUpgradeable {
    function initialize(string memory name, string memory symbol) public virtual initializer {
        __ERC20PresetMinterPauser_init(name, symbol);
    }
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");

    /**
     * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` and `PAUSER_ROLE` to the
     * account that deploys the contract.
     *
     * See {ERC20-constructor}.
     */
    function __ERC20PresetMinterPauser_init(string memory name, string memory symbol) internal onlyInitializing {
        __ERC20_init_unchained(name, symbol);
        __Pausable_init_unchained();
        __ERC20PresetMinterPauser_init_unchained(name, symbol);
    }

    function __ERC20PresetMinterPauser_init_unchained(string memory, string memory) internal onlyInitializing {
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());

        _setupRole(MINTER_ROLE, _msgSender());
        _setupRole(PAUSER_ROLE, _msgSender());
    }

    /**
     * @dev Creates `amount` new tokens for `to`.
     *
     * See {ERC20-_mint}.
     *
     * Requirements:
     *
     * - the caller must have the `MINTER_ROLE`.
     */
    function mint(address to, uint256 amount) public virtual {
        require(hasRole(MINTER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have minter role to mint");
        _mint(to, amount);
    }

    /**
     * @dev Pauses all token transfers.
     *
     * See {ERC20Pausable} and {Pausable-_pause}.
     *
     * Requirements:
     *
     * - the caller must have the `PAUSER_ROLE`.
     */
    function pause() public virtual {
        require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have pauser role to pause");
        _pause();
    }

    /**
     * @dev Unpauses all token transfers.
     *
     * See {ERC20Pausable} and {Pausable-_unpause}.
     *
     * Requirements:
     *
     * - the caller must have the `PAUSER_ROLE`.
     */
    function unpause() public virtual {
        require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have pauser role to unpause");
        _unpause();
    }

    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual override(ERC20Upgradeable, ERC20PausableUpgradeable) {
        super._beforeTokenTransfer(from, to, amount);
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library AddressUpgradeable {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

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

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

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

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.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 ContextUpgradeable is Initializable {
    function __Context_init() internal onlyInitializing {
    }

    function __Context_init_unchained() internal onlyInitializing {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

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

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

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

pragma solidity ^0.8.0;

/**
 * @dev String operations.
 */
library StringsUpgradeable {
    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);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165Upgradeable.sol";
import "../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {
    function __ERC165_init() internal onlyInitializing {
    }

    function __ERC165_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165Upgradeable).interfaceId;
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 */
library EnumerableSetUpgradeable {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            if (lastIndex != toDeleteIndex) {
                bytes32 lastvalue = set._values[lastIndex];

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastvalue;
                // Update the index for the moved value
                set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        return _values(set._inner);
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        assembly {
            result := store
        }

        return result;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControl.sol)

pragma solidity ^0.8.0;

import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping(address => bool) members;
        bytes32 adminRole;
    }

    mapping(bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with a standardized message including the required role.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     *
     * _Available since v4.1._
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role, _msgSender());
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
        return _roles[role].members[account];
    }

    /**
     * @dev Revert with a standard message if `account` is missing `role`.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert(
                string(
                    abi.encodePacked(
                        "AccessControl: account ",
                        Strings.toHexString(uint160(account), 20),
                        " is missing role ",
                        Strings.toHexString(uint256(role), 32)
                    )
                )
            );
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) public virtual override {
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");

        _revokeRole(role, account);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     *
     * NOTE: This function is deprecated in favor of {_grantRole}.
     */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * Internal function without access restriction.
     */
    function _grantRole(bytes32 role, address account) internal virtual {
        if (!hasRole(role, account)) {
            _roles[role].members[account] = true;
            emit RoleGranted(role, account, _msgSender());
        }
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * Internal function without access restriction.
     */
    function _revokeRole(bytes32 role, address account) internal virtual {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, _msgSender());
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)

pragma solidity ^0.8.0;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/Clones.sol)

pragma solidity ^0.8.0;

/**
 * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
 * deploying minimal proxy contracts, also known as "clones".
 *
 * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
 * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
 *
 * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
 * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
 * deterministic method.
 *
 * _Available since v3.4._
 */
library Clones {
    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create opcode, which should never revert.
     */
    function clone(address implementation) internal returns (address instance) {
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            instance := create(0, ptr, 0x37)
        }
        require(instance != address(0), "ERC1167: create failed");
    }

    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create2 opcode and a `salt` to deterministically deploy
     * the clone. Using the same `implementation` and `salt` multiple time will revert, since
     * the clones cannot be deployed twice at the same address.
     */
    function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            instance := create2(0, ptr, 0x37, salt)
        }
        require(instance != address(0), "ERC1167: create2 failed");
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000)
            mstore(add(ptr, 0x38), shl(0x60, deployer))
            mstore(add(ptr, 0x4c), salt)
            mstore(add(ptr, 0x6c), keccak256(ptr, 0x37))
            predicted := keccak256(add(ptr, 0x37), 0x55)
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(address implementation, bytes32 salt)
        internal
        view
        returns (address predicted)
    {
        return predictDeterministicAddress(implementation, salt, address(this));
    }
}

File 23 of 50 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * 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;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/ERC721.sol)

pragma solidity ^0.8.0;

import "./IERC721.sol";
import "./IERC721Receiver.sol";
import "./extensions/IERC721Metadata.sol";
import "../../utils/Address.sol";
import "../../utils/Context.sol";
import "../../utils/Strings.sol";
import "../../utils/introspection/ERC165.sol";

/**
 * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
 * the Metadata extension, but not including the Enumerable extension, which is available separately as
 * {ERC721Enumerable}.
 */
contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
    using Address for address;
    using Strings for uint256;

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    // Mapping from token ID to owner address
    mapping(uint256 => address) private _owners;

    // Mapping owner address to token count
    mapping(address => uint256) private _balances;

    // Mapping from token ID to approved address
    mapping(uint256 => address) private _tokenApprovals;

    // Mapping from owner to operator approvals
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    /**
     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return
            interfaceId == type(IERC721).interfaceId ||
            interfaceId == type(IERC721Metadata).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC721-balanceOf}.
     */
    function balanceOf(address owner) public view virtual override returns (uint256) {
        require(owner != address(0), "ERC721: balance query for the zero address");
        return _balances[owner];
    }

    /**
     * @dev See {IERC721-ownerOf}.
     */
    function ownerOf(uint256 tokenId) public view virtual override returns (address) {
        address owner = _owners[tokenId];
        require(owner != address(0), "ERC721: owner query for nonexistent token");
        return owner;
    }

    /**
     * @dev See {IERC721Metadata-name}.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev See {IERC721Metadata-symbol}.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");

        string memory baseURI = _baseURI();
        return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
    }

    /**
     * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
     * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
     * by default, can be overriden in child contracts.
     */
    function _baseURI() internal view virtual returns (string memory) {
        return "";
    }

    /**
     * @dev See {IERC721-approve}.
     */
    function approve(address to, uint256 tokenId) public virtual override {
        address owner = ERC721.ownerOf(tokenId);
        require(to != owner, "ERC721: approval to current owner");

        require(
            _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
            "ERC721: approve caller is not owner nor approved for all"
        );

        _approve(to, tokenId);
    }

    /**
     * @dev See {IERC721-getApproved}.
     */
    function getApproved(uint256 tokenId) public view virtual override returns (address) {
        require(_exists(tokenId), "ERC721: approved query for nonexistent token");

        return _tokenApprovals[tokenId];
    }

    /**
     * @dev See {IERC721-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual override {
        _setApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @dev See {IERC721-isApprovedForAll}.
     */
    function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
        return _operatorApprovals[owner][operator];
    }

    /**
     * @dev See {IERC721-transferFrom}.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        //solhint-disable-next-line max-line-length
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");

        _transfer(from, to, tokenId);
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) public virtual override {
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
        _safeTransfer(from, to, tokenId, _data);
    }

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * `_data` is additional data, it has no specified format and it is sent in call to `to`.
     *
     * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
     * implement alternative mechanisms to perform token transfer, such as signature-based.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeTransfer(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) internal virtual {
        _transfer(from, to, tokenId);
        require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
    }

    /**
     * @dev Returns whether `tokenId` exists.
     *
     * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
     *
     * Tokens start existing when they are minted (`_mint`),
     * and stop existing when they are burned (`_burn`).
     */
    function _exists(uint256 tokenId) internal view virtual returns (bool) {
        return _owners[tokenId] != address(0);
    }

    /**
     * @dev Returns whether `spender` is allowed to manage `tokenId`.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
        require(_exists(tokenId), "ERC721: operator query for nonexistent token");
        address owner = ERC721.ownerOf(tokenId);
        return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
    }

    /**
     * @dev Safely mints `tokenId` and transfers it to `to`.
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeMint(address to, uint256 tokenId) internal virtual {
        _safeMint(to, tokenId, "");
    }

    /**
     * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
     */
    function _safeMint(
        address to,
        uint256 tokenId,
        bytes memory _data
    ) internal virtual {
        _mint(to, tokenId);
        require(
            _checkOnERC721Received(address(0), to, tokenId, _data),
            "ERC721: transfer to non ERC721Receiver implementer"
        );
    }

    /**
     * @dev Mints `tokenId` and transfers it to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - `to` cannot be the zero address.
     *
     * Emits a {Transfer} event.
     */
    function _mint(address to, uint256 tokenId) internal virtual {
        require(to != address(0), "ERC721: mint to the zero address");
        require(!_exists(tokenId), "ERC721: token already minted");

        _beforeTokenTransfer(address(0), to, tokenId);

        _balances[to] += 1;
        _owners[tokenId] = to;

        emit Transfer(address(0), to, tokenId);

        _afterTokenTransfer(address(0), to, tokenId);
    }

    /**
     * @dev Destroys `tokenId`.
     * The approval is cleared when the token is burned.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     *
     * Emits a {Transfer} event.
     */
    function _burn(uint256 tokenId) internal virtual {
        address owner = ERC721.ownerOf(tokenId);

        _beforeTokenTransfer(owner, address(0), tokenId);

        // Clear approvals
        _approve(address(0), tokenId);

        _balances[owner] -= 1;
        delete _owners[tokenId];

        emit Transfer(owner, address(0), tokenId);

        _afterTokenTransfer(owner, address(0), tokenId);
    }

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     *
     * Emits a {Transfer} event.
     */
    function _transfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {
        require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
        require(to != address(0), "ERC721: transfer to the zero address");

        _beforeTokenTransfer(from, to, tokenId);

        // Clear approvals from the previous owner
        _approve(address(0), tokenId);

        _balances[from] -= 1;
        _balances[to] += 1;
        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);

        _afterTokenTransfer(from, to, tokenId);
    }

    /**
     * @dev Approve `to` to operate on `tokenId`
     *
     * Emits a {Approval} event.
     */
    function _approve(address to, uint256 tokenId) internal virtual {
        _tokenApprovals[tokenId] = to;
        emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
    }

    /**
     * @dev Approve `operator` to operate on all of `owner` tokens
     *
     * Emits a {ApprovalForAll} event.
     */
    function _setApprovalForAll(
        address owner,
        address operator,
        bool approved
    ) internal virtual {
        require(owner != operator, "ERC721: approve to caller");
        _operatorApprovals[owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }

    /**
     * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
     * The call is not executed if the target address is not a contract.
     *
     * @param from address representing the previous owner of the given token ID
     * @param to target address that will receive the tokens
     * @param tokenId uint256 ID of the token to be transferred
     * @param _data bytes optional data to send along with the call
     * @return bool whether the call correctly returned the expected magic value
     */
    function _checkOnERC721Received(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) private returns (bool) {
        if (to.isContract()) {
            try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) {
                return retval == IERC721Receiver.onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert("ERC721: transfer to non ERC721Receiver implementer");
                } else {
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        } else {
            return true;
        }
    }

    /**
     * @dev Hook that is called before any token transfer. This includes minting
     * and burning.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
     * transferred to `to`.
     * - When `from` is zero, `tokenId` will be minted for `to`.
     * - When `to` is zero, ``from``'s `tokenId` 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 tokenId
    ) 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.
     * - `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 tokenId
    ) internal virtual {}
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

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

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool _approved) external;

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external;
}

File 26 of 50 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

File 27 of 50 : ERC721Burnable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/ERC721Burnable.sol)

pragma solidity ^0.8.0;

import "../ERC721.sol";
import "../../../utils/Context.sol";

/**
 * @title ERC721 Burnable Token
 * @dev ERC721 Token that can be irreversibly burned (destroyed).
 */
abstract contract ERC721Burnable is Context, ERC721 {
    /**
     * @dev Burns `tokenId`. See {ERC721-_burn}.
     *
     * Requirements:
     *
     * - The caller must own `tokenId` or be an approved operator.
     */
    function burn(uint256 tokenId) public virtual {
        //solhint-disable-next-line max-line-length
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721Burnable: caller is not owner nor approved");
        _burn(tokenId);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/ERC721Enumerable.sol)

pragma solidity ^0.8.0;

import "../ERC721.sol";
import "./IERC721Enumerable.sol";

/**
 * @dev This implements an optional extension of {ERC721} defined in the EIP that adds
 * enumerability of all the token ids in the contract as well as all token ids owned by each
 * account.
 */
abstract contract ERC721Enumerable is ERC721, IERC721Enumerable {
    // Mapping from owner to list of owned token IDs
    mapping(address => mapping(uint256 => uint256)) private _ownedTokens;

    // Mapping from token ID to index of the owner tokens list
    mapping(uint256 => uint256) private _ownedTokensIndex;

    // Array with all token ids, used for enumeration
    uint256[] private _allTokens;

    // Mapping from token id to position in the allTokens array
    mapping(uint256 => uint256) private _allTokensIndex;

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) {
        return interfaceId == type(IERC721Enumerable).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
     */
    function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) {
        require(index < ERC721.balanceOf(owner), "ERC721Enumerable: owner index out of bounds");
        return _ownedTokens[owner][index];
    }

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

    /**
     * @dev See {IERC721Enumerable-tokenByIndex}.
     */
    function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
        require(index < ERC721Enumerable.totalSupply(), "ERC721Enumerable: global index out of bounds");
        return _allTokens[index];
    }

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

        if (from == address(0)) {
            _addTokenToAllTokensEnumeration(tokenId);
        } else if (from != to) {
            _removeTokenFromOwnerEnumeration(from, tokenId);
        }
        if (to == address(0)) {
            _removeTokenFromAllTokensEnumeration(tokenId);
        } else if (to != from) {
            _addTokenToOwnerEnumeration(to, tokenId);
        }
    }

    /**
     * @dev Private function to add a token to this extension's ownership-tracking data structures.
     * @param to address representing the new owner of the given token ID
     * @param tokenId uint256 ID of the token to be added to the tokens list of the given address
     */
    function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
        uint256 length = ERC721.balanceOf(to);
        _ownedTokens[to][length] = tokenId;
        _ownedTokensIndex[tokenId] = length;
    }

    /**
     * @dev Private function to add a token to this extension's token tracking data structures.
     * @param tokenId uint256 ID of the token to be added to the tokens list
     */
    function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
        _allTokensIndex[tokenId] = _allTokens.length;
        _allTokens.push(tokenId);
    }

    /**
     * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
     * while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for
     * gas optimizations e.g. when performing a transfer operation (avoiding double writes).
     * This has O(1) time complexity, but alters the order of the _ownedTokens array.
     * @param from address representing the previous owner of the given token ID
     * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
     */
    function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
        // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
        // then delete the last slot (swap and pop).

        uint256 lastTokenIndex = ERC721.balanceOf(from) - 1;
        uint256 tokenIndex = _ownedTokensIndex[tokenId];

        // When the token to delete is the last token, the swap operation is unnecessary
        if (tokenIndex != lastTokenIndex) {
            uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];

            _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
            _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
        }

        // This also deletes the contents at the last position of the array
        delete _ownedTokensIndex[tokenId];
        delete _ownedTokens[from][lastTokenIndex];
    }

    /**
     * @dev Private function to remove a token from this extension's token tracking data structures.
     * This has O(1) time complexity, but alters the order of the _allTokens array.
     * @param tokenId uint256 ID of the token to be removed from the tokens list
     */
    function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
        // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and
        // then delete the last slot (swap and pop).

        uint256 lastTokenIndex = _allTokens.length - 1;
        uint256 tokenIndex = _allTokensIndex[tokenId];

        // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so
        // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding
        // an 'if' statement (like in _removeTokenFromOwnerEnumeration)
        uint256 lastTokenId = _allTokens[lastTokenIndex];

        _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
        _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index

        // This also deletes the contents at the last position of the array
        delete _allTokensIndex[tokenId];
        _allTokens.pop();
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol)

pragma solidity ^0.8.0;

import "../IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Enumerable is IERC721 {
    /**
     * @dev Returns the total amount of tokens stored by the contract.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns a token ID owned by `owner` at a given `index` of its token list.
     * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
     */
    function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);

    /**
     * @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
     * Use along with {totalSupply} to enumerate all tokens.
     */
    function tokenByIndex(uint256 index) external view returns (uint256);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Metadata is IERC721 {
    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

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

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

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

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

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

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with 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;
    }
}

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

pragma solidity ^0.8.0;

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

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

pragma solidity ^0.8.0;

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol)

pragma solidity ^0.8.0;

// CAUTION
// This version of SafeMath should only be used with Solidity 0.8 or later,
// because it relies on the compiler's built in overflow checks.

/**
 * @dev Wrappers over Solidity's arithmetic operations.
 *
 * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
 * now has built in overflow checking.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the substraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        return a + b;
    }

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

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        return a * b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator.
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return a / b;
    }

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

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b <= a, errorMessage);
            return a - b;
        }
    }

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

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a % b;
        }
    }
}

//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import {IERC721Enumerable} from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol";
import {IERC20} from "../external/interfaces/IERC20.sol";

/// @title SSOV V3 interface
interface ISsovV3 is IERC721Enumerable {
    struct Addresses {
        address feeStrategy;
        address stakingStrategy;
        address optionPricing;
        address priceOracle;
        address volatilityOracle;
        address feeDistributor;
        address optionsTokenImplementation;
    }

    struct EpochData {
        bool expired;
        uint256 startTime;
        uint256 expiry;
        uint256 settlementPrice;
        uint256 totalCollateralBalance; // Premium + Deposits from all strikes
        uint256 collateralExchangeRate; // Exchange rate for collateral to underlying
        uint256[] strikes;
        uint256[] totalRewardsCollected;
        uint256[] rewardDistributionRatios;
        address[] rewardTokensToDistribute;
    }

    struct EpochStrikeData {
        address strikeToken;
        uint256 totalCollateral;
        uint256 activeCollateral;
        uint256 totalPremiums;
        uint256 checkpointPointer;
        uint256[] rewardStoredForPremiums;
        uint256[] rewardDistributionRatiosForPremiums;
    }

    struct VaultCheckpoint {
        uint256 startTime;
        uint256 activeCollateral;
        uint256 totalCollateral;
        uint256 accruedPremium;
    }

    function isPut() external view returns (bool);

    function currentEpoch() external view returns (uint256);

    function collateralPrecision() external view returns (uint256);

    function addresses() external view returns (Addresses memory);

    function collateralToken() external view returns (IERC20);

    function getCheckpoints(uint256 epoch, uint256 strike)
        external
        view
        returns (VaultCheckpoint[] memory);

    function deposit(
        uint256 strikeIndex,
        uint256 amount,
        address to
    ) external returns (uint256 tokenId);

    function purchase(
        uint256 strikeIndex,
        uint256 amount,
        address to
    ) external returns (uint256 premium, uint256 totalFee);

    function settle(
        uint256 strikeIndex,
        uint256 amount,
        uint256 epoch,
        address to
    ) external returns (uint256 pnl);

    function withdraw(uint256 tokenId, address to)
        external
        returns (
            uint256 collateralTokenWithdrawAmount,
            uint256[] memory rewardTokenWithdrawAmounts
        );

    function getUnderlyingPrice() external view returns (uint256);

    function getCollateralPrice() external view returns (uint256);

    function getVolatility(uint256 _strike) external view returns (uint256);

    function calculatePremium(
        uint256 _strike,
        uint256 _amount,
        uint256 _expiry
    ) external view returns (uint256 premium);

    function calculatePnl(
        uint256 price,
        uint256 strike,
        uint256 amount
    ) external view returns (uint256);

    function calculatePurchaseFees(uint256 strike, uint256 amount)
        external
        view
        returns (uint256);

    function calculateSettlementFees(uint256 pnl)
        external
        view
        returns (uint256);

    function getEpochTimes(uint256 epoch)
        external
        view
        returns (uint256 start, uint256 end);

    function getEpochStrikes(uint256 epoch)
        external
        view
        returns (uint256[] memory);

    function writePosition(uint256 tokenId)
        external
        view
        returns (
            uint256 epoch,
            uint256 strike,
            uint256 collateralAmount,
            uint256 checkpointIndex,
            uint256[] memory rewardDistributionRatios
        );

    function getEpochData(uint256 epoch)
        external
        view
        returns (EpochData memory);

    function getEpochStrikeData(uint256 epoch, uint256 strike)
        external
        view
        returns (EpochStrikeData memory);
}

File 39 of 50 : SsovV3.sol
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

// Libraries
import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
import {SafeERC20} from "../external/libraries/SafeERC20.sol";

// Contracts
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import {ERC721Enumerable} from "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import {ERC721Burnable} from "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol";
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
import {Counters} from "@openzeppelin/contracts/utils/Counters.sol";
import {SsovV3State} from "./SsovV3State.sol";
import {SsovV3OptionsToken} from "../options-token/SsovV3OptionsToken.sol";
import {ContractWhitelist} from "../helpers/ContractWhitelist.sol";
import {Pausable} from "../helpers/Pausable.sol";

// Interfaces
import {IERC20} from "../external/interfaces/IERC20.sol";
import {IOptionPricing} from "../interfaces/IOptionPricing.sol";
import {IFeeStrategy} from "../interfaces/IFeeStrategy.sol";
import {IStakingStrategy} from "../staking-strategies/IStakingStrategy.sol";
import {IPriceOracle} from "../interfaces/IPriceOracle.sol";
import {IVolatilityOracle} from "../interfaces/IVolatilityOracle.sol";

/// @title SSOV V3 contract
/// @dev Option tokens are in erc20 18 decimals & Strikes are in 1e8 precision
contract SsovV3 is
    ReentrancyGuard,
    Pausable,
    ERC721,
    ERC721Enumerable,
    ERC721Burnable,
    AccessControl,
    ContractWhitelist,
    SsovV3State
{
    using SafeERC20 for IERC20;
    using Counters for Counters.Counter;

    /// @dev Token ID counter for write positions
    Counters.Counter private _tokenIdCounter;

    /*==== CONSTRUCTOR ====*/

    constructor(
        string memory _name,
        string memory _symbol,
        string memory _underlyingSymbol,
        address _collateralToken,
        bool _isPut
    ) ERC721(_name, _symbol) {
        if (_collateralToken == address(0)) revert E1();

        underlyingSymbol = _underlyingSymbol;
        collateralToken = IERC20(_collateralToken);
        collateralPrecision = 10**collateralToken.decimals();
        isPut = _isPut;

        _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _setupRole(MANAGER_ROLE, msg.sender);
    }

    /*==== METHODS ====*/

    /// @notice Pauses the vault for emergency cases
    /// @dev Can only be called by the owner
    function pause() external onlyRole(DEFAULT_ADMIN_ROLE) {
        _pause();
        _updateFinalEpochBalances();
    }

    /// @notice Unpauses the vault
    /// @dev Can only be called by the owner
    function unpause() external onlyRole(DEFAULT_ADMIN_ROLE) {
        _unpause();
    }

    /// @notice Add a contract to the whitelist
    /// @dev Can only be called by the owner
    /// @param _contract Address of the contract that needs to be added to the whitelist
    function addToContractWhitelist(address _contract)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        _addToContractWhitelist(_contract);
    }

    /// @notice Remove a contract to the whitelist
    /// @dev Can only be called by the owner
    /// @param _contract Address of the contract that needs to be removed from the whitelist
    function removeFromContractWhitelist(address _contract)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        _removeFromContractWhitelist(_contract);
    }

    /// @notice Updates the delay tolerance for the expiry epoch function
    /// @dev Can only be called by the owner
    function updateExpireDelayTolerance(uint256 _expireDelayTolerance)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        expireDelayTolerance = _expireDelayTolerance;
        emit ExpireDelayToleranceUpdate(_expireDelayTolerance);
    }

    /// @notice Updates the checkpoint interval time
    /// @dev Can only be called by the owner
    function updateCheckpointIntervalTime(uint256 _checkpointIntervalTime)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        checkpointIntervalTime = _checkpointIntervalTime;
        emit CheckpointIntervalTimeUpdate(_checkpointIntervalTime);
    }

    /// @notice Sets (adds) a list of addresses to the address list
    /// @dev Can only be called by the owner
    /// @param _addresses addresses of contracts in the Addresses struct
    function setAddresses(Addresses calldata _addresses)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        addresses = _addresses;
        emit AddressesSet(_addresses);
    }

    /// @notice Change the collateral token allowance to the StakingStrategy contract
    /// @dev Can only be called by the owner
    /// @param _increase bool
    /// @param _allowance uint256
    function changeAllowanceForStakingStrategy(
        bool _increase,
        uint256 _allowance
    ) external onlyRole(DEFAULT_ADMIN_ROLE) {
        if (_increase) {
            collateralToken.safeIncreaseAllowance(
                addresses.stakingStrategy,
                _allowance
            );
        } else {
            collateralToken.safeDecreaseAllowance(
                addresses.stakingStrategy,
                _allowance
            );
        }
    }

    /// @notice Transfers all funds to msg.sender
    /// @dev Can only be called by the owner
    /// @param tokens The list of erc20 tokens to withdraw
    /// @param transferNative Whether should transfer the native currency
    function emergencyWithdraw(address[] calldata tokens, bool transferNative)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        _whenPaused();
        if (transferNative) payable(msg.sender).transfer(address(this).balance);

        IERC20 token;

        for (uint256 i = 0; i < tokens.length; i++) {
            token = IERC20(tokens[i]);
            token.safeTransfer(msg.sender, token.balanceOf(address(this)));
        }

        emit EmergencyWithdraw(msg.sender);
    }

    /// @dev Internal function to expire an epoch
    /// @param _settlementPrice the settlement price
    function _expire(uint256 _settlementPrice) private nonReentrant {
        _whenNotPaused();
        _epochNotExpired(currentEpoch);
        epochData[currentEpoch].settlementPrice = _settlementPrice;

        _updateFinalEpochBalances();

        epochData[currentEpoch].expired = true;

        emit EpochExpired(msg.sender, _settlementPrice);
    }

    /// @notice Sets the current epoch as expired
    function expire() external {
        _isEligibleSender();
        (, uint256 epochExpiry) = getEpochTimes(currentEpoch);
        if (block.timestamp < epochExpiry) revert E4();
        if (block.timestamp > epochExpiry + expireDelayTolerance) revert E13();
        _expire(getUnderlyingPrice());
    }

    /// @notice Sets the current epoch as expired
    /// @dev Can only be called by the owner
    /// @param _settlementPrice The settlement price
    function expire(uint256 _settlementPrice)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        _epochNotExpired(currentEpoch);
        _expire(_settlementPrice);
    }

    /// @dev Internal function to unstake collateral, gather yield and checkpoint each strike
    function _updateFinalEpochBalances() private {
        uint256[] memory strikes = getEpochStrikes(currentEpoch);

        uint256[] memory rewardTokenAmounts = IStakingStrategy(
            addresses.stakingStrategy
        ).unstake();

        for (uint256 i = 0; i < strikes.length; i++) {
            _updateRewards(strikes[i], rewardTokenAmounts, true);
        }
    }

    /// @notice Bootstraps a new epoch with new strikes
    /// @dev Can only be called by the owner
    /// @param strikes the strikes for the epoch
    /// @param expiry the expiry for the epoch
    /// @param expirySymbol the expiry symbol for the epoch
    function bootstrap(
        uint256[] memory strikes,
        uint256 expiry,
        string memory expirySymbol
    ) external nonReentrant onlyRole(MANAGER_ROLE) {
        _whenNotPaused();
        uint256 nextEpoch = currentEpoch + 1;
        if (block.timestamp > expiry) revert E2();
        if (currentEpoch > 0 && !epochData[currentEpoch].expired) revert E6();

        // Set the next epoch strikes
        epochData[nextEpoch].strikes = strikes;

        // Set the next epoch start time
        epochData[nextEpoch].startTime = block.timestamp;

        // Set the next epochs expiry
        epochData[nextEpoch].expiry = expiry;

        // Increase the current epoch
        currentEpoch = nextEpoch;

        uint256 rewardTokensLength = IStakingStrategy(addresses.stakingStrategy)
            .getRewardTokens()
            .length;

        uint256 strike;

        SsovV3OptionsToken _optionsToken;

        for (uint256 i = 0; i < strikes.length; i++) {
            strike = strikes[i];
            // Create options tokens representing the option for selected strike in epoch
            _optionsToken = SsovV3OptionsToken(
                Clones.clone(addresses.optionsTokenImplementation)
            );
            _optionsToken.initialize(
                address(this),
                isPut,
                strike,
                expiry,
                underlyingSymbol,
                collateralToken.symbol(),
                expirySymbol
            );
            epochStrikeData[nextEpoch][strike].strikeToken = address(
                _optionsToken
            );

            epochStrikeData[nextEpoch][strike]
                .rewardStoredForPremiums = new uint256[](rewardTokensLength);
            epochStrikeData[nextEpoch][strike]
                .rewardDistributionRatiosForPremiums = new uint256[](
                rewardTokensLength
            );
        }
        epochData[nextEpoch].totalRewardsCollected = new uint256[](
            rewardTokensLength
        );
        epochData[nextEpoch].rewardDistributionRatios = new uint256[](
            rewardTokensLength
        );
        epochData[nextEpoch].rewardTokensToDistribute = IStakingStrategy(
            addresses.stakingStrategy
        ).getRewardTokens();
        epochData[nextEpoch].collateralExchangeRate =
            (getUnderlyingPrice() * 1e8) /
            getCollateralPrice();

        emit Bootstrap(nextEpoch, strikes);
    }

    /// @dev Internal function to update the total collateral by adding the collateral from the last checkpoint if interval is completed
    /// @param _strike strike
    function _updateTotalCollateral(uint256 _strike) private {
        uint256 _epoch = currentEpoch;

        VaultCheckpoint memory _checkpoint = checkpoints[_epoch][_strike][
            checkpoints[_epoch][_strike].length - 1
        ];

        if (_checkpoint.startTime + checkpointIntervalTime < block.timestamp) {
            epochStrikeData[_epoch][_strike].totalCollateral += _checkpoint
                .totalCollateral;
            checkpoints[_epoch][_strike].push(
                VaultCheckpoint({
                    startTime: block.timestamp,
                    accruedPremium: 0,
                    activeCollateral: 0,
                    totalCollateral: 0
                })
            );
        }
    }

    /// @dev Internal function to checkpoint the vault for an epoch and strike
    /// @param strike strike
    /// @param collateralAdded collateral added
    function _vaultCheckpoint(uint256 strike, uint256 collateralAdded)
        private
        returns (uint256)
    {
        uint256 _epoch = currentEpoch;

        if (checkpoints[_epoch][strike].length == 0) {
            checkpoints[_epoch][strike].push(
                VaultCheckpoint({
                    startTime: block.timestamp,
                    accruedPremium: 0,
                    activeCollateral: 0,
                    totalCollateral: collateralAdded
                })
            );
            return (checkpoints[_epoch][strike].length - 1);
        }

        _updateTotalCollateral(strike);

        VaultCheckpoint memory _checkpoint = checkpoints[_epoch][strike][
            checkpoints[_epoch][strike].length - 1
        ];

        checkpoints[_epoch][strike][
            checkpoints[_epoch][strike].length - 1
        ] = VaultCheckpoint({
            startTime: _checkpoint.startTime,
            accruedPremium: _checkpoint.accruedPremium,
            activeCollateral: _checkpoint.activeCollateral,
            totalCollateral: _checkpoint.totalCollateral + collateralAdded
        });

        return (checkpoints[_epoch][strike].length - 1);
    }

    /// @dev Internal function to squeeze collateral from n checkpoints to fullfil a purchase
    /// @param _strike strike
    /// @param _requiredCollateral required collateral to fullfil purchase
    /// @param _premium premium awarded
    function _squeeze(
        uint256 _strike,
        uint256 _requiredCollateral,
        uint256 _premium
    ) private {
        VaultCheckpoint memory _checkpoint;

        uint256 _epoch = currentEpoch;
        uint256 _acquiredCollateral;
        uint256 _availableCollateral;
        uint256 _remainingRequiredCollateral;
        uint256 _premiumPerCollateral = (_premium * 1e18) / _requiredCollateral;
        uint256 _checkpointPointer = epochStrikeData[_epoch][_strike]
            .checkpointPointer;

        while (_acquiredCollateral < _requiredCollateral) {
            _checkpoint = checkpoints[_epoch][_strike][_checkpointPointer];

            _remainingRequiredCollateral =
                _requiredCollateral -
                _acquiredCollateral;

            _availableCollateral =
                _checkpoint.totalCollateral -
                _checkpoint.activeCollateral;

            if (_availableCollateral >= _remainingRequiredCollateral) {
                _acquiredCollateral += _remainingRequiredCollateral;
                checkpoints[_epoch][_strike][_checkpointPointer]
                    .activeCollateral += _remainingRequiredCollateral;
                checkpoints[_epoch][_strike][_checkpointPointer]
                    .accruedPremium +=
                    (_remainingRequiredCollateral * _premiumPerCollateral) /
                    1e18;
            } else {
                _acquiredCollateral += _availableCollateral;
                checkpoints[_epoch][_strike][_checkpointPointer]
                    .activeCollateral += _availableCollateral;
                checkpoints[_epoch][_strike][_checkpointPointer]
                    .accruedPremium +=
                    (_availableCollateral * _premiumPerCollateral) /
                    1e18;
                _checkpointPointer += 1;
            }
        }
    }

    /// @dev Internal function to mint a write position token
    /// @param to the address to mint the position to
    function _mintPositionToken(address to) private returns (uint256 tokenId) {
        tokenId = _tokenIdCounter.current();
        _tokenIdCounter.increment();
        _safeMint(to, tokenId);
    }

    /// @dev Calculates & updates the total rewards collected & the rewards distribution ratios
    /// @param strike the strike
    /// @param totalRewardsArray the totalRewardsArray
    /// @param isPurchase whether this was called on purchase
    function _updateRewards(
        uint256 strike,
        uint256[] memory totalRewardsArray,
        bool isPurchase
    ) private returns (uint256[] memory rewardsDistributionRatios) {
        rewardsDistributionRatios = new uint256[](totalRewardsArray.length);
        uint256 newRewardsCollected;

        for (uint256 i = 0; i < totalRewardsArray.length; i++) {
            // Calculate the new rewards accrued
            newRewardsCollected =
                totalRewardsArray[i] -
                epochData[currentEpoch].totalRewardsCollected[i];

            // Update the new total rewards accrued
            epochData[currentEpoch].totalRewardsCollected[
                i
            ] = totalRewardsArray[i];

            // Calculate the reward distribution ratios for the new rewards accrued
            if (epochData[currentEpoch].totalCollateralBalance == 0) {
                rewardsDistributionRatios[i] = 0;
            } else {
                rewardsDistributionRatios[i] =
                    (newRewardsCollected * 1e18) /
                    epochData[currentEpoch].totalCollateralBalance;
            }

            // Add it to the current reward distribution ratios
            epochData[currentEpoch].rewardDistributionRatios[
                    i
                ] += rewardsDistributionRatios[i];

            if (isPurchase) {
                // Add the new rewards accrued for the premiums staked until now
                epochStrikeData[currentEpoch][strike].rewardStoredForPremiums[
                        i
                    ] +=
                    ((epochData[currentEpoch].rewardDistributionRatios[i] -
                        epochStrikeData[currentEpoch][strike]
                            .rewardDistributionRatiosForPremiums[i]) *
                        epochStrikeData[currentEpoch][strike].totalPremiums) /
                    1e18;
                // Update the reward distribution ratios for the strike
                epochStrikeData[currentEpoch][strike]
                    .rewardDistributionRatiosForPremiums[i] = epochData[
                    currentEpoch
                ].rewardDistributionRatios[i];
            }

            rewardsDistributionRatios[i] = epochData[currentEpoch]
                .rewardDistributionRatios[i];
        }
    }

    /// @notice Deposit into the ssov to mint options in the next epoch for selected strikes
    /// @param strikeIndex Index of strike
    /// @param amount Amout of collateralToken to deposit
    /// @param to Address of to send the write position to
    /// @return tokenId token id of the deposit token
    function deposit(
        uint256 strikeIndex,
        uint256 amount,
        address to
    ) public nonReentrant returns (uint256 tokenId) {
        uint256 epoch = currentEpoch;

        _whenNotPaused();
        _isEligibleSender();
        _epochNotExpired(epoch);
        _amountNotZero(amount);

        // Must be a valid strike
        uint256 strike = epochData[epoch].strikes[strikeIndex];
        _strikeNotZero(strike);

        // Transfer collateralToken from msg.sender (maybe different from user param) to ssov
        collateralToken.safeTransferFrom(msg.sender, address(this), amount);

        // Stake the collateral into the staking strategy and calculates rewards
        uint256[] memory rewardDistributionRatios = _updateRewards(
            strike,
            IStakingStrategy(addresses.stakingStrategy).stake(amount),
            false
        );

        // Checkpoint the vault
        uint256 checkpointIndex = _vaultCheckpoint(strike, amount);

        // Update the total collateral balance
        epochData[currentEpoch].totalCollateralBalance += amount;

        // Mint a write position token
        tokenId = _mintPositionToken(to);

        // Store the write position
        writePositions[tokenId] = WritePosition({
            epoch: epoch,
            strike: strike,
            collateralAmount: amount,
            checkpointIndex: checkpointIndex,
            rewardDistributionRatios: rewardDistributionRatios
        });

        emit Deposit(tokenId, to, msg.sender);
    }

    /// @notice Purchases options for the current epoch
    /// @param strikeIndex Strike index for current epoch
    /// @param amount Amount of options to purchase
    /// @param to address to send the purchased options to
    function purchase(
        uint256 strikeIndex,
        uint256 amount,
        address to
    ) external nonReentrant returns (uint256 premium, uint256 totalFee) {
        _whenNotPaused();
        _isEligibleSender();
        _amountNotZero(amount);

        uint256 epoch = currentEpoch;

        // Check if expiry time is beyond block.timestamp
        (, uint256 epochExpiry) = getEpochTimes(epoch);
        if (block.timestamp >= epochExpiry) revert E3();

        uint256 strike = epochData[epoch].strikes[strikeIndex];
        _strikeNotZero(strike);

        // Update the total collateral of a strike from an older checkpoint
        _updateTotalCollateral(strike);

        // Check if vault has enough collateral to write the options
        uint256 availableCollateral = epochStrikeData[epoch][strike]
            .totalCollateral - epochStrikeData[epoch][strike].activeCollateral;
        uint256 requiredCollateral = isPut
            ? ((amount * strike * collateralPrecision) / getCollateralPrice()) /
                1e18
            : (amount *
                epochData[epoch].collateralExchangeRate *
                collateralPrecision) / (1e26); /* 1e8 is the precision for the collateralExchangeRate, 1e18 is the precision of the options token */
        if (requiredCollateral > availableCollateral) revert E14();

        // Get total premium for all options being purchased
        premium = calculatePremium(strike, amount, epochExpiry);

        // Total fee charged
        totalFee = calculatePurchaseFees(strike, amount);

        _squeeze(strike, requiredCollateral, premium);

        // Transfer premium from msg.sender (need not be same as user)
        collateralToken.safeTransferFrom(
            msg.sender,
            address(this),
            premium + totalFee
        );

        // Stake premium into the staking strategy and calculates rewards
        _updateRewards(
            strike,
            IStakingStrategy(addresses.stakingStrategy).stake(premium),
            true
        );

        // Update active collateral
        epochStrikeData[epoch][strike].activeCollateral += requiredCollateral;

        // Update total premiums
        epochStrikeData[epoch][strike].totalPremiums += premium;

        // Update the totalCollateralBalance
        epochData[epoch].totalCollateralBalance += premium;

        // Transfer fee to FeeDistributor
        collateralToken.safeTransfer(addresses.feeDistributor, totalFee);

        // Mint option tokens
        SsovV3OptionsToken(epochStrikeData[epoch][strike].strikeToken).mint(
            to,
            amount
        );

        emit Purchase(epoch, strike, amount, premium, totalFee, to, msg.sender);
    }

    /// @notice Settle calculates the PnL for the user and withdraws the PnL in the BaseToken to the user. Will also the burn the option tokens from the user.
    /// @param strikeIndex Strike index
    /// @param amount Amount of options
    /// @param to The address to transfer pnl too
    /// @return pnl
    function settle(
        uint256 strikeIndex,
        uint256 amount,
        uint256 epoch,
        address to
    ) external nonReentrant returns (uint256 pnl) {
        _whenNotPaused();
        _isEligibleSender();
        _amountNotZero(amount);
        _epochExpired(epoch);

        uint256 strike = epochData[epoch].strikes[strikeIndex];

        SsovV3OptionsToken strikeToken = SsovV3OptionsToken(
            epochStrikeData[epoch][strike].strikeToken
        );

        if (strikeToken.balanceOf(msg.sender) < amount) {
            revert E11();
        }

        // Burn option tokens from user
        strikeToken.burnFrom(msg.sender, amount);

        // Get the settlement price for the epoch
        uint256 settlementPrice = epochData[epoch].settlementPrice;

        // Calculate pnl
        pnl = calculatePnl(settlementPrice, strike, amount);

        // Total fee charged
        uint256 totalFee = calculateSettlementFees(pnl);

        if (pnl <= 0) {
            revert E10();
        }

        // Transfer fee to FeeDistributor
        collateralToken.safeTransfer(addresses.feeDistributor, totalFee);

        // Transfer PnL
        collateralToken.safeTransfer(to, pnl - totalFee);

        emit Settle(
            epoch,
            strike,
            amount,
            pnl - totalFee,
            totalFee,
            to,
            msg.sender
        );
    }

    /// @notice Withdraw from the ssov via burning a write position token
    /// @param tokenId token id of the write position
    /// @param to address to transfer collateral and rewards
    function withdraw(uint256 tokenId, address to)
        external
        nonReentrant
        returns (
            uint256 collateralTokenWithdrawAmount,
            uint256[] memory rewardTokenWithdrawAmounts
        )
    {
        _whenNotPaused();
        _isEligibleSender();

        (
            uint256 epoch,
            uint256 strike,
            uint256 collateralAmount,
            uint256 checkpointIndex,
            uint256[] memory rewardDistributionRatios
        ) = writePosition(tokenId);

        _strikeNotZero(strike);
        _epochExpired(epoch);

        // Burn the write position token
        burn(tokenId);

        // Get the checkpoint
        VaultCheckpoint memory _checkpoint = checkpoints[epoch][strike][
            checkpointIndex
        ];

        // Rewards calculations
        rewardTokenWithdrawAmounts = new uint256[](
            epochData[epoch].rewardTokensToDistribute.length
        );

        uint256 accruedPremium = (_checkpoint.accruedPremium *
            collateralAmount) / _checkpoint.totalCollateral;

        uint256 optionsWritten = isPut
            ? (_checkpoint.activeCollateral * 1e8) / strike
            : _checkpoint.activeCollateral;

        // Get the settlement price for the epoch
        uint256 settlementPrice = epochData[epoch].settlementPrice;

        // Calculate the withdrawable collateral amount
        collateralTokenWithdrawAmount =
            ((_checkpoint.totalCollateral -
                calculatePnl(settlementPrice, strike, optionsWritten)) *
                collateralAmount) /
            _checkpoint.totalCollateral;

        // Add premiums
        collateralTokenWithdrawAmount += accruedPremium;

        // Calculate and transfer rewards
        for (uint256 i = 0; i < rewardTokenWithdrawAmounts.length; i++) {
            rewardTokenWithdrawAmounts[i] +=
                ((epochData[epoch].rewardDistributionRatios[i] -
                    rewardDistributionRatios[i]) * collateralAmount) /
                1e18;
            if (epochStrikeData[epoch][strike].totalPremiums > 0)
                rewardTokenWithdrawAmounts[i] +=
                    (accruedPremium *
                        epochStrikeData[epoch][strike].rewardStoredForPremiums[
                            i
                        ]) /
                    epochStrikeData[epoch][strike].totalPremiums;
            IERC20(epochData[epoch].rewardTokensToDistribute[i]).safeTransfer(
                to,
                rewardTokenWithdrawAmounts[i]
            );
        }

        // Transfer the collateralTokenWithdrawAmount
        collateralToken.safeTransfer(to, collateralTokenWithdrawAmount);

        emit Withdraw(
            tokenId,
            collateralTokenWithdrawAmount,
            rewardTokenWithdrawAmounts,
            to,
            msg.sender
        );
    }

    /*==== VIEWS ====*/

    /// @notice Returns the price of the underlying in USD in 1e8 precision
    function getUnderlyingPrice() public view returns (uint256) {
        return IPriceOracle(addresses.priceOracle).getUnderlyingPrice();
    }

    /// @notice Returns the price of the collateral token in 1e8 precision
    /// @dev This contract assumes that this price can never decrease in ratio of the underlying price
    function getCollateralPrice() public view returns (uint256) {
        return IPriceOracle(addresses.priceOracle).getCollateralPrice();
    }

    /// @notice Returns the volatility from the volatility oracle
    /// @param _strike Strike of the option
    function getVolatility(uint256 _strike) public view returns (uint256) {
        return
            IVolatilityOracle(addresses.volatilityOracle).getVolatility(
                _strike
            );
    }

    /// @notice Calculate premium for an option
    /// @param _strike Strike price of the option
    /// @param _amount Amount of options (1e18 precision)
    /// @param _expiry Expiry of the option
    /// @return premium in collateralToken in collateral precision
    function calculatePremium(
        uint256 _strike,
        uint256 _amount,
        uint256 _expiry
    ) public view returns (uint256 premium) {
        premium = (IOptionPricing(addresses.optionPricing).getOptionPrice(
            isPut,
            _expiry,
            _strike,
            getUnderlyingPrice(),
            getVolatility(_strike)
        ) * _amount);

        premium = (premium * collateralPrecision) / getCollateralPrice() / 1e18;
    }

    /// @notice Calculate Pnl
    /// @param price price of the underlying asset
    /// @param strike strike price of the option
    /// @param amount amount of options
    function calculatePnl(
        uint256 price,
        uint256 strike,
        uint256 amount
    ) public view returns (uint256) {
        if (isPut)
            return
                strike > price
                    ? (((strike - price) * amount * collateralPrecision) /
                        getCollateralPrice()) / 1e18
                    : 0;
        return
            price > strike
                ? (((price - strike) * amount * collateralPrecision) /
                    getCollateralPrice()) / 1e18
                : 0;
    }

    /// @notice Calculate fees for purchase
    /// @param strike strike price of the BaseToken option
    /// @param amount amount of options being bought
    function calculatePurchaseFees(uint256 strike, uint256 amount)
        public
        view
        returns (uint256)
    {
        return ((IFeeStrategy(addresses.feeStrategy).calculatePurchaseFees(
            getUnderlyingPrice(),
            strike,
            amount
        ) * collateralPrecision) / getCollateralPrice());
    }

    /// @notice Calculate fees for settlement of options
    /// @param pnl total pnl
    function calculateSettlementFees(uint256 pnl)
        public
        view
        returns (uint256)
    {
        return IFeeStrategy(addresses.feeStrategy).calculateSettlementFees(pnl);
    }

    /// @notice Returns start and end times for an epoch
    /// @param epoch Target epoch
    function getEpochTimes(uint256 epoch)
        public
        view
        returns (uint256 start, uint256 end)
    {
        _epochGreaterThanZero(epoch);

        return (epochData[epoch].startTime, epochData[epoch].expiry);
    }

    /// @notice Returns the array of strikes in an epoch
    /// @param epoch the epoch for which the array of strikes need to be returned for
    function getEpochStrikes(uint256 epoch)
        public
        view
        returns (uint256[] memory)
    {
        _epochGreaterThanZero(epoch);

        return epochData[epoch].strikes;
    }

    /// @notice View a write position
    /// @param tokenId tokenId a parameter just like in doxygen (must be followed by parameter name)
    function writePosition(uint256 tokenId)
        public
        view
        returns (
            uint256 epoch,
            uint256 strike,
            uint256 collateralAmount,
            uint256 checkpointIndex,
            uint256[] memory rewardDistributionRatios
        )
    {
        WritePosition memory _writePosition = writePositions[tokenId];

        return (
            _writePosition.epoch,
            _writePosition.strike,
            _writePosition.collateralAmount,
            _writePosition.checkpointIndex,
            _writePosition.rewardDistributionRatios
        );
    }

    /// @notice Returns the data for an epoch
    /// @param epoch the epoch
    function getEpochData(uint256 epoch)
        external
        view
        returns (EpochData memory)
    {
        _epochGreaterThanZero(epoch);
        return epochData[epoch];
    }

    /// @notice Returns the checkpoints for an epoch and strike
    /// @param epoch the epoch
    /// @param strike the strike
    function getCheckpoints(uint256 epoch, uint256 strike)
        external
        view
        returns (VaultCheckpoint[] memory)
    {
        _epochGreaterThanZero(epoch);
        return checkpoints[epoch][strike];
    }

    /// @notice Returns the data for an epoch and strike
    /// @param epoch the epoch
    /// @param strike the strike
    function getEpochStrikeData(uint256 epoch, uint256 strike)
        external
        view
        returns (EpochStrikeData memory)
    {
        _epochGreaterThanZero(epoch);
        return epochStrikeData[epoch][strike];
    }

    /*==== PRIVATE FUCNTIONS FOR REVERTS ====*/

    /// @dev Internal function to check if the epoch passed is greater than 0. Revert if 0.
    /// @param _epoch the epoch
    function _epochGreaterThanZero(uint256 _epoch) private pure {
        if (_epoch <= 0) revert E9();
    }

    /// @dev Internal function to check if the epoch passed is not expired. Revert if expired.
    /// @param _epoch the epoch
    function _epochNotExpired(uint256 _epoch) private view {
        if (epochData[_epoch].expired) revert E3();
    }

    /// @dev Internal function to check if the epoch passed is expired. Revert if not expired.
    /// @param _epoch the epoch
    function _epochExpired(uint256 _epoch) private view {
        if (!epochData[_epoch].expired) revert E12();
    }

    /// @dev Internal function to check if the amount passed is not 0. Revert if 0.
    /// @param _amount the amount
    function _amountNotZero(uint256 _amount) private pure {
        if (_amount <= 0) revert E7();
    }

    /// @dev Internal function to check if the strike passed is not 0. Revert if 0.
    /// @param _strike the strike
    function _strikeNotZero(uint256 _strike) private pure {
        if (_strike <= 0) revert E8();
    }

    /*==== ERRORS ====*/

    /// @notice Address cannot be a zero address
    error E1();

    /// @notice Expiry passed must be after current block.timestamp
    error E2();

    /// @notice Epoch must not be expired
    error E3();

    /// @notice Cannot expire epoch before epoch's expiry
    error E4();

    // error E5(); This error is deprecated

    /// @notice Cannot bootstrap before current epoch's expiry
    error E6();

    /// @notice Amount must not be 0
    error E7();

    /// @notice Strike must not be 0
    error E8();

    /// @notice Epoch must be greater than 0
    error E9();

    /// @notice Pnl must be greater than 0
    error E10();

    /// @notice Option token balance is not enough
    error E11();

    /// @notice Epoch must be expired
    error E12();

    /// @notice Expire delay tolerance exceeded
    error E13();

    /// @notice Available collateral must be greater than or equal to required collateral
    error E14();

    // The following functions are overrides required by Solidity.

    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal override(ERC721, ERC721Enumerable) {
        _whenNotPaused();
        super._beforeTokenTransfer(from, to, tokenId);
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721, ERC721Enumerable, AccessControl)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }
}

File 40 of 50 : SsovV3State.sol
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import {IERC20} from "../external/interfaces/IERC20.sol";

contract SsovV3State {
    /// @dev Manager role which handles bootstrapping
    bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE");

    /// @dev Underlying assets symbol
    string public underlyingSymbol;

    /// @notice Whether this is a Put or Call SSOV
    bool public isPut;

    /// @dev Contract addresses
    Addresses public addresses;

    /// @dev Collateral Token
    IERC20 public collateralToken;

    /// @dev Current epoch for ssov
    uint256 public currentEpoch;

    /// @dev Expire delay tolerance
    uint256 public expireDelayTolerance = 5 minutes;

    /// @dev The precision of the collateral token
    uint256 public collateralPrecision;

    /// @dev Checkpoint interval time
    uint256 public checkpointIntervalTime = 2 hours;

    /// @dev Checkpoints (epoch => strike => checkpoints[])
    mapping(uint256 => mapping(uint256 => VaultCheckpoint[]))
        public checkpoints;

    /// @dev epoch => EpochData
    mapping(uint256 => EpochData) public epochData;

    /// @dev Mapping of (epoch => (strike => EpochStrikeData))
    mapping(uint256 => mapping(uint256 => EpochStrikeData))
        public epochStrikeData;

    /// @dev tokenId => WritePosition
    mapping(uint256 => WritePosition) internal writePositions;

    /*==== STRUCTS ====*/

    struct Addresses {
        address feeStrategy;
        address stakingStrategy;
        address optionPricing;
        address priceOracle;
        address volatilityOracle;
        address feeDistributor;
        address optionsTokenImplementation;
    }

    struct EpochData {
        bool expired;
        uint256 startTime;
        uint256 expiry;
        uint256 settlementPrice;
        uint256 totalCollateralBalance; // Premium + Deposits from all strikes
        uint256 collateralExchangeRate; // Exchange rate for collateral to underlying
        uint256[] strikes;
        uint256[] totalRewardsCollected;
        uint256[] rewardDistributionRatios;
        address[] rewardTokensToDistribute;
    }

    struct EpochStrikeData {
        address strikeToken;
        uint256 totalCollateral;
        uint256 activeCollateral;
        uint256 totalPremiums;
        uint256 checkpointPointer;
        uint256[] rewardStoredForPremiums;
        uint256[] rewardDistributionRatiosForPremiums;
    }

    struct VaultCheckpoint {
        uint256 startTime;
        uint256 activeCollateral;
        uint256 totalCollateral;
        uint256 accruedPremium;
    }

    struct WritePosition {
        uint256 epoch;
        uint256 strike;
        uint256 collateralAmount;
        uint256 checkpointIndex;
        uint256[] rewardDistributionRatios;
    }

    /*==== ERRORS ====*/

    event ExpireDelayToleranceUpdate(uint256 expireDelayTolerance);

    event CheckpointIntervalTimeUpdate(uint256 checkpointIntervalTime);

    event AddressesSet(Addresses addresses);

    event EmergencyWithdraw(address sender);

    event EpochExpired(address sender, uint256 settlementPrice);

    event Bootstrap(uint256 epoch, uint256[] strikes);

    event Deposit(uint256 tokenId, address indexed to, address indexed sender);

    event Purchase(
        uint256 epoch,
        uint256 strike,
        uint256 amount,
        uint256 premium,
        uint256 fee,
        address indexed to,
        address indexed sender
    );

    event Settle(
        uint256 epoch,
        uint256 strike,
        uint256 amount,
        uint256 pnl, // pnl transfered to the user
        uint256 fee, // fee sent to fee distributor
        address indexed to,
        address indexed sender
    );

    event Withdraw(
        uint256 tokenId,
        uint256 collateralTokenWithdrawn,
        uint256[] rewardTokenWithdrawAmounts,
        address indexed to,
        address indexed sender
    );
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 * NOTE: Modified to include symbols and decimals.
 */
interface IERC20 {
  function totalSupply() external view returns (uint256);

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

  function decimals() external view returns (uint8);

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

  function transfer(address recipient, uint256 amount) external returns (bool);

  function allowance(address owner, address spender)
    external
    view
    returns (uint256);

  function approve(address spender, uint256 amount) external returns (bool);

  function transferFrom(
    address sender,
    address recipient,
    uint256 amount
  ) external returns (bool);

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

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IERC20} from "../interfaces/IERC20.sol";
import {SafeMath} from "@openzeppelin/contracts/utils/math/SafeMath.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";

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

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

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

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        // solhint-disable-next-line max-line-length
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(
            token,
            abi.encodeWithSelector(token.approve.selector, spender, value)
        );
    }

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

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

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(
            data,
            "SafeERC20: low-level call failed"
        );
        if (returndata.length > 0) {
            // Return data is optional
            // solhint-disable-next-line max-line-length
            require(
                abi.decode(returndata, (bool)),
                "SafeERC20: ERC20 operation did not succeed"
            );
        }
    }
}

//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

/// @title ContractWhitelist
/// @author witherblock
/// @notice A helper contract that lets you add a list of whitelisted contracts that should be able to interact with restricited functions
abstract contract ContractWhitelist {
    /// @dev contract => whitelisted or not
    mapping(address => bool) public whitelistedContracts;

    /*==== SETTERS ====*/

    /// @dev add to the contract whitelist
    /// @param _contract the address of the contract to add to the contract whitelist
    function _addToContractWhitelist(address _contract) internal {
        require(isContract(_contract), "Address must be a contract");
        require(
            !whitelistedContracts[_contract],
            "Contract already whitelisted"
        );

        whitelistedContracts[_contract] = true;

        emit AddToContractWhitelist(_contract);
    }

    /// @dev remove from  the contract whitelist
    /// @param _contract the address of the contract to remove from the contract whitelist
    function _removeFromContractWhitelist(address _contract) internal {
        require(whitelistedContracts[_contract], "Contract not whitelisted");

        whitelistedContracts[_contract] = false;

        emit RemoveFromContractWhitelist(_contract);
    }

    // modifier is eligible sender modifier
    function _isEligibleSender() internal view {
        // the below condition checks whether the caller is a contract or not
        if (msg.sender != tx.origin)
            require(
                whitelistedContracts[msg.sender],
                "Contract must be whitelisted"
            );
    }

    /*==== VIEWS ====*/

    /// @dev checks for contract or eoa addresses
    /// @param addr the address to check
    /// @return bool whether the passed address is a contract address
    function isContract(address addr) public view returns (bool) {
        uint256 size;
        assembly {
            size := extcodesize(addr)
        }
        return size > 0;
    }

    /*==== EVENTS ====*/

    event AddToContractWhitelist(address indexed _contract);

    event RemoveFromContractWhitelist(address indexed _contract);
}

// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

/// @title Lighter version of the Openzeppelin Pausable contract
/// @author witherblock
/// @notice Helps pause a contract to block the execution of selected functions
/// @dev Difference from the Openzeppelin version is changing the modifiers to internal fns and requires to reverts
abstract contract Pausable {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _paused = false;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Internal function to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _whenNotPaused() internal view {
        if (paused()) revert ContractPaused();
    }

    /**
     * @dev Internal function to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _whenPaused() internal view {
        if (!paused()) revert ContractNotPaused();
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual {
        _whenNotPaused();
        _paused = true;
        emit Paused(msg.sender);
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual {
        _whenPaused();
        _paused = false;
        emit Unpaused(msg.sender);
    }

    error ContractPaused();
    error ContractNotPaused();
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

interface IFeeStrategy {
    function calculatePurchaseFees(
        uint256,
        uint256,
        uint256
    ) external view returns (uint256);

    function calculateSettlementFees(uint256) external view returns (uint256);
}

File 46 of 50 : IOptionPricing.sol
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

interface IOptionPricing {
  function getOptionPrice(
    bool isPut,
    uint256 expiry,
    uint256 strike,
    uint256 lastPrice,
    uint256 baseIv
  ) external view returns (uint256);
}

//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

interface IPriceOracle {
    function getCollateralPrice() external view returns (uint256);

    function getUnderlyingPrice() external view returns (uint256);
}

//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

interface IVolatilityOracle {
    function getVolatility(uint256) external view returns (uint256);
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

// Contracts
import {ERC20PresetMinterPauserUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/presets/ERC20PresetMinterPauserUpgradeable.sol";

// Interfaces
import {ISsovV3} from "../core/ISsovV3.sol";

// Libraries
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";

/**
 * @title Dopex SSOV V3 ERC20 Options Token
 */
contract SsovV3OptionsToken is ERC20PresetMinterPauserUpgradeable {
    using Strings for uint256;

    /// @dev Is this a PUT or CALL options contract
    bool public isPut;

    /// @dev The strike of the options contract
    uint256 public strike;

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

    /// @dev The address of the ssov creating the options contract
    address public ssov;

    /// @dev The symbol reperesenting the underlying asset of the option
    string public underlyingSymbol;

    /// @dev The symbol representing the collateral token of the option
    string public collateralSymbol;

    /*==== INITIALIZE FUNCTION ====*/

    /**
     * @notice Initialize function, equivalent of a constructor for upgradeable contracts
     * @param _ssov The address of the ssov creating the options contract
     * @param _isPut Whether the options is a put option
     * @param _strike The amount of strike asset that will be paid out per doToken
     * @param _expiry The time at which the insurance expires
     * @param _underlyingSymbol The symbol of the underlying asset token
     * @param _collateralSymbol The symbol of the collateral token
     * @param _expirySymbol The symbol representing the expiry
     */
    function initialize(
        address _ssov,
        bool _isPut,
        uint256 _strike,
        uint256 _expiry,
        string memory _underlyingSymbol,
        string memory _collateralSymbol,
        string memory _expirySymbol
    ) public {
        require(block.timestamp < _expiry, "Can't deploy an expired contract");

        ssov = _ssov;
        underlyingSymbol = _underlyingSymbol;
        collateralSymbol = _collateralSymbol;
        isPut = _isPut;
        strike = _strike;
        expiry = _expiry;

        string memory symbol = concatenate(_underlyingSymbol, "-");
        symbol = concatenate(symbol, _expirySymbol);
        symbol = concatenate(symbol, "-");
        symbol = concatenate(symbol, (strike / 1e8).toString());
        symbol = concatenate(symbol, isPut ? "-P" : "-C");

        super.initialize("Dopex SSOV V3 Options Token", symbol);
    }

    /*==== VIEWS ====*/

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

    /**
     * @notice Returns the current value of an option
     */
    function optionValue() external view returns (uint256) {
        return ISsovV3(ssov).calculatePremium(strike, 1e18, expiry);
    }

    /*==== PURE FUNCTIONS ====*/

    /**
     * @notice Returns a concatenated string of a and b
     * @param _a string a
     * @param _b string b
     */
    function concatenate(string memory _a, string memory _b)
        internal
        pure
        returns (string memory)
    {
        return string(abi.encodePacked(_a, _b));
    }
}

//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

interface IStakingStrategy {
    function stake(uint256) external returns (uint256[] memory);

    function unstake() external returns (uint256[] memory);

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

    event Stake(
        address sender,
        uint256 amountStaked,
        uint256 totalStakedBalance,
        uint256[] totalRewardsArray
    );

    event Unstake(
        address sender,
        uint256 amountUnstaked,
        uint256[] rewardTokenAmounts
    );
}

Settings
{
  "evmVersion": "london",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs",
    "useLiteralContent": true
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "remappings": [],
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ContractNotPaused","type":"error"},{"inputs":[],"name":"ContractPaused","type":"error"},{"inputs":[],"name":"E1","type":"error"},{"inputs":[],"name":"E10","type":"error"},{"inputs":[],"name":"E11","type":"error"},{"inputs":[],"name":"E12","type":"error"},{"inputs":[],"name":"E13","type":"error"},{"inputs":[],"name":"E14","type":"error"},{"inputs":[],"name":"E2","type":"error"},{"inputs":[],"name":"E3","type":"error"},{"inputs":[],"name":"E4","type":"error"},{"inputs":[],"name":"E6","type":"error"},{"inputs":[],"name":"E7","type":"error"},{"inputs":[],"name":"E8","type":"error"},{"inputs":[],"name":"E9","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_contract","type":"address"}],"name":"AddToContractWhitelist","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"feeStrategy","type":"address"},{"internalType":"address","name":"stakingStrategy","type":"address"},{"internalType":"address","name":"optionPricing","type":"address"},{"internalType":"address","name":"priceOracle","type":"address"},{"internalType":"address","name":"volatilityOracle","type":"address"},{"internalType":"address","name":"feeDistributor","type":"address"},{"internalType":"address","name":"optionsTokenImplementation","type":"address"}],"indexed":false,"internalType":"struct SsovV3State.Addresses","name":"addresses","type":"tuple"}],"name":"AddressesSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"strikes","type":"uint256[]"}],"name":"Bootstrap","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"checkpointIntervalTime","type":"uint256"}],"name":"CheckpointIntervalTimeUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"}],"name":"EmergencyWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"settlementPrice","type":"uint256"}],"name":"EpochExpired","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"expireDelayTolerance","type":"uint256"}],"name":"ExpireDelayToleranceUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"strike","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"premium","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"Purchase","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_contract","type":"address"}],"name":"RemoveFromContractWhitelist","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"strike","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"pnl","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"Settle","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"collateralTokenWithdrawn","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"rewardTokenWithdrawAmounts","type":"uint256[]"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MANAGER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_contract","type":"address"}],"name":"addToContractWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"addresses","outputs":[{"internalType":"address","name":"feeStrategy","type":"address"},{"internalType":"address","name":"stakingStrategy","type":"address"},{"internalType":"address","name":"optionPricing","type":"address"},{"internalType":"address","name":"priceOracle","type":"address"},{"internalType":"address","name":"volatilityOracle","type":"address"},{"internalType":"address","name":"feeDistributor","type":"address"},{"internalType":"address","name":"optionsTokenImplementation","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"strikes","type":"uint256[]"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"string","name":"expirySymbol","type":"string"}],"name":"bootstrap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"calculatePnl","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_strike","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_expiry","type":"uint256"}],"name":"calculatePremium","outputs":[{"internalType":"uint256","name":"premium","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"calculatePurchaseFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"pnl","type":"uint256"}],"name":"calculateSettlementFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"_increase","type":"bool"},{"internalType":"uint256","name":"_allowance","type":"uint256"}],"name":"changeAllowanceForStakingStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"checkpointIntervalTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"checkpoints","outputs":[{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"activeCollateral","type":"uint256"},{"internalType":"uint256","name":"totalCollateral","type":"uint256"},{"internalType":"uint256","name":"accruedPremium","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collateralPrecision","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collateralToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"strikeIndex","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"bool","name":"transferNative","type":"bool"}],"name":"emergencyWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"epochData","outputs":[{"internalType":"bool","name":"expired","type":"bool"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint256","name":"settlementPrice","type":"uint256"},{"internalType":"uint256","name":"totalCollateralBalance","type":"uint256"},{"internalType":"uint256","name":"collateralExchangeRate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"epochStrikeData","outputs":[{"internalType":"address","name":"strikeToken","type":"address"},{"internalType":"uint256","name":"totalCollateral","type":"uint256"},{"internalType":"uint256","name":"activeCollateral","type":"uint256"},{"internalType":"uint256","name":"totalPremiums","type":"uint256"},{"internalType":"uint256","name":"checkpointPointer","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"expire","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_settlementPrice","type":"uint256"}],"name":"expire","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"expireDelayTolerance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"strike","type":"uint256"}],"name":"getCheckpoints","outputs":[{"components":[{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"activeCollateral","type":"uint256"},{"internalType":"uint256","name":"totalCollateral","type":"uint256"},{"internalType":"uint256","name":"accruedPremium","type":"uint256"}],"internalType":"struct SsovV3State.VaultCheckpoint[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCollateralPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"getEpochData","outputs":[{"components":[{"internalType":"bool","name":"expired","type":"bool"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint256","name":"settlementPrice","type":"uint256"},{"internalType":"uint256","name":"totalCollateralBalance","type":"uint256"},{"internalType":"uint256","name":"collateralExchangeRate","type":"uint256"},{"internalType":"uint256[]","name":"strikes","type":"uint256[]"},{"internalType":"uint256[]","name":"totalRewardsCollected","type":"uint256[]"},{"internalType":"uint256[]","name":"rewardDistributionRatios","type":"uint256[]"},{"internalType":"address[]","name":"rewardTokensToDistribute","type":"address[]"}],"internalType":"struct SsovV3State.EpochData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"strike","type":"uint256"}],"name":"getEpochStrikeData","outputs":[{"components":[{"internalType":"address","name":"strikeToken","type":"address"},{"internalType":"uint256","name":"totalCollateral","type":"uint256"},{"internalType":"uint256","name":"activeCollateral","type":"uint256"},{"internalType":"uint256","name":"totalPremiums","type":"uint256"},{"internalType":"uint256","name":"checkpointPointer","type":"uint256"},{"internalType":"uint256[]","name":"rewardStoredForPremiums","type":"uint256[]"},{"internalType":"uint256[]","name":"rewardDistributionRatiosForPremiums","type":"uint256[]"}],"internalType":"struct SsovV3State.EpochStrikeData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"getEpochStrikes","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"getEpochTimes","outputs":[{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"end","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUnderlyingPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_strike","type":"uint256"}],"name":"getVolatility","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"isContract","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isPut","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"strikeIndex","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"purchase","outputs":[{"internalType":"uint256","name":"premium","type":"uint256"},{"internalType":"uint256","name":"totalFee","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_contract","type":"address"}],"name":"removeFromContractWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"feeStrategy","type":"address"},{"internalType":"address","name":"stakingStrategy","type":"address"},{"internalType":"address","name":"optionPricing","type":"address"},{"internalType":"address","name":"priceOracle","type":"address"},{"internalType":"address","name":"volatilityOracle","type":"address"},{"internalType":"address","name":"feeDistributor","type":"address"},{"internalType":"address","name":"optionsTokenImplementation","type":"address"}],"internalType":"struct SsovV3State.Addresses","name":"_addresses","type":"tuple"}],"name":"setAddresses","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"strikeIndex","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"settle","outputs":[{"internalType":"uint256","name":"pnl","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"underlyingSymbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_checkpointIntervalTime","type":"uint256"}],"name":"updateCheckpointIntervalTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_expireDelayTolerance","type":"uint256"}],"name":"updateExpireDelayTolerance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"whitelistedContracts","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"collateralTokenWithdrawAmount","type":"uint256"},{"internalType":"uint256[]","name":"rewardTokenWithdrawAmounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"writePosition","outputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"collateralAmount","type":"uint256"},{"internalType":"uint256","name":"checkpointIndex","type":"uint256"},{"internalType":"uint256[]","name":"rewardDistributionRatios","type":"uint256[]"}],"stateMutability":"view","type":"function"}]

608060405261012c601955611c20601b553480156200001d57600080fd5b5060408051808201825260198082527f435256205745454b4c5920505554532053534f562056332032000000000000006020808401918252845180860186529283527f4352562d5745454b4c592d505554532d53534f562d56332d3200000000000000838201528451808601909552600385526221a92b60e91b9085015260016000819055805460ff19168155835193949293737f90122bf0700f9e7e1f688fe926940e8839f3539286918691620000d99160029190620002e9565b508051620000ef906003906020840190620002e9565b5050506001600160a01b0382166200011a5760405163440a57bf60e01b815260040160405180910390fd5b82516200012f90600e906020860190620002e9565b50601780546001600160a01b0319166001600160a01b0384169081179091556040805163313ce56760e01b8152905163313ce56791600480820192602092909190829003018186803b1580156200018557600080fd5b505afa1580156200019a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001c091906200038f565b620001cd90600a620004ce565b601a55600f805460ff1916821515179055620001eb60003362000222565b620002177f241ecf16d79d0f8dbfb92cbc07fe17840425976cf0667f022fe9877caa831b083362000222565b50505050506200051c565b6200022e828262000232565b5050565b6200023e8282620002bc565b6200022e576000828152600c602090815260408083206001600160a01b03851684529091529020805460ff19166001179055620002783390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6000828152600c602090815260408083206001600160a01b038516845290915290205460ff165b92915050565b828054620002f790620004df565b90600052602060002090601f0160209004810192826200031b576000855562000366565b82601f106200033657805160ff191683800117855562000366565b8280016001018555821562000366579182015b828111156200036657825182559160200191906001019062000349565b506200037492915062000378565b5090565b5b8082111562000374576000815560010162000379565b600060208284031215620003a257600080fd5b815160ff81168114620003b457600080fd5b9392505050565b634e487b7160e01b600052601160045260246000fd5b600181815b8085111562000412578160001904821115620003f657620003f6620003bb565b808516156200040457918102915b93841c9390800290620003d6565b509250929050565b6000826200042b57506001620002e3565b816200043a57506000620002e3565b81600181146200045357600281146200045e576200047e565b6001915050620002e3565b60ff841115620004725762000472620003bb565b50506001821b620002e3565b5060208310610133831016604e8410600b8410161715620004a3575081810a620002e3565b620004af8383620003d1565b8060001904821115620004c657620004c6620003bb565b029392505050565b6000620003b460ff8416836200041a565b600181811c90821680620004f457607f821691505b602082108114156200051657634e487b7160e01b600052602260045260246000fd5b50919050565b616611806200052c6000396000f3fe608060405234801561001057600080fd5b50600436106103f05760003560e01c80637c4b52cb11610215578063b375d49211610125578063d547741f116100b8578063e985e9c511610087578063e985e9c514610a35578063ea3bd5df14610a71578063ec87621c14610a84578063f3c274a614610aab578063f7683bbc14610ab857600080fd5b8063d547741f14610986578063d8202b8b14610999578063d90a730e146109ac578063da0321cd146109b457600080fd5b8063c3cbb71c116100f4578063c3cbb71c1461091a578063c3d9ed391461094d578063c48ce05b14610960578063c87b56dd1461097357600080fd5b8063b375d492146108ce578063b88d4fde146108e1578063bf81bf43146108f4578063c189c19b1461090757600080fd5b8063925076eb116101a8578063a22cb46511610177578063a22cb4651461085e578063a624ec6314610871578063ab2150cf14610895578063acc3a006146108a8578063b2016bd4146108bb57600080fd5b8063925076eb1461083257806395d89b41146108455780639d5144de1461084d578063a217fddf1461085657600080fd5b80638dbdbe6d116101e45780638dbdbe6d146107e657806391aee9a8146107f957806391d148541461080c578063920023de1461081f57600080fd5b80637c4b52cb146107985780638456cb59146107ab578063859e7d32146107b357806388e18e13146107d357600080fd5b806342842e0e116103105780636352211e116102a35780636f56f56f116102725780636f56f56f1461073957806370a0823114610761578063751eb65c14610774578063766718081461078757806379599f961461079057600080fd5b80636352211e146106705780636493f5011461068357806369e3b748146106a35780636e821b2e146106c357600080fd5b80634f6ccce7116102df5780634f6ccce7146105c55780635871dc95146105d85780635c975abb1461065c578063616443731461066757600080fd5b806342842e0e1461058e57806342966c68146105a1578063430aea2e146105b4578063468f02d2146105bd57600080fd5b806318160ddd116103885780632f745c59116103575780632f745c591461053d57806336568abe14610550578063391feebb146105635780633f4ba83a1461058657600080fd5b806318160ddd146104ec57806323b872dd146104f4578063248a9ca3146105075780632f2ff15d1461052a57600080fd5b8063095ea7b3116103c4578063095ea7b314610482578063156f1d741461049757806315943b99146104b857806316279055146104d857600080fd5b8062f714ce146103f557806301ffc9a71461041f57806306fdde0314610442578063081812fc14610457575b600080fd5b61040861040336600461564a565b610ac0565b6040516104169291906156b5565b60405180910390f35b61043261042d3660046156e4565b610ee5565b6040519015158152602001610416565b61044a610ef6565b6040516104169190615759565b61046a61046536600461576c565b610f88565b6040516001600160a01b039091168152602001610416565b610495610490366004615785565b61101d565b005b6104aa6104a53660046157b1565b611133565b604051908152602001610416565b6104cb6104c636600461576c565b611235565b60405161041691906157dd565b6104326104e63660046157f0565b3b151590565b600a546104aa565b61049561050236600461580d565b6112a3565b6104aa61051536600461576c565b6000908152600c602052604090206001015490565b61049561053836600461564a565b6112d5565b6104aa61054b366004615785565b6112fb565b61049561055e36600461564a565b611391565b6104326105713660046157f0565b600d6020526000908152604090205460ff1681565b61049561140f565b61049561059c36600461580d565b611426565b6104956105af36600461576c565b611441565b6104aa601a5481565b6104aa6114b8565b6104aa6105d336600461576c565b61153a565b61062a6105e636600461584e565b601e602090815260009283526040808420909152908252902080546001820154600283015460038401546004909401546001600160a01b0390931693919290919085565b604080516001600160a01b0390961686526020860194909452928401919091526060830152608082015260a001610416565b60015460ff16610432565b6104aa60195481565b61046a61067e36600461576c565b6115cd565b61069661069136600461584e565b611644565b6040516104169190615870565b6106b66106b136600461584e565b6116e6565b60405161041691906158d4565b61070a6106d136600461576c565b601d6020526000908152604090208054600182015460028301546003840154600485015460059095015460ff9094169492939192909186565b6040805196151587526020870195909552938501929092526060840152608083015260a082015260c001610416565b61074c61074736600461576c565b611845565b60408051928352602083019190915201610416565b6104aa61076f3660046157f0565b611870565b610495610782366004615a36565b6118f7565b6104aa60185481565b610495611f09565b6104956107a6366004615b05565b611f7f565b6104956120d8565b6107c66107c136600461576c565b6120f4565b6040516104169190615bc3565b6104aa6107e13660046157b1565b61231a565b6104aa6107f4366004615c8b565b6123cc565b6104aa610807366004615cb9565b6125fe565b61043261081a36600461564a565b61288f565b6104aa61082d36600461584e565b6128ba565b61049561084036600461576c565b612971565b61044a6129ba565b6104aa601b5481565b6104aa600081565b61049561086c366004615cfa565b6129c9565b61088461087f36600461576c565b6129d4565b604051610416959493929190615d28565b6104956108a336600461576c565b612aa7565b6104956108b63660046157f0565b612ae8565b60175461046a906001600160a01b031681565b6104956108dc366004615d53565b612afd565b6104956108ef366004615d6b565b612b48565b61049561090236600461576c565b612b80565b6104aa61091536600461576c565b612ba0565b61092d6109283660046157b1565b612c1e565b604080519485526020850193909352918301526060820152608001610416565b61049561095b3660046157f0565b612c73565b6104aa61096e36600461576c565b612c88565b61044a61098136600461576c565b612cba565b61049561099436600461564a565b612da1565b6104956109a7366004615dea565b612dc7565b61044a612e13565b6010546011546012546013546014546015546016546109ec966001600160a01b03908116968116958116948116938116928116911687565b604080516001600160a01b039889168152968816602088015294871694860194909452918516606085015284166080840152831660a083015290911660c082015260e001610416565b610432610a43366004615e08565b6001600160a01b03918216600090815260076020908152604080832093909416825291909152205460ff1690565b61074c610a7f366004615c8b565b612ea1565b6104aa7f241ecf16d79d0f8dbfb92cbc07fe17840425976cf0667f022fe9877caa831b0881565b600f546104329060ff1681565b6104aa6132a6565b6000606060026000541415610af05760405162461bcd60e51b8152600401610ae790615e36565b60405180910390fd5b6002600055610afd6132eb565b610b05613311565b6000806000806000610b16896129d4565b94509450945094509450610b2984613377565b610b3285613398565b610b3b89611441565b6000858152601c602090815260408083208784529091528120805484908110610b6657610b66615e6d565b6000918252602080832060408051608081018252600490940290910180548452600181015484840152600281015484830152600301546060840152898452601d909152909120600901549091506001600160401b03811115610bca57610bca615948565b604051908082528060200260200182016040528015610bf3578160200160208202803683370190505b50965060008160400151858360600151610c0d9190615e99565b610c179190615ece565b600f5490915060009060ff16610c31578260200151610c50565b8683602001516305f5e100610c469190615e99565b610c509190615ece565b6000898152601d60205260409081902060030154908501519192509087610c78838b8661231a565b8660400151610c879190615ee2565b610c919190615e99565b610c9b9190615ece565b9a50610ca7838c615ef9565b9a5060005b8a51811015610e6657670de0b6b3a764000088878381518110610cd157610cd1615e6d565b6020026020010151601d60008e81526020019081526020016000206008018481548110610d0057610d00615e6d565b9060005260206000200154610d159190615ee2565b610d1f9190615e99565b610d299190615ece565b8b8281518110610d3b57610d3b615e6d565b60200260200101818151610d4f9190615ef9565b90525060008a8152601e602090815260408083208c845290915290206003015415610df35760008a8152601e602090815260408083208c8452909152902060038101546005909101805483908110610da957610da9615e6d565b906000526020600020015485610dbf9190615e99565b610dc99190615ece565b8b8281518110610ddb57610ddb615e6d565b60200260200101818151610def9190615ef9565b9052505b610e548d8c8381518110610e0957610e09615e6d565b6020026020010151601d60008e81526020019081526020016000206009018481548110610e3857610e38615e6d565b6000918252602090912001546001600160a01b031691906133c7565b80610e5e81615f11565b915050610cac565b50601754610e7e906001600160a01b03168d8d6133c7565b336001600160a01b03168c6001600160a01b03167f091323a211bfa86543b51f77a7a67270d25f9bd406aebf4d2382312c908462618f8e8e604051610ec593929190615f2c565b60405180910390a350505050505050505060016000819055509250929050565b6000610ef08261342a565b92915050565b606060028054610f0590615f4b565b80601f0160208091040260200160405190810160405280929190818152602001828054610f3190615f4b565b8015610f7e5780601f10610f5357610100808354040283529160200191610f7e565b820191906000526020600020905b815481529060010190602001808311610f6157829003601f168201915b5050505050905090565b6000818152600460205260408120546001600160a01b03166110015760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a20617070726f76656420717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b6064820152608401610ae7565b506000908152600660205260409020546001600160a01b031690565b6000611028826115cd565b9050806001600160a01b0316836001600160a01b031614156110965760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b6064820152608401610ae7565b336001600160a01b03821614806110b257506110b28133610a43565b6111245760405162461bcd60e51b815260206004820152603860248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f74206f7760448201527f6e6572206e6f7220617070726f76656420666f7220616c6c00000000000000006064820152608401610ae7565b61112e838361344f565b505050565b601254600f5460009184916001600160a01b0390911690635b7b6d889060ff16858861115d6114b8565b6111668b612ba0565b6040516001600160e01b031960e088901b1681529415156004860152602485019390935260448401919091526064830152608482015260a40160206040518083038186803b1580156111b757600080fd5b505afa1580156111cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111ef9190615f80565b6111f99190615e99565b9050670de0b6b3a764000061120c6132a6565b601a546112199084615e99565b6112239190615ece565b61122d9190615ece565b949350505050565b6060611240826134bd565b6000828152601d60209081526040918290206006018054835181840281018401909452808452909183018282801561129757602002820191906000526020600020905b815481526020019060010190808311611283575b50505050509050919050565b6112ae335b826134de565b6112ca5760405162461bcd60e51b8152600401610ae790615f99565b61112e8383836135d1565b6000828152600c60205260409020600101546112f18133613778565b61112e83836137dc565b600061130683611870565b82106113685760405162461bcd60e51b815260206004820152602b60248201527f455243373231456e756d657261626c653a206f776e657220696e646578206f7560448201526a74206f6620626f756e647360a81b6064820152608401610ae7565b506001600160a01b03919091166000908152600860209081526040808320938352929052205490565b6001600160a01b03811633146114015760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401610ae7565b61140b8282613862565b5050565b600061141b8133613778565b6114236138c9565b50565b61112e83838360405180602001604052806000815250612b48565b61144a336112a8565b6114af5760405162461bcd60e51b815260206004820152603060248201527f4552433732314275726e61626c653a2063616c6c6572206973206e6f74206f7760448201526f1b995c881b9bdc88185c1c1c9bdd995960821b6064820152608401610ae7565b61142381613911565b60135460408051632347816960e11b815290516000926001600160a01b03169163468f02d2916004808301926020929190829003018186803b1580156114fd57600080fd5b505afa158015611511573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115359190615f80565b905090565b6000611545600a5490565b82106115a85760405162461bcd60e51b815260206004820152602c60248201527f455243373231456e756d657261626c653a20676c6f62616c20696e646578206f60448201526b7574206f6620626f756e647360a01b6064820152608401610ae7565b600a82815481106115bb576115bb615e6d565b90600052602060002001549050919050565b6000818152600460205260408120546001600160a01b031680610ef05760405162461bcd60e51b815260206004820152602960248201527f4552433732313a206f776e657220717565727920666f72206e6f6e657869737460448201526832b73a103a37b5b2b760b91b6064820152608401610ae7565b606061164f836134bd565b6000838152601c60209081526040808320858452825280832080548251818502810185019093528083529193909284015b828210156116da578382906000526020600020906004020160405180608001604052908160008201548152602001600182015481526020016002820154815260200160038201548152505081526020019060010190611680565b50505050905092915050565b61172f6040518060e0016040528060006001600160a01b031681526020016000815260200160008152602001600081526020016000815260200160608152602001606081525090565b611738836134bd565b6000838152601e60209081526040808320858452825291829020825160e08101845281546001600160a01b0316815260018201548184015260028201548185015260038201546060820152600482015460808201526005820180548551818602810186019096528086529194929360a086019392908301828280156117dc57602002820191906000526020600020905b8154815260200190600101908083116117c8575b505050505081526020016006820180548060200260200160405190810160405280929190818152602001828054801561183457602002820191906000526020600020905b815481526020019060010190808311611820575b505050505081525050905092915050565b600080611851836134bd565b50506000908152601d6020526040902060018101546002909101549091565b60006001600160a01b0382166118db5760405162461bcd60e51b815260206004820152602a60248201527f4552433732313a2062616c616e636520717565727920666f7220746865207a65604482015269726f206164647265737360b01b6064820152608401610ae7565b506001600160a01b031660009081526005602052604090205490565b6002600054141561191a5760405162461bcd60e51b8152600401610ae790615e36565b60026000557f241ecf16d79d0f8dbfb92cbc07fe17840425976cf0667f022fe9877caa831b0861194a8133613778565b6119526132eb565b600060185460016119639190615ef9565b90508342111561198657604051633485c51360e21b815260040160405180910390fd5b60006018541180156119aa57506018546000908152601d602052604090205460ff16155b156119c857604051639ce22bbb60e01b815260040160405180910390fd5b6000818152601d6020908152604090912086516119ed92600690920191880190615575565b506000818152601d60205260408082204260018201556002018690556018839055601154815163c4f59f9b60e01b815291516001600160a01b039091169163c4f59f9b9160048083019286929190829003018186803b158015611a4f57600080fd5b505afa158015611a63573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611a8b9190810190615fea565b51905060008060005b8851811015611cff57888181518110611aaf57611aaf615e6d565b6020908102919091010151601654909350611ad2906001600160a01b03166139b8565b9150816001600160a01b0316635edabd8b30600f60009054906101000a900460ff16868c600e601760009054906101000a90046001600160a01b03166001600160a01b03166395d89b416040518163ffffffff1660e01b815260040160006040518083038186803b158015611b4657600080fd5b505afa158015611b5a573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611b829190810190616078565b8e6040518863ffffffff1660e01b8152600401611ba597969594939291906160e5565b600060405180830381600087803b158015611bbf57600080fd5b505af1158015611bd3573d6000803e3d6000fd5b5050506000868152601e60209081526040808320878452909152902080546001600160a01b0319166001600160a01b03851617905550836001600160401b03811115611c2157611c21615948565b604051908082528060200260200182016040528015611c4a578160200160208202803683370190505b506000868152601e6020908152604080832087845282529091208251611c7a936005909201929190910190615575565b50836001600160401b03811115611c9357611c93615948565b604051908082528060200260200182016040528015611cbc578160200160208202803683370190505b506000868152601e6020908152604080832087845282529091208251611cec936006909201929190910190615575565b5080611cf781615f11565b915050611a94565b50826001600160401b03811115611d1857611d18615948565b604051908082528060200260200182016040528015611d41578160200160208202803683370190505b50601d60008681526020019081526020016000206007019080519060200190611d6b929190615575565b50826001600160401b03811115611d8457611d84615948565b604051908082528060200260200182016040528015611dad578160200160208202803683370190505b50601d60008681526020019081526020016000206008019080519060200190611dd7929190615575565b506011546040805163c4f59f9b60e01b815290516001600160a01b039092169163c4f59f9b91600480820192600092909190829003018186803b158015611e1d57600080fd5b505afa158015611e31573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611e599190810190615fea565b601d60008681526020019081526020016000206009019080519060200190611e829291906155c0565b50611e8b6132a6565b611e936114b8565b611ea1906305f5e100615e99565b611eab9190615ece565b6000858152601d60205260409081902060050191909155517fa65bbf2074405dcc0df5134b6ae33db0e62584996f2a786d1a30be9d8efcf90d90611ef29086908b906156b5565b60405180910390a150506001600055505050505050565b611f11613311565b6000611f1e601854611845565b91505080421015611f4257604051631bd18a1960e31b815260040160405180910390fd5b601954611f4f9082615ef9565b421115611f6f5760405163496abc0760e01b815260040160405180910390fd5b611423611f7a6114b8565b613a55565b6000611f8b8133613778565b611f93613b0d565b8115611fc75760405133904780156108fc02916000818181858888f19350505050158015611fc5573d6000803e3d6000fd5b505b6000805b8481101561209d57858582818110611fe557611fe5615e6d565b9050602002016020810190611ffa91906157f0565b6040516370a0823160e01b815230600482015290925061208b9033906001600160a01b038516906370a082319060240160206040518083038186803b15801561204257600080fd5b505afa158015612056573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061207a9190615f80565b6001600160a01b03851691906133c7565b8061209581615f11565b915050611fcb565b506040513381527f5e7b34819cd91b239220bec92fcfd3c10da2214ba13e4e2b1f6c9cfdbd68a9a29060200160405180910390a15050505050565b60006120e48133613778565b6120ec613b30565b611423613b74565b61214c6040518061014001604052806000151581526020016000815260200160008152602001600081526020016000815260200160008152602001606081526020016060815260200160608152602001606081525090565b612155826134bd565b6000828152601d6020908152604091829020825161014081018452815460ff16151581526001820154818401526002820154818501526003820154606082015260048201546080820152600582015460a08201526006820180548551818602810186019096528086529194929360c086019392908301828280156121f857602002820191906000526020600020905b8154815260200190600101908083116121e4575b505050505081526020016007820180548060200260200160405190810160405280929190818152602001828054801561225057602002820191906000526020600020905b81548152602001906001019080831161223c575b50505050508152602001600882018054806020026020016040519081016040528092919081815260200182805480156122a857602002820191906000526020600020905b815481526020019060010190808311612294575b505050505081526020016009820180548060200260200160405190810160405280929190818152602001828054801561230a57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116122ec575b5050505050815250509050919050565b600f5460009060ff16156123845783831161233657600061237d565b670de0b6b3a76400006123476132a6565b601a54846123558888615ee2565b61235f9190615e99565b6123699190615e99565b6123739190615ece565b61237d9190615ece565b90506123c5565b82841161239257600061122d565b670de0b6b3a76400006123a36132a6565b601a54846123b18789615ee2565b6123bb9190615e99565b6112199190615e99565b9392505050565b6000600260005414156123f15760405162461bcd60e51b8152600401610ae790615e36565b60026000556018546124016132eb565b612409613311565b61241281613c5a565b61241b84613c8a565b6000818152601d6020526040812060060180548790811061243e5761243e615e6d565b9060005260206000200154905061245481613377565b60175461246c906001600160a01b0316333088613cab565b60115460405163534a7e1d60e11b8152600481018790526000916124fc9184916001600160a01b03169063a694fc3a90602401600060405180830381600087803b1580156124b957600080fd5b505af11580156124cd573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526124f591908101906161de565b6000613ce3565b9050600061250a8388614099565b905086601d6000601854815260200190815260200160002060040160008282546125349190615ef9565b90915550612543905086614297565b6040805160a08101825286815260208082018781528284018c815260608401878152608085018981526000888152601f8652969096208551815592516001840155905160028301555160038201559251805194995091936125aa9260048501920190615575565b50506040518681523391506001600160a01b038816907fa3d2cbcb90e0658235d4ba62aed9a50c231df9bc5bbfb74c95badbc798f38d1a9060200160405180910390a3505060016000555090949350505050565b6000600260005414156126235760405162461bcd60e51b8152600401610ae790615e36565b60026000556126306132eb565b612638613311565b61264184613c8a565b61264a83613398565b6000838152601d6020526040812060060180548790811061266d5761266d615e6d565b600091825260208083209190910154868352601e82526040808420828552909252918190205490516370a0823160e01b81523360048201529192506001600160a01b031690869082906370a082319060240160206040518083038186803b1580156126d757600080fd5b505afa1580156126eb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061270f9190615f80565b101561272e576040516301899ea960e01b815260040160405180910390fd5b60405163079cc67960e41b8152336004820152602481018790526001600160a01b038216906379cc679090604401600060405180830381600087803b15801561277657600080fd5b505af115801561278a573d6000803e3d6000fd5b5050506000868152601d602052604090206003015490506127ac81848961231a565b935060006127b985612c88565b9050600085116127dc5760405163e217c62b60e01b815260040160405180910390fd5b6015546017546127f9916001600160a01b039182169116836133c7565b61281a866128078388615ee2565b6017546001600160a01b031691906133c7565b336001600160a01b0387167f4ab5b5ebf96669ee045b167a5af455fc9ef9757a07606c2145140e9af15c5da389878c612853878c615ee2565b6040805194855260208501939093529183015260608201526080810185905260a00160405180910390a350506001600055509095945050505050565b6000918252600c602090815260408084206001600160a01b0393909316845291905290205460ff1690565b60006128c46132a6565b601a546010546001600160a01b031663e8468ee46128e06114b8565b6040516001600160e01b031960e084901b1681526004810191909152602481018890526044810187905260640160206040518083038186803b15801561292557600080fd5b505afa158015612939573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061295d9190615f80565b6129679190615e99565b6123c59190615ece565b600061297d8133613778565b601b8290556040518281527fb906343790d92967932431f0fd8b892c072a7310b947c30fc0ce0bfbb3116b12906020015b60405180910390a15050565b606060038054610f0590615f4b565b61140b3383836142bc565b60008060008060606000601f60008881526020019081526020016000206040518060a00160405290816000820154815260200160018201548152602001600282015481526020016003820154815260200160048201805480602002602001604051908101604052809291908181526020018280548015612a7357602002820191906000526020600020905b815481526020019060010190808311612a5f575b5050509190925250508151602083015160408401516060850151608090950151929c919b5099509297509550909350505050565b6000612ab38133613778565b60198290556040518281527f5259439c9ac2029585dd4a349897aa1e8298d26537c3b57453f275126db91918906020016129ae565b6000612af48133613778565b61140b8261438b565b6000612b098133613778565b816010612b168282616283565b9050507f626084554ed3d487b613abf59611e713f22caa8095d0c7b0d16b226ea45c3bc4826040516129ae9190616340565b612b5233836134de565b612b6e5760405162461bcd60e51b8152600401610ae790615f99565b612b7a8484848461448e565b50505050565b6000612b8c8133613778565b612b97601854613c5a565b61140b82613a55565b60145460405163c189c19b60e01b8152600481018390526000916001600160a01b03169063c189c19b906024015b60206040518083038186803b158015612be657600080fd5b505afa158015612bfa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ef09190615f80565b601c6020528260005260406000206020528160005260406000208181548110612c4657600080fd5b60009182526020909120600490910201805460018201546002830154600390930154919550935090915084565b6000612c7f8133613778565b61140b826144c1565b60105460405163c48ce05b60e01b8152600481018390526000916001600160a01b03169063c48ce05b90602401612bce565b6000818152600460205260409020546060906001600160a01b0316612d395760405162461bcd60e51b815260206004820152602f60248201527f4552433732314d657461646174613a2055524920717565727920666f72206e6f60448201526e3732bc34b9ba32b73a103a37b5b2b760891b6064820152608401610ae7565b6000612d5060408051602081019091526000815290565b90506000815111612d7057604051806020016040528060008152506123c5565b80612d7a84614572565b604051602001612d8b9291906163f2565b6040516020818303038152906040529392505050565b6000828152600c6020526040902060010154612dbd8133613778565b61112e8383613862565b6000612dd38133613778565b8215612df65760115460175461112e916001600160a01b0391821691168461466f565b60115460175461112e916001600160a01b03918216911684614731565b600e8054612e2090615f4b565b80601f0160208091040260200160405190810160405280929190818152602001828054612e4c90615f4b565b8015612e995780601f10612e6e57610100808354040283529160200191612e99565b820191906000526020600020905b815481529060010190602001808311612e7c57829003601f168201915b505050505081565b60008060026000541415612ec75760405162461bcd60e51b8152600401610ae790615e36565b6002600055612ed46132eb565b612edc613311565b612ee584613c8a565b6018546000612ef382611845565b915050804210612f1657604051632fd8225d60e21b815260040160405180910390fd5b6000828152601d60205260408120600601805489908110612f3957612f39615e6d565b90600052602060002001549050612f4f81613377565b612f58816147d6565b6000838152601e6020908152604080832084845290915281206002810154600190910154612f869190615ee2565b600f5490915060009060ff16612fdc57601a546000868152601d60205260409020600501546a52b7d2dcc80cd2e40000009190612fc3908c615e99565b612fcd9190615e99565b612fd79190615ece565b613018565b670de0b6b3a7640000612fed6132a6565b601a54612ffa868d615e99565b6130049190615e99565b61300e9190615ece565b6130189190615ece565b90508181111561303b576040516302b0eba760e21b815260040160405180910390fd5b613046838a86611133565b9650613052838a6128ba565b955061305f83828961490e565b613082333061306e898b615ef9565b6017546001600160a01b0316929190613cab565b60115460405163534a7e1d60e11b8152600481018990526131119185916001600160a01b039091169063a694fc3a90602401600060405180830381600087803b1580156130ce57600080fd5b505af11580156130e2573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261310a91908101906161de565b6001613ce3565b506000858152601e602090815260408083208684529091528120600201805483929061313e908490615ef9565b90915550506000858152601e602090815260408083208684529091528120600301805489929061316f908490615ef9565b90915550506000858152601d602052604081206004018054899290613195908490615ef9565b90915550506015546017546131b7916001600160a01b039182169116886133c7565b6000858152601e60209081526040808320868452909152908190205490516340c10f1960e01b81526001600160a01b038a81166004830152602482018c9052909116906340c10f1990604401600060405180830381600087803b15801561321d57600080fd5b505af1158015613231573d6000803e3d6000fd5b505060408051888152602081018790529081018c9052606081018a9052608081018990523392506001600160a01b038b1691507f96d7fd34a786c8810a555ee775a241bf4b8f243f8e64aaf266ec5398d1199b0c9060a00160405180910390a350505050506001600081905550935093915050565b60135460408051633dda0eef60e21b815290516000926001600160a01b03169163f7683bbc916004808301926020929190829003018186803b1580156114fd57600080fd5b60015460ff161561330f5760405163ab35696f60e01b815260040160405180910390fd5b565b33321461330f57336000908152600d602052604090205460ff1661330f5760405162461bcd60e51b815260206004820152601c60248201527f436f6e7472616374206d7573742062652077686974656c6973746564000000006044820152606401610ae7565b60008111611423576040516329be7c4760e11b815260040160405180910390fd5b6000818152601d602052604090205460ff166114235760405163652399f960e11b815260040160405180910390fd5b6040516001600160a01b03831660248201526044810182905261112e90849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152614bdf565b60006001600160e01b03198216637965db0b60e01b1480610ef05750610ef082614cb1565b600081815260066020526040902080546001600160a01b0319166001600160a01b0384169081179091558190613484826115cd565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b6000811161142357604051630cba69cb60e01b815260040160405180910390fd5b6000818152600460205260408120546001600160a01b03166135575760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a206f70657261746f7220717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b6064820152608401610ae7565b6000613562836115cd565b9050806001600160a01b0316846001600160a01b0316148061359d5750836001600160a01b031661359284610f88565b6001600160a01b0316145b8061122d57506001600160a01b0380821660009081526007602090815260408083209388168352929052205460ff1661122d565b826001600160a01b03166135e4826115cd565b6001600160a01b0316146136485760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201526437bbb732b960d91b6064820152608401610ae7565b6001600160a01b0382166136aa5760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b6064820152608401610ae7565b6136b5838383614cd6565b6136c060008261344f565b6001600160a01b03831660009081526005602052604081208054600192906136e9908490615ee2565b90915550506001600160a01b0382166000908152600560205260408120805460019290613717908490615ef9565b909155505060008181526004602052604080822080546001600160a01b0319166001600160a01b0386811691821790925591518493918716917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b613782828261288f565b61140b5761379a816001600160a01b03166014614ce9565b6137a5836020614ce9565b6040516020016137b6929190616421565b60408051601f198184030181529082905262461bcd60e51b8252610ae791600401615759565b6137e6828261288f565b61140b576000828152600c602090815260408083206001600160a01b03851684529091529020805460ff1916600117905561381e3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b61386c828261288f565b1561140b576000828152600c602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6138d1613b0d565b6001805460ff191690556040513381527f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa906020015b60405180910390a1565b600061391c826115cd565b905061392a81600084614cd6565b61393560008361344f565b6001600160a01b038116600090815260056020526040812080546001929061395e908490615ee2565b909155505060008281526004602052604080822080546001600160a01b0319169055518391906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b6000604051733d602d80600a3d3981f3363d3d373d3d3d363d7360601b81528260601b60148201526e5af43d82803e903d91602b57fd5bf360881b60288201526037816000f09150506001600160a01b038116613a505760405162461bcd60e51b8152602060048201526016602482015275115490cc4c4d8dce8818dc99585d194819985a5b195960521b6044820152606401610ae7565b919050565b60026000541415613a785760405162461bcd60e51b8152600401610ae790615e36565b6002600055613a856132eb565b613a90601854613c5a565b6018546000908152601d60205260409020600301819055613aaf613b74565b6018546000908152601d6020908152604091829020805460ff1916600117905581513381529081018390527feebcafefe23aabb68b4dab0cabce2ec0228ff1d73f042e91acdd09efd17845b2910160405180910390a1506001600055565b60015460ff1661330f5760405163dcdde9dd60e01b815260040160405180910390fd5b613b386132eb565b6001805460ff1916811790556040513381527f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25890602001613907565b6000613b81601854611235565b90506000601060010160009054906101000a90046001600160a01b03166001600160a01b0316632def66206040518163ffffffff1660e01b8152600401600060405180830381600087803b158015613bd857600080fd5b505af1158015613bec573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613c1491908101906161de565b905060005b825181101561112e57613c47838281518110613c3757613c37615e6d565b6020026020010151836001613ce3565b5080613c5281615f11565b915050613c19565b6000818152601d602052604090205460ff161561142357604051632fd8225d60e21b815260040160405180910390fd5b6000811161142357604051633c4117f160e21b815260040160405180910390fd5b6040516001600160a01b0380851660248301528316604482015260648101829052612b7a9085906323b872dd60e01b906084016133f3565b606082516001600160401b03811115613cfe57613cfe615948565b604051908082528060200260200182016040528015613d27578160200160208202803683370190505b5090506000805b8451811015614090576018546000908152601d60205260409020600701805482908110613d5d57613d5d615e6d565b9060005260206000200154858281518110613d7a57613d7a615e6d565b6020026020010151613d8c9190615ee2565b9150848181518110613da057613da0615e6d565b6020026020010151601d600060185481526020019081526020016000206007018281548110613dd157613dd1615e6d565b60009182526020808320909101929092556018548152601d9091526040902060040154613e1d576000838281518110613e0c57613e0c615e6d565b602002602001018181525050613e6d565b6018546000908152601d6020526040902060040154613e4483670de0b6b3a7640000615e99565b613e4e9190615ece565b838281518110613e6057613e60615e6d565b6020026020010181815250505b828181518110613e7f57613e7f615e6d565b6020026020010151601d600060185481526020019081526020016000206008018281548110613eb057613eb0615e6d565b906000526020600020016000828254613ec99190615ef9565b90915550508315614030576018546000908152601e602090815260408083208984529091529020600381015460069091018054670de0b6b3a764000092919084908110613f1857613f18615e6d565b9060005260206000200154601d600060185481526020019081526020016000206008018481548110613f4c57613f4c615e6d565b9060005260206000200154613f619190615ee2565b613f6b9190615e99565b613f759190615ece565b6018546000908152601e602090815260408083208a84529091529020600501805483908110613fa657613fa6615e6d565b906000526020600020016000828254613fbf9190615ef9565b90915550506018546000908152601d60205260409020600801805482908110613fea57613fea615e6d565b60009182526020808320909101546018548352601e825260408084208a8552909252912060060180548390811061402357614023615e6d565b6000918252602090912001555b6018546000908152601d6020526040902060080180548290811061405657614056615e6d565b906000526020600020015483828151811061407357614073615e6d565b60209081029190910101528061408881615f11565b915050613d2e565b50509392505050565b6018546000818152601c60209081526040808320868452909152812054909190614143576000818152601c60209081526040808320878452808352818420825160808101845242815280850186815293810189815260608201878152835460018181018655858a52888a2094516004909202909401908155955186840155905160028601555160039094019390935593889052909152905461413b9190615ee2565b915050610ef0565b61414c846147d6565b6000818152601c602090815260408083208784529091528120805461417390600190615ee2565b8154811061418357614183615e6d565b90600052602060002090600402016040518060800160405290816000820154815260200160018201548152602001600282015481526020016003820154815250509050604051806080016040528082600001518152602001826020015181526020018583604001516141f59190615ef9565b815260608301516020918201526000848152601c8252604080822089835290925220805461422590600190615ee2565b8154811061423557614235615e6d565b600091825260208083208451600490930201918255838101516001808401919091556040808601516002850155606090950151600390930192909255858352601c815283832089845290529190205461428e9190615ee2565b95945050505050565b60006142a260205490565b90506142b2602080546001019055565b613a508282614e84565b816001600160a01b0316836001600160a01b0316141561431e5760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c6572000000000000006044820152606401610ae7565b6001600160a01b03838116600081815260076020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b803b6143d95760405162461bcd60e51b815260206004820152601a60248201527f41646472657373206d757374206265206120636f6e74726163740000000000006044820152606401610ae7565b6001600160a01b0381166000908152600d602052604090205460ff16156144425760405162461bcd60e51b815260206004820152601c60248201527f436f6e747261637420616c72656164792077686974656c6973746564000000006044820152606401610ae7565b6001600160a01b0381166000818152600d6020526040808220805460ff19166001179055517ffbd3cde7ff522a917e485c8ed2a6e87590887ab399f5ac312307903f498543079190a250565b6144998484846135d1565b6144a584848484614e9e565b612b7a5760405162461bcd60e51b8152600401610ae790616496565b6001600160a01b0381166000908152600d602052604090205460ff166145295760405162461bcd60e51b815260206004820152601860248201527f436f6e7472616374206e6f742077686974656c697374656400000000000000006044820152606401610ae7565b6001600160a01b0381166000818152600d6020526040808220805460ff19169055517f8e81447740597754af5db3e176253a36f7981a9549f48ace3f0cb233913f9d859190a250565b6060816145965750506040805180820190915260018152600360fc1b602082015290565b8160005b81156145c057806145aa81615f11565b91506145b99050600a83615ece565b915061459a565b6000816001600160401b038111156145da576145da615948565b6040519080825280601f01601f191660200182016040528015614604576020820181803683370190505b5090505b841561122d57614619600183615ee2565b9150614626600a866164e8565b614631906030615ef9565b60f81b81838151811061464657614646615e6d565b60200101906001600160f81b031916908160001a905350614668600a86615ece565b9450614608565b604051636eb1769f60e11b81523060048201526001600160a01b0383811660248301526000916146fe9184919087169063dd62ed3e9060440160206040518083038186803b1580156146c057600080fd5b505afa1580156146d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146f89190615f80565b90614fab565b6040516001600160a01b038516602482015260448101829052909150612b7a90859063095ea7b360e01b906064016133f3565b60006146fe826040518060600160405280602981526020016165b360299139604051636eb1769f60e11b81523060048201526001600160a01b03878116602483015288169063dd62ed3e9060440160206040518083038186803b15801561479757600080fd5b505afa1580156147ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906147cf9190615f80565b9190614fb7565b6018546000818152601c602090815260408083208584529091528120805461480090600190615ee2565b8154811061481057614810615e6d565b9060005260206000209060040201604051806080016040529081600082015481526020016001820154815260200160028201548152602001600382015481525050905042601b5482600001516148669190615ef9565b101561112e576040808201516000848152601e6020908152838220878352905291822060010180549192909161489d908490615ef9565b9091555050506000908152601c6020908152604080832093835292815282822083516080810185524281528083018481529481018481526060820185815283546001808201865594875294909520915160049094029091019283559351908201559151600283015551600390910155565b6149396040518060800160405280600081526020016000815260200160008152602001600081525090565b60185460008080808761495488670de0b6b3a7640000615e99565b61495e9190615ece565b6000868152601e602090815260408083208d84529091529020600401549091505b88851015614bd3576000868152601c602090815260408083208d845290915290208054829081106149b2576149b2615e6d565b906000526020600020906004020160405180608001604052908160008201548152602001600182015481526020016002820154815260200160038201548152505096508489614a019190615ee2565b925086602001518760400151614a179190615ee2565b9350828410614af357614a2a8386615ef9565b6000878152601c602090815260408083208e845290915290208054919650849183908110614a5a57614a5a615e6d565b90600052602060002090600402016001016000828254614a7a9190615ef9565b90915550670de0b6b3a76400009050614a938385615e99565b614a9d9190615ece565b6000878152601c602090815260408083208e84529091529020805483908110614ac857614ac8615e6d565b90600052602060002090600402016003016000828254614ae89190615ef9565b9091555061497f9050565b614afd8486615ef9565b6000878152601c602090815260408083208e845290915290208054919650859183908110614b2d57614b2d615e6d565b90600052602060002090600402016001016000828254614b4d9190615ef9565b90915550670de0b6b3a76400009050614b668386615e99565b614b709190615ece565b6000878152601c602090815260408083208e84529091529020805483908110614b9b57614b9b615e6d565b90600052602060002090600402016003016000828254614bbb9190615ef9565b90915550614bcc9050600182615ef9565b905061497f565b50505050505050505050565b6000614c34826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614fe39092919063ffffffff16565b80519091501561112e5780806020019051810190614c5291906164fc565b61112e5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610ae7565b60006001600160e01b0319821663780e9d6360e01b1480610ef05750610ef082614ff2565b614cde6132eb565b61112e838383615042565b60606000614cf8836002615e99565b614d03906002615ef9565b6001600160401b03811115614d1a57614d1a615948565b6040519080825280601f01601f191660200182016040528015614d44576020820181803683370190505b509050600360fc1b81600081518110614d5f57614d5f615e6d565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110614d8e57614d8e615e6d565b60200101906001600160f81b031916908160001a9053506000614db2846002615e99565b614dbd906001615ef9565b90505b6001811115614e35576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110614df157614df1615e6d565b1a60f81b828281518110614e0757614e07615e6d565b60200101906001600160f81b031916908160001a90535060049490941c93614e2e81616519565b9050614dc0565b5083156123c55760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610ae7565b61140b8282604051806020016040528060008152506150fa565b60006001600160a01b0384163b15614fa057604051630a85bd0160e11b81526001600160a01b0385169063150b7a0290614ee2903390899088908890600401616530565b602060405180830381600087803b158015614efc57600080fd5b505af1925050508015614f2c575060408051601f3d908101601f19168201909252614f2991810190616563565b60015b614f86573d808015614f5a576040519150601f19603f3d011682016040523d82523d6000602084013e614f5f565b606091505b508051614f7e5760405162461bcd60e51b8152600401610ae790616496565b805181602001fd5b6001600160e01b031916630a85bd0160e11b14905061122d565b506001949350505050565b60006123c58284615ef9565b60008184841115614fdb5760405162461bcd60e51b8152600401610ae79190615759565b505050900390565b606061122d848460008561512d565b60006001600160e01b031982166380ac58cd60e01b148061502357506001600160e01b03198216635b5e139f60e01b145b80610ef057506301ffc9a760e01b6001600160e01b0319831614610ef0565b6001600160a01b03831661509d5761509881600a80546000838152600b60205260408120829055600182018355919091527fc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a80155565b6150c0565b816001600160a01b0316836001600160a01b0316146150c0576150c0838261525e565b6001600160a01b0382166150d75761112e816152fb565b826001600160a01b0316826001600160a01b03161461112e5761112e82826153aa565b61510483836153ee565b6151116000848484614e9e565b61112e5760405162461bcd60e51b8152600401610ae790616496565b60608247101561518e5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610ae7565b6001600160a01b0385163b6151e55760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610ae7565b600080866001600160a01b031685876040516152019190616580565b60006040518083038185875af1925050503d806000811461523e576040519150601f19603f3d011682016040523d82523d6000602084013e615243565b606091505b509150915061525382828661553c565b979650505050505050565b6000600161526b84611870565b6152759190615ee2565b6000838152600960205260409020549091508082146152c8576001600160a01b03841660009081526008602090815260408083208584528252808320548484528184208190558352600990915290208190555b5060009182526009602090815260408084208490556001600160a01b039094168352600881528383209183525290812055565b600a5460009061530d90600190615ee2565b6000838152600b6020526040812054600a805493945090928490811061533557615335615e6d565b9060005260206000200154905080600a838154811061535657615356615e6d565b6000918252602080832090910192909255828152600b9091526040808220849055858252812055600a80548061538e5761538e61659c565b6001900381819060005260206000200160009055905550505050565b60006153b583611870565b6001600160a01b039093166000908152600860209081526040808320868452825280832085905593825260099052919091209190915550565b6001600160a01b0382166154445760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152606401610ae7565b6000818152600460205260409020546001600160a01b0316156154a95760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606401610ae7565b6154b560008383614cd6565b6001600160a01b03821660009081526005602052604081208054600192906154de908490615ef9565b909155505060008181526004602052604080822080546001600160a01b0319166001600160a01b03861690811790915590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b6060831561554b5750816123c5565b82511561555b5782518084602001fd5b8160405162461bcd60e51b8152600401610ae79190615759565b8280548282559060005260206000209081019282156155b0579160200282015b828111156155b0578251825591602001919060010190615595565b506155bc929150615615565b5090565b8280548282559060005260206000209081019282156155b0579160200282015b828111156155b057825182546001600160a01b0319166001600160a01b039091161782556020909201916001909101906155e0565b5b808211156155bc5760008155600101615616565b6001600160a01b038116811461142357600080fd5b8035613a508161562a565b6000806040838503121561565d57600080fd5b82359150602083013561566f8161562a565b809150509250929050565b600081518084526020808501945080840160005b838110156156aa5781518752958201959082019060010161568e565b509495945050505050565b82815260406020820152600061122d604083018461567a565b6001600160e01b03198116811461142357600080fd5b6000602082840312156156f657600080fd5b81356123c5816156ce565b60005b8381101561571c578181015183820152602001615704565b83811115612b7a5750506000910152565b60008151808452615745816020860160208601615701565b601f01601f19169290920160200192915050565b6020815260006123c5602083018461572d565b60006020828403121561577e57600080fd5b5035919050565b6000806040838503121561579857600080fd5b82356157a38161562a565b946020939093013593505050565b6000806000606084860312156157c657600080fd5b505081359360208301359350604090920135919050565b6020815260006123c5602083018461567a565b60006020828403121561580257600080fd5b81356123c58161562a565b60008060006060848603121561582257600080fd5b833561582d8161562a565b9250602084013561583d8161562a565b929592945050506040919091013590565b6000806040838503121561586157600080fd5b50508035926020909101359150565b602080825282518282018190526000919060409081850190868401855b828110156158c75781518051855286810151878601528581015186860152606090810151908501526080909301929085019060010161588d565b5091979650505050505050565b6020815260018060a01b038251166020820152602082015160408201526040820151606082015260608201516080820152608082015160a0820152600060a083015160e060c084015261592b61010084018261567a565b905060c0840151601f198483030160e085015261428e828261567a565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b038111828210171561598657615986615948565b604052919050565b60006001600160401b038211156159a7576159a7615948565b5060051b60200190565b60006001600160401b038211156159ca576159ca615948565b50601f01601f191660200190565b60006159eb6159e6846159b1565b61595e565b90508281528383830111156159ff57600080fd5b828260208301376000602084830101529392505050565b600082601f830112615a2757600080fd5b6123c5838335602085016159d8565b600080600060608486031215615a4b57600080fd5b83356001600160401b0380821115615a6257600080fd5b818601915086601f830112615a7657600080fd5b81356020615a866159e68361598e565b82815260059290921b8401810191818101908a841115615aa557600080fd5b948201945b83861015615ac357853582529482019490820190615aaa565b9750508701359450506040860135915080821115615ae057600080fd5b50615aed86828701615a16565b9150509250925092565b801515811461142357600080fd5b600080600060408486031215615b1a57600080fd5b83356001600160401b0380821115615b3157600080fd5b818601915086601f830112615b4557600080fd5b813581811115615b5457600080fd5b8760208260051b8501011115615b6957600080fd5b60209283019550935050840135615b7f81615af7565b809150509250925092565b600081518084526020808501945080840160005b838110156156aa5781516001600160a01b031687529582019590820190600101615b9e565b60208152615bd660208201835115159052565b602082015160408201526040820151606082015260608201516080820152608082015160a082015260a082015160c0820152600060c08301516101408060e0850152615c2661016085018361567a565b915060e0850151601f19610100818786030181880152615c46858461567a565b945080880151925050610120818786030181880152615c65858461567a565b908801518782039092018488015293509050615c818382615b8a565b9695505050505050565b600080600060608486031215615ca057600080fd5b83359250602084013591506040840135615b7f8161562a565b60008060008060808587031215615ccf57600080fd5b8435935060208501359250604085013591506060850135615cef8161562a565b939692955090935050565b60008060408385031215615d0d57600080fd5b8235615d188161562a565b9150602083013561566f81615af7565b85815284602082015283604082015282606082015260a06080820152600061525360a083018461567a565b600060e08284031215615d6557600080fd5b50919050565b60008060008060808587031215615d8157600080fd5b8435615d8c8161562a565b93506020850135615d9c8161562a565b92506040850135915060608501356001600160401b03811115615dbe57600080fd5b8501601f81018713615dcf57600080fd5b615dde878235602084016159d8565b91505092959194509250565b60008060408385031215615dfd57600080fd5b82356157a381615af7565b60008060408385031215615e1b57600080fd5b8235615e268161562a565b9150602083013561566f8161562a565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000816000190483118215151615615eb357615eb3615e83565b500290565b634e487b7160e01b600052601260045260246000fd5b600082615edd57615edd615eb8565b500490565b600082821015615ef457615ef4615e83565b500390565b60008219821115615f0c57615f0c615e83565b500190565b6000600019821415615f2557615f25615e83565b5060010190565b83815282602082015260606040820152600061428e606083018461567a565b600181811c90821680615f5f57607f821691505b60208210811415615d6557634e487b7160e01b600052602260045260246000fd5b600060208284031215615f9257600080fd5b5051919050565b60208082526031908201527f4552433732313a207472616e736665722063616c6c6572206973206e6f74206f6040820152701ddb995c881b9bdc88185c1c1c9bdd9959607a1b606082015260800190565b60006020808385031215615ffd57600080fd5b82516001600160401b0381111561601357600080fd5b8301601f8101851361602457600080fd5b80516160326159e68261598e565b81815260059190911b8201830190838101908783111561605157600080fd5b928401925b828410156152535783516160698161562a565b82529284019290840190616056565b60006020828403121561608a57600080fd5b81516001600160401b038111156160a057600080fd5b8201601f810184136160b157600080fd5b80516160bf6159e6826159b1565b8181528560208385010111156160d457600080fd5b61428e826020830160208601615701565b60018060a01b0388168152600060208815158184015287604084015286606084015260e060808401526000865481600182811c91508083168061612957607f831692505b85831081141561614757634e487b7160e01b85526022600452602485fd5b60e0880183905261010088018180156161675760018114616178576161a3565b60ff198616825287820196506161a3565b60008d81526020902060005b8681101561619d57815484820152908501908901616184565b83019750505b50505050505083810360a08501526161bb818761572d565b91505082810360c08401526161d0818561572d565b9a9950505050505050505050565b600060208083850312156161f157600080fd5b82516001600160401b0381111561620757600080fd5b8301601f8101851361621857600080fd5b80516162266159e68261598e565b81815260059190911b8201830190838101908783111561624557600080fd5b928401925b828410156152535783518252928401929084019061624a565b80546001600160a01b0319166001600160a01b0392909216919091179055565b813561628e8161562a565b6162988183616263565b5060208201356162a78161562a565b6162b48160018401616263565b5060408201356162c38161562a565b6162d08160028401616263565b5060608201356162df8161562a565b6162ec8160038401616263565b5060808201356162fb8161562a565b6163088160048401616263565b5060a08201356163178161562a565b6163248160058401616263565b5060c08201356163338161562a565b61112e8160068401616263565b60e08101823561634f8161562a565b6001600160a01b03908116835260208401359061636b8261562a565b90811660208401526040840135906163828261562a565b90811660408401526060840135906163998261562a565b90811660608401526080840135906163b08261562a565b1660808301526163c260a0840161563f565b6001600160a01b031660a08301526163dc60c0840161563f565b6001600160a01b03811660c08401525092915050565b60008351616404818460208801615701565b835190830190616418818360208801615701565b01949350505050565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351616459816017850160208801615701565b7001034b99036b4b9b9b4b733903937b6329607d1b601791840191820152835161648a816028840160208801615701565b01602801949350505050565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b6000826164f7576164f7615eb8565b500690565b60006020828403121561650e57600080fd5b81516123c581615af7565b60008161652857616528615e83565b506000190190565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090615c819083018461572d565b60006020828403121561657557600080fd5b81516123c5816156ce565b60008251616592818460208701615701565b9190910192915050565b634e487b7160e01b600052603160045260246000fdfe5361666545524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa26469706673582212206887d296dda2e6e5b3b20d8abd8ae9e41c77a94c3db4619b74baed194af042cc64736f6c63430008090033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106103f05760003560e01c80637c4b52cb11610215578063b375d49211610125578063d547741f116100b8578063e985e9c511610087578063e985e9c514610a35578063ea3bd5df14610a71578063ec87621c14610a84578063f3c274a614610aab578063f7683bbc14610ab857600080fd5b8063d547741f14610986578063d8202b8b14610999578063d90a730e146109ac578063da0321cd146109b457600080fd5b8063c3cbb71c116100f4578063c3cbb71c1461091a578063c3d9ed391461094d578063c48ce05b14610960578063c87b56dd1461097357600080fd5b8063b375d492146108ce578063b88d4fde146108e1578063bf81bf43146108f4578063c189c19b1461090757600080fd5b8063925076eb116101a8578063a22cb46511610177578063a22cb4651461085e578063a624ec6314610871578063ab2150cf14610895578063acc3a006146108a8578063b2016bd4146108bb57600080fd5b8063925076eb1461083257806395d89b41146108455780639d5144de1461084d578063a217fddf1461085657600080fd5b80638dbdbe6d116101e45780638dbdbe6d146107e657806391aee9a8146107f957806391d148541461080c578063920023de1461081f57600080fd5b80637c4b52cb146107985780638456cb59146107ab578063859e7d32146107b357806388e18e13146107d357600080fd5b806342842e0e116103105780636352211e116102a35780636f56f56f116102725780636f56f56f1461073957806370a0823114610761578063751eb65c14610774578063766718081461078757806379599f961461079057600080fd5b80636352211e146106705780636493f5011461068357806369e3b748146106a35780636e821b2e146106c357600080fd5b80634f6ccce7116102df5780634f6ccce7146105c55780635871dc95146105d85780635c975abb1461065c578063616443731461066757600080fd5b806342842e0e1461058e57806342966c68146105a1578063430aea2e146105b4578063468f02d2146105bd57600080fd5b806318160ddd116103885780632f745c59116103575780632f745c591461053d57806336568abe14610550578063391feebb146105635780633f4ba83a1461058657600080fd5b806318160ddd146104ec57806323b872dd146104f4578063248a9ca3146105075780632f2ff15d1461052a57600080fd5b8063095ea7b3116103c4578063095ea7b314610482578063156f1d741461049757806315943b99146104b857806316279055146104d857600080fd5b8062f714ce146103f557806301ffc9a71461041f57806306fdde0314610442578063081812fc14610457575b600080fd5b61040861040336600461564a565b610ac0565b6040516104169291906156b5565b60405180910390f35b61043261042d3660046156e4565b610ee5565b6040519015158152602001610416565b61044a610ef6565b6040516104169190615759565b61046a61046536600461576c565b610f88565b6040516001600160a01b039091168152602001610416565b610495610490366004615785565b61101d565b005b6104aa6104a53660046157b1565b611133565b604051908152602001610416565b6104cb6104c636600461576c565b611235565b60405161041691906157dd565b6104326104e63660046157f0565b3b151590565b600a546104aa565b61049561050236600461580d565b6112a3565b6104aa61051536600461576c565b6000908152600c602052604090206001015490565b61049561053836600461564a565b6112d5565b6104aa61054b366004615785565b6112fb565b61049561055e36600461564a565b611391565b6104326105713660046157f0565b600d6020526000908152604090205460ff1681565b61049561140f565b61049561059c36600461580d565b611426565b6104956105af36600461576c565b611441565b6104aa601a5481565b6104aa6114b8565b6104aa6105d336600461576c565b61153a565b61062a6105e636600461584e565b601e602090815260009283526040808420909152908252902080546001820154600283015460038401546004909401546001600160a01b0390931693919290919085565b604080516001600160a01b0390961686526020860194909452928401919091526060830152608082015260a001610416565b60015460ff16610432565b6104aa60195481565b61046a61067e36600461576c565b6115cd565b61069661069136600461584e565b611644565b6040516104169190615870565b6106b66106b136600461584e565b6116e6565b60405161041691906158d4565b61070a6106d136600461576c565b601d6020526000908152604090208054600182015460028301546003840154600485015460059095015460ff9094169492939192909186565b6040805196151587526020870195909552938501929092526060840152608083015260a082015260c001610416565b61074c61074736600461576c565b611845565b60408051928352602083019190915201610416565b6104aa61076f3660046157f0565b611870565b610495610782366004615a36565b6118f7565b6104aa60185481565b610495611f09565b6104956107a6366004615b05565b611f7f565b6104956120d8565b6107c66107c136600461576c565b6120f4565b6040516104169190615bc3565b6104aa6107e13660046157b1565b61231a565b6104aa6107f4366004615c8b565b6123cc565b6104aa610807366004615cb9565b6125fe565b61043261081a36600461564a565b61288f565b6104aa61082d36600461584e565b6128ba565b61049561084036600461576c565b612971565b61044a6129ba565b6104aa601b5481565b6104aa600081565b61049561086c366004615cfa565b6129c9565b61088461087f36600461576c565b6129d4565b604051610416959493929190615d28565b6104956108a336600461576c565b612aa7565b6104956108b63660046157f0565b612ae8565b60175461046a906001600160a01b031681565b6104956108dc366004615d53565b612afd565b6104956108ef366004615d6b565b612b48565b61049561090236600461576c565b612b80565b6104aa61091536600461576c565b612ba0565b61092d6109283660046157b1565b612c1e565b604080519485526020850193909352918301526060820152608001610416565b61049561095b3660046157f0565b612c73565b6104aa61096e36600461576c565b612c88565b61044a61098136600461576c565b612cba565b61049561099436600461564a565b612da1565b6104956109a7366004615dea565b612dc7565b61044a612e13565b6010546011546012546013546014546015546016546109ec966001600160a01b03908116968116958116948116938116928116911687565b604080516001600160a01b039889168152968816602088015294871694860194909452918516606085015284166080840152831660a083015290911660c082015260e001610416565b610432610a43366004615e08565b6001600160a01b03918216600090815260076020908152604080832093909416825291909152205460ff1690565b61074c610a7f366004615c8b565b612ea1565b6104aa7f241ecf16d79d0f8dbfb92cbc07fe17840425976cf0667f022fe9877caa831b0881565b600f546104329060ff1681565b6104aa6132a6565b6000606060026000541415610af05760405162461bcd60e51b8152600401610ae790615e36565b60405180910390fd5b6002600055610afd6132eb565b610b05613311565b6000806000806000610b16896129d4565b94509450945094509450610b2984613377565b610b3285613398565b610b3b89611441565b6000858152601c602090815260408083208784529091528120805484908110610b6657610b66615e6d565b6000918252602080832060408051608081018252600490940290910180548452600181015484840152600281015484830152600301546060840152898452601d909152909120600901549091506001600160401b03811115610bca57610bca615948565b604051908082528060200260200182016040528015610bf3578160200160208202803683370190505b50965060008160400151858360600151610c0d9190615e99565b610c179190615ece565b600f5490915060009060ff16610c31578260200151610c50565b8683602001516305f5e100610c469190615e99565b610c509190615ece565b6000898152601d60205260409081902060030154908501519192509087610c78838b8661231a565b8660400151610c879190615ee2565b610c919190615e99565b610c9b9190615ece565b9a50610ca7838c615ef9565b9a5060005b8a51811015610e6657670de0b6b3a764000088878381518110610cd157610cd1615e6d565b6020026020010151601d60008e81526020019081526020016000206008018481548110610d0057610d00615e6d565b9060005260206000200154610d159190615ee2565b610d1f9190615e99565b610d299190615ece565b8b8281518110610d3b57610d3b615e6d565b60200260200101818151610d4f9190615ef9565b90525060008a8152601e602090815260408083208c845290915290206003015415610df35760008a8152601e602090815260408083208c8452909152902060038101546005909101805483908110610da957610da9615e6d565b906000526020600020015485610dbf9190615e99565b610dc99190615ece565b8b8281518110610ddb57610ddb615e6d565b60200260200101818151610def9190615ef9565b9052505b610e548d8c8381518110610e0957610e09615e6d565b6020026020010151601d60008e81526020019081526020016000206009018481548110610e3857610e38615e6d565b6000918252602090912001546001600160a01b031691906133c7565b80610e5e81615f11565b915050610cac565b50601754610e7e906001600160a01b03168d8d6133c7565b336001600160a01b03168c6001600160a01b03167f091323a211bfa86543b51f77a7a67270d25f9bd406aebf4d2382312c908462618f8e8e604051610ec593929190615f2c565b60405180910390a350505050505050505060016000819055509250929050565b6000610ef08261342a565b92915050565b606060028054610f0590615f4b565b80601f0160208091040260200160405190810160405280929190818152602001828054610f3190615f4b565b8015610f7e5780601f10610f5357610100808354040283529160200191610f7e565b820191906000526020600020905b815481529060010190602001808311610f6157829003601f168201915b5050505050905090565b6000818152600460205260408120546001600160a01b03166110015760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a20617070726f76656420717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b6064820152608401610ae7565b506000908152600660205260409020546001600160a01b031690565b6000611028826115cd565b9050806001600160a01b0316836001600160a01b031614156110965760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b6064820152608401610ae7565b336001600160a01b03821614806110b257506110b28133610a43565b6111245760405162461bcd60e51b815260206004820152603860248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f74206f7760448201527f6e6572206e6f7220617070726f76656420666f7220616c6c00000000000000006064820152608401610ae7565b61112e838361344f565b505050565b601254600f5460009184916001600160a01b0390911690635b7b6d889060ff16858861115d6114b8565b6111668b612ba0565b6040516001600160e01b031960e088901b1681529415156004860152602485019390935260448401919091526064830152608482015260a40160206040518083038186803b1580156111b757600080fd5b505afa1580156111cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111ef9190615f80565b6111f99190615e99565b9050670de0b6b3a764000061120c6132a6565b601a546112199084615e99565b6112239190615ece565b61122d9190615ece565b949350505050565b6060611240826134bd565b6000828152601d60209081526040918290206006018054835181840281018401909452808452909183018282801561129757602002820191906000526020600020905b815481526020019060010190808311611283575b50505050509050919050565b6112ae335b826134de565b6112ca5760405162461bcd60e51b8152600401610ae790615f99565b61112e8383836135d1565b6000828152600c60205260409020600101546112f18133613778565b61112e83836137dc565b600061130683611870565b82106113685760405162461bcd60e51b815260206004820152602b60248201527f455243373231456e756d657261626c653a206f776e657220696e646578206f7560448201526a74206f6620626f756e647360a81b6064820152608401610ae7565b506001600160a01b03919091166000908152600860209081526040808320938352929052205490565b6001600160a01b03811633146114015760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401610ae7565b61140b8282613862565b5050565b600061141b8133613778565b6114236138c9565b50565b61112e83838360405180602001604052806000815250612b48565b61144a336112a8565b6114af5760405162461bcd60e51b815260206004820152603060248201527f4552433732314275726e61626c653a2063616c6c6572206973206e6f74206f7760448201526f1b995c881b9bdc88185c1c1c9bdd995960821b6064820152608401610ae7565b61142381613911565b60135460408051632347816960e11b815290516000926001600160a01b03169163468f02d2916004808301926020929190829003018186803b1580156114fd57600080fd5b505afa158015611511573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115359190615f80565b905090565b6000611545600a5490565b82106115a85760405162461bcd60e51b815260206004820152602c60248201527f455243373231456e756d657261626c653a20676c6f62616c20696e646578206f60448201526b7574206f6620626f756e647360a01b6064820152608401610ae7565b600a82815481106115bb576115bb615e6d565b90600052602060002001549050919050565b6000818152600460205260408120546001600160a01b031680610ef05760405162461bcd60e51b815260206004820152602960248201527f4552433732313a206f776e657220717565727920666f72206e6f6e657869737460448201526832b73a103a37b5b2b760b91b6064820152608401610ae7565b606061164f836134bd565b6000838152601c60209081526040808320858452825280832080548251818502810185019093528083529193909284015b828210156116da578382906000526020600020906004020160405180608001604052908160008201548152602001600182015481526020016002820154815260200160038201548152505081526020019060010190611680565b50505050905092915050565b61172f6040518060e0016040528060006001600160a01b031681526020016000815260200160008152602001600081526020016000815260200160608152602001606081525090565b611738836134bd565b6000838152601e60209081526040808320858452825291829020825160e08101845281546001600160a01b0316815260018201548184015260028201548185015260038201546060820152600482015460808201526005820180548551818602810186019096528086529194929360a086019392908301828280156117dc57602002820191906000526020600020905b8154815260200190600101908083116117c8575b505050505081526020016006820180548060200260200160405190810160405280929190818152602001828054801561183457602002820191906000526020600020905b815481526020019060010190808311611820575b505050505081525050905092915050565b600080611851836134bd565b50506000908152601d6020526040902060018101546002909101549091565b60006001600160a01b0382166118db5760405162461bcd60e51b815260206004820152602a60248201527f4552433732313a2062616c616e636520717565727920666f7220746865207a65604482015269726f206164647265737360b01b6064820152608401610ae7565b506001600160a01b031660009081526005602052604090205490565b6002600054141561191a5760405162461bcd60e51b8152600401610ae790615e36565b60026000557f241ecf16d79d0f8dbfb92cbc07fe17840425976cf0667f022fe9877caa831b0861194a8133613778565b6119526132eb565b600060185460016119639190615ef9565b90508342111561198657604051633485c51360e21b815260040160405180910390fd5b60006018541180156119aa57506018546000908152601d602052604090205460ff16155b156119c857604051639ce22bbb60e01b815260040160405180910390fd5b6000818152601d6020908152604090912086516119ed92600690920191880190615575565b506000818152601d60205260408082204260018201556002018690556018839055601154815163c4f59f9b60e01b815291516001600160a01b039091169163c4f59f9b9160048083019286929190829003018186803b158015611a4f57600080fd5b505afa158015611a63573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611a8b9190810190615fea565b51905060008060005b8851811015611cff57888181518110611aaf57611aaf615e6d565b6020908102919091010151601654909350611ad2906001600160a01b03166139b8565b9150816001600160a01b0316635edabd8b30600f60009054906101000a900460ff16868c600e601760009054906101000a90046001600160a01b03166001600160a01b03166395d89b416040518163ffffffff1660e01b815260040160006040518083038186803b158015611b4657600080fd5b505afa158015611b5a573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611b829190810190616078565b8e6040518863ffffffff1660e01b8152600401611ba597969594939291906160e5565b600060405180830381600087803b158015611bbf57600080fd5b505af1158015611bd3573d6000803e3d6000fd5b5050506000868152601e60209081526040808320878452909152902080546001600160a01b0319166001600160a01b03851617905550836001600160401b03811115611c2157611c21615948565b604051908082528060200260200182016040528015611c4a578160200160208202803683370190505b506000868152601e6020908152604080832087845282529091208251611c7a936005909201929190910190615575565b50836001600160401b03811115611c9357611c93615948565b604051908082528060200260200182016040528015611cbc578160200160208202803683370190505b506000868152601e6020908152604080832087845282529091208251611cec936006909201929190910190615575565b5080611cf781615f11565b915050611a94565b50826001600160401b03811115611d1857611d18615948565b604051908082528060200260200182016040528015611d41578160200160208202803683370190505b50601d60008681526020019081526020016000206007019080519060200190611d6b929190615575565b50826001600160401b03811115611d8457611d84615948565b604051908082528060200260200182016040528015611dad578160200160208202803683370190505b50601d60008681526020019081526020016000206008019080519060200190611dd7929190615575565b506011546040805163c4f59f9b60e01b815290516001600160a01b039092169163c4f59f9b91600480820192600092909190829003018186803b158015611e1d57600080fd5b505afa158015611e31573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611e599190810190615fea565b601d60008681526020019081526020016000206009019080519060200190611e829291906155c0565b50611e8b6132a6565b611e936114b8565b611ea1906305f5e100615e99565b611eab9190615ece565b6000858152601d60205260409081902060050191909155517fa65bbf2074405dcc0df5134b6ae33db0e62584996f2a786d1a30be9d8efcf90d90611ef29086908b906156b5565b60405180910390a150506001600055505050505050565b611f11613311565b6000611f1e601854611845565b91505080421015611f4257604051631bd18a1960e31b815260040160405180910390fd5b601954611f4f9082615ef9565b421115611f6f5760405163496abc0760e01b815260040160405180910390fd5b611423611f7a6114b8565b613a55565b6000611f8b8133613778565b611f93613b0d565b8115611fc75760405133904780156108fc02916000818181858888f19350505050158015611fc5573d6000803e3d6000fd5b505b6000805b8481101561209d57858582818110611fe557611fe5615e6d565b9050602002016020810190611ffa91906157f0565b6040516370a0823160e01b815230600482015290925061208b9033906001600160a01b038516906370a082319060240160206040518083038186803b15801561204257600080fd5b505afa158015612056573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061207a9190615f80565b6001600160a01b03851691906133c7565b8061209581615f11565b915050611fcb565b506040513381527f5e7b34819cd91b239220bec92fcfd3c10da2214ba13e4e2b1f6c9cfdbd68a9a29060200160405180910390a15050505050565b60006120e48133613778565b6120ec613b30565b611423613b74565b61214c6040518061014001604052806000151581526020016000815260200160008152602001600081526020016000815260200160008152602001606081526020016060815260200160608152602001606081525090565b612155826134bd565b6000828152601d6020908152604091829020825161014081018452815460ff16151581526001820154818401526002820154818501526003820154606082015260048201546080820152600582015460a08201526006820180548551818602810186019096528086529194929360c086019392908301828280156121f857602002820191906000526020600020905b8154815260200190600101908083116121e4575b505050505081526020016007820180548060200260200160405190810160405280929190818152602001828054801561225057602002820191906000526020600020905b81548152602001906001019080831161223c575b50505050508152602001600882018054806020026020016040519081016040528092919081815260200182805480156122a857602002820191906000526020600020905b815481526020019060010190808311612294575b505050505081526020016009820180548060200260200160405190810160405280929190818152602001828054801561230a57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116122ec575b5050505050815250509050919050565b600f5460009060ff16156123845783831161233657600061237d565b670de0b6b3a76400006123476132a6565b601a54846123558888615ee2565b61235f9190615e99565b6123699190615e99565b6123739190615ece565b61237d9190615ece565b90506123c5565b82841161239257600061122d565b670de0b6b3a76400006123a36132a6565b601a54846123b18789615ee2565b6123bb9190615e99565b6112199190615e99565b9392505050565b6000600260005414156123f15760405162461bcd60e51b8152600401610ae790615e36565b60026000556018546124016132eb565b612409613311565b61241281613c5a565b61241b84613c8a565b6000818152601d6020526040812060060180548790811061243e5761243e615e6d565b9060005260206000200154905061245481613377565b60175461246c906001600160a01b0316333088613cab565b60115460405163534a7e1d60e11b8152600481018790526000916124fc9184916001600160a01b03169063a694fc3a90602401600060405180830381600087803b1580156124b957600080fd5b505af11580156124cd573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526124f591908101906161de565b6000613ce3565b9050600061250a8388614099565b905086601d6000601854815260200190815260200160002060040160008282546125349190615ef9565b90915550612543905086614297565b6040805160a08101825286815260208082018781528284018c815260608401878152608085018981526000888152601f8652969096208551815592516001840155905160028301555160038201559251805194995091936125aa9260048501920190615575565b50506040518681523391506001600160a01b038816907fa3d2cbcb90e0658235d4ba62aed9a50c231df9bc5bbfb74c95badbc798f38d1a9060200160405180910390a3505060016000555090949350505050565b6000600260005414156126235760405162461bcd60e51b8152600401610ae790615e36565b60026000556126306132eb565b612638613311565b61264184613c8a565b61264a83613398565b6000838152601d6020526040812060060180548790811061266d5761266d615e6d565b600091825260208083209190910154868352601e82526040808420828552909252918190205490516370a0823160e01b81523360048201529192506001600160a01b031690869082906370a082319060240160206040518083038186803b1580156126d757600080fd5b505afa1580156126eb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061270f9190615f80565b101561272e576040516301899ea960e01b815260040160405180910390fd5b60405163079cc67960e41b8152336004820152602481018790526001600160a01b038216906379cc679090604401600060405180830381600087803b15801561277657600080fd5b505af115801561278a573d6000803e3d6000fd5b5050506000868152601d602052604090206003015490506127ac81848961231a565b935060006127b985612c88565b9050600085116127dc5760405163e217c62b60e01b815260040160405180910390fd5b6015546017546127f9916001600160a01b039182169116836133c7565b61281a866128078388615ee2565b6017546001600160a01b031691906133c7565b336001600160a01b0387167f4ab5b5ebf96669ee045b167a5af455fc9ef9757a07606c2145140e9af15c5da389878c612853878c615ee2565b6040805194855260208501939093529183015260608201526080810185905260a00160405180910390a350506001600055509095945050505050565b6000918252600c602090815260408084206001600160a01b0393909316845291905290205460ff1690565b60006128c46132a6565b601a546010546001600160a01b031663e8468ee46128e06114b8565b6040516001600160e01b031960e084901b1681526004810191909152602481018890526044810187905260640160206040518083038186803b15801561292557600080fd5b505afa158015612939573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061295d9190615f80565b6129679190615e99565b6123c59190615ece565b600061297d8133613778565b601b8290556040518281527fb906343790d92967932431f0fd8b892c072a7310b947c30fc0ce0bfbb3116b12906020015b60405180910390a15050565b606060038054610f0590615f4b565b61140b3383836142bc565b60008060008060606000601f60008881526020019081526020016000206040518060a00160405290816000820154815260200160018201548152602001600282015481526020016003820154815260200160048201805480602002602001604051908101604052809291908181526020018280548015612a7357602002820191906000526020600020905b815481526020019060010190808311612a5f575b5050509190925250508151602083015160408401516060850151608090950151929c919b5099509297509550909350505050565b6000612ab38133613778565b60198290556040518281527f5259439c9ac2029585dd4a349897aa1e8298d26537c3b57453f275126db91918906020016129ae565b6000612af48133613778565b61140b8261438b565b6000612b098133613778565b816010612b168282616283565b9050507f626084554ed3d487b613abf59611e713f22caa8095d0c7b0d16b226ea45c3bc4826040516129ae9190616340565b612b5233836134de565b612b6e5760405162461bcd60e51b8152600401610ae790615f99565b612b7a8484848461448e565b50505050565b6000612b8c8133613778565b612b97601854613c5a565b61140b82613a55565b60145460405163c189c19b60e01b8152600481018390526000916001600160a01b03169063c189c19b906024015b60206040518083038186803b158015612be657600080fd5b505afa158015612bfa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ef09190615f80565b601c6020528260005260406000206020528160005260406000208181548110612c4657600080fd5b60009182526020909120600490910201805460018201546002830154600390930154919550935090915084565b6000612c7f8133613778565b61140b826144c1565b60105460405163c48ce05b60e01b8152600481018390526000916001600160a01b03169063c48ce05b90602401612bce565b6000818152600460205260409020546060906001600160a01b0316612d395760405162461bcd60e51b815260206004820152602f60248201527f4552433732314d657461646174613a2055524920717565727920666f72206e6f60448201526e3732bc34b9ba32b73a103a37b5b2b760891b6064820152608401610ae7565b6000612d5060408051602081019091526000815290565b90506000815111612d7057604051806020016040528060008152506123c5565b80612d7a84614572565b604051602001612d8b9291906163f2565b6040516020818303038152906040529392505050565b6000828152600c6020526040902060010154612dbd8133613778565b61112e8383613862565b6000612dd38133613778565b8215612df65760115460175461112e916001600160a01b0391821691168461466f565b60115460175461112e916001600160a01b03918216911684614731565b600e8054612e2090615f4b565b80601f0160208091040260200160405190810160405280929190818152602001828054612e4c90615f4b565b8015612e995780601f10612e6e57610100808354040283529160200191612e99565b820191906000526020600020905b815481529060010190602001808311612e7c57829003601f168201915b505050505081565b60008060026000541415612ec75760405162461bcd60e51b8152600401610ae790615e36565b6002600055612ed46132eb565b612edc613311565b612ee584613c8a565b6018546000612ef382611845565b915050804210612f1657604051632fd8225d60e21b815260040160405180910390fd5b6000828152601d60205260408120600601805489908110612f3957612f39615e6d565b90600052602060002001549050612f4f81613377565b612f58816147d6565b6000838152601e6020908152604080832084845290915281206002810154600190910154612f869190615ee2565b600f5490915060009060ff16612fdc57601a546000868152601d60205260409020600501546a52b7d2dcc80cd2e40000009190612fc3908c615e99565b612fcd9190615e99565b612fd79190615ece565b613018565b670de0b6b3a7640000612fed6132a6565b601a54612ffa868d615e99565b6130049190615e99565b61300e9190615ece565b6130189190615ece565b90508181111561303b576040516302b0eba760e21b815260040160405180910390fd5b613046838a86611133565b9650613052838a6128ba565b955061305f83828961490e565b613082333061306e898b615ef9565b6017546001600160a01b0316929190613cab565b60115460405163534a7e1d60e11b8152600481018990526131119185916001600160a01b039091169063a694fc3a90602401600060405180830381600087803b1580156130ce57600080fd5b505af11580156130e2573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261310a91908101906161de565b6001613ce3565b506000858152601e602090815260408083208684529091528120600201805483929061313e908490615ef9565b90915550506000858152601e602090815260408083208684529091528120600301805489929061316f908490615ef9565b90915550506000858152601d602052604081206004018054899290613195908490615ef9565b90915550506015546017546131b7916001600160a01b039182169116886133c7565b6000858152601e60209081526040808320868452909152908190205490516340c10f1960e01b81526001600160a01b038a81166004830152602482018c9052909116906340c10f1990604401600060405180830381600087803b15801561321d57600080fd5b505af1158015613231573d6000803e3d6000fd5b505060408051888152602081018790529081018c9052606081018a9052608081018990523392506001600160a01b038b1691507f96d7fd34a786c8810a555ee775a241bf4b8f243f8e64aaf266ec5398d1199b0c9060a00160405180910390a350505050506001600081905550935093915050565b60135460408051633dda0eef60e21b815290516000926001600160a01b03169163f7683bbc916004808301926020929190829003018186803b1580156114fd57600080fd5b60015460ff161561330f5760405163ab35696f60e01b815260040160405180910390fd5b565b33321461330f57336000908152600d602052604090205460ff1661330f5760405162461bcd60e51b815260206004820152601c60248201527f436f6e7472616374206d7573742062652077686974656c6973746564000000006044820152606401610ae7565b60008111611423576040516329be7c4760e11b815260040160405180910390fd5b6000818152601d602052604090205460ff166114235760405163652399f960e11b815260040160405180910390fd5b6040516001600160a01b03831660248201526044810182905261112e90849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152614bdf565b60006001600160e01b03198216637965db0b60e01b1480610ef05750610ef082614cb1565b600081815260066020526040902080546001600160a01b0319166001600160a01b0384169081179091558190613484826115cd565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b6000811161142357604051630cba69cb60e01b815260040160405180910390fd5b6000818152600460205260408120546001600160a01b03166135575760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a206f70657261746f7220717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b6064820152608401610ae7565b6000613562836115cd565b9050806001600160a01b0316846001600160a01b0316148061359d5750836001600160a01b031661359284610f88565b6001600160a01b0316145b8061122d57506001600160a01b0380821660009081526007602090815260408083209388168352929052205460ff1661122d565b826001600160a01b03166135e4826115cd565b6001600160a01b0316146136485760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201526437bbb732b960d91b6064820152608401610ae7565b6001600160a01b0382166136aa5760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b6064820152608401610ae7565b6136b5838383614cd6565b6136c060008261344f565b6001600160a01b03831660009081526005602052604081208054600192906136e9908490615ee2565b90915550506001600160a01b0382166000908152600560205260408120805460019290613717908490615ef9565b909155505060008181526004602052604080822080546001600160a01b0319166001600160a01b0386811691821790925591518493918716917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b613782828261288f565b61140b5761379a816001600160a01b03166014614ce9565b6137a5836020614ce9565b6040516020016137b6929190616421565b60408051601f198184030181529082905262461bcd60e51b8252610ae791600401615759565b6137e6828261288f565b61140b576000828152600c602090815260408083206001600160a01b03851684529091529020805460ff1916600117905561381e3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b61386c828261288f565b1561140b576000828152600c602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6138d1613b0d565b6001805460ff191690556040513381527f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa906020015b60405180910390a1565b600061391c826115cd565b905061392a81600084614cd6565b61393560008361344f565b6001600160a01b038116600090815260056020526040812080546001929061395e908490615ee2565b909155505060008281526004602052604080822080546001600160a01b0319169055518391906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b6000604051733d602d80600a3d3981f3363d3d373d3d3d363d7360601b81528260601b60148201526e5af43d82803e903d91602b57fd5bf360881b60288201526037816000f09150506001600160a01b038116613a505760405162461bcd60e51b8152602060048201526016602482015275115490cc4c4d8dce8818dc99585d194819985a5b195960521b6044820152606401610ae7565b919050565b60026000541415613a785760405162461bcd60e51b8152600401610ae790615e36565b6002600055613a856132eb565b613a90601854613c5a565b6018546000908152601d60205260409020600301819055613aaf613b74565b6018546000908152601d6020908152604091829020805460ff1916600117905581513381529081018390527feebcafefe23aabb68b4dab0cabce2ec0228ff1d73f042e91acdd09efd17845b2910160405180910390a1506001600055565b60015460ff1661330f5760405163dcdde9dd60e01b815260040160405180910390fd5b613b386132eb565b6001805460ff1916811790556040513381527f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25890602001613907565b6000613b81601854611235565b90506000601060010160009054906101000a90046001600160a01b03166001600160a01b0316632def66206040518163ffffffff1660e01b8152600401600060405180830381600087803b158015613bd857600080fd5b505af1158015613bec573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613c1491908101906161de565b905060005b825181101561112e57613c47838281518110613c3757613c37615e6d565b6020026020010151836001613ce3565b5080613c5281615f11565b915050613c19565b6000818152601d602052604090205460ff161561142357604051632fd8225d60e21b815260040160405180910390fd5b6000811161142357604051633c4117f160e21b815260040160405180910390fd5b6040516001600160a01b0380851660248301528316604482015260648101829052612b7a9085906323b872dd60e01b906084016133f3565b606082516001600160401b03811115613cfe57613cfe615948565b604051908082528060200260200182016040528015613d27578160200160208202803683370190505b5090506000805b8451811015614090576018546000908152601d60205260409020600701805482908110613d5d57613d5d615e6d565b9060005260206000200154858281518110613d7a57613d7a615e6d565b6020026020010151613d8c9190615ee2565b9150848181518110613da057613da0615e6d565b6020026020010151601d600060185481526020019081526020016000206007018281548110613dd157613dd1615e6d565b60009182526020808320909101929092556018548152601d9091526040902060040154613e1d576000838281518110613e0c57613e0c615e6d565b602002602001018181525050613e6d565b6018546000908152601d6020526040902060040154613e4483670de0b6b3a7640000615e99565b613e4e9190615ece565b838281518110613e6057613e60615e6d565b6020026020010181815250505b828181518110613e7f57613e7f615e6d565b6020026020010151601d600060185481526020019081526020016000206008018281548110613eb057613eb0615e6d565b906000526020600020016000828254613ec99190615ef9565b90915550508315614030576018546000908152601e602090815260408083208984529091529020600381015460069091018054670de0b6b3a764000092919084908110613f1857613f18615e6d565b9060005260206000200154601d600060185481526020019081526020016000206008018481548110613f4c57613f4c615e6d565b9060005260206000200154613f619190615ee2565b613f6b9190615e99565b613f759190615ece565b6018546000908152601e602090815260408083208a84529091529020600501805483908110613fa657613fa6615e6d565b906000526020600020016000828254613fbf9190615ef9565b90915550506018546000908152601d60205260409020600801805482908110613fea57613fea615e6d565b60009182526020808320909101546018548352601e825260408084208a8552909252912060060180548390811061402357614023615e6d565b6000918252602090912001555b6018546000908152601d6020526040902060080180548290811061405657614056615e6d565b906000526020600020015483828151811061407357614073615e6d565b60209081029190910101528061408881615f11565b915050613d2e565b50509392505050565b6018546000818152601c60209081526040808320868452909152812054909190614143576000818152601c60209081526040808320878452808352818420825160808101845242815280850186815293810189815260608201878152835460018181018655858a52888a2094516004909202909401908155955186840155905160028601555160039094019390935593889052909152905461413b9190615ee2565b915050610ef0565b61414c846147d6565b6000818152601c602090815260408083208784529091528120805461417390600190615ee2565b8154811061418357614183615e6d565b90600052602060002090600402016040518060800160405290816000820154815260200160018201548152602001600282015481526020016003820154815250509050604051806080016040528082600001518152602001826020015181526020018583604001516141f59190615ef9565b815260608301516020918201526000848152601c8252604080822089835290925220805461422590600190615ee2565b8154811061423557614235615e6d565b600091825260208083208451600490930201918255838101516001808401919091556040808601516002850155606090950151600390930192909255858352601c815283832089845290529190205461428e9190615ee2565b95945050505050565b60006142a260205490565b90506142b2602080546001019055565b613a508282614e84565b816001600160a01b0316836001600160a01b0316141561431e5760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c6572000000000000006044820152606401610ae7565b6001600160a01b03838116600081815260076020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b803b6143d95760405162461bcd60e51b815260206004820152601a60248201527f41646472657373206d757374206265206120636f6e74726163740000000000006044820152606401610ae7565b6001600160a01b0381166000908152600d602052604090205460ff16156144425760405162461bcd60e51b815260206004820152601c60248201527f436f6e747261637420616c72656164792077686974656c6973746564000000006044820152606401610ae7565b6001600160a01b0381166000818152600d6020526040808220805460ff19166001179055517ffbd3cde7ff522a917e485c8ed2a6e87590887ab399f5ac312307903f498543079190a250565b6144998484846135d1565b6144a584848484614e9e565b612b7a5760405162461bcd60e51b8152600401610ae790616496565b6001600160a01b0381166000908152600d602052604090205460ff166145295760405162461bcd60e51b815260206004820152601860248201527f436f6e7472616374206e6f742077686974656c697374656400000000000000006044820152606401610ae7565b6001600160a01b0381166000818152600d6020526040808220805460ff19169055517f8e81447740597754af5db3e176253a36f7981a9549f48ace3f0cb233913f9d859190a250565b6060816145965750506040805180820190915260018152600360fc1b602082015290565b8160005b81156145c057806145aa81615f11565b91506145b99050600a83615ece565b915061459a565b6000816001600160401b038111156145da576145da615948565b6040519080825280601f01601f191660200182016040528015614604576020820181803683370190505b5090505b841561122d57614619600183615ee2565b9150614626600a866164e8565b614631906030615ef9565b60f81b81838151811061464657614646615e6d565b60200101906001600160f81b031916908160001a905350614668600a86615ece565b9450614608565b604051636eb1769f60e11b81523060048201526001600160a01b0383811660248301526000916146fe9184919087169063dd62ed3e9060440160206040518083038186803b1580156146c057600080fd5b505afa1580156146d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146f89190615f80565b90614fab565b6040516001600160a01b038516602482015260448101829052909150612b7a90859063095ea7b360e01b906064016133f3565b60006146fe826040518060600160405280602981526020016165b360299139604051636eb1769f60e11b81523060048201526001600160a01b03878116602483015288169063dd62ed3e9060440160206040518083038186803b15801561479757600080fd5b505afa1580156147ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906147cf9190615f80565b9190614fb7565b6018546000818152601c602090815260408083208584529091528120805461480090600190615ee2565b8154811061481057614810615e6d565b9060005260206000209060040201604051806080016040529081600082015481526020016001820154815260200160028201548152602001600382015481525050905042601b5482600001516148669190615ef9565b101561112e576040808201516000848152601e6020908152838220878352905291822060010180549192909161489d908490615ef9565b9091555050506000908152601c6020908152604080832093835292815282822083516080810185524281528083018481529481018481526060820185815283546001808201865594875294909520915160049094029091019283559351908201559151600283015551600390910155565b6149396040518060800160405280600081526020016000815260200160008152602001600081525090565b60185460008080808761495488670de0b6b3a7640000615e99565b61495e9190615ece565b6000868152601e602090815260408083208d84529091529020600401549091505b88851015614bd3576000868152601c602090815260408083208d845290915290208054829081106149b2576149b2615e6d565b906000526020600020906004020160405180608001604052908160008201548152602001600182015481526020016002820154815260200160038201548152505096508489614a019190615ee2565b925086602001518760400151614a179190615ee2565b9350828410614af357614a2a8386615ef9565b6000878152601c602090815260408083208e845290915290208054919650849183908110614a5a57614a5a615e6d565b90600052602060002090600402016001016000828254614a7a9190615ef9565b90915550670de0b6b3a76400009050614a938385615e99565b614a9d9190615ece565b6000878152601c602090815260408083208e84529091529020805483908110614ac857614ac8615e6d565b90600052602060002090600402016003016000828254614ae89190615ef9565b9091555061497f9050565b614afd8486615ef9565b6000878152601c602090815260408083208e845290915290208054919650859183908110614b2d57614b2d615e6d565b90600052602060002090600402016001016000828254614b4d9190615ef9565b90915550670de0b6b3a76400009050614b668386615e99565b614b709190615ece565b6000878152601c602090815260408083208e84529091529020805483908110614b9b57614b9b615e6d565b90600052602060002090600402016003016000828254614bbb9190615ef9565b90915550614bcc9050600182615ef9565b905061497f565b50505050505050505050565b6000614c34826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614fe39092919063ffffffff16565b80519091501561112e5780806020019051810190614c5291906164fc565b61112e5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610ae7565b60006001600160e01b0319821663780e9d6360e01b1480610ef05750610ef082614ff2565b614cde6132eb565b61112e838383615042565b60606000614cf8836002615e99565b614d03906002615ef9565b6001600160401b03811115614d1a57614d1a615948565b6040519080825280601f01601f191660200182016040528015614d44576020820181803683370190505b509050600360fc1b81600081518110614d5f57614d5f615e6d565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110614d8e57614d8e615e6d565b60200101906001600160f81b031916908160001a9053506000614db2846002615e99565b614dbd906001615ef9565b90505b6001811115614e35576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110614df157614df1615e6d565b1a60f81b828281518110614e0757614e07615e6d565b60200101906001600160f81b031916908160001a90535060049490941c93614e2e81616519565b9050614dc0565b5083156123c55760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610ae7565b61140b8282604051806020016040528060008152506150fa565b60006001600160a01b0384163b15614fa057604051630a85bd0160e11b81526001600160a01b0385169063150b7a0290614ee2903390899088908890600401616530565b602060405180830381600087803b158015614efc57600080fd5b505af1925050508015614f2c575060408051601f3d908101601f19168201909252614f2991810190616563565b60015b614f86573d808015614f5a576040519150601f19603f3d011682016040523d82523d6000602084013e614f5f565b606091505b508051614f7e5760405162461bcd60e51b8152600401610ae790616496565b805181602001fd5b6001600160e01b031916630a85bd0160e11b14905061122d565b506001949350505050565b60006123c58284615ef9565b60008184841115614fdb5760405162461bcd60e51b8152600401610ae79190615759565b505050900390565b606061122d848460008561512d565b60006001600160e01b031982166380ac58cd60e01b148061502357506001600160e01b03198216635b5e139f60e01b145b80610ef057506301ffc9a760e01b6001600160e01b0319831614610ef0565b6001600160a01b03831661509d5761509881600a80546000838152600b60205260408120829055600182018355919091527fc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a80155565b6150c0565b816001600160a01b0316836001600160a01b0316146150c0576150c0838261525e565b6001600160a01b0382166150d75761112e816152fb565b826001600160a01b0316826001600160a01b03161461112e5761112e82826153aa565b61510483836153ee565b6151116000848484614e9e565b61112e5760405162461bcd60e51b8152600401610ae790616496565b60608247101561518e5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610ae7565b6001600160a01b0385163b6151e55760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610ae7565b600080866001600160a01b031685876040516152019190616580565b60006040518083038185875af1925050503d806000811461523e576040519150601f19603f3d011682016040523d82523d6000602084013e615243565b606091505b509150915061525382828661553c565b979650505050505050565b6000600161526b84611870565b6152759190615ee2565b6000838152600960205260409020549091508082146152c8576001600160a01b03841660009081526008602090815260408083208584528252808320548484528184208190558352600990915290208190555b5060009182526009602090815260408084208490556001600160a01b039094168352600881528383209183525290812055565b600a5460009061530d90600190615ee2565b6000838152600b6020526040812054600a805493945090928490811061533557615335615e6d565b9060005260206000200154905080600a838154811061535657615356615e6d565b6000918252602080832090910192909255828152600b9091526040808220849055858252812055600a80548061538e5761538e61659c565b6001900381819060005260206000200160009055905550505050565b60006153b583611870565b6001600160a01b039093166000908152600860209081526040808320868452825280832085905593825260099052919091209190915550565b6001600160a01b0382166154445760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152606401610ae7565b6000818152600460205260409020546001600160a01b0316156154a95760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606401610ae7565b6154b560008383614cd6565b6001600160a01b03821660009081526005602052604081208054600192906154de908490615ef9565b909155505060008181526004602052604080822080546001600160a01b0319166001600160a01b03861690811790915590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b6060831561554b5750816123c5565b82511561555b5782518084602001fd5b8160405162461bcd60e51b8152600401610ae79190615759565b8280548282559060005260206000209081019282156155b0579160200282015b828111156155b0578251825591602001919060010190615595565b506155bc929150615615565b5090565b8280548282559060005260206000209081019282156155b0579160200282015b828111156155b057825182546001600160a01b0319166001600160a01b039091161782556020909201916001909101906155e0565b5b808211156155bc5760008155600101615616565b6001600160a01b038116811461142357600080fd5b8035613a508161562a565b6000806040838503121561565d57600080fd5b82359150602083013561566f8161562a565b809150509250929050565b600081518084526020808501945080840160005b838110156156aa5781518752958201959082019060010161568e565b509495945050505050565b82815260406020820152600061122d604083018461567a565b6001600160e01b03198116811461142357600080fd5b6000602082840312156156f657600080fd5b81356123c5816156ce565b60005b8381101561571c578181015183820152602001615704565b83811115612b7a5750506000910152565b60008151808452615745816020860160208601615701565b601f01601f19169290920160200192915050565b6020815260006123c5602083018461572d565b60006020828403121561577e57600080fd5b5035919050565b6000806040838503121561579857600080fd5b82356157a38161562a565b946020939093013593505050565b6000806000606084860312156157c657600080fd5b505081359360208301359350604090920135919050565b6020815260006123c5602083018461567a565b60006020828403121561580257600080fd5b81356123c58161562a565b60008060006060848603121561582257600080fd5b833561582d8161562a565b9250602084013561583d8161562a565b929592945050506040919091013590565b6000806040838503121561586157600080fd5b50508035926020909101359150565b602080825282518282018190526000919060409081850190868401855b828110156158c75781518051855286810151878601528581015186860152606090810151908501526080909301929085019060010161588d565b5091979650505050505050565b6020815260018060a01b038251166020820152602082015160408201526040820151606082015260608201516080820152608082015160a0820152600060a083015160e060c084015261592b61010084018261567a565b905060c0840151601f198483030160e085015261428e828261567a565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b038111828210171561598657615986615948565b604052919050565b60006001600160401b038211156159a7576159a7615948565b5060051b60200190565b60006001600160401b038211156159ca576159ca615948565b50601f01601f191660200190565b60006159eb6159e6846159b1565b61595e565b90508281528383830111156159ff57600080fd5b828260208301376000602084830101529392505050565b600082601f830112615a2757600080fd5b6123c5838335602085016159d8565b600080600060608486031215615a4b57600080fd5b83356001600160401b0380821115615a6257600080fd5b818601915086601f830112615a7657600080fd5b81356020615a866159e68361598e565b82815260059290921b8401810191818101908a841115615aa557600080fd5b948201945b83861015615ac357853582529482019490820190615aaa565b9750508701359450506040860135915080821115615ae057600080fd5b50615aed86828701615a16565b9150509250925092565b801515811461142357600080fd5b600080600060408486031215615b1a57600080fd5b83356001600160401b0380821115615b3157600080fd5b818601915086601f830112615b4557600080fd5b813581811115615b5457600080fd5b8760208260051b8501011115615b6957600080fd5b60209283019550935050840135615b7f81615af7565b809150509250925092565b600081518084526020808501945080840160005b838110156156aa5781516001600160a01b031687529582019590820190600101615b9e565b60208152615bd660208201835115159052565b602082015160408201526040820151606082015260608201516080820152608082015160a082015260a082015160c0820152600060c08301516101408060e0850152615c2661016085018361567a565b915060e0850151601f19610100818786030181880152615c46858461567a565b945080880151925050610120818786030181880152615c65858461567a565b908801518782039092018488015293509050615c818382615b8a565b9695505050505050565b600080600060608486031215615ca057600080fd5b83359250602084013591506040840135615b7f8161562a565b60008060008060808587031215615ccf57600080fd5b8435935060208501359250604085013591506060850135615cef8161562a565b939692955090935050565b60008060408385031215615d0d57600080fd5b8235615d188161562a565b9150602083013561566f81615af7565b85815284602082015283604082015282606082015260a06080820152600061525360a083018461567a565b600060e08284031215615d6557600080fd5b50919050565b60008060008060808587031215615d8157600080fd5b8435615d8c8161562a565b93506020850135615d9c8161562a565b92506040850135915060608501356001600160401b03811115615dbe57600080fd5b8501601f81018713615dcf57600080fd5b615dde878235602084016159d8565b91505092959194509250565b60008060408385031215615dfd57600080fd5b82356157a381615af7565b60008060408385031215615e1b57600080fd5b8235615e268161562a565b9150602083013561566f8161562a565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000816000190483118215151615615eb357615eb3615e83565b500290565b634e487b7160e01b600052601260045260246000fd5b600082615edd57615edd615eb8565b500490565b600082821015615ef457615ef4615e83565b500390565b60008219821115615f0c57615f0c615e83565b500190565b6000600019821415615f2557615f25615e83565b5060010190565b83815282602082015260606040820152600061428e606083018461567a565b600181811c90821680615f5f57607f821691505b60208210811415615d6557634e487b7160e01b600052602260045260246000fd5b600060208284031215615f9257600080fd5b5051919050565b60208082526031908201527f4552433732313a207472616e736665722063616c6c6572206973206e6f74206f6040820152701ddb995c881b9bdc88185c1c1c9bdd9959607a1b606082015260800190565b60006020808385031215615ffd57600080fd5b82516001600160401b0381111561601357600080fd5b8301601f8101851361602457600080fd5b80516160326159e68261598e565b81815260059190911b8201830190838101908783111561605157600080fd5b928401925b828410156152535783516160698161562a565b82529284019290840190616056565b60006020828403121561608a57600080fd5b81516001600160401b038111156160a057600080fd5b8201601f810184136160b157600080fd5b80516160bf6159e6826159b1565b8181528560208385010111156160d457600080fd5b61428e826020830160208601615701565b60018060a01b0388168152600060208815158184015287604084015286606084015260e060808401526000865481600182811c91508083168061612957607f831692505b85831081141561614757634e487b7160e01b85526022600452602485fd5b60e0880183905261010088018180156161675760018114616178576161a3565b60ff198616825287820196506161a3565b60008d81526020902060005b8681101561619d57815484820152908501908901616184565b83019750505b50505050505083810360a08501526161bb818761572d565b91505082810360c08401526161d0818561572d565b9a9950505050505050505050565b600060208083850312156161f157600080fd5b82516001600160401b0381111561620757600080fd5b8301601f8101851361621857600080fd5b80516162266159e68261598e565b81815260059190911b8201830190838101908783111561624557600080fd5b928401925b828410156152535783518252928401929084019061624a565b80546001600160a01b0319166001600160a01b0392909216919091179055565b813561628e8161562a565b6162988183616263565b5060208201356162a78161562a565b6162b48160018401616263565b5060408201356162c38161562a565b6162d08160028401616263565b5060608201356162df8161562a565b6162ec8160038401616263565b5060808201356162fb8161562a565b6163088160048401616263565b5060a08201356163178161562a565b6163248160058401616263565b5060c08201356163338161562a565b61112e8160068401616263565b60e08101823561634f8161562a565b6001600160a01b03908116835260208401359061636b8261562a565b90811660208401526040840135906163828261562a565b90811660408401526060840135906163998261562a565b90811660608401526080840135906163b08261562a565b1660808301526163c260a0840161563f565b6001600160a01b031660a08301526163dc60c0840161563f565b6001600160a01b03811660c08401525092915050565b60008351616404818460208801615701565b835190830190616418818360208801615701565b01949350505050565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351616459816017850160208801615701565b7001034b99036b4b9b9b4b733903937b6329607d1b601791840191820152835161648a816028840160208801615701565b01602801949350505050565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b6000826164f7576164f7615eb8565b500690565b60006020828403121561650e57600080fd5b81516123c581615af7565b60008161652857616528615e83565b506000190190565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090615c819083018461572d565b60006020828403121561657557600080fd5b81516123c5816156ce565b60008251616592818460208701615701565b9190910192915050565b634e487b7160e01b600052603160045260246000fdfe5361666545524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa26469706673582212206887d296dda2e6e5b3b20d8abd8ae9e41c77a94c3db4619b74baed194af042cc64736f6c63430008090033

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

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