ETH Price: $3,194.75 (-0.23%)

Contract

0x09e8f8aF4B4f4e379Fe2Fa8E6a704A33003eC3a9
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
0x60806040206676772024-09-03 4:31:2369 days ago1725337883IN
 Create: LPNRegistryV1
0 ETH0.00301291.17487499

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
LPNRegistryV1

Compiler Version
v0.8.25+commit.b61c2a91

Optimization Enabled:
Yes with 200 runs

Other Settings:
cancun EvmVersion
File 1 of 14 : LPNRegistryV1.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import {OwnableWhitelist} from "../utils/OwnableWhitelist.sol";
import {Initializable} from
    "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {RegistrationManager} from "./RegistrationManager.sol";
import {QueryManager} from "./QueryManager.sol";

/// @title LPNRegistryV1
/// @notice A registry contract for managing LPN (Lagrange Proving Network) registrations and requests.
contract LPNRegistryV1 is
    QueryManager,
    RegistrationManager,
    OwnableWhitelist,
    Initializable
{
    function initialize(address owner) external initializer {
        OwnableWhitelist._initialize(owner);
    }

    /// @dev Only owner can register tables currently
    function registerTable(
        bytes32 hash,
        address contractAddr,
        uint96 chainId,
        uint256 genesisBlock,
        string calldata name,
        string calldata schema
    ) external onlyOwner {
        // TODO: Implement payment model
        _registerTable(hash, contractAddr, chainId, genesisBlock, name, schema);
    }

    /// @notice The owner withdraws all fees accumulated
    function withdrawFees() external onlyOwner returns (bool) {
        return _withdrawFees();
    }
}

File 2 of 14 : OwnableWhitelist.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import {Ownable} from "solady/auth/Ownable.sol";

/// @notice Error thrown when an unauthorized caller attempts to perform an action.
error NotAuthorized();

/// @title OwnableWhitelist
/// @notice A contract for managing whitelisted addresses.
abstract contract OwnableWhitelist is Ownable {
    event Whitelisted(address addr, bool value);
    event BatchWhitelisted(address[] addrs, bool value);

    /// @notice Mapping to track whitelisted addresses.
    mapping(address => bool) public whitelist;

    /// @notice Modifier to restrict access to whitelisted addresses only.
    modifier onlyWhitelist(address someAddress) {
        if (!whitelist[someAddress]) {
            revert NotAuthorized();
        }
        _;
    }

    function _initialize(address owner) internal {
        _initializeOwner(owner);
    }

    function toggleWhitelist(address client) external onlyOwner {
        bool newState = !whitelist[client];
        whitelist[client] = newState;

        emit Whitelisted(client, newState);
    }

    /// @notice add a batch of addresses to the whitelist.
    /// @param addrs an array of addresses to add.
    function addToWhitelist(address[] calldata addrs) external onlyOwner {
        for (uint256 i; i < addrs.length; i++) {
            whitelist[addrs[i]] = true;
        }
        emit BatchWhitelisted(addrs, true);
    }

    /// @notice Remove a batch of addresses from the whitelist.
    /// @param addrs an array of addresses to remove.
    function removeFromWhitelist(address[] calldata addrs) external onlyOwner {
        for (uint256 i; i < addrs.length; i++) {
            delete whitelist[addrs[i]];
        }
        emit BatchWhitelisted(addrs, false);
    }
}

File 3 of 14 : Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.20;

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Storage of the initializable contract.
     *
     * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
     * when using with upgradeable contracts.
     *
     * @custom:storage-location erc7201:openzeppelin.storage.Initializable
     */
    struct InitializableStorage {
        /**
         * @dev Indicates that the contract has been initialized.
         */
        uint64 _initialized;
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool _initializing;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;

    /**
     * @dev The contract is already initialized.
     */
    error InvalidInitialization();

    /**
     * @dev The contract is not initializing.
     */
    error NotInitializing();

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint64 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
     * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
     * production.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        // Cache values to avoid duplicated sloads
        bool isTopLevelCall = !$._initializing;
        uint64 initialized = $._initialized;

        // Allowed calls:
        // - initialSetup: the contract is not in the initializing state and no previous version was
        //                 initialized
        // - construction: the contract is initialized at version 1 (no reininitialization) and the
        //                 current contract is just being deployed
        bool initialSetup = initialized == 0 && isTopLevelCall;
        bool construction = initialized == 1 && address(this).code.length == 0;

        if (!initialSetup && !construction) {
            revert InvalidInitialization();
        }
        $._initialized = 1;
        if (isTopLevelCall) {
            $._initializing = true;
        }
        _;
        if (isTopLevelCall) {
            $._initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint64 version) {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing || $._initialized >= version) {
            revert InvalidInitialization();
        }
        $._initialized = version;
        $._initializing = true;
        _;
        $._initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        _checkInitializing();
        _;
    }

    /**
     * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
     */
    function _checkInitializing() internal view virtual {
        if (!_isInitializing()) {
            revert NotInitializing();
        }
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing) {
            revert InvalidInitialization();
        }
        if ($._initialized != type(uint64).max) {
            $._initialized = type(uint64).max;
            emit Initialized(type(uint64).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint64) {
        return _getInitializableStorage()._initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _getInitializableStorage()._initializing;
    }

    /**
     * @dev Returns a pointer to the storage namespace.
     */
    // solhint-disable-next-line var-name-mixedcase
    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
        assembly {
            $.slot := INITIALIZABLE_STORAGE
        }
    }
}

File 4 of 14 : RegistrationManager.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import {isEthereum, isOPStack, isMantle, isCDK} from "../utils/Constants.sol";
import {IRegistrationManager} from "./interfaces/IRegistrationManager.sol";

/// @notice Error thrown when attempting to register a table more than once.
error TableAlreadyRegistered();

/// @notice Error thrown when attempting to register a query more than once.
error QueryAlreadyRegistered();

/// @title RegistrationManager
/// @notice TODO
contract RegistrationManager is IRegistrationManager {
    /// @notice Mapping to track registered tables
    mapping(bytes32 hash => bool registered) public tables;

    /// @notice Mapping to track registered queries
    mapping(bytes32 hash => bool registered) public queries;

    /// @dev Reserves storage slots for future upgrades
    uint256[48] private __gap;

    function _registerTable(
        bytes32 hash,
        address contractAddr,
        uint96 chainId,
        uint256 genesisBlock,
        string calldata name, // TODO: Should we save name?
        string calldata schema
    ) internal {
        if (tables[hash]) {
            revert TableAlreadyRegistered();
        }

        tables[hash] = true; // TODO: Do we need this?

        emit NewTableRegistration(
            hash, contractAddr, chainId, genesisBlock, name, schema
        );
    }

    function registerQuery(bytes32 hash, bytes32 tableHash, string calldata sql)
        external
    {
        if (queries[hash]) {
            revert QueryAlreadyRegistered();
        }

        queries[hash] = true; // TODO: Do we need this?

        emit NewQueryRegistration(hash, tableHash, sql);
    }
}

File 5 of 14 : QueryManager.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import {
    Groth16VerifierExtensions,
    QueryInput,
    QueryOutput
} from "./Groth16VerifierExtensions.sol";
import {ILPNClientV1} from "./interfaces/ILPNClientV1.sol";
import {isCDK} from "../utils/Constants.sol";
import {L1BlockHash, L1BlockNumber} from "../utils/L1Block.sol";
import {isEthereum, isOPStack, isMantle, isCDK} from "../utils/Constants.sol";
import {IQueryManager} from "./interfaces/IQueryManager.sol";

/// @notice Error thrown when attempting to query a block number that is after the current block.
/// @dev endBlock > block.number
error QueryAfterCurrentBlock();

/// @notice Error thrown when attempting to query a range that exceeds the maximum allowed range.
/// @dev endBlock - startBlock > MAX_QUERY_RANGE
error QueryGreaterThanMaxRange();

/// @notice Error thrown when attempting to query an invalid range.
/// @dev startBlock > endBlock
error QueryInvalidRange();

/// @notice Error thrown when gas fee is not paid.
error InsufficientGasFee();

/// @title QueryManager
/// @notice TODO
contract QueryManager is IQueryManager {
    /// @notice The maximum number of blocks a query can be computed over
    uint256 public constant MAX_QUERY_RANGE = 50_000;

    /// @notice A constant gas fee paid for each request to reimburse the relayer when it delivers the response
    uint256 public constant ETH_GAS_FEE = 0.005 ether;
    uint256 public constant OP_GAS_FEE = 0.00045 ether;
    uint256 public constant CDK_GAS_FEE = 0.00045 ether;
    /// @dev Mantle uses a custom gas token
    uint256 public constant MANTLE_GAS_FEE = 1.5 ether;

    /// @notice A counter that assigns unique ids for client requests.
    // TODO: Need to ensure this does not conflict with V0
    uint256 public requestId;

    struct QueryRequest {
        address client;
        QueryInput input;
    }

    /// @notice Mapping to track requests and their associated clients.
    mapping(uint256 requestId => QueryRequest query) public requests;

    /// @dev Reserves storage slots for future upgrades
    uint256[48] private __gap;

    modifier requireGasFee() {
        if (msg.value < gasFee()) {
            revert InsufficientGasFee();
        }
        _;
    }

    /// @notice Validates the query range for a storage contract.
    /// @param startBlock The starting block number of the query range.
    /// @param endBlock The ending block number of the query range.
    /// @dev Reverts with appropriate errors if the query range is invalid:
    ///      - QueryAfterCurrentBlock: If the ending block is after the current block number.
    ///      - QueryInvalidRange: If the starting block is greater than the ending block.
    ///      - QueryGreaterThanMaxRange: If the range (ending block - starting block) exceeds the maximum allowed range.
    modifier validateQueryRange(uint256 startBlock, uint256 endBlock) {
        if (!isCDK() && endBlock > L1BlockNumber()) {
            revert QueryAfterCurrentBlock();
        }
        if (startBlock > endBlock) {
            revert QueryInvalidRange();
        }
        if (endBlock - startBlock + 1 > MAX_QUERY_RANGE) {
            revert QueryGreaterThanMaxRange();
        }
        _;
    }

    function request(
        bytes32 queryHash,
        bytes32[] calldata placeholders,
        uint256 startBlock,
        uint256 endBlock
    )
        external
        payable
        requireGasFee
        validateQueryRange(startBlock, endBlock)
        returns (uint256)
    {
        unchecked {
            requestId++;
        }

        uint256 proofBlock = 0;
        bytes32 blockHash = 0;

        // TODO: Maybe store proofBlock for L1 queries as well
        if (!isEthereum()) {
            proofBlock = L1BlockNumber();
            blockHash = L1BlockHash();
        }

        requests[requestId] = QueryRequest({
            input: QueryInput({
                // TODO:
                limit: 0,
                // TODO:
                offset: 0,
                minBlockNumber: uint64(startBlock),
                maxBlockNumber: uint64(endBlock),
                blockHash: blockHash,
                computationalHash: queryHash,
                userPlaceholders: placeholders
            }),
            client: msg.sender
        });

        // TODO: Limit and offset separate from placeholders ?
        emit NewRequest(
            requestId,
            queryHash,
            msg.sender,
            placeholders,
            startBlock,
            endBlock,
            msg.value,
            proofBlock
        );

        return requestId;
    }

    function respond(
        uint256 requestId_,
        bytes32[] calldata data,
        uint256 blockNumber
    ) external {
        QueryRequest memory query = requests[requestId_];
        delete requests[requestId_];

        if (isEthereum()) {
            query.input.blockHash = blockhash(blockNumber);
        }

        QueryOutput memory result =
            Groth16VerifierExtensions.processQuery(data, query.input);

        ILPNClientV1(query.client).lpnCallback(requestId_, result);

        emit NewResponse(requestId_, query.client, result);
    }

    function gasFee() public view returns (uint256) {
        if (isEthereum()) {
            return ETH_GAS_FEE;
        }

        if (isMantle()) {
            return MANTLE_GAS_FEE;
        }

        if (isOPStack()) {
            return OP_GAS_FEE;
        }

        if (isCDK()) {
            return CDK_GAS_FEE;
        }

        revert("Chain not supported");
    }

    /// @notice The relayer withdraws all fees accumulated
    function _withdrawFees() internal returns (bool) {
        (bool sent,) = msg.sender.call{value: address(this).balance}("");
        return sent;
    }
}

