ETH Price: $2,434.60 (+3.65%)

Transaction Decoder

Block:
19011779 at Jan-15-2024 10:46:59 AM +UTC
Transaction Fee:
0.00198916299598545 ETH $4.84
Gas Used:
86,850 Gas / 22.903431157 Gwei

Emitted Events:

247 Proxy.0xa7aaf2512769da4e444e3de247be2564225c2e7a8f74cfe528e46e17d24868e2( 0xa7aaf2512769da4e444e3de247be2564225c2e7a8f74cfe528e46e17d24868e2, 0x1dc88ef759e14ba0ccac2423479781c1d8cd6d3cd1f767c476bca097337868f7, 0x0000000000000000000000000000000000000000000000000000000000002419, 0x000000000000000000000000000000000000000000000000000000000010ec30, 0000000000000000000000000000000000000000000000000000000065a50d23 )

Account State Difference:

  Address   Before After State Difference Code
4.228520818640758885 Eth4.228521687276244885 Eth0.000000868635486
0x30c78967...19EA05C1D
0x853bDfF7...651f90aCa
(Manta Pacific: Proposer)
1.512104781009054778 Eth
Nonce: 9241
1.510115618013069328 Eth
Nonce: 9242
0.00198916299598545

Execution Trace

Proxy.9aaab648( )
  • L2OutputOracle.proposeL2Output( _outputRoot=1DC88EF759E14BA0CCAC2423479781C1D8CD6D3CD1F767C476BCA097337868F7, _l2BlockNumber=1109040, _l1BlockHash=B6888A9D93A8F22FC59A478B8FBF301AC398F9FDE33AE002F7F38E2448D5E155, _l1BlockNumber=19011767 )
    File 1 of 2: Proxy
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.15;
    /**
     * @title Proxy
     * @notice Proxy is a transparent proxy that passes through the call if the caller is the owner or
     *         if the caller is address(0), meaning that the call originated from an off-chain
     *         simulation.
     */
    contract Proxy {
        /**
         * @notice The storage slot that holds the address of the implementation.
         *         bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
         */
        bytes32 internal constant IMPLEMENTATION_KEY =
            0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
        /**
         * @notice The storage slot that holds the address of the owner.
         *         bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)
         */
        bytes32 internal constant OWNER_KEY =
            0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
        /**
         * @notice An event that is emitted each time the implementation is changed. This event is part
         *         of the EIP-1967 specification.
         *
         * @param implementation The address of the implementation contract
         */
        event Upgraded(address indexed implementation);
        /**
         * @notice An event that is emitted each time the owner is upgraded. This event is part of the
         *         EIP-1967 specification.
         *
         * @param previousAdmin The previous owner of the contract
         * @param newAdmin      The new owner of the contract
         */
        event AdminChanged(address previousAdmin, address newAdmin);
        /**
         * @notice A modifier that reverts if not called by the owner or by address(0) to allow
         *         eth_call to interact with this proxy without needing to use low-level storage
         *         inspection. We assume that nobody is able to trigger calls from address(0) during
         *         normal EVM execution.
         */
        modifier proxyCallIfNotAdmin() {
            if (msg.sender == _getAdmin() || msg.sender == address(0)) {
                _;
            } else {
                // This WILL halt the call frame on completion.
                _doProxyCall();
            }
        }
        /**
         * @notice Sets the initial admin during contract deployment. Admin address is stored at the
         *         EIP-1967 admin storage slot so that accidental storage collision with the
         *         implementation is not possible.
         *
         * @param _admin Address of the initial contract admin. Admin as the ability to access the
         *               transparent proxy interface.
         */
        constructor(address _admin) {
            _changeAdmin(_admin);
        }
        // slither-disable-next-line locked-ether
        receive() external payable {
            // Proxy call by default.
            _doProxyCall();
        }
        // slither-disable-next-line locked-ether
        fallback() external payable {
            // Proxy call by default.
            _doProxyCall();
        }
        /**
         * @notice Set the implementation contract address. The code at the given address will execute
         *         when this contract is called.
         *
         * @param _implementation Address of the implementation contract.
         */
        function upgradeTo(address _implementation) public virtual proxyCallIfNotAdmin {
            _setImplementation(_implementation);
        }
        /**
         * @notice Set the implementation and call a function in a single transaction. Useful to ensure
         *         atomic execution of initialization-based upgrades.
         *
         * @param _implementation Address of the implementation contract.
         * @param _data           Calldata to delegatecall the new implementation with.
         */
        function upgradeToAndCall(address _implementation, bytes calldata _data)
            public
            payable
            virtual
            proxyCallIfNotAdmin
            returns (bytes memory)
        {
            _setImplementation(_implementation);
            (bool success, bytes memory returndata) = _implementation.delegatecall(_data);
            require(success, "Proxy: delegatecall to new implementation contract failed");
            return returndata;
        }
        /**
         * @notice Changes the owner of the proxy contract. Only callable by the owner.
         *
         * @param _admin New owner of the proxy contract.
         */
        function changeAdmin(address _admin) public virtual proxyCallIfNotAdmin {
            _changeAdmin(_admin);
        }
        /**
         * @notice Gets the owner of the proxy contract.
         *
         * @return Owner address.
         */
        function admin() public virtual proxyCallIfNotAdmin returns (address) {
            return _getAdmin();
        }
        /**
         * @notice Queries the implementation address.
         *
         * @return Implementation address.
         */
        function implementation() public virtual proxyCallIfNotAdmin returns (address) {
            return _getImplementation();
        }
        /**
         * @notice Sets the implementation address.
         *
         * @param _implementation New implementation address.
         */
        function _setImplementation(address _implementation) internal {
            assembly {
                sstore(IMPLEMENTATION_KEY, _implementation)
            }
            emit Upgraded(_implementation);
        }
        /**
         * @notice Changes the owner of the proxy contract.
         *
         * @param _admin New owner of the proxy contract.
         */
        function _changeAdmin(address _admin) internal {
            address previous = _getAdmin();
            assembly {
                sstore(OWNER_KEY, _admin)
            }
            emit AdminChanged(previous, _admin);
        }
        /**
         * @notice Performs the proxy call via a delegatecall.
         */
        function _doProxyCall() internal {
            address impl = _getImplementation();
            require(impl != address(0), "Proxy: implementation not initialized");
            assembly {
                // Copy calldata into memory at 0x0....calldatasize.
                calldatacopy(0x0, 0x0, calldatasize())
                // Perform the delegatecall, make sure to pass all available gas.
                let success := delegatecall(gas(), impl, 0x0, calldatasize(), 0x0, 0x0)
                // Copy returndata into memory at 0x0....returndatasize. Note that this *will*
                // overwrite the calldata that we just copied into memory but that doesn't really
                // matter because we'll be returning in a second anyway.
                returndatacopy(0x0, 0x0, returndatasize())
                // Success == 0 means a revert. We'll revert too and pass the data up.
                if iszero(success) {
                    revert(0x0, returndatasize())
                }
                // Otherwise we'll just return and pass the data up.
                return(0x0, returndatasize())
            }
        }
        /**
         * @notice Queries the implementation address.
         *
         * @return Implementation address.
         */
        function _getImplementation() internal view returns (address) {
            address impl;
            assembly {
                impl := sload(IMPLEMENTATION_KEY)
            }
            return impl;
        }
        /**
         * @notice Queries the owner of the proxy contract.
         *
         * @return Owner address.
         */
        function _getAdmin() internal view returns (address) {
            address owner;
            assembly {
                owner := sload(OWNER_KEY)
            }
            return owner;
        }
    }
    

    File 2 of 2: L2OutputOracle
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.15;
    import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
    import { Semver } from "../universal/Semver.sol";
    import { Types } from "../libraries/Types.sol";
    /**
     * @custom:proxied
     * @title L2OutputOracle
     * @notice The L2OutputOracle contains an array of L2 state outputs, where each output is a
     *         commitment to the state of the L2 chain. Other contracts like the OptimismPortal use
     *         these outputs to verify information about the state of L2.
     */
    contract L2OutputOracle is Initializable, Semver {
        /**
         * @notice The interval in L2 blocks at which checkpoints must be submitted. Although this is
         *         immutable, it can safely be modified by upgrading the implementation contract.
         */
        uint256 public immutable SUBMISSION_INTERVAL;
        /**
         * @notice The time between L2 blocks in seconds. Once set, this value MUST NOT be modified.
         */
        uint256 public immutable L2_BLOCK_TIME;
        /**
         * @notice The address of the challenger. Can be updated via upgrade.
         */
        address public immutable CHALLENGER;
        /**
         * @notice The address of the proposer. Can be updated via upgrade.
         */
        address public immutable PROPOSER;
        /**
         * @notice Minimum time (in seconds) that must elapse before a withdrawal can be finalized.
         */
        uint256 public immutable FINALIZATION_PERIOD_SECONDS;
        /**
         * @notice The number of the first L2 block recorded in this contract.
         */
        uint256 public startingBlockNumber;
        /**
         * @notice The timestamp of the first L2 block recorded in this contract.
         */
        uint256 public startingTimestamp;
        /**
         * @notice Array of L2 output proposals.
         */
        Types.OutputProposal[] internal l2Outputs;
        /**
         * @notice Emitted when an output is proposed.
         *
         * @param outputRoot    The output root.
         * @param l2OutputIndex The index of the output in the l2Outputs array.
         * @param l2BlockNumber The L2 block number of the output root.
         * @param l1Timestamp   The L1 timestamp when proposed.
         */
        event OutputProposed(
            bytes32 indexed outputRoot,
            uint256 indexed l2OutputIndex,
            uint256 indexed l2BlockNumber,
            uint256 l1Timestamp
        );
        /**
         * @notice Emitted when outputs are deleted.
         *
         * @param prevNextOutputIndex Next L2 output index before the deletion.
         * @param newNextOutputIndex  Next L2 output index after the deletion.
         */
        event OutputsDeleted(uint256 indexed prevNextOutputIndex, uint256 indexed newNextOutputIndex);
        /**
         * @custom:semver 1.2.0
         *
         * @param _submissionInterval  Interval in blocks at which checkpoints must be submitted.
         * @param _l2BlockTime         The time per L2 block, in seconds.
         * @param _startingBlockNumber The number of the first L2 block.
         * @param _startingTimestamp   The timestamp of the first L2 block.
         * @param _proposer            The address of the proposer.
         * @param _challenger          The address of the challenger.
         */
        constructor(
            uint256 _submissionInterval,
            uint256 _l2BlockTime,
            uint256 _startingBlockNumber,
            uint256 _startingTimestamp,
            address _proposer,
            address _challenger,
            uint256 _finalizationPeriodSeconds
        ) Semver(1, 2, 0) {
            require(_l2BlockTime > 0, "L2OutputOracle: L2 block time must be greater than 0");
            require(
                _submissionInterval > _l2BlockTime,
                "L2OutputOracle: submission interval must be greater than L2 block time"
            );
            SUBMISSION_INTERVAL = _submissionInterval;
            L2_BLOCK_TIME = _l2BlockTime;
            PROPOSER = _proposer;
            CHALLENGER = _challenger;
            FINALIZATION_PERIOD_SECONDS = _finalizationPeriodSeconds;
            initialize(_startingBlockNumber, _startingTimestamp);
        }
        /**
         * @notice Initializer.
         *
         * @param _startingBlockNumber Block number for the first recoded L2 block.
         * @param _startingTimestamp   Timestamp for the first recoded L2 block.
         */
        function initialize(uint256 _startingBlockNumber, uint256 _startingTimestamp)
            public
            initializer
        {
            require(
                _startingTimestamp <= block.timestamp,
                "L2OutputOracle: starting L2 timestamp must be less than current time"
            );
            startingTimestamp = _startingTimestamp;
            startingBlockNumber = _startingBlockNumber;
        }
        /**
         * @notice Deletes all output proposals after and including the proposal that corresponds to
         *         the given output index. Only the challenger address can delete outputs.
         *
         * @param _l2OutputIndex Index of the first L2 output to be deleted. All outputs after this
         *                       output will also be deleted.
         */
        // solhint-disable-next-line ordering
        function deleteL2Outputs(uint256 _l2OutputIndex) external {
            require(
                msg.sender == CHALLENGER,
                "L2OutputOracle: only the challenger address can delete outputs"
            );
            // Make sure we're not *increasing* the length of the array.
            require(
                _l2OutputIndex < l2Outputs.length,
                "L2OutputOracle: cannot delete outputs after the latest output index"
            );
            // Do not allow deleting any outputs that have already been finalized.
            require(
                block.timestamp - l2Outputs[_l2OutputIndex].timestamp < FINALIZATION_PERIOD_SECONDS,
                "L2OutputOracle: cannot delete outputs that have already been finalized"
            );
            uint256 prevNextL2OutputIndex = nextOutputIndex();
            // Use assembly to delete the array elements because Solidity doesn't allow it.
            assembly {
                sstore(l2Outputs.slot, _l2OutputIndex)
            }
            emit OutputsDeleted(prevNextL2OutputIndex, _l2OutputIndex);
        }
        /**
         * @notice Accepts an outputRoot and the timestamp of the corresponding L2 block. The timestamp
         *         must be equal to the current value returned by `nextTimestamp()` in order to be
         *         accepted. This function may only be called by the Proposer.
         *
         * @param _outputRoot    The L2 output of the checkpoint block.
         * @param _l2BlockNumber The L2 block number that resulted in _outputRoot.
         * @param _l1BlockHash   A block hash which must be included in the current chain.
         * @param _l1BlockNumber The block number with the specified block hash.
         */
        function proposeL2Output(
            bytes32 _outputRoot,
            uint256 _l2BlockNumber,
            bytes32 _l1BlockHash,
            uint256 _l1BlockNumber
        ) external payable {
            require(
                msg.sender == PROPOSER,
                "L2OutputOracle: only the proposer address can propose new outputs"
            );
            require(
                _l2BlockNumber == nextBlockNumber(),
                "L2OutputOracle: block number must be equal to next expected block number"
            );
            require(
                computeL2Timestamp(_l2BlockNumber) < block.timestamp,
                "L2OutputOracle: cannot propose L2 output in the future"
            );
            require(
                _outputRoot != bytes32(0),
                "L2OutputOracle: L2 output proposal cannot be the zero hash"
            );
            if (_l1BlockHash != bytes32(0)) {
                // This check allows the proposer to propose an output based on a given L1 block,
                // without fear that it will be reorged out.
                // It will also revert if the blockheight provided is more than 256 blocks behind the
                // chain tip (as the hash will return as zero). This does open the door to a griefing
                // attack in which the proposer's submission is censored until the block is no longer
                // retrievable, if the proposer is experiencing this attack it can simply leave out the
                // blockhash value, and delay submission until it is confident that the L1 block is
                // finalized.
                require(
                    blockhash(_l1BlockNumber) == _l1BlockHash,
                    "L2OutputOracle: block hash does not match the hash at the expected height"
                );
            }
            emit OutputProposed(_outputRoot, nextOutputIndex(), _l2BlockNumber, block.timestamp);
            l2Outputs.push(
                Types.OutputProposal({
                    outputRoot: _outputRoot,
                    timestamp: uint128(block.timestamp),
                    l2BlockNumber: uint128(_l2BlockNumber)
                })
            );
        }
        /**
         * @notice Returns an output by index. Exists because Solidity's array access will return a
         *         tuple instead of a struct.
         *
         * @param _l2OutputIndex Index of the output to return.
         *
         * @return The output at the given index.
         */
        function getL2Output(uint256 _l2OutputIndex)
            external
            view
            returns (Types.OutputProposal memory)
        {
            return l2Outputs[_l2OutputIndex];
        }
        /**
         * @notice Returns the index of the L2 output that checkpoints a given L2 block number. Uses a
         *         binary search to find the first output greater than or equal to the given block.
         *
         * @param _l2BlockNumber L2 block number to find a checkpoint for.
         *
         * @return Index of the first checkpoint that commits to the given L2 block number.
         */
        function getL2OutputIndexAfter(uint256 _l2BlockNumber) public view returns (uint256) {
            // Make sure an output for this block number has actually been proposed.
            require(
                _l2BlockNumber <= latestBlockNumber(),
                "L2OutputOracle: cannot get output for a block that has not been proposed"
            );
            // Make sure there's at least one output proposed.
            require(
                l2Outputs.length > 0,
                "L2OutputOracle: cannot get output as no outputs have been proposed yet"
            );
            // Find the output via binary search, guaranteed to exist.
            uint256 lo = 0;
            uint256 hi = l2Outputs.length;
            while (lo < hi) {
                uint256 mid = (lo + hi) / 2;
                if (l2Outputs[mid].l2BlockNumber < _l2BlockNumber) {
                    lo = mid + 1;
                } else {
                    hi = mid;
                }
            }
            return lo;
        }
        /**
         * @notice Returns the L2 output proposal that checkpoints a given L2 block number. Uses a
         *         binary search to find the first output greater than or equal to the given block.
         *
         * @param _l2BlockNumber L2 block number to find a checkpoint for.
         *
         * @return First checkpoint that commits to the given L2 block number.
         */
        function getL2OutputAfter(uint256 _l2BlockNumber)
            external
            view
            returns (Types.OutputProposal memory)
        {
            return l2Outputs[getL2OutputIndexAfter(_l2BlockNumber)];
        }
        /**
         * @notice Returns the number of outputs that have been proposed. Will revert if no outputs
         *         have been proposed yet.
         *
         * @return The number of outputs that have been proposed.
         */
        function latestOutputIndex() external view returns (uint256) {
            return l2Outputs.length - 1;
        }
        /**
         * @notice Returns the index of the next output to be proposed.
         *
         * @return The index of the next output to be proposed.
         */
        function nextOutputIndex() public view returns (uint256) {
            return l2Outputs.length;
        }
        /**
         * @notice Returns the block number of the latest submitted L2 output proposal. If no proposals
         *         been submitted yet then this function will return the starting block number.
         *
         * @return Latest submitted L2 block number.
         */
        function latestBlockNumber() public view returns (uint256) {
            return
                l2Outputs.length == 0
                    ? startingBlockNumber
                    : l2Outputs[l2Outputs.length - 1].l2BlockNumber;
        }
        /**
         * @notice Computes the block number of the next L2 block that needs to be checkpointed.
         *
         * @return Next L2 block number.
         */
        function nextBlockNumber() public view returns (uint256) {
            return latestBlockNumber() + SUBMISSION_INTERVAL;
        }
        /**
         * @notice Returns the L2 timestamp corresponding to a given L2 block number.
         *
         * @param _l2BlockNumber The L2 block number of the target block.
         *
         * @return L2 timestamp of the given block.
         */
        function computeL2Timestamp(uint256 _l2BlockNumber) public view returns (uint256) {
            return startingTimestamp + ((_l2BlockNumber - startingBlockNumber) * L2_BLOCK_TIME);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    /**
     * @title Types
     * @notice Contains various types used throughout the Optimism contract system.
     */
    library Types {
        /**
         * @notice OutputProposal represents a commitment to the L2 state. The timestamp is the L1
         *         timestamp that the output root is posted. This timestamp is used to verify that the
         *         finalization period has passed since the output root was submitted.
         *
         * @custom:field outputRoot    Hash of the L2 output.
         * @custom:field timestamp     Timestamp of the L1 block that the output root was submitted in.
         * @custom:field l2BlockNumber L2 block number that the output corresponds to.
         */
        struct OutputProposal {
            bytes32 outputRoot;
            uint128 timestamp;
            uint128 l2BlockNumber;
        }
        /**
         * @notice Struct representing the elements that are hashed together to generate an output root
         *         which itself represents a snapshot of the L2 state.
         *
         * @custom:field version                  Version of the output root.
         * @custom:field stateRoot                Root of the state trie at the block of this output.
         * @custom:field messagePasserStorageRoot Root of the message passer storage trie.
         * @custom:field latestBlockhash          Hash of the block this output was generated from.
         */
        struct OutputRootProof {
            bytes32 version;
            bytes32 stateRoot;
            bytes32 messagePasserStorageRoot;
            bytes32 latestBlockhash;
        }
        /**
         * @notice Struct representing a deposit transaction (L1 => L2 transaction) created by an end
         *         user (as opposed to a system deposit transaction generated by the system).
         *
         * @custom:field from        Address of the sender of the transaction.
         * @custom:field to          Address of the recipient of the transaction.
         * @custom:field isCreation  True if the transaction is a contract creation.
         * @custom:field value       Value to send to the recipient.
         * @custom:field mint        Amount of ETH to mint.
         * @custom:field gasLimit    Gas limit of the transaction.
         * @custom:field data        Data of the transaction.
         * @custom:field l1BlockHash Hash of the block the transaction was submitted in.
         * @custom:field logIndex    Index of the log in the block the transaction was submitted in.
         */
        struct UserDepositTransaction {
            address from;
            address to;
            bool isCreation;
            uint256 value;
            uint256 mint;
            uint64 gasLimit;
            bytes data;
            bytes32 l1BlockHash;
            uint256 logIndex;
        }
        /**
         * @notice Struct representing a withdrawal transaction.
         *
         * @custom:field nonce    Nonce of the withdrawal transaction
         * @custom:field sender   Address of the sender of the transaction.
         * @custom:field target   Address of the recipient of the transaction.
         * @custom:field value    Value to send to the recipient.
         * @custom:field gasLimit Gas limit of the transaction.
         * @custom:field data     Data of the transaction.
         */
        struct WithdrawalTransaction {
            uint256 nonce;
            address sender;
            address target;
            uint256 value;
            uint256 gasLimit;
            bytes data;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
    /**
     * @title Semver
     * @notice Semver is a simple contract for managing contract versions.
     */
    contract Semver {
        /**
         * @notice Contract version number (major).
         */
        uint256 private immutable MAJOR_VERSION;
        /**
         * @notice Contract version number (minor).
         */
        uint256 private immutable MINOR_VERSION;
        /**
         * @notice Contract version number (patch).
         */
        uint256 private immutable PATCH_VERSION;
        /**
         * @param _major Version number (major).
         * @param _minor Version number (minor).
         * @param _patch Version number (patch).
         */
        constructor(
            uint256 _major,
            uint256 _minor,
            uint256 _patch
        ) {
            MAJOR_VERSION = _major;
            MINOR_VERSION = _minor;
            PATCH_VERSION = _patch;
        }
        /**
         * @notice Returns the full semver contract version.
         *
         * @return Semver contract version as a string.
         */
        function version() public view returns (string memory) {
            return
                string(
                    abi.encodePacked(
                        Strings.toString(MAJOR_VERSION),
                        ".",
                        Strings.toString(MINOR_VERSION),
                        ".",
                        Strings.toString(PATCH_VERSION)
                    )
                );
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol)
    pragma solidity ^0.8.2;
    import "../../utils/Address.sol";
    /**
     * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
     * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
     * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
     * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
     *
     * 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]
     * ```
     * 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 Indicates that the contract has been initialized.
         * @custom:oz-retyped-from bool
         */
        uint8 private _initialized;
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool private _initializing;
        /**
         * @dev Triggered when the contract has been initialized or reinitialized.
         */
        event Initialized(uint8 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. Equivalent to `reinitializer(1)`.
         */
        modifier initializer() {
            bool isTopLevelCall = !_initializing;
            require(
                (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1),
                "Initializable: contract is already initialized"
            );
            _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.
         *
         * `initializer` is equivalent to `reinitializer(1)`, so 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.
         *
         * 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.
         */
        modifier reinitializer(uint8 version) {
            require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
            _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() {
            require(_initializing, "Initializable: contract is not initializing");
            _;
        }
        /**
         * @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.
         */
        function _disableInitializers() internal virtual {
            require(!_initializing, "Initializable: contract is initializing");
            if (_initialized < type(uint8).max) {
                _initialized = type(uint8).max;
                emit Initialized(type(uint8).max);
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)
    pragma solidity ^0.8.1;
    /**
     * @dev Collection of functions related to the address type
     */
    library Address {
        /**
         * @dev Returns true if `account` is a contract.
         *
         * [IMPORTANT]
         * ====
         * It is unsafe to assume that an address for which this function returns
         * false is an externally-owned account (EOA) and not a contract.
         *
         * Among others, `isContract` will return false for the following
         * types of addresses:
         *
         *  - an externally-owned account
         *  - a contract in construction
         *  - an address where a contract will be created
         *  - an address where a contract lived, but was destroyed
         * ====
         *
         * [IMPORTANT]
         * ====
         * You shouldn't rely on `isContract` to protect against flash loan attacks!
         *
         * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
         * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
         * constructor.
         * ====
         */
        function isContract(address account) internal view returns (bool) {
            // This method relies on extcodesize/address.code.length, which returns 0
            // for contracts in construction, since the code is only stored at the end
            // of the constructor execution.
            return account.code.length > 0;
        }
        /**
         * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
         * `recipient`, forwarding all available gas and reverting on errors.
         *
         * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
         * of certain opcodes, possibly making contracts go over the 2300 gas limit
         * imposed by `transfer`, making them unable to receive funds via
         * `transfer`. {sendValue} removes this limitation.
         *
         * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
         *
         * IMPORTANT: because control is transferred to `recipient`, care must be
         * taken to not create reentrancy vulnerabilities. Consider using
         * {ReentrancyGuard} or the
         * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
         */
        function sendValue(address payable recipient, uint256 amount) internal {
            require(address(this).balance >= amount, "Address: insufficient balance");
            (bool success, ) = recipient.call{value: amount}("");
            require(success, "Address: unable to send value, recipient may have reverted");
        }
        /**
         * @dev Performs a Solidity function call using a low level `call`. A
         * plain `call` is an unsafe replacement for a function call: use this
         * function instead.
         *
         * If `target` reverts with a revert reason, it is bubbled up by this
         * function (like regular Solidity function calls).
         *
         * Returns the raw returned data. To convert to the expected return value,
         * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
         *
         * Requirements:
         *
         * - `target` must be a contract.
         * - calling `target` with `data` must not revert.
         *
         * _Available since v3.1._
         */
        function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCall(target, data, "Address: low-level call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
         * `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but also transferring `value` wei to `target`.
         *
         * Requirements:
         *
         * - the calling contract must have an ETH balance of at least `value`.
         * - the called Solidity function must be `payable`.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(
            address target,
            bytes memory data,
            uint256 value
        ) internal returns (bytes memory) {
            return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
        }
        /**
         * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
         * with `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(
            address target,
            bytes memory data,
            uint256 value,
            string memory errorMessage
        ) internal returns (bytes memory) {
            require(address(this).balance >= value, "Address: insufficient balance for call");
            require(isContract(target), "Address: call to non-contract");
            (bool success, bytes memory returndata) = target.call{value: value}(data);
            return verifyCallResult(success, returndata, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
            return functionStaticCall(target, data, "Address: low-level static call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal view returns (bytes memory) {
            require(isContract(target), "Address: static call to non-contract");
            (bool success, bytes memory returndata) = target.staticcall(data);
            return verifyCallResult(success, returndata, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionDelegateCall(target, data, "Address: low-level delegate call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            require(isContract(target), "Address: delegate call to non-contract");
            (bool success, bytes memory returndata) = target.delegatecall(data);
            return verifyCallResult(success, returndata, errorMessage);
        }
        /**
         * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
         * revert reason using the provided one.
         *
         * _Available since v4.3._
         */
        function verifyCallResult(
            bool success,
            bytes memory returndata,
            string memory errorMessage
        ) internal pure returns (bytes memory) {
            if (success) {
                return returndata;
            } else {
                // Look for revert reason and bubble it up if present
                if (returndata.length > 0) {
                    // The easiest way to bubble the revert reason is using memory via assembly
                    /// @solidity memory-safe-assembly
                    assembly {
                        let returndata_size := mload(returndata)
                        revert(add(32, returndata), returndata_size)
                    }
                } else {
                    revert(errorMessage);
                }
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev String operations.
     */
    library Strings {
        bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
        uint8 private constant _ADDRESS_LENGTH = 20;
        /**
         * @dev Converts a `uint256` to its ASCII `string` decimal representation.
         */
        function toString(uint256 value) internal pure returns (string memory) {
            // Inspired by OraclizeAPI's implementation - MIT licence
            // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
            if (value == 0) {
                return "0";
            }
            uint256 temp = value;
            uint256 digits;
            while (temp != 0) {
                digits++;
                temp /= 10;
            }
            bytes memory buffer = new bytes(digits);
            while (value != 0) {
                digits -= 1;
                buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
                value /= 10;
            }
            return string(buffer);
        }
        /**
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
         */
        function toHexString(uint256 value) internal pure returns (string memory) {
            if (value == 0) {
                return "0x00";
            }
            uint256 temp = value;
            uint256 length = 0;
            while (temp != 0) {
                length++;
                temp >>= 8;
            }
            return toHexString(value, length);
        }
        /**
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
         */
        function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
            bytes memory buffer = new bytes(2 * length + 2);
            buffer[0] = "0";
            buffer[1] = "x";
            for (uint256 i = 2 * length + 1; i > 1; --i) {
                buffer[i] = _HEX_SYMBOLS[value & 0xf];
                value >>= 4;
            }
            require(value == 0, "Strings: hex length insufficient");
            return string(buffer);
        }
        /**
         * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
         */
        function toHexString(address addr) internal pure returns (string memory) {
            return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
        }
    }