ETH Price: $2,425.89 (-0.98%)

Transaction Decoder

Block:
22788935 at Jun-26-2025 01:32:11 PM +UTC
Transaction Fee:
0.000809065337335203 ETH $1.96
Gas Used:
173,667 Gas / 4.658716609 Gwei

Emitted Events:

68 Token.Approval( owner=[Receiver] 0x503f48501a9c0de2543a5b5f961a489b809edaf3, spender=TransparentUpgradeableProxy, value=501226038876433000000000 )
69 Token.Transfer( from=[Receiver] 0x503f48501a9c0de2543a5b5f961a489b809edaf3, to=TransparentUpgradeableProxy, value=501226038876433000000000 )
70 Token.Approval( owner=[Receiver] 0x503f48501a9c0de2543a5b5f961a489b809edaf3, spender=TransparentUpgradeableProxy, value=0 )
71 TransparentUpgradeableProxy.0x5e3c1311ea442664e8b1611bfabef659120ea7a0a2cfc0667700bebc69cbffe1( 0x5e3c1311ea442664e8b1611bfabef659120ea7a0a2cfc0667700bebc69cbffe1, 0x00000000000000000000000000000000000000000000000000000000000006e9, 0x88ca9dd7357133da6a2fe82f9107abf21ca69a4ddc15514d1c2ec6a07dc3342b, 000000000000000000000000e92df19f4e0fd067fe3b788cf03ffd06cd9be4a7, 0000000000000000000000000000000000000000000000000000000000000009, 000000000000000000000000615048501a9c0de2543a5b5f961a489b809eec04, 18800523b8ef3f453907083a6bae9ed0e6b77d6a94a748827b084e9b450ea691, 0000000000000000000000000000000000000000000000000000000000000000, 00000000000000000000000000000000000000000000000000000000685d4bdb )
72 Token.Transfer( from=TransparentUpgradeableProxy, to=TransparentUpgradeableProxy, value=501226038876433000000000 )
73 Token.Approval( owner=TransparentUpgradeableProxy, spender=TransparentUpgradeableProxy, value=115792089237316195423570985008687907853269984665639729072974459223918922169673 )
74 TransparentUpgradeableProxy.0xff64905f73a67fb594e0f940a8075a860db489ad991e032f48c81123eb52d60b( 0xff64905f73a67fb594e0f940a8075a860db489ad991e032f48c81123eb52d60b, 0x00000000000000000000000000000000000000000000000000000000000006e9, 0000000000000000000000000000000000000000000000000000000000000020, 0000000000000000000000000000000000000000000000000000000000000120, 000000000000000000000000503f48501a9c0de2543a5b5f961a489b809edaf3, 000000000000000000000000000000000000000000006a2384946754b98a8600, 000000000000000000000000000000000000000000006a23849c7cbf60816a00, 0000000000000000000000000000000000000000000000000008050bb35bc400, 000000000000000000000000615048501a9c0de2543a5b5f961a489b809eec04, 000000000000000000000000615048501a9c0de2543a5b5f961a489b809eec04, 00000000000000000000000000000000000000000000000000000000000493e0, 0000000000000000000000000000000000000000000000000000000003938700, 0000000000000000000000000000000000000000000000000000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
(Titan Builder)
6.484474145650498441 Eth6.484821479650498441 Eth0.000347334
0x503f4850...B809edAF3
0.016691478029326054 Eth
Nonce: 57
0.015882412691990851 Eth
Nonce: 59
0.000809065337335203From: 0 To: 22892026855592066050609947431602401211538835161166308139
0x93aA0ccD...eb04085d6
0xd3643255...7CD62E499

Execution Trace

0x503f48501a9c0de2543a5b5f961a489b809edaf3.e9ae5c53( )
  • Token.approve( to=0xE92Df19F4e0Fd067FE3b788Cf03ffD06Cd9Be4A7, amount=501226038876433000000000 ) => ( True )
  • TransparentUpgradeableProxy.549e8426( )
    • ERC20Inbox.createRetryableTicket( to=0x503f48501A9C0DE2543A5B5f961a489B809edAF3, l2CallValue=501226036601085374400000, maxSubmissionCost=2257347625600000, excessFeeRefundAddress=0x503f48501A9C0DE2543A5B5f961a489B809edAF3, callValueRefundAddress=0x503f48501A9C0DE2543A5B5f961a489B809edAF3, gasLimit=300000, maxFeePerGas=60000000, tokenTotalFeeAmount=501226038876433000000000, data=0x ) => ( 1769 )
      • TransparentUpgradeableProxy.STATICCALL( )
        • ERC20Bridge.DELEGATECALL( )
        • TransparentUpgradeableProxy.STATICCALL( )
          • ERC20Bridge.DELEGATECALL( )
          • TransparentUpgradeableProxy.STATICCALL( )
            • ERC20Bridge.DELEGATECALL( )
            • Token.balanceOf( account=0xE92Df19F4e0Fd067FE3b788Cf03ffD06Cd9Be4A7 ) => ( 0 )
            • Token.transferFrom( sender=0x503f48501A9C0DE2543A5B5f961a489B809edAF3, recipient=0xE92Df19F4e0Fd067FE3b788Cf03ffD06Cd9Be4A7, amount=501226038876433000000000 ) => ( True )
            • TransparentUpgradeableProxy.75d81e25( )
              • ERC20Bridge.enqueueDelayedMessage( kind=9, sender=0x615048501a9C0de2543a5B5f961a489B809EEc04, messageDataHash=18800523B8EF3F453907083A6BAE9ED0E6B77D6A94A748827B084E9B450EA691, tokenFeeAmount=501226038876433000000000 ) => ( 1769 )
                • Token.transferFrom( sender=0xE92Df19F4e0Fd067FE3b788Cf03ffD06Cd9Be4A7, recipient=0xd3643255ea784c75a5325CC5a4A549C7CD62E499, amount=501226038876433000000000 ) => ( True )
                  File 1 of 5: Token
                  // SPDX-License-Identifier: MIT
                  
                  pragma solidity 0.8.9;
                  
                  /**
                   * @dev Interface of the ERC20 standard as defined in the EIP.
                   */
                  interface IERC20 {
                    /**
                     * @dev Emitted when `value` tokens are moved from one account (`from`) to
                     * another (`to`).
                     *
                     * Note that `value` may be zero.
                     */
                    event Transfer(address indexed from, address indexed to, uint256 value);
                  
                    /**
                     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                     * a call to {approve}. `value` is the new allowance.
                     */
                    event Approval(address indexed owner, address indexed spender, uint256 value);
                  
                    /**
                     * @dev Returns the amount of tokens in existence.
                     */
                    function totalSupply() external view returns (uint256);
                  
                    /**
                     * @dev Returns the amount of tokens owned by `account`.
                     */
                    function balanceOf(address account) external view returns (uint256);
                  
                    /**
                     * @dev Moves `amount` tokens from the caller's account to `to`.
                     *
                     * Returns a boolean value indicating whether the operation succeeded.
                     *
                     * Emits a {Transfer} event.
                     */
                    function transfer(address to, uint256 amount) external returns (bool);
                  
                    /**
                     * @dev Returns the remaining number of tokens that `spender` will be
                     * allowed to spend on behalf of `owner` through {transferFrom}. This is
                     * zero by default.
                     *
                     * This value changes when {approve} or {transferFrom} are called.
                     */
                    function allowance(address owner, address spender) external view returns (uint256);
                  
                    /**
                     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                     *
                     * Returns a boolean value indicating whether the operation succeeded.
                     *
                     * IMPORTANT: Beware that changing an allowance with this method brings the risk
                     * that someone may use both the old and the new allowance by unfortunate
                     * transaction ordering. One possible solution to mitigate this race
                     * condition is to first reduce the spender's allowance to 0 and set the
                     * desired value afterwards:
                     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                     *
                     * Emits an {Approval} event.
                     */
                    function approve(address spender, uint256 amount) external returns (bool);
                  
                    /**
                     * @dev Moves `amount` tokens from `from` to `to` using the
                     * allowance mechanism. `amount` is then deducted from the caller's
                     * allowance.
                     *
                     * Returns a boolean value indicating whether the operation succeeded.
                     *
                     * Emits a {Transfer} event.
                     */
                    function transferFrom(address from, address to, uint256 amount) external returns (bool);
                  }
                  
                  /**
                   * @dev Interface for the optional metadata functions from the ERC20 standard.
                   *
                   * _Available since v4.1._
                   */
                  interface IERC20Metadata is IERC20 {
                    /**
                     * @dev Returns the name of the token.
                     */
                    function name() external view returns (string memory);
                  
                    /**
                     * @dev Returns the symbol of the token.
                     */
                    function symbol() external view returns (string memory);
                  
                    /**
                     * @dev Returns the decimals places of the token.
                     */
                    function decimals() external view returns (uint8);
                  }
                  
                  /**
                   * @dev Provides information about the current execution context, including the
                   * sender of the transaction and its data. While these are generally available
                   * via msg.sender and msg.data, they should not be accessed in such a direct
                   * manner, since when dealing with meta-transactions the account sending and
                   * paying for execution may not be the actual sender (as far as an application
                   * is concerned).
                   *
                   * This contract is only required for intermediate, library-like contracts.
                   */
                  abstract contract Context {
                    function _msgSender() internal view virtual returns (address) {
                      return msg.sender;
                    }
                  
                    function _msgData() internal view virtual returns (bytes calldata) {
                      return msg.data;
                    }
                  }
                  
                  /**
                   * @dev Contract module which provides a basic access control mechanism, where
                   * there is an account (an owner) that can be granted exclusive access to
                   * specific functions.
                   *
                   * By default, the owner account will be the one that deploys the contract. This
                   * can later be changed with {transferOwnership}.
                   *
                   * This module is used through inheritance. It will make available the modifier
                   * `onlyOwner`, which can be applied to your functions to restrict their use to
                   * the owner.
                   */
                  abstract contract Ownable is Context {
                    address private _owner;
                  
                    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                  
                    /**
                     * @dev Initializes the contract setting the deployer as the initial owner.
                     */
                    constructor() {
                      _transferOwnership(_msgSender());
                    }
                  
                    /**
                     * @dev Throws if called by any account other than the owner.
                     */
                    modifier onlyOwner() {
                      _checkOwner();
                      _;
                    }
                  
                    /**
                     * @dev Returns the address of the current owner.
                     */
                    function owner() public view virtual returns (address) {
                      return _owner;
                    }
                  
                    /**
                     * @dev Throws if the sender is not the owner.
                     */
                    function _checkOwner() internal view virtual {
                      require(owner() == _msgSender(), 'Ownable: caller is not the owner');
                    }
                  
                    /**
                     * @dev Leaves the contract without owner. It will not be possible to call
                     * `onlyOwner` functions. Can only be called by the current owner.
                     *
                     * NOTE: Renouncing ownership will leave the contract without an owner,
                     * thereby disabling any functionality that is only available to the owner.
                     */
                    function renounceOwnership() public virtual onlyOwner {
                      _transferOwnership(address(0));
                    }
                  
                    /**
                     * @dev Transfers ownership of the contract to a new account (`newOwner`).
                     * Can only be called by the current owner.
                     */
                    function transferOwnership(address newOwner) public virtual onlyOwner {
                      require(newOwner != address(0), 'Ownable: new owner is the zero address');
                      _transferOwnership(newOwner);
                    }
                  
                    /**
                     * @dev Transfers ownership of the contract to a new account (`newOwner`).
                     * Internal function without access restriction.
                     */
                    function _transferOwnership(address newOwner) internal virtual {
                      address oldOwner = _owner;
                      _owner = newOwner;
                      emit OwnershipTransferred(oldOwner, newOwner);
                    }
                  }
                  
                  contract Token is Context, IERC20Metadata, Ownable {
                    mapping(address => uint256) private _balances;
                  
                    mapping(address => mapping(address => uint256)) private _allowances;
                  
                    uint256 private _totalSupply;
                  
                    string private _name;
                    string private _symbol;
                    uint8 private constant _decimals = 18;
                  
                    /**
                     * @dev Contract constructor.
                     */
                    constructor() {
                      _name = 'Pepe Unchained';
                      _symbol = 'PEPU';
                      _mint(msg.sender, 16_000_000_000 * 10 ** 18);
                    }
                  
                    /**
                     * @dev Returns the name of the token.
                     * @return The name of the token.
                     */
                    function name() public view virtual override returns (string memory) {
                      return _name;
                    }
                  
                    /**
                     * @dev Returns the symbol of the token.
                     * @return The symbol of the token.
                     */
                    function symbol() public view virtual override returns (string memory) {
                      return _symbol;
                    }
                  
                    /**
                     * @dev Returns the number of decimals used for token display.
                     * @return The number of decimals.
                     */
                    function decimals() public view virtual override returns (uint8) {
                      return _decimals;
                    }
                  
                    /**
                     * @dev Returns the total supply of the token.
                     * @return The total supply.
                     */
                    function totalSupply() public view virtual override returns (uint256) {
                      return _totalSupply;
                    }
                  
                    /**
                     * @dev Returns the balance of the specified account.
                     * @param account The address to check the balance for.
                     * @return The balance of the account.
                     */
                    function balanceOf(address account) public view virtual override returns (uint256) {
                      return _balances[account];
                    }
                  
                    /**
                     * @dev Transfers tokens from the caller to a specified recipient.
                     * @param recipient The address to transfer tokens to.
                     * @param amount The amount of tokens to transfer.
                     * @return A boolean value indicating whether the transfer was successful.
                     */
                    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
                      _transfer(_msgSender(), recipient, amount);
                      return true;
                    }
                  
                    /**
                     * @dev Returns the amount of tokens that the spender is allowed to spend on behalf of the owner.
                     * @param from The address that approves the spending.
                     * @param to The address that is allowed to spend.
                     * @return The remaining allowance for the spender.
                     */
                    function allowance(address from, address to) public view virtual override returns (uint256) {
                      return _allowances[from][to];
                    }
                  
                    /**
                     * @dev Approves the specified address to spend the specified amount of tokens on behalf of the caller.
                     * @param to The address to approve the spending for.
                     * @param amount The amount of tokens to approve.
                     * @return A boolean value indicating whether the approval was successful.
                     */
                    function approve(address to, uint256 amount) public virtual override returns (bool) {
                      _approve(_msgSender(), to, amount);
                      return true;
                    }
                  
                    /**
                     * @dev Transfers tokens from one address to another.
                     * @param sender The address to transfer tokens from.
                     * @param recipient The address to transfer tokens to.
                     * @param amount The amount of tokens to transfer.
                     * @return A boolean value indicating whether the transfer was successful.
                     */
                    function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
                      _transfer(sender, recipient, amount);
                  
                      uint256 currentAllowance = _allowances[sender][_msgSender()];
                      require(currentAllowance >= amount, 'ERC20: transfer amount exceeds allowance');
                      unchecked {
                        _approve(sender, _msgSender(), currentAllowance - amount);
                      }
                  
                      return true;
                    }
                  
                    /**
                     * @dev Increases the allowance of the specified address to spend tokens on behalf of the caller.
                     * @param to The address to increase the allowance for.
                     * @param addedValue The amount of tokens to increase the allowance by.
                     * @return A boolean value indicating whether the increase was successful.
                     */
                    function increaseAllowance(address to, uint256 addedValue) public virtual returns (bool) {
                      _approve(_msgSender(), to, _allowances[_msgSender()][to] + addedValue);
                      return true;
                    }
                  
                    /**
                     * @dev Decreases the allowance granted by the owner of the tokens to `to` account.
                     * @param to The account allowed to spend the tokens.
                     * @param subtractedValue The amount of tokens to decrease the allowance by.
                     * @return A boolean value indicating whether the operation succeeded.
                     */
                    function decreaseAllowance(address to, uint256 subtractedValue) public virtual returns (bool) {
                      uint256 currentAllowance = _allowances[_msgSender()][to];
                      require(currentAllowance >= subtractedValue, 'ERC20: decreased allowance below zero');
                      unchecked {
                        _approve(_msgSender(), to, currentAllowance - subtractedValue);
                      }
                  
                      return true;
                    }
                  
                    /**
                     * @dev Transfers `amount` tokens from `sender` to `recipient`.
                     * @param sender The account to transfer tokens from.
                     * @param recipient The account to transfer tokens to.
                     * @param amount The amount of tokens to transfer.
                     */
                    function _transfer(address sender, address recipient, uint256 amount) internal virtual {
                      require(amount > 0, 'ERC20: transfer amount zero');
                      require(sender != address(0), 'ERC20: transfer from the zero address');
                      require(recipient != address(0), 'ERC20: transfer to the zero address');
                  
                      uint256 senderBalance = _balances[sender];
                      require(senderBalance >= amount, 'ERC20: transfer amount exceeds balance');
                      unchecked {
                        _balances[sender] = senderBalance - amount;
                      }
                      _balances[recipient] += amount;
                  
                      emit Transfer(sender, recipient, amount);
                    }
                  
                    /**
                     * @dev Creates `amount` tokens and assigns them to `account`.
                     * @param account The account to assign the newly created tokens to.
                     * @param amount The amount of tokens to create.
                     */
                    function _mint(address account, uint256 amount) internal virtual {
                      require(account != address(0), 'ERC20: mint to the zero address');
                  
                      _totalSupply += amount;
                      _balances[account] += amount;
                      emit Transfer(address(0), account, amount);
                    }
                  
                    /**
                     * @dev Destroys `amount` tokens from `account`, reducing the total supply.
                     * @param account The account to burn tokens from.
                     * @param amount The amount of tokens to burn.
                     */
                    function _burn(address account, uint256 amount) internal virtual {
                      require(account != address(0), 'ERC20: burn from the zero address');
                  
                      uint256 accountBalance = _balances[account];
                      require(accountBalance >= amount, 'ERC20: burn amount exceeds balance');
                      unchecked {
                        _balances[account] = accountBalance - amount;
                      }
                      _totalSupply -= amount;
                  
                      emit Transfer(account, address(0), amount);
                    }
                  
                    /**
                     * @dev Destroys `amount` tokens from the caller's account, reducing the total supply.
                     * @param amount The amount of tokens to burn.
                     */
                    function burn(uint256 amount) external {
                      _burn(_msgSender(), amount);
                    }
                  
                    /**
                     * @dev Sets `amount` as the allowance of `to` over the caller's tokens.
                     * @param from The account granting the allowance.
                     * @param to The account allowed to spend the tokens.
                     * @param amount The amount of tokens to allow.
                     */
                    function _approve(address from, address to, uint256 amount) internal virtual {
                      require(from != address(0), 'ERC20: approve from the zero address');
                      require(to != address(0), 'ERC20: approve to the zero address');
                  
                      _allowances[from][to] = amount;
                      emit Approval(from, to, amount);
                    }
                  }

                  File 2 of 5: 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 3 of 5: 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 4 of 5: ERC20Inbox
                  // 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 v4.4.1 (security/Pausable.sol)
                  pragma solidity ^0.8.0;
                  import "../utils/ContextUpgradeable.sol";
                  import "../proxy/utils/Initializable.sol";
                  /**
                   * @dev Contract module which allows children to implement an emergency stop
                   * mechanism that can be triggered by an authorized account.
                   *
                   * This module is used through inheritance. It will make available the
                   * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
                   * the functions of your contract. Note that they will not be pausable by
                   * simply including this module, only once the modifiers are put in place.
                   */
                  abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
                      /**
                       * @dev Emitted when the pause is triggered by `account`.
                       */
                      event Paused(address account);
                      /**
                       * @dev Emitted when the pause is lifted by `account`.
                       */
                      event Unpaused(address account);
                      bool private _paused;
                      /**
                       * @dev Initializes the contract in unpaused state.
                       */
                      function __Pausable_init() internal onlyInitializing {
                          __Pausable_init_unchained();
                      }
                      function __Pausable_init_unchained() internal onlyInitializing {
                          _paused = false;
                      }
                      /**
                       * @dev Returns true if the contract is paused, and false otherwise.
                       */
                      function paused() public view virtual returns (bool) {
                          return _paused;
                      }
                      /**
                       * @dev Modifier to make a function callable only when the contract is not paused.
                       *
                       * Requirements:
                       *
                       * - The contract must not be paused.
                       */
                      modifier whenNotPaused() {
                          require(!paused(), "Pausable: paused");
                          _;
                      }
                      /**
                       * @dev Modifier to make a function callable only when the contract is paused.
                       *
                       * Requirements:
                       *
                       * - The contract must be paused.
                       */
                      modifier whenPaused() {
                          require(paused(), "Pausable: not paused");
                          _;
                      }
                      /**
                       * @dev Triggers stopped state.
                       *
                       * Requirements:
                       *
                       * - The contract must not be paused.
                       */
                      function _pause() internal virtual whenNotPaused {
                          _paused = true;
                          emit Paused(_msgSender());
                      }
                      /**
                       * @dev Returns to normal state.
                       *
                       * Requirements:
                       *
                       * - The contract must be paused.
                       */
                      function _unpause() internal virtual whenPaused {
                          _paused = false;
                          emit Unpaused(_msgSender());
                      }
                      /**
                       * @dev This empty reserved space is put in place to allow future versions to add new
                       * variables without shifting down storage in the inheritance chain.
                       * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                       */
                      uint256[49] private __gap;
                  }
                  // 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);
                              }
                          }
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
                  pragma solidity ^0.8.0;
                  import "../proxy/utils/Initializable.sol";
                  /**
                   * @dev Provides information about the current execution context, including the
                   * sender of the transaction and its data. While these are generally available
                   * via msg.sender and msg.data, they should not be accessed in such a direct
                   * manner, since when dealing with meta-transactions the account sending and
                   * paying for execution may not be the actual sender (as far as an application
                   * is concerned).
                   *
                   * This contract is only required for intermediate, library-like contracts.
                   */
                  abstract contract ContextUpgradeable is Initializable {
                      function __Context_init() internal onlyInitializing {
                      }
                      function __Context_init_unchained() internal onlyInitializing {
                      }
                      function _msgSender() internal view virtual returns (address) {
                          return msg.sender;
                      }
                      function _msgData() internal view virtual returns (bytes calldata) {
                          return msg.data;
                      }
                      /**
                       * @dev This empty reserved space is put in place to allow future versions to add new
                       * variables without shifting down storage in the inheritance chain.
                       * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                       */
                      uint256[50] private __gap;
                  }
                  // 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 StorageSlotUpgradeable {
                      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
                          }
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/ERC20.sol)
                  pragma solidity ^0.8.0;
                  import "./IERC20.sol";
                  import "./extensions/IERC20Metadata.sol";
                  import "../../utils/Context.sol";
                  /**
                   * @dev Implementation of the {IERC20} interface.
                   *
                   * This implementation is agnostic to the way tokens are created. This means
                   * that a supply mechanism has to be added in a derived contract using {_mint}.
                   * For a generic mechanism see {ERC20PresetMinterPauser}.
                   *
                   * TIP: For a detailed writeup see our guide
                   * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
                   * to implement supply mechanisms].
                   *
                   * We have followed general OpenZeppelin Contracts guidelines: functions revert
                   * instead returning `false` on failure. This behavior is nonetheless
                   * conventional and does not conflict with the expectations of ERC20
                   * applications.
                   *
                   * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
                   * This allows applications to reconstruct the allowance for all accounts just
                   * by listening to said events. Other implementations of the EIP may not emit
                   * these events, as it isn't required by the specification.
                   *
                   * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
                   * functions have been added to mitigate the well-known issues around setting
                   * allowances. See {IERC20-approve}.
                   */
                  contract ERC20 is Context, IERC20, IERC20Metadata {
                      mapping(address => uint256) private _balances;
                      mapping(address => mapping(address => uint256)) private _allowances;
                      uint256 private _totalSupply;
                      string private _name;
                      string private _symbol;
                      /**
                       * @dev Sets the values for {name} and {symbol}.
                       *
                       * The default value of {decimals} is 18. To select a different value for
                       * {decimals} you should overload it.
                       *
                       * All two of these values are immutable: they can only be set once during
                       * construction.
                       */
                      constructor(string memory name_, string memory symbol_) {
                          _name = name_;
                          _symbol = symbol_;
                      }
                      /**
                       * @dev Returns the name of the token.
                       */
                      function name() public view virtual override returns (string memory) {
                          return _name;
                      }
                      /**
                       * @dev Returns the symbol of the token, usually a shorter version of the
                       * name.
                       */
                      function symbol() public view virtual override returns (string memory) {
                          return _symbol;
                      }
                      /**
                       * @dev Returns the number of decimals used to get its user representation.
                       * For example, if `decimals` equals `2`, a balance of `505` tokens should
                       * be displayed to a user as `5.05` (`505 / 10 ** 2`).
                       *
                       * Tokens usually opt for a value of 18, imitating the relationship between
                       * Ether and Wei. This is the value {ERC20} uses, unless this function is
                       * overridden;
                       *
                       * NOTE: This information is only used for _display_ purposes: it in
                       * no way affects any of the arithmetic of the contract, including
                       * {IERC20-balanceOf} and {IERC20-transfer}.
                       */
                      function decimals() public view virtual override returns (uint8) {
                          return 18;
                      }
                      /**
                       * @dev See {IERC20-totalSupply}.
                       */
                      function totalSupply() public view virtual override returns (uint256) {
                          return _totalSupply;
                      }
                      /**
                       * @dev See {IERC20-balanceOf}.
                       */
                      function balanceOf(address account) public view virtual override returns (uint256) {
                          return _balances[account];
                      }
                      /**
                       * @dev See {IERC20-transfer}.
                       *
                       * Requirements:
                       *
                       * - `to` cannot be the zero address.
                       * - the caller must have a balance of at least `amount`.
                       */
                      function transfer(address to, uint256 amount) public virtual override returns (bool) {
                          address owner = _msgSender();
                          _transfer(owner, to, amount);
                          return true;
                      }
                      /**
                       * @dev See {IERC20-allowance}.
                       */
                      function allowance(address owner, address spender) public view virtual override returns (uint256) {
                          return _allowances[owner][spender];
                      }
                      /**
                       * @dev See {IERC20-approve}.
                       *
                       * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
                       * `transferFrom`. This is semantically equivalent to an infinite approval.
                       *
                       * Requirements:
                       *
                       * - `spender` cannot be the zero address.
                       */
                      function approve(address spender, uint256 amount) public virtual override returns (bool) {
                          address owner = _msgSender();
                          _approve(owner, spender, amount);
                          return true;
                      }
                      /**
                       * @dev See {IERC20-transferFrom}.
                       *
                       * Emits an {Approval} event indicating the updated allowance. This is not
                       * required by the EIP. See the note at the beginning of {ERC20}.
                       *
                       * NOTE: Does not update the allowance if the current allowance
                       * is the maximum `uint256`.
                       *
                       * Requirements:
                       *
                       * - `from` and `to` cannot be the zero address.
                       * - `from` must have a balance of at least `amount`.
                       * - the caller must have allowance for ``from``'s tokens of at least
                       * `amount`.
                       */
                      function transferFrom(
                          address from,
                          address to,
                          uint256 amount
                      ) public virtual override returns (bool) {
                          address spender = _msgSender();
                          _spendAllowance(from, spender, amount);
                          _transfer(from, to, amount);
                          return true;
                      }
                      /**
                       * @dev Atomically increases the allowance granted to `spender` by the caller.
                       *
                       * This is an alternative to {approve} that can be used as a mitigation for
                       * problems described in {IERC20-approve}.
                       *
                       * Emits an {Approval} event indicating the updated allowance.
                       *
                       * Requirements:
                       *
                       * - `spender` cannot be the zero address.
                       */
                      function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
                          address owner = _msgSender();
                          _approve(owner, spender, _allowances[owner][spender] + addedValue);
                          return true;
                      }
                      /**
                       * @dev Atomically decreases the allowance granted to `spender` by the caller.
                       *
                       * This is an alternative to {approve} that can be used as a mitigation for
                       * problems described in {IERC20-approve}.
                       *
                       * Emits an {Approval} event indicating the updated allowance.
                       *
                       * Requirements:
                       *
                       * - `spender` cannot be the zero address.
                       * - `spender` must have allowance for the caller of at least
                       * `subtractedValue`.
                       */
                      function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
                          address owner = _msgSender();
                          uint256 currentAllowance = _allowances[owner][spender];
                          require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
                          unchecked {
                              _approve(owner, spender, currentAllowance - subtractedValue);
                          }
                          return true;
                      }
                      /**
                       * @dev Moves `amount` of tokens from `sender` to `recipient`.
                       *
                       * This internal function is equivalent to {transfer}, and can be used to
                       * e.g. implement automatic token fees, slashing mechanisms, etc.
                       *
                       * Emits a {Transfer} event.
                       *
                       * Requirements:
                       *
                       * - `from` cannot be the zero address.
                       * - `to` cannot be the zero address.
                       * - `from` must have a balance of at least `amount`.
                       */
                      function _transfer(
                          address from,
                          address to,
                          uint256 amount
                      ) internal virtual {
                          require(from != address(0), "ERC20: transfer from the zero address");
                          require(to != address(0), "ERC20: transfer to the zero address");
                          _beforeTokenTransfer(from, to, amount);
                          uint256 fromBalance = _balances[from];
                          require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
                          unchecked {
                              _balances[from] = fromBalance - amount;
                          }
                          _balances[to] += amount;
                          emit Transfer(from, to, amount);
                          _afterTokenTransfer(from, to, amount);
                      }
                      /** @dev Creates `amount` tokens and assigns them to `account`, increasing
                       * the total supply.
                       *
                       * Emits a {Transfer} event with `from` set to the zero address.
                       *
                       * Requirements:
                       *
                       * - `account` cannot be the zero address.
                       */
                      function _mint(address account, uint256 amount) internal virtual {
                          require(account != address(0), "ERC20: mint to the zero address");
                          _beforeTokenTransfer(address(0), account, amount);
                          _totalSupply += amount;
                          _balances[account] += amount;
                          emit Transfer(address(0), account, amount);
                          _afterTokenTransfer(address(0), account, amount);
                      }
                      /**
                       * @dev Destroys `amount` tokens from `account`, reducing the
                       * total supply.
                       *
                       * Emits a {Transfer} event with `to` set to the zero address.
                       *
                       * Requirements:
                       *
                       * - `account` cannot be the zero address.
                       * - `account` must have at least `amount` tokens.
                       */
                      function _burn(address account, uint256 amount) internal virtual {
                          require(account != address(0), "ERC20: burn from the zero address");
                          _beforeTokenTransfer(account, address(0), amount);
                          uint256 accountBalance = _balances[account];
                          require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
                          unchecked {
                              _balances[account] = accountBalance - amount;
                          }
                          _totalSupply -= amount;
                          emit Transfer(account, address(0), amount);
                          _afterTokenTransfer(account, address(0), amount);
                      }
                      /**
                       * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
                       *
                       * This internal function is equivalent to `approve`, and can be used to
                       * e.g. set automatic allowances for certain subsystems, etc.
                       *
                       * Emits an {Approval} event.
                       *
                       * Requirements:
                       *
                       * - `owner` cannot be the zero address.
                       * - `spender` cannot be the zero address.
                       */
                      function _approve(
                          address owner,
                          address spender,
                          uint256 amount
                      ) internal virtual {
                          require(owner != address(0), "ERC20: approve from the zero address");
                          require(spender != address(0), "ERC20: approve to the zero address");
                          _allowances[owner][spender] = amount;
                          emit Approval(owner, spender, amount);
                      }
                      /**
                       * @dev Spend `amount` form the allowance of `owner` toward `spender`.
                       *
                       * Does not update the allowance amount in case of infinite allowance.
                       * Revert if not enough allowance is available.
                       *
                       * Might emit an {Approval} event.
                       */
                      function _spendAllowance(
                          address owner,
                          address spender,
                          uint256 amount
                      ) internal virtual {
                          uint256 currentAllowance = allowance(owner, spender);
                          if (currentAllowance != type(uint256).max) {
                              require(currentAllowance >= amount, "ERC20: insufficient allowance");
                              unchecked {
                                  _approve(owner, spender, currentAllowance - amount);
                              }
                          }
                      }
                      /**
                       * @dev Hook that is called before any transfer of tokens. This includes
                       * minting and burning.
                       *
                       * Calling conditions:
                       *
                       * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                       * will be transferred to `to`.
                       * - when `from` is zero, `amount` tokens will be minted for `to`.
                       * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
                       * - `from` and `to` are never both zero.
                       *
                       * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                       */
                      function _beforeTokenTransfer(
                          address from,
                          address to,
                          uint256 amount
                      ) internal virtual {}
                      /**
                       * @dev Hook that is called after any transfer of tokens. This includes
                       * minting and burning.
                       *
                       * Calling conditions:
                       *
                       * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                       * has been transferred to `to`.
                       * - when `from` is zero, `amount` tokens have been minted for `to`.
                       * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
                       * - `from` and `to` are never both zero.
                       *
                       * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                       */
                      function _afterTokenTransfer(
                          address from,
                          address to,
                          uint256 amount
                      ) internal virtual {}
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
                  pragma solidity ^0.8.0;
                  import "../IERC20.sol";
                  /**
                   * @dev Interface for the optional metadata functions from the ERC20 standard.
                   *
                   * _Available since v4.1._
                   */
                  interface IERC20Metadata is IERC20 {
                      /**
                       * @dev Returns the name of the token.
                       */
                      function name() external view returns (string memory);
                      /**
                       * @dev Returns the symbol of the token.
                       */
                      function symbol() external view returns (string memory);
                      /**
                       * @dev Returns the decimals places of the token.
                       */
                      function decimals() external view returns (uint8);
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)
                  pragma solidity ^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 `to`.
                       *
                       * Returns a boolean value indicating whether the operation succeeded.
                       *
                       * Emits a {Transfer} event.
                       */
                      function transfer(address to, uint256 amount) external returns (bool);
                      /**
                       * @dev Returns the remaining number of tokens that `spender` will be
                       * allowed to spend on behalf of `owner` through {transferFrom}. This is
                       * zero by default.
                       *
                       * This value changes when {approve} or {transferFrom} are called.
                       */
                      function allowance(address owner, address spender) external view returns (uint256);
                      /**
                       * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                       *
                       * Returns a boolean value indicating whether the operation succeeded.
                       *
                       * IMPORTANT: Beware that changing an allowance with this method brings the risk
                       * that someone may use both the old and the new allowance by unfortunate
                       * transaction ordering. One possible solution to mitigate this race
                       * condition is to first reduce the spender's allowance to 0 and set the
                       * desired value afterwards:
                       * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                       *
                       * Emits an {Approval} event.
                       */
                      function approve(address spender, uint256 amount) external returns (bool);
                      /**
                       * @dev Moves `amount` tokens from `from` to `to` using the
                       * allowance mechanism. `amount` is then deducted from the caller's
                       * allowance.
                       *
                       * Returns a boolean value indicating whether the operation succeeded.
                       *
                       * Emits a {Transfer} event.
                       */
                      function transferFrom(
                          address from,
                          address to,
                          uint256 amount
                      ) external returns (bool);
                      /**
                       * @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
                  // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)
                  pragma solidity ^0.8.0;
                  import "../IERC20.sol";
                  import "../../../utils/Address.sol";
                  /**
                   * @title SafeERC20
                   * @dev Wrappers around ERC20 operations that throw on failure (when the token
                   * contract returns false). Tokens that return no value (and instead revert or
                   * throw on failure) are also supported, non-reverting calls are assumed to be
                   * successful.
                   * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
                   * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
                   */
                  library SafeERC20 {
                      using Address for address;
                      function safeTransfer(
                          IERC20 token,
                          address to,
                          uint256 value
                      ) internal {
                          _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                      }
                      function safeTransferFrom(
                          IERC20 token,
                          address from,
                          address to,
                          uint256 value
                      ) internal {
                          _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                      }
                      /**
                       * @dev Deprecated. This function has issues similar to the ones found in
                       * {IERC20-approve}, and its usage is discouraged.
                       *
                       * Whenever possible, use {safeIncreaseAllowance} and
                       * {safeDecreaseAllowance} instead.
                       */
                      function safeApprove(
                          IERC20 token,
                          address spender,
                          uint256 value
                      ) internal {
                          // safeApprove should only be called when setting an initial allowance,
                          // or when resetting it to zero. To increase and decrease it, use
                          // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                          require(
                              (value == 0) || (token.allowance(address(this), spender) == 0),
                              "SafeERC20: approve from non-zero to non-zero allowance"
                          );
                          _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                      }
                      function safeIncreaseAllowance(
                          IERC20 token,
                          address spender,
                          uint256 value
                      ) internal {
                          uint256 newAllowance = token.allowance(address(this), spender) + value;
                          _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                      }
                      function safeDecreaseAllowance(
                          IERC20 token,
                          address spender,
                          uint256 value
                      ) internal {
                          unchecked {
                              uint256 oldAllowance = token.allowance(address(this), spender);
                              require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                              uint256 newAllowance = oldAllowance - value;
                              _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                          }
                      }
                      /**
                       * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                       * on the return value: the return value is optional (but if data is returned, it must not be false).
                       * @param token The token targeted by the call.
                       * @param data The call data (encoded using abi.encode or one of its variants).
                       */
                      function _callOptionalReturn(IERC20 token, bytes memory data) private {
                          // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                          // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
                          // the target address contains contract code and also asserts for success in the low-level call.
                          bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                          if (returndata.length > 0) {
                              // Return data is optional
                              require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                          }
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // 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/Context.sol)
                  pragma solidity ^0.8.0;
                  /**
                   * @dev Provides information about the current execution context, including the
                   * sender of the transaction and its data. While these are generally available
                   * via msg.sender and msg.data, they should not be accessed in such a direct
                   * manner, since when dealing with meta-transactions the account sending and
                   * paying for execution may not be the actual sender (as far as an application
                   * is concerned).
                   *
                   * This contract is only required for intermediate, library-like contracts.
                   */
                  abstract contract Context {
                      function _msgSender() internal view virtual returns (address) {
                          return msg.sender;
                      }
                      function _msgData() internal view virtual returns (bytes calldata) {
                          return msg.data;
                      }
                  }
                  // 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 {
                      DataTooLarge,
                      GasLimitTooLarge,
                      InsufficientValue,
                      InsufficientSubmissionCost,
                      L1Forked,
                      NotAllowedOrigin,
                      NotCodelessOrigin,
                      NotRollupOrOwner,
                      RetryableData
                  } from "../libraries/Error.sol";
                  import "./IInboxBase.sol";
                  import "./ISequencerInbox.sol";
                  import "./IBridge.sol";
                  import "../libraries/AddressAliasHelper.sol";
                  import "../libraries/CallerChecker.sol";
                  import "../libraries/DelegateCallAware.sol";
                  import {
                      L1MessageType_submitRetryableTx,
                      L2MessageType_unsignedContractTx,
                      L2MessageType_unsignedEOATx,
                      L2_MSG
                  } from "../libraries/MessageTypes.sol";
                  import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
                  import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
                  import "@openzeppelin/contracts-upgradeable/utils/StorageSlotUpgradeable.sol";
                  /**
                   * @title Inbox for user and contract originated messages
                   * @notice Messages created via this inbox are enqueued in the delayed accumulator
                   * to await inclusion in the SequencerInbox
                   */
                  abstract contract AbsInbox is DelegateCallAware, PausableUpgradeable, IInboxBase {
                      /// @dev Storage slot with the admin of the contract.
                      /// This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
                      bytes32 internal constant _ADMIN_SLOT =
                          0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                      /// @inheritdoc IInboxBase
                      IBridge public bridge;
                      /// @inheritdoc IInboxBase
                      ISequencerInbox public sequencerInbox;
                      /// ------------------------------------ allow list start ------------------------------------ ///
                      /// @inheritdoc IInboxBase
                      bool public allowListEnabled;
                      /// @inheritdoc IInboxBase
                      mapping(address => bool) public isAllowed;
                      event AllowListAddressSet(address indexed user, bool val);
                      event AllowListEnabledUpdated(bool isEnabled);
                      /// @inheritdoc IInboxBase
                      function setAllowList(address[] memory user, bool[] memory val) external onlyRollupOrOwner {
                          require(user.length == val.length, "INVALID_INPUT");
                          for (uint256 i = 0; i < user.length; i++) {
                              isAllowed[user[i]] = val[i];
                              emit AllowListAddressSet(user[i], val[i]);
                          }
                      }
                      /// @inheritdoc IInboxBase
                      function setAllowListEnabled(bool _allowListEnabled) external onlyRollupOrOwner {
                          require(_allowListEnabled != allowListEnabled, "ALREADY_SET");
                          allowListEnabled = _allowListEnabled;
                          emit AllowListEnabledUpdated(_allowListEnabled);
                      }
                      /// @dev this modifier checks the tx.origin instead of msg.sender for convenience (ie it allows
                      /// allowed users to interact with the token bridge without needing the token bridge to be allowList aware).
                      /// this modifier is not intended to use to be used for security (since this opens the allowList to
                      /// a smart contract phishing risk).
                      modifier onlyAllowed() {
                          // solhint-disable-next-line avoid-tx-origin
                          if (allowListEnabled && !isAllowed[tx.origin]) revert NotAllowedOrigin(tx.origin);
                          _;
                      }
                      /// ------------------------------------ allow list end ------------------------------------ ///
                      modifier onlyRollupOrOwner() {
                          IOwnable rollup = bridge.rollup();
                          if (msg.sender != address(rollup)) {
                              address rollupOwner = rollup.owner();
                              if (msg.sender != rollupOwner) {
                                  revert NotRollupOrOwner(msg.sender, address(rollup), rollupOwner);
                              }
                          }
                          _;
                      }
                      // On L1 this should be set to 117964: 90% of Geth's 128KB tx size limit, leaving ~13KB for proving
                      uint256 public immutable maxDataSize;
                      uint256 internal immutable deployTimeChainId = block.chainid;
                      constructor(uint256 _maxDataSize) {
                          maxDataSize = _maxDataSize;
                      }
                      function _chainIdChanged() internal view returns (bool) {
                          return deployTimeChainId != block.chainid;
                      }
                      /// @inheritdoc IInboxBase
                      function pause() external onlyRollupOrOwner {
                          _pause();
                      }
                      /// @inheritdoc IInboxBase
                      function unpause() external onlyRollupOrOwner {
                          _unpause();
                      }
                      /* solhint-disable func-name-mixedcase */
                      function __AbsInbox_init(IBridge _bridge, ISequencerInbox _sequencerInbox)
                          internal
                          onlyInitializing
                      {
                          bridge = _bridge;
                          sequencerInbox = _sequencerInbox;
                          allowListEnabled = false;
                          __Pausable_init();
                      }
                      /// @inheritdoc IInboxBase
                      function sendL2MessageFromOrigin(bytes calldata messageData)
                          external
                          whenNotPaused
                          onlyAllowed
                          returns (uint256)
                      {
                          if (_chainIdChanged()) revert L1Forked();
                          if (!CallerChecker.isCallerCodelessOrigin()) revert NotCodelessOrigin();
                          if (messageData.length > maxDataSize) revert DataTooLarge(messageData.length, maxDataSize);
                          uint256 msgNum = _deliverToBridge(L2_MSG, msg.sender, keccak256(messageData), 0);
                          emit InboxMessageDeliveredFromOrigin(msgNum);
                          return msgNum;
                      }
                      /// @inheritdoc IInboxBase
                      function sendL2Message(bytes calldata messageData)
                          external
                          whenNotPaused
                          onlyAllowed
                          returns (uint256)
                      {
                          if (_chainIdChanged()) revert L1Forked();
                          return _deliverMessage(L2_MSG, msg.sender, messageData, 0);
                      }
                      /// @inheritdoc IInboxBase
                      function sendUnsignedTransaction(
                          uint256 gasLimit,
                          uint256 maxFeePerGas,
                          uint256 nonce,
                          address to,
                          uint256 value,
                          bytes calldata data
                      ) external whenNotPaused onlyAllowed returns (uint256) {
                          // arbos will discard unsigned tx with gas limit too large
                          if (gasLimit > type(uint64).max) {
                              revert GasLimitTooLarge();
                          }
                          return
                              _deliverMessage(
                                  L2_MSG,
                                  msg.sender,
                                  abi.encodePacked(
                                      L2MessageType_unsignedEOATx,
                                      gasLimit,
                                      maxFeePerGas,
                                      nonce,
                                      uint256(uint160(to)),
                                      value,
                                      data
                                  ),
                                  0
                              );
                      }
                      /// @inheritdoc IInboxBase
                      function sendContractTransaction(
                          uint256 gasLimit,
                          uint256 maxFeePerGas,
                          address to,
                          uint256 value,
                          bytes calldata data
                      ) external whenNotPaused onlyAllowed returns (uint256) {
                          // arbos will discard unsigned tx with gas limit too large
                          if (gasLimit > type(uint64).max) {
                              revert GasLimitTooLarge();
                          }
                          return
                              _deliverMessage(
                                  L2_MSG,
                                  msg.sender,
                                  abi.encodePacked(
                                      L2MessageType_unsignedContractTx,
                                      gasLimit,
                                      maxFeePerGas,
                                      uint256(uint160(to)),
                                      value,
                                      data
                                  ),
                                  0
                              );
                      }
                      /// @inheritdoc IInboxBase
                      function getProxyAdmin() external view returns (address) {
                          return StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value;
                      }
                      function _createRetryableTicket(
                          address to,
                          uint256 l2CallValue,
                          uint256 maxSubmissionCost,
                          address excessFeeRefundAddress,
                          address callValueRefundAddress,
                          uint256 gasLimit,
                          uint256 maxFeePerGas,
                          uint256 amount,
                          bytes calldata data
                      ) internal returns (uint256) {
                          // Ensure the user's deposit alone will make submission succeed.
                          // In case of native token having non-18 decimals: 'amount' is denominated in native token's decimals. All other
                          // value params - l2CallValue, maxSubmissionCost and maxFeePerGas are denominated in child chain's native 18 decimals.
                          uint256 amountToBeMintedOnL2 = _fromNativeTo18Decimals(amount);
                          if (amountToBeMintedOnL2 < (maxSubmissionCost + l2CallValue + gasLimit * maxFeePerGas)) {
                              revert InsufficientValue(
                                  maxSubmissionCost + l2CallValue + gasLimit * maxFeePerGas,
                                  amountToBeMintedOnL2
                              );
                          }
                          // if a refund address is a contract, we apply the alias to it
                          // so that it can access its funds on the L2
                          // since the beneficiary and other refund addresses don't get rewritten by arb-os
                          if (AddressUpgradeable.isContract(excessFeeRefundAddress)) {
                              excessFeeRefundAddress = AddressAliasHelper.applyL1ToL2Alias(excessFeeRefundAddress);
                          }
                          if (AddressUpgradeable.isContract(callValueRefundAddress)) {
                              // this is the beneficiary. be careful since this is the address that can cancel the retryable in the L2
                              callValueRefundAddress = AddressAliasHelper.applyL1ToL2Alias(callValueRefundAddress);
                          }
                          // gas limit is validated to be within uint64 in unsafeCreateRetryableTicket
                          return
                              _unsafeCreateRetryableTicket(
                                  to,
                                  l2CallValue,
                                  maxSubmissionCost,
                                  excessFeeRefundAddress,
                                  callValueRefundAddress,
                                  gasLimit,
                                  maxFeePerGas,
                                  amount,
                                  data
                              );
                      }
                      function _unsafeCreateRetryableTicket(
                          address to,
                          uint256 l2CallValue,
                          uint256 maxSubmissionCost,
                          address excessFeeRefundAddress,
                          address callValueRefundAddress,
                          uint256 gasLimit,
                          uint256 maxFeePerGas,
                          uint256 amount,
                          bytes calldata data
                      ) internal returns (uint256) {
                          // gas price and limit of 1 should never be a valid input, so instead they are used as
                          // magic values to trigger a revert in eth calls that surface data without requiring a tx trace
                          if (gasLimit == 1 || maxFeePerGas == 1)
                              revert RetryableData(
                                  msg.sender,
                                  to,
                                  l2CallValue,
                                  amount,
                                  maxSubmissionCost,
                                  excessFeeRefundAddress,
                                  callValueRefundAddress,
                                  gasLimit,
                                  maxFeePerGas,
                                  data
                              );
                          // arbos will discard retryable with gas limit too large
                          if (gasLimit > type(uint64).max) {
                              revert GasLimitTooLarge();
                          }
                          uint256 submissionFee = calculateRetryableSubmissionFee(data.length, block.basefee);
                          if (maxSubmissionCost < submissionFee)
                              revert InsufficientSubmissionCost(submissionFee, maxSubmissionCost);
                          return
                              _deliverMessage(
                                  L1MessageType_submitRetryableTx,
                                  msg.sender,
                                  abi.encodePacked(
                                      uint256(uint160(to)),
                                      l2CallValue,
                                      _fromNativeTo18Decimals(amount),
                                      maxSubmissionCost,
                                      uint256(uint160(excessFeeRefundAddress)),
                                      uint256(uint160(callValueRefundAddress)),
                                      gasLimit,
                                      maxFeePerGas,
                                      data.length,
                                      data
                                  ),
                                  amount
                              );
                      }
                      function _deliverMessage(
                          uint8 _kind,
                          address _sender,
                          bytes memory _messageData,
                          uint256 amount
                      ) internal returns (uint256) {
                          if (_messageData.length > maxDataSize)
                              revert DataTooLarge(_messageData.length, maxDataSize);
                          uint256 msgNum = _deliverToBridge(_kind, _sender, keccak256(_messageData), amount);
                          emit InboxMessageDelivered(msgNum, _messageData);
                          return msgNum;
                      }
                      function _deliverToBridge(
                          uint8 kind,
                          address sender,
                          bytes32 messageDataHash,
                          uint256 amount
                      ) internal virtual returns (uint256);
                      function calculateRetryableSubmissionFee(uint256 dataLength, uint256 baseFee)
                          public
                          view
                          virtual
                          returns (uint256);
                      /// @notice get amount of ETH/token to mint on child chain based on provided value.
                      ///         In case of ETH-based rollup this amount will always equal the provided
                      ///         value. In case of ERC20-based rollup where native token has number of
                      ///         decimals different thatn 18, amount will be re-adjusted to reflect 18
                      ///         decimals used for native currency on child chain.
                      /// @dev    provided value has to be less than 'type(uint256).max/10**(18-decimalsIn)'
                      ///         or otherwise it will overflow.
                      function _fromNativeTo18Decimals(uint256 value) internal view virtual returns (uint256);
                      /**
                       * @dev This empty reserved space is put in place to allow future versions to add new
                       * variables without shifting down storage in the inheritance chain.
                       * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                       */
                      uint256[47] private __gap;
                  }
                  // 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 "./AbsInbox.sol";
                  import "./IERC20Inbox.sol";
                  import "./IERC20Bridge.sol";
                  import "../libraries/AddressAliasHelper.sol";
                  import {L1MessageType_ethDeposit} from "../libraries/MessageTypes.sol";
                  import {AmountTooLarge} from "../libraries/Error.sol";
                  import {MAX_UPSCALE_AMOUNT} from "../libraries/Constants.sol";
                  import {DecimalsConverterHelper} from "../libraries/DecimalsConverterHelper.sol";
                  import {AddressUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
                  import {IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
                  import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
                  /**
                   * @title Inbox for user and contract originated messages
                   * @notice Messages created via this inbox are enqueued in the delayed accumulator
                   * to await inclusion in the SequencerInbox
                   */
                  contract ERC20Inbox is AbsInbox, IERC20Inbox {
                      using SafeERC20 for IERC20;
                      constructor(uint256 _maxDataSize) AbsInbox(_maxDataSize) {}
                      /// @inheritdoc IInboxBase
                      function initialize(IBridge _bridge, ISequencerInbox _sequencerInbox)
                          external
                          initializer
                          onlyDelegated
                      {
                          __AbsInbox_init(_bridge, _sequencerInbox);
                          // inbox holds native token in transit used to pay for retryable tickets, approve bridge to use it
                          address nativeToken = IERC20Bridge(address(bridge)).nativeToken();
                          IERC20(nativeToken).safeApprove(address(bridge), type(uint256).max);
                      }
                      /// @inheritdoc IERC20Inbox
                      function depositERC20(uint256 amount) public whenNotPaused onlyAllowed returns (uint256) {
                          address dest = msg.sender;
                          // solhint-disable-next-line avoid-tx-origin
                          if (AddressUpgradeable.isContract(msg.sender) || tx.origin != msg.sender) {
                              // isContract check fails if this function is called during a contract's constructor.
                              dest = AddressAliasHelper.applyL1ToL2Alias(msg.sender);
                          }
                          uint256 amountToMintOnL2 = _fromNativeTo18Decimals(amount);
                          return
                              _deliverMessage(
                                  L1MessageType_ethDeposit,
                                  msg.sender,
                                  abi.encodePacked(dest, amountToMintOnL2),
                                  amount
                              );
                      }
                      /// @inheritdoc IERC20Inbox
                      function createRetryableTicket(
                          address to,
                          uint256 l2CallValue,
                          uint256 maxSubmissionCost,
                          address excessFeeRefundAddress,
                          address callValueRefundAddress,
                          uint256 gasLimit,
                          uint256 maxFeePerGas,
                          uint256 tokenTotalFeeAmount,
                          bytes calldata data
                      ) external whenNotPaused onlyAllowed returns (uint256) {
                          return
                              _createRetryableTicket(
                                  to,
                                  l2CallValue,
                                  maxSubmissionCost,
                                  excessFeeRefundAddress,
                                  callValueRefundAddress,
                                  gasLimit,
                                  maxFeePerGas,
                                  tokenTotalFeeAmount,
                                  data
                              );
                      }
                      /// @inheritdoc IERC20Inbox
                      function unsafeCreateRetryableTicket(
                          address to,
                          uint256 l2CallValue,
                          uint256 maxSubmissionCost,
                          address excessFeeRefundAddress,
                          address callValueRefundAddress,
                          uint256 gasLimit,
                          uint256 maxFeePerGas,
                          uint256 tokenTotalFeeAmount,
                          bytes calldata data
                      ) public whenNotPaused onlyAllowed returns (uint256) {
                          return
                              _unsafeCreateRetryableTicket(
                                  to,
                                  l2CallValue,
                                  maxSubmissionCost,
                                  excessFeeRefundAddress,
                                  callValueRefundAddress,
                                  gasLimit,
                                  maxFeePerGas,
                                  tokenTotalFeeAmount,
                                  data
                              );
                      }
                      /// @inheritdoc IInboxBase
                      function calculateRetryableSubmissionFee(uint256, uint256)
                          public
                          pure
                          override(AbsInbox, IInboxBase)
                          returns (uint256)
                      {
                          // retryable ticket's submission fee is not charged when ERC20 token is used to pay for fees
                          return 0;
                      }
                      function _deliverToBridge(
                          uint8 kind,
                          address sender,
                          bytes32 messageDataHash,
                          uint256 tokenAmount
                      ) internal override returns (uint256) {
                          // Fetch native token from sender if inbox doesn't already hold enough tokens to pay for fees.
                          // Inbox might have been pre-funded in prior call, ie. as part of token bridging flow.
                          address nativeToken = IERC20Bridge(address(bridge)).nativeToken();
                          uint256 inboxNativeTokenBalance = IERC20(nativeToken).balanceOf(address(this));
                          if (inboxNativeTokenBalance < tokenAmount) {
                              uint256 diff = tokenAmount - inboxNativeTokenBalance;
                              IERC20(nativeToken).safeTransferFrom(msg.sender, address(this), diff);
                          }
                          return
                              IERC20Bridge(address(bridge)).enqueueDelayedMessage(
                                  kind,
                                  AddressAliasHelper.applyL1ToL2Alias(sender),
                                  messageDataHash,
                                  tokenAmount
                              );
                      }
                      /// @inheritdoc AbsInbox
                      function _fromNativeTo18Decimals(uint256 value) internal view override returns (uint256) {
                          // In order to keep compatibility of child chain's native currency with external 3rd party tooling we
                          // expect 18 decimals to be always used for native currency. If native token uses different number of
                          // decimals then here it will be normalized to 18. Keep in mind, when withdrawing from child chain back
                          // to parent chain then the amount has to match native token's granularity, otherwise it will be rounded
                          // down.
                          uint8 nativeTokenDecimals = IERC20Bridge(address(bridge)).nativeTokenDecimals();
                          // Also make sure that inflated amount does not overflow uint256
                          if (nativeTokenDecimals < 18) {
                              if (value > MAX_UPSCALE_AMOUNT) {
                                  revert AmountTooLarge(value);
                              }
                          }
                          return DecimalsConverterHelper.adjustDecimals(value, nativeTokenDecimals, 18);
                      }
                  }
                  // Copyright 2021-2022, Offchain Labs, Inc.
                  // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/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 {
                      /// @dev This is an instruction to offchain readers to inform them where to look
                      ///      for sequencer inbox batch data. This is not the type of data (eg. das, brotli encoded, or blob versioned hash)
                      ///      and this enum is not used in the state transition function, rather it informs an offchain
                      ///      reader where to find the data so that they can supply it to the replay binary
                      enum BatchDataLocation {
                          /// @notice The data can be found in the transaction call data
                          TxInput,
                          /// @notice The data can be found in an event emitted during the transaction
                          SeparateBatchEvent,
                          /// @notice This batch contains no data
                          NoData,
                          /// @notice The data can be found in the 4844 data blobs on this transaction
                          Blob
                      }
                      struct TimeBounds {
                          uint64 minTimestamp;
                          uint64 maxTimestamp;
                          uint64 minBlockNumber;
                          uint64 maxBlockNumber;
                      }
                      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);
                      event RollupUpdated(address rollup);
                      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);
                      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;
                      function updateRollupAddress(IOwnable _rollup) external;
                  }
                  // Copyright 2021-2022, Offchain Labs, Inc.
                  // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE
                  // SPDX-License-Identifier: BUSL-1.1
                  // solhint-disable-next-line compiler-version
                  pragma solidity >=0.6.9 <0.9.0;
                  interface IDelayedMessageProvider {
                      /// @dev event emitted when a inbox message is added to the Bridge's delayed accumulator
                      event InboxMessageDelivered(uint256 indexed messageNum, bytes data);
                      /// @dev event emitted when a inbox message is added to the Bridge's delayed accumulator
                      /// same as InboxMessageDelivered but the batch data is available in tx.input
                      event InboxMessageDeliveredFromOrigin(uint256 indexed messageNum);
                  }
                  // 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";
                  import "./IBridge.sol";
                  interface IERC20Bridge is IBridge {
                      /**
                       * @dev token that is escrowed in bridge on L1 side and minted on L2 as native currency.
                       * Fees are paid in this token. There are certain restrictions on the native token:
                       *  - The token can't be rebasing or have a transfer fee
                       *  - The token must only be transferrable via a call to the token address itself
                       *  - The token must only be able to set allowance via a call to the token address itself
                       *  - The token must not have a callback on transfer, and more generally a user must not be able to make a transfer to themselves revert
                       */
                      function nativeToken() external view returns (address);
                      /**
                       * @dev number of decimals used by the native token
                       *      This is set on bridge initialization using nativeToken.decimals()
                       *      If the token does not have decimals() method, we assume it have 0 decimals
                       */
                      function nativeTokenDecimals() external view returns (uint8);
                      /**
                       * @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,
                          uint256 tokenFeeAmount
                      ) external returns (uint256);
                      // ---------- initializer ----------
                      function initialize(IOwnable rollup_, address nativeToken_) 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 "./IInboxBase.sol";
                  interface IERC20Inbox is IInboxBase {
                      /**
                       * @notice Deposit native token from L1 to L2 to address of the sender if sender is an EOA, and to its aliased address if the sender is a contract
                       * @dev This does not trigger the fallback function when receiving in the L2 side.
                       *      Look into retryable tickets if you are interested in this functionality.
                       * @dev This function should not be called inside contract constructors
                       */
                      function depositERC20(uint256 amount) external returns (uint256);
                      /**
                       * @notice Put a message in the L2 inbox that can be reexecuted for some fixed amount of time if it reverts
                       * @dev all tokenTotalFeeAmount will be deposited to callValueRefundAddress on L2
                       * @dev Gas limit and maxFeePerGas should not be set to 1 as that is used to trigger the RetryableData error
                       * @dev In case of native token having non-18 decimals: tokenTotalFeeAmount is denominated in native token's decimals. All other value params - l2CallValue, maxSubmissionCost and maxFeePerGas are denominated in child chain's native 18 decimals.
                       * @param to destination L2 contract address
                       * @param l2CallValue call value for retryable L2 message
                       * @param maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee
                       * @param excessFeeRefundAddress the address which receives the difference between execution fee paid and the actual execution cost. In case this address is a contract, funds will be received in its alias on L2.
                       * @param callValueRefundAddress l2Callvalue gets credited here on L2 if retryable txn times out or gets cancelled. In case this address is a contract, funds will be received in its alias on L2.
                       * @param gasLimit Max gas deducted from user's L2 balance to cover L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)
                       * @param maxFeePerGas price bid for L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)
                       * @param tokenTotalFeeAmount amount of fees to be deposited in native token to cover for retryable ticket cost
                       * @param data ABI encoded data of L2 message
                       * @return unique message number of the retryable transaction
                       */
                      function createRetryableTicket(
                          address to,
                          uint256 l2CallValue,
                          uint256 maxSubmissionCost,
                          address excessFeeRefundAddress,
                          address callValueRefundAddress,
                          uint256 gasLimit,
                          uint256 maxFeePerGas,
                          uint256 tokenTotalFeeAmount,
                          bytes calldata data
                      ) external returns (uint256);
                      /**
                       * @notice Put a message in the L2 inbox that can be reexecuted for some fixed amount of time if it reverts
                       * @dev Same as createRetryableTicket, but does not guarantee that submission will succeed by requiring the needed funds
                       * come from the deposit alone, rather than falling back on the user's L2 balance
                       * @dev Advanced usage only (does not rewrite aliases for excessFeeRefundAddress and callValueRefundAddress).
                       * createRetryableTicket method is the recommended standard.
                       * @dev Gas limit and maxFeePerGas should not be set to 1 as that is used to trigger the RetryableData error
                       * @param to destination L2 contract address
                       * @param l2CallValue call value for retryable L2 message
                       * @param maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee
                       * @param excessFeeRefundAddress the address which receives the difference between execution fee paid and the actual execution cost
                       * @param callValueRefundAddress l2Callvalue gets credited here on L2 if retryable txn times out or gets cancelled
                       * @param gasLimit Max gas deducted from user's L2 balance to cover L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)
                       * @param maxFeePerGas price bid for L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)
                       * @param tokenTotalFeeAmount amount of fees to be deposited in native token to cover for retryable ticket cost
                       * @param data ABI encoded data of L2 message
                       * @return unique message number of the retryable transaction
                       */
                      function unsafeCreateRetryableTicket(
                          address to,
                          uint256 l2CallValue,
                          uint256 maxSubmissionCost,
                          address excessFeeRefundAddress,
                          address callValueRefundAddress,
                          uint256 gasLimit,
                          uint256 maxFeePerGas,
                          uint256 tokenTotalFeeAmount,
                          bytes calldata data
                      ) external returns (uint256);
                  }
                  // Copyright 2021-2022, Offchain Labs, Inc.
                  // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE
                  // SPDX-License-Identifier: BUSL-1.1
                  // solhint-disable-next-line compiler-version
                  pragma solidity >=0.6.9 <0.9.0;
                  import "./IBridge.sol";
                  import "./IDelayedMessageProvider.sol";
                  import "./ISequencerInbox.sol";
                  interface IInboxBase is IDelayedMessageProvider {
                      function bridge() external view returns (IBridge);
                      function sequencerInbox() external view returns (ISequencerInbox);
                      function maxDataSize() external view returns (uint256);
                      /**
                       * @notice Send a generic L2 message to the chain
                       * @dev This method is an optimization to avoid having to emit the entirety of the messageData in a log. Instead validators are expected to be able to parse the data from the transaction's input
                       * @param messageData Data of the message being sent
                       */
                      function sendL2MessageFromOrigin(bytes calldata messageData) external returns (uint256);
                      /**
                       * @notice Send a generic L2 message to the chain
                       * @dev This method can be used to send any type of message that doesn't require L1 validation
                       * @param messageData Data of the message being sent
                       */
                      function sendL2Message(bytes calldata messageData) external returns (uint256);
                      function sendUnsignedTransaction(
                          uint256 gasLimit,
                          uint256 maxFeePerGas,
                          uint256 nonce,
                          address to,
                          uint256 value,
                          bytes calldata data
                      ) external returns (uint256);
                      function sendContractTransaction(
                          uint256 gasLimit,
                          uint256 maxFeePerGas,
                          address to,
                          uint256 value,
                          bytes calldata data
                      ) external returns (uint256);
                      /**
                       * @notice Get the L1 fee for submitting a retryable
                       * @dev This fee can be paid by funds already in the L2 aliased address or by the current message value
                       * @dev This formula may change in the future, to future proof your code query this method instead of inlining!!
                       * @param dataLength The length of the retryable's calldata, in bytes
                       * @param baseFee The block basefee when the retryable is included in the chain, if 0 current block.basefee will be used
                       */
                      function calculateRetryableSubmissionFee(uint256 dataLength, uint256 baseFee)
                          external
                          view
                          returns (uint256);
                      // ---------- onlyRollupOrOwner functions ----------
                      /// @notice pauses all inbox functionality
                      function pause() external;
                      /// @notice unpauses all inbox functionality
                      function unpause() external;
                      /// @notice add or remove users from allowList
                      function setAllowList(address[] memory user, bool[] memory val) external;
                      /// @notice enable or disable allowList
                      function setAllowListEnabled(bool _allowListEnabled) external;
                      /// @notice check if user is in allowList
                      function isAllowed(address user) external view returns (bool);
                      /// @notice check if allowList is enabled
                      function allowListEnabled() external view returns (bool);
                      function initialize(IBridge _bridge, ISequencerInbox _sequencerInbox) external;
                      /// @notice returns the current admin
                      function getProxyAdmin() external view returns (address);
                  }
                  // Copyright 2021-2022, Offchain Labs, Inc.
                  // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/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);
                  }
                  // Copyright 2021-2022, Offchain Labs, Inc.
                  // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE
                  // SPDX-License-Identifier: BUSL-1.1
                  // solhint-disable-next-line compiler-version
                  pragma solidity >=0.6.9 <0.9.0;
                  pragma experimental ABIEncoderV2;
                  import "../libraries/IGasRefunder.sol";
                  import "./IDelayedMessageProvider.sol";
                  import "./IBridge.sol";
                  interface ISequencerInbox is IDelayedMessageProvider {
                      struct MaxTimeVariation {
                          uint256 delayBlocks;
                          uint256 futureBlocks;
                          uint256 delaySeconds;
                          uint256 futureSeconds;
                      }
                      event SequencerBatchDelivered(
                          uint256 indexed batchSequenceNumber,
                          bytes32 indexed beforeAcc,
                          bytes32 indexed afterAcc,
                          bytes32 delayedAcc,
                          uint256 afterDelayedMessagesRead,
                          IBridge.TimeBounds timeBounds,
                          IBridge.BatchDataLocation dataLocation
                      );
                      event OwnerFunctionCalled(uint256 indexed id);
                      /// @dev a separate event that emits batch data when this isn't easily accessible in the tx.input
                      event SequencerBatchData(uint256 indexed batchSequenceNumber, bytes data);
                      /// @dev a valid keyset was added
                      event SetValidKeyset(bytes32 indexed keysetHash, bytes keysetBytes);
                      /// @dev a keyset was invalidated
                      event InvalidateKeyset(bytes32 indexed keysetHash);
                      function totalDelayedMessagesRead() external view returns (uint256);
                      function bridge() external view returns (IBridge);
                      /// @dev The size of the batch header
                      // solhint-disable-next-line func-name-mixedcase
                      function HEADER_LENGTH() external view returns (uint256);
                      /// @dev If the first batch data byte after the header has this bit set,
                      ///      the sequencer inbox has authenticated the data. Currently only used for 4844 blob support.
                      ///      See: https://github.com/OffchainLabs/nitro/blob/69de0603abf6f900a4128cab7933df60cad54ded/arbstate/das_reader.go
                      // solhint-disable-next-line func-name-mixedcase
                      function DATA_AUTHENTICATED_FLAG() external view returns (bytes1);
                      /// @dev If the first data byte after the header has this bit set,
                      ///      then the batch data is to be found in 4844 data blobs
                      ///      See: https://github.com/OffchainLabs/nitro/blob/69de0603abf6f900a4128cab7933df60cad54ded/arbstate/das_reader.go
                      // solhint-disable-next-line func-name-mixedcase
                      function DATA_BLOB_HEADER_FLAG() external view returns (bytes1);
                      /// @dev If the first data byte after the header has this bit set,
                      ///      then the batch data is a das message
                      ///      See: https://github.com/OffchainLabs/nitro/blob/69de0603abf6f900a4128cab7933df60cad54ded/arbstate/das_reader.go
                      // solhint-disable-next-line func-name-mixedcase
                      function DAS_MESSAGE_HEADER_FLAG() external view returns (bytes1);
                      /// @dev If the first data byte after the header has this bit set,
                      ///      then the batch data is a das message that employs a merklesization strategy
                      ///      See: https://github.com/OffchainLabs/nitro/blob/69de0603abf6f900a4128cab7933df60cad54ded/arbstate/das_reader.go
                      // solhint-disable-next-line func-name-mixedcase
                      function TREE_DAS_MESSAGE_HEADER_FLAG() external view returns (bytes1);
                      /// @dev If the first data byte after the header has this bit set,
                      ///      then the batch data has been brotli compressed
                      ///      See: https://github.com/OffchainLabs/nitro/blob/69de0603abf6f900a4128cab7933df60cad54ded/arbstate/das_reader.go
                      // solhint-disable-next-line func-name-mixedcase
                      function BROTLI_MESSAGE_HEADER_FLAG() external view returns (bytes1);
                      /// @dev If the first data byte after the header has this bit set,
                      ///      then the batch data uses a zero heavy encoding
                      ///      See: https://github.com/OffchainLabs/nitro/blob/69de0603abf6f900a4128cab7933df60cad54ded/arbstate/das_reader.go
                      // solhint-disable-next-line func-name-mixedcase
                      function ZERO_HEAVY_MESSAGE_HEADER_FLAG() external view returns (bytes1);
                      function rollup() external view returns (IOwnable);
                      function isBatchPoster(address) external view returns (bool);
                      function isSequencer(address) external view returns (bool);
                      function maxDataSize() external view returns (uint256);
                      /// @notice The batch poster manager has the ability to change the batch poster addresses
                      ///         This enables the batch poster to do key rotation
                      function batchPosterManager() external view returns (address);
                      struct DasKeySetInfo {
                          bool isValidKeyset;
                          uint64 creationBlock;
                      }
                      /// @dev returns 4 uint256 to be compatible with older version
                      function maxTimeVariation()
                          external
                          view
                          returns (
                              uint256 delayBlocks,
                              uint256 futureBlocks,
                              uint256 delaySeconds,
                              uint256 futureSeconds
                          );
                      function dasKeySetInfo(bytes32) external view returns (bool, uint64);
                      /// @notice Remove force inclusion delay after a L1 chainId fork
                      function removeDelayAfterFork() external;
                      /// @notice Force messages from the delayed inbox to be included in the chain
                      ///         Callable by any address, but message can only be force-included after maxTimeVariation.delayBlocks and
                      ///         maxTimeVariation.delaySeconds has elapsed. As part of normal behaviour the sequencer will include these
                      ///         messages so it's only necessary to call this if the sequencer is down, or not including any delayed messages.
                      /// @param _totalDelayedMessagesRead The total number of messages to read up to
                      /// @param kind The kind of the last message to be included
                      /// @param l1BlockAndTime The l1 block and the l1 timestamp of the last message to be included
                      /// @param baseFeeL1 The l1 gas price of the last message to be included
                      /// @param sender The sender of the last message to be included
                      /// @param messageDataHash The messageDataHash of the last message to be included
                      function forceInclusion(
                          uint256 _totalDelayedMessagesRead,
                          uint8 kind,
                          uint64[2] calldata l1BlockAndTime,
                          uint256 baseFeeL1,
                          address sender,
                          bytes32 messageDataHash
                      ) external;
                      function inboxAccs(uint256 index) external view returns (bytes32);
                      function batchCount() external view returns (uint256);
                      function isValidKeysetHash(bytes32 ksHash) external view returns (bool);
                      /// @notice the creation block is intended to still be available after a keyset is deleted
                      function getKeysetCreationBlock(bytes32 ksHash) external view returns (uint256);
                      // ---------- BatchPoster functions ----------
                      function addSequencerL2BatchFromOrigin(
                          uint256 sequenceNumber,
                          bytes calldata data,
                          uint256 afterDelayedMessagesRead,
                          IGasRefunder gasRefunder
                      ) external;
                      function addSequencerL2BatchFromOrigin(
                          uint256 sequenceNumber,
                          bytes calldata data,
                          uint256 afterDelayedMessagesRead,
                          IGasRefunder gasRefunder,
                          uint256 prevMessageCount,
                          uint256 newMessageCount
                      ) external;
                      function addSequencerL2Batch(
                          uint256 sequenceNumber,
                          bytes calldata data,
                          uint256 afterDelayedMessagesRead,
                          IGasRefunder gasRefunder,
                          uint256 prevMessageCount,
                          uint256 newMessageCount
                      ) external;
                      function addSequencerL2BatchFromBlobs(
                          uint256 sequenceNumber,
                          uint256 afterDelayedMessagesRead,
                          IGasRefunder gasRefunder,
                          uint256 prevMessageCount,
                          uint256 newMessageCount
                      ) external;
                      // ---------- onlyRollupOrOwner functions ----------
                      /**
                       * @notice Set max delay for sequencer inbox
                       * @param maxTimeVariation_ the maximum time variation parameters
                       */
                      function setMaxTimeVariation(MaxTimeVariation memory maxTimeVariation_) external;
                      /**
                       * @notice Updates whether an address is authorized to be a batch poster at the sequencer inbox
                       * @param addr the address
                       * @param isBatchPoster_ if the specified address should be authorized as a batch poster
                       */
                      function setIsBatchPoster(address addr, bool isBatchPoster_) external;
                      /**
                       * @notice Makes Data Availability Service keyset valid
                       * @param keysetBytes bytes of the serialized keyset
                       */
                      function setValidKeyset(bytes calldata keysetBytes) external;
                      /**
                       * @notice Invalidates a Data Availability Service keyset
                       * @param ksHash hash of the keyset
                       */
                      function invalidateKeysetHash(bytes32 ksHash) external;
                      /**
                       * @notice Updates whether an address is authorized to be a sequencer.
                       * @dev The IsSequencer information is used only off-chain by the nitro node to validate sequencer feed signer.
                       * @param addr the address
                       * @param isSequencer_ if the specified address should be authorized as a sequencer
                       */
                      function setIsSequencer(address addr, bool isSequencer_) external;
                      /**
                       * @notice Updates the batch poster manager, the address which has the ability to rotate batch poster keys
                       * @param newBatchPosterManager The new batch poster manager to be set
                       */
                      function setBatchPosterManager(address newBatchPosterManager) external;
                      /// @notice Allows the rollup owner to sync the rollup address
                      function updateRollupAddress() external;
                      // ---------- initializer ----------
                      function initialize(IBridge bridge_, MaxTimeVariation calldata maxTimeVariation_) external;
                  }
                  // Copyright 2021-2022, Offchain Labs, Inc.
                  // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.0;
                  library AddressAliasHelper {
                      uint160 internal constant OFFSET = uint160(0x1111000000000000000000000000000000001111);
                      /// @notice Utility function that converts the address in the L1 that submitted a tx to
                      /// the inbox to the msg.sender viewed in the L2
                      /// @param l1Address the address in the L1 that triggered the tx to L2
                      /// @return l2Address L2 address as viewed in msg.sender
                      function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) {
                          unchecked {
                              l2Address = address(uint160(l1Address) + OFFSET);
                          }
                      }
                      /// @notice Utility function that converts the msg.sender viewed in the L2 to the
                      /// address in the L1 that submitted a tx to the inbox
                      /// @param l2Address L2 address as viewed in msg.sender
                      /// @return l1Address the address in the L1 that triggered the tx to L2
                      function undoL1ToL2Alias(address l2Address) internal pure returns (address l1Address) {
                          unchecked {
                              l1Address = address(uint160(l2Address) - OFFSET);
                          }
                      }
                  }
                  // Copyright 2021-2024, Offchain Labs, Inc.
                  // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.0;
                  library CallerChecker {
                      /**
                       * @notice A EIP-7702 safe check to ensure the caller is the origin and is codeless
                       * @return bool true if the caller is the origin and is codeless, false otherwise
                       * @dev    If the caller is the origin and is codeless, then msg.data is guaranteed to be same as tx.data
                       *         It also mean the caller would not be able to call a contract multiple times with the same transaction
                       */
                      function isCallerCodelessOrigin() internal view returns (bool) {
                          // solhint-disable-next-line avoid-tx-origin
                          return msg.sender == tx.origin && msg.sender.code.length == 0;
                      }
                  }
                  // Copyright 2021-2022, Offchain Labs, Inc.
                  // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.4;
                  uint64 constant NO_CHAL_INDEX = 0;
                  // Expected seconds per block in Ethereum PoS
                  uint256 constant ETH_POS_BLOCK_TIME = 12;
                  /// @dev If nativeTokenDecimals is different than 18 decimals, bridge will inflate or deflate token amounts
                  ///      when depositing to child chain to match 18 decimal denomination. Opposite process happens when
                  ///      amount is withdrawn back to parent chain. In order to avoid uint256 overflows we restrict max number
                  ///      of decimals to 36 which should be enough for most practical use-cases.
                  uint8 constant MAX_ALLOWED_NATIVE_TOKEN_DECIMALS = uint8(36);
                  /// @dev Max amount of erc20 native token that can deposit when upscaling is required (i.e. < 18 decimals)
                  ///      Amounts higher than this would risk uint256 overflows when adjusting decimals. Considering
                  ///      18 decimals are 60 bits, we choose 2^192 as the limit which equals to ~6.3*10^57 weis of token
                  uint256 constant MAX_UPSCALE_AMOUNT = type(uint192).max;
                  // Copyright 2021-2022, Offchain Labs, Inc.
                  // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.0;
                  import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
                  library DecimalsConverterHelper {
                      /// @notice generic function for mapping amount from one decimal denomination to another
                      /// @dev Ie. let's say amount is 752. If token has 16 decimals and is being adjusted to
                      ///      18 decimals then amount will be 75200. If token has 20 decimals adjusted amount
                      ///      is 7. If token uses no decimals converted amount is 752*10^18.
                      ///      When amount is adjusted from 18 decimals back to native token decimals, opposite
                      ///      process is performed.
                      /// @param amount amount to convert
                      /// @param decimalsIn current decimals
                      /// @param decimalsOut target decimals
                      /// @return amount converted to 'decimalsOut' decimals
                      function adjustDecimals(
                          uint256 amount,
                          uint8 decimalsIn,
                          uint8 decimalsOut
                      ) internal pure returns (uint256) {
                          if (decimalsIn == decimalsOut) {
                              return amount;
                          } else if (decimalsIn < decimalsOut) {
                              return amount * 10**(decimalsOut - decimalsIn);
                          } else {
                              return amount / 10**(decimalsIn - decimalsOut);
                          }
                      }
                  }
                  // Copyright 2021-2022, Offchain Labs, Inc.
                  // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/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/OffchainLabs/nitro-contracts/blob/main/LICENSE
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.4;
                  /// @dev Init was already called
                  error AlreadyInit();
                  /// @dev Init was called with param set to zero that must be nonzero
                  error HadZeroInit();
                  /// @dev Thrown when post upgrade init validation fails
                  error BadPostUpgradeInit();
                  /// @dev Thrown when the caller is not a codeless origin
                  error NotCodelessOrigin();
                  /// @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);
                  /// @dev The provided token address isn't valid
                  /// @param token address of token being set
                  error InvalidTokenSet(address token);
                  /// @dev Call to this specific address is not allowed
                  /// @param target address of the call receiver
                  error CallTargetNotAllowed(address target);
                  /// @dev Call that changes the balance of ERC20Bridge is not allowed
                  error CallNotAllowed();
                  // Inbox Errors
                  /// @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
                  );
                  /// @dev Thrown when a L1 chainId fork is detected
                  error L1Forked();
                  /// @dev Thrown when a L1 chainId fork is not detected
                  error NotForked();
                  /// @dev The provided gasLimit is larger than uint64
                  error GasLimitTooLarge();
                  /// @dev The provided amount cannot be adjusted to 18 decimals due to overflow
                  error AmountTooLarge(uint256 amount);
                  /// @dev Number of native token's decimals is restricted to enable conversions to 18 decimals
                  error NativeTokenDecimalsTooLarge(uint256 decimals);
                  // 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 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);
                  /// @dev Thrown when the provided address is not the designated batch poster manager
                  error NotBatchPosterManager(address);
                  /// @dev Thrown when a data blob feature is attempted to be used on a chain that doesnt support it
                  error DataBlobsNotSupported();
                  /// @dev Thrown when an init param was supplied as empty
                  error InitParamZero(string name);
                  /// @dev Thrown when data hashes where expected but not where present on the tx
                  error MissingDataHashes();
                  /// @dev Thrown when rollup is not updated with updateRollupAddress
                  error RollupNotChanged();
                  /// @dev Unsupported header flag was provided
                  error InvalidHeaderFlag(bytes1);
                  /// @dev SequencerInbox and Bridge are not in the same feeToken/ETH mode
                  error NativeTokenMismatch();
                  /// @dev Thrown when a deprecated function is called
                  error Deprecated();
                  /// @dev Thrown when any component of maxTimeVariation is over uint64
                  error BadMaxTimeVariation();
                  // Copyright 2021-2022, Offchain Labs, Inc.
                  // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE
                  // SPDX-License-Identifier: BUSL-1.1
                  // solhint-disable-next-line compiler-version
                  pragma solidity >=0.6.9 <0.9.0;
                  interface IGasRefunder {
                      function onGasSpent(
                          address payable spender,
                          uint256 gasUsed,
                          uint256 calldataSize
                      ) external returns (bool success);
                  }
                  // Copyright 2021-2022, Offchain Labs, Inc.
                  // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/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;
                  

                  File 5 of 5: ERC20Bridge
                  // 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);
                              }
                          }
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/ERC20.sol)
                  pragma solidity ^0.8.0;
                  import "./IERC20.sol";
                  import "./extensions/IERC20Metadata.sol";
                  import "../../utils/Context.sol";
                  /**
                   * @dev Implementation of the {IERC20} interface.
                   *
                   * This implementation is agnostic to the way tokens are created. This means
                   * that a supply mechanism has to be added in a derived contract using {_mint}.
                   * For a generic mechanism see {ERC20PresetMinterPauser}.
                   *
                   * TIP: For a detailed writeup see our guide
                   * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
                   * to implement supply mechanisms].
                   *
                   * We have followed general OpenZeppelin Contracts guidelines: functions revert
                   * instead returning `false` on failure. This behavior is nonetheless
                   * conventional and does not conflict with the expectations of ERC20
                   * applications.
                   *
                   * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
                   * This allows applications to reconstruct the allowance for all accounts just
                   * by listening to said events. Other implementations of the EIP may not emit
                   * these events, as it isn't required by the specification.
                   *
                   * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
                   * functions have been added to mitigate the well-known issues around setting
                   * allowances. See {IERC20-approve}.
                   */
                  contract ERC20 is Context, IERC20, IERC20Metadata {
                      mapping(address => uint256) private _balances;
                      mapping(address => mapping(address => uint256)) private _allowances;
                      uint256 private _totalSupply;
                      string private _name;
                      string private _symbol;
                      /**
                       * @dev Sets the values for {name} and {symbol}.
                       *
                       * The default value of {decimals} is 18. To select a different value for
                       * {decimals} you should overload it.
                       *
                       * All two of these values are immutable: they can only be set once during
                       * construction.
                       */
                      constructor(string memory name_, string memory symbol_) {
                          _name = name_;
                          _symbol = symbol_;
                      }
                      /**
                       * @dev Returns the name of the token.
                       */
                      function name() public view virtual override returns (string memory) {
                          return _name;
                      }
                      /**
                       * @dev Returns the symbol of the token, usually a shorter version of the
                       * name.
                       */
                      function symbol() public view virtual override returns (string memory) {
                          return _symbol;
                      }
                      /**
                       * @dev Returns the number of decimals used to get its user representation.
                       * For example, if `decimals` equals `2`, a balance of `505` tokens should
                       * be displayed to a user as `5.05` (`505 / 10 ** 2`).
                       *
                       * Tokens usually opt for a value of 18, imitating the relationship between
                       * Ether and Wei. This is the value {ERC20} uses, unless this function is
                       * overridden;
                       *
                       * NOTE: This information is only used for _display_ purposes: it in
                       * no way affects any of the arithmetic of the contract, including
                       * {IERC20-balanceOf} and {IERC20-transfer}.
                       */
                      function decimals() public view virtual override returns (uint8) {
                          return 18;
                      }
                      /**
                       * @dev See {IERC20-totalSupply}.
                       */
                      function totalSupply() public view virtual override returns (uint256) {
                          return _totalSupply;
                      }
                      /**
                       * @dev See {IERC20-balanceOf}.
                       */
                      function balanceOf(address account) public view virtual override returns (uint256) {
                          return _balances[account];
                      }
                      /**
                       * @dev See {IERC20-transfer}.
                       *
                       * Requirements:
                       *
                       * - `to` cannot be the zero address.
                       * - the caller must have a balance of at least `amount`.
                       */
                      function transfer(address to, uint256 amount) public virtual override returns (bool) {
                          address owner = _msgSender();
                          _transfer(owner, to, amount);
                          return true;
                      }
                      /**
                       * @dev See {IERC20-allowance}.
                       */
                      function allowance(address owner, address spender) public view virtual override returns (uint256) {
                          return _allowances[owner][spender];
                      }
                      /**
                       * @dev See {IERC20-approve}.
                       *
                       * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
                       * `transferFrom`. This is semantically equivalent to an infinite approval.
                       *
                       * Requirements:
                       *
                       * - `spender` cannot be the zero address.
                       */
                      function approve(address spender, uint256 amount) public virtual override returns (bool) {
                          address owner = _msgSender();
                          _approve(owner, spender, amount);
                          return true;
                      }
                      /**
                       * @dev See {IERC20-transferFrom}.
                       *
                       * Emits an {Approval} event indicating the updated allowance. This is not
                       * required by the EIP. See the note at the beginning of {ERC20}.
                       *
                       * NOTE: Does not update the allowance if the current allowance
                       * is the maximum `uint256`.
                       *
                       * Requirements:
                       *
                       * - `from` and `to` cannot be the zero address.
                       * - `from` must have a balance of at least `amount`.
                       * - the caller must have allowance for ``from``'s tokens of at least
                       * `amount`.
                       */
                      function transferFrom(
                          address from,
                          address to,
                          uint256 amount
                      ) public virtual override returns (bool) {
                          address spender = _msgSender();
                          _spendAllowance(from, spender, amount);
                          _transfer(from, to, amount);
                          return true;
                      }
                      /**
                       * @dev Atomically increases the allowance granted to `spender` by the caller.
                       *
                       * This is an alternative to {approve} that can be used as a mitigation for
                       * problems described in {IERC20-approve}.
                       *
                       * Emits an {Approval} event indicating the updated allowance.
                       *
                       * Requirements:
                       *
                       * - `spender` cannot be the zero address.
                       */
                      function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
                          address owner = _msgSender();
                          _approve(owner, spender, _allowances[owner][spender] + addedValue);
                          return true;
                      }
                      /**
                       * @dev Atomically decreases the allowance granted to `spender` by the caller.
                       *
                       * This is an alternative to {approve} that can be used as a mitigation for
                       * problems described in {IERC20-approve}.
                       *
                       * Emits an {Approval} event indicating the updated allowance.
                       *
                       * Requirements:
                       *
                       * - `spender` cannot be the zero address.
                       * - `spender` must have allowance for the caller of at least
                       * `subtractedValue`.
                       */
                      function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
                          address owner = _msgSender();
                          uint256 currentAllowance = _allowances[owner][spender];
                          require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
                          unchecked {
                              _approve(owner, spender, currentAllowance - subtractedValue);
                          }
                          return true;
                      }
                      /**
                       * @dev Moves `amount` of tokens from `sender` to `recipient`.
                       *
                       * This internal function is equivalent to {transfer}, and can be used to
                       * e.g. implement automatic token fees, slashing mechanisms, etc.
                       *
                       * Emits a {Transfer} event.
                       *
                       * Requirements:
                       *
                       * - `from` cannot be the zero address.
                       * - `to` cannot be the zero address.
                       * - `from` must have a balance of at least `amount`.
                       */
                      function _transfer(
                          address from,
                          address to,
                          uint256 amount
                      ) internal virtual {
                          require(from != address(0), "ERC20: transfer from the zero address");
                          require(to != address(0), "ERC20: transfer to the zero address");
                          _beforeTokenTransfer(from, to, amount);
                          uint256 fromBalance = _balances[from];
                          require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
                          unchecked {
                              _balances[from] = fromBalance - amount;
                          }
                          _balances[to] += amount;
                          emit Transfer(from, to, amount);
                          _afterTokenTransfer(from, to, amount);
                      }
                      /** @dev Creates `amount` tokens and assigns them to `account`, increasing
                       * the total supply.
                       *
                       * Emits a {Transfer} event with `from` set to the zero address.
                       *
                       * Requirements:
                       *
                       * - `account` cannot be the zero address.
                       */
                      function _mint(address account, uint256 amount) internal virtual {
                          require(account != address(0), "ERC20: mint to the zero address");
                          _beforeTokenTransfer(address(0), account, amount);
                          _totalSupply += amount;
                          _balances[account] += amount;
                          emit Transfer(address(0), account, amount);
                          _afterTokenTransfer(address(0), account, amount);
                      }
                      /**
                       * @dev Destroys `amount` tokens from `account`, reducing the
                       * total supply.
                       *
                       * Emits a {Transfer} event with `to` set to the zero address.
                       *
                       * Requirements:
                       *
                       * - `account` cannot be the zero address.
                       * - `account` must have at least `amount` tokens.
                       */
                      function _burn(address account, uint256 amount) internal virtual {
                          require(account != address(0), "ERC20: burn from the zero address");
                          _beforeTokenTransfer(account, address(0), amount);
                          uint256 accountBalance = _balances[account];
                          require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
                          unchecked {
                              _balances[account] = accountBalance - amount;
                          }
                          _totalSupply -= amount;
                          emit Transfer(account, address(0), amount);
                          _afterTokenTransfer(account, address(0), amount);
                      }
                      /**
                       * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
                       *
                       * This internal function is equivalent to `approve`, and can be used to
                       * e.g. set automatic allowances for certain subsystems, etc.
                       *
                       * Emits an {Approval} event.
                       *
                       * Requirements:
                       *
                       * - `owner` cannot be the zero address.
                       * - `spender` cannot be the zero address.
                       */
                      function _approve(
                          address owner,
                          address spender,
                          uint256 amount
                      ) internal virtual {
                          require(owner != address(0), "ERC20: approve from the zero address");
                          require(spender != address(0), "ERC20: approve to the zero address");
                          _allowances[owner][spender] = amount;
                          emit Approval(owner, spender, amount);
                      }
                      /**
                       * @dev Spend `amount` form the allowance of `owner` toward `spender`.
                       *
                       * Does not update the allowance amount in case of infinite allowance.
                       * Revert if not enough allowance is available.
                       *
                       * Might emit an {Approval} event.
                       */
                      function _spendAllowance(
                          address owner,
                          address spender,
                          uint256 amount
                      ) internal virtual {
                          uint256 currentAllowance = allowance(owner, spender);
                          if (currentAllowance != type(uint256).max) {
                              require(currentAllowance >= amount, "ERC20: insufficient allowance");
                              unchecked {
                                  _approve(owner, spender, currentAllowance - amount);
                              }
                          }
                      }
                      /**
                       * @dev Hook that is called before any transfer of tokens. This includes
                       * minting and burning.
                       *
                       * Calling conditions:
                       *
                       * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                       * will be transferred to `to`.
                       * - when `from` is zero, `amount` tokens will be minted for `to`.
                       * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
                       * - `from` and `to` are never both zero.
                       *
                       * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                       */
                      function _beforeTokenTransfer(
                          address from,
                          address to,
                          uint256 amount
                      ) internal virtual {}
                      /**
                       * @dev Hook that is called after any transfer of tokens. This includes
                       * minting and burning.
                       *
                       * Calling conditions:
                       *
                       * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                       * has been transferred to `to`.
                       * - when `from` is zero, `amount` tokens have been minted for `to`.
                       * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
                       * - `from` and `to` are never both zero.
                       *
                       * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                       */
                      function _afterTokenTransfer(
                          address from,
                          address to,
                          uint256 amount
                      ) internal virtual {}
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
                  pragma solidity ^0.8.0;
                  import "../IERC20.sol";
                  /**
                   * @dev Interface for the optional metadata functions from the ERC20 standard.
                   *
                   * _Available since v4.1._
                   */
                  interface IERC20Metadata is IERC20 {
                      /**
                       * @dev Returns the name of the token.
                       */
                      function name() external view returns (string memory);
                      /**
                       * @dev Returns the symbol of the token.
                       */
                      function symbol() external view returns (string memory);
                      /**
                       * @dev Returns the decimals places of the token.
                       */
                      function decimals() external view returns (uint8);
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)
                  pragma solidity ^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 `to`.
                       *
                       * Returns a boolean value indicating whether the operation succeeded.
                       *
                       * Emits a {Transfer} event.
                       */
                      function transfer(address to, uint256 amount) external returns (bool);
                      /**
                       * @dev Returns the remaining number of tokens that `spender` will be
                       * allowed to spend on behalf of `owner` through {transferFrom}. This is
                       * zero by default.
                       *
                       * This value changes when {approve} or {transferFrom} are called.
                       */
                      function allowance(address owner, address spender) external view returns (uint256);
                      /**
                       * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                       *
                       * Returns a boolean value indicating whether the operation succeeded.
                       *
                       * IMPORTANT: Beware that changing an allowance with this method brings the risk
                       * that someone may use both the old and the new allowance by unfortunate
                       * transaction ordering. One possible solution to mitigate this race
                       * condition is to first reduce the spender's allowance to 0 and set the
                       * desired value afterwards:
                       * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                       *
                       * Emits an {Approval} event.
                       */
                      function approve(address spender, uint256 amount) external returns (bool);
                      /**
                       * @dev Moves `amount` tokens from `from` to `to` using the
                       * allowance mechanism. `amount` is then deducted from the caller's
                       * allowance.
                       *
                       * Returns a boolean value indicating whether the operation succeeded.
                       *
                       * Emits a {Transfer} event.
                       */
                      function transferFrom(
                          address from,
                          address to,
                          uint256 amount
                      ) external returns (bool);
                      /**
                       * @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
                  // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)
                  pragma solidity ^0.8.0;
                  import "../IERC20.sol";
                  import "../../../utils/Address.sol";
                  /**
                   * @title SafeERC20
                   * @dev Wrappers around ERC20 operations that throw on failure (when the token
                   * contract returns false). Tokens that return no value (and instead revert or
                   * throw on failure) are also supported, non-reverting calls are assumed to be
                   * successful.
                   * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
                   * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
                   */
                  library SafeERC20 {
                      using Address for address;
                      function safeTransfer(
                          IERC20 token,
                          address to,
                          uint256 value
                      ) internal {
                          _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                      }
                      function safeTransferFrom(
                          IERC20 token,
                          address from,
                          address to,
                          uint256 value
                      ) internal {
                          _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                      }
                      /**
                       * @dev Deprecated. This function has issues similar to the ones found in
                       * {IERC20-approve}, and its usage is discouraged.
                       *
                       * Whenever possible, use {safeIncreaseAllowance} and
                       * {safeDecreaseAllowance} instead.
                       */
                      function safeApprove(
                          IERC20 token,
                          address spender,
                          uint256 value
                      ) internal {
                          // safeApprove should only be called when setting an initial allowance,
                          // or when resetting it to zero. To increase and decrease it, use
                          // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                          require(
                              (value == 0) || (token.allowance(address(this), spender) == 0),
                              "SafeERC20: approve from non-zero to non-zero allowance"
                          );
                          _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                      }
                      function safeIncreaseAllowance(
                          IERC20 token,
                          address spender,
                          uint256 value
                      ) internal {
                          uint256 newAllowance = token.allowance(address(this), spender) + value;
                          _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                      }
                      function safeDecreaseAllowance(
                          IERC20 token,
                          address spender,
                          uint256 value
                      ) internal {
                          unchecked {
                              uint256 oldAllowance = token.allowance(address(this), spender);
                              require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                              uint256 newAllowance = oldAllowance - value;
                              _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                          }
                      }
                      /**
                       * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                       * on the return value: the return value is optional (but if data is returned, it must not be false).
                       * @param token The token targeted by the call.
                       * @param data The call data (encoded using abi.encode or one of its variants).
                       */
                      function _callOptionalReturn(IERC20 token, bytes memory data) private {
                          // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                          // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
                          // the target address contains contract code and also asserts for success in the low-level call.
                          bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                          if (returndata.length > 0) {
                              // Return data is optional
                              require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                          }
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // 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/Context.sol)
                  pragma solidity ^0.8.0;
                  /**
                   * @dev Provides information about the current execution context, including the
                   * sender of the transaction and its data. While these are generally available
                   * via msg.sender and msg.data, they should not be accessed in such a direct
                   * manner, since when dealing with meta-transactions the account sending and
                   * paying for execution may not be the actual sender (as far as an application
                   * is concerned).
                   *
                   * This contract is only required for intermediate, library-like contracts.
                   */
                  abstract contract Context {
                      function _msgSender() internal view virtual returns (address) {
                          return msg.sender;
                      }
                      function _msgData() internal view virtual returns (bytes calldata) {
                          return msg.data;
                      }
                  }
                  // 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.
                   * 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.
                   */
                  abstract contract AbsBridge 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 internal _activeOutbox;
                      /// @inheritdoc IBridge
                      bytes32[] public delayedInboxAccs;
                      /// @inheritdoc IBridge
                      bytes32[] public sequencerInboxAccs;
                      IOwnable public rollup;
                      address public sequencerInbox;
                      uint256 public override sequencerReportedSubMessageCount;
                      address internal constant EMPTY_ACTIVEOUTBOX = address(type(uint160).max);
                      modifier onlyRollupOrOwner() {
                          if (msg.sender != address(rollup)) {
                              address rollupOwner = rollup.owner();
                              if (msg.sender != rollupOwner) {
                                  revert NotRollupOrOwner(msg.sender, address(rollup), rollupOwner);
                              }
                          }
                          _;
                      }
                      /// @notice Allows the rollup owner to set another rollup address
                      function updateRollupAddress(IOwnable _rollup) external onlyRollupOrOwner {
                          rollup = _rollup;
                          emit RollupUpdated(address(_rollup));
                      }
                      /// @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) public view returns (bool) {
                          return allowedDelayedInboxesMap[inbox].allowed;
                      }
                      function allowedOutboxes(address outbox) public 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
                              );
                      }
                      function _enqueueDelayedMessage(
                          uint8 kind,
                          address sender,
                          bytes32 messageDataHash,
                          uint256 amount
                      ) internal returns (uint256) {
                          if (!allowedDelayedInboxes(msg.sender)) revert NotDelayedInbox(msg.sender);
                          uint256 messageCount = addMessageToDelayedAccumulator(
                              kind,
                              sender,
                              uint64(block.number),
                              uint64(block.timestamp), // solhint-disable-line not-rely-on-time
                              _baseFeeToReport(),
                              messageDataHash
                          );
                          _transferFunds(amount);
                          return messageCount;
                      }
                      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;
                      }
                      /// @inheritdoc IBridge
                      function executeCall(
                          address to,
                          uint256 value,
                          bytes calldata data
                      ) external returns (bool success, bytes memory returnData) {
                          if (!allowedOutboxes(msg.sender)) 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
                          (success, returnData) = _executeLowLevelCall(to, 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) {
                              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) {
                              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 {}
                      /// @dev transfer funds provided to pay for crosschain msg
                      function _transferFunds(uint256 amount) internal virtual;
                      function _executeLowLevelCall(
                          address to,
                          uint256 value,
                          bytes memory data
                      ) internal virtual returns (bool success, bytes memory returnData);
                      /// @dev get base fee which is emitted in `MessageDelivered` event and then picked up and
                      /// used in ArbOs to calculate the submission fee for retryable ticket
                      function _baseFeeToReport() internal view virtual returns (uint256);
                      /**
                       * @dev This empty reserved space is put in place to allow future versions to add new
                       * variables without shifting down storage in the inheritance chain.
                       * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                       */
                      uint256[40] private __gap;
                  }
                  // 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 "./AbsBridge.sol";
                  import "./IERC20Bridge.sol";
                  import "../libraries/AddressAliasHelper.sol";
                  import {
                      InvalidTokenSet,
                      CallTargetNotAllowed,
                      CallNotAllowed,
                      NativeTokenDecimalsTooLarge
                  } from "../libraries/Error.sol";
                  import {DecimalsConverterHelper} from "../libraries/DecimalsConverterHelper.sol";
                  import {MAX_ALLOWED_NATIVE_TOKEN_DECIMALS} from "../libraries/Constants.sol";
                  import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
                  import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
                  import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
                  /**
                   * @title Staging ground for incoming and outgoing messages
                   * @notice Unlike the standard Eth bridge, native token bridge escrows the custom ERC20 token which is
                   * used as native currency on L2.
                   * @dev Fees are paid in this token. There are certain restrictions on the native token:
                   *       - The token can't be rebasing or have a transfer fee
                   *       - The token must only be transferrable via a call to the token address itself
                   *       - The token must only be able to set allowance via a call to the token address itself
                   *       - The token must not have a callback on transfer, and more generally a user must not be able to make a transfer to themselves revert
                   *       - The token must have a max of 2^256 - 1 wei total supply unscaled
                   *       - The token must have a max of 2^256 - 1 wei total supply when scaled to 18 decimals
                   */
                  contract ERC20Bridge is AbsBridge, IERC20Bridge {
                      using SafeERC20 for IERC20;
                      /// @inheritdoc IERC20Bridge
                      address public nativeToken;
                      /// @inheritdoc IERC20Bridge
                      uint8 public nativeTokenDecimals;
                      /// @inheritdoc IERC20Bridge
                      function initialize(IOwnable rollup_, address nativeToken_) external initializer onlyDelegated {
                          if (nativeToken_ == address(0)) revert InvalidTokenSet(nativeToken_);
                          nativeToken = nativeToken_;
                          _activeOutbox = EMPTY_ACTIVEOUTBOX;
                          rollup = rollup_;
                          // store number of decimals used by native token
                          try ERC20(nativeToken_).decimals() returns (uint8 decimals) {
                              if (decimals > MAX_ALLOWED_NATIVE_TOKEN_DECIMALS) {
                                  revert NativeTokenDecimalsTooLarge(decimals);
                              }
                              nativeTokenDecimals = decimals;
                          } catch {
                              // decimal is not part of the ERC20 spec
                              // assume it have 0 decimals if it does not have decimals() method
                              // we do this to align with the token bridge behavior
                              nativeTokenDecimals = 0;
                          }
                      }
                      /// @notice When upgrading a custom fee chain from v1.x.x to v2.1.2, nativeTokenDecimals must be set to 18.
                      ///         This is because v1.x.x contracts assume 18 decimals, but the ERC20Bridge does not have the decimals set in storage.
                      function postUpgradeInit() external onlyDelegated onlyProxyOwner {
                          // this zero check might save you from accidentally upgrading from v2.x.x to v2.1.2
                          // it will not save you if your native token is supposed to have 0 decimals
                          require(nativeTokenDecimals == 0, "NONZERO_NATIVE_TOKEN_DECIMALS");
                          nativeTokenDecimals = 18;
                      }
                      /// @inheritdoc IERC20Bridge
                      function enqueueDelayedMessage(
                          uint8 kind,
                          address sender,
                          bytes32 messageDataHash,
                          uint256 tokenFeeAmount
                      ) external returns (uint256) {
                          return _enqueueDelayedMessage(kind, sender, messageDataHash, tokenFeeAmount);
                      }
                      function _transferFunds(uint256 amount) internal override {
                          // fetch native token from Inbox
                          if (amount > 0) {
                              IERC20(nativeToken).safeTransferFrom(msg.sender, address(this), amount);
                          }
                      }
                      function _executeLowLevelCall(
                          address to,
                          uint256 value,
                          bytes memory data
                      ) internal override returns (bool success, bytes memory returnData) {
                          address _nativeToken = nativeToken;
                          // we don't allow outgoing calls to native token contract because it could
                          // result in loss of native tokens which are escrowed by ERC20Bridge
                          if (to == _nativeToken) {
                              revert CallTargetNotAllowed(_nativeToken);
                          }
                          // first release native token
                          if (value > 0) {
                              IERC20(_nativeToken).safeTransfer(to, value);
                          }
                          success = true;
                          // if there's data do additional contract call. Make sure that call is not used to
                          // decrease bridge contract's balance of the native token
                          if (data.length > 0) {
                              uint256 bridgeBalanceBefore = IERC20(_nativeToken).balanceOf(address(this));
                              // solhint-disable-next-line avoid-low-level-calls
                              (success, returnData) = to.call(data);
                              uint256 bridgeBalanceAfter = IERC20(_nativeToken).balanceOf(address(this));
                              if (bridgeBalanceAfter < bridgeBalanceBefore) {
                                  revert CallNotAllowed();
                              }
                          }
                      }
                      function _baseFeeToReport() internal pure override returns (uint256) {
                          // ArbOs uses formula 'l1BaseFee * (1400 + 6 * calldataLengthInBytes)' to calculate retryable ticket's
                          // submission fee. When custom ERC20 token is used to pay for fees, submission fee shall be 0. That's
                          // why baseFee is reported as 0 here.
                          return 0;
                      }
                  }
                  // Copyright 2021-2022, Offchain Labs, Inc.
                  // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/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 {
                      /// @dev This is an instruction to offchain readers to inform them where to look
                      ///      for sequencer inbox batch data. This is not the type of data (eg. das, brotli encoded, or blob versioned hash)
                      ///      and this enum is not used in the state transition function, rather it informs an offchain
                      ///      reader where to find the data so that they can supply it to the replay binary
                      enum BatchDataLocation {
                          /// @notice The data can be found in the transaction call data
                          TxInput,
                          /// @notice The data can be found in an event emitted during the transaction
                          SeparateBatchEvent,
                          /// @notice This batch contains no data
                          NoData,
                          /// @notice The data can be found in the 4844 data blobs on this transaction
                          Blob
                      }
                      struct TimeBounds {
                          uint64 minTimestamp;
                          uint64 maxTimestamp;
                          uint64 minBlockNumber;
                          uint64 maxBlockNumber;
                      }
                      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);
                      event RollupUpdated(address rollup);
                      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);
                      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;
                      function updateRollupAddress(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 "./IOwnable.sol";
                  import "./IBridge.sol";
                  interface IERC20Bridge is IBridge {
                      /**
                       * @dev token that is escrowed in bridge on L1 side and minted on L2 as native currency.
                       * Fees are paid in this token. There are certain restrictions on the native token:
                       *  - The token can't be rebasing or have a transfer fee
                       *  - The token must only be transferrable via a call to the token address itself
                       *  - The token must only be able to set allowance via a call to the token address itself
                       *  - The token must not have a callback on transfer, and more generally a user must not be able to make a transfer to themselves revert
                       */
                      function nativeToken() external view returns (address);
                      /**
                       * @dev number of decimals used by the native token
                       *      This is set on bridge initialization using nativeToken.decimals()
                       *      If the token does not have decimals() method, we assume it have 0 decimals
                       */
                      function nativeTokenDecimals() external view returns (uint8);
                      /**
                       * @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,
                          uint256 tokenFeeAmount
                      ) external returns (uint256);
                      // ---------- initializer ----------
                      function initialize(IOwnable rollup_, address nativeToken_) external;
                  }
                  // Copyright 2021-2022, Offchain Labs, Inc.
                  // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/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);
                  }
                  // Copyright 2021-2022, Offchain Labs, Inc.
                  // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/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/OffchainLabs/nitro-contracts/blob/main/LICENSE
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.0;
                  library AddressAliasHelper {
                      uint160 internal constant OFFSET = uint160(0x1111000000000000000000000000000000001111);
                      /// @notice Utility function that converts the address in the L1 that submitted a tx to
                      /// the inbox to the msg.sender viewed in the L2
                      /// @param l1Address the address in the L1 that triggered the tx to L2
                      /// @return l2Address L2 address as viewed in msg.sender
                      function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) {
                          unchecked {
                              l2Address = address(uint160(l1Address) + OFFSET);
                          }
                      }
                      /// @notice Utility function that converts the msg.sender viewed in the L2 to the
                      /// address in the L1 that submitted a tx to the inbox
                      /// @param l2Address L2 address as viewed in msg.sender
                      /// @return l1Address the address in the L1 that triggered the tx to L2
                      function undoL1ToL2Alias(address l2Address) internal pure returns (address l1Address) {
                          unchecked {
                              l1Address = address(uint160(l2Address) - OFFSET);
                          }
                      }
                  }
                  // Copyright 2021-2022, Offchain Labs, Inc.
                  // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.4;
                  uint64 constant NO_CHAL_INDEX = 0;
                  // Expected seconds per block in Ethereum PoS
                  uint256 constant ETH_POS_BLOCK_TIME = 12;
                  /// @dev If nativeTokenDecimals is different than 18 decimals, bridge will inflate or deflate token amounts
                  ///      when depositing to child chain to match 18 decimal denomination. Opposite process happens when
                  ///      amount is withdrawn back to parent chain. In order to avoid uint256 overflows we restrict max number
                  ///      of decimals to 36 which should be enough for most practical use-cases.
                  uint8 constant MAX_ALLOWED_NATIVE_TOKEN_DECIMALS = uint8(36);
                  /// @dev Max amount of erc20 native token that can deposit when upscaling is required (i.e. < 18 decimals)
                  ///      Amounts higher than this would risk uint256 overflows when adjusting decimals. Considering
                  ///      18 decimals are 60 bits, we choose 2^192 as the limit which equals to ~6.3*10^57 weis of token
                  uint256 constant MAX_UPSCALE_AMOUNT = type(uint192).max;
                  // Copyright 2021-2022, Offchain Labs, Inc.
                  // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.0;
                  import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
                  library DecimalsConverterHelper {
                      /// @notice generic function for mapping amount from one decimal denomination to another
                      /// @dev Ie. let's say amount is 752. If token has 16 decimals and is being adjusted to
                      ///      18 decimals then amount will be 75200. If token has 20 decimals adjusted amount
                      ///      is 7. If token uses no decimals converted amount is 752*10^18.
                      ///      When amount is adjusted from 18 decimals back to native token decimals, opposite
                      ///      process is performed.
                      /// @param amount amount to convert
                      /// @param decimalsIn current decimals
                      /// @param decimalsOut target decimals
                      /// @return amount converted to 'decimalsOut' decimals
                      function adjustDecimals(
                          uint256 amount,
                          uint8 decimalsIn,
                          uint8 decimalsOut
                      ) internal pure returns (uint256) {
                          if (decimalsIn == decimalsOut) {
                              return amount;
                          } else if (decimalsIn < decimalsOut) {
                              return amount * 10**(decimalsOut - decimalsIn);
                          } else {
                              return amount / 10**(decimalsIn - decimalsOut);
                          }
                      }
                  }
                  // Copyright 2021-2022, Offchain Labs, Inc.
                  // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/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/OffchainLabs/nitro-contracts/blob/main/LICENSE
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity ^0.8.4;
                  /// @dev Init was already called
                  error AlreadyInit();
                  /// @dev Init was called with param set to zero that must be nonzero
                  error HadZeroInit();
                  /// @dev Thrown when post upgrade init validation fails
                  error BadPostUpgradeInit();
                  /// @dev Thrown when the caller is not a codeless origin
                  error NotCodelessOrigin();
                  /// @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);
                  /// @dev The provided token address isn't valid
                  /// @param token address of token being set
                  error InvalidTokenSet(address token);
                  /// @dev Call to this specific address is not allowed
                  /// @param target address of the call receiver
                  error CallTargetNotAllowed(address target);
                  /// @dev Call that changes the balance of ERC20Bridge is not allowed
                  error CallNotAllowed();
                  // Inbox Errors
                  /// @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
                  );
                  /// @dev Thrown when a L1 chainId fork is detected
                  error L1Forked();
                  /// @dev Thrown when a L1 chainId fork is not detected
                  error NotForked();
                  /// @dev The provided gasLimit is larger than uint64
                  error GasLimitTooLarge();
                  /// @dev The provided amount cannot be adjusted to 18 decimals due to overflow
                  error AmountTooLarge(uint256 amount);
                  /// @dev Number of native token's decimals is restricted to enable conversions to 18 decimals
                  error NativeTokenDecimalsTooLarge(uint256 decimals);
                  // 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 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);
                  /// @dev Thrown when the provided address is not the designated batch poster manager
                  error NotBatchPosterManager(address);
                  /// @dev Thrown when a data blob feature is attempted to be used on a chain that doesnt support it
                  error DataBlobsNotSupported();
                  /// @dev Thrown when an init param was supplied as empty
                  error InitParamZero(string name);
                  /// @dev Thrown when data hashes where expected but not where present on the tx
                  error MissingDataHashes();
                  /// @dev Thrown when rollup is not updated with updateRollupAddress
                  error RollupNotChanged();
                  /// @dev Unsupported header flag was provided
                  error InvalidHeaderFlag(bytes1);
                  /// @dev SequencerInbox and Bridge are not in the same feeToken/ETH mode
                  error NativeTokenMismatch();
                  /// @dev Thrown when a deprecated function is called
                  error Deprecated();
                  /// @dev Thrown when any component of maxTimeVariation is over uint64
                  error BadMaxTimeVariation();
                  // Copyright 2021-2022, Offchain Labs, Inc.
                  // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/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;