MON Price: $0.018944 (+3.41%)

Contract

0xbf6Cc109c6eBcA4B28e3e51FD8798294599CFe2A

Overview

MON Balance

Monad Chain LogoMonad Chain LogoMonad Chain Logo0 MON

MON Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
KuruForwarder

Compiler Version
v0.8.30+commit.73712a01

Optimization Enabled:
Yes with 1000 runs

Other Settings:
prague EvmVersion
//SPDX-License-Identifier: BUSL-1.1

//   ░█████░     ░█████▓    ███                                                       
//   ███████    ▓███████▒   ███▒                                                      
//   ███████   █████████▒   ███▒                                                      
//   ███████  ██████████░   ███▒    ████  ████     ████  ▓████████ ░███░    ░███▓     
//     ░████▒██████▒░       ███▒   ████   ████     ████  ▓████████ ▒███▒    ░███▓     
//                          ███▒ ▒████    ████     ████  ▓████     ▒███░    ░███▓     
//     ▓████▒███████▓       █████████     ████     ████  ▓███░     ▒███░    ░███▓     
//   ███████  ██████████▒   █████████▒    ████     ████  ▓███      ░███▒    ░███▒     
//   ███████   █████████▒   ███▒  ████▓   ▓███▒   ░████  ▓███       ████    ████░     
//   ███████    ▓███████▒   ███▒   ▒████   ███████████   ▓███       ░██████████▒      
//    ▒▓▓▓▓       ▒▓▓▓▓▒    ▓██      ███▒    ░█████░      ██▓          ▒████▒           
                                                                                       
pragma solidity ^0.8.20;

// ============ External Imports ============
import {ECDSA} from "solady/src/utils/ECDSA.sol";
import {EIP712} from "solady/src/utils/EIP712.sol";
import {Initializable} from "solady/src/utils/Initializable.sol";
import {Ownable} from "solady/src/auth/Ownable.sol";
import {UUPSUpgradeable} from "solady/src/utils/UUPSUpgradeable.sol";

// ============ Internal Interface Imports ============
import {IOrderBook} from "./interfaces/IOrderBook.sol";
import {KuruForwarderErrors} from "./libraries/Errors.sol";

contract KuruForwarder is EIP712, Initializable, Ownable, UUPSUpgradeable {
    using ECDSA for bytes32;

    error ExecutionFailed(bytes);
    error PriceDependentRequestFailed(uint256 _currentPrice, uint256 _breakpointPrice);
    error DeadlineExpired();

    mapping(bytes4 => bool) public allowedInterface;
    mapping(bytes32 => bool) public executedPriceDependentRequest;

    struct ForwardRequest {
        address from;
        address market;
        uint256 value;
        uint256 nonce;
        uint256 deadline;
        bytes4 selector;
        bytes data;
    }

    struct PriceDependentRequest {
        address from;
        address market;
        uint256 price;
        uint256 value;
        uint256 nonce;
        uint256 deadline;
        bool isBelowPrice;
        bytes4 selector;
        bytes data;
    }

    struct CancelPriceDependentRequest {
        address from;
        uint256 nonce;
        uint256 deadline;
    }

    struct MarginAccountRequest {
        address from;
        address marginAccount;
        uint256 value;
        uint256 nonce;
        uint256 deadline;
        bytes4 selector;
        bytes data;
    }

    bytes32 private constant _FORWARD_TYPEHASH = keccak256(
        "ForwardRequest(address from,address market,uint256 value,uint256 nonce,uint256 deadline,bytes4 selector,bytes data)"
    );

    bytes32 private constant _PRICE_DEPENDENT_TYPEHASH = keccak256(
        "PriceDependentRequest(address from,address market,uint256 price,uint256 value,uint256 nonce,uint256 deadline,bool isBelowPrice,bytes4 selector,bytes data)"
    );

    bytes32 private constant _CANCEL_PRICE_DEPENDENT_TYPEHASH =
        keccak256("CancelPriceDependentRequest(address from,uint256 nonce,uint256 deadline)");

    bytes32 private constant _MARGIN_ACCOUNT_REQUEST_TYPEHASH = keccak256(
        "MarginAccountRequest(address from,address marginAccount,uint256 value,uint256 nonce,uint256 deadline,bytes4 selector,bytes data)"
    );

    mapping(address => uint256) private _nonces;

    constructor() {
        _disableInitializers();
    }

    // ============ auth =================
    function _guardInitializeOwner() internal pure override returns (bool guard) {
        guard = true;
    }

    function _authorizeUpgrade(address) internal view override {
        _checkOwner();
    }

    // ============ initializer =================

    function initialize(address _owner, bytes4[] memory _allowedInterfaces) public initializer {
        require(_owner != address(0), KuruForwarderErrors.ZeroAddressNotAllowed());
        _initializeOwner(_owner);
        for (uint256 i = 0; i < _allowedInterfaces.length; i++) {
            allowedInterface[_allowedInterfaces[i]] = true;
        }
    }

    // ============ owner functions =============

    function setAllowedInterfaces(bytes4[] memory _allowedInterfaces) external onlyOwner {
        for (uint256 i = 0; i < _allowedInterfaces.length; i++) {
            allowedInterface[_allowedInterfaces[i]] = true;
        }
    }

    function removeAllowedInterfaces(bytes4[] memory _allowedInterfaces) external onlyOwner {
        for (uint256 i = 0; i < _allowedInterfaces.length; i++) {
            allowedInterface[_allowedInterfaces[i]] = false;
        }
    }

    function _domainNameAndVersionMayChange() internal pure override returns (bool result) {
        return true;
    }

    function _domainNameAndVersion() internal pure override returns (string memory name, string memory version) {
        name = "KuruForwarder";
        version = "1.0.0";
    }

    function getNonce(address from) public view returns (uint256) {
        return _nonces[from];
    }

    function verify(ForwardRequest calldata req, bytes calldata signature) public view returns (bool) {
        require(block.timestamp <= req.deadline, DeadlineExpired());
        address signer = _hashTypedData(
            keccak256(
                abi.encode(
                    _FORWARD_TYPEHASH,
                    req.from,
                    req.market,
                    req.value,
                    req.nonce,
                    req.deadline,
                    req.selector,
                    keccak256(req.data)
                )
            )
        ).recoverCalldata(signature);
        return req.nonce >= _nonces[req.from] && signer == req.from;
    }

    //Does not check nonce because price dependent transaction nonces are not iterative
    //executePriceDependent() has special checks for nonces
    function verifyPriceDependent(PriceDependentRequest calldata req, bytes calldata signature)
        public
        view
        returns (bool)
    {
        require(block.timestamp <= req.deadline, DeadlineExpired());
        address signer = _hashTypedData(
            keccak256(
                abi.encode(
                    _PRICE_DEPENDENT_TYPEHASH,
                    req.from,
                    req.market,
                    req.price,
                    req.value,
                    req.nonce,
                    req.deadline,
                    req.isBelowPrice,
                    req.selector,
                    keccak256(req.data)
                )
            )
        ).recoverCalldata(signature);
        require(
            !executedPriceDependentRequest[keccak256(abi.encodePacked(req.from, req.nonce))],
            KuruForwarderErrors.NonceAlreadyUsed()
        );
        return signer == req.from;
    }

    function verifyCancelPriceDependent(CancelPriceDependentRequest calldata req, bytes calldata signature)
        public
        view
        returns (bool)
    {
        require(block.timestamp <= req.deadline, DeadlineExpired());
        address signer = _hashTypedData(
            keccak256(abi.encode(_CANCEL_PRICE_DEPENDENT_TYPEHASH, req.from, req.nonce, req.deadline))
        ).recoverCalldata(signature);
        require(
            !executedPriceDependentRequest[keccak256(abi.encodePacked(req.from, req.nonce))],
            KuruForwarderErrors.NonceAlreadyUsed()
        );
        return signer == req.from;
    }

    function verifyMarginAccountRequest(MarginAccountRequest calldata req, bytes calldata signature)
        public
        view
        returns (bool)
    {
        require(block.timestamp <= req.deadline, DeadlineExpired());
        address signer = _hashTypedData(
            keccak256(
                abi.encode(
                    _MARGIN_ACCOUNT_REQUEST_TYPEHASH,
                    req.from,
                    req.marginAccount,
                    req.value,
                    req.nonce,
                    req.deadline,
                    req.selector,
                    keccak256(req.data)
                )
            )
        ).recoverCalldata(signature);
        return req.nonce >= _nonces[req.from] && signer == req.from;
    }

    function executeMarginAccountRequest(MarginAccountRequest calldata req, bytes calldata signature)
        public
        payable
        returns (bytes memory)
    {
        require(verifyMarginAccountRequest(req, signature), KuruForwarderErrors.SignatureMismatch());
        require(msg.value >= req.value, KuruForwarderErrors.InsufficientValue());
        require(allowedInterface[req.selector], KuruForwarderErrors.InterfaceNotAllowed());

        _nonces[req.from] = req.nonce + 1;

        (bool success, bytes memory returndata) =
            req.marginAccount.call{value: req.value}(abi.encodePacked(req.selector, req.data, req.from));

        if (!success) {
            revert ExecutionFailed(returndata);
        }

        return returndata;
    }

    function execute(ForwardRequest calldata req, bytes calldata signature) public payable returns (bytes memory) {
        require(verify(req, signature), KuruForwarderErrors.SignatureMismatch());
        require(msg.value >= req.value, KuruForwarderErrors.InsufficientValue());
        require(allowedInterface[req.selector], KuruForwarderErrors.InterfaceNotAllowed());

        _nonces[req.from] = req.nonce + 1;

        (bool success, bytes memory returndata) =
            req.market.call{value: req.value}(abi.encodePacked(req.selector, req.data, req.from));

        if (!success) {
            revert ExecutionFailed(returndata);
        }

        return returndata;
    }

    function executePriceDependent(PriceDependentRequest calldata req, bytes calldata signature)
        public
        payable
        returns (bytes memory)
    {
        require(verifyPriceDependent(req, signature), KuruForwarderErrors.SignatureMismatch());
        require(msg.value >= req.value, KuruForwarderErrors.InsufficientValue());
        require(allowedInterface[req.selector], KuruForwarderErrors.InterfaceNotAllowed());
        executedPriceDependentRequest[keccak256(abi.encodePacked(req.from, req.nonce))] = true;

        (uint256 _currentBidPrice,) = IOrderBook(req.market).bestBidAsk();
        require(
            (req.isBelowPrice && req.price <= _currentBidPrice) || (!req.isBelowPrice && req.price >= _currentBidPrice),
            PriceDependentRequestFailed(_currentBidPrice, req.price)
        );
        (bool success, bytes memory returndata) =
            req.market.call{value: req.value}(abi.encodePacked(req.selector, req.data, req.from));

        if (!success) {
            revert ExecutionFailed(returndata);
        }

        return returndata;
    }

    function cancelPriceDependent(CancelPriceDependentRequest calldata req, bytes calldata signature) public {
        require(verifyCancelPriceDependent(req, signature), KuruForwarderErrors.SignatureMismatch());

        executedPriceDependentRequest[keccak256(abi.encodePacked(req.from, req.nonce))] = true;
    }
}

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