File 6 of 14 : Ownable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
///
/// @dev Note:
/// This implementation does NOT auto-initialize the owner to `msg.sender`.
/// You MUST call the `_initializeOwner` in the constructor / initializer.
///
/// While the ownable portion follows
/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
/// the nomenclature for the 2-step ownership handover may be unique to this codebase.
abstract contract Ownable {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The caller is not authorized to call the function.
    error Unauthorized();

    /// @dev The `newOwner` cannot be the zero address.
    error NewOwnerIsZeroAddress();

    /// @dev The `pendingOwner` does not have a valid handover request.
    error NoHandoverRequest();

    /// @dev Cannot double-initialize.
    error AlreadyInitialized();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ownership is transferred from `oldOwner` to `newOwner`.
    /// This event is intentionally kept the same as OpenZeppelin's Ownable to be
    /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
    /// despite it not being as lightweight as a single argument event.
    event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);

    /// @dev An ownership handover to `pendingOwner` has been requested.
    event OwnershipHandoverRequested(address indexed pendingOwner);

    /// @dev The ownership handover to `pendingOwner` has been canceled.
    event OwnershipHandoverCanceled(address indexed pendingOwner);

    /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
    uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
        0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;

    /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
        0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;

    /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
        0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STORAGE                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The owner slot is given by:
    /// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`.
    /// It is intentionally chosen to be a high value
    /// to avoid collision with lower slots.
    /// The choice of manual storage layout is to enable compatibility
    /// with both regular and upgradeable contracts.
    bytes32 internal constant _OWNER_SLOT =
        0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;

    /// The ownership handover slot of `newOwner` is given by:
    /// ```
    ///     mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
    ///     let handoverSlot := keccak256(0x00, 0x20)
    /// ```
    /// It stores the expiry timestamp of the two-step ownership handover.
    uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     INTERNAL FUNCTIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Override to return true to make `_initializeOwner` prevent double-initialization.
    function _guardInitializeOwner() internal pure virtual returns (bool guard) {}

    /// @dev Initializes the owner directly without authorization guard.
    /// This function must be called upon initialization,
    /// regardless of whether the contract is upgradeable or not.
    /// This is to enable generalization to both regular and upgradeable contracts,
    /// and to save gas in case the initial owner is not the caller.
    /// For performance reasons, this function will not check if there
    /// is an existing owner.
    function _initializeOwner(address newOwner) internal virtual {
        if (_guardInitializeOwner()) {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                if sload(ownerSlot) {
                    mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.
                    revert(0x1c, 0x04)
                }
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Store the new value.
                sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
            }
        } else {
            /// @solidity memory-safe-assembly
            assembly {
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Store the new value.
                sstore(_OWNER_SLOT, newOwner)
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
            }
        }
    }

    /// @dev Sets the owner directly without authorization guard.
    function _setOwner(address newOwner) internal virtual {
        if (_guardInitializeOwner()) {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                // Store the new value.
                sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
            }
        } else {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                // Store the new value.
                sstore(ownerSlot, newOwner)
            }
        }
    }

    /// @dev Throws if the sender is not the owner.
    function _checkOwner() internal view virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // If the caller is not the stored owner, revert.
            if iszero(eq(caller(), sload(_OWNER_SLOT))) {
                mstore(0x00, 0x82b42900) // `Unauthorized()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Returns how long a two-step ownership handover is valid for in seconds.
    /// Override to return a different value if needed.
    /// Made internal to conserve bytecode. Wrap it in a public function if needed.
    function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
        return 48 * 3600;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  PUBLIC UPDATE FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Allows the owner to transfer the ownership to `newOwner`.
    function transferOwnership(address newOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(shl(96, newOwner)) {
                mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
                revert(0x1c, 0x04)
            }
        }
        _setOwner(newOwner);
    }

    /// @dev Allows the owner to renounce their ownership.
    function renounceOwnership() public payable virtual onlyOwner {
        _setOwner(address(0));
    }

    /// @dev Request a two-step ownership handover to the caller.
    /// The request will automatically expire in 48 hours (172800 seconds) by default.
    function requestOwnershipHandover() public payable virtual {
        unchecked {
            uint256 expires = block.timestamp + _ownershipHandoverValidFor();
            /// @solidity memory-safe-assembly
            assembly {
                // Compute and set the handover slot to `expires`.
                mstore(0x0c, _HANDOVER_SLOT_SEED)
                mstore(0x00, caller())
                sstore(keccak256(0x0c, 0x20), expires)
                // Emit the {OwnershipHandoverRequested} event.
                log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
            }
        }
    }

    /// @dev Cancels the two-step ownership handover to the caller, if any.
    function cancelOwnershipHandover() public payable virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, caller())
            sstore(keccak256(0x0c, 0x20), 0)
            // Emit the {OwnershipHandoverCanceled} event.
            log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
        }
    }

    /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
    /// Reverts if there is no existing ownership handover requested by `pendingOwner`.
    function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            let handoverSlot := keccak256(0x0c, 0x20)
            // If the handover does not exist, or has expired.
            if gt(timestamp(), sload(handoverSlot)) {
                mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
                revert(0x1c, 0x04)
            }
            // Set the handover slot to 0.
            sstore(handoverSlot, 0)
        }
        _setOwner(pendingOwner);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   PUBLIC READ FUNCTIONS                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the owner of the contract.
    function owner() public view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := sload(_OWNER_SLOT)
        }
    }

    /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
    function ownershipHandoverExpiresAt(address pendingOwner)
        public
        view
        virtual
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the handover slot.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            // Load the handover slot.
            result := sload(keccak256(0x0c, 0x20))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         MODIFIERS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Marks a function as only callable by the owner.
    modifier onlyOwner() virtual {
        _checkOwner();
        _;
    }
}

File 7 of 14 : Constants.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import {IOptimismL1Block} from "../interfaces/IOptimismL1Block.sol";

uint256 constant LOCAL = 1337;
uint256 constant ANVIL = 31337;

uint256 constant ETH_MAINNET = 1;
uint256 constant ETH_SEPOLIA = 11155111;
uint256 constant ETH_HOLESKY = 17000;

uint256 constant BASE_MAINNET = 8453;
uint256 constant BASE_SEPOLIA = 84532;

uint256 constant FRAXTAL_MAINNET = 252;
uint256 constant FRAXTAL_HOLESKY = 2522;

uint256 constant MANTLE_SEPOLIA = 5003;
uint256 constant MANTLE_MAINNET = 5000;
uint256 constant POLYGON_ZKEVM_MAINNET = 1101;

address constant OP_STACK_L1_BLOCK_PREDEPLOY_ADDR =
    0x4200000000000000000000000000000000000015;

address constant L1_BASE_BRIDGE = 0x3154Cf16ccdb4C6d922629664174b904d80F2C35;

address constant L1_FRAXTAL_BRIDGE = 0x34C0bD5877A5Ee7099D0f5688D65F4bB9158BDE2;
address constant L1_FRAXTAL_HOLESKY_BRIDGE =
    0x0BaafC217162f64930909aD9f2B27125121d6332;

address constant L1_MANTLE_BRIDGE = 0xb4133552BA49dFb60DA6eb5cA0102d0f94ce071f;
address constant L1_MANTLE_SEPOLIA_BRIDGE =
    0xf26e9932106E6477a4Ae15dA0eDDCdB985065a1a;

address constant PUDGEY_PENGUINS = 0xBd3531dA5CF5857e7CfAA92426877b022e612cf8;

uint256 constant PUDGEY_PENGUINS_MAPPING_SLOT = 2;
uint256 constant PUDGEY_PENGUINS_LENGTH_SLOT = 8;

uint256 constant LAGRANGE_LOONS_MAPPING_SLOT = 2;
uint256 constant LAGRANGE_LOONS_LENGTH_SLOT = 8;

uint256 constant TEST_ERC20_MAPPING_SLOT = 4;

function isEthereum() view returns (bool) {
    return block.chainid == ETH_MAINNET || block.chainid == ETH_SEPOLIA
        || block.chainid == ETH_HOLESKY;
}

function isOPStack() view returns (bool) {
    uint32 size;
    assembly {
        size := extcodesize(OP_STACK_L1_BLOCK_PREDEPLOY_ADDR)
    }
    return (size > 0);
}

function isCDK() view returns (bool) {
    return block.chainid == POLYGON_ZKEVM_MAINNET;
}

function isMantle() view returns (bool) {
    return block.chainid == MANTLE_MAINNET || block.chainid == MANTLE_MAINNET;
}

function isLocal() view returns (bool) {
    return block.chainid == LOCAL || block.chainid == ANVIL;
}

function isTestnet() view returns (bool) {
    uint256[5] memory testnets = [
        ETH_SEPOLIA,
        ETH_HOLESKY,
        BASE_SEPOLIA,
        FRAXTAL_HOLESKY,
        MANTLE_SEPOLIA
    ];
    return chainMatches(testnets);
}

function isMainnet() view returns (bool) {
    uint256[5] memory mainnets = [
        ETH_MAINNET,
        BASE_MAINNET,
        FRAXTAL_MAINNET,
        MANTLE_MAINNET,
        POLYGON_ZKEVM_MAINNET
    ];
    return chainMatches(mainnets);
}

function chainMatches(uint256[4] memory chains) view returns (bool) {
    for (uint256 i = 0; i < chains.length; i++) {
        if (chains[i] == block.chainid) return true;
    }
    return false;
}

function chainMatches(uint256[5] memory chains) view returns (bool) {
    for (uint256 i = 0; i < chains.length; i++) {
        if (chains[i] == block.chainid) return true;
    }
    return false;
}

File 8 of 14 : IRegistrationManager.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

/// @title IRegistrationManager
/// @notice
interface IRegistrationManager {
    event NewTableRegistration(
        bytes32 indexed hash,
        address indexed contractAddr,
        uint256 chainId,
        uint256 genesisBlock,
        string name,
        string schema
    );

    event NewQueryRegistration(
        bytes32 indexed hash, bytes32 indexed tableHash, string sql
    );

    function registerQuery(bytes32 hash, bytes32 tableHash, string calldata sql)
        external;
}

File 9 of 14 : Groth16VerifierExtensions.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./Groth16Verifier.sol";
import {isCDK} from "../utils/Constants.sol";

// The query input struct passed into the processQuery function
struct QueryInput {
    // Query limit parameter
    uint32 limit;
    // Query offset parameter
    uint32 offset;
    // Minimum block number
    uint64 minBlockNumber;
    // Maximum block number
    uint64 maxBlockNumber;
    // Block hash
    bytes32 blockHash;
    // Computational hash
    bytes32 computationalHash;
    // User placeholder values
    bytes32[] userPlaceholders;
}

// The query output struct returned from the processQuery function
struct QueryOutput {
    // Total number of the all matching rows
    uint256 totalMatchedRows;
    // Returned rows of the current cursor
    bytes[] rows;
}

library Groth16VerifierExtensions {
    // Top 3 bits mask.
    uint256 constant TOP_THREE_BIT_MASK = ~(uint256(7) << 253);

    // Generic constants for the supported queries
    // TODO: These constants are possible to be changed depending on user queries exploration.
    // Once we know which queries users are mostly doing, we'll be able to modify these constants.
    // Maximum number of the results
    uint32 constant MAX_NUM_OUTPUTS = 3;
    // Maximum number of the items per result
    uint32 constant MAX_NUM_ITEMS_PER_OUTPUT = 5;
    // Maximum number of the placeholders
    uint32 constant MAX_NUM_PLACEHOLDERS = 14;

    // The start uint256 offset of the public inputs in calldata.
    // groth16_proof_number (8) + groth16_input_number (3)
    uint32 constant PI_OFFSET = 11;

    // These values are aligned and each is an uint256.
    // Block hash uint256 position in the public inputs
    uint32 constant BLOCK_HASH_POS = 0;
    // Flattened computational hash uint256 position
    uint32 constant COMPUTATIONAL_HASH_POS = BLOCK_HASH_POS + 1;
    // Placeholder values uint256 position
    uint32 constant PLACEHOLDER_VALUES_POS = COMPUTATIONAL_HASH_POS + 1;
    // Result values uint256 position
    uint32 constant RESULT_VALUES_POS =
        PLACEHOLDER_VALUES_POS + MAX_NUM_PLACEHOLDERS;

    // The remaining items of public inputs are saved in one uint256.
    // The uint256 offset of the last uint256 of public inputs in calldata.
    uint32 constant PI_REM_OFFSET = PI_OFFSET + RESULT_VALUES_POS
        + MAX_NUM_OUTPUTS * MAX_NUM_ITEMS_PER_OUTPUT;
    // Placeholder number uint32 position in the last uint256
    uint32 constant REM_NUM_PLACEHOLDERS_POS = 0;
    // Result number uint32 position
    uint32 constant REM_NUM_RESULTS_POS = 1;
    // Entry count (current result number) uint32 position
    uint32 constant REM_ENTRY_COUNT_POS = 2;
    // Overflow flag uint32 position
    uint32 constant REM_OVERFLOW_POS = 3;
    // Query limit uint32 position
    uint32 constant REM_QUERY_LIMIT_POS = 4;
    // Query offset uint32 position
    uint32 constant REM_QUERY_OFFSET_POS = 5;

    // The total byte length of public inputs
    uint32 constant PI_LEN =
        32 * (PI_REM_OFFSET - PI_OFFSET) + (REM_QUERY_OFFSET_POS + 1) * 4;

    // A computation overflow error during the query process
    error QueryComputationOverflow();

    // The processQuery function does the followings:
    // 1. Parse the Groth16 proofs (8 uint256) and inputs (3 uint256) from the `data`
    //    argument, and call `verifyProof` function for Groth16 verification.
    // 2. Calculate sha256 on the public inputs, and set the top 3 bits of this hash to 0.
    //    Then ensure this hash value equals to the last Groth16 input (groth16_inputs[2]).
    // 3. Parse the items from public inputs, and check as expected for query.
    // 4. Parse and return the query output from public inputs.
    function processQuery(bytes32[] calldata data, QueryInput memory query)
        internal
        view
        returns (QueryOutput memory)
    {
        // 1. Groth16 verification
        uint256[3] memory groth16Inputs = verifyGroth16Proof(data);

        // 2. Ensure the sha256 of public inputs equals to the last Groth16 input.
        verifyPublicInputs(data, groth16Inputs);

        // 3. Ensure the items of public inputs equal as expected for query.
        verifyQuery(data, query);

        // 4. Parse and return the query output.
        return parseOutput(data);
    }

    // Parse the Groth16 proofs and inputs, do verification, and returns the Groth16 inputs.
    function verifyGroth16Proof(bytes32[] calldata data)
        internal
        view
        returns (uint256[3] memory)
    {
        uint256[8] memory proofs;
        uint256[3] memory inputs;

        for (uint32 i = 0; i < 8; ++i) {
            proofs[i] = uint256(data[i]);
        }
        for (uint32 i = 0; i < 3; ++i) {
            inputs[i] = uint256(data[i + 8]);
        }

        // Ensure the sha256 hash equals to the last Groth16 input.
        require(
            inputs[0] == uint256(Groth16Verifier.CIRCUIT_DIGEST),
            "The first Groth16 input must be equal to the circuit digest"
        );

        // Verify the Groth16 proof.
        Groth16Verifier.verifyProof(proofs, inputs);

        return inputs;
    }

    // Compute sha256 on the public inputs, and ensure it equals to the last Groth16 input.
    function verifyPublicInputs(
        bytes32[] calldata data,
        uint256[3] memory groth16Inputs
    ) internal pure {
        // Parse the public inputs from calldata.
        bytes memory pi = parsePublicInputs(data);

        // Calculate sha256.
        uint256 hash = uint256(sha256(pi));
        // Set the top 3 bits of the hash value to 0.
        hash = hash & TOP_THREE_BIT_MASK;

        // Require the sha256 equals to the last Groth16 input.
        require(
            hash == groth16Inputs[2],
            "The sha256 hash of public inputs must be equal to the last of the Groth16 inputs"
        );
    }

    // Parse the public inputs from calldata.
    function parsePublicInputs(bytes32[] calldata data)
        internal
        pure
        returns (bytes memory)
    {
        bytes memory pi = new bytes(PI_LEN);

        // The calldata is encoded as Bytes32.
        uint256 b32Len = PI_LEN / 32;
        for (uint256 i = 0; i < b32Len; ++i) {
            bytes32 b = data[PI_OFFSET + i];
            for (uint32 j = 0; j < 32; ++j) {
                pi[i * 32 + j] = bytes1(b[j]);
            }
        }
        bytes32 rem = data[PI_OFFSET + b32Len];
        for (uint32 i = 0; i < PI_LEN % 32; ++i) {
            pi[b32Len * 32 + i] = rem[i];
        }

        return pi;
    }

    // Verify the public inputs with the expected query.
    function verifyQuery(bytes32[] calldata data, QueryInput memory query)
        internal
        view
    {
        // Retrieve the last Uint256 of public inputs.
        bytes32 rem = data[PI_REM_OFFSET];

        // Check the block hash and computational hash.
        bytes32 blockHash = data[PI_OFFSET + BLOCK_HASH_POS];
        require(
            isCDK() || blockHash == query.blockHash,
            "Block hash must equal as expected."
        );
        bytes32 computationalHash = data[PI_OFFSET + COMPUTATIONAL_HASH_POS];
        require(
            computationalHash == query.computationalHash,
            "Computational hash must equal as expected."
        );

        uint32 numPlaceholders =
            uint32(bytes4(rem << (REM_NUM_PLACEHOLDERS_POS * 32)));
        require(
            numPlaceholders <= MAX_NUM_PLACEHOLDERS,
            "Placeholder number cannot overflow."
        );
        require(
            // The first two placeholders are minimum and maximum block numbers.
            numPlaceholders == query.userPlaceholders.length + 2,
            "Placeholder number cannot overflow and must equal as expected."
        );
        // Check the minimum and maximum block numbers.
        require(
            uint256(data[PI_OFFSET + PLACEHOLDER_VALUES_POS])
                == query.minBlockNumber,
            "The first placeholder must be the expected minimum block number."
        );
        require(
            uint256(data[PI_OFFSET + PLACEHOLDER_VALUES_POS + 1])
                == query.maxBlockNumber,
            "The second placeholder must be the expected maximum block number."
        );
        // Check the user placeholders.
        for (uint256 i = 0; i < numPlaceholders - 2; ++i) {
            require(
                data[PI_OFFSET + PLACEHOLDER_VALUES_POS + 2 + i]
                    == query.userPlaceholders[i],
                "The user placeholder must equal as expected."
            );
        }

        // Check the query limit and offset.
        // uint32 limit = uint32(bytes4(rem << (REM_QUERY_LIMIT_POS * 32)));
        // require(limit == query.limit, "Query limit must equal as expected.");
        // uint32 offset = uint32(bytes4(rem << (REM_QUERY_OFFSET_POS * 32)));
        // require(offset == query.offset, "Query offset must equal as expected.");

        // Throw an error if overflow.
        uint32 overflow = uint32(bytes4(rem << (REM_OVERFLOW_POS * 32)));
        if (overflow != 0) {
            revert QueryComputationOverflow();
        }
    }

    // Parse the query output from the public inputs.
    function parseOutput(bytes32[] calldata data)
        internal
        pure
        returns (QueryOutput memory)
    {
        bytes32 rem = data[PI_REM_OFFSET];

        // Retrieve total number of the matched rows.
        uint32 totalMatchedRows =
            uint32(bytes4(rem << (REM_ENTRY_COUNT_POS * 32)));

        // Retrieve the current result number.
        uint32 numResults = uint32(bytes4(rem << (REM_NUM_RESULTS_POS * 32)));
        require(numResults <= MAX_NUM_OUTPUTS, "Result number cannot overflow.");

        // TODO: Each result value is an Uint256 and need to confirm this encoding code.
        // And it encodes the whole row with dummy values, since we don't know the real
        // number of items per result here.
        uint32 offset = PI_OFFSET + RESULT_VALUES_POS;
        bytes[] memory rows = new bytes[](numResults);
        for (uint256 i = 0; i < numResults; ++i) {
            rows[i] = abi.encode(
                data[
                    offset + i * MAX_NUM_ITEMS_PER_OUTPUT:
                        offset + (i + 1) * MAX_NUM_ITEMS_PER_OUTPUT
                ]
            );
        }

        QueryOutput memory output =
            QueryOutput({totalMatchedRows: totalMatchedRows, rows: rows});

        return output;
    }
}

