ETH Price: $3,449.19 (+0.81%)
Gas: 10 Gwei

Contract

0x5E4DE6Bb8c6824f29c44Bd3473d44da120387d08
 

Overview

ETH Balance

0.015 ETH

Eth Value

$51.74 (@ $3,449.19/ETH)

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Activate Prover177142752023-07-17 16:49:35365 days ago1689612575IN
Relic Protocol: Reliquary
0 ETH0.0020271628.77735149
Activate Prover177142742023-07-17 16:49:23365 days ago1689612563IN
Relic Protocol: Reliquary
0 ETH0.0020804229.53842027
Activate Prover177142732023-07-17 16:49:11365 days ago1689612551IN
Relic Protocol: Reliquary
0 ETH0.0020443429.0212863
Activate Prover177142722023-07-17 16:48:59365 days ago1689612539IN
Relic Protocol: Reliquary
0 ETH0.0021708830.81759738
Activate Prover177142712023-07-17 16:48:47365 days ago1689612527IN
Relic Protocol: Reliquary
0 ETH0.0019469827.63912383
Activate Prover177142702023-07-17 16:48:35365 days ago1689612515IN
Relic Protocol: Reliquary
0 ETH0.0020482629.07687087
Activate Prover177142692023-07-17 16:48:23365 days ago1689612503IN
Relic Protocol: Reliquary
0 ETH0.0021885931.06897477
Activate Prover177142682023-07-17 16:48:11365 days ago1689612491IN
Relic Protocol: Reliquary
0 ETH0.0021004429.81761073
Activate Prover177142672023-07-17 16:47:59365 days ago1689612479IN
Relic Protocol: Reliquary
0 ETH0.0022170131.4724796
Activate Prover177142662023-07-17 16:47:47365 days ago1689612467IN
Relic Protocol: Reliquary
0 ETH0.0021966731.18366338
Activate Prover177142652023-07-17 16:47:35365 days ago1689612455IN
Relic Protocol: Reliquary
0 ETH0.0023204932.94709773
Activate Prover163568392023-01-07 18:56:35556 days ago1673117795IN
Relic Protocol: Reliquary
0 ETH0.0010817615.35655832
Activate Prover163568372023-01-07 18:56:11556 days ago1673117771IN
Relic Protocol: Reliquary
0 ETH0.0010276714.58880328
Activate Prover163568352023-01-07 18:55:47556 days ago1673117747IN
Relic Protocol: Reliquary
0 ETH0.0011023115.64828326
Activate Prover163568332023-01-07 18:55:23556 days ago1673117723IN
Relic Protocol: Reliquary
0 ETH0.0010249414.5500516
Activate Prover163568272023-01-07 18:54:11556 days ago1673117651IN
Relic Protocol: Reliquary
0 ETH0.0011195815.89355036
Activate Prover163568222023-01-07 18:53:11556 days ago1673117591IN
Relic Protocol: Reliquary
0 ETH0.0012425417.64203718
Activate Prover157420352022-10-13 22:01:11642 days ago1665698471IN
Relic Protocol: Reliquary
0 ETH0.0016851423.92210256
Revoke Role156403072022-09-29 16:51:47656 days ago1664470307IN
Relic Protocol: Reliquary
0 ETH0.0008055627.35841453
Revoke Role156403042022-09-29 16:51:11656 days ago1664470271IN
Relic Protocol: Reliquary
0 ETH0.0007677726.07500893
Grant Role156399532022-09-29 15:40:23656 days ago1664466023IN
Relic Protocol: Reliquary
0 ETH0.0010022419.49558702
Grant Role156399512022-09-29 15:39:59656 days ago1664465999IN
Relic Protocol: Reliquary
0 ETH0.0009333418.15520907
Grant Role156399402022-09-29 15:37:47656 days ago1664465867IN
Relic Protocol: Reliquary
0 ETH0.0008364916.39376714
Set Initialized155723452022-09-20 4:34:11666 days ago1663648451IN
Relic Protocol: Reliquary
0 ETH0.000274596
Set Prover Fee155723452022-09-20 4:34:11666 days ago1663648451IN
Relic Protocol: Reliquary
0 ETH0.000329186
View all transactions

Latest 3 internal transactions

Advanced mode:
Parent Transaction Hash Block From To
164215102023-01-16 19:41:59547 days ago1673898119
Relic Protocol: Reliquary
0.005 ETH
164208592023-01-16 17:31:11547 days ago1673890271
Relic Protocol: Reliquary
0.005 ETH
164092492023-01-15 2:33:23549 days ago1673750003
Relic Protocol: Reliquary
0.005 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ReliquaryWithFee

Compiler Version
v0.8.12+commit.f00d7308

Optimization Enabled:
Yes with 1000 runs

Other Settings:
default evmVersion, Unlicense license
File 1 of 16 : ReliquaryWithFee.sol
/// SPDX-License-Identifier: UNLICENSED
/// (c) Theori, Inc. 2022
/// All rights reserved

pragma solidity >=0.8.12;

import "@openzeppelin/contracts/interfaces/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./Reliquary.sol";

struct FeeAccount {
    uint64 subscriberUntilTime;
    uint192 credits;
}

/**
 * @title Holder of Relics and Artifacts with fees
 * @author Theori, Inc.
 * @notice The Reliquary is the heart of Relic. All issuers of Relics and Artifacts
 *         must be added to the Reliquary. Queries about Relics and Artifacts should
 *         be made to the Reliquary. This Reliquary may charge fees for proving or
 *         accessing facts.
 */
contract ReliquaryWithFee is Reliquary {
    using SafeERC20 for IERC20;

    bytes32 public constant CREDITS_ROLE = keccak256("CREDITS_ROLE");
    bytes32 public constant SUBSCRIPTION_ROLE = keccak256("SUBSCRIPTION_ROLE");

    /// Mapping of fact classes to fee infos about them
    mapping(uint8 => FeeInfo) public factFees;
    /// Information about fee accounts (credits & subscriptions)
    mapping(address => FeeAccount) public feeAccounts;
    /// External tokens and fee delegates for use by
    mapping(uint256 => address) public feeExternals;
    uint32 internal feeExternalsCount;
    /// FeeInfo struct for block queries
    FeeInfo public verifyBlockFeeInfo;

    constructor() Reliquary() {}

    /**
     * @notice Query for associated information for a fact
     * @param account The address to which the fact belongs
     * @param factSig The unique signature identifying the fact
     * @return exists whether or not a fact with the given signature
     *         is associated with the queried account
     * @return version the prover version id that proved this fact
     * @return data any associated fact data
     * @dev A fee may be required based on the factSig
     */
    function verifyFact(address account, FactSignature factSig)
        external
        payable
        returns (
            bool exists,
            uint64 version,
            bytes memory data
        )
    {
        checkVerifyFactFee(factSig);
        return _verifyFact(account, factSig);
    }

    /**
     * @notice Query for some information for a fact
     * @param account The address to which the fact belongs
     * @param factSig The unique signature identifying the fact
     * @return exists whether or not a fact with the given signature
     *         is associated with the queried account
     * @return version the prover version id that proved this fact
     * @dev A fee may be required based on the factSig
     */
    function verifyFactVersion(address account, FactSignature factSig)
        external
        payable
        returns (bool exists, uint64 version)
    {
        checkVerifyFactFee(factSig);
        return _verifyFactVersion(account, factSig);
    }

    /**
     * @notice Verify if a particular block had a particular hash
     * @param verifier The block history verifier to use for the query
     * @param hash The block hash in question
     * @param num The block number to query
     * @param proof Any witness information needed by the verifier
     * @return boolean indication of whether or not the given block was
     *         proven to have the given hash.
     * @dev A fee may be required based on the block in question
     */
    function validBlockHash(
        address verifier,
        bytes32 hash,
        uint256 num,
        bytes calldata proof
    ) public payable returns (bool) {
        checkValidBlockFee(num);

        // validBlockHash is a view function, so it cannot modify state and is safe to call
        return IBlockHistory(verifier).validBlockHash(hash, num, proof);
    }

    /**
     * @notice Asserts that a particular block had a particular hash
     * @param verifier The block history verifier to use for the query
     * @param hash The block hash in question
     * @param num The block number to query
     * @param proof Any witness information needed by the verifier
     * @dev Reverts if the given block was not proven to have the given hash.
     * @dev A fee may be required based on the block in question
     */
    function assertValidBlockHash(
        address verifier,
        bytes32 hash,
        uint256 num,
        bytes calldata proof
    ) external payable {
        require(validBlockHash(verifier, hash, num, proof), "invalid block hash");
    }

    /**
     * @notice Helper function to calculate fee for a given feeInfo struct
     * @param feeInfo The FeeInfo struct in question
     * @return The associated fee in wei
     */
    function getFeeWei(FeeInfo memory feeInfo) internal pure returns (uint256) {
        return feeInfo.feeWeiMantissa * (10**feeInfo.feeWeiExponent);
    }

    /**
     * @notice Require that an appropriate fee is paid for queries
     * @param sender The initiator of the query
     * @param feeInfo The FeeInfo struct associated with the query
     * @param data Opaque data that may be needed by downstream fee functions
     * @dev Reverts if the fee is not sufficient
     */
    function checkFeeInfo(
        address sender,
        FeeInfo memory feeInfo,
        bytes memory data
    ) internal {
        uint256 feeWei = getFeeWei(feeInfo);

        if (feeInfo.flags & (1 << uint256(FeeFlags.FeeNone)) != 0) {
            return;
        }

        if (feeInfo.flags & (1 << uint256(FeeFlags.FeeNative)) != 0) {
            if (msg.value >= feeWei) {
                return;
            }
        }

        if (feeInfo.flags & (1 << uint256(FeeFlags.FeeCredits)) != 0) {
            FeeAccount memory feeAccount = feeAccounts[sender];
            // check if sender has a valid subscription
            if (feeAccount.subscriberUntilTime > block.timestamp) {
                return;
            }
            // otherwise subtract credits
            if (feeAccount.credits >= feeInfo.feeCredits) {
                feeAccount.credits -= feeInfo.feeCredits;
                feeAccounts[sender] = feeAccount;
                return;
            }
        }

        if (feeInfo.flags & (1 << uint256(FeeFlags.FeeExternalDelegate)) != 0) {
            require(feeInfo.feeExternalId != 0);
            address delegate = feeExternals[feeInfo.feeExternalId];
            require(delegate != address(0));
            IFeeDelegate(delegate).checkFee{value: msg.value}(sender, data);
            return;
        }

        if (feeInfo.flags & (1 << uint256(FeeFlags.FeeExternalToken)) != 0) {
            require(feeInfo.feeExternalId != 0);
            address token = feeExternals[feeInfo.feeExternalId];
            require(token != address(0));
            IERC20(token).safeTransferFrom(sender, address(this), feeWei);
            return;
        }

        revert("insufficient fee");
    }

    /**
     * @notice Require that an appropriate fee is paid for verify fact queries
     * @param factSig The signature of the desired fact
     * @dev Reverts if the fee is not sufficient
     * @dev Only to be used internally
     */
    function checkVerifyFactFee(FactSignature factSig) internal {
        uint8 cls = Facts.toFactClass(factSig);
        if (cls != Facts.NO_FEE) {
            checkFeeInfo(msg.sender, factFees[cls], abi.encode("verifyFact", factSig));
        }
    }

    /**
     * @notice Require that an appropriate fee is paid for proving a fact
     * @param sender The account wanting to prove a fact
     * @dev The fee is derived from the prover which calls this  function
     * @dev Reverts if the fee is not sufficient
     * @dev Only to be called by a prover
     */
    function checkProveFactFee(address sender) external payable {
        address prover = msg.sender;
        ProverInfo memory proverInfo = provers[prover];
        checkProver(proverInfo);

        checkFeeInfo(sender, proverInfo.feeInfo, abi.encode("proveFact", prover));
    }

    /**
     * @notice Require that an appropriate fee is paid for verify block queries
     * @param blockNum The block number to verify
     * @dev Reverts if the fee is not sufficient
     * @dev Only to be used internally
     */
    function checkValidBlockFee(uint256 blockNum) internal {
        checkFeeInfo(msg.sender, verifyBlockFeeInfo, abi.encode("verifyBlock", blockNum));
    }

    // Functions to help callers determine how much fee they need to pay
    /**
     * @notice Determine the appropriate ETH fee to query a fact
     * @param factSig The signature of the desired fact
     * @return the fee in wei
     * @dev Reverts if the fee is not to be paid in ETH
     */
    function getVerifyFactNativeFee(FactSignature factSig) external view returns (uint256) {
        uint8 cls = Facts.toFactClass(factSig);
        if (cls == Facts.NO_FEE) {
            return 0;
        }
        FeeInfo memory feeInfo = factFees[cls];
        require(feeInfo.flags & (1 << uint256(FeeFlags.FeeNative)) != 0);
        return getFeeWei(feeInfo);
    }

    /**
     * @notice Determine the appropriate token fee to query a fact
     * @param factSig The signature of the desired fact
     * @return the fee in wei
     * @dev Reverts if the fee is not to be paid in external tokens
     */
    function getVerifyFactTokenFee(FactSignature factSig) external view returns (uint256) {
        uint8 cls = Facts.toFactClass(factSig);
        if (cls == Facts.NO_FEE) {
            return 0;
        }
        FeeInfo memory feeInfo = factFees[cls];
        require(feeInfo.flags & (1 << uint256(FeeFlags.FeeExternalToken)) != 0);
        return getFeeWei(feeInfo);
    }

    /**
     * @notice Determine the appropriate ETH fee to prove a fact
     * @param prover The prover of the desired fact
     * @return the fee in wei
     * @dev Reverts if the fee is not to be paid in ETH
     */
    function getProveFactNativeFee(address prover) external view returns (uint256) {
        ProverInfo memory proverInfo = provers[prover];
        checkProver(proverInfo);

        require(proverInfo.feeInfo.flags & (1 << uint256(FeeFlags.FeeNative)) != 0);
        return getFeeWei(proverInfo.feeInfo);
    }

    /**
     * @notice Determine the appropriate token fee to prove a fact
     * @param prover The prover of the desired fact
     * @return the fee in wei
     * @dev Reverts if the fee is not to be paid in external tokens
     */
    function getProveFactTokenFee(address prover) external view returns (uint256) {
        ProverInfo memory proverInfo = provers[prover];
        checkProver(proverInfo);

        require(proverInfo.feeInfo.flags & (1 << uint256(FeeFlags.FeeExternalToken)) != 0);
        return getFeeWei(proverInfo.feeInfo);
    }

    /**
     * @notice Check how many credits a given account possesses
     * @param user The account in question
     * @return The number of credits
     */
    function credits(address user) public view returns (uint192) {
        return feeAccounts[user].credits;
    }

    /**
     * @notice Check if an account has an active subscription
     * @param user The account in question
     * @return True if the account is active, otherwise false
     */
    function isSubscriber(address user) public view returns (bool) {
        return feeAccounts[user].subscriberUntilTime > block.timestamp;
    }

    /**
     * @notice Adds a new external fee provider (token or delegate) to a feeInfo
     * @param feeInfo The feeInfo to update with this provider
     * @dev Always appends to the global feeExternals
     */
    function _setFeeExternalId(FeeInfo memory feeInfo, address feeExternal)
        internal
        returns (FeeInfo memory)
    {
        uint32 feeExternalId = 0;
        if (feeExternal != address(0)) {
            feeExternalsCount++;
            feeExternalId = feeExternalsCount;
            feeExternals[feeExternalId] = feeExternal;
        }
        feeInfo.feeExternalId = feeExternalId;
        return feeInfo;
    }

    /**
     * @notice Sets the FeeInfo for a particular fee class
     * @param cls The fee class
     * @param feeInfo The FeeInfo to use for the class
     * @param feeExternal An external fee provider (token or delegate). If
     *        none is required, this should be set to 0.
     */
    function setFactFee(
        uint8 cls,
        FeeInfo memory feeInfo,
        address feeExternal
    ) external onlyRole(GOVERNANCE_ROLE) {
        feeInfo = _setFeeExternalId(feeInfo, feeExternal);
        factFees[cls] = feeInfo;
    }

    /**
     * @notice Sets the FeeInfo for a particular prover
     * @param prover The prover in question
     * @param feeInfo The FeeInfo to use for the class
     * @param feeExternal An external fee provider (token or delegate). If
     *        none is required, this should be set to 0.
     */
    function setProverFee(
        address prover,
        FeeInfo memory feeInfo,
        address feeExternal
    ) external onlyRole(GOVERNANCE_ROLE) {
        ProverInfo memory proverInfo = provers[prover];
        checkProver(proverInfo);

        feeInfo = _setFeeExternalId(feeInfo, feeExternal);
        proverInfo.feeInfo = feeInfo;
        provers[prover] = proverInfo;
    }

    /**
     * @notice Sets the FeeInfo for block verification
     * @param feeInfo The FeeInfo to use for the class
     * @param feeExternal An external fee provider (token or delegate). If
     *        none is required, this should be set to 0.
     */
    function setValidBlockFee(FeeInfo memory feeInfo, address feeExternal)
        external
        onlyRole(GOVERNANCE_ROLE)
    {
        feeInfo = _setFeeExternalId(feeInfo, feeExternal);
        verifyBlockFeeInfo = feeInfo;
    }

    /**
     * @notice Add/update a subscription
     * @param user The subscriber account to modify
     * @param ts The new block timestamp at which the subscription expires
     */
    function addSubscriber(address user, uint64 ts) external onlyRole(SUBSCRIPTION_ROLE) {
        require(feeAccounts[user].subscriberUntilTime < ts);
        feeAccounts[user].subscriberUntilTime = ts;
    }

    /**
     * @notice Remove a subscription
     * @param user The subscriber account to modify
     */
    function removeSubscriber(address user) external onlyRole(SUBSCRIPTION_ROLE) {
        feeAccounts[user].subscriberUntilTime = 0;
    }

    /**
     * @notice Set credits for an account
     * @param user The account for which credits should be set
     * @param amount The credits that the account should be updated to hold
     */
    function setCredits(address user, uint192 amount) external onlyRole(CREDITS_ROLE) {
        feeAccounts[user].credits = amount;
    }

    /**
     * @notice Add credits to an account
     * @param user The account to which more credits should be granted
     * @param amount The number of credits to be added
     */
    function addCredits(address user, uint192 amount) external onlyRole(CREDITS_ROLE) {
        feeAccounts[user].credits += amount;
    }

    /**
     * @notice Remove credits from an account
     * @param user The account from which credits should be removed
     * @param amount The number of credits to be removed
     */
    function removeCredits(address user, uint192 amount) external onlyRole(CREDITS_ROLE) {
        feeAccounts[user].credits -= amount;
    }

    /**
     * @notice Extract accumulated fees
     * @param token The ERC20 token from which to extract fees. Or the 0 address for
     *        native ETH
     * @param dest The address to which fees should be transferred
     */
    function withdrawFees(address token, address payable dest) external onlyRole(GOVERNANCE_ROLE) {
        require(dest != address(0));

        if (token == address(0)) {
            dest.transfer(address(this).balance);
        } else {
            IERC20(token).transfer(dest, IERC20(token).balanceOf(address(this)));
        }
    }
}