/// @notice Gas optimized ECDSA wrapper.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ECDSA.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ECDSA.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol)
///
/// @dev Note:
/// - The recovery functions use the ecrecover precompile (0x1).
/// - As of Solady version 0.0.68, the `recover` variants will revert upon recovery failure.
///   This is for more safety by default.
///   Use the `tryRecover` variants if you need to get the zero address back
///   upon recovery failure instead.
/// - As of Solady version 0.0.134, all `bytes signature` variants accept both
///   regular 65-byte `(r, s, v)` and EIP-2098 `(r, vs)` short form signatures.
///   See: https://eips.ethereum.org/EIPS/eip-2098
///   This is for calldata efficiency on smart accounts prevalent on L2s.
///
/// WARNING! Do NOT directly use signatures as unique identifiers:
/// - The recovery operations do NOT check if a signature is non-malleable.
/// - Use a nonce in the digest to prevent replay attacks on the same contract.
/// - Use EIP-712 for the digest to prevent replay attacks across different chains and contracts.
///   EIP-712 also enables readable signing of typed data for better user safety.
/// - If you need a unique hash from a signature, please use the `canonicalHash` functions.
library ECDSA {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The order of the secp256k1 elliptic curve.
    uint256 internal constant N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141;

    /// @dev `N/2 + 1`. Used for checking the malleability of the signature.
    uint256 private constant _HALF_N_PLUS_1 =
        0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                        CUSTOM ERRORS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The signature is invalid.
    error InvalidSignature();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                    RECOVERY OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
    function recover(bytes32 hash, bytes memory signature) internal view returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            for { let m := mload(0x40) } 1 {
                mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                revert(0x1c, 0x04)
            } {
                switch mload(signature)
                case 64 {
                    let vs := mload(add(signature, 0x40))
                    mstore(0x20, add(shr(255, vs), 27)) // `v`.
                    mstore(0x60, shr(1, shl(1, vs))) // `s`.
                }
                case 65 {
                    mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                    mstore(0x60, mload(add(signature, 0x40))) // `s`.
                }
                default { continue }
                mstore(0x00, hash)
                mstore(0x40, mload(add(signature, 0x20))) // `r`.
                result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
                mstore(0x60, 0) // Restore the zero slot.
                mstore(0x40, m) // Restore the free memory pointer.
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                if returndatasize() { break }
            }
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
    function recoverCalldata(bytes32 hash, bytes calldata signature)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            for { let m := mload(0x40) } 1 {
                mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                revert(0x1c, 0x04)
            } {
                switch signature.length
                case 64 {
                    let vs := calldataload(add(signature.offset, 0x20))
                    mstore(0x20, add(shr(255, vs), 27)) // `v`.
                    mstore(0x40, calldataload(signature.offset)) // `r`.
                    mstore(0x60, shr(1, shl(1, vs))) // `s`.
                }
                case 65 {
                    mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                    calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
                }
                default { continue }
                mstore(0x00, hash)
                result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
                mstore(0x60, 0) // Restore the zero slot.
                mstore(0x40, m) // Restore the free memory pointer.
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                if returndatasize() { break }
            }
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`,
    /// and the EIP-2098 short form signature defined by `r` and `vs`.
    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal view returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x00, hash)
            mstore(0x20, add(shr(255, vs), 27)) // `v`.
            mstore(0x40, r)
            mstore(0x60, shr(1, shl(1, vs))) // `s`.
            result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
            if iszero(returndatasize()) {
                mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`,
    /// and the signature defined by `v`, `r`, `s`.
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x00, hash)
            mstore(0x20, and(v, 0xff))
            mstore(0x40, r)
            mstore(0x60, s)
            result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
            if iszero(returndatasize()) {
                mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   TRY-RECOVER OPERATIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // WARNING!
    // These functions will NOT revert upon recovery failure.
    // Instead, they will return the zero address upon recovery failure.
    // It is critical that the returned address is NEVER compared against
    // a zero address (e.g. an uninitialized address variable).

    /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
    function tryRecover(bytes32 hash, bytes memory signature)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            for { let m := mload(0x40) } 1 {} {
                switch mload(signature)
                case 64 {
                    let vs := mload(add(signature, 0x40))
                    mstore(0x20, add(shr(255, vs), 27)) // `v`.
                    mstore(0x60, shr(1, shl(1, vs))) // `s`.
                }
                case 65 {
                    mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                    mstore(0x60, mload(add(signature, 0x40))) // `s`.
                }
                default { break }
                mstore(0x00, hash)
                mstore(0x40, mload(add(signature, 0x20))) // `r`.
                pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20))
                mstore(0x60, 0) // Restore the zero slot.
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                result := mload(xor(0x60, returndatasize()))
                mstore(0x40, m) // Restore the free memory pointer.
                break
            }
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
    function tryRecoverCalldata(bytes32 hash, bytes calldata signature)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            for { let m := mload(0x40) } 1 {} {
                switch signature.length
                case 64 {
                    let vs := calldataload(add(signature.offset, 0x20))
                    mstore(0x20, add(shr(255, vs), 27)) // `v`.
                    mstore(0x40, calldataload(signature.offset)) // `r`.
                    mstore(0x60, shr(1, shl(1, vs))) // `s`.
                }
                case 65 {
                    mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                    calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
                }
                default { break }
                mstore(0x00, hash)
                pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20))
                mstore(0x60, 0) // Restore the zero slot.
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                result := mload(xor(0x60, returndatasize()))
                mstore(0x40, m) // Restore the free memory pointer.
                break
            }
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`,
    /// and the EIP-2098 short form signature defined by `r` and `vs`.
    function tryRecover(bytes32 hash, bytes32 r, bytes32 vs)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x00, hash)
            mstore(0x20, add(shr(255, vs), 27)) // `v`.
            mstore(0x40, r)
            mstore(0x60, shr(1, shl(1, vs))) // `s`.
            pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20))
            mstore(0x60, 0) // Restore the zero slot.
            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
            result := mload(xor(0x60, returndatasize()))
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`,
    /// and the signature defined by `v`, `r`, `s`.
    function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x00, hash)
            mstore(0x20, and(v, 0xff))
            mstore(0x40, r)
            mstore(0x60, s)
            pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20))
            mstore(0x60, 0) // Restore the zero slot.
            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
            result := mload(xor(0x60, returndatasize()))
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     HASHING OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns an Ethereum Signed Message, created from a `hash`.
    /// This produces a hash corresponding to the one signed with the
    /// [`eth_sign`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign)
    /// JSON-RPC method as part of EIP-191.
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x20, hash) // Store into scratch space for keccak256.
            mstore(0x00, "\x00\x00\x00\x00\x19Ethereum Signed Message:\n32") // 28 bytes.
            result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`.
        }
    }

    /// @dev Returns an Ethereum Signed Message, created from `s`.
    /// This produces a hash corresponding to the one signed with the
    /// [`eth_sign`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign)
    /// JSON-RPC method as part of EIP-191.
    /// Note: Supports lengths of `s` up to 999999 bytes.
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let sLength := mload(s)
            let o := 0x20
            mstore(o, "\x19Ethereum Signed Message:\n") // 26 bytes, zero-right-padded.
            mstore(0x00, 0x00)
            // Convert the `s.length` to ASCII decimal representation: `base10(s.length)`.
            for { let temp := sLength } 1 {} {
                o := sub(o, 1)
                mstore8(o, add(48, mod(temp, 10)))
                temp := div(temp, 10)
                if iszero(temp) { break }
            }
            let n := sub(0x3a, o) // Header length: `26 + 32 - o`.
            // Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes.
            returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20))
            mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header.
            result := keccak256(add(s, sub(0x20, n)), add(n, sLength))
            mstore(s, sLength) // Restore the length.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  CANONICAL HASH FUNCTIONS                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // The following functions return the hash of the signature in its canonicalized format,
    // which is the 65-byte `abi.encodePacked(r, s, uint8(v))`, where `v` is either 27 or 28.
    // If `s` is greater than `N / 2` then it will be converted to `N - s`
    // and the `v` value will be flipped.
    // If the signature has an invalid length, or if `v` is invalid,
    // a uniquely corrupt hash will be returned.
    // These functions are useful for "poor-mans-VRF".

    /// @dev Returns the canonical hash of `signature`.
    function canonicalHash(bytes memory signature) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let l := mload(signature)
            for {} 1 {} {
                mstore(0x00, mload(add(signature, 0x20))) // `r`.
                let s := mload(add(signature, 0x40))
                let v := mload(add(signature, 0x41))
                if eq(l, 64) {
                    v := add(shr(255, s), 27)
                    s := shr(1, shl(1, s))
                }
                if iszero(lt(s, _HALF_N_PLUS_1)) {
                    v := xor(v, 7)
                    s := sub(N, s)
                }
                mstore(0x21, v)
                mstore(0x20, s)
                result := keccak256(0x00, 0x41)
                mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
                break
            }

            // If the length is neither 64 nor 65, return a uniquely corrupted hash.
            if iszero(lt(sub(l, 64), 2)) {
                // `bytes4(keccak256("InvalidSignatureLength"))`.
                result := xor(keccak256(add(signature, 0x20), l), 0xd62f1ab2)
            }
        }
    }

    /// @dev Returns the canonical hash of `signature`.
    function canonicalHashCalldata(bytes calldata signature)
        internal
        pure
        returns (bytes32 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            for {} 1 {} {
                mstore(0x00, calldataload(signature.offset)) // `r`.
                let s := calldataload(add(signature.offset, 0x20))
                let v := calldataload(add(signature.offset, 0x21))
                if eq(signature.length, 64) {
                    v := add(shr(255, s), 27)
                    s := shr(1, shl(1, s))
                }
                if iszero(lt(s, _HALF_N_PLUS_1)) {
                    v := xor(v, 7)
                    s := sub(N, s)
                }
                mstore(0x21, v)
                mstore(0x20, s)
                result := keccak256(0x00, 0x41)
                mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
                break
            }
            // If the length is neither 64 nor 65, return a uniquely corrupted hash.
            if iszero(lt(sub(signature.length, 64), 2)) {
                calldatacopy(mload(0x40), signature.offset, signature.length)
                // `bytes4(keccak256("InvalidSignatureLength"))`.
                result := xor(keccak256(mload(0x40), signature.length), 0xd62f1ab2)
            }
        }
    }

    /// @dev Returns the canonical hash of `signature`.
    function canonicalHash(bytes32 r, bytes32 vs) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, r) // `r`.
            let v := add(shr(255, vs), 27)
            let s := shr(1, shl(1, vs))
            mstore(0x21, v)
            mstore(0x20, s)
            result := keccak256(0x00, 0x41)
            mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /// @dev Returns the canonical hash of `signature`.
    function canonicalHash(uint8 v, bytes32 r, bytes32 s) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, r) // `r`.
            if iszero(lt(s, _HALF_N_PLUS_1)) {
                v := xor(v, 7)
                s := sub(N, s)
            }
            mstore(0x21, v)
            mstore(0x20, s)
            result := keccak256(0x00, 0x41)
            mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   EMPTY CALLDATA HELPERS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns an empty calldata bytes.
    function emptySignature() internal pure returns (bytes calldata signature) {
        /// @solidity memory-safe-assembly
        assembly {
            signature.length := 0
        }
    }
}

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

/// @notice Contract for EIP-712 typed structured data hashing and signing.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/EIP712.sol)
/// @author Modified from Solbase (https://github.com/Sol-DAO/solbase/blob/main/src/utils/EIP712.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/EIP712.sol)
///
/// @dev Note, this implementation:
/// - Uses `address(this)` for the `verifyingContract` field.
/// - Does NOT use the optional EIP-712 salt.
/// - Does NOT use any EIP-712 extensions.
/// This is for simplicity and to save gas.
/// If you need to customize, please fork / modify accordingly.
abstract contract EIP712 {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  CONSTANTS AND IMMUTABLES                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`.
    bytes32 internal constant _DOMAIN_TYPEHASH =
        0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;

    /// @dev `keccak256("EIP712Domain(string name,string version,address verifyingContract)")`.
    /// This is only used in `_hashTypedDataSansChainId`.
    bytes32 internal constant _DOMAIN_TYPEHASH_SANS_CHAIN_ID =
        0x91ab3d17e3a50a9d89e63fd30b92be7f5336b03b287bb946787a83a9d62a2766;

    /// @dev `keccak256("EIP712Domain(string name,string version)")`.
    /// This is only used in `_hashTypedDataSansChainIdAndVerifyingContract`.
    bytes32 internal constant _DOMAIN_TYPEHASH_SANS_CHAIN_ID_AND_VERIFYING_CONTRACT =
        0xb03948446334eb9b2196d5eb166f69b9d49403eb4a12f36de8d3f9f3cb8e15c3;

    /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId)")`.
    /// This is only used in `_hashTypedDataSansVerifyingContract`.
    bytes32 internal constant _DOMAIN_TYPEHASH_SANS_VERIFYING_CONTRACT =
        0xc2f8787176b8ac6bf7215b4adcc1e069bf4ab82d9ab1df05a57a91d425935b6e;

    uint256 private immutable _cachedThis;
    uint256 private immutable _cachedChainId;
    bytes32 private immutable _cachedNameHash;
    bytes32 private immutable _cachedVersionHash;
    bytes32 private immutable _cachedDomainSeparator;

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

    /// @dev Cache the hashes for cheaper runtime gas costs.
    /// In the case of upgradeable contracts (i.e. proxies),
    /// or if the chain id changes due to a hard fork,
    /// the domain separator will be seamlessly calculated on-the-fly.
    constructor() {
        _cachedThis = uint256(uint160(address(this)));
        _cachedChainId = block.chainid;

        string memory name;
        string memory version;
        if (!_domainNameAndVersionMayChange()) (name, version) = _domainNameAndVersion();
        bytes32 nameHash = _domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(name));
        bytes32 versionHash =
            _domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(version));
        _cachedNameHash = nameHash;
        _cachedVersionHash = versionHash;

        bytes32 separator;
        if (!_domainNameAndVersionMayChange()) {
            /// @solidity memory-safe-assembly
            assembly {
                let m := mload(0x40) // Load the free memory pointer.
                mstore(m, _DOMAIN_TYPEHASH)
                mstore(add(m, 0x20), nameHash)
                mstore(add(m, 0x40), versionHash)
                mstore(add(m, 0x60), chainid())
                mstore(add(m, 0x80), address())
                separator := keccak256(m, 0xa0)
            }
        }
        _cachedDomainSeparator = separator;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   FUNCTIONS TO OVERRIDE                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Please override this function to return the domain name and version.
    /// ```
    ///     function _domainNameAndVersion()
    ///         internal
    ///         pure
    ///         virtual
    ///         returns (string memory name, string memory version)
    ///     {
    ///         name = "Solady";
    ///         version = "1";
    ///     }
    /// ```
    ///
    /// Note: If the returned result may change after the contract has been deployed,
    /// you must override `_domainNameAndVersionMayChange()` to return true.
    function _domainNameAndVersion()
        internal
        view
        virtual
        returns (string memory name, string memory version);

    /// @dev Returns if `_domainNameAndVersion()` may change
    /// after the contract has been deployed (i.e. after the constructor).
    /// Default: false.
    function _domainNameAndVersionMayChange() internal pure virtual returns (bool result) {}

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     HASHING OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the EIP-712 domain separator.
    function _domainSeparator() internal view virtual returns (bytes32 separator) {
        if (_domainNameAndVersionMayChange()) {
            separator = _buildDomainSeparator();
        } else {
            separator = _cachedDomainSeparator;
            if (_cachedDomainSeparatorInvalidated()) separator = _buildDomainSeparator();
        }
    }

    /// @dev Returns the hash of the fully encoded EIP-712 message for this domain,
    /// given `structHash`, as defined in
    /// https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct.
    ///
    /// The hash can be used together with {ECDSA-recover} to obtain the signer of a message:
    /// ```
    ///     bytes32 digest = _hashTypedData(keccak256(abi.encode(
    ///         keccak256("Mail(address to,string contents)"),
    ///         mailTo,
    ///         keccak256(bytes(mailContents))
    ///     )));
    ///     address signer = ECDSA.recover(digest, signature);
    /// ```
    function _hashTypedData(bytes32 structHash) internal view virtual returns (bytes32 digest) {
        // We will use `digest` to store the domain separator to save a bit of gas.
        if (_domainNameAndVersionMayChange()) {
            digest = _buildDomainSeparator();
        } else {
            digest = _cachedDomainSeparator;
            if (_cachedDomainSeparatorInvalidated()) digest = _buildDomainSeparator();
        }
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the digest.
            mstore(0x00, 0x1901000000000000) // Store "\x19\x01".
            mstore(0x1a, digest) // Store the domain separator.
            mstore(0x3a, structHash) // Store the struct hash.
            digest := keccak256(0x18, 0x42)
            // Restore the part of the free memory slot that was overwritten.
            mstore(0x3a, 0)
        }
    }

    /// @dev Variant of `_hashTypedData` that excludes the chain ID.
    /// Included for the niche use case of cross-chain workflows.
    function _hashTypedDataSansChainId(bytes32 structHash)
        internal
        view
        virtual
        returns (bytes32 digest)
    {
        (string memory name, string memory version) = _domainNameAndVersion();
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Load the free memory pointer.
            mstore(0x00, _DOMAIN_TYPEHASH_SANS_CHAIN_ID)
            mstore(0x20, keccak256(add(name, 0x20), mload(name)))
            mstore(0x40, keccak256(add(version, 0x20), mload(version)))
            mstore(0x60, address())
            // Compute the digest.
            mstore(0x20, keccak256(0x00, 0x80)) // Store the domain separator.
            mstore(0x00, 0x1901) // Store "\x19\x01".
            mstore(0x40, structHash) // Store the struct hash.
            digest := keccak256(0x1e, 0x42)
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero pointer.
        }
    }

    /// @dev Variant of `_hashTypedData` that excludes the chain ID and verifying contract.
    /// Included for the niche use case of cross-chain and multi-verifier workflows.
    function _hashTypedDataSansChainIdAndVerifyingContract(bytes32 structHash)
        internal
        view
        virtual
        returns (bytes32 digest)
    {
        (string memory name, string memory version) = _domainNameAndVersion();
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Load the free memory pointer.
            mstore(0x00, _DOMAIN_TYPEHASH_SANS_CHAIN_ID_AND_VERIFYING_CONTRACT)
            mstore(0x20, keccak256(add(name, 0x20), mload(name)))
            mstore(0x40, keccak256(add(version, 0x20), mload(version)))
            // Compute the digest.
            mstore(0x20, keccak256(0x00, 0x60)) // Store the domain separator.
            mstore(0x00, 0x1901) // Store "\x19\x01".
            mstore(0x40, structHash) // Store the struct hash.
            digest := keccak256(0x1e, 0x42)
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero pointer.
        }
    }

    /// @dev Variant of `_hashTypedData` that excludes the chain ID and verifying contract.
    /// Included for the niche use case of multi-verifier workflows.
    function _hashTypedDataSansVerifyingContract(bytes32 structHash)
        internal
        view
        virtual
        returns (bytes32 digest)
    {
        (string memory name, string memory version) = _domainNameAndVersion();
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Load the free memory pointer.
            mstore(0x00, _DOMAIN_TYPEHASH_SANS_VERIFYING_CONTRACT)
            mstore(0x20, keccak256(add(name, 0x20), mload(name)))
            mstore(0x40, keccak256(add(version, 0x20), mload(version)))
            mstore(0x60, chainid())
            // Compute the digest.
            mstore(0x20, keccak256(0x00, 0x80)) // Store the domain separator.
            mstore(0x00, 0x1901) // Store "\x19\x01".
            mstore(0x40, structHash) // Store the struct hash.
            digest := keccak256(0x1e, 0x42)
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero pointer.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                    EIP-5267 OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev See: https://eips.ethereum.org/EIPS/eip-5267
    function eip712Domain()
        public
        view
        virtual
        returns (
            bytes1 fields,
            string memory name,
            string memory version,
            uint256 chainId,
            address verifyingContract,
            bytes32 salt,
            uint256[] memory extensions
        )
    {
        fields = hex"0f"; // `0b01111`.
        (name, version) = _domainNameAndVersion();
        chainId = block.chainid;
        verifyingContract = address(this);
        salt = salt; // `bytes32(0)`.
        extensions = extensions; // `new uint256[](0)`.
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      PRIVATE HELPERS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the EIP-712 domain separator.
    function _buildDomainSeparator() private view returns (bytes32 separator) {
        // We will use `separator` to store the name hash to save a bit of gas.
        bytes32 versionHash;
        if (_domainNameAndVersionMayChange()) {
            (string memory name, string memory version) = _domainNameAndVersion();
            separator = keccak256(bytes(name));
            versionHash = keccak256(bytes(version));
        } else {
            separator = _cachedNameHash;
            versionHash = _cachedVersionHash;
        }
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Load the free memory pointer.
            mstore(m, _DOMAIN_TYPEHASH)
            mstore(add(m, 0x20), separator) // Name hash.
            mstore(add(m, 0x40), versionHash)
            mstore(add(m, 0x60), chainid())
            mstore(add(m, 0x80), address())
            separator := keccak256(m, 0xa0)
        }
    }

    /// @dev Returns if the cached domain separator has been invalidated.
    function _cachedDomainSeparatorInvalidated() private view returns (bool result) {
        uint256 cachedChainId = _cachedChainId;
        uint256 cachedThis = _cachedThis;
        /// @solidity memory-safe-assembly
        assembly {
            result := iszero(and(eq(chainid(), cachedChainId), eq(address(), cachedThis)))
        }
    }
}

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

