ETH Price: $3,205.25 (-9.03%)

Contract

0x4E2eBa30a786c0643699b92234d74a71e958C08E
 

Overview

ETH Balance

0.054 ETH

Eth Value

$173.08 (@ $3,205.25/ETH)

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Prove Withdrawal231912792025-08-21 18:34:3584 days ago1755801275IN
0x4E2eBa30...1e958C08E
0 ETH0.000564272.33782494
Initiate Deposit231233472025-08-12 7:02:4794 days ago1754982167IN
0x4E2eBa30...1e958C08E
0.05 ETH0.000031060.48302725
Initiate Deposit231196442025-08-11 18:37:4794 days ago1754937467IN
0x4E2eBa30...1e958C08E
0.001 ETH0.000182262.83421429
Initiate Deposit231195702025-08-11 18:22:5994 days ago1754936579IN
0x4E2eBa30...1e958C08E
0.002 ETH0.000194243.02051226
Transfer230845442025-08-06 20:54:5999 days ago1754513699IN
0x4E2eBa30...1e958C08E
0.001 ETH0.000183132.27545262
Renounce Ownersh...230844642025-08-06 20:38:4799 days ago1754512727IN
0x4E2eBa30...1e958C08E
0 ETH0.00000720.3100529
Set L2Bridge230844622025-08-06 20:38:2399 days ago1754512703IN
0x4E2eBa30...1e958C08E
0 ETH0.000015880.34520479

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
L1Bridge

Compiler Version
v0.8.24+commit.e11b9ed9

Optimization Enabled:
Yes with 200 runs

Other Settings:
cancun EvmVersion
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol";
import {Types} from "src/libraries/Types.sol";
import {Hashing} from "src/libraries/Hashing.sol";
import {SecureMerkleTrie} from "src/libraries/trie/SecureMerkleTrie.sol";
import {LibFacet} from "facet-sol/src/utils/LibFacet.sol";
import {SafeTransferLib} from "solady/src/utils/SafeTransferLib.sol";
import {L2Bridge} from "src/L2Bridge.sol";
import {Rollup} from "src/Rollup.sol";
import {EOA} from "src/facet-libraries/EOA.sol";

/**
 * @title L1Bridge
 * @notice L1 ETH bridge that accepts deposits and uses ZK proofs to verify withdrawal claims.
 * 
 * @dev CRITICAL ARCHITECTURE NOTICE FOR USERS:
 * 
 * This bridge verifies withdrawals using ZK proofs from a Rollup contract. Unlike bridges that
 * rely on upgradeable proof systems, each Rollup contract here is immutable - hardcoded to prove
 * one specific state transition function forever.
 * 
 * KEY IMPLICATIONS:
 * 
 * 1. FORK HANDLING: When the L2 network upgrades, this bridge won't automatically recognize the
 *    new rules. The bridge owner must call setRollup() to point to a new Rollup contract that
 *    proves the updated state transition function.
 * 
 * 2. OWNERSHIP TRADE-OFFS:
 *    - With active owner: Can adapt to forks but requires trusting the owner won't set a 
 *      malicious rollup contract
 *    - With renounced ownership: Becomes trustless* with respect to human operators,
 *      but permanently locked to a single fork's rules (*Security depends solely on the ZK proof 
 *      system and smart contract correctness)
 * 
 * 3. TRUST MODELS: Users can choose between:
 *    - Active ownership: Trust a human operator to handle forks properly (more flexible)
 *    - Renounced ownership: Trust only the code and ZK proofs (more secure but less flexible)
 * 
 * 4. FORK INCOMPATIBILITY: If the L2 network forks and modifies how bridged assets work, but
 *    this bridge isn't updated to point to a new Rollup contract, those modifications won't be
 *    reflected in withdrawal capabilities. Your assets remain subject to the original rules.
 * 
 * RECOMMENDATION: Before depositing, verify:
 * - Current owner status: check owner() - address(0) means renounced
 * - If owned: research the owner's reputation and track record
 * - Which Rollup contract is currently being used (check rollup() function)
 * - Your preference: human-free operation vs flexibility for upgrades
 */
contract L1Bridge is Ownable, ReentrancyGuard, Pausable {
    using SafeTransferLib for address;
    
    /*//////////////////////////////////////////////////////////////
                                STRUCTS
    //////////////////////////////////////////////////////////////*/
    
    /**
     * @notice Represents a deposit transaction that can be replayed if FACET blocks are full
     * @dev This struct enables deposit retry functionality when L2 blocks hit gas limits
     * @param nonce Unique global nonce for the deposit, used for replay verification
     * @param to Address that will receive the deposit on L2
     * @param amount Amount of ETH being deposited (in wei)
     */
    struct DepositTransaction {
        uint256 nonce;
        address to;
        uint256 amount;
    }

    /*//////////////////////////////////////////////////////////////
                            CUSTOM ERRORS
    //////////////////////////////////////////////////////////////*/

    error L2BridgeNotSet();
    error WithdrawalAlreadyProven();
    error WithdrawalAlreadyFinalized();
    error ProposalNotCanonical();
    error InvalidOutputRoot();
    error InvalidWithdrawalProof();
    error WithdrawalNotProven();
    error InvalidDepositAmount();
    error L2BridgeAlreadySet();
    error RootBlacklisted();
    error WithdrawalDelayNotMet();
    error InvalidDepositParameters();
    error InvalidNonce();
    error OnlyCanDepositWithoutTo();

    /*//////////////////////////////////////////////////////////////
                                CONFIG
    //////////////////////////////////////////////////////////////*/

    Rollup public rollup;
    address public l2Bridge;
    
    // Training wheels
    mapping(bytes32 => bool) public rootBlacklisted;
    uint256 public withdrawalDelay; // seconds

    /*//////////////////////////////////////////////////////////////
                               STORAGE
    //////////////////////////////////////////////////////////////*/

    struct ProvenWithdrawal {
        uint32 proposalId;
        uint32 provenAt;
    }

    mapping(bytes32 => mapping(Rollup => ProvenWithdrawal)) public proven;
    mapping(bytes32 => bool) public finalized;
    
    /**
     * @notice Stores hashes of deposit parameters for replay verification
     * @dev Maps nonce => keccak256(nonce, to, amount) to ensure replays use identical parameters
     */
    mapping(uint256 => bytes32) public depositHashes;
    
    /**
     * @notice Global nonce counter for deposits
     * @dev Incremented for each new deposit to ensure uniqueness
     */
    uint256 public depositNonce;

    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event DepositInitiated(uint256 indexed nonce, address indexed from, address indexed to, uint256 amount);
    event DepositReplayed(uint256 indexed nonce, address indexed to, uint256 amount);
    event WithdrawalProven(address indexed rollup, address indexed to, uint256 amount, uint256 nonce, uint256 proposalId);
    event WithdrawalFinalized(address indexed to, uint256 amount, uint256 nonce);
    event RollupUpdated(address indexed oldRollup, address indexed newRollup);
    event RootBlacklistStatusChanged(bytes32 indexed root, bool blacklisted);
    event WithdrawalDelayUpdated(uint256 oldDelay, uint256 newDelay);

    /*//////////////////////////////////////////////////////////////
                               CONSTANTS
    //////////////////////////////////////////////////////////////*/

    // storage key slot used by the L2ToL1MessagePasser contract
    bytes32 internal constant MESSAGE_PASSER_SLOT = bytes32(uint256(0));
    
    // Gas limit for FACET transactions
    uint256 internal constant DEPOSIT_GAS_LIMIT = 500_000;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(Rollup _rollup) {
        rollup = _rollup;
    }

    function setL2Bridge(address _l2Bridge) external onlyOwner {
        if (l2Bridge != address(0)) revert L2BridgeAlreadySet();

        l2Bridge = _l2Bridge;
    }
    
    /*//////////////////////////////////////////////////////////////
                           TRAINING WHEELS
    //////////////////////////////////////////////////////////////*/
    
    /**
     * @notice Update the rollup contract reference to support new forks or state transition rules.
     * @dev CRITICAL: This function enables fork flexibility but also represents the primary
     *      trust assumption. If ownership is renounced, this function becomes inaccessible,
     *      making the bridge trustless (no human control) but locked to the current state transition rules.
     * @param _rollup New rollup contract address that proves the updated state transition rules
     */
    function setRollup(address _rollup) external onlyOwner {
        address oldRollup = address(rollup);
        rollup = Rollup(_rollup);
        emit RollupUpdated(oldRollup, _rollup);
    }
    
    /**
     * @notice Pause the bridge
     */
    function pause() external onlyOwner {
        _pause();
    }
    
    /**
     * @notice Unpause the bridge
     */
    function unpause() external onlyOwner {
        _unpause();
    }
    
    /**
     * @notice Blacklist or unblacklist a root
     * @param root The root to blacklist/unblacklist
     * @param blacklisted True to blacklist, false to unblacklist
     */
    function setRootBlacklisted(bytes32 root, bool blacklisted) external onlyOwner {
        rootBlacklisted[root] = blacklisted;
        emit RootBlacklistStatusChanged(root, blacklisted);
    }
    
    /**
     * @notice Update withdrawal delay period
     * @param _withdrawalDelay New delay in seconds
     */
    function setWithdrawalDelay(uint256 _withdrawalDelay) external onlyOwner {
        uint256 oldDelay = withdrawalDelay;
        withdrawalDelay = _withdrawalDelay;
        emit WithdrawalDelayUpdated(oldDelay, _withdrawalDelay);
    }

    /*//////////////////////////////////////////////////////////////
                                  DEPOSIT
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Initiates a new deposit to L2 that can be replayed if FACET block is full
     * @dev Stores a hash of deposit parameters to enable safe replay with identical parameters.
     *      This is crucial for FACET integration where blocks may be full due to gas limits.
     * @param to Address that will receive the deposit on L2
     * @return nonce Unique nonce for this deposit that must be used for any replay attempts
     */
    function initiateDeposit(address to) public payable virtual whenNotPaused returns (uint256 nonce) {
        if (l2Bridge == address(0)) revert L2BridgeNotSet();
        if (msg.value == 0) revert InvalidDepositAmount();
        
        // Increment nonce and store deposit hash
        nonce = ++depositNonce;
        
        // Create deposit transaction struct
        DepositTransaction memory deposit = DepositTransaction({
            nonce: nonce,
            to: to,
            amount: msg.value
        });
        
        // Store hash of deposit for replay verification
        depositHashes[nonce] = _hashDeposit(deposit);
        
        // Send the deposit message to L2
        _sendDepositToL2(deposit);
        
        emit DepositInitiated(nonce, msg.sender, to, msg.value);
    }

    /**
     * @notice Replays a previously initiated deposit with identical parameters
     * @dev Used when the initial deposit attempt failed due to FACET block being full.
     *      Verifies that the deposit parameters match exactly what was originally stored.
     *      This function does NOT require msg.value as it uses the originally deposited ETH.
     * @param deposit The deposit transaction to replay (must match original parameters exactly)
     */
    function replayDeposit(
        DepositTransaction calldata deposit
    ) external virtual whenNotPaused {
        // Verify nonce exists and parameters match
        bytes32 storedHash = depositHashes[deposit.nonce];
        bytes32 paramsHash = _hashDeposit(deposit);
        
        if (storedHash != paramsHash) revert InvalidDepositParameters();
        
        // Send the same deposit message to L2
        _sendDepositToL2(deposit);
        
        emit DepositReplayed(deposit.nonce, deposit.to, deposit.amount);
    }
    
    /**
     * @notice Internal function to send deposit message to L2
     * @dev Uses LibFacet to send cross-chain message to FACET L2
     * @param deposit The deposit transaction to send to L2
     */
    function _sendDepositToL2(
        DepositTransaction memory deposit
    ) internal {
        bytes memory data = abi.encodeWithSelector(
            L2Bridge.finalizeDeposit.selector,
            deposit
        );
        
        LibFacet.sendFacetTransaction({to: l2Bridge, gasLimit: DEPOSIT_GAS_LIMIT, data: data});
    }

    /**
     * @notice Allows EOAs to deposit ETH by sending it directly to the bridge
     * @dev Restricted to EOAs only to prevent aliasing issues with contract addresses.
     *      When an EOA sends ETH directly, it's deposited to their own address on L2.
     *      Includes EIP-7702 delegated EOA support.
     */
    receive() external payable {
        if (!EOA.isSenderEOA()) revert OnlyCanDepositWithoutTo();
        
        // For EOAs depositing via receive, deposit to themselves
        initiateDeposit(msg.sender);
    }

    /*//////////////////////////////////////////////////////////////
                             WITHDRAWAL – PROVE
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Prove a withdrawal by verifying merkle proof against canonical L2 state
     * @dev This verification is bound to the current rollup contract's state transition rules.
     *      If the L2 forks but this bridge's rollup reference isn't updated, withdrawals
     *      must still conform to the original state transition function's rules.
     * @param amount Amount of tokens to withdraw
     * @param to Recipient address on L1
     * @param nonce Withdrawal nonce from L2
     * @param proposalId The canonical proposal ID from current Rollup contract
     * @param rootProof The merkle proof components from the L2 output root
     * @param withdrawalProof Merkle proof path in the L2ToL1MessagePasser storage trie
     */
    function proveWithdrawal(
        address to,
        uint256 amount,
        uint256 nonce,
        uint256 proposalId,
        Types.OutputRootProof calldata rootProof,
        bytes[] calldata withdrawalProof
    ) external virtual whenNotPaused {
        bytes32 withdrawalHash = _hashWithdrawal(to, amount, nonce);

        ProvenWithdrawal storage info = proven[withdrawalHash][rollup];

        if (info.provenAt != 0) revert WithdrawalAlreadyProven();
        if (finalized[withdrawalHash]) revert WithdrawalAlreadyFinalized();
        if (!rollup.proposalIsCanonical(proposalId)) revert ProposalNotCanonical();

        Rollup.Proposal memory prop = rollup.getProposal(proposalId);
        
        // Check if root is blacklisted
        if (rootBlacklisted[prop.rootClaim]) revert RootBlacklisted();

        if (prop.rootClaim != Hashing.hashOutputRootProof(rootProof)) revert InvalidOutputRoot();

        // verify inclusion of message in L2 storage
        bytes32 storageKey = keccak256(abi.encode(withdrawalHash, uint256(0))); // slot 0
        bool valid = SecureMerkleTrie.verifyInclusionProof({
            _key: abi.encode(storageKey),
            _value: hex"01", // value of 1 indicates withdrawal exists
            _proof: withdrawalProof,
            _root: rootProof.messagePasserStorageRoot
        });
        if (!valid) revert InvalidWithdrawalProof();

        proven[withdrawalHash][rollup] = ProvenWithdrawal({
            proposalId: uint32(proposalId),
            provenAt: uint32(block.timestamp)
        });

        emit WithdrawalProven(address(rollup), to, amount, nonce, proposalId);
    }

    /*//////////////////////////////////////////////////////////////
                            WITHDRAWAL – FINALIZE
    //////////////////////////////////////////////////////////////*/

    function finalizeWithdrawal(address to, uint256 amount, uint256 nonce) external nonReentrant whenNotPaused {
        bytes32 withdrawalHash = _hashWithdrawal(to, amount, nonce);

        ProvenWithdrawal storage info = proven[withdrawalHash][rollup];

        if (info.provenAt == 0) revert WithdrawalNotProven();
        if (finalized[withdrawalHash]) revert WithdrawalAlreadyFinalized();
        
        // Respect safety delay
        if (block.timestamp <= info.provenAt + withdrawalDelay) revert WithdrawalDelayNotMet();
        
        // Check if the root of the proposal used for proving is blacklisted
        Rollup.Proposal memory prop = rollup.getProposal(info.proposalId);
        if (rootBlacklisted[prop.rootClaim]) revert RootBlacklisted();

        finalized[withdrawalHash] = true;

        to.forceSafeTransferETH(amount, SafeTransferLib.GAS_STIPEND_NO_STORAGE_WRITES);

        emit WithdrawalFinalized(to, amount, nonce);
    }

    /*//////////////////////////////////////////////////////////////
                               HELPERS
    //////////////////////////////////////////////////////////////*/

    function _hashWithdrawal(address to, uint256 amount, uint256 nonce) internal view returns (bytes32) {
        bytes memory data = abi.encode(to, amount);
        Types.WithdrawalTransaction memory w = Types.WithdrawalTransaction({
            nonce: nonce,
            sender: l2Bridge,
            target: address(this),
            value: 0,
            gasLimit: 0,
            data: data
        });
        return Hashing.hashWithdrawal(w);
    }
    
    /**
     * @notice Internal function to hash a deposit transaction for replay verification
     * @dev Creates a unique hash from deposit parameters to ensure replay safety
     * @param deposit The deposit transaction to hash
     * @return Hash of the deposit transaction (keccak256 of encoded parameters)
     */
    function _hashDeposit(DepositTransaction memory deposit) internal pure returns (bytes32) {
        return keccak256(abi.encode(deposit.nonce, deposit.to, deposit.amount));
    }
}

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

pragma solidity ^0.8.0;

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

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

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

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}

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

pragma solidity ^0.8.0;

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

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

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

    bool private _paused;

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

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

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

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

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        require(!paused(), "Pausable: paused");
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        require(paused(), "Pausable: not paused");
    }

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

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

File 5 of 26 : Types.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @title Types
/// @notice Contains various types used throughout the Optimism contract system.
library Types {
    /// @notice OutputProposal represents a commitment to the L2 state. The timestamp is the L1
    ///         timestamp that the output root is posted. This timestamp is used to verify that the
    ///         finalization period has passed since the output root was submitted.
    /// @custom:field outputRoot    Hash of the L2 output.
    /// @custom:field timestamp     Timestamp of the L1 block that the output root was submitted in.
    /// @custom:field l2BlockNumber L2 block number that the output corresponds to.
    struct OutputProposal {
        bytes32 outputRoot;
        uint128 timestamp;
        uint128 l2BlockNumber;
    }

    /// @notice Struct representing the elements that are hashed together to generate an output root
    ///         which itself represents a snapshot of the L2 state.
    /// @custom:field version                  Version of the output root.
    /// @custom:field stateRoot                Root of the state trie at the block of this output.
    /// @custom:field messagePasserStorageRoot Root of the message passer storage trie.
    /// @custom:field latestBlockhash          Hash of the block this output was generated from.
    struct OutputRootProof {
        bytes32 version;
        bytes32 stateRoot;
        bytes32 messagePasserStorageRoot;
        bytes32 latestBlockhash;
    }

    /// @notice Struct representing a deposit transaction (L1 => L2 transaction) created by an end
    ///         user (as opposed to a system deposit transaction generated by the system).
    /// @custom:field from        Address of the sender of the transaction.
    /// @custom:field to          Address of the recipient of the transaction.
    /// @custom:field isCreation  True if the transaction is a contract creation.
    /// @custom:field value       Value to send to the recipient.
    /// @custom:field mint        Amount of ETH to mint.
    /// @custom:field gasLimit    Gas limit of the transaction.
    /// @custom:field data        Data of the transaction.
    /// @custom:field l1BlockHash Hash of the block the transaction was submitted in.
    /// @custom:field logIndex    Index of the log in the block the transaction was submitted in.
    struct UserDepositTransaction {
        address from;
        address to;
        bool isCreation;
        uint256 value;
        uint256 mint;
        uint64 gasLimit;
        bytes data;
        bytes32 l1BlockHash;
        uint256 logIndex;
    }

    /// @notice Struct representing a withdrawal transaction.
    /// @custom:field nonce    Nonce of the withdrawal transaction
    /// @custom:field sender   Address of the sender of the transaction.
    /// @custom:field target   Address of the recipient of the transaction.
    /// @custom:field value    Value to send to the recipient.
    /// @custom:field gasLimit Gas limit of the transaction.
    /// @custom:field data     Data of the transaction.
    struct WithdrawalTransaction {
        uint256 nonce;
        address sender;
        address target;
        uint256 value;
        uint256 gasLimit;
        bytes data;
    }

    /// @notice Enum representing where the FeeVault withdraws funds to.
    /// @custom:value L1 FeeVault withdraws funds to L1.
    /// @custom:value L2 FeeVault withdraws funds to L2.
    enum WithdrawalNetwork {
        L1,
        L2
    }
}

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

// Libraries
import { Types } from "src/libraries/Types.sol";
import { Encoding } from "src/libraries/Encoding.sol";

