ETH Price: $2,521.00 (-1.03%)

Transaction Decoder

Block:
22547350 at May-23-2025 06:27:35 PM +UTC
Transaction Fee:
0.000072242 ETH $0.18
Gas Used:
36,121 Gas / 2 Gwei

Account State Difference:

  Address   Before After State Difference Code
0x290ca4DA...3d64d4E6A
1.353394596559032884 Eth
Nonce: 277773
1.353322354559032884 Eth
Nonce: 277774
0.000072242
(Titan Builder)
6.819156606730186458 Eth6.819169080016821225 Eth0.000012473286634767

Execution Trace

L1ChugSplashProxy.68747470( )
  • ProxyAdmin.STATICCALL( )
  • L1StandardBridge.68747470( )
    File 1 of 3: L1ChugSplashProxy
    // SPDX-License-Identifier: MIT
    pragma solidity >0.5.0 <0.8.0;
    import { iL1ChugSplashDeployer } from "./interfaces/iL1ChugSplashDeployer.sol";
    /**
     * @title L1ChugSplashProxy
     * @dev Basic ChugSplash proxy contract for L1. Very close to being a normal proxy but has added
     * functions `setCode` and `setStorage` for changing the code or storage of the contract. Nifty!
     *
     * Note for future developers: do NOT make anything in this contract 'public' unless you know what
     * you're doing. Anything public can potentially have a function signature that conflicts with a
     * signature attached to the implementation contract. Public functions SHOULD always have the
     * 'proxyCallIfNotOwner' modifier unless there's some *really* good reason not to have that
     * modifier. And there almost certainly is not a good reason to not have that modifier. Beware!
     */
    contract L1ChugSplashProxy {
        /*************
         * Constants *
         *************/
        // "Magic" prefix. When prepended to some arbitrary bytecode and used to create a contract, the
        // appended bytecode will be deployed as given.
        bytes13 constant internal DEPLOY_CODE_PREFIX = 0x600D380380600D6000396000f3;
        // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
        bytes32 constant internal IMPLEMENTATION_KEY = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
        // bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)
        bytes32 constant internal OWNER_KEY = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
        /***************
         * Constructor *
         ***************/
        
        /**
         * @param _owner Address of the initial contract owner.
         */
        constructor(
            address _owner
        ) {
            _setOwner(_owner);
        }
        /**********************
         * Function Modifiers *
         **********************/
        /**
         * Blocks a function from being called when the parent signals that the system should be paused
         * via an isUpgrading function.
         */
        modifier onlyWhenNotPaused() {
            address owner = _getOwner();
            // We do a low-level call because there's no guarantee that the owner actually *is* an
            // L1ChugSplashDeployer contract and Solidity will throw errors if we do a normal call and
            // it turns out that it isn't the right type of contract.
            (bool success, bytes memory returndata) = owner.staticcall(
                abi.encodeWithSelector(
                    iL1ChugSplashDeployer.isUpgrading.selector
                )
            );
            // If the call was unsuccessful then we assume that there's no "isUpgrading" method and we
            // can just continue as normal. We also expect that the return value is exactly 32 bytes
            // long. If this isn't the case then we can safely ignore the result.
            if (success && returndata.length == 32) {
                // Although the expected value is a *boolean*, it's safer to decode as a uint256 in the
                // case that the isUpgrading function returned something other than 0 or 1. But we only
                // really care about the case where this value is 0 (= false).
                uint256 ret = abi.decode(returndata, (uint256));
                require(
                    ret == 0,
                    "L1ChugSplashProxy: system is currently being upgraded"
                );
            }
            _;
        }
        /**
         * Makes a proxy call instead of triggering the given function when the caller is either the
         * owner or the zero address. Caller can only ever be the zero address if this function is
         * being called off-chain via eth_call, which is totally fine and can be convenient for
         * client-side tooling. Avoids situations where the proxy and implementation share a sighash
         * and the proxy function ends up being called instead of the implementation one.
         *
         * Note: msg.sender == address(0) can ONLY be triggered off-chain via eth_call. If there's a
         * way for someone to send a transaction with msg.sender == address(0) in any real context then
         * we have much bigger problems. Primary reason to include this additional allowed sender is
         * because the owner address can be changed dynamically and we do not want clients to have to
         * keep track of the current owner in order to make an eth_call that doesn't trigger the
         * proxied contract.
         */
        modifier proxyCallIfNotOwner() {
            if (msg.sender == _getOwner() || msg.sender == address(0)) {
                _;
            } else {
                // This WILL halt the call frame on completion.
                _doProxyCall();
            }
        }
        /*********************
         * Fallback Function *
         *********************/
        fallback()
            external
            payable
        {
            // Proxy call by default.
            _doProxyCall();
        }
        /********************
         * Public Functions *
         ********************/
        /**
         * Sets the code that should be running behind this proxy. Note that this scheme is a bit
         * different from the standard proxy scheme where one would typically deploy the code
         * separately and then set the implementation address. We're doing it this way because it gives
         * us a lot more freedom on the client side. Can only be triggered by the contract owner.
         * @param _code New contract code to run inside this contract.
         */
        function setCode(
            bytes memory _code
        )
            proxyCallIfNotOwner
            public
        {
            // Get the code hash of the current implementation.
            address implementation = _getImplementation();
            // If the code hash matches the new implementation then we return early.
            if (keccak256(_code) == _getAccountCodeHash(implementation)) {
                return;
            }
            // Create the deploycode by appending the magic prefix.
            bytes memory deploycode = abi.encodePacked(
                DEPLOY_CODE_PREFIX,
                _code
            );
            // Deploy the code and set the new implementation address.
            address newImplementation;
            assembly {
                newImplementation := create(0x0, add(deploycode, 0x20), mload(deploycode))
            }
            // Check that the code was actually deployed correctly. I'm not sure if you can ever
            // actually fail this check. Should only happen if the contract creation from above runs
            // out of gas but this parent execution thread does NOT run out of gas. Seems like we
            // should be doing this check anyway though.
            require(
                _getAccountCodeHash(newImplementation) == keccak256(_code),
                "L1ChugSplashProxy: code was not correctly deployed."
            );
            _setImplementation(newImplementation);
        }
        /**
         * Modifies some storage slot within the proxy contract. Gives us a lot of power to perform
         * upgrades in a more transparent way. Only callable by the owner.
         * @param _key Storage key to modify.
         * @param _value New value for the storage key.
         */
        function setStorage(
            bytes32 _key,
            bytes32 _value
        )
            proxyCallIfNotOwner
            public
        {
            assembly {
                sstore(_key, _value)
            }
        }
        /**
         * Changes the owner of the proxy contract. Only callable by the owner.
         * @param _owner New owner of the proxy contract.
         */
        function setOwner(
            address _owner
        )
            proxyCallIfNotOwner
            public
        {
            _setOwner(_owner);
        }
        /**
         * Queries the owner of the proxy contract. Can only be called by the owner OR by making an
         * eth_call and setting the "from" address to address(0).
         * @return Owner address.
         */
        function getOwner()
            proxyCallIfNotOwner
            public
            returns (
                address
            )
        {
            return _getOwner();
        }
        /**
         * Queries the implementation address. Can only be called by the owner OR by making an
         * eth_call and setting the "from" address to address(0).
         * @return Implementation address.
         */
        function getImplementation()
            proxyCallIfNotOwner
            public
            returns (
                address
            )
        {
            return _getImplementation();
        }
        /**********************
         * Internal Functions *
         **********************/
        /**
         * Sets the implementation address.
         * @param _implementation New implementation address.
         */
        function _setImplementation(
            address _implementation
        )
            internal
        {
            assembly {
                sstore(IMPLEMENTATION_KEY, _implementation)
            }
        }
        /**
         * Queries the implementation address.
         * @return Implementation address.
         */
        function _getImplementation()
            internal
            view
            returns (
                address
            )
        {
            address implementation;
            assembly {
                implementation := sload(IMPLEMENTATION_KEY)
            }
            return implementation;
        }
        /**
         * Changes the owner of the proxy contract.
         * @param _owner New owner of the proxy contract.
         */
        function _setOwner(
            address _owner
        )
            internal
        {
            assembly {
                sstore(OWNER_KEY, _owner)
            }
        }
        /**
         * Queries the owner of the proxy contract.
         * @return Owner address.
         */
        function _getOwner()
            internal
            view 
            returns (
                address
            )
        {
            address owner;
            assembly {
                owner := sload(OWNER_KEY)
            }
            return owner;
        }
        /**
         * Gets the code hash for a given account.
         * @param _account Address of the account to get a code hash for.
         * @return Code hash for the account.
         */
        function _getAccountCodeHash(
            address _account
        )
            internal
            view
            returns (
                bytes32
            )
        {
            bytes32 codeHash;
            assembly {
                codeHash := extcodehash(_account)
            }
            return codeHash;
        }
        /**
         * Performs the proxy call via a delegatecall.
         */
        function _doProxyCall()
            onlyWhenNotPaused
            internal
        {
            address implementation = _getImplementation();
            require(
                implementation != address(0),
                "L1ChugSplashProxy: implementation is not set yet"
            );
            assembly {
                // Copy calldata into memory at 0x0....calldatasize.
                calldatacopy(0x0, 0x0, calldatasize())
                // Perform the delegatecall, make sure to pass all available gas.
                let success := delegatecall(gas(), implementation, 0x0, calldatasize(), 0x0, 0x0)
                // Copy returndata into memory at 0x0....returndatasize. Note that this *will*
                // overwrite the calldata that we just copied into memory but that doesn't really
                // matter because we'll be returning in a second anyway.
                returndatacopy(0x0, 0x0, returndatasize())
                
                // Success == 0 means a revert. We'll revert too and pass the data up.
                if iszero(success) {
                    revert(0x0, returndatasize())
                }
                // Otherwise we'll just return and pass the data up.
                return(0x0, returndatasize())
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >0.5.0 <0.8.0;
    /**
     * @title iL1ChugSplashDeployer
     */
    interface iL1ChugSplashDeployer {
        function isUpgrading()
            external
            view
            returns (
                bool
            );
    }
    

    File 2 of 3: ProxyAdmin
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.15;
    import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
    /**
     * @custom:legacy
     * @title AddressManager
     * @notice AddressManager is a legacy contract that was used in the old version of the Optimism
     *         system to manage a registry of string names to addresses. We now use a more standard
     *         proxy system instead, but this contract is still necessary for backwards compatibility
     *         with several older contracts.
     */
    contract AddressManager is Ownable {
        /**
         * @notice Mapping of the hashes of string names to addresses.
         */
        mapping(bytes32 => address) private addresses;
        /**
         * @notice Emitted when an address is modified in the registry.
         *
         * @param name       String name being set in the registry.
         * @param newAddress Address set for the given name.
         * @param oldAddress Address that was previously set for the given name.
         */
        event AddressSet(string indexed name, address newAddress, address oldAddress);
        /**
         * @notice Changes the address associated with a particular name.
         *
         * @param _name    String name to associate an address with.
         * @param _address Address to associate with the name.
         */
        function setAddress(string memory _name, address _address) external onlyOwner {
            bytes32 nameHash = _getNameHash(_name);
            address oldAddress = addresses[nameHash];
            addresses[nameHash] = _address;
            emit AddressSet(_name, _address, oldAddress);
        }
        /**
         * @notice Retrieves the address associated with a given name.
         *
         * @param _name Name to retrieve an address for.
         *
         * @return Address associated with the given name.
         */
        function getAddress(string memory _name) external view returns (address) {
            return addresses[_getNameHash(_name)];
        }
        /**
         * @notice Computes the hash of a name.
         *
         * @param _name Name to compute a hash for.
         *
         * @return Hash of the given name.
         */
        function _getNameHash(string memory _name) internal pure returns (bytes32) {
            return keccak256(abi.encodePacked(_name));
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.15;
    /**
     * @title IL1ChugSplashDeployer
     */
    interface IL1ChugSplashDeployer {
        function isUpgrading() external view returns (bool);
    }
    /**
     * @custom:legacy
     * @title L1ChugSplashProxy
     * @notice Basic ChugSplash proxy contract for L1. Very close to being a normal proxy but has added
     *         functions `setCode` and `setStorage` for changing the code or storage of the contract.
     *
     *         Note for future developers: do NOT make anything in this contract 'public' unless you
     *         know what you're doing. Anything public can potentially have a function signature that
     *         conflicts with a signature attached to the implementation contract. Public functions
     *         SHOULD always have the `proxyCallIfNotOwner` modifier unless there's some *really* good
     *         reason not to have that modifier. And there almost certainly is not a good reason to not
     *         have that modifier. Beware!
     */
    contract L1ChugSplashProxy {
        /**
         * @notice "Magic" prefix. When prepended to some arbitrary bytecode and used to create a
         *         contract, the appended bytecode will be deployed as given.
         */
        bytes13 internal constant DEPLOY_CODE_PREFIX = 0x600D380380600D6000396000f3;
        /**
         * @notice bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
         */
        bytes32 internal constant IMPLEMENTATION_KEY =
            0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
        /**
         * @notice bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)
         */
        bytes32 internal constant OWNER_KEY =
            0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
        /**
         * @notice Blocks a function from being called when the parent signals that the system should
         *         be paused via an isUpgrading function.
         */
        modifier onlyWhenNotPaused() {
            address owner = _getOwner();
            // We do a low-level call because there's no guarantee that the owner actually *is* an
            // L1ChugSplashDeployer contract and Solidity will throw errors if we do a normal call and
            // it turns out that it isn't the right type of contract.
            (bool success, bytes memory returndata) = owner.staticcall(
                abi.encodeWithSelector(IL1ChugSplashDeployer.isUpgrading.selector)
            );
            // If the call was unsuccessful then we assume that there's no "isUpgrading" method and we
            // can just continue as normal. We also expect that the return value is exactly 32 bytes
            // long. If this isn't the case then we can safely ignore the result.
            if (success && returndata.length == 32) {
                // Although the expected value is a *boolean*, it's safer to decode as a uint256 in the
                // case that the isUpgrading function returned something other than 0 or 1. But we only
                // really care about the case where this value is 0 (= false).
                uint256 ret = abi.decode(returndata, (uint256));
                require(ret == 0, "L1ChugSplashProxy: system is currently being upgraded");
            }
            _;
        }
        /**
         * @notice Makes a proxy call instead of triggering the given function when the caller is
         *         either the owner or the zero address. Caller can only ever be the zero address if
         *         this function is being called off-chain via eth_call, which is totally fine and can
         *         be convenient for client-side tooling. Avoids situations where the proxy and
         *         implementation share a sighash and the proxy function ends up being called instead
         *         of the implementation one.
         *
         *         Note: msg.sender == address(0) can ONLY be triggered off-chain via eth_call. If
         *         there's a way for someone to send a transaction with msg.sender == address(0) in any
         *         real context then we have much bigger problems. Primary reason to include this
         *         additional allowed sender is because the owner address can be changed dynamically
         *         and we do not want clients to have to keep track of the current owner in order to
         *         make an eth_call that doesn't trigger the proxied contract.
         */
        // slither-disable-next-line incorrect-modifier
        modifier proxyCallIfNotOwner() {
            if (msg.sender == _getOwner() || msg.sender == address(0)) {
                _;
            } else {
                // This WILL halt the call frame on completion.
                _doProxyCall();
            }
        }
        /**
         * @param _owner Address of the initial contract owner.
         */
        constructor(address _owner) {
            _setOwner(_owner);
        }
        // slither-disable-next-line locked-ether
        receive() external payable {
            // Proxy call by default.
            _doProxyCall();
        }
        // slither-disable-next-line locked-ether
        fallback() external payable {
            // Proxy call by default.
            _doProxyCall();
        }
        /**
         * @notice Sets the code that should be running behind this proxy.
         *
         *         Note: This scheme is a bit different from the standard proxy scheme where one would
         *         typically deploy the code separately and then set the implementation address. We're
         *         doing it this way because it gives us a lot more freedom on the client side. Can
         *         only be triggered by the contract owner.
         *
         * @param _code New contract code to run inside this contract.
         */
        function setCode(bytes memory _code) external proxyCallIfNotOwner {
            // Get the code hash of the current implementation.
            address implementation = _getImplementation();
            // If the code hash matches the new implementation then we return early.
            if (keccak256(_code) == _getAccountCodeHash(implementation)) {
                return;
            }
            // Create the deploycode by appending the magic prefix.
            bytes memory deploycode = abi.encodePacked(DEPLOY_CODE_PREFIX, _code);
            // Deploy the code and set the new implementation address.
            address newImplementation;
            assembly {
                newImplementation := create(0x0, add(deploycode, 0x20), mload(deploycode))
            }
            // Check that the code was actually deployed correctly. I'm not sure if you can ever
            // actually fail this check. Should only happen if the contract creation from above runs
            // out of gas but this parent execution thread does NOT run out of gas. Seems like we
            // should be doing this check anyway though.
            require(
                _getAccountCodeHash(newImplementation) == keccak256(_code),
                "L1ChugSplashProxy: code was not correctly deployed"
            );
            _setImplementation(newImplementation);
        }
        /**
         * @notice Modifies some storage slot within the proxy contract. Gives us a lot of power to
         *         perform upgrades in a more transparent way. Only callable by the owner.
         *
         * @param _key   Storage key to modify.
         * @param _value New value for the storage key.
         */
        function setStorage(bytes32 _key, bytes32 _value) external proxyCallIfNotOwner {
            assembly {
                sstore(_key, _value)
            }
        }
        /**
         * @notice Changes the owner of the proxy contract. Only callable by the owner.
         *
         * @param _owner New owner of the proxy contract.
         */
        function setOwner(address _owner) external proxyCallIfNotOwner {
            _setOwner(_owner);
        }
        /**
         * @notice Queries the owner of the proxy contract. Can only be called by the owner OR by
         *         making an eth_call and setting the "from" address to address(0).
         *
         * @return Owner address.
         */
        function getOwner() external proxyCallIfNotOwner returns (address) {
            return _getOwner();
        }
        /**
         * @notice Queries the implementation address. Can only be called by the owner OR by making an
         *         eth_call and setting the "from" address to address(0).
         *
         * @return Implementation address.
         */
        function getImplementation() external proxyCallIfNotOwner returns (address) {
            return _getImplementation();
        }
        /**
         * @notice Sets the implementation address.
         *
         * @param _implementation New implementation address.
         */
        function _setImplementation(address _implementation) internal {
            assembly {
                sstore(IMPLEMENTATION_KEY, _implementation)
            }
        }
        /**
         * @notice Changes the owner of the proxy contract.
         *
         * @param _owner New owner of the proxy contract.
         */
        function _setOwner(address _owner) internal {
            assembly {
                sstore(OWNER_KEY, _owner)
            }
        }
        /**
         * @notice Performs the proxy call via a delegatecall.
         */
        function _doProxyCall() internal onlyWhenNotPaused {
            address implementation = _getImplementation();
            require(implementation != address(0), "L1ChugSplashProxy: implementation is not set yet");
            assembly {
                // Copy calldata into memory at 0x0....calldatasize.
                calldatacopy(0x0, 0x0, calldatasize())
                // Perform the delegatecall, make sure to pass all available gas.
                let success := delegatecall(gas(), implementation, 0x0, calldatasize(), 0x0, 0x0)
                // Copy returndata into memory at 0x0....returndatasize. Note that this *will*
                // overwrite the calldata that we just copied into memory but that doesn't really
                // matter because we'll be returning in a second anyway.
                returndatacopy(0x0, 0x0, returndatasize())
                // Success == 0 means a revert. We'll revert too and pass the data up.
                if iszero(success) {
                    revert(0x0, returndatasize())
                }
                // Otherwise we'll just return and pass the data up.
                return(0x0, returndatasize())
            }
        }
        /**
         * @notice Queries the implementation address.
         *
         * @return Implementation address.
         */
        function _getImplementation() internal view returns (address) {
            address implementation;
            assembly {
                implementation := sload(IMPLEMENTATION_KEY)
            }
            return implementation;
        }
        /**
         * @notice Queries the owner of the proxy contract.
         *
         * @return Owner address.
         */
        function _getOwner() internal view returns (address) {
            address owner;
            assembly {
                owner := sload(OWNER_KEY)
            }
            return owner;
        }
        /**
         * @notice Gets the code hash for a given account.
         *
         * @param _account Address of the account to get a code hash for.
         *
         * @return Code hash for the account.
         */
        function _getAccountCodeHash(address _account) internal view returns (bytes32) {
            bytes32 codeHash;
            assembly {
                codeHash := extcodehash(_account)
            }
            return codeHash;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.15;
    /**
     * @title Proxy
     * @notice Proxy is a transparent proxy that passes through the call if the caller is the owner or
     *         if the caller is address(0), meaning that the call originated from an off-chain
     *         simulation.
     */
    contract Proxy {
        /**
         * @notice The storage slot that holds the address of the implementation.
         *         bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
         */
        bytes32 internal constant IMPLEMENTATION_KEY =
            0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
        /**
         * @notice The storage slot that holds the address of the owner.
         *         bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)
         */
        bytes32 internal constant OWNER_KEY =
            0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
        /**
         * @notice An event that is emitted each time the implementation is changed. This event is part
         *         of the EIP-1967 specification.
         *
         * @param implementation The address of the implementation contract
         */
        event Upgraded(address indexed implementation);
        /**
         * @notice An event that is emitted each time the owner is upgraded. This event is part of the
         *         EIP-1967 specification.
         *
         * @param previousAdmin The previous owner of the contract
         * @param newAdmin      The new owner of the contract
         */
        event AdminChanged(address previousAdmin, address newAdmin);
        /**
         * @notice A modifier that reverts if not called by the owner or by address(0) to allow
         *         eth_call to interact with this proxy without needing to use low-level storage
         *         inspection. We assume that nobody is able to trigger calls from address(0) during
         *         normal EVM execution.
         */
        modifier proxyCallIfNotAdmin() {
            if (msg.sender == _getAdmin() || msg.sender == address(0)) {
                _;
            } else {
                // This WILL halt the call frame on completion.
                _doProxyCall();
            }
        }
        /**
         * @notice Sets the initial admin during contract deployment. Admin address is stored at the
         *         EIP-1967 admin storage slot so that accidental storage collision with the
         *         implementation is not possible.
         *
         * @param _admin Address of the initial contract admin. Admin as the ability to access the
         *               transparent proxy interface.
         */
        constructor(address _admin) {
            _changeAdmin(_admin);
        }
        // slither-disable-next-line locked-ether
        receive() external payable {
            // Proxy call by default.
            _doProxyCall();
        }
        // slither-disable-next-line locked-ether
        fallback() external payable {
            // Proxy call by default.
            _doProxyCall();
        }
        /**
         * @notice Set the implementation contract address. The code at the given address will execute
         *         when this contract is called.
         *
         * @param _implementation Address of the implementation contract.
         */
        function upgradeTo(address _implementation) public virtual proxyCallIfNotAdmin {
            _setImplementation(_implementation);
        }
        /**
         * @notice Set the implementation and call a function in a single transaction. Useful to ensure
         *         atomic execution of initialization-based upgrades.
         *
         * @param _implementation Address of the implementation contract.
         * @param _data           Calldata to delegatecall the new implementation with.
         */
        function upgradeToAndCall(address _implementation, bytes calldata _data)
            public
            payable
            virtual
            proxyCallIfNotAdmin
            returns (bytes memory)
        {
            _setImplementation(_implementation);
            (bool success, bytes memory returndata) = _implementation.delegatecall(_data);
            require(success, "Proxy: delegatecall to new implementation contract failed");
            return returndata;
        }
        /**
         * @notice Changes the owner of the proxy contract. Only callable by the owner.
         *
         * @param _admin New owner of the proxy contract.
         */
        function changeAdmin(address _admin) public virtual proxyCallIfNotAdmin {
            _changeAdmin(_admin);
        }
        /**
         * @notice Gets the owner of the proxy contract.
         *
         * @return Owner address.
         */
        function admin() public virtual proxyCallIfNotAdmin returns (address) {
            return _getAdmin();
        }
        /**
         * @notice Queries the implementation address.
         *
         * @return Implementation address.
         */
        function implementation() public virtual proxyCallIfNotAdmin returns (address) {
            return _getImplementation();
        }
        /**
         * @notice Sets the implementation address.
         *
         * @param _implementation New implementation address.
         */
        function _setImplementation(address _implementation) internal {
            assembly {
                sstore(IMPLEMENTATION_KEY, _implementation)
            }
            emit Upgraded(_implementation);
        }
        /**
         * @notice Changes the owner of the proxy contract.
         *
         * @param _admin New owner of the proxy contract.
         */
        function _changeAdmin(address _admin) internal {
            address previous = _getAdmin();
            assembly {
                sstore(OWNER_KEY, _admin)
            }
            emit AdminChanged(previous, _admin);
        }
        /**
         * @notice Performs the proxy call via a delegatecall.
         */
        function _doProxyCall() internal {
            address impl = _getImplementation();
            require(impl != address(0), "Proxy: implementation not initialized");
            assembly {
                // Copy calldata into memory at 0x0....calldatasize.
                calldatacopy(0x0, 0x0, calldatasize())
                // Perform the delegatecall, make sure to pass all available gas.
                let success := delegatecall(gas(), impl, 0x0, calldatasize(), 0x0, 0x0)
                // Copy returndata into memory at 0x0....returndatasize. Note that this *will*
                // overwrite the calldata that we just copied into memory but that doesn't really
                // matter because we'll be returning in a second anyway.
                returndatacopy(0x0, 0x0, returndatasize())
                // Success == 0 means a revert. We'll revert too and pass the data up.
                if iszero(success) {
                    revert(0x0, returndatasize())
                }
                // Otherwise we'll just return and pass the data up.
                return(0x0, returndatasize())
            }
        }
        /**
         * @notice Queries the implementation address.
         *
         * @return Implementation address.
         */
        function _getImplementation() internal view returns (address) {
            address impl;
            assembly {
                impl := sload(IMPLEMENTATION_KEY)
            }
            return impl;
        }
        /**
         * @notice Queries the owner of the proxy contract.
         *
         * @return Owner address.
         */
        function _getAdmin() internal view returns (address) {
            address owner;
            assembly {
                owner := sload(OWNER_KEY)
            }
            return owner;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.15;
    import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
    import { Proxy } from "./Proxy.sol";
    import { AddressManager } from "../legacy/AddressManager.sol";
    import { L1ChugSplashProxy } from "../legacy/L1ChugSplashProxy.sol";
    /**
     * @title IStaticERC1967Proxy
     * @notice IStaticERC1967Proxy is a static version of the ERC1967 proxy interface.
     */
    interface IStaticERC1967Proxy {
        function implementation() external view returns (address);
        function admin() external view returns (address);
    }
    /**
     * @title IStaticL1ChugSplashProxy
     * @notice IStaticL1ChugSplashProxy is a static version of the ChugSplash proxy interface.
     */
    interface IStaticL1ChugSplashProxy {
        function getImplementation() external view returns (address);
        function getOwner() external view returns (address);
    }
    /**
     * @title ProxyAdmin
     * @notice This is an auxiliary contract meant to be assigned as the admin of an ERC1967 Proxy,
     *         based on the OpenZeppelin implementation. It has backwards compatibility logic to work
     *         with the various types of proxies that have been deployed by Optimism in the past.
     */
    contract ProxyAdmin is Ownable {
        /**
         * @notice The proxy types that the ProxyAdmin can manage.
         *
         * @custom:value ERC1967    Represents an ERC1967 compliant transparent proxy interface.
         * @custom:value CHUGSPLASH Represents the Chugsplash proxy interface (legacy).
         * @custom:value RESOLVED   Represents the ResolvedDelegate proxy (legacy).
         */
        enum ProxyType {
            ERC1967,
            CHUGSPLASH,
            RESOLVED
        }
        /**
         * @notice A mapping of proxy types, used for backwards compatibility.
         */
        mapping(address => ProxyType) public proxyType;
        /**
         * @notice A reverse mapping of addresses to names held in the AddressManager. This must be
         *         manually kept up to date with changes in the AddressManager for this contract
         *         to be able to work as an admin for the ResolvedDelegateProxy type.
         */
        mapping(address => string) public implementationName;
        /**
         * @notice The address of the address manager, this is required to manage the
         *         ResolvedDelegateProxy type.
         */
        AddressManager public addressManager;
        /**
         * @notice A legacy upgrading indicator used by the old Chugsplash Proxy.
         */
        bool internal upgrading;
        /**
         * @param _owner Address of the initial owner of this contract.
         */
        constructor(address _owner) Ownable() {
            _transferOwnership(_owner);
        }
        /**
         * @notice Sets the proxy type for a given address. Only required for non-standard (legacy)
         *         proxy types.
         *
         * @param _address Address of the proxy.
         * @param _type    Type of the proxy.
         */
        function setProxyType(address _address, ProxyType _type) external onlyOwner {
            proxyType[_address] = _type;
        }
        /**
         * @notice Sets the implementation name for a given address. Only required for
         *         ResolvedDelegateProxy type proxies that have an implementation name.
         *
         * @param _address Address of the ResolvedDelegateProxy.
         * @param _name    Name of the implementation for the proxy.
         */
        function setImplementationName(address _address, string memory _name) external onlyOwner {
            implementationName[_address] = _name;
        }
        /**
         * @notice Set the address of the AddressManager. This is required to manage legacy
         *         ResolvedDelegateProxy type proxy contracts.
         *
         * @param _address Address of the AddressManager.
         */
        function setAddressManager(AddressManager _address) external onlyOwner {
            addressManager = _address;
        }
        /**
         * @custom:legacy
         * @notice Set an address in the address manager. Since only the owner of the AddressManager
         *         can directly modify addresses and the ProxyAdmin will own the AddressManager, this
         *         gives the owner of the ProxyAdmin the ability to modify addresses directly.
         *
         * @param _name    Name to set within the AddressManager.
         * @param _address Address to attach to the given name.
         */
        function setAddress(string memory _name, address _address) external onlyOwner {
            addressManager.setAddress(_name, _address);
        }
        /**
         * @custom:legacy
         * @notice Set the upgrading status for the Chugsplash proxy type.
         *
         * @param _upgrading Whether or not the system is upgrading.
         */
        function setUpgrading(bool _upgrading) external onlyOwner {
            upgrading = _upgrading;
        }
        /**
         * @custom:legacy
         * @notice Legacy function used to tell ChugSplashProxy contracts if an upgrade is happening.
         *
         * @return Whether or not there is an upgrade going on. May not actually tell you whether an
         *         upgrade is going on, since we don't currently plan to use this variable for anything
         *         other than a legacy indicator to fix a UX bug in the ChugSplash proxy.
         */
        function isUpgrading() external view returns (bool) {
            return upgrading;
        }
        /**
         * @notice Returns the implementation of the given proxy address.
         *
         * @param _proxy Address of the proxy to get the implementation of.
         *
         * @return Address of the implementation of the proxy.
         */
        function getProxyImplementation(address _proxy) external view returns (address) {
            ProxyType ptype = proxyType[_proxy];
            if (ptype == ProxyType.ERC1967) {
                return IStaticERC1967Proxy(_proxy).implementation();
            } else if (ptype == ProxyType.CHUGSPLASH) {
                return IStaticL1ChugSplashProxy(_proxy).getImplementation();
            } else if (ptype == ProxyType.RESOLVED) {
                return addressManager.getAddress(implementationName[_proxy]);
            } else {
                revert("ProxyAdmin: unknown proxy type");
            }
        }
        /**
         * @notice Returns the admin of the given proxy address.
         *
         * @param _proxy Address of the proxy to get the admin of.
         *
         * @return Address of the admin of the proxy.
         */
        function getProxyAdmin(address payable _proxy) external view returns (address) {
            ProxyType ptype = proxyType[_proxy];
            if (ptype == ProxyType.ERC1967) {
                return IStaticERC1967Proxy(_proxy).admin();
            } else if (ptype == ProxyType.CHUGSPLASH) {
                return IStaticL1ChugSplashProxy(_proxy).getOwner();
            } else if (ptype == ProxyType.RESOLVED) {
                return addressManager.owner();
            } else {
                revert("ProxyAdmin: unknown proxy type");
            }
        }
        /**
         * @notice Updates the admin of the given proxy address.
         *
         * @param _proxy    Address of the proxy to update.
         * @param _newAdmin Address of the new proxy admin.
         */
        function changeProxyAdmin(address payable _proxy, address _newAdmin) external onlyOwner {
            ProxyType ptype = proxyType[_proxy];
            if (ptype == ProxyType.ERC1967) {
                Proxy(_proxy).changeAdmin(_newAdmin);
            } else if (ptype == ProxyType.CHUGSPLASH) {
                L1ChugSplashProxy(_proxy).setOwner(_newAdmin);
            } else if (ptype == ProxyType.RESOLVED) {
                addressManager.transferOwnership(_newAdmin);
            } else {
                revert("ProxyAdmin: unknown proxy type");
            }
        }
        /**
         * @notice Changes a proxy's implementation contract.
         *
         * @param _proxy          Address of the proxy to upgrade.
         * @param _implementation Address of the new implementation address.
         */
        function upgrade(address payable _proxy, address _implementation) public onlyOwner {
            ProxyType ptype = proxyType[_proxy];
            if (ptype == ProxyType.ERC1967) {
                Proxy(_proxy).upgradeTo(_implementation);
            } else if (ptype == ProxyType.CHUGSPLASH) {
                L1ChugSplashProxy(_proxy).setStorage(
                    // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
                    0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc,
                    bytes32(uint256(uint160(_implementation)))
                );
            } else if (ptype == ProxyType.RESOLVED) {
                string memory name = implementationName[_proxy];
                addressManager.setAddress(name, _implementation);
            } else {
                // It should not be possible to retrieve a ProxyType value which is not matched by
                // one of the previous conditions.
                assert(false);
            }
        }
        /**
         * @notice Changes a proxy's implementation contract and delegatecalls the new implementation
         *         with some given data. Useful for atomic upgrade-and-initialize calls.
         *
         * @param _proxy          Address of the proxy to upgrade.
         * @param _implementation Address of the new implementation address.
         * @param _data           Data to trigger the new implementation with.
         */
        function upgradeAndCall(
            address payable _proxy,
            address _implementation,
            bytes memory _data
        ) external payable onlyOwner {
            ProxyType ptype = proxyType[_proxy];
            if (ptype == ProxyType.ERC1967) {
                Proxy(_proxy).upgradeToAndCall{ value: msg.value }(_implementation, _data);
            } else {
                // reverts if proxy type is unknown
                upgrade(_proxy, _implementation);
                (bool success, ) = _proxy.call{ value: msg.value }(_data);
                require(success, "ProxyAdmin: call to proxy after upgrade failed");
            }
        }
    }
    // 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);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Provides information about the current execution context, including the
     * sender of the transaction and its data. While these are generally available
     * via msg.sender and msg.data, they should not be accessed in such a direct
     * manner, since when dealing with meta-transactions the account sending and
     * paying for execution may not be the actual sender (as far as an application
     * is concerned).
     *
     * This contract is only required for intermediate, library-like contracts.
     */
    abstract contract Context {
        function _msgSender() internal view virtual returns (address) {
            return msg.sender;
        }
        function _msgData() internal view virtual returns (bytes calldata) {
            return msg.data;
        }
    }
    

    File 3 of 3: L1StandardBridge
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.15;
    // Contracts
    import { StandardBridge } from "src/universal/StandardBridge.sol";
    // Libraries
    import { Predeploys } from "src/libraries/Predeploys.sol";
    // Interfaces
    import { ISemver } from "interfaces/universal/ISemver.sol";
    import { ICrossDomainMessenger } from "interfaces/universal/ICrossDomainMessenger.sol";
    import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol";
    /// @custom:proxied true
    /// @title L1StandardBridge
    /// @notice The L1StandardBridge is responsible for transfering ETH and ERC20 tokens between L1 and
    ///         L2. In the case that an ERC20 token is native to L1, it will be escrowed within this
    ///         contract. If the ERC20 token is native to L2, it will be burnt. Before Bedrock, ETH was
    ///         stored within this contract. After Bedrock, ETH is instead stored inside the
    ///         OptimismPortal contract.
    ///         NOTE: this contract is not intended to support all variations of ERC20 tokens. Examples
    ///         of some token types that may not be properly supported by this contract include, but are
    ///         not limited to: tokens with transfer fees, rebasing tokens, and tokens with blocklists.
    contract L1StandardBridge is StandardBridge, ISemver {
        /// @custom:legacy
        /// @notice Emitted whenever a deposit of ETH from L1 into L2 is initiated.
        /// @param from      Address of the depositor.
        /// @param to        Address of the recipient on L2.
        /// @param amount    Amount of ETH deposited.
        /// @param extraData Extra data attached to the deposit.
        event ETHDepositInitiated(address indexed from, address indexed to, uint256 amount, bytes extraData);
        /// @custom:legacy
        /// @notice Emitted whenever a withdrawal of ETH from L2 to L1 is finalized.
        /// @param from      Address of the withdrawer.
        /// @param to        Address of the recipient on L1.
        /// @param amount    Amount of ETH withdrawn.
        /// @param extraData Extra data attached to the withdrawal.
        event ETHWithdrawalFinalized(address indexed from, address indexed to, uint256 amount, bytes extraData);
        /// @custom:legacy
        /// @notice Emitted whenever an ERC20 deposit is initiated.
        /// @param l1Token   Address of the token on L1.
        /// @param l2Token   Address of the corresponding token on L2.
        /// @param from      Address of the depositor.
        /// @param to        Address of the recipient on L2.
        /// @param amount    Amount of the ERC20 deposited.
        /// @param extraData Extra data attached to the deposit.
        event ERC20DepositInitiated(
            address indexed l1Token,
            address indexed l2Token,
            address indexed from,
            address to,
            uint256 amount,
            bytes extraData
        );
        /// @custom:legacy
        /// @notice Emitted whenever an ERC20 withdrawal is finalized.
        /// @param l1Token   Address of the token on L1.
        /// @param l2Token   Address of the corresponding token on L2.
        /// @param from      Address of the withdrawer.
        /// @param to        Address of the recipient on L1.
        /// @param amount    Amount of the ERC20 withdrawn.
        /// @param extraData Extra data attached to the withdrawal.
        event ERC20WithdrawalFinalized(
            address indexed l1Token,
            address indexed l2Token,
            address indexed from,
            address to,
            uint256 amount,
            bytes extraData
        );
        /// @notice Semantic version.
        /// @custom:semver 2.3.0
        string public constant version = "2.3.0";
        /// @notice Address of the SuperchainConfig contract.
        ISuperchainConfig public superchainConfig;
        /// @custom:legacy
        /// @custom:spacer systemConfig
        /// @notice Spacer taking up the legacy `systemConfig` slot.
        address private spacer_51_0_20;
        /// @notice Constructs the L1StandardBridge contract.
        constructor() StandardBridge() {
            _disableInitializers();
        }
        /// @notice Initializer.
        /// @param _messenger        Contract for the CrossDomainMessenger on this network.
        /// @param _superchainConfig Contract for the SuperchainConfig on this network.
        function initialize(ICrossDomainMessenger _messenger, ISuperchainConfig _superchainConfig) external initializer {
            superchainConfig = _superchainConfig;
            __StandardBridge_init({
                _messenger: _messenger,
                _otherBridge: StandardBridge(payable(Predeploys.L2_STANDARD_BRIDGE))
            });
        }
        /// @inheritdoc StandardBridge
        function paused() public view override returns (bool) {
            return superchainConfig.paused();
        }
        /// @notice Allows EOAs to bridge ETH by sending directly to the bridge.
        receive() external payable override onlyEOA {
            _initiateETHDeposit(msg.sender, msg.sender, RECEIVE_DEFAULT_GAS_LIMIT, bytes(""));
        }
        /// @custom:legacy
        /// @notice Deposits some amount of ETH into the sender's account on L2.
        /// @param _minGasLimit Minimum gas limit for the deposit message on L2.
        /// @param _extraData   Optional data to forward to L2.
        ///                     Data supplied here will not be used to execute any code on L2 and is
        ///                     only emitted as extra data for the convenience of off-chain tooling.
        function depositETH(uint32 _minGasLimit, bytes calldata _extraData) external payable onlyEOA {
            _initiateETHDeposit(msg.sender, msg.sender, _minGasLimit, _extraData);
        }
        /// @custom:legacy
        /// @notice Deposits some amount of ETH into a target account on L2.
        ///         Note that if ETH is sent to a contract on L2 and the call fails, then that ETH will
        ///         be locked in the L2StandardBridge. ETH may be recoverable if the call can be
        ///         successfully replayed by increasing the amount of gas supplied to the call. If the
        ///         call will fail for any amount of gas, then the ETH will be locked permanently.
        /// @param _to          Address of the recipient on L2.
        /// @param _minGasLimit Minimum gas limit for the deposit message on L2.
        /// @param _extraData   Optional data to forward to L2.
        ///                     Data supplied here will not be used to execute any code on L2 and is
        ///                     only emitted as extra data for the convenience of off-chain tooling.
        function depositETHTo(address _to, uint32 _minGasLimit, bytes calldata _extraData) external payable {
            _initiateETHDeposit(msg.sender, _to, _minGasLimit, _extraData);
        }
        /// @custom:legacy
        /// @notice Deposits some amount of ERC20 tokens into the sender's account on L2.
        /// @param _l1Token     Address of the L1 token being deposited.
        /// @param _l2Token     Address of the corresponding token on L2.
        /// @param _amount      Amount of the ERC20 to deposit.
        /// @param _minGasLimit Minimum gas limit for the deposit message on L2.
        /// @param _extraData   Optional data to forward to L2.
        ///                     Data supplied here will not be used to execute any code on L2 and is
        ///                     only emitted as extra data for the convenience of off-chain tooling.
        function depositERC20(
            address _l1Token,
            address _l2Token,
            uint256 _amount,
            uint32 _minGasLimit,
            bytes calldata _extraData
        )
            external
            virtual
            onlyEOA
        {
            _initiateERC20Deposit(_l1Token, _l2Token, msg.sender, msg.sender, _amount, _minGasLimit, _extraData);
        }
        /// @custom:legacy
        /// @notice Deposits some amount of ERC20 tokens into a target account on L2.
        /// @param _l1Token     Address of the L1 token being deposited.
        /// @param _l2Token     Address of the corresponding token on L2.
        /// @param _to          Address of the recipient on L2.
        /// @param _amount      Amount of the ERC20 to deposit.
        /// @param _minGasLimit Minimum gas limit for the deposit message on L2.
        /// @param _extraData   Optional data to forward to L2.
        ///                     Data supplied here will not be used to execute any code on L2 and is
        ///                     only emitted as extra data for the convenience of off-chain tooling.
        function depositERC20To(
            address _l1Token,
            address _l2Token,
            address _to,
            uint256 _amount,
            uint32 _minGasLimit,
            bytes calldata _extraData
        )
            external
            virtual
        {
            _initiateERC20Deposit(_l1Token, _l2Token, msg.sender, _to, _amount, _minGasLimit, _extraData);
        }
        /// @custom:legacy
        /// @notice Finalizes a withdrawal of ETH from L2.
        /// @param _from      Address of the withdrawer on L2.
        /// @param _to        Address of the recipient on L1.
        /// @param _amount    Amount of ETH to withdraw.
        /// @param _extraData Optional data forwarded from L2.
        function finalizeETHWithdrawal(
            address _from,
            address _to,
            uint256 _amount,
            bytes calldata _extraData
        )
            external
            payable
        {
            finalizeBridgeETH(_from, _to, _amount, _extraData);
        }
        /// @custom:legacy
        /// @notice Finalizes a withdrawal of ERC20 tokens from L2.
        /// @param _l1Token   Address of the token on L1.
        /// @param _l2Token   Address of the corresponding token on L2.
        /// @param _from      Address of the withdrawer on L2.
        /// @param _to        Address of the recipient on L1.
        /// @param _amount    Amount of the ERC20 to withdraw.
        /// @param _extraData Optional data forwarded from L2.
        function finalizeERC20Withdrawal(
            address _l1Token,
            address _l2Token,
            address _from,
            address _to,
            uint256 _amount,
            bytes calldata _extraData
        )
            external
        {
            finalizeBridgeERC20(_l1Token, _l2Token, _from, _to, _amount, _extraData);
        }
        /// @custom:legacy
        /// @notice Retrieves the access of the corresponding L2 bridge contract.
        /// @return Address of the corresponding L2 bridge contract.
        function l2TokenBridge() external view returns (address) {
            return address(otherBridge);
        }
        /// @notice Internal function for initiating an ETH deposit.
        /// @param _from        Address of the sender on L1.
        /// @param _to          Address of the recipient on L2.
        /// @param _minGasLimit Minimum gas limit for the deposit message on L2.
        /// @param _extraData   Optional data to forward to L2.
        function _initiateETHDeposit(address _from, address _to, uint32 _minGasLimit, bytes memory _extraData) internal {
            _initiateBridgeETH(_from, _to, msg.value, _minGasLimit, _extraData);
        }
        /// @notice Internal function for initiating an ERC20 deposit.
        /// @param _l1Token     Address of the L1 token being deposited.
        /// @param _l2Token     Address of the corresponding token on L2.
        /// @param _from        Address of the sender on L1.
        /// @param _to          Address of the recipient on L2.
        /// @param _amount      Amount of the ERC20 to deposit.
        /// @param _minGasLimit Minimum gas limit for the deposit message on L2.
        /// @param _extraData   Optional data to forward to L2.
        function _initiateERC20Deposit(
            address _l1Token,
            address _l2Token,
            address _from,
            address _to,
            uint256 _amount,
            uint32 _minGasLimit,
            bytes memory _extraData
        )
            internal
        {
            _initiateBridgeERC20(_l1Token, _l2Token, _from, _to, _amount, _minGasLimit, _extraData);
        }
        /// @inheritdoc StandardBridge
        /// @notice Emits the legacy ETHDepositInitiated event followed by the ETHBridgeInitiated event.
        ///         This is necessary for backwards compatibility with the legacy bridge.
        function _emitETHBridgeInitiated(
            address _from,
            address _to,
            uint256 _amount,
            bytes memory _extraData
        )
            internal
            override
        {
            emit ETHDepositInitiated(_from, _to, _amount, _extraData);
            super._emitETHBridgeInitiated(_from, _to, _amount, _extraData);
        }
        /// @inheritdoc StandardBridge
        /// @notice Emits the legacy ERC20DepositInitiated event followed by the ERC20BridgeInitiated
        ///         event. This is necessary for backwards compatibility with the legacy bridge.
        function _emitETHBridgeFinalized(
            address _from,
            address _to,
            uint256 _amount,
            bytes memory _extraData
        )
            internal
            override
        {
            emit ETHWithdrawalFinalized(_from, _to, _amount, _extraData);
            super._emitETHBridgeFinalized(_from, _to, _amount, _extraData);
        }
        /// @inheritdoc StandardBridge
        /// @notice Emits the legacy ERC20WithdrawalFinalized event followed by the ERC20BridgeFinalized
        ///         event. This is necessary for backwards compatibility with the legacy bridge.
        function _emitERC20BridgeInitiated(
            address _localToken,
            address _remoteToken,
            address _from,
            address _to,
            uint256 _amount,
            bytes memory _extraData
        )
            internal
            override
        {
            emit ERC20DepositInitiated(_localToken, _remoteToken, _from, _to, _amount, _extraData);
            super._emitERC20BridgeInitiated(_localToken, _remoteToken, _from, _to, _amount, _extraData);
        }
        /// @inheritdoc StandardBridge
        /// @notice Emits the legacy ERC20WithdrawalFinalized event followed by the ERC20BridgeFinalized
        ///         event. This is necessary for backwards compatibility with the legacy bridge.
        function _emitERC20BridgeFinalized(
            address _localToken,
            address _remoteToken,
            address _from,
            address _to,
            uint256 _amount,
            bytes memory _extraData
        )
            internal
            override
        {
            emit ERC20WithdrawalFinalized(_localToken, _remoteToken, _from, _to, _amount, _extraData);
            super._emitERC20BridgeFinalized(_localToken, _remoteToken, _from, _to, _amount, _extraData);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.15;
    // Contracts
    import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
    // Libraries
    import { ERC165Checker } from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
    import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
    import { SafeCall } from "src/libraries/SafeCall.sol";
    import { EOA } from "src/libraries/EOA.sol";
    // Interfaces
    import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    import { IOptimismMintableERC20 } from "interfaces/universal/IOptimismMintableERC20.sol";
    import { ILegacyMintableERC20 } from "interfaces/legacy/ILegacyMintableERC20.sol";
    import { ICrossDomainMessenger } from "interfaces/universal/ICrossDomainMessenger.sol";
    /// @custom:upgradeable
    /// @title StandardBridge
    /// @notice StandardBridge is a base contract for the L1 and L2 standard ERC20 bridges. It handles
    ///         the core bridging logic, including escrowing tokens that are native to the local chain
    ///         and minting/burning tokens that are native to the remote chain.
    abstract contract StandardBridge is Initializable {
        using SafeERC20 for IERC20;
        /// @notice The L2 gas limit set when eth is depoisited using the receive() function.
        uint32 internal constant RECEIVE_DEFAULT_GAS_LIMIT = 200_000;
        /// @custom:legacy
        /// @custom:spacer messenger
        /// @notice Spacer for backwards compatibility.
        bytes30 private spacer_0_2_30;
        /// @custom:legacy
        /// @custom:spacer l2TokenBridge
        /// @notice Spacer for backwards compatibility.
        address private spacer_1_0_20;
        /// @notice Mapping that stores deposits for a given pair of local and remote tokens.
        mapping(address => mapping(address => uint256)) public deposits;
        /// @notice Messenger contract on this domain.
        /// @custom:network-specific
        ICrossDomainMessenger public messenger;
        /// @notice Corresponding bridge on the other domain.
        /// @custom:network-specific
        StandardBridge public otherBridge;
        /// @notice Reserve extra slots (to a total of 50) in the storage layout for future upgrades.
        ///         A gap size of 45 was chosen here, so that the first slot used in a child contract
        ///         would be a multiple of 50.
        uint256[45] private __gap;
        /// @notice Emitted when an ETH bridge is initiated to the other chain.
        /// @param from      Address of the sender.
        /// @param to        Address of the receiver.
        /// @param amount    Amount of ETH sent.
        /// @param extraData Extra data sent with the transaction.
        event ETHBridgeInitiated(address indexed from, address indexed to, uint256 amount, bytes extraData);
        /// @notice Emitted when an ETH bridge is finalized on this chain.
        /// @param from      Address of the sender.
        /// @param to        Address of the receiver.
        /// @param amount    Amount of ETH sent.
        /// @param extraData Extra data sent with the transaction.
        event ETHBridgeFinalized(address indexed from, address indexed to, uint256 amount, bytes extraData);
        /// @notice Emitted when an ERC20 bridge is initiated to the other chain.
        /// @param localToken  Address of the ERC20 on this chain.
        /// @param remoteToken Address of the ERC20 on the remote chain.
        /// @param from        Address of the sender.
        /// @param to          Address of the receiver.
        /// @param amount      Amount of the ERC20 sent.
        /// @param extraData   Extra data sent with the transaction.
        event ERC20BridgeInitiated(
            address indexed localToken,
            address indexed remoteToken,
            address indexed from,
            address to,
            uint256 amount,
            bytes extraData
        );
        /// @notice Emitted when an ERC20 bridge is finalized on this chain.
        /// @param localToken  Address of the ERC20 on this chain.
        /// @param remoteToken Address of the ERC20 on the remote chain.
        /// @param from        Address of the sender.
        /// @param to          Address of the receiver.
        /// @param amount      Amount of the ERC20 sent.
        /// @param extraData   Extra data sent with the transaction.
        event ERC20BridgeFinalized(
            address indexed localToken,
            address indexed remoteToken,
            address indexed from,
            address to,
            uint256 amount,
            bytes extraData
        );
        /// @notice Only allow EOAs to call the functions. Note that this is not safe against contracts
        ///         calling code within their constructors, but also doesn't really matter since we're
        ///         just trying to prevent users accidentally depositing with smart contract wallets.
        modifier onlyEOA() {
            require(EOA.isSenderEOA(), "StandardBridge: function can only be called from an EOA");
            _;
        }
        /// @notice Ensures that the caller is a cross-chain message from the other bridge.
        modifier onlyOtherBridge() {
            require(
                msg.sender == address(messenger) && messenger.xDomainMessageSender() == address(otherBridge),
                "StandardBridge: function can only be called from the other bridge"
            );
            _;
        }
        /// @notice Initializer.
        /// @param _messenger   Contract for CrossDomainMessenger on this network.
        /// @param _otherBridge Contract for the other StandardBridge contract.
        function __StandardBridge_init(
            ICrossDomainMessenger _messenger,
            StandardBridge _otherBridge
        )
            internal
            onlyInitializing
        {
            messenger = _messenger;
            otherBridge = _otherBridge;
        }
        /// @notice Allows EOAs to bridge ETH by sending directly to the bridge.
        ///         Must be implemented by contracts that inherit.
        receive() external payable virtual;
        /// @notice Getter for messenger contract.
        ///         Public getter is legacy and will be removed in the future. Use `messenger` instead.
        /// @return Contract of the messenger on this domain.
        /// @custom:legacy
        function MESSENGER() external view returns (ICrossDomainMessenger) {
            return messenger;
        }
        /// @notice Getter for the other bridge contract.
        ///         Public getter is legacy and will be removed in the future. Use `otherBridge` instead.
        /// @return Contract of the bridge on the other network.
        /// @custom:legacy
        function OTHER_BRIDGE() external view returns (StandardBridge) {
            return otherBridge;
        }
        /// @notice This function should return true if the contract is paused.
        ///         On L1 this function will check the SuperchainConfig for its paused status.
        ///         On L2 this function should be a no-op.
        /// @return Whether or not the contract is paused.
        function paused() public view virtual returns (bool) {
            return false;
        }
        /// @notice Sends ETH to the sender's address on the other chain.
        /// @param _minGasLimit Minimum amount of gas that the bridge can be relayed with.
        /// @param _extraData   Extra data to be sent with the transaction. Note that the recipient will
        ///                     not be triggered with this data, but it will be emitted and can be used
        ///                     to identify the transaction.
        function bridgeETH(uint32 _minGasLimit, bytes calldata _extraData) public payable onlyEOA {
            _initiateBridgeETH(msg.sender, msg.sender, msg.value, _minGasLimit, _extraData);
        }
        /// @notice Sends ETH to a receiver's address on the other chain. Note that if ETH is sent to a
        ///         smart contract and the call fails, the ETH will be temporarily locked in the
        ///         StandardBridge on the other chain until the call is replayed. If the call cannot be
        ///         replayed with any amount of gas (call always reverts), then the ETH will be
        ///         permanently locked in the StandardBridge on the other chain. ETH will also
        ///         be locked if the receiver is the other bridge, because finalizeBridgeETH will revert
        ///         in that case.
        /// @param _to          Address of the receiver.
        /// @param _minGasLimit Minimum amount of gas that the bridge can be relayed with.
        /// @param _extraData   Extra data to be sent with the transaction. Note that the recipient will
        ///                     not be triggered with this data, but it will be emitted and can be used
        ///                     to identify the transaction.
        function bridgeETHTo(address _to, uint32 _minGasLimit, bytes calldata _extraData) public payable {
            _initiateBridgeETH(msg.sender, _to, msg.value, _minGasLimit, _extraData);
        }
        /// @notice Sends ERC20 tokens to the sender's address on the other chain.
        /// @param _localToken  Address of the ERC20 on this chain.
        /// @param _remoteToken Address of the corresponding token on the remote chain.
        /// @param _amount      Amount of local tokens to deposit.
        /// @param _minGasLimit Minimum amount of gas that the bridge can be relayed with.
        /// @param _extraData   Extra data to be sent with the transaction. Note that the recipient will
        ///                     not be triggered with this data, but it will be emitted and can be used
        ///                     to identify the transaction.
        function bridgeERC20(
            address _localToken,
            address _remoteToken,
            uint256 _amount,
            uint32 _minGasLimit,
            bytes calldata _extraData
        )
            public
            virtual
            onlyEOA
        {
            _initiateBridgeERC20(_localToken, _remoteToken, msg.sender, msg.sender, _amount, _minGasLimit, _extraData);
        }
        /// @notice Sends ERC20 tokens to a receiver's address on the other chain.
        /// @param _localToken  Address of the ERC20 on this chain.
        /// @param _remoteToken Address of the corresponding token on the remote chain.
        /// @param _to          Address of the receiver.
        /// @param _amount      Amount of local tokens to deposit.
        /// @param _minGasLimit Minimum amount of gas that the bridge can be relayed with.
        /// @param _extraData   Extra data to be sent with the transaction. Note that the recipient will
        ///                     not be triggered with this data, but it will be emitted and can be used
        ///                     to identify the transaction.
        function bridgeERC20To(
            address _localToken,
            address _remoteToken,
            address _to,
            uint256 _amount,
            uint32 _minGasLimit,
            bytes calldata _extraData
        )
            public
            virtual
        {
            _initiateBridgeERC20(_localToken, _remoteToken, msg.sender, _to, _amount, _minGasLimit, _extraData);
        }
        /// @notice Finalizes an ETH bridge on this chain. Can only be triggered by the other
        ///         StandardBridge contract on the remote chain.
        /// @param _from      Address of the sender.
        /// @param _to        Address of the receiver.
        /// @param _amount    Amount of ETH being bridged.
        /// @param _extraData Extra data to be sent with the transaction. Note that the recipient will
        ///                   not be triggered with this data, but it will be emitted and can be used
        ///                   to identify the transaction.
        function finalizeBridgeETH(
            address _from,
            address _to,
            uint256 _amount,
            bytes calldata _extraData
        )
            public
            payable
            onlyOtherBridge
        {
            require(paused() == false, "StandardBridge: paused");
            require(msg.value == _amount, "StandardBridge: amount sent does not match amount required");
            require(_to != address(this), "StandardBridge: cannot send to self");
            require(_to != address(messenger), "StandardBridge: cannot send to messenger");
            // Emit the correct events. By default this will be _amount, but child
            // contracts may override this function in order to emit legacy events as well.
            _emitETHBridgeFinalized(_from, _to, _amount, _extraData);
            bool success = SafeCall.call(_to, gasleft(), _amount, hex"");
            require(success, "StandardBridge: ETH transfer failed");
        }
        /// @notice Finalizes an ERC20 bridge on this chain. Can only be triggered by the other
        ///         StandardBridge contract on the remote chain.
        /// @param _localToken  Address of the ERC20 on this chain.
        /// @param _remoteToken Address of the corresponding token on the remote chain.
        /// @param _from        Address of the sender.
        /// @param _to          Address of the receiver.
        /// @param _amount      Amount of the ERC20 being bridged.
        /// @param _extraData   Extra data to be sent with the transaction. Note that the recipient will
        ///                     not be triggered with this data, but it will be emitted and can be used
        ///                     to identify the transaction.
        function finalizeBridgeERC20(
            address _localToken,
            address _remoteToken,
            address _from,
            address _to,
            uint256 _amount,
            bytes calldata _extraData
        )
            public
            onlyOtherBridge
        {
            require(paused() == false, "StandardBridge: paused");
            if (_isOptimismMintableERC20(_localToken)) {
                require(
                    _isCorrectTokenPair(_localToken, _remoteToken),
                    "StandardBridge: wrong remote token for Optimism Mintable ERC20 local token"
                );
                IOptimismMintableERC20(_localToken).mint(_to, _amount);
            } else {
                deposits[_localToken][_remoteToken] = deposits[_localToken][_remoteToken] - _amount;
                IERC20(_localToken).safeTransfer(_to, _amount);
            }
            // Emit the correct events. By default this will be ERC20BridgeFinalized, but child
            // contracts may override this function in order to emit legacy events as well.
            _emitERC20BridgeFinalized(_localToken, _remoteToken, _from, _to, _amount, _extraData);
        }
        /// @notice Initiates a bridge of ETH through the CrossDomainMessenger.
        /// @param _from        Address of the sender.
        /// @param _to          Address of the receiver.
        /// @param _amount      Amount of ETH being bridged.
        /// @param _minGasLimit Minimum amount of gas that the bridge can be relayed with.
        /// @param _extraData   Extra data to be sent with the transaction. Note that the recipient will
        ///                     not be triggered with this data, but it will be emitted and can be used
        ///                     to identify the transaction.
        function _initiateBridgeETH(
            address _from,
            address _to,
            uint256 _amount,
            uint32 _minGasLimit,
            bytes memory _extraData
        )
            internal
        {
            require(msg.value == _amount, "StandardBridge: bridging ETH must include sufficient ETH value");
            // Emit the correct events. By default this will be _amount, but child
            // contracts may override this function in order to emit legacy events as well.
            _emitETHBridgeInitiated(_from, _to, _amount, _extraData);
            messenger.sendMessage{ value: _amount }({
                _target: address(otherBridge),
                _message: abi.encodeWithSelector(this.finalizeBridgeETH.selector, _from, _to, _amount, _extraData),
                _minGasLimit: _minGasLimit
            });
        }
        /// @notice Sends ERC20 tokens to a receiver's address on the other chain.
        /// @param _localToken  Address of the ERC20 on this chain.
        /// @param _remoteToken Address of the corresponding token on the remote chain.
        /// @param _to          Address of the receiver.
        /// @param _amount      Amount of local tokens to deposit.
        /// @param _minGasLimit Minimum amount of gas that the bridge can be relayed with.
        /// @param _extraData   Extra data to be sent with the transaction. Note that the recipient will
        ///                     not be triggered with this data, but it will be emitted and can be used
        ///                     to identify the transaction.
        function _initiateBridgeERC20(
            address _localToken,
            address _remoteToken,
            address _from,
            address _to,
            uint256 _amount,
            uint32 _minGasLimit,
            bytes memory _extraData
        )
            internal
        {
            require(msg.value == 0, "StandardBridge: cannot send value");
            if (_isOptimismMintableERC20(_localToken)) {
                require(
                    _isCorrectTokenPair(_localToken, _remoteToken),
                    "StandardBridge: wrong remote token for Optimism Mintable ERC20 local token"
                );
                IOptimismMintableERC20(_localToken).burn(_from, _amount);
            } else {
                IERC20(_localToken).safeTransferFrom(_from, address(this), _amount);
                deposits[_localToken][_remoteToken] = deposits[_localToken][_remoteToken] + _amount;
            }
            // Emit the correct events. By default this will be ERC20BridgeInitiated, but child
            // contracts may override this function in order to emit legacy events as well.
            _emitERC20BridgeInitiated(_localToken, _remoteToken, _from, _to, _amount, _extraData);
            messenger.sendMessage({
                _target: address(otherBridge),
                _message: abi.encodeWithSelector(
                    this.finalizeBridgeERC20.selector,
                    // Because this call will be executed on the remote chain, we reverse the order of
                    // the remote and local token addresses relative to their order in the
                    // finalizeBridgeERC20 function.
                    _remoteToken,
                    _localToken,
                    _from,
                    _to,
                    _amount,
                    _extraData
                ),
                _minGasLimit: _minGasLimit
            });
        }
        /// @notice Checks if a given address is an OptimismMintableERC20. Not perfect, but good enough.
        ///         Just the way we like it.
        /// @param _token Address of the token to check.
        /// @return True if the token is an OptimismMintableERC20.
        function _isOptimismMintableERC20(address _token) internal view returns (bool) {
            return ERC165Checker.supportsInterface(_token, type(ILegacyMintableERC20).interfaceId)
                || ERC165Checker.supportsInterface(_token, type(IOptimismMintableERC20).interfaceId);
        }
        /// @notice Checks if the "other token" is the correct pair token for the OptimismMintableERC20.
        ///         Calls can be saved in the future by combining this logic with
        ///         `_isOptimismMintableERC20`.
        /// @param _mintableToken OptimismMintableERC20 to check against.
        /// @param _otherToken    Pair token to check.
        /// @return True if the other token is the correct pair token for the OptimismMintableERC20.
        function _isCorrectTokenPair(address _mintableToken, address _otherToken) internal view returns (bool) {
            if (ERC165Checker.supportsInterface(_mintableToken, type(ILegacyMintableERC20).interfaceId)) {
                return _otherToken == ILegacyMintableERC20(_mintableToken).l1Token();
            } else {
                return _otherToken == IOptimismMintableERC20(_mintableToken).remoteToken();
            }
        }
        /// @notice Emits the ETHBridgeInitiated event and if necessary the appropriate legacy event
        ///         when an ETH bridge is finalized on this chain.
        /// @param _from      Address of the sender.
        /// @param _to        Address of the receiver.
        /// @param _amount    Amount of ETH sent.
        /// @param _extraData Extra data sent with the transaction.
        function _emitETHBridgeInitiated(
            address _from,
            address _to,
            uint256 _amount,
            bytes memory _extraData
        )
            internal
            virtual
        {
            emit ETHBridgeInitiated(_from, _to, _amount, _extraData);
        }
        /// @notice Emits the ETHBridgeFinalized and if necessary the appropriate legacy event when an
        ///         ETH bridge is finalized on this chain.
        /// @param _from      Address of the sender.
        /// @param _to        Address of the receiver.
        /// @param _amount    Amount of ETH sent.
        /// @param _extraData Extra data sent with the transaction.
        function _emitETHBridgeFinalized(
            address _from,
            address _to,
            uint256 _amount,
            bytes memory _extraData
        )
            internal
            virtual
        {
            emit ETHBridgeFinalized(_from, _to, _amount, _extraData);
        }
        /// @notice Emits the ERC20BridgeInitiated event and if necessary the appropriate legacy
        ///         event when an ERC20 bridge is initiated to the other chain.
        /// @param _localToken  Address of the ERC20 on this chain.
        /// @param _remoteToken Address of the ERC20 on the remote chain.
        /// @param _from        Address of the sender.
        /// @param _to          Address of the receiver.
        /// @param _amount      Amount of the ERC20 sent.
        /// @param _extraData   Extra data sent with the transaction.
        function _emitERC20BridgeInitiated(
            address _localToken,
            address _remoteToken,
            address _from,
            address _to,
            uint256 _amount,
            bytes memory _extraData
        )
            internal
            virtual
        {
            emit ERC20BridgeInitiated(_localToken, _remoteToken, _from, _to, _amount, _extraData);
        }
        /// @notice Emits the ERC20BridgeFinalized event and if necessary the appropriate legacy
        ///         event when an ERC20 bridge is initiated to the other chain.
        /// @param _localToken  Address of the ERC20 on this chain.
        /// @param _remoteToken Address of the ERC20 on the remote chain.
        /// @param _from        Address of the sender.
        /// @param _to          Address of the receiver.
        /// @param _amount      Amount of the ERC20 sent.
        /// @param _extraData   Extra data sent with the transaction.
        function _emitERC20BridgeFinalized(
            address _localToken,
            address _remoteToken,
            address _from,
            address _to,
            uint256 _amount,
            bytes memory _extraData
        )
            internal
            virtual
        {
            emit ERC20BridgeFinalized(_localToken, _remoteToken, _from, _to, _amount, _extraData);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    /// @title Predeploys
    /// @notice Contains constant addresses for protocol contracts that are pre-deployed to the L2 system.
    //          This excludes the preinstalls (non-protocol contracts).
    library Predeploys {
        /// @notice Number of predeploy-namespace addresses reserved for protocol usage.
        uint256 internal constant PREDEPLOY_COUNT = 2048;
        /// @custom:legacy
        /// @notice Address of the LegacyMessagePasser predeploy. Deprecate. Use the updated
        ///         L2ToL1MessagePasser contract instead.
        address internal constant LEGACY_MESSAGE_PASSER = 0x4200000000000000000000000000000000000000;
        /// @custom:legacy
        /// @notice Address of the L1MessageSender predeploy. Deprecated. Use L2CrossDomainMessenger
        ///         or access tx.origin (or msg.sender) in a L1 to L2 transaction instead.
        ///         Not embedded into new OP-Stack chains.
        address internal constant L1_MESSAGE_SENDER = 0x4200000000000000000000000000000000000001;
        /// @custom:legacy
        /// @notice Address of the DeployerWhitelist predeploy. No longer active.
        address internal constant DEPLOYER_WHITELIST = 0x4200000000000000000000000000000000000002;
        /// @notice Address of the canonical WETH contract.
        address internal constant WETH = 0x4200000000000000000000000000000000000006;
        /// @notice Address of the L2CrossDomainMessenger predeploy.
        address internal constant L2_CROSS_DOMAIN_MESSENGER = 0x4200000000000000000000000000000000000007;
        /// @notice Address of the GasPriceOracle predeploy. Includes fee information
        ///         and helpers for computing the L1 portion of the transaction fee.
        address internal constant GAS_PRICE_ORACLE = 0x420000000000000000000000000000000000000F;
        /// @notice Address of the L2StandardBridge predeploy.
        address internal constant L2_STANDARD_BRIDGE = 0x4200000000000000000000000000000000000010;
        //// @notice Address of the SequencerFeeWallet predeploy.
        address internal constant SEQUENCER_FEE_WALLET = 0x4200000000000000000000000000000000000011;
        /// @notice Address of the OptimismMintableERC20Factory predeploy.
        address internal constant OPTIMISM_MINTABLE_ERC20_FACTORY = 0x4200000000000000000000000000000000000012;
        /// @custom:legacy
        /// @notice Address of the L1BlockNumber predeploy. Deprecated. Use the L1Block predeploy
        ///         instead, which exposes more information about the L1 state.
        address internal constant L1_BLOCK_NUMBER = 0x4200000000000000000000000000000000000013;
        /// @notice Address of the L2ERC721Bridge predeploy.
        address internal constant L2_ERC721_BRIDGE = 0x4200000000000000000000000000000000000014;
        /// @notice Address of the L1Block predeploy.
        address internal constant L1_BLOCK_ATTRIBUTES = 0x4200000000000000000000000000000000000015;
        /// @notice Address of the L2ToL1MessagePasser predeploy.
        address internal constant L2_TO_L1_MESSAGE_PASSER = 0x4200000000000000000000000000000000000016;
        /// @notice Address of the OptimismMintableERC721Factory predeploy.
        address internal constant OPTIMISM_MINTABLE_ERC721_FACTORY = 0x4200000000000000000000000000000000000017;
        /// @notice Address of the ProxyAdmin predeploy.
        address internal constant PROXY_ADMIN = 0x4200000000000000000000000000000000000018;
        /// @notice Address of the BaseFeeVault predeploy.
        address internal constant BASE_FEE_VAULT = 0x4200000000000000000000000000000000000019;
        /// @notice Address of the L1FeeVault predeploy.
        address internal constant L1_FEE_VAULT = 0x420000000000000000000000000000000000001A;
        /// @notice Address of the OperatorFeeVault predeploy.
        address internal constant OPERATOR_FEE_VAULT = 0x420000000000000000000000000000000000001b;
        /// @notice Address of the SchemaRegistry predeploy.
        address internal constant SCHEMA_REGISTRY = 0x4200000000000000000000000000000000000020;
        /// @notice Address of the EAS predeploy.
        address internal constant EAS = 0x4200000000000000000000000000000000000021;
        /// @notice Address of the GovernanceToken predeploy.
        address internal constant GOVERNANCE_TOKEN = 0x4200000000000000000000000000000000000042;
        /// @custom:legacy
        /// @notice Address of the LegacyERC20ETH predeploy. Deprecated. Balances are migrated to the
        ///         state trie as of the Bedrock upgrade. Contract has been locked and write functions
        ///         can no longer be accessed.
        address internal constant LEGACY_ERC20_ETH = 0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000;
        /// @notice Address of the CrossL2Inbox predeploy.
        address internal constant CROSS_L2_INBOX = 0x4200000000000000000000000000000000000022;
        /// @notice Address of the L2ToL2CrossDomainMessenger predeploy.
        address internal constant L2_TO_L2_CROSS_DOMAIN_MESSENGER = 0x4200000000000000000000000000000000000023;
        /// @notice Address of the SuperchainWETH predeploy.
        address internal constant SUPERCHAIN_WETH = 0x4200000000000000000000000000000000000024;
        /// @notice Address of the ETHLiquidity predeploy.
        address internal constant ETH_LIQUIDITY = 0x4200000000000000000000000000000000000025;
        /// @notice Address of the OptimismSuperchainERC20Factory predeploy.
        address internal constant OPTIMISM_SUPERCHAIN_ERC20_FACTORY = 0x4200000000000000000000000000000000000026;
        /// @notice Address of the OptimismSuperchainERC20Beacon predeploy.
        address internal constant OPTIMISM_SUPERCHAIN_ERC20_BEACON = 0x4200000000000000000000000000000000000027;
        // TODO: Precalculate the address of the implementation contract
        /// @notice Arbitrary address of the OptimismSuperchainERC20 implementation contract.
        address internal constant OPTIMISM_SUPERCHAIN_ERC20 = 0xB9415c6cA93bdC545D4c5177512FCC22EFa38F28;
        /// @notice Address of the SuperchainTokenBridge predeploy.
        address internal constant SUPERCHAIN_TOKEN_BRIDGE = 0x4200000000000000000000000000000000000028;
        /// @notice Returns the name of the predeploy at the given address.
        function getName(address _addr) internal pure returns (string memory out_) {
            require(isPredeployNamespace(_addr), "Predeploys: address must be a predeploy");
            if (_addr == LEGACY_MESSAGE_PASSER) return "LegacyMessagePasser";
            if (_addr == L1_MESSAGE_SENDER) return "L1MessageSender";
            if (_addr == DEPLOYER_WHITELIST) return "DeployerWhitelist";
            if (_addr == WETH) return "WETH";
            if (_addr == L2_CROSS_DOMAIN_MESSENGER) return "L2CrossDomainMessenger";
            if (_addr == GAS_PRICE_ORACLE) return "GasPriceOracle";
            if (_addr == L2_STANDARD_BRIDGE) return "L2StandardBridge";
            if (_addr == SEQUENCER_FEE_WALLET) return "SequencerFeeVault";
            if (_addr == OPTIMISM_MINTABLE_ERC20_FACTORY) return "OptimismMintableERC20Factory";
            if (_addr == L1_BLOCK_NUMBER) return "L1BlockNumber";
            if (_addr == L2_ERC721_BRIDGE) return "L2ERC721Bridge";
            if (_addr == L1_BLOCK_ATTRIBUTES) return "L1Block";
            if (_addr == L2_TO_L1_MESSAGE_PASSER) return "L2ToL1MessagePasser";
            if (_addr == OPTIMISM_MINTABLE_ERC721_FACTORY) return "OptimismMintableERC721Factory";
            if (_addr == PROXY_ADMIN) return "ProxyAdmin";
            if (_addr == BASE_FEE_VAULT) return "BaseFeeVault";
            if (_addr == L1_FEE_VAULT) return "L1FeeVault";
            if (_addr == OPERATOR_FEE_VAULT) return "OperatorFeeVault";
            if (_addr == SCHEMA_REGISTRY) return "SchemaRegistry";
            if (_addr == EAS) return "EAS";
            if (_addr == GOVERNANCE_TOKEN) return "GovernanceToken";
            if (_addr == LEGACY_ERC20_ETH) return "LegacyERC20ETH";
            if (_addr == CROSS_L2_INBOX) return "CrossL2Inbox";
            if (_addr == L2_TO_L2_CROSS_DOMAIN_MESSENGER) return "L2ToL2CrossDomainMessenger";
            if (_addr == SUPERCHAIN_WETH) return "SuperchainWETH";
            if (_addr == ETH_LIQUIDITY) return "ETHLiquidity";
            if (_addr == OPTIMISM_SUPERCHAIN_ERC20_FACTORY) return "OptimismSuperchainERC20Factory";
            if (_addr == OPTIMISM_SUPERCHAIN_ERC20_BEACON) return "OptimismSuperchainERC20Beacon";
            if (_addr == SUPERCHAIN_TOKEN_BRIDGE) return "SuperchainTokenBridge";
            revert("Predeploys: unnamed predeploy");
        }
        /// @notice Returns true if the predeploy is not proxied.
        function notProxied(address _addr) internal pure returns (bool) {
            return _addr == GOVERNANCE_TOKEN || _addr == WETH;
        }
        /// @notice Returns true if the address is a defined predeploy that is embedded into new OP-Stack chains.
        function isSupportedPredeploy(address _addr, bool _useInterop) internal pure returns (bool) {
            return _addr == LEGACY_MESSAGE_PASSER || _addr == DEPLOYER_WHITELIST || _addr == WETH
                || _addr == L2_CROSS_DOMAIN_MESSENGER || _addr == GAS_PRICE_ORACLE || _addr == L2_STANDARD_BRIDGE
                || _addr == SEQUENCER_FEE_WALLET || _addr == OPTIMISM_MINTABLE_ERC20_FACTORY || _addr == L1_BLOCK_NUMBER
                || _addr == L2_ERC721_BRIDGE || _addr == L1_BLOCK_ATTRIBUTES || _addr == L2_TO_L1_MESSAGE_PASSER
                || _addr == OPTIMISM_MINTABLE_ERC721_FACTORY || _addr == PROXY_ADMIN || _addr == BASE_FEE_VAULT
                || _addr == L1_FEE_VAULT || _addr == OPERATOR_FEE_VAULT || _addr == SCHEMA_REGISTRY || _addr == EAS
                || _addr == GOVERNANCE_TOKEN || (_useInterop && _addr == CROSS_L2_INBOX)
                || (_useInterop && _addr == L2_TO_L2_CROSS_DOMAIN_MESSENGER) || (_useInterop && _addr == SUPERCHAIN_WETH)
                || (_useInterop && _addr == ETH_LIQUIDITY) || (_useInterop && _addr == SUPERCHAIN_TOKEN_BRIDGE);
        }
        function isPredeployNamespace(address _addr) internal pure returns (bool) {
            return uint160(_addr) >> 11 == uint160(0x4200000000000000000000000000000000000000) >> 11;
        }
        /// @notice Function to compute the expected address of the predeploy implementation
        ///         in the genesis state.
        function predeployToCodeNamespace(address _addr) internal pure returns (address) {
            require(
                isPredeployNamespace(_addr), "Predeploys: can only derive code-namespace address for predeploy addresses"
            );
            return address(
                uint160(uint256(uint160(_addr)) & 0xffff | uint256(uint160(0xc0D3C0d3C0d3C0D3c0d3C0d3c0D3C0d3c0d30000)))
            );
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    /// @title ISemver
    /// @notice ISemver is a simple contract for ensuring that contracts are
    ///         versioned using semantic versioning.
    interface ISemver {
        /// @notice Getter for the semantic version of the contract. This is not
        ///         meant to be used onchain but instead meant to be used by offchain
        ///         tooling.
        /// @return Semver contract version as a string.
        function version() external view returns (string memory);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    interface ICrossDomainMessenger {
        event FailedRelayedMessage(bytes32 indexed msgHash);
        event Initialized(uint8 version);
        event RelayedMessage(bytes32 indexed msgHash);
        event SentMessage(address indexed target, address sender, bytes message, uint256 messageNonce, uint256 gasLimit);
        event SentMessageExtension1(address indexed sender, uint256 value);
        function MESSAGE_VERSION() external view returns (uint16);
        function MIN_GAS_CALLDATA_OVERHEAD() external view returns (uint64);
        function MIN_GAS_DYNAMIC_OVERHEAD_DENOMINATOR() external view returns (uint64);
        function MIN_GAS_DYNAMIC_OVERHEAD_NUMERATOR() external view returns (uint64);
        function OTHER_MESSENGER() external view returns (ICrossDomainMessenger);
        function RELAY_CALL_OVERHEAD() external view returns (uint64);
        function RELAY_CONSTANT_OVERHEAD() external view returns (uint64);
        function RELAY_GAS_CHECK_BUFFER() external view returns (uint64);
        function RELAY_RESERVED_GAS() external view returns (uint64);
        function TX_BASE_GAS() external view returns (uint64);
        function FLOOR_CALLDATA_OVERHEAD() external view returns (uint64);
        function ENCODING_OVERHEAD() external view returns (uint64);
        function baseGas(bytes memory _message, uint32 _minGasLimit) external pure returns (uint64);
        function failedMessages(bytes32) external view returns (bool);
        function messageNonce() external view returns (uint256);
        function otherMessenger() external view returns (ICrossDomainMessenger);
        function paused() external view returns (bool);
        function relayMessage(
            uint256 _nonce,
            address _sender,
            address _target,
            uint256 _value,
            uint256 _minGasLimit,
            bytes memory _message
        )
            external
            payable;
        function sendMessage(address _target, bytes memory _message, uint32 _minGasLimit) external payable;
        function successfulMessages(bytes32) external view returns (bool);
        function xDomainMessageSender() external view returns (address);
        function __constructor__() external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    interface ISuperchainConfig {
        enum UpdateType {
            GUARDIAN
        }
        event ConfigUpdate(UpdateType indexed updateType, bytes data);
        event Initialized(uint8 version);
        event Paused(string identifier);
        event Unpaused();
        function GUARDIAN_SLOT() external view returns (bytes32);
        function PAUSED_SLOT() external view returns (bytes32);
        function guardian() external view returns (address guardian_);
        function initialize(address _guardian, bool _paused) external;
        function pause(string memory _identifier) external;
        function paused() external view returns (bool paused_);
        function unpause() external;
        function version() external view returns (string memory);
        function __constructor__() external;
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol)
    pragma solidity ^0.8.2;
    import "../../utils/Address.sol";
    /**
     * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
     * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
     * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
     * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
     *
     * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
     * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
     * case an upgrade adds a module that needs to be initialized.
     *
     * For example:
     *
     * [.hljs-theme-light.nopadding]
     * ```
     * contract MyToken is ERC20Upgradeable {
     *     function initialize() initializer public {
     *         __ERC20_init("MyToken", "MTK");
     *     }
     * }
     * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
     *     function initializeV2() reinitializer(2) public {
     *         __ERC20Permit_init("MyToken");
     *     }
     * }
     * ```
     *
     * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
     * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
     *
     * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
     * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
     *
     * [CAUTION]
     * ====
     * Avoid leaving a contract uninitialized.
     *
     * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
     * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
     * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
     *
     * [.hljs-theme-light.nopadding]
     * ```
     * /// @custom:oz-upgrades-unsafe-allow constructor
     * constructor() {
     *     _disableInitializers();
     * }
     * ```
     * ====
     */
    abstract contract Initializable {
        /**
         * @dev Indicates that the contract has been initialized.
         * @custom:oz-retyped-from bool
         */
        uint8 private _initialized;
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool private _initializing;
        /**
         * @dev Triggered when the contract has been initialized or reinitialized.
         */
        event Initialized(uint8 version);
        /**
         * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
         * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`.
         */
        modifier initializer() {
            bool isTopLevelCall = !_initializing;
            require(
                (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1),
                "Initializable: contract is already initialized"
            );
            _initialized = 1;
            if (isTopLevelCall) {
                _initializing = true;
            }
            _;
            if (isTopLevelCall) {
                _initializing = false;
                emit Initialized(1);
            }
        }
        /**
         * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
         * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
         * used to initialize parent contracts.
         *
         * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original
         * initialization step. This is essential to configure modules that are added through upgrades and that require
         * initialization.
         *
         * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
         * a contract, executing them in the right order is up to the developer or operator.
         */
        modifier reinitializer(uint8 version) {
            require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
            _initialized = version;
            _initializing = true;
            _;
            _initializing = false;
            emit Initialized(version);
        }
        /**
         * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
         * {initializer} and {reinitializer} modifiers, directly or indirectly.
         */
        modifier onlyInitializing() {
            require(_initializing, "Initializable: contract is not initializing");
            _;
        }
        /**
         * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
         * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
         * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
         * through proxies.
         */
        function _disableInitializers() internal virtual {
            require(!_initializing, "Initializable: contract is initializing");
            if (_initialized < type(uint8).max) {
                _initialized = type(uint8).max;
                emit Initialized(type(uint8).max);
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.7.2) (utils/introspection/ERC165Checker.sol)
    pragma solidity ^0.8.0;
    import "./IERC165.sol";
    /**
     * @dev Library used to query support of an interface declared via {IERC165}.
     *
     * Note that these functions return the actual result of the query: they do not
     * `revert` if an interface is not supported. It is up to the caller to decide
     * what to do in these cases.
     */
    library ERC165Checker {
        // As per the EIP-165 spec, no interface should ever match 0xffffffff
        bytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff;
        /**
         * @dev Returns true if `account` supports the {IERC165} interface,
         */
        function supportsERC165(address account) internal view returns (bool) {
            // Any contract that implements ERC165 must explicitly indicate support of
            // InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid
            return
                _supportsERC165Interface(account, type(IERC165).interfaceId) &&
                !_supportsERC165Interface(account, _INTERFACE_ID_INVALID);
        }
        /**
         * @dev Returns true if `account` supports the interface defined by
         * `interfaceId`. Support for {IERC165} itself is queried automatically.
         *
         * See {IERC165-supportsInterface}.
         */
        function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) {
            // query support of both ERC165 as per the spec and support of _interfaceId
            return supportsERC165(account) && _supportsERC165Interface(account, interfaceId);
        }
        /**
         * @dev Returns a boolean array where each value corresponds to the
         * interfaces passed in and whether they're supported or not. This allows
         * you to batch check interfaces for a contract where your expectation
         * is that some interfaces may not be supported.
         *
         * See {IERC165-supportsInterface}.
         *
         * _Available since v3.4._
         */
        function getSupportedInterfaces(address account, bytes4[] memory interfaceIds)
            internal
            view
            returns (bool[] memory)
        {
            // an array of booleans corresponding to interfaceIds and whether they're supported or not
            bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length);
            // query support of ERC165 itself
            if (supportsERC165(account)) {
                // query support of each interface in interfaceIds
                for (uint256 i = 0; i < interfaceIds.length; i++) {
                    interfaceIdsSupported[i] = _supportsERC165Interface(account, interfaceIds[i]);
                }
            }
            return interfaceIdsSupported;
        }
        /**
         * @dev Returns true if `account` supports all the interfaces defined in
         * `interfaceIds`. Support for {IERC165} itself is queried automatically.
         *
         * Batch-querying can lead to gas savings by skipping repeated checks for
         * {IERC165} support.
         *
         * See {IERC165-supportsInterface}.
         */
        function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) {
            // query support of ERC165 itself
            if (!supportsERC165(account)) {
                return false;
            }
            // query support of each interface in _interfaceIds
            for (uint256 i = 0; i < interfaceIds.length; i++) {
                if (!_supportsERC165Interface(account, interfaceIds[i])) {
                    return false;
                }
            }
            // all interfaces supported
            return true;
        }
        /**
         * @notice Query if a contract implements an interface, does not check ERC165 support
         * @param account The address of the contract to query for support of an interface
         * @param interfaceId The interface identifier, as specified in ERC-165
         * @return true if the contract at account indicates support of the interface with
         * identifier interfaceId, false otherwise
         * @dev Assumes that account contains a contract that supports ERC165, otherwise
         * the behavior of this method is undefined. This precondition can be checked
         * with {supportsERC165}.
         * Interface identification is specified in ERC-165.
         */
        function _supportsERC165Interface(address account, bytes4 interfaceId) private view returns (bool) {
            // prepare call
            bytes memory encodedParams = abi.encodeWithSelector(IERC165.supportsInterface.selector, interfaceId);
            // perform static call
            bool success;
            uint256 returnSize;
            uint256 returnValue;
            assembly {
                success := staticcall(30000, account, add(encodedParams, 0x20), mload(encodedParams), 0x00, 0x20)
                returnSize := returndatasize()
                returnValue := mload(0x00)
            }
            return success && returnSize >= 0x20 && returnValue > 0;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/utils/SafeERC20.sol)
    pragma solidity ^0.8.0;
    import "../IERC20.sol";
    import "../extensions/draft-IERC20Permit.sol";
    import "../../../utils/Address.sol";
    /**
     * @title SafeERC20
     * @dev Wrappers around ERC20 operations that throw on failure (when the token
     * contract returns false). Tokens that return no value (and instead revert or
     * throw on failure) are also supported, non-reverting calls are assumed to be
     * successful.
     * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
     * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
     */
    library SafeERC20 {
        using Address for address;
        function safeTransfer(
            IERC20 token,
            address to,
            uint256 value
        ) internal {
            _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
        }
        function safeTransferFrom(
            IERC20 token,
            address from,
            address to,
            uint256 value
        ) internal {
            _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
        }
        /**
         * @dev Deprecated. This function has issues similar to the ones found in
         * {IERC20-approve}, and its usage is discouraged.
         *
         * Whenever possible, use {safeIncreaseAllowance} and
         * {safeDecreaseAllowance} instead.
         */
        function safeApprove(
            IERC20 token,
            address spender,
            uint256 value
        ) internal {
            // safeApprove should only be called when setting an initial allowance,
            // or when resetting it to zero. To increase and decrease it, use
            // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
            require(
                (value == 0) || (token.allowance(address(this), spender) == 0),
                "SafeERC20: approve from non-zero to non-zero allowance"
            );
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
        }
        function safeIncreaseAllowance(
            IERC20 token,
            address spender,
            uint256 value
        ) internal {
            uint256 newAllowance = token.allowance(address(this), spender) + value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
        function safeDecreaseAllowance(
            IERC20 token,
            address spender,
            uint256 value
        ) internal {
            unchecked {
                uint256 oldAllowance = token.allowance(address(this), spender);
                require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                uint256 newAllowance = oldAllowance - value;
                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
            }
        }
        function safePermit(
            IERC20Permit token,
            address owner,
            address spender,
            uint256 value,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) internal {
            uint256 nonceBefore = token.nonces(owner);
            token.permit(owner, spender, value, deadline, v, r, s);
            uint256 nonceAfter = token.nonces(owner);
            require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
        }
        /**
         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
         * on the return value: the return value is optional (but if data is returned, it must not be false).
         * @param token The token targeted by the call.
         * @param data The call data (encoded using abi.encode or one of its variants).
         */
        function _callOptionalReturn(IERC20 token, bytes memory data) private {
            // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
            // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
            // the target address contains contract code and also asserts for success in the low-level call.
            bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
            if (returndata.length > 0) {
                // Return data is optional
                require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    /// @title SafeCall
    /// @notice Perform low level safe calls
    library SafeCall {
        /// @notice Performs a low level call without copying any returndata.
        /// @dev Passes no calldata to the call context.
        /// @param _target   Address to call
        /// @param _gas      Amount of gas to pass to the call
        /// @param _value    Amount of value to pass to the call
        function send(address _target, uint256 _gas, uint256 _value) internal returns (bool success_) {
            assembly {
                success_ :=
                    call(
                        _gas, // gas
                        _target, // recipient
                        _value, // ether value
                        0, // inloc
                        0, // inlen
                        0, // outloc
                        0 // outlen
                    )
            }
        }
        /// @notice Perform a low level call with all gas without copying any returndata
        /// @param _target   Address to call
        /// @param _value    Amount of value to pass to the call
        function send(address _target, uint256 _value) internal returns (bool success_) {
            success_ = send(_target, gasleft(), _value);
        }
        /// @notice Perform a low level call without copying any returndata
        /// @param _target   Address to call
        /// @param _gas      Amount of gas to pass to the call
        /// @param _value    Amount of value to pass to the call
        /// @param _calldata Calldata to pass to the call
        function call(
            address _target,
            uint256 _gas,
            uint256 _value,
            bytes memory _calldata
        )
            internal
            returns (bool success_)
        {
            assembly {
                success_ :=
                    call(
                        _gas, // gas
                        _target, // recipient
                        _value, // ether value
                        add(_calldata, 32), // inloc
                        mload(_calldata), // inlen
                        0, // outloc
                        0 // outlen
                    )
            }
        }
        /// @notice Perform a low level call without copying any returndata
        /// @param _target   Address to call
        /// @param _value    Amount of value to pass to the call
        /// @param _calldata Calldata to pass to the call
        function call(address _target, uint256 _value, bytes memory _calldata) internal returns (bool success_) {
            success_ = call({ _target: _target, _gas: gasleft(), _value: _value, _calldata: _calldata });
        }
        /// @notice Perform a low level call without copying any returndata
        /// @param _target   Address to call
        /// @param _calldata Calldata to pass to the call
        function call(address _target, bytes memory _calldata) internal returns (bool success_) {
            success_ = call({ _target: _target, _gas: gasleft(), _value: 0, _calldata: _calldata });
        }
        /// @notice Helper function to determine if there is sufficient gas remaining within the context
        ///         to guarantee that the minimum gas requirement for a call will be met as well as
        ///         optionally reserving a specified amount of gas for after the call has concluded.
        /// @param _minGas      The minimum amount of gas that may be passed to the target context.
        /// @param _reservedGas Optional amount of gas to reserve for the caller after the execution
        ///                     of the target context.
        /// @return `true` if there is enough gas remaining to safely supply `_minGas` to the target
        ///         context as well as reserve `_reservedGas` for the caller after the execution of
        ///         the target context.
        /// @dev !!!!! FOOTGUN ALERT !!!!!
        ///      1.) The 40_000 base buffer is to account for the worst case of the dynamic cost of the
        ///          `CALL` opcode's `address_access_cost`, `positive_value_cost`, and
        ///          `value_to_empty_account_cost` factors with an added buffer of 5,700 gas. It is
        ///          still possible to self-rekt by initiating a withdrawal with a minimum gas limit
        ///          that does not account for the `memory_expansion_cost` & `code_execution_cost`
        ///          factors of the dynamic cost of the `CALL` opcode.
        ///      2.) This function should *directly* precede the external call if possible. There is an
        ///          added buffer to account for gas consumed between this check and the call, but it
        ///          is only 5,700 gas.
        ///      3.) Because EIP-150 ensures that a maximum of 63/64ths of the remaining gas in the call
        ///          frame may be passed to a subcontext, we need to ensure that the gas will not be
        ///          truncated.
        ///      4.) Use wisely. This function is not a silver bullet.
        function hasMinGas(uint256 _minGas, uint256 _reservedGas) internal view returns (bool) {
            bool _hasMinGas;
            assembly {
                // Equation: gas × 63 ≥ minGas × 64 + 63(40_000 + reservedGas)
                _hasMinGas := iszero(lt(mul(gas(), 63), add(mul(_minGas, 64), mul(add(40000, _reservedGas), 63))))
            }
            return _hasMinGas;
        }
        /// @notice Perform a low level call without copying any returndata. This function
        ///         will revert if the call cannot be performed with the specified minimum
        ///         gas.
        /// @param _target   Address to call
        /// @param _minGas   The minimum amount of gas that may be passed to the call
        /// @param _value    Amount of value to pass to the call
        /// @param _calldata Calldata to pass to the call
        function callWithMinGas(
            address _target,
            uint256 _minGas,
            uint256 _value,
            bytes memory _calldata
        )
            internal
            returns (bool)
        {
            bool _success;
            bool _hasMinGas = hasMinGas(_minGas, 0);
            assembly {
                // Assertion: gasleft() >= (_minGas * 64) / 63 + 40_000
                if iszero(_hasMinGas) {
                    // Store the "Error(string)" selector in scratch space.
                    mstore(0, 0x08c379a0)
                    // Store the pointer to the string length in scratch space.
                    mstore(32, 32)
                    // Store the string.
                    //
                    // SAFETY:
                    // - We pad the beginning of the string with two zero bytes as well as the
                    // length (24) to ensure that we override the free memory pointer at offset
                    // 0x40. This is necessary because the free memory pointer is likely to
                    // be greater than 1 byte when this function is called, but it is incredibly
                    // unlikely that it will be greater than 3 bytes. As for the data within
                    // 0x60, it is ensured that it is 0 due to 0x60 being the zero offset.
                    // - It's fine to clobber the free memory pointer, we're reverting.
                    mstore(88, 0x0000185361666543616c6c3a204e6f7420656e6f75676820676173)
                    // Revert with 'Error("SafeCall: Not enough gas")'
                    revert(28, 100)
                }
                // The call will be supplied at least ((_minGas * 64) / 63) gas due to the
                // above assertion. This ensures that, in all circumstances (except for when the
                // `_minGas` does not account for the `memory_expansion_cost` and `code_execution_cost`
                // factors of the dynamic cost of the `CALL` opcode), the call will receive at least
                // the minimum amount of gas specified.
                _success :=
                    call(
                        gas(), // gas
                        _target, // recipient
                        _value, // ether value
                        add(_calldata, 32), // inloc
                        mload(_calldata), // inlen
                        0x00, // outloc
                        0x00 // outlen
                    )
            }
            return _success;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    /// @title EOA
    /// @notice A library for detecting if an address is an EOA.
    library EOA {
        /// @notice Returns true if sender address is an EOA.
        /// @return isEOA_ True if the sender address is an EOA.
        function isSenderEOA() internal view returns (bool isEOA_) {
            if (msg.sender == tx.origin) {
                isEOA_ = true;
            } else if (address(msg.sender).code.length == 23) {
                // If the sender is not the origin, check for 7702 delegated EOAs.
                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 (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
    pragma solidity ^0.8.0;
    import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
    /// @title IOptimismMintableERC20
    /// @notice This interface is available on the OptimismMintableERC20 contract.
    ///         We declare it as a separate interface so that it can be used in
    ///         custom implementations of OptimismMintableERC20.
    interface IOptimismMintableERC20 is IERC165 {
        function remoteToken() external view returns (address);
        function bridge() external returns (address);
        function mint(address _to, uint256 _amount) external;
        function burn(address _from, uint256 _amount) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
    /// @custom:legacy
    /// @title ILegacyMintableERC20
    /// @notice This interface was available on the legacy L2StandardERC20 contract.
    ///         It remains available on the OptimismMintableERC20 contract for
    ///         backwards compatibility.
    interface ILegacyMintableERC20 is IERC165 {
        function l1Token() external view returns (address);
        function mint(address _to, uint256 _amount) external;
        function burn(address _from, uint256 _amount) external;
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)
    pragma solidity ^0.8.1;
    /**
     * @dev Collection of functions related to the address type
     */
    library Address {
        /**
         * @dev Returns true if `account` is a contract.
         *
         * [IMPORTANT]
         * ====
         * It is unsafe to assume that an address for which this function returns
         * false is an externally-owned account (EOA) and not a contract.
         *
         * Among others, `isContract` will return false for the following
         * types of addresses:
         *
         *  - an externally-owned account
         *  - a contract in construction
         *  - an address where a contract will be created
         *  - an address where a contract lived, but was destroyed
         * ====
         *
         * [IMPORTANT]
         * ====
         * You shouldn't rely on `isContract` to protect against flash loan attacks!
         *
         * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
         * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
         * constructor.
         * ====
         */
        function isContract(address account) internal view returns (bool) {
            // This method relies on extcodesize/address.code.length, which returns 0
            // for contracts in construction, since the code is only stored at the end
            // of the constructor execution.
            return account.code.length > 0;
        }
        /**
         * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
         * `recipient`, forwarding all available gas and reverting on errors.
         *
         * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
         * of certain opcodes, possibly making contracts go over the 2300 gas limit
         * imposed by `transfer`, making them unable to receive funds via
         * `transfer`. {sendValue} removes this limitation.
         *
         * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
         *
         * IMPORTANT: because control is transferred to `recipient`, care must be
         * taken to not create reentrancy vulnerabilities. Consider using
         * {ReentrancyGuard} or the
         * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
         */
        function sendValue(address payable recipient, uint256 amount) internal {
            require(address(this).balance >= amount, "Address: insufficient balance");
            (bool success, ) = recipient.call{value: amount}("");
            require(success, "Address: unable to send value, recipient may have reverted");
        }
        /**
         * @dev Performs a Solidity function call using a low level `call`. A
         * plain `call` is an unsafe replacement for a function call: use this
         * function instead.
         *
         * If `target` reverts with a revert reason, it is bubbled up by this
         * function (like regular Solidity function calls).
         *
         * Returns the raw returned data. To convert to the expected return value,
         * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
         *
         * Requirements:
         *
         * - `target` must be a contract.
         * - calling `target` with `data` must not revert.
         *
         * _Available since v3.1._
         */
        function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCall(target, data, "Address: low-level call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
         * `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but also transferring `value` wei to `target`.
         *
         * Requirements:
         *
         * - the calling contract must have an ETH balance of at least `value`.
         * - the called Solidity function must be `payable`.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(
            address target,
            bytes memory data,
            uint256 value
        ) internal returns (bytes memory) {
            return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
        }
        /**
         * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
         * with `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(
            address target,
            bytes memory data,
            uint256 value,
            string memory errorMessage
        ) internal returns (bytes memory) {
            require(address(this).balance >= value, "Address: insufficient balance for call");
            require(isContract(target), "Address: call to non-contract");
            (bool success, bytes memory returndata) = target.call{value: value}(data);
            return verifyCallResult(success, returndata, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
            return functionStaticCall(target, data, "Address: low-level static call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal view returns (bytes memory) {
            require(isContract(target), "Address: static call to non-contract");
            (bool success, bytes memory returndata) = target.staticcall(data);
            return verifyCallResult(success, returndata, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionDelegateCall(target, data, "Address: low-level delegate call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            require(isContract(target), "Address: delegate call to non-contract");
            (bool success, bytes memory returndata) = target.delegatecall(data);
            return verifyCallResult(success, returndata, errorMessage);
        }
        /**
         * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
         * revert reason using the provided one.
         *
         * _Available since v4.3._
         */
        function verifyCallResult(
            bool success,
            bytes memory returndata,
            string memory errorMessage
        ) internal pure returns (bytes memory) {
            if (success) {
                return returndata;
            } else {
                // Look for revert reason and bubble it up if present
                if (returndata.length > 0) {
                    // The easiest way to bubble the revert reason is using memory via assembly
                    /// @solidity memory-safe-assembly
                    assembly {
                        let returndata_size := mload(returndata)
                        revert(add(32, returndata), returndata_size)
                    }
                } else {
                    revert(errorMessage);
                }
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC165 standard, as defined in the
     * https://eips.ethereum.org/EIPS/eip-165[EIP].
     *
     * Implementers can declare support of contract interfaces, which can then be
     * queried by others ({ERC165Checker}).
     *
     * For an implementation, see {ERC165}.
     */
    interface IERC165 {
        /**
         * @dev Returns true if this contract implements the interface defined by
         * `interfaceId`. See the corresponding
         * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
         * to learn more about how these ids are created.
         *
         * This function call must use less than 30 000 gas.
         */
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
     * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
     *
     * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
     * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
     * need to send a transaction, and thus is not required to hold Ether at all.
     */
    interface IERC20Permit {
        /**
         * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
         * given ``owner``'s signed approval.
         *
         * IMPORTANT: The same issues {IERC20-approve} has related to transaction
         * ordering also apply here.
         *
         * Emits an {Approval} event.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         * - `deadline` must be a timestamp in the future.
         * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
         * over the EIP712-formatted function arguments.
         * - the signature must use ``owner``'s current nonce (see {nonces}).
         *
         * For more information on the signature format, see the
         * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
         * section].
         */
        function permit(
            address owner,
            address spender,
            uint256 value,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external;
        /**
         * @dev Returns the current nonce for `owner`. This value must be
         * included whenever a signature is generated for {permit}.
         *
         * Every successful call to {permit} increases ``owner``'s nonce by one. This
         * prevents a signature from being used multiple times.
         */
        function nonces(address owner) external view returns (uint256);
        /**
         * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
         */
        // solhint-disable-next-line func-name-mixedcase
        function DOMAIN_SEPARATOR() external view returns (bytes32);
    }