/// @notice Initializable mixin for the upgradeable contracts.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Initializable.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/proxy/utils/Initializable.sol)
abstract contract Initializable {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

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

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

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

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

    /// @dev `keccak256(bytes("Initialized(uint64)"))`.
    bytes32 private constant _INITIALIZED_EVENT_SIGNATURE =
        0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2;

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

    /// @dev The default initializable slot is given by:
    /// `bytes32(~uint256(uint32(bytes4(keccak256("_INITIALIZABLE_SLOT")))))`.
    ///
    /// Bits Layout:
    /// - [0]     `initializing`
    /// - [1..64] `initializedVersion`
    bytes32 private constant _INITIALIZABLE_SLOT =
        0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffbf601132;

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

    constructor() {
        // Construction time check to ensure that `_initializableSlot()` is not
        // overridden to zero. Will be optimized away if there is no revert.
        require(_initializableSlot() != bytes32(0));
    }

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

    /// @dev Override to return a non-zero custom storage slot if required.
    function _initializableSlot() internal pure virtual returns (bytes32) {
        return _INITIALIZABLE_SLOT;
    }

    /// @dev Guards an initializer function so that it can be invoked at most once.
    ///
    /// You can guard a function with `onlyInitializing` such that it can be called
    /// through a function guarded with `initializer`.
    ///
    /// This is similar to `reinitializer(1)`, except that in the context of a constructor,
    /// an `initializer` guarded function can be invoked multiple times.
    /// This can be useful during testing and is not expected to be used in production.
    ///
    /// Emits an {Initialized} event.
    modifier initializer() virtual {
        bytes32 s = _initializableSlot();
        /// @solidity memory-safe-assembly
        assembly {
            let i := sload(s)
            // Set `initializing` to 1, `initializedVersion` to 1.
            sstore(s, 3)
            // If `!(initializing == 0 && initializedVersion == 0)`.
            if i {
                // If `!(address(this).code.length == 0 && initializedVersion == 1)`.
                if iszero(lt(extcodesize(address()), eq(shr(1, i), 1))) {
                    mstore(0x00, 0xf92ee8a9) // `InvalidInitialization()`.
                    revert(0x1c, 0x04)
                }
                s := shl(shl(255, i), s) // Skip initializing if `initializing == 1`.
            }
        }
        _;
        /// @solidity memory-safe-assembly
        assembly {
            if s {
                // Set `initializing` to 0, `initializedVersion` to 1.
                sstore(s, 2)
                // Emit the {Initialized} event.
                mstore(0x20, 1)
                log1(0x20, 0x20, _INITIALIZED_EVENT_SIGNATURE)
            }
        }
    }

    /// @dev Guards a reinitializer function so that it can be invoked at most once.
    ///
    /// You can guard a function with `onlyInitializing` such that it can be called
    /// through a function guarded with `reinitializer`.
    ///
    /// Emits an {Initialized} event.
    modifier reinitializer(uint64 version) virtual {
        bytes32 s = _initializableSlot();
        /// @solidity memory-safe-assembly
        assembly {
            // Clean upper bits, and shift left by 1 to make space for the initializing bit.
            version := shl(1, and(version, 0xffffffffffffffff))
            let i := sload(s)
            // If `initializing == 1 || initializedVersion >= version`.
            if iszero(lt(and(i, 1), lt(i, version))) {
                mstore(0x00, 0xf92ee8a9) // `InvalidInitialization()`.
                revert(0x1c, 0x04)
            }
            // Set `initializing` to 1, `initializedVersion` to `version`.
            sstore(s, or(1, version))
        }
        _;
        /// @solidity memory-safe-assembly
        assembly {
            // Set `initializing` to 0, `initializedVersion` to `version`.
            sstore(s, version)
            // Emit the {Initialized} event.
            mstore(0x20, shr(1, version))
            log1(0x20, 0x20, _INITIALIZED_EVENT_SIGNATURE)
        }
    }

    /// @dev Guards a function such that it can only be called in the scope
    /// of a function guarded with `initializer` or `reinitializer`.
    modifier onlyInitializing() virtual {
        _checkInitializing();
        _;
    }

    /// @dev Reverts if the contract is not initializing.
    function _checkInitializing() internal view virtual {
        bytes32 s = _initializableSlot();
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(and(1, sload(s))) {
                mstore(0x00, 0xd7e6bcf8) // `NotInitializing()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Locks any future initializations by setting the initialized version to `2**64 - 1`.
    ///
    /// Calling this in the constructor will prevent the contract from being initialized
    /// or reinitialized. 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 called.
    function _disableInitializers() internal virtual {
        bytes32 s = _initializableSlot();
        /// @solidity memory-safe-assembly
        assembly {
            let i := sload(s)
            if and(i, 1) {
                mstore(0x00, 0xf92ee8a9) // `InvalidInitialization()`.
                revert(0x1c, 0x04)
            }
            let uint64max := 0xffffffffffffffff
            if iszero(eq(shr(1, i), uint64max)) {
                // Set `initializing` to 0, `initializedVersion` to `2**64 - 1`.
                sstore(s, shl(1, uint64max))
                // Emit the {Initialized} event.
                mstore(0x20, uint64max)
                log1(0x20, 0x20, _INITIALIZED_EVENT_SIGNATURE)
            }
        }
    }

    /// @dev Returns the highest version that has been initialized.
    function _getInitializedVersion() internal view virtual returns (uint64 version) {
        bytes32 s = _initializableSlot();
        /// @solidity memory-safe-assembly
        assembly {
            version := shr(1, sload(s))
        }
    }

    /// @dev Returns whether the contract is currently initializing.
    function _isInitializing() internal view virtual returns (bool result) {
        bytes32 s = _initializableSlot();
        /// @solidity memory-safe-assembly
        assembly {
            result := and(1, sload(s))
        }
    }
}

// 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();
        _;
    }
}

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