File 10 of 14 : ILPNClientV1.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {QueryOutput} from "../Groth16VerifierExtensions.sol";

/**
 * @title ILPNClientV1
 * @notice Interface for the LPNClientV1 contract.
 */
interface ILPNClientV1 {
    /// @notice Callback function called by the LPNRegistry contract.
    /// @param requestId The ID of the request.
    /// @param result The result of the request.
    function lpnCallback(uint256 requestId, QueryOutput memory result)
        external;
}

File 11 of 14 : L1Block.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import {IOptimismL1Block} from "../interfaces/IOptimismL1Block.sol";
import {
    OP_STACK_L1_BLOCK_PREDEPLOY_ADDR,
    isEthereum,
    isOPStack
} from "./Constants.sol";

/// @notice The latest L1 blockhash.
function L1BlockHash() view returns (bytes32) {
    if (isEthereum()) {
        return blockhash(block.number);
    }

    if (isOPStack()) {
        return IOptimismL1Block(OP_STACK_L1_BLOCK_PREDEPLOY_ADDR).hash();
    }

    return bytes32(0);
}

/// @notice The latest L1 block number.
function L1BlockNumber() view returns (uint256) {
    if (isEthereum()) {
        return block.number;
    }

    if (isOPStack()) {
        return
            uint256(IOptimismL1Block(OP_STACK_L1_BLOCK_PREDEPLOY_ADDR).number());
    }

    return 0;
}

File 12 of 14 : IQueryManager.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {QueryOutput} from "../Groth16VerifierExtensions.sol";

/// @title IQueryManager
/// @notice
interface IQueryManager {
    /// @notice Event emitted when a new request is made.
    /// @param requestId The ID of the request.
    /// @param queryHash The identifier of the SQL query associated with the request.
    /// @param client The address of the client who made this request.
    /// @param placeholders Values for the numbered placeholders in the query.
    /// @param startBlock The starting block for the computation.
    /// @param endBlock The ending block for the computation.
    /// @param proofBlock The requested block for the proof to be computed against.
    ///                   Currently required for OP Stack chains
    event NewRequest(
        uint256 indexed requestId,
        bytes32 indexed queryHash,
        address indexed client,
        bytes32[] placeholders,
        uint256 startBlock,
        uint256 endBlock,
        uint256 gasFee,
        uint256 proofBlock
    );

    /// @notice Event emitted when a response is received.
    /// @param requestId The ID of the request.
    /// @param client The address of the client who made the matching request.
    /// @param result The computed results for the request.
    event NewResponse(
        uint256 indexed requestId, address indexed client, QueryOutput result
    );

    /// @notice Submits a new request to the registry.
    /// @param queryHash The identifier of the SQL query associated with the request.
    /// @param placeholders Values for the numbered placeholders in the query.
    /// @param startBlock The starting block for the computation.
    /// @param endBlock The ending block for the computation.
    /// @return The ID of the newly created request.
    function request(
        bytes32 queryHash,
        bytes32[] calldata placeholders,
        uint256 startBlock,
        uint256 endBlock
    ) external payable returns (uint256);

    /// @notice Submits a response to a specific request.
    /// @param requestId_ The ID of the request to respond to.
    /// @param data The proof, inputs, and public inputs to verify.
    /// - groth16_proof.proofs: 8 * U256 = 256 bytes
    /// - groth16_proof.inputs: 3 * U256 = 96 bytes
    /// - plonky2_proof.public_inputs: the little-endian bytes of public inputs exported by user
    /// @param blockNumber The block number of the block hash corresponding to the proof.
    function respond(
        uint256 requestId_,
        bytes32[] calldata data,
        uint256 blockNumber
    ) external;
}

File 13 of 14 : IOptimismL1Block.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

/// @custom:proxied
/// @custom:predeploy 0x4200000000000000000000000000000000000015
/// @title IOptimismL1Block
/// @notice from https://github.com/ethereum-optimism/optimism/blob/0d221da603418fe99e96bba944d2af64feee94eb/packages/contracts-bedrock/src/L2/L1Block.sol
///         The L1Block predeploy gives users access to information about the last known L1 block.
interface IOptimismL1Block {
    /// @notice The latest L1 blockhash.
    function hash() external view returns (bytes32);

    /// @notice The latest L1 block number known by the L2 system.
    function number() external view returns (uint64);
}