File 2 of 16 : AccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.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);
        _;
    }

    /**
     * @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 `_msgSender()` is missing `role`.
     * Overriding this function changes the behavior of the {onlyRole} modifier.
     *
     * Format of the revert message is described in {_checkRole}.
     *
     * _Available since v4.6._
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @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.
     *
     * May emit a {RoleGranted} event.
     */
    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.
     *
     * May emit a {RoleRevoked} event.
     */
    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`.
     *
     * May emit a {RoleRevoked} event.
     */
    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.
     *
     * May emit a {RoleGranted} event.
     *
     * [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.
     *
     * May emit a {RoleGranted} event.
     */
    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.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, _msgSender());
        }
    }
}

File 3 of 16 : IAccessControl.sol
// 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;
}

File 4 of 16 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC20.sol)

pragma solidity ^0.8.0;

import "../token/ERC20/IERC20.sol";

File 5 of 16 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

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

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

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) external returns (bool);
}

File 6 of 16 : draft-IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)

pragma solidity ^0.8.0;

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

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

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

File 7 of 16 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/draft-IERC20Permit.sol";
import "../../../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 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'
        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) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

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

    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @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
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 8 of 16 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.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
                /// @solidity memory-safe-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 9 of 16 : Context.sol
// 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;
    }
}

File 10 of 16 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

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

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

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }
}

File 11 of 16 : ERC165.sol
// 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;
    }
}

File 12 of 16 : IERC165.sol
// 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);
}

File 13 of 16 : Reliquary.sol
/// SPDX-License-Identifier: UNLICENSED
/// (c) Theori, Inc. 2022
/// All rights reserved

pragma solidity >=0.8.12;

import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/interfaces/IERC20.sol";
import "./interfaces/IBlockHistory.sol";
import "./interfaces/IFeeDelegate.sol";
import "./lib/Facts.sol";

struct PendingProver {
    uint64 timestamp;
    uint64 version;
}

struct ProverInfo {
    uint64 version;
    FeeInfo feeInfo;
    bool revoked;
}

// It would be nice if these could be stored in ReliquaryWithFee, but we want
// to store fee information in ProverInfo for gas optimization.
enum FeeFlags {
    FeeNone,
    FeeNative,
    FeeCredits,
    FeeExternalDelegate,
    FeeExternalToken
}

struct FeeInfo {
    uint8 flags;
    uint16 feeCredits;
    // feeWei = feeWeiMantissa * pow(10, feeWeiExponent)
    uint8 feeWeiMantissa;
    uint8 feeWeiExponent;
    uint32 feeExternalId;
}

/**
 * @title Holder of Relics and Artifacts
 * @author Theori, Inc.
 * @notice The Reliquary is the heart of Relic. All issuers of Relics and Artifacts
 *         must be added to the Reliquary. Queries about Relics and Artifacts should
 *         be made to the Reliquary.
 */
contract Reliquary is AccessControl {
    /// Minimum delay before a new prover can be active
    uint64 public constant DELAY = 2 days;

    bytes32 public constant ADD_PROVER_ROLE = keccak256("ADD_PROVER_ROLE");
    bytes32 public constant GOVERNANCE_ROLE = keccak256("GOVERNANCE_ROLE");

    /// Once initialized, new provers can only be added with a delay
    bool public initialized;
    /// Append-only map of added prover addresses to information on them
    mapping(address => ProverInfo) public provers;
    /**
     *  Reverse mapping of version information to the unique prover able
     *  to issue statements with that version
     */
    mapping(uint64 => address) public versions;
    /// List of provers that may be added to the Reliquary, for public scrutiny
    mapping(address => PendingProver) public pendingProvers;

    mapping(address => mapping(FactSignature => bytes)) internal provenFacts;

    constructor() {
        _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
    }

    /**
     * @notice Issued when a new prover is accepted into the Reliquary
     * @param prover the address of the prover contract
     * @param version the identifier that will always be associated with the prover
     */
    event NewProver(address prover, uint64 version);

    /**
     * @notice Issued when a new prover is placed under consideration for acceptance
     *         into the Reliquary
     * @param prover the address of the prover contract
     * @param version the proposed identifier to always be associated with the prover
     * @param timestamp the earliest this prover can be brought into the Reliquary
     */
    event PendingProverAdded(address prover, uint64 version, uint64 timestamp);

    /**
     * @notice Issued when an existing prover is banished from the Reliquary
     * @param prover the address of the prover contract
     * @param version the identifier that can never be used again
     * @dev revoked provers may not issue new Relics or Artifacts. The meaning of
     *      any previously introduced Relics or Artifacts is implementation dependent.
     */
    event ProverRevoked(address prover, uint64 version);

    /**
     * @notice Helper function to parse fact storage
     * @param fact The data associated with the proven Relic or Artifact
     * @return version the version identifier of the prover who introduced this item
     * @return data any data associated with this item
     */
    function parseFact(bytes storage fact)
        internal
        pure
        returns (uint64 version, bytes memory data)
    {
        // copy the storage bytes to memory
        data = fact;

        if (data.length > 0) {
            require(data.length >= 8, "fact data length invalid");

            // version is stored in first 8 bytes of the data
            version = uint64(bytes8(data));

            // rather than copy the rest of the data to a new bytes array,
            // just point data to data + 8 and setup length field
            assembly {
                let len := mload(data)
                data := add(data, 8)
                mstore(data, sub(len, 8))
            }
        }
    }

    /**
     * @notice Helper function to query the status of a prover
     * @param prover the ProverInfo associated with the prover in question
     * @dev reverts if the prover is invalid or revoked
     */
    function checkProver(ProverInfo memory prover) public pure {
        require(prover.revoked != true, "revoked prover");
        require(prover.version != 0, "unknown prover");
    }

    /**
     * @notice Deletes the fact from the Reliquary
     * @param account The account to which this information is bound (may be
     *        the null account for information bound to no specific address)
     * @param factSig The unique signature of the particular fact being deleted
     * @dev May only be called by non-revoked provers
     */
    function resetFact(address account, FactSignature factSig) external {
        ProverInfo memory prover = provers[msg.sender];
        checkProver(prover);

        delete provenFacts[account][factSig];
    }

    /**
     * @notice Adds the given information to the Reliquary
     * @param account The account to which this information is bound (may be
     *        the null account for information bound to no specific address)
     * @param factSig The unique signature of the particular fact being proven
     * @param data Associated data to store with this item
     * @dev May only be called by non-revoked provers
     */
    function setFact(
        address account,
        FactSignature factSig,
        bytes calldata data
    ) external {
        ProverInfo memory prover = provers[msg.sender];
        checkProver(prover);

        provenFacts[account][factSig] = abi.encodePacked(prover.version, data);
    }

    /**
     * @notice Query for associated information for a fact
     * @param account The address to which the fact belongs
     * @param factSig The unique signature identifying the fact
     * @return exists whether or not a fact with the given signature
     *         is associated with the queried account
     * @return version the prover version id that proved this fact
     * @return data any associated fact data
     * @dev This function is only for use by provers (reverts otherwise)
     */
    function getFact(address account, FactSignature factSig)
        external
        view
        returns (
            bool exists,
            uint64 version,
            bytes memory data
        )
    {
        ProverInfo memory prover = provers[msg.sender];
        checkProver(prover);

        (version, data) = parseFact(provenFacts[account][factSig]);
        exists = version != 0;
    }

    /**
     * @notice Query for associated information for a fact
     * @param account The address to which the fact belongs
     * @param factSig The unique signature identifying the fact
     * @return exists whether or not a fact with the given signature
     *         is associated with the queried account
     * @return version the prover version id that proved this fact
     * @return data any associated fact data
     * @dev This function is only for use by the Reliquary itself
     */
    function _verifyFact(address account, FactSignature factSig)
        internal
        view
        returns (
            bool exists,
            uint64 version,
            bytes memory data
        )
    {
        (version, data) = parseFact(provenFacts[account][factSig]);
        exists = version != 0;
    }

    /**
     * @notice Query for some information for a fact
     * @param account The address to which the fact belongs
     * @param factSig The unique signature identifying the fact
     * @return exists whether or not a fact with the given signature
     *         is associated with the queried account
     * @return version the prover version id that proved this fact
     * @dev This function is only for use by the Reliquary itself
     */
    function _verifyFactVersion(address account, FactSignature factSig)
        internal
        view
        returns (bool exists, uint64 version)
    {
        version = uint64(bytes8(provenFacts[account][factSig]));
        exists = version != 0;
    }

    /**
     * @notice Query for associated information for a fact
     * @param account The address to which the fact belongs
     * @param factSig The unique signature identifying the fact
     * @return exists whether or not a fact with the given signature
     *         is associated with the queried account
     * @return version the prover version id that proved this fact
     * @return data any associated fact data
     * @dev This function is for use by anyone
     * @dev This function reverts if the fact requires a fee to query
     */
    function verifyFactNoFee(address account, FactSignature factSig)
        external
        view
        returns (
            bool exists,
            uint64 version,
            bytes memory data
        )
    {
        require(Facts.toFactClass(factSig) == Facts.NO_FEE);
        return _verifyFact(account, factSig);
    }

    /**
     * @notice Query for some information for a fact
     * @param account The address to which the fact belongs
     * @param factSig The unique signature identifying the fact
     * @return exists whether or not a fact with the given signature
     *         is associated with the queried account
     * @return version the prover version id that proved this fact
     * @dev This function is for use by anyone
     * @dev This function reverts if the fact requires a fee to query
     */
    function verifyFactVersionNoFee(address account, FactSignature factSig)
        external
        view
        returns (bool exists, uint64 version)
    {
        require(Facts.toFactClass(factSig) == Facts.NO_FEE);
        return _verifyFactVersion(account, factSig);
    }

    /**
     * @notice Verify if a particular block had a particular hash
     * @param verifier The block history verifier to use for the query
     * @param hash The block hash in question
     * @param num The block number to query
     * @param proof Any witness information needed by the verifier
     * @return boolean indication of whether or not the given block was
     *         proven to have the given hash.
     * @dev This function is only for use by provers (reverts otherwise)
     */
    function validBlockHashFromProver(
        address verifier,
        bytes32 hash,
        uint256 num,
        bytes calldata proof
    ) public view returns (bool) {
        ProverInfo memory proverInfo = provers[msg.sender];
        checkProver(proverInfo);
        return IBlockHistory(verifier).validBlockHash(hash, num, proof);
    }

    /**
     * @notice Asserts that a particular block had a particular hash
     * @param verifier The block history verifier to use for the query
     * @param hash The block hash in question
     * @param num The block number to query
     * @param proof Any witness information needed by the verifier
     * @dev Reverts if the given block was not proven to have the given hash.
     * @dev This function is only for use by provers (reverts otherwise)
     */
    function assertValidBlockHashFromProver(
        address verifier,
        bytes32 hash,
        uint256 num,
        bytes calldata proof
    ) external view {
        require(validBlockHashFromProver(verifier, hash, num, proof), "invalid block hash");
    }

    /**
     * @notice Query for associated information for a fact
     * @param account The address to which the fact belongs
     * @param factSig The unique signature identifying the fact
     * @return exists whether or not a fact with the given signature
     *         is associated with the queried account
     * @return version the prover version id that proved this fact
     * @return data any associated fact data
     * @dev This function is for use by off-chain tools only (reverts otherwise)
     */
    function debugVerifyFact(address account, FactSignature factSig)
        external
        view
        returns (
            bool exists,
            uint64 version,
            bytes memory data
        )
    {
        require(tx.origin == address(0));
        return _verifyFact(account, factSig);
    }

    /**
     * @notice Verify if a particular block had a particular hash
     * @param verifier The block history verifier to use for the query
     * @param hash The block hash in question
     * @param num The block number to query
     * @param proof Any witness information needed by the verifier
     * @return boolean indication of whether or not the given block was
     *         proven to have the given hash.
     * @dev This function is for use by off-chain tools only (reverts otherwise)
     */
    function debugValidBlockHash(
        address verifier,
        bytes32 hash,
        uint256 num,
        bytes calldata proof
    ) external view returns (bool) {
        require(tx.origin == address(0));
        return IBlockHistory(verifier).validBlockHash(hash, num, proof);
    }

    /*
     * Allow a new prover to produce facts. Provers must have distinct versions.
     *
     * Once added, a new prover is not usable until it is activated after a short delay. This delay
     * prevents future governance from abusing its powers, and gives downstream users an
     * opportunity to decide if they still trust Reliquary.
     */
    /**
     * @notice Add/propose a new prover to prove facts.
     * @param prover the address of the prover in question
     * @param version the unique version string to associate with this prover
     * @dev Provers and proposed provers must have unique version IDs
     * @dev After the Reliquary is initialized, a review period of 64k blocks
     *      must conclude before a prover may be added. The request must then
     *      be re-submitted to take effect. Before initialization is complete,
     *      the review period is skipped.
     * @dev Pending provers may be removed by submitting a new prover with a
     *      version of 0.
     * @dev Emits PendingProveRemoved when a pending prover proposal is withdrawn
     * @dev Emits NewProver when a prover is added to the reliquary
     * @dev Emits PendingProverAdded when a prover is proposed for inclusion
     */
    function addProver(address prover, uint64 version) external onlyRole(ADD_PROVER_ROLE) {
        require(version != 0, "version must not be zero");
        require(versions[version] == address(0), "duplicate version");
        require(provers[prover].version == 0, "duplicate prover");
        require(pendingProvers[prover].version == 0, "already pending");

        uint256 delay = initialized ? DELAY : 0;
        PendingProver memory pendingProver = PendingProver(
            uint64(block.timestamp + delay),
            version
        );
        pendingProvers[prover] = pendingProver;

        // Pre-initialize ProverInfo so fee can be set before activation
        // As version = 0, the prover will not be usable yet
        provers[prover] = ProverInfo(0, FeeInfo(0, 0, 0, 0, 0), false);

        emit PendingProverAdded(prover, pendingProver.version, pendingProver.timestamp);
    }

    /* Anyone can activate a prover once it has been added and is no longer pending */
    function activateProver(address prover) external {
        require(provers[prover].version == 0, "duplicate prover");

        PendingProver memory pendingProver = pendingProvers[prover];
        require(pendingProver.version != 0, "invalid address");
        require(pendingProver.timestamp <= block.timestamp, "not ready");
        require(versions[pendingProver.version] == address(0), "duplicate version");

        versions[pendingProver.version] = prover;
        provers[prover].version = pendingProver.version;

        emit NewProver(prover, pendingProver.version);
    }

    /**
     * @notice Stop accepting proofs from this prover
     * @param prover The prover to banish from the reliquary
     * @dev Emits ProverRevoked
     * @dev Note: existing facts proved by the prover may still stand
     */
    function revokeProver(address prover) external onlyRole(GOVERNANCE_ROLE) {
        provers[prover].revoked = true;
        emit ProverRevoked(prover, pendingProvers[prover].version);
    }

    /**
     * @notice Initialize the Reliquary, enforcing the time lock for new provers
     */
    function setInitialized() external onlyRole(ADD_PROVER_ROLE) {
        initialized = true;
    }
}

File 14 of 16 : IBlockHistory.sol
/// SPDX-License-Identifier: UNLICENSED
/// (c) Theori, Inc. 2022
/// All rights reserved

pragma solidity >=0.8.0;

/**
 * @title Block history provider
 * @author Theori, Inc.
 * @notice IBlockHistory provides a way to verify a blockhash
 */

interface IBlockHistory {
    /**
     * @notice Determine if the given hash corresponds to the given block
     * @param hash the hash if the block in question
     * @param num the number of the block in question
     * @param proof any witness data required to prove the block hash is
     *        correct (such as a Merkle or SNARK proof)
     * @return boolean indicating if the block hash can be verified correct
     */
    function validBlockHash(
        bytes32 hash,
        uint256 num,
        bytes calldata proof
    ) external view returns (bool);
}

File 15 of 16 : IFeeDelegate.sol
/// SPDX-License-Identifier: UNLICENSED
/// (c) Theori, Inc. 2022
/// All rights reserved

pragma solidity >=0.8.0;

/**
 * @title Fee provider
 * @author Theori, Inc.
 * @notice Acceptor of fees for functions requiring payment
 */
interface IFeeDelegate {
    /**
     * @notice Accept any required fee from the sender
     * @param sender the originator of the call that may be paying
     * @param data opaque data to help determine costs
     * @dev reverts if a fee is required and not provided
     */
    function checkFee(address sender, bytes calldata data) external payable;
}

File 16 of 16 : Facts.sol
/// SPDX-License-Identifier: UNLICENSED
/// (c) Theori, Inc. 2022
/// All rights reserved

pragma solidity >=0.8.12;

type FactSignature is bytes32;

library Facts {
    uint8 internal constant NO_FEE = 0;

    function toFactSignature(uint8 cls, bytes memory data) internal pure returns (FactSignature) {
        return FactSignature.wrap(bytes32((uint256(keccak256(data)) << 8) | cls));
    }

    function toFactClass(FactSignature factSig) internal pure returns (uint8) {
        return uint8(uint256(FactSignature.unwrap(factSig)));
    }
}

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

Contract Security Audit

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"prover","type":"address"},{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"NewProver","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"prover","type":"address"},{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"timestamp","type":"uint64"}],"name":"PendingProverAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"prover","type":"address"},{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"ProverRevoked","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"},{"inputs":[],"name":"ADD_PROVER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CREDITS_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DELAY","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GOVERNANCE_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SUBSCRIPTION_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"prover","type":"address"}],"name":"activateProver","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint192","name":"amount","type":"uint192"}],"name":"addCredits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"prover","type":"address"},{"internalType":"uint64","name":"version","type":"uint64"}],"name":"addProver","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint64","name":"ts","type":"uint64"}],"name":"addSubscriber","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"verifier","type":"address"},{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"uint256","name":"num","type":"uint256"},{"internalType":"bytes","name":"proof","type":"bytes"}],"name":"assertValidBlockHash","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"verifier","type":"address"},{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"uint256","name":"num","type":"uint256"},{"internalType":"bytes","name":"proof","type":"bytes"}],"name":"assertValidBlockHashFromProver","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"checkProveFactFee","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"uint64","name":"version","type":"uint64"},{"components":[{"internalType":"uint8","name":"flags","type":"uint8"},{"internalType":"uint16","name":"feeCredits","type":"uint16"},{"internalType":"uint8","name":"feeWeiMantissa","type":"uint8"},{"internalType":"uint8","name":"feeWeiExponent","type":"uint8"},{"internalType":"uint32","name":"feeExternalId","type":"uint32"}],"internalType":"struct FeeInfo","name":"feeInfo","type":"tuple"},{"internalType":"bool","name":"revoked","type":"bool"}],"internalType":"struct ProverInfo","name":"prover","type":"tuple"}],"name":"checkProver","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"credits","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"verifier","type":"address"},{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"uint256","name":"num","type":"uint256"},{"internalType":"bytes","name":"proof","type":"bytes"}],"name":"debugValidBlockHash","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"FactSignature","name":"factSig","type":"bytes32"}],"name":"debugVerifyFact","outputs":[{"internalType":"bool","name":"exists","type":"bool"},{"internalType":"uint64","name":"version","type":"uint64"},{"internalType":"bytes","name":"data","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"","type":"uint8"}],"name":"factFees","outputs":[{"internalType":"uint8","name":"flags","type":"uint8"},{"internalType":"uint16","name":"feeCredits","type":"uint16"},{"internalType":"uint8","name":"feeWeiMantissa","type":"uint8"},{"internalType":"uint8","name":"feeWeiExponent","type":"uint8"},{"internalType":"uint32","name":"feeExternalId","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"feeAccounts","outputs":[{"internalType":"uint64","name":"subscriberUntilTime","type":"uint64"},{"internalType":"uint192","name":"credits","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"feeExternals","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"FactSignature","name":"factSig","type":"bytes32"}],"name":"getFact","outputs":[{"internalType":"bool","name":"exists","type":"bool"},{"internalType":"uint64","name":"version","type":"uint64"},{"internalType":"bytes","name":"data","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"prover","type":"address"}],"name":"getProveFactNativeFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"prover","type":"address"}],"name":"getProveFactTokenFee","outputs":[{"internalType":"uint256","name":"","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":[{"internalType":"FactSignature","name":"factSig","type":"bytes32"}],"name":"getVerifyFactNativeFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"FactSignature","name":"factSig","type":"bytes32"}],"name":"getVerifyFactTokenFee","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":[],"name":"initialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"isSubscriber","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"pendingProvers","outputs":[{"internalType":"uint64","name":"timestamp","type":"uint64"},{"internalType":"uint64","name":"version","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"provers","outputs":[{"internalType":"uint64","name":"version","type":"uint64"},{"components":[{"internalType":"uint8","name":"flags","type":"uint8"},{"internalType":"uint16","name":"feeCredits","type":"uint16"},{"internalType":"uint8","name":"feeWeiMantissa","type":"uint8"},{"internalType":"uint8","name":"feeWeiExponent","type":"uint8"},{"internalType":"uint32","name":"feeExternalId","type":"uint32"}],"internalType":"struct FeeInfo","name":"feeInfo","type":"tuple"},{"internalType":"bool","name":"revoked","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint192","name":"amount","type":"uint192"}],"name":"removeCredits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"removeSubscriber","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":"address","name":"account","type":"address"},{"internalType":"FactSignature","name":"factSig","type":"bytes32"}],"name":"resetFact","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"prover","type":"address"}],"name":"revokeProver","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":"user","type":"address"},{"internalType":"uint192","name":"amount","type":"uint192"}],"name":"setCredits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"FactSignature","name":"factSig","type":"bytes32"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"setFact","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"cls","type":"uint8"},{"components":[{"internalType":"uint8","name":"flags","type":"uint8"},{"internalType":"uint16","name":"feeCredits","type":"uint16"},{"internalType":"uint8","name":"feeWeiMantissa","type":"uint8"},{"internalType":"uint8","name":"feeWeiExponent","type":"uint8"},{"internalType":"uint32","name":"feeExternalId","type":"uint32"}],"internalType":"struct FeeInfo","name":"feeInfo","type":"tuple"},{"internalType":"address","name":"feeExternal","type":"address"}],"name":"setFactFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setInitialized","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"prover","type":"address"},{"components":[{"internalType":"uint8","name":"flags","type":"uint8"},{"internalType":"uint16","name":"feeCredits","type":"uint16"},{"internalType":"uint8","name":"feeWeiMantissa","type":"uint8"},{"internalType":"uint8","name":"feeWeiExponent","type":"uint8"},{"internalType":"uint32","name":"feeExternalId","type":"uint32"}],"internalType":"struct FeeInfo","name":"feeInfo","type":"tuple"},{"internalType":"address","name":"feeExternal","type":"address"}],"name":"setProverFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint8","name":"flags","type":"uint8"},{"internalType":"uint16","name":"feeCredits","type":"uint16"},{"internalType":"uint8","name":"feeWeiMantissa","type":"uint8"},{"internalType":"uint8","name":"feeWeiExponent","type":"uint8"},{"internalType":"uint32","name":"feeExternalId","type":"uint32"}],"internalType":"struct FeeInfo","name":"feeInfo","type":"tuple"},{"internalType":"address","name":"feeExternal","type":"address"}],"name":"setValidBlockFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"verifier","type":"address"},{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"uint256","name":"num","type":"uint256"},{"internalType":"bytes","name":"proof","type":"bytes"}],"name":"validBlockHash","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"verifier","type":"address"},{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"uint256","name":"num","type":"uint256"},{"internalType":"bytes","name":"proof","type":"bytes"}],"name":"validBlockHashFromProver","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"verifyBlockFeeInfo","outputs":[{"internalType":"uint8","name":"flags","type":"uint8"},{"internalType":"uint16","name":"feeCredits","type":"uint16"},{"internalType":"uint8","name":"feeWeiMantissa","type":"uint8"},{"internalType":"uint8","name":"feeWeiExponent","type":"uint8"},{"internalType":"uint32","name":"feeExternalId","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"FactSignature","name":"factSig","type":"bytes32"}],"name":"verifyFact","outputs":[{"internalType":"bool","name":"exists","type":"bool"},{"internalType":"uint64","name":"version","type":"uint64"},{"internalType":"bytes","name":"data","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"FactSignature","name":"factSig","type":"bytes32"}],"name":"verifyFactNoFee","outputs":[{"internalType":"bool","name":"exists","type":"bool"},{"internalType":"uint64","name":"version","type":"uint64"},{"internalType":"bytes","name":"data","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"FactSignature","name":"factSig","type":"bytes32"}],"name":"verifyFactVersion","outputs":[{"internalType":"bool","name":"exists","type":"bool"},{"internalType":"uint64","name":"version","type":"uint64"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"FactSignature","name":"factSig","type":"bytes32"}],"name":"verifyFactVersionNoFee","outputs":[{"internalType":"bool","name":"exists","type":"bool"},{"internalType":"uint64","name":"version","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"","type":"uint64"}],"name":"versions","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address payable","name":"dest","type":"address"}],"name":"withdrawFees","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60806040523480156200001157600080fd5b506200001f60003362000025565b620000d5565b62000031828262000035565b5050565b6000828152602081815260408083206001600160a01b038516845290915290205460ff1662000031576000828152602081815260408083206001600160a01b03851684529091529020805460ff19166001179055620000913390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b613e2780620000e56000396000f3fe6080604052600436106103555760003560e01c80637c9acc23116101bb578063d547741f116100f7578063e7157b7411610095578063f25552781161006f578063f255527814610c80578063f2d76d0d14610ca0578063f36c8f5c14610cd6578063fe5ff46814610d0a57600080fd5b8063e7157b7414610bdd578063ee90a85414610bfd578063ef49664d14610c1d57600080fd5b8063ddb0ed96116100d1578063ddb0ed9614610a71578063e2e71f9314610af9578063e4a4fa5f14610b6a578063e65db16d14610b7d57600080fd5b8063d547741f14610a1e578063d80992e714610a3e578063dd898a7f14610a5e57600080fd5b8063a432d4ca11610164578063c3e9ffbd1161013e578063c3e9ffbd1461097a578063c4804ef01461099a578063c59a9703146109de578063cd7cbe5c146109fe57600080fd5b8063a432d4ca1461091a578063b9a52efe1461093a578063bd629d261461095a57600080fd5b806391d148541161019557806391d14854146108815780639e3c54ae146108c5578063a217fddf1461090557600080fd5b80637c9acc231461082c578063801928211461084c57806384d688b61461086157600080fd5b80632f2ff15d116102955780634f1e73ea11610233578063627c34ad1161020d578063627c34ad146107a957806369b41170146107c957806369fa06d0146107f957806372fc6d621461081957600080fd5b80634f1e73ea1461075657806350ee31e7146107765780636219880e1461078957600080fd5b806349fae6591161026f57806349fae659146106c25780634a1ec016146106e25780634ed8611f146107025780634eecf4411461072257600080fd5b80632f2ff15d1461064e57806336568abe1461066e578063376202951461068e57600080fd5b8063162bd10a116103025780631dec844b116102dc5780631dec844b146104c95780631f0a2b6e146105ca578063241c10a3146105ea578063248a9ca31461061e57600080fd5b8063162bd10a1461046757806318b33775146104875780631accba8f146104a957600080fd5b80630ee1d786116103335780630ee1d786146103ff57806310da418c1461041f578063158ef93e1461044d57600080fd5b806301ffc9a71461035a5780630abd834e1461038f5780630e71d8a2146103dd575b600080fd5b34801561036657600080fd5b5061037a61037536600461342d565b610d68565b60405190151581526020015b60405180910390f35b34801561039b57600080fd5b506103c56103aa366004613457565b6008602052600090815260409020546001600160a01b031681565b6040516001600160a01b039091168152602001610386565b3480156103e957600080fd5b506103fd6103f8366004613485565b610dd1565b005b34801561040b57600080fd5b5061037a61041a3660046134f3565b610e9a565b34801561042b57600080fd5b5061043f61043a366004613457565b610f24565b604051908152602001610386565b34801561045957600080fd5b5060015461037a9060ff1681565b34801561047357600080fd5b5061043f61048236600461355d565b610fc9565b61049a610495366004613485565b611097565b604051610386939291906135d2565b3480156104b557600080fd5b5061043f6104c4366004613457565b6110bc565b3480156104d557600080fd5b506105636104e436600461355d565b6002602081815260009283526040928390208054845160a081018652600183015460ff8082168352610100820461ffff169583019590955263010000008104851696820196909652640100000000860484166060820152600160281b90950463ffffffff16608086015292015467ffffffffffffffff90921692911683565b6040805167ffffffffffffffff9094168452825160ff90811660208087019190915284015161ffff1685830152908301518116606080860191909152830151166080808501919091529091015163ffffffff1660a0830152151560c082015260e001610386565b3480156105d657600080fd5b506103fd6105e53660046136e6565b611140565b3480156105f657600080fd5b5061043f7fd2279127a6db36ad34c84940e4b56256cf8df416f8098e820e0db61bb41e312981565b34801561062a57600080fd5b5061043f610639366004613457565b60009081526020819052604090206001015490565b34801561065a57600080fd5b506103fd610669366004613730565b611357565b34801561067a57600080fd5b506103fd610689366004613730565b61137c565b34801561069a57600080fd5b5061043f7fb7c19c4897e038440c53ecfaa804a0f8d20573cb698f85962cd7515a7744106481565b3480156106ce57600080fd5b506103fd6106dd3660046134f3565b61140d565b3480156106ee57600080fd5b5061043f6106fd36600461355d565b61146d565b34801561070e57600080fd5b5061037a61071d3660046134f3565b611513565b34801561072e57600080fd5b5061043f7f1991205f9b9e6359222ed4bbde98eebd6c5a90f432c11ce781941f6acee3127e81565b34801561076257600080fd5b506103fd610771366004613760565b611627565b61037a6107843660046134f3565b6116f9565b34801561079557600080fd5b506103fd6107a436600461377e565b611704565b3480156107b557600080fd5b506103fd6107c436600461355d565b611792565b3480156107d557600080fd5b506107e06202a30081565b60405167ffffffffffffffff9091168152602001610386565b34801561080557600080fd5b506103fd61081436600461355d565b6117e5565b6103fd61082736600461355d565b611a65565b34801561083857600080fd5b5061049a610847366004613485565b611b67565b34801561085857600080fd5b506103fd611b7a565b34801561086d57600080fd5b506103fd61087c3660046137d0565b611bb3565b34801561088d57600080fd5b5061037a61089c366004613730565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b3480156108d157600080fd5b506108e56108e0366004613485565b611c4a565b60408051921515835267ffffffffffffffff909116602083015201610386565b34801561091157600080fd5b5061043f600081565b34801561092657600080fd5b506103fd610935366004613805565b611c71565b34801561094657600080fd5b506103fd6109553660046137d0565b611d69565b34801561096657600080fd5b506103fd61097536600461355d565b61215e565b34801561098657600080fd5b506103fd61099536600461377e565b612200565b3480156109a657600080fd5b5061037a6109b536600461355d565b6001600160a01b03166000908152600760205260409020544267ffffffffffffffff9091161190565b3480156109ea57600080fd5b5061049a6109f9366004613485565b61226b565b348015610a0a57600080fd5b506103fd610a1936600461377e565b612346565b348015610a2a57600080fd5b506103fd610a39366004613730565b6123ab565b348015610a4a57600080fd5b506103fd610a59366004613861565b6123d0565b6103fd610a6c3660046134f3565b612493565b348015610a7d57600080fd5b50600a54610abb9060ff80821691610100810461ffff169163010000008204811691640100000000810490911690600160281b900463ffffffff1685565b6040805160ff968716815261ffff90951660208601529285169284019290925292909216606082015263ffffffff909116608082015260a001610386565b348015610b0557600080fd5b50610b42610b1436600461355d565b60076020526000908152604090205467ffffffffffffffff811690600160401b90046001600160c01b031682565b6040805167ffffffffffffffff90931683526001600160c01b03909116602083015201610386565b6108e5610b78366004613485565b6124a0565b348015610b8957600080fd5b50610abb610b9836600461388e565b60066020526000908152604090205460ff80821691610100810461ffff169163010000008204811691640100000000810490911690600160281b900463ffffffff1685565b348015610be957600080fd5b506103fd610bf83660046138b7565b6124ac565b348015610c0957600080fd5b5061049a610c18366004613485565b61255e565b348015610c2957600080fd5b50610c5f610c3836600461355d565b60046020526000908152604090205467ffffffffffffffff80821691600160401b90041682565b6040805167ffffffffffffffff938416815292909116602083015201610386565b348015610c8c57600080fd5b506103fd610c9b366004613934565b61256e565b348015610cac57600080fd5b506103c5610cbb366004613962565b6003602052600090815260409020546001600160a01b031681565b348015610ce257600080fd5b5061043f7f71840dc4906352362b0cdaf79870196c8e42acafade72d5d5a6d59291253ceb181565b348015610d1657600080fd5b50610d50610d2536600461355d565b6001600160a01b0316600090815260076020526040902054600160401b90046001600160c01b031690565b6040516001600160c01b039091168152602001610386565b60006001600160e01b031982167f7965db0b000000000000000000000000000000000000000000000000000000001480610dcb57507f01ffc9a7000000000000000000000000000000000000000000000000000000006001600160e01b03198316145b92915050565b33600090815260026020818152604092839020835160608082018652825467ffffffffffffffff168252855160a081018752600184015460ff8082168352610100820461ffff1683880152630100000082048116838a01526401000000008204811693830193909352600160281b900463ffffffff1660808201529382019390935292015416151591810191909152610e69816124ac565b6001600160a01b03831660009081526005602090815260408083208584529091528120610e959161335a565b505050565b60003215610ea757600080fd5b60405163f50ef57d60e01b81526001600160a01b0387169063f50ef57d90610ed990889088908890889060040161397d565b602060405180830381865afa158015610ef6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f1a91906139ba565b9695505050505050565b60008160ff8116610f385750600092915050565b60ff818116600090815260066020908152604091829020825160a08101845290548085168252610100810461ffff16928201929092526301000000820484169281019290925264010000000081049092166060820152600160281b90910463ffffffff16608082015260015b8151600190911b1660ff16610fb857600080fd5b610fc1816126ee565b949350505050565b6001600160a01b0381166000908152600260208181526040808420815160608082018452825467ffffffffffffffff168252835160a081018552600184015460ff8082168352610100820461ffff1683890152630100000082048116838801526401000000008204811693830193909352600160281b900463ffffffff16608082015294820194909452930154909116151590820152611068816124ac565b60045b602082015151600190911b1660ff1661108357600080fd5b61109081602001516126ee565b9392505050565b60008060606110a584612713565b6110af85856127d3565b9250925092509250925092565b60008160ff81166110d05750600092915050565b60ff818116600090815260066020908152604091829020825160a08101845290548085168252610100810461ffff16928201929092526301000000820484169281019290925264010000000081049092166060820152600160281b90910463ffffffff1660808201526004610fa4565b7f71840dc4906352362b0cdaf79870196c8e42acafade72d5d5a6d59291253ceb161116a8161281d565b6001600160a01b038416600090815260026020818152604092839020835160608082018652825467ffffffffffffffff168252855160a081018752600184015460ff8082168352610100820461ffff1683880152630100000082048116838a01526401000000008204811693830193909352600160281b900463ffffffff166080820152938201939093529201541615159181019190915261120b816124ac565b6112158484612827565b93508381602001819052508060026000876001600160a01b03166001600160a01b0316815260200190815260200160002060008201518160000160006101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555060208201518160010160008201518160000160006101000a81548160ff021916908360ff16021790555060208201518160000160016101000a81548161ffff021916908361ffff16021790555060408201518160000160036101000a81548160ff021916908360ff16021790555060608201518160000160046101000a81548160ff021916908360ff16021790555060808201518160000160056101000a81548163ffffffff021916908363ffffffff160217905550505060408201518160020160006101000a81548160ff0219169083151502179055509050505050505050565b6000828152602081905260409020600101546113728161281d565b610e9583836128e2565b6001600160a01b03811633146113ff5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c66000000000000000000000000000000000060648201526084015b60405180910390fd5b6114098282612980565b5050565b61141a8585858585611513565b6114665760405162461bcd60e51b815260206004820152601260248201527f696e76616c696420626c6f636b2068617368000000000000000000000000000060448201526064016113f6565b5050505050565b6001600160a01b0381166000908152600260208181526040808420815160608082018452825467ffffffffffffffff168252835160a081018552600184015460ff8082168352610100820461ffff1683890152630100000082048116838801526401000000008204811693830193909352600160281b900463ffffffff1660808201529482019490945293015490911615159082015261150c816124ac565b600161106b565b336000908152600260208181526040808420815160608082018452825467ffffffffffffffff168252835160a081018552600184015460ff8082168352610100820461ffff1683890152630100000082048116838801526401000000008204811693830193909352600160281b900463ffffffff166080820152948201949094529301549091161515908201526115a9816124ac565b60405163f50ef57d60e01b81526001600160a01b0388169063f50ef57d906115db90899089908990899060040161397d565b602060405180830381865afa1580156115f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061161c91906139ba565b979650505050505050565b7f71840dc4906352362b0cdaf79870196c8e42acafade72d5d5a6d59291253ceb16116518161281d565b61165b8383612827565b60ff948516600090815260066020908152604091829020835181549285015193850151606086015160809096015163ffffffff16600160281b0268ffffffff000000000019968b166401000000000264ff0000000019928c166301000000029290921664ffff0000001961ffff9097166101000262ffffff1990961693909b1692909217939093179390931697909717179190911617909355505050565b6000610ea7846129ff565b7fd2279127a6db36ad34c84940e4b56256cf8df416f8098e820e0db61bb41e312961172e8161281d565b6001600160a01b03831660009081526007602052604090208054839190600890611769908490600160401b90046001600160c01b03166139ed565b92506101000a8154816001600160c01b0302191690836001600160c01b03160217905550505050565b7fb7c19c4897e038440c53ecfaa804a0f8d20573cb698f85962cd7515a774410646117bc8161281d565b506001600160a01b03166000908152600760205260409020805467ffffffffffffffff19169055565b6001600160a01b03811660009081526002602052604090205467ffffffffffffffff16156118555760405162461bcd60e51b815260206004820152601060248201527f6475706c69636174652070726f7665720000000000000000000000000000000060448201526064016113f6565b6001600160a01b03811660009081526004602090815260409182902082518084019093525467ffffffffffffffff8082168452600160401b909104169082018190526118e35760405162461bcd60e51b815260206004820152600f60248201527f696e76616c69642061646472657373000000000000000000000000000000000060448201526064016113f6565b42816000015167ffffffffffffffff1611156119415760405162461bcd60e51b815260206004820152600960248201527f6e6f74207265616479000000000000000000000000000000000000000000000060448201526064016113f6565b60208082015167ffffffffffffffff166000908152600390915260409020546001600160a01b0316156119b65760405162461bcd60e51b815260206004820152601160248201527f6475706c69636174652076657273696f6e00000000000000000000000000000060448201526064016113f6565b6020818101805167ffffffffffffffff9081166000908152600384526040808220805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03891690811790915584518184526002875292829020805467ffffffffffffffff19169385169390931790925592518351918252909116928101929092527f3410506653c3f6710aba2bb8abaa167692179aad5f586d2a8630c6007a3844c291015b60405180910390a15050565b33600081815260026020818152604092839020835160608082018652825467ffffffffffffffff168252855160a081018752600184015460ff8082168352610100820461ffff1683880152630100000082048116838a01526401000000008204811693830193909352600160281b900463ffffffff1660808201529382019390935292015416151591810191909152611afd816124ac565b60208082015160408051928301819052600960608401527f70726f766546616374000000000000000000000000000000000000000000000060808401526001600160a01b03851690830152610e959185919060a0015b604051602081830303815290604052612aa7565b600080606060ff8416156110a557600080fd5b7f1991205f9b9e6359222ed4bbde98eebd6c5a90f432c11ce781941f6acee3127e611ba48161281d565b506001805460ff191681179055565b7fb7c19c4897e038440c53ecfaa804a0f8d20573cb698f85962cd7515a77441064611bdd8161281d565b6001600160a01b03831660009081526007602052604090205467ffffffffffffffff808416911610611c0e57600080fd5b506001600160a01b03919091166000908152600760205260409020805467ffffffffffffffff191667ffffffffffffffff909216919091179055565b60008060ff831615611c5b57600080fd5b611c658484612d3e565b915091505b9250929050565b33600090815260026020818152604092839020835160608082018652825467ffffffffffffffff168252855160a081018752600184015460ff8082168352610100820461ffff1683880152630100000082048116838a01526401000000008204811693830193909352600160281b900463ffffffff1660808201529382019390935292015416151591810191909152611d09816124ac565b8051604051611d1f919085908590602001613a18565b60408051601f198184030181529181526001600160a01b03871660009081526005602090815282822088835281529190208251611d6193919290910190613394565b505050505050565b7f1991205f9b9e6359222ed4bbde98eebd6c5a90f432c11ce781941f6acee3127e611d938161281d565b67ffffffffffffffff8216611dea5760405162461bcd60e51b815260206004820152601860248201527f76657273696f6e206d757374206e6f74206265207a65726f000000000000000060448201526064016113f6565b67ffffffffffffffff82166000908152600360205260409020546001600160a01b031615611e5a5760405162461bcd60e51b815260206004820152601160248201527f6475706c69636174652076657273696f6e00000000000000000000000000000060448201526064016113f6565b6001600160a01b03831660009081526002602052604090205467ffffffffffffffff1615611eca5760405162461bcd60e51b815260206004820152601060248201527f6475706c69636174652070726f7665720000000000000000000000000000000060448201526064016113f6565b6001600160a01b038316600090815260046020526040902054600160401b900467ffffffffffffffff1615611f415760405162461bcd60e51b815260206004820152600f60248201527f616c72656164792070656e64696e67000000000000000000000000000000000060448201526064016113f6565b60015460009060ff16611f55576000611f5a565b6202a3005b67ffffffffffffffff169050600060405180604001604052808342611f7f9190613a50565b67ffffffffffffffff90811682528681166020928301526001600160a01b03881660008181526004845260408082208651815488880180518816600160401b027fffffffffffffffffffffffffffffffff000000000000000000000000000000009092169288169290921717909155815160608082018452848252835160a0810185528581528089018690528085018690528082018690526080808201879052838a019182528386018781529787526002808b529686902093518454991667ffffffffffffffff19999099169890981783555180516001840180549a830151838801519484015193909a015163ffffffff16600160281b0268ffffffff00000000001960ff9485166401000000000264ff00000000199686166301000000029690961664ffff0000001961ffff9d909d166101000262ffffff19909e1694909516939093179b909b17999099169190911791909117169690961790945591519301805493151560ff19949094169390931790925551825191519293507f3439752cf7c5ae5b0b2c0ce121f5c0f492fa64e5166586d7a53d6fbdcca238739261214f928992916001600160a01b0393909316835267ffffffffffffffff918216602084015216604082015260600190565b60405180910390a15050505050565b7f71840dc4906352362b0cdaf79870196c8e42acafade72d5d5a6d59291253ceb16121888161281d565b6001600160a01b0382166000818152600260208181526040808420909201805460ff19166001179055600481529181902054815193845267ffffffffffffffff600160401b90910416918301919091527f369ae2dd83c9f965bbf9db538808d71ce25d5c2bf1b548406deeb09d0653d2d29101611a59565b7fd2279127a6db36ad34c84940e4b56256cf8df416f8098e820e0db61bb41e312961222a8161281d565b506001600160a01b03909116600090815260076020526040902080546001600160c01b03909216600160401b0267ffffffffffffffff909216919091179055565b336000908152600260208181526040808420815160608082018452825467ffffffffffffffff168252835160a081018552600184015460ff8082168352610100820461ffff1683890152630100000082048116838801526401000000008204811683850152600160281b90910463ffffffff1660808301529583015291909401549092161515908301528291612300816124ac565b6001600160a01b0386166000908152600560209081526040808320888452909152902061232c90612d7c565b67ffffffffffffffff821615159891975095509350505050565b7fd2279127a6db36ad34c84940e4b56256cf8df416f8098e820e0db61bb41e31296123708161281d565b6001600160a01b03831660009081526007602052604090208054839190600890611769908490600160401b90046001600160c01b0316613a68565b6000828152602081905260409020600101546123c68161281d565b610e958383612980565b7f71840dc4906352362b0cdaf79870196c8e42acafade72d5d5a6d59291253ceb16123fa8161281d565b6124048383612827565b8051600a805460208401516040850151606086015160809096015163ffffffff16600160281b0268ffffffff00000000001960ff9788166401000000000264ff00000000199389166301000000029390931664ffff0000001961ffff9095166101000262ffffff1990961698909716979097179390931791909116939093179290921792909216179055505050565b61141a85858585856116f9565b600080611c5b83612713565b60408101511515600114156125035760405162461bcd60e51b815260206004820152600e60248201527f7265766f6b65642070726f76657200000000000000000000000000000000000060448201526064016113f6565b805167ffffffffffffffff1661255b5760405162461bcd60e51b815260206004820152600e60248201527f756e6b6e6f776e2070726f76657200000000000000000000000000000000000060448201526064016113f6565b50565b600080606032156110a557600080fd5b7f71840dc4906352362b0cdaf79870196c8e42acafade72d5d5a6d59291253ceb16125988161281d565b6001600160a01b0382166125ab57600080fd5b6001600160a01b0383166125f4576040516001600160a01b038316904780156108fc02916000818181858888f193505050501580156125ee573d6000803e3d6000fd5b50505050565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0384169063a9059cbb90849083906370a0823190602401602060405180830381865afa15801561265b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061267f9190613a90565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044016020604051808303816000875af11580156126ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125ee91906139ba565b60008160600151600a6127019190613b8d565b826040015160ff16610dcb9190613b9c565b8060ff8116156114095760ff808216600090815260066020908152604091829020825160a0810184529054808516825261ffff61010082041682840152630100000081048516828501526401000000008104909416606082015263ffffffff600160281b90940493909316608084015290516114099233929091611b53918791016040808252600a908201527f76657269667946616374000000000000000000000000000000000000000000006060820152602081019190915260800190565b6001600160a01b03821660009081526005602090815260408083208484529091528120819060609061280490612d7c565b67ffffffffffffffff8216151597919650945092505050565b61255b8133612e8a565b6040805160a0810182526000808252602082018190529181018290526060810182905260808101829052906001600160a01b038316156128d0576009805463ffffffff1690600061287783613bbb565b82546101009290920a63ffffffff818102199093169183160217909155600954166000818152600860205260409020805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0387161790559150505b63ffffffff1660808401525090919050565b6000828152602081815260408083206001600160a01b038516845290915290205460ff16611409576000828152602081815260408083206001600160a01b03851684529091529020805460ff1916600117905561293c3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6000828152602081815260408083206001600160a01b038516845290915290205460ff1615611409576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6040805160a081018252600a5460ff808216835261ffff610100830416602080850191909152630100000083048216848601526401000000008304909116606084015263ffffffff600160281b909204919091166080830152915161255b92339291611b53918691016040808252600b908201527f766572696679426c6f636b0000000000000000000000000000000000000000006060820152602081019190915260800190565b6000612ab2836126ee565b835190915060011615612ac55750505050565b825160021615612adc57803410612adc5750505050565b825160041615612bc9576001600160a01b03841660009081526007602090815260409182902082518084019093525467ffffffffffffffff8116808452600160401b9091046001600160c01b031691830191909152421015612b3f575050505050565b836020015161ffff1681602001516001600160c01b031610612bc757836020015161ffff1681602001818151612b759190613a68565b6001600160c01b039081169091526001600160a01b03909616600090815260076020908152604090912083519190930151909616600160401b0267ffffffffffffffff90961695909517905550505050565b505b825160081615612c9657608083015163ffffffff16612be757600080fd5b608083015163ffffffff166000908152600860205260409020546001600160a01b031680612c1457600080fd5b6040517f3e23e3990000000000000000000000000000000000000000000000000000000081526001600160a01b03821690633e23e399903490612c5d9089908890600401613bdf565b6000604051808303818588803b158015612c7657600080fd5b505af1158015612c8a573d6000803e3d6000fd5b50505050505050505050565b825160101615612cf657608083015163ffffffff16612cb457600080fd5b608083015163ffffffff166000908152600860205260409020546001600160a01b031680612ce157600080fd5b6114666001600160a01b038216863085612f08565b60405162461bcd60e51b815260206004820152601060248201527f696e73756666696369656e74206665650000000000000000000000000000000060448201526064016113f6565b6001600160a01b038216600090815260056020908152604080832084845290915281208190612d6c90613c3c565b60c01c8015159590945092505050565b60006060828054612d8c90613c01565b80601f0160208091040260200160405190810160405280929190818152602001828054612db890613c01565b8015612e055780601f10612dda57610100808354040283529160200191612e05565b820191906000526020600020905b815481529060010190602001808311612de857829003601f168201915b50505050509050600081511115612e8557600881511015612e685760405162461bcd60e51b815260206004820152601860248201527f666163742064617461206c656e67746820696e76616c6964000000000000000060448201526064016113f6565b612e7181613ccf565b815160071901600890920191825260c01c91505b915091565b6000828152602081815260408083206001600160a01b038516845290915290205460ff1661140957612ec6816001600160a01b03166014612f90565b612ed1836020612f90565b604051602001612ee2929190613d14565b60408051601f198184030181529082905262461bcd60e51b82526113f691600401613d95565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd000000000000000000000000000000000000000000000000000000001790526125ee908590613171565b60606000612f9f836002613b9c565b612faa906002613a50565b67ffffffffffffffff811115612fc257612fc2613606565b6040519080825280601f01601f191660200182016040528015612fec576020820181803683370190505b5090507f30000000000000000000000000000000000000000000000000000000000000008160008151811061302357613023613da8565b60200101906001600160f81b031916908160001a9053507f78000000000000000000000000000000000000000000000000000000000000008160018151811061306e5761306e613da8565b60200101906001600160f81b031916908160001a9053506000613092846002613b9c565b61309d906001613a50565b90505b6001811115613122577f303132333435363738396162636465660000000000000000000000000000000085600f16601081106130de576130de613da8565b1a60f81b8282815181106130f4576130f4613da8565b60200101906001600160f81b031916908160001a90535060049490941c9361311b81613dbe565b90506130a0565b5083156110905760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e7460448201526064016113f6565b60006131c6826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166132569092919063ffffffff16565b805190915015610e9557808060200190518101906131e491906139ba565b610e955760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016113f6565b6060610fc18484600085856001600160a01b0385163b6132b85760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016113f6565b600080866001600160a01b031685876040516132d49190613dd5565b60006040518083038185875af1925050503d8060008114613311576040519150601f19603f3d011682016040523d82523d6000602084013e613316565b606091505b509150915061161c82828660608315613330575081611090565b8251156133405782518084602001fd5b8160405162461bcd60e51b81526004016113f69190613d95565b50805461336690613c01565b6000825580601f10613376575050565b601f01602090049060005260206000209081019061255b9190613418565b8280546133a090613c01565b90600052602060002090601f0160209004810192826133c25760008555613408565b82601f106133db57805160ff1916838001178555613408565b82800160010185558215613408579182015b828111156134085782518255916020019190600101906133ed565b50613414929150613418565b5090565b5b808211156134145760008155600101613419565b60006020828403121561343f57600080fd5b81356001600160e01b03198116811461109057600080fd5b60006020828403121561346957600080fd5b5035919050565b6001600160a01b038116811461255b57600080fd5b6000806040838503121561349857600080fd5b82356134a381613470565b946020939093013593505050565b60008083601f8401126134c357600080fd5b50813567ffffffffffffffff8111156134db57600080fd5b602083019150836020828501011115611c6a57600080fd5b60008060008060006080868803121561350b57600080fd5b853561351681613470565b94506020860135935060408601359250606086013567ffffffffffffffff81111561354057600080fd5b61354c888289016134b1565b969995985093965092949392505050565b60006020828403121561356f57600080fd5b813561109081613470565b60005b8381101561359557818101518382015260200161357d565b838111156125ee5750506000910152565b600081518084526135be81602086016020860161357a565b601f01601f19169290920160200192915050565b831515815267ffffffffffffffff831660208201526060604082015260006135fd60608301846135a6565b95945050505050565b634e487b7160e01b600052604160045260246000fd5b803560ff8116811461362d57600080fd5b919050565b600060a0828403121561364457600080fd5b60405160a0810181811067ffffffffffffffff8211171561367557634e487b7160e01b600052604160045260246000fd5b6040529050806136848361361c565b8152602083013561ffff8116811461369b57600080fd5b60208201526136ac6040840161361c565b60408201526136bd6060840161361c565b6060820152608083013563ffffffff811681146136d957600080fd5b6080919091015292915050565b600080600060e084860312156136fb57600080fd5b833561370681613470565b92506137158560208601613632565b915060c084013561372581613470565b809150509250925092565b6000806040838503121561374357600080fd5b82359150602083013561375581613470565b809150509250929050565b600080600060e0848603121561377557600080fd5b6137068461361c565b6000806040838503121561379157600080fd5b823561379c81613470565b915060208301356001600160c01b038116811461375557600080fd5b803567ffffffffffffffff8116811461362d57600080fd5b600080604083850312156137e357600080fd5b82356137ee81613470565b91506137fc602084016137b8565b90509250929050565b6000806000806060858703121561381b57600080fd5b843561382681613470565b935060208501359250604085013567ffffffffffffffff81111561384957600080fd5b613855878288016134b1565b95989497509550505050565b60008060c0838503121561387457600080fd5b61387e8484613632565b915060a083013561375581613470565b6000602082840312156138a057600080fd5b6110908261361c565b801515811461255b57600080fd5b600060e082840312156138c957600080fd5b6040516060810181811067ffffffffffffffff821117156138fa57634e487b7160e01b600052604160045260246000fd5b604052613906836137b8565b81526139158460208501613632565b602082015260c0830135613928816138a9565b60408201529392505050565b6000806040838503121561394757600080fd5b823561395281613470565b9150602083013561375581613470565b60006020828403121561397457600080fd5b611090826137b8565b84815283602082015260606040820152816060820152818360808301376000818301608090810191909152601f909201601f191601019392505050565b6000602082840312156139cc57600080fd5b8151611090816138a9565b634e487b7160e01b600052601160045260246000fd5b60006001600160c01b03808316818516808303821115613a0f57613a0f6139d7565b01949350505050565b77ffffffffffffffffffffffffffffffffffffffffffffffff198460c01b168152818360088301376000910160080190815292915050565b60008219821115613a6357613a636139d7565b500190565b60006001600160c01b0383811690831681811015613a8857613a886139d7565b039392505050565b600060208284031215613aa257600080fd5b5051919050565b600181815b80851115613ae4578160001904821115613aca57613aca6139d7565b80851615613ad757918102915b93841c9390800290613aae565b509250929050565b600082613afb57506001610dcb565b81613b0857506000610dcb565b8160018114613b1e5760028114613b2857613b44565b6001915050610dcb565b60ff841115613b3957613b396139d7565b50506001821b610dcb565b5060208310610133831016604e8410600b8410161715613b67575081810a610dcb565b613b718383613aa9565b8060001904821115613b8557613b856139d7565b029392505050565b600061109060ff841683613aec565b6000816000190483118215151615613bb657613bb66139d7565b500290565b600063ffffffff80831681811415613bd557613bd56139d7565b6001019392505050565b6001600160a01b0383168152604060208201526000610fc160408301846135a6565b600181811c90821680613c1557607f821691505b60208210811415613c3657634e487b7160e01b600052602260045260246000fd5b50919050565b8054600090600180821691901c81613c5257607f165b60208110821415613c7357634e487b7160e01b600052602260045260246000fd5b839150601f811115613c8b5783600052602060002091505b8154915077ffffffffffffffffffffffffffffffffffffffffffffffff1980831693506008821015613cc75780818360080360031b1b84161693505b505050919050565b60008151602083015177ffffffffffffffffffffffffffffffffffffffffffffffff1980821693506008831015613cc75760089290920360031b82901b161692915050565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351613d4c81601785016020880161357a565b7f206973206d697373696e6720726f6c65200000000000000000000000000000006017918401918201528351613d8981602884016020880161357a565b01602801949350505050565b60208152600061109060208301846135a6565b634e487b7160e01b600052603260045260246000fd5b600081613dcd57613dcd6139d7565b506000190190565b60008251613de781846020870161357a565b919091019291505056fea2646970667358221220d5c5ed31dc6311fe75d5e2e872337d050fd769c1bc4616eeeed884fd015c266364736f6c634300080c0033

Deployed Bytecode

0x6080604052600436106103555760003560e01c80637c9acc23116101bb578063d547741f116100f7578063e7157b7411610095578063f25552781161006f578063f255527814610c80578063f2d76d0d14610ca0578063f36c8f5c14610cd6578063fe5ff46814610d0a57600080fd5b8063e7157b7414610bdd578063ee90a85414610bfd578063ef49664d14610c1d57600080fd5b8063ddb0ed96116100d1578063ddb0ed9614610a71578063e2e71f9314610af9578063e4a4fa5f14610b6a578063e65db16d14610b7d57600080fd5b8063d547741f14610a1e578063d80992e714610a3e578063dd898a7f14610a5e57600080fd5b8063a432d4ca11610164578063c3e9ffbd1161013e578063c3e9ffbd1461097a578063c4804ef01461099a578063c59a9703146109de578063cd7cbe5c146109fe57600080fd5b8063a432d4ca1461091a578063b9a52efe1461093a578063bd629d261461095a57600080fd5b806391d148541161019557806391d14854146108815780639e3c54ae146108c5578063a217fddf1461090557600080fd5b80637c9acc231461082c578063801928211461084c57806384d688b61461086157600080fd5b80632f2ff15d116102955780634f1e73ea11610233578063627c34ad1161020d578063627c34ad146107a957806369b41170146107c957806369fa06d0146107f957806372fc6d621461081957600080fd5b80634f1e73ea1461075657806350ee31e7146107765780636219880e1461078957600080fd5b806349fae6591161026f57806349fae659146106c25780634a1ec016146106e25780634ed8611f146107025780634eecf4411461072257600080fd5b80632f2ff15d1461064e57806336568abe1461066e578063376202951461068e57600080fd5b8063162bd10a116103025780631dec844b116102dc5780631dec844b146104c95780631f0a2b6e146105ca578063241c10a3146105ea578063248a9ca31461061e57600080fd5b8063162bd10a1461046757806318b33775146104875780631accba8f146104a957600080fd5b80630ee1d786116103335780630ee1d786146103ff57806310da418c1461041f578063158ef93e1461044d57600080fd5b806301ffc9a71461035a5780630abd834e1461038f5780630e71d8a2146103dd575b600080fd5b34801561036657600080fd5b5061037a61037536600461342d565b610d68565b60405190151581526020015b60405180910390f35b34801561039b57600080fd5b506103c56103aa366004613457565b6008602052600090815260409020546001600160a01b031681565b6040516001600160a01b039091168152602001610386565b3480156103e957600080fd5b506103fd6103f8366004613485565b610dd1565b005b34801561040b57600080fd5b5061037a61041a3660046134f3565b610e9a565b34801561042b57600080fd5b5061043f61043a366004613457565b610f24565b604051908152602001610386565b34801561045957600080fd5b5060015461037a9060ff1681565b34801561047357600080fd5b5061043f61048236600461355d565b610fc9565b61049a610495366004613485565b611097565b604051610386939291906135d2565b3480156104b557600080fd5b5061043f6104c4366004613457565b6110bc565b3480156104d557600080fd5b506105636104e436600461355d565b6002602081815260009283526040928390208054845160a081018652600183015460ff8082168352610100820461ffff169583019590955263010000008104851696820196909652640100000000860484166060820152600160281b90950463ffffffff16608086015292015467ffffffffffffffff90921692911683565b6040805167ffffffffffffffff9094168452825160ff90811660208087019190915284015161ffff1685830152908301518116606080860191909152830151166080808501919091529091015163ffffffff1660a0830152151560c082015260e001610386565b3480156105d657600080fd5b506103fd6105e53660046136e6565b611140565b3480156105f657600080fd5b5061043f7fd2279127a6db36ad34c84940e4b56256cf8df416f8098e820e0db61bb41e312981565b34801561062a57600080fd5b5061043f610639366004613457565b60009081526020819052604090206001015490565b34801561065a57600080fd5b506103fd610669366004613730565b611357565b34801561067a57600080fd5b506103fd610689366004613730565b61137c565b34801561069a57600080fd5b5061043f7fb7c19c4897e038440c53ecfaa804a0f8d20573cb698f85962cd7515a7744106481565b3480156106ce57600080fd5b506103fd6106dd3660046134f3565b61140d565b3480156106ee57600080fd5b5061043f6106fd36600461355d565b61146d565b34801561070e57600080fd5b5061037a61071d3660046134f3565b611513565b34801561072e57600080fd5b5061043f7f1991205f9b9e6359222ed4bbde98eebd6c5a90f432c11ce781941f6acee3127e81565b34801561076257600080fd5b506103fd610771366004613760565b611627565b61037a6107843660046134f3565b6116f9565b34801561079557600080fd5b506103fd6107a436600461377e565b611704565b3480156107b557600080fd5b506103fd6107c436600461355d565b611792565b3480156107d557600080fd5b506107e06202a30081565b60405167ffffffffffffffff9091168152602001610386565b34801561080557600080fd5b506103fd61081436600461355d565b6117e5565b6103fd61082736600461355d565b611a65565b34801561083857600080fd5b5061049a610847366004613485565b611b67565b34801561085857600080fd5b506103fd611b7a565b34801561086d57600080fd5b506103fd61087c3660046137d0565b611bb3565b34801561088d57600080fd5b5061037a61089c366004613730565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b3480156108d157600080fd5b506108e56108e0366004613485565b611c4a565b60408051921515835267ffffffffffffffff909116602083015201610386565b34801561091157600080fd5b5061043f600081565b34801561092657600080fd5b506103fd610935366004613805565b611c71565b34801561094657600080fd5b506103fd6109553660046137d0565b611d69565b34801561096657600080fd5b506103fd61097536600461355d565b61215e565b34801561098657600080fd5b506103fd61099536600461377e565b612200565b3480156109a657600080fd5b5061037a6109b536600461355d565b6001600160a01b03166000908152600760205260409020544267ffffffffffffffff9091161190565b3480156109ea57600080fd5b5061049a6109f9366004613485565b61226b565b348015610a0a57600080fd5b506103fd610a1936600461377e565b612346565b348015610a2a57600080fd5b506103fd610a39366004613730565b6123ab565b348015610a4a57600080fd5b506103fd610a59366004613861565b6123d0565b6103fd610a6c3660046134f3565b612493565b348015610a7d57600080fd5b50600a54610abb9060ff80821691610100810461ffff169163010000008204811691640100000000810490911690600160281b900463ffffffff1685565b6040805160ff968716815261ffff90951660208601529285169284019290925292909216606082015263ffffffff909116608082015260a001610386565b348015610b0557600080fd5b50610b42610b1436600461355d565b60076020526000908152604090205467ffffffffffffffff811690600160401b90046001600160c01b031682565b6040805167ffffffffffffffff90931683526001600160c01b03909116602083015201610386565b6108e5610b78366004613485565b6124a0565b348015610b8957600080fd5b50610abb610b9836600461388e565b60066020526000908152604090205460ff80821691610100810461ffff169163010000008204811691640100000000810490911690600160281b900463ffffffff1685565b348015610be957600080fd5b506103fd610bf83660046138b7565b6124ac565b348015610c0957600080fd5b5061049a610c18366004613485565b61255e565b348015610c2957600080fd5b50610c5f610c3836600461355d565b60046020526000908152604090205467ffffffffffffffff80821691600160401b90041682565b6040805167ffffffffffffffff938416815292909116602083015201610386565b348015610c8c57600080fd5b506103fd610c9b366004613934565b61256e565b348015610cac57600080fd5b506103c5610cbb366004613962565b6003602052600090815260409020546001600160a01b031681565b348015610ce257600080fd5b5061043f7f71840dc4906352362b0cdaf79870196c8e42acafade72d5d5a6d59291253ceb181565b348015610d1657600080fd5b50610d50610d2536600461355d565b6001600160a01b0316600090815260076020526040902054600160401b90046001600160c01b031690565b6040516001600160c01b039091168152602001610386565b60006001600160e01b031982167f7965db0b000000000000000000000000000000000000000000000000000000001480610dcb57507f01ffc9a7000000000000000000000000000000000000000000000000000000006001600160e01b03198316145b92915050565b33600090815260026020818152604092839020835160608082018652825467ffffffffffffffff168252855160a081018752600184015460ff8082168352610100820461ffff1683880152630100000082048116838a01526401000000008204811693830193909352600160281b900463ffffffff1660808201529382019390935292015416151591810191909152610e69816124ac565b6001600160a01b03831660009081526005602090815260408083208584529091528120610e959161335a565b505050565b60003215610ea757600080fd5b60405163f50ef57d60e01b81526001600160a01b0387169063f50ef57d90610ed990889088908890889060040161397d565b602060405180830381865afa158015610ef6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f1a91906139ba565b9695505050505050565b60008160ff8116610f385750600092915050565b60ff818116600090815260066020908152604091829020825160a08101845290548085168252610100810461ffff16928201929092526301000000820484169281019290925264010000000081049092166060820152600160281b90910463ffffffff16608082015260015b8151600190911b1660ff16610fb857600080fd5b610fc1816126ee565b949350505050565b6001600160a01b0381166000908152600260208181526040808420815160608082018452825467ffffffffffffffff168252835160a081018552600184015460ff8082168352610100820461ffff1683890152630100000082048116838801526401000000008204811693830193909352600160281b900463ffffffff16608082015294820194909452930154909116151590820152611068816124ac565b60045b602082015151600190911b1660ff1661108357600080fd5b61109081602001516126ee565b9392505050565b60008060606110a584612713565b6110af85856127d3565b9250925092509250925092565b60008160ff81166110d05750600092915050565b60ff818116600090815260066020908152604091829020825160a08101845290548085168252610100810461ffff16928201929092526301000000820484169281019290925264010000000081049092166060820152600160281b90910463ffffffff1660808201526004610fa4565b7f71840dc4906352362b0cdaf79870196c8e42acafade72d5d5a6d59291253ceb161116a8161281d565b6001600160a01b038416600090815260026020818152604092839020835160608082018652825467ffffffffffffffff168252855160a081018752600184015460ff8082168352610100820461ffff1683880152630100000082048116838a01526401000000008204811693830193909352600160281b900463ffffffff166080820152938201939093529201541615159181019190915261120b816124ac565b6112158484612827565b93508381602001819052508060026000876001600160a01b03166001600160a01b0316815260200190815260200160002060008201518160000160006101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555060208201518160010160008201518160000160006101000a81548160ff021916908360ff16021790555060208201518160000160016101000a81548161ffff021916908361ffff16021790555060408201518160000160036101000a81548160ff021916908360ff16021790555060608201518160000160046101000a81548160ff021916908360ff16021790555060808201518160000160056101000a81548163ffffffff021916908363ffffffff160217905550505060408201518160020160006101000a81548160ff0219169083151502179055509050505050505050565b6000828152602081905260409020600101546113728161281d565b610e9583836128e2565b6001600160a01b03811633146113ff5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c66000000000000000000000000000000000060648201526084015b60405180910390fd5b6114098282612980565b5050565b61141a8585858585611513565b6114665760405162461bcd60e51b815260206004820152601260248201527f696e76616c696420626c6f636b2068617368000000000000000000000000000060448201526064016113f6565b5050505050565b6001600160a01b0381166000908152600260208181526040808420815160608082018452825467ffffffffffffffff168252835160a081018552600184015460ff8082168352610100820461ffff1683890152630100000082048116838801526401000000008204811693830193909352600160281b900463ffffffff1660808201529482019490945293015490911615159082015261150c816124ac565b600161106b565b336000908152600260208181526040808420815160608082018452825467ffffffffffffffff168252835160a081018552600184015460ff8082168352610100820461ffff1683890152630100000082048116838801526401000000008204811693830193909352600160281b900463ffffffff166080820152948201949094529301549091161515908201526115a9816124ac565b60405163f50ef57d60e01b81526001600160a01b0388169063f50ef57d906115db90899089908990899060040161397d565b602060405180830381865afa1580156115f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061161c91906139ba565b979650505050505050565b7f71840dc4906352362b0cdaf79870196c8e42acafade72d5d5a6d59291253ceb16116518161281d565b61165b8383612827565b60ff948516600090815260066020908152604091829020835181549285015193850151606086015160809096015163ffffffff16600160281b0268ffffffff000000000019968b166401000000000264ff0000000019928c166301000000029290921664ffff0000001961ffff9097166101000262ffffff1990961693909b1692909217939093179390931697909717179190911617909355505050565b6000610ea7846129ff565b7fd2279127a6db36ad34c84940e4b56256cf8df416f8098e820e0db61bb41e312961172e8161281d565b6001600160a01b03831660009081526007602052604090208054839190600890611769908490600160401b90046001600160c01b03166139ed565b92506101000a8154816001600160c01b0302191690836001600160c01b03160217905550505050565b7fb7c19c4897e038440c53ecfaa804a0f8d20573cb698f85962cd7515a774410646117bc8161281d565b506001600160a01b03166000908152600760205260409020805467ffffffffffffffff19169055565b6001600160a01b03811660009081526002602052604090205467ffffffffffffffff16156118555760405162461bcd60e51b815260206004820152601060248201527f6475706c69636174652070726f7665720000000000000000000000000000000060448201526064016113f6565b6001600160a01b03811660009081526004602090815260409182902082518084019093525467ffffffffffffffff8082168452600160401b909104169082018190526118e35760405162461bcd60e51b815260206004820152600f60248201527f696e76616c69642061646472657373000000000000000000000000000000000060448201526064016113f6565b42816000015167ffffffffffffffff1611156119415760405162461bcd60e51b815260206004820152600960248201527f6e6f74207265616479000000000000000000000000000000000000000000000060448201526064016113f6565b60208082015167ffffffffffffffff166000908152600390915260409020546001600160a01b0316156119b65760405162461bcd60e51b815260206004820152601160248201527f6475706c69636174652076657273696f6e00000000000000000000000000000060448201526064016113f6565b6020818101805167ffffffffffffffff9081166000908152600384526040808220805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03891690811790915584518184526002875292829020805467ffffffffffffffff19169385169390931790925592518351918252909116928101929092527f3410506653c3f6710aba2bb8abaa167692179aad5f586d2a8630c6007a3844c291015b60405180910390a15050565b33600081815260026020818152604092839020835160608082018652825467ffffffffffffffff168252855160a081018752600184015460ff8082168352610100820461ffff1683880152630100000082048116838a01526401000000008204811693830193909352600160281b900463ffffffff1660808201529382019390935292015416151591810191909152611afd816124ac565b60208082015160408051928301819052600960608401527f70726f766546616374000000000000000000000000000000000000000000000060808401526001600160a01b03851690830152610e959185919060a0015b604051602081830303815290604052612aa7565b600080606060ff8416156110a557600080fd5b7f1991205f9b9e6359222ed4bbde98eebd6c5a90f432c11ce781941f6acee3127e611ba48161281d565b506001805460ff191681179055565b7fb7c19c4897e038440c53ecfaa804a0f8d20573cb698f85962cd7515a77441064611bdd8161281d565b6001600160a01b03831660009081526007602052604090205467ffffffffffffffff808416911610611c0e57600080fd5b506001600160a01b03919091166000908152600760205260409020805467ffffffffffffffff191667ffffffffffffffff909216919091179055565b60008060ff831615611c5b57600080fd5b611c658484612d3e565b915091505b9250929050565b33600090815260026020818152604092839020835160608082018652825467ffffffffffffffff168252855160a081018752600184015460ff8082168352610100820461ffff1683880152630100000082048116838a01526401000000008204811693830193909352600160281b900463ffffffff1660808201529382019390935292015416151591810191909152611d09816124ac565b8051604051611d1f919085908590602001613a18565b60408051601f198184030181529181526001600160a01b03871660009081526005602090815282822088835281529190208251611d6193919290910190613394565b505050505050565b7f1991205f9b9e6359222ed4bbde98eebd6c5a90f432c11ce781941f6acee3127e611d938161281d565b67ffffffffffffffff8216611dea5760405162461bcd60e51b815260206004820152601860248201527f76657273696f6e206d757374206e6f74206265207a65726f000000000000000060448201526064016113f6565b67ffffffffffffffff82166000908152600360205260409020546001600160a01b031615611e5a5760405162461bcd60e51b815260206004820152601160248201527f6475706c69636174652076657273696f6e00000000000000000000000000000060448201526064016113f6565b6001600160a01b03831660009081526002602052604090205467ffffffffffffffff1615611eca5760405162461bcd60e51b815260206004820152601060248201527f6475706c69636174652070726f7665720000000000000000000000000000000060448201526064016113f6565b6001600160a01b038316600090815260046020526040902054600160401b900467ffffffffffffffff1615611f415760405162461bcd60e51b815260206004820152600f60248201527f616c72656164792070656e64696e67000000000000000000000000000000000060448201526064016113f6565b60015460009060ff16611f55576000611f5a565b6202a3005b67ffffffffffffffff169050600060405180604001604052808342611f7f9190613a50565b67ffffffffffffffff90811682528681166020928301526001600160a01b03881660008181526004845260408082208651815488880180518816600160401b027fffffffffffffffffffffffffffffffff000000000000000000000000000000009092169288169290921717909155815160608082018452848252835160a0810185528581528089018690528085018690528082018690526080808201879052838a019182528386018781529787526002808b529686902093518454991667ffffffffffffffff19999099169890981783555180516001840180549a830151838801519484015193909a015163ffffffff16600160281b0268ffffffff00000000001960ff9485166401000000000264ff00000000199686166301000000029690961664ffff0000001961ffff9d909d166101000262ffffff19909e1694909516939093179b909b17999099169190911791909117169690961790945591519301805493151560ff19949094169390931790925551825191519293507f3439752cf7c5ae5b0b2c0ce121f5c0f492fa64e5166586d7a53d6fbdcca238739261214f928992916001600160a01b0393909316835267ffffffffffffffff918216602084015216604082015260600190565b60405180910390a15050505050565b7f71840dc4906352362b0cdaf79870196c8e42acafade72d5d5a6d59291253ceb16121888161281d565b6001600160a01b0382166000818152600260208181526040808420909201805460ff19166001179055600481529181902054815193845267ffffffffffffffff600160401b90910416918301919091527f369ae2dd83c9f965bbf9db538808d71ce25d5c2bf1b548406deeb09d0653d2d29101611a59565b7fd2279127a6db36ad34c84940e4b56256cf8df416f8098e820e0db61bb41e312961222a8161281d565b506001600160a01b03909116600090815260076020526040902080546001600160c01b03909216600160401b0267ffffffffffffffff909216919091179055565b336000908152600260208181526040808420815160608082018452825467ffffffffffffffff168252835160a081018552600184015460ff8082168352610100820461ffff1683890152630100000082048116838801526401000000008204811683850152600160281b90910463ffffffff1660808301529583015291909401549092161515908301528291612300816124ac565b6001600160a01b0386166000908152600560209081526040808320888452909152902061232c90612d7c565b67ffffffffffffffff821615159891975095509350505050565b7fd2279127a6db36ad34c84940e4b56256cf8df416f8098e820e0db61bb41e31296123708161281d565b6001600160a01b03831660009081526007602052604090208054839190600890611769908490600160401b90046001600160c01b0316613a68565b6000828152602081905260409020600101546123c68161281d565b610e958383612980565b7f71840dc4906352362b0cdaf79870196c8e42acafade72d5d5a6d59291253ceb16123fa8161281d565b6124048383612827565b8051600a805460208401516040850151606086015160809096015163ffffffff16600160281b0268ffffffff00000000001960ff9788166401000000000264ff00000000199389166301000000029390931664ffff0000001961ffff9095166101000262ffffff1990961698909716979097179390931791909116939093179290921792909216179055505050565b61141a85858585856116f9565b600080611c5b83612713565b60408101511515600114156125035760405162461bcd60e51b815260206004820152600e60248201527f7265766f6b65642070726f76657200000000000000000000000000000000000060448201526064016113f6565b805167ffffffffffffffff1661255b5760405162461bcd60e51b815260206004820152600e60248201527f756e6b6e6f776e2070726f76657200000000000000000000000000000000000060448201526064016113f6565b50565b600080606032156110a557600080fd5b7f71840dc4906352362b0cdaf79870196c8e42acafade72d5d5a6d59291253ceb16125988161281d565b6001600160a01b0382166125ab57600080fd5b6001600160a01b0383166125f4576040516001600160a01b038316904780156108fc02916000818181858888f193505050501580156125ee573d6000803e3d6000fd5b50505050565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0384169063a9059cbb90849083906370a0823190602401602060405180830381865afa15801561265b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061267f9190613a90565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044016020604051808303816000875af11580156126ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125ee91906139ba565b60008160600151600a6127019190613b8d565b826040015160ff16610dcb9190613b9c565b8060ff8116156114095760ff808216600090815260066020908152604091829020825160a0810184529054808516825261ffff61010082041682840152630100000081048516828501526401000000008104909416606082015263ffffffff600160281b90940493909316608084015290516114099233929091611b53918791016040808252600a908201527f76657269667946616374000000000000000000000000000000000000000000006060820152602081019190915260800190565b6001600160a01b03821660009081526005602090815260408083208484529091528120819060609061280490612d7c565b67ffffffffffffffff8216151597919650945092505050565b61255b8133612e8a565b6040805160a0810182526000808252602082018190529181018290526060810182905260808101829052906001600160a01b038316156128d0576009805463ffffffff1690600061287783613bbb565b82546101009290920a63ffffffff818102199093169183160217909155600954166000818152600860205260409020805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0387161790559150505b63ffffffff1660808401525090919050565b6000828152602081815260408083206001600160a01b038516845290915290205460ff16611409576000828152602081815260408083206001600160a01b03851684529091529020805460ff1916600117905561293c3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6000828152602081815260408083206001600160a01b038516845290915290205460ff1615611409576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6040805160a081018252600a5460ff808216835261ffff610100830416602080850191909152630100000083048216848601526401000000008304909116606084015263ffffffff600160281b909204919091166080830152915161255b92339291611b53918691016040808252600b908201527f766572696679426c6f636b0000000000000000000000000000000000000000006060820152602081019190915260800190565b6000612ab2836126ee565b835190915060011615612ac55750505050565b825160021615612adc57803410612adc5750505050565b825160041615612bc9576001600160a01b03841660009081526007602090815260409182902082518084019093525467ffffffffffffffff8116808452600160401b9091046001600160c01b031691830191909152421015612b3f575050505050565b836020015161ffff1681602001516001600160c01b031610612bc757836020015161ffff1681602001818151612b759190613a68565b6001600160c01b039081169091526001600160a01b03909616600090815260076020908152604090912083519190930151909616600160401b0267ffffffffffffffff90961695909517905550505050565b505b825160081615612c9657608083015163ffffffff16612be757600080fd5b608083015163ffffffff166000908152600860205260409020546001600160a01b031680612c1457600080fd5b6040517f3e23e3990000000000000000000000000000000000000000000000000000000081526001600160a01b03821690633e23e399903490612c5d9089908890600401613bdf565b6000604051808303818588803b158015612c7657600080fd5b505af1158015612c8a573d6000803e3d6000fd5b50505050505050505050565b825160101615612cf657608083015163ffffffff16612cb457600080fd5b608083015163ffffffff166000908152600860205260409020546001600160a01b031680612ce157600080fd5b6114666001600160a01b038216863085612f08565b60405162461bcd60e51b815260206004820152601060248201527f696e73756666696369656e74206665650000000000000000000000000000000060448201526064016113f6565b6001600160a01b038216600090815260056020908152604080832084845290915281208190612d6c90613c3c565b60c01c8015159590945092505050565b60006060828054612d8c90613c01565b80601f0160208091040260200160405190810160405280929190818152602001828054612db890613c01565b8015612e055780601f10612dda57610100808354040283529160200191612e05565b820191906000526020600020905b815481529060010190602001808311612de857829003601f168201915b50505050509050600081511115612e8557600881511015612e685760405162461bcd60e51b815260206004820152601860248201527f666163742064617461206c656e67746820696e76616c6964000000000000000060448201526064016113f6565b612e7181613ccf565b815160071901600890920191825260c01c91505b915091565b6000828152602081815260408083206001600160a01b038516845290915290205460ff1661140957612ec6816001600160a01b03166014612f90565b612ed1836020612f90565b604051602001612ee2929190613d14565b60408051601f198184030181529082905262461bcd60e51b82526113f691600401613d95565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd000000000000000000000000000000000000000000000000000000001790526125ee908590613171565b60606000612f9f836002613b9c565b612faa906002613a50565b67ffffffffffffffff811115612fc257612fc2613606565b6040519080825280601f01601f191660200182016040528015612fec576020820181803683370190505b5090507f30000000000000000000000000000000000000000000000000000000000000008160008151811061302357613023613da8565b60200101906001600160f81b031916908160001a9053507f78000000000000000000000000000000000000000000000000000000000000008160018151811061306e5761306e613da8565b60200101906001600160f81b031916908160001a9053506000613092846002613b9c565b61309d906001613a50565b90505b6001811115613122577f303132333435363738396162636465660000000000000000000000000000000085600f16601081106130de576130de613da8565b1a60f81b8282815181106130f4576130f4613da8565b60200101906001600160f81b031916908160001a90535060049490941c9361311b81613dbe565b90506130a0565b5083156110905760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e7460448201526064016113f6565b60006131c6826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166132569092919063ffffffff16565b805190915015610e9557808060200190518101906131e491906139ba565b610e955760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016113f6565b6060610fc18484600085856001600160a01b0385163b6132b85760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016113f6565b600080866001600160a01b031685876040516132d49190613dd5565b60006040518083038185875af1925050503d8060008114613311576040519150601f19603f3d011682016040523d82523d6000602084013e613316565b606091505b509150915061161c82828660608315613330575081611090565b8251156133405782518084602001fd5b8160405162461bcd60e51b81526004016113f69190613d95565b50805461336690613c01565b6000825580601f10613376575050565b601f01602090049060005260206000209081019061255b9190613418565b8280546133a090613c01565b90600052602060002090601f0160209004810192826133c25760008555613408565b82601f106133db57805160ff1916838001178555613408565b82800160010185558215613408579182015b828111156134085782518255916020019190600101906133ed565b50613414929150613418565b5090565b5b808211156134145760008155600101613419565b60006020828403121561343f57600080fd5b81356001600160e01b03198116811461109057600080fd5b60006020828403121561346957600080fd5b5035919050565b6001600160a01b038116811461255b57600080fd5b6000806040838503121561349857600080fd5b82356134a381613470565b946020939093013593505050565b60008083601f8401126134c357600080fd5b50813567ffffffffffffffff8111156134db57600080fd5b602083019150836020828501011115611c6a57600080fd5b60008060008060006080868803121561350b57600080fd5b853561351681613470565b94506020860135935060408601359250606086013567ffffffffffffffff81111561354057600080fd5b61354c888289016134b1565b969995985093965092949392505050565b60006020828403121561356f57600080fd5b813561109081613470565b60005b8381101561359557818101518382015260200161357d565b838111156125ee5750506000910152565b600081518084526135be81602086016020860161357a565b601f01601f19169290920160200192915050565b831515815267ffffffffffffffff831660208201526060604082015260006135fd60608301846135a6565b95945050505050565b634e487b7160e01b600052604160045260246000fd5b803560ff8116811461362d57600080fd5b919050565b600060a0828403121561364457600080fd5b60405160a0810181811067ffffffffffffffff8211171561367557634e487b7160e01b600052604160045260246000fd5b6040529050806136848361361c565b8152602083013561ffff8116811461369b57600080fd5b60208201526136ac6040840161361c565b60408201526136bd6060840161361c565b6060820152608083013563ffffffff811681146136d957600080fd5b6080919091015292915050565b600080600060e084860312156136fb57600080fd5b833561370681613470565b92506137158560208601613632565b915060c084013561372581613470565b809150509250925092565b6000806040838503121561374357600080fd5b82359150602083013561375581613470565b809150509250929050565b600080600060e0848603121561377557600080fd5b6137068461361c565b6000806040838503121561379157600080fd5b823561379c81613470565b915060208301356001600160c01b038116811461375557600080fd5b803567ffffffffffffffff8116811461362d57600080fd5b600080604083850312156137e357600080fd5b82356137ee81613470565b91506137fc602084016137b8565b90509250929050565b6000806000806060858703121561381b57600080fd5b843561382681613470565b935060208501359250604085013567ffffffffffffffff81111561384957600080fd5b613855878288016134b1565b95989497509550505050565b60008060c0838503121561387457600080fd5b61387e8484613632565b915060a083013561375581613470565b6000602082840312156138a057600080fd5b6110908261361c565b801515811461255b57600080fd5b600060e082840312156138c957600080fd5b6040516060810181811067ffffffffffffffff821117156138fa57634e487b7160e01b600052604160045260246000fd5b604052613906836137b8565b81526139158460208501613632565b602082015260c0830135613928816138a9565b60408201529392505050565b6000806040838503121561394757600080fd5b823561395281613470565b9150602083013561375581613470565b60006020828403121561397457600080fd5b611090826137b8565b84815283602082015260606040820152816060820152818360808301376000818301608090810191909152601f909201601f191601019392505050565b6000602082840312156139cc57600080fd5b8151611090816138a9565b634e487b7160e01b600052601160045260246000fd5b60006001600160c01b03808316818516808303821115613a0f57613a0f6139d7565b01949350505050565b77ffffffffffffffffffffffffffffffffffffffffffffffff198460c01b168152818360088301376000910160080190815292915050565b60008219821115613a6357613a636139d7565b500190565b60006001600160c01b0383811690831681811015613a8857613a886139d7565b039392505050565b600060208284031215613aa257600080fd5b5051919050565b600181815b80851115613ae4578160001904821115613aca57613aca6139d7565b80851615613ad757918102915b93841c9390800290613aae565b509250929050565b600082613afb57506001610dcb565b81613b0857506000610dcb565b8160018114613b1e5760028114613b2857613b44565b6001915050610dcb565b60ff841115613b3957613b396139d7565b50506001821b610dcb565b5060208310610133831016604e8410600b8410161715613b67575081810a610dcb565b613b718383613aa9565b8060001904821115613b8557613b856139d7565b029392505050565b600061109060ff841683613aec565b6000816000190483118215151615613bb657613bb66139d7565b500290565b600063ffffffff80831681811415613bd557613bd56139d7565b6001019392505050565b6001600160a01b0383168152604060208201526000610fc160408301846135a6565b600181811c90821680613c1557607f821691505b60208210811415613c3657634e487b7160e01b600052602260045260246000fd5b50919050565b8054600090600180821691901c81613c5257607f165b60208110821415613c7357634e487b7160e01b600052602260045260246000fd5b839150601f811115613c8b5783600052602060002091505b8154915077ffffffffffffffffffffffffffffffffffffffffffffffff1980831693506008821015613cc75780818360080360031b1b84161693505b505050919050565b60008151602083015177ffffffffffffffffffffffffffffffffffffffffffffffff1980821693506008831015613cc75760089290920360031b82901b161692915050565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351613d4c81601785016020880161357a565b7f206973206d697373696e6720726f6c65200000000000000000000000000000006017918401918201528351613d8981602884016020880161357a565b01602801949350505050565b60208152600061109060208301846135a6565b634e487b7160e01b600052603260045260246000fd5b600081613dcd57613dcd6139d7565b506000190190565b60008251613de781846020870161357a565b919091019291505056fea2646970667358221220d5c5ed31dc6311fe75d5e2e872337d050fd769c1bc4616eeeed884fd015c266364736f6c634300080c0033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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