/// @title Hashing
/// @notice Hashing handles Optimism's various different hashing schemes.
library Hashing {
    /// @notice Computes the hash of the RLP encoded L2 transaction that would be generated when a
    ///         given deposit is sent to the L2 system. Useful for searching for a deposit in the L2
    ///         system.
    /// @param _tx User deposit transaction to hash.
    /// @return Hash of the RLP encoded L2 deposit transaction.
    function hashDepositTransaction(Types.UserDepositTransaction memory _tx) internal pure returns (bytes32) {
        return keccak256(Encoding.encodeDepositTransaction(_tx));
    }

    /// @notice Computes the deposit transaction's "source hash", a value that guarantees the hash
    ///         of the L2 transaction that corresponds to a deposit is unique and is
    ///         deterministically generated from L1 transaction data.
    /// @param _l1BlockHash Hash of the L1 block where the deposit was included.
    /// @param _logIndex    The index of the log that created the deposit transaction.
    /// @return Hash of the deposit transaction's "source hash".
    function hashDepositSource(bytes32 _l1BlockHash, uint256 _logIndex) internal pure returns (bytes32) {
        bytes32 depositId = keccak256(abi.encode(_l1BlockHash, _logIndex));
        return keccak256(abi.encode(bytes32(0), depositId));
    }

    /// @notice Hashes the cross domain message based on the version that is encoded into the
    ///         message nonce.
    /// @param _nonce    Message nonce with version encoded into the first two bytes.
    /// @param _sender   Address of the sender of the message.
    /// @param _target   Address of the target of the message.
    /// @param _value    ETH value to send to the target.
    /// @param _gasLimit Gas limit to use for the message.
    /// @param _data     Data to send with the message.
    /// @return Hashed cross domain message.
    function hashCrossDomainMessage(
        uint256 _nonce,
        address _sender,
        address _target,
        uint256 _value,
        uint256 _gasLimit,
        bytes memory _data
    )
        internal
        pure
        returns (bytes32)
    {
        (, uint16 version) = Encoding.decodeVersionedNonce(_nonce);
        if (version == 0) {
            return hashCrossDomainMessageV0(_target, _sender, _data, _nonce);
        } else if (version == 1) {
            return hashCrossDomainMessageV1(_nonce, _sender, _target, _value, _gasLimit, _data);
        } else {
            revert("Hashing: unknown cross domain message version");
        }
    }

    /// @notice Hashes a cross domain message based on the V0 (legacy) encoding.
    /// @param _target Address of the target of the message.
    /// @param _sender Address of the sender of the message.
    /// @param _data   Data to send with the message.
    /// @param _nonce  Message nonce.
    /// @return Hashed cross domain message.
    function hashCrossDomainMessageV0(
        address _target,
        address _sender,
        bytes memory _data,
        uint256 _nonce
    )
        internal
        pure
        returns (bytes32)
    {
        return keccak256(Encoding.encodeCrossDomainMessageV0(_target, _sender, _data, _nonce));
    }

    /// @notice Hashes a cross domain message based on the V1 (current) encoding.
    /// @param _nonce    Message nonce.
    /// @param _sender   Address of the sender of the message.
    /// @param _target   Address of the target of the message.
    /// @param _value    ETH value to send to the target.
    /// @param _gasLimit Gas limit to use for the message.
    /// @param _data     Data to send with the message.
    /// @return Hashed cross domain message.
    function hashCrossDomainMessageV1(
        uint256 _nonce,
        address _sender,
        address _target,
        uint256 _value,
        uint256 _gasLimit,
        bytes memory _data
    )
        internal
        pure
        returns (bytes32)
    {
        return keccak256(Encoding.encodeCrossDomainMessageV1(_nonce, _sender, _target, _value, _gasLimit, _data));
    }

    /// @notice Derives the withdrawal hash according to the encoding in the L2 Withdrawer contract
    /// @param _tx Withdrawal transaction to hash.
    /// @return Hashed withdrawal transaction.
    function hashWithdrawal(Types.WithdrawalTransaction memory _tx) internal pure returns (bytes32) {
        return keccak256(abi.encode(_tx.nonce, _tx.sender, _tx.target, _tx.value, _tx.gasLimit, _tx.data));
    }

    /// @notice Hashes the various elements of an output root proof into an output root hash which
    ///         can be used to check if the proof is valid.
    /// @param _outputRootProof Output root proof which should hash to an output root.
    /// @return Hashed output root proof.
    function hashOutputRootProof(Types.OutputRootProof memory _outputRootProof) internal pure returns (bytes32) {
        return keccak256(
            abi.encode(
                _outputRootProof.version,
                _outputRootProof.stateRoot,
                _outputRootProof.messagePasserStorageRoot,
                _outputRootProof.latestBlockhash
            )
        );
    }

    /// @notice Generates a unique hash for cross l2 messages. This hash is used to identify
    ///         the message and ensure it is not relayed more than once.
    /// @param _destination Chain ID of the destination chain.
    /// @param _source Chain ID of the source chain.
    /// @param _nonce Unique nonce associated with the message to prevent replay attacks.
    /// @param _sender Address of the user who originally sent the message.
    /// @param _target Address of the contract or wallet that the message is targeting on the destination chain.
    /// @param _message The message payload to be relayed to the target on the destination chain.
    /// @return Hash of the encoded message parameters, used to uniquely identify the message.
    function hashL2toL2CrossDomainMessage(
        uint256 _destination,
        uint256 _source,
        uint256 _nonce,
        address _sender,
        address _target,
        bytes memory _message
    )
        internal
        pure
        returns (bytes32)
    {
        return keccak256(abi.encode(_destination, _source, _nonce, _sender, _target, _message));
    }
}

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

// Libraries
import { MerkleTrie } from "src/libraries/trie/MerkleTrie.sol";

/// @title SecureMerkleTrie
/// @notice SecureMerkleTrie is a thin wrapper around the MerkleTrie library that hashes the input
///         keys. Ethereum's state trie hashes input keys before storing them.
library SecureMerkleTrie {
    /// @notice Verifies a proof that a given key/value pair is present in the Merkle trie.
    /// @param _key   Key of the node to search for, as a hex string.
    /// @param _value Value of the node to search for, as a hex string.
    /// @param _proof Merkle trie inclusion proof for the desired node. Unlike traditional Merkle
    ///               trees, this proof is executed top-down and consists of a list of RLP-encoded
    ///               nodes that make a path down to the target node.
    /// @param _root  Known root of the Merkle trie. Used to verify that the included proof is
    ///               correctly constructed.
    /// @return valid_ Whether or not the proof is valid.
    function verifyInclusionProof(
        bytes memory _key,
        bytes memory _value,
        bytes[] memory _proof,
        bytes32 _root
    )
        internal
        pure
        returns (bool valid_)
    {
        bytes memory key = _getSecureKey(_key);
        valid_ = MerkleTrie.verifyInclusionProof(key, _value, _proof, _root);
    }

    /// @notice Retrieves the value associated with a given key.
    /// @param _key   Key to search for, as hex bytes.
    /// @param _proof Merkle trie inclusion proof for the key.
    /// @param _root  Known root of the Merkle trie.
    /// @return value_ Value of the key if it exists.
    function get(bytes memory _key, bytes[] memory _proof, bytes32 _root) internal pure returns (bytes memory value_) {
        bytes memory key = _getSecureKey(_key);
        value_ = MerkleTrie.get(key, _proof, _root);
    }

    /// @notice Computes the hashed version of the input key.
    /// @param _key Key to hash.
    /// @return hash_ Hashed version of the key.
    function _getSecureKey(bytes memory _key) private pure returns (bytes memory hash_) {
        hash_ = abi.encodePacked(keccak256(_key));
    }
}

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

import { LibRLP } from "../../lib/solady/src/utils/LibRLP.sol";

