ETH Price: $1,944.79 (-1.54%)

Transaction Decoder

Block:
22024958 at Mar-11-2025 04:56:11 PM +UTC
Transaction Fee:
0.0008228540166114 ETH $1.60
Gas Used:
704,980 Gas / 1.16720193 Gwei

Account State Difference:

  Address   Before After State Difference Code
(Titan Builder)
12.113535460564521831 Eth12.113539778304769271 Eth0.00000431774024744
0xA7551d66...e7E5D1a56
0.791527258343965768 Eth
Nonce: 28
0.790704404327354368 Eth
Nonce: 29
0.0008228540166114

Execution Trace

L1ChugSplashProxy.838b2520( )
  • ProxyAdmin.STATICCALL( )
  • L1StandardBridge.depositERC20To( _l1Token=0xdAC17F958D2ee523a2206206994597C13D831ec7, _l2Token=0xF1B50eD67A9e2CC94Ad3c477779E2d4cBfFf9029, _to=0xA7551d664Ae5578e4B6090bEeEBd4B4e7E5D1a56, _amount=1971043158, _minGasLimit=200000, _extraData=0x7375706572627269646765 )
    • TetherToken.01ffc9a7( )
    • TetherToken.01ffc9a7( )
    • TetherToken.transferFrom( _from=0xA7551d664Ae5578e4B6090bEeEBd4B4e7E5D1a56, _to=0x2171E6d3B7964fA9654Ce41dA8a8fFAff2Cc70be, _value=1971043158 )
      File 1 of 4: L1ChugSplashProxy
      // 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;
          }
      }
      

      File 2 of 4: 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 4: 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 "src/universal/interfaces/ISemver.sol";
      import { ICrossDomainMessenger } from "src/universal/interfaces/ICrossDomainMessenger.sol";
      import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol";
      import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.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.2.1-beta.1
          string public constant version = "2.2.1-beta.1";
          /// @notice Address of the SuperchainConfig contract.
          ISuperchainConfig public superchainConfig;
          /// @notice Address of the SystemConfig contract.
          ISystemConfig public systemConfig;
          /// @notice Constructs the L1StandardBridge contract.
          constructor() StandardBridge() {
              initialize({
                  _messenger: ICrossDomainMessenger(address(0)),
                  _superchainConfig: ISuperchainConfig(address(0)),
                  _systemConfig: ISystemConfig(address(0))
              });
          }
          /// @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,
              ISystemConfig _systemConfig
          )
              public
              initializer
          {
              superchainConfig = _superchainConfig;
              systemConfig = _systemConfig;
              __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(""));
          }
          /// @inheritdoc StandardBridge
          function gasPayingToken() internal view override returns (address addr_, uint8 decimals_) {
              (addr_, decimals_) = systemConfig.gasPayingToken();
          }
          /// @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;
      import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      import { ERC165Checker } from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
      import { Address } from "@openzeppelin/contracts/utils/Address.sol";
      import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
      import { SafeCall } from "src/libraries/SafeCall.sol";
      import { IOptimismMintableERC20, ILegacyMintableERC20 } from "src/universal/interfaces/IOptimismMintableERC20.sol";
      import { ICrossDomainMessenger } from "src/universal/interfaces/ICrossDomainMessenger.sol";
      import { OptimismMintableERC20 } from "src/universal/OptimismMintableERC20.sol";
      import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
      import { Constants } from "src/libraries/Constants.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(!Address.isContract(msg.sender), "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 Returns the address of the custom gas token and the token's decimals.
          function gasPayingToken() internal view virtual returns (address, uint8);
          /// @notice Returns whether the chain uses a custom gas token or not.
          function isCustomGasToken() internal view returns (bool) {
              (address token,) = gasPayingToken();
              return token != Constants.ETHER;
          }
          /// @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(isCustomGasToken() == false, "StandardBridge: cannot bridge ETH with custom gas token");
              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"
                  );
                  OptimismMintableERC20(_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(isCustomGasToken() == false, "StandardBridge: cannot bridge ETH with custom gas token");
              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"
                  );
                  OptimismMintableERC20(_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 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 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 == 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";
              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 == 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 == OPTIMISM_SUPERCHAIN_ERC20_FACTORY)
                  || (_useInterop && _addr == OPTIMISM_SUPERCHAIN_ERC20_BEACON);
          }
          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 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);
      }
      // 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);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import { IResourceMetering } from "src/L1/interfaces/IResourceMetering.sol";
      interface ISystemConfig {
          enum UpdateType {
              BATCHER,
              GAS_CONFIG,
              GAS_LIMIT,
              UNSAFE_BLOCK_SIGNER
          }
          struct Addresses {
              address l1CrossDomainMessenger;
              address l1ERC721Bridge;
              address l1StandardBridge;
              address disputeGameFactory;
              address optimismPortal;
              address optimismMintableERC20Factory;
              address gasPayingToken;
          }
          event ConfigUpdate(uint256 indexed version, UpdateType indexed updateType, bytes data);
          event Initialized(uint8 version);
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
          function BATCH_INBOX_SLOT() external view returns (bytes32);
          function DISPUTE_GAME_FACTORY_SLOT() external view returns (bytes32);
          function L1_CROSS_DOMAIN_MESSENGER_SLOT() external view returns (bytes32);
          function L1_ERC_721_BRIDGE_SLOT() external view returns (bytes32);
          function L1_STANDARD_BRIDGE_SLOT() external view returns (bytes32);
          function OPTIMISM_MINTABLE_ERC20_FACTORY_SLOT() external view returns (bytes32);
          function OPTIMISM_PORTAL_SLOT() external view returns (bytes32);
          function START_BLOCK_SLOT() external view returns (bytes32);
          function UNSAFE_BLOCK_SIGNER_SLOT() external view returns (bytes32);
          function VERSION() external view returns (uint256);
          function basefeeScalar() external view returns (uint32);
          function batchInbox() external view returns (address addr_);
          function batcherHash() external view returns (bytes32);
          function blobbasefeeScalar() external view returns (uint32);
          function disputeGameFactory() external view returns (address addr_);
          function gasLimit() external view returns (uint64);
          function gasPayingToken() external view returns (address addr_, uint8 decimals_);
          function gasPayingTokenName() external view returns (string memory name_);
          function gasPayingTokenSymbol() external view returns (string memory symbol_);
          function initialize(
              address _owner,
              uint32 _basefeeScalar,
              uint32 _blobbasefeeScalar,
              bytes32 _batcherHash,
              uint64 _gasLimit,
              address _unsafeBlockSigner,
              IResourceMetering.ResourceConfig memory _config,
              address _batchInbox,
              Addresses memory _addresses
          )
              external;
          function isCustomGasToken() external view returns (bool);
          function l1CrossDomainMessenger() external view returns (address addr_);
          function l1ERC721Bridge() external view returns (address addr_);
          function l1StandardBridge() external view returns (address addr_);
          function maximumGasLimit() external pure returns (uint64);
          function minimumGasLimit() external view returns (uint64);
          function optimismMintableERC20Factory() external view returns (address addr_);
          function optimismPortal() external view returns (address addr_);
          function overhead() external view returns (uint256);
          function owner() external view returns (address);
          function renounceOwnership() external;
          function resourceConfig() external view returns (IResourceMetering.ResourceConfig memory);
          function scalar() external view returns (uint256);
          function setBatcherHash(bytes32 _batcherHash) external;
          function setGasConfig(uint256 _overhead, uint256 _scalar) external;
          function setGasConfigEcotone(uint32 _basefeeScalar, uint32 _blobbasefeeScalar) external;
          function setGasLimit(uint64 _gasLimit) external;
          function setUnsafeBlockSigner(address _unsafeBlockSigner) external;
          function startBlock() external view returns (uint256 startBlock_);
          function transferOwnership(address newOwner) external;
          function unsafeBlockSigner() external view returns (address addr_);
          function version() external pure returns (string memory);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Interface of the ERC20 standard as defined in the EIP.
       */
      interface IERC20 {
          /**
           * @dev Emitted when `value` tokens are moved from one account (`from`) to
           * another (`to`).
           *
           * Note that `value` may be zero.
           */
          event Transfer(address indexed from, address indexed to, uint256 value);
          /**
           * @dev Emitted when the allowance of a `spender` for an `owner` is set by
           * a call to {approve}. `value` is the new allowance.
           */
          event Approval(address indexed owner, address indexed spender, uint256 value);
          /**
           * @dev Returns the amount of tokens in existence.
           */
          function totalSupply() external view returns (uint256);
          /**
           * @dev Returns the amount of tokens owned by `account`.
           */
          function balanceOf(address account) external view returns (uint256);
          /**
           * @dev Moves `amount` tokens from the caller's account to `to`.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transfer(address to, uint256 amount) external returns (bool);
          /**
           * @dev Returns the remaining number of tokens that `spender` will be
           * allowed to spend on behalf of `owner` through {transferFrom}. This is
           * zero by default.
           *
           * This value changes when {approve} or {transferFrom} are called.
           */
          function allowance(address owner, address spender) external view returns (uint256);
          /**
           * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * IMPORTANT: Beware that changing an allowance with this method brings the risk
           * that someone may use both the old and the new allowance by unfortunate
           * transaction ordering. One possible solution to mitigate this race
           * condition is to first reduce the spender's allowance to 0 and set the
           * desired value afterwards:
           * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
           *
           * Emits an {Approval} event.
           */
          function approve(address spender, uint256 amount) external returns (bool);
          /**
           * @dev Moves `amount` tokens from `from` to `to` using the
           * allowance mechanism. `amount` is then deducted from the caller's
           * allowance.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transferFrom(
              address from,
              address to,
              uint256 amount
          ) external returns (bool);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (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) (utils/Address.sol)
      pragma solidity ^0.8.1;
      /**
       * @dev Collection of functions related to the address type
       */
      library Address {
          /**
           * @dev Returns true if `account` is a contract.
           *
           * [IMPORTANT]
           * ====
           * It is unsafe to assume that an address for which this function returns
           * false is an externally-owned account (EOA) and not a contract.
           *
           * Among others, `isContract` will return false for the following
           * types of addresses:
           *
           *  - an externally-owned account
           *  - a contract in construction
           *  - an address where a contract will be created
           *  - an address where a contract lived, but was destroyed
           * ====
           *
           * [IMPORTANT]
           * ====
           * You shouldn't rely on `isContract` to protect against flash loan attacks!
           *
           * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
           * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
           * constructor.
           * ====
           */
          function isContract(address account) internal view returns (bool) {
              // This method relies on extcodesize/address.code.length, which returns 0
              // for contracts in construction, since the code is only stored at the end
              // of the constructor execution.
              return account.code.length > 0;
          }
          /**
           * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
           * `recipient`, forwarding all available gas and reverting on errors.
           *
           * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
           * of certain opcodes, possibly making contracts go over the 2300 gas limit
           * imposed by `transfer`, making them unable to receive funds via
           * `transfer`. {sendValue} removes this limitation.
           *
           * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
           *
           * IMPORTANT: because control is transferred to `recipient`, care must be
           * taken to not create reentrancy vulnerabilities. Consider using
           * {ReentrancyGuard} or the
           * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              require(address(this).balance >= amount, "Address: insufficient balance");
              (bool success, ) = recipient.call{value: amount}("");
              require(success, "Address: unable to send value, recipient may have reverted");
          }
          /**
           * @dev Performs a Solidity function call using a low level `call`. A
           * plain `call` is an unsafe replacement for a function call: use this
           * function instead.
           *
           * If `target` reverts with a revert reason, it is bubbled up by this
           * function (like regular Solidity function calls).
           *
           * Returns the raw returned data. To convert to the expected return value,
           * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
           *
           * Requirements:
           *
           * - `target` must be a contract.
           * - calling `target` with `data` must not revert.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionCall(target, data, "Address: low-level call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
           * `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal returns (bytes memory) {
              return functionCallWithValue(target, data, 0, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but also transferring `value` wei to `target`.
           *
           * Requirements:
           *
           * - the calling contract must have an ETH balance of at least `value`.
           * - the called Solidity function must be `payable`.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(
              address target,
              bytes memory data,
              uint256 value
          ) internal returns (bytes memory) {
              return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
          }
          /**
           * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
           * with `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(
              address target,
              bytes memory data,
              uint256 value,
              string memory errorMessage
          ) internal returns (bytes memory) {
              require(address(this).balance >= value, "Address: insufficient balance for call");
              require(isContract(target), "Address: call to non-contract");
              (bool success, bytes memory returndata) = target.call{value: value}(data);
              return verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
              return functionStaticCall(target, data, "Address: low-level static call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal view returns (bytes memory) {
              require(isContract(target), "Address: static call to non-contract");
              (bool success, bytes memory returndata) = target.staticcall(data);
              return verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionDelegateCall(target, data, "Address: low-level delegate call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal returns (bytes memory) {
              require(isContract(target), "Address: delegate call to non-contract");
              (bool success, bytes memory returndata) = target.delegatecall(data);
              return verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
           * revert reason using the provided one.
           *
           * _Available since v4.3._
           */
          function verifyCallResult(
              bool success,
              bytes memory returndata,
              string memory errorMessage
          ) internal pure returns (bytes memory) {
              if (success) {
                  return returndata;
              } else {
                  // Look for revert reason and bubble it up if present
                  if (returndata.length > 0) {
                      // The easiest way to bubble the revert reason is using memory via assembly
                      /// @solidity memory-safe-assembly
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert(errorMessage);
                  }
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.7.0) (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;
      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;
      }
      /// @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
      pragma solidity 0.8.15;
      import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
      import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
      import { ILegacyMintableERC20, IOptimismMintableERC20 } from "src/universal/interfaces/IOptimismMintableERC20.sol";
      import { ISemver } from "src/universal/interfaces/ISemver.sol";
      /// @title OptimismMintableERC20
      /// @notice OptimismMintableERC20 is a standard extension of the base ERC20 token contract designed
      ///         to allow the StandardBridge contracts to mint and burn tokens. This makes it possible to
      ///         use an OptimismMintablERC20 as the L2 representation of an L1 token, or vice-versa.
      ///         Designed to be backwards compatible with the older StandardL2ERC20 token which was only
      ///         meant for use on L2.
      contract OptimismMintableERC20 is IOptimismMintableERC20, ILegacyMintableERC20, ERC20, ISemver {
          /// @notice Address of the corresponding version of this token on the remote chain.
          address public immutable REMOTE_TOKEN;
          /// @notice Address of the StandardBridge on this network.
          address public immutable BRIDGE;
          /// @notice Decimals of the token
          uint8 private immutable DECIMALS;
          /// @notice Emitted whenever tokens are minted for an account.
          /// @param account Address of the account tokens are being minted for.
          /// @param amount  Amount of tokens minted.
          event Mint(address indexed account, uint256 amount);
          /// @notice Emitted whenever tokens are burned from an account.
          /// @param account Address of the account tokens are being burned from.
          /// @param amount  Amount of tokens burned.
          event Burn(address indexed account, uint256 amount);
          /// @notice A modifier that only allows the bridge to call
          modifier onlyBridge() {
              require(msg.sender == BRIDGE, "OptimismMintableERC20: only bridge can mint and burn");
              _;
          }
          /// @notice Semantic version.
          /// @custom:semver 1.3.1-beta.1
          string public constant version = "1.3.1-beta.1";
          /// @param _bridge      Address of the L2 standard bridge.
          /// @param _remoteToken Address of the corresponding L1 token.
          /// @param _name        ERC20 name.
          /// @param _symbol      ERC20 symbol.
          constructor(
              address _bridge,
              address _remoteToken,
              string memory _name,
              string memory _symbol,
              uint8 _decimals
          )
              ERC20(_name, _symbol)
          {
              REMOTE_TOKEN = _remoteToken;
              BRIDGE = _bridge;
              DECIMALS = _decimals;
          }
          /// @notice Allows the StandardBridge on this network to mint tokens.
          /// @param _to     Address to mint tokens to.
          /// @param _amount Amount of tokens to mint.
          function mint(
              address _to,
              uint256 _amount
          )
              external
              virtual
              override(IOptimismMintableERC20, ILegacyMintableERC20)
              onlyBridge
          {
              _mint(_to, _amount);
              emit Mint(_to, _amount);
          }
          /// @notice Allows the StandardBridge on this network to burn tokens.
          /// @param _from   Address to burn tokens from.
          /// @param _amount Amount of tokens to burn.
          function burn(
              address _from,
              uint256 _amount
          )
              external
              virtual
              override(IOptimismMintableERC20, ILegacyMintableERC20)
              onlyBridge
          {
              _burn(_from, _amount);
              emit Burn(_from, _amount);
          }
          /// @notice ERC165 interface check function.
          /// @param _interfaceId Interface ID to check.
          /// @return Whether or not the interface is supported by this contract.
          function supportsInterface(bytes4 _interfaceId) external pure virtual returns (bool) {
              bytes4 iface1 = type(IERC165).interfaceId;
              // Interface corresponding to the legacy L2StandardERC20.
              bytes4 iface2 = type(ILegacyMintableERC20).interfaceId;
              // Interface corresponding to the updated OptimismMintableERC20 (this contract).
              bytes4 iface3 = type(IOptimismMintableERC20).interfaceId;
              return _interfaceId == iface1 || _interfaceId == iface2 || _interfaceId == iface3;
          }
          /// @custom:legacy
          /// @notice Legacy getter for the remote token. Use REMOTE_TOKEN going forward.
          function l1Token() public view returns (address) {
              return REMOTE_TOKEN;
          }
          /// @custom:legacy
          /// @notice Legacy getter for the bridge. Use BRIDGE going forward.
          function l2Bridge() public view returns (address) {
              return BRIDGE;
          }
          /// @custom:legacy
          /// @notice Legacy getter for REMOTE_TOKEN.
          function remoteToken() public view returns (address) {
              return REMOTE_TOKEN;
          }
          /// @custom:legacy
          /// @notice Legacy getter for BRIDGE.
          function bridge() public view returns (address) {
              return BRIDGE;
          }
          /// @dev Returns the number of decimals used to get its user representation.
          /// For example, if `decimals` equals `2`, a balance of `505` tokens should
          /// be displayed to a user as `5.05` (`505 / 10 ** 2`).
          /// NOTE: This information is only used for _display_ purposes: it in
          /// no way affects any of the arithmetic of the contract, including
          /// {IERC20-balanceOf} and {IERC20-transfer}.
          function decimals() public view override returns (uint8) {
              return DECIMALS;
          }
      }
      // 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
      pragma solidity ^0.8.0;
      import { IResourceMetering } from "src/L1/interfaces/IResourceMetering.sol";
      /// @title Constants
      /// @notice Constants is a library for storing constants. Simple! Don't put everything in here, just
      ///         the stuff used in multiple contracts. Constants that only apply to a single contract
      ///         should be defined in that contract instead.
      library Constants {
          /// @notice Special address to be used as the tx origin for gas estimation calls in the
          ///         OptimismPortal and CrossDomainMessenger calls. You only need to use this address if
          ///         the minimum gas limit specified by the user is not actually enough to execute the
          ///         given message and you're attempting to estimate the actual necessary gas limit. We
          ///         use address(1) because it's the ecrecover precompile and therefore guaranteed to
          ///         never have any code on any EVM chain.
          address internal constant ESTIMATION_ADDRESS = address(1);
          /// @notice Value used for the L2 sender storage slot in both the OptimismPortal and the
          ///         CrossDomainMessenger contracts before an actual sender is set. This value is
          ///         non-zero to reduce the gas cost of message passing transactions.
          address internal constant DEFAULT_L2_SENDER = 0x000000000000000000000000000000000000dEaD;
          /// @notice The storage slot that holds the address of a proxy implementation.
          /// @dev `bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)`
          bytes32 internal constant PROXY_IMPLEMENTATION_ADDRESS =
              0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
          /// @notice The storage slot that holds the address of the owner.
          /// @dev `bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)`
          bytes32 internal constant PROXY_OWNER_ADDRESS = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
          /// @notice The address that represents ether when dealing with ERC20 token addresses.
          address internal constant ETHER = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
          /// @notice The address that represents the system caller responsible for L1 attributes
          ///         transactions.
          address internal constant DEPOSITOR_ACCOUNT = 0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001;
          /// @notice Returns the default values for the ResourceConfig. These are the recommended values
          ///         for a production network.
          function DEFAULT_RESOURCE_CONFIG() internal pure returns (IResourceMetering.ResourceConfig memory) {
              IResourceMetering.ResourceConfig memory config = IResourceMetering.ResourceConfig({
                  maxResourceLimit: 20_000_000,
                  elasticityMultiplier: 10,
                  baseFeeMaxChangeDenominator: 8,
                  minimumBaseFee: 1 gwei,
                  systemTxMaxGas: 1_000_000,
                  maximumBaseFee: type(uint128).max
              });
              return config;
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      interface IResourceMetering {
          struct ResourceParams {
              uint128 prevBaseFee;
              uint64 prevBoughtGas;
              uint64 prevBlockNum;
          }
          struct ResourceConfig {
              uint32 maxResourceLimit;
              uint8 elasticityMultiplier;
              uint8 baseFeeMaxChangeDenominator;
              uint32 minimumBaseFee;
              uint32 systemTxMaxGas;
              uint128 maximumBaseFee;
          }
          error OutOfGas();
          event Initialized(uint8 version);
          function params() external view returns (uint128 prevBaseFee, uint64 prevBoughtGas, uint64 prevBlockNum);
      }
      // 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);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/ERC20.sol)
      pragma solidity ^0.8.0;
      import "./IERC20.sol";
      import "./extensions/IERC20Metadata.sol";
      import "../../utils/Context.sol";
      /**
       * @dev Implementation of the {IERC20} interface.
       *
       * This implementation is agnostic to the way tokens are created. This means
       * that a supply mechanism has to be added in a derived contract using {_mint}.
       * For a generic mechanism see {ERC20PresetMinterPauser}.
       *
       * TIP: For a detailed writeup see our guide
       * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
       * to implement supply mechanisms].
       *
       * We have followed general OpenZeppelin Contracts guidelines: functions revert
       * instead returning `false` on failure. This behavior is nonetheless
       * conventional and does not conflict with the expectations of ERC20
       * applications.
       *
       * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
       * This allows applications to reconstruct the allowance for all accounts just
       * by listening to said events. Other implementations of the EIP may not emit
       * these events, as it isn't required by the specification.
       *
       * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
       * functions have been added to mitigate the well-known issues around setting
       * allowances. See {IERC20-approve}.
       */
      contract ERC20 is Context, IERC20, IERC20Metadata {
          mapping(address => uint256) private _balances;
          mapping(address => mapping(address => uint256)) private _allowances;
          uint256 private _totalSupply;
          string private _name;
          string private _symbol;
          /**
           * @dev Sets the values for {name} and {symbol}.
           *
           * The default value of {decimals} is 18. To select a different value for
           * {decimals} you should overload it.
           *
           * All two of these values are immutable: they can only be set once during
           * construction.
           */
          constructor(string memory name_, string memory symbol_) {
              _name = name_;
              _symbol = symbol_;
          }
          /**
           * @dev Returns the name of the token.
           */
          function name() public view virtual override returns (string memory) {
              return _name;
          }
          /**
           * @dev Returns the symbol of the token, usually a shorter version of the
           * name.
           */
          function symbol() public view virtual override returns (string memory) {
              return _symbol;
          }
          /**
           * @dev Returns the number of decimals used to get its user representation.
           * For example, if `decimals` equals `2`, a balance of `505` tokens should
           * be displayed to a user as `5.05` (`505 / 10 ** 2`).
           *
           * Tokens usually opt for a value of 18, imitating the relationship between
           * Ether and Wei. This is the value {ERC20} uses, unless this function is
           * overridden;
           *
           * NOTE: This information is only used for _display_ purposes: it in
           * no way affects any of the arithmetic of the contract, including
           * {IERC20-balanceOf} and {IERC20-transfer}.
           */
          function decimals() public view virtual override returns (uint8) {
              return 18;
          }
          /**
           * @dev See {IERC20-totalSupply}.
           */
          function totalSupply() public view virtual override returns (uint256) {
              return _totalSupply;
          }
          /**
           * @dev See {IERC20-balanceOf}.
           */
          function balanceOf(address account) public view virtual override returns (uint256) {
              return _balances[account];
          }
          /**
           * @dev See {IERC20-transfer}.
           *
           * Requirements:
           *
           * - `to` cannot be the zero address.
           * - the caller must have a balance of at least `amount`.
           */
          function transfer(address to, uint256 amount) public virtual override returns (bool) {
              address owner = _msgSender();
              _transfer(owner, to, amount);
              return true;
          }
          /**
           * @dev See {IERC20-allowance}.
           */
          function allowance(address owner, address spender) public view virtual override returns (uint256) {
              return _allowances[owner][spender];
          }
          /**
           * @dev See {IERC20-approve}.
           *
           * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
           * `transferFrom`. This is semantically equivalent to an infinite approval.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           */
          function approve(address spender, uint256 amount) public virtual override returns (bool) {
              address owner = _msgSender();
              _approve(owner, spender, amount);
              return true;
          }
          /**
           * @dev See {IERC20-transferFrom}.
           *
           * Emits an {Approval} event indicating the updated allowance. This is not
           * required by the EIP. See the note at the beginning of {ERC20}.
           *
           * NOTE: Does not update the allowance if the current allowance
           * is the maximum `uint256`.
           *
           * Requirements:
           *
           * - `from` and `to` cannot be the zero address.
           * - `from` must have a balance of at least `amount`.
           * - the caller must have allowance for ``from``'s tokens of at least
           * `amount`.
           */
          function transferFrom(
              address from,
              address to,
              uint256 amount
          ) public virtual override returns (bool) {
              address spender = _msgSender();
              _spendAllowance(from, spender, amount);
              _transfer(from, to, amount);
              return true;
          }
          /**
           * @dev Atomically increases the allowance granted to `spender` by the caller.
           *
           * This is an alternative to {approve} that can be used as a mitigation for
           * problems described in {IERC20-approve}.
           *
           * Emits an {Approval} event indicating the updated allowance.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           */
          function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
              address owner = _msgSender();
              _approve(owner, spender, allowance(owner, spender) + addedValue);
              return true;
          }
          /**
           * @dev Atomically decreases the allowance granted to `spender` by the caller.
           *
           * This is an alternative to {approve} that can be used as a mitigation for
           * problems described in {IERC20-approve}.
           *
           * Emits an {Approval} event indicating the updated allowance.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           * - `spender` must have allowance for the caller of at least
           * `subtractedValue`.
           */
          function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
              address owner = _msgSender();
              uint256 currentAllowance = allowance(owner, spender);
              require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
              unchecked {
                  _approve(owner, spender, currentAllowance - subtractedValue);
              }
              return true;
          }
          /**
           * @dev Moves `amount` of tokens from `from` to `to`.
           *
           * This internal function is equivalent to {transfer}, and can be used to
           * e.g. implement automatic token fees, slashing mechanisms, etc.
           *
           * Emits a {Transfer} event.
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           * - `from` must have a balance of at least `amount`.
           */
          function _transfer(
              address from,
              address to,
              uint256 amount
          ) internal virtual {
              require(from != address(0), "ERC20: transfer from the zero address");
              require(to != address(0), "ERC20: transfer to the zero address");
              _beforeTokenTransfer(from, to, amount);
              uint256 fromBalance = _balances[from];
              require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
              unchecked {
                  _balances[from] = fromBalance - amount;
              }
              _balances[to] += amount;
              emit Transfer(from, to, amount);
              _afterTokenTransfer(from, to, amount);
          }
          /** @dev Creates `amount` tokens and assigns them to `account`, increasing
           * the total supply.
           *
           * Emits a {Transfer} event with `from` set to the zero address.
           *
           * Requirements:
           *
           * - `account` cannot be the zero address.
           */
          function _mint(address account, uint256 amount) internal virtual {
              require(account != address(0), "ERC20: mint to the zero address");
              _beforeTokenTransfer(address(0), account, amount);
              _totalSupply += amount;
              _balances[account] += amount;
              emit Transfer(address(0), account, amount);
              _afterTokenTransfer(address(0), account, amount);
          }
          /**
           * @dev Destroys `amount` tokens from `account`, reducing the
           * total supply.
           *
           * Emits a {Transfer} event with `to` set to the zero address.
           *
           * Requirements:
           *
           * - `account` cannot be the zero address.
           * - `account` must have at least `amount` tokens.
           */
          function _burn(address account, uint256 amount) internal virtual {
              require(account != address(0), "ERC20: burn from the zero address");
              _beforeTokenTransfer(account, address(0), amount);
              uint256 accountBalance = _balances[account];
              require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
              unchecked {
                  _balances[account] = accountBalance - amount;
              }
              _totalSupply -= amount;
              emit Transfer(account, address(0), amount);
              _afterTokenTransfer(account, address(0), amount);
          }
          /**
           * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
           *
           * This internal function is equivalent to `approve`, and can be used to
           * e.g. set automatic allowances for certain subsystems, etc.
           *
           * Emits an {Approval} event.
           *
           * Requirements:
           *
           * - `owner` cannot be the zero address.
           * - `spender` cannot be the zero address.
           */
          function _approve(
              address owner,
              address spender,
              uint256 amount
          ) internal virtual {
              require(owner != address(0), "ERC20: approve from the zero address");
              require(spender != address(0), "ERC20: approve to the zero address");
              _allowances[owner][spender] = amount;
              emit Approval(owner, spender, amount);
          }
          /**
           * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
           *
           * Does not update the allowance amount in case of infinite allowance.
           * Revert if not enough allowance is available.
           *
           * Might emit an {Approval} event.
           */
          function _spendAllowance(
              address owner,
              address spender,
              uint256 amount
          ) internal virtual {
              uint256 currentAllowance = allowance(owner, spender);
              if (currentAllowance != type(uint256).max) {
                  require(currentAllowance >= amount, "ERC20: insufficient allowance");
                  unchecked {
                      _approve(owner, spender, currentAllowance - amount);
                  }
              }
          }
          /**
           * @dev Hook that is called before any transfer of tokens. This includes
           * minting and burning.
           *
           * Calling conditions:
           *
           * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
           * will be transferred to `to`.
           * - when `from` is zero, `amount` tokens will be minted for `to`.
           * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
           * - `from` and `to` are never both zero.
           *
           * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
           */
          function _beforeTokenTransfer(
              address from,
              address to,
              uint256 amount
          ) internal virtual {}
          /**
           * @dev Hook that is called after any transfer of tokens. This includes
           * minting and burning.
           *
           * Calling conditions:
           *
           * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
           * has been transferred to `to`.
           * - when `from` is zero, `amount` tokens have been minted for `to`.
           * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
           * - `from` and `to` are never both zero.
           *
           * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
           */
          function _afterTokenTransfer(
              address from,
              address to,
              uint256 amount
          ) internal virtual {}
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
      pragma solidity ^0.8.0;
      import "../IERC20.sol";
      /**
       * @dev Interface for the optional metadata functions from the ERC20 standard.
       *
       * _Available since v4.1._
       */
      interface IERC20Metadata is IERC20 {
          /**
           * @dev Returns the name of the token.
           */
          function name() external view returns (string memory);
          /**
           * @dev Returns the symbol of the token.
           */
          function symbol() external view returns (string memory);
          /**
           * @dev Returns the decimals places of the token.
           */
          function decimals() external view returns (uint8);
      }
      // 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 4 of 4: TetherToken
      pragma solidity ^0.4.17;
      
      /**
       * @title SafeMath
       * @dev Math operations with safety checks that throw on error
       */
      library SafeMath {
          function mul(uint256 a, uint256 b) internal pure returns (uint256) {
              if (a == 0) {
                  return 0;
              }
              uint256 c = a * b;
              assert(c / a == b);
              return c;
          }
      
          function div(uint256 a, uint256 b) internal pure returns (uint256) {
              // assert(b > 0); // Solidity automatically throws when dividing by 0
              uint256 c = a / b;
              // assert(a == b * c + a % b); // There is no case in which this doesn't hold
              return c;
          }
      
          function sub(uint256 a, uint256 b) internal pure returns (uint256) {
              assert(b <= a);
              return a - b;
          }
      
          function add(uint256 a, uint256 b) internal pure returns (uint256) {
              uint256 c = a + b;
              assert(c >= a);
              return c;
          }
      }
      
      /**
       * @title Ownable
       * @dev The Ownable contract has an owner address, and provides basic authorization control
       * functions, this simplifies the implementation of "user permissions".
       */
      contract Ownable {
          address public owner;
      
          /**
            * @dev The Ownable constructor sets the original `owner` of the contract to the sender
            * account.
            */
          function Ownable() public {
              owner = msg.sender;
          }
      
          /**
            * @dev Throws if called by any account other than the owner.
            */
          modifier onlyOwner() {
              require(msg.sender == owner);
              _;
          }
      
          /**
          * @dev Allows the current owner to transfer control of the contract to a newOwner.
          * @param newOwner The address to transfer ownership to.
          */
          function transferOwnership(address newOwner) public onlyOwner {
              if (newOwner != address(0)) {
                  owner = newOwner;
              }
          }
      
      }
      
      /**
       * @title ERC20Basic
       * @dev Simpler version of ERC20 interface
       * @dev see https://github.com/ethereum/EIPs/issues/20
       */
      contract ERC20Basic {
          uint public _totalSupply;
          function totalSupply() public constant returns (uint);
          function balanceOf(address who) public constant returns (uint);
          function transfer(address to, uint value) public;
          event Transfer(address indexed from, address indexed to, uint value);
      }
      
      /**
       * @title ERC20 interface
       * @dev see https://github.com/ethereum/EIPs/issues/20
       */
      contract ERC20 is ERC20Basic {
          function allowance(address owner, address spender) public constant returns (uint);
          function transferFrom(address from, address to, uint value) public;
          function approve(address spender, uint value) public;
          event Approval(address indexed owner, address indexed spender, uint value);
      }
      
      /**
       * @title Basic token
       * @dev Basic version of StandardToken, with no allowances.
       */
      contract BasicToken is Ownable, ERC20Basic {
          using SafeMath for uint;
      
          mapping(address => uint) public balances;
      
          // additional variables for use if transaction fees ever became necessary
          uint public basisPointsRate = 0;
          uint public maximumFee = 0;
      
          /**
          * @dev Fix for the ERC20 short address attack.
          */
          modifier onlyPayloadSize(uint size) {
              require(!(msg.data.length < size + 4));
              _;
          }
      
          /**
          * @dev transfer token for a specified address
          * @param _to The address to transfer to.
          * @param _value The amount to be transferred.
          */
          function transfer(address _to, uint _value) public onlyPayloadSize(2 * 32) {
              uint fee = (_value.mul(basisPointsRate)).div(10000);
              if (fee > maximumFee) {
                  fee = maximumFee;
              }
              uint sendAmount = _value.sub(fee);
              balances[msg.sender] = balances[msg.sender].sub(_value);
              balances[_to] = balances[_to].add(sendAmount);
              if (fee > 0) {
                  balances[owner] = balances[owner].add(fee);
                  Transfer(msg.sender, owner, fee);
              }
              Transfer(msg.sender, _to, sendAmount);
          }
      
          /**
          * @dev Gets the balance of the specified address.
          * @param _owner The address to query the the balance of.
          * @return An uint representing the amount owned by the passed address.
          */
          function balanceOf(address _owner) public constant returns (uint balance) {
              return balances[_owner];
          }
      
      }
      
      /**
       * @title Standard ERC20 token
       *
       * @dev Implementation of the basic standard token.
       * @dev https://github.com/ethereum/EIPs/issues/20
       * @dev Based oncode by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
       */
      contract StandardToken is BasicToken, ERC20 {
      
          mapping (address => mapping (address => uint)) public allowed;
      
          uint public constant MAX_UINT = 2**256 - 1;
      
          /**
          * @dev Transfer tokens from one address to another
          * @param _from address The address which you want to send tokens from
          * @param _to address The address which you want to transfer to
          * @param _value uint the amount of tokens to be transferred
          */
          function transferFrom(address _from, address _to, uint _value) public onlyPayloadSize(3 * 32) {
              var _allowance = allowed[_from][msg.sender];
      
              // Check is not needed because sub(_allowance, _value) will already throw if this condition is not met
              // if (_value > _allowance) throw;
      
              uint fee = (_value.mul(basisPointsRate)).div(10000);
              if (fee > maximumFee) {
                  fee = maximumFee;
              }
              if (_allowance < MAX_UINT) {
                  allowed[_from][msg.sender] = _allowance.sub(_value);
              }
              uint sendAmount = _value.sub(fee);
              balances[_from] = balances[_from].sub(_value);
              balances[_to] = balances[_to].add(sendAmount);
              if (fee > 0) {
                  balances[owner] = balances[owner].add(fee);
                  Transfer(_from, owner, fee);
              }
              Transfer(_from, _to, sendAmount);
          }
      
          /**
          * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
          * @param _spender The address which will spend the funds.
          * @param _value The amount of tokens to be spent.
          */
          function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {
      
              // To change the approve amount you first have to reduce the addresses`
              //  allowance to zero by calling `approve(_spender, 0)` if it is not
              //  already 0 to mitigate the race condition described here:
              //  https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
              require(!((_value != 0) && (allowed[msg.sender][_spender] != 0)));
      
              allowed[msg.sender][_spender] = _value;
              Approval(msg.sender, _spender, _value);
          }
      
          /**
          * @dev Function to check the amount of tokens than an owner allowed to a spender.
          * @param _owner address The address which owns the funds.
          * @param _spender address The address which will spend the funds.
          * @return A uint specifying the amount of tokens still available for the spender.
          */
          function allowance(address _owner, address _spender) public constant returns (uint remaining) {
              return allowed[_owner][_spender];
          }
      
      }
      
      
      /**
       * @title Pausable
       * @dev Base contract which allows children to implement an emergency stop mechanism.
       */
      contract Pausable is Ownable {
        event Pause();
        event Unpause();
      
        bool public paused = false;
      
      
        /**
         * @dev Modifier to make a function callable only when the contract is not paused.
         */
        modifier whenNotPaused() {
          require(!paused);
          _;
        }
      
        /**
         * @dev Modifier to make a function callable only when the contract is paused.
         */
        modifier whenPaused() {
          require(paused);
          _;
        }
      
        /**
         * @dev called by the owner to pause, triggers stopped state
         */
        function pause() onlyOwner whenNotPaused public {
          paused = true;
          Pause();
        }
      
        /**
         * @dev called by the owner to unpause, returns to normal state
         */
        function unpause() onlyOwner whenPaused public {
          paused = false;
          Unpause();
        }
      }
      
      contract BlackList is Ownable, BasicToken {
      
          /////// Getters to allow the same blacklist to be used also by other contracts (including upgraded Tether) ///////
          function getBlackListStatus(address _maker) external constant returns (bool) {
              return isBlackListed[_maker];
          }
      
          function getOwner() external constant returns (address) {
              return owner;
          }
      
          mapping (address => bool) public isBlackListed;
          
          function addBlackList (address _evilUser) public onlyOwner {
              isBlackListed[_evilUser] = true;
              AddedBlackList(_evilUser);
          }
      
          function removeBlackList (address _clearedUser) public onlyOwner {
              isBlackListed[_clearedUser] = false;
              RemovedBlackList(_clearedUser);
          }
      
          function destroyBlackFunds (address _blackListedUser) public onlyOwner {
              require(isBlackListed[_blackListedUser]);
              uint dirtyFunds = balanceOf(_blackListedUser);
              balances[_blackListedUser] = 0;
              _totalSupply -= dirtyFunds;
              DestroyedBlackFunds(_blackListedUser, dirtyFunds);
          }
      
          event DestroyedBlackFunds(address _blackListedUser, uint _balance);
      
          event AddedBlackList(address _user);
      
          event RemovedBlackList(address _user);
      
      }
      
      contract UpgradedStandardToken is StandardToken{
          // those methods are called by the legacy contract
          // and they must ensure msg.sender to be the contract address
          function transferByLegacy(address from, address to, uint value) public;
          function transferFromByLegacy(address sender, address from, address spender, uint value) public;
          function approveByLegacy(address from, address spender, uint value) public;
      }
      
      contract TetherToken is Pausable, StandardToken, BlackList {
      
          string public name;
          string public symbol;
          uint public decimals;
          address public upgradedAddress;
          bool public deprecated;
      
          //  The contract can be initialized with a number of tokens
          //  All the tokens are deposited to the owner address
          //
          // @param _balance Initial supply of the contract
          // @param _name Token Name
          // @param _symbol Token symbol
          // @param _decimals Token decimals
          function TetherToken(uint _initialSupply, string _name, string _symbol, uint _decimals) public {
              _totalSupply = _initialSupply;
              name = _name;
              symbol = _symbol;
              decimals = _decimals;
              balances[owner] = _initialSupply;
              deprecated = false;
          }
      
          // Forward ERC20 methods to upgraded contract if this one is deprecated
          function transfer(address _to, uint _value) public whenNotPaused {
              require(!isBlackListed[msg.sender]);
              if (deprecated) {
                  return UpgradedStandardToken(upgradedAddress).transferByLegacy(msg.sender, _to, _value);
              } else {
                  return super.transfer(_to, _value);
              }
          }
      
          // Forward ERC20 methods to upgraded contract if this one is deprecated
          function transferFrom(address _from, address _to, uint _value) public whenNotPaused {
              require(!isBlackListed[_from]);
              if (deprecated) {
                  return UpgradedStandardToken(upgradedAddress).transferFromByLegacy(msg.sender, _from, _to, _value);
              } else {
                  return super.transferFrom(_from, _to, _value);
              }
          }
      
          // Forward ERC20 methods to upgraded contract if this one is deprecated
          function balanceOf(address who) public constant returns (uint) {
              if (deprecated) {
                  return UpgradedStandardToken(upgradedAddress).balanceOf(who);
              } else {
                  return super.balanceOf(who);
              }
          }
      
          // Forward ERC20 methods to upgraded contract if this one is deprecated
          function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {
              if (deprecated) {
                  return UpgradedStandardToken(upgradedAddress).approveByLegacy(msg.sender, _spender, _value);
              } else {
                  return super.approve(_spender, _value);
              }
          }
      
          // Forward ERC20 methods to upgraded contract if this one is deprecated
          function allowance(address _owner, address _spender) public constant returns (uint remaining) {
              if (deprecated) {
                  return StandardToken(upgradedAddress).allowance(_owner, _spender);
              } else {
                  return super.allowance(_owner, _spender);
              }
          }
      
          // deprecate current contract in favour of a new one
          function deprecate(address _upgradedAddress) public onlyOwner {
              deprecated = true;
              upgradedAddress = _upgradedAddress;
              Deprecate(_upgradedAddress);
          }
      
          // deprecate current contract if favour of a new one
          function totalSupply() public constant returns (uint) {
              if (deprecated) {
                  return StandardToken(upgradedAddress).totalSupply();
              } else {
                  return _totalSupply;
              }
          }
      
          // Issue a new amount of tokens
          // these tokens are deposited into the owner address
          //
          // @param _amount Number of tokens to be issued
          function issue(uint amount) public onlyOwner {
              require(_totalSupply + amount > _totalSupply);
              require(balances[owner] + amount > balances[owner]);
      
              balances[owner] += amount;
              _totalSupply += amount;
              Issue(amount);
          }
      
          // Redeem tokens.
          // These tokens are withdrawn from the owner address
          // if the balance must be enough to cover the redeem
          // or the call will fail.
          // @param _amount Number of tokens to be issued
          function redeem(uint amount) public onlyOwner {
              require(_totalSupply >= amount);
              require(balances[owner] >= amount);
      
              _totalSupply -= amount;
              balances[owner] -= amount;
              Redeem(amount);
          }
      
          function setParams(uint newBasisPoints, uint newMaxFee) public onlyOwner {
              // Ensure transparency by hardcoding limit beyond which fees can never be added
              require(newBasisPoints < 20);
              require(newMaxFee < 50);
      
              basisPointsRate = newBasisPoints;
              maximumFee = newMaxFee.mul(10**decimals);
      
              Params(basisPointsRate, maximumFee);
          }
      
          // Called when new token are issued
          event Issue(uint amount);
      
          // Called when tokens are redeemed
          event Redeem(uint amount);
      
          // Called when contract is deprecated
          event Deprecate(address newAddress);
      
          // Called if contract ever adds fees
          event Params(uint feeBasisPoints, uint maxFee);
      }