ETH Price: $2,543.05 (-7.51%)

Transaction Decoder

Block:
21484832 at Dec-26-2024 06:25:11 AM +UTC
Transaction Fee:
0.00110694672600714 ETH $2.82
Gas Used:
166,316 Gas / 6.655683915 Gwei

Emitted Events:

137 TransparentUpgradeableProxy.0x20af7f3bbfe38132b8900ae295cd9c8d1914be7052d061a511f3f728dab18964( 0x20af7f3bbfe38132b8900ae295cd9c8d1914be7052d061a511f3f728dab18964, 0x000000000000000000000000a3a7b6f88361f48403514059f1f16c8e78d60eec, 0x00000000000000000000000009e9222e96e7b4ae2a407b98d48e330053351eee, 0x0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000021552 )
138 LinkToken.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000a3a7b6f88361f48403514059f1f16c8e78d60eec, 0x00000000000000000000000093a6898a1d1a985657cee210f813899bb1d573f2, 00000000000000000000000000000000000000000000054b4eab7e00e08a63ac )
139 TransparentUpgradeableProxy.0x891afe029c75c4f8c5855fc3480598bc5a53739344f6ae575bdb7ea2a79f56b3( 0x891afe029c75c4f8c5855fc3480598bc5a53739344f6ae575bdb7ea2a79f56b3, 0x00000000000000000000000093a6898a1d1a985657cee210f813899bb1d573f2, 0x00000000000000000000000093a6898a1d1a985657cee210f813899bb1d573f2, 0x0000000000000000000000000000000000000000000000000000000000006ff5, 000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca, 00000000000000000000000000000000000000000000054b4eab7e00e08a63ac )
140 TransparentUpgradeableProxy.0x2d9d115ef3e4a606d698913b1eae831a3cdfe20d9a83d48007b0526749c3d466( 0x2d9d115ef3e4a606d698913b1eae831a3cdfe20d9a83d48007b0526749c3d466, 0x0000000000000000000000000b9857ae2d4a3dbe74ffe1d7df045bb7f96e4840, 0x000000000000000000000000a3a7b6f88361f48403514059f1f16c8e78d60eec, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000124, 2e567b36000000000000000000000000514910771af9ca656af840dff83e8264, ecf986ca00000000000000000000000093a6898a1d1a985657cee210f813899b, b1d573f200000000000000000000000093a6898a1d1a985657cee210f813899b, b1d573f200000000000000000000000000000000000000000000054b4eab7e00, e08a63ac00000000000000000000000000000000000000000000000000000000, 000000a000000000000000000000000000000000000000000000000000000000, 0000006000000000000000000000000000000000000000000000000000000000, 00006ff500000000000000000000000000000000000000000000000000000000, 0000004000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
0x0B9857ae...7F96E4840
(Arbitrum: Outbox 4)
0x51491077...4EcF986CA
0x93A6898A...bB1D573F2
0.698689320595118636 Eth
Nonce: 29
0.697582373869111496 Eth
Nonce: 30
0.00110694672600714
(beaverbuild)
19.029290740669334036 Eth19.029447227393734036 Eth0.0001564867244

Execution Trace

TransparentUpgradeableProxy.08635a95( )
  • Outbox.executeTransaction( )
    • TransparentUpgradeableProxy.9e5d4c49( )
      • Bridge.executeCall( to=0xa3A7B6F88361F48403514059F1F16C8E78d60EeC, value=0, data=0x2E567B36000000000000000000000000514910771AF9CA656AF840DFF83E8264ECF986CA00000000000000000000000093A6898A1D1A985657CEE210F813899BB1D573F200000000000000000000000093A6898A1D1A985657CEE210F813899BB1D573F200000000000000000000000000000000000000000000054B4EAB7E00E08A63AC00000000000000000000000000000000000000000000000000000000000000A000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000006FF500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000 ) => ( success=True, returnData=0x )
        • TransparentUpgradeableProxy.2e567b36( )
          • L1ERC20Gateway.finalizeInboundTransfer( _token=0x514910771AF9Ca656af840dff83E8264EcF986CA, _from=0x93A6898A1d1a985657CeE210f813899bB1D573F2, _to=0x93A6898A1d1a985657CeE210f813899bB1D573F2, _amount=25001006982974650606508, _data=0x0000000000000000000000000000000000000000000000000000000000006FF500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000 )
            • TransparentUpgradeableProxy.STATICCALL( )
            • TransparentUpgradeableProxy.STATICCALL( )
            • TransparentUpgradeableProxy.STATICCALL( )
            • TransparentUpgradeableProxy.STATICCALL( )
            • LinkToken.transfer( _to=0x93A6898A1d1a985657CeE210f813899bB1D573F2, _value=25001006982974650606508 ) => ( success=True )
              executeTransaction[Outbox (ln:106)]
              File 1 of 8: TransparentUpgradeableProxy
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (proxy/transparent/TransparentUpgradeableProxy.sol)
              pragma solidity ^0.8.0;
              import "../ERC1967/ERC1967Proxy.sol";
              /**
               * @dev This contract implements a proxy that is upgradeable by an admin.
               *
               * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
               * clashing], which can potentially be used in an attack, this contract uses the
               * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
               * things that go hand in hand:
               *
               * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
               * that call matches one of the admin functions exposed by the proxy itself.
               * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
               * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
               * "admin cannot fallback to proxy target".
               *
               * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
               * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
               * to sudden errors when trying to call a function from the proxy implementation.
               *
               * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
               * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
               */
              contract TransparentUpgradeableProxy is ERC1967Proxy {
                  /**
                   * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
                   * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
                   */
                  constructor(
                      address _logic,
                      address admin_,
                      bytes memory _data
                  ) payable ERC1967Proxy(_logic, _data) {
                      assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
                      _changeAdmin(admin_);
                  }
                  /**
                   * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
                   */
                  modifier ifAdmin() {
                      if (msg.sender == _getAdmin()) {
                          _;
                      } else {
                          _fallback();
                      }
                  }
                  /**
                   * @dev Returns the current admin.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                   * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
                   */
                  function admin() external ifAdmin returns (address admin_) {
                      admin_ = _getAdmin();
                  }
                  /**
                   * @dev Returns the current implementation.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                   * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
                   */
                  function implementation() external ifAdmin returns (address implementation_) {
                      implementation_ = _implementation();
                  }
                  /**
                   * @dev Changes the admin of the proxy.
                   *
                   * Emits an {AdminChanged} event.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
                   */
                  function changeAdmin(address newAdmin) external virtual ifAdmin {
                      _changeAdmin(newAdmin);
                  }
                  /**
                   * @dev Upgrade the implementation of the proxy.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
                   */
                  function upgradeTo(address newImplementation) external ifAdmin {
                      _upgradeToAndCall(newImplementation, bytes(""), false);
                  }
                  /**
                   * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
                   * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
                   * proxied contract.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
                   */
                  function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {
                      _upgradeToAndCall(newImplementation, data, true);
                  }
                  /**
                   * @dev Returns the current admin.
                   */
                  function _admin() internal view virtual returns (address) {
                      return _getAdmin();
                  }
                  /**
                   * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
                   */
                  function _beforeFallback() internal virtual override {
                      require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
                      super._beforeFallback();
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (proxy/ERC1967/ERC1967Proxy.sol)
              pragma solidity ^0.8.0;
              import "../Proxy.sol";
              import "./ERC1967Upgrade.sol";
              /**
               * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
               * implementation address that can be changed. This address is stored in storage in the location specified by
               * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
               * implementation behind the proxy.
               */
              contract ERC1967Proxy is Proxy, ERC1967Upgrade {
                  /**
                   * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
                   *
                   * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
                   * function call, and allows initializating the storage of the proxy like a Solidity constructor.
                   */
                  constructor(address _logic, bytes memory _data) payable {
                      assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
                      _upgradeToAndCall(_logic, _data, false);
                  }
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function _implementation() internal view virtual override returns (address impl) {
                      return ERC1967Upgrade._getImplementation();
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.0) (proxy/Proxy.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
               * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
               * be specified by overriding the virtual {_implementation} function.
               *
               * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
               * different contract through the {_delegate} function.
               *
               * The success and return data of the delegated call will be returned back to the caller of the proxy.
               */
              abstract contract Proxy {
                  /**
                   * @dev Delegates the current call to `implementation`.
                   *
                   * This function does not return to its internal call site, it will return directly to the external caller.
                   */
                  function _delegate(address implementation) internal virtual {
                      assembly {
                          // Copy msg.data. We take full control of memory in this inline assembly
                          // block because it will not return to Solidity code. We overwrite the
                          // Solidity scratch pad at memory position 0.
                          calldatacopy(0, 0, calldatasize())
                          // Call the implementation.
                          // out and outsize are 0 because we don't know the size yet.
                          let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
                          // Copy the returned data.
                          returndatacopy(0, 0, returndatasize())
                          switch result
                          // delegatecall returns 0 on error.
                          case 0 {
                              revert(0, returndatasize())
                          }
                          default {
                              return(0, returndatasize())
                          }
                      }
                  }
                  /**
                   * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
                   * and {_fallback} should delegate.
                   */
                  function _implementation() internal view virtual returns (address);
                  /**
                   * @dev Delegates the current call to the address returned by `_implementation()`.
                   *
                   * This function does not return to its internall call site, it will return directly to the external caller.
                   */
                  function _fallback() internal virtual {
                      _beforeFallback();
                      _delegate(_implementation());
                  }
                  /**
                   * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
                   * function in the contract matches the call data.
                   */
                  fallback() external payable virtual {
                      _fallback();
                  }
                  /**
                   * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
                   * is empty.
                   */
                  receive() external payable virtual {
                      _fallback();
                  }
                  /**
                   * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
                   * call, or as part of the Solidity `fallback` or `receive` functions.
                   *
                   * If overriden should call `super._beforeFallback()`.
                   */
                  function _beforeFallback() internal virtual {}
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.0) (proxy/ERC1967/ERC1967Upgrade.sol)
              pragma solidity ^0.8.2;
              import "../beacon/IBeacon.sol";
              import "../../interfaces/draft-IERC1822.sol";
              import "../../utils/Address.sol";
              import "../../utils/StorageSlot.sol";
              /**
               * @dev This abstract contract provides getters and event emitting update functions for
               * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
               *
               * _Available since v4.1._
               *
               * @custom:oz-upgrades-unsafe-allow delegatecall
               */
              abstract contract ERC1967Upgrade {
                  // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
                  bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
                  /**
                   * @dev Storage slot with the address of the current implementation.
                   * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                   * validated in the constructor.
                   */
                  bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                  /**
                   * @dev Emitted when the implementation is upgraded.
                   */
                  event Upgraded(address indexed implementation);
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function _getImplementation() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                  }
                  /**
                   * @dev Stores a new address in the EIP1967 implementation slot.
                   */
                  function _setImplementation(address newImplementation) private {
                      require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                      StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                  }
                  /**
                   * @dev Perform implementation upgrade
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeTo(address newImplementation) internal {
                      _setImplementation(newImplementation);
                      emit Upgraded(newImplementation);
                  }
                  /**
                   * @dev Perform implementation upgrade with additional setup call.
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeToAndCall(
                      address newImplementation,
                      bytes memory data,
                      bool forceCall
                  ) internal {
                      _upgradeTo(newImplementation);
                      if (data.length > 0 || forceCall) {
                          Address.functionDelegateCall(newImplementation, data);
                      }
                  }
                  /**
                   * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeToAndCallUUPS(
                      address newImplementation,
                      bytes memory data,
                      bool forceCall
                  ) internal {
                      // Upgrades from old implementations will perform a rollback test. This test requires the new
                      // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
                      // this special case will break upgrade paths from old UUPS implementation to new ones.
                      if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
                          _setImplementation(newImplementation);
                      } else {
                          try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                              require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
                          } catch {
                              revert("ERC1967Upgrade: new implementation is not UUPS");
                          }
                          _upgradeToAndCall(newImplementation, data, forceCall);
                      }
                  }
                  /**
                   * @dev Storage slot with the admin of the contract.
                   * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
                   * validated in the constructor.
                   */
                  bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                  /**
                   * @dev Emitted when the admin account has changed.
                   */
                  event AdminChanged(address previousAdmin, address newAdmin);
                  /**
                   * @dev Returns the current admin.
                   */
                  function _getAdmin() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
                  }
                  /**
                   * @dev Stores a new address in the EIP1967 admin slot.
                   */
                  function _setAdmin(address newAdmin) private {
                      require(newAdmin != address(0), "ERC1967: new admin is the zero address");
                      StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
                  }
                  /**
                   * @dev Changes the admin of the proxy.
                   *
                   * Emits an {AdminChanged} event.
                   */
                  function _changeAdmin(address newAdmin) internal {
                      emit AdminChanged(_getAdmin(), newAdmin);
                      _setAdmin(newAdmin);
                  }
                  /**
                   * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
                   * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
                   */
                  bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
                  /**
                   * @dev Emitted when the beacon is upgraded.
                   */
                  event BeaconUpgraded(address indexed beacon);
                  /**
                   * @dev Returns the current beacon.
                   */
                  function _getBeacon() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
                  }
                  /**
                   * @dev Stores a new beacon in the EIP1967 beacon slot.
                   */
                  function _setBeacon(address newBeacon) private {
                      require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract");
                      require(
                          Address.isContract(IBeacon(newBeacon).implementation()),
                          "ERC1967: beacon implementation is not a contract"
                      );
                      StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
                  }
                  /**
                   * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
                   * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
                   *
                   * Emits a {BeaconUpgraded} event.
                   */
                  function _upgradeBeaconToAndCall(
                      address newBeacon,
                      bytes memory data,
                      bool forceCall
                  ) internal {
                      _setBeacon(newBeacon);
                      emit BeaconUpgraded(newBeacon);
                      if (data.length > 0 || forceCall) {
                          Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev This is the interface that {BeaconProxy} expects of its beacon.
               */
              interface IBeacon {
                  /**
                   * @dev Must return an address that can be used as a delegate call target.
                   *
                   * {BeaconProxy} will check that this address is a contract.
                   */
                  function implementation() external view returns (address);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
               * proxy whose upgrades are fully controlled by the current implementation.
               */
              interface IERC1822Proxiable {
                  /**
                   * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
                   * address.
                   *
                   * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
                   * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
                   * function revert if invoked through a proxy.
                   */
                  function proxiableUUID() external view returns (bytes32);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.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
                              assembly {
                                  let returndata_size := mload(returndata)
                                  revert(add(32, returndata), returndata_size)
                              }
                          } else {
                              revert(errorMessage);
                          }
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/StorageSlot.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Library for reading and writing primitive types to specific storage slots.
               *
               * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
               * This library helps with reading and writing to such slots without the need for inline assembly.
               *
               * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
               *
               * Example usage to set ERC1967 implementation slot:
               * ```
               * contract ERC1967 {
               *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
               *
               *     function _getImplementation() internal view returns (address) {
               *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
               *     }
               *
               *     function _setImplementation(address newImplementation) internal {
               *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
               *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
               *     }
               * }
               * ```
               *
               * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
               */
              library StorageSlot {
                  struct AddressSlot {
                      address value;
                  }
                  struct BooleanSlot {
                      bool value;
                  }
                  struct Bytes32Slot {
                      bytes32 value;
                  }
                  struct Uint256Slot {
                      uint256 value;
                  }
                  /**
                   * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                   */
                  function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                   */
                  function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                   */
                  function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                   */
                  function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
              }
              

              File 2 of 8: LinkToken
              pragma solidity ^0.4.16;
              
              
              /**
               * @title SafeMath
               * @dev Math operations with safety checks that throw on error
               */
              library SafeMath {
                function mul(uint256 a, uint256 b) internal constant returns (uint256) {
                  uint256 c = a * b;
                  assert(a == 0 || c / a == b);
                  return c;
                }
              
                function div(uint256 a, uint256 b) internal constant 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 constant returns (uint256) {
                  assert(b <= a);
                  return a - b;
                }
              
                function add(uint256 a, uint256 b) internal constant returns (uint256) {
                  uint256 c = a + b;
                  assert(c >= a);
                  return c;
                }
              }
              
              
              /**
               * @title ERC20Basic
               * @dev Simpler version of ERC20 interface
               * @dev see https://github.com/ethereum/EIPs/issues/179
               */
              contract ERC20Basic {
                uint256 public totalSupply;
                function balanceOf(address who) constant returns (uint256);
                function transfer(address to, uint256 value) returns (bool);
                event Transfer(address indexed from, address indexed to, uint256 value);
              }
              /**
               * @title ERC20 interface
               * @dev see https://github.com/ethereum/EIPs/issues/20
               */
              contract ERC20 is ERC20Basic {
                function allowance(address owner, address spender) constant returns (uint256);
                function transferFrom(address from, address to, uint256 value) returns (bool);
                function approve(address spender, uint256 value) returns (bool);
                event Approval(address indexed owner, address indexed spender, uint256 value);
              }
              
              contract ERC677 is ERC20 {
                function transferAndCall(address to, uint value, bytes data) returns (bool success);
              
                event Transfer(address indexed from, address indexed to, uint value, bytes data);
              }
              
              contract ERC677Receiver {
                function onTokenTransfer(address _sender, uint _value, bytes _data);
              }
              
              /**
               * @title Basic token
               * @dev Basic version of StandardToken, with no allowances. 
               */
              contract BasicToken is ERC20Basic {
                using SafeMath for uint256;
              
                mapping(address => uint256) balances;
              
                /**
                * @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, uint256 _value) returns (bool) {
                  balances[msg.sender] = balances[msg.sender].sub(_value);
                  balances[_to] = balances[_to].add(_value);
                  Transfer(msg.sender, _to, _value);
                  return true;
                }
              
                /**
                * @dev Gets the balance of the specified address.
                * @param _owner The address to query the the balance of. 
                * @return An uint256 representing the amount owned by the passed address.
                */
                function balanceOf(address _owner) constant returns (uint256 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 on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
               */
              contract StandardToken is ERC20, BasicToken {
              
                mapping (address => mapping (address => uint256)) allowed;
              
              
                /**
                 * @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 uint256 the amount of tokens to be transferred
                 */
                function transferFrom(address _from, address _to, uint256 _value) returns (bool) {
                  var _allowance = allowed[_from][msg.sender];
              
                  // Check is not needed because sub(_allowance, _value) will already throw if this condition is not met
                  // require (_value <= _allowance);
              
                  balances[_from] = balances[_from].sub(_value);
                  balances[_to] = balances[_to].add(_value);
                  allowed[_from][msg.sender] = _allowance.sub(_value);
                  Transfer(_from, _to, _value);
                  return true;
                }
              
                /**
                 * @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, uint256 _value) returns (bool) {
                  allowed[msg.sender][_spender] = _value;
                  Approval(msg.sender, _spender, _value);
                  return true;
                }
              
                /**
                 * @dev Function to check the amount of tokens that 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 uint256 specifying the amount of tokens still available for the spender.
                 */
                function allowance(address _owner, address _spender) constant returns (uint256 remaining) {
                  return allowed[_owner][_spender];
                }
                
                  /*
                 * approve should be called when allowed[_spender] == 0. To increment
                 * allowed value is better to use this function to avoid 2 calls (and wait until 
                 * the first transaction is mined)
                 * From MonolithDAO Token.sol
                 */
                function increaseApproval (address _spender, uint _addedValue) 
                  returns (bool success) {
                  allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue);
                  Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
                  return true;
                }
              
                function decreaseApproval (address _spender, uint _subtractedValue) 
                  returns (bool success) {
                  uint oldValue = allowed[msg.sender][_spender];
                  if (_subtractedValue > oldValue) {
                    allowed[msg.sender][_spender] = 0;
                  } else {
                    allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
                  }
                  Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
                  return true;
                }
              
              }
              
              contract ERC677Token is ERC677 {
              
                /**
                * @dev transfer token to a contract address with additional data if the recipient is a contact.
                * @param _to The address to transfer to.
                * @param _value The amount to be transferred.
                * @param _data The extra data to be passed to the receiving contract.
                */
                function transferAndCall(address _to, uint _value, bytes _data)
                  public
                  returns (bool success)
                {
                  super.transfer(_to, _value);
                  Transfer(msg.sender, _to, _value, _data);
                  if (isContract(_to)) {
                    contractFallback(_to, _value, _data);
                  }
                  return true;
                }
              
              
                // PRIVATE
              
                function contractFallback(address _to, uint _value, bytes _data)
                  private
                {
                  ERC677Receiver receiver = ERC677Receiver(_to);
                  receiver.onTokenTransfer(msg.sender, _value, _data);
                }
              
                function isContract(address _addr)
                  private
                  returns (bool hasCode)
                {
                  uint length;
                  assembly { length := extcodesize(_addr) }
                  return length > 0;
                }
              
              }
              
              contract LinkToken is StandardToken, ERC677Token {
              
                uint public constant totalSupply = 10**27;
                string public constant name = 'ChainLink Token';
                uint8 public constant decimals = 18;
                string public constant symbol = 'LINK';
              
                function LinkToken()
                  public
                {
                  balances[msg.sender] = totalSupply;
                }
              
                /**
                * @dev transfer token to a specified address with additional data if the recipient is a contract.
                * @param _to The address to transfer to.
                * @param _value The amount to be transferred.
                * @param _data The extra data to be passed to the receiving contract.
                */
                function transferAndCall(address _to, uint _value, bytes _data)
                  public
                  validRecipient(_to)
                  returns (bool success)
                {
                  return super.transferAndCall(_to, _value, _data);
                }
              
                /**
                * @dev transfer token to a specified address.
                * @param _to The address to transfer to.
                * @param _value The amount to be transferred.
                */
                function transfer(address _to, uint _value)
                  public
                  validRecipient(_to)
                  returns (bool success)
                {
                  return super.transfer(_to, _value);
                }
              
                /**
                 * @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, uint256 _value)
                  public
                  validRecipient(_spender)
                  returns (bool)
                {
                  return super.approve(_spender,  _value);
                }
              
                /**
                 * @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 uint256 the amount of tokens to be transferred
                 */
                function transferFrom(address _from, address _to, uint256 _value)
                  public
                  validRecipient(_to)
                  returns (bool)
                {
                  return super.transferFrom(_from, _to, _value);
                }
              
              
                // MODIFIERS
              
                modifier validRecipient(address _recipient) {
                  require(_recipient != address(0) && _recipient != address(this));
                  _;
                }
              
              }

              File 3 of 8: TransparentUpgradeableProxy
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.6.0 <0.8.0;
              import "./UpgradeableProxy.sol";
              /**
               * @dev This contract implements a proxy that is upgradeable by an admin.
               *
               * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
               * clashing], which can potentially be used in an attack, this contract uses the
               * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
               * things that go hand in hand:
               *
               * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
               * that call matches one of the admin functions exposed by the proxy itself.
               * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
               * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
               * "admin cannot fallback to proxy target".
               *
               * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
               * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
               * to sudden errors when trying to call a function from the proxy implementation.
               *
               * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
               * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
               */
              contract TransparentUpgradeableProxy is UpgradeableProxy {
                  /**
                   * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
                   * optionally initialized with `_data` as explained in {UpgradeableProxy-constructor}.
                   */
                  constructor(address _logic, address admin_, bytes memory _data) public payable UpgradeableProxy(_logic, _data) {
                      assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
                      _setAdmin(admin_);
                  }
                  /**
                   * @dev Emitted when the admin account has changed.
                   */
                  event AdminChanged(address previousAdmin, address newAdmin);
                  /**
                   * @dev Storage slot with the admin of the contract.
                   * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
                   * validated in the constructor.
                   */
                  bytes32 private constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                  /**
                   * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
                   */
                  modifier ifAdmin() {
                      if (msg.sender == _admin()) {
                          _;
                      } else {
                          _fallback();
                      }
                  }
                  /**
                   * @dev Returns the current admin.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                   * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
                   */
                  function admin() external ifAdmin returns (address admin_) {
                      admin_ = _admin();
                  }
                  /**
                   * @dev Returns the current implementation.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                   * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
                   */
                  function implementation() external ifAdmin returns (address implementation_) {
                      implementation_ = _implementation();
                  }
                  /**
                   * @dev Changes the admin of the proxy.
                   *
                   * Emits an {AdminChanged} event.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
                   */
                  function changeAdmin(address newAdmin) external virtual ifAdmin {
                      require(newAdmin != address(0), "TransparentUpgradeableProxy: new admin is the zero address");
                      emit AdminChanged(_admin(), newAdmin);
                      _setAdmin(newAdmin);
                  }
                  /**
                   * @dev Upgrade the implementation of the proxy.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
                   */
                  function upgradeTo(address newImplementation) external virtual ifAdmin {
                      _upgradeTo(newImplementation);
                  }
                  /**
                   * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
                   * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
                   * proxied contract.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
                   */
                  function upgradeToAndCall(address newImplementation, bytes calldata data) external payable virtual ifAdmin {
                      _upgradeTo(newImplementation);
                      Address.functionDelegateCall(newImplementation, data);
                  }
                  /**
                   * @dev Returns the current admin.
                   */
                  function _admin() internal view virtual returns (address adm) {
                      bytes32 slot = _ADMIN_SLOT;
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          adm := sload(slot)
                      }
                  }
                  /**
                   * @dev Stores a new address in the EIP1967 admin slot.
                   */
                  function _setAdmin(address newAdmin) private {
                      bytes32 slot = _ADMIN_SLOT;
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          sstore(slot, newAdmin)
                      }
                  }
                  /**
                   * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
                   */
                  function _beforeFallback() internal virtual override {
                      require(msg.sender != _admin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
                      super._beforeFallback();
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.6.0 <0.8.0;
              import "./Proxy.sol";
              import "../utils/Address.sol";
              /**
               * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
               * implementation address that can be changed. This address is stored in storage in the location specified by
               * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
               * implementation behind the proxy.
               *
               * Upgradeability is only provided internally through {_upgradeTo}. For an externally upgradeable proxy see
               * {TransparentUpgradeableProxy}.
               */
              contract UpgradeableProxy is Proxy {
                  /**
                   * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
                   *
                   * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
                   * function call, and allows initializating the storage of the proxy like a Solidity constructor.
                   */
                  constructor(address _logic, bytes memory _data) public payable {
                      assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
                      _setImplementation(_logic);
                      if(_data.length > 0) {
                          Address.functionDelegateCall(_logic, _data);
                      }
                  }
                  /**
                   * @dev Emitted when the implementation is upgraded.
                   */
                  event Upgraded(address indexed implementation);
                  /**
                   * @dev Storage slot with the address of the current implementation.
                   * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                   * validated in the constructor.
                   */
                  bytes32 private constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function _implementation() internal view virtual override returns (address impl) {
                      bytes32 slot = _IMPLEMENTATION_SLOT;
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          impl := sload(slot)
                      }
                  }
                  /**
                   * @dev Upgrades the proxy to a new implementation.
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeTo(address newImplementation) internal virtual {
                      _setImplementation(newImplementation);
                      emit Upgraded(newImplementation);
                  }
                  /**
                   * @dev Stores a new address in the EIP1967 implementation slot.
                   */
                  function _setImplementation(address newImplementation) private {
                      require(Address.isContract(newImplementation), "UpgradeableProxy: new implementation is not a contract");
                      bytes32 slot = _IMPLEMENTATION_SLOT;
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          sstore(slot, newImplementation)
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.6.0 <0.8.0;
              /**
               * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
               * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
               * be specified by overriding the virtual {_implementation} function.
               *
               * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
               * different contract through the {_delegate} function.
               *
               * The success and return data of the delegated call will be returned back to the caller of the proxy.
               */
              abstract contract Proxy {
                  /**
                   * @dev Delegates the current call to `implementation`.
                   *
                   * This function does not return to its internall call site, it will return directly to the external caller.
                   */
                  function _delegate(address implementation) internal virtual {
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          // Copy msg.data. We take full control of memory in this inline assembly
                          // block because it will not return to Solidity code. We overwrite the
                          // Solidity scratch pad at memory position 0.
                          calldatacopy(0, 0, calldatasize())
                          // Call the implementation.
                          // out and outsize are 0 because we don't know the size yet.
                          let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
                          // Copy the returned data.
                          returndatacopy(0, 0, returndatasize())
                          switch result
                          // delegatecall returns 0 on error.
                          case 0 { revert(0, returndatasize()) }
                          default { return(0, returndatasize()) }
                      }
                  }
                  /**
                   * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
                   * and {_fallback} should delegate.
                   */
                  function _implementation() internal view virtual returns (address);
                  /**
                   * @dev Delegates the current call to the address returned by `_implementation()`.
                   *
                   * This function does not return to its internall call site, it will return directly to the external caller.
                   */
                  function _fallback() internal virtual {
                      _beforeFallback();
                      _delegate(_implementation());
                  }
                  /**
                   * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
                   * function in the contract matches the call data.
                   */
                  fallback () external payable virtual {
                      _fallback();
                  }
                  /**
                   * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
                   * is empty.
                   */
                  receive () external payable virtual {
                      _fallback();
                  }
                  /**
                   * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
                   * call, or as part of the Solidity `fallback` or `receive` functions.
                   *
                   * If overriden should call `super._beforeFallback()`.
                   */
                  function _beforeFallback() internal virtual {
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.6.2 <0.8.0;
              /**
               * @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
                   * ====
                   */
                  function isContract(address account) internal view returns (bool) {
                      // This method relies on extcodesize, which returns 0 for contracts in
                      // construction, since the code is only stored at the end of the
                      // constructor execution.
                      uint256 size;
                      // solhint-disable-next-line no-inline-assembly
                      assembly { size := extcodesize(account) }
                      return size > 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");
                      // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                      (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");
                      // solhint-disable-next-line avoid-low-level-calls
                      (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");
                      // solhint-disable-next-line avoid-low-level-calls
                      (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");
                      // solhint-disable-next-line avoid-low-level-calls
                      (bool success, bytes memory returndata) = target.delegatecall(data);
                      return _verifyCallResult(success, returndata, errorMessage);
                  }
                  function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private 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
                              // solhint-disable-next-line no-inline-assembly
                              assembly {
                                  let returndata_size := mload(returndata)
                                  revert(add(32, returndata), returndata_size)
                              }
                          } else {
                              revert(errorMessage);
                          }
                      }
                  }
              }
              

              File 4 of 8: TransparentUpgradeableProxy
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (proxy/transparent/TransparentUpgradeableProxy.sol)
              pragma solidity ^0.8.0;
              import "../ERC1967/ERC1967Proxy.sol";
              /**
               * @dev This contract implements a proxy that is upgradeable by an admin.
               *
               * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
               * clashing], which can potentially be used in an attack, this contract uses the
               * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
               * things that go hand in hand:
               *
               * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
               * that call matches one of the admin functions exposed by the proxy itself.
               * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
               * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
               * "admin cannot fallback to proxy target".
               *
               * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
               * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
               * to sudden errors when trying to call a function from the proxy implementation.
               *
               * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
               * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
               */
              contract TransparentUpgradeableProxy is ERC1967Proxy {
                  /**
                   * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
                   * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
                   */
                  constructor(
                      address _logic,
                      address admin_,
                      bytes memory _data
                  ) payable ERC1967Proxy(_logic, _data) {
                      assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
                      _changeAdmin(admin_);
                  }
                  /**
                   * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
                   */
                  modifier ifAdmin() {
                      if (msg.sender == _getAdmin()) {
                          _;
                      } else {
                          _fallback();
                      }
                  }
                  /**
                   * @dev Returns the current admin.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                   * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
                   */
                  function admin() external ifAdmin returns (address admin_) {
                      admin_ = _getAdmin();
                  }
                  /**
                   * @dev Returns the current implementation.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                   * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
                   */
                  function implementation() external ifAdmin returns (address implementation_) {
                      implementation_ = _implementation();
                  }
                  /**
                   * @dev Changes the admin of the proxy.
                   *
                   * Emits an {AdminChanged} event.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
                   */
                  function changeAdmin(address newAdmin) external virtual ifAdmin {
                      _changeAdmin(newAdmin);
                  }
                  /**
                   * @dev Upgrade the implementation of the proxy.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
                   */
                  function upgradeTo(address newImplementation) external ifAdmin {
                      _upgradeToAndCall(newImplementation, bytes(""), false);
                  }
                  /**
                   * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
                   * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
                   * proxied contract.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
                   */
                  function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {
                      _upgradeToAndCall(newImplementation, data, true);
                  }
                  /**
                   * @dev Returns the current admin.
                   */
                  function _admin() internal view virtual returns (address) {
                      return _getAdmin();
                  }
                  /**
                   * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
                   */
                  function _beforeFallback() internal virtual override {
                      require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
                      super._beforeFallback();
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (proxy/ERC1967/ERC1967Proxy.sol)
              pragma solidity ^0.8.0;
              import "../Proxy.sol";
              import "./ERC1967Upgrade.sol";
              /**
               * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
               * implementation address that can be changed. This address is stored in storage in the location specified by
               * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
               * implementation behind the proxy.
               */
              contract ERC1967Proxy is Proxy, ERC1967Upgrade {
                  /**
                   * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
                   *
                   * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
                   * function call, and allows initializating the storage of the proxy like a Solidity constructor.
                   */
                  constructor(address _logic, bytes memory _data) payable {
                      assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
                      _upgradeToAndCall(_logic, _data, false);
                  }
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function _implementation() internal view virtual override returns (address impl) {
                      return ERC1967Upgrade._getImplementation();
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.0) (proxy/Proxy.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
               * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
               * be specified by overriding the virtual {_implementation} function.
               *
               * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
               * different contract through the {_delegate} function.
               *
               * The success and return data of the delegated call will be returned back to the caller of the proxy.
               */
              abstract contract Proxy {
                  /**
                   * @dev Delegates the current call to `implementation`.
                   *
                   * This function does not return to its internal call site, it will return directly to the external caller.
                   */
                  function _delegate(address implementation) internal virtual {
                      assembly {
                          // Copy msg.data. We take full control of memory in this inline assembly
                          // block because it will not return to Solidity code. We overwrite the
                          // Solidity scratch pad at memory position 0.
                          calldatacopy(0, 0, calldatasize())
                          // Call the implementation.
                          // out and outsize are 0 because we don't know the size yet.
                          let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
                          // Copy the returned data.
                          returndatacopy(0, 0, returndatasize())
                          switch result
                          // delegatecall returns 0 on error.
                          case 0 {
                              revert(0, returndatasize())
                          }
                          default {
                              return(0, returndatasize())
                          }
                      }
                  }
                  /**
                   * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
                   * and {_fallback} should delegate.
                   */
                  function _implementation() internal view virtual returns (address);
                  /**
                   * @dev Delegates the current call to the address returned by `_implementation()`.
                   *
                   * This function does not return to its internall call site, it will return directly to the external caller.
                   */
                  function _fallback() internal virtual {
                      _beforeFallback();
                      _delegate(_implementation());
                  }
                  /**
                   * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
                   * function in the contract matches the call data.
                   */
                  fallback() external payable virtual {
                      _fallback();
                  }
                  /**
                   * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
                   * is empty.
                   */
                  receive() external payable virtual {
                      _fallback();
                  }
                  /**
                   * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
                   * call, or as part of the Solidity `fallback` or `receive` functions.
                   *
                   * If overriden should call `super._beforeFallback()`.
                   */
                  function _beforeFallback() internal virtual {}
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.0) (proxy/ERC1967/ERC1967Upgrade.sol)
              pragma solidity ^0.8.2;
              import "../beacon/IBeacon.sol";
              import "../../interfaces/draft-IERC1822.sol";
              import "../../utils/Address.sol";
              import "../../utils/StorageSlot.sol";
              /**
               * @dev This abstract contract provides getters and event emitting update functions for
               * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
               *
               * _Available since v4.1._
               *
               * @custom:oz-upgrades-unsafe-allow delegatecall
               */
              abstract contract ERC1967Upgrade {
                  // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
                  bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
                  /**
                   * @dev Storage slot with the address of the current implementation.
                   * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                   * validated in the constructor.
                   */
                  bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                  /**
                   * @dev Emitted when the implementation is upgraded.
                   */
                  event Upgraded(address indexed implementation);
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function _getImplementation() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                  }
                  /**
                   * @dev Stores a new address in the EIP1967 implementation slot.
                   */
                  function _setImplementation(address newImplementation) private {
                      require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                      StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                  }
                  /**
                   * @dev Perform implementation upgrade
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeTo(address newImplementation) internal {
                      _setImplementation(newImplementation);
                      emit Upgraded(newImplementation);
                  }
                  /**
                   * @dev Perform implementation upgrade with additional setup call.
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeToAndCall(
                      address newImplementation,
                      bytes memory data,
                      bool forceCall
                  ) internal {
                      _upgradeTo(newImplementation);
                      if (data.length > 0 || forceCall) {
                          Address.functionDelegateCall(newImplementation, data);
                      }
                  }
                  /**
                   * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeToAndCallUUPS(
                      address newImplementation,
                      bytes memory data,
                      bool forceCall
                  ) internal {
                      // Upgrades from old implementations will perform a rollback test. This test requires the new
                      // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
                      // this special case will break upgrade paths from old UUPS implementation to new ones.
                      if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
                          _setImplementation(newImplementation);
                      } else {
                          try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                              require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
                          } catch {
                              revert("ERC1967Upgrade: new implementation is not UUPS");
                          }
                          _upgradeToAndCall(newImplementation, data, forceCall);
                      }
                  }
                  /**
                   * @dev Storage slot with the admin of the contract.
                   * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
                   * validated in the constructor.
                   */
                  bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                  /**
                   * @dev Emitted when the admin account has changed.
                   */
                  event AdminChanged(address previousAdmin, address newAdmin);
                  /**
                   * @dev Returns the current admin.
                   */
                  function _getAdmin() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
                  }
                  /**
                   * @dev Stores a new address in the EIP1967 admin slot.
                   */
                  function _setAdmin(address newAdmin) private {
                      require(newAdmin != address(0), "ERC1967: new admin is the zero address");
                      StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
                  }
                  /**
                   * @dev Changes the admin of the proxy.
                   *
                   * Emits an {AdminChanged} event.
                   */
                  function _changeAdmin(address newAdmin) internal {
                      emit AdminChanged(_getAdmin(), newAdmin);
                      _setAdmin(newAdmin);
                  }
                  /**
                   * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
                   * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
                   */
                  bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
                  /**
                   * @dev Emitted when the beacon is upgraded.
                   */
                  event BeaconUpgraded(address indexed beacon);
                  /**
                   * @dev Returns the current beacon.
                   */
                  function _getBeacon() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
                  }
                  /**
                   * @dev Stores a new beacon in the EIP1967 beacon slot.
                   */
                  function _setBeacon(address newBeacon) private {
                      require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract");
                      require(
                          Address.isContract(IBeacon(newBeacon).implementation()),
                          "ERC1967: beacon implementation is not a contract"
                      );
                      StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
                  }
                  /**
                   * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
                   * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
                   *
                   * Emits a {BeaconUpgraded} event.
                   */
                  function _upgradeBeaconToAndCall(
                      address newBeacon,
                      bytes memory data,
                      bool forceCall
                  ) internal {
                      _setBeacon(newBeacon);
                      emit BeaconUpgraded(newBeacon);
                      if (data.length > 0 || forceCall) {
                          Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev This is the interface that {BeaconProxy} expects of its beacon.
               */
              interface IBeacon {
                  /**
                   * @dev Must return an address that can be used as a delegate call target.
                   *
                   * {BeaconProxy} will check that this address is a contract.
                   */
                  function implementation() external view returns (address);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
               * proxy whose upgrades are fully controlled by the current implementation.
               */
              interface IERC1822Proxiable {
                  /**
                   * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
                   * address.
                   *
                   * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
                   * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
                   * function revert if invoked through a proxy.
                   */
                  function proxiableUUID() external view returns (bytes32);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.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
                              assembly {
                                  let returndata_size := mload(returndata)
                                  revert(add(32, returndata), returndata_size)
                              }
                          } else {
                              revert(errorMessage);
                          }
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/StorageSlot.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Library for reading and writing primitive types to specific storage slots.
               *
               * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
               * This library helps with reading and writing to such slots without the need for inline assembly.
               *
               * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
               *
               * Example usage to set ERC1967 implementation slot:
               * ```
               * contract ERC1967 {
               *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
               *
               *     function _getImplementation() internal view returns (address) {
               *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
               *     }
               *
               *     function _setImplementation(address newImplementation) internal {
               *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
               *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
               *     }
               * }
               * ```
               *
               * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
               */
              library StorageSlot {
                  struct AddressSlot {
                      address value;
                  }
                  struct BooleanSlot {
                      bool value;
                  }
                  struct Bytes32Slot {
                      bytes32 value;
                  }
                  struct Uint256Slot {
                      uint256 value;
                  }
                  /**
                   * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                   */
                  function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                   */
                  function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                   */
                  function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                   */
                  function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
              }
              

              File 5 of 8: Outbox
              // Copyright 2021-2022, Offchain Labs, Inc.
              // For license information, see https://github.com/nitro/blob/master/LICENSE
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity ^0.8.4;
              import {
                  AlreadyInit,
                  NotRollup,
                  ProofTooLong,
                  PathNotMinimal,
                  UnknownRoot,
                  AlreadySpent,
                  BridgeCallFailed,
                  HadZeroInit
              } from "../libraries/Error.sol";
              import "./IBridge.sol";
              import "./IOutbox.sol";
              import "../libraries/MerkleLib.sol";
              import "../libraries/DelegateCallAware.sol";
              /// @dev this error is thrown since certain functions are only expected to be used in simulations, not in actual txs
              error SimulationOnlyEntrypoint();
              contract Outbox is DelegateCallAware, IOutbox {
                  address public rollup; // the rollup contract
                  IBridge public bridge; // the bridge contract
                  mapping(uint256 => bytes32) public spent; // packed spent bitmap
                  mapping(bytes32 => bytes32) public roots; // maps root hashes => L2 block hash
                  struct L2ToL1Context {
                      uint128 l2Block;
                      uint128 l1Block;
                      uint128 timestamp;
                      bytes32 outputId;
                      address sender;
                  }
                  // Note, these variables are set and then wiped during a single transaction.
                  // Therefore their values don't need to be maintained, and their slots will
                  // be empty outside of transactions
                  L2ToL1Context internal context;
                  // default context values to be used in storage instead of zero, to save on storage refunds
                  // it is assumed that arb-os never assigns these values to a valid leaf to be redeemed
                  uint128 private constant L2BLOCK_DEFAULT_CONTEXT = type(uint128).max;
                  uint128 private constant L1BLOCK_DEFAULT_CONTEXT = type(uint128).max;
                  uint128 private constant TIMESTAMP_DEFAULT_CONTEXT = type(uint128).max;
                  bytes32 private constant OUTPUTID_DEFAULT_CONTEXT = bytes32(type(uint256).max);
                  address private constant SENDER_DEFAULT_CONTEXT = address(type(uint160).max);
                  uint128 public constant OUTBOX_VERSION = 2;
                  function initialize(IBridge _bridge) external onlyDelegated {
                      if (address(_bridge) == address(0)) revert HadZeroInit();
                      if (address(bridge) != address(0)) revert AlreadyInit();
                      // address zero is returned if no context is set, but the values used in storage
                      // are non-zero to save users some gas (as storage refunds are usually maxed out)
                      // EIP-1153 would help here
                      context = L2ToL1Context({
                          l2Block: L2BLOCK_DEFAULT_CONTEXT,
                          l1Block: L1BLOCK_DEFAULT_CONTEXT,
                          timestamp: TIMESTAMP_DEFAULT_CONTEXT,
                          outputId: OUTPUTID_DEFAULT_CONTEXT,
                          sender: SENDER_DEFAULT_CONTEXT
                      });
                      bridge = _bridge;
                      rollup = address(_bridge.rollup());
                  }
                  function updateSendRoot(bytes32 root, bytes32 l2BlockHash) external {
                      if (msg.sender != rollup) revert NotRollup(msg.sender, rollup);
                      roots[root] = l2BlockHash;
                      emit SendRootUpdated(root, l2BlockHash);
                  }
                  /// @inheritdoc IOutbox
                  function l2ToL1Sender() external view returns (address) {
                      address sender = context.sender;
                      // we don't return the default context value to avoid a breaking change in the API
                      if (sender == SENDER_DEFAULT_CONTEXT) return address(0);
                      return sender;
                  }
                  /// @inheritdoc IOutbox
                  function l2ToL1Block() external view returns (uint256) {
                      uint128 l2Block = context.l2Block;
                      // we don't return the default context value to avoid a breaking change in the API
                      if (l2Block == L1BLOCK_DEFAULT_CONTEXT) return uint256(0);
                      return uint256(l2Block);
                  }
                  /// @inheritdoc IOutbox
                  function l2ToL1EthBlock() external view returns (uint256) {
                      uint128 l1Block = context.l1Block;
                      // we don't return the default context value to avoid a breaking change in the API
                      if (l1Block == L1BLOCK_DEFAULT_CONTEXT) return uint256(0);
                      return uint256(l1Block);
                  }
                  /// @inheritdoc IOutbox
                  function l2ToL1Timestamp() external view returns (uint256) {
                      uint128 timestamp = context.timestamp;
                      // we don't return the default context value to avoid a breaking change in the API
                      if (timestamp == TIMESTAMP_DEFAULT_CONTEXT) return uint256(0);
                      return uint256(timestamp);
                  }
                  /// @notice batch number is deprecated and now always returns 0
                  function l2ToL1BatchNum() external pure returns (uint256) {
                      return 0;
                  }
                  /// @inheritdoc IOutbox
                  function l2ToL1OutputId() external view returns (bytes32) {
                      bytes32 outputId = context.outputId;
                      // we don't return the default context value to avoid a breaking change in the API
                      if (outputId == OUTPUTID_DEFAULT_CONTEXT) return bytes32(0);
                      return outputId;
                  }
                  /// @inheritdoc IOutbox
                  function executeTransaction(
                      bytes32[] calldata proof,
                      uint256 index,
                      address l2Sender,
                      address to,
                      uint256 l2Block,
                      uint256 l1Block,
                      uint256 l2Timestamp,
                      uint256 value,
                      bytes calldata data
                  ) external {
                      bytes32 userTx = calculateItemHash(
                          l2Sender,
                          to,
                          l2Block,
                          l1Block,
                          l2Timestamp,
                          value,
                          data
                      );
                      recordOutputAsSpent(proof, index, userTx);
                      executeTransactionImpl(index, l2Sender, to, l2Block, l1Block, l2Timestamp, value, data);
                  }
                  /// @inheritdoc IOutbox
                  function executeTransactionSimulation(
                      uint256 index,
                      address l2Sender,
                      address to,
                      uint256 l2Block,
                      uint256 l1Block,
                      uint256 l2Timestamp,
                      uint256 value,
                      bytes calldata data
                  ) external {
                      if (msg.sender != address(0)) revert SimulationOnlyEntrypoint();
                      executeTransactionImpl(index, l2Sender, to, l2Block, l1Block, l2Timestamp, value, data);
                  }
                  function executeTransactionImpl(
                      uint256 outputId,
                      address l2Sender,
                      address to,
                      uint256 l2Block,
                      uint256 l1Block,
                      uint256 l2Timestamp,
                      uint256 value,
                      bytes calldata data
                  ) internal {
                      emit OutBoxTransactionExecuted(to, l2Sender, 0, outputId);
                      // we temporarily store the previous values so the outbox can naturally
                      // unwind itself when there are nested calls to `executeTransaction`
                      L2ToL1Context memory prevContext = context;
                      context = L2ToL1Context({
                          sender: l2Sender,
                          l2Block: uint128(l2Block),
                          l1Block: uint128(l1Block),
                          timestamp: uint128(l2Timestamp),
                          outputId: bytes32(outputId)
                      });
                      // set and reset vars around execution so they remain valid during call
                      executeBridgeCall(to, value, data);
                      context = prevContext;
                  }
                  function _calcSpentIndexOffset(uint256 index)
                      internal
                      view
                      returns (
                          uint256,
                          uint256,
                          bytes32
                      )
                  {
                      uint256 spentIndex = index / 255; // Note: Reserves the MSB.
                      uint256 bitOffset = index % 255;
                      bytes32 replay = spent[spentIndex];
                      return (spentIndex, bitOffset, replay);
                  }
                  function _isSpent(uint256 bitOffset, bytes32 replay) internal pure returns (bool) {
                      return ((replay >> bitOffset) & bytes32(uint256(1))) != bytes32(0);
                  }
                  /// @inheritdoc IOutbox
                  function isSpent(uint256 index) external view returns (bool) {
                      (, uint256 bitOffset, bytes32 replay) = _calcSpentIndexOffset(index);
                      return _isSpent(bitOffset, replay);
                  }
                  function recordOutputAsSpent(
                      bytes32[] memory proof,
                      uint256 index,
                      bytes32 item
                  ) internal {
                      if (proof.length >= 256) revert ProofTooLong(proof.length);
                      if (index >= 2**proof.length) revert PathNotMinimal(index, 2**proof.length);
                      // Hash the leaf an extra time to prove it's a leaf
                      bytes32 calcRoot = calculateMerkleRoot(proof, index, item);
                      if (roots[calcRoot] == bytes32(0)) revert UnknownRoot(calcRoot);
                      (uint256 spentIndex, uint256 bitOffset, bytes32 replay) = _calcSpentIndexOffset(index);
                      if (_isSpent(bitOffset, replay)) revert AlreadySpent(index);
                      spent[spentIndex] = (replay | bytes32(1 << bitOffset));
                  }
                  function executeBridgeCall(
                      address to,
                      uint256 value,
                      bytes memory data
                  ) internal {
                      (bool success, bytes memory returndata) = bridge.executeCall(to, value, data);
                      if (!success) {
                          if (returndata.length > 0) {
                              // solhint-disable-next-line no-inline-assembly
                              assembly {
                                  let returndata_size := mload(returndata)
                                  revert(add(32, returndata), returndata_size)
                              }
                          } else {
                              revert BridgeCallFailed();
                          }
                      }
                  }
                  function calculateItemHash(
                      address l2Sender,
                      address to,
                      uint256 l2Block,
                      uint256 l1Block,
                      uint256 l2Timestamp,
                      uint256 value,
                      bytes calldata data
                  ) public pure returns (bytes32) {
                      return
                          keccak256(abi.encodePacked(l2Sender, to, l2Block, l1Block, l2Timestamp, value, data));
                  }
                  function calculateMerkleRoot(
                      bytes32[] memory proof,
                      uint256 path,
                      bytes32 item
                  ) public pure returns (bytes32) {
                      return MerkleLib.calculateRoot(proof, path, keccak256(abi.encodePacked(item)));
                  }
              }
              // Copyright 2021-2022, Offchain Labs, Inc.
              // For license information, see https://github.com/nitro/blob/master/LICENSE
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity ^0.8.4;
              /// @dev Init was already called
              error AlreadyInit();
              /// Init was called with param set to zero that must be nonzero
              error HadZeroInit();
              /// @dev Thrown when non owner tries to access an only-owner function
              /// @param sender The msg.sender who is not the owner
              /// @param owner The owner address
              error NotOwner(address sender, address owner);
              /// @dev Thrown when an address that is not the rollup tries to call an only-rollup function
              /// @param sender The sender who is not the rollup
              /// @param rollup The rollup address authorized to call this function
              error NotRollup(address sender, address rollup);
              /// @dev Thrown when the contract was not called directly from the origin ie msg.sender != tx.origin
              error NotOrigin();
              /// @dev Provided data was too large
              /// @param dataLength The length of the data that is too large
              /// @param maxDataLength The max length the data can be
              error DataTooLarge(uint256 dataLength, uint256 maxDataLength);
              /// @dev The provided is not a contract and was expected to be
              /// @param addr The adddress in question
              error NotContract(address addr);
              /// @dev The merkle proof provided was too long
              /// @param actualLength The length of the merkle proof provided
              /// @param maxProofLength The max length a merkle proof can have
              error MerkleProofTooLong(uint256 actualLength, uint256 maxProofLength);
              /// @dev Thrown when an un-authorized address tries to access an admin function
              /// @param sender The un-authorized sender
              /// @param rollup The rollup, which would be authorized
              /// @param owner The rollup's owner, which would be authorized
              error NotRollupOrOwner(address sender, address rollup, address owner);
              // Bridge Errors
              /// @dev Thrown when an un-authorized address tries to access an only-inbox function
              /// @param sender The un-authorized sender
              error NotDelayedInbox(address sender);
              /// @dev Thrown when an un-authorized address tries to access an only-sequencer-inbox function
              /// @param sender The un-authorized sender
              error NotSequencerInbox(address sender);
              /// @dev Thrown when an un-authorized address tries to access an only-outbox function
              /// @param sender The un-authorized sender
              error NotOutbox(address sender);
              /// @dev the provided outbox address isn't valid
              /// @param outbox address of outbox being set
              error InvalidOutboxSet(address outbox);
              // Inbox Errors
              /// @dev The contract is paused, so cannot be paused
              error AlreadyPaused();
              /// @dev The contract is unpaused, so cannot be unpaused
              error AlreadyUnpaused();
              /// @dev The contract is paused
              error Paused();
              /// @dev msg.value sent to the inbox isn't high enough
              error InsufficientValue(uint256 expected, uint256 actual);
              /// @dev submission cost provided isn't enough to create retryable ticket
              error InsufficientSubmissionCost(uint256 expected, uint256 actual);
              /// @dev address not allowed to interact with the given contract
              error NotAllowedOrigin(address origin);
              /// @dev used to convey retryable tx data in eth calls without requiring a tx trace
              /// this follows a pattern similar to EIP-3668 where reverts surface call information
              error RetryableData(
                  address from,
                  address to,
                  uint256 l2CallValue,
                  uint256 deposit,
                  uint256 maxSubmissionCost,
                  address excessFeeRefundAddress,
                  address callValueRefundAddress,
                  uint256 gasLimit,
                  uint256 maxFeePerGas,
                  bytes data
              );
              // Outbox Errors
              /// @dev The provided proof was too long
              /// @param proofLength The length of the too-long proof
              error ProofTooLong(uint256 proofLength);
              /// @dev The output index was greater than the maximum
              /// @param index The output index
              /// @param maxIndex The max the index could be
              error PathNotMinimal(uint256 index, uint256 maxIndex);
              /// @dev The calculated root does not exist
              /// @param root The calculated root
              error UnknownRoot(bytes32 root);
              /// @dev The record has already been spent
              /// @param index The index of the spent record
              error AlreadySpent(uint256 index);
              /// @dev A call to the bridge failed with no return data
              error BridgeCallFailed();
              // Sequencer Inbox Errors
              /// @dev Thrown when someone attempts to read fewer messages than have already been read
              error DelayedBackwards();
              /// @dev Thrown when someone attempts to read more messages than exist
              error DelayedTooFar();
              /// @dev Force include can only read messages more blocks old than the delay period
              error ForceIncludeBlockTooSoon();
              /// @dev Force include can only read messages more seconds old than the delay period
              error ForceIncludeTimeTooSoon();
              /// @dev The message provided did not match the hash in the delayed inbox
              error IncorrectMessagePreimage();
              /// @dev This can only be called by the batch poster
              error NotBatchPoster();
              /// @dev The sequence number provided to this message was inconsistent with the number of batches already included
              error BadSequencerNumber(uint256 stored, uint256 received);
              /// @dev The batch data has the inbox authenticated bit set, but the batch data was not authenticated by the inbox
              error DataNotAuthenticated();
              /// @dev Tried to create an already valid Data Availability Service keyset
              error AlreadyValidDASKeyset(bytes32);
              /// @dev Tried to use or invalidate an already invalid Data Availability Service keyset
              error NoSuchKeyset(bytes32);
              // Copyright 2021-2022, Offchain Labs, Inc.
              // For license information, see https://github.com/nitro/blob/master/LICENSE
              // SPDX-License-Identifier: BUSL-1.1
              // solhint-disable-next-line compiler-version
              pragma solidity >=0.6.9 <0.9.0;
              import "./IOwnable.sol";
              interface IBridge {
                  event MessageDelivered(
                      uint256 indexed messageIndex,
                      bytes32 indexed beforeInboxAcc,
                      address inbox,
                      uint8 kind,
                      address sender,
                      bytes32 messageDataHash,
                      uint256 baseFeeL1,
                      uint64 timestamp
                  );
                  event BridgeCallTriggered(
                      address indexed outbox,
                      address indexed to,
                      uint256 value,
                      bytes data
                  );
                  event InboxToggle(address indexed inbox, bool enabled);
                  event OutboxToggle(address indexed outbox, bool enabled);
                  event SequencerInboxUpdated(address newSequencerInbox);
                  function allowedDelayedInboxList(uint256) external returns (address);
                  function allowedOutboxList(uint256) external returns (address);
                  /// @dev Accumulator for delayed inbox messages; tail represents hash of the current state; each element represents the inclusion of a new message.
                  function delayedInboxAccs(uint256) external view returns (bytes32);
                  /// @dev Accumulator for sequencer inbox messages; tail represents hash of the current state; each element represents the inclusion of a new message.
                  function sequencerInboxAccs(uint256) external view returns (bytes32);
                  function rollup() external view returns (IOwnable);
                  function sequencerInbox() external view returns (address);
                  function activeOutbox() external view returns (address);
                  function allowedDelayedInboxes(address inbox) external view returns (bool);
                  function allowedOutboxes(address outbox) external view returns (bool);
                  /**
                   * @dev Enqueue a message in the delayed inbox accumulator.
                   *      These messages are later sequenced in the SequencerInbox, either
                   *      by the sequencer as part of a normal batch, or by force inclusion.
                   */
                  function enqueueDelayedMessage(
                      uint8 kind,
                      address sender,
                      bytes32 messageDataHash
                  ) external payable returns (uint256);
                  function executeCall(
                      address to,
                      uint256 value,
                      bytes calldata data
                  ) external returns (bool success, bytes memory returnData);
                  function delayedMessageCount() external view returns (uint256);
                  function sequencerMessageCount() external view returns (uint256);
                  // ---------- onlySequencerInbox functions ----------
                  function enqueueSequencerMessage(bytes32 dataHash, uint256 afterDelayedMessagesRead)
                      external
                      returns (
                          uint256 seqMessageIndex,
                          bytes32 beforeAcc,
                          bytes32 delayedAcc,
                          bytes32 acc
                      );
                  /**
                   * @dev Allows the sequencer inbox to submit a delayed message of the batchPostingReport type
                   *      This is done through a separate function entrypoint instead of allowing the sequencer inbox
                   *      to call `enqueueDelayedMessage` to avoid the gas overhead of an extra SLOAD in either
                   *      every delayed inbox or every sequencer inbox call.
                   */
                  function submitBatchSpendingReport(address batchPoster, bytes32 dataHash)
                      external
                      returns (uint256 msgNum);
                  // ---------- onlyRollupOrOwner functions ----------
                  function setSequencerInbox(address _sequencerInbox) external;
                  function setDelayedInbox(address inbox, bool enabled) external;
                  function setOutbox(address inbox, bool enabled) external;
                  // ---------- initializer ----------
                  function initialize(IOwnable rollup_) external;
              }
              // Copyright 2021-2022, Offchain Labs, Inc.
              // For license information, see https://github.com/nitro/blob/master/LICENSE
              // SPDX-License-Identifier: BUSL-1.1
              // solhint-disable-next-line compiler-version
              pragma solidity >=0.6.9 <0.9.0;
              import "./IBridge.sol";
              interface IOutbox {
                  event SendRootUpdated(bytes32 indexed blockHash, bytes32 indexed outputRoot);
                  event OutBoxTransactionExecuted(
                      address indexed to,
                      address indexed l2Sender,
                      uint256 indexed zero,
                      uint256 transactionIndex
                  );
                  function rollup() external view returns (address); // the rollup contract
                  function bridge() external view returns (IBridge); // the bridge contract
                  function spent(uint256) external view returns (bytes32); // packed spent bitmap
                  function roots(bytes32) external view returns (bytes32); // maps root hashes => L2 block hash
                  // solhint-disable-next-line func-name-mixedcase
                  function OUTBOX_VERSION() external view returns (uint128); // the outbox version
                  function updateSendRoot(bytes32 sendRoot, bytes32 l2BlockHash) external;
                  /// @notice When l2ToL1Sender returns a nonzero address, the message was originated by an L2 account
                  ///         When the return value is zero, that means this is a system message
                  /// @dev the l2ToL1Sender behaves as the tx.origin, the msg.sender should be validated to protect against reentrancies
                  function l2ToL1Sender() external view returns (address);
                  /// @return l2Block return L2 block when the L2 tx was initiated or 0 if no L2 to L1 transaction is active
                  function l2ToL1Block() external view returns (uint256);
                  /// @return l1Block return L1 block when the L2 tx was initiated or 0 if no L2 to L1 transaction is active
                  function l2ToL1EthBlock() external view returns (uint256);
                  /// @return timestamp return L2 timestamp when the L2 tx was initiated or 0 if no L2 to L1 transaction is active
                  function l2ToL1Timestamp() external view returns (uint256);
                  /// @return outputId returns the unique output identifier of the L2 to L1 tx or 0 if no L2 to L1 transaction is active
                  function l2ToL1OutputId() external view returns (bytes32);
                  /**
                   * @notice Executes a messages in an Outbox entry.
                   * @dev Reverts if dispute period hasn't expired, since the outbox entry
                   *      is only created once the rollup confirms the respective assertion.
                   * @dev it is not possible to execute any L2-to-L1 transaction which contains data
                   *      to a contract address without any code (as enforced by the Bridge contract).
                   * @param proof Merkle proof of message inclusion in send root
                   * @param index Merkle path to message
                   * @param l2Sender sender if original message (i.e., caller of ArbSys.sendTxToL1)
                   * @param to destination address for L1 contract call
                   * @param l2Block l2 block number at which sendTxToL1 call was made
                   * @param l1Block l1 block number at which sendTxToL1 call was made
                   * @param l2Timestamp l2 Timestamp at which sendTxToL1 call was made
                   * @param value wei in L1 message
                   * @param data abi-encoded L1 message data
                   */
                  function executeTransaction(
                      bytes32[] calldata proof,
                      uint256 index,
                      address l2Sender,
                      address to,
                      uint256 l2Block,
                      uint256 l1Block,
                      uint256 l2Timestamp,
                      uint256 value,
                      bytes calldata data
                  ) external;
                  /**
                   *  @dev function used to simulate the result of a particular function call from the outbox
                   *       it is useful for things such as gas estimates. This function includes all costs except for
                   *       proof validation (which can be considered offchain as a somewhat of a fixed cost - it's
                   *       not really a fixed cost, but can be treated as so with a fixed overhead for gas estimation).
                   *       We can't include the cost of proof validation since this is intended to be used to simulate txs
                   *       that are included in yet-to-be confirmed merkle roots. The simulation entrypoint could instead pretend
                   *       to confirm a pending merkle root, but that would be less pratical for integrating with tooling.
                   *       It is only possible to trigger it when the msg sender is address zero, which should be impossible
                   *       unless under simulation in an eth_call or eth_estimateGas
                   */
                  function executeTransactionSimulation(
                      uint256 index,
                      address l2Sender,
                      address to,
                      uint256 l2Block,
                      uint256 l1Block,
                      uint256 l2Timestamp,
                      uint256 value,
                      bytes calldata data
                  ) external;
                  /**
                   * @param index Merkle path to message
                   * @return true if the message has been spent
                   */
                  function isSpent(uint256 index) external view returns (bool);
                  function calculateItemHash(
                      address l2Sender,
                      address to,
                      uint256 l2Block,
                      uint256 l1Block,
                      uint256 l2Timestamp,
                      uint256 value,
                      bytes calldata data
                  ) external pure returns (bytes32);
                  function calculateMerkleRoot(
                      bytes32[] memory proof,
                      uint256 path,
                      bytes32 item
                  ) external pure returns (bytes32);
              }
              // Copyright 2021-2022, Offchain Labs, Inc.
              // For license information, see https://github.com/nitro/blob/master/LICENSE
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity ^0.8.4;
              import {MerkleProofTooLong} from "./Error.sol";
              library MerkleLib {
                  function generateRoot(bytes32[] memory _hashes) internal pure returns (bytes32) {
                      bytes32[] memory prevLayer = _hashes;
                      while (prevLayer.length > 1) {
                          bytes32[] memory nextLayer = new bytes32[]((prevLayer.length + 1) / 2);
                          for (uint256 i = 0; i < nextLayer.length; i++) {
                              if (2 * i + 1 < prevLayer.length) {
                                  nextLayer[i] = keccak256(
                                      abi.encodePacked(prevLayer[2 * i], prevLayer[2 * i + 1])
                                  );
                              } else {
                                  nextLayer[i] = prevLayer[2 * i];
                              }
                          }
                          prevLayer = nextLayer;
                      }
                      return prevLayer[0];
                  }
                  function calculateRoot(
                      bytes32[] memory nodes,
                      uint256 route,
                      bytes32 item
                  ) internal pure returns (bytes32) {
                      uint256 proofItems = nodes.length;
                      if (proofItems > 256) revert MerkleProofTooLong(proofItems, 256);
                      bytes32 h = item;
                      for (uint256 i = 0; i < proofItems; ) {
                          bytes32 node = nodes[i];
                          if ((route & (1 << i)) == 0) {
                              assembly {
                                  mstore(0x00, h)
                                  mstore(0x20, node)
                                  h := keccak256(0x00, 0x40)
                              }
                          } else {
                              assembly {
                                  mstore(0x00, node)
                                  mstore(0x20, h)
                                  h := keccak256(0x00, 0x40)
                              }
                          }
                          unchecked {
                              ++i;
                          }
                      }
                      return h;
                  }
              }
              // Copyright 2021-2022, Offchain Labs, Inc.
              // For license information, see https://github.com/nitro/blob/master/LICENSE
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity ^0.8.0;
              import {NotOwner} from "./Error.sol";
              /// @dev A stateless contract that allows you to infer if the current call has been delegated or not
              /// Pattern used here is from UUPS implementation by the OpenZeppelin team
              abstract contract DelegateCallAware {
                  address private immutable __self = address(this);
                  /**
                   * @dev Check that the execution is being performed through a delegate call. This allows a function to be
                   * callable on the proxy contract but not on the logic contract.
                   */
                  modifier onlyDelegated() {
                      require(address(this) != __self, "Function must be called through delegatecall");
                      _;
                  }
                  /**
                   * @dev Check that the execution is not being performed through a delegate call. This allows a function to be
                   * callable on the implementing contract but not through proxies.
                   */
                  modifier notDelegated() {
                      require(address(this) == __self, "Function must not be called through delegatecall");
                      _;
                  }
                  /// @dev Check that msg.sender is the current EIP 1967 proxy admin
                  modifier onlyProxyOwner() {
                      // Storage slot with the admin of the proxy contract
                      // This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1
                      bytes32 slot = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                      address admin;
                      assembly {
                          admin := sload(slot)
                      }
                      if (msg.sender != admin) revert NotOwner(msg.sender, admin);
                      _;
                  }
              }
              // Copyright 2021-2022, Offchain Labs, Inc.
              // For license information, see https://github.com/nitro/blob/master/LICENSE
              // SPDX-License-Identifier: BUSL-1.1
              // solhint-disable-next-line compiler-version
              pragma solidity >=0.4.21 <0.9.0;
              interface IOwnable {
                  function owner() external view returns (address);
              }
              

              File 6 of 8: Bridge
              // Copyright 2021-2022, Offchain Labs, Inc.
              // For license information, see https://github.com/nitro/blob/master/LICENSE
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity ^0.8.4;
              import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
              import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
              import {
                  NotContract,
                  NotRollupOrOwner,
                  NotDelayedInbox,
                  NotSequencerInbox,
                  NotOutbox,
                  InvalidOutboxSet,
                  BadSequencerMessageNumber
              } from "../libraries/Error.sol";
              import "./IBridge.sol";
              import "./Messages.sol";
              import "../libraries/DelegateCallAware.sol";
              import {L1MessageType_batchPostingReport} from "../libraries/MessageTypes.sol";
              /**
               * @title Staging ground for incoming and outgoing messages
               * @notice Holds the inbox accumulator for sequenced and delayed messages.
               * It is also the ETH escrow for value sent with these messages.
               * Since the escrow is held here, this contract also contains a list of allowed
               * outboxes that can make calls from here and withdraw this escrow.
               */
              contract Bridge is Initializable, DelegateCallAware, IBridge {
                  using AddressUpgradeable for address;
                  struct InOutInfo {
                      uint256 index;
                      bool allowed;
                  }
                  mapping(address => InOutInfo) private allowedDelayedInboxesMap;
                  mapping(address => InOutInfo) private allowedOutboxesMap;
                  address[] public allowedDelayedInboxList;
                  address[] public allowedOutboxList;
                  address private _activeOutbox;
                  /// @inheritdoc IBridge
                  bytes32[] public delayedInboxAccs;
                  /// @inheritdoc IBridge
                  bytes32[] public sequencerInboxAccs;
                  IOwnable public rollup;
                  address public sequencerInbox;
                  uint256 public override sequencerReportedSubMessageCount;
                  address private constant EMPTY_ACTIVEOUTBOX = address(type(uint160).max);
                  function initialize(IOwnable rollup_) external initializer onlyDelegated {
                      _activeOutbox = EMPTY_ACTIVEOUTBOX;
                      rollup = rollup_;
                  }
                  modifier onlyRollupOrOwner() {
                      if (msg.sender != address(rollup)) {
                          address rollupOwner = rollup.owner();
                          if (msg.sender != rollupOwner) {
                              revert NotRollupOrOwner(msg.sender, address(rollup), rollupOwner);
                          }
                      }
                      _;
                  }
                  /// @dev returns the address of current active Outbox, or zero if no outbox is active
                  function activeOutbox() public view returns (address) {
                      address outbox = _activeOutbox;
                      // address zero is returned if no outbox is set, but the value used in storage
                      // is non-zero to save users some gas (as storage refunds are usually maxed out)
                      // EIP-1153 would help here.
                      // we don't return `EMPTY_ACTIVEOUTBOX` to avoid a breaking change on the current api
                      if (outbox == EMPTY_ACTIVEOUTBOX) return address(0);
                      return outbox;
                  }
                  function allowedDelayedInboxes(address inbox) external view returns (bool) {
                      return allowedDelayedInboxesMap[inbox].allowed;
                  }
                  function allowedOutboxes(address outbox) external view returns (bool) {
                      return allowedOutboxesMap[outbox].allowed;
                  }
                  modifier onlySequencerInbox() {
                      if (msg.sender != sequencerInbox) revert NotSequencerInbox(msg.sender);
                      _;
                  }
                  function enqueueSequencerMessage(
                      bytes32 dataHash,
                      uint256 afterDelayedMessagesRead,
                      uint256 prevMessageCount,
                      uint256 newMessageCount
                  )
                      external
                      onlySequencerInbox
                      returns (
                          uint256 seqMessageIndex,
                          bytes32 beforeAcc,
                          bytes32 delayedAcc,
                          bytes32 acc
                      )
                  {
                      if (
                          sequencerReportedSubMessageCount != prevMessageCount &&
                          prevMessageCount != 0 &&
                          sequencerReportedSubMessageCount != 0
                      ) {
                          revert BadSequencerMessageNumber(sequencerReportedSubMessageCount, prevMessageCount);
                      }
                      sequencerReportedSubMessageCount = newMessageCount;
                      seqMessageIndex = sequencerInboxAccs.length;
                      if (sequencerInboxAccs.length > 0) {
                          beforeAcc = sequencerInboxAccs[sequencerInboxAccs.length - 1];
                      }
                      if (afterDelayedMessagesRead > 0) {
                          delayedAcc = delayedInboxAccs[afterDelayedMessagesRead - 1];
                      }
                      acc = keccak256(abi.encodePacked(beforeAcc, dataHash, delayedAcc));
                      sequencerInboxAccs.push(acc);
                  }
                  /// @inheritdoc IBridge
                  function submitBatchSpendingReport(address sender, bytes32 messageDataHash)
                      external
                      onlySequencerInbox
                      returns (uint256)
                  {
                      return
                          addMessageToDelayedAccumulator(
                              L1MessageType_batchPostingReport,
                              sender,
                              uint64(block.number),
                              uint64(block.timestamp), // solhint-disable-line not-rely-on-time,
                              block.basefee,
                              messageDataHash
                          );
                  }
                  /// @inheritdoc IBridge
                  function enqueueDelayedMessage(
                      uint8 kind,
                      address sender,
                      bytes32 messageDataHash
                  ) external payable returns (uint256) {
                      if (!allowedDelayedInboxesMap[msg.sender].allowed) revert NotDelayedInbox(msg.sender);
                      return
                          addMessageToDelayedAccumulator(
                              kind,
                              sender,
                              uint64(block.number),
                              uint64(block.timestamp), // solhint-disable-line not-rely-on-time
                              block.basefee,
                              messageDataHash
                          );
                  }
                  function addMessageToDelayedAccumulator(
                      uint8 kind,
                      address sender,
                      uint64 blockNumber,
                      uint64 blockTimestamp,
                      uint256 baseFeeL1,
                      bytes32 messageDataHash
                  ) internal returns (uint256) {
                      uint256 count = delayedInboxAccs.length;
                      bytes32 messageHash = Messages.messageHash(
                          kind,
                          sender,
                          blockNumber,
                          blockTimestamp,
                          count,
                          baseFeeL1,
                          messageDataHash
                      );
                      bytes32 prevAcc = 0;
                      if (count > 0) {
                          prevAcc = delayedInboxAccs[count - 1];
                      }
                      delayedInboxAccs.push(Messages.accumulateInboxMessage(prevAcc, messageHash));
                      emit MessageDelivered(
                          count,
                          prevAcc,
                          msg.sender,
                          kind,
                          sender,
                          messageDataHash,
                          baseFeeL1,
                          blockTimestamp
                      );
                      return count;
                  }
                  function executeCall(
                      address to,
                      uint256 value,
                      bytes calldata data
                  ) external returns (bool success, bytes memory returnData) {
                      if (!allowedOutboxesMap[msg.sender].allowed) revert NotOutbox(msg.sender);
                      if (data.length > 0 && !to.isContract()) revert NotContract(to);
                      address prevOutbox = _activeOutbox;
                      _activeOutbox = msg.sender;
                      // We set and reset active outbox around external call so activeOutbox remains valid during call
                      // We use a low level call here since we want to bubble up whether it succeeded or failed to the caller
                      // rather than reverting on failure as well as allow contract and non-contract calls
                      // solhint-disable-next-line avoid-low-level-calls
                      (success, returnData) = to.call{value: value}(data);
                      _activeOutbox = prevOutbox;
                      emit BridgeCallTriggered(msg.sender, to, value, data);
                  }
                  function setSequencerInbox(address _sequencerInbox) external onlyRollupOrOwner {
                      sequencerInbox = _sequencerInbox;
                      emit SequencerInboxUpdated(_sequencerInbox);
                  }
                  function setDelayedInbox(address inbox, bool enabled) external onlyRollupOrOwner {
                      InOutInfo storage info = allowedDelayedInboxesMap[inbox];
                      bool alreadyEnabled = info.allowed;
                      emit InboxToggle(inbox, enabled);
                      if ((alreadyEnabled && enabled) || (!alreadyEnabled && !enabled)) {
                          return;
                      }
                      if (enabled) {
                          allowedDelayedInboxesMap[inbox] = InOutInfo(allowedDelayedInboxList.length, true);
                          allowedDelayedInboxList.push(inbox);
                      } else {
                          allowedDelayedInboxList[info.index] = allowedDelayedInboxList[
                              allowedDelayedInboxList.length - 1
                          ];
                          allowedDelayedInboxesMap[allowedDelayedInboxList[info.index]].index = info.index;
                          allowedDelayedInboxList.pop();
                          delete allowedDelayedInboxesMap[inbox];
                      }
                  }
                  function setOutbox(address outbox, bool enabled) external onlyRollupOrOwner {
                      if (outbox == EMPTY_ACTIVEOUTBOX) revert InvalidOutboxSet(outbox);
                      InOutInfo storage info = allowedOutboxesMap[outbox];
                      bool alreadyEnabled = info.allowed;
                      emit OutboxToggle(outbox, enabled);
                      if ((alreadyEnabled && enabled) || (!alreadyEnabled && !enabled)) {
                          return;
                      }
                      if (enabled) {
                          allowedOutboxesMap[outbox] = InOutInfo(allowedOutboxList.length, true);
                          allowedOutboxList.push(outbox);
                      } else {
                          allowedOutboxList[info.index] = allowedOutboxList[allowedOutboxList.length - 1];
                          allowedOutboxesMap[allowedOutboxList[info.index]].index = info.index;
                          allowedOutboxList.pop();
                          delete allowedOutboxesMap[outbox];
                      }
                  }
                  function setSequencerReportedSubMessageCount(uint256 newMsgCount) external onlyRollupOrOwner {
                      sequencerReportedSubMessageCount = newMsgCount;
                  }
                  function delayedMessageCount() external view override returns (uint256) {
                      return delayedInboxAccs.length;
                  }
                  function sequencerMessageCount() external view returns (uint256) {
                      return sequencerInboxAccs.length;
                  }
                  /// @dev For the classic -> nitro migration. TODO: remove post-migration.
                  function acceptFundsFromOldBridge() external payable {}
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.0) (proxy/utils/Initializable.sol)
              pragma solidity ^0.8.0;
              import "../../utils/AddressUpgradeable.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.
               *
               * 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 initialize the implementation contract, you can either invoke the
               * initializer manually, or you can include a constructor to automatically mark it as initialized when it is deployed:
               *
               * [.hljs-theme-light.nopadding]
               * ```
               * /// @custom:oz-upgrades-unsafe-allow constructor
               * constructor() initializer {}
               * ```
               * ====
               */
              abstract contract Initializable {
                  /**
                   * @dev Indicates that the contract has been initialized.
                   */
                  bool private _initialized;
                  /**
                   * @dev Indicates that the contract is in the process of being initialized.
                   */
                  bool private _initializing;
                  /**
                   * @dev Modifier to protect an initializer function from being invoked twice.
                   */
                  modifier initializer() {
                      // If the contract is initializing we ignore whether _initialized is set in order to support multiple
                      // inheritance patterns, but we only do this in the context of a constructor, because in other contexts the
                      // contract may have been reentered.
                      require(_initializing ? _isConstructor() : !_initialized, "Initializable: contract is already initialized");
                      bool isTopLevelCall = !_initializing;
                      if (isTopLevelCall) {
                          _initializing = true;
                          _initialized = true;
                      }
                      _;
                      if (isTopLevelCall) {
                          _initializing = false;
                      }
                  }
                  /**
                   * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
                   * {initializer} modifier, directly or indirectly.
                   */
                  modifier onlyInitializing() {
                      require(_initializing, "Initializable: contract is not initializing");
                      _;
                  }
                  function _isConstructor() private view returns (bool) {
                      return !AddressUpgradeable.isContract(address(this));
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)
              pragma solidity ^0.8.1;
              /**
               * @dev Collection of functions related to the address type
               */
              library AddressUpgradeable {
                  /**
                   * @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 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
                              assembly {
                                  let returndata_size := mload(returndata)
                                  revert(add(32, returndata), returndata_size)
                              }
                          } else {
                              revert(errorMessage);
                          }
                      }
                  }
              }
              // Copyright 2021-2022, Offchain Labs, Inc.
              // For license information, see https://github.com/nitro/blob/master/LICENSE
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity ^0.8.4;
              /// @dev Init was already called
              error AlreadyInit();
              /// Init was called with param set to zero that must be nonzero
              error HadZeroInit();
              /// @dev Thrown when non owner tries to access an only-owner function
              /// @param sender The msg.sender who is not the owner
              /// @param owner The owner address
              error NotOwner(address sender, address owner);
              /// @dev Thrown when an address that is not the rollup tries to call an only-rollup function
              /// @param sender The sender who is not the rollup
              /// @param rollup The rollup address authorized to call this function
              error NotRollup(address sender, address rollup);
              /// @dev Thrown when the contract was not called directly from the origin ie msg.sender != tx.origin
              error NotOrigin();
              /// @dev Provided data was too large
              /// @param dataLength The length of the data that is too large
              /// @param maxDataLength The max length the data can be
              error DataTooLarge(uint256 dataLength, uint256 maxDataLength);
              /// @dev The provided is not a contract and was expected to be
              /// @param addr The adddress in question
              error NotContract(address addr);
              /// @dev The merkle proof provided was too long
              /// @param actualLength The length of the merkle proof provided
              /// @param maxProofLength The max length a merkle proof can have
              error MerkleProofTooLong(uint256 actualLength, uint256 maxProofLength);
              /// @dev Thrown when an un-authorized address tries to access an admin function
              /// @param sender The un-authorized sender
              /// @param rollup The rollup, which would be authorized
              /// @param owner The rollup's owner, which would be authorized
              error NotRollupOrOwner(address sender, address rollup, address owner);
              // Bridge Errors
              /// @dev Thrown when an un-authorized address tries to access an only-inbox function
              /// @param sender The un-authorized sender
              error NotDelayedInbox(address sender);
              /// @dev Thrown when an un-authorized address tries to access an only-sequencer-inbox function
              /// @param sender The un-authorized sender
              error NotSequencerInbox(address sender);
              /// @dev Thrown when an un-authorized address tries to access an only-outbox function
              /// @param sender The un-authorized sender
              error NotOutbox(address sender);
              /// @dev the provided outbox address isn't valid
              /// @param outbox address of outbox being set
              error InvalidOutboxSet(address outbox);
              // Inbox Errors
              /// @dev The contract is paused, so cannot be paused
              error AlreadyPaused();
              /// @dev The contract is unpaused, so cannot be unpaused
              error AlreadyUnpaused();
              /// @dev The contract is paused
              error Paused();
              /// @dev msg.value sent to the inbox isn't high enough
              error InsufficientValue(uint256 expected, uint256 actual);
              /// @dev submission cost provided isn't enough to create retryable ticket
              error InsufficientSubmissionCost(uint256 expected, uint256 actual);
              /// @dev address not allowed to interact with the given contract
              error NotAllowedOrigin(address origin);
              /// @dev used to convey retryable tx data in eth calls without requiring a tx trace
              /// this follows a pattern similar to EIP-3668 where reverts surface call information
              error RetryableData(
                  address from,
                  address to,
                  uint256 l2CallValue,
                  uint256 deposit,
                  uint256 maxSubmissionCost,
                  address excessFeeRefundAddress,
                  address callValueRefundAddress,
                  uint256 gasLimit,
                  uint256 maxFeePerGas,
                  bytes data
              );
              // Outbox Errors
              /// @dev The provided proof was too long
              /// @param proofLength The length of the too-long proof
              error ProofTooLong(uint256 proofLength);
              /// @dev The output index was greater than the maximum
              /// @param index The output index
              /// @param maxIndex The max the index could be
              error PathNotMinimal(uint256 index, uint256 maxIndex);
              /// @dev The calculated root does not exist
              /// @param root The calculated root
              error UnknownRoot(bytes32 root);
              /// @dev The record has already been spent
              /// @param index The index of the spent record
              error AlreadySpent(uint256 index);
              /// @dev A call to the bridge failed with no return data
              error BridgeCallFailed();
              // Sequencer Inbox Errors
              /// @dev Thrown when someone attempts to read fewer messages than have already been read
              error DelayedBackwards();
              /// @dev Thrown when someone attempts to read more messages than exist
              error DelayedTooFar();
              /// @dev Force include can only read messages more blocks old than the delay period
              error ForceIncludeBlockTooSoon();
              /// @dev Force include can only read messages more seconds old than the delay period
              error ForceIncludeTimeTooSoon();
              /// @dev The message provided did not match the hash in the delayed inbox
              error IncorrectMessagePreimage();
              /// @dev This can only be called by the batch poster
              error NotBatchPoster();
              /// @dev The sequence number provided to this message was inconsistent with the number of batches already included
              error BadSequencerNumber(uint256 stored, uint256 received);
              /// @dev The sequence message number provided to this message was inconsistent with the previous one
              error BadSequencerMessageNumber(uint256 stored, uint256 received);
              /// @dev The batch data has the inbox authenticated bit set, but the batch data was not authenticated by the inbox
              error DataNotAuthenticated();
              /// @dev Tried to create an already valid Data Availability Service keyset
              error AlreadyValidDASKeyset(bytes32);
              /// @dev Tried to use or invalidate an already invalid Data Availability Service keyset
              error NoSuchKeyset(bytes32);
              // Copyright 2021-2022, Offchain Labs, Inc.
              // For license information, see https://github.com/nitro/blob/master/LICENSE
              // SPDX-License-Identifier: BUSL-1.1
              // solhint-disable-next-line compiler-version
              pragma solidity >=0.6.9 <0.9.0;
              import "./IOwnable.sol";
              interface IBridge {
                  event MessageDelivered(
                      uint256 indexed messageIndex,
                      bytes32 indexed beforeInboxAcc,
                      address inbox,
                      uint8 kind,
                      address sender,
                      bytes32 messageDataHash,
                      uint256 baseFeeL1,
                      uint64 timestamp
                  );
                  event BridgeCallTriggered(
                      address indexed outbox,
                      address indexed to,
                      uint256 value,
                      bytes data
                  );
                  event InboxToggle(address indexed inbox, bool enabled);
                  event OutboxToggle(address indexed outbox, bool enabled);
                  event SequencerInboxUpdated(address newSequencerInbox);
                  function allowedDelayedInboxList(uint256) external returns (address);
                  function allowedOutboxList(uint256) external returns (address);
                  /// @dev Accumulator for delayed inbox messages; tail represents hash of the current state; each element represents the inclusion of a new message.
                  function delayedInboxAccs(uint256) external view returns (bytes32);
                  /// @dev Accumulator for sequencer inbox messages; tail represents hash of the current state; each element represents the inclusion of a new message.
                  function sequencerInboxAccs(uint256) external view returns (bytes32);
                  function rollup() external view returns (IOwnable);
                  function sequencerInbox() external view returns (address);
                  function activeOutbox() external view returns (address);
                  function allowedDelayedInboxes(address inbox) external view returns (bool);
                  function allowedOutboxes(address outbox) external view returns (bool);
                  function sequencerReportedSubMessageCount() external view returns (uint256);
                  /**
                   * @dev Enqueue a message in the delayed inbox accumulator.
                   *      These messages are later sequenced in the SequencerInbox, either
                   *      by the sequencer as part of a normal batch, or by force inclusion.
                   */
                  function enqueueDelayedMessage(
                      uint8 kind,
                      address sender,
                      bytes32 messageDataHash
                  ) external payable returns (uint256);
                  function executeCall(
                      address to,
                      uint256 value,
                      bytes calldata data
                  ) external returns (bool success, bytes memory returnData);
                  function delayedMessageCount() external view returns (uint256);
                  function sequencerMessageCount() external view returns (uint256);
                  // ---------- onlySequencerInbox functions ----------
                  function enqueueSequencerMessage(
                      bytes32 dataHash,
                      uint256 afterDelayedMessagesRead,
                      uint256 prevMessageCount,
                      uint256 newMessageCount
                  )
                      external
                      returns (
                          uint256 seqMessageIndex,
                          bytes32 beforeAcc,
                          bytes32 delayedAcc,
                          bytes32 acc
                      );
                  /**
                   * @dev Allows the sequencer inbox to submit a delayed message of the batchPostingReport type
                   *      This is done through a separate function entrypoint instead of allowing the sequencer inbox
                   *      to call `enqueueDelayedMessage` to avoid the gas overhead of an extra SLOAD in either
                   *      every delayed inbox or every sequencer inbox call.
                   */
                  function submitBatchSpendingReport(address batchPoster, bytes32 dataHash)
                      external
                      returns (uint256 msgNum);
                  // ---------- onlyRollupOrOwner functions ----------
                  function setSequencerInbox(address _sequencerInbox) external;
                  function setDelayedInbox(address inbox, bool enabled) external;
                  function setOutbox(address inbox, bool enabled) external;
                  // ---------- initializer ----------
                  function initialize(IOwnable rollup_) external;
              }
              // Copyright 2021-2022, Offchain Labs, Inc.
              // For license information, see https://github.com/nitro/blob/master/LICENSE
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity ^0.8.0;
              library Messages {
                  function messageHash(
                      uint8 kind,
                      address sender,
                      uint64 blockNumber,
                      uint64 timestamp,
                      uint256 inboxSeqNum,
                      uint256 baseFeeL1,
                      bytes32 messageDataHash
                  ) internal pure returns (bytes32) {
                      return
                          keccak256(
                              abi.encodePacked(
                                  kind,
                                  sender,
                                  blockNumber,
                                  timestamp,
                                  inboxSeqNum,
                                  baseFeeL1,
                                  messageDataHash
                              )
                          );
                  }
                  function accumulateInboxMessage(bytes32 prevAcc, bytes32 message)
                      internal
                      pure
                      returns (bytes32)
                  {
                      return keccak256(abi.encodePacked(prevAcc, message));
                  }
              }
              // Copyright 2021-2022, Offchain Labs, Inc.
              // For license information, see https://github.com/nitro/blob/master/LICENSE
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity ^0.8.0;
              import {NotOwner} from "./Error.sol";
              /// @dev A stateless contract that allows you to infer if the current call has been delegated or not
              /// Pattern used here is from UUPS implementation by the OpenZeppelin team
              abstract contract DelegateCallAware {
                  address private immutable __self = address(this);
                  /**
                   * @dev Check that the execution is being performed through a delegate call. This allows a function to be
                   * callable on the proxy contract but not on the logic contract.
                   */
                  modifier onlyDelegated() {
                      require(address(this) != __self, "Function must be called through delegatecall");
                      _;
                  }
                  /**
                   * @dev Check that the execution is not being performed through a delegate call. This allows a function to be
                   * callable on the implementing contract but not through proxies.
                   */
                  modifier notDelegated() {
                      require(address(this) == __self, "Function must not be called through delegatecall");
                      _;
                  }
                  /// @dev Check that msg.sender is the current EIP 1967 proxy admin
                  modifier onlyProxyOwner() {
                      // Storage slot with the admin of the proxy contract
                      // This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1
                      bytes32 slot = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                      address admin;
                      assembly {
                          admin := sload(slot)
                      }
                      if (msg.sender != admin) revert NotOwner(msg.sender, admin);
                      _;
                  }
              }
              // Copyright 2021-2022, Offchain Labs, Inc.
              // For license information, see https://github.com/nitro/blob/master/LICENSE
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity ^0.8.4;
              uint8 constant L2_MSG = 3;
              uint8 constant L1MessageType_L2FundedByL1 = 7;
              uint8 constant L1MessageType_submitRetryableTx = 9;
              uint8 constant L1MessageType_ethDeposit = 12;
              uint8 constant L1MessageType_batchPostingReport = 13;
              uint8 constant L2MessageType_unsignedEOATx = 0;
              uint8 constant L2MessageType_unsignedContractTx = 1;
              uint8 constant ROLLUP_PROTOCOL_EVENT_TYPE = 8;
              uint8 constant INITIALIZATION_MSG_TYPE = 11;
              // Copyright 2021-2022, Offchain Labs, Inc.
              // For license information, see https://github.com/nitro/blob/master/LICENSE
              // SPDX-License-Identifier: BUSL-1.1
              // solhint-disable-next-line compiler-version
              pragma solidity >=0.4.21 <0.9.0;
              interface IOwnable {
                  function owner() external view returns (address);
              }
              

              File 7 of 8: L1ERC20Gateway
              // SPDX-License-Identifier: Apache-2.0
              /*
               * Copyright 2020, Offchain Labs, Inc.
               *
               * Licensed under the Apache License, Version 2.0 (the "License");
               * you may not use this file except in compliance with the License.
               * You may obtain a copy of the License at
               *
               *    http://www.apache.org/licenses/LICENSE-2.0
               *
               * Unless required by applicable law or agreed to in writing, software
               * distributed under the License is distributed on an "AS IS" BASIS,
               * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
               * See the License for the specific language governing permissions and
               * limitations under the License.
               */
              pragma solidity ^0.6.11;
              import "./L1ArbitrumExtendedGateway.sol";
              import "@openzeppelin/contracts/utils/Create2.sol";
              import "arb-bridge-eth/contracts/libraries/Whitelist.sol";
              /**
               * @title Layer 1 Gateway contract for bridging standard ERC20s
               * @notice This contract handles token deposits, holds the escrowed tokens on layer 1, and (ultimately) finalizes withdrawals.
               * @dev Any ERC20 that requires non-standard functionality should use a separate gateway.
               * Messages to layer 2 use the inbox's createRetryableTicket method.
               */
              contract L1ERC20Gateway is L1ArbitrumExtendedGateway {
                  // used for create2 address calculation
                  bytes32 public cloneableProxyHash;
                  // We don't use the solidity creationCode as it breaks when upgrading contracts
                  // keccak256(type(ClonableBeaconProxy).creationCode);
                  address public l2BeaconProxyFactory;
                  // whitelist not used anymore
                  address public whitelist;
                  // start of inline reentrancy guard
                  // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.2/contracts/utils/ReentrancyGuard.sol
                  uint256 private constant _NOT_ENTERED = 1;
                  uint256 private constant _ENTERED = 2;
                  uint256 private _status;
                  modifier nonReentrant() {
                      // On the first call to nonReentrant, _notEntered will be true
                      require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
                      // Any calls to nonReentrant after this point will fail
                      _status = _ENTERED;
                      _;
                      _status = _NOT_ENTERED;
                  }
                  // end of inline reentrancy guard
                  function outboundTransferCustomRefund(
                      address _l1Token,
                      address _refundTo,
                      address _to,
                      uint256 _amount,
                      uint256 _maxGas,
                      uint256 _gasPriceBid,
                      bytes calldata _data
                  ) public payable override nonReentrant returns (bytes memory res) {
                      return
                          super.outboundTransferCustomRefund(
                              _l1Token,
                              _refundTo,
                              _to,
                              _amount,
                              _maxGas,
                              _gasPriceBid,
                              _data
                          );
                  }
                  function finalizeInboundTransfer(
                      address _token,
                      address _from,
                      address _to,
                      uint256 _amount,
                      bytes calldata _data
                  ) public payable override nonReentrant {
                      // the superclass checks onlyCounterpartGateway
                      super.finalizeInboundTransfer(_token, _from, _to, _amount, _data);
                  }
                  function initialize(
                      address _l2Counterpart,
                      address _router,
                      address _inbox,
                      bytes32 _cloneableProxyHash,
                      address _l2BeaconProxyFactory
                  ) public {
                      L1ArbitrumGateway._initialize(_l2Counterpart, _router, _inbox);
                      require(_cloneableProxyHash != bytes32(0), "INVALID_PROXYHASH");
                      require(_l2BeaconProxyFactory != address(0), "INVALID_BEACON");
                      cloneableProxyHash = _cloneableProxyHash;
                      l2BeaconProxyFactory = _l2BeaconProxyFactory;
                      // disable whitelist by default
                      whitelist = address(0);
                      // reentrancy guard
                      _status = _NOT_ENTERED;
                  }
                  /**
                   * @notice utility function used to perform external read-only calls.
                   * @dev the result is returned even if the call failed or was directed at an EOA,
                   * it is cheaper to have the L2 consumer identify and deal with this.
                   * @return result bytes, even if the call failed.
                   */
                  function callStatic(address targetContract, bytes4 targetFunction)
                      internal
                      view
                      returns (bytes memory)
                  {
                      (
                          ,
                          /* bool success */
                          bytes memory res
                      ) = targetContract.staticcall(abi.encodeWithSelector(targetFunction));
                      return res;
                  }
                  function getOutboundCalldata(
                      address _token,
                      address _from,
                      address _to,
                      uint256 _amount,
                      bytes memory _data
                  ) public view override returns (bytes memory outboundCalldata) {
                      // TODO: cheaper to make static calls or save isDeployed to storage?
                      bytes memory deployData = abi.encode(
                          callStatic(_token, ERC20.name.selector),
                          callStatic(_token, ERC20.symbol.selector),
                          callStatic(_token, ERC20.decimals.selector)
                      );
                      outboundCalldata = abi.encodeWithSelector(
                          ITokenGateway.finalizeInboundTransfer.selector,
                          _token,
                          _from,
                          _to,
                          _amount,
                          GatewayMessageHandler.encodeToL2GatewayMsg(deployData, _data)
                      );
                      return outboundCalldata;
                  }
                  function calculateL2TokenAddress(address l1ERC20) public view override returns (address) {
                      bytes32 salt = getSalt(l1ERC20);
                      return Create2.computeAddress(salt, cloneableProxyHash, l2BeaconProxyFactory);
                  }
                  function getSalt(address l1ERC20) internal view returns (bytes32) {
                      // TODO: use a library
                      return keccak256(abi.encode(counterpartGateway, keccak256(abi.encode(l1ERC20))));
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              /*
               * Copyright 2020, Offchain Labs, Inc.
               *
               * Licensed under the Apache License, Version 2.0 (the "License");
               * you may not use this file except in compliance with the License.
               * You may obtain a copy of the License at
               *
               *    http://www.apache.org/licenses/LICENSE-2.0
               *
               * Unless required by applicable law or agreed to in writing, software
               * distributed under the License is distributed on an "AS IS" BASIS,
               * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
               * See the License for the specific language governing permissions and
               * limitations under the License.
               */
              pragma solidity ^0.6.11;
              import "../../libraries/ITransferAndCall.sol";
              import "./L1ArbitrumGateway.sol";
              interface ITradeableExitReceiver {
                  function onExitTransfer(
                      address sender,
                      uint256 exitNum,
                      bytes calldata data
                  ) external returns (bool);
              }
              abstract contract L1ArbitrumExtendedGateway is L1ArbitrumGateway {
                  struct ExitData {
                      bool isExit;
                      address _newTo;
                      bytes _newData;
                  }
                  mapping(bytes32 => ExitData) public redirectedExits;
                  event WithdrawRedirected(
                      address indexed from,
                      address indexed to,
                      uint256 indexed exitNum,
                      bytes newData,
                      bytes data,
                      bool madeExternalCall
                  );
                  /**
                   * @notice Allows a user to redirect their right to claim a withdrawal to another address.
                   * @dev This method also allows you to make an arbitrary call after the transfer.
                   * This does not validate if the exit was already triggered. It is assumed the `_exitNum` is
                   * validated off-chain to ensure this was not yet triggered.
                   * @param _exitNum Sequentially increasing exit counter determined by the L2 bridge
                   * @param _initialDestination address the L2 withdrawal call initially set as the destination.
                   * @param _newDestination address the L1 will now call instead of the previously set destination
                   * @param _newData data to be used in inboundEscrowAndCall
                   * @param _data optional data for external call upon transfering the exit
                   */
                  function transferExitAndCall(
                      uint256 _exitNum,
                      address _initialDestination,
                      address _newDestination,
                      bytes calldata _newData,
                      bytes calldata _data
                  ) external {
                      // the initial data doesn't make a difference when transfering you exit
                      // since the L2 bridge gives a unique exit ID to each exit
                      (address expectedSender, ) = getExternalCall(_exitNum, _initialDestination, "");
                      // if you want to transfer your exit, you must be the current destination
                      require(msg.sender == expectedSender, "NOT_EXPECTED_SENDER");
                      // the inboundEscrowAndCall functionality has been disabled, so no data is allowed
                      require(_newData.length == 0, "NO_DATA_ALLOWED");
                      setRedirectedExit(_exitNum, _initialDestination, _newDestination, _newData);
                      if (_data.length > 0) {
                          require(_newDestination.isContract(), "TO_NOT_CONTRACT");
                          bool success = ITradeableExitReceiver(_newDestination).onExitTransfer(
                              expectedSender,
                              _exitNum,
                              _data
                          );
                          require(success, "TRANSFER_HOOK_FAIL");
                      }
                      emit WithdrawRedirected(
                          expectedSender,
                          _newDestination,
                          _exitNum,
                          _newData,
                          _data,
                          _data.length > 0
                      );
                  }
                  /// @notice this does not verify if the external call was already done
                  function getExternalCall(
                      uint256 _exitNum,
                      address _initialDestination,
                      bytes memory _initialData
                  ) public view virtual override returns (address target, bytes memory data) {
                      // this function is virtual so that subclasses can override it with custom logic where necessary
                      bytes32 withdrawData = encodeWithdrawal(_exitNum, _initialDestination);
                      ExitData storage exit = redirectedExits[withdrawData];
                      // here we don't authenticate `_initialData`. we could hash it into `withdrawData` but would increase gas costs
                      // this is safe because if the exit isn't overriden, the _initialData coming from L2 is trusted
                      // but if the exit is traded, all we care about is the latest user calldata
                      if (exit.isExit) {
                          return (exit._newTo, exit._newData);
                      } else {
                          return (_initialDestination, _initialData);
                      }
                  }
                  function setRedirectedExit(
                      uint256 _exitNum,
                      address _initialDestination,
                      address _newDestination,
                      bytes memory _newData
                  ) internal {
                      bytes32 withdrawData = encodeWithdrawal(_exitNum, _initialDestination);
                      redirectedExits[withdrawData] = ExitData(true, _newDestination, _newData);
                  }
                  function encodeWithdrawal(uint256 _exitNum, address _initialDestination)
                      public
                      pure
                      returns (bytes32)
                  {
                      // here we assume the L2 bridge gives a unique exitNum to each exit
                      return keccak256(abi.encode(_exitNum, _initialDestination));
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.6.0 <0.8.0;
              /**
               * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
               * `CREATE2` can be used to compute in advance the address where a smart
               * contract will be deployed, which allows for interesting new mechanisms known
               * as 'counterfactual interactions'.
               *
               * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more
               * information.
               */
              library Create2 {
                  /**
                   * @dev Deploys a contract using `CREATE2`. The address where the contract
                   * will be deployed can be known in advance via {computeAddress}.
                   *
                   * The bytecode for a contract can be obtained from Solidity with
                   * `type(contractName).creationCode`.
                   *
                   * Requirements:
                   *
                   * - `bytecode` must not be empty.
                   * - `salt` must have not been used for `bytecode` already.
                   * - the factory must have a balance of at least `amount`.
                   * - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
                   */
                  function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address) {
                      address addr;
                      require(address(this).balance >= amount, "Create2: insufficient balance");
                      require(bytecode.length != 0, "Create2: bytecode length is zero");
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
                      }
                      require(addr != address(0), "Create2: Failed on deploy");
                      return addr;
                  }
                  /**
                   * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the
                   * `bytecodeHash` or `salt` will result in a new destination address.
                   */
                  function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {
                      return computeAddress(salt, bytecodeHash, address(this));
                  }
                  /**
                   * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
                   * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
                   */
                  function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address) {
                      bytes32 _data = keccak256(
                          abi.encodePacked(bytes1(0xff), deployer, salt, bytecodeHash)
                      );
                      return address(uint160(uint256(_data)));
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              /*
               * Copyright 2021, Offchain Labs, Inc.
               *
               * Licensed under the Apache License, Version 2.0 (the "License");
               * you may not use this file except in compliance with the License.
               * You may obtain a copy of the License at
               *
               *    http://www.apache.org/licenses/LICENSE-2.0
               *
               * Unless required by applicable law or agreed to in writing, software
               * distributed under the License is distributed on an "AS IS" BASIS,
               * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
               * See the License for the specific language governing permissions and
               * limitations under the License.
               */
              pragma solidity ^0.6.11;
              abstract contract WhitelistConsumer {
                  address public whitelist;
                  event WhitelistSourceUpdated(address newSource);
                  modifier onlyWhitelisted() {
                      if (whitelist != address(0)) {
                          require(Whitelist(whitelist).isAllowed(msg.sender), "NOT_WHITELISTED");
                      }
                      _;
                  }
                  function updateWhitelistSource(address newSource) external {
                      require(msg.sender == whitelist, "NOT_FROM_LIST");
                      whitelist = newSource;
                      emit WhitelistSourceUpdated(newSource);
                  }
              }
              contract Whitelist {
                  address public owner;
                  mapping(address => bool) public isAllowed;
                  event OwnerUpdated(address newOwner);
                  event WhitelistUpgraded(address newWhitelist, address[] targets);
                  constructor() public {
                      owner = msg.sender;
                  }
                  modifier onlyOwner() {
                      require(msg.sender == owner, "ONLY_OWNER");
                      _;
                  }
                  function setOwner(address newOwner) external onlyOwner {
                      owner = newOwner;
                      emit OwnerUpdated(newOwner);
                  }
                  function setWhitelist(address[] memory user, bool[] memory val) external onlyOwner {
                      require(user.length == val.length, "INVALID_INPUT");
                      for (uint256 i = 0; i < user.length; i++) {
                          isAllowed[user[i]] = val[i];
                      }
                  }
                  // set new whitelist to address(0) to disable whitelist
                  function triggerConsumers(address newWhitelist, address[] memory targets) external onlyOwner {
                      for (uint256 i = 0; i < targets.length; i++) {
                          WhitelistConsumer(targets[i]).updateWhitelistSource(newWhitelist);
                      }
                      emit WhitelistUpgraded(newWhitelist, targets);
                  }
              }
              // SPDX-License-Identifier: MIT
              // solhint-disable-next-line compiler-version
              pragma solidity >0.6.0 <0.8.0;
              import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
              interface ITransferAndCall is IERC20Upgradeable {
                  function transferAndCall(
                      address to,
                      uint256 value,
                      bytes memory data
                  ) external returns (bool success);
                  event Transfer(address indexed from, address indexed to, uint256 value, bytes data);
              }
              /**
               * @notice note that implementation of ITransferAndCallReceiver is not expected to return a success bool
               */
              interface ITransferAndCallReceiver {
                  function onTokenTransfer(
                      address _sender,
                      uint256 _value,
                      bytes memory _data
                  ) external;
              }
              // SPDX-License-Identifier: Apache-2.0
              /*
               * Copyright 2020, Offchain Labs, Inc.
               *
               * Licensed under the Apache License, Version 2.0 (the "License");
               * you may not use this file except in compliance with the License.
               * You may obtain a copy of the License at
               *
               *    http://www.apache.org/licenses/LICENSE-2.0
               *
               * Unless required by applicable law or agreed to in writing, software
               * distributed under the License is distributed on an "AS IS" BASIS,
               * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
               * See the License for the specific language governing permissions and
               * limitations under the License.
               */
              pragma solidity ^0.6.11;
              import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
              import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
              import "@openzeppelin/contracts/utils/Create2.sol";
              import "@openzeppelin/contracts/utils/Address.sol";
              import "@openzeppelin/contracts/math/SafeMath.sol";
              import "arb-bridge-eth/contracts/bridge/interfaces/IInbox.sol";
              import "arb-bridge-eth/contracts/libraries/ProxyUtil.sol";
              import "../L1ArbitrumMessenger.sol";
              import "../../libraries/gateway/GatewayMessageHandler.sol";
              import "../../libraries/gateway/TokenGateway.sol";
              import "./IL1ArbitrumGateway.sol";
              import "../../libraries/ITransferAndCall.sol";
              import "../../libraries/ERC165.sol";
              /**
               * @title Common interface for gatways on L1 messaging to Arbitrum.
               */
              abstract contract L1ArbitrumGateway is
                  L1ArbitrumMessenger,
                  TokenGateway,
                  ERC165,
                  IL1ArbitrumGateway
              {
                  using SafeERC20 for IERC20;
                  using Address for address;
                  address public override inbox;
                  event DepositInitiated(
                      address l1Token,
                      address indexed _from,
                      address indexed _to,
                      uint256 indexed _sequenceNumber,
                      uint256 _amount
                  );
                  event WithdrawalFinalized(
                      address l1Token,
                      address indexed _from,
                      address indexed _to,
                      uint256 indexed _exitNum,
                      uint256 _amount
                  );
                  modifier onlyCounterpartGateway() override {
                      address _inbox = inbox;
                      // a message coming from the counterpart gateway was executed by the bridge
                      address bridge = address(super.getBridge(_inbox));
                      require(msg.sender == bridge, "NOT_FROM_BRIDGE");
                      // and the outbox reports that the L2 address of the sender is the counterpart gateway
                      address l2ToL1Sender = super.getL2ToL1Sender(_inbox);
                      require(l2ToL1Sender == counterpartGateway, "ONLY_COUNTERPART_GATEWAY");
                      _;
                  }
                  function postUpgradeInit() external {
                      // it is assumed the L1 Arbitrum Gateway contract is behind a Proxy controlled by a proxy admin
                      // this function can only be called by the proxy admin contract
                      address proxyAdmin = ProxyUtil.getProxyAdmin();
                      require(msg.sender == proxyAdmin, "NOT_FROM_ADMIN");
                      // this has no other logic since the current upgrade doesn't require this logic
                  }
                  function _initialize(
                      address _l2Counterpart,
                      address _router,
                      address _inbox
                  ) internal {
                      TokenGateway._initialize(_l2Counterpart, _router);
                      // L1 gateway must have a router
                      require(_router != address(0), "BAD_ROUTER");
                      require(_inbox != address(0), "BAD_INBOX");
                      inbox = _inbox;
                  }
                  /**
                   * @notice Finalizes a withdrawal via Outbox message; callable only by L2Gateway.outboundTransfer
                   * @param _token L1 address of token being withdrawn from
                   * @param _from initiator of withdrawal
                   * @param _to address the L2 withdrawal call set as the destination.
                   * @param _amount Token amount being withdrawn
                   * @param _data encoded exitNum (Sequentially increasing exit counter determined by the L2Gateway) and additinal hook data
                   */
                  function finalizeInboundTransfer(
                      address _token,
                      address _from,
                      address _to,
                      uint256 _amount,
                      bytes calldata _data
                  ) public payable virtual override onlyCounterpartGateway {
                      // this function is marked as virtual so superclasses can override it to add modifiers
                      (uint256 exitNum, bytes memory callHookData) = GatewayMessageHandler.parseToL1GatewayMsg(
                          _data
                      );
                      if (callHookData.length != 0) {
                          // callHookData should always be 0 since inboundEscrowAndCall is disabled
                          callHookData = bytes("");
                      }
                      // we ignore the returned data since the callHook feature is now disabled
                      (_to, ) = getExternalCall(exitNum, _to, callHookData);
                      inboundEscrowTransfer(_token, _to, _amount);
                      emit WithdrawalFinalized(_token, _from, _to, exitNum, _amount);
                  }
                  function getExternalCall(
                      uint256, /* _exitNum */
                      address _initialDestination,
                      bytes memory _initialData
                  ) public view virtual returns (address target, bytes memory data) {
                      // this method is virtual so the destination of a call can be changed
                      // using tradeable exits in a subclass (L1ArbitrumExtendedGateway)
                      target = _initialDestination;
                      data = _initialData;
                  }
                  function inboundEscrowTransfer(
                      address _l1Token,
                      address _dest,
                      uint256 _amount
                  ) internal virtual {
                      // this method is virtual since different subclasses can handle escrow differently
                      IERC20(_l1Token).safeTransfer(_dest, _amount);
                  }
                  /**
                   * @dev Only excess gas is refunded to the _refundTo account, l2 call value is always returned to the _to account
                   */
                  function createOutboundTxCustomRefund(
                      address _refundTo,
                      address _from,
                      uint256, /* _tokenAmount */
                      uint256 _maxGas,
                      uint256 _gasPriceBid,
                      uint256 _maxSubmissionCost,
                      bytes memory _outboundCalldata
                  ) internal virtual returns (uint256) {
                      // We make this function virtual since outboundTransfer logic is the same for many gateways
                      // but sometimes (ie weth) you construct the outgoing message differently.
                      // msg.value is sent, but 0 is set to the L2 call value
                      // the eth sent is used to pay for the tx's gas
                      return
                          sendTxToL2CustomRefund(
                              inbox,
                              counterpartGateway,
                              _refundTo,
                              _from,
                              msg.value, // we forward the L1 call value to the inbox
                              0, // l2 call value 0 by default
                              L2GasParams({
                                  _maxSubmissionCost: _maxSubmissionCost,
                                  _maxGas: _maxGas,
                                  _gasPriceBid: _gasPriceBid
                              }),
                              _outboundCalldata
                          );
                  }
                  /**
                   * @notice DEPRECATED - look at createOutboundTxCustomRefund instead
                   */
                  function createOutboundTx(
                      address _from,
                      uint256 _tokenAmount,
                      uint256 _maxGas,
                      uint256 _gasPriceBid,
                      uint256 _maxSubmissionCost,
                      bytes memory _outboundCalldata
                  ) internal returns (uint256) {
                      return
                          createOutboundTxCustomRefund(
                              _from,
                              _from,
                              _tokenAmount,
                              _maxGas,
                              _gasPriceBid,
                              _maxSubmissionCost,
                              _outboundCalldata
                          );
                  }
                  /**
                   * @notice DEPRECATED - look at outboundTransferCustomRefund instead
                   */
                  function outboundTransfer(
                      address _l1Token,
                      address _to,
                      uint256 _amount,
                      uint256 _maxGas,
                      uint256 _gasPriceBid,
                      bytes calldata _data
                  ) public payable override returns (bytes memory res) {
                      return
                          outboundTransferCustomRefund(_l1Token, _to, _to, _amount, _maxGas, _gasPriceBid, _data);
                  }
                  /**
                   * @notice Deposit ERC20 token from Ethereum into Arbitrum. If L2 side hasn't been deployed yet, includes name/symbol/decimals data for initial L2 deploy. Initiate by GatewayRouter.
                   * @dev L2 address alias will not be applied to the following types of addresses on L1:
                   *      - 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
                   * @param _l1Token L1 address of ERC20
                   * @param _refundTo Account, or its L2 alias if it have code in L1, to be credited with excess gas refund in L2
                   * @param _to Account to be credited with the tokens in the L2 (can be the user's L2 account or a contract), not subject to L2 aliasing
                                This account, or its L2 alias if it have code in L1, will also be able to cancel the retryable ticket and receive callvalue refund
                   * @param _amount Token Amount
                   * @param _maxGas Max gas deducted from user's L2 balance to cover L2 execution
                   * @param _gasPriceBid Gas price for L2 execution
                   * @param _data encoded data from router and user
                   * @return res abi encoded inbox sequence number
                   */
                  //  * @param maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee
                  function outboundTransferCustomRefund(
                      address _l1Token,
                      address _refundTo,
                      address _to,
                      uint256 _amount,
                      uint256 _maxGas,
                      uint256 _gasPriceBid,
                      bytes calldata _data
                  ) public payable virtual override returns (bytes memory res) {
                      require(isRouter(msg.sender), "NOT_FROM_ROUTER");
                      // This function is set as public and virtual so that subclasses can override
                      // it and add custom validation for callers (ie only whitelisted users)
                      address _from;
                      uint256 seqNum;
                      bytes memory extraData;
                      {
                          uint256 _maxSubmissionCost;
                          if (super.isRouter(msg.sender)) {
                              // router encoded
                              (_from, extraData) = GatewayMessageHandler.parseFromRouterToGateway(_data);
                          } else {
                              _from = msg.sender;
                              extraData = _data;
                          }
                          // user encoded
                          (_maxSubmissionCost, extraData) = abi.decode(extraData, (uint256, bytes));
                          // the inboundEscrowAndCall functionality has been disabled, so no data is allowed
                          require(extraData.length == 0, "EXTRA_DATA_DISABLED");
                          require(_l1Token.isContract(), "L1_NOT_CONTRACT");
                          address l2Token = calculateL2TokenAddress(_l1Token);
                          require(l2Token != address(0), "NO_L2_TOKEN_SET");
                          _amount = outboundEscrowTransfer(_l1Token, _from, _amount);
                          // we override the res field to save on the stack
                          res = getOutboundCalldata(_l1Token, _from, _to, _amount, extraData);
                          seqNum = createOutboundTxCustomRefund(
                              _refundTo,
                              _from,
                              _amount,
                              _maxGas,
                              _gasPriceBid,
                              _maxSubmissionCost,
                              res
                          );
                      }
                      emit DepositInitiated(_l1Token, _from, _to, seqNum, _amount);
                      return abi.encode(seqNum);
                  }
                  function outboundEscrowTransfer(
                      address _l1Token,
                      address _from,
                      uint256 _amount
                  ) internal virtual returns (uint256 amountReceived) {
                      // this method is virtual since different subclasses can handle escrow differently
                      // user funds are escrowed on the gateway using this function
                      uint256 prevBalance = IERC20(_l1Token).balanceOf(address(this));
                      IERC20(_l1Token).safeTransferFrom(_from, address(this), _amount);
                      uint256 postBalance = IERC20(_l1Token).balanceOf(address(this));
                      return SafeMath.sub(postBalance, prevBalance);
                  }
                  function getOutboundCalldata(
                      address _l1Token,
                      address _from,
                      address _to,
                      uint256 _amount,
                      bytes memory _data
                  ) public view virtual override returns (bytes memory outboundCalldata) {
                      // this function is public so users can query how much calldata will be sent to the L2
                      // before execution
                      // it is virtual since different gateway subclasses can build this calldata differently
                      // ( ie the standard ERC20 gateway queries for a tokens name/symbol/decimals )
                      bytes memory emptyBytes = "";
                      outboundCalldata = abi.encodeWithSelector(
                          ITokenGateway.finalizeInboundTransfer.selector,
                          _l1Token,
                          _from,
                          _to,
                          _amount,
                          GatewayMessageHandler.encodeToL2GatewayMsg(emptyBytes, _data)
                      );
                      return outboundCalldata;
                  }
                  function supportsInterface(bytes4 interfaceId)
                      public
                      view
                      virtual
                      override(ERC165, IERC165)
                      returns (bool)
                  {
                      // registering interfaces that is added after arb-bridge-peripherals >1.0.11
                      // using function selector instead of single function interfaces to reduce bloat
                      return
                          interfaceId == this.outboundTransferCustomRefund.selector ||
                          super.supportsInterface(interfaceId);
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.6.0 <0.8.0;
              /**
               * @dev Interface of the ERC20 standard as defined in the EIP.
               */
              interface IERC20Upgradeable {
                  /**
                   * @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 `recipient`.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transfer(address recipient, 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 `sender` to `recipient` 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 sender, address recipient, uint256 amount) external returns (bool);
                  /**
                   * @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);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.6.0 <0.8.0;
              import "../../utils/Context.sol";
              import "./IERC20.sol";
              import "../../math/SafeMath.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 guidelines: functions revert instead
               * of 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 {
                  using SafeMath for uint256;
                  mapping (address => uint256) private _balances;
                  mapping (address => mapping (address => uint256)) private _allowances;
                  uint256 private _totalSupply;
                  string private _name;
                  string private _symbol;
                  uint8 private _decimals;
                  /**
                   * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
                   * a default value of 18.
                   *
                   * To select a different value for {decimals}, use {_setupDecimals}.
                   *
                   * All three of these values are immutable: they can only be set once during
                   * construction.
                   */
                  constructor (string memory name_, string memory symbol_) public {
                      _name = name_;
                      _symbol = symbol_;
                      _decimals = 18;
                  }
                  /**
                   * @dev Returns the name of the token.
                   */
                  function name() public view virtual returns (string memory) {
                      return _name;
                  }
                  /**
                   * @dev Returns the symbol of the token, usually a shorter version of the
                   * name.
                   */
                  function symbol() public view virtual 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 {_setupDecimals} is
                   * called.
                   *
                   * 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 returns (uint8) {
                      return _decimals;
                  }
                  /**
                   * @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:
                   *
                   * - `recipient` cannot be the zero address.
                   * - the caller must have a balance of at least `amount`.
                   */
                  function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
                      _transfer(_msgSender(), recipient, 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}.
                   *
                   * Requirements:
                   *
                   * - `spender` cannot be the zero address.
                   */
                  function approve(address spender, uint256 amount) public virtual override returns (bool) {
                      _approve(_msgSender(), 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}.
                   *
                   * Requirements:
                   *
                   * - `sender` and `recipient` cannot be the zero address.
                   * - `sender` must have a balance of at least `amount`.
                   * - the caller must have allowance for ``sender``'s tokens of at least
                   * `amount`.
                   */
                  function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
                      _transfer(sender, recipient, amount);
                      _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
                      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) {
                      _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(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) {
                      _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
                      return true;
                  }
                  /**
                   * @dev Moves tokens `amount` from `sender` to `recipient`.
                   *
                   * This is 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:
                   *
                   * - `sender` cannot be the zero address.
                   * - `recipient` cannot be the zero address.
                   * - `sender` must have a balance of at least `amount`.
                   */
                  function _transfer(address sender, address recipient, uint256 amount) internal virtual {
                      require(sender != address(0), "ERC20: transfer from the zero address");
                      require(recipient != address(0), "ERC20: transfer to the zero address");
                      _beforeTokenTransfer(sender, recipient, amount);
                      _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
                      _balances[recipient] = _balances[recipient].add(amount);
                      emit Transfer(sender, recipient, 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:
                   *
                   * - `to` 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 = _totalSupply.add(amount);
                      _balances[account] = _balances[account].add(amount);
                      emit Transfer(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);
                      _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
                      _totalSupply = _totalSupply.sub(amount);
                      emit Transfer(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 Sets {decimals} to a value other than the default one of 18.
                   *
                   * WARNING: This function should only be called from the constructor. Most
                   * applications that interact with token contracts will not expect
                   * {decimals} to ever change, and may work incorrectly if it does.
                   */
                  function _setupDecimals(uint8 decimals_) internal virtual {
                      _decimals = decimals_;
                  }
                  /**
                   * @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 to 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 { }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.6.0 <0.8.0;
              import "./IERC20.sol";
              import "../../math/SafeMath.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 SafeMath for uint256;
                  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'
                      // solhint-disable-next-line max-line-length
                      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).add(value);
                      _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                  }
                  function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                      uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
                      _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                  }
                  /**
                   * @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
                          // solhint-disable-next-line max-line-length
                          require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.6.2 <0.8.0;
              /**
               * @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
                   * ====
                   */
                  function isContract(address account) internal view returns (bool) {
                      // This method relies on extcodesize, which returns 0 for contracts in
                      // construction, since the code is only stored at the end of the
                      // constructor execution.
                      uint256 size;
                      // solhint-disable-next-line no-inline-assembly
                      assembly { size := extcodesize(account) }
                      return size > 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");
                      // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                      (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");
                      // solhint-disable-next-line avoid-low-level-calls
                      (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");
                      // solhint-disable-next-line avoid-low-level-calls
                      (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");
                      // solhint-disable-next-line avoid-low-level-calls
                      (bool success, bytes memory returndata) = target.delegatecall(data);
                      return _verifyCallResult(success, returndata, errorMessage);
                  }
                  function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private 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
                              // solhint-disable-next-line no-inline-assembly
                              assembly {
                                  let returndata_size := mload(returndata)
                                  revert(add(32, returndata), returndata_size)
                              }
                          } else {
                              revert(errorMessage);
                          }
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.6.0 <0.8.0;
              /**
               * @dev Wrappers over Solidity's arithmetic operations with added overflow
               * checks.
               *
               * Arithmetic operations in Solidity wrap on overflow. This can easily result
               * in bugs, because programmers usually assume that an overflow raises an
               * error, which is the standard behavior in high level programming languages.
               * `SafeMath` restores this intuition by reverting the transaction when an
               * operation overflows.
               *
               * Using this library instead of the unchecked operations eliminates an entire
               * class of bugs, so it's recommended to use it always.
               */
              library SafeMath {
                  /**
                   * @dev Returns the addition of two unsigned integers, with an overflow flag.
                   *
                   * _Available since v3.4._
                   */
                  function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                      uint256 c = a + b;
                      if (c < a) return (false, 0);
                      return (true, c);
                  }
                  /**
                   * @dev Returns the substraction of two unsigned integers, with an overflow flag.
                   *
                   * _Available since v3.4._
                   */
                  function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                      if (b > a) return (false, 0);
                      return (true, a - b);
                  }
                  /**
                   * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
                   *
                   * _Available since v3.4._
                   */
                  function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                      // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                      // benefit is lost if 'b' is also tested.
                      // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                      if (a == 0) return (true, 0);
                      uint256 c = a * b;
                      if (c / a != b) return (false, 0);
                      return (true, c);
                  }
                  /**
                   * @dev Returns the division of two unsigned integers, with a division by zero flag.
                   *
                   * _Available since v3.4._
                   */
                  function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                      if (b == 0) return (false, 0);
                      return (true, a / b);
                  }
                  /**
                   * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
                   *
                   * _Available since v3.4._
                   */
                  function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                      if (b == 0) return (false, 0);
                      return (true, a % b);
                  }
                  /**
                   * @dev Returns the addition of two unsigned integers, reverting on
                   * overflow.
                   *
                   * Counterpart to Solidity's `+` operator.
                   *
                   * Requirements:
                   *
                   * - Addition cannot overflow.
                   */
                  function add(uint256 a, uint256 b) internal pure returns (uint256) {
                      uint256 c = a + b;
                      require(c >= a, "SafeMath: addition overflow");
                      return c;
                  }
                  /**
                   * @dev Returns the subtraction of two unsigned integers, reverting on
                   * overflow (when the result is negative).
                   *
                   * Counterpart to Solidity's `-` operator.
                   *
                   * Requirements:
                   *
                   * - Subtraction cannot overflow.
                   */
                  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                      require(b <= a, "SafeMath: subtraction overflow");
                      return a - b;
                  }
                  /**
                   * @dev Returns the multiplication of two unsigned integers, reverting on
                   * overflow.
                   *
                   * Counterpart to Solidity's `*` operator.
                   *
                   * Requirements:
                   *
                   * - Multiplication cannot overflow.
                   */
                  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                      if (a == 0) return 0;
                      uint256 c = a * b;
                      require(c / a == b, "SafeMath: multiplication overflow");
                      return c;
                  }
                  /**
                   * @dev Returns the integer division of two unsigned integers, reverting on
                   * division by zero. The result is rounded towards zero.
                   *
                   * Counterpart to Solidity's `/` operator. Note: this function uses a
                   * `revert` opcode (which leaves remaining gas untouched) while Solidity
                   * uses an invalid opcode to revert (consuming all remaining gas).
                   *
                   * Requirements:
                   *
                   * - The divisor cannot be zero.
                   */
                  function div(uint256 a, uint256 b) internal pure returns (uint256) {
                      require(b > 0, "SafeMath: division by zero");
                      return a / b;
                  }
                  /**
                   * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                   * reverting when dividing by zero.
                   *
                   * Counterpart to Solidity's `%` operator. This function uses a `revert`
                   * opcode (which leaves remaining gas untouched) while Solidity uses an
                   * invalid opcode to revert (consuming all remaining gas).
                   *
                   * Requirements:
                   *
                   * - The divisor cannot be zero.
                   */
                  function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                      require(b > 0, "SafeMath: modulo by zero");
                      return a % b;
                  }
                  /**
                   * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
                   * overflow (when the result is negative).
                   *
                   * CAUTION: This function is deprecated because it requires allocating memory for the error
                   * message unnecessarily. For custom revert reasons use {trySub}.
                   *
                   * Counterpart to Solidity's `-` operator.
                   *
                   * Requirements:
                   *
                   * - Subtraction cannot overflow.
                   */
                  function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                      require(b <= a, errorMessage);
                      return a - b;
                  }
                  /**
                   * @dev Returns the integer division of two unsigned integers, reverting with custom message on
                   * division by zero. The result is rounded towards zero.
                   *
                   * CAUTION: This function is deprecated because it requires allocating memory for the error
                   * message unnecessarily. For custom revert reasons use {tryDiv}.
                   *
                   * Counterpart to Solidity's `/` operator. Note: this function uses a
                   * `revert` opcode (which leaves remaining gas untouched) while Solidity
                   * uses an invalid opcode to revert (consuming all remaining gas).
                   *
                   * Requirements:
                   *
                   * - The divisor cannot be zero.
                   */
                  function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                      require(b > 0, errorMessage);
                      return a / b;
                  }
                  /**
                   * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                   * reverting with custom message when dividing by zero.
                   *
                   * CAUTION: This function is deprecated because it requires allocating memory for the error
                   * message unnecessarily. For custom revert reasons use {tryMod}.
                   *
                   * Counterpart to Solidity's `%` operator. This function uses a `revert`
                   * opcode (which leaves remaining gas untouched) while Solidity uses an
                   * invalid opcode to revert (consuming all remaining gas).
                   *
                   * Requirements:
                   *
                   * - The divisor cannot be zero.
                   */
                  function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                      require(b > 0, errorMessage);
                      return a % b;
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              /*
               * Copyright 2021, Offchain Labs, Inc.
               *
               * Licensed under the Apache License, Version 2.0 (the "License");
               * you may not use this file except in compliance with the License.
               * You may obtain a copy of the License at
               *
               *    http://www.apache.org/licenses/LICENSE-2.0
               *
               * Unless required by applicable law or agreed to in writing, software
               * distributed under the License is distributed on an "AS IS" BASIS,
               * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
               * See the License for the specific language governing permissions and
               * limitations under the License.
               */
              // solhint-disable-next-line compiler-version
              pragma solidity >=0.6.9 <0.9.0;
              import "./IBridge.sol";
              import "./IMessageProvider.sol";
              interface IInbox is IMessageProvider {
                  function sendL2Message(bytes calldata messageData) external returns (uint256);
                  function sendUnsignedTransaction(
                      uint256 maxGas,
                      uint256 gasPriceBid,
                      uint256 nonce,
                      address destAddr,
                      uint256 amount,
                      bytes calldata data
                  ) external returns (uint256);
                  function sendContractTransaction(
                      uint256 maxGas,
                      uint256 gasPriceBid,
                      address destAddr,
                      uint256 amount,
                      bytes calldata data
                  ) external returns (uint256);
                  function sendL1FundedUnsignedTransaction(
                      uint256 maxGas,
                      uint256 gasPriceBid,
                      uint256 nonce,
                      address destAddr,
                      bytes calldata data
                  ) external payable returns (uint256);
                  function sendL1FundedContractTransaction(
                      uint256 maxGas,
                      uint256 gasPriceBid,
                      address destAddr,
                      bytes calldata data
                  ) external payable returns (uint256);
                  function createRetryableTicket(
                      address destAddr,
                      uint256 arbTxCallValue,
                      uint256 maxSubmissionCost,
                      address submissionRefundAddress,
                      address valueRefundAddress,
                      uint256 maxGas,
                      uint256 gasPriceBid,
                      bytes calldata data
                  ) external payable returns (uint256);
                  function unsafeCreateRetryableTicket(
                      address destAddr,
                      uint256 arbTxCallValue,
                      uint256 maxSubmissionCost,
                      address submissionRefundAddress,
                      address valueRefundAddress,
                      uint256 maxGas,
                      uint256 gasPriceBid,
                      bytes calldata data
                  ) external payable returns (uint256);
                  function depositEth(uint256 maxSubmissionCost) external payable returns (uint256);
                  function bridge() external view returns (IBridge);
                  function pauseCreateRetryables() external;
                  function unpauseCreateRetryables() external;
                  function startRewriteAddress() external;
                  function stopRewriteAddress() external;
              }
              // SPDX-License-Identifier: Apache-2.0
              /*
               * Copyright 2021, Offchain Labs, Inc.
               *
               * Licensed under the Apache License, Version 2.0 (the "License");
               * you may not use this file except in compliance with the License.
               * You may obtain a copy of the License at
               *
               *    http://www.apache.org/licenses/LICENSE-2.0
               *
               * Unless required by applicable law or agreed to in writing, software
               * distributed under the License is distributed on an "AS IS" BASIS,
               * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
               * See the License for the specific language governing permissions and
               * limitations under the License.
               */
              pragma solidity ^0.6.11;
              library ProxyUtil {
                  function getProxyAdmin() internal view returns (address admin) {
                      // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.0/contracts/proxy/TransparentUpgradeableProxy.sol#L48
                      // Storage slot with the admin of the proxy contract.
                      // This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
                      bytes32 slot = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                      assembly {
                          admin := sload(slot)
                      }
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              /*
               * Copyright 2020, Offchain Labs, Inc.
               *
               * Licensed under the Apache License, Version 2.0 (the "License");
               * you may not use this file except in compliance with the License.
               * You may obtain a copy of the License at
               *
               *    http://www.apache.org/licenses/LICENSE-2.0
               *
               * Unless required by applicable law or agreed to in writing, software
               * distributed under the License is distributed on an "AS IS" BASIS,
               * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
               * See the License for the specific language governing permissions and
               * limitations under the License.
               */
              pragma solidity ^0.6.11;
              import "arb-bridge-eth/contracts/bridge/interfaces/IInbox.sol";
              import "arb-bridge-eth/contracts/bridge/interfaces/IOutbox.sol";
              /// @notice L1 utility contract to assist with L1 <=> L2 interactions
              /// @dev this is an abstract contract instead of library so the functions can be easily overriden when testing
              abstract contract L1ArbitrumMessenger {
                  event TxToL2(address indexed _from, address indexed _to, uint256 indexed _seqNum, bytes _data);
                  struct L2GasParams {
                      uint256 _maxSubmissionCost;
                      uint256 _maxGas;
                      uint256 _gasPriceBid;
                  }
                  function sendTxToL2CustomRefund(
                      address _inbox,
                      address _to,
                      address _refundTo,
                      address _user,
                      uint256 _l1CallValue,
                      uint256 _l2CallValue,
                      L2GasParams memory _l2GasParams,
                      bytes memory _data
                  ) internal returns (uint256) {
                      // alternative function entry point when struggling with the stack size
                      return
                          sendTxToL2CustomRefund(
                              _inbox,
                              _to,
                              _refundTo,
                              _user,
                              _l1CallValue,
                              _l2CallValue,
                              _l2GasParams._maxSubmissionCost,
                              _l2GasParams._maxGas,
                              _l2GasParams._gasPriceBid,
                              _data
                          );
                  }
                  function sendTxToL2(
                      address _inbox,
                      address _to,
                      address _user,
                      uint256 _l1CallValue,
                      uint256 _l2CallValue,
                      L2GasParams memory _l2GasParams,
                      bytes memory _data
                  ) internal returns (uint256) {
                      // alternative function entry point when struggling with the stack size
                      return
                          sendTxToL2(
                              _inbox,
                              _to,
                              _user,
                              _l1CallValue,
                              _l2CallValue,
                              _l2GasParams._maxSubmissionCost,
                              _l2GasParams._maxGas,
                              _l2GasParams._gasPriceBid,
                              _data
                          );
                  }
                  function sendTxToL2CustomRefund(
                      address _inbox,
                      address _to,
                      address _refundTo,
                      address _user,
                      uint256 _l1CallValue,
                      uint256 _l2CallValue,
                      uint256 _maxSubmissionCost,
                      uint256 _maxGas,
                      uint256 _gasPriceBid,
                      bytes memory _data
                  ) internal returns (uint256) {
                      uint256 seqNum = IInbox(_inbox).createRetryableTicket{ value: _l1CallValue }(
                          _to,
                          _l2CallValue,
                          _maxSubmissionCost,
                          _refundTo, // only refund excess fee to the custom address
                          _user, // user can cancel the retryable and receive call value refund
                          _maxGas,
                          _gasPriceBid,
                          _data
                      );
                      emit TxToL2(_user, _to, seqNum, _data);
                      return seqNum;
                  }
                  function sendTxToL2(
                      address _inbox,
                      address _to,
                      address _user,
                      uint256 _l1CallValue,
                      uint256 _l2CallValue,
                      uint256 _maxSubmissionCost,
                      uint256 _maxGas,
                      uint256 _gasPriceBid,
                      bytes memory _data
                  ) internal returns (uint256) {
                      return
                          sendTxToL2CustomRefund(
                              _inbox,
                              _to,
                              _user,
                              _user,
                              _l1CallValue,
                              _l2CallValue,
                              _maxSubmissionCost,
                              _maxGas,
                              _gasPriceBid,
                              _data
                          );
                  }
                  function getBridge(address _inbox) internal view returns (IBridge) {
                      return IInbox(_inbox).bridge();
                  }
                  /// @dev the l2ToL1Sender behaves as the tx.origin, the msg.sender should be validated to protect against reentrancies
                  function getL2ToL1Sender(address _inbox) internal view returns (address) {
                      IOutbox outbox = IOutbox(getBridge(_inbox).activeOutbox());
                      address l2ToL1Sender = outbox.l2ToL1Sender();
                      require(l2ToL1Sender != address(0), "NO_SENDER");
                      return l2ToL1Sender;
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              /*
               * Copyright 2021, Offchain Labs, Inc.
               *
               * Licensed under the Apache License, Version 2.0 (the "License");
               * you may not use this file except in compliance with the License.
               * You may obtain a copy of the License at
               *
               *    http://www.apache.org/licenses/LICENSE-2.0
               *
               * Unless required by applicable law or agreed to in writing, software
               * distributed under the License is distributed on an "AS IS" BASIS,
               * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
               * See the License for the specific language governing permissions and
               * limitations under the License.
               */
              pragma solidity ^0.6.11;
              /// @notice this library manages encoding and decoding of gateway communication
              library GatewayMessageHandler {
                  // these are for communication from L1 to L2 gateway
                  function encodeToL2GatewayMsg(bytes memory gatewayData, bytes memory callHookData)
                      internal
                      pure
                      returns (bytes memory res)
                  {
                      res = abi.encode(gatewayData, callHookData);
                  }
                  function parseFromL1GatewayMsg(bytes calldata _data)
                      internal
                      pure
                      returns (bytes memory gatewayData, bytes memory callHookData)
                  {
                      // abi decode may revert, but the encoding is done by L1 gateway, so we trust it
                      (gatewayData, callHookData) = abi.decode(_data, (bytes, bytes));
                  }
                  // these are for communication from L2 to L1 gateway
                  function encodeFromL2GatewayMsg(uint256 exitNum, bytes memory callHookData)
                      internal
                      pure
                      returns (bytes memory res)
                  {
                      res = abi.encode(exitNum, callHookData);
                  }
                  function parseToL1GatewayMsg(bytes calldata _data)
                      internal
                      pure
                      returns (uint256 exitNum, bytes memory callHookData)
                  {
                      // abi decode may revert, but the encoding is done by L1 gateway, so we trust it
                      (exitNum, callHookData) = abi.decode(_data, (uint256, bytes));
                  }
                  // these are for communication from router to gateway
                  function encodeFromRouterToGateway(address _from, bytes calldata _data)
                      internal
                      pure
                      returns (bytes memory res)
                  {
                      // abi decode may revert, but the encoding is done by L1 gateway, so we trust it
                      return abi.encode(_from, _data);
                  }
                  function parseFromRouterToGateway(bytes calldata _data)
                      internal
                      pure
                      returns (address, bytes memory res)
                  {
                      // abi decode may revert, but the encoding is done by L1 gateway, so we trust it
                      return abi.decode(_data, (address, bytes));
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              /*
               * Copyright 2020, Offchain Labs, Inc.
               *
               * Licensed under the Apache License, Version 2.0 (the "License");
               * you may not use this file except in compliance with the License.
               * You may obtain a copy of the License at
               *
               *    http://www.apache.org/licenses/LICENSE-2.0
               *
               * Unless required by applicable law or agreed to in writing, software
               * distributed under the License is distributed on an "AS IS" BASIS,
               * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
               * See the License for the specific language governing permissions and
               * limitations under the License.
               */
              pragma solidity ^0.6.11;
              import "./ITokenGateway.sol";
              import "@openzeppelin/contracts/utils/Address.sol";
              abstract contract TokenGateway is ITokenGateway {
                  using Address for address;
                  address public counterpartGateway;
                  address public router;
                  // This modifier is overriden in gateways to validate the message sender
                  // For L1 to L2 messages need to be validated against the aliased counterpartGateway
                  // For L2 to L1 messages need to be validated against the bridge and L2ToL1Sender
                  // prettier-ignore
                  modifier onlyCounterpartGateway() virtual;
                  function _initialize(address _counterpartGateway, address _router) internal virtual {
                      // This initializes internal variables of the abstract contract it can be chained together with other functions.
                      // It is virtual so subclasses can override or wrap around this logic.
                      // An example where this is useful is different subclasses that validate the router address differently
                      require(_counterpartGateway != address(0), "INVALID_COUNTERPART");
                      require(counterpartGateway == address(0), "ALREADY_INIT");
                      counterpartGateway = _counterpartGateway;
                      router = _router;
                  }
                  function isRouter(address _target) internal view returns (bool isTargetRouter) {
                      return _target == router;
                  }
                  /**
                   * @notice Calculate the address used when bridging an ERC20 token
                   * @dev the L1 and L2 address oracles may not always be in sync.
                   * For example, a custom token may have been registered but not deploy or the contract self destructed.
                   * @param l1ERC20 address of L1 token
                   * @return L2 address of a bridged ERC20 token
                   */
                  function calculateL2TokenAddress(address l1ERC20)
                      public
                      view
                      virtual
                      override
                      returns (address);
              }
              // SPDX-License-Identifier: Apache-2.0
              /*
               * Copyright 2020, Offchain Labs, Inc.
               *
               * Licensed under the Apache License, Version 2.0 (the "License");
               * you may not use this file except in compliance with the License.
               * You may obtain a copy of the License at
               *
               *    http://www.apache.org/licenses/LICENSE-2.0
               *
               * Unless required by applicable law or agreed to in writing, software
               * distributed under the License is distributed on an "AS IS" BASIS,
               * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
               * See the License for the specific language governing permissions and
               * limitations under the License.
               */
              // solhint-disable-next-line compiler-version
              pragma solidity >=0.6.9 <0.9.0;
              import "../../libraries/gateway/ITokenGateway.sol";
              import "../../libraries/IERC165.sol";
              /**
               * @title Common interface for gatways on L1 messaging to Arbitrum.
               */
              interface IL1ArbitrumGateway is ITokenGateway, IERC165 {
                  function inbox() external view returns (address);
                  /**
                   * @notice Deposit ERC20 token from Ethereum into Arbitrum. If L2 side hasn't been deployed yet, includes name/symbol/decimals data for initial L2 deploy. Initiate by GatewayRouter.
                   * @dev L2 address alias will not be applied to the following types of addresses on L1:
                   *      - 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
                   * @param _l1Token L1 address of ERC20
                   * @param _refundTo Account, or its L2 alias if it have code in L1, to be credited with excess gas refund in L2
                   * @param _to Account to be credited with the tokens in the L2 (can be the user's L2 account or a contract), not subject to L2 aliasing
                                This account, or its L2 alias if it have code in L1, will also be able to cancel the retryable ticket and receive callvalue refund
                   * @param _amount Token Amount
                   * @param _maxGas Max gas deducted from user's L2 balance to cover L2 execution
                   * @param _gasPriceBid Gas price for L2 execution
                   * @param _data encoded data from router and user
                   * @return res abi encoded inbox sequence number
                   */
                  //  * @param maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee
                  function outboundTransferCustomRefund(
                      address _l1Token,
                      address _refundTo,
                      address _to,
                      uint256 _amount,
                      uint256 _maxGas,
                      uint256 _gasPriceBid,
                      bytes calldata _data
                  ) external payable returns (bytes memory);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
              // With pragma modification to support ^0.6.11
              // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.6/contracts/utils/introspection/ERC165.sol
              pragma solidity ^0.6.11;
              import "./IERC165.sol";
              /**
               * @dev Implementation of the {IERC165} interface.
               *
               * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
               * for the additional interface id that will be supported. For example:
               *
               * ```solidity
               * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
               *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
               * }
               * ```
               *
               * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
               */
              abstract contract ERC165 is IERC165 {
                  /**
                   * @dev See {IERC165-supportsInterface}.
                   */
                  function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                      return interfaceId == type(IERC165).interfaceId;
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.6.0 <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 GSN 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 payable) {
                      return msg.sender;
                  }
                  function _msgData() internal view virtual returns (bytes memory) {
                      this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                      return msg.data;
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.6.0 <0.8.0;
              /**
               * @dev Interface of the ERC20 standard as defined in the EIP.
               */
              interface IERC20 {
                  /**
                   * @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 `recipient`.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transfer(address recipient, 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 `sender` to `recipient` 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 sender, address recipient, uint256 amount) external returns (bool);
                  /**
                   * @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);
              }
              // SPDX-License-Identifier: Apache-2.0
              /*
               * Copyright 2021, Offchain Labs, Inc.
               *
               * Licensed under the Apache License, Version 2.0 (the "License");
               * you may not use this file except in compliance with the License.
               * You may obtain a copy of the License at
               *
               *    http://www.apache.org/licenses/LICENSE-2.0
               *
               * Unless required by applicable law or agreed to in writing, software
               * distributed under the License is distributed on an "AS IS" BASIS,
               * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
               * See the License for the specific language governing permissions and
               * limitations under the License.
               */
              // solhint-disable-next-line compiler-version
              pragma solidity >=0.6.9 <0.9.0;
              interface IBridge {
                  event MessageDelivered(
                      uint256 indexed messageIndex,
                      bytes32 indexed beforeInboxAcc,
                      address inbox,
                      uint8 kind,
                      address sender,
                      bytes32 messageDataHash
                  );
                  event BridgeCallTriggered(
                      address indexed outbox,
                      address indexed destAddr,
                      uint256 amount,
                      bytes data
                  );
                  event InboxToggle(address indexed inbox, bool enabled);
                  event OutboxToggle(address indexed outbox, bool enabled);
                  function deliverMessageToInbox(
                      uint8 kind,
                      address sender,
                      bytes32 messageDataHash
                  ) external payable returns (uint256);
                  function executeCall(
                      address destAddr,
                      uint256 amount,
                      bytes calldata data
                  ) external returns (bool success, bytes memory returnData);
                  // These are only callable by the admin
                  function setInbox(address inbox, bool enabled) external;
                  function setOutbox(address inbox, bool enabled) external;
                  // View functions
                  function activeOutbox() external view returns (address);
                  function allowedInboxes(address inbox) external view returns (bool);
                  function allowedOutboxes(address outbox) external view returns (bool);
                  function inboxAccs(uint256 index) external view returns (bytes32);
                  function messageCount() external view returns (uint256);
              }
              // SPDX-License-Identifier: Apache-2.0
              /*
               * Copyright 2021, Offchain Labs, Inc.
               *
               * Licensed under the Apache License, Version 2.0 (the "License");
               * you may not use this file except in compliance with the License.
               * You may obtain a copy of the License at
               *
               *    http://www.apache.org/licenses/LICENSE-2.0
               *
               * Unless required by applicable law or agreed to in writing, software
               * distributed under the License is distributed on an "AS IS" BASIS,
               * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
               * See the License for the specific language governing permissions and
               * limitations under the License.
               */
              // solhint-disable-next-line compiler-version
              pragma solidity >=0.6.9 <0.9.0;
              interface IMessageProvider {
                  event InboxMessageDelivered(uint256 indexed messageNum, bytes data);
                  event InboxMessageDeliveredFromOrigin(uint256 indexed messageNum);
              }
              // SPDX-License-Identifier: Apache-2.0
              /*
               * Copyright 2021, Offchain Labs, Inc.
               *
               * Licensed under the Apache License, Version 2.0 (the "License");
               * you may not use this file except in compliance with the License.
               * You may obtain a copy of the License at
               *
               *    http://www.apache.org/licenses/LICENSE-2.0
               *
               * Unless required by applicable law or agreed to in writing, software
               * distributed under the License is distributed on an "AS IS" BASIS,
               * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
               * See the License for the specific language governing permissions and
               * limitations under the License.
               */
              // solhint-disable-next-line compiler-version
              pragma solidity >=0.6.9 <0.9.0;
              interface IOutbox {
                  event OutboxEntryCreated(
                      uint256 indexed batchNum,
                      uint256 outboxEntryIndex,
                      bytes32 outputRoot,
                      uint256 numInBatch
                  );
                  event OutBoxTransactionExecuted(
                      address indexed destAddr,
                      address indexed l2Sender,
                      uint256 indexed outboxEntryIndex,
                      uint256 transactionIndex
                  );
                  function l2ToL1Sender() external view returns (address);
                  function l2ToL1Block() external view returns (uint256);
                  function l2ToL1EthBlock() external view returns (uint256);
                  function l2ToL1Timestamp() external view returns (uint256);
                  function l2ToL1BatchNum() external view returns (uint256);
                  function l2ToL1OutputId() external view returns (bytes32);
                  function processOutgoingMessages(bytes calldata sendsData, uint256[] calldata sendLengths)
                      external;
                  function outboxEntryExists(uint256 batchNum) external view returns (bool);
              }
              // SPDX-License-Identifier: Apache-2.0
              /*
               * Copyright 2020, Offchain Labs, Inc.
               *
               * Licensed under the Apache License, Version 2.0 (the "License");
               * you may not use this file except in compliance with the License.
               * You may obtain a copy of the License at
               *
               *    http://www.apache.org/licenses/LICENSE-2.0
               *
               * Unless required by applicable law or agreed to in writing, software
               * distributed under the License is distributed on an "AS IS" BASIS,
               * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
               * See the License for the specific language governing permissions and
               * limitations under the License.
               */
              // solhint-disable-next-line compiler-version
              pragma solidity >=0.6.9 <0.9.0;
              interface ITokenGateway {
                  /// @notice event deprecated in favor of DepositInitiated and WithdrawalInitiated
                  // event OutboundTransferInitiated(
                  //     address token,
                  //     address indexed _from,
                  //     address indexed _to,
                  //     uint256 indexed _transferId,
                  //     uint256 _amount,
                  //     bytes _data
                  // );
                  /// @notice event deprecated in favor of DepositFinalized and WithdrawalFinalized
                  // event InboundTransferFinalized(
                  //     address token,
                  //     address indexed _from,
                  //     address indexed _to,
                  //     uint256 indexed _transferId,
                  //     uint256 _amount,
                  //     bytes _data
                  // );
                  function outboundTransfer(
                      address _token,
                      address _to,
                      uint256 _amount,
                      uint256 _maxGas,
                      uint256 _gasPriceBid,
                      bytes calldata _data
                  ) external payable returns (bytes memory);
                  function finalizeInboundTransfer(
                      address _token,
                      address _from,
                      address _to,
                      uint256 _amount,
                      bytes calldata _data
                  ) external payable;
                  /**
                   * @notice Calculate the address used when bridging an ERC20 token
                   * @dev the L1 and L2 address oracles may not always be in sync.
                   * For example, a custom token may have been registered but not deploy or the contract self destructed.
                   * @param l1ERC20 address of L1 token
                   * @return L2 address of a bridged ERC20 token
                   */
                  function calculateL2TokenAddress(address l1ERC20) external view returns (address);
                  function getOutboundCalldata(
                      address _token,
                      address _from,
                      address _to,
                      uint256 _amount,
                      bytes memory _data
                  ) external view returns (bytes memory);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
              // With pragma modification to allow interface compatibility with >=0.6.9 <0.9.0
              // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.6/contracts/utils/introspection/IERC165.sol
              // solhint-disable-next-line compiler-version
              pragma solidity >=0.6.9 <0.9.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);
              }
              

              File 8 of 8: TransparentUpgradeableProxy
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.6.0 <0.8.0;
              /**
               * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
               * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
               * be specified by overriding the virtual {_implementation} function.
               *
               * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
               * different contract through the {_delegate} function.
               *
               * The success and return data of the delegated call will be returned back to the caller of the proxy.
               */
              abstract contract Proxy {
                  /**
                   * @dev Delegates the current call to `implementation`.
                   *
                   * This function does not return to its internall call site, it will return directly to the external caller.
                   */
                  function _delegate(address implementation) internal virtual {
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          // Copy msg.data. We take full control of memory in this inline assembly
                          // block because it will not return to Solidity code. We overwrite the
                          // Solidity scratch pad at memory position 0.
                          calldatacopy(0, 0, calldatasize())
                          // Call the implementation.
                          // out and outsize are 0 because we don't know the size yet.
                          let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
                          // Copy the returned data.
                          returndatacopy(0, 0, returndatasize())
                          switch result
                          // delegatecall returns 0 on error.
                          case 0 { revert(0, returndatasize()) }
                          default { return(0, returndatasize()) }
                      }
                  }
                  /**
                   * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
                   * and {_fallback} should delegate.
                   */
                  function _implementation() internal view virtual returns (address);
                  /**
                   * @dev Delegates the current call to the address returned by `_implementation()`.
                   *
                   * This function does not return to its internall call site, it will return directly to the external caller.
                   */
                  function _fallback() internal virtual {
                      _beforeFallback();
                      _delegate(_implementation());
                  }
                  /**
                   * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
                   * function in the contract matches the call data.
                   */
                  fallback () external payable virtual {
                      _fallback();
                  }
                  /**
                   * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
                   * is empty.
                   */
                  receive () external payable virtual {
                      _fallback();
                  }
                  /**
                   * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
                   * call, or as part of the Solidity `fallback` or `receive` functions.
                   *
                   * If overriden should call `super._beforeFallback()`.
                   */
                  function _beforeFallback() internal virtual {
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.6.0 <0.8.0;
              import "./UpgradeableProxy.sol";
              /**
               * @dev This contract implements a proxy that is upgradeable by an admin.
               *
               * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
               * clashing], which can potentially be used in an attack, this contract uses the
               * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
               * things that go hand in hand:
               *
               * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
               * that call matches one of the admin functions exposed by the proxy itself.
               * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
               * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
               * "admin cannot fallback to proxy target".
               *
               * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
               * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
               * to sudden errors when trying to call a function from the proxy implementation.
               *
               * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
               * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
               */
              contract TransparentUpgradeableProxy is UpgradeableProxy {
                  /**
                   * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
                   * optionally initialized with `_data` as explained in {UpgradeableProxy-constructor}.
                   */
                  constructor(address _logic, address admin_, bytes memory _data) public payable UpgradeableProxy(_logic, _data) {
                      assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
                      _setAdmin(admin_);
                  }
                  /**
                   * @dev Emitted when the admin account has changed.
                   */
                  event AdminChanged(address previousAdmin, address newAdmin);
                  /**
                   * @dev Storage slot with the admin of the contract.
                   * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
                   * validated in the constructor.
                   */
                  bytes32 private constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                  /**
                   * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
                   */
                  modifier ifAdmin() {
                      if (msg.sender == _admin()) {
                          _;
                      } else {
                          _fallback();
                      }
                  }
                  /**
                   * @dev Returns the current admin.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                   * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
                   */
                  function admin() external ifAdmin returns (address admin_) {
                      admin_ = _admin();
                  }
                  /**
                   * @dev Returns the current implementation.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                   * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
                   */
                  function implementation() external ifAdmin returns (address implementation_) {
                      implementation_ = _implementation();
                  }
                  /**
                   * @dev Changes the admin of the proxy.
                   *
                   * Emits an {AdminChanged} event.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
                   */
                  function changeAdmin(address newAdmin) external virtual ifAdmin {
                      require(newAdmin != address(0), "TransparentUpgradeableProxy: new admin is the zero address");
                      emit AdminChanged(_admin(), newAdmin);
                      _setAdmin(newAdmin);
                  }
                  /**
                   * @dev Upgrade the implementation of the proxy.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
                   */
                  function upgradeTo(address newImplementation) external virtual ifAdmin {
                      _upgradeTo(newImplementation);
                  }
                  /**
                   * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
                   * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
                   * proxied contract.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
                   */
                  function upgradeToAndCall(address newImplementation, bytes calldata data) external payable virtual ifAdmin {
                      _upgradeTo(newImplementation);
                      Address.functionDelegateCall(newImplementation, data);
                  }
                  /**
                   * @dev Returns the current admin.
                   */
                  function _admin() internal view virtual returns (address adm) {
                      bytes32 slot = _ADMIN_SLOT;
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          adm := sload(slot)
                      }
                  }
                  /**
                   * @dev Stores a new address in the EIP1967 admin slot.
                   */
                  function _setAdmin(address newAdmin) private {
                      bytes32 slot = _ADMIN_SLOT;
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          sstore(slot, newAdmin)
                      }
                  }
                  /**
                   * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
                   */
                  function _beforeFallback() internal virtual override {
                      require(msg.sender != _admin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
                      super._beforeFallback();
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.6.0 <0.8.0;
              import "./Proxy.sol";
              import "../utils/Address.sol";
              /**
               * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
               * implementation address that can be changed. This address is stored in storage in the location specified by
               * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
               * implementation behind the proxy.
               *
               * Upgradeability is only provided internally through {_upgradeTo}. For an externally upgradeable proxy see
               * {TransparentUpgradeableProxy}.
               */
              contract UpgradeableProxy is Proxy {
                  /**
                   * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
                   *
                   * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
                   * function call, and allows initializating the storage of the proxy like a Solidity constructor.
                   */
                  constructor(address _logic, bytes memory _data) public payable {
                      assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
                      _setImplementation(_logic);
                      if(_data.length > 0) {
                          Address.functionDelegateCall(_logic, _data);
                      }
                  }
                  /**
                   * @dev Emitted when the implementation is upgraded.
                   */
                  event Upgraded(address indexed implementation);
                  /**
                   * @dev Storage slot with the address of the current implementation.
                   * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                   * validated in the constructor.
                   */
                  bytes32 private constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function _implementation() internal view virtual override returns (address impl) {
                      bytes32 slot = _IMPLEMENTATION_SLOT;
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          impl := sload(slot)
                      }
                  }
                  /**
                   * @dev Upgrades the proxy to a new implementation.
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeTo(address newImplementation) internal virtual {
                      _setImplementation(newImplementation);
                      emit Upgraded(newImplementation);
                  }
                  /**
                   * @dev Stores a new address in the EIP1967 implementation slot.
                   */
                  function _setImplementation(address newImplementation) private {
                      require(Address.isContract(newImplementation), "UpgradeableProxy: new implementation is not a contract");
                      bytes32 slot = _IMPLEMENTATION_SLOT;
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          sstore(slot, newImplementation)
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.6.2 <0.8.0;
              /**
               * @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
                   * ====
                   */
                  function isContract(address account) internal view returns (bool) {
                      // This method relies on extcodesize, which returns 0 for contracts in
                      // construction, since the code is only stored at the end of the
                      // constructor execution.
                      uint256 size;
                      // solhint-disable-next-line no-inline-assembly
                      assembly { size := extcodesize(account) }
                      return size > 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");
                      // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                      (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");
                      // solhint-disable-next-line avoid-low-level-calls
                      (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");
                      // solhint-disable-next-line avoid-low-level-calls
                      (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");
                      // solhint-disable-next-line avoid-low-level-calls
                      (bool success, bytes memory returndata) = target.delegatecall(data);
                      return _verifyCallResult(success, returndata, errorMessage);
                  }
                  function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private 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
                              // solhint-disable-next-line no-inline-assembly
                              assembly {
                                  let returndata_size := mload(returndata)
                                  revert(add(32, returndata), returndata_size)
                              }
                          } else {
                              revert(errorMessage);
                          }
                      }
                  }
              }