library LibFacet {
    using LibRLP for LibRLP.List;

    address constant facetInboxAddress = 0x00000000000000000000000000000000000FacE7;
    bytes32 constant facetEventSignature = 0x00000000000000000000000000000000000000000000000000000000000face7;
    uint8 constant facetTxType = 0x46;

    function sendFacetTransaction(
        uint256 gasLimit,
        bytes memory data
    ) internal {
        sendFacetTransaction({
            to: bytes(''),
            value: 0,
            gasLimit: gasLimit,
            data: data,
            mineBoost: bytes('')
        });
    }

    function sendFacetTransaction(
        address to,
        uint256 gasLimit,
        bytes memory data
    ) internal {
        sendFacetTransaction({
            to: abi.encodePacked(to),
            value: 0,
            gasLimit: gasLimit,
            data: data,
            mineBoost: bytes('')
        });
    }

    function prepareFacetTransaction(
        uint256 chainId,
        bytes memory to,
        uint256 value,
        uint256 gasLimit,
        bytes memory data,
        bytes memory mineBoost
    ) internal pure returns (bytes memory) {
        LibRLP.List memory list;

        list.p(chainId);
        list.p(to);
        list.p(value);
        list.p(gasLimit);
        list.p(data);
        list.p(mineBoost);
        return abi.encodePacked(facetTxType, list.encode());
    }

    function prepareFacetTransaction(
        bytes memory to,
        uint256 value,
        uint256 gasLimit,
        bytes memory data,
        bytes memory mineBoost
    ) internal view returns (bytes memory) {
        uint256 chainId;

        if (block.chainid == 1) {
            chainId = 0xface7;
        } else if (block.chainid == 11155111) {
            chainId = 0xface7a;
        } else {
            revert("Unsupported chainId");
        }

        return prepareFacetTransaction({
            chainId: chainId,
            to: to,
            value: value,
            gasLimit: gasLimit,
            data: data,
            mineBoost: mineBoost
        });
    }

    function sendFacetTransaction(
        bytes memory to,
        uint256 value,
        uint256 gasLimit,
        bytes memory data,
        bytes memory mineBoost
    ) internal {
        bytes memory payload = prepareFacetTransaction({
            to: to,
            value: value,
            gasLimit: gasLimit,
            data: data,
            mineBoost: mineBoost
        });

        assembly {
            log1(add(payload, 32), mload(payload), facetEventSignature)
        }
    }
}

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

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
///
/// @dev Note:
/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection.
/// - For ERC20s, this implementation won't check that a token has code,
///   responsibility is delegated to the caller.
library SafeTransferLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ETH transfer has failed.
    error ETHTransferFailed();

    /// @dev The ERC20 `transferFrom` has failed.
    error TransferFromFailed();

    /// @dev The ERC20 `transfer` has failed.
    error TransferFailed();

    /// @dev The ERC20 `approve` has failed.
    error ApproveFailed();

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

    /// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes.
    uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300;

    /// @dev Suggested gas stipend for contract receiving ETH to perform a few
    /// storage reads and writes, but low enough to prevent griefing.
    uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;

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

    // If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
    //
    // The regular variants:
    // - Forwards all remaining gas to the target.
    // - Reverts if the target reverts.
    // - Reverts if the current contract has insufficient balance.
    //
    // The force variants:
    // - Forwards with an optional gas stipend
    //   (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
    // - If the target reverts, or if the gas stipend is exhausted,
    //   creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
    //   Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
    // - Reverts if the current contract has insufficient balance.
    //
    // The try variants:
    // - Forwards with a mandatory gas stipend.
    // - Instead of reverting, returns whether the transfer succeeded.

    /// @dev Sends `amount` (in wei) ETH to `to`.
    function safeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`.
    function safeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // Transfer all the ETH and check if it succeeded or not.
            if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function forceSafeTransferAllETH(address to, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // forgefmt: disable-next-item
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function trySafeTransferAllETH(address to, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)
        }
    }

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

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for
    /// the current contract to manage.
    function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends all of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have their entire balance approved for
    /// the current contract to manage.
    function safeTransferAllFrom(address token, address from, address to)
        internal
        returns (uint256 amount)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
            amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransfer(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sends all of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransferAll(address token, address to) internal returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
            mstore(0x20, address()) // Store the address of the current contract.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x14, to) // Store the `to` argument.
            amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// Reverts upon failure.
    function safeApprove(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            // Perform the approval, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
    /// then retries the approval again (some tokens, e.g. USDT, requires this).
    /// Reverts upon failure.
    function safeApproveWithRetry(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            // Perform the approval, retrying upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x34, 0) // Store 0 for the `amount`.
                mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
                pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval.
                mstore(0x34, amount) // Store back the original `amount`.
                // Retry the approval, reverting upon failure.
                if iszero(
                    and(
                        or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                        call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                    )
                ) {
                    mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Returns the amount of ERC20 `token` owned by `account`.
    /// Returns zero if the `token` does not exist.
    function balanceOf(address token, address account) internal view returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, account) // Store the `account` argument.
            mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            amount :=
                mul(
                    mload(0x20),
                    and( // The arguments of `and` are evaluated from right to left.
                        gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                        staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
                    )
                )
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {AddressAliasHelper} from "optimism/packages/contracts-bedrock/src/vendor/AddressAliasHelper.sol";
import {L1Bridge} from "src/L1Bridge.sol";

interface IL2ToL1MessagePasser {
    function initiateWithdrawal(address _target, uint256 _gasLimit, bytes calldata _data) external payable;
}

/**
 * @title L2Bridge
 * @notice L2 side of the ETH bridge that mints wrapped ETH tokens on FACET L2
 * @dev This contract handles:
 *      - Finalizing deposits from L1 by minting wrapped ETH
 *      - Initiating withdrawals back to L1 by burning wrapped ETH
 *      - Replay protection to prevent double-spending of deposits
 *      Only the aliased L1 bridge address can finalize deposits.
 */
contract L2Bridge is ERC20 {
    /*//////////////////////////////////////////////////////////////
                            CUSTOM ERRORS
    //////////////////////////////////////////////////////////////*/

    error UnauthorizedBridge();
    error InvalidWithdrawalAmount();
    error InvalidL1Bridge();
    error DepositAlreadyFinalized();

    /*//////////////////////////////////////////////////////////////
                               CONSTANTS
    //////////////////////////////////////////////////////////////*/

    address public immutable l1Bridge;
    IL2ToL1MessagePasser public constant MESSAGE_PASSER =
        IL2ToL1MessagePasser(0x4200000000000000000000000000000000000016);
    
    /*//////////////////////////////////////////////////////////////
                               STORAGE
    //////////////////////////////////////////////////////////////*/
    
    /**
     * @notice Tracks which deposit nonces have been finalized
     * @dev Prevents replay attacks where the same deposit could be finalized multiple times.
     *      This is critical for FACET's retry mechanism - a deposit can be retried on L1
     *      but must only be finalized once on L2.
     */
    mapping(uint256 => bool) public finalizedDeposits;

    /*//////////////////////////////////////////////////////////////
                                EVENTS
    //////////////////////////////////////////////////////////////*/

    event DepositFinalized(uint256 indexed nonce, address indexed to, uint256 amount);
    event WithdrawalInitiated(address indexed from, address indexed to, uint256 amount);

    /**
     * @notice Modifier to restrict functions to only the aliased L1 bridge
     * @dev Ensures only legitimate cross-chain messages from L1 bridge can mint tokens
     */
    modifier onlyL1Bridge() {
        if (msg.sender != aliasedL1Bridge()) revert UnauthorizedBridge();
        _;
    }

    constructor(string memory name_, string memory symbol_, address _l1Bridge) ERC20(name_, symbol_) {
        if (_l1Bridge == address(0)) revert InvalidL1Bridge();
        l1Bridge = _l1Bridge;
    }

    /*//////////////////////////////////////////////////////////////
                               DEPOSIT
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Finalizes a deposit from L1 by minting wrapped ETH to the recipient
     * @dev Called by the L1 bridge via cross-chain message. Uses nonce-based replay protection
     *      to ensure each deposit is only finalized once, even if retried multiple times on L1
     *      due to FACET block gas limits.
     * @param deposit The deposit transaction containing nonce, recipient, and amount
     */
    function finalizeDeposit(
        L1Bridge.DepositTransaction calldata deposit
    ) external onlyL1Bridge {
        // Check if deposit has already been finalized
        if (finalizedDeposits[deposit.nonce]) revert DepositAlreadyFinalized();
        
        // Mark deposit as finalized
        finalizedDeposits[deposit.nonce] = true;
        
        // Mint tokens to recipient
        _mint(deposit.to, deposit.amount);
        
        emit DepositFinalized(deposit.nonce, deposit.to, deposit.amount);
    }

    /*//////////////////////////////////////////////////////////////
                              WITHDRAWAL
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Initiates a withdrawal of wrapped ETH back to L1
     * @dev Burns the wrapped ETH and sends a message to L1 via the L2ToL1MessagePasser.
     *      The withdrawal must be proven and finalized on L1 using merkle proofs.
     * @param to The address that will receive the ETH on L1
     * @param amount The amount of wrapped ETH to withdraw (burned on L2, received on L1)
     */
    function initiateWithdrawal(address to, uint256 amount) external {
        if (amount == 0) revert InvalidWithdrawalAmount();

        _burn(msg.sender, amount);

        bytes memory data = abi.encode(to, amount);

        MESSAGE_PASSER.initiateWithdrawal(l1Bridge, 0, data);

        emit WithdrawalInitiated(msg.sender, to, amount);
    }

    /**
     * @notice Returns the aliased L1 bridge address
     * @dev L1 to L2 messages have their sender address aliased for security.
     *      This function computes what the L1 bridge address becomes when aliased.
     * @return The aliased address of the L1 bridge
     */
    function aliasedL1Bridge() public view returns (address) {
        return AddressAliasHelper.applyL1ToL2Alias(l1Bridge);
    }
}

File 11 of 26 : Rollup.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import { ISP1Verifier } from "@sp1-contracts/src/ISP1Verifier.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import { SafeCastLib } from "solady/src/utils/SafeCastLib.sol";

/// @title Rollup
/// @notice Dual-track ZK fault validity proof system
/// @dev Supports two tracks:
/// @dev 1. Fault proofs: Optimistic proposals that can be challenged (low cost)
/// @dev 2. Validity proofs: Direct ZK proofs that bypass challenges (instant finality)
/// @dev 
/// @dev Key features:
/// @dev - Single-round dispute resolution.
/// @dev - Bulk invalidation: Validity proofs invalidate all conflicting fault proofs
/// @dev - Censorship resistance: Fallback window allows anyone to propose after timeout
contract Rollup is Ownable, ReentrancyGuard {
    using SafeCastLib for uint256;
    
    /*//////////////////////////////////////////////////////////////
                               CONSTANTS
    //////////////////////////////////////////////////////////////*/

    uint256 public immutable MAX_CHALLENGE_SECS;
    uint256 public immutable MAX_PROVE_SECS;
    uint256 public immutable CHALLENGER_BOND;
    uint256 public immutable PROPOSER_BOND;
    uint256 public immutable FALLBACK_TIMEOUT_SECS;
    uint256 public immutable PROPOSAL_INTERVAL;
    uint256 public immutable L2_START_TIMESTAMP;
    uint256 public immutable L2_BLOCK_TIME;

    ISP1Verifier public immutable VERIFIER;
    bytes32 public immutable ROLLUP_CONFIG_HASH;
    bytes32 public immutable AGG_VKEY;
    bytes32 public immutable RANGE_VKEY_COMMITMENT;

    string public constant version = "1.0.0";

    /*//////////////////////////////////////////////////////////////
                               ENUMS
    //////////////////////////////////////////////////////////////*/

    enum ResolutionStatus { IN_PROGRESS, DEFENDER_WINS, CHALLENGER_WINS }

    enum ProposalStatus {
        Unchallenged,
        Challenged,
        UnchallengedAndProven,
        ChallengedAndProven,
        Resolved
    }
    
    /*//////////////////////////////////////////////////////////////
                               EVENTS
    //////////////////////////////////////////////////////////////*/

    event ProposalSubmitted(
        uint256 indexed proposalId,
        uint256 indexed parentId,
        address indexed proposer,
        bytes32 root,
        uint256 l2BlockNumber);
    
    event ProposalChallenged(uint256 indexed proposalId, address indexed challenger);
    event ProposalProven(uint256 indexed proposalId, address indexed prover);
    event ProposalResolved(uint256 indexed proposalId, ResolutionStatus status);
    event AnchorUpdated(uint256 indexed proposalId, bytes32 root, uint256 l2BlockNumber);
    event ProposalClosed(uint256 indexed proposalId);
    event ProposerPermissionUpdated(address indexed proposer, bool allowed);
    event BlockProven(uint256 indexed l2BlockNumber, bytes32 root, address indexed prover);
    event L1BlockHashCheckpointed(uint256 indexed l1BlockNumber, bytes32 blockHash);

    /*//////////////////////////////////////////////////////////////
                               ERRORS
    //////////////////////////////////////////////////////////////*/

    error BadAuth();
    error IncorrectBondAmount();
    error AlreadyChallenged();
    error GameNotOver();
    error GameOver();
    error AlreadyResolved();
    error NoCredit();
    error TransferFailed();
    error InvalidParentGame();
    error BadCadence();
    error ParentGameNotResolved();
    error ProposingBackwards();
    error ProposingFutureBlock();
    error BlockAlreadyProven();
    error L1BlockHashNotAvailable();
    error L1BlockHashNotCheckpointed();
    error NoCanonicalProposal();
    error InvalidL2BlockNumber();

    /*//////////////////////////////////////////////////////////////
                               STRUCTS
    //////////////////////////////////////////////////////////////*/

    struct Proposal {
        bytes32 rootClaim;

        // packed slot
        address proposer;
        uint32  l2BlockNumber;
        uint32  parentIndex;
        uint32  deadline;

        uint32  resolvedAt;
        ProposalStatus proposalStatus;
        ResolutionStatus resolutionStatus;
        address challenger;
        address prover;  // Who proved this specific proposal
    }
    
    // No struct needed - just store the prover address directly
    
    struct AggregationOutputs {
        bytes32 l1Head;
        bytes32 l2PreRoot;
        bytes32 claimRoot;
        uint256 claimBlockNum;
        bytes32 rollupConfigHash;
        bytes32 rangeVkeyCommitment;
        address proverAddress;
    }

    /*//////////////////////////////////////////////////////////////
                               STORAGE
    //////////////////////////////////////////////////////////////*/

    Proposal[] proposals; // index == proposalId

    mapping(address => uint256) public credit;

    mapping(address => bool) public whitelistedProposer;

    // The anchor tracks the latest accepted block
    uint256 public anchorL2BlockNumber;
    
    // Checkpointed L1 block hashes for proof verification
    mapping(uint256 => bytes32) public l1BlockHashes;

    // Maps L2 block number to the canonical proposal ID
    // 0 means no canonical proposal exists (uninitialized storage)
    // type(uint256).max means proposal 0 (genesis) is canonical
    uint256 private constant GENESIS_SENTINEL = type(uint256).max;
    mapping(uint256 => uint256) private _canonical;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(
        uint256 _challengeSecs,
        uint256 _proveSecs,
        uint256 _challengerBond,
        uint256 _proposerBond,
        uint256 _fallbackTimeout,
        uint256 _proposalInterval,
        bytes32 _startRoot,
        uint256 _startBlock,
        uint256 _l2StartTimestamp,
        uint256 _l2BlockTime,
        ISP1Verifier _verifier,
        bytes32 _rollupHash,
        bytes32 _aggVkey,
        bytes32 _rangeCommit
    ) {
        MAX_CHALLENGE_SECS    = _challengeSecs;
        MAX_PROVE_SECS        = _proveSecs;
        CHALLENGER_BOND       = _challengerBond;
        PROPOSER_BOND         = _proposerBond;
        FALLBACK_TIMEOUT_SECS = _fallbackTimeout;
        PROPOSAL_INTERVAL     = _proposalInterval;
        L2_START_TIMESTAMP    = _l2StartTimestamp;
        L2_BLOCK_TIME         = _l2BlockTime;
        
        VERIFIER              = _verifier;
        ROLLUP_CONFIG_HASH    = _rollupHash;
        AGG_VKEY              = _aggVkey;
        RANGE_VKEY_COMMITMENT = _rangeCommit;

        anchorL2BlockNumber = _startBlock;
        
        // Create genesis proposal representing the starting anchor
        Proposal memory genesis = Proposal({
            rootClaim: _startRoot,
            l2BlockNumber: _startBlock.toUint32(),
            parentIndex: 0,
            deadline: 0,
            proposer: address(0),
            challenger: address(0),
            resolvedAt: 0,
            proposalStatus: ProposalStatus.Resolved,
            resolutionStatus: ResolutionStatus.DEFENDER_WINS,
            prover: address(0)
        });
        
        proposals.push(genesis);

        _trySetCanonical(_startBlock, 0);
    }

    /*//////////////////////////////////////////////////////////////
                               ACTIONS
    //////////////////////////////////////////////////////////////*/

    /// @notice Creates a proposal with validation common to both fault proofs and validity proofs
    /// @param root L2 output root being proposed
    /// @param l2BlockNumber L2 block number (must be parentBlock + PROPOSAL_INTERVAL)
    /// @param parentId Parent proposal ID (must be valid and not invalidated)
    /// @param proposer Proposer address (address(0) for validity proofs, actual address for fault proofs)
    /// @return proposalId ID of the created proposal
    function _createProposal(
        bytes32 root,
        uint256 l2BlockNumber,
        uint256 parentId,
        address proposer
    ) internal returns (uint256 proposalId) {
        if (parentId >= proposals.length) revert InvalidParentGame();
        Proposal storage parent = proposals[parentId];
        
        if (l2BlockNumber <= anchorL2BlockNumber) revert ProposingBackwards();
        if (computeL2Timestamp(l2BlockNumber) >= block.timestamp) revert ProposingFutureBlock();
        
        if (_canonicalExistsFor(l2BlockNumber)) revert BlockAlreadyProven();
        
        // Proposals must follow the exact interval cadence from their parent
        if (l2BlockNumber != parent.l2BlockNumber + PROPOSAL_INTERVAL) {
            revert BadCadence();
        }
        
        // Cannot build on top of invalidated proposals
        if (parent.resolutionStatus == ResolutionStatus.CHALLENGER_WINS) {
            revert InvalidParentGame();
        }
        proposals.push();
        proposalId = proposals.length - 1;
        
        Proposal storage p = proposals[proposalId];
        p.rootClaim = root;
        p.l2BlockNumber = l2BlockNumber.toUint32();
        p.parentIndex = parentId.toUint32();
        p.deadline = (block.timestamp + MAX_CHALLENGE_SECS).toUint32();
        p.proposer = proposer;
        
        emit ProposalSubmitted(proposalId, parentId, proposer, root, l2BlockNumber);
    }

    /// @notice Submit a fault proof proposal that can be challenged
    /// @param root Output root claim for the L2 block
    /// @param l2BlockNumber L2 block number being proposed
    /// @param parentId Parent proposal to build upon
    /// @return proposalId ID of created proposal
    /// @dev Requires PROPOSER_BOND and proposer must be whitelisted or in fallback window
    function submitProposal(
        bytes32 root,
        uint256 l2BlockNumber,
        uint256  parentId
    ) external payable returns (uint256 proposalId) {
        if (msg.value != PROPOSER_BOND) revert IncorrectBondAmount();
        
        // Authorization: whitelist OR 2-week fallback (censorship resistance)
        if (!isWhitelistedProposer(msg.sender) && !isInFallbackWindow(l2BlockNumber)) {
            revert BadAuth();
        }
        proposalId = _createProposal({
            root: root,
            l2BlockNumber: l2BlockNumber,
            parentId: parentId,
            proposer: msg.sender
        });
    }
    
    /// @notice Challenge a proposal claiming it has the wrong root
    /// @param id Proposal ID to challenge
    /// @dev Requires CHALLENGER_BOND, starts proof countdown
    function challengeProposal(uint256 id) external payable onlyIfGameNotOver(id) {
        if (msg.value != CHALLENGER_BOND) revert IncorrectBondAmount();
        
        Proposal storage p = proposals[id];
        if (p.proposalStatus != ProposalStatus.Unchallenged) revert AlreadyChallenged();

        p.challenger = msg.sender;
        p.proposalStatus = ProposalStatus.Challenged;
        p.deadline = (block.timestamp + MAX_PROVE_SECS).toUint32();

        emit ProposalChallenged(id, msg.sender);
    }

    /// @notice Submit a validity proof that bypasses the optimistic flow
    /// @param l2BlockNumber L2 block to prove (must be anchorBlock + PROPOSAL_INTERVAL)
    /// @param root Correct output root for this block
    /// @param l1BlockNumber L1 block used in proof (for L1 data availability)
    /// @param proof ZK proof of state transition from anchor to this block
    /// @dev Creates, proves, and resolves a proposal atomically. No bond required.
    function proveBlock(
        uint256 l2BlockNumber,
        bytes32 root,
        uint256 l1BlockNumber,
        bytes calldata proof
    ) external {
        // Validity proofs must build directly on the anchor to ensure linear progression
        // Anchor always has a canonical (genesis at minimum)
        uint256 parentProposalId = anchorProposalId();
        
        // address(0) proposer indicates no bond was collected
        uint256 proposalId = _createProposal({
            root: root,
            l2BlockNumber: l2BlockNumber,
            parentId: parentProposalId,
            proposer: address(0)
        });
        
        proveProposal(proposalId, l1BlockNumber, proof);
        resolveProposal(proposalId);
        
        emit BlockProven(l2BlockNumber, root, msg.sender);
    }

    /// @notice Submit ZK proof defending a proposal
    /// @param id Proposal to prove
    /// @param l1BlockNumber L1 block for data availability context
    /// @param proof ZK proof of state transition from parent to this proposal
    /// @dev Parent must be valid for proof to be useful, but that's checked in resolution
    function proveProposal(
        uint256 id, 
        uint256 l1BlockNumber,
        bytes calldata proof
    ) public onlyIfGameNotOver(id) {
        Proposal storage p = proposals[id];
        
        bytes32 parentRoot = proposals[p.parentIndex].rootClaim;
        bytes32 l1BlockHash = getCheckpointedL1BlockHash(l1BlockNumber);
        AggregationOutputs memory pub = AggregationOutputs({
            l1Head: l1BlockHash,
            l2PreRoot: parentRoot,
            claimRoot: p.rootClaim,
            claimBlockNum: p.l2BlockNumber,
            rollupConfigHash: ROLLUP_CONFIG_HASH,
            rangeVkeyCommitment: RANGE_VKEY_COMMITMENT,
            proverAddress: msg.sender
        });
        
        VERIFIER.verifyProof(AGG_VKEY, abi.encode(pub), proof);
        
        p.prover = msg.sender;
        p.proposalStatus = (p.proposalStatus == ProposalStatus.Challenged) 
            ? ProposalStatus.ChallengedAndProven 
            : ProposalStatus.UnchallengedAndProven;
        
        emit ProposalProven(id, msg.sender);
    }

    /*//////////////////////////////////////////////////////////////
                               MODIFIERS
    //////////////////////////////////////////////////////////////*/

    modifier onlyIfGameOver(uint256 proposalId) {
        if (!gameOver(proposalId)) revert GameNotOver();
        _;
    }
    
    modifier onlyIfGameNotOver(uint256 proposalId) {
        if (gameOver(proposalId)) revert GameOver();
        _;
    }

    /*//////////////////////////////////////////////////////////////
                          GAME STATE LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @notice Check if a proposal's game has ended (ready for resolution)
    /// @param proposalId Proposal to check
    /// @return True if deadline passed, proven locally, or canonical exists for block
    function gameOver(uint256 proposalId) public view returns (bool) {
        Proposal storage p = proposals[proposalId];
        return p.deadline < block.timestamp || 
               p.prover != address(0) ||
               _canonicalExistsFor(p.l2BlockNumber);
    }

    /// @notice Calculate how long ago an L2 block should have been created
    /// @param l2BlockNumber L2 block number
    /// @return Age in seconds since the block's expected timestamp
    function l2BlockAge(uint256 l2BlockNumber) public view returns (uint256) {
        return block.timestamp - computeL2Timestamp(l2BlockNumber);
    }
    
    /// @notice Returns the L2 timestamp corresponding to a given L2 block number.
    /// @notice Compute the expected timestamp for an L2 block
    /// @param _l2BlockNumber L2 block number
    /// @return Expected timestamp based on L2_BLOCK_TIME and genesis
    function computeL2Timestamp(uint256 _l2BlockNumber) public view returns (uint256) {
        if (_l2BlockNumber < proposals[0].l2BlockNumber) {
            revert InvalidL2BlockNumber();
        }
        
        return L2_START_TIMESTAMP + ((_l2BlockNumber - proposals[0].l2BlockNumber) * L2_BLOCK_TIME);
    }
    
    /// @notice Get L1 block hash from checkpoint or EVM history
    /// @param l1BlockNumber L1 block number
    /// @return l1BlockHash Block hash (reverts if too old and not checkpointed)
    function getCheckpointedL1BlockHash(uint256 l1BlockNumber) internal view returns (bytes32 l1BlockHash) {
        l1BlockHash = l1BlockHashes[l1BlockNumber];
        if (l1BlockHash == bytes32(0)) {
            revert L1BlockHashNotCheckpointed();
        }
    }

    /// @notice Resolve a proposal determining if defender or challenger wins
    /// @param id Proposal to resolve
    /// @dev Resolution hierarchy:
    /// @dev 1. Parent invalid → challenger wins (cascading invalidation)
    /// @dev 2. Conflicts with canonical → challenger wins (bulk invalidation) 
    /// @dev 3. Has valid proof → defender wins
    /// @dev 4. Timeout: challenged → challenger wins, unchallenged → defender wins
    function resolveProposal(uint256 id) public onlyIfGameOver(id) {
        Proposal storage p = proposals[id];
        Proposal storage parent = proposals[p.parentIndex];
        
        if (parent.resolutionStatus == ResolutionStatus.IN_PROGRESS) {
            revert ParentGameNotResolved();
        }
        if (p.resolutionStatus != ResolutionStatus.IN_PROGRESS) {
            revert AlreadyResolved();
        }

        // Resolution hierarchy (order matters!)
        if (parent.resolutionStatus == ResolutionStatus.CHALLENGER_WINS) {
            p.resolutionStatus = ResolutionStatus.CHALLENGER_WINS;
        }
        else if (_proposalConflictsWithCanonical(p)) {
            p.resolutionStatus = ResolutionStatus.CHALLENGER_WINS;
        }
        else if (_getProposalProver(p) != address(0)) {
            p.resolutionStatus = ResolutionStatus.DEFENDER_WINS;
        }
        else if (p.proposalStatus == ProposalStatus.Challenged) {
            p.resolutionStatus = ResolutionStatus.CHALLENGER_WINS;
        } else {
            p.resolutionStatus = ResolutionStatus.DEFENDER_WINS;
        }
        
        if (p.resolutionStatus == ResolutionStatus.DEFENDER_WINS) {
            _trySetCanonical(p.l2BlockNumber, id);
            
            // Advance anchor only if this directly extends it
            if (p.l2BlockNumber == anchorL2BlockNumber + PROPOSAL_INTERVAL) {
                anchorL2BlockNumber = p.l2BlockNumber;
                emit AnchorUpdated(id, p.rootClaim, p.l2BlockNumber);
            }
            
            _pay(p.proposer, _proposerBond(p));
            _pay(_getProposalProver(p), _challengerBond(p));
        } else if (p.resolutionStatus == ResolutionStatus.CHALLENGER_WINS) {
            address recipient = p.challenger;
            if (recipient == address(0)) {
                // Bulk invalidation case: pay canonical prover who proved correct root
                // If no canonical exists yet, bond is burned (recipient remains address(0))
                if (_canonicalExistsFor(p.l2BlockNumber)) {
                    recipient = _getProposalProver(_canonicalProposalFor(p.l2BlockNumber));
                }
            }
            _pay(recipient, _totalBond(p));
        }
        
        p.proposalStatus = ProposalStatus.Resolved;
        p.resolvedAt = (block.timestamp).toUint32();
        emit ProposalResolved(id, p.resolutionStatus);
        emit ProposalClosed(id);
    }

    /*//////////////////////////////////////////////////////////////
                           INTERNAL PAY-OUT HELPERS
    //////////////////////////////////////////////////////////////*/

    /// @dev Credits account
    function _pay(address to, uint256 amount) internal {
        if (to != address(0) && amount != 0) credit[to] += amount;
    }

    /// @dev Returns proposer bond amount (0 for validity proofs)
    function _proposerBond(Proposal storage p) internal view returns (uint256) {
        return p.proposer != address(0) ? PROPOSER_BOND : 0;
    }

    /// @dev Returns challenger bond amount (0 if unchallenged)
    function _challengerBond(Proposal storage p) internal view returns (uint256) {
        return p.challenger != address(0) ? CHALLENGER_BOND : 0;
    }

    /// @dev Total bonds at stake for this proposal
    function _totalBond(Proposal storage p) internal view returns (uint256) {
        return _proposerBond(p) + _challengerBond(p);
    }

    /// @dev Find who proved this proposal (considering canonical fallback)
    /// @return Prover address or address(0) if conflicts with canonical
    function _getProposalProver(Proposal storage p) internal view returns (address) {
        if (_proposalConflictsWithCanonical(p)) {
            return address(0);
        }
        
        // Prefer local prover, fall back to canonical proposal's prover
        if (p.prover != address(0)) {
            return p.prover;
        }
        
        if (_canonicalExistsFor(p.l2BlockNumber)) {
            Proposal storage canonical = _canonicalProposalFor(p.l2BlockNumber);
            return canonical.prover != address(0) ? canonical.prover : canonical.proposer;
        }
        
        return address(0);
    }
    
    /// @dev Check if proposal has wrong root compared to canonical
    function _proposalConflictsWithCanonical(Proposal storage p) internal view returns (bool) {
        return _canonicalExistsFor(p.l2BlockNumber) && 
               _canonicalProposalFor(p.l2BlockNumber).rootClaim != p.rootClaim;
    }


    /*//////////////////////////////////////////////////////////////
                         CREDIT WITHDRAWAL
    //////////////////////////////////////////////////////////////*/

    /// @notice Withdraw accumulated credit from bonds
    /// @param recipient Address to withdraw credit for
    /// @dev Uses pull pattern to prevent reentrancy
    function claimCredit(address recipient) public nonReentrant {
        uint256 amount = credit[recipient];
        if (amount == 0) revert NoCredit();
        
        credit[recipient] = 0;
        (bool ok,) = recipient.call{ value: amount }("");
        if (!ok) revert TransferFailed();
    }
    
    /*//////////////////////////////////////////////////////////////
                    PERMISSIONS & AUTHORIZATION
    //////////////////////////////////////////////////////////////*/

    /// @notice Update proposer whitelist
    /// @param proposer Address to update
    /// @param allowed Whether address can propose
    function setProposer(address proposer, bool allowed) external onlyOwner {
        whitelistedProposer[proposer] = allowed;
        emit ProposerPermissionUpdated(proposer, allowed);
    }

    /// @notice Check if address can submit proposals
    /// @param proposer Address to check
    /// @return True if whitelisted or wildcard enabled
    function isWhitelistedProposer(address proposer) public view returns (bool) {
        return whitelistedProposer[proposer] || whitelistedProposer[address(0)];
    }
    
    /// @notice Check if fallback window is active (anyone can propose)
    /// @param l2BlockNumber Block to check
    /// @return True if block is old enough for fallback
    function isInFallbackWindow(uint256 l2BlockNumber) public view returns (bool) {
        return l2BlockAge(l2BlockNumber) > FALLBACK_TIMEOUT_SECS;
    }

    /// @notice Store L1 block hash for proofs requiring old blocks
    /// @param l1BlockNumber L1 block to checkpoint (must be recent)
    /// @dev Only needed for blocks older than 256 blocks
    function checkpointL1BlockHash(uint256 l1BlockNumber) external {
        bytes32 blockHash = blockhash(l1BlockNumber);
        if (blockHash == bytes32(0)) {
            revert L1BlockHashNotAvailable();
        }
        l1BlockHashes[l1BlockNumber] = blockHash;
        
        emit L1BlockHashCheckpointed(l1BlockNumber, blockHash);
    }
    
    /*//////////////////////////////////////////////////////////////
                                GETTERS
    //////////////////////////////////////////////////////////////*/

    /// @notice Get the current anchor's output root
    /// @return Output root of the anchor block
    function anchorRoot() public view returns (bytes32) {
        // Anchor always has a canonical proposal (genesis at minimum)
        return _canonicalProposalFor(anchorL2BlockNumber).rootClaim;
    }

    /// @notice Get a single proposal
    /// @param id Proposal ID
    /// @return The proposal struct
    function getProposal(uint256 id) external view returns (Proposal memory) {
        return proposals[id];
    }
    
    /// @notice Get current anchor root and block number
    /// @return root Output root
    /// @return blockNumber L2 block number
    function getAnchorRoot() external view returns (bytes32, uint256) {
        return (anchorRoot(), anchorL2BlockNumber);
    }

    /// @notice Batch get proposals by IDs
    /// @param ids Array of proposal IDs
    /// @return out Array of proposals
    function getProposals(uint256[] calldata ids) external view returns (Proposal[] memory out) {
        out = new Proposal[](ids.length);
        for (uint256 i; i < ids.length; ++i) out[i] = proposals[ids[i]];
    }

    /// @notice Get most recent proposal IDs
    /// @param count Number of proposals to return
    /// @return ids Array of proposal IDs (newest first)
    function latestProposals(uint256 count) external view returns (uint256[] memory ids) {
        uint256 total = proposals.length;
        if (count > total) count = total;
        ids = new uint256[](count);
        for (uint256 i; i < count; ++i) ids[i] = total - 1 - i;
    }

    /// @notice Get total number of proposals
    /// @return Number of proposals created
    function getProposalsLength() external view returns (uint256) {
        return proposals.length;
    }
    
    /// @notice Check if proposal can be resolved
    /// @param proposalId Proposal to check
    /// @return True if game over and not yet resolved
    function isResolvable(uint256 proposalId) external view returns (bool) {
        if (proposalId >= proposals.length) return false;
        Proposal storage p = proposals[proposalId];
        return gameOver(proposalId) && p.resolutionStatus == ResolutionStatus.IN_PROGRESS;
    }
    
    /// @notice Check if proposal needs a proof
    /// @param proposalId Proposal to check  
    /// @return True if challenged and deadline not passed
    function needsDefense(uint256 proposalId) external view returns (bool) {
        if (proposalId >= proposals.length) return false;
        return !gameOver(proposalId) && proposals[proposalId].proposalStatus == ProposalStatus.Challenged;
    }
    
    /// @notice Get the canonical proposal ID for the anchor block
    /// @return Proposal ID of current anchor
    function anchorProposalId() public view returns (uint256) {
        return canonicalProposalIdFor(anchorL2BlockNumber);
    }

    /*//////////////////////////////////////////////////////////////
                     CANONICAL PROPOSAL HELPERS
    //////////////////////////////////////////////////////////////*/

    /// @notice Get the canonical proposal for an L2 block
    /// @param l2BlockNumber The L2 block number
    /// @return The canonical proposal, or empty proposal if none exists
    function canonicalProposalFor(uint256 l2BlockNumber) public view returns (Proposal memory) {
        if (!_canonicalExistsFor(l2BlockNumber)) revert NoCanonicalProposal();
        
        uint256 canonicalId = canonicalProposalIdFor(l2BlockNumber);
        return proposals[canonicalId];
    }

    /// @notice Get the canonical proposal ID for an L2 block
    /// @param l2BlockNumber The L2 block number
    /// @return The canonical proposal ID, or 0 if none exists
    function canonicalProposalIdFor(uint256 l2BlockNumber) public view returns (uint256) {
        if (!_canonicalExistsFor(l2BlockNumber)) revert NoCanonicalProposal();
        
        return _canonical[l2BlockNumber] == GENESIS_SENTINEL ? 0 : _canonical[l2BlockNumber];
    }
    
    function proposalIsCanonical(uint256 proposalId) public view returns (bool) {
        if (proposalId >= proposals.length) return false;
        Proposal storage p = proposals[proposalId];
        return _canonicalExistsFor(p.l2BlockNumber) && proposalId == canonicalProposalIdFor(p.l2BlockNumber);
    }

    /// @dev Get the canonical proposal storage reference (reverts if doesn't exist)
    function _canonicalProposalFor(uint256 l2BlockNumber) internal view returns (Proposal storage) {
        uint256 canonicalId = canonicalProposalIdFor(l2BlockNumber);
        
        return proposals[canonicalId];
    }

    /// @dev Check if a canonical proposal exists for an L2 block
    function _canonicalExistsFor(uint256 l2BlockNumber) internal view returns (bool) {
        return _canonical[l2BlockNumber] != 0;
    }

    /// @dev Try to set the canonical proposal for an L2 block (only sets if not already set)
    function _trySetCanonical(uint256 l2BlockNumber, uint256 proposalId) internal {
        if (!_canonicalExistsFor(l2BlockNumber)) {
            _canonical[l2BlockNumber] = proposalId == 0 ? GENESIS_SENTINEL : proposalId;
        }
    }
}

File 12 of 26 : EOA.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

/**
 * @title EOA
 * @notice A library for detecting if an address is an EOA.
 * @dev Includes support for EIP-7702 delegated EOAs
 */
library EOA {
    /**
     * @notice Returns true if sender address is an EOA.
     * @dev Checks for regular EOAs and EIP-7702 delegated EOAs
     * @return isEOA_ True if the sender address is an EOA.
     */
    function isSenderEOA() internal view returns (bool isEOA_) {
        if (msg.sender == tx.origin) {
            // Regular EOA: sender is the transaction origin
            isEOA_ = true;
        } else if (address(msg.sender).code.length == 23) {
            // Check for EIP-7702 delegated EOAs (23 bytes of code)
            assembly {
                let ptr := mload(0x40)
                mstore(0x40, add(ptr, 0x20))
                extcodecopy(caller(), ptr, 0, 0x20)
                isEOA_ := eq(shr(232, mload(ptr)), 0xEF0100)
            }
        } else {
            // If more or less than 23 bytes of code, not a 7702 delegated EOA
            isEOA_ = false;
        }
    }
}

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

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

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

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

// Libraries
import { Types } from "src/libraries/Types.sol";
import { Hashing } from "src/libraries/Hashing.sol";
import { RLPWriter } from "src/libraries/rlp/RLPWriter.sol";

/// @title Encoding
/// @notice Encoding handles Optimism's various different encoding schemes.
library Encoding {
    /// @notice RLP encodes the L2 transaction that would be generated when a given deposit is sent
    ///         to the L2 system. Useful for searching for a deposit in the L2 system. The
    ///         transaction is prefixed with 0x7e to identify its EIP-2718 type.
    /// @param _tx User deposit transaction to encode.
    /// @return RLP encoded L2 deposit transaction.
    function encodeDepositTransaction(Types.UserDepositTransaction memory _tx) internal pure returns (bytes memory) {
        bytes32 source = Hashing.hashDepositSource(_tx.l1BlockHash, _tx.logIndex);
        bytes[] memory raw = new bytes[](8);
        raw[0] = RLPWriter.writeBytes(abi.encodePacked(source));
        raw[1] = RLPWriter.writeAddress(_tx.from);
        raw[2] = _tx.isCreation ? RLPWriter.writeBytes("") : RLPWriter.writeAddress(_tx.to);
        raw[3] = RLPWriter.writeUint(_tx.mint);
        raw[4] = RLPWriter.writeUint(_tx.value);
        raw[5] = RLPWriter.writeUint(uint256(_tx.gasLimit));
        raw[6] = RLPWriter.writeBool(false);
        raw[7] = RLPWriter.writeBytes(_tx.data);
        return abi.encodePacked(uint8(0x7e), RLPWriter.writeList(raw));
    }

    /// @notice Encodes the cross domain message based on the version that is encoded into the
    ///         message nonce.
    /// @param _nonce    Message nonce with version encoded into the first two bytes.
    /// @param _sender   Address of the sender of the message.
    /// @param _target   Address of the target of the message.
    /// @param _value    ETH value to send to the target.
    /// @param _gasLimit Gas limit to use for the message.
    /// @param _data     Data to send with the message.
    /// @return Encoded cross domain message.
    function encodeCrossDomainMessage(
        uint256 _nonce,
        address _sender,
        address _target,
        uint256 _value,
        uint256 _gasLimit,
        bytes memory _data
    )
        internal
        pure
        returns (bytes memory)
    {
        (, uint16 version) = decodeVersionedNonce(_nonce);
        if (version == 0) {
            return encodeCrossDomainMessageV0(_target, _sender, _data, _nonce);
        } else if (version == 1) {
            return encodeCrossDomainMessageV1(_nonce, _sender, _target, _value, _gasLimit, _data);
        } else {
            revert("Encoding: unknown cross domain message version");
        }
    }

    /// @notice Encodes a cross domain message based on the V0 (legacy) encoding.
    /// @param _target Address of the target of the message.
    /// @param _sender Address of the sender of the message.
    /// @param _data   Data to send with the message.
    /// @param _nonce  Message nonce.
    /// @return Encoded cross domain message.
    function encodeCrossDomainMessageV0(
        address _target,
        address _sender,
        bytes memory _data,
        uint256 _nonce
    )
        internal
        pure
        returns (bytes memory)
    {
        // nosemgrep: sol-style-use-abi-encodecall
        return abi.encodeWithSignature("relayMessage(address,address,bytes,uint256)", _target, _sender, _data, _nonce);
    }

    /// @notice Encodes a cross domain message based on the V1 (current) encoding.
    /// @param _nonce    Message nonce.
    /// @param _sender   Address of the sender of the message.
    /// @param _target   Address of the target of the message.
    /// @param _value    ETH value to send to the target.
    /// @param _gasLimit Gas limit to use for the message.
    /// @param _data     Data to send with the message.
    /// @return Encoded cross domain message.
    function encodeCrossDomainMessageV1(
        uint256 _nonce,
        address _sender,
        address _target,
        uint256 _value,
        uint256 _gasLimit,
        bytes memory _data
    )
        internal
        pure
        returns (bytes memory)
    {
        // nosemgrep: sol-style-use-abi-encodecall
        return abi.encodeWithSignature(
            "relayMessage(uint256,address,address,uint256,uint256,bytes)",
            _nonce,
            _sender,
            _target,
            _value,
            _gasLimit,
            _data
        );
    }

    /// @notice Adds a version number into the first two bytes of a message nonce.
    /// @param _nonce   Message nonce to encode into.
    /// @param _version Version number to encode into the message nonce.
    /// @return Message nonce with version encoded into the first two bytes.
    function encodeVersionedNonce(uint240 _nonce, uint16 _version) internal pure returns (uint256) {
        uint256 nonce;
        assembly {
            nonce := or(shl(240, _version), _nonce)
        }
        return nonce;
    }

    /// @notice Pulls the version out of a version-encoded nonce.
    /// @param _nonce Message nonce with version encoded into the first two bytes.
    /// @return Nonce without encoded version.
    /// @return Version of the message.
    function decodeVersionedNonce(uint256 _nonce) internal pure returns (uint240, uint16) {
        uint240 nonce;
        uint16 version;
        assembly {
            nonce := and(_nonce, 0x0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
            version := shr(240, _nonce)
        }
        return (nonce, version);
    }

    /// @notice Returns an appropriately encoded call to L1Block.setL1BlockValuesEcotone
    /// @param _baseFeeScalar       L1 base fee Scalar
    /// @param _blobBaseFeeScalar   L1 blob base fee Scalar
    /// @param _sequenceNumber      Number of L2 blocks since epoch start.
    /// @param _timestamp           L1 timestamp.
    /// @param _number              L1 blocknumber.
    /// @param _baseFee             L1 base fee.
    /// @param _blobBaseFee         L1 blob base fee.
    /// @param _hash                L1 blockhash.
    /// @param _batcherHash         Versioned hash to authenticate batcher by.
    function encodeSetL1BlockValuesEcotone(
        uint32 _baseFeeScalar,
        uint32 _blobBaseFeeScalar,
        uint64 _sequenceNumber,
        uint64 _timestamp,
        uint64 _number,
        uint256 _baseFee,
        uint256 _blobBaseFee,
        bytes32 _hash,
        bytes32 _batcherHash
    )
        internal
        pure
        returns (bytes memory)
    {
        bytes4 functionSignature = bytes4(keccak256("setL1BlockValuesEcotone()"));
        return abi.encodePacked(
            functionSignature,
            _baseFeeScalar,
            _blobBaseFeeScalar,
            _sequenceNumber,
            _timestamp,
            _number,
            _baseFee,
            _blobBaseFee,
            _hash,
            _batcherHash
        );
    }

    /// @notice Returns an appropriately encoded call to L1Block.setL1BlockValuesInterop
    /// @param _baseFeeScalar       L1 base fee Scalar
    /// @param _blobBaseFeeScalar   L1 blob base fee Scalar
    /// @param _sequenceNumber      Number of L2 blocks since epoch start.
    /// @param _timestamp           L1 timestamp.
    /// @param _number              L1 blocknumber.
    /// @param _baseFee             L1 base fee.
    /// @param _blobBaseFee         L1 blob base fee.
    /// @param _hash                L1 blockhash.
    /// @param _batcherHash         Versioned hash to authenticate batcher by.
    function encodeSetL1BlockValuesInterop(
        uint32 _baseFeeScalar,
        uint32 _blobBaseFeeScalar,
        uint64 _sequenceNumber,
        uint64 _timestamp,
        uint64 _number,
        uint256 _baseFee,
        uint256 _blobBaseFee,
        bytes32 _hash,
        bytes32 _batcherHash
    )
        internal
        pure
        returns (bytes memory)
    {
        bytes4 functionSignature = bytes4(keccak256("setL1BlockValuesInterop()"));
        return abi.encodePacked(
            functionSignature,
            _baseFeeScalar,
            _blobBaseFeeScalar,
            _sequenceNumber,
            _timestamp,
            _number,
            _baseFee,
            _blobBaseFee,
            _hash,
            _batcherHash
        );
    }
}

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

// Libraries
import { Bytes } from "src/libraries/Bytes.sol";
import { RLPReader } from "src/libraries/rlp/RLPReader.sol";

/// @title MerkleTrie
/// @notice MerkleTrie is a small library for verifying standard Ethereum Merkle-Patricia trie
///         inclusion proofs. By default, this library assumes a hexary trie. One can change the
///         trie radix constant to support other trie radixes.
library MerkleTrie {
    /// @notice Struct representing a node in the trie.
    /// @custom:field encoded The RLP-encoded node.
    /// @custom:field decoded The RLP-decoded node.
    struct TrieNode {
        bytes encoded;
        RLPReader.RLPItem[] decoded;
    }

    /// @notice Determines the number of elements per branch node.
    uint256 internal constant TREE_RADIX = 16;

    /// @notice Branch nodes have TREE_RADIX elements and one value element.
    uint256 internal constant BRANCH_NODE_LENGTH = TREE_RADIX + 1;

    /// @notice Leaf nodes and extension nodes have two elements, a `path` and a `value`.
    uint256 internal constant LEAF_OR_EXTENSION_NODE_LENGTH = 2;

    /// @notice Prefix for even-nibbled extension node paths.
    uint8 internal constant PREFIX_EXTENSION_EVEN = 0;

    /// @notice Prefix for odd-nibbled extension node paths.
    uint8 internal constant PREFIX_EXTENSION_ODD = 1;

    /// @notice Prefix for even-nibbled leaf node paths.
    uint8 internal constant PREFIX_LEAF_EVEN = 2;

    /// @notice Prefix for odd-nibbled leaf node paths.
    uint8 internal constant PREFIX_LEAF_ODD = 3;

    /// @notice Verifies a proof that a given key/value pair is present in the trie.
    /// @param _key   Key of the node to search for, as a hex string.
    /// @param _value Value of the node to search for, as a hex string.
    /// @param _proof Merkle trie inclusion proof for the desired node. Unlike traditional Merkle
    ///               trees, this proof is executed top-down and consists of a list of RLP-encoded
    ///               nodes that make a path down to the target node.
    /// @param _root  Known root of the Merkle trie. Used to verify that the included proof is
    ///               correctly constructed.
    /// @return valid_ Whether or not the proof is valid.
    function verifyInclusionProof(
        bytes memory _key,
        bytes memory _value,
        bytes[] memory _proof,
        bytes32 _root
    )
        internal
        pure
        returns (bool valid_)
    {
        valid_ = Bytes.equal(_value, get(_key, _proof, _root));
    }

    /// @notice Retrieves the value associated with a given key.
    /// @param _key   Key to search for, as hex bytes.
    /// @param _proof Merkle trie inclusion proof for the key.
    /// @param _root  Known root of the Merkle trie.
    /// @return value_ Value of the key if it exists.
    function get(bytes memory _key, bytes[] memory _proof, bytes32 _root) internal pure returns (bytes memory value_) {
        require(_key.length > 0, "MerkleTrie: empty key");

        TrieNode[] memory proof = _parseProof(_proof);
        bytes memory key = Bytes.toNibbles(_key);
        bytes memory currentNodeID = abi.encodePacked(_root);
        uint256 currentKeyIndex = 0;

        // Proof is top-down, so we start at the first element (root).
        for (uint256 i = 0; i < proof.length; i++) {
            TrieNode memory currentNode = proof[i];

            // Key index should never exceed total key length or we'll be out of bounds.
            require(currentKeyIndex <= key.length, "MerkleTrie: key index exceeds total key length");

            if (currentKeyIndex == 0) {
                // First proof element is always the root node.
                require(
                    Bytes.equal(abi.encodePacked(keccak256(currentNode.encoded)), currentNodeID),
                    "MerkleTrie: invalid root hash"
                );
            } else if (currentNode.encoded.length >= 32) {
                // Nodes 32 bytes or larger are hashed inside branch nodes.
                require(
                    Bytes.equal(abi.encodePacked(keccak256(currentNode.encoded)), currentNodeID),
                    "MerkleTrie: invalid large internal hash"
                );
            } else {
                // Nodes smaller than 32 bytes aren't hashed.
                require(Bytes.equal(currentNode.encoded, currentNodeID), "MerkleTrie: invalid internal node hash");
            }

            if (currentNode.decoded.length == BRANCH_NODE_LENGTH) {
                if (currentKeyIndex == key.length) {
                    // Value is the last element of the decoded list (for branch nodes). There's
                    // some ambiguity in the Merkle trie specification because bytes(0) is a
                    // valid value to place into the trie, but for branch nodes bytes(0) can exist
                    // even when the value wasn't explicitly placed there. Geth treats a value of
                    // bytes(0) as "key does not exist" and so we do the same.
                    value_ = RLPReader.readBytes(currentNode.decoded[TREE_RADIX]);
                    require(value_.length > 0, "MerkleTrie: value length must be greater than zero (branch)");

                    // Extra proof elements are not allowed.
                    require(i == proof.length - 1, "MerkleTrie: value node must be last node in proof (branch)");

                    return value_;
                } else {
                    // We're not at the end of the key yet.
                    // Figure out what the next node ID should be and continue.
                    uint8 branchKey = uint8(key[currentKeyIndex]);
                    RLPReader.RLPItem memory nextNode = currentNode.decoded[branchKey];
                    currentNodeID = _getNodeID(nextNode);
                    currentKeyIndex += 1;
                }
            } else if (currentNode.decoded.length == LEAF_OR_EXTENSION_NODE_LENGTH) {
                bytes memory path = _getNodePath(currentNode);
                uint8 prefix = uint8(path[0]);
                uint8 offset = 2 - (prefix % 2);
                bytes memory pathRemainder = Bytes.slice(path, offset);
                bytes memory keyRemainder = Bytes.slice(key, currentKeyIndex);
                uint256 sharedNibbleLength = _getSharedNibbleLength(pathRemainder, keyRemainder);

                // Whether this is a leaf node or an extension node, the path remainder MUST be a
                // prefix of the key remainder (or be equal to the key remainder) or the proof is
                // considered invalid.
                require(
                    pathRemainder.length == sharedNibbleLength,
                    "MerkleTrie: path remainder must share all nibbles with key"
                );

                if (prefix == PREFIX_LEAF_EVEN || prefix == PREFIX_LEAF_ODD) {
                    // Prefix of 2 or 3 means this is a leaf node. For the leaf node to be valid,
                    // the key remainder must be exactly equal to the path remainder. We already
                    // did the necessary byte comparison, so it's more efficient here to check that
                    // the key remainder length equals the shared nibble length, which implies
                    // equality with the path remainder (since we already did the same check with
                    // the path remainder and the shared nibble length).
                    require(
                        keyRemainder.length == sharedNibbleLength,
                        "MerkleTrie: key remainder must be identical to path remainder"
                    );

                    // Our Merkle Trie is designed specifically for the purposes of the Ethereum
                    // state trie. Empty values are not allowed in the state trie, so we can safely
                    // say that if the value is empty, the key should not exist and the proof is
                    // invalid.
                    value_ = RLPReader.readBytes(currentNode.decoded[1]);
                    require(value_.length > 0, "MerkleTrie: value length must be greater than zero (leaf)");

                    // Extra proof elements are not allowed.
                    require(i == proof.length - 1, "MerkleTrie: value node must be last node in proof (leaf)");

                    return value_;
                } else if (prefix == PREFIX_EXTENSION_EVEN || prefix == PREFIX_EXTENSION_ODD) {
                    // Prefix of 0 or 1 means this is an extension node. We move onto the next node
                    // in the proof and increment the key index by the length of the path remainder
                    // which is equal to the shared nibble length.
                    currentNodeID = _getNodeID(currentNode.decoded[1]);
                    currentKeyIndex += sharedNibbleLength;
                } else {
                    revert("MerkleTrie: received a node with an unknown prefix");
                }
            } else {
                revert("MerkleTrie: received an unparseable node");
            }
        }

        revert("MerkleTrie: ran out of proof elements");
    }

    /// @notice Parses an array of proof elements into a new array that contains both the original
    ///         encoded element and the RLP-decoded element.
    /// @param _proof Array of proof elements to parse.
    /// @return proof_ Proof parsed into easily accessible structs.
    function _parseProof(bytes[] memory _proof) private pure returns (TrieNode[] memory proof_) {
        uint256 length = _proof.length;
        proof_ = new TrieNode[](length);
        for (uint256 i = 0; i < length;) {
            proof_[i] = TrieNode({ encoded: _proof[i], decoded: RLPReader.readList(_proof[i]) });
            unchecked {
                ++i;
            }
        }
    }

    /// @notice Picks out the ID for a node. Node ID is referred to as the "hash" within the
    ///         specification, but nodes < 32 bytes are not actually hashed.
    /// @param _node Node to pull an ID for.
    /// @return id_ ID for the node, depending on the size of its contents.
    function _getNodeID(RLPReader.RLPItem memory _node) private pure returns (bytes memory id_) {
        id_ = _node.length < 32 ? RLPReader.readRawBytes(_node) : RLPReader.readBytes(_node);
    }

    /// @notice Gets the path for a leaf or extension node.
    /// @param _node Node to get a path for.
    /// @return nibbles_ Node path, converted to an array of nibbles.
    function _getNodePath(TrieNode memory _node) private pure returns (bytes memory nibbles_) {
        nibbles_ = Bytes.toNibbles(RLPReader.readBytes(_node.decoded[0]));
    }

    /// @notice Utility; determines the number of nibbles shared between two nibble arrays.
    /// @param _a First nibble array.
    /// @param _b Second nibble array.
    /// @return shared_ Number of shared nibbles.
    function _getSharedNibbleLength(bytes memory _a, bytes memory _b) private pure returns (uint256 shared_) {
        uint256 max = (_a.length < _b.length) ? _a.length : _b.length;
        for (; shared_ < max && _a[shared_] == _b[shared_];) {
            unchecked {
                ++shared_;
            }
        }
    }
}

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

/// @notice Library for RLP encoding and CREATE address computation.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibRLP.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibRLP.sol)
library LibRLP {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STRUCTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev A pointer to a RLP item list in memory.
    struct List {
        // Do NOT modify the `_data` directly.
        uint256 _data;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                 CREATE ADDRESS PREDICTION                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the address where a contract will be stored if deployed via
    /// `deployer` with `nonce` using the `CREATE` opcode.
    /// For the specification of the Recursive Length Prefix (RLP)
    /// encoding scheme, please refer to p. 19 of the Ethereum Yellow Paper
    /// (https://ethereum.github.io/yellowpaper/paper.pdf)
    /// and the Ethereum Wiki (https://eth.wiki/fundamentals/rlp).
    ///
    /// Based on the EIP-161 (https://github.com/ethereum/EIPs/blob/master/EIPS/eip-161.md)
    /// specification, all contract accounts on the Ethereum mainnet are initiated with
    /// `nonce = 1`. Thus, the first contract address created by another contract
    /// is calculated with a non-zero nonce.
    ///
    /// The theoretical allowed limit, based on EIP-2681
    /// (https://eips.ethereum.org/EIPS/eip-2681), for an account nonce is 2**64-2.
    ///
    /// Caution! This function will NOT check that the nonce is within the theoretical range.
    /// This is for performance, as exceeding the range is extremely impractical.
    /// It is the user's responsibility to ensure that the nonce is valid
    /// (e.g. no dirty bits after packing / unpacking).
    ///
    /// This is equivalent to:
    /// `address(uint160(uint256(keccak256(LibRLP.p(deployer).p(nonce).encode()))))`.
    ///
    /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
    function computeAddress(address deployer, uint256 nonce)
        internal
        pure
        returns (address deployed)
    {
        /// @solidity memory-safe-assembly
        assembly {
            for {} 1 {} {
                // The integer zero is treated as an empty byte string,
                // and as a result it only has a length prefix, 0x80,
                // computed via `0x80 + 0`.

                // A one-byte integer in the [0x00, 0x7f] range uses its
                // own value as a length prefix,
                // there is no additional `0x80 + length` prefix that precedes it.
                if iszero(gt(nonce, 0x7f)) {
                    mstore(0x00, deployer)
                    // Using `mstore8` instead of `or` naturally cleans
                    // any dirty upper bits of `deployer`.
                    mstore8(0x0b, 0x94)
                    mstore8(0x0a, 0xd6)
                    // `shl` 7 is equivalent to multiplying by 0x80.
                    mstore8(0x20, or(shl(7, iszero(nonce)), nonce))
                    deployed := keccak256(0x0a, 0x17)
                    break
                }
                let i := 8
                // Just use a loop to generalize all the way with minimal bytecode size.
                for {} shr(i, nonce) { i := add(i, 8) } {}
                // `shr` 3 is equivalent to dividing by 8.
                i := shr(3, i)
                // Store in descending slot sequence to overlap the values correctly.
                mstore(i, nonce)
                mstore(0x00, shl(8, deployer))
                mstore8(0x1f, add(0x80, i))
                mstore8(0x0a, 0x94)
                mstore8(0x09, add(0xd6, i))
                deployed := keccak256(0x09, add(0x17, i))
                break
            }
        }
    }

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

    // Note:
    // - addresses are treated like byte strings of length 20, agnostic of leading zero bytes.
    // - uint256s are converted to byte strings, stripped of leading zero bytes, and encoded.
    // - bools are converted to uint256s (`b ? 1 : 0`), then encoded with the uint256.
    // - For bytes1 to bytes32, you must manually convert them to bytes memory
    //   with `abi.encodePacked(x)` before encoding.

    /// @dev Returns a new empty list.
    function p() internal pure returns (List memory result) {}

    /// @dev Returns a new list with `x` as the only element. Equivalent to `LibRLP.p().p(x)`.
    function p(uint256 x) internal pure returns (List memory result) {
        p(result, x);
    }

    /// @dev Returns a new list with `x` as the only element. Equivalent to `LibRLP.p().p(x)`.
    function p(address x) internal pure returns (List memory result) {
        p(result, x);
    }

    /// @dev Returns a new list with `x` as the only element. Equivalent to `LibRLP.p().p(x)`.
    function p(bool x) internal pure returns (List memory result) {
        p(result, x);
    }

    /// @dev Returns a new list with `x` as the only element. Equivalent to `LibRLP.p().p(x)`.
    function p(bytes memory x) internal pure returns (List memory result) {
        p(result, x);
    }

    /// @dev Returns a new list with `x` as the only element. Equivalent to `LibRLP.p().p(x)`.
    function p(List memory x) internal pure returns (List memory result) {
        p(result, x);
    }

    /// @dev Appends `x` to `list`. Returns `list` for function chaining.
    function p(List memory list, uint256 x) internal pure returns (List memory result) {
        result._data = x << 48;
        _updateTail(list, result);
        /// @solidity memory-safe-assembly
        assembly {
            // If `x` is too big, we cannot pack it inline with the node.
            // We'll have to allocate a new slot for `x` and store the pointer to it in the node.
            if shr(208, x) {
                let m := mload(0x40)
                mstore(m, x)
                mstore(0x40, add(m, 0x20))
                mstore(result, shl(40, or(1, shl(8, m))))
            }
        }
        result = list;
    }

    /// @dev Appends `x` to `list`. Returns `list` for function chaining.
    function p(List memory list, address x) internal pure returns (List memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(result, shl(40, or(4, shl(8, x))))
        }
        _updateTail(list, result);
        result = list;
    }

    /// @dev Appends `x` to `list`. Returns `list` for function chaining.
    function p(List memory list, bool x) internal pure returns (List memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(result, shl(48, iszero(iszero(x))))
        }
        _updateTail(list, result);
        result = list;
    }

    /// @dev Appends `x` to `list`. Returns `list` for function chaining.
    function p(List memory list, bytes memory x) internal pure returns (List memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(result, shl(40, or(2, shl(8, x))))
        }
        _updateTail(list, result);
        result = list;
    }

    /// @dev Appends `x` to `list`. Returns `list` for function chaining.
    function p(List memory list, List memory x) internal pure returns (List memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(result, shl(40, or(3, shl(8, x))))
        }
        _updateTail(list, result);
        result = list;
    }

    /// @dev Returns the RLP encoding of `list`.
    function encode(List memory list) internal pure returns (bytes memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            function encodeUint(x_, o_) -> _o {
                _o := add(o_, 1)
                if iszero(gt(x_, 0x7f)) {
                    mstore8(o_, or(shl(7, iszero(x_)), x_)) // Copy `x_`.
                    leave
                }
                let r_ := shl(7, lt(0xffffffffffffffffffffffffffffffff, x_))
                r_ := or(r_, shl(6, lt(0xffffffffffffffff, shr(r_, x_))))
                r_ := or(r_, shl(5, lt(0xffffffff, shr(r_, x_))))
                r_ := or(r_, shl(4, lt(0xffff, shr(r_, x_))))
                r_ := or(shr(3, r_), lt(0xff, shr(r_, x_)))
                mstore8(o_, add(r_, 0x81)) // Store the prefix.
                mstore(0x00, x_)
                mstore(_o, mload(xor(31, r_))) // Copy `x_`.
                _o := add(add(1, r_), _o)
            }
            function encodeAddress(x_, o_) -> _o {
                _o := add(o_, 0x15)
                mstore(o_, shl(88, x_))
                mstore8(o_, 0x94)
            }
            function encodeBytes(x_, o_, c_) -> _o {
                _o := add(o_, 1)
                let n_ := mload(x_)
                if iszero(gt(n_, 55)) {
                    let f_ := mload(add(0x20, x_))
                    if iszero(and(eq(1, n_), lt(byte(0, f_), 0x80))) {
                        mstore8(o_, add(n_, c_)) // Store the prefix.
                        mstore(add(0x21, o_), mload(add(0x40, x_)))
                        mstore(_o, f_)
                        _o := add(n_, _o)
                        leave
                    }
                    mstore(o_, f_) // Copy `x_`.
                    leave
                }
                returndatacopy(returndatasize(), returndatasize(), shr(32, n_))
                let r_ := add(1, add(lt(0xff, n_), add(lt(0xffff, n_), lt(0xffffff, n_))))
                mstore(o_, shl(248, add(r_, add(c_, 55)))) // Store the prefix.
                // Copy `x`.
                let i_ := add(r_, _o)
                _o := add(i_, n_)
                for { let d_ := sub(add(0x20, x_), i_) } 1 {} {
                    mstore(i_, mload(add(d_, i_)))
                    i_ := add(i_, 0x20)
                    if iszero(lt(i_, _o)) { break }
                }
                mstore(o_, or(mload(o_), shl(sub(248, shl(3, r_)), n_))) // Store the prefix.
            }
            function encodeList(l_, o_) -> _o {
                if iszero(mload(l_)) {
                    mstore8(o_, 0xc0)
                    _o := add(o_, 1)
                    leave
                }
                let j_ := add(o_, 0x20)
                for { let h_ := l_ } 1 {} {
                    h_ := and(mload(h_), 0xffffffffff)
                    if iszero(h_) { break }
                    let t_ := byte(26, mload(h_))
                    if iszero(gt(t_, 1)) {
                        if iszero(t_) {
                            j_ := encodeUint(shr(48, mload(h_)), j_)
                            continue
                        }
                        j_ := encodeUint(mload(shr(48, mload(h_))), j_)
                        continue
                    }
                    if eq(t_, 2) {
                        j_ := encodeBytes(shr(48, mload(h_)), j_, 0x80)
                        continue
                    }
                    if eq(t_, 3) {
                        j_ := encodeList(shr(48, mload(h_)), j_)
                        continue
                    }
                    j_ := encodeAddress(shr(48, mload(h_)), j_)
                }
                let n_ := sub(j_, add(o_, 0x20))
                if iszero(gt(n_, 55)) {
                    mstore8(o_, add(n_, 0xc0)) // Store the prefix.
                    mstore(add(0x01, o_), mload(add(0x20, o_)))
                    mstore(add(0x21, o_), mload(add(0x40, o_)))
                    _o := add(n_, add(0x01, o_))
                    leave
                }
                mstore(o_, n_)
                _o := encodeBytes(o_, o_, 0xc0)
            }
            result := mload(0x40)
            let begin := add(result, 0x20)
            let end := encodeList(list, begin)
            mstore(result, sub(end, begin)) // Store the length of `result`.
            mstore(end, 0) // Zeroize the slot after `result`.
            mstore(0x40, add(end, 0x20)) // Allocate memory for `result`.
        }
    }

    /// @dev Returns the RLP encoding of `x`.
    function encode(uint256 x) internal pure returns (bytes memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            for {} 1 {} {
                result := mload(0x40)
                if iszero(gt(x, 0x7f)) {
                    mstore(result, 1) // Store the length of `result`.
                    mstore(add(result, 0x20), shl(248, or(shl(7, iszero(x)), x))) // Copy `x`.
                    mstore(0x40, add(result, 0x40)) // Allocate memory for `result`.
                    break
                }
                let r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
                r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
                r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
                r := or(r, shl(4, lt(0xffff, shr(r, x))))
                r := add(2, or(shr(3, r), lt(0xff, shr(r, x))))
                mstore(add(r, result), x) // Copy `x`.
                mstore(add(result, 1), add(r, 0x7f)) // Store the prefix.
                mstore(result, r) // Store the length of `result`.
                mstore(add(r, add(result, 0x20)), 0) // Zeroize the slot after `result`.
                mstore(0x40, add(result, 0x60)) // Allocate memory for `result`.
                break
            }
        }
    }

    /// @dev Returns the RLP encoding of `x`.
    function encode(address x) internal pure returns (bytes memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            mstore(result, 0x15)
            let o := add(0x20, result)
            mstore(o, shl(88, x))
            mstore8(o, 0x94)
            mstore(0x40, add(0x20, o))
        }
    }

    /// @dev Returns the RLP encoding of `x`.
    function encode(bool x) internal pure returns (bytes memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            mstore(result, 1)
            mstore(add(0x20, result), shl(add(0xf8, mul(7, iszero(x))), 0x01))
            mstore(0x40, add(0x40, result))
        }
    }

    /// @dev Returns the RLP encoding of `x`.
    function encode(bytes memory x) internal pure returns (bytes memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := x

            for {} iszero(and(eq(1, mload(x)), lt(byte(0, mload(add(x, 0x20))), 0x80))) {} {
                result := mload(0x40)
                let n := mload(x) // Length of `x`.
                if iszero(gt(n, 55)) {
                    mstore(0x40, add(result, 0x60))
                    mstore(add(0x41, result), mload(add(0x40, x)))
                    mstore(add(0x21, result), mload(add(0x20, x)))
                    mstore(add(1, result), add(n, 0x80)) // Store the prefix.
                    mstore(result, add(1, n)) // Store the length of `result`.
                    mstore(add(add(result, 0x21), n), 0) // Zeroize the slot after `result`.
                    break
                }
                returndatacopy(returndatasize(), returndatasize(), shr(32, n))
                let r := add(2, add(lt(0xff, n), add(lt(0xffff, n), lt(0xffffff, n))))
                // Copy `x`.
                let i := add(r, add(0x20, result))
                let end := add(i, n)
                for { let d := sub(add(0x20, x), i) } 1 {} {
                    mstore(i, mload(add(d, i)))
                    i := add(i, 0x20)
                    if iszero(lt(i, end)) { break }
                }
                mstore(add(r, result), n) // Store the prefix.
                mstore(add(1, result), add(r, 0xb6)) // Store the prefix.
                mstore(result, add(r, n)) // Store the length of `result`.
                mstore(end, 0) // Zeroize the slot after `result`.
                mstore(0x40, add(end, 0x20)) // Allocate memory.
                break
            }
        }
    }

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

    /// @dev Updates the tail in `list`.
    function _updateTail(List memory list, List memory result) private pure {
        /// @solidity memory-safe-assembly
        assembly {
            let v := or(shr(mload(list), result), mload(list))
            let tail := shr(40, v)
            mstore(list, xor(shl(40, xor(tail, result)), v)) // Update the tail.
            mstore(tail, or(mload(tail), result)) // Make the previous tail point to `result`.
        }
    }
}

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

pragma solidity ^0.8.0;

import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20, IERC20Metadata {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * The default value of {decimals} is 18. To select a different value for
     * {decimals} you should overload it.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless this function is
     * overridden;
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

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

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address to, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `amount`.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual override returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, amount);
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, allowance(owner, spender) + addedValue);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        address owner = _msgSender();
        uint256 currentAllowance = allowance(owner, spender);
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(owner, spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     */
    function _transfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(from, to, amount);

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
        }
        _balances[to] += amount;

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        _balances[account] += amount;
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
        }
        _totalSupply -= amount;

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
     *
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Might emit an {Approval} event.
     */
    function _spendAllowance(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }

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

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}
}

// SPDX-License-Identifier: Apache-2.0

/*
 * Copyright 2019-2021, Offchain Labs, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

pragma solidity ^0.8.0;

library AddressAliasHelper {
    uint160 constant offset = uint160(0x1111000000000000000000000000000000001111);

    /// @notice Utility function that converts the address in the L1 that submitted a tx to
    /// the inbox to the msg.sender viewed in the L2
    /// @param l1Address the address in the L1 that triggered the tx to L2
    /// @return l2Address L2 address as viewed in msg.sender
    function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) {
        unchecked {
            l2Address = address(uint160(l1Address) + offset);
        }
    }

    /// @notice Utility function that converts the msg.sender viewed in the L2 to the
    /// address in the L1 that submitted a tx to the inbox
    /// @param l2Address L2 address as viewed in msg.sender
    /// @return l1Address the address in the L1 that triggered the tx to L2
    function undoL1ToL2Alias(address l2Address) internal pure returns (address l1Address) {
        unchecked {
            l1Address = address(uint160(l2Address) - offset);
        }
    }
}

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

/// @title SP1 Verifier Interface
/// @author Succinct Labs
/// @notice This contract is the interface for the SP1 Verifier.
interface ISP1Verifier {
    /// @notice Verifies a proof with given public values and vkey.
    /// @dev It is expected that the first 4 bytes of proofBytes must match the first 4 bytes of
    /// target verifier's VERIFIER_HASH.
    /// @param programVKey The verification key for the RISC-V program.
    /// @param publicValues The public values encoded as bytes.
    /// @param proofBytes The proof of the program execution the SP1 zkVM encoded as bytes.
    function verifyProof(
        bytes32 programVKey,
        bytes calldata publicValues,
        bytes calldata proofBytes
    ) external view;
}

interface ISP1VerifierWithHash is ISP1Verifier {
    /// @notice Returns the hash of the verifier.
    function VERIFIER_HASH() external pure returns (bytes32);
}

File 20 of 26 : SafeCastLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Safe integer casting library that reverts on overflow.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeCastLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeCast.sol)
library SafeCastLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    error Overflow();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*          UNSIGNED INTEGER SAFE CASTING OPERATIONS          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    function toUint8(uint256 x) internal pure returns (uint8) {
        if (x >= 1 << 8) _revertOverflow();
        return uint8(x);
    }

    function toUint16(uint256 x) internal pure returns (uint16) {
        if (x >= 1 << 16) _revertOverflow();
        return uint16(x);
    }

    function toUint24(uint256 x) internal pure returns (uint24) {
        if (x >= 1 << 24) _revertOverflow();
        return uint24(x);
    }

    function toUint32(uint256 x) internal pure returns (uint32) {
        if (x >= 1 << 32) _revertOverflow();
        return uint32(x);
    }

    function toUint40(uint256 x) internal pure returns (uint40) {
        if (x >= 1 << 40) _revertOverflow();
        return uint40(x);
    }

    function toUint48(uint256 x) internal pure returns (uint48) {
        if (x >= 1 << 48) _revertOverflow();
        return uint48(x);
    }

    function toUint56(uint256 x) internal pure returns (uint56) {
        if (x >= 1 << 56) _revertOverflow();
        return uint56(x);
    }

    function toUint64(uint256 x) internal pure returns (uint64) {
        if (x >= 1 << 64) _revertOverflow();
        return uint64(x);
    }

    function toUint72(uint256 x) internal pure returns (uint72) {
        if (x >= 1 << 72) _revertOverflow();
        return uint72(x);
    }

    function toUint80(uint256 x) internal pure returns (uint80) {
        if (x >= 1 << 80) _revertOverflow();
        return uint80(x);
    }

    function toUint88(uint256 x) internal pure returns (uint88) {
        if (x >= 1 << 88) _revertOverflow();
        return uint88(x);
    }

    function toUint96(uint256 x) internal pure returns (uint96) {
        if (x >= 1 << 96) _revertOverflow();
        return uint96(x);
    }

    function toUint104(uint256 x) internal pure returns (uint104) {
        if (x >= 1 << 104) _revertOverflow();
        return uint104(x);
    }

    function toUint112(uint256 x) internal pure returns (uint112) {
        if (x >= 1 << 112) _revertOverflow();
        return uint112(x);
    }

    function toUint120(uint256 x) internal pure returns (uint120) {
        if (x >= 1 << 120) _revertOverflow();
        return uint120(x);
    }

    function toUint128(uint256 x) internal pure returns (uint128) {
        if (x >= 1 << 128) _revertOverflow();
        return uint128(x);
    }

    function toUint136(uint256 x) internal pure returns (uint136) {
        if (x >= 1 << 136) _revertOverflow();
        return uint136(x);
    }

    function toUint144(uint256 x) internal pure returns (uint144) {
        if (x >= 1 << 144) _revertOverflow();
        return uint144(x);
    }

    function toUint152(uint256 x) internal pure returns (uint152) {
        if (x >= 1 << 152) _revertOverflow();
        return uint152(x);
    }

    function toUint160(uint256 x) internal pure returns (uint160) {
        if (x >= 1 << 160) _revertOverflow();
        return uint160(x);
    }

    function toUint168(uint256 x) internal pure returns (uint168) {
        if (x >= 1 << 168) _revertOverflow();
        return uint168(x);
    }

    function toUint176(uint256 x) internal pure returns (uint176) {
        if (x >= 1 << 176) _revertOverflow();
        return uint176(x);
    }

    function toUint184(uint256 x) internal pure returns (uint184) {
        if (x >= 1 << 184) _revertOverflow();
        return uint184(x);
    }

    function toUint192(uint256 x) internal pure returns (uint192) {
        if (x >= 1 << 192) _revertOverflow();
        return uint192(x);
    }

    function toUint200(uint256 x) internal pure returns (uint200) {
        if (x >= 1 << 200) _revertOverflow();
        return uint200(x);
    }

    function toUint208(uint256 x) internal pure returns (uint208) {
        if (x >= 1 << 208) _revertOverflow();
        return uint208(x);
    }

    function toUint216(uint256 x) internal pure returns (uint216) {
        if (x >= 1 << 216) _revertOverflow();
        return uint216(x);
    }

    function toUint224(uint256 x) internal pure returns (uint224) {
        if (x >= 1 << 224) _revertOverflow();
        return uint224(x);
    }

    function toUint232(uint256 x) internal pure returns (uint232) {
        if (x >= 1 << 232) _revertOverflow();
        return uint232(x);
    }

    function toUint240(uint256 x) internal pure returns (uint240) {
        if (x >= 1 << 240) _revertOverflow();
        return uint240(x);
    }

    function toUint248(uint256 x) internal pure returns (uint248) {
        if (x >= 1 << 248) _revertOverflow();
        return uint248(x);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*           SIGNED INTEGER SAFE CASTING OPERATIONS           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    function toInt8(int256 x) internal pure returns (int8) {
        int8 y = int8(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt16(int256 x) internal pure returns (int16) {
        int16 y = int16(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt24(int256 x) internal pure returns (int24) {
        int24 y = int24(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt32(int256 x) internal pure returns (int32) {
        int32 y = int32(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt40(int256 x) internal pure returns (int40) {
        int40 y = int40(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt48(int256 x) internal pure returns (int48) {
        int48 y = int48(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt56(int256 x) internal pure returns (int56) {
        int56 y = int56(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt64(int256 x) internal pure returns (int64) {
        int64 y = int64(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt72(int256 x) internal pure returns (int72) {
        int72 y = int72(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt80(int256 x) internal pure returns (int80) {
        int80 y = int80(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt88(int256 x) internal pure returns (int88) {
        int88 y = int88(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt96(int256 x) internal pure returns (int96) {
        int96 y = int96(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt104(int256 x) internal pure returns (int104) {
        int104 y = int104(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt112(int256 x) internal pure returns (int112) {
        int112 y = int112(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt120(int256 x) internal pure returns (int120) {
        int120 y = int120(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt128(int256 x) internal pure returns (int128) {
        int128 y = int128(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt136(int256 x) internal pure returns (int136) {
        int136 y = int136(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt144(int256 x) internal pure returns (int144) {
        int144 y = int144(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt152(int256 x) internal pure returns (int152) {
        int152 y = int152(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt160(int256 x) internal pure returns (int160) {
        int160 y = int160(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt168(int256 x) internal pure returns (int168) {
        int168 y = int168(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt176(int256 x) internal pure returns (int176) {
        int176 y = int176(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt184(int256 x) internal pure returns (int184) {
        int184 y = int184(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt192(int256 x) internal pure returns (int192) {
        int192 y = int192(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt200(int256 x) internal pure returns (int200) {
        int200 y = int200(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt208(int256 x) internal pure returns (int208) {
        int208 y = int208(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt216(int256 x) internal pure returns (int216) {
        int216 y = int216(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt224(int256 x) internal pure returns (int224) {
        int224 y = int224(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt232(int256 x) internal pure returns (int232) {
        int232 y = int232(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt240(int256 x) internal pure returns (int240) {
        int240 y = int240(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt248(int256 x) internal pure returns (int248) {
        int248 y = int248(x);
        if (x != y) _revertOverflow();
        return y;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*               OTHER SAFE CASTING OPERATIONS                */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    function toInt256(uint256 x) internal pure returns (int256) {
        if (x >= 1 << 255) _revertOverflow();
        return int256(x);
    }

    function toUint256(int256 x) internal pure returns (uint256) {
        if (x < 0) _revertOverflow();
        return uint256(x);
    }

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

    function _revertOverflow() private pure {
        /// @solidity memory-safe-assembly
        assembly {
            // Store the function selector of `Overflow()`.
            mstore(0x00, 0x35278d12)
            // Revert with (offset, size).
            revert(0x1c, 0x04)
        }
    }
}

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

/// @custom:attribution https://github.com/bakaoh/solidity-rlp-encode
/// @title RLPWriter
/// @author RLPWriter is a library for encoding Solidity types to RLP bytes. Adapted from Bakaoh's
///         RLPEncode library (https://github.com/bakaoh/solidity-rlp-encode) with minor
///         modifications to improve legibility.
library RLPWriter {
    /// @notice RLP encodes a byte string.
    /// @param _in The byte string to encode.
    /// @return out_ The RLP encoded string in bytes.
    function writeBytes(bytes memory _in) internal pure returns (bytes memory out_) {
        if (_in.length == 1 && uint8(_in[0]) < 128) {
            out_ = _in;
        } else {
            out_ = abi.encodePacked(_writeLength(_in.length, 128), _in);
        }
    }

    /// @notice RLP encodes a list of RLP encoded byte byte strings.
    /// @param _in The list of RLP encoded byte strings.
    /// @return list_ The RLP encoded list of items in bytes.
    function writeList(bytes[] memory _in) internal pure returns (bytes memory list_) {
        list_ = _flatten(_in);
        list_ = abi.encodePacked(_writeLength(list_.length, 192), list_);
    }

    /// @notice RLP encodes a string.
    /// @param _in The string to encode.
    /// @return out_ The RLP encoded string in bytes.
    function writeString(string memory _in) internal pure returns (bytes memory out_) {
        out_ = writeBytes(bytes(_in));
    }

    /// @notice RLP encodes an address.
    /// @param _in The address to encode.
    /// @return out_ The RLP encoded address in bytes.
    function writeAddress(address _in) internal pure returns (bytes memory out_) {
        out_ = writeBytes(abi.encodePacked(_in));
    }

    /// @notice RLP encodes a uint.
    /// @param _in The uint256 to encode.
    /// @return out_ The RLP encoded uint256 in bytes.
    function writeUint(uint256 _in) internal pure returns (bytes memory out_) {
        out_ = writeBytes(_toBinary(_in));
    }

    /// @notice RLP encodes a bool.
    /// @param _in The bool to encode.
    /// @return out_ The RLP encoded bool in bytes.
    function writeBool(bool _in) internal pure returns (bytes memory out_) {
        out_ = new bytes(1);
        out_[0] = (_in ? bytes1(0x01) : bytes1(0x80));
    }

    /// @notice Encode the first byte and then the `len` in binary form if `length` is more than 55.
    /// @param _len    The length of the string or the payload.
    /// @param _offset 128 if item is string, 192 if item is list.
    /// @return out_ RLP encoded bytes.
    function _writeLength(uint256 _len, uint256 _offset) private pure returns (bytes memory out_) {
        if (_len < 56) {
            out_ = new bytes(1);
            out_[0] = bytes1(uint8(_len) + uint8(_offset));
        } else {
            uint256 lenLen;
            uint256 i = 1;
            while (_len / i != 0) {
                lenLen++;
                i *= 256;
            }

            out_ = new bytes(lenLen + 1);
            out_[0] = bytes1(uint8(lenLen) + uint8(_offset) + 55);
            for (i = 1; i <= lenLen; i++) {
                out_[i] = bytes1(uint8((_len / (256 ** (lenLen - i))) % 256));
            }
        }
    }

    /// @notice Encode integer in big endian binary form with no leading zeroes.
    /// @param _x The integer to encode.
    /// @return out_ RLP encoded bytes.
    function _toBinary(uint256 _x) private pure returns (bytes memory out_) {
        bytes memory b = abi.encodePacked(_x);

        uint256 i = 0;
        for (; i < 32; i++) {
            if (b[i] != 0) {
                break;
            }
        }

        out_ = new bytes(32 - i);
        for (uint256 j = 0; j < out_.length; j++) {
            out_[j] = b[i++];
        }
    }

    /// @custom:attribution https://github.com/Arachnid/solidity-stringutils
    /// @notice Copies a piece of memory to another location.
    /// @param _dest Destination location.
    /// @param _src  Source location.
    /// @param _len  Length of memory to copy.
    function _memcpy(uint256 _dest, uint256 _src, uint256 _len) private pure {
        uint256 dest = _dest;
        uint256 src = _src;
        uint256 len = _len;

        for (; len >= 32; len -= 32) {
            assembly {
                mstore(dest, mload(src))
            }
            dest += 32;
            src += 32;
        }

        uint256 mask;
        unchecked {
            mask = 256 ** (32 - len) - 1;
        }
        assembly {
            let srcpart := and(mload(src), not(mask))
            let destpart := and(mload(dest), mask)
            mstore(dest, or(destpart, srcpart))
        }
    }

    /// @custom:attribution https://github.com/sammayo/solidity-rlp-encoder
    /// @notice Flattens a list of byte strings into one byte string.
    /// @param _list List of byte strings to flatten.
    /// @return out_ The flattened byte string.
    function _flatten(bytes[] memory _list) private pure returns (bytes memory out_) {
        if (_list.length == 0) {
            return new bytes(0);
        }

        uint256 len;
        uint256 i = 0;
        for (; i < _list.length; i++) {
            len += _list[i].length;
        }

        out_ = new bytes(len);
        uint256 flattenedPtr;
        assembly {
            flattenedPtr := add(out_, 0x20)
        }

        for (i = 0; i < _list.length; i++) {
            bytes memory item = _list[i];

            uint256 listPtr;
            assembly {
                listPtr := add(item, 0x20)
            }

            _memcpy(flattenedPtr, listPtr, item.length);
            flattenedPtr += _list[i].length;
        }
    }
}

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

/// @title Bytes
/// @notice Bytes is a library for manipulating byte arrays.
library Bytes {
    /// @custom:attribution https://github.com/GNSPS/solidity-bytes-utils
    /// @notice Slices a byte array with a given starting index and length. Returns a new byte array
    ///         as opposed to a pointer to the original array. Will throw if trying to slice more
    ///         bytes than exist in the array.
    /// @param _bytes Byte array to slice.
    /// @param _start Starting index of the slice.
    /// @param _length Length of the slice.
    /// @return Slice of the input byte array.
    function slice(bytes memory _bytes, uint256 _start, uint256 _length) internal pure returns (bytes memory) {
        unchecked {
            require(_length + 31 >= _length, "slice_overflow");
            require(_start + _length >= _start, "slice_overflow");
            require(_bytes.length >= _start + _length, "slice_outOfBounds");
        }

        bytes memory tempBytes;

        assembly {
            switch iszero(_length)
            case 0 {
                // Get a location of some free memory and store it in tempBytes as
                // Solidity does for memory variables.
                tempBytes := mload(0x40)

                // The first word of the slice result is potentially a partial
                // word read from the original array. To read it, we calculate
                // the length of that partial word and start copying that many
                // bytes into the array. The first word we copy will start with
                // data we don't care about, but the last `lengthmod` bytes will
                // land at the beginning of the contents of the new array. When
                // we're done copying, we overwrite the full first word with
                // the actual length of the slice.
                let lengthmod := and(_length, 31)

                // The multiplication in the next line is necessary
                // because when slicing multiples of 32 bytes (lengthmod == 0)
                // the following copy loop was copying the origin's length
                // and then ending prematurely not copying everything it should.
                let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
                let end := add(mc, _length)

                for {
                    // The multiplication in the next line has the same exact purpose
                    // as the one above.
                    let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
                } lt(mc, end) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } { mstore(mc, mload(cc)) }

                mstore(tempBytes, _length)

                //update free-memory pointer
                //allocating the array padded to 32 bytes like the compiler does now
                mstore(0x40, and(add(mc, 31), not(31)))
            }
            //if we want a zero-length slice let's just return a zero-length array
            default {
                tempBytes := mload(0x40)

                //zero out the 32 bytes slice we are about to return
                //we need to do it because Solidity does not garbage collect
                mstore(tempBytes, 0)

                mstore(0x40, add(tempBytes, 0x20))
            }
        }

        return tempBytes;
    }

    /// @notice Slices a byte array with a given starting index up to the end of the original byte
    ///         array. Returns a new array rathern than a pointer to the original.
    /// @param _bytes Byte array to slice.
    /// @param _start Starting index of the slice.
    /// @return Slice of the input byte array.
    function slice(bytes memory _bytes, uint256 _start) internal pure returns (bytes memory) {
        if (_start >= _bytes.length) {
            return bytes("");
        }
        return slice(_bytes, _start, _bytes.length - _start);
    }

    /// @notice Converts a byte array into a nibble array by splitting each byte into two nibbles.
    ///         Resulting nibble array will be exactly twice as long as the input byte array.
    /// @param _bytes Input byte array to convert.
    /// @return Resulting nibble array.
    function toNibbles(bytes memory _bytes) internal pure returns (bytes memory) {
        bytes memory _nibbles;
        assembly {
            // Grab a free memory offset for the new array
            _nibbles := mload(0x40)

            // Load the length of the passed bytes array from memory
            let bytesLength := mload(_bytes)

            // Calculate the length of the new nibble array
            // This is the length of the input array times 2
            let nibblesLength := shl(0x01, bytesLength)

            // Update the free memory pointer to allocate memory for the new array.
            // To do this, we add the length of the new array + 32 bytes for the array length
            // rounded up to the nearest 32 byte boundary to the current free memory pointer.
            mstore(0x40, add(_nibbles, and(not(0x1F), add(nibblesLength, 0x3F))))

            // Store the length of the new array in memory
            mstore(_nibbles, nibblesLength)

            // Store the memory offset of the _bytes array's contents on the stack
            let bytesStart := add(_bytes, 0x20)

            // Store the memory offset of the nibbles array's contents on the stack
            let nibblesStart := add(_nibbles, 0x20)

            // Loop through each byte in the input array
            for { let i := 0x00 } lt(i, bytesLength) { i := add(i, 0x01) } {
                // Get the starting offset of the next 2 bytes in the nibbles array
                let offset := add(nibblesStart, shl(0x01, i))
                // Load the byte at the current index within the `_bytes` array
                let b := byte(0x00, mload(add(bytesStart, i)))

                // Pull out the first nibble and store it in the new array
                mstore8(offset, shr(0x04, b))
                // Pull out the second nibble and store it in the new array
                mstore8(add(offset, 0x01), and(b, 0x0F))
            }
        }
        return _nibbles;
    }

    /// @notice Compares two byte arrays by comparing their keccak256 hashes.
    /// @param _bytes First byte array to compare.
    /// @param _other Second byte array to compare.
    /// @return True if the two byte arrays are equal, false otherwise.
    function equal(bytes memory _bytes, bytes memory _other) internal pure returns (bool) {
        return keccak256(_bytes) == keccak256(_other);
    }
}

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

// Libraries
import {
    EmptyItem,
    UnexpectedString,
    InvalidDataRemainder,
    ContentLengthMismatch,
    InvalidHeader,
    UnexpectedList
} from "src/libraries/rlp/RLPErrors.sol";

/// @custom:attribution https://github.com/hamdiallam/Solidity-RLP
/// @title RLPReader
/// @notice RLPReader is a library for parsing RLP-encoded byte arrays into Solidity types. Adapted
///         from Solidity-RLP (https://github.com/hamdiallam/Solidity-RLP) by Hamdi Allam with
///         various tweaks to improve readability.
library RLPReader {
    /// @notice Custom pointer type to avoid confusion between pointers and uint256s.
    type MemoryPointer is uint256;

    /// @notice RLP item types.
    /// @custom:value DATA_ITEM Represents an RLP data item (NOT a list).
    /// @custom:value LIST_ITEM Represents an RLP list item.
    enum RLPItemType {
        DATA_ITEM,
        LIST_ITEM
    }

    /// @notice Struct representing an RLP item.
    /// @custom:field length Length of the RLP item.
    /// @custom:field ptr    Pointer to the RLP item in memory.
    struct RLPItem {
        uint256 length;
        MemoryPointer ptr;
    }

    /// @notice Max list length that this library will accept.
    uint256 internal constant MAX_LIST_LENGTH = 32;

    /// @notice Converts bytes to a reference to memory position and length.
    /// @param _in Input bytes to convert.
    /// @return out_ Output memory reference.
    function toRLPItem(bytes memory _in) internal pure returns (RLPItem memory out_) {
        // Empty arrays are not RLP items.
        if (_in.length == 0) revert EmptyItem();

        MemoryPointer ptr;
        assembly {
            ptr := add(_in, 32)
        }

        out_ = RLPItem({ length: _in.length, ptr: ptr });
    }

    /// @notice Reads an RLP list value into a list of RLP items.
    /// @param _in RLP list value.
    /// @return out_ Decoded RLP list items.
    function readList(RLPItem memory _in) internal pure returns (RLPItem[] memory out_) {
        (uint256 listOffset, uint256 listLength, RLPItemType itemType) = _decodeLength(_in);

        if (itemType != RLPItemType.LIST_ITEM) revert UnexpectedString();

        if (listOffset + listLength != _in.length) revert InvalidDataRemainder();

        // Solidity in-memory arrays can't be increased in size, but *can* be decreased in size by
        // writing to the length. Since we can't know the number of RLP items without looping over
        // the entire input, we'd have to loop twice to accurately size this array. It's easier to
        // simply set a reasonable maximum list length and decrease the size before we finish.
        out_ = new RLPItem[](MAX_LIST_LENGTH);

        uint256 itemCount = 0;
        uint256 offset = listOffset;
        while (offset < _in.length) {
            (uint256 itemOffset, uint256 itemLength,) = _decodeLength(
                RLPItem({ length: _in.length - offset, ptr: MemoryPointer.wrap(MemoryPointer.unwrap(_in.ptr) + offset) })
            );

            // We don't need to check itemCount < out.length explicitly because Solidity already
            // handles this check on our behalf, we'd just be wasting gas.
            out_[itemCount] = RLPItem({
                length: itemLength + itemOffset,
                ptr: MemoryPointer.wrap(MemoryPointer.unwrap(_in.ptr) + offset)
            });

            itemCount += 1;
            offset += itemOffset + itemLength;
        }

        // Decrease the array size to match the actual item count.
        assembly {
            mstore(out_, itemCount)
        }
    }

    /// @notice Reads an RLP list value into a list of RLP items.
    /// @param _in RLP list value.
    /// @return out_ Decoded RLP list items.
    function readList(bytes memory _in) internal pure returns (RLPItem[] memory out_) {
        out_ = readList(toRLPItem(_in));
    }

    /// @notice Reads an RLP bytes value into bytes.
    /// @param _in RLP bytes value.
    /// @return out_ Decoded bytes.
    function readBytes(RLPItem memory _in) internal pure returns (bytes memory out_) {
        (uint256 itemOffset, uint256 itemLength, RLPItemType itemType) = _decodeLength(_in);

        if (itemType != RLPItemType.DATA_ITEM) revert UnexpectedList();

        if (_in.length != itemOffset + itemLength) revert InvalidDataRemainder();

        out_ = _copy(_in.ptr, itemOffset, itemLength);
    }

    /// @notice Reads an RLP bytes value into bytes.
    /// @param _in RLP bytes value.
    /// @return out_ Decoded bytes.
    function readBytes(bytes memory _in) internal pure returns (bytes memory out_) {
        out_ = readBytes(toRLPItem(_in));
    }

    /// @notice Reads the raw bytes of an RLP item.
    /// @param _in RLP item to read.
    /// @return out_ Raw RLP bytes.
    function readRawBytes(RLPItem memory _in) internal pure returns (bytes memory out_) {
        out_ = _copy(_in.ptr, 0, _in.length);
    }

    /// @notice Decodes the length of an RLP item.
    /// @param _in RLP item to decode.
    /// @return offset_ Offset of the encoded data.
    /// @return length_ Length of the encoded data.
    /// @return type_ RLP item type (LIST_ITEM or DATA_ITEM).
    function _decodeLength(RLPItem memory _in)
        private
        pure
        returns (uint256 offset_, uint256 length_, RLPItemType type_)
    {
        // Short-circuit if there's nothing to decode, note that we perform this check when
        // the user creates an RLP item via toRLPItem, but it's always possible for them to bypass
        // that function and create an RLP item directly. So we need to check this anyway.
        if (_in.length == 0) revert EmptyItem();

        MemoryPointer ptr = _in.ptr;
        uint256 prefix;
        assembly {
            prefix := byte(0, mload(ptr))
        }

        if (prefix <= 0x7f) {
            // Single byte.
            return (0, 1, RLPItemType.DATA_ITEM);
        } else if (prefix <= 0xb7) {
            // Short string.

            // slither-disable-next-line variable-scope
            uint256 strLen = prefix - 0x80;

            if (_in.length <= strLen) revert ContentLengthMismatch();

            bytes1 firstByteOfContent;
            assembly {
                firstByteOfContent := and(mload(add(ptr, 1)), shl(248, 0xff))
            }

            if (strLen == 1 && firstByteOfContent < 0x80) revert InvalidHeader();

            return (1, strLen, RLPItemType.DATA_ITEM);
        } else if (prefix <= 0xbf) {
            // Long string.
            uint256 lenOfStrLen = prefix - 0xb7;

            if (_in.length <= lenOfStrLen) revert ContentLengthMismatch();

            bytes1 firstByteOfContent;
            assembly {
                firstByteOfContent := and(mload(add(ptr, 1)), shl(248, 0xff))
            }

            if (firstByteOfContent == 0x00) revert InvalidHeader();

            uint256 strLen;
            assembly {
                strLen := shr(sub(256, mul(8, lenOfStrLen)), mload(add(ptr, 1)))
            }

            if (strLen <= 55) revert InvalidHeader();

            if (_in.length <= lenOfStrLen + strLen) revert ContentLengthMismatch();

            return (1 + lenOfStrLen, strLen, RLPItemType.DATA_ITEM);
        } else if (prefix <= 0xf7) {
            // Short list.
            // slither-disable-next-line variable-scope
            uint256 listLen = prefix - 0xc0;

            if (_in.length <= listLen) revert ContentLengthMismatch();

            return (1, listLen, RLPItemType.LIST_ITEM);
        } else {
            // Long list.
            uint256 lenOfListLen = prefix - 0xf7;

            if (_in.length <= lenOfListLen) revert ContentLengthMismatch();

            bytes1 firstByteOfContent;
            assembly {
                firstByteOfContent := and(mload(add(ptr, 1)), shl(248, 0xff))
            }

            if (firstByteOfContent == 0x00) revert InvalidHeader();

            uint256 listLen;
            assembly {
                listLen := shr(sub(256, mul(8, lenOfListLen)), mload(add(ptr, 1)))
            }

            if (listLen <= 55) revert InvalidHeader();

            if (_in.length <= lenOfListLen + listLen) revert ContentLengthMismatch();

            return (1 + lenOfListLen, listLen, RLPItemType.LIST_ITEM);
        }
    }

    /// @notice Copies the bytes from a memory location.
    /// @param _src    Pointer to the location to read from.
    /// @param _offset Offset to start reading from.
    /// @param _length Number of bytes to read.
    /// @return out_ Copied bytes.
    function _copy(MemoryPointer _src, uint256 _offset, uint256 _length) private pure returns (bytes memory out_) {
        out_ = new bytes(_length);
        if (_length == 0) {
            return out_;
        }

        // Mostly based on Solidity's copy_memory_to_memory:
        // https://github.com/ethereum/solidity/blob/34dd30d71b4da730488be72ff6af7083cf2a91f6/libsolidity/codegen/YulUtilFunctions.cpp#L102-L114
        uint256 src = MemoryPointer.unwrap(_src) + _offset;
        assembly {
            let dest := add(out_, 32)
            let i := 0
            for { } lt(i, _length) { i := add(i, 32) } { mstore(add(dest, i), mload(add(src, i))) }

            if gt(i, _length) { mstore(add(dest, _length), 0) }
        }
    }
}

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

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

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

File 26 of 26 : RLPErrors.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice The length of an RLP item must be greater than zero to be decodable
error EmptyItem();

/// @notice The decoded item type for list is not a list item
error UnexpectedString();

/// @notice The RLP item has an invalid data remainder
error InvalidDataRemainder();

/// @notice Decoded item type for bytes is not a string item
error UnexpectedList();

/// @notice The length of the content must be greater than the RLP item length
error ContentLengthMismatch();

/// @notice Invalid RLP header for RLP item
error InvalidHeader();

Settings
{
  "remappings": [
    "0xFacet/facet-sol-1.0.0/=dependencies/0xFacet-facet-sol-1.0.0/",
    "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "@optimism/=lib/optimism/packages/contracts-bedrock/",
    "@forge-std/=lib/forge-std/src/",
    "@solady/=lib/solady/src/",
    "@sp1-contracts/=lib/sp1-contracts/contracts/",
    "@rari-capital/=lib/optimism/packages/contracts-bedrock/lib/",
    "src/libraries/=lib/optimism/packages/contracts-bedrock/src/libraries/",
    "src/L1/=lib/optimism/packages/contracts-bedrock/src/L1/",
    "src/L2/=lib/optimism/packages/contracts-bedrock/src/L2/",
    "src/dispute/=lib/optimism/packages/contracts-bedrock/src/dispute/",
    "interfaces/=lib/optimism/packages/contracts-bedrock/interfaces/",
    "@lib-keccak/=lib/lib-keccak/contracts/lib/",
    "src/cannon/libraries/=lib/optimism/packages/contracts-bedrock/src/cannon/libraries/",
    "facet-sol/=lib/facet-sol/",
    "optimism/=lib/optimism/",
    "@solady-test/=lib/lib-keccak/lib/solady/test/",
    "ds-test/=lib/solady/lib/ds-test/src/",
    "erc4626-tests/=lib/optimism/packages/contracts-bedrock/lib/openzeppelin-contracts-v5/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "kontrol-cheatcodes/=lib/optimism/packages/contracts-bedrock/lib/kontrol-cheatcodes/src/",
    "lib-keccak/=lib/lib-keccak/contracts/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts-v5/=lib/optimism/packages/contracts-bedrock/lib/openzeppelin-contracts-v5/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "safe-contracts/=lib/optimism/packages/contracts-bedrock/lib/safe-contracts/contracts/",
    "solady-v0.0.245/=lib/optimism/packages/contracts-bedrock/lib/solady-v0.0.245/src/",
    "solady/=lib/solady/",
    "solmate/=lib/optimism/packages/contracts-bedrock/lib/solmate/src/",
    "sp1-contracts/=lib/sp1-contracts/contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": true
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"contract Rollup","name":"_rollup","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ContentLengthMismatch","type":"error"},{"inputs":[],"name":"EmptyItem","type":"error"},{"inputs":[],"name":"InvalidDataRemainder","type":"error"},{"inputs":[],"name":"InvalidDepositAmount","type":"error"},{"inputs":[],"name":"InvalidDepositParameters","type":"error"},{"inputs":[],"name":"InvalidHeader","type":"error"},{"inputs":[],"name":"InvalidNonce","type":"error"},{"inputs":[],"name":"InvalidOutputRoot","type":"error"},{"inputs":[],"name":"InvalidWithdrawalProof","type":"error"},{"inputs":[],"name":"L2BridgeAlreadySet","type":"error"},{"inputs":[],"name":"L2BridgeNotSet","type":"error"},{"inputs":[],"name":"OnlyCanDepositWithoutTo","type":"error"},{"inputs":[],"name":"ProposalNotCanonical","type":"error"},{"inputs":[],"name":"RootBlacklisted","type":"error"},{"inputs":[],"name":"UnexpectedList","type":"error"},{"inputs":[],"name":"UnexpectedString","type":"error"},{"inputs":[],"name":"WithdrawalAlreadyFinalized","type":"error"},{"inputs":[],"name":"WithdrawalAlreadyProven","type":"error"},{"inputs":[],"name":"WithdrawalDelayNotMet","type":"error"},{"inputs":[],"name":"WithdrawalNotProven","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"nonce","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"DepositInitiated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"nonce","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"DepositReplayed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldRollup","type":"address"},{"indexed":true,"internalType":"address","name":"newRollup","type":"address"}],"name":"RollupUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"root","type":"bytes32"},{"indexed":false,"internalType":"bool","name":"blacklisted","type":"bool"}],"name":"RootBlacklistStatusChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldDelay","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newDelay","type":"uint256"}],"name":"WithdrawalDelayUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"WithdrawalFinalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"rollup","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"proposalId","type":"uint256"}],"name":"WithdrawalProven","type":"event"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"depositHashes","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"depositNonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"finalizeWithdrawal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"finalized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"initiateDeposit","outputs":[{"internalType":"uint256","name":"nonce","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"l2Bridge","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"proposalId","type":"uint256"},{"components":[{"internalType":"bytes32","name":"version","type":"bytes32"},{"internalType":"bytes32","name":"stateRoot","type":"bytes32"},{"internalType":"bytes32","name":"messagePasserStorageRoot","type":"bytes32"},{"internalType":"bytes32","name":"latestBlockhash","type":"bytes32"}],"internalType":"struct Types.OutputRootProof","name":"rootProof","type":"tuple"},{"internalType":"bytes[]","name":"withdrawalProof","type":"bytes[]"}],"name":"proveWithdrawal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"contract Rollup","name":"","type":"address"}],"name":"proven","outputs":[{"internalType":"uint32","name":"proposalId","type":"uint32"},{"internalType":"uint32","name":"provenAt","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct L1Bridge.DepositTransaction","name":"deposit","type":"tuple"}],"name":"replayDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rollup","outputs":[{"internalType":"contract Rollup","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"rootBlacklisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_l2Bridge","type":"address"}],"name":"setL2Bridge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_rollup","type":"address"}],"name":"setRollup","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"root","type":"bytes32"},{"internalType":"bool","name":"blacklisted","type":"bool"}],"name":"setRootBlacklisted","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_withdrawalDelay","type":"uint256"}],"name":"setWithdrawalDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawalDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

6080346100c257601f62002d6038819003918201601f19168301916001600160401b038311848410176100c6578084926020946040528339810103126100c257516001600160a01b039081811681036100c2575f543360018060a01b03198216175f55604051923391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a360018055600280546001600160a81b03191660089290921b610100600160a81b0316919091179055612c859081620000db8239f35b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe60806040526004361015610022575b3615610018575f80fd5b6100206112ca565b005b5f3560e01c80630abea268146101815780633d36d9711461017c5780633f4ba83a146101775780635c975abb146101725780636897a52d1461016d578063715018a614610168578063763131a0146101635780638456cb591461015e5780638da5cb5b14610159578063a7ab696114610154578063abc0f4011461014f578063ae1f6aaf1461014a578063b8012cb514610145578063cb23bcb514610140578063cbc43bb91461013b578063d2c13da514610136578063de35f5cb14610131578063e6cd84bf1461012c578063e7a9ad5314610127578063eb12dd5614610122578063ec489d171461011d5763f2fde38b0361000e57610ac8565b610a4a565b6109ed565b610942565b61084f565b610832565b6107cd565b61075d565b610727565b6106fd565b6106d5565b61047c565b61045f565b610438565b6103df565b61035f565b610308565b6102d9565b6102b7565b610224565b6101c5565b346101b05760203660031901126101b0576004355f526007602052602060ff60405f2054166040519015158152f35b5f80fd5b6001600160a01b038116036101b057565b346101b05760203660031901126101b0576004356101e2816101b4565b6101ea6113b2565b600354906001600160a01b0390818316610212576001600160a01b0319909216911617600355005b60405163299fdc3960e01b8152600490fd5b346101b0575f3660031901126101b05761023c6113b2565b60025460ff81161561027b5760ff19166002557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a1005b60405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606490fd5b346101b0575f3660031901126101b057602060ff600254166040519015158152f35b346101b05760203660031901126101b0576004355f526004602052602060ff60405f2054166040519015158152f35b346101b0575f3660031901126101b0576103206113b2565b5f80546001600160a01b0319811682556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346101b05760203660031901126101b05760043561037c816101b4565b6103846113b2565b60028054610100600160a81b03198116600884811b610100600160a81b0316919091179092556001600160a01b0392831692911c167f68cd55ad97baa2df51330d7c27f1e5cffbb01f028c038157ec439e8e4ddfec075f80a3005b346101b0575f3660031901126101b0576103f76113b2565b6103ff611409565b600160ff1960025416176002557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a1005b346101b0575f3660031901126101b0575f546040516001600160a01b039091168152602090f35b346101b0575f3660031901126101b0576020600554604051908152f35b346101b05760603660031901126101b057600480359061049b826101b4565b60243590604435926002600154146106915760026001556104ba611409565b6104c584848361144d565b6104d7815f52600660205260405f2090565b60025460081c6001600160a01b03165f8181526020929092526040909120909390549363ffffffff808660201c16801561068057610527610520865f52600760205260405f2090565b5460ff1690565b61066f5760055461053791610bc7565b42111561065e576040516318feeb1560e31b8152951663ffffffff168286019081526001600160a01b039591610140918291849182908a9082906020010392165afa80156106595761059f92610520925f9261062c575b5050515f52600460205260405f2090565b61061d57507f7effe66fd2d9671e7fc0ed5f9e2ab55fec23fb2f726bc2dc15c02e661528672d93946105ec6105df610611935f52600760205260405f2090565b805460ff19166001179055565b6105f68684611515565b60405193849316958360209093929193604081019481520152565b0390a261002060018055565b604051638edd8c9560e01b8152fd5b61064b9250803d10610652575b6106438183610c59565b810190610cec565b5f8061058e565b503d610639565b610da3565b604051635ca831e160e11b81528390fd5b604051632ba2651560e21b81528490fd5b6040516367ba7fe160e11b81528490fd5b60405162461bcd60e51b8152602081840152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b346101b0575f3660031901126101b0576003546040516001600160a01b039091168152602090f35b346101b05760203660031901126101b0576004355f526008602052602060405f2054604051908152f35b346101b0575f3660031901126101b05760025460405160089190911c6001600160a01b03168152602090f35b801515036101b057565b346101b05760403660031901126101b0576004357f6e5a4355570a915cfb0aafb4210d86823e2f3484b7610255ace1bf5f28534a9260206024356107a081610753565b6107a86113b2565b835f526004825260405f209015159060ff1981541660ff8316179055604051908152a2005b346101b05760203660031901126101b0577f9c3f1b54b1487e018f1d0593ff5cf7fb625b2df6332c974a6cc56bb35887984160043561080a6113b2565b6005548160055561082d6040519283928360209093929193604081019481520152565b0390a1005b346101b0575f3660031901126101b0576020600954604051908152f35b60203660031901126101b057600435610867816101b4565b61086f611409565b6003546001600160a01b039081161561093057341561091e57602091610896600954610dae565b916108a083600955565b6108e36108ab610c9a565b8481526001600160a01b038416868201523460408201526108cb81611559565b6108dd865f52600860205260405f2090565b556115a6565b604051348152911690339083907f7142c1446622b71fb14ba6808d8d08a5973a86e4433f33546e133c1d6cfa86bc90602090a4604051908152f35b60405163fe9ba5cd60e01b8152600490fd5b604051633322339360e11b8152600490fd5b346101b05760603660031901126101b05761095b611409565b600435805f52600860205260405f205461097c61097736610dbc565b611559565b036109db5761099261098d36610dbc565b6115a6565b6024359061099f826101b4565b60405160443581526001600160a01b03909216917f9ed601de5fce88cea72c2f1c91a4868d6144eaa63a5ea75453cbbceb0ef3929190602090a3005b60405163d8ca7f1760e01b8152600490fd5b346101b05760403660031901126101b0576040610a31602435610a0f816101b4565b6004355f526006602052825f209060018060a01b03165f5260205260405f2090565b5463ffffffff825191818116835260201c166020820152f35b346101b0576101203660031901126101b057600435610a68816101b4565b60803660831901126101b05761010435906001600160401b03908183116101b057366023840112156101b05782600401359182116101b0573660248360051b850101116101b0576024610020930190606435906044359060243590610e06565b346101b05760203660031901126101b057600435610ae5816101b4565b610aed6113b2565b6001600160a01b03908116908115610b3e575f54826bffffffffffffffffffffffff60a01b8216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b60405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608490fd5b634e487b7160e01b5f52601160045260245ffd5b9060018201809211610bb457565b610b92565b6001019081600111610bb457565b91908201809211610bb457565b634e487b7160e01b5f52604160045260245ffd5b604081019081106001600160401b03821117610c0357604052565b610bd4565b602081019081106001600160401b03821117610c0357604052565b606081019081106001600160401b03821117610c0357604052565b60a081019081106001600160401b03821117610c0357604052565b90601f801991011681019081106001600160401b03821117610c0357604052565b6040519061014082018281106001600160401b03821117610c0357604052565b60405190610ca782610c23565b565b60405190610ca782610be8565b5190610ca7826101b4565b519063ffffffff821682036101b057565b519060058210156101b057565b519060038210156101b057565b90816101409103126101b057610d00610c7a565b9080518252610d1160208201610cb6565b6020830152610d2260408201610cc1565b6040830152610d3360608201610cc1565b6060830152610d4460808201610cc1565b6080830152610d5560a08201610cc1565b60a0830152610d6660c08201610cd2565b60c0830152610d7760e08201610cdf565b60e0830152610100610d8a818301610cb6565b90830152610d9c610120809201610cb6565b9082015290565b6040513d5f823e3d90fd5b5f198114610bb45760010190565b60609060031901126101b05760405190606082018281106001600160401b03821117610c0357604052816004358152602435610df7816101b4565b60208201526040604435910152565b9194929094610e13611409565b610e1e81878561144d565b610e30815f52600660205260405f2090565b60025460081c6001600160a01b0316918291610e72610e6563ffffffff9586939060018060a01b03165f5260205260405f2090565b5460201c63ffffffff1690565b1661116057610e8c610520825f52600760205260405f2090565b61114e5760408051636338049b60e01b8152600480820188905291996001600160a01b039960209392909190868c16908581602481855afa908115610659575f91611121575b5015611111578c516318feeb1560e31b81528481018b81526101409283918391908290819060200103915afa918215610659575f926110f4575b5050610f2461052082515f52600460205260405f2090565b6110e45751610f3a610f353661118a565b61173c565b036110d457610fc191610f94610fbd92610fa08f80519089820182610f6c8d8360205f91939293604081019481520152565b0392610f80601f1994858101835282610c59565b51902090518a810191825294859160200190565b03908101845283610c59565b610fb7610fab6111ee565b9160c435943691611222565b91611777565b1590565b6110c65750926110456110c1959361103061107f946110227fb7f82ff0ef7dc798b8b68a2fb464607fae371501d4b0b87f4123f657dcbe20ce9c9b9a98611006610ca9565b63ffffffff8b89161681529642169087019063ffffffff169052565b5f52600660205260405f2090565b9060018060a01b03165f5260205260405f2090565b9063ffffffff81511667ffffffff0000000060208454938363ffffffff198616178655015160201b16916001600160401b03191617179055565b6002546110a5906110999060081c6001600160a01b031681565b6001600160a01b031690565b9651978852602088015260408701529116939081906060820190565b0390a3565b8951632e1aaf2760e21b8152fd5b8b516315ae1f5d60e31b81528390fd5b8c51638edd8c9560e01b81528490fd5b61110a9250803d10610652576106438183610c59565b5f80610f0c565b8c516307f310d160e41b81528490fd5b6111419150863d8811611147575b6111398183610c59565b810190611172565b5f610ed2565b503d61112f565b604051632ba2651560e21b8152600490fd5b60405163030f885f60e61b8152600490fd5b908160209103126101b0575161118781610753565b90565b60809060831901126101b05760405190608082018281106001600160401b03821117610c035760405281608435815260a435602082015260c4356040820152606060e435910152565b6001600160401b038111610c0357601f01601f191660200190565b604051906111fb82610be8565b60018252600160f81b6020830152565b6001600160401b038111610c035760051b60200190565b9291909261122f8461120b565b916040946112406040519485610c59565b839581855260208095019160051b8401938385116101b05780925b85841061126b5750505050505050565b83356001600160401b0381116101b057820185601f820112156101b057803591611294836111d3565b6112a086519182610c59565b838152878a85850101116101b0575f8a85819682809701838601378301015281520193019261125b565b3332036113855760015b15611373576112e1611409565b6003546001600160a01b03161561093057341561091e57611303600954610dae565b61130c81600955565b611341611317610c9a565b82815233602082015234604082015261132f81611559565b6108dd845f52600860205260405f2090565b604051348152339182917f7142c1446622b71fb14ba6808d8d08a5973a86e4433f33546e133c1d6cfa86bc90602090a4565b60405163770d5e3960e11b8152600490fd5b333b6017036113ac5762ef01006040516020810160405260205f82333c5160e81c146112d4565b5f6112d4565b5f546001600160a01b031633036113c557565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b60ff6002541661141557565b60405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606490fd5b604091825191602083019160018060a01b0380921683528484015283835261147483610c23565b600354169280519260c08401938085106001600160401b03861117610c035760a0829161150f96855288815287602082015230858201525f60608201525f608082015201528151948592602084019788528301523060608301525f60808301525f60a083015260c080830152518060e08301526114f88161010094858501906117b7565b601f801991011681010360e0810184520182610c59565b51902090565b81471061154c575f38818085856108fcf11561152f575050565b601691600b915f526073825360ff602053f01561154857565b3838fd5b63b12d13eb5f526004601cfd5b805190604060018060a01b0360208301511691015160405191602083019384526040830152606082015260608152608081018181106001600160401b03821117610c035760405251902090565b604080519181602091636093e77160e01b838601528051602486015260018060a01b038382015116604486015201516064840152606483526115e783610c3e565b600354928251936bffffffffffffffffffffffff199060601b16828501526014845261161284610be8565b82519061161e82610c08565b5f8252466001036116d757620face79493926116846116d29361167d6021946116628a995b61165b8a519b6116528d610c08565b5f8d528c61284b565b508a6128a9565b5061166c896127e2565b5061167689612812565b50886128a9565b50866128a9565b508351946116c26116988388018093612ae8565b92601f198885030188525f8452830195868152830196602360f91b885251809260418501906117b7565b8101036001810184520182610c59565b5190a1565b4662aa36a70361170257620face79493926116846116d29361167d60219461166262face7a99611643565b835162461bcd60e51b8152600481018490526013602482015272155b9cdd5c1c1bdc9d19590818da185a5b9259606a1b6044820152606490fd5b805190602081015190606060408201519101519060405192602084019485526040840152606083015260808201526080815261150f81610c3e565b926117a3919261118794602081519101206040519060208201526020815261179e81610be8565b611d02565b906020815191012090602081519101201490565b5f5b8381106117c85750505f910152565b81810151838201526020016117b9565b156117df57565b60405162461bcd60e51b81526020600482015260156024820152744d65726b6c65547269653a20656d707479206b657960581b6044820152606490fd5b634e487b7160e01b5f52603260045260245ffd5b8051600110156118405760400190565b61181c565b805160101015611840576102200190565b80518210156118405760209160051b010190565b1561187157565b60405162461bcd60e51b815260206004820152602e60248201527f4d65726b6c65547269653a206b657920696e646578206578636565647320746f60448201526d0e8c2d840d6caf240d8cadccee8d60931b6064820152608490fd5b156118d457565b60405162461bcd60e51b815260206004820152602660248201527f4d65726b6c65547269653a20696e76616c696420696e7465726e616c206e6f646044820152650ca40d0c2e6d60d31b6064820152608490fd5b1561192f57565b60405162461bcd60e51b815260206004820152602760248201527f4d65726b6c65547269653a20696e76616c6964206c6172676520696e7465726e6044820152660c2d840d0c2e6d60cb1b6064820152608490fd5b1561198b57565b60405162461bcd60e51b815260206004820152601d60248201527f4d65726b6c65547269653a20696e76616c696420726f6f7420686173680000006044820152606490fd5b8051156118405760200190565b908151811015611840570160200190565b60ff166002039060ff8211610bb457565b15611a0657565b60405162461bcd60e51b815260206004820152603a60248201527f4d65726b6c65547269653a20706174682072656d61696e646572206d7573742060448201527f736861726520616c6c206e6962626c65732077697468206b65790000000000006064820152608490fd5b15611a7857565b60405162461bcd60e51b815260206004820152603d60248201527f4d65726b6c65547269653a206b65792072656d61696e646572206d757374206260448201527f65206964656e746963616c20746f20706174682072656d61696e6465720000006064820152608490fd5b15611aea57565b60405162461bcd60e51b815260206004820152603960248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286c65616629000000000000006064820152608490fd5b5f19810191908211610bb457565b60f619810191908211610bb457565b60bf19810191908211610bb457565b60b619810191908211610bb457565b607f19810191908211610bb457565b91908203918211610bb457565b15611bb357565b60405162461bcd60e51b815260206004820152603860248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286c6561662900000000000000006064820152608490fd5b15611c2557565b60405162461bcd60e51b815260206004820152603b60248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286272616e63682900000000006064820152608490fd5b15611c9757565b60405162461bcd60e51b815260206004820152603a60248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286272616e6368290000000000006064820152608490fd5b9190611d1c611d2291611d17855115156117d8565b6120d9565b92612192565b90604080519081611d3b60209485830160209181520190565b0391611d4f601f1993848101835282610c59565b935f925f945b875186101561208757611d688689611856565b5196611d77845187111561186a565b8561203357611dc1611dc691611da2611dae8b51868151910120895192839188830160209181520190565b03878101835282610c59565b6020815191012090602081519101201490565b611984565b80870196875151601181145f14611e7257505082518503611e18575050505050611e12611dff611df96111879451611845565b51612365565b93611e0c85511515611c1e565b51611b55565b14611c90565b909192949395611e66611e60611e5a60019360ff611e50611e4a611e3c8e8c6119dd565b516001600160f81b03191690565b60f81c90565b9151911690611856565b51612323565b97610ba6565b945b0194929190611d55565b94929099989795939160028096145f14611fde57611e9360ff999a9b6121e6565b95611ea3611e4a611e3c896119d0565b96611ec4600191611ebe611eb8848c166119ee565b60ff1690565b90612206565b9a8b98611ee7611ede611ed78b8a612206565b809c6122bd565b809e51146119ff565b169182148015611fd4575b15611f305750505050505050611df9611f2a92611f1761118796611f1d945114611a71565b51611830565b93611e0c85511515611ae3565b14611bac565b90809295999893969a94975015918215611fca575b505015611f6b57600191611f5f611e5a611f659351611830565b98610bc7565b94611e68565b865162461bcd60e51b815260206004820152603260248201527f4d65726b6c65547269653a2072656365697665642061206e6f64652077697468604482015271040c2dc40eadcd6dcdeeedc40e0e4caccd2f60731b6064820152608490fd5b1490505f80611f45565b5060038214611ef2565b835162461bcd60e51b815260206004820152602860248201527f4d65726b6c65547269653a20726563656976656420616e20756e706172736561604482015267626c65206e6f646560c01b6064820152608490fd5b87518051831161206e575061206461206991611da2611dae8b51868151910120895192839188830160209181520190565b611928565b611dc6565b80516020918201208251919092012061206991146118cd565b835162461bcd60e51b815260206004820152602560248201527f4d65726b6c65547269653a2072616e206f7574206f662070726f6f6620656c656044820152646d656e747360d81b6064820152608490fd5b9081516120e58161120b565b60406120f46040519283610c59565b828252601f196121038461120b565b01905f5b82811061216f5750505080935f5b8381106121225750505050565b8061212f60019284611856565b5161214361213d8386611856565b516123c1565b61214b610ca9565b918252602082015261215d8286611856565b526121688185611856565b5001612115565b602090825161217d81610be8565b60608082528382015285820183015201612107565b6040519080516001918160011b603f8101601f1916850160405284525f5b8281106121be575050505090565b808491821b86016021600f60208085880101515f1a908160041c9085015316910153016121b0565b60200151805115611840576122016020611187920151612365565b612192565b9081518110156122a957815181810390808211610bb457829061222e83601f810110156124fc565b612249838301612240848210156124fc565b86511015612539565b03612261575050506040515f81526020810160405290565b60405192601f821692831560051b80858701019484860193010101905b8084106122965750508252601f01601f191660405290565b909283518152602080910193019061227e565b50506040516122b781610c08565b5f815290565b805182515f94939185918082101561231a575094505b5b848110806122f0575b156122ea576001016122d4565b93505050565b506001600160f81b03198061230583866119dd565b51169061231283856119dd565b5116146122dd565b905094506122d3565b60208151105f1461233e5760208101519051611187916126f3565b61118790612365565b6002111561235157565b634e487b7160e01b5f52602160045260245ffd5b61236e81612579565b6002819392931015612351576123af57825182820190818311610bb4570361239d576020611187930151612769565b604051630b8aa6f760e31b8152600490fd5b6040516307fe6cb960e21b8152600490fd5b6123c9612bed565b508051156124ea576020815191604051926123e384610be8565b83526020808401910181526123f783612579565b91939061240383612347565b60018093036124d8576124169085610bc7565b85510361239d57612425612c05565b945f94835b612439575b5050505050815290565b8151808210156124d2578161244d91611b9f565b8186519061245a91610bc7565b612462610ca9565b9182528482015261247290612579565b509661247e8289610bc7565b8388519061248b91610bc7565b612493610ca9565b918252868201526124a4828b611856565b526124af818a611856565b506124b990610ba6565b966124c391610bc7565b6124cc91610bc7565b8361242a565b5061242f565b6040516325ce355f60e11b8152600490fd5b604051635ab458fb60e01b8152600490fd5b1561250357565b60405162461bcd60e51b815260206004820152600e60248201526d736c6963655f6f766572666c6f7760901b6044820152606490fd5b1561254057565b60405162461bcd60e51b8152602060048201526011602482015270736c6963655f6f75744f66426f756e647360781b6044820152606490fd5b908151156124ea57602082015180515f1a607f811161259e57505f9250600191839150565b60b78111612611576125af90611b90565b80935111156125ff57600190810151908314906001600160f81b031916816125f1575b506125df57600191905f90565b60405163babb01dd60e01b8152600490fd5b600160ff1b1190505f6125d2565b6040516366c9448560e01b8152600490fd5b60bf81116126745761262290611b81565b9081845111156125ff5760010180516001600160f81b031916156125df57518160031b610100031c9260378411156125df575161265f8483610bc7565b10156125ff5761266e90610bb9565b91905f90565b60f78111612697576126869150611b72565b80925111156125ff57600191908290565b6126a090611b63565b9081845111156125ff5760010180516001600160f81b031916156125df57518160031b610100031c9260378411156125df57516126dd8483610bc7565b10156125ff576126ec90610bb9565b9190600190565b91906126fe816111d3565b9261270c6040519485610c59565b818452612718826111d3565b602090601f19013686830137848315612762575082915f915b83831061274c575050116127425750565b60205f9184010152565b8183015187840182015284935091820191612731565b9450505050565b92919092612776826111d3565b936127846040519586610c59565b828552612790836111d3565b60209190601f190136878401378584156127da57508201809211610bb45782915f915b8383106127c4575050116127425750565b81830151878401820152849350918201916127b3565b955050505050565b6111876040516127f181610c08565b5f81528290815181811c17918260281c9282841860281b1890528151179052565b61118760405161282181610c08565b6807a12000000000000081528290815181811c17918260281c9282841860281b1890528151179052565b906040519061285982610c08565b8060301b8252612881828490815181811c17918260281c9282841860281b1890528151179052565b8060d01c61288e57505090565b6040519081526020810160405260081b60011760281b905290565b9061118790604051906128bb82610c08565b60081b60021760281b81528290815181811c17918260281c9282841860281b1890528151179052565b91906001810192607f8111156129565760029293816fffffffffffffffffffffffffffffffff1060071b82811c6001600160401b031060061b1782811c63ffffffff1060051b1782811c61ffff1060041b1782811c60ff109060031c17916081830184535f5281601f18519052010190565b801560071b179053565b60581b815260158101919060949053565b9190600180820184519160378311156129f6576020958360201c3d3d3e8362ffffff108461ffff10018460ff10019260b8840160f81b86528301968280890181878b0101998a9403915b6129d3575b505050500160031b60f8031b8151179052565b818101601f0151815282019084848310156129f0579190916129bb565b506129c0565b909492918360200151936080855f1a108284141615612a155750505052565b60808294959396979201855360400151846021015252010190565b919060018082018451916037831115612aae576020958360201c3d3d3e8362ffffff108461ffff10018460ff10019260f8840160f81b86528301968280890181878b0101998a9403915b612a9157505050500160031b60f8031b8151179052565b818101601f0151815282019084848310156129f057919091612a7a565b909492918360200151936080855f1a108284141615612acd5750505052565b60c08294959396979201855360400151846021015252010190565b9091815115612be05764ffffffffff60208401925b5116918215612b9a57825180601a1a906001821115612b70575060028114612b5b57600314612b4057612b3a64ffffffffff91845160301c612960565b92612afd565b612b4e90835160301c612ae8565b64ffffffffff9092612afd565b50612b3a64ffffffffff91845160301c612971565b909164ffffffffff9215612b8e57612b3a9150845160301c516128e4565b612b3a9160301c6128e4565b9092908190039150601f1982016037811115612bbd576111879250815280612a30565b5060a082018153602081015160018201526040810151602182015201601e190190565b916001915060c081530190565b60405190612bfa82610be8565b5f6020838281520152565b6040519061042082018281106001600160401b03821117610c03576040526020808352825f5b6104008110612c3957505050565b8290612c43612bed565b82828501015201612c2b56fea264697066735822122039f983ffde1b5c75b55f395dc71d5bea0e96a5a46e7a5b5d1568bca06030e1a164736f6c63430008180033000000000000000000000000686e7d01c7bfcb563721333a007699f154c04eb4

Deployed Bytecode

0x60806040526004361015610022575b3615610018575f80fd5b6100206112ca565b005b5f3560e01c80630abea268146101815780633d36d9711461017c5780633f4ba83a146101775780635c975abb146101725780636897a52d1461016d578063715018a614610168578063763131a0146101635780638456cb591461015e5780638da5cb5b14610159578063a7ab696114610154578063abc0f4011461014f578063ae1f6aaf1461014a578063b8012cb514610145578063cb23bcb514610140578063cbc43bb91461013b578063d2c13da514610136578063de35f5cb14610131578063e6cd84bf1461012c578063e7a9ad5314610127578063eb12dd5614610122578063ec489d171461011d5763f2fde38b0361000e57610ac8565b610a4a565b6109ed565b610942565b61084f565b610832565b6107cd565b61075d565b610727565b6106fd565b6106d5565b61047c565b61045f565b610438565b6103df565b61035f565b610308565b6102d9565b6102b7565b610224565b6101c5565b346101b05760203660031901126101b0576004355f526007602052602060ff60405f2054166040519015158152f35b5f80fd5b6001600160a01b038116036101b057565b346101b05760203660031901126101b0576004356101e2816101b4565b6101ea6113b2565b600354906001600160a01b0390818316610212576001600160a01b0319909216911617600355005b60405163299fdc3960e01b8152600490fd5b346101b0575f3660031901126101b05761023c6113b2565b60025460ff81161561027b5760ff19166002557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a1005b60405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606490fd5b346101b0575f3660031901126101b057602060ff600254166040519015158152f35b346101b05760203660031901126101b0576004355f526004602052602060ff60405f2054166040519015158152f35b346101b0575f3660031901126101b0576103206113b2565b5f80546001600160a01b0319811682556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346101b05760203660031901126101b05760043561037c816101b4565b6103846113b2565b60028054610100600160a81b03198116600884811b610100600160a81b0316919091179092556001600160a01b0392831692911c167f68cd55ad97baa2df51330d7c27f1e5cffbb01f028c038157ec439e8e4ddfec075f80a3005b346101b0575f3660031901126101b0576103f76113b2565b6103ff611409565b600160ff1960025416176002557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a1005b346101b0575f3660031901126101b0575f546040516001600160a01b039091168152602090f35b346101b0575f3660031901126101b0576020600554604051908152f35b346101b05760603660031901126101b057600480359061049b826101b4565b60243590604435926002600154146106915760026001556104ba611409565b6104c584848361144d565b6104d7815f52600660205260405f2090565b60025460081c6001600160a01b03165f8181526020929092526040909120909390549363ffffffff808660201c16801561068057610527610520865f52600760205260405f2090565b5460ff1690565b61066f5760055461053791610bc7565b42111561065e576040516318feeb1560e31b8152951663ffffffff168286019081526001600160a01b039591610140918291849182908a9082906020010392165afa80156106595761059f92610520925f9261062c575b5050515f52600460205260405f2090565b61061d57507f7effe66fd2d9671e7fc0ed5f9e2ab55fec23fb2f726bc2dc15c02e661528672d93946105ec6105df610611935f52600760205260405f2090565b805460ff19166001179055565b6105f68684611515565b60405193849316958360209093929193604081019481520152565b0390a261002060018055565b604051638edd8c9560e01b8152fd5b61064b9250803d10610652575b6106438183610c59565b810190610cec565b5f8061058e565b503d610639565b610da3565b604051635ca831e160e11b81528390fd5b604051632ba2651560e21b81528490fd5b6040516367ba7fe160e11b81528490fd5b60405162461bcd60e51b8152602081840152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b346101b0575f3660031901126101b0576003546040516001600160a01b039091168152602090f35b346101b05760203660031901126101b0576004355f526008602052602060405f2054604051908152f35b346101b0575f3660031901126101b05760025460405160089190911c6001600160a01b03168152602090f35b801515036101b057565b346101b05760403660031901126101b0576004357f6e5a4355570a915cfb0aafb4210d86823e2f3484b7610255ace1bf5f28534a9260206024356107a081610753565b6107a86113b2565b835f526004825260405f209015159060ff1981541660ff8316179055604051908152a2005b346101b05760203660031901126101b0577f9c3f1b54b1487e018f1d0593ff5cf7fb625b2df6332c974a6cc56bb35887984160043561080a6113b2565b6005548160055561082d6040519283928360209093929193604081019481520152565b0390a1005b346101b0575f3660031901126101b0576020600954604051908152f35b60203660031901126101b057600435610867816101b4565b61086f611409565b6003546001600160a01b039081161561093057341561091e57602091610896600954610dae565b916108a083600955565b6108e36108ab610c9a565b8481526001600160a01b038416868201523460408201526108cb81611559565b6108dd865f52600860205260405f2090565b556115a6565b604051348152911690339083907f7142c1446622b71fb14ba6808d8d08a5973a86e4433f33546e133c1d6cfa86bc90602090a4604051908152f35b60405163fe9ba5cd60e01b8152600490fd5b604051633322339360e11b8152600490fd5b346101b05760603660031901126101b05761095b611409565b600435805f52600860205260405f205461097c61097736610dbc565b611559565b036109db5761099261098d36610dbc565b6115a6565b6024359061099f826101b4565b60405160443581526001600160a01b03909216917f9ed601de5fce88cea72c2f1c91a4868d6144eaa63a5ea75453cbbceb0ef3929190602090a3005b60405163d8ca7f1760e01b8152600490fd5b346101b05760403660031901126101b0576040610a31602435610a0f816101b4565b6004355f526006602052825f209060018060a01b03165f5260205260405f2090565b5463ffffffff825191818116835260201c166020820152f35b346101b0576101203660031901126101b057600435610a68816101b4565b60803660831901126101b05761010435906001600160401b03908183116101b057366023840112156101b05782600401359182116101b0573660248360051b850101116101b0576024610020930190606435906044359060243590610e06565b346101b05760203660031901126101b057600435610ae5816101b4565b610aed6113b2565b6001600160a01b03908116908115610b3e575f54826bffffffffffffffffffffffff60a01b8216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b60405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608490fd5b634e487b7160e01b5f52601160045260245ffd5b9060018201809211610bb457565b610b92565b6001019081600111610bb457565b91908201809211610bb457565b634e487b7160e01b5f52604160045260245ffd5b604081019081106001600160401b03821117610c0357604052565b610bd4565b602081019081106001600160401b03821117610c0357604052565b606081019081106001600160401b03821117610c0357604052565b60a081019081106001600160401b03821117610c0357604052565b90601f801991011681019081106001600160401b03821117610c0357604052565b6040519061014082018281106001600160401b03821117610c0357604052565b60405190610ca782610c23565b565b60405190610ca782610be8565b5190610ca7826101b4565b519063ffffffff821682036101b057565b519060058210156101b057565b519060038210156101b057565b90816101409103126101b057610d00610c7a565b9080518252610d1160208201610cb6565b6020830152610d2260408201610cc1565b6040830152610d3360608201610cc1565b6060830152610d4460808201610cc1565b6080830152610d5560a08201610cc1565b60a0830152610d6660c08201610cd2565b60c0830152610d7760e08201610cdf565b60e0830152610100610d8a818301610cb6565b90830152610d9c610120809201610cb6565b9082015290565b6040513d5f823e3d90fd5b5f198114610bb45760010190565b60609060031901126101b05760405190606082018281106001600160401b03821117610c0357604052816004358152602435610df7816101b4565b60208201526040604435910152565b9194929094610e13611409565b610e1e81878561144d565b610e30815f52600660205260405f2090565b60025460081c6001600160a01b0316918291610e72610e6563ffffffff9586939060018060a01b03165f5260205260405f2090565b5460201c63ffffffff1690565b1661116057610e8c610520825f52600760205260405f2090565b61114e5760408051636338049b60e01b8152600480820188905291996001600160a01b039960209392909190868c16908581602481855afa908115610659575f91611121575b5015611111578c516318feeb1560e31b81528481018b81526101409283918391908290819060200103915afa918215610659575f926110f4575b5050610f2461052082515f52600460205260405f2090565b6110e45751610f3a610f353661118a565b61173c565b036110d457610fc191610f94610fbd92610fa08f80519089820182610f6c8d8360205f91939293604081019481520152565b0392610f80601f1994858101835282610c59565b51902090518a810191825294859160200190565b03908101845283610c59565b610fb7610fab6111ee565b9160c435943691611222565b91611777565b1590565b6110c65750926110456110c1959361103061107f946110227fb7f82ff0ef7dc798b8b68a2fb464607fae371501d4b0b87f4123f657dcbe20ce9c9b9a98611006610ca9565b63ffffffff8b89161681529642169087019063ffffffff169052565b5f52600660205260405f2090565b9060018060a01b03165f5260205260405f2090565b9063ffffffff81511667ffffffff0000000060208454938363ffffffff198616178655015160201b16916001600160401b03191617179055565b6002546110a5906110999060081c6001600160a01b031681565b6001600160a01b031690565b9651978852602088015260408701529116939081906060820190565b0390a3565b8951632e1aaf2760e21b8152fd5b8b516315ae1f5d60e31b81528390fd5b8c51638edd8c9560e01b81528490fd5b61110a9250803d10610652576106438183610c59565b5f80610f0c565b8c516307f310d160e41b81528490fd5b6111419150863d8811611147575b6111398183610c59565b810190611172565b5f610ed2565b503d61112f565b604051632ba2651560e21b8152600490fd5b60405163030f885f60e61b8152600490fd5b908160209103126101b0575161118781610753565b90565b60809060831901126101b05760405190608082018281106001600160401b03821117610c035760405281608435815260a435602082015260c4356040820152606060e435910152565b6001600160401b038111610c0357601f01601f191660200190565b604051906111fb82610be8565b60018252600160f81b6020830152565b6001600160401b038111610c035760051b60200190565b9291909261122f8461120b565b916040946112406040519485610c59565b839581855260208095019160051b8401938385116101b05780925b85841061126b5750505050505050565b83356001600160401b0381116101b057820185601f820112156101b057803591611294836111d3565b6112a086519182610c59565b838152878a85850101116101b0575f8a85819682809701838601378301015281520193019261125b565b3332036113855760015b15611373576112e1611409565b6003546001600160a01b03161561093057341561091e57611303600954610dae565b61130c81600955565b611341611317610c9a565b82815233602082015234604082015261132f81611559565b6108dd845f52600860205260405f2090565b604051348152339182917f7142c1446622b71fb14ba6808d8d08a5973a86e4433f33546e133c1d6cfa86bc90602090a4565b60405163770d5e3960e11b8152600490fd5b333b6017036113ac5762ef01006040516020810160405260205f82333c5160e81c146112d4565b5f6112d4565b5f546001600160a01b031633036113c557565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b60ff6002541661141557565b60405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606490fd5b604091825191602083019160018060a01b0380921683528484015283835261147483610c23565b600354169280519260c08401938085106001600160401b03861117610c035760a0829161150f96855288815287602082015230858201525f60608201525f608082015201528151948592602084019788528301523060608301525f60808301525f60a083015260c080830152518060e08301526114f88161010094858501906117b7565b601f801991011681010360e0810184520182610c59565b51902090565b81471061154c575f38818085856108fcf11561152f575050565b601691600b915f526073825360ff602053f01561154857565b3838fd5b63b12d13eb5f526004601cfd5b805190604060018060a01b0360208301511691015160405191602083019384526040830152606082015260608152608081018181106001600160401b03821117610c035760405251902090565b604080519181602091636093e77160e01b838601528051602486015260018060a01b038382015116604486015201516064840152606483526115e783610c3e565b600354928251936bffffffffffffffffffffffff199060601b16828501526014845261161284610be8565b82519061161e82610c08565b5f8252466001036116d757620face79493926116846116d29361167d6021946116628a995b61165b8a519b6116528d610c08565b5f8d528c61284b565b508a6128a9565b5061166c896127e2565b5061167689612812565b50886128a9565b50866128a9565b508351946116c26116988388018093612ae8565b92601f198885030188525f8452830195868152830196602360f91b885251809260418501906117b7565b8101036001810184520182610c59565b5190a1565b4662aa36a70361170257620face79493926116846116d29361167d60219461166262face7a99611643565b835162461bcd60e51b8152600481018490526013602482015272155b9cdd5c1c1bdc9d19590818da185a5b9259606a1b6044820152606490fd5b805190602081015190606060408201519101519060405192602084019485526040840152606083015260808201526080815261150f81610c3e565b926117a3919261118794602081519101206040519060208201526020815261179e81610be8565b611d02565b906020815191012090602081519101201490565b5f5b8381106117c85750505f910152565b81810151838201526020016117b9565b156117df57565b60405162461bcd60e51b81526020600482015260156024820152744d65726b6c65547269653a20656d707479206b657960581b6044820152606490fd5b634e487b7160e01b5f52603260045260245ffd5b8051600110156118405760400190565b61181c565b805160101015611840576102200190565b80518210156118405760209160051b010190565b1561187157565b60405162461bcd60e51b815260206004820152602e60248201527f4d65726b6c65547269653a206b657920696e646578206578636565647320746f60448201526d0e8c2d840d6caf240d8cadccee8d60931b6064820152608490fd5b156118d457565b60405162461bcd60e51b815260206004820152602660248201527f4d65726b6c65547269653a20696e76616c696420696e7465726e616c206e6f646044820152650ca40d0c2e6d60d31b6064820152608490fd5b1561192f57565b60405162461bcd60e51b815260206004820152602760248201527f4d65726b6c65547269653a20696e76616c6964206c6172676520696e7465726e6044820152660c2d840d0c2e6d60cb1b6064820152608490fd5b1561198b57565b60405162461bcd60e51b815260206004820152601d60248201527f4d65726b6c65547269653a20696e76616c696420726f6f7420686173680000006044820152606490fd5b8051156118405760200190565b908151811015611840570160200190565b60ff166002039060ff8211610bb457565b15611a0657565b60405162461bcd60e51b815260206004820152603a60248201527f4d65726b6c65547269653a20706174682072656d61696e646572206d7573742060448201527f736861726520616c6c206e6962626c65732077697468206b65790000000000006064820152608490fd5b15611a7857565b60405162461bcd60e51b815260206004820152603d60248201527f4d65726b6c65547269653a206b65792072656d61696e646572206d757374206260448201527f65206964656e746963616c20746f20706174682072656d61696e6465720000006064820152608490fd5b15611aea57565b60405162461bcd60e51b815260206004820152603960248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286c65616629000000000000006064820152608490fd5b5f19810191908211610bb457565b60f619810191908211610bb457565b60bf19810191908211610bb457565b60b619810191908211610bb457565b607f19810191908211610bb457565b91908203918211610bb457565b15611bb357565b60405162461bcd60e51b815260206004820152603860248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286c6561662900000000000000006064820152608490fd5b15611c2557565b60405162461bcd60e51b815260206004820152603b60248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286272616e63682900000000006064820152608490fd5b15611c9757565b60405162461bcd60e51b815260206004820152603a60248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286272616e6368290000000000006064820152608490fd5b9190611d1c611d2291611d17855115156117d8565b6120d9565b92612192565b90604080519081611d3b60209485830160209181520190565b0391611d4f601f1993848101835282610c59565b935f925f945b875186101561208757611d688689611856565b5196611d77845187111561186a565b8561203357611dc1611dc691611da2611dae8b51868151910120895192839188830160209181520190565b03878101835282610c59565b6020815191012090602081519101201490565b611984565b80870196875151601181145f14611e7257505082518503611e18575050505050611e12611dff611df96111879451611845565b51612365565b93611e0c85511515611c1e565b51611b55565b14611c90565b909192949395611e66611e60611e5a60019360ff611e50611e4a611e3c8e8c6119dd565b516001600160f81b03191690565b60f81c90565b9151911690611856565b51612323565b97610ba6565b945b0194929190611d55565b94929099989795939160028096145f14611fde57611e9360ff999a9b6121e6565b95611ea3611e4a611e3c896119d0565b96611ec4600191611ebe611eb8848c166119ee565b60ff1690565b90612206565b9a8b98611ee7611ede611ed78b8a612206565b809c6122bd565b809e51146119ff565b169182148015611fd4575b15611f305750505050505050611df9611f2a92611f1761118796611f1d945114611a71565b51611830565b93611e0c85511515611ae3565b14611bac565b90809295999893969a94975015918215611fca575b505015611f6b57600191611f5f611e5a611f659351611830565b98610bc7565b94611e68565b865162461bcd60e51b815260206004820152603260248201527f4d65726b6c65547269653a2072656365697665642061206e6f64652077697468604482015271040c2dc40eadcd6dcdeeedc40e0e4caccd2f60731b6064820152608490fd5b1490505f80611f45565b5060038214611ef2565b835162461bcd60e51b815260206004820152602860248201527f4d65726b6c65547269653a20726563656976656420616e20756e706172736561604482015267626c65206e6f646560c01b6064820152608490fd5b87518051831161206e575061206461206991611da2611dae8b51868151910120895192839188830160209181520190565b611928565b611dc6565b80516020918201208251919092012061206991146118cd565b835162461bcd60e51b815260206004820152602560248201527f4d65726b6c65547269653a2072616e206f7574206f662070726f6f6620656c656044820152646d656e747360d81b6064820152608490fd5b9081516120e58161120b565b60406120f46040519283610c59565b828252601f196121038461120b565b01905f5b82811061216f5750505080935f5b8381106121225750505050565b8061212f60019284611856565b5161214361213d8386611856565b516123c1565b61214b610ca9565b918252602082015261215d8286611856565b526121688185611856565b5001612115565b602090825161217d81610be8565b60608082528382015285820183015201612107565b6040519080516001918160011b603f8101601f1916850160405284525f5b8281106121be575050505090565b808491821b86016021600f60208085880101515f1a908160041c9085015316910153016121b0565b60200151805115611840576122016020611187920151612365565b612192565b9081518110156122a957815181810390808211610bb457829061222e83601f810110156124fc565b612249838301612240848210156124fc565b86511015612539565b03612261575050506040515f81526020810160405290565b60405192601f821692831560051b80858701019484860193010101905b8084106122965750508252601f01601f191660405290565b909283518152602080910193019061227e565b50506040516122b781610c08565b5f815290565b805182515f94939185918082101561231a575094505b5b848110806122f0575b156122ea576001016122d4565b93505050565b506001600160f81b03198061230583866119dd565b51169061231283856119dd565b5116146122dd565b905094506122d3565b60208151105f1461233e5760208101519051611187916126f3565b61118790612365565b6002111561235157565b634e487b7160e01b5f52602160045260245ffd5b61236e81612579565b6002819392931015612351576123af57825182820190818311610bb4570361239d576020611187930151612769565b604051630b8aa6f760e31b8152600490fd5b6040516307fe6cb960e21b8152600490fd5b6123c9612bed565b508051156124ea576020815191604051926123e384610be8565b83526020808401910181526123f783612579565b91939061240383612347565b60018093036124d8576124169085610bc7565b85510361239d57612425612c05565b945f94835b612439575b5050505050815290565b8151808210156124d2578161244d91611b9f565b8186519061245a91610bc7565b612462610ca9565b9182528482015261247290612579565b509661247e8289610bc7565b8388519061248b91610bc7565b612493610ca9565b918252868201526124a4828b611856565b526124af818a611856565b506124b990610ba6565b966124c391610bc7565b6124cc91610bc7565b8361242a565b5061242f565b6040516325ce355f60e11b8152600490fd5b604051635ab458fb60e01b8152600490fd5b1561250357565b60405162461bcd60e51b815260206004820152600e60248201526d736c6963655f6f766572666c6f7760901b6044820152606490fd5b1561254057565b60405162461bcd60e51b8152602060048201526011602482015270736c6963655f6f75744f66426f756e647360781b6044820152606490fd5b908151156124ea57602082015180515f1a607f811161259e57505f9250600191839150565b60b78111612611576125af90611b90565b80935111156125ff57600190810151908314906001600160f81b031916816125f1575b506125df57600191905f90565b60405163babb01dd60e01b8152600490fd5b600160ff1b1190505f6125d2565b6040516366c9448560e01b8152600490fd5b60bf81116126745761262290611b81565b9081845111156125ff5760010180516001600160f81b031916156125df57518160031b610100031c9260378411156125df575161265f8483610bc7565b10156125ff5761266e90610bb9565b91905f90565b60f78111612697576126869150611b72565b80925111156125ff57600191908290565b6126a090611b63565b9081845111156125ff5760010180516001600160f81b031916156125df57518160031b610100031c9260378411156125df57516126dd8483610bc7565b10156125ff576126ec90610bb9565b9190600190565b91906126fe816111d3565b9261270c6040519485610c59565b818452612718826111d3565b602090601f19013686830137848315612762575082915f915b83831061274c575050116127425750565b60205f9184010152565b8183015187840182015284935091820191612731565b9450505050565b92919092612776826111d3565b936127846040519586610c59565b828552612790836111d3565b60209190601f190136878401378584156127da57508201809211610bb45782915f915b8383106127c4575050116127425750565b81830151878401820152849350918201916127b3565b955050505050565b6111876040516127f181610c08565b5f81528290815181811c17918260281c9282841860281b1890528151179052565b61118760405161282181610c08565b6807a12000000000000081528290815181811c17918260281c9282841860281b1890528151179052565b906040519061285982610c08565b8060301b8252612881828490815181811c17918260281c9282841860281b1890528151179052565b8060d01c61288e57505090565b6040519081526020810160405260081b60011760281b905290565b9061118790604051906128bb82610c08565b60081b60021760281b81528290815181811c17918260281c9282841860281b1890528151179052565b91906001810192607f8111156129565760029293816fffffffffffffffffffffffffffffffff1060071b82811c6001600160401b031060061b1782811c63ffffffff1060051b1782811c61ffff1060041b1782811c60ff109060031c17916081830184535f5281601f18519052010190565b801560071b179053565b60581b815260158101919060949053565b9190600180820184519160378311156129f6576020958360201c3d3d3e8362ffffff108461ffff10018460ff10019260b8840160f81b86528301968280890181878b0101998a9403915b6129d3575b505050500160031b60f8031b8151179052565b818101601f0151815282019084848310156129f0579190916129bb565b506129c0565b909492918360200151936080855f1a108284141615612a155750505052565b60808294959396979201855360400151846021015252010190565b919060018082018451916037831115612aae576020958360201c3d3d3e8362ffffff108461ffff10018460ff10019260f8840160f81b86528301968280890181878b0101998a9403915b612a9157505050500160031b60f8031b8151179052565b818101601f0151815282019084848310156129f057919091612a7a565b909492918360200151936080855f1a108284141615612acd5750505052565b60c08294959396979201855360400151846021015252010190565b9091815115612be05764ffffffffff60208401925b5116918215612b9a57825180601a1a906001821115612b70575060028114612b5b57600314612b4057612b3a64ffffffffff91845160301c612960565b92612afd565b612b4e90835160301c612ae8565b64ffffffffff9092612afd565b50612b3a64ffffffffff91845160301c612971565b909164ffffffffff9215612b8e57612b3a9150845160301c516128e4565b612b3a9160301c6128e4565b9092908190039150601f1982016037811115612bbd576111879250815280612a30565b5060a082018153602081015160018201526040810151602182015201601e190190565b916001915060c081530190565b60405190612bfa82610be8565b5f6020838281520152565b6040519061042082018281106001600160401b03821117610c03576040526020808352825f5b6104008110612c3957505050565b8290612c43612bed565b82828501015201612c2b56fea264697066735822122039f983ffde1b5c75b55f395dc71d5bea0e96a5a46e7a5b5d1568bca06030e1a164736f6c63430008180033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000686e7d01c7bfcb563721333a007699f154c04eb4

-----Decoded View---------------
Arg [0] : _rollup (address): 0x686E7d01C7BFCB563721333A007699F154C04eb4

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000686e7d01c7bfcb563721333a007699f154c04eb4


Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]

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