import {CallContextChecker} from "./CallContextChecker.sol";

/// @notice UUPS proxy mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/UUPSUpgradeable.sol)
/// @author Modified from OpenZeppelin
/// (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/utils/UUPSUpgradeable.sol)
///
/// @dev Note:
/// - This implementation is intended to be used with ERC1967 proxies.
/// See: `LibClone.deployERC1967` and related functions.
/// - This implementation is NOT compatible with legacy OpenZeppelin proxies
/// which do not store the implementation at `_ERC1967_IMPLEMENTATION_SLOT`.
abstract contract UUPSUpgradeable is CallContextChecker {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The upgrade failed.
    error UpgradeFailed();

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

    /// @dev Emitted when the proxy's implementation is upgraded.
    event Upgraded(address indexed implementation);

    /// @dev `keccak256(bytes("Upgraded(address)"))`.
    uint256 private constant _UPGRADED_EVENT_SIGNATURE =
        0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b;

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

    /// @dev The ERC-1967 storage slot for the implementation in the proxy.
    /// `uint256(keccak256("eip1967.proxy.implementation")) - 1`.
    bytes32 internal constant _ERC1967_IMPLEMENTATION_SLOT =
        0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      UUPS OPERATIONS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Please override this function to check if `msg.sender` is authorized
    /// to upgrade the proxy to `newImplementation`, reverting if not.
    /// ```
    ///     function _authorizeUpgrade(address) internal override onlyOwner {}
    /// ```
    function _authorizeUpgrade(address newImplementation) internal virtual;

    /// @dev Returns the storage slot used by the implementation,
    /// as specified in [ERC1822](https://eips.ethereum.org/EIPS/eip-1822).
    ///
    /// Note: The `notDelegated` modifier prevents accidental upgrades to
    /// an implementation that is a proxy contract.
    function proxiableUUID() public view virtual notDelegated returns (bytes32) {
        // This function must always return `_ERC1967_IMPLEMENTATION_SLOT` to comply with ERC1967.
        return _ERC1967_IMPLEMENTATION_SLOT;
    }

    /// @dev Upgrades the proxy's implementation to `newImplementation`.
    /// Emits a {Upgraded} event.
    ///
    /// Note: Passing in empty `data` skips the delegatecall to `newImplementation`.
    function upgradeToAndCall(address newImplementation, bytes calldata data)
        public
        payable
        virtual
        onlyProxy
    {
        _authorizeUpgrade(newImplementation);
        /// @solidity memory-safe-assembly
        assembly {
            newImplementation := shr(96, shl(96, newImplementation)) // Clears upper 96 bits.
            mstore(0x00, returndatasize())
            mstore(0x01, 0x52d1902d) // `proxiableUUID()`.
            let s := _ERC1967_IMPLEMENTATION_SLOT
            // Check if `newImplementation` implements `proxiableUUID` correctly.
            if iszero(eq(mload(staticcall(gas(), newImplementation, 0x1d, 0x04, 0x01, 0x20)), s)) {
                mstore(0x01, 0x55299b49) // `UpgradeFailed()`.
                revert(0x1d, 0x04)
            }
            // Emit the {Upgraded} event.
            log2(codesize(), 0x00, _UPGRADED_EVENT_SIGNATURE, newImplementation)
            sstore(s, newImplementation) // Updates the implementation.

            // Perform a delegatecall to `newImplementation` if `data` is non-empty.
            if data.length {
                // Forwards the `data` to `newImplementation` via delegatecall.
                let m := mload(0x40)
                calldatacopy(m, data.offset, data.length)
                if iszero(delegatecall(gas(), newImplementation, m, data.length, codesize(), 0x00))
                {
                    // Bubble up the revert if the call reverts.
                    returndatacopy(m, 0x00, returndatasize())
                    revert(m, returndatasize())
                }
            }
        }
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later

pragma solidity ^0.8.20;

interface IOrderBook {
    enum OrderBookType {
        NO_NATIVE,
        NATIVE_IN_BASE,
        NATIVE_IN_QUOTE
    }

    enum OrderStatus {
        ACTIVE,
        FILLED,
        CANCELED
    }

    enum MarketState {
        ACTIVE,
        SOFT_PAUSED,
        HARD_PAUSED
    }

    struct Order {
        address ownerAddress;
        uint96 size;
        uint40 prev;
        uint40 next;
        uint40 flippedId;
        uint32 price;
        uint32 flippedPrice;
        bool isBuy;
    }

    event MarketStateUpdated(MarketState previousState, MarketState newState);

    /**
     * @dev Emitted when a new order is created.
     * @param orderId Unique identifier for the newly created order.
     * @param owner Address of the user who created the order.
     * @param price Price point of the order in the specified precision.
     * @param size Size of the order in the specified precision.
     * @param isBuy Boolean indicating if the order is a buy (true) or sell (false) order.
     */
    event OrderCreated(uint40 orderId, address owner, uint96 size, uint32 price, bool isBuy);

    /**
     * @dev Emitted when a flip order is created
     * @param orderId Unique identifier for the newly created order.
     * @param flippedId Unique identifier for the flipped order.
     * @param owner Address of the user who created the order.
     * @param size Size of the order in the specified precision.
     * @param price Price point of the order in the specified precision.
     * @param flippedPrice Price point of the flipped order in the specified precision.
     * @param isBuy Boolean indicating if the order is a buy (true) or sell (false) order.
     */
    event FlipOrderCreated(
        uint40 orderId, uint40 flippedId, address owner, uint96 size, uint32 price, uint32 flippedPrice, bool isBuy
    );

    /**
     * @dev Emitted when a flip order is partially/completely filled and it results in a new order
     * @param orderId Unique identifier for the newly created order.
     * @param flippedId Unique identifier for the flipped order.
     * @param owner Address of the user who created the order.
     * @param size Size of the order in the specified precision.
     * @param price Price point of the order in the specified precision.
     * @param flippedPrice Price point of the flipped order in the specified precision.
     * @param isBuy Boolean indicating if the order is a buy (true) or sell (false) order.
     */
    event FlippedOrderCreated(
        uint40 orderId, uint40 flippedId, address owner, uint96 size, uint32 price, uint32 flippedPrice, bool isBuy
    );

    /**
     * @dev Emitted when a flip order is updated
     * @param orderId Unique identifier for the order.
     * @param size Size of the order in the specified precision.
     */
    event FlipOrderUpdated(uint40 orderId, uint96 size);

    /**
     * @dev Emitted when one or more flip orders are canceled
     * @param orderIds Array of order identifiers that were canceled.
     * @param owner Address of the user who canceled the orders.
     */
    event FlipOrdersCanceled(uint40[] orderIds, address owner);

    /**
     * @dev Emitted when one or more orders are completed or canceled.
     * @param orderId Array of order identifiers that were completed or canceled.
     */
    event OrdersCanceled(uint40[] orderId, address owner);

    /**
     * @dev Emitted for each cancel
     */
    event OrderCanceled(uint40 orderId, address owner, uint32 price, uint96 size, bool isBuy);

    /**
     * @dev Emitted when the vault params are updated
     * @param _vaultAskOrderSize Size of the vault ask order
     * @param _vaultAskPartiallyFilledSize Size of the vault ask partially filled order
     * @param _vaultBidOrderSize Size of the vault bid order
     * @param _vaultBidPartiallyFilledSize Size of the vault bid partially filled order
     * @param _askPrice The vault best ask price
     * @param _bidPrice The vault best bid price
     */
    event VaultParamsUpdated(
        uint96 _vaultAskOrderSize,
        uint96 _vaultAskPartiallyFilledSize,
        uint96 _vaultBidOrderSize,
        uint96 _vaultBidPartiallyFilledSize,
        uint256 _askPrice,
        uint256 _bidPrice
    );

    /**
     *
     * @dev Emitted when a trade goes through.
     * @param orderId Order Id of the order that was filled.
     * PS. All data regarding the original order can be found out from the order ID
     * @param updatedSize New size of the order
     * @param takerAddress Address of the taker.
     * @param filledSize Size taken by the taker.
     */
    event Trade(
        uint40 orderId,
        address makerAddress,
        bool isBuy,
        uint256 price,
        uint96 updatedSize,
        address takerAddress,
        address txOrigin,
        uint96 filledSize
    );

    function initialize(
        address _factory,
        OrderBookType _type,
        address _baseAssetAddress,
        uint256 _baseAssetDecimals,
        address _quoteAssetAddress,
        uint256 _quoteAssetDecimals,
        address _marginAccountAddress,
        uint96 _sizePrecision,
        uint32 _pricePrecision,
        uint32 _tickSize,
        uint96 _minSize,
        uint96 _maxSize,
        uint256 _takerFeeBps,
        uint256 _makerFeeBps,
        address _kuruAmmVault,
        uint96 _kuruAmmSpread,
        address __trustedForwarder
    ) external;

    function toggleMarket(MarketState _state) external;

    function addBuyOrder(uint32 _price, uint96 size, bool _postOnly) external;

    function addFlipBuyOrder(uint32 _price, uint32 _flippedPrice, uint96 _size, bool _provisionOrRevert) external;

    function addSellOrder(uint32 _price, uint96 size, bool _postOnly) external;

    function addFlipSellOrder(uint32 _price, uint32 _flippedPrice, uint96 _size, bool _provisionOrRevert) external;

    function batchCancelOrders(uint40[] calldata _orderIds) external;

    function batchCancelFlipOrders(uint40[] calldata _orderIds) external;

    function batchUpdate(
        uint32[] calldata buyPrices,
        uint96[] calldata buySizes,
        uint32[] calldata sellPrices,
        uint96[] calldata sellSizes,
        uint40[] calldata orderIdsToCancel,
        bool postOnly
    ) external;

    function placeAndExecuteMarketBuy(uint96 _quoteAmount, uint256 _minAmountOut, bool _isMargin, bool _isFillOrKill)
        external
        payable
        returns (uint256);

    function placeAndExecuteMarketSell(uint96 _size, uint256 _minAmountOut, bool _isMargin, bool _isFillOrKill)
        external
        payable
        returns (uint256);

    function bestBidAsk() external view returns (uint256, uint256);

    function updateVaultOrdSz(
        uint96 _vaultAskOrderSize,
        uint96 _vaultBidOrderSize,
        uint256 _askPrice,
        uint256 _bidPrice,
        bool _nullifyPartialFills
    ) external;

    function getMarketParams()
        external
        view
        returns (uint32, uint96, address, uint256, address, uint256, uint32, uint96, uint96, uint256, uint256);

    function getVaultParams()
        external
        view
        returns (address, uint256, uint96, uint256, uint96, uint96, uint96, uint96);

    function vaultAskOrderSize() external view returns (uint96);

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

//SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;

library OrderBookErrors {
    /**
     * @dev Thrown when a user is not the owner and tries to execute a privileged function
     */
    error Unauthorized();
    /**
     * @dev Thrown when a market is paused and a user tries to execute an action or if the owner passes an already existing market state for toggling
     */
    error MarketStateError();
    /**
     * @dev Thrown when maker fee passed to initializer is too high/invalid
     */
    error MarketFeeError();
    /**
     * @dev Thrown when minSize = 0 or maxSize < minSize
     */
    error MarketSizeError();
    /**
     * @dev Thrown when available size is rounded down to zero
     */
    error VaultSizeZero();
    /**
     * @dev Thrown when Kuru AMM Vault spread passed to initializer is too high or too low or is not a multiple of 10
     */
    error InvalidSpread();
    /**
     * @dev Thrown when the inputted price while adding an order is invalid
     */
    error PriceError();
    /**
     * @dev Thrown when vault initialization price crosses the book
     */
    error VaultInitializationPriceCrossesBook();
    /**
     * @dev Thrown when the size inputted is invalid, i.e, < minSize or > maxSize
     */
    error SizeError();
    /**
     * @dev Thrown when price is not divisible by tick
     */
    error TickSizeError();
    /**
     * @dev Thrown when a post only order gets filled
     */
    error PostOnlyError();
    /**
     * @dev Thrown when a flip order matches with a price
     */
    error ProvisionError();
    /**
     * @dev Thrown when a non-owner tries to execute a privileged function, i.e, if non owner tries to pause/unpause a market or
     * if a user tries to cancel an order that they did not place
     */
    error OnlyOwnerAllowedError();
    /**
     * @dev Thrown when wrong interface is called for a cancel
     */
    error WrongOrderTypeCancel();
    /**
     * @dev Thrown when cancelOrder is called on an order which is already filled or cancelled
     */
    error OrderAlreadyFilledOrCancelled();
    /**
     * @dev Thrown when length mismatch occurs between inputted arrays
     */
    error LengthMismatch();
    /**
     * @dev Thrown when msg.value is insufficient in market orders
     */
    error NativeAssetInsufficient();
    /**
     * @dev Thrown when msg.value is surplus in market orders
     */
    error NativeAssetSurplus();
    /**
     * @dev Thrown when msg.value is greater than 0 when native assets are not required
     */
    error NativeAssetNotRequired();
    /**
     * @dev Thrown when native asset transfer fails
     */
    error NativeAssetTransferFail();
    /**
     * @dev Thrown when IOC orders do not get filled by the market
     */
    error InsufficientLiquidity();
    /**
     * @dev Throws when slippage is exceeded in market orders
     */
    error SlippageExceeded();
    /**
     * @dev Thrown when quote size in uint32 overflows
     */
    error TooMuchSizeFilled();
    /**
     * @dev Thrown when safe transfer from fails
     */
    error TransferFromFailed();
    /**
     * @dev Thrown when the call is not made by the vault
     */
    error OnlyVaultAllowed();
    /**
     * @dev Thrown when safe cast to uint96 fails
     */
    error Uint96Overflow();
    /**
     * @dev Thrown when safe cast to uint32 fails
     */
    error Uint32Overflow();
}

library MarginAccountErrors {
    /**
     * @dev Thrown when a non-router tries to update markets
     */
    error OnlyRouterAllowed();
    /**
     * @dev Thrown when a non-verified market tries to execute a market action
     */
    error OnlyVerifiedMarketsAllowed();
    /**
     * @dev Thrown when a user has insufficient margin account balance
     */
    error InsufficientBalance();
    /**
     * @dev Thrown when native asset transfer fails
     */
    error NativeAssetTransferFail();
    /**
     * @dev Thrown when msg.value is not zero when native assets are not required
     */
    error NativeAssetMismatch();
    /**
     * @dev Thrown when zero address is passed as a parameter
     */
    error ZeroAddressNotAllowed();
    /**
     * @dev Thrown when protocol is paused
     */
    error ProtocolPaused();
    /**
     * @dev Thrown when protocol state is not changed
     */
    error ProtocolStateNotChanged();
    /**
     * @dev Thrown when fee collector is not set
     */
    error FeeCollectorNotChanged();
}

library RouterErrors {
    /**
     * @dev Thrown when zero address is passed as a parameter
     */
    error ZeroAddressNotAllowed();
    /**
     * @dev Thrown when base and quote asset addresses are the same
     */
    error BaseAndQuoteAssetSame();
    /**
     * @dev Thrown when market type given and token addresses are not compatible
     */
    error MarketTypeMismatch();
    /**
     * @dev Thrown when tick size is 0
     */
    error InvalidTickSize();
    /**
     * @dev Thrown when size precision is not a power of 10
     */
    error InvalidSizePrecision();
    /**
     * @dev Thrown when price precision is not a power of 10
     */
    error InvalidPricePrecision();
    /**
     * @dev Thrown when no markets are passed as input
     */
    error NoMarketsPassed();
    /**
     * @dev Thrown when the length of market addresses, isBuy, and nativeSend arrays are not the same
     */
    error LengthMismatch();
    /**
     * @dev Thrown when the market is invalid
     */
    error InvalidMarket();
    /**
     * @dev Thrown when the slippage exceeds the expected value
     */
    error SlippageExceeded();
    /**
     * @dev Thrown when the native asset transfer fails
     */
    error NativeAssetTransferFail();
    /**
     * @dev Thrown when safe cast to uint96 fails
     */
    error Uint96Overflow();
    /**
     * @dev Thrown when there is no change in the implementation
     */
    error ImplementationNotChanged();
}

library KuruAMMVaultErrors {
    /**
     * @dev Thrown when a user is not the owner and tries to execute a privileged function
     */
    error Unauthorized();
    /**
     * @dev Thrown when native token passed as argument and msg.value does not match
     */
    error NativeAssetMismatch();
    /**
     * @dev Thrown when first deposit ask price is 0
     */
    error AskPriceZero();
    /**
     * @dev Thrown when vault sizes are 0
     */
    error InvalidVaultSize();
    /**
     * @dev Thrown when amount of quote tokens passed is insufficient
     */
    error InsufficientQuoteToken();
    /**
     * @dev Thrown when insufficient liquidity is minted
     */
    error InsufficientLiquidityMinted();
    /**
     * @dev Thrown when balance of owner is too less
     */
    error InsufficientBalance();
    /**
     * @dev Thrown when quote used in a deposit is too low
     */
    error InsufficientQuoteUsed();
    /**
     * @dev Thrown when native asset transfer fails
     */
    error NativeAssetTransferFail();
    /**
     * @dev Thrown when new size exceeds partially filled size
     */
    error NewSizeExceedsPartiallyFilledSize();
    /**
     * @dev Thrown when the amounts withdrawn are negative
     */
    error NegativeAmountWithdrawn();
    /**
     * @dev Thrown when safe cast to uint96 fails
     */
    error Uint96Overflow();
}

library KuruForwarderErrors {
    /**
     * @dev Thrown when zero address is passed as a parameter
     */
    error ZeroAddressNotAllowed();
    /**
     * @dev Thrown when the signature does not match the request
     */
    error SignatureMismatch();
    /**
     * @dev Thrown when the interface is not allowed
     */
    error InterfaceNotAllowed();
    /**
     * @dev Thrown when the execution fails
     */
    error ExecutionFailed();
    /**
     * @dev Thrown when the nonce is already used
     */
    error NonceAlreadyUsed();
    /**
     * @dev Thrown when the value is insufficient
     */
    error InsufficientValue();
}

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

/// @notice Call context checker mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/CallContextChecker.sol)
contract CallContextChecker {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The call is from an unauthorized call context.
    error UnauthorizedCallContext();

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

    /// @dev For checking if the context is a delegate call.
    ///
    /// Note: To enable use cases with an immutable default implementation in the bytecode,
    /// (see: ERC6551Proxy), we don't require that the proxy address must match the
    /// value stored in the implementation slot, which may not be initialized.
    uint256 private immutable __self = uint256(uint160(address(this)));

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                    CALL CONTEXT CHECKS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // A proxy call can be either via a `delegatecall` to an implementation,
    // or a 7702 call on an authority that points to a delegation.

    /// @dev Returns whether the current call context is on a EIP7702 authority
    /// (i.e. externally owned account).
    function _onEIP7702Authority() internal view virtual returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            extcodecopy(address(), 0x00, 0x00, 0x20)
            // Note: Checking that it starts with hex"ef01" is the most general and futureproof.
            // 7702 bytecode is `abi.encodePacked(hex"ef01", uint8(version), address(delegation))`.
            result := eq(0xef01, shr(240, mload(0x00)))
        }
    }

    /// @dev Returns the implementation of this contract.
    function _selfImplementation() internal view virtual returns (address) {
        return address(uint160(__self));
    }

    /// @dev Returns whether the current call context is on the implementation itself.
    function _onImplementation() internal view virtual returns (bool) {
        return __self == uint160(address(this));
    }

    /// @dev Requires that the current call context is performed via a EIP7702 authority.
    function _checkOnlyEIP7702Authority() internal view virtual {
        if (!_onEIP7702Authority()) _revertUnauthorizedCallContext();
    }

    /// @dev Requires that the current call context is performed via a proxy.
    function _checkOnlyProxy() internal view virtual {
        if (_onImplementation()) _revertUnauthorizedCallContext();
    }

    /// @dev Requires that the current call context is NOT performed via a proxy.
    /// This is the opposite of `checkOnlyProxy`.
    function _checkNotDelegated() internal view virtual {
        if (!_onImplementation()) _revertUnauthorizedCallContext();
    }

    /// @dev Requires that the current call context is performed via a EIP7702 authority.
    modifier onlyEIP7702Authority() virtual {
        _checkOnlyEIP7702Authority();
        _;
    }

    /// @dev Requires that the current call context is performed via a proxy.
    modifier onlyProxy() virtual {
        _checkOnlyProxy();
        _;
    }

    /// @dev Requires that the current call context is NOT performed via a proxy.
    /// This is the opposite of `onlyProxy`.
    modifier notDelegated() virtual {
        _checkNotDelegated();
        _;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      PRIVATE HELPERS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    function _revertUnauthorizedCallContext() private pure {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x9f03a026) // `UnauthorizedCallContext()`.
            revert(0x1c, 0x04)
        }
    }
}