File 14 of 14 : Groth16Verifier.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @title Groth16 verifier template.
/// @author Remco Bloemen
/// @notice Supports verifying Groth16 proofs. Proofs can be in uncompressed
/// (256 bytes) and compressed (128 bytes) format. A view function is provided
/// to compress proofs.
/// @notice See <https://2π.com/23/bn254-compression> for further explanation.
library Groth16Verifier {
    /// Some of the provided public input values are larger than the field modulus.
    /// @dev Public input elements are not automatically reduced, as this is can be
    /// a dangerous source of bugs.
    error PublicInputNotInField();

    /// The proof is invalid.
    /// @dev This can mean that provided Groth16 proof points are not on their
    /// curves, that pairing equation fails, or that the proof is not for the
    /// provided public input.
    error ProofInvalid();

    // Addresses of precompiles
    uint256 constant PRECOMPILE_MODEXP = 0x05;
    uint256 constant PRECOMPILE_ADD = 0x06;
    uint256 constant PRECOMPILE_MUL = 0x07;
    uint256 constant PRECOMPILE_VERIFY = 0x08;

    // Base field Fp order P and scalar field Fr order R.
    // For BN254 these are computed as follows:
    //     t = 4965661367192848881
    //     P = 36⋅t⁴ + 36⋅t³ + 24⋅t² + 6⋅t + 1
    //     R = 36⋅t⁴ + 36⋅t³ + 18⋅t² + 6⋅t + 1
    uint256 constant P =
        0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47;
    uint256 constant R =
        0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001;

    // Extension field Fp2 = Fp[i] / (i² + 1)
    // Note: This is the complex extension field of Fp with i² = -1.
    //       Values in Fp2 are represented as a pair of Fp elements (a₀, a₁) as a₀ + a₁⋅i.
    // Note: The order of Fp2 elements is *opposite* that of the pairing contract, which
    //       expects Fp2 elements in order (a₁, a₀). This is also the order in which
    //       Fp2 elements are encoded in the public interface as this became convention.

    // Constants in Fp
    uint256 constant FRACTION_1_2_FP =
        0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea4;
    uint256 constant FRACTION_27_82_FP =
        0x2b149d40ceb8aaae81be18991be06ac3b5b4c5e559dbefa33267e6dc24a138e5;
    uint256 constant FRACTION_3_82_FP =
        0x2fcd3ac2a640a154eb23960892a85a68f031ca0c8344b23a577dcf1052b9e775;

    // Exponents for inversions and square roots mod P
    uint256 constant EXP_INVERSE_FP =
        0x30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD45; // P - 2
    uint256 constant EXP_SQRT_FP =
        0xC19139CB84C680A6E14116DA060561765E05AA45A1C72A34F082305B61F3F52; // (P + 1) / 4;

    // Groth16 alpha point in G1
    uint256 constant ALPHA_X =
        17907337084192034765119714908911515289906764691979827894695344987889478541781;
    uint256 constant ALPHA_Y =
        12081345641630512954554054616291084687120235460903563256531721577550101103355;

    // Groth16 beta point in G2 in powers of i
    uint256 constant BETA_NEG_X_0 =
        1321340443706860886436957793321584755563222541101768411890716174570741920888;
    uint256 constant BETA_NEG_X_1 =
        920363870669119483034097620638982273796249632396243853650793408827776494946;
    uint256 constant BETA_NEG_Y_0 =
        4195793325450645855813358536487033233301305964202899721144542528515217913046;
    uint256 constant BETA_NEG_Y_1 =
        4536471338710790329574260299407145985054983030628740264745065379798505540277;

    // Groth16 gamma point in G2 in powers of i
    uint256 constant GAMMA_NEG_X_0 =
        12571758756220634190514175557306433519556272056257098779840280778464430061066;
    uint256 constant GAMMA_NEG_X_1 =
        14273535764379946710540177133638120129655732233944995562618230898164782072255;
    uint256 constant GAMMA_NEG_Y_0 =
        11103029107274626994884738710525502032304625660495260793626815437650071921453;
    uint256 constant GAMMA_NEG_Y_1 =
        21429319373771741409083380502870292961538602348974432497032400475283747952572;

    // Groth16 delta point in G2 in powers of i
    uint256 constant DELTA_NEG_X_0 =
        12919809559089348596199526724652277781891068305322193275707327055616857379966;
    uint256 constant DELTA_NEG_X_1 =
        5511422485602468108861855528593943865334080850416272727097143838293216639215;
    uint256 constant DELTA_NEG_Y_0 =
        8970731587382751322010109173871096578249484627287472271602859842894560360684;
    uint256 constant DELTA_NEG_Y_1 =
        7509029918022231803529436917154276080065723840110322075236055027947365873148;

    // Constant and public input points
    uint256 constant CONSTANT_X =
        12050350580237785728456149127021902371527603008304628288955799130439009294661;
    uint256 constant CONSTANT_Y =
        8971262406129550343747641016671753435023288156830139050058550579971406166994;
    uint256 constant PUB_0_X =
        15432206935594661954711361759382054827491859145187546845262342689137822766451;
    uint256 constant PUB_0_Y =
        6225727369118324581048921232767732114392225356569519817159401999523544733879;
    uint256 constant PUB_1_X =
        11568293976853107246955142917121588330248018158931691036422993254958632295940;
    uint256 constant PUB_1_Y =
        9714678980441493265192687167866917501505220659712154610563614762542916480933;
    uint256 constant PUB_2_X =
        21033449728405906145571511337915392892876480995206328441395146396091883633727;
    uint256 constant PUB_2_Y =
        8630849778917286526883999490781889401640014431687430806620475130538470507687;

    /// Negation in Fp.
    /// @notice Returns a number x such that a + x = 0 in Fp.
    /// @notice The input does not need to be reduced.
    /// @param a the base
    /// @return x the result
    function negate(uint256 a) internal pure returns (uint256 x) {
        unchecked {
            x = (P - (a % P)) % P; // Modulo is cheaper than branching
        }
    }

    /// Exponentiation in Fp.
    /// @notice Returns a number x such that a ^ e = x in Fp.
    /// @notice The input does not need to be reduced.
    /// @param a the base
    /// @param e the exponent
    /// @return x the result
    function exp(uint256 a, uint256 e) internal view returns (uint256 x) {
        bool success;
        assembly ("memory-safe") {
            let f := mload(0x40)
            mstore(f, 0x20)
            mstore(add(f, 0x20), 0x20)
            mstore(add(f, 0x40), 0x20)
            mstore(add(f, 0x60), a)
            mstore(add(f, 0x80), e)
            mstore(add(f, 0xa0), P)
            success := staticcall(gas(), PRECOMPILE_MODEXP, f, 0xc0, f, 0x20)
            x := mload(f)
        }
        if (!success) {
            // Exponentiation failed.
            // Should not happen.
            revert ProofInvalid();
        }
    }

    /// Invertsion in Fp.
    /// @notice Returns a number x such that a * x = 1 in Fp.
    /// @notice The input does not need to be reduced.
    /// @notice Reverts with ProofInvalid() if the inverse does not exist
    /// @param a the input
    /// @return x the solution
    function invert_Fp(uint256 a) internal view returns (uint256 x) {
        x = exp(a, EXP_INVERSE_FP);
        if (mulmod(a, x, P) != 1) {
            // Inverse does not exist.
            // Can only happen during G2 point decompression.
            revert ProofInvalid();
        }
    }

    /// Square root in Fp.
    /// @notice Returns a number x such that x * x = a in Fp.
    /// @notice Will revert with InvalidProof() if the input is not a square
    /// or not reduced.
    /// @param a the square
    /// @return x the solution
    function sqrt_Fp(uint256 a) internal view returns (uint256 x) {
        x = exp(a, EXP_SQRT_FP);
        if (mulmod(x, x, P) != a) {
            // Square root does not exist or a is not reduced.
            // Happens when G1 point is not on curve.
            revert ProofInvalid();
        }
    }

    /// Square test in Fp.
    /// @notice Returns wheter a number x exists such that x * x = a in Fp.
    /// @notice Will revert with InvalidProof() if the input is not a square
    /// or not reduced.
    /// @param a the square
    /// @return x the solution
    function isSquare_Fp(uint256 a) internal view returns (bool) {
        uint256 x = exp(a, EXP_SQRT_FP);
        return mulmod(x, x, P) == a;
    }

    /// Square root in Fp2.
    /// @notice Fp2 is the complex extension Fp[i]/(i^2 + 1). The input is
    /// a0 + a1 ⋅ i and the result is x0 + x1 ⋅ i.
    /// @notice Will revert with InvalidProof() if
    ///   * the input is not a square,
    ///   * the hint is incorrect, or
    ///   * the input coefficents are not reduced.
    /// @param a0 The real part of the input.
    /// @param a1 The imaginary part of the input.
    /// @param hint A hint which of two possible signs to pick in the equation.
    /// @return x0 The real part of the square root.
    /// @return x1 The imaginary part of the square root.
    function sqrt_Fp2(uint256 a0, uint256 a1, bool hint)
        internal
        view
        returns (uint256 x0, uint256 x1)
    {
        // If this square root reverts there is no solution in Fp2.
        uint256 d = sqrt_Fp(addmod(mulmod(a0, a0, P), mulmod(a1, a1, P), P));
        if (hint) {
            d = negate(d);
        }
        // If this square root reverts there is no solution in Fp2.
        x0 = sqrt_Fp(mulmod(addmod(a0, d, P), FRACTION_1_2_FP, P));
        x1 = mulmod(a1, invert_Fp(mulmod(x0, 2, P)), P);

        // Check result to make sure we found a root.
        // Note: this also fails if a0 or a1 is not reduced.
        if (
            a0 != addmod(mulmod(x0, x0, P), negate(mulmod(x1, x1, P)), P)
                || a1 != mulmod(2, mulmod(x0, x1, P), P)
        ) {
            revert ProofInvalid();
        }
    }

    /// Compress a G1 point.
    /// @notice Reverts with InvalidProof if the coordinates are not reduced
    /// or if the point is not on the curve.
    /// @notice The point at infinity is encoded as (0,0) and compressed to 0.
    /// @param x The X coordinate in Fp.
    /// @param y The Y coordinate in Fp.
    /// @return c The compresed point (x with one signal bit).
    function compress_g1(uint256 x, uint256 y)
        internal
        view
        returns (uint256 c)
    {
        if (x >= P || y >= P) {
            // G1 point not in field.
            revert ProofInvalid();
        }
        if (x == 0 && y == 0) {
            // Point at infinity
            return 0;
        }

        // Note: sqrt_Fp reverts if there is no solution, i.e. the x coordinate is invalid.
        uint256 y_pos = sqrt_Fp(addmod(mulmod(mulmod(x, x, P), x, P), 3, P));
        if (y == y_pos) {
            return (x << 1) | 0;
        } else if (y == negate(y_pos)) {
            return (x << 1) | 1;
        } else {
            // G1 point not on curve.
            revert ProofInvalid();
        }
    }

    /// Decompress a G1 point.
    /// @notice Reverts with InvalidProof if the input does not represent a valid point.
    /// @notice The point at infinity is encoded as (0,0) and compressed to 0.
    /// @param c The compresed point (x with one signal bit).
    /// @return x The X coordinate in Fp.
    /// @return y The Y coordinate in Fp.
    function decompress_g1(uint256 c)
        internal
        view
        returns (uint256 x, uint256 y)
    {
        // Note that X = 0 is not on the curve since 0³ + 3 = 3 is not a square.
        // so we can use it to represent the point at infinity.
        if (c == 0) {
            // Point at infinity as encoded in EIP196 and EIP197.
            return (0, 0);
        }
        bool negate_point = c & 1 == 1;
        x = c >> 1;
        if (x >= P) {
            // G1 x coordinate not in field.
            revert ProofInvalid();
        }

        // Note: (x³ + 3) is irreducible in Fp, so it can not be zero and therefore
        //       y can not be zero.
        // Note: sqrt_Fp reverts if there is no solution, i.e. the point is not on the curve.
        y = sqrt_Fp(addmod(mulmod(mulmod(x, x, P), x, P), 3, P));
        if (negate_point) {
            y = negate(y);
        }
    }

    /// Compress a G2 point.
    /// @notice Reverts with InvalidProof if the coefficients are not reduced
    /// or if the point is not on the curve.
    /// @notice The G2 curve is defined over the complex extension Fp[i]/(i^2 + 1)
    /// with coordinates (x0 + x1 ⋅ i, y0 + y1 ⋅ i).
    /// @notice The point at infinity is encoded as (0,0,0,0) and compressed to (0,0).
    /// @param x0 The real part of the X coordinate.
    /// @param x1 The imaginary poart of the X coordinate.
    /// @param y0 The real part of the Y coordinate.
    /// @param y1 The imaginary part of the Y coordinate.
    /// @return c0 The first half of the compresed point (x0 with two signal bits).
    /// @return c1 The second half of the compressed point (x1 unmodified).
    function compress_g2(uint256 x0, uint256 x1, uint256 y0, uint256 y1)
        internal
        view
        returns (uint256 c0, uint256 c1)
    {
        if (x0 >= P || x1 >= P || y0 >= P || y1 >= P) {
            // G2 point not in field.
            revert ProofInvalid();
        }
        if ((x0 | x1 | y0 | y1) == 0) {
            // Point at infinity
            return (0, 0);
        }

        // Compute y^2
        // Note: shadowing variables and scoping to avoid stack-to-deep.
        uint256 y0_pos;
        uint256 y1_pos;
        {
            uint256 n3ab = mulmod(mulmod(x0, x1, P), P - 3, P);
            uint256 a_3 = mulmod(mulmod(x0, x0, P), x0, P);
            uint256 b_3 = mulmod(mulmod(x1, x1, P), x1, P);
            y0_pos = addmod(
                FRACTION_27_82_FP, addmod(a_3, mulmod(n3ab, x1, P), P), P
            );
            y1_pos = negate(
                addmod(FRACTION_3_82_FP, addmod(b_3, mulmod(n3ab, x0, P), P), P)
            );
        }

        // Determine hint bit
        // If this sqrt fails the x coordinate is not on the curve.
        bool hint;
        {
            uint256 d = sqrt_Fp(
                addmod(mulmod(y0_pos, y0_pos, P), mulmod(y1_pos, y1_pos, P), P)
            );
            hint =
                !isSquare_Fp(mulmod(addmod(y0_pos, d, P), FRACTION_1_2_FP, P));
        }

        // Recover y
        (y0_pos, y1_pos) = sqrt_Fp2(y0_pos, y1_pos, hint);
        if (y0 == y0_pos && y1 == y1_pos) {
            c0 = (x0 << 2) | (hint ? 2 : 0) | 0;
            c1 = x1;
        } else if (y0 == negate(y0_pos) && y1 == negate(y1_pos)) {
            c0 = (x0 << 2) | (hint ? 2 : 0) | 1;
            c1 = x1;
        } else {
            // G1 point not on curve.
            revert ProofInvalid();
        }
    }

    /// Decompress a G2 point.
    /// @notice Reverts with InvalidProof if the input does not represent a valid point.
    /// @notice The G2 curve is defined over the complex extension Fp[i]/(i^2 + 1)
    /// with coordinates (x0 + x1 ⋅ i, y0 + y1 ⋅ i).
    /// @notice The point at infinity is encoded as (0,0,0,0) and compressed to (0,0).
    /// @param c0 The first half of the compresed point (x0 with two signal bits).
    /// @param c1 The second half of the compressed point (x1 unmodified).
    /// @return x0 The real part of the X coordinate.
    /// @return x1 The imaginary poart of the X coordinate.
    /// @return y0 The real part of the Y coordinate.
    /// @return y1 The imaginary part of the Y coordinate.
    function decompress_g2(uint256 c0, uint256 c1)
        internal
        view
        returns (uint256 x0, uint256 x1, uint256 y0, uint256 y1)
    {
        // Note that X = (0, 0) is not on the curve since 0³ + 3/(9 + i) is not a square.
        // so we can use it to represent the point at infinity.
        if (c0 == 0 && c1 == 0) {
            // Point at infinity as encoded in EIP197.
            return (0, 0, 0, 0);
        }
        bool negate_point = c0 & 1 == 1;
        bool hint = c0 & 2 == 2;
        x0 = c0 >> 2;
        x1 = c1;
        if (x0 >= P || x1 >= P) {
            // G2 x0 or x1 coefficient not in field.
            revert ProofInvalid();
        }

        uint256 n3ab = mulmod(mulmod(x0, x1, P), P - 3, P);
        uint256 a_3 = mulmod(mulmod(x0, x0, P), x0, P);
        uint256 b_3 = mulmod(mulmod(x1, x1, P), x1, P);

        y0 = addmod(FRACTION_27_82_FP, addmod(a_3, mulmod(n3ab, x1, P), P), P);
        y1 = negate(
            addmod(FRACTION_3_82_FP, addmod(b_3, mulmod(n3ab, x0, P), P), P)
        );

        // Note: sqrt_Fp2 reverts if there is no solution, i.e. the point is not on the curve.
        // Note: (X³ + 3/(9 + i)) is irreducible in Fp2, so y can not be zero.
        //       But y0 or y1 may still independently be zero.
        (y0, y1) = sqrt_Fp2(y0, y1, hint);
        if (negate_point) {
            y0 = negate(y0);
            y1 = negate(y1);
        }
    }

    /// Compute the public input linear combination.
    /// @notice Reverts with PublicInputNotInField if the input is not in the field.
    /// @notice Computes the multi-scalar-multiplication of the public input
    /// elements and the verification key including the constant term.
    /// @param input The public inputs. These are elements of the scalar field Fr.
    /// @return x The X coordinate of the resulting G1 point.
    /// @return y The Y coordinate of the resulting G1 point.
    function publicInputMSM(uint256[3] memory input)
        internal
        view
        returns (uint256 x, uint256 y)
    {
        // Note: The ECMUL precompile does not reject unreduced values, so we check this.
        // Note: Unrolling this loop does not cost much extra in code-size, the bulk of the
        //       code-size is in the PUB_ constants.
        // ECMUL has input (x, y, scalar) and output (x', y').
        // ECADD has input (x1, y1, x2, y2) and output (x', y').
        // We call them such that ecmul output is already in the second point
        // argument to ECADD so we can have a tight loop.
        bool success = true;
        assembly ("memory-safe") {
            let f := mload(0x40)
            let g := add(f, 0x40)
            let s
            mstore(f, CONSTANT_X)
            mstore(add(f, 0x20), CONSTANT_Y)
            mstore(g, PUB_0_X)
            mstore(add(g, 0x20), PUB_0_Y)
            s := mload(input)
            mstore(add(g, 0x40), s)
            success := and(success, lt(s, R))
            success :=
                and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
            success :=
                and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
            mstore(g, PUB_1_X)
            mstore(add(g, 0x20), PUB_1_Y)
            s := mload(add(input, 32))
            mstore(add(g, 0x40), s)
            success := and(success, lt(s, R))
            success :=
                and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
            success :=
                and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
            mstore(g, PUB_2_X)
            mstore(add(g, 0x20), PUB_2_Y)
            s := mload(add(input, 64))
            mstore(add(g, 0x40), s)
            success := and(success, lt(s, R))
            success :=
                and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40))
            success :=
                and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40))
            x := mload(f)
            y := mload(add(f, 0x20))
        }
        if (!success) {
            // Either Public input not in field, or verification key invalid.
            // We assume the contract is correctly generated, so the verification key is valid.
            revert PublicInputNotInField();
        }
    }

    /// Compress a proof.
    /// @notice Will revert with InvalidProof if the curve points are invalid,
    /// but does not verify the proof itself.
    /// @param proof The uncompressed Groth16 proof. Elements are in the same order as for
    /// verifyProof. I.e. Groth16 points (A, B, C) encoded as in EIP-197.
    /// @return compressed The compressed proof. Elements are in the same order as for
    /// verifyCompressedProof. I.e. points (A, B, C) in compressed format.
    function compressProof(uint256[8] memory proof)
        internal
        view
        returns (uint256[4] memory compressed)
    {
        compressed[0] = compress_g1(proof[0], proof[1]);
        (compressed[2], compressed[1]) =
            compress_g2(proof[3], proof[2], proof[5], proof[4]);
        compressed[3] = compress_g1(proof[6], proof[7]);
    }

    /// Verify a Groth16 proof with compressed points.
    /// @notice Reverts with InvalidProof if the proof is invalid or
    /// with PublicInputNotInField the public input is not reduced.
    /// @notice There is no return value. If the function does not revert, the
    /// proof was successfully verified.
    /// @param compressedProof the points (A, B, C) in compressed format
    /// matching the output of compressProof.
    /// @param input the public input field elements in the scalar field Fr.
    /// Elements must be reduced.
    function verifyCompressedProof(
        uint256[4] calldata compressedProof,
        uint256[3] memory input
    ) internal view {
        (uint256 Ax, uint256 Ay) = decompress_g1(compressedProof[0]);
        (uint256 Bx0, uint256 Bx1, uint256 By0, uint256 By1) =
            decompress_g2(compressedProof[2], compressedProof[1]);
        (uint256 Cx, uint256 Cy) = decompress_g1(compressedProof[3]);
        (uint256 Lx, uint256 Ly) = publicInputMSM(input);

        // Verify the pairing
        // Note: The precompile expects the F2 coefficients in big-endian order.
        // Note: The pairing precompile rejects unreduced values, so we won't check that here.
        uint256[24] memory pairings;
        // e(A, B)
        pairings[0] = Ax;
        pairings[1] = Ay;
        pairings[2] = Bx1;
        pairings[3] = Bx0;
        pairings[4] = By1;
        pairings[5] = By0;
        // e(C, -δ)
        pairings[6] = Cx;
        pairings[7] = Cy;
        pairings[8] = DELTA_NEG_X_1;
        pairings[9] = DELTA_NEG_X_0;
        pairings[10] = DELTA_NEG_Y_1;
        pairings[11] = DELTA_NEG_Y_0;
        // e(α, -β)
        pairings[12] = ALPHA_X;
        pairings[13] = ALPHA_Y;
        pairings[14] = BETA_NEG_X_1;
        pairings[15] = BETA_NEG_X_0;
        pairings[16] = BETA_NEG_Y_1;
        pairings[17] = BETA_NEG_Y_0;
        // e(L_pub, -γ)
        pairings[18] = Lx;
        pairings[19] = Ly;
        pairings[20] = GAMMA_NEG_X_1;
        pairings[21] = GAMMA_NEG_X_0;
        pairings[22] = GAMMA_NEG_Y_1;
        pairings[23] = GAMMA_NEG_Y_0;

        // Check pairing equation.
        bool success;
        uint256[1] memory output;
        assembly ("memory-safe") {
            success :=
                staticcall(gas(), PRECOMPILE_VERIFY, pairings, 0x300, output, 0x20)
        }
        if (!success || output[0] != 1) {
            // Either proof or verification key invalid.
            // We assume the contract is correctly generated, so the verification key is valid.
            revert ProofInvalid();
        }
    }

    /// Verify an uncompressed Groth16 proof.
    /// @notice Reverts with InvalidProof if the proof is invalid or
    /// with PublicInputNotInField the public input is not reduced.
    /// @notice There is no return value. If the function does not revert, the
    /// proof was successfully verified.
    /// @param proof the points (A, B, C) in EIP-197 format matching the output
    /// of compressProof.
    /// @param input the public input field elements in the scalar field Fr.
    /// Elements must be reduced.
    function verifyProof(uint256[8] memory proof, uint256[3] memory input)
        internal
        view
    {
        (uint256 x, uint256 y) = publicInputMSM(input);

        // Note: The precompile expects the F2 coefficients in big-endian order.
        // Note: The pairing precompile rejects unreduced values, so we won't check that here.

        bool success;
        assembly ("memory-safe") {
            let f := mload(0x40) // Free memory pointer.

            // Copy points (A, B, C) to memory. They are already in correct encoding.
            // This is pairing e(A, B) and G1 of e(C, -δ).
            mstore(f, mload(add(proof, 0x00)))
            mstore(add(f, 0x20), mload(add(proof, 0x20)))
            mstore(add(f, 0x40), mload(add(proof, 0x40)))
            mstore(add(f, 0x60), mload(add(proof, 0x60)))
            mstore(add(f, 0x80), mload(add(proof, 0x80)))
            mstore(add(f, 0xa0), mload(add(proof, 0xa0)))
            mstore(add(f, 0xc0), mload(add(proof, 0xc0)))
            mstore(add(f, 0xe0), mload(add(proof, 0xe0)))

            // Complete e(C, -δ) and write e(α, -β), e(L_pub, -γ) to memory.
            // OPT: This could be better done using a single codecopy, but
            //      Solidity (unlike standalone Yul) doesn't provide a way to
            //      to do this.
            mstore(add(f, 0x100), DELTA_NEG_X_1)
            mstore(add(f, 0x120), DELTA_NEG_X_0)
            mstore(add(f, 0x140), DELTA_NEG_Y_1)
            mstore(add(f, 0x160), DELTA_NEG_Y_0)
            mstore(add(f, 0x180), ALPHA_X)
            mstore(add(f, 0x1a0), ALPHA_Y)
            mstore(add(f, 0x1c0), BETA_NEG_X_1)
            mstore(add(f, 0x1e0), BETA_NEG_X_0)
            mstore(add(f, 0x200), BETA_NEG_Y_1)
            mstore(add(f, 0x220), BETA_NEG_Y_0)
            mstore(add(f, 0x240), x)
            mstore(add(f, 0x260), y)
            mstore(add(f, 0x280), GAMMA_NEG_X_1)
            mstore(add(f, 0x2a0), GAMMA_NEG_X_0)
            mstore(add(f, 0x2c0), GAMMA_NEG_Y_1)
            mstore(add(f, 0x2e0), GAMMA_NEG_Y_0)

            // Check pairing equation.
            success := staticcall(gas(), PRECOMPILE_VERIFY, f, 0x300, f, 0x20)
            // Also check returned value (both are either 1 or 0).
            success := and(success, mload(f))
        }
        if (!success) {
            // Either proof or verification key invalid.
            // We assume the contract is correctly generated, so the verification key is valid.
            revert ProofInvalid();
        }
    }

    bytes32 constant CIRCUIT_DIGEST =
        0x1ef3e0e4525d9634361ccfbbaab5825505dadaae75dfc5278ba0ab9824dabfc0;
}

Settings
{
  "remappings": [
    "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "ds-test/=lib/openzeppelin-contracts-upgradeable/lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "solady/=lib/solady/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"InsufficientGasFee","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"ProofInvalid","type":"error"},{"inputs":[],"name":"PublicInputNotInField","type":"error"},{"inputs":[],"name":"QueryAfterCurrentBlock","type":"error"},{"inputs":[],"name":"QueryAlreadyRegistered","type":"error"},{"inputs":[],"name":"QueryComputationOverflow","type":"error"},{"inputs":[],"name":"QueryGreaterThanMaxRange","type":"error"},{"inputs":[],"name":"QueryInvalidRange","type":"error"},{"inputs":[],"name":"TableAlreadyRegistered","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"addrs","type":"address[]"},{"indexed":false,"internalType":"bool","name":"value","type":"bool"}],"name":"BatchWhitelisted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"hash","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"tableHash","type":"bytes32"},{"indexed":false,"internalType":"string","name":"sql","type":"string"}],"name":"NewQueryRegistration","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":true,"internalType":"bytes32","name":"queryHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"client","type":"address"},{"indexed":false,"internalType":"bytes32[]","name":"placeholders","type":"bytes32[]"},{"indexed":false,"internalType":"uint256","name":"startBlock","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"endBlock","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"gasFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"proofBlock","type":"uint256"}],"name":"NewRequest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":true,"internalType":"address","name":"client","type":"address"},{"components":[{"internalType":"uint256","name":"totalMatchedRows","type":"uint256"},{"internalType":"bytes[]","name":"rows","type":"bytes[]"}],"indexed":false,"internalType":"struct QueryOutput","name":"result","type":"tuple"}],"name":"NewResponse","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"hash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"contractAddr","type":"address"},{"indexed":false,"internalType":"uint256","name":"chainId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"genesisBlock","type":"uint256"},{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":false,"internalType":"string","name":"schema","type":"string"}],"name":"NewTableRegistration","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"addr","type":"address"},{"indexed":false,"internalType":"bool","name":"value","type":"bool"}],"name":"Whitelisted","type":"event"},{"inputs":[],"name":"CDK_GAS_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ETH_GAS_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MANTLE_GAS_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_QUERY_RANGE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OP_GAS_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"addrs","type":"address[]"}],"name":"addToWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"gasFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"hash","type":"bytes32"}],"name":"queries","outputs":[{"internalType":"bool","name":"registered","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"bytes32","name":"tableHash","type":"bytes32"},{"internalType":"string","name":"sql","type":"string"}],"name":"registerQuery","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"address","name":"contractAddr","type":"address"},{"internalType":"uint96","name":"chainId","type":"uint96"},{"internalType":"uint256","name":"genesisBlock","type":"uint256"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"schema","type":"string"}],"name":"registerTable","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"addrs","type":"address[]"}],"name":"removeFromWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"queryHash","type":"bytes32"},{"internalType":"bytes32[]","name":"placeholders","type":"bytes32[]"},{"internalType":"uint256","name":"startBlock","type":"uint256"},{"internalType":"uint256","name":"endBlock","type":"uint256"}],"name":"request","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"requestId","type":"uint256"}],"name":"requests","outputs":[{"internalType":"address","name":"client","type":"address"},{"components":[{"internalType":"uint32","name":"limit","type":"uint32"},{"internalType":"uint32","name":"offset","type":"uint32"},{"internalType":"uint64","name":"minBlockNumber","type":"uint64"},{"internalType":"uint64","name":"maxBlockNumber","type":"uint64"},{"internalType":"bytes32","name":"blockHash","type":"bytes32"},{"internalType":"bytes32","name":"computationalHash","type":"bytes32"},{"internalType":"bytes32[]","name":"userPlaceholders","type":"bytes32[]"}],"internalType":"struct QueryInput","name":"input","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"requestId_","type":"uint256"},{"internalType":"bytes32[]","name":"data","type":"bytes32[]"},{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"respond","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"hash","type":"bytes32"}],"name":"tables","outputs":[{"internalType":"bool","name":"registered","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"client","type":"address"}],"name":"toggleWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"whitelist","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawFees","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]

6080604052348015600e575f80fd5b50612d5e8061001c5f395ff3fe608060405260043610610194575f3560e01c80637f649783116100e7578063c81de78c11610087578063d3098b9a11610062578063d3098b9a14610415578063f04e283e14610443578063f2fde38b14610456578063fee81cf414610469575f80fd5b8063c81de78c146103e0578063c9179506146103fb578063cf72dddd146103fb575f80fd5b80639468441c116100c25780639468441c146103665780639b19251a146103805780639bc24290146103ae578063c4d66de8146103c1575f80fd5b80637f649783146102ef57806381d12c581461030e5780638da5cb5b1461033b575f80fd5b8063476343ee1161015257806355c2c6711161012d57806355c2c67114610295578063658612e9146102b4578063715018a6146102c85780637b20c8dc146102d0575f80fd5b8063476343ee1461025a578063548db1741461026e57806354d1f13d1461028d575f80fd5b80626d6cae1461019857806308506853146101bf57806312def999146101d457806321dfb600146101f5578063256929621461023357806339393ac91461023b575b5f80fd5b3480156101a3575f80fd5b506101ac5f5481565b6040519081526020015b60405180910390f35b3480156101ca575f80fd5b506101ac61c35081565b3480156101df575f80fd5b506101f36101ee366004612644565b61049a565b005b348015610200575f80fd5b5061022361020f3660046126f2565b60336020525f908152604090205460ff1681565b60405190151581526020016101b6565b6101f36104bc565b348015610246575f80fd5b506101f3610255366004612709565b610508565b348015610265575f80fd5b50610223610579565b348015610279575f80fd5b506101f3610288366004612769565b61058f565b6101f3610627565b3480156102a0575f80fd5b506101f36102af3660046127a7565b610660565b3480156102bf575f80fd5b506101ac6106eb565b6101f3610797565b3480156102db575f80fd5b506101f36102ea3660046127f5565b6107aa565b3480156102fa575f80fd5b506101f3610309366004612769565b6109b2565b348015610319575f80fd5b5061032d6103283660046126f2565b610a54565b6040516101b692919061287d565b348015610346575f80fd5b50638b78c6d819546040516001600160a01b0390911681526020016101b6565b348015610371575f80fd5b506101ac6611c37937e0800081565b34801561038b575f80fd5b5061022361039a366004612709565b60646020525f908152604090205460ff1681565b6101ac6103bc366004612908565b610b2a565b3480156103cc575f80fd5b506101f36103db366004612709565b610dfd565b3480156103eb575f80fd5b506101ac6714d1120d7b16000081565b348015610406575f80fd5b506101ac66019945ca26200081565b348015610420575f80fd5b5061022361042f3660046126f2565b60326020525f908152604090205460ff1681565b6101f3610451366004612709565b610f09565b6101f3610464366004612709565b610f46565b348015610474575f80fd5b506101ac610483366004612709565b63389a75e1600c9081525f91909152602090205490565b6104a2610f6c565b6104b28888888888888888610f86565b5050505050505050565b5f6202a3006001600160401b03164201905063389a75e1600c52335f52806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f80a250565b610510610f6c565b6001600160a01b0381165f81815260646020908152604091829020805460ff81161560ff1990911681179091558251938452908301819052917fa54714518c5d275fdcd3d2a461e4858e4e8cb04fb93cd0bca9d6d34115f2644091015b60405180910390a15050565b5f610582610f6c565b61058a611026565b905090565b610597610f6c565b5f5b818110156105f35760645f8484848181106105b6576105b6612960565b90506020020160208101906105cb9190612709565b6001600160a01b0316815260208101919091526040015f20805460ff19169055600101610599565b507fe481b9d265afbda7824b6945408c17197396b0621a7e5418ac204f8c5abe644582825f60405161056d93929190612974565b63389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f80a2565b5f8481526033602052604090205460ff161561068f576040516319ed6f1960e11b815260040160405180910390fd5b5f8481526033602052604090819020805460ff1916600117905551839085907fc7f97dc1b8d74ceebb3210d4fa3c174bc04a0723402ea2b5800c07dbc645e2c0906106dd90869086906129f2565b60405180910390a350505050565b5f6106f4611075565b1561070557506611c37937e0800090565b61070d611096565b1561071f57506714d1120d7b16000090565b6015602160991b013b63ffffffff161561073f575066019945ca26200090565b61044d4603610754575066019945ca26200090565b60405162461bcd60e51b815260206004820152601360248201527210da185a5b881b9bdd081cdd5c1c1bdc9d1959606a1b60448201526064015b60405180910390fd5b61079f610f6c565b6107a85f6110ab565b565b5f8481526001602081815260408084208151808301835281546001600160a01b03168152825160e081018452948201805463ffffffff8082168852640100000000820416878701526001600160401b03600160401b8204811688870152600160801b90910416606087015260028301546080870152600383015460a08701526004830180548551818802810188019096528086529296939587810195929360c0860193929183018282801561087c57602002820191905f5260205f20905b815481526020019060010190808311610868575b505050919092525050509052505f868152600160208190526040822080546001600160a01b031916815590810180546001600160c01b0319168155600282018390556003820183905592935091816108d76004850182612530565b505050506108e3611075565b156108f657602081015182406080909101525b5f610906858584602001516110e8565b8251604051635e5ff5c560e01b81529192506001600160a01b031690635e5ff5c5906109389089908590600401612a92565b5f604051808303815f87803b15801561094f575f80fd5b505af1158015610961573d5f803e3d5ffd5b50505050815f01516001600160a01b0316867f380bea3763a1f2305283f679e3da2563d01a42cfe16d5744b7bd4c9d60ed61bf836040516109a29190612aaa565b60405180910390a3505050505050565b6109ba610f6c565b5f5b81811015610a1f57600160645f8585858181106109db576109db612960565b90506020020160208101906109f09190612709565b6001600160a01b0316815260208101919091526040015f20805460ff19169115159190911790556001016109bc565b507fe481b9d265afbda7824b6945408c17197396b0621a7e5418ac204f8c5abe64458282600160405161056d93929190612974565b600160208181525f9283526040928390208054845160e081018652938201805463ffffffff8082168752640100000000820416868601526001600160401b03600160401b8204811687890152600160801b90910416606086015260028301546080860152600383015460a08601526004830180548751818702810187019098528088526001600160a01b0390931696939594919360c08601939092830182828015610b1c57602002820191905f5260205f20905b815481526020019060010190808311610b08575b505050505081525050905082565b5f610b336106eb565b341015610b535760405163e6b6589360e01b815260040160405180910390fd5b828261044d4614158015610b6d5750610b6a611132565b81115b15610b8b57604051636d53123b60e01b815260040160405180910390fd5b80821115610bac57604051635d6fbc4960e11b815260040160405180910390fd5b61c350610bb98383612ad0565b610bc4906001612ae3565b1115610be3576040516383d304cf60e01b815260040160405180910390fd5b5f8054600101815580610bf4611075565b610c0d57610c00611132565b9150610c0a6111d3565b90505b6040518060400160405280336001600160a01b031681526020016040518060e001604052805f63ffffffff1681526020015f63ffffffff1681526020018a6001600160401b03168152602001896001600160401b031681526020018481526020018d81526020018c8c808060200260200160405190810160405280939291908181526020018383602002808284375f920182905250939094525050919092528054815260016020818152604092839020855181546001600160a01b0319166001600160a01b0390911617815585820151805193820180548285015196830151606084015163ffffffff97881667ffffffffffffffff199093169290921764010000000097909816969096029690961777ffffffffffffffffffffffffffffffff00000000000000001916600160401b6001600160401b039687160267ffffffffffffffff60801b191617600160801b95909616949094029490941783556080840151600282015560a0840151600382015560c08401518051919550610d9992600487019291019061254b565b505050905050336001600160a01b03168a5f547f5f80fe6d2eee22c9cc67535c0777535ddfbc865871b6f0369a4644936102e0a38c8c8c8c348a604051610de596959493929190612b26565b60405180910390a450505f5498975050505050505050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f81158015610e415750825b90505f826001600160401b03166001148015610e5c5750303b155b905081158015610e6a575080155b15610e885760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610eb257845460ff60401b1916600160401b1785555b610ebb86611262565b8315610f0157845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050565b610f11610f6c565b63389a75e1600c52805f526020600c208054421115610f3757636f5e88185f526004601cfd5b5f9055610f43816110ab565b50565b610f4e610f6c565b8060601b610f6357637448fbae5f526004601cfd5b610f43816110ab565b638b78c6d8195433146107a8576382b429005f526004601cfd5b5f8881526032602052604090205460ff1615610fb55760405163e5b70def60e01b815260040160405180910390fd5b5f8881526032602052604090819020805460ff19166001179055516001600160a01b0388169089907f77a60dab33c0274a972cadc956c819927458e5ed5b76a69c45c37ba6ed39f72690611014908a908a908a908a908a908a90612b5e565b60405180910390a35050505050505050565b6040515f908190339047908381818185875af1925050503d805f8114611067576040519150601f19603f3d011682016040523d82523d5f602084013e61106c565b606091505b50909392505050565b5f6001461480611087575062aa36a746145b8061058a575050466142681490565b5f61138846148061058a575050466113881490565b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a355565b604080518082019091525f8152606060208201525f611107858561126b565b90506111148585836113e7565b61111f8585856114ea565b6111298585611a5a565b95945050505050565b5f61113b611075565b1561114557504390565b6015602160991b013b63ffffffff16156111ce576015602160991b016001600160a01b0316638381f58a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561119c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111c09190612bab565b6001600160401b0316905090565b505f90565b5f6111dc611075565b156111e75750434090565b6015602160991b013b63ffffffff16156111ce576015602160991b016001600160a01b03166309bd5a606040518163ffffffff1660e01b8152600401602060405180830381865afa15801561123e573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061058a9190612bd1565b610f4381611cad565b611273612594565b61127b6125b2565b611283612594565b5f5b60088163ffffffff1610156112da5785858263ffffffff168181106112ac576112ac612960565b905060200201355f1c838263ffffffff16600881106112cd576112cd612960565b6020020152600101611285565b505f5b60038163ffffffff16101561133c5785856112f9836008612be8565b63ffffffff1681811061130e5761130e612960565b905060200201355f1c828263ffffffff166003811061132f5761132f612960565b60200201526001016112dd565b5080517f1ef3e0e4525d9634361ccfbbaab5825505dadaae75dfc5278ba0ab9824dabfc0146113d35760405162461bcd60e51b815260206004820152603b60248201527f5468652066697273742047726f7468313620696e707574206d7573742062652060448201527f657175616c20746f207468652063697263756974206469676573740000000000606482015260840161078e565b6113dd8282611ce8565b9150505b92915050565b5f6113f28484611fa5565b90505f6002826040516114059190612c0c565b602060405180830381855afa158015611420573d5f803e3d5ffd5b5050506040513d601f19601f820116820180604052508101906114439190612bd1565b6001600160fd1b03169050826002602002015181146114e35760405162461bcd60e51b815260206004820152605060248201527f546865207368613235362068617368206f66207075626c696320696e7075747360448201527f206d75737420626520657175616c20746f20746865206c617374206f6620746860648201526f652047726f7468313620696e7075747360801b608482015260a40161078e565b5050505050565b5f83836114f960056003612c22565b600e6115065f6001612be8565b611511906001612be8565b61151b9190612be8565b61152690600b612be8565b6115309190612be8565b63ffffffff1681811061154557611545612960565b9050602002013590505f84845f600b61155e9190612be8565b63ffffffff1681811061157357611573612960565b9050602002013590506115874661044d1490565b806115955750826080015181145b6115ec5760405162461bcd60e51b815260206004820152602260248201527f426c6f636b2068617368206d75737420657175616c2061732065787065637465604482015261321760f11b606482015260840161078e565b5f85856115fa836001612be8565b61160590600b612be8565b63ffffffff1681811061161a5761161a612960565b9050602002013590508360a0015181146116895760405162461bcd60e51b815260206004820152602a60248201527f436f6d7075746174696f6e616c2068617368206d75737420657175616c2061736044820152691032bc3832b1ba32b21760b11b606482015260840161078e565b5f611695816020612c22565b63ffffffff1684901b60e01c9050600e8111156117005760405162461bcd60e51b815260206004820152602360248201527f506c616365686f6c646572206e756d6265722063616e6e6f74206f766572666c60448201526237bb9760e91b606482015260840161078e565b60c085015151611711906002612ae3565b8163ffffffff161461178b5760405162461bcd60e51b815260206004820152603e60248201527f506c616365686f6c646572206e756d6265722063616e6e6f74206f766572666c60448201527f6f7720616e64206d75737420657175616c2061732065787065637465642e0000606482015260840161078e565b60408501516001600160401b031687876117a65f6001612be8565b6117b1906001612be8565b6117bc90600b612be8565b63ffffffff168181106117d1576117d1612960565b905060200201355f1c1461184f576040805162461bcd60e51b81526020600482015260248101919091527f54686520666972737420706c616365686f6c646572206d75737420626520746860448201527f65206578706563746564206d696e696d756d20626c6f636b206e756d6265722e606482015260840161078e565b60608501516001600160401b0316878761186a5f6001612be8565b611875906001612be8565b61188090600b612be8565b61188b906001612be8565b63ffffffff168181106118a0576118a0612960565b905060200201355f1c146119265760405162461bcd60e51b815260206004820152604160248201527f546865207365636f6e6420706c616365686f6c646572206d757374206265207460448201527f6865206578706563746564206d6178696d756d20626c6f636b206e756d6265726064820152601760f91b608482015260a40161078e565b5f5b611933600283612c4a565b63ffffffff16811015611a1f578560c00151818151811061195657611956612960565b60200260200101518888835f600161196e9190612be8565b611979906001612be8565b61198490600b612be8565b61198f906002612be8565b63ffffffff1661199f9190612ae3565b8181106119ae576119ae612960565b9050602002013514611a175760405162461bcd60e51b815260206004820152602c60248201527f546865207573657220706c616365686f6c646572206d75737420657175616c2060448201526b30b99032bc3832b1ba32b21760a11b606482015260840161078e565b600101611928565b505f611a2d60036020612c22565b63ffffffff1685901b60e01c905080156104b25760405163a729eb9d60e01b815260040160405180910390fd5b604080518082019091525f8152606060208201525f8383611a7d60056003612c22565b600e611a8a5f6001612be8565b611a95906001612be8565b611a9f9190612be8565b611aaa90600b612be8565b611ab49190612be8565b63ffffffff16818110611ac957611ac9612960565b9050602002013590505f60026020611ae19190612c22565b63ffffffff1682901b60e01c90505f611afc60016020612c22565b63ffffffff1683901b60e01c90506003811115611b5b5760405162461bcd60e51b815260206004820152601e60248201527f526573756c74206e756d6265722063616e6e6f74206f766572666c6f772e0000604482015260640161078e565b5f600e611b69826001612be8565b611b74906001612be8565b611b7e9190612be8565b611b8990600b612be8565b90505f8263ffffffff166001600160401b03811115611baa57611baa612c67565b604051908082528060200260200182016040528015611bdd57816020015b6060815260200190600190039081611bc85790505b5090505f5b8363ffffffff16811015611c87578888611bfd600584612c7b565b611c0d9063ffffffff8716612ae3565b906005611c1b856001612ae3565b611c259190612c7b565b611c359063ffffffff8816612ae3565b92611c4293929190612c92565b604051602001611c53929190612cbd565b604051602081830303815290604052828281518110611c7457611c74612960565b6020908102919091010152600101611be2565b506040805180820190915263ffffffff9094168452602084015250909250505092915050565b6001600160a01b0316638b78c6d819819055805f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a350565b5f80611cf3836122e4565b915091505f6040515f86015181526020860151602082015260408601516040820152606086015160608201526080860151608082015260a086015160a082015260c086015160c082015260e086015160e08201527f0c2f5ac5d38ac6bc5ffcc6d0443959df057e1b248b2179d185238ae4998ab8ef6101008201527f1c905a62d2a5dfbcf97c7134a26053e0225f7c3dbbeed02cf53050bfa8a0947e6101208201527f1099f5d793846a7f876ab3ada153ce75145db25319db01334448f437228635fc6101408201527f13d5410fcd052f4dbcfeab3213d2abda0036b2adceaf3b2315ebaf189171f8ec6101608201527f279731908d1a2a3cba0f3bdb5f96692b6ea1ac4826ff30a7422b6e38526a21d56101808201527f1ab5cc9fa00adbfdd948d63c641ca9772b0f1b82ed4bed83bebfc500af1842fb6101a08201527f0208e8514cb036a5a0c06c74fe594ebd71d3efd7657b6b1e3a45a0a48f4eed626101c08201527f02ebda29026e498463705b9f25bbefea6278dd729e7976a7a6af4d8a6ac574786101e08201527f0a078d41eb6d0b701853ba078cc0d26c04c95b72f9fb6c3720a1787c4c6ccab56102008201527f0946bc201905fd784086058caf5489559f443bee8b4eb332f23f5a0401f574d661022082015283610240820152826102608201527f1f8e88f27e31f5c6d77f8bbf1727178390b35df1509fd39963804629a8c801bf6102808201527f1bcb5d01090abf1d5f2a0da22e30d77db2259b3699f0840152937b5ad3eef60a6102a08201527f2f6090a0432c2a0e8affb6d97e10aef434ce8c7d19887a528b35008d963ed7bc6102c08201527f188c177e8f44e7b4d3b38029e7162e4b3998e8215c72e3309814b6e9f92c232d6102e08201526020816103008360085afa9051169050806114e357604051631ff3747d60e21b815260040160405180910390fd5b60605f611fb460056001612be8565b611fbf906004612c22565b600b611fcd60056003612c22565b600e611fda5f6001612be8565b611fe5906001612be8565b611fef9190612be8565b611ffa90600b612be8565b6120049190612be8565b61200e9190612c4a565b612019906020612c22565b6120239190612be8565b63ffffffff166001600160401b0381111561204057612040612c67565b6040519080825280601f01601f19166020018201604052801561206a576020820181803683370190505b5090505f602061207c60056001612be8565b612087906004612c22565b600b61209560056003612c22565b600e6120a25f6001612be8565b6120ad906001612be8565b6120b79190612be8565b6120c290600b612be8565b6120cc9190612be8565b6120d69190612c4a565b6120e1906020612c22565b6120eb9190612be8565b6120f59190612ce4565b63ffffffff1690505f5b818110156121af575f868661211584600b612ae3565b81811061212457612124612960565b9050602002013590505f5b60208163ffffffff1610156121a557818163ffffffff166020811061215657612156612960565b1a60f81b8563ffffffff831661216d866020612c7b565b6121779190612ae3565b8151811061218757612187612960565b60200101906001600160f81b03191690815f1a90535060010161212f565b50506001016120ff565b505f85856121be84600b612ae3565b8181106121cd576121cd612960565b9050602002013590505f5b60206121e660056001612be8565b6121f1906004612c22565b600b6121ff60056003612c22565b600e61220c5f6001612be8565b612217906001612be8565b6122219190612be8565b61222c90600b612be8565b6122369190612be8565b6122409190612c4a565b61224b906020612c22565b6122559190612be8565b61225f9190612d06565b63ffffffff168163ffffffff1610156122d957818163ffffffff166020811061228a5761228a612960565b1a60f81b8463ffffffff83166122a1866020612c7b565b6122ab9190612ae3565b815181106122bb576122bb612960565b60200101906001600160f81b03191690815f1a9053506001016121d8565b509195945050505050565b5f805f60019050604051604081015f7f1aa441b8e6cdc56f63ee195b4583f410a0f4ace2aee367532ca44844d465d94583527f13d58df8f66425d86bb391d54bb55fa9c0fe63e9fafafe6ca3601d582303fbd260208401527f221e51d0d785f9584d94c40aec1ad869d729a0980dd83f57d67d62acc8c7957382527f0dc3a30680180e89d0710191d17fca3f2976b4d44d0fb6826744feac537e98b76020830152865190508060408301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181108416935060408260608460075afa8416935060408360808560065afa841693507f19936c1fb812df1ee42ed160cb8edc80f17e0cf083e7a2115bc904c95d045e0482527f157a503e431747341c40111cd07246ef27d3d3d00c87e26572dd93df03ecafa56020830152602087015190508060408301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181108416935060408260608460075afa8416935060408360808560065afa7f2e8082bb071c7f8f8b22e411ca17cfd99d1f3699cdf5ef73ad9d35f7ce26bc3f83527f1314e34adb2f6120fef9c5a6a67056a06f52b0609a26316edbb685ce2f6984a760208401526040888101518185018190527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000011191909516169390508160608160075afa831692505060408160808360065afa8151602090920151919450909250168061252a5760405163a54f8e2760e01b815260040160405180910390fd5b50915091565b5080545f8255905f5260205f2090810190610f4391906125d1565b828054828255905f5260205f20908101928215612584579160200282015b82811115612584578251825591602001919060010190612569565b506125909291506125d1565b5090565b60405180606001604052806003906020820280368337509192915050565b6040518061010001604052806008906020820280368337509192915050565b5b80821115612590575f81556001016125d2565b80356001600160a01b03811681146125fb575f80fd5b919050565b5f8083601f840112612610575f80fd5b5081356001600160401b03811115612626575f80fd5b60208301915083602082850101111561263d575f80fd5b9250929050565b5f805f805f805f8060c0898b03121561265b575f80fd5b8835975061266b60208a016125e5565b965060408901356bffffffffffffffffffffffff8116811461268b575f80fd5b95506060890135945060808901356001600160401b03808211156126ad575f80fd5b6126b98c838d01612600565b909650945060a08b01359150808211156126d1575f80fd5b506126de8b828c01612600565b999c989b5096995094979396929594505050565b5f60208284031215612702575f80fd5b5035919050565b5f60208284031215612719575f80fd5b612722826125e5565b9392505050565b5f8083601f840112612739575f80fd5b5081356001600160401b0381111561274f575f80fd5b6020830191508360208260051b850101111561263d575f80fd5b5f806020838503121561277a575f80fd5b82356001600160401b0381111561278f575f80fd5b61279b85828601612729565b90969095509350505050565b5f805f80606085870312156127ba575f80fd5b843593506020850135925060408501356001600160401b038111156127dd575f80fd5b6127e987828801612600565b95989497509550505050565b5f805f8060608587031215612808575f80fd5b8435935060208501356001600160401b03811115612824575f80fd5b61283087828801612729565b9598909750949560400135949350505050565b5f815180845260208085019450602084015f5b8381101561287257815187529582019590820190600101612856565b509495945050505050565b60018060a01b0383168152604060208201525f63ffffffff808451166040840152806020850151166060840152506001600160401b03604084015116608083015260608301516128d860a08401826001600160401b03169052565b50608083015160c083015260a083015160e083015260c083015160e0610100840152611129610120840182612843565b5f805f805f6080868803121561291c575f80fd5b8535945060208601356001600160401b03811115612938575f80fd5b61294488828901612729565b9699909850959660408101359660609091013595509350505050565b634e487b7160e01b5f52603260045260245ffd5b604080825281018390525f8460608301825b868110156129b4576001600160a01b0361299f846125e5565b16825260209283019290910190600101612986565b5080925050508215156020830152949350505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b602081525f612a056020830184866129ca565b949350505050565b5f604083018251845260208084015160408287015282815180855260608801915060608160051b890101945083830192505f5b81811015612a8557888603605f190183528351805180885280878301888a015e5f888201880152601f01601f1916909601850195509284019291840191600101612a40565b5093979650505050505050565b828152604060208201525f612a056040830184612a0d565b602081525f6127226020830184612a0d565b634e487b7160e01b5f52601160045260245ffd5b818103818111156113e1576113e1612abc565b808201808211156113e1576113e1612abc565b8183525f6001600160fb1b03831115612b0d575f80fd5b8260051b80836020870137939093016020019392505050565b60a081525f612b3960a08301888a612af6565b9050856020830152846040830152836060830152826080830152979650505050505050565b6bffffffffffffffffffffffff87168152856020820152608060408201525f612b8b6080830186886129ca565b8281036060840152612b9e8185876129ca565b9998505050505050505050565b5f60208284031215612bbb575f80fd5b81516001600160401b0381168114612722575f80fd5b5f60208284031215612be1575f80fd5b5051919050565b63ffffffff818116838216019080821115612c0557612c05612abc565b5092915050565b5f82518060208501845e5f920191825250919050565b63ffffffff818116838216028082169190828114612c4257612c42612abc565b505092915050565b63ffffffff828116828216039080821115612c0557612c05612abc565b634e487b7160e01b5f52604160045260245ffd5b80820281158282048414176113e1576113e1612abc565b5f8085851115612ca0575f80fd5b83861115612cac575f80fd5b5050600583901b0193919092039150565b602081525f612a05602083018486612af6565b634e487b7160e01b5f52601260045260245ffd5b5f63ffffffff80841680612cfa57612cfa612cd0565b92169190910492915050565b5f63ffffffff80841680612d1c57612d1c612cd0565b9216919091069291505056fea26469706673582212209c8c3ea02e94c3470a01fa01109bfe82bfc9daf8d8c82550503116f36279e4af64736f6c63430008190033

Deployed Bytecode

0x608060405260043610610194575f3560e01c80637f649783116100e7578063c81de78c11610087578063d3098b9a11610062578063d3098b9a14610415578063f04e283e14610443578063f2fde38b14610456578063fee81cf414610469575f80fd5b8063c81de78c146103e0578063c9179506146103fb578063cf72dddd146103fb575f80fd5b80639468441c116100c25780639468441c146103665780639b19251a146103805780639bc24290146103ae578063c4d66de8146103c1575f80fd5b80637f649783146102ef57806381d12c581461030e5780638da5cb5b1461033b575f80fd5b8063476343ee1161015257806355c2c6711161012d57806355c2c67114610295578063658612e9146102b4578063715018a6146102c85780637b20c8dc146102d0575f80fd5b8063476343ee1461025a578063548db1741461026e57806354d1f13d1461028d575f80fd5b80626d6cae1461019857806308506853146101bf57806312def999146101d457806321dfb600146101f5578063256929621461023357806339393ac91461023b575b5f80fd5b3480156101a3575f80fd5b506101ac5f5481565b6040519081526020015b60405180910390f35b3480156101ca575f80fd5b506101ac61c35081565b3480156101df575f80fd5b506101f36101ee366004612644565b61049a565b005b348015610200575f80fd5b5061022361020f3660046126f2565b60336020525f908152604090205460ff1681565b60405190151581526020016101b6565b6101f36104bc565b348015610246575f80fd5b506101f3610255366004612709565b610508565b348015610265575f80fd5b50610223610579565b348015610279575f80fd5b506101f3610288366004612769565b61058f565b6101f3610627565b3480156102a0575f80fd5b506101f36102af3660046127a7565b610660565b3480156102bf575f80fd5b506101ac6106eb565b6101f3610797565b3480156102db575f80fd5b506101f36102ea3660046127f5565b6107aa565b3480156102fa575f80fd5b506101f3610309366004612769565b6109b2565b348015610319575f80fd5b5061032d6103283660046126f2565b610a54565b6040516101b692919061287d565b348015610346575f80fd5b50638b78c6d819546040516001600160a01b0390911681526020016101b6565b348015610371575f80fd5b506101ac6611c37937e0800081565b34801561038b575f80fd5b5061022361039a366004612709565b60646020525f908152604090205460ff1681565b6101ac6103bc366004612908565b610b2a565b3480156103cc575f80fd5b506101f36103db366004612709565b610dfd565b3480156103eb575f80fd5b506101ac6714d1120d7b16000081565b348015610406575f80fd5b506101ac66019945ca26200081565b348015610420575f80fd5b5061022361042f3660046126f2565b60326020525f908152604090205460ff1681565b6101f3610451366004612709565b610f09565b6101f3610464366004612709565b610f46565b348015610474575f80fd5b506101ac610483366004612709565b63389a75e1600c9081525f91909152602090205490565b6104a2610f6c565b6104b28888888888888888610f86565b5050505050505050565b5f6202a3006001600160401b03164201905063389a75e1600c52335f52806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f80a250565b610510610f6c565b6001600160a01b0381165f81815260646020908152604091829020805460ff81161560ff1990911681179091558251938452908301819052917fa54714518c5d275fdcd3d2a461e4858e4e8cb04fb93cd0bca9d6d34115f2644091015b60405180910390a15050565b5f610582610f6c565b61058a611026565b905090565b610597610f6c565b5f5b818110156105f35760645f8484848181106105b6576105b6612960565b90506020020160208101906105cb9190612709565b6001600160a01b0316815260208101919091526040015f20805460ff19169055600101610599565b507fe481b9d265afbda7824b6945408c17197396b0621a7e5418ac204f8c5abe644582825f60405161056d93929190612974565b63389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f80a2565b5f8481526033602052604090205460ff161561068f576040516319ed6f1960e11b815260040160405180910390fd5b5f8481526033602052604090819020805460ff1916600117905551839085907fc7f97dc1b8d74ceebb3210d4fa3c174bc04a0723402ea2b5800c07dbc645e2c0906106dd90869086906129f2565b60405180910390a350505050565b5f6106f4611075565b1561070557506611c37937e0800090565b61070d611096565b1561071f57506714d1120d7b16000090565b6015602160991b013b63ffffffff161561073f575066019945ca26200090565b61044d4603610754575066019945ca26200090565b60405162461bcd60e51b815260206004820152601360248201527210da185a5b881b9bdd081cdd5c1c1bdc9d1959606a1b60448201526064015b60405180910390fd5b61079f610f6c565b6107a85f6110ab565b565b5f8481526001602081815260408084208151808301835281546001600160a01b03168152825160e081018452948201805463ffffffff8082168852640100000000820416878701526001600160401b03600160401b8204811688870152600160801b90910416606087015260028301546080870152600383015460a08701526004830180548551818802810188019096528086529296939587810195929360c0860193929183018282801561087c57602002820191905f5260205f20905b815481526020019060010190808311610868575b505050919092525050509052505f868152600160208190526040822080546001600160a01b031916815590810180546001600160c01b0319168155600282018390556003820183905592935091816108d76004850182612530565b505050506108e3611075565b156108f657602081015182406080909101525b5f610906858584602001516110e8565b8251604051635e5ff5c560e01b81529192506001600160a01b031690635e5ff5c5906109389089908590600401612a92565b5f604051808303815f87803b15801561094f575f80fd5b505af1158015610961573d5f803e3d5ffd5b50505050815f01516001600160a01b0316867f380bea3763a1f2305283f679e3da2563d01a42cfe16d5744b7bd4c9d60ed61bf836040516109a29190612aaa565b60405180910390a3505050505050565b6109ba610f6c565b5f5b81811015610a1f57600160645f8585858181106109db576109db612960565b90506020020160208101906109f09190612709565b6001600160a01b0316815260208101919091526040015f20805460ff19169115159190911790556001016109bc565b507fe481b9d265afbda7824b6945408c17197396b0621a7e5418ac204f8c5abe64458282600160405161056d93929190612974565b600160208181525f9283526040928390208054845160e081018652938201805463ffffffff8082168752640100000000820416868601526001600160401b03600160401b8204811687890152600160801b90910416606086015260028301546080860152600383015460a08601526004830180548751818702810187019098528088526001600160a01b0390931696939594919360c08601939092830182828015610b1c57602002820191905f5260205f20905b815481526020019060010190808311610b08575b505050505081525050905082565b5f610b336106eb565b341015610b535760405163e6b6589360e01b815260040160405180910390fd5b828261044d4614158015610b6d5750610b6a611132565b81115b15610b8b57604051636d53123b60e01b815260040160405180910390fd5b80821115610bac57604051635d6fbc4960e11b815260040160405180910390fd5b61c350610bb98383612ad0565b610bc4906001612ae3565b1115610be3576040516383d304cf60e01b815260040160405180910390fd5b5f8054600101815580610bf4611075565b610c0d57610c00611132565b9150610c0a6111d3565b90505b6040518060400160405280336001600160a01b031681526020016040518060e001604052805f63ffffffff1681526020015f63ffffffff1681526020018a6001600160401b03168152602001896001600160401b031681526020018481526020018d81526020018c8c808060200260200160405190810160405280939291908181526020018383602002808284375f920182905250939094525050919092528054815260016020818152604092839020855181546001600160a01b0319166001600160a01b0390911617815585820151805193820180548285015196830151606084015163ffffffff97881667ffffffffffffffff199093169290921764010000000097909816969096029690961777ffffffffffffffffffffffffffffffff00000000000000001916600160401b6001600160401b039687160267ffffffffffffffff60801b191617600160801b95909616949094029490941783556080840151600282015560a0840151600382015560c08401518051919550610d9992600487019291019061254b565b505050905050336001600160a01b03168a5f547f5f80fe6d2eee22c9cc67535c0777535ddfbc865871b6f0369a4644936102e0a38c8c8c8c348a604051610de596959493929190612b26565b60405180910390a450505f5498975050505050505050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f81158015610e415750825b90505f826001600160401b03166001148015610e5c5750303b155b905081158015610e6a575080155b15610e885760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610eb257845460ff60401b1916600160401b1785555b610ebb86611262565b8315610f0157845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050565b610f11610f6c565b63389a75e1600c52805f526020600c208054421115610f3757636f5e88185f526004601cfd5b5f9055610f43816110ab565b50565b610f4e610f6c565b8060601b610f6357637448fbae5f526004601cfd5b610f43816110ab565b638b78c6d8195433146107a8576382b429005f526004601cfd5b5f8881526032602052604090205460ff1615610fb55760405163e5b70def60e01b815260040160405180910390fd5b5f8881526032602052604090819020805460ff19166001179055516001600160a01b0388169089907f77a60dab33c0274a972cadc956c819927458e5ed5b76a69c45c37ba6ed39f72690611014908a908a908a908a908a908a90612b5e565b60405180910390a35050505050505050565b6040515f908190339047908381818185875af1925050503d805f8114611067576040519150601f19603f3d011682016040523d82523d5f602084013e61106c565b606091505b50909392505050565b5f6001461480611087575062aa36a746145b8061058a575050466142681490565b5f61138846148061058a575050466113881490565b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a355565b604080518082019091525f8152606060208201525f611107858561126b565b90506111148585836113e7565b61111f8585856114ea565b6111298585611a5a565b95945050505050565b5f61113b611075565b1561114557504390565b6015602160991b013b63ffffffff16156111ce576015602160991b016001600160a01b0316638381f58a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561119c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111c09190612bab565b6001600160401b0316905090565b505f90565b5f6111dc611075565b156111e75750434090565b6015602160991b013b63ffffffff16156111ce576015602160991b016001600160a01b03166309bd5a606040518163ffffffff1660e01b8152600401602060405180830381865afa15801561123e573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061058a9190612bd1565b610f4381611cad565b611273612594565b61127b6125b2565b611283612594565b5f5b60088163ffffffff1610156112da5785858263ffffffff168181106112ac576112ac612960565b905060200201355f1c838263ffffffff16600881106112cd576112cd612960565b6020020152600101611285565b505f5b60038163ffffffff16101561133c5785856112f9836008612be8565b63ffffffff1681811061130e5761130e612960565b905060200201355f1c828263ffffffff166003811061132f5761132f612960565b60200201526001016112dd565b5080517f1ef3e0e4525d9634361ccfbbaab5825505dadaae75dfc5278ba0ab9824dabfc0146113d35760405162461bcd60e51b815260206004820152603b60248201527f5468652066697273742047726f7468313620696e707574206d7573742062652060448201527f657175616c20746f207468652063697263756974206469676573740000000000606482015260840161078e565b6113dd8282611ce8565b9150505b92915050565b5f6113f28484611fa5565b90505f6002826040516114059190612c0c565b602060405180830381855afa158015611420573d5f803e3d5ffd5b5050506040513d601f19601f820116820180604052508101906114439190612bd1565b6001600160fd1b03169050826002602002015181146114e35760405162461bcd60e51b815260206004820152605060248201527f546865207368613235362068617368206f66207075626c696320696e7075747360448201527f206d75737420626520657175616c20746f20746865206c617374206f6620746860648201526f652047726f7468313620696e7075747360801b608482015260a40161078e565b5050505050565b5f83836114f960056003612c22565b600e6115065f6001612be8565b611511906001612be8565b61151b9190612be8565b61152690600b612be8565b6115309190612be8565b63ffffffff1681811061154557611545612960565b9050602002013590505f84845f600b61155e9190612be8565b63ffffffff1681811061157357611573612960565b9050602002013590506115874661044d1490565b806115955750826080015181145b6115ec5760405162461bcd60e51b815260206004820152602260248201527f426c6f636b2068617368206d75737420657175616c2061732065787065637465604482015261321760f11b606482015260840161078e565b5f85856115fa836001612be8565b61160590600b612be8565b63ffffffff1681811061161a5761161a612960565b9050602002013590508360a0015181146116895760405162461bcd60e51b815260206004820152602a60248201527f436f6d7075746174696f6e616c2068617368206d75737420657175616c2061736044820152691032bc3832b1ba32b21760b11b606482015260840161078e565b5f611695816020612c22565b63ffffffff1684901b60e01c9050600e8111156117005760405162461bcd60e51b815260206004820152602360248201527f506c616365686f6c646572206e756d6265722063616e6e6f74206f766572666c60448201526237bb9760e91b606482015260840161078e565b60c085015151611711906002612ae3565b8163ffffffff161461178b5760405162461bcd60e51b815260206004820152603e60248201527f506c616365686f6c646572206e756d6265722063616e6e6f74206f766572666c60448201527f6f7720616e64206d75737420657175616c2061732065787065637465642e0000606482015260840161078e565b60408501516001600160401b031687876117a65f6001612be8565b6117b1906001612be8565b6117bc90600b612be8565b63ffffffff168181106117d1576117d1612960565b905060200201355f1c1461184f576040805162461bcd60e51b81526020600482015260248101919091527f54686520666972737420706c616365686f6c646572206d75737420626520746860448201527f65206578706563746564206d696e696d756d20626c6f636b206e756d6265722e606482015260840161078e565b60608501516001600160401b0316878761186a5f6001612be8565b611875906001612be8565b61188090600b612be8565b61188b906001612be8565b63ffffffff168181106118a0576118a0612960565b905060200201355f1c146119265760405162461bcd60e51b815260206004820152604160248201527f546865207365636f6e6420706c616365686f6c646572206d757374206265207460448201527f6865206578706563746564206d6178696d756d20626c6f636b206e756d6265726064820152601760f91b608482015260a40161078e565b5f5b611933600283612c4a565b63ffffffff16811015611a1f578560c00151818151811061195657611956612960565b60200260200101518888835f600161196e9190612be8565b611979906001612be8565b61198490600b612be8565b61198f906002612be8565b63ffffffff1661199f9190612ae3565b8181106119ae576119ae612960565b9050602002013514611a175760405162461bcd60e51b815260206004820152602c60248201527f546865207573657220706c616365686f6c646572206d75737420657175616c2060448201526b30b99032bc3832b1ba32b21760a11b606482015260840161078e565b600101611928565b505f611a2d60036020612c22565b63ffffffff1685901b60e01c905080156104b25760405163a729eb9d60e01b815260040160405180910390fd5b604080518082019091525f8152606060208201525f8383611a7d60056003612c22565b600e611a8a5f6001612be8565b611a95906001612be8565b611a9f9190612be8565b611aaa90600b612be8565b611ab49190612be8565b63ffffffff16818110611ac957611ac9612960565b9050602002013590505f60026020611ae19190612c22565b63ffffffff1682901b60e01c90505f611afc60016020612c22565b63ffffffff1683901b60e01c90506003811115611b5b5760405162461bcd60e51b815260206004820152601e60248201527f526573756c74206e756d6265722063616e6e6f74206f766572666c6f772e0000604482015260640161078e565b5f600e611b69826001612be8565b611b74906001612be8565b611b7e9190612be8565b611b8990600b612be8565b90505f8263ffffffff166001600160401b03811115611baa57611baa612c67565b604051908082528060200260200182016040528015611bdd57816020015b6060815260200190600190039081611bc85790505b5090505f5b8363ffffffff16811015611c87578888611bfd600584612c7b565b611c0d9063ffffffff8716612ae3565b906005611c1b856001612ae3565b611c259190612c7b565b611c359063ffffffff8816612ae3565b92611c4293929190612c92565b604051602001611c53929190612cbd565b604051602081830303815290604052828281518110611c7457611c74612960565b6020908102919091010152600101611be2565b506040805180820190915263ffffffff9094168452602084015250909250505092915050565b6001600160a01b0316638b78c6d819819055805f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a350565b5f80611cf3836122e4565b915091505f6040515f86015181526020860151602082015260408601516040820152606086015160608201526080860151608082015260a086015160a082015260c086015160c082015260e086015160e08201527f0c2f5ac5d38ac6bc5ffcc6d0443959df057e1b248b2179d185238ae4998ab8ef6101008201527f1c905a62d2a5dfbcf97c7134a26053e0225f7c3dbbeed02cf53050bfa8a0947e6101208201527f1099f5d793846a7f876ab3ada153ce75145db25319db01334448f437228635fc6101408201527f13d5410fcd052f4dbcfeab3213d2abda0036b2adceaf3b2315ebaf189171f8ec6101608201527f279731908d1a2a3cba0f3bdb5f96692b6ea1ac4826ff30a7422b6e38526a21d56101808201527f1ab5cc9fa00adbfdd948d63c641ca9772b0f1b82ed4bed83bebfc500af1842fb6101a08201527f0208e8514cb036a5a0c06c74fe594ebd71d3efd7657b6b1e3a45a0a48f4eed626101c08201527f02ebda29026e498463705b9f25bbefea6278dd729e7976a7a6af4d8a6ac574786101e08201527f0a078d41eb6d0b701853ba078cc0d26c04c95b72f9fb6c3720a1787c4c6ccab56102008201527f0946bc201905fd784086058caf5489559f443bee8b4eb332f23f5a0401f574d661022082015283610240820152826102608201527f1f8e88f27e31f5c6d77f8bbf1727178390b35df1509fd39963804629a8c801bf6102808201527f1bcb5d01090abf1d5f2a0da22e30d77db2259b3699f0840152937b5ad3eef60a6102a08201527f2f6090a0432c2a0e8affb6d97e10aef434ce8c7d19887a528b35008d963ed7bc6102c08201527f188c177e8f44e7b4d3b38029e7162e4b3998e8215c72e3309814b6e9f92c232d6102e08201526020816103008360085afa9051169050806114e357604051631ff3747d60e21b815260040160405180910390fd5b60605f611fb460056001612be8565b611fbf906004612c22565b600b611fcd60056003612c22565b600e611fda5f6001612be8565b611fe5906001612be8565b611fef9190612be8565b611ffa90600b612be8565b6120049190612be8565b61200e9190612c4a565b612019906020612c22565b6120239190612be8565b63ffffffff166001600160401b0381111561204057612040612c67565b6040519080825280601f01601f19166020018201604052801561206a576020820181803683370190505b5090505f602061207c60056001612be8565b612087906004612c22565b600b61209560056003612c22565b600e6120a25f6001612be8565b6120ad906001612be8565b6120b79190612be8565b6120c290600b612be8565b6120cc9190612be8565b6120d69190612c4a565b6120e1906020612c22565b6120eb9190612be8565b6120f59190612ce4565b63ffffffff1690505f5b818110156121af575f868661211584600b612ae3565b81811061212457612124612960565b9050602002013590505f5b60208163ffffffff1610156121a557818163ffffffff166020811061215657612156612960565b1a60f81b8563ffffffff831661216d866020612c7b565b6121779190612ae3565b8151811061218757612187612960565b60200101906001600160f81b03191690815f1a90535060010161212f565b50506001016120ff565b505f85856121be84600b612ae3565b8181106121cd576121cd612960565b9050602002013590505f5b60206121e660056001612be8565b6121f1906004612c22565b600b6121ff60056003612c22565b600e61220c5f6001612be8565b612217906001612be8565b6122219190612be8565b61222c90600b612be8565b6122369190612be8565b6122409190612c4a565b61224b906020612c22565b6122559190612be8565b61225f9190612d06565b63ffffffff168163ffffffff1610156122d957818163ffffffff166020811061228a5761228a612960565b1a60f81b8463ffffffff83166122a1866020612c7b565b6122ab9190612ae3565b815181106122bb576122bb612960565b60200101906001600160f81b03191690815f1a9053506001016121d8565b509195945050505050565b5f805f60019050604051604081015f7f1aa441b8e6cdc56f63ee195b4583f410a0f4ace2aee367532ca44844d465d94583527f13d58df8f66425d86bb391d54bb55fa9c0fe63e9fafafe6ca3601d582303fbd260208401527f221e51d0d785f9584d94c40aec1ad869d729a0980dd83f57d67d62acc8c7957382527f0dc3a30680180e89d0710191d17fca3f2976b4d44d0fb6826744feac537e98b76020830152865190508060408301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181108416935060408260608460075afa8416935060408360808560065afa841693507f19936c1fb812df1ee42ed160cb8edc80f17e0cf083e7a2115bc904c95d045e0482527f157a503e431747341c40111cd07246ef27d3d3d00c87e26572dd93df03ecafa56020830152602087015190508060408301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181108416935060408260608460075afa8416935060408360808560065afa7f2e8082bb071c7f8f8b22e411ca17cfd99d1f3699cdf5ef73ad9d35f7ce26bc3f83527f1314e34adb2f6120fef9c5a6a67056a06f52b0609a26316edbb685ce2f6984a760208401526040888101518185018190527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000011191909516169390508160608160075afa831692505060408160808360065afa8151602090920151919450909250168061252a5760405163a54f8e2760e01b815260040160405180910390fd5b50915091565b5080545f8255905f5260205f2090810190610f4391906125d1565b828054828255905f5260205f20908101928215612584579160200282015b82811115612584578251825591602001919060010190612569565b506125909291506125d1565b5090565b60405180606001604052806003906020820280368337509192915050565b6040518061010001604052806008906020820280368337509192915050565b5b80821115612590575f81556001016125d2565b80356001600160a01b03811681146125fb575f80fd5b919050565b5f8083601f840112612610575f80fd5b5081356001600160401b03811115612626575f80fd5b60208301915083602082850101111561263d575f80fd5b9250929050565b5f805f805f805f8060c0898b03121561265b575f80fd5b8835975061266b60208a016125e5565b965060408901356bffffffffffffffffffffffff8116811461268b575f80fd5b95506060890135945060808901356001600160401b03808211156126ad575f80fd5b6126b98c838d01612600565b909650945060a08b01359150808211156126d1575f80fd5b506126de8b828c01612600565b999c989b5096995094979396929594505050565b5f60208284031215612702575f80fd5b5035919050565b5f60208284031215612719575f80fd5b612722826125e5565b9392505050565b5f8083601f840112612739575f80fd5b5081356001600160401b0381111561274f575f80fd5b6020830191508360208260051b850101111561263d575f80fd5b5f806020838503121561277a575f80fd5b82356001600160401b0381111561278f575f80fd5b61279b85828601612729565b90969095509350505050565b5f805f80606085870312156127ba575f80fd5b843593506020850135925060408501356001600160401b038111156127dd575f80fd5b6127e987828801612600565b95989497509550505050565b5f805f8060608587031215612808575f80fd5b8435935060208501356001600160401b03811115612824575f80fd5b61283087828801612729565b9598909750949560400135949350505050565b5f815180845260208085019450602084015f5b8381101561287257815187529582019590820190600101612856565b509495945050505050565b60018060a01b0383168152604060208201525f63ffffffff808451166040840152806020850151166060840152506001600160401b03604084015116608083015260608301516128d860a08401826001600160401b03169052565b50608083015160c083015260a083015160e083015260c083015160e0610100840152611129610120840182612843565b5f805f805f6080868803121561291c575f80fd5b8535945060208601356001600160401b03811115612938575f80fd5b61294488828901612729565b9699909850959660408101359660609091013595509350505050565b634e487b7160e01b5f52603260045260245ffd5b604080825281018390525f8460608301825b868110156129b4576001600160a01b0361299f846125e5565b16825260209283019290910190600101612986565b5080925050508215156020830152949350505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b602081525f612a056020830184866129ca565b949350505050565b5f604083018251845260208084015160408287015282815180855260608801915060608160051b890101945083830192505f5b81811015612a8557888603605f190183528351805180885280878301888a015e5f888201880152601f01601f1916909601850195509284019291840191600101612a40565b5093979650505050505050565b828152604060208201525f612a056040830184612a0d565b602081525f6127226020830184612a0d565b634e487b7160e01b5f52601160045260245ffd5b818103818111156113e1576113e1612abc565b808201808211156113e1576113e1612abc565b8183525f6001600160fb1b03831115612b0d575f80fd5b8260051b80836020870137939093016020019392505050565b60a081525f612b3960a08301888a612af6565b9050856020830152846040830152836060830152826080830152979650505050505050565b6bffffffffffffffffffffffff87168152856020820152608060408201525f612b8b6080830186886129ca565b8281036060840152612b9e8185876129ca565b9998505050505050505050565b5f60208284031215612bbb575f80fd5b81516001600160401b0381168114612722575f80fd5b5f60208284031215612be1575f80fd5b5051919050565b63ffffffff818116838216019080821115612c0557612c05612abc565b5092915050565b5f82518060208501845e5f920191825250919050565b63ffffffff818116838216028082169190828114612c4257612c42612abc565b505092915050565b63ffffffff828116828216039080821115612c0557612c05612abc565b634e487b7160e01b5f52604160045260245ffd5b80820281158282048414176113e1576113e1612abc565b5f8085851115612ca0575f80fd5b83861115612cac575f80fd5b5050600583901b0193919092039150565b602081525f612a05602083018486612af6565b634e487b7160e01b5f52601260045260245ffd5b5f63ffffffff80841680612cfa57612cfa612cd0565b92169190910492915050565b5f63ffffffff80841680612d1c57612d1c612cd0565b9216919091069291505056fea26469706673582212209c8c3ea02e94c3470a01fa01109bfe82bfc9daf8d8c82550503116f36279e4af64736f6c63430008190033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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.