Settings
{
  "remappings": [
    "forge-std/=lib/forge-std/src/",
    "@openzeppelin/=lib/openzeppelin-contracts/",
    "@chainlink/=node_modules/@chainlink/",
    "@eth-optimism/=node_modules/@eth-optimism/",
    "ds-test/=lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "eth-gas-reporter/=node_modules/eth-gas-reporter/",
    "halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
    "hardhat/=node_modules/hardhat/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin-solidity/=node_modules/openzeppelin-solidity/",
    "solady/=node_modules/solady/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 1000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "prague",
  "viaIR": true
}

Contract Security Audit

Contract ABI

API
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"DeadlineExpired","type":"error"},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"}],"name":"ExecutionFailed","type":"error"},{"inputs":[],"name":"InsufficientValue","type":"error"},{"inputs":[],"name":"InterfaceNotAllowed","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"NonceAlreadyUsed","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[{"internalType":"uint256","name":"_currentPrice","type":"uint256"},{"internalType":"uint256","name":"_breakpointPrice","type":"uint256"}],"name":"PriceDependentRequestFailed","type":"error"},{"inputs":[],"name":"SignatureMismatch","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"UnauthorizedCallContext","type":"error"},{"inputs":[],"name":"UpgradeFailed","type":"error"},{"inputs":[],"name":"ZeroAddressNotAllowed","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","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":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"inputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"name":"allowedInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"internalType":"struct KuruForwarder.CancelPriceDependentRequest","name":"req","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"cancelPriceDependent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"eip712Domain","outputs":[{"internalType":"bytes1","name":"fields","type":"bytes1"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"verifyingContract","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256[]","name":"extensions","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"market","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes4","name":"selector","type":"bytes4"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct KuruForwarder.ForwardRequest","name":"req","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"execute","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"marginAccount","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes4","name":"selector","type":"bytes4"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct KuruForwarder.MarginAccountRequest","name":"req","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"executeMarginAccountRequest","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"market","type":"address"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bool","name":"isBelowPrice","type":"bool"},{"internalType":"bytes4","name":"selector","type":"bytes4"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct KuruForwarder.PriceDependentRequest","name":"req","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"executePriceDependent","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"executedPriceDependentRequest","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"}],"name":"getNonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"bytes4[]","name":"_allowedInterfaces","type":"bytes4[]"}],"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":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4[]","name":"_allowedInterfaces","type":"bytes4[]"}],"name":"removeAllowedInterfaces","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes4[]","name":"_allowedInterfaces","type":"bytes4[]"}],"name":"setAllowedInterfaces","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"market","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes4","name":"selector","type":"bytes4"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct KuruForwarder.ForwardRequest","name":"req","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"verify","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"internalType":"struct KuruForwarder.CancelPriceDependentRequest","name":"req","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"verifyCancelPriceDependent","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"marginAccount","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes4","name":"selector","type":"bytes4"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct KuruForwarder.MarginAccountRequest","name":"req","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"verifyMarginAccountRequest","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"market","type":"address"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bool","name":"isBelowPrice","type":"bool"},{"internalType":"bytes4","name":"selector","type":"bytes4"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct KuruForwarder.PriceDependentRequest","name":"req","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"verifyPriceDependent","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]

610140604052346100d657306080524660a0525f60c0525f60e0525f61010052306101205263409feecd1954600181166100c95760011c6002600160401b031901610084575b60405161168290816100db823960805181505060a05181505060c05181505060e051815050610100518150506101205181818161097e0152610a120152f35b6002600160411b0363409feecd19556001600160401b0360209081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29080a1610045565b63f92ee8a95f526004601cfd5b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c806310012a1514610c9b5780631691267114610c6c5780631a4e971f14610bee5780631fbe2e4f14610bae5780632569296214610b655780632d0335ab14610b2d5780633bcb61ef14610b14578063487ed96e14610aff5780634f1ef286146109d957806352d1902d1461096b57806354d1f13d146109275780635b31c2e6146108b15780636b446f49146108985780636cce95f0146107285780636e0849051461066a578063715018a61461060157806384b0196e146105475780638da5cb5b1461051d578063950b2e5114610504578063df477d5014610204578063e6bc6b56146101e1578063f04e283e14610194578063f2fde38b146101575763fee81cf414610121575f80fd5b346101535760203660031901126101535761013a610e26565b63389a75e1600c525f52602080600c2054604051908152f35b5f80fd5b60203660031901126101535761016b610e26565b610173611469565b8060601b156101875761018590611595565b005b637448fbae5f526004601cfd5b6020366003190112610153576101a8610e26565b6101b0611469565b63389a75e1600c52805f526020600c2090815442116101d4575f6101859255611595565b636f5e88185f526004601cfd5b346101535760206101fa6101f436610e3c565b91611398565b6040519015158152f35b61021961021036610eb0565b9082939261118e565b156104dc576060810135908134106104b45760e08101916001600160e01b031961024284611081565b165f525f60205260ff60405f2054161561048c5761025f82610f29565b60405160609190911b6bffffffffffffffffffffffff19166020820190815260808401356034830152906102a081605481015b03601f198101835282610d0f565b5190205f52600160205260405f20600160ff1982541617905560208201600460406001600160a01b036102d284610f29565b168151928380927fb4de8b700000000000000000000000000000000000000000000000000000000082525afa908115610481575f9161044f575b5060c0840161031a8161114b565b908161043f575b811561041a575b50156103e6575f8086868661036561037661034b6103458a610f29565b95611081565b9361029261035d610100830183611096565b949092610f29565b6040519485936020850198896110c9565b51925af161038261111c565b90156103a4576103a090604051918291602083526020830190610e8c565b0390f35b6103e2906040519182917f15fcd675000000000000000000000000000000000000000000000000000000008352602060048401526024830190610e8c565b0390fd5b836040917feaac67f1000000000000000000000000000000000000000000000000000000005f52600452013560245260445ffd5b610424915061114b565b1580610431575b86610328565b50806040850135101561042b565b9050816040860135111590610321565b90506040813d604011610479575b8161046a60409383610d0f565b8101031261015357518561030c565b3d915061045d565b6040513d5f823e3d90fd5b7fd02b530d000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f11011294000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f73a8ee18000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101535760206101fa61051736610e3c565b91611286565b34610153575f366003190112610153576020638b78c6d819546001600160a01b0360405191168152f35b34610153575f366003190112610153576105a26105b06105656115d9565b6040929192519384937f0f00000000000000000000000000000000000000000000000000000000000000855260e0602086015260e0850190610e8c565b908382036040850152610e8c565b4660608301523060808301525f60a083015281810360c083015260206060519182815201906080905f5b8181106105e8575050500390f35b82518452859450602093840193909201916001016105da565b5f36600319011261015357610614611469565b5f638b78c6d819547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a37f8000000000000000000000000000000000000000000000000000000000000000638b78c6d81955005b61067f61067636610e3c565b90829392611398565b156104dc576040810135908134106104b45760a081016001600160e01b03196106a782611081565b165f525f60205260ff60405f2054161561048c5760608201359160018301809311610714575f9384936001600160a01b036106e184610f29565b1685526002602052604085205561036561037661070361034560208601610f29565b9361029261035d60c0830183611096565b634e487b7160e01b5f52601160045260245ffd5b3461015357604036600319011261015357610741610e26565b60243567ffffffffffffffff811161015357610761903690600401610d45565b9063409feecd199081548060038455610867575b506001600160a01b0316801561083f57638b78c6d819546108325780638b78c6d819555f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a35f5b82518110156107fa57806001600160e01b03196107de60019386610f01565b51165f525f60205260405f208260ff19825416179055016107bf565b508061080257005b6002905560016020527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602080a1005b630dc149f05f526004601cfd5b7f8579befe000000000000000000000000000000000000000000000000000000005f5260045ffd5b9091600182811c14303b101561088b576001600160a01b039160ff1b1b9190610775565b63f92ee8a95f526004601cfd5b346101535760206101fa6108ab36610eb0565b9161118e565b346101535760203660031901126101535760043567ffffffffffffffff8111610153576108e2903690600401610d45565b6108ea611469565b5f5b815181101561018557806001600160e01b031961090b60019385610f01565b51165f525f60205260405f208260ff19825416179055016108ec565b5f3660031901126101535763389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f80a2005b34610153575f36600319011261015357307f0000000000000000000000000000000000000000000000000000000000000000036109cc5760206040517f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8152f35b639f03a0265f526004601cfd5b6040366003190112610153576109ed610e26565b60243567ffffffffffffffff811161015357610a0d903690600401610dbd565b9091307f0000000000000000000000000000000000000000000000000000000000000000146109cc576001600160a01b0390610a47611469565b163d5f526352d1902d6001527f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc602060016004601d855afa5103610af157807fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a281817f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55610ad457005b815f926040519485378338925af415610ae957005b3d5f823e3d90fd5b6355299b496001526004601dfd5b61067f610b0b36610e3c565b90829392611286565b346101535760206101fa610b2736610deb565b91610f3d565b34610153576020366003190112610153576001600160a01b03610b4e610e26565b165f526002602052602060405f2054604051908152f35b5f3660031901126101535763389a75e1600c52335f526202a30042016020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f80a2005b34610153576020366003190112610153576004356001600160e01b03198116809103610153575f525f602052602060ff60405f2054166040519015158152f35b3461015357610c08610bff36610deb565b90829392610f3d565b156104dc57610292610c4f610c1c83610f29565b6040519283916020808401960135908690916034926bffffffffffffffffffffffff199060601b16825260148201520190565b5190205f52600160205260405f20600160ff198254161790555f80f35b34610153576020366003190112610153576004355f526001602052602060ff60405f2054166040519015158152f35b346101535760203660031901126101535760043567ffffffffffffffff811161015357610ccc903690600401610d45565b610cd4611469565b5f5b815181101561018557806001600160e01b0319610cf560019385610f01565b51165f525f60205260405f2060ff19815416905501610cd6565b90601f8019910116810190811067ffffffffffffffff821117610d3157604052565b634e487b7160e01b5f52604160045260245ffd5b81601f820112156101535780359167ffffffffffffffff8311610d31578260051b9160405193610d786020850186610d0f565b845260208085019382010191821161015357602001915b818310610d9c5750505090565b82356001600160e01b03198116810361015357815260209283019201610d8f565b9181601f840112156101535782359167ffffffffffffffff8311610153576020838186019501011161015357565b9060031982016080811261015357606013610153576004916064359067ffffffffffffffff821161015357610e2291600401610dbd565b9091565b600435906001600160a01b038216820361015357565b9060406003198301126101535760043567ffffffffffffffff81116101535760e0818403600319011261015357600401916024359067ffffffffffffffff821161015357610e2291600401610dbd565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b9060406003198301126101535760043567ffffffffffffffff811161015357610120818403600319011261015357600401916024359067ffffffffffffffff821161015357610e2291600401610dbd565b8051821015610f155760209160051b010190565b634e487b7160e01b5f52603260045260245ffd5b356001600160a01b03811681036101535790565b9060408201359283421161105957610fc291610fbd610f5b85610f29565b602086013596604051906001600160a01b0360208301937f3ec94f0b764242014d547a29ab20da6056912fc1b658b4ad364cd66f407a80058552166040830152886060830152608082015260808152610fb560a082610d0f565b519020611485565b6114fa565b91610292611002610fd284610f29565b9260405192839160208301958690916034926bffffffffffffffffffffffff199060601b16825260148201520190565b5190205f52600160205260ff60405f205416611031576001600160a01b0361102a8192610f29565b1691161490565b7f1fb09b80000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f1ab7da6b000000000000000000000000000000000000000000000000000000005f5260045ffd5b356001600160e01b0319811681036101535790565b903590601e1981360301821215610153570180359067ffffffffffffffff82116101535760200191813603831361015357565b909280926001600160e01b031960189695168352600483013701906bffffffffffffffffffffffff199060601b1660048201520190565b67ffffffffffffffff8111610d3157601f01601f191660200190565b3d15611146573d9061112d82611100565b9161113b6040519384610d0f565b82523d5f602084013e565b606090565b3580151581036101535790565b92919261116482611100565b916111726040519384610d0f565b829481845281830111610153578281602093845f960137010152565b9060a08201359283421161105957610fc291610fbd6111ac85610f29565b6111b860208701610f29565b6080870135976001600160e01b03196111d360c08a0161114b565b6111df60e08b01611081565b906111f86111f16101008d018d611096565b3691611158565b60208151910120936001600160a01b03604051968160208901997f1122e3187221cfd9d40ac63fa267c39b342d8710972523d6cbf343f43c0322f78b5216604089015216606087015260408c0135608087015260608c013560a08701528c60c087015260e08601521515610100850152166101208301526101408201526101408152610fb561016082610d0f565b91906080830135918242116110595761135791610fbd6112a586610f29565b6112b160208801610f29565b6060880135966001600160e01b03196112cc60a08b01611081565b6112dc6111f160c08d018d611096565b60208151910120926001600160a01b03604051958160208801987f199c879f0d8420ea1ff045984f2ac5541f91073952f48791833c79f1bea4534e8a5216604088015216606086015260408c013560808601528a60a086015260c08501521660e08301526101008201526101008152610fb561012082610d0f565b906001600160a01b0361136984610f29565b165f52600260205260405f20541115918261138357505090565b6001600160a01b0391925061102a8291610f29565b91906080830135918242116110595761135791610fbd6113b786610f29565b6113c360208801610f29565b6060880135966001600160e01b03196113de60a08b01611081565b6113ee6111f160c08d018d611096565b60208151910120926001600160a01b03604051958160208801987f4a5f28af10183842f321d76044fae00f1697f0bc9a8eae077fe67857cc5d64048a5216604088015216606086015260408c013560808601528a60a086015260c08501521660e08301526101008201526101008152610fb561012082610d0f565b638b78c6d81954330361147857565b6382b429005f526004601cfd5b60a061148f6115d9565b90602081519101209060208151910120604051917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f835260208301526040820152466060820152306080820152206719010000000000005f52601a52603a526042601820905f603a52565b9092919260405193806040146115555760411461152357505050505b638baa579f5f526004601cfd5b806040809201355f1a60205281375b5f526020600160805f825afa51915f6060526040523d611553575050611516565b565b508060207f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff92013590601b8260ff1c016020523560405216606052611532565b6001600160a01b031680638b78c6d819547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3801560ff1b17638b78c6d81955565b60409081516115e88382610d0f565b600d81527f4b757275466f727761726465720000000000000000000000000000000000000060208201529161161f81519182610d0f565b600581527f312e302e3000000000000000000000000000000000000000000000000000000060208201529056fea264697066735822122021914d377a3c44f9ba6399b129149d33b1ae319d823a7985b703b09e46e2b23864736f6c634300081e0033

Deployed Bytecode

0x60806040526004361015610011575f80fd5b5f3560e01c806310012a1514610c9b5780631691267114610c6c5780631a4e971f14610bee5780631fbe2e4f14610bae5780632569296214610b655780632d0335ab14610b2d5780633bcb61ef14610b14578063487ed96e14610aff5780634f1ef286146109d957806352d1902d1461096b57806354d1f13d146109275780635b31c2e6146108b15780636b446f49146108985780636cce95f0146107285780636e0849051461066a578063715018a61461060157806384b0196e146105475780638da5cb5b1461051d578063950b2e5114610504578063df477d5014610204578063e6bc6b56146101e1578063f04e283e14610194578063f2fde38b146101575763fee81cf414610121575f80fd5b346101535760203660031901126101535761013a610e26565b63389a75e1600c525f52602080600c2054604051908152f35b5f80fd5b60203660031901126101535761016b610e26565b610173611469565b8060601b156101875761018590611595565b005b637448fbae5f526004601cfd5b6020366003190112610153576101a8610e26565b6101b0611469565b63389a75e1600c52805f526020600c2090815442116101d4575f6101859255611595565b636f5e88185f526004601cfd5b346101535760206101fa6101f436610e3c565b91611398565b6040519015158152f35b61021961021036610eb0565b9082939261118e565b156104dc576060810135908134106104b45760e08101916001600160e01b031961024284611081565b165f525f60205260ff60405f2054161561048c5761025f82610f29565b60405160609190911b6bffffffffffffffffffffffff19166020820190815260808401356034830152906102a081605481015b03601f198101835282610d0f565b5190205f52600160205260405f20600160ff1982541617905560208201600460406001600160a01b036102d284610f29565b168151928380927fb4de8b700000000000000000000000000000000000000000000000000000000082525afa908115610481575f9161044f575b5060c0840161031a8161114b565b908161043f575b811561041a575b50156103e6575f8086868661036561037661034b6103458a610f29565b95611081565b9361029261035d610100830183611096565b949092610f29565b6040519485936020850198896110c9565b51925af161038261111c565b90156103a4576103a090604051918291602083526020830190610e8c565b0390f35b6103e2906040519182917f15fcd675000000000000000000000000000000000000000000000000000000008352602060048401526024830190610e8c565b0390fd5b836040917feaac67f1000000000000000000000000000000000000000000000000000000005f52600452013560245260445ffd5b610424915061114b565b1580610431575b86610328565b50806040850135101561042b565b9050816040860135111590610321565b90506040813d604011610479575b8161046a60409383610d0f565b8101031261015357518561030c565b3d915061045d565b6040513d5f823e3d90fd5b7fd02b530d000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f11011294000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f73a8ee18000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101535760206101fa61051736610e3c565b91611286565b34610153575f366003190112610153576020638b78c6d819546001600160a01b0360405191168152f35b34610153575f366003190112610153576105a26105b06105656115d9565b6040929192519384937f0f00000000000000000000000000000000000000000000000000000000000000855260e0602086015260e0850190610e8c565b908382036040850152610e8c565b4660608301523060808301525f60a083015281810360c083015260206060519182815201906080905f5b8181106105e8575050500390f35b82518452859450602093840193909201916001016105da565b5f36600319011261015357610614611469565b5f638b78c6d819547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a37f8000000000000000000000000000000000000000000000000000000000000000638b78c6d81955005b61067f61067636610e3c565b90829392611398565b156104dc576040810135908134106104b45760a081016001600160e01b03196106a782611081565b165f525f60205260ff60405f2054161561048c5760608201359160018301809311610714575f9384936001600160a01b036106e184610f29565b1685526002602052604085205561036561037661070361034560208601610f29565b9361029261035d60c0830183611096565b634e487b7160e01b5f52601160045260245ffd5b3461015357604036600319011261015357610741610e26565b60243567ffffffffffffffff811161015357610761903690600401610d45565b9063409feecd199081548060038455610867575b506001600160a01b0316801561083f57638b78c6d819546108325780638b78c6d819555f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a35f5b82518110156107fa57806001600160e01b03196107de60019386610f01565b51165f525f60205260405f208260ff19825416179055016107bf565b508061080257005b6002905560016020527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602080a1005b630dc149f05f526004601cfd5b7f8579befe000000000000000000000000000000000000000000000000000000005f5260045ffd5b9091600182811c14303b101561088b576001600160a01b039160ff1b1b9190610775565b63f92ee8a95f526004601cfd5b346101535760206101fa6108ab36610eb0565b9161118e565b346101535760203660031901126101535760043567ffffffffffffffff8111610153576108e2903690600401610d45565b6108ea611469565b5f5b815181101561018557806001600160e01b031961090b60019385610f01565b51165f525f60205260405f208260ff19825416179055016108ec565b5f3660031901126101535763389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f80a2005b34610153575f36600319011261015357307f000000000000000000000000bf6cc109c6ebca4b28e3e51fd8798294599cfe2a036109cc5760206040517f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8152f35b639f03a0265f526004601cfd5b6040366003190112610153576109ed610e26565b60243567ffffffffffffffff811161015357610a0d903690600401610dbd565b9091307f000000000000000000000000bf6cc109c6ebca4b28e3e51fd8798294599cfe2a146109cc576001600160a01b0390610a47611469565b163d5f526352d1902d6001527f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc602060016004601d855afa5103610af157807fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a281817f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55610ad457005b815f926040519485378338925af415610ae957005b3d5f823e3d90fd5b6355299b496001526004601dfd5b61067f610b0b36610e3c565b90829392611286565b346101535760206101fa610b2736610deb565b91610f3d565b34610153576020366003190112610153576001600160a01b03610b4e610e26565b165f526002602052602060405f2054604051908152f35b5f3660031901126101535763389a75e1600c52335f526202a30042016020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f80a2005b34610153576020366003190112610153576004356001600160e01b03198116809103610153575f525f602052602060ff60405f2054166040519015158152f35b3461015357610c08610bff36610deb565b90829392610f3d565b156104dc57610292610c4f610c1c83610f29565b6040519283916020808401960135908690916034926bffffffffffffffffffffffff199060601b16825260148201520190565b5190205f52600160205260405f20600160ff198254161790555f80f35b34610153576020366003190112610153576004355f526001602052602060ff60405f2054166040519015158152f35b346101535760203660031901126101535760043567ffffffffffffffff811161015357610ccc903690600401610d45565b610cd4611469565b5f5b815181101561018557806001600160e01b0319610cf560019385610f01565b51165f525f60205260405f2060ff19815416905501610cd6565b90601f8019910116810190811067ffffffffffffffff821117610d3157604052565b634e487b7160e01b5f52604160045260245ffd5b81601f820112156101535780359167ffffffffffffffff8311610d31578260051b9160405193610d786020850186610d0f565b845260208085019382010191821161015357602001915b818310610d9c5750505090565b82356001600160e01b03198116810361015357815260209283019201610d8f565b9181601f840112156101535782359167ffffffffffffffff8311610153576020838186019501011161015357565b9060031982016080811261015357606013610153576004916064359067ffffffffffffffff821161015357610e2291600401610dbd565b9091565b600435906001600160a01b038216820361015357565b9060406003198301126101535760043567ffffffffffffffff81116101535760e0818403600319011261015357600401916024359067ffffffffffffffff821161015357610e2291600401610dbd565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b9060406003198301126101535760043567ffffffffffffffff811161015357610120818403600319011261015357600401916024359067ffffffffffffffff821161015357610e2291600401610dbd565b8051821015610f155760209160051b010190565b634e487b7160e01b5f52603260045260245ffd5b356001600160a01b03811681036101535790565b9060408201359283421161105957610fc291610fbd610f5b85610f29565b602086013596604051906001600160a01b0360208301937f3ec94f0b764242014d547a29ab20da6056912fc1b658b4ad364cd66f407a80058552166040830152886060830152608082015260808152610fb560a082610d0f565b519020611485565b6114fa565b91610292611002610fd284610f29565b9260405192839160208301958690916034926bffffffffffffffffffffffff199060601b16825260148201520190565b5190205f52600160205260ff60405f205416611031576001600160a01b0361102a8192610f29565b1691161490565b7f1fb09b80000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f1ab7da6b000000000000000000000000000000000000000000000000000000005f5260045ffd5b356001600160e01b0319811681036101535790565b903590601e1981360301821215610153570180359067ffffffffffffffff82116101535760200191813603831361015357565b909280926001600160e01b031960189695168352600483013701906bffffffffffffffffffffffff199060601b1660048201520190565b67ffffffffffffffff8111610d3157601f01601f191660200190565b3d15611146573d9061112d82611100565b9161113b6040519384610d0f565b82523d5f602084013e565b606090565b3580151581036101535790565b92919261116482611100565b916111726040519384610d0f565b829481845281830111610153578281602093845f960137010152565b9060a08201359283421161105957610fc291610fbd6111ac85610f29565b6111b860208701610f29565b6080870135976001600160e01b03196111d360c08a0161114b565b6111df60e08b01611081565b906111f86111f16101008d018d611096565b3691611158565b60208151910120936001600160a01b03604051968160208901997f1122e3187221cfd9d40ac63fa267c39b342d8710972523d6cbf343f43c0322f78b5216604089015216606087015260408c0135608087015260608c013560a08701528c60c087015260e08601521515610100850152166101208301526101408201526101408152610fb561016082610d0f565b91906080830135918242116110595761135791610fbd6112a586610f29565b6112b160208801610f29565b6060880135966001600160e01b03196112cc60a08b01611081565b6112dc6111f160c08d018d611096565b60208151910120926001600160a01b03604051958160208801987f199c879f0d8420ea1ff045984f2ac5541f91073952f48791833c79f1bea4534e8a5216604088015216606086015260408c013560808601528a60a086015260c08501521660e08301526101008201526101008152610fb561012082610d0f565b906001600160a01b0361136984610f29565b165f52600260205260405f20541115918261138357505090565b6001600160a01b0391925061102a8291610f29565b91906080830135918242116110595761135791610fbd6113b786610f29565b6113c360208801610f29565b6060880135966001600160e01b03196113de60a08b01611081565b6113ee6111f160c08d018d611096565b60208151910120926001600160a01b03604051958160208801987f4a5f28af10183842f321d76044fae00f1697f0bc9a8eae077fe67857cc5d64048a5216604088015216606086015260408c013560808601528a60a086015260c08501521660e08301526101008201526101008152610fb561012082610d0f565b638b78c6d81954330361147857565b6382b429005f526004601cfd5b60a061148f6115d9565b90602081519101209060208151910120604051917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f835260208301526040820152466060820152306080820152206719010000000000005f52601a52603a526042601820905f603a52565b9092919260405193806040146115555760411461152357505050505b638baa579f5f526004601cfd5b806040809201355f1a60205281375b5f526020600160805f825afa51915f6060526040523d611553575050611516565b565b508060207f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff92013590601b8260ff1c016020523560405216606052611532565b6001600160a01b031680638b78c6d819547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3801560ff1b17638b78c6d81955565b60409081516115e88382610d0f565b600d81527f4b757275466f727761726465720000000000000000000000000000000000000060208201529161161f81519182610d0f565b600581527f312e302e3000000000000000000000000000000000000000000000000000000060208201529056fea264697066735822122021914d377a3c44f9ba6399b129149d33b1ae319d823a7985b703b09e46e2b23864736f6c634300081e0033

Block Transaction Gas Used Reward
view all blocks produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits

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.