ETH Price: $3,434.31 (+11.70%)
Gas: 64 Gwei

Transaction Decoder

Block:
12888372 at Jul-24-2021 10:11:02 AM +UTC
Transaction Fee:
0.002727231 ETH $9.37
Gas Used:
209,787 Gas / 13 Gwei

Emitted Events:

208 OVM_CanonicalTransactionChain.TransactionEnqueued( _l1TxOrigin=Lib_ResolvedDelegateProxy, _target=0x42000000...000000007, _gasLimit=1300000, _data=0xCBD4ECE9000000000000000000000000420000000000000000000000000000000000001000000000000000000000000099C9FC46F92E8A1C0DEC1B1747D010903E884BE100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000001B4200000000000000000000000000000000000000000000000000000000000000E4662A633A000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000060000000000000000000000008D9A44ECA839A661232C6964F959B69D6739FE5D0000000000000000000000008D9A44ECA839A661232C6964F959B69D6739FE5D00000000000000000000000000000000000000000000000000859AEE5701A63400000000000000000000000000000000000000000000000000000000000000C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, _queueIndex=6978, _timestamp=1627121462 )
209 Lib_ResolvedDelegateProxy.0x0ee9ffdb2334d78de97ffb066b23a352a4d35180cefb36589d663fbb1eb6f326( 0x0ee9ffdb2334d78de97ffb066b23a352a4d35180cefb36589d663fbb1eb6f326, 0000000000000000000000000000000000000000000000000000000000000020, 00000000000000000000000000000000000000000000000000000000000001a4, cbd4ece900000000000000000000000042000000000000000000000000000000, 0000001000000000000000000000000099c9fc46f92e8a1c0dec1b1747d01090, 3e884be100000000000000000000000000000000000000000000000000000000, 0000008000000000000000000000000000000000000000000000000000000000, 00001b4200000000000000000000000000000000000000000000000000000000, 000000e4662a633a000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000420000000000000000000000, 00000000000000060000000000000000000000008d9a44eca839a661232c6964, f959b69d6739fe5d0000000000000000000000008d9a44eca839a661232c6964, f959b69d6739fe5d000000000000000000000000000000000000000000000000, 00859aee5701a634000000000000000000000000000000000000000000000000, 00000000000000c0000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000 )
210 L1ChugSplashProxy.0x35d79ab81f2b2017e19afb5c5571778877782d7a8786f5907f93b0f4702f4f23( 0x35d79ab81f2b2017e19afb5c5571778877782d7a8786f5907f93b0f4702f4f23, 0x0000000000000000000000008d9a44eca839a661232c6964f959b69d6739fe5d, 0x0000000000000000000000008d9a44eca839a661232c6964f959b69d6739fe5d, 00000000000000000000000000000000000000000000000000859aee5701a634, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
(Spark Pool)
27.300300313168832964 Eth27.303027544168832964 Eth0.002727231
0x8d9A44eC...D6739FE5D
0.04322313 Eth
Nonce: 0
0.002889378644862924 Eth
Nonce: 1
0.040333751355137076
0x99C9fc46...03E884bE1
(Optimism: Gateway)
3,549.899314371245051582 Eth3,549.936920891600188658 Eth0.037606520355137076
0xA0b912b3...ED6E38039
(Optimism: OVM Chain Storage Container CTC Queue)

Execution Trace

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

                  File 2 of 7: OVM_CanonicalTransactionChain
                  // SPDX-License-Identifier: MIT
                  pragma solidity >=0.6.0 <0.8.0;
                  import "../utils/Context.sol";
                  /**
                   * @dev Contract module which provides a basic access control mechanism, where
                   * there is an account (an owner) that can be granted exclusive access to
                   * specific functions.
                   *
                   * By default, the owner account will be the one that deploys the contract. This
                   * can later be changed with {transferOwnership}.
                   *
                   * This module is used through inheritance. It will make available the modifier
                   * `onlyOwner`, which can be applied to your functions to restrict their use to
                   * the owner.
                   */
                  abstract contract Ownable is Context {
                      address private _owner;
                      event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                      /**
                       * @dev Initializes the contract setting the deployer as the initial owner.
                       */
                      constructor () internal {
                          address msgSender = _msgSender();
                          _owner = msgSender;
                          emit OwnershipTransferred(address(0), msgSender);
                      }
                      /**
                       * @dev Returns the address of the current owner.
                       */
                      function owner() public view virtual returns (address) {
                          return _owner;
                      }
                      /**
                       * @dev Throws if called by any account other than the owner.
                       */
                      modifier onlyOwner() {
                          require(owner() == _msgSender(), "Ownable: caller is not the owner");
                          _;
                      }
                      /**
                       * @dev Leaves the contract without owner. It will not be possible to call
                       * `onlyOwner` functions anymore. Can only be called by the current owner.
                       *
                       * NOTE: Renouncing ownership will leave the contract without an owner,
                       * thereby removing any functionality that is only available to the owner.
                       */
                      function renounceOwnership() public virtual onlyOwner {
                          emit OwnershipTransferred(_owner, address(0));
                          _owner = 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");
                          emit OwnershipTransferred(_owner, newOwner);
                          _owner = newOwner;
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >=0.6.0 <0.8.0;
                  /**
                   * @dev Standard math utilities missing in the Solidity language.
                   */
                  library Math {
                      /**
                       * @dev Returns the largest of two numbers.
                       */
                      function max(uint256 a, uint256 b) internal pure returns (uint256) {
                          return a >= b ? a : b;
                      }
                      /**
                       * @dev Returns the smallest of two numbers.
                       */
                      function min(uint256 a, uint256 b) internal pure returns (uint256) {
                          return a < b ? a : b;
                      }
                      /**
                       * @dev Returns the average of two numbers. The result is rounded towards
                       * zero.
                       */
                      function average(uint256 a, uint256 b) internal pure returns (uint256) {
                          // (a + b) / 2 can overflow, so we distribute
                          return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >=0.6.0 <0.8.0;
                  /*
                   * @dev Provides information about the current execution context, including the
                   * sender of the transaction and its data. While these are generally available
                   * via msg.sender and msg.data, they should not be accessed in such a direct
                   * manner, since when dealing with GSN meta-transactions the account sending and
                   * paying for execution may not be the actual sender (as far as an application
                   * is concerned).
                   *
                   * This contract is only required for intermediate, library-like contracts.
                   */
                  abstract contract Context {
                      function _msgSender() internal view virtual returns (address payable) {
                          return msg.sender;
                      }
                      function _msgData() internal view virtual returns (bytes memory) {
                          this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                          return msg.data;
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // @unsupported: ovm
                  pragma solidity >0.5.0 <0.8.0;
                  pragma experimental ABIEncoderV2;
                  /* Library Imports */
                  import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
                  import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
                  import { Lib_MerkleTree } from "../../libraries/utils/Lib_MerkleTree.sol";
                  /* Interface Imports */
                  import { iOVM_CanonicalTransactionChain } from "../../iOVM/chain/iOVM_CanonicalTransactionChain.sol";
                  import { iOVM_ChainStorageContainer } from "../../iOVM/chain/iOVM_ChainStorageContainer.sol";
                  /* Contract Imports */
                  import { OVM_ExecutionManager } from "../execution/OVM_ExecutionManager.sol";
                  /* External Imports */
                  import { Math } from "@openzeppelin/contracts/math/Math.sol";
                  /**
                   * @title OVM_CanonicalTransactionChain
                   * @dev The Canonical Transaction Chain (CTC) contract is an append-only log of transactions
                   * which must be applied to the rollup state. It defines the ordering of rollup transactions by
                   * writing them to the 'CTC:batches' instance of the Chain Storage Container.
                   * The CTC also allows any account to 'enqueue' an L2 transaction, which will require that the Sequencer
                   * will eventually append it to the rollup state.
                   * If the Sequencer does not include an enqueued transaction within the 'force inclusion period',
                   * then any account may force it to be included by calling appendQueueBatch().
                   *
                   * Compiler used: solc
                   * Runtime target: EVM
                   */
                  contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_AddressResolver {
                      /*************
                       * Constants *
                       *************/
                      // L2 tx gas-related
                      uint256 constant public MIN_ROLLUP_TX_GAS = 100000;
                      uint256 constant public MAX_ROLLUP_TX_SIZE = 50000;
                      uint256 constant public L2_GAS_DISCOUNT_DIVISOR = 32;
                      // Encoding-related (all in bytes)
                      uint256 constant internal BATCH_CONTEXT_SIZE = 16;
                      uint256 constant internal BATCH_CONTEXT_LENGTH_POS = 12;
                      uint256 constant internal BATCH_CONTEXT_START_POS = 15;
                      uint256 constant internal TX_DATA_HEADER_SIZE = 3;
                      uint256 constant internal BYTES_TILL_TX_DATA = 65;
                      /*************
                       * Variables *
                       *************/
                      uint256 public forceInclusionPeriodSeconds;
                      uint256 public forceInclusionPeriodBlocks;
                      uint256 public maxTransactionGasLimit;
                      /***************
                       * Constructor *
                       ***************/
                      constructor(
                          address _libAddressManager,
                          uint256 _forceInclusionPeriodSeconds,
                          uint256 _forceInclusionPeriodBlocks,
                          uint256 _maxTransactionGasLimit
                      )
                          Lib_AddressResolver(_libAddressManager)
                      {
                          forceInclusionPeriodSeconds = _forceInclusionPeriodSeconds;
                          forceInclusionPeriodBlocks = _forceInclusionPeriodBlocks;
                          maxTransactionGasLimit = _maxTransactionGasLimit;
                      }
                      /********************
                       * Public Functions *
                       ********************/
                      /**
                       * Accesses the batch storage container.
                       * @return Reference to the batch storage container.
                       */
                      function batches()
                          override
                          public
                          view
                          returns (
                              iOVM_ChainStorageContainer
                          )
                      {
                          return iOVM_ChainStorageContainer(
                              resolve("OVM_ChainStorageContainer-CTC-batches")
                          );
                      }
                      /**
                       * Accesses the queue storage container.
                       * @return Reference to the queue storage container.
                       */
                      function queue()
                          override
                          public
                          view
                          returns (
                              iOVM_ChainStorageContainer
                          )
                      {
                          return iOVM_ChainStorageContainer(
                              resolve("OVM_ChainStorageContainer-CTC-queue")
                          );
                      }
                      /**
                       * Retrieves the total number of elements submitted.
                       * @return _totalElements Total submitted elements.
                       */
                      function getTotalElements()
                          override
                          public
                          view
                          returns (
                              uint256 _totalElements
                          )
                      {
                          (uint40 totalElements,,,) = _getBatchExtraData();
                          return uint256(totalElements);
                      }
                      /**
                       * Retrieves the total number of batches submitted.
                       * @return _totalBatches Total submitted batches.
                       */
                      function getTotalBatches()
                          override
                          public
                          view
                          returns (
                              uint256 _totalBatches
                          )
                      {
                          return batches().length();
                      }
                      /**
                       * Returns the index of the next element to be enqueued.
                       * @return Index for the next queue element.
                       */
                      function getNextQueueIndex()
                          override
                          public
                          view
                          returns (
                              uint40
                          )
                      {
                          (,uint40 nextQueueIndex,,) = _getBatchExtraData();
                          return nextQueueIndex;
                      }
                      /**
                       * Returns the timestamp of the last transaction.
                       * @return Timestamp for the last transaction.
                       */
                      function getLastTimestamp()
                          override
                          public
                          view
                          returns (
                              uint40
                          )
                      {
                          (,,uint40 lastTimestamp,) = _getBatchExtraData();
                          return lastTimestamp;
                      }
                      /**
                       * Returns the blocknumber of the last transaction.
                       * @return Blocknumber for the last transaction.
                       */
                      function getLastBlockNumber()
                          override
                          public
                          view
                          returns (
                              uint40
                          )
                      {
                          (,,,uint40 lastBlockNumber) = _getBatchExtraData();
                          return lastBlockNumber;
                      }
                      /**
                       * Gets the queue element at a particular index.
                       * @param _index Index of the queue element to access.
                       * @return _element Queue element at the given index.
                       */
                      function getQueueElement(
                          uint256 _index
                      )
                          override
                          public
                          view
                          returns (
                              Lib_OVMCodec.QueueElement memory _element
                          )
                      {
                          return _getQueueElement(
                              _index,
                              queue()
                          );
                      }
                      /**
                       * Get the number of queue elements which have not yet been included.
                       * @return Number of pending queue elements.
                       */
                      function getNumPendingQueueElements()
                          override
                          public
                          view
                          returns (
                              uint40
                          )
                      {
                          return getQueueLength() - getNextQueueIndex();
                      }
                     /**
                       * Retrieves the length of the queue, including
                       * both pending and canonical transactions.
                       * @return Length of the queue.
                       */
                      function getQueueLength()
                          override
                          public
                          view
                          returns (
                              uint40
                          )
                      {
                          return _getQueueLength(
                              queue()
                          );
                      }
                      /**
                       * Adds a transaction to the queue.
                       * @param _target Target L2 contract to send the transaction to.
                       * @param _gasLimit Gas limit for the enqueued L2 transaction.
                       * @param _data Transaction data.
                       */
                      function enqueue(
                          address _target,
                          uint256 _gasLimit,
                          bytes memory _data
                      )
                          override
                          public
                      {
                          require(
                              _data.length <= MAX_ROLLUP_TX_SIZE,
                              "Transaction data size exceeds maximum for rollup transaction."
                          );
                          require(
                              _gasLimit <= maxTransactionGasLimit,
                              "Transaction gas limit exceeds maximum for rollup transaction."
                          );
                          require(
                              _gasLimit >= MIN_ROLLUP_TX_GAS,
                              "Transaction gas limit too low to enqueue."
                          );
                          // We need to consume some amount of L1 gas in order to rate limit transactions going into
                          // L2. However, L2 is cheaper than L1 so we only need to burn some small proportion of the
                          // provided L1 gas.
                          uint256 gasToConsume = _gasLimit/L2_GAS_DISCOUNT_DIVISOR;
                          uint256 startingGas = gasleft();
                          // Although this check is not necessary (burn below will run out of gas if not true), it
                          // gives the user an explicit reason as to why the enqueue attempt failed.
                          require(
                              startingGas > gasToConsume,
                              "Insufficient gas for L2 rate limiting burn."
                          );
                          // Here we do some "dumb" work in order to burn gas, although we should probably replace
                          // this with something like minting gas token later on.
                          uint256 i;
                          while(startingGas - gasleft() < gasToConsume) {
                              i++;
                          }
                          bytes32 transactionHash = keccak256(
                              abi.encode(
                                  msg.sender,
                                  _target,
                                  _gasLimit,
                                  _data
                              )
                          );
                          bytes32 timestampAndBlockNumber;
                          assembly {
                              timestampAndBlockNumber := timestamp()
                              timestampAndBlockNumber := or(timestampAndBlockNumber, shl(40, number()))
                          }
                          iOVM_ChainStorageContainer queueRef = queue();
                          queueRef.push(transactionHash);
                          queueRef.push(timestampAndBlockNumber);
                          // The underlying queue data structure stores 2 elements
                          // per insertion, so to get the real queue length we need
                          // to divide by 2 and subtract 1.
                          uint256 queueIndex = queueRef.length() / 2 - 1;
                          emit TransactionEnqueued(
                              msg.sender,
                              _target,
                              _gasLimit,
                              _data,
                              queueIndex,
                              block.timestamp
                          );
                      }
                      /**
                       * Appends a given number of queued transactions as a single batch.
                       * param _numQueuedTransactions Number of transactions to append.
                       */
                      function appendQueueBatch(
                          uint256 // _numQueuedTransactions
                      )
                          override
                          public
                          pure
                      {
                          // TEMPORARY: Disable `appendQueueBatch` for minnet
                          revert("appendQueueBatch is currently disabled.");
                          // _numQueuedTransactions = Math.min(_numQueuedTransactions, getNumPendingQueueElements());
                          // require(
                          //     _numQueuedTransactions > 0,
                          //     "Must append more than zero transactions."
                          // );
                          // bytes32[] memory leaves = new bytes32[](_numQueuedTransactions);
                          // uint40 nextQueueIndex = getNextQueueIndex();
                          // for (uint256 i = 0; i < _numQueuedTransactions; i++) {
                          //     if (msg.sender != resolve("OVM_Sequencer")) {
                          //         Lib_OVMCodec.QueueElement memory el = getQueueElement(nextQueueIndex);
                          //         require(
                          //             el.timestamp + forceInclusionPeriodSeconds < block.timestamp,
                          //             "Queue transactions cannot be submitted during the sequencer inclusion period."
                          //         );
                          //     }
                          //     leaves[i] = _getQueueLeafHash(nextQueueIndex);
                          //     nextQueueIndex++;
                          // }
                          // Lib_OVMCodec.QueueElement memory lastElement = getQueueElement(nextQueueIndex - 1);
                          // _appendBatch(
                          //     Lib_MerkleTree.getMerkleRoot(leaves),
                          //     _numQueuedTransactions,
                          //     _numQueuedTransactions,
                          //     lastElement.timestamp,
                          //     lastElement.blockNumber
                          // );
                          // emit QueueBatchAppended(
                          //     nextQueueIndex - _numQueuedTransactions,
                          //     _numQueuedTransactions,
                          //     getTotalElements()
                          // );
                      }
                      /**
                       * Allows the sequencer to append a batch of transactions.
                       * @dev This function uses a custom encoding scheme for efficiency reasons.
                       * .param _shouldStartAtElement Specific batch we expect to start appending to.
                       * .param _totalElementsToAppend Total number of batch elements we expect to append.
                       * .param _contexts Array of batch contexts.
                       * .param _transactionDataFields Array of raw transaction data.
                       */
                      function appendSequencerBatch()
                          override
                          public
                      {
                          uint40 shouldStartAtElement;
                          uint24 totalElementsToAppend;
                          uint24 numContexts;
                          assembly {
                              shouldStartAtElement  := shr(216, calldataload(4))
                              totalElementsToAppend := shr(232, calldataload(9))
                              numContexts           := shr(232, calldataload(12))
                          }
                          require(
                              shouldStartAtElement == getTotalElements(),
                              "Actual batch start index does not match expected start index."
                          );
                          require(
                              msg.sender == resolve("OVM_Sequencer"),
                              "Function can only be called by the Sequencer."
                          );
                          require(
                              numContexts > 0,
                              "Must provide at least one batch context."
                          );
                          require(
                              totalElementsToAppend > 0,
                              "Must append at least one element."
                          );
                          uint40 nextTransactionPtr = uint40(BATCH_CONTEXT_START_POS + BATCH_CONTEXT_SIZE * numContexts);
                          require(
                              msg.data.length >= nextTransactionPtr,
                              "Not enough BatchContexts provided."
                          );
                          // Take a reference to the queue and its length so we don't have to keep resolving it.
                          // Length isn't going to change during the course of execution, so it's fine to simply
                          // resolve this once at the start. Saves gas.
                          iOVM_ChainStorageContainer queueRef = queue();
                          uint40 queueLength = _getQueueLength(queueRef);
                          // Reserve some memory to save gas on hashing later on. This is a relatively safe estimate
                          // for the average transaction size that will prevent having to resize this chunk of memory
                          // later on. Saves gas.
                          bytes memory hashMemory = new bytes((msg.data.length / totalElementsToAppend) * 2);
                          // Initialize the array of canonical chain leaves that we will append.
                          bytes32[] memory leaves = new bytes32[](totalElementsToAppend);
                          // Each leaf index corresponds to a tx, either sequenced or enqueued.
                          uint32 leafIndex = 0;
                          // Counter for number of sequencer transactions appended so far.
                          uint32 numSequencerTransactions = 0;
                          // We will sequentially append leaves which are pointers to the queue.
                          // The initial queue index is what is currently in storage.
                          uint40 nextQueueIndex = getNextQueueIndex();
                          BatchContext memory curContext;
                          for (uint32 i = 0; i < numContexts; i++) {
                              BatchContext memory nextContext = _getBatchContext(i);
                              if (i == 0) {
                                  // Execute a special check for the first batch.
                                  _validateFirstBatchContext(nextContext);
                              }
                              // Execute this check on every single batch, including the first one.
                              _validateNextBatchContext(
                                  curContext,
                                  nextContext,
                                  nextQueueIndex,
                                  queueRef
                              );
                              // Now we can update our current context.
                              curContext = nextContext;
                              // Process sequencer transactions first.
                              for (uint32 j = 0; j < curContext.numSequencedTransactions; j++) {
                                  uint256 txDataLength;
                                  assembly {
                                      txDataLength := shr(232, calldataload(nextTransactionPtr))
                                  }
                                  require(
                                      txDataLength <= MAX_ROLLUP_TX_SIZE,
                                      "Transaction data size exceeds maximum for rollup transaction."
                                  );
                                  leaves[leafIndex] = _getSequencerLeafHash(
                                      curContext,
                                      nextTransactionPtr,
                                      txDataLength,
                                      hashMemory
                                  );
                                  nextTransactionPtr += uint40(TX_DATA_HEADER_SIZE + txDataLength);
                                  numSequencerTransactions++;
                                  leafIndex++;
                              }
                              // Now process any subsequent queue transactions.
                              for (uint32 j = 0; j < curContext.numSubsequentQueueTransactions; j++) {
                                  require(
                                      nextQueueIndex < queueLength,
                                      "Not enough queued transactions to append."
                                  );
                                  leaves[leafIndex] = _getQueueLeafHash(nextQueueIndex);
                                  nextQueueIndex++;
                                  leafIndex++;
                              }
                          }
                          _validateFinalBatchContext(
                              curContext,
                              nextQueueIndex,
                              queueLength,
                              queueRef
                          );
                          require(
                              msg.data.length == nextTransactionPtr,
                              "Not all sequencer transactions were processed."
                          );
                          require(
                              leafIndex == totalElementsToAppend,
                              "Actual transaction index does not match expected total elements to append."
                          );
                          // Generate the required metadata that we need to append this batch
                          uint40 numQueuedTransactions = totalElementsToAppend - numSequencerTransactions;
                          uint40 blockTimestamp;
                          uint40 blockNumber;
                          if (curContext.numSubsequentQueueTransactions == 0) {
                              // The last element is a sequencer tx, therefore pull timestamp and block number from the last context.
                              blockTimestamp = uint40(curContext.timestamp);
                              blockNumber = uint40(curContext.blockNumber);
                          } else {
                              // The last element is a queue tx, therefore pull timestamp and block number from the queue element.
                              // curContext.numSubsequentQueueTransactions > 0 which means that we've processed at least one queue element.
                              // We increment nextQueueIndex after processing each queue element,
                              // so the index of the last element we processed is nextQueueIndex - 1.
                              Lib_OVMCodec.QueueElement memory lastElement = _getQueueElement(
                                  nextQueueIndex - 1,
                                  queueRef
                              );
                              blockTimestamp = lastElement.timestamp;
                              blockNumber = lastElement.blockNumber;
                          }
                          // For efficiency reasons getMerkleRoot modifies the `leaves` argument in place
                          // while calculating the root hash therefore any arguments passed to it must not
                          // be used again afterwards
                          _appendBatch(
                              Lib_MerkleTree.getMerkleRoot(leaves),
                              totalElementsToAppend,
                              numQueuedTransactions,
                              blockTimestamp,
                              blockNumber
                          );
                          emit SequencerBatchAppended(
                              nextQueueIndex - numQueuedTransactions,
                              numQueuedTransactions,
                              getTotalElements()
                          );
                      }
                      /**
                       * Verifies whether a transaction is included in the chain.
                       * @param _transaction Transaction to verify.
                       * @param _txChainElement Transaction chain element corresponding to the transaction.
                       * @param _batchHeader Header of the batch the transaction was included in.
                       * @param _inclusionProof Inclusion proof for the provided transaction chain element.
                       * @return True if the transaction exists in the CTC, false if not.
                       */
                      function verifyTransaction(
                          Lib_OVMCodec.Transaction memory _transaction,
                          Lib_OVMCodec.TransactionChainElement memory _txChainElement,
                          Lib_OVMCodec.ChainBatchHeader memory _batchHeader,
                          Lib_OVMCodec.ChainInclusionProof memory _inclusionProof
                      )
                          override
                          public
                          view
                          returns (
                              bool
                          )
                      {
                          if (_txChainElement.isSequenced == true) {
                              return _verifySequencerTransaction(
                                  _transaction,
                                  _txChainElement,
                                  _batchHeader,
                                  _inclusionProof
                              );
                          } else {
                              return _verifyQueueTransaction(
                                  _transaction,
                                  _txChainElement.queueIndex,
                                  _batchHeader,
                                  _inclusionProof
                              );
                          }
                      }
                      /**********************
                       * Internal Functions *
                       **********************/
                      /**
                       * Returns the BatchContext located at a particular index.
                       * @param _index The index of the BatchContext
                       * @return The BatchContext at the specified index.
                       */
                      function _getBatchContext(
                          uint256 _index
                      )
                          internal
                          pure
                          returns (
                              BatchContext memory
                          )
                      {
                          uint256 contextPtr = 15 + _index * BATCH_CONTEXT_SIZE;
                          uint256 numSequencedTransactions;
                          uint256 numSubsequentQueueTransactions;
                          uint256 ctxTimestamp;
                          uint256 ctxBlockNumber;
                          assembly {
                              numSequencedTransactions       := shr(232, calldataload(contextPtr))
                              numSubsequentQueueTransactions := shr(232, calldataload(add(contextPtr, 3)))
                              ctxTimestamp                   := shr(216, calldataload(add(contextPtr, 6)))
                              ctxBlockNumber                 := shr(216, calldataload(add(contextPtr, 11)))
                          }
                          return BatchContext({
                              numSequencedTransactions: numSequencedTransactions,
                              numSubsequentQueueTransactions: numSubsequentQueueTransactions,
                              timestamp: ctxTimestamp,
                              blockNumber: ctxBlockNumber
                          });
                      }
                      /**
                       * Parses the batch context from the extra data.
                       * @return Total number of elements submitted.
                       * @return Index of the next queue element.
                       */
                      function _getBatchExtraData()
                          internal
                          view
                          returns (
                              uint40,
                              uint40,
                              uint40,
                              uint40
                          )
                      {
                          bytes27 extraData = batches().getGlobalMetadata();
                          uint40 totalElements;
                          uint40 nextQueueIndex;
                          uint40 lastTimestamp;
                          uint40 lastBlockNumber;
                          assembly {
                              extraData       :=  shr(40, extraData)
                              totalElements   :=  and(extraData, 0x000000000000000000000000000000000000000000000000000000FFFFFFFFFF)
                              nextQueueIndex  :=  shr(40, and(extraData, 0x00000000000000000000000000000000000000000000FFFFFFFFFF0000000000))
                              lastTimestamp   :=  shr(80, and(extraData, 0x0000000000000000000000000000000000FFFFFFFFFF00000000000000000000))
                              lastBlockNumber :=  shr(120, and(extraData, 0x000000000000000000000000FFFFFFFFFF000000000000000000000000000000))
                          }
                          return (
                              totalElements,
                              nextQueueIndex,
                              lastTimestamp,
                              lastBlockNumber
                          );
                      }
                      /**
                       * Encodes the batch context for the extra data.
                       * @param _totalElements Total number of elements submitted.
                       * @param _nextQueueIndex Index of the next queue element.
                       * @param _timestamp Timestamp for the last batch.
                       * @param _blockNumber Block number of the last batch.
                       * @return Encoded batch context.
                       */
                      function _makeBatchExtraData(
                          uint40 _totalElements,
                          uint40 _nextQueueIndex,
                          uint40 _timestamp,
                          uint40 _blockNumber
                      )
                          internal
                          pure
                          returns (
                              bytes27
                          )
                      {
                          bytes27 extraData;
                          assembly {
                              extraData := _totalElements
                              extraData := or(extraData, shl(40, _nextQueueIndex))
                              extraData := or(extraData, shl(80, _timestamp))
                              extraData := or(extraData, shl(120, _blockNumber))
                              extraData := shl(40, extraData)
                          }
                          return extraData;
                      }
                      /**
                       * Retrieves the hash of a queue element.
                       * @param _index Index of the queue element to retrieve a hash for.
                       * @return Hash of the queue element.
                       */
                      function _getQueueLeafHash(
                          uint256 _index
                      )
                          internal
                          pure
                          returns (
                              bytes32
                          )
                      {
                          return _hashTransactionChainElement(
                              Lib_OVMCodec.TransactionChainElement({
                                  isSequenced: false,
                                  queueIndex: _index,
                                  timestamp: 0,
                                  blockNumber: 0,
                                  txData: hex""
                              })
                          );
                      }
                      /**
                       * Gets the queue element at a particular index.
                       * @param _index Index of the queue element to access.
                       * @return _element Queue element at the given index.
                       */
                      function _getQueueElement(
                          uint256 _index,
                          iOVM_ChainStorageContainer _queueRef
                      )
                          internal
                          view
                          returns (
                              Lib_OVMCodec.QueueElement memory _element
                          )
                      {
                          // The underlying queue data structure stores 2 elements
                          // per insertion, so to get the actual desired queue index
                          // we need to multiply by 2.
                          uint40 trueIndex = uint40(_index * 2);
                          bytes32 transactionHash = _queueRef.get(trueIndex);
                          bytes32 timestampAndBlockNumber = _queueRef.get(trueIndex + 1);
                          uint40 elementTimestamp;
                          uint40 elementBlockNumber;
                          assembly {
                              elementTimestamp   :=         and(timestampAndBlockNumber, 0x000000000000000000000000000000000000000000000000000000FFFFFFFFFF)
                              elementBlockNumber := shr(40, and(timestampAndBlockNumber, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000))
                          }
                          return Lib_OVMCodec.QueueElement({
                              transactionHash: transactionHash,
                              timestamp: elementTimestamp,
                              blockNumber: elementBlockNumber
                          });
                      }
                      /**
                       * Retrieves the length of the queue.
                       * @return Length of the queue.
                       */
                      function _getQueueLength(
                          iOVM_ChainStorageContainer _queueRef
                      )
                          internal
                          view
                          returns (
                              uint40
                          )
                      {
                          // The underlying queue data structure stores 2 elements
                          // per insertion, so to get the real queue length we need
                          // to divide by 2.
                          return uint40(_queueRef.length() / 2);
                      }
                      /**
                       * Retrieves the hash of a sequencer element.
                       * @param _context Batch context for the given element.
                       * @param _nextTransactionPtr Pointer to the next transaction in the calldata.
                       * @param _txDataLength Length of the transaction item.
                       * @return Hash of the sequencer element.
                       */
                      function _getSequencerLeafHash(
                          BatchContext memory _context,
                          uint256 _nextTransactionPtr,
                          uint256 _txDataLength,
                          bytes memory _hashMemory
                      )
                          internal
                          pure
                          returns (
                              bytes32
                          )
                      {
                          // Only allocate more memory if we didn't reserve enough to begin with.
                          if (BYTES_TILL_TX_DATA + _txDataLength > _hashMemory.length) {
                              _hashMemory = new bytes(BYTES_TILL_TX_DATA + _txDataLength);
                          }
                          uint256 ctxTimestamp = _context.timestamp;
                          uint256 ctxBlockNumber = _context.blockNumber;
                          bytes32 leafHash;
                          assembly {
                              let chainElementStart := add(_hashMemory, 0x20)
                              // Set the first byte equal to `1` to indicate this is a sequencer chain element.
                              // This distinguishes sequencer ChainElements from queue ChainElements because
                              // all queue ChainElements are ABI encoded and the first byte of ABI encoded
                              // elements is always zero
                              mstore8(chainElementStart, 1)
                              mstore(add(chainElementStart, 1), ctxTimestamp)
                              mstore(add(chainElementStart, 33), ctxBlockNumber)
                              calldatacopy(add(chainElementStart, BYTES_TILL_TX_DATA), add(_nextTransactionPtr, 3), _txDataLength)
                              leafHash := keccak256(chainElementStart, add(BYTES_TILL_TX_DATA, _txDataLength))
                          }
                          return leafHash;
                      }
                      /**
                       * Retrieves the hash of a sequencer element.
                       * @param _txChainElement The chain element which is hashed to calculate the leaf.
                       * @return Hash of the sequencer element.
                       */
                      function _getSequencerLeafHash(
                          Lib_OVMCodec.TransactionChainElement memory _txChainElement
                      )
                          internal
                          view
                          returns(
                              bytes32
                          )
                      {
                          bytes memory txData = _txChainElement.txData;
                          uint256 txDataLength = _txChainElement.txData.length;
                          bytes memory chainElement = new bytes(BYTES_TILL_TX_DATA + txDataLength);
                          uint256 ctxTimestamp = _txChainElement.timestamp;
                          uint256 ctxBlockNumber = _txChainElement.blockNumber;
                          bytes32 leafHash;
                          assembly {
                              let chainElementStart := add(chainElement, 0x20)
                              // Set the first byte equal to `1` to indicate this is a sequencer chain element.
                              // This distinguishes sequencer ChainElements from queue ChainElements because
                              // all queue ChainElements are ABI encoded and the first byte of ABI encoded
                              // elements is always zero
                              mstore8(chainElementStart, 1)
                              mstore(add(chainElementStart, 1), ctxTimestamp)
                              mstore(add(chainElementStart, 33), ctxBlockNumber)
                              pop(staticcall(gas(), 0x04, add(txData, 0x20), txDataLength, add(chainElementStart, BYTES_TILL_TX_DATA), txDataLength))
                              leafHash := keccak256(chainElementStart, add(BYTES_TILL_TX_DATA, txDataLength))
                          }
                          return leafHash;
                      }
                      /**
                       * Inserts a batch into the chain of batches.
                       * @param _transactionRoot Root of the transaction tree for this batch.
                       * @param _batchSize Number of elements in the batch.
                       * @param _numQueuedTransactions Number of queue transactions in the batch.
                       * @param _timestamp The latest batch timestamp.
                       * @param _blockNumber The latest batch blockNumber.
                       */
                      function _appendBatch(
                          bytes32 _transactionRoot,
                          uint256 _batchSize,
                          uint256 _numQueuedTransactions,
                          uint40 _timestamp,
                          uint40 _blockNumber
                      )
                          internal
                      {
                          iOVM_ChainStorageContainer batchesRef = batches();
                          (uint40 totalElements, uint40 nextQueueIndex,,) = _getBatchExtraData();
                          Lib_OVMCodec.ChainBatchHeader memory header = Lib_OVMCodec.ChainBatchHeader({
                              batchIndex: batchesRef.length(),
                              batchRoot: _transactionRoot,
                              batchSize: _batchSize,
                              prevTotalElements: totalElements,
                              extraData: hex""
                          });
                          emit TransactionBatchAppended(
                              header.batchIndex,
                              header.batchRoot,
                              header.batchSize,
                              header.prevTotalElements,
                              header.extraData
                          );
                          bytes32 batchHeaderHash = Lib_OVMCodec.hashBatchHeader(header);
                          bytes27 latestBatchContext = _makeBatchExtraData(
                              totalElements + uint40(header.batchSize),
                              nextQueueIndex + uint40(_numQueuedTransactions),
                              _timestamp,
                              _blockNumber
                          );
                          batchesRef.push(batchHeaderHash, latestBatchContext);
                      }
                      /**
                       * Checks that the first batch context in a sequencer submission is valid
                       * @param _firstContext The batch context to validate.
                       */
                      function _validateFirstBatchContext(
                          BatchContext memory _firstContext
                      )
                          internal
                          view
                      {
                          // If there are existing elements, this batch must have the same context
                          // or a later timestamp and block number.
                          if (getTotalElements() > 0) {
                              (,, uint40 lastTimestamp, uint40 lastBlockNumber) = _getBatchExtraData();
                              require(
                                  _firstContext.blockNumber >= lastBlockNumber,
                                  "Context block number is lower than last submitted."
                              );
                              require(
                                  _firstContext.timestamp >= lastTimestamp,
                                  "Context timestamp is lower than last submitted."
                              );
                          }
                          // Sequencer cannot submit contexts which are more than the force inclusion period old.
                          require(
                              _firstContext.timestamp + forceInclusionPeriodSeconds >= block.timestamp,
                              "Context timestamp too far in the past."
                          );
                          require(
                              _firstContext.blockNumber + forceInclusionPeriodBlocks >= block.number,
                              "Context block number too far in the past."
                          );
                      }
                      /**
                       * Checks that a given batch context has a time context which is below a given que element
                       * @param _context The batch context to validate has values lower.
                       * @param _queueIndex Index of the queue element we are validating came later than the context.
                       * @param _queueRef The storage container for the queue.
                       */
                      function _validateContextBeforeEnqueue(
                          BatchContext memory _context,
                          uint40 _queueIndex,
                          iOVM_ChainStorageContainer _queueRef
                      )
                          internal
                          view
                      {
                              Lib_OVMCodec.QueueElement memory nextQueueElement = _getQueueElement(
                                  _queueIndex,
                                  _queueRef
                              );
                              // If the force inclusion period has passed for an enqueued transaction, it MUST be the next chain element.
                              require(
                                  block.timestamp < nextQueueElement.timestamp + forceInclusionPeriodSeconds,
                                  "Previously enqueued batches have expired and must be appended before a new sequencer batch."
                              );
                              // Just like sequencer transaction times must be increasing relative to each other,
                              // We also require that they be increasing relative to any interspersed queue elements.
                              require(
                                  _context.timestamp <= nextQueueElement.timestamp,
                                  "Sequencer transaction timestamp exceeds that of next queue element."
                              );
                              require(
                                  _context.blockNumber <= nextQueueElement.blockNumber,
                                  "Sequencer transaction blockNumber exceeds that of next queue element."
                              );
                      }
                      /**
                       * Checks that a given batch context is valid based on its previous context, and the next queue elemtent.
                       * @param _prevContext The previously validated batch context.
                       * @param _nextContext The batch context to validate with this call.
                       * @param _nextQueueIndex Index of the next queue element to process for the _nextContext's subsequentQueueElements.
                       * @param _queueRef The storage container for the queue.
                       */
                      function _validateNextBatchContext(
                          BatchContext memory _prevContext,
                          BatchContext memory _nextContext,
                          uint40 _nextQueueIndex,
                          iOVM_ChainStorageContainer _queueRef
                      )
                          internal
                          view
                      {
                          // All sequencer transactions' times must be greater than or equal to the previous ones.
                          require(
                              _nextContext.timestamp >= _prevContext.timestamp,
                              "Context timestamp values must monotonically increase."
                          );
                          require(
                              _nextContext.blockNumber >= _prevContext.blockNumber,
                              "Context blockNumber values must monotonically increase."
                          );
                          // If there is going to be a queue element pulled in from this context:
                          if (_nextContext.numSubsequentQueueTransactions > 0) {
                              _validateContextBeforeEnqueue(
                                  _nextContext,
                                  _nextQueueIndex,
                                  _queueRef
                              );
                          }
                      }
                      /**
                       * Checks that the final batch context in a sequencer submission is valid.
                       * @param _finalContext The batch context to validate.
                       * @param _queueLength The length of the queue at the start of the batchAppend call.
                       * @param _nextQueueIndex The next element in the queue that will be pulled into the CTC.
                       * @param _queueRef The storage container for the queue.
                       */
                      function _validateFinalBatchContext(
                          BatchContext memory _finalContext,
                          uint40 _nextQueueIndex,
                          uint40 _queueLength,
                          iOVM_ChainStorageContainer _queueRef
                      )
                          internal
                          view
                      {
                          // If the queue is not now empty, check the mononoticity of whatever the next batch that will come in is.
                          if (_queueLength - _nextQueueIndex > 0 && _finalContext.numSubsequentQueueTransactions == 0) {
                              _validateContextBeforeEnqueue(
                                  _finalContext,
                                  _nextQueueIndex,
                                  _queueRef
                              );
                          }
                          // Batches cannot be added from the future, or subsequent enqueue() contexts would violate monotonicity.
                          require(_finalContext.timestamp <= block.timestamp, "Context timestamp is from the future.");
                          require(_finalContext.blockNumber <= block.number, "Context block number is from the future.");
                      }
                      /**
                       * Hashes a transaction chain element.
                       * @param _element Chain element to hash.
                       * @return Hash of the chain element.
                       */
                      function _hashTransactionChainElement(
                          Lib_OVMCodec.TransactionChainElement memory _element
                      )
                          internal
                          pure
                          returns (
                              bytes32
                          )
                      {
                          return keccak256(
                              abi.encode(
                                  _element.isSequenced,
                                  _element.queueIndex,
                                  _element.timestamp,
                                  _element.blockNumber,
                                  _element.txData
                              )
                          );
                      }
                      /**
                       * Verifies a sequencer transaction, returning true if it was indeed included in the CTC
                       * @param _transaction The transaction we are verifying inclusion of.
                       * @param _txChainElement The chain element that the transaction is claimed to be a part of.
                       * @param _batchHeader Header of the batch the transaction was included in.
                       * @param _inclusionProof An inclusion proof into the CTC at a particular index.
                       * @return True if the transaction was included in the specified location, else false.
                       */
                      function _verifySequencerTransaction(
                          Lib_OVMCodec.Transaction memory _transaction,
                          Lib_OVMCodec.TransactionChainElement memory _txChainElement,
                          Lib_OVMCodec.ChainBatchHeader memory _batchHeader,
                          Lib_OVMCodec.ChainInclusionProof memory _inclusionProof
                      )
                          internal
                          view
                          returns (
                              bool
                          )
                      {
                          OVM_ExecutionManager ovmExecutionManager = OVM_ExecutionManager(resolve("OVM_ExecutionManager"));
                          uint256 gasLimit = ovmExecutionManager.getMaxTransactionGasLimit();
                          bytes32 leafHash = _getSequencerLeafHash(_txChainElement);
                          require(
                              _verifyElement(
                                  leafHash,
                                  _batchHeader,
                                  _inclusionProof
                              ),
                              "Invalid Sequencer transaction inclusion proof."
                          );
                          require(
                              _transaction.blockNumber        == _txChainElement.blockNumber
                              && _transaction.timestamp       == _txChainElement.timestamp
                              && _transaction.entrypoint      == resolve("OVM_DecompressionPrecompileAddress")
                              && _transaction.gasLimit        == gasLimit
                              && _transaction.l1TxOrigin      == address(0)
                              && _transaction.l1QueueOrigin   == Lib_OVMCodec.QueueOrigin.SEQUENCER_QUEUE
                              && keccak256(_transaction.data) == keccak256(_txChainElement.txData),
                              "Invalid Sequencer transaction."
                          );
                          return true;
                      }
                      /**
                       * Verifies a queue transaction, returning true if it was indeed included in the CTC
                       * @param _transaction The transaction we are verifying inclusion of.
                       * @param _queueIndex The queueIndex of the queued transaction.
                       * @param _batchHeader Header of the batch the transaction was included in.
                       * @param _inclusionProof An inclusion proof into the CTC at a particular index (should point to queue tx).
                       * @return True if the transaction was included in the specified location, else false.
                       */
                      function _verifyQueueTransaction(
                          Lib_OVMCodec.Transaction memory _transaction,
                          uint256 _queueIndex,
                          Lib_OVMCodec.ChainBatchHeader memory _batchHeader,
                          Lib_OVMCodec.ChainInclusionProof memory _inclusionProof
                      )
                          internal
                          view
                          returns (
                              bool
                          )
                      {
                          bytes32 leafHash = _getQueueLeafHash(_queueIndex);
                          require(
                              _verifyElement(
                                  leafHash,
                                  _batchHeader,
                                  _inclusionProof
                              ),
                              "Invalid Queue transaction inclusion proof."
                          );
                          bytes32 transactionHash = keccak256(
                              abi.encode(
                                  _transaction.l1TxOrigin,
                                  _transaction.entrypoint,
                                  _transaction.gasLimit,
                                  _transaction.data
                              )
                          );
                          Lib_OVMCodec.QueueElement memory el = getQueueElement(_queueIndex);
                          require(
                              el.transactionHash      == transactionHash
                              && el.timestamp   == _transaction.timestamp
                              && el.blockNumber == _transaction.blockNumber,
                              "Invalid Queue transaction."
                          );
                          return true;
                      }
                      /**
                       * Verifies a batch inclusion proof.
                       * @param _element Hash of the element to verify a proof for.
                       * @param _batchHeader Header of the batch in which the element was included.
                       * @param _proof Merkle inclusion proof for the element.
                       */
                      function _verifyElement(
                          bytes32 _element,
                          Lib_OVMCodec.ChainBatchHeader memory _batchHeader,
                          Lib_OVMCodec.ChainInclusionProof memory _proof
                      )
                          internal
                          view
                          returns (
                              bool
                          )
                      {
                          require(
                              Lib_OVMCodec.hashBatchHeader(_batchHeader) == batches().get(uint32(_batchHeader.batchIndex)),
                              "Invalid batch header."
                          );
                          require(
                              Lib_MerkleTree.verify(
                                  _batchHeader.batchRoot,
                                  _element,
                                  _proof.index,
                                  _proof.siblings,
                                  _batchHeader.batchSize
                              ),
                              "Invalid inclusion proof."
                          );
                          return true;
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // @unsupported: ovm
                  pragma solidity >0.5.0 <0.8.0;
                  pragma experimental ABIEncoderV2;
                  /* Library Imports */
                  import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
                  import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
                  import { Lib_Bytes32Utils } from "../../libraries/utils/Lib_Bytes32Utils.sol";
                  import { Lib_EthUtils } from "../../libraries/utils/Lib_EthUtils.sol";
                  import { Lib_ErrorUtils } from "../../libraries/utils/Lib_ErrorUtils.sol";
                  import { Lib_PredeployAddresses } from "../../libraries/constants/Lib_PredeployAddresses.sol";
                  /* Interface Imports */
                  import { iOVM_ExecutionManager } from "../../iOVM/execution/iOVM_ExecutionManager.sol";
                  import { iOVM_StateManager } from "../../iOVM/execution/iOVM_StateManager.sol";
                  import { iOVM_SafetyChecker } from "../../iOVM/execution/iOVM_SafetyChecker.sol";
                  /* Contract Imports */
                  import { OVM_DeployerWhitelist } from "../predeploys/OVM_DeployerWhitelist.sol";
                  /* External Imports */
                  import { Math } from "@openzeppelin/contracts/math/Math.sol";
                  /**
                   * @title OVM_ExecutionManager
                   * @dev The Execution Manager (EM) is the core of our OVM implementation, and provides a sandboxed
                   * environment allowing us to execute OVM transactions deterministically on either Layer 1 or
                   * Layer 2.
                   * The EM's run() function is the first function called during the execution of any
                   * transaction on L2.
                   * For each context-dependent EVM operation the EM has a function which implements a corresponding
                   * OVM operation, which will read state from the State Manager contract.
                   * The EM relies on the Safety Checker to verify that code deployed to Layer 2 does not contain any
                   * context-dependent operations.
                   *
                   * Compiler used: solc
                   * Runtime target: EVM
                   */
                  contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
                      /********************************
                       * External Contract References *
                       ********************************/
                      iOVM_SafetyChecker internal ovmSafetyChecker;
                      iOVM_StateManager internal ovmStateManager;
                      /*******************************
                       * Execution Context Variables *
                       *******************************/
                      GasMeterConfig internal gasMeterConfig;
                      GlobalContext internal globalContext;
                      TransactionContext internal transactionContext;
                      MessageContext internal messageContext;
                      TransactionRecord internal transactionRecord;
                      MessageRecord internal messageRecord;
                      /**************************
                       * Gas Metering Constants *
                       **************************/
                      address constant GAS_METADATA_ADDRESS = 0x06a506A506a506A506a506a506A506A506A506A5;
                      uint256 constant NUISANCE_GAS_SLOAD = 20000;
                      uint256 constant NUISANCE_GAS_SSTORE = 20000;
                      uint256 constant MIN_NUISANCE_GAS_PER_CONTRACT = 30000;
                      uint256 constant NUISANCE_GAS_PER_CONTRACT_BYTE = 100;
                      uint256 constant MIN_GAS_FOR_INVALID_STATE_ACCESS = 30000;
                      /**************************
                       * Native Value Constants *
                       **************************/
                      // Public so we can access and make assertions in integration tests.
                      uint256 public constant CALL_WITH_VALUE_INTRINSIC_GAS = 90000;
                      /**************************
                       * Default Context Values *
                       **************************/
                      uint256 constant DEFAULT_UINT256 = 0xdefa017defa017defa017defa017defa017defa017defa017defa017defa017d;
                      address constant DEFAULT_ADDRESS = 0xdEfa017defA017DeFA017DEfa017DeFA017DeFa0;
                      /*************************************
                       * Container Contract Address Prefix *
                       *************************************/
                      /**
                       * @dev The Execution Manager and State Manager each have this 30 byte prefix, and are uncallable.
                       */
                      address constant CONTAINER_CONTRACT_PREFIX = 0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000;
                      /***************
                       * Constructor *
                       ***************/
                      /**
                       * @param _libAddressManager Address of the Address Manager.
                       */
                      constructor(
                          address _libAddressManager,
                          GasMeterConfig memory _gasMeterConfig,
                          GlobalContext memory _globalContext
                      )
                          Lib_AddressResolver(_libAddressManager)
                      {
                          ovmSafetyChecker = iOVM_SafetyChecker(resolve("OVM_SafetyChecker"));
                          gasMeterConfig = _gasMeterConfig;
                          globalContext = _globalContext;
                          _resetContext();
                      }
                      /**********************
                       * Function Modifiers *
                       **********************/
                      /**
                       * Applies dynamically-sized refund to a transaction to account for the difference in execution
                       * between L1 and L2, so that the overall cost of the ovmOPCODE is fixed.
                       * @param _cost Desired gas cost for the function after the refund.
                       */
                      modifier netGasCost(
                          uint256 _cost
                      ) {
                          uint256 gasProvided = gasleft();
                          _;
                          uint256 gasUsed = gasProvided - gasleft();
                          // We want to refund everything *except* the specified cost.
                          if (_cost < gasUsed) {
                              transactionRecord.ovmGasRefund += gasUsed - _cost;
                          }
                      }
                      /**
                       * Applies a fixed-size gas refund to a transaction to account for the difference in execution
                       * between L1 and L2, so that the overall cost of an ovmOPCODE can be lowered.
                       * @param _discount Amount of gas cost to refund for the ovmOPCODE.
                       */
                      modifier fixedGasDiscount(
                          uint256 _discount
                      ) {
                          uint256 gasProvided = gasleft();
                          _;
                          uint256 gasUsed = gasProvided - gasleft();
                          // We want to refund the specified _discount, unless this risks underflow.
                          if (_discount < gasUsed) {
                              transactionRecord.ovmGasRefund += _discount;
                          } else {
                              // refund all we can without risking underflow.
                              transactionRecord.ovmGasRefund += gasUsed;
                          }
                      }
                      /**
                       * Makes sure we're not inside a static context.
                       */
                      modifier notStatic() {
                          if (messageContext.isStatic == true) {
                              _revertWithFlag(RevertFlag.STATIC_VIOLATION);
                          }
                          _;
                      }
                      /************************************
                       * Transaction Execution Entrypoint *
                       ************************************/
                      /**
                       * Starts the execution of a transaction via the OVM_ExecutionManager.
                       * @param _transaction Transaction data to be executed.
                       * @param _ovmStateManager iOVM_StateManager implementation providing account state.
                       */
                      function run(
                          Lib_OVMCodec.Transaction memory _transaction,
                          address _ovmStateManager
                      )
                          override
                          external
                          returns (
                              bytes memory
                          )
                      {
                          // Make sure that run() is not re-enterable.  This condition should always be satisfied
                          // Once run has been called once, due to the behavior of _isValidInput().
                          if (transactionContext.ovmNUMBER != DEFAULT_UINT256) {
                              return bytes("");
                          }
                          // Store our OVM_StateManager instance (significantly easier than attempting to pass the
                          // address around in calldata).
                          ovmStateManager = iOVM_StateManager(_ovmStateManager);
                          // Make sure this function can't be called by anyone except the owner of the
                          // OVM_StateManager (expected to be an OVM_StateTransitioner). We can revert here because
                          // this would make the `run` itself invalid.
                          require(
                              // This method may return false during fraud proofs, but always returns true in L2 nodes' State Manager precompile.
                              ovmStateManager.isAuthenticated(msg.sender),
                              "Only authenticated addresses in ovmStateManager can call this function"
                          );
                          // Initialize the execution context, must be initialized before we perform any gas metering
                          // or we'll throw a nuisance gas error.
                          _initContext(_transaction);
                          // TEMPORARY: Gas metering is disabled for minnet.
                          // // Check whether we need to start a new epoch, do so if necessary.
                          // _checkNeedsNewEpoch(_transaction.timestamp);
                          // Make sure the transaction's gas limit is valid. We don't revert here because we reserve
                          // reverts for INVALID_STATE_ACCESS.
                          if (_isValidInput(_transaction) == false) {
                              _resetContext();
                              return bytes("");
                          }
                          // TEMPORARY: Gas metering is disabled for minnet.
                          // // Check gas right before the call to get total gas consumed by OVM transaction.
                          // uint256 gasProvided = gasleft();
                          // Run the transaction, make sure to meter the gas usage.
                          (, bytes memory returndata) = ovmCALL(
                              _transaction.gasLimit - gasMeterConfig.minTransactionGasLimit,
                              _transaction.entrypoint,
                              0,
                              _transaction.data
                          );
                          // TEMPORARY: Gas metering is disabled for minnet.
                          // // Update the cumulative gas based on the amount of gas used.
                          // uint256 gasUsed = gasProvided - gasleft();
                          // _updateCumulativeGas(gasUsed, _transaction.l1QueueOrigin);
                          // Wipe the execution context.
                          _resetContext();
                          return returndata;
                      }
                      /******************************
                       * Opcodes: Execution Context *
                       ******************************/
                      /**
                       * @notice Overrides CALLER.
                       * @return _CALLER Address of the CALLER within the current message context.
                       */
                      function ovmCALLER()
                          override
                          external
                          view
                          returns (
                              address _CALLER
                          )
                      {
                          return messageContext.ovmCALLER;
                      }
                      /**
                       * @notice Overrides ADDRESS.
                       * @return _ADDRESS Active ADDRESS within the current message context.
                       */
                      function ovmADDRESS()
                          override
                          public
                          view
                          returns (
                              address _ADDRESS
                          )
                      {
                          return messageContext.ovmADDRESS;
                      }
                      /**
                       * @notice Overrides CALLVALUE.
                       * @return _CALLVALUE Value sent along with the call according to the current message context.
                       */
                      function ovmCALLVALUE()
                          override
                          public
                          view
                          returns (
                              uint256 _CALLVALUE
                          )
                      {
                          return messageContext.ovmCALLVALUE;
                      }
                      /**
                       * @notice Overrides TIMESTAMP.
                       * @return _TIMESTAMP Value of the TIMESTAMP within the transaction context.
                       */
                      function ovmTIMESTAMP()
                          override
                          external
                          view
                          returns (
                              uint256 _TIMESTAMP
                          )
                      {
                          return transactionContext.ovmTIMESTAMP;
                      }
                      /**
                       * @notice Overrides NUMBER.
                       * @return _NUMBER Value of the NUMBER within the transaction context.
                       */
                      function ovmNUMBER()
                          override
                          external
                          view
                          returns (
                              uint256 _NUMBER
                          )
                      {
                          return transactionContext.ovmNUMBER;
                      }
                      /**
                       * @notice Overrides GASLIMIT.
                       * @return _GASLIMIT Value of the block's GASLIMIT within the transaction context.
                       */
                      function ovmGASLIMIT()
                          override
                          external
                          view
                          returns (
                              uint256 _GASLIMIT
                          )
                      {
                          return transactionContext.ovmGASLIMIT;
                      }
                      /**
                       * @notice Overrides CHAINID.
                       * @return _CHAINID Value of the chain's CHAINID within the global context.
                       */
                      function ovmCHAINID()
                          override
                          external
                          view
                          returns (
                              uint256 _CHAINID
                          )
                      {
                          return globalContext.ovmCHAINID;
                      }
                      /*********************************
                       * Opcodes: L2 Execution Context *
                       *********************************/
                      /**
                       * @notice Specifies from which source (Sequencer or Queue) this transaction originated from.
                       * @return _queueOrigin Enum indicating the ovmL1QUEUEORIGIN within the current message context.
                       */
                      function ovmL1QUEUEORIGIN()
                          override
                          external
                          view
                          returns (
                              Lib_OVMCodec.QueueOrigin _queueOrigin
                          )
                      {
                          return transactionContext.ovmL1QUEUEORIGIN;
                      }
                      /**
                       * @notice Specifies which L1 account, if any, sent this transaction by calling enqueue().
                       * @return _l1TxOrigin Address of the account which sent the tx into L2 from L1.
                       */
                      function ovmL1TXORIGIN()
                          override
                          external
                          view
                          returns (
                              address _l1TxOrigin
                          )
                      {
                          return transactionContext.ovmL1TXORIGIN;
                      }
                      /********************
                       * Opcodes: Halting *
                       ********************/
                      /**
                       * @notice Overrides REVERT.
                       * @param _data Bytes data to pass along with the REVERT.
                       */
                      function ovmREVERT(
                          bytes memory _data
                      )
                          override
                          public
                      {
                          _revertWithFlag(RevertFlag.INTENTIONAL_REVERT, _data);
                      }
                      /******************************
                       * Opcodes: Contract Creation *
                       ******************************/
                      /**
                       * @notice Overrides CREATE.
                       * @param _bytecode Code to be used to CREATE a new contract.
                       * @return Address of the created contract.
                       * @return Revert data, if and only if the creation threw an exception.
                       */
                      function ovmCREATE(
                          bytes memory _bytecode
                      )
                          override
                          public
                          notStatic
                          fixedGasDiscount(40000)
                          returns (
                              address,
                              bytes memory
                          )
                      {
                          // Creator is always the current ADDRESS.
                          address creator = ovmADDRESS();
                          // Check that the deployer is whitelisted, or
                          // that arbitrary contract deployment has been enabled.
                          _checkDeployerAllowed(creator);
                          // Generate the correct CREATE address.
                          address contractAddress = Lib_EthUtils.getAddressForCREATE(
                              creator,
                              _getAccountNonce(creator)
                          );
                          return _createContract(
                              contractAddress,
                              _bytecode,
                              MessageType.ovmCREATE
                          );
                      }
                      /**
                       * @notice Overrides CREATE2.
                       * @param _bytecode Code to be used to CREATE2 a new contract.
                       * @param _salt Value used to determine the contract's address.
                       * @return Address of the created contract.
                       * @return Revert data, if and only if the creation threw an exception.
                       */
                      function ovmCREATE2(
                          bytes memory _bytecode,
                          bytes32 _salt
                      )
                          override
                          external
                          notStatic
                          fixedGasDiscount(40000)
                          returns (
                              address,
                              bytes memory
                          )
                      {
                          // Creator is always the current ADDRESS.
                          address creator = ovmADDRESS();
                          // Check that the deployer is whitelisted, or
                          // that arbitrary contract deployment has been enabled.
                          _checkDeployerAllowed(creator);
                          // Generate the correct CREATE2 address.
                          address contractAddress = Lib_EthUtils.getAddressForCREATE2(
                              creator,
                              _bytecode,
                              _salt
                          );
                          return _createContract(
                              contractAddress,
                              _bytecode,
                              MessageType.ovmCREATE2
                          );
                      }
                      /*******************************
                       * Account Abstraction Opcodes *
                       ******************************/
                      /**
                       * Retrieves the nonce of the current ovmADDRESS.
                       * @return _nonce Nonce of the current contract.
                       */
                      function ovmGETNONCE()
                          override
                          external
                          returns (
                              uint256 _nonce
                          )
                      {
                          return _getAccountNonce(ovmADDRESS());
                      }
                      /**
                       * Bumps the nonce of the current ovmADDRESS by one.
                       */
                      function ovmINCREMENTNONCE()
                          override
                          external
                          notStatic
                      {
                          address account = ovmADDRESS();
                          uint256 nonce = _getAccountNonce(account);
                          // Prevent overflow.
                          if (nonce + 1 > nonce) {
                              _setAccountNonce(account, nonce + 1);
                          }
                      }
                      /**
                       * Creates a new EOA contract account, for account abstraction.
                       * @dev Essentially functions like ovmCREATE or ovmCREATE2, but we can bypass a lot of checks
                       *      because the contract we're creating is trusted (no need to do safety checking or to
                       *      handle unexpected reverts). Doesn't need to return an address because the address is
                       *      assumed to be the user's actual address.
                       * @param _messageHash Hash of a message signed by some user, for verification.
                       * @param _v Signature `v` parameter.
                       * @param _r Signature `r` parameter.
                       * @param _s Signature `s` parameter.
                       */
                      function ovmCREATEEOA(
                          bytes32 _messageHash,
                          uint8 _v,
                          bytes32 _r,
                          bytes32 _s
                      )
                          override
                          public
                          notStatic
                      {
                          // Recover the EOA address from the message hash and signature parameters. Since we do the
                          // hashing in advance, we don't have handle different message hashing schemes. Even if this
                          // function were to return the wrong address (rather than explicitly returning the zero
                          // address), the rest of the transaction would simply fail (since there's no EOA account to
                          // actually execute the transaction).
                          address eoa = ecrecover(
                              _messageHash,
                              _v + 27,
                              _r,
                              _s
                          );
                          // Invalid signature is a case we proactively handle with a revert. We could alternatively
                          // have this function return a `success` boolean, but this is just easier.
                          if (eoa == address(0)) {
                              ovmREVERT(bytes("Signature provided for EOA contract creation is invalid."));
                          }
                          // If the user already has an EOA account, then there's no need to perform this operation.
                          if (_hasEmptyAccount(eoa) == false) {
                              return;
                          }
                          // We always need to initialize the contract with the default account values.
                          _initPendingAccount(eoa);
                          // Temporarily set the current address so it's easier to access on L2.
                          address prevADDRESS = messageContext.ovmADDRESS;
                          messageContext.ovmADDRESS = eoa;
                          // Creates a duplicate of the OVM_ProxyEOA located at 0x42....09. Uses the following
                          // "magic" prefix to deploy an exact copy of the code:
                          // PUSH1 0x0D   # size of this prefix in bytes
                          // CODESIZE
                          // SUB          # subtract prefix size from codesize
                          // DUP1
                          // PUSH1 0x0D
                          // PUSH1 0x00
                          // CODECOPY     # copy everything after prefix into memory at pos 0
                          // PUSH1 0x00
                          // RETURN       # return the copied code
                          address proxyEOA = Lib_EthUtils.createContract(abi.encodePacked(
                              hex"600D380380600D6000396000f3",
                              ovmEXTCODECOPY(
                                  Lib_PredeployAddresses.PROXY_EOA,
                                  0,
                                  ovmEXTCODESIZE(Lib_PredeployAddresses.PROXY_EOA)
                              )
                          ));
                          // Reset the address now that we're done deploying.
                          messageContext.ovmADDRESS = prevADDRESS;
                          // Commit the account with its final values.
                          _commitPendingAccount(
                              eoa,
                              address(proxyEOA),
                              keccak256(Lib_EthUtils.getCode(address(proxyEOA)))
                          );
                          _setAccountNonce(eoa, 0);
                      }
                      /*********************************
                       * Opcodes: Contract Interaction *
                       *********************************/
                      /**
                       * @notice Overrides CALL.
                       * @param _gasLimit Amount of gas to be passed into this call.
                       * @param _address Address of the contract to call.
                       * @param _value ETH value to pass with the call.
                       * @param _calldata Data to send along with the call.
                       * @return _success Whether or not the call returned (rather than reverted).
                       * @return _returndata Data returned by the call.
                       */
                      function ovmCALL(
                          uint256 _gasLimit,
                          address _address,
                          uint256 _value,
                          bytes memory _calldata
                      )
                          override
                          public
                          fixedGasDiscount(100000)
                          returns (
                              bool _success,
                              bytes memory _returndata
                          )
                      {
                          // CALL updates the CALLER and ADDRESS.
                          MessageContext memory nextMessageContext = messageContext;
                          nextMessageContext.ovmCALLER = nextMessageContext.ovmADDRESS;
                          nextMessageContext.ovmADDRESS = _address;
                          nextMessageContext.ovmCALLVALUE = _value;
                          return _callContract(
                              nextMessageContext,
                              _gasLimit,
                              _address,
                              _calldata,
                              MessageType.ovmCALL
                          );
                      }
                      /**
                       * @notice Overrides STATICCALL.
                       * @param _gasLimit Amount of gas to be passed into this call.
                       * @param _address Address of the contract to call.
                       * @param _calldata Data to send along with the call.
                       * @return _success Whether or not the call returned (rather than reverted).
                       * @return _returndata Data returned by the call.
                       */
                      function ovmSTATICCALL(
                          uint256 _gasLimit,
                          address _address,
                          bytes memory _calldata
                      )
                          override
                          public
                          fixedGasDiscount(80000)
                          returns (
                              bool _success,
                              bytes memory _returndata
                          )
                      {
                          // STATICCALL updates the CALLER, updates the ADDRESS, and runs in a static, valueless context.
                          MessageContext memory nextMessageContext = messageContext;
                          nextMessageContext.ovmCALLER = nextMessageContext.ovmADDRESS;
                          nextMessageContext.ovmADDRESS = _address;
                          nextMessageContext.isStatic = true;
                          nextMessageContext.ovmCALLVALUE = 0;
                          return _callContract(
                              nextMessageContext,
                              _gasLimit,
                              _address,
                              _calldata,
                              MessageType.ovmSTATICCALL
                          );
                      }
                      /**
                       * @notice Overrides DELEGATECALL.
                       * @param _gasLimit Amount of gas to be passed into this call.
                       * @param _address Address of the contract to call.
                       * @param _calldata Data to send along with the call.
                       * @return _success Whether or not the call returned (rather than reverted).
                       * @return _returndata Data returned by the call.
                       */
                      function ovmDELEGATECALL(
                          uint256 _gasLimit,
                          address _address,
                          bytes memory _calldata
                      )
                          override
                          public
                          fixedGasDiscount(40000)
                          returns (
                              bool _success,
                              bytes memory _returndata
                          )
                      {
                          // DELEGATECALL does not change anything about the message context.
                          MessageContext memory nextMessageContext = messageContext;
                          return _callContract(
                              nextMessageContext,
                              _gasLimit,
                              _address,
                              _calldata,
                              MessageType.ovmDELEGATECALL
                          );
                      }
                      /**
                       * @notice Legacy ovmCALL function which did not support ETH value; this maintains backwards compatibility.
                       * @param _gasLimit Amount of gas to be passed into this call.
                       * @param _address Address of the contract to call.
                       * @param _calldata Data to send along with the call.
                       * @return _success Whether or not the call returned (rather than reverted).
                       * @return _returndata Data returned by the call.
                       */
                      function ovmCALL(
                          uint256 _gasLimit,
                          address _address,
                          bytes memory _calldata
                      )
                          override
                          public
                          returns(
                              bool _success,
                              bytes memory _returndata
                          )
                      {
                          // Legacy ovmCALL assumed always-0 value.
                          return ovmCALL(
                              _gasLimit,
                              _address,
                              0,
                              _calldata
                          );
                      }
                      /************************************
                       * Opcodes: Contract Storage Access *
                       ************************************/
                      /**
                       * @notice Overrides SLOAD.
                       * @param _key 32 byte key of the storage slot to load.
                       * @return _value 32 byte value of the requested storage slot.
                       */
                      function ovmSLOAD(
                          bytes32 _key
                      )
                          override
                          external
                          netGasCost(40000)
                          returns (
                              bytes32 _value
                          )
                      {
                          // We always SLOAD from the storage of ADDRESS.
                          address contractAddress = ovmADDRESS();
                          return _getContractStorage(
                              contractAddress,
                              _key
                          );
                      }
                      /**
                       * @notice Overrides SSTORE.
                       * @param _key 32 byte key of the storage slot to set.
                       * @param _value 32 byte value for the storage slot.
                       */
                      function ovmSSTORE(
                          bytes32 _key,
                          bytes32 _value
                      )
                          override
                          external
                          notStatic
                          netGasCost(60000)
                      {
                          // We always SSTORE to the storage of ADDRESS.
                          address contractAddress = ovmADDRESS();
                          _putContractStorage(
                              contractAddress,
                              _key,
                              _value
                          );
                      }
                      /*********************************
                       * Opcodes: Contract Code Access *
                       *********************************/
                      /**
                       * @notice Overrides EXTCODECOPY.
                       * @param _contract Address of the contract to copy code from.
                       * @param _offset Offset in bytes from the start of contract code to copy beyond.
                       * @param _length Total number of bytes to copy from the contract's code.
                       * @return _code Bytes of code copied from the requested contract.
                       */
                      function ovmEXTCODECOPY(
                          address _contract,
                          uint256 _offset,
                          uint256 _length
                      )
                          override
                          public
                          returns (
                              bytes memory _code
                          )
                      {
                          return Lib_EthUtils.getCode(
                              _getAccountEthAddress(_contract),
                              _offset,
                              _length
                          );
                      }
                      /**
                       * @notice Overrides EXTCODESIZE.
                       * @param _contract Address of the contract to query the size of.
                       * @return _EXTCODESIZE Size of the requested contract in bytes.
                       */
                      function ovmEXTCODESIZE(
                          address _contract
                      )
                          override
                          public
                          returns (
                              uint256 _EXTCODESIZE
                          )
                      {
                          return Lib_EthUtils.getCodeSize(
                              _getAccountEthAddress(_contract)
                          );
                      }
                      /**
                       * @notice Overrides EXTCODEHASH.
                       * @param _contract Address of the contract to query the hash of.
                       * @return _EXTCODEHASH Hash of the requested contract.
                       */
                      function ovmEXTCODEHASH(
                          address _contract
                      )
                          override
                          external
                          returns (
                              bytes32 _EXTCODEHASH
                          )
                      {
                          return Lib_EthUtils.getCodeHash(
                              _getAccountEthAddress(_contract)
                          );
                      }
                      /***************************************
                       * Public Functions: ETH Value Opcodes *
                       ***************************************/
                      /**
                       * @notice Overrides BALANCE.
                       * NOTE: In the future, this could be optimized to directly invoke EM._getContractStorage(...).
                       * @param _contract Address of the contract to query the OVM_ETH balance of.
                       * @return _BALANCE OVM_ETH balance of the requested contract.
                       */
                      function ovmBALANCE(
                          address _contract
                      )
                          override
                          public
                          returns (
                              uint256 _BALANCE
                          )
                      {
                          // Easiest way to get the balance is query OVM_ETH as normal.
                          bytes memory balanceOfCalldata = abi.encodeWithSignature(
                              "balanceOf(address)",
                              _contract
                          );
                          // Static call because this should be a read-only query.
                          (bool success, bytes memory returndata) = ovmSTATICCALL(
                              gasleft(),
                              Lib_PredeployAddresses.OVM_ETH,
                              balanceOfCalldata
                          );
                          // All balanceOf queries should successfully return a uint, otherwise this must be an OOG.
                          if (!success || returndata.length != 32) {
                              _revertWithFlag(RevertFlag.OUT_OF_GAS);
                          }
                          // Return the decoded balance.
                          return abi.decode(returndata, (uint256));
                      }
                      /**
                       * @notice Overrides SELFBALANCE.
                       * @return _BALANCE OVM_ETH balance of the requesting contract.
                       */
                      function ovmSELFBALANCE()
                          override
                          external
                          returns (
                              uint256 _BALANCE
                          )
                      {
                          return ovmBALANCE(ovmADDRESS());
                      }
                      /***************************************
                       * Public Functions: Execution Context *
                       ***************************************/
                      function getMaxTransactionGasLimit()
                          external
                          view
                          override
                          returns (
                              uint256 _maxTransactionGasLimit
                          )
                      {
                          return gasMeterConfig.maxTransactionGasLimit;
                      }
                      /********************************************
                       * Public Functions: Deployment Whitelisting *
                       ********************************************/
                      /**
                       * Checks whether the given address is on the whitelist to ovmCREATE/ovmCREATE2, and reverts if not.
                       * @param _deployerAddress Address attempting to deploy a contract.
                       */
                      function _checkDeployerAllowed(
                          address _deployerAddress
                      )
                          internal
                      {
                          // From an OVM semantics perspective, this will appear identical to
                          // the deployer ovmCALLing the whitelist.  This is fine--in a sense, we are forcing them to.
                          (bool success, bytes memory data) = ovmSTATICCALL(
                              gasleft(),
                              Lib_PredeployAddresses.DEPLOYER_WHITELIST,
                              abi.encodeWithSelector(
                                  OVM_DeployerWhitelist.isDeployerAllowed.selector,
                                  _deployerAddress
                              )
                          );
                          bool isAllowed = abi.decode(data, (bool));
                          if (!isAllowed || !success) {
                              _revertWithFlag(RevertFlag.CREATOR_NOT_ALLOWED);
                          }
                      }
                      /********************************************
                       * Internal Functions: Contract Interaction *
                       ********************************************/
                      /**
                       * Creates a new contract and associates it with some contract address.
                       * @param _contractAddress Address to associate the created contract with.
                       * @param _bytecode Bytecode to be used to create the contract.
                       * @return Final OVM contract address.
                       * @return Revertdata, if and only if the creation threw an exception.
                       */
                      function _createContract(
                          address _contractAddress,
                          bytes memory _bytecode,
                          MessageType _messageType
                      )
                          internal
                          returns (
                              address,
                              bytes memory
                          )
                      {
                          // We always update the nonce of the creating account, even if the creation fails.
                          _setAccountNonce(ovmADDRESS(), _getAccountNonce(ovmADDRESS()) + 1);
                          // We're stepping into a CREATE or CREATE2, so we need to update ADDRESS to point
                          // to the contract's associated address and CALLER to point to the previous ADDRESS.
                          MessageContext memory nextMessageContext = messageContext;
                          nextMessageContext.ovmCALLER = messageContext.ovmADDRESS;
                          nextMessageContext.ovmADDRESS = _contractAddress;
                          // Run the common logic which occurs between call-type and create-type messages,
                          // passing in the creation bytecode and `true` to trigger create-specific logic.
                          (bool success, bytes memory data) = _handleExternalMessage(
                              nextMessageContext,
                              gasleft(),
                              _contractAddress,
                              _bytecode,
                              _messageType
                          );
                          // Yellow paper requires that address returned is zero if the contract deployment fails.
                          return (
                              success ? _contractAddress : address(0),
                              data
                          );
                      }
                      /**
                       * Calls the deployed contract associated with a given address.
                       * @param _nextMessageContext Message context to be used for the call.
                       * @param _gasLimit Amount of gas to be passed into this call.
                       * @param _contract OVM address to be called.
                       * @param _calldata Data to send along with the call.
                       * @return _success Whether or not the call returned (rather than reverted).
                       * @return _returndata Data returned by the call.
                       */
                      function _callContract(
                          MessageContext memory _nextMessageContext,
                          uint256 _gasLimit,
                          address _contract,
                          bytes memory _calldata,
                          MessageType _messageType
                      )
                          internal
                          returns (
                              bool _success,
                              bytes memory _returndata
                          )
                      {
                          // We reserve addresses of the form 0xdeaddeaddead...NNNN for the container contracts in L2 geth.
                          // So, we block calls to these addresses since they are not safe to run as an OVM contract itself.
                          if (
                              (uint256(_contract) & uint256(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000))
                              == uint256(CONTAINER_CONTRACT_PREFIX)
                          ) {
                              // EVM does not return data in the success case, see: https://github.com/ethereum/go-ethereum/blob/aae7660410f0ef90279e14afaaf2f429fdc2a186/core/vm/instructions.go#L600-L604
                              return (true, hex'');
                          }
                          // Both 0x0000... and the EVM precompiles have the same address on L1 and L2 --> no trie lookup needed.
                          address codeContractAddress =
                              uint(_contract) < 100
                              ? _contract
                              : _getAccountEthAddress(_contract);
                          return _handleExternalMessage(
                              _nextMessageContext,
                              _gasLimit,
                              codeContractAddress,
                              _calldata,
                              _messageType
                          );
                      }
                      /**
                       * Handles all interactions which involve the execution manager calling out to untrusted code (both calls and creates).
                       * Ensures that OVM-related measures are enforced, including L2 gas refunds, nuisance gas, and flagged reversions.
                       *
                       * @param _nextMessageContext Message context to be used for the external message.
                       * @param _gasLimit Amount of gas to be passed into this message. NOTE: this argument is overwritten in some cases to avoid stack-too-deep.
                       * @param _contract OVM address being called or deployed to
                       * @param _data Data for the message (either calldata or creation code)
                       * @param _messageType What type of ovmOPCODE this message corresponds to.
                       * @return Whether or not the message (either a call or deployment) succeeded.
                       * @return Data returned by the message.
                       */
                      function _handleExternalMessage(
                          MessageContext memory _nextMessageContext,
                          // NOTE: this argument is overwritten in some cases to avoid stack-too-deep.
                          uint256 _gasLimit,
                          address _contract,
                          bytes memory _data,
                          MessageType _messageType
                      )
                          internal
                          returns (
                              bool,
                              bytes memory
                          )
                      {
                          uint256 messageValue = _nextMessageContext.ovmCALLVALUE;
                          // If there is value in this message, we need to transfer the ETH over before switching contexts.
                          if (
                              messageValue > 0
                              && _isValueType(_messageType)
                          ) {
                              // Handle out-of-intrinsic gas consistent with EVM behavior -- the subcall "appears to revert" if we don't have enough gas to transfer the ETH.
                              // Similar to dynamic gas cost of value exceeding gas here:
                              // https://github.com/ethereum/go-ethereum/blob/c503f98f6d5e80e079c1d8a3601d188af2a899da/core/vm/interpreter.go#L268-L273
                              if (gasleft() < CALL_WITH_VALUE_INTRINSIC_GAS) {
                                  return (false, hex"");
                              }
                              // If there *is* enough gas to transfer ETH, then we need to make sure this amount of gas is reserved (i.e. not
                              // given to the _contract.call below) to guarantee that _handleExternalMessage can't run out of gas.
                              // In particular, in the event that the call fails, we will need to transfer the ETH back to the sender.
                              // Taking the lesser of _gasLimit and gasleft() - CALL_WITH_VALUE_INTRINSIC_GAS guarantees that the second
                              // _attemptForcedEthTransfer below, if needed, always has enough gas to succeed.
                              _gasLimit = Math.min(
                                  _gasLimit,
                                  gasleft() - CALL_WITH_VALUE_INTRINSIC_GAS // Cannot overflow due to the above check.
                              );
                              // Now transfer the value of the call.
                              // The target is interpreted to be the next message's ovmADDRESS account.
                              bool transferredOvmEth = _attemptForcedEthTransfer(
                                  _nextMessageContext.ovmADDRESS,
                                  messageValue
                              );
                              // If the ETH transfer fails (should only be possible in the case of insufficient balance), then treat this as a revert.
                              // This mirrors EVM behavior, see https://github.com/ethereum/go-ethereum/blob/2dee31930c9977af2a9fcb518fb9838aa609a7cf/core/vm/evm.go#L298
                              if (!transferredOvmEth) {
                                  return (false, hex"");
                              }
                          }
                          // We need to switch over to our next message context for the duration of this call.
                          MessageContext memory prevMessageContext = messageContext;
                          _switchMessageContext(prevMessageContext, _nextMessageContext);
                          // Nuisance gas is a system used to bound the ability for an attacker to make fraud proofs
                          // expensive by touching a lot of different accounts or storage slots. Since most contracts
                          // only use a few storage slots during any given transaction, this shouldn't be a limiting
                          // factor.
                          uint256 prevNuisanceGasLeft = messageRecord.nuisanceGasLeft;
                          uint256 nuisanceGasLimit = _getNuisanceGasLimit(_gasLimit);
                          messageRecord.nuisanceGasLeft = nuisanceGasLimit;
                          // Make the call and make sure to pass in the gas limit. Another instance of hidden
                          // complexity. `_contract` is guaranteed to be a safe contract, meaning its return/revert
                          // behavior can be controlled. In particular, we enforce that flags are passed through
                          // revert data as to retrieve execution metadata that would normally be reverted out of
                          // existence.
                          bool success;
                          bytes memory returndata;
                          if (_isCreateType(_messageType)) {
                              // safeCREATE() is a function which replicates a CREATE message, but uses return values
                              // Which match that of CALL (i.e. bool, bytes).  This allows many security checks to be
                              // to be shared between untrusted call and create call frames.
                              (success, returndata) = address(this).call{gas: _gasLimit}(
                                  abi.encodeWithSelector(
                                      this.safeCREATE.selector,
                                      _data,
                                      _contract
                                  )
                              );
                          } else {
                              (success, returndata) = _contract.call{gas: _gasLimit}(_data);
                          }
                          // If the message threw an exception, its value should be returned back to the sender.
                          // So, we force it back, BEFORE returning the messageContext to the previous addresses.
                          // This operation is part of the reason we "reserved the intrinsic gas" above.
                          if (
                              messageValue > 0
                              && _isValueType(_messageType)
                              && !success
                          ) {
                              bool transferredOvmEth = _attemptForcedEthTransfer(
                                  prevMessageContext.ovmADDRESS,
                                  messageValue
                              );
                              // Since we transferred it in above and the call reverted, the transfer back should always pass.
                              // This code path should NEVER be triggered since we sent `messageValue` worth of OVM_ETH into the target
                              // and reserved sufficient gas to execute the transfer, but in case there is some edge case which has
                              // been missed, we revert the entire frame (and its parent) to make sure the ETH gets sent back.
                              if (!transferredOvmEth) {
                                  _revertWithFlag(RevertFlag.OUT_OF_GAS);
                              }
                          }
                          // Switch back to the original message context now that we're out of the call and all OVM_ETH is in the right place.
                          _switchMessageContext(_nextMessageContext, prevMessageContext);
                          // Assuming there were no reverts, the message record should be accurate here. We'll update
                          // this value in the case of a revert.
                          uint256 nuisanceGasLeft = messageRecord.nuisanceGasLeft;
                          // Reverts at this point are completely OK, but we need to make a few updates based on the
                          // information passed through the revert.
                          if (success == false) {
                              (
                                  RevertFlag flag,
                                  uint256 nuisanceGasLeftPostRevert,
                                  uint256 ovmGasRefund,
                                  bytes memory returndataFromFlag
                              ) = _decodeRevertData(returndata);
                              // INVALID_STATE_ACCESS is the only flag that triggers an immediate abort of the
                              // parent EVM message. This behavior is necessary because INVALID_STATE_ACCESS must
                              // halt any further transaction execution that could impact the execution result.
                              if (flag == RevertFlag.INVALID_STATE_ACCESS) {
                                  _revertWithFlag(flag);
                              }
                              // INTENTIONAL_REVERT, UNSAFE_BYTECODE, STATIC_VIOLATION, and CREATOR_NOT_ALLOWED aren't
                              // dependent on the input state, so we can just handle them like standard reverts. Our only change here
                              // is to record the gas refund reported by the call (enforced by safety checking).
                              if (
                                  flag == RevertFlag.INTENTIONAL_REVERT
                                  || flag == RevertFlag.UNSAFE_BYTECODE
                                  || flag == RevertFlag.STATIC_VIOLATION
                                  || flag == RevertFlag.CREATOR_NOT_ALLOWED
                              ) {
                                  transactionRecord.ovmGasRefund = ovmGasRefund;
                              }
                              // INTENTIONAL_REVERT needs to pass up the user-provided return data encoded into the
                              // flag, *not* the full encoded flag.  Additionally, we surface custom error messages
                              // to developers in the case of unsafe creations for improved devex.
                              // All other revert types return no data.
                              if (
                                  flag == RevertFlag.INTENTIONAL_REVERT
                                  || flag == RevertFlag.UNSAFE_BYTECODE
                              ) {
                                  returndata = returndataFromFlag;
                              } else {
                                  returndata = hex'';
                              }
                              // Reverts mean we need to use up whatever "nuisance gas" was used by the call.
                              // EXCEEDS_NUISANCE_GAS explicitly reduces the remaining nuisance gas for this message
                              // to zero. OUT_OF_GAS is a "pseudo" flag given that messages return no data when they
                              // run out of gas, so we have to treat this like EXCEEDS_NUISANCE_GAS. All other flags
                              // will simply pass up the remaining nuisance gas.
                              nuisanceGasLeft = nuisanceGasLeftPostRevert;
                          }
                          // We need to reset the nuisance gas back to its original value minus the amount used here.
                          messageRecord.nuisanceGasLeft = prevNuisanceGasLeft - (nuisanceGasLimit - nuisanceGasLeft);
                          return (
                              success,
                              returndata
                          );
                      }
                      /**
                       * Handles the creation-specific safety measures required for OVM contract deployment.
                       * This function sanitizes the return types for creation messages to match calls (bool, bytes),
                       * by being an external function which the EM can call, that mimics the success/fail case of the CREATE.
                       * This allows for consistent handling of both types of messages in _handleExternalMessage().
                       * Having this step occur as a separate call frame also allows us to easily revert the
                       * contract deployment in the event that the code is unsafe.
                       *
                       * @param _creationCode Code to pass into CREATE for deployment.
                       * @param _address OVM address being deployed to.
                       */
                      function safeCREATE(
                          bytes memory _creationCode,
                          address _address
                      )
                          external
                      {
                          // The only way this should callable is from within _createContract(),
                          // and it should DEFINITELY not be callable by a non-EM code contract.
                          if (msg.sender != address(this)) {
                              return;
                          }
                          // Check that there is not already code at this address.
                          if (_hasEmptyAccount(_address) == false) {
                              // Note: in the EVM, this case burns all allotted gas.  For improved
                              // developer experience, we do return the remaining gas.
                              _revertWithFlag(
                                  RevertFlag.CREATE_COLLISION
                              );
                          }
                          // Check the creation bytecode against the OVM_SafetyChecker.
                          if (ovmSafetyChecker.isBytecodeSafe(_creationCode) == false) {
                              // Note: in the EVM, this case burns all allotted gas.  For improved
                              // developer experience, we do return the remaining gas.
                              _revertWithFlag(
                                  RevertFlag.UNSAFE_BYTECODE,
                                  Lib_ErrorUtils.encodeRevertString("Contract creation code contains unsafe opcodes. Did you use the right compiler or pass an unsafe constructor argument?")
                              );
                          }
                          // We always need to initialize the contract with the default account values.
                          _initPendingAccount(_address);
                          // Actually execute the EVM create message.
                          // NOTE: The inline assembly below means we can NOT make any evm calls between here and then.
                          address ethAddress = Lib_EthUtils.createContract(_creationCode);
                          if (ethAddress == address(0)) {
                              // If the creation fails, the EVM lets us grab its revert data. This may contain a revert flag
                              // to be used above in _handleExternalMessage, so we pass the revert data back up unmodified.
                              assembly {
                                  returndatacopy(0,0,returndatasize())
                                  revert(0, returndatasize())
                              }
                          }
                          // Again simply checking that the deployed code is safe too. Contracts can generate
                          // arbitrary deployment code, so there's no easy way to analyze this beforehand.
                          bytes memory deployedCode = Lib_EthUtils.getCode(ethAddress);
                          if (ovmSafetyChecker.isBytecodeSafe(deployedCode) == false) {
                              _revertWithFlag(
                                  RevertFlag.UNSAFE_BYTECODE,
                                  Lib_ErrorUtils.encodeRevertString("Constructor attempted to deploy unsafe bytecode.")
                              );
                          }
                          // Contract creation didn't need to be reverted and the bytecode is safe. We finish up by
                          // associating the desired address with the newly created contract's code hash and address.
                          _commitPendingAccount(
                              _address,
                              ethAddress,
                              Lib_EthUtils.getCodeHash(ethAddress)
                          );
                      }
                      /******************************************
                       * Internal Functions: Value Manipulation *
                       ******************************************/
                      /**
                       * Invokes an ovmCALL to OVM_ETH.transfer on behalf of the current ovmADDRESS, allowing us to force movement of OVM_ETH in correspondence with ETH's native value functionality.
                       * WARNING: this will send on behalf of whatever the messageContext.ovmADDRESS is in storage at the time of the call.
                       * NOTE: In the future, this could be optimized to directly invoke EM._setContractStorage(...).
                       * @param _to Amount of OVM_ETH to be sent.
                       * @param _value Amount of OVM_ETH to send.
                       * @return _success Whether or not the transfer worked.
                       */
                      function _attemptForcedEthTransfer(
                          address _to,
                          uint256 _value
                      )
                          internal
                          returns(
                              bool _success
                          )
                      {
                          bytes memory transferCalldata = abi.encodeWithSignature(
                              "transfer(address,uint256)",
                              _to,
                              _value
                          );
                          // OVM_ETH inherits from the UniswapV2ERC20 standard.  In this implementation, its return type
                          // is a boolean.  However, the implementation always returns true if it does not revert.
                          // Thus, success of the call frame is sufficient to infer success of the transfer itself.
                          (bool success, ) = ovmCALL(
                              gasleft(),
                              Lib_PredeployAddresses.OVM_ETH,
                              0,
                              transferCalldata
                          );
                          return success;
                      }
                      /******************************************
                       * Internal Functions: State Manipulation *
                       ******************************************/
                      /**
                       * Checks whether an account exists within the OVM_StateManager.
                       * @param _address Address of the account to check.
                       * @return _exists Whether or not the account exists.
                       */
                      function _hasAccount(
                          address _address
                      )
                          internal
                          returns (
                              bool _exists
                          )
                      {
                          _checkAccountLoad(_address);
                          return ovmStateManager.hasAccount(_address);
                      }
                      /**
                       * Checks whether a known empty account exists within the OVM_StateManager.
                       * @param _address Address of the account to check.
                       * @return _exists Whether or not the account empty exists.
                       */
                      function _hasEmptyAccount(
                          address _address
                      )
                          internal
                          returns (
                              bool _exists
                          )
                      {
                          _checkAccountLoad(_address);
                          return ovmStateManager.hasEmptyAccount(_address);
                      }
                      /**
                       * Sets the nonce of an account.
                       * @param _address Address of the account to modify.
                       * @param _nonce New account nonce.
                       */
                      function _setAccountNonce(
                          address _address,
                          uint256 _nonce
                      )
                          internal
                      {
                          _checkAccountChange(_address);
                          ovmStateManager.setAccountNonce(_address, _nonce);
                      }
                      /**
                       * Gets the nonce of an account.
                       * @param _address Address of the account to access.
                       * @return _nonce Nonce of the account.
                       */
                      function _getAccountNonce(
                          address _address
                      )
                          internal
                          returns (
                              uint256 _nonce
                          )
                      {
                          _checkAccountLoad(_address);
                          return ovmStateManager.getAccountNonce(_address);
                      }
                      /**
                       * Retrieves the Ethereum address of an account.
                       * @param _address Address of the account to access.
                       * @return _ethAddress Corresponding Ethereum address.
                       */
                      function _getAccountEthAddress(
                          address _address
                      )
                          internal
                          returns (
                              address _ethAddress
                          )
                      {
                          _checkAccountLoad(_address);
                          return ovmStateManager.getAccountEthAddress(_address);
                      }
                      /**
                       * Creates the default account object for the given address.
                       * @param _address Address of the account create.
                       */
                      function _initPendingAccount(
                          address _address
                      )
                          internal
                      {
                          // Although it seems like `_checkAccountChange` would be more appropriate here, we don't
                          // actually consider an account "changed" until it's inserted into the state (in this case
                          // by `_commitPendingAccount`).
                          _checkAccountLoad(_address);
                          ovmStateManager.initPendingAccount(_address);
                      }
                      /**
                       * Stores additional relevant data for a new account, thereby "committing" it to the state.
                       * This function is only called during `ovmCREATE` and `ovmCREATE2` after a successful contract
                       * creation.
                       * @param _address Address of the account to commit.
                       * @param _ethAddress Address of the associated deployed contract.
                       * @param _codeHash Hash of the code stored at the address.
                       */
                      function _commitPendingAccount(
                          address _address,
                          address _ethAddress,
                          bytes32 _codeHash
                      )
                          internal
                      {
                          _checkAccountChange(_address);
                          ovmStateManager.commitPendingAccount(
                              _address,
                              _ethAddress,
                              _codeHash
                          );
                      }
                      /**
                       * Retrieves the value of a storage slot.
                       * @param _contract Address of the contract to query.
                       * @param _key 32 byte key of the storage slot.
                       * @return _value 32 byte storage slot value.
                       */
                      function _getContractStorage(
                          address _contract,
                          bytes32 _key
                      )
                          internal
                          returns (
                              bytes32 _value
                          )
                      {
                          _checkContractStorageLoad(_contract, _key);
                          return ovmStateManager.getContractStorage(_contract, _key);
                      }
                      /**
                       * Sets the value of a storage slot.
                       * @param _contract Address of the contract to modify.
                       * @param _key 32 byte key of the storage slot.
                       * @param _value 32 byte storage slot value.
                       */
                      function _putContractStorage(
                          address _contract,
                          bytes32 _key,
                          bytes32 _value
                      )
                          internal
                      {
                          // We don't set storage if the value didn't change. Although this acts as a convenient
                          // optimization, it's also necessary to avoid the case in which a contract with no storage
                          // attempts to store the value "0" at any key. Putting this value (and therefore requiring
                          // that the value be committed into the storage trie after execution) would incorrectly
                          // modify the storage root.
                          if (_getContractStorage(_contract, _key) == _value) {
                              return;
                          }
                          _checkContractStorageChange(_contract, _key);
                          ovmStateManager.putContractStorage(_contract, _key, _value);
                      }
                      /**
                       * Validation whenever a contract needs to be loaded. Checks that the account exists, charges
                       * nuisance gas if the account hasn't been loaded before.
                       * @param _address Address of the account to load.
                       */
                      function _checkAccountLoad(
                          address _address
                      )
                          internal
                      {
                          // See `_checkContractStorageLoad` for more information.
                          if (gasleft() < MIN_GAS_FOR_INVALID_STATE_ACCESS) {
                              _revertWithFlag(RevertFlag.OUT_OF_GAS);
                          }
                          // See `_checkContractStorageLoad` for more information.
                          if (ovmStateManager.hasAccount(_address) == false) {
                              _revertWithFlag(RevertFlag.INVALID_STATE_ACCESS);
                          }
                          // Check whether the account has been loaded before and mark it as loaded if not. We need
                          // this because "nuisance gas" only applies to the first time that an account is loaded.
                          (
                              bool _wasAccountAlreadyLoaded
                          ) = ovmStateManager.testAndSetAccountLoaded(_address);
                          // If we hadn't already loaded the account, then we'll need to charge "nuisance gas" based
                          // on the size of the contract code.
                          if (_wasAccountAlreadyLoaded == false) {
                              _useNuisanceGas(
                                  (Lib_EthUtils.getCodeSize(_getAccountEthAddress(_address)) * NUISANCE_GAS_PER_CONTRACT_BYTE) + MIN_NUISANCE_GAS_PER_CONTRACT
                              );
                          }
                      }
                      /**
                       * Validation whenever a contract needs to be changed. Checks that the account exists, charges
                       * nuisance gas if the account hasn't been changed before.
                       * @param _address Address of the account to change.
                       */
                      function _checkAccountChange(
                          address _address
                      )
                          internal
                      {
                          // Start by checking for a load as we only want to charge nuisance gas proportional to
                          // contract size once.
                          _checkAccountLoad(_address);
                          // Check whether the account has been changed before and mark it as changed if not. We need
                          // this because "nuisance gas" only applies to the first time that an account is changed.
                          (
                              bool _wasAccountAlreadyChanged
                          ) = ovmStateManager.testAndSetAccountChanged(_address);
                          // If we hadn't already loaded the account, then we'll need to charge "nuisance gas" based
                          // on the size of the contract code.
                          if (_wasAccountAlreadyChanged == false) {
                              ovmStateManager.incrementTotalUncommittedAccounts();
                              _useNuisanceGas(
                                  (Lib_EthUtils.getCodeSize(_getAccountEthAddress(_address)) * NUISANCE_GAS_PER_CONTRACT_BYTE) + MIN_NUISANCE_GAS_PER_CONTRACT
                              );
                          }
                      }
                      /**
                       * Validation whenever a slot needs to be loaded. Checks that the account exists, charges
                       * nuisance gas if the slot hasn't been loaded before.
                       * @param _contract Address of the account to load from.
                       * @param _key 32 byte key to load.
                       */
                      function _checkContractStorageLoad(
                          address _contract,
                          bytes32 _key
                      )
                          internal
                      {
                          // Another case of hidden complexity. If we didn't enforce this requirement, then a
                          // contract could pass in just enough gas to cause the INVALID_STATE_ACCESS check to fail
                          // on L1 but not on L2. A contract could use this behavior to prevent the
                          // OVM_ExecutionManager from detecting an invalid state access. Reverting with OUT_OF_GAS
                          // allows us to also charge for the full message nuisance gas, because you deserve that for
                          // trying to break the contract in this way.
                          if (gasleft() < MIN_GAS_FOR_INVALID_STATE_ACCESS) {
                              _revertWithFlag(RevertFlag.OUT_OF_GAS);
                          }
                          // We need to make sure that the transaction isn't trying to access storage that hasn't
                          // been provided to the OVM_StateManager. We'll immediately abort if this is the case.
                          // We know that we have enough gas to do this check because of the above test.
                          if (ovmStateManager.hasContractStorage(_contract, _key) == false) {
                              _revertWithFlag(RevertFlag.INVALID_STATE_ACCESS);
                          }
                          // Check whether the slot has been loaded before and mark it as loaded if not. We need
                          // this because "nuisance gas" only applies to the first time that a slot is loaded.
                          (
                              bool _wasContractStorageAlreadyLoaded
                          ) = ovmStateManager.testAndSetContractStorageLoaded(_contract, _key);
                          // If we hadn't already loaded the account, then we'll need to charge some fixed amount of
                          // "nuisance gas".
                          if (_wasContractStorageAlreadyLoaded == false) {
                              _useNuisanceGas(NUISANCE_GAS_SLOAD);
                          }
                      }
                      /**
                       * Validation whenever a slot needs to be changed. Checks that the account exists, charges
                       * nuisance gas if the slot hasn't been changed before.
                       * @param _contract Address of the account to change.
                       * @param _key 32 byte key to change.
                       */
                      function _checkContractStorageChange(
                          address _contract,
                          bytes32 _key
                      )
                          internal
                      {
                          // Start by checking for load to make sure we have the storage slot and that we charge the
                          // "nuisance gas" necessary to prove the storage slot state.
                          _checkContractStorageLoad(_contract, _key);
                          // Check whether the slot has been changed before and mark it as changed if not. We need
                          // this because "nuisance gas" only applies to the first time that a slot is changed.
                          (
                              bool _wasContractStorageAlreadyChanged
                          ) = ovmStateManager.testAndSetContractStorageChanged(_contract, _key);
                          // If we hadn't already changed the account, then we'll need to charge some fixed amount of
                          // "nuisance gas".
                          if (_wasContractStorageAlreadyChanged == false) {
                              // Changing a storage slot means that we're also going to have to change the
                              // corresponding account, so do an account change check.
                              _checkAccountChange(_contract);
                              ovmStateManager.incrementTotalUncommittedContractStorage();
                              _useNuisanceGas(NUISANCE_GAS_SSTORE);
                          }
                      }
                      /************************************
                       * Internal Functions: Revert Logic *
                       ************************************/
                      /**
                       * Simple encoding for revert data.
                       * @param _flag Flag to revert with.
                       * @param _data Additional user-provided revert data.
                       * @return _revertdata Encoded revert data.
                       */
                      function _encodeRevertData(
                          RevertFlag _flag,
                          bytes memory _data
                      )
                          internal
                          view
                          returns (
                              bytes memory _revertdata
                          )
                      {
                          // Out of gas and create exceptions will fundamentally return no data, so simulating it shouldn't either.
                          if (
                              _flag == RevertFlag.OUT_OF_GAS
                          ) {
                              return bytes('');
                          }
                          // INVALID_STATE_ACCESS doesn't need to return any data other than the flag.
                          if (_flag == RevertFlag.INVALID_STATE_ACCESS) {
                              return abi.encode(
                                  _flag,
                                  0,
                                  0,
                                  bytes('')
                              );
                          }
                          // Just ABI encode the rest of the parameters.
                          return abi.encode(
                              _flag,
                              messageRecord.nuisanceGasLeft,
                              transactionRecord.ovmGasRefund,
                              _data
                          );
                      }
                      /**
                       * Simple decoding for revert data.
                       * @param _revertdata Revert data to decode.
                       * @return _flag Flag used to revert.
                       * @return _nuisanceGasLeft Amount of nuisance gas unused by the message.
                       * @return _ovmGasRefund Amount of gas refunded during the message.
                       * @return _data Additional user-provided revert data.
                       */
                      function _decodeRevertData(
                          bytes memory _revertdata
                      )
                          internal
                          pure
                          returns (
                              RevertFlag _flag,
                              uint256 _nuisanceGasLeft,
                              uint256 _ovmGasRefund,
                              bytes memory _data
                          )
                      {
                          // A length of zero means the call ran out of gas, just return empty data.
                          if (_revertdata.length == 0) {
                              return (
                                  RevertFlag.OUT_OF_GAS,
                                  0,
                                  0,
                                  bytes('')
                              );
                          }
                          // ABI decode the incoming data.
                          return abi.decode(_revertdata, (RevertFlag, uint256, uint256, bytes));
                      }
                      /**
                       * Causes a message to revert or abort.
                       * @param _flag Flag to revert with.
                       * @param _data Additional user-provided data.
                       */
                      function _revertWithFlag(
                          RevertFlag _flag,
                          bytes memory _data
                      )
                          internal
                          view
                      {
                          bytes memory revertdata = _encodeRevertData(
                              _flag,
                              _data
                          );
                          assembly {
                              revert(add(revertdata, 0x20), mload(revertdata))
                          }
                      }
                      /**
                       * Causes a message to revert or abort.
                       * @param _flag Flag to revert with.
                       */
                      function _revertWithFlag(
                          RevertFlag _flag
                      )
                          internal
                      {
                          _revertWithFlag(_flag, bytes(''));
                      }
                      /******************************************
                       * Internal Functions: Nuisance Gas Logic *
                       ******************************************/
                      /**
                       * Computes the nuisance gas limit from the gas limit.
                       * @dev This function is currently using a naive implementation whereby the nuisance gas limit
                       *      is set to exactly equal the lesser of the gas limit or remaining gas. It's likely that
                       *      this implementation is perfectly fine, but we may change this formula later.
                       * @param _gasLimit Gas limit to compute from.
                       * @return _nuisanceGasLimit Computed nuisance gas limit.
                       */
                      function _getNuisanceGasLimit(
                          uint256 _gasLimit
                      )
                          internal
                          view
                          returns (
                              uint256 _nuisanceGasLimit
                          )
                      {
                          return _gasLimit < gasleft() ? _gasLimit : gasleft();
                      }
                      /**
                       * Uses a certain amount of nuisance gas.
                       * @param _amount Amount of nuisance gas to use.
                       */
                      function _useNuisanceGas(
                          uint256 _amount
                      )
                          internal
                      {
                          // Essentially the same as a standard OUT_OF_GAS, except we also retain a record of the gas
                          // refund to be given at the end of the transaction.
                          if (messageRecord.nuisanceGasLeft < _amount) {
                              _revertWithFlag(RevertFlag.EXCEEDS_NUISANCE_GAS);
                          }
                          messageRecord.nuisanceGasLeft -= _amount;
                      }
                      /************************************
                       * Internal Functions: Gas Metering *
                       ************************************/
                      /**
                       * Checks whether a transaction needs to start a new epoch and does so if necessary.
                       * @param _timestamp Transaction timestamp.
                       */
                      function _checkNeedsNewEpoch(
                          uint256 _timestamp
                      )
                          internal
                      {
                          if (
                              _timestamp >= (
                                  _getGasMetadata(GasMetadataKey.CURRENT_EPOCH_START_TIMESTAMP)
                                  + gasMeterConfig.secondsPerEpoch
                              )
                          ) {
                              _putGasMetadata(
                                  GasMetadataKey.CURRENT_EPOCH_START_TIMESTAMP,
                                  _timestamp
                              );
                              _putGasMetadata(
                                  GasMetadataKey.PREV_EPOCH_SEQUENCER_QUEUE_GAS,
                                  _getGasMetadata(
                                      GasMetadataKey.CUMULATIVE_SEQUENCER_QUEUE_GAS
                                  )
                              );
                              _putGasMetadata(
                                  GasMetadataKey.PREV_EPOCH_L1TOL2_QUEUE_GAS,
                                  _getGasMetadata(
                                      GasMetadataKey.CUMULATIVE_L1TOL2_QUEUE_GAS
                                  )
                              );
                          }
                      }
                      /**
                       * Validates the input values of a transaction.
                       * @return _valid Whether or not the transaction data is valid.
                       */
                      function _isValidInput(
                          Lib_OVMCodec.Transaction memory _transaction
                      )
                          view
                          internal
                          returns (
                              bool
                          )
                      {
                          // Prevent reentrancy to run():
                          // This check prevents calling run with the default ovmNumber.
                          // Combined with the first check in run():
                          //      if (transactionContext.ovmNUMBER != DEFAULT_UINT256) { return; }
                          // It should be impossible to re-enter since run() returns before any other call frames are created.
                          // Since this value is already being written to storage, we save much gas compared to
                          // using the standard nonReentrant pattern.
                          if (_transaction.blockNumber == DEFAULT_UINT256)  {
                              return false;
                          }
                          if (_isValidGasLimit(_transaction.gasLimit, _transaction.l1QueueOrigin) == false) {
                              return false;
                          }
                          return true;
                      }
                      /**
                       * Validates the gas limit for a given transaction.
                       * @param _gasLimit Gas limit provided by the transaction.
                       * param _queueOrigin Queue from which the transaction originated.
                       * @return _valid Whether or not the gas limit is valid.
                       */
                      function _isValidGasLimit(
                          uint256 _gasLimit,
                          Lib_OVMCodec.QueueOrigin // _queueOrigin
                      )
                          view
                          internal
                          returns (
                              bool _valid
                          )
                      {
                          // Always have to be below the maximum gas limit.
                          if (_gasLimit > gasMeterConfig.maxTransactionGasLimit) {
                              return false;
                          }
                          // Always have to be above the minimum gas limit.
                          if (_gasLimit < gasMeterConfig.minTransactionGasLimit) {
                              return false;
                          }
                          // TEMPORARY: Gas metering is disabled for minnet.
                          return true;
                          // GasMetadataKey cumulativeGasKey;
                          // GasMetadataKey prevEpochGasKey;
                          // if (_queueOrigin == Lib_OVMCodec.QueueOrigin.SEQUENCER_QUEUE) {
                          //     cumulativeGasKey = GasMetadataKey.CUMULATIVE_SEQUENCER_QUEUE_GAS;
                          //     prevEpochGasKey = GasMetadataKey.PREV_EPOCH_SEQUENCER_QUEUE_GAS;
                          // } else {
                          //     cumulativeGasKey = GasMetadataKey.CUMULATIVE_L1TOL2_QUEUE_GAS;
                          //     prevEpochGasKey = GasMetadataKey.PREV_EPOCH_L1TOL2_QUEUE_GAS;
                          // }
                          // return (
                          //     (
                          //         _getGasMetadata(cumulativeGasKey)
                          //         - _getGasMetadata(prevEpochGasKey)
                          //         + _gasLimit
                          //     ) < gasMeterConfig.maxGasPerQueuePerEpoch
                          // );
                      }
                      /**
                       * Updates the cumulative gas after a transaction.
                       * @param _gasUsed Gas used by the transaction.
                       * @param _queueOrigin Queue from which the transaction originated.
                       */
                      function _updateCumulativeGas(
                          uint256 _gasUsed,
                          Lib_OVMCodec.QueueOrigin _queueOrigin
                      )
                          internal
                      {
                          GasMetadataKey cumulativeGasKey;
                          if (_queueOrigin == Lib_OVMCodec.QueueOrigin.SEQUENCER_QUEUE) {
                              cumulativeGasKey = GasMetadataKey.CUMULATIVE_SEQUENCER_QUEUE_GAS;
                          } else {
                              cumulativeGasKey = GasMetadataKey.CUMULATIVE_L1TOL2_QUEUE_GAS;
                          }
                          _putGasMetadata(
                              cumulativeGasKey,
                              (
                                  _getGasMetadata(cumulativeGasKey)
                                  + gasMeterConfig.minTransactionGasLimit
                                  + _gasUsed
                                  - transactionRecord.ovmGasRefund
                              )
                          );
                      }
                      /**
                       * Retrieves the value of a gas metadata key.
                       * @param _key Gas metadata key to retrieve.
                       * @return _value Value stored at the given key.
                       */
                      function _getGasMetadata(
                          GasMetadataKey _key
                      )
                          internal
                          returns (
                              uint256 _value
                          )
                      {
                          return uint256(_getContractStorage(
                              GAS_METADATA_ADDRESS,
                              bytes32(uint256(_key))
                          ));
                      }
                      /**
                       * Sets the value of a gas metadata key.
                       * @param _key Gas metadata key to set.
                       * @param _value Value to store at the given key.
                       */
                      function _putGasMetadata(
                          GasMetadataKey _key,
                          uint256 _value
                      )
                          internal
                      {
                          _putContractStorage(
                              GAS_METADATA_ADDRESS,
                              bytes32(uint256(_key)),
                              bytes32(uint256(_value))
                          );
                      }
                      /*****************************************
                       * Internal Functions: Execution Context *
                       *****************************************/
                      /**
                       * Swaps over to a new message context.
                       * @param _prevMessageContext Context we're switching from.
                       * @param _nextMessageContext Context we're switching to.
                       */
                      function _switchMessageContext(
                          MessageContext memory _prevMessageContext,
                          MessageContext memory _nextMessageContext
                      )
                          internal
                      {
                          // These conditionals allow us to avoid unneccessary SSTOREs.  However, they do mean that the current storage
                          // value for the messageContext MUST equal the _prevMessageContext argument, or an SSTORE might be erroneously skipped.
                          if (_prevMessageContext.ovmCALLER != _nextMessageContext.ovmCALLER) {
                              messageContext.ovmCALLER = _nextMessageContext.ovmCALLER;
                          }
                          if (_prevMessageContext.ovmADDRESS != _nextMessageContext.ovmADDRESS) {
                              messageContext.ovmADDRESS = _nextMessageContext.ovmADDRESS;
                          }
                          if (_prevMessageContext.isStatic != _nextMessageContext.isStatic) {
                              messageContext.isStatic = _nextMessageContext.isStatic;
                          }
                          if (_prevMessageContext.ovmCALLVALUE != _nextMessageContext.ovmCALLVALUE) {
                              messageContext.ovmCALLVALUE = _nextMessageContext.ovmCALLVALUE;
                          }
                      }
                      /**
                       * Initializes the execution context.
                       * @param _transaction OVM transaction being executed.
                       */
                      function _initContext(
                          Lib_OVMCodec.Transaction memory _transaction
                      )
                          internal
                      {
                          transactionContext.ovmTIMESTAMP = _transaction.timestamp;
                          transactionContext.ovmNUMBER = _transaction.blockNumber;
                          transactionContext.ovmTXGASLIMIT = _transaction.gasLimit;
                          transactionContext.ovmL1QUEUEORIGIN = _transaction.l1QueueOrigin;
                          transactionContext.ovmL1TXORIGIN = _transaction.l1TxOrigin;
                          transactionContext.ovmGASLIMIT = gasMeterConfig.maxGasPerQueuePerEpoch;
                          messageRecord.nuisanceGasLeft = _getNuisanceGasLimit(_transaction.gasLimit);
                      }
                      /**
                       * Resets the transaction and message context.
                       */
                      function _resetContext()
                          internal
                      {
                          transactionContext.ovmL1TXORIGIN = DEFAULT_ADDRESS;
                          transactionContext.ovmTIMESTAMP = DEFAULT_UINT256;
                          transactionContext.ovmNUMBER = DEFAULT_UINT256;
                          transactionContext.ovmGASLIMIT = DEFAULT_UINT256;
                          transactionContext.ovmTXGASLIMIT = DEFAULT_UINT256;
                          transactionContext.ovmL1QUEUEORIGIN = Lib_OVMCodec.QueueOrigin.SEQUENCER_QUEUE;
                          transactionRecord.ovmGasRefund = DEFAULT_UINT256;
                          messageContext.ovmCALLER = DEFAULT_ADDRESS;
                          messageContext.ovmADDRESS = DEFAULT_ADDRESS;
                          messageContext.isStatic = false;
                          messageRecord.nuisanceGasLeft = DEFAULT_UINT256;
                          // Reset the ovmStateManager.
                          ovmStateManager = iOVM_StateManager(address(0));
                      }
                      /******************************************
                       * Internal Functions: Message Typechecks *
                       ******************************************/
                      /**
                       * Returns whether or not the given message type is a CREATE-type.
                       * @param _messageType the message type in question.
                       */
                      function _isCreateType(
                          MessageType _messageType
                      )
                          internal
                          pure
                          returns(
                              bool
                          )
                      {
                          return (
                              _messageType == MessageType.ovmCREATE
                              || _messageType == MessageType.ovmCREATE2
                          );
                      }
                      /**
                       * Returns whether or not the given message type (potentially) requires the transfer of ETH value along with the message.
                       * @param _messageType the message type in question.
                       */
                      function _isValueType(
                          MessageType _messageType
                      )
                          internal
                          pure
                          returns(
                              bool
                          )
                      {
                          // ovmSTATICCALL and ovmDELEGATECALL types do not accept or transfer value.
                          return (
                              _messageType == MessageType.ovmCALL
                              || _messageType == MessageType.ovmCREATE
                              || _messageType == MessageType.ovmCREATE2
                          );
                      }
                      /*****************************
                       * L2-only Helper Functions *
                       *****************************/
                      /**
                       * Unreachable helper function for simulating eth_calls with an OVM message context.
                       * This function will throw an exception in all cases other than when used as a custom entrypoint in L2 Geth to simulate eth_call.
                       * @param _transaction the message transaction to simulate.
                       * @param _from the OVM account the simulated call should be from.
                       * @param _value the amount of ETH value to send.
                       * @param _ovmStateManager the address of the OVM_StateManager precompile in the L2 state.
                       */
                      function simulateMessage(
                          Lib_OVMCodec.Transaction memory _transaction,
                          address _from,
                          uint256 _value,
                          iOVM_StateManager _ovmStateManager
                      )
                          external
                          returns (
                              bytes memory
                          )
                      {
                          // Prevent this call from having any effect unless in a custom-set VM frame
                          require(msg.sender == address(0));
                          // Initialize the EM's internal state, ignoring nuisance gas.
                          ovmStateManager = _ovmStateManager;
                          _initContext(_transaction);
                          messageRecord.nuisanceGasLeft = uint(-1);
                          // Set the ovmADDRESS to the _from so that the subsequent call frame "comes from" them.
                          messageContext.ovmADDRESS = _from;
                          // Execute the desired message.
                          bool isCreate = _transaction.entrypoint == address(0);
                          if (isCreate) {
                              (address created, bytes memory revertData) = ovmCREATE(_transaction.data);
                              if (created == address(0)) {
                                  return abi.encode(false, revertData);
                              } else {
                                  // The eth_call RPC endpoint for to = undefined will return the deployed bytecode
                                  // in the success case, differing from standard create messages.
                                  return abi.encode(true, Lib_EthUtils.getCode(created));
                              }
                          } else {
                              (bool success, bytes memory returndata) = ovmCALL(
                                  _transaction.gasLimit,
                                  _transaction.entrypoint,
                                  _value,
                                  _transaction.data
                              );
                              return abi.encode(success, returndata);
                          }
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  /* Interface Imports */
                  import { iOVM_DeployerWhitelist } from "../../iOVM/predeploys/iOVM_DeployerWhitelist.sol";
                  /**
                   * @title OVM_DeployerWhitelist
                   * @dev The Deployer Whitelist is a temporary predeploy used to provide additional safety during the
                   * initial phases of our mainnet roll out. It is owned by the Optimism team, and defines accounts
                   * which are allowed to deploy contracts on Layer2. The Execution Manager will only allow an
                   * ovmCREATE or ovmCREATE2 operation to proceed if the deployer's address whitelisted.
                   *
                   * Compiler used: optimistic-solc
                   * Runtime target: OVM
                   */
                  contract OVM_DeployerWhitelist is iOVM_DeployerWhitelist {
                      /**********************
                       * Contract Constants *
                       **********************/
                      bool public initialized;
                      bool public allowArbitraryDeployment;
                      address override public owner;
                      mapping (address => bool) public whitelist;
                      /**********************
                       * Function Modifiers *
                       **********************/
                      /**
                       * Blocks functions to anyone except the contract owner.
                       */
                      modifier onlyOwner() {
                          require(
                              msg.sender == owner,
                              "Function can only be called by the owner of this contract."
                          );
                          _;
                      }
                      /********************
                       * Public Functions *
                       ********************/
                      /**
                       * Initializes the whitelist.
                       * @param _owner Address of the owner for this contract.
                       * @param _allowArbitraryDeployment Whether or not to allow arbitrary contract deployment.
                       */
                      function initialize(
                          address _owner,
                          bool _allowArbitraryDeployment
                      )
                          override
                          external
                      {
                          if (initialized == true) {
                              return;
                          }
                          initialized = true;
                          allowArbitraryDeployment = _allowArbitraryDeployment;
                          owner = _owner;
                      }
                      /**
                       * Adds or removes an address from the deployment whitelist.
                       * @param _deployer Address to update permissions for.
                       * @param _isWhitelisted Whether or not the address is whitelisted.
                       */
                      function setWhitelistedDeployer(
                          address _deployer,
                          bool _isWhitelisted
                      )
                          override
                          external
                          onlyOwner
                      {
                          whitelist[_deployer] = _isWhitelisted;
                      }
                      /**
                       * Updates the owner of this contract.
                       * @param _owner Address of the new owner.
                       */
                      function setOwner(
                          address _owner
                      )
                          override
                          public
                          onlyOwner
                      {
                          owner = _owner;
                      }
                      /**
                       * Updates the arbitrary deployment flag.
                       * @param _allowArbitraryDeployment Whether or not to allow arbitrary contract deployment.
                       */
                      function setAllowArbitraryDeployment(
                          bool _allowArbitraryDeployment
                      )
                          override
                          public
                          onlyOwner
                      {
                          allowArbitraryDeployment = _allowArbitraryDeployment;
                      }
                      /**
                       * Permanently enables arbitrary contract deployment and deletes the owner.
                       */
                      function enableArbitraryContractDeployment()
                          override
                          external
                          onlyOwner
                      {
                          setAllowArbitraryDeployment(true);
                          setOwner(address(0));
                      }
                      /**
                       * Checks whether an address is allowed to deploy contracts.
                       * @param _deployer Address to check.
                       * @return _allowed Whether or not the address can deploy contracts.
                       */
                      function isDeployerAllowed(
                          address _deployer
                      )
                          override
                          external
                          returns (
                              bool
                          )
                      {
                          return (
                              initialized == false
                              || allowArbitraryDeployment == true
                              || whitelist[_deployer]
                          );
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  pragma experimental ABIEncoderV2;
                  /* Library Imports */
                  import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
                  /* Interface Imports */
                  import { iOVM_ChainStorageContainer } from "./iOVM_ChainStorageContainer.sol";
                  /**
                   * @title iOVM_CanonicalTransactionChain
                   */
                  interface iOVM_CanonicalTransactionChain {
                      /**********
                       * Events *
                       **********/
                      event TransactionEnqueued(
                          address _l1TxOrigin,
                          address _target,
                          uint256 _gasLimit,
                          bytes _data,
                          uint256 _queueIndex,
                          uint256 _timestamp
                      );
                      event QueueBatchAppended(
                          uint256 _startingQueueIndex,
                          uint256 _numQueueElements,
                          uint256 _totalElements
                      );
                      event SequencerBatchAppended(
                          uint256 _startingQueueIndex,
                          uint256 _numQueueElements,
                          uint256 _totalElements
                      );
                      event TransactionBatchAppended(
                          uint256 indexed _batchIndex,
                          bytes32 _batchRoot,
                          uint256 _batchSize,
                          uint256 _prevTotalElements,
                          bytes _extraData
                      );
                      /***********
                       * Structs *
                       ***********/
                      struct BatchContext {
                          uint256 numSequencedTransactions;
                          uint256 numSubsequentQueueTransactions;
                          uint256 timestamp;
                          uint256 blockNumber;
                      }
                      /********************
                       * Public Functions *
                       ********************/
                      /**
                       * Accesses the batch storage container.
                       * @return Reference to the batch storage container.
                       */
                      function batches()
                          external
                          view
                          returns (
                              iOVM_ChainStorageContainer
                          );
                      /**
                       * Accesses the queue storage container.
                       * @return Reference to the queue storage container.
                       */
                      function queue()
                          external
                          view
                          returns (
                              iOVM_ChainStorageContainer
                          );
                      /**
                       * Retrieves the total number of elements submitted.
                       * @return _totalElements Total submitted elements.
                       */
                      function getTotalElements()
                          external
                          view
                          returns (
                              uint256 _totalElements
                          );
                      /**
                       * Retrieves the total number of batches submitted.
                       * @return _totalBatches Total submitted batches.
                       */
                      function getTotalBatches()
                          external
                          view
                          returns (
                              uint256 _totalBatches
                          );
                      /**
                       * Returns the index of the next element to be enqueued.
                       * @return Index for the next queue element.
                       */
                      function getNextQueueIndex()
                          external
                          view
                          returns (
                              uint40
                          );
                      /**
                       * Gets the queue element at a particular index.
                       * @param _index Index of the queue element to access.
                       * @return _element Queue element at the given index.
                       */
                      function getQueueElement(
                          uint256 _index
                      )
                          external
                          view
                          returns (
                              Lib_OVMCodec.QueueElement memory _element
                          );
                      /**
                       * Returns the timestamp of the last transaction.
                       * @return Timestamp for the last transaction.
                       */
                      function getLastTimestamp()
                          external
                          view
                          returns (
                              uint40
                          );
                      /**
                       * Returns the blocknumber of the last transaction.
                       * @return Blocknumber for the last transaction.
                       */
                      function getLastBlockNumber()
                          external
                          view
                          returns (
                              uint40
                          );
                      /**
                       * Get the number of queue elements which have not yet been included.
                       * @return Number of pending queue elements.
                       */
                      function getNumPendingQueueElements()
                          external
                          view
                          returns (
                              uint40
                          );
                      /**
                       * Retrieves the length of the queue, including
                       * both pending and canonical transactions.
                       * @return Length of the queue.
                       */
                      function getQueueLength()
                          external
                          view
                          returns (
                              uint40
                          );
                      /**
                       * Adds a transaction to the queue.
                       * @param _target Target contract to send the transaction to.
                       * @param _gasLimit Gas limit for the given transaction.
                       * @param _data Transaction data.
                       */
                      function enqueue(
                          address _target,
                          uint256 _gasLimit,
                          bytes memory _data
                      )
                          external;
                      /**
                       * Appends a given number of queued transactions as a single batch.
                       * @param _numQueuedTransactions Number of transactions to append.
                       */
                      function appendQueueBatch(
                          uint256 _numQueuedTransactions
                      )
                          external;
                      /**
                       * Allows the sequencer to append a batch of transactions.
                       * @dev This function uses a custom encoding scheme for efficiency reasons.
                       * .param _shouldStartAtElement Specific batch we expect to start appending to.
                       * .param _totalElementsToAppend Total number of batch elements we expect to append.
                       * .param _contexts Array of batch contexts.
                       * .param _transactionDataFields Array of raw transaction data.
                       */
                      function appendSequencerBatch(
                          // uint40 _shouldStartAtElement,
                          // uint24 _totalElementsToAppend,
                          // BatchContext[] _contexts,
                          // bytes[] _transactionDataFields
                      )
                          external;
                      /**
                       * Verifies whether a transaction is included in the chain.
                       * @param _transaction Transaction to verify.
                       * @param _txChainElement Transaction chain element corresponding to the transaction.
                       * @param _batchHeader Header of the batch the transaction was included in.
                       * @param _inclusionProof Inclusion proof for the provided transaction chain element.
                       * @return True if the transaction exists in the CTC, false if not.
                       */
                      function verifyTransaction(
                          Lib_OVMCodec.Transaction memory _transaction,
                          Lib_OVMCodec.TransactionChainElement memory _txChainElement,
                          Lib_OVMCodec.ChainBatchHeader memory _batchHeader,
                          Lib_OVMCodec.ChainInclusionProof memory _inclusionProof
                      )
                          external
                          view
                          returns (
                              bool
                          );
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  /**
                   * @title iOVM_ChainStorageContainer
                   */
                  interface iOVM_ChainStorageContainer {
                      /********************
                       * Public Functions *
                       ********************/
                      /**
                       * Sets the container's global metadata field. We're using `bytes27` here because we use five
                       * bytes to maintain the length of the underlying data structure, meaning we have an extra
                       * 27 bytes to store arbitrary data.
                       * @param _globalMetadata New global metadata to set.
                       */
                      function setGlobalMetadata(
                          bytes27 _globalMetadata
                      )
                          external;
                      /**
                       * Retrieves the container's global metadata field.
                       * @return Container global metadata field.
                       */
                      function getGlobalMetadata()
                          external
                          view
                          returns (
                              bytes27
                          );
                      /**
                       * Retrieves the number of objects stored in the container.
                       * @return Number of objects in the container.
                       */
                      function length()
                          external
                          view
                          returns (
                              uint256
                          );
                      /**
                       * Pushes an object into the container.
                       * @param _object A 32 byte value to insert into the container.
                       */
                      function push(
                          bytes32 _object
                      )
                          external;
                      /**
                       * Pushes an object into the container. Function allows setting the global metadata since
                       * we'll need to touch the "length" storage slot anyway, which also contains the global
                       * metadata (it's an optimization).
                       * @param _object A 32 byte value to insert into the container.
                       * @param _globalMetadata New global metadata for the container.
                       */
                      function push(
                          bytes32 _object,
                          bytes27 _globalMetadata
                      )
                          external;
                      /**
                       * Retrieves an object from the container.
                       * @param _index Index of the particular object to access.
                       * @return 32 byte object value.
                       */
                      function get(
                          uint256 _index
                      )
                          external
                          view
                          returns (
                              bytes32
                          );
                      /**
                       * Removes all objects after and including a given index.
                       * @param _index Object index to delete from.
                       */
                      function deleteElementsAfterInclusive(
                          uint256 _index
                      )
                          external;
                      /**
                       * Removes all objects after and including a given index. Also allows setting the global
                       * metadata field.
                       * @param _index Object index to delete from.
                       * @param _globalMetadata New global metadata for the container.
                       */
                      function deleteElementsAfterInclusive(
                          uint256 _index,
                          bytes27 _globalMetadata
                      )
                          external;
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  pragma experimental ABIEncoderV2;
                  /* Library Imports */
                  import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
                  interface iOVM_ExecutionManager {
                      /**********
                       * Enums *
                       *********/
                      enum RevertFlag {
                          OUT_OF_GAS,
                          INTENTIONAL_REVERT,
                          EXCEEDS_NUISANCE_GAS,
                          INVALID_STATE_ACCESS,
                          UNSAFE_BYTECODE,
                          CREATE_COLLISION,
                          STATIC_VIOLATION,
                          CREATOR_NOT_ALLOWED
                      }
                      enum GasMetadataKey {
                          CURRENT_EPOCH_START_TIMESTAMP,
                          CUMULATIVE_SEQUENCER_QUEUE_GAS,
                          CUMULATIVE_L1TOL2_QUEUE_GAS,
                          PREV_EPOCH_SEQUENCER_QUEUE_GAS,
                          PREV_EPOCH_L1TOL2_QUEUE_GAS
                      }
                      enum MessageType {
                          ovmCALL,
                          ovmSTATICCALL,
                          ovmDELEGATECALL,
                          ovmCREATE,
                          ovmCREATE2
                      }
                      /***********
                       * Structs *
                       ***********/
                      struct GasMeterConfig {
                          uint256 minTransactionGasLimit;
                          uint256 maxTransactionGasLimit;
                          uint256 maxGasPerQueuePerEpoch;
                          uint256 secondsPerEpoch;
                      }
                      struct GlobalContext {
                          uint256 ovmCHAINID;
                      }
                      struct TransactionContext {
                          Lib_OVMCodec.QueueOrigin ovmL1QUEUEORIGIN;
                          uint256 ovmTIMESTAMP;
                          uint256 ovmNUMBER;
                          uint256 ovmGASLIMIT;
                          uint256 ovmTXGASLIMIT;
                          address ovmL1TXORIGIN;
                      }
                      struct TransactionRecord {
                          uint256 ovmGasRefund;
                      }
                      struct MessageContext {
                          address ovmCALLER;
                          address ovmADDRESS;
                          uint256 ovmCALLVALUE;
                          bool isStatic;
                      }
                      struct MessageRecord {
                          uint256 nuisanceGasLeft;
                      }
                      /************************************
                       * Transaction Execution Entrypoint *
                       ************************************/
                      function run(
                          Lib_OVMCodec.Transaction calldata _transaction,
                          address _txStateManager
                      ) external returns (bytes memory);
                      /*******************
                       * Context Opcodes *
                       *******************/
                      function ovmCALLER() external view returns (address _caller);
                      function ovmADDRESS() external view returns (address _address);
                      function ovmCALLVALUE() external view returns (uint _callValue);
                      function ovmTIMESTAMP() external view returns (uint256 _timestamp);
                      function ovmNUMBER() external view returns (uint256 _number);
                      function ovmGASLIMIT() external view returns (uint256 _gasLimit);
                      function ovmCHAINID() external view returns (uint256 _chainId);
                      /**********************
                       * L2 Context Opcodes *
                       **********************/
                      function ovmL1QUEUEORIGIN() external view returns (Lib_OVMCodec.QueueOrigin _queueOrigin);
                      function ovmL1TXORIGIN() external view returns (address _l1TxOrigin);
                      /*******************
                       * Halting Opcodes *
                       *******************/
                      function ovmREVERT(bytes memory _data) external;
                      /*****************************
                       * Contract Creation Opcodes *
                       *****************************/
                      function ovmCREATE(bytes memory _bytecode) external returns (address _contract, bytes memory _revertdata);
                      function ovmCREATE2(bytes memory _bytecode, bytes32 _salt) external returns (address _contract, bytes memory _revertdata);
                      /*******************************
                       * Account Abstraction Opcodes *
                       ******************************/
                      function ovmGETNONCE() external returns (uint256 _nonce);
                      function ovmINCREMENTNONCE() external;
                      function ovmCREATEEOA(bytes32 _messageHash, uint8 _v, bytes32 _r, bytes32 _s) external;
                      /****************************
                       * Contract Calling Opcodes *
                       ****************************/
                      // Valueless ovmCALL for maintaining backwards compatibility with legacy OVM bytecode.
                      function ovmCALL(uint256 _gasLimit, address _address, bytes memory _calldata) external returns (bool _success, bytes memory _returndata);
                      function ovmCALL(uint256 _gasLimit, address _address, uint256 _value, bytes memory _calldata) external returns (bool _success, bytes memory _returndata);
                      function ovmSTATICCALL(uint256 _gasLimit, address _address, bytes memory _calldata) external returns (bool _success, bytes memory _returndata);
                      function ovmDELEGATECALL(uint256 _gasLimit, address _address, bytes memory _calldata) external returns (bool _success, bytes memory _returndata);
                      /****************************
                       * Contract Storage Opcodes *
                       ****************************/
                      function ovmSLOAD(bytes32 _key) external returns (bytes32 _value);
                      function ovmSSTORE(bytes32 _key, bytes32 _value) external;
                      /*************************
                       * Contract Code Opcodes *
                       *************************/
                      function ovmEXTCODECOPY(address _contract, uint256 _offset, uint256 _length) external returns (bytes memory _code);
                      function ovmEXTCODESIZE(address _contract) external returns (uint256 _size);
                      function ovmEXTCODEHASH(address _contract) external returns (bytes32 _hash);
                      /*********************
                       * ETH Value Opcodes *
                       *********************/
                      function ovmBALANCE(address _contract) external returns (uint256 _balance);
                      function ovmSELFBALANCE() external returns (uint256 _balance);
                      /***************************************
                       * Public Functions: Execution Context *
                       ***************************************/
                      function getMaxTransactionGasLimit() external view returns (uint _maxTransactionGasLimit);
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  /**
                   * @title iOVM_SafetyChecker
                   */
                  interface iOVM_SafetyChecker {
                      /********************
                       * Public Functions *
                       ********************/
                      function isBytecodeSafe(bytes calldata _bytecode) external pure returns (bool);
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  pragma experimental ABIEncoderV2;
                  /* Library Imports */
                  import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
                  /**
                   * @title iOVM_StateManager
                   */
                  interface iOVM_StateManager {
                      /*******************
                       * Data Structures *
                       *******************/
                      enum ItemState {
                          ITEM_UNTOUCHED,
                          ITEM_LOADED,
                          ITEM_CHANGED,
                          ITEM_COMMITTED
                      }
                      /***************************
                       * Public Functions: Misc *
                       ***************************/
                      function isAuthenticated(address _address) external view returns (bool);
                      /***************************
                       * Public Functions: Setup *
                       ***************************/
                      function owner() external view returns (address _owner);
                      function ovmExecutionManager() external view returns (address _ovmExecutionManager);
                      function setExecutionManager(address _ovmExecutionManager) external;
                      /************************************
                       * Public Functions: Account Access *
                       ************************************/
                      function putAccount(address _address, Lib_OVMCodec.Account memory _account) external;
                      function putEmptyAccount(address _address) external;
                      function getAccount(address _address) external view returns (Lib_OVMCodec.Account memory _account);
                      function hasAccount(address _address) external view returns (bool _exists);
                      function hasEmptyAccount(address _address) external view returns (bool _exists);
                      function setAccountNonce(address _address, uint256 _nonce) external;
                      function getAccountNonce(address _address) external view returns (uint256 _nonce);
                      function getAccountEthAddress(address _address) external view returns (address _ethAddress);
                      function getAccountStorageRoot(address _address) external view returns (bytes32 _storageRoot);
                      function initPendingAccount(address _address) external;
                      function commitPendingAccount(address _address, address _ethAddress, bytes32 _codeHash) external;
                      function testAndSetAccountLoaded(address _address) external returns (bool _wasAccountAlreadyLoaded);
                      function testAndSetAccountChanged(address _address) external returns (bool _wasAccountAlreadyChanged);
                      function commitAccount(address _address) external returns (bool _wasAccountCommitted);
                      function incrementTotalUncommittedAccounts() external;
                      function getTotalUncommittedAccounts() external view returns (uint256 _total);
                      function wasAccountChanged(address _address) external view returns (bool);
                      function wasAccountCommitted(address _address) external view returns (bool);
                      /************************************
                       * Public Functions: Storage Access *
                       ************************************/
                      function putContractStorage(address _contract, bytes32 _key, bytes32 _value) external;
                      function getContractStorage(address _contract, bytes32 _key) external view returns (bytes32 _value);
                      function hasContractStorage(address _contract, bytes32 _key) external view returns (bool _exists);
                      function testAndSetContractStorageLoaded(address _contract, bytes32 _key) external returns (bool _wasContractStorageAlreadyLoaded);
                      function testAndSetContractStorageChanged(address _contract, bytes32 _key) external returns (bool _wasContractStorageAlreadyChanged);
                      function commitContractStorage(address _contract, bytes32 _key) external returns (bool _wasContractStorageCommitted);
                      function incrementTotalUncommittedContractStorage() external;
                      function getTotalUncommittedContractStorage() external view returns (uint256 _total);
                      function wasContractStorageChanged(address _contract, bytes32 _key) external view returns (bool);
                      function wasContractStorageCommitted(address _contract, bytes32 _key) external view returns (bool);
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  /**
                   * @title iOVM_DeployerWhitelist
                   */
                  interface iOVM_DeployerWhitelist {
                      /********************
                       * Public Functions *
                       ********************/
                      function initialize(address _owner, bool _allowArbitraryDeployment) external;
                      function owner() external returns (address _owner);
                      function setWhitelistedDeployer(address _deployer, bool _isWhitelisted) external;
                      function setOwner(address _newOwner) external;
                      function setAllowArbitraryDeployment(bool _allowArbitraryDeployment) external;
                      function enableArbitraryContractDeployment() external;
                      function isDeployerAllowed(address _deployer) external returns (bool _allowed);
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  pragma experimental ABIEncoderV2;
                  /* Library Imports */
                  import { Lib_RLPReader } from "../rlp/Lib_RLPReader.sol";
                  import { Lib_RLPWriter } from "../rlp/Lib_RLPWriter.sol";
                  import { Lib_BytesUtils } from "../utils/Lib_BytesUtils.sol";
                  import { Lib_Bytes32Utils } from "../utils/Lib_Bytes32Utils.sol";
                  /**
                   * @title Lib_OVMCodec
                   */
                  library Lib_OVMCodec {
                      /*********
                       * Enums *
                       *********/
                      enum QueueOrigin {
                          SEQUENCER_QUEUE,
                          L1TOL2_QUEUE
                      }
                      /***********
                       * Structs *
                       ***********/
                      struct Account {
                          uint256 nonce;
                          uint256 balance;
                          bytes32 storageRoot;
                          bytes32 codeHash;
                          address ethAddress;
                          bool isFresh;
                      }
                      struct EVMAccount {
                          uint256 nonce;
                          uint256 balance;
                          bytes32 storageRoot;
                          bytes32 codeHash;
                      }
                      struct ChainBatchHeader {
                          uint256 batchIndex;
                          bytes32 batchRoot;
                          uint256 batchSize;
                          uint256 prevTotalElements;
                          bytes extraData;
                      }
                      struct ChainInclusionProof {
                          uint256 index;
                          bytes32[] siblings;
                      }
                      struct Transaction {
                          uint256 timestamp;
                          uint256 blockNumber;
                          QueueOrigin l1QueueOrigin;
                          address l1TxOrigin;
                          address entrypoint;
                          uint256 gasLimit;
                          bytes data;
                      }
                      struct TransactionChainElement {
                          bool isSequenced;
                          uint256 queueIndex;  // QUEUED TX ONLY
                          uint256 timestamp;   // SEQUENCER TX ONLY
                          uint256 blockNumber; // SEQUENCER TX ONLY
                          bytes txData;        // SEQUENCER TX ONLY
                      }
                      struct QueueElement {
                          bytes32 transactionHash;
                          uint40 timestamp;
                          uint40 blockNumber;
                      }
                      /**********************
                       * Internal Functions *
                       **********************/
                      /**
                       * Encodes a standard OVM transaction.
                       * @param _transaction OVM transaction to encode.
                       * @return Encoded transaction bytes.
                       */
                      function encodeTransaction(
                          Transaction memory _transaction
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          return abi.encodePacked(
                              _transaction.timestamp,
                              _transaction.blockNumber,
                              _transaction.l1QueueOrigin,
                              _transaction.l1TxOrigin,
                              _transaction.entrypoint,
                              _transaction.gasLimit,
                              _transaction.data
                          );
                      }
                      /**
                       * Hashes a standard OVM transaction.
                       * @param _transaction OVM transaction to encode.
                       * @return Hashed transaction
                       */
                      function hashTransaction(
                          Transaction memory _transaction
                      )
                          internal
                          pure
                          returns (
                              bytes32
                          )
                      {
                          return keccak256(encodeTransaction(_transaction));
                      }
                      /**
                       * Converts an OVM account to an EVM account.
                       * @param _in OVM account to convert.
                       * @return Converted EVM account.
                       */
                      function toEVMAccount(
                          Account memory _in
                      )
                          internal
                          pure
                          returns (
                              EVMAccount memory
                          )
                      {
                          return EVMAccount({
                              nonce: _in.nonce,
                              balance: _in.balance,
                              storageRoot: _in.storageRoot,
                              codeHash: _in.codeHash
                          });
                      }
                      /**
                       * @notice RLP-encodes an account state struct.
                       * @param _account Account state struct.
                       * @return RLP-encoded account state.
                       */
                      function encodeEVMAccount(
                          EVMAccount memory _account
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          bytes[] memory raw = new bytes[](4);
                          // Unfortunately we can't create this array outright because
                          // Lib_RLPWriter.writeList will reject fixed-size arrays. Assigning
                          // index-by-index circumvents this issue.
                          raw[0] = Lib_RLPWriter.writeBytes(
                              Lib_Bytes32Utils.removeLeadingZeros(
                                  bytes32(_account.nonce)
                              )
                          );
                          raw[1] = Lib_RLPWriter.writeBytes(
                              Lib_Bytes32Utils.removeLeadingZeros(
                                  bytes32(_account.balance)
                              )
                          );
                          raw[2] = Lib_RLPWriter.writeBytes(abi.encodePacked(_account.storageRoot));
                          raw[3] = Lib_RLPWriter.writeBytes(abi.encodePacked(_account.codeHash));
                          return Lib_RLPWriter.writeList(raw);
                      }
                      /**
                       * @notice Decodes an RLP-encoded account state into a useful struct.
                       * @param _encoded RLP-encoded account state.
                       * @return Account state struct.
                       */
                      function decodeEVMAccount(
                          bytes memory _encoded
                      )
                          internal
                          pure
                          returns (
                              EVMAccount memory
                          )
                      {
                          Lib_RLPReader.RLPItem[] memory accountState = Lib_RLPReader.readList(_encoded);
                          return EVMAccount({
                              nonce: Lib_RLPReader.readUint256(accountState[0]),
                              balance: Lib_RLPReader.readUint256(accountState[1]),
                              storageRoot: Lib_RLPReader.readBytes32(accountState[2]),
                              codeHash: Lib_RLPReader.readBytes32(accountState[3])
                          });
                      }
                      /**
                       * Calculates a hash for a given batch header.
                       * @param _batchHeader Header to hash.
                       * @return Hash of the header.
                       */
                      function hashBatchHeader(
                          Lib_OVMCodec.ChainBatchHeader memory _batchHeader
                      )
                          internal
                          pure
                          returns (
                              bytes32
                          )
                      {
                          return keccak256(
                              abi.encode(
                                  _batchHeader.batchRoot,
                                  _batchHeader.batchSize,
                                  _batchHeader.prevTotalElements,
                                  _batchHeader.extraData
                              )
                          );
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  /**
                   * @title Lib_PredeployAddresses
                   */
                  library Lib_PredeployAddresses {
                      address internal constant L2_TO_L1_MESSAGE_PASSER = 0x4200000000000000000000000000000000000000;
                      address internal constant L1_MESSAGE_SENDER = 0x4200000000000000000000000000000000000001;
                      address internal constant DEPLOYER_WHITELIST = 0x4200000000000000000000000000000000000002;
                      address internal constant ECDSA_CONTRACT_ACCOUNT = 0x4200000000000000000000000000000000000003;
                      address internal constant SEQUENCER_ENTRYPOINT = 0x4200000000000000000000000000000000000005;
                      address payable internal constant OVM_ETH = 0x4200000000000000000000000000000000000006;
                      address internal constant L2_CROSS_DOMAIN_MESSENGER = 0x4200000000000000000000000000000000000007;
                      address internal constant LIB_ADDRESS_MANAGER = 0x4200000000000000000000000000000000000008;
                      address internal constant PROXY_EOA = 0x4200000000000000000000000000000000000009;
                      address internal constant EXECUTION_MANAGER_WRAPPER = 0x420000000000000000000000000000000000000B;
                      address internal constant SEQUENCER_FEE_WALLET = 0x4200000000000000000000000000000000000011;
                      address internal constant ERC1820_REGISTRY = 0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24;
                      address internal constant L2_STANDARD_BRIDGE = 0x4200000000000000000000000000000000000010;
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  /* External Imports */
                  import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
                  /**
                   * @title Lib_AddressManager
                   */
                  contract Lib_AddressManager is Ownable {
                      /**********
                       * Events *
                       **********/
                      event AddressSet(
                          string indexed _name,
                          address _newAddress,
                          address _oldAddress
                      );
                      /*************
                       * Variables *
                       *************/
                      mapping (bytes32 => address) private addresses;
                      /********************
                       * Public Functions *
                       ********************/
                      /**
                       * Changes the address associated with a particular name.
                       * @param _name String name to associate an address with.
                       * @param _address Address to associate with the name.
                       */
                      function setAddress(
                          string memory _name,
                          address _address
                      )
                          external
                          onlyOwner
                      {
                          bytes32 nameHash = _getNameHash(_name);
                          address oldAddress = addresses[nameHash];
                          addresses[nameHash] = _address;
                          emit AddressSet(
                              _name,
                              _address,
                              oldAddress
                          );
                      }
                      /**
                       * Retrieves the address associated with a given name.
                       * @param _name Name to retrieve an address for.
                       * @return Address associated with the given name.
                       */
                      function getAddress(
                          string memory _name
                      )
                          external
                          view
                          returns (
                              address
                          )
                      {
                          return addresses[_getNameHash(_name)];
                      }
                      /**********************
                       * Internal Functions *
                       **********************/
                      /**
                       * Computes the hash of a name.
                       * @param _name Name to compute a hash for.
                       * @return Hash of the given name.
                       */
                      function _getNameHash(
                          string memory _name
                      )
                          internal
                          pure
                          returns (
                              bytes32
                          )
                      {
                          return keccak256(abi.encodePacked(_name));
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  /* Library Imports */
                  import { Lib_AddressManager } from "./Lib_AddressManager.sol";
                  /**
                   * @title Lib_AddressResolver
                   */
                  abstract contract Lib_AddressResolver {
                      /*************
                       * Variables *
                       *************/
                      Lib_AddressManager public libAddressManager;
                      /***************
                       * Constructor *
                       ***************/
                      /**
                       * @param _libAddressManager Address of the Lib_AddressManager.
                       */
                      constructor(
                          address _libAddressManager
                      ) {
                          libAddressManager = Lib_AddressManager(_libAddressManager);
                      }
                      /********************
                       * Public Functions *
                       ********************/
                      /**
                       * Resolves the address associated with a given name.
                       * @param _name Name to resolve an address for.
                       * @return Address associated with the given name.
                       */
                      function resolve(
                          string memory _name
                      )
                          public
                          view
                          returns (
                              address
                          )
                      {
                          return libAddressManager.getAddress(_name);
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  /**
                   * @title Lib_RLPReader
                   * @dev Adapted from "RLPReader" by Hamdi Allam ([email protected]).
                   */
                  library Lib_RLPReader {
                      /*************
                       * Constants *
                       *************/
                      uint256 constant internal MAX_LIST_LENGTH = 32;
                      /*********
                       * Enums *
                       *********/
                      enum RLPItemType {
                          DATA_ITEM,
                          LIST_ITEM
                      }
                      /***********
                       * Structs *
                       ***********/
                      struct RLPItem {
                          uint256 length;
                          uint256 ptr;
                      }
                      /**********************
                       * Internal Functions *
                       **********************/
                      /**
                       * Converts bytes to a reference to memory position and length.
                       * @param _in Input bytes to convert.
                       * @return Output memory reference.
                       */
                      function toRLPItem(
                          bytes memory _in
                      )
                          internal
                          pure
                          returns (
                              RLPItem memory
                          )
                      {
                          uint256 ptr;
                          assembly {
                              ptr := add(_in, 32)
                          }
                          return RLPItem({
                              length: _in.length,
                              ptr: ptr
                          });
                      }
                      /**
                       * Reads an RLP list value into a list of RLP items.
                       * @param _in RLP list value.
                       * @return Decoded RLP list items.
                       */
                      function readList(
                          RLPItem memory _in
                      )
                          internal
                          pure
                          returns (
                              RLPItem[] memory
                          )
                      {
                          (
                              uint256 listOffset,
                              ,
                              RLPItemType itemType
                          ) = _decodeLength(_in);
                          require(
                              itemType == RLPItemType.LIST_ITEM,
                              "Invalid RLP list value."
                          );
                          // Solidity in-memory arrays can't be increased in size, but *can* be decreased in size by
                          // writing to the length. Since we can't know the number of RLP items without looping over
                          // the entire input, we'd have to loop twice to accurately size this array. It's easier to
                          // simply set a reasonable maximum list length and decrease the size before we finish.
                          RLPItem[] memory out = new RLPItem[](MAX_LIST_LENGTH);
                          uint256 itemCount = 0;
                          uint256 offset = listOffset;
                          while (offset < _in.length) {
                              require(
                                  itemCount < MAX_LIST_LENGTH,
                                  "Provided RLP list exceeds max list length."
                              );
                              (
                                  uint256 itemOffset,
                                  uint256 itemLength,
                              ) = _decodeLength(RLPItem({
                                  length: _in.length - offset,
                                  ptr: _in.ptr + offset
                              }));
                              out[itemCount] = RLPItem({
                                  length: itemLength + itemOffset,
                                  ptr: _in.ptr + offset
                              });
                              itemCount += 1;
                              offset += itemOffset + itemLength;
                          }
                          // Decrease the array size to match the actual item count.
                          assembly {
                              mstore(out, itemCount)
                          }
                          return out;
                      }
                      /**
                       * Reads an RLP list value into a list of RLP items.
                       * @param _in RLP list value.
                       * @return Decoded RLP list items.
                       */
                      function readList(
                          bytes memory _in
                      )
                          internal
                          pure
                          returns (
                              RLPItem[] memory
                          )
                      {
                          return readList(
                              toRLPItem(_in)
                          );
                      }
                      /**
                       * Reads an RLP bytes value into bytes.
                       * @param _in RLP bytes value.
                       * @return Decoded bytes.
                       */
                      function readBytes(
                          RLPItem memory _in
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          (
                              uint256 itemOffset,
                              uint256 itemLength,
                              RLPItemType itemType
                          ) = _decodeLength(_in);
                          require(
                              itemType == RLPItemType.DATA_ITEM,
                              "Invalid RLP bytes value."
                          );
                          return _copy(_in.ptr, itemOffset, itemLength);
                      }
                      /**
                       * Reads an RLP bytes value into bytes.
                       * @param _in RLP bytes value.
                       * @return Decoded bytes.
                       */
                      function readBytes(
                          bytes memory _in
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          return readBytes(
                              toRLPItem(_in)
                          );
                      }
                      /**
                       * Reads an RLP string value into a string.
                       * @param _in RLP string value.
                       * @return Decoded string.
                       */
                      function readString(
                          RLPItem memory _in
                      )
                          internal
                          pure
                          returns (
                              string memory
                          )
                      {
                          return string(readBytes(_in));
                      }
                      /**
                       * Reads an RLP string value into a string.
                       * @param _in RLP string value.
                       * @return Decoded string.
                       */
                      function readString(
                          bytes memory _in
                      )
                          internal
                          pure
                          returns (
                              string memory
                          )
                      {
                          return readString(
                              toRLPItem(_in)
                          );
                      }
                      /**
                       * Reads an RLP bytes32 value into a bytes32.
                       * @param _in RLP bytes32 value.
                       * @return Decoded bytes32.
                       */
                      function readBytes32(
                          RLPItem memory _in
                      )
                          internal
                          pure
                          returns (
                              bytes32
                          )
                      {
                          require(
                              _in.length <= 33,
                              "Invalid RLP bytes32 value."
                          );
                          (
                              uint256 itemOffset,
                              uint256 itemLength,
                              RLPItemType itemType
                          ) = _decodeLength(_in);
                          require(
                              itemType == RLPItemType.DATA_ITEM,
                              "Invalid RLP bytes32 value."
                          );
                          uint256 ptr = _in.ptr + itemOffset;
                          bytes32 out;
                          assembly {
                              out := mload(ptr)
                              // Shift the bytes over to match the item size.
                              if lt(itemLength, 32) {
                                  out := div(out, exp(256, sub(32, itemLength)))
                              }
                          }
                          return out;
                      }
                      /**
                       * Reads an RLP bytes32 value into a bytes32.
                       * @param _in RLP bytes32 value.
                       * @return Decoded bytes32.
                       */
                      function readBytes32(
                          bytes memory _in
                      )
                          internal
                          pure
                          returns (
                              bytes32
                          )
                      {
                          return readBytes32(
                              toRLPItem(_in)
                          );
                      }
                      /**
                       * Reads an RLP uint256 value into a uint256.
                       * @param _in RLP uint256 value.
                       * @return Decoded uint256.
                       */
                      function readUint256(
                          RLPItem memory _in
                      )
                          internal
                          pure
                          returns (
                              uint256
                          )
                      {
                          return uint256(readBytes32(_in));
                      }
                      /**
                       * Reads an RLP uint256 value into a uint256.
                       * @param _in RLP uint256 value.
                       * @return Decoded uint256.
                       */
                      function readUint256(
                          bytes memory _in
                      )
                          internal
                          pure
                          returns (
                              uint256
                          )
                      {
                          return readUint256(
                              toRLPItem(_in)
                          );
                      }
                      /**
                       * Reads an RLP bool value into a bool.
                       * @param _in RLP bool value.
                       * @return Decoded bool.
                       */
                      function readBool(
                          RLPItem memory _in
                      )
                          internal
                          pure
                          returns (
                              bool
                          )
                      {
                          require(
                              _in.length == 1,
                              "Invalid RLP boolean value."
                          );
                          uint256 ptr = _in.ptr;
                          uint256 out;
                          assembly {
                              out := byte(0, mload(ptr))
                          }
                          require(
                              out == 0 || out == 1,
                              "Lib_RLPReader: Invalid RLP boolean value, must be 0 or 1"
                          );
                          return out != 0;
                      }
                      /**
                       * Reads an RLP bool value into a bool.
                       * @param _in RLP bool value.
                       * @return Decoded bool.
                       */
                      function readBool(
                          bytes memory _in
                      )
                          internal
                          pure
                          returns (
                              bool
                          )
                      {
                          return readBool(
                              toRLPItem(_in)
                          );
                      }
                      /**
                       * Reads an RLP address value into a address.
                       * @param _in RLP address value.
                       * @return Decoded address.
                       */
                      function readAddress(
                          RLPItem memory _in
                      )
                          internal
                          pure
                          returns (
                              address
                          )
                      {
                          if (_in.length == 1) {
                              return address(0);
                          }
                          require(
                              _in.length == 21,
                              "Invalid RLP address value."
                          );
                          return address(readUint256(_in));
                      }
                      /**
                       * Reads an RLP address value into a address.
                       * @param _in RLP address value.
                       * @return Decoded address.
                       */
                      function readAddress(
                          bytes memory _in
                      )
                          internal
                          pure
                          returns (
                              address
                          )
                      {
                          return readAddress(
                              toRLPItem(_in)
                          );
                      }
                      /**
                       * Reads the raw bytes of an RLP item.
                       * @param _in RLP item to read.
                       * @return Raw RLP bytes.
                       */
                      function readRawBytes(
                          RLPItem memory _in
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          return _copy(_in);
                      }
                      /*********************
                       * Private Functions *
                       *********************/
                      /**
                       * Decodes the length of an RLP item.
                       * @param _in RLP item to decode.
                       * @return Offset of the encoded data.
                       * @return Length of the encoded data.
                       * @return RLP item type (LIST_ITEM or DATA_ITEM).
                       */
                      function _decodeLength(
                          RLPItem memory _in
                      )
                          private
                          pure
                          returns (
                              uint256,
                              uint256,
                              RLPItemType
                          )
                      {
                          require(
                              _in.length > 0,
                              "RLP item cannot be null."
                          );
                          uint256 ptr = _in.ptr;
                          uint256 prefix;
                          assembly {
                              prefix := byte(0, mload(ptr))
                          }
                          if (prefix <= 0x7f) {
                              // Single byte.
                              return (0, 1, RLPItemType.DATA_ITEM);
                          } else if (prefix <= 0xb7) {
                              // Short string.
                              uint256 strLen = prefix - 0x80;
                              require(
                                  _in.length > strLen,
                                  "Invalid RLP short string."
                              );
                              return (1, strLen, RLPItemType.DATA_ITEM);
                          } else if (prefix <= 0xbf) {
                              // Long string.
                              uint256 lenOfStrLen = prefix - 0xb7;
                              require(
                                  _in.length > lenOfStrLen,
                                  "Invalid RLP long string length."
                              );
                              uint256 strLen;
                              assembly {
                                  // Pick out the string length.
                                  strLen := div(
                                      mload(add(ptr, 1)),
                                      exp(256, sub(32, lenOfStrLen))
                                  )
                              }
                              require(
                                  _in.length > lenOfStrLen + strLen,
                                  "Invalid RLP long string."
                              );
                              return (1 + lenOfStrLen, strLen, RLPItemType.DATA_ITEM);
                          } else if (prefix <= 0xf7) {
                              // Short list.
                              uint256 listLen = prefix - 0xc0;
                              require(
                                  _in.length > listLen,
                                  "Invalid RLP short list."
                              );
                              return (1, listLen, RLPItemType.LIST_ITEM);
                          } else {
                              // Long list.
                              uint256 lenOfListLen = prefix - 0xf7;
                              require(
                                  _in.length > lenOfListLen,
                                  "Invalid RLP long list length."
                              );
                              uint256 listLen;
                              assembly {
                                  // Pick out the list length.
                                  listLen := div(
                                      mload(add(ptr, 1)),
                                      exp(256, sub(32, lenOfListLen))
                                  )
                              }
                              require(
                                  _in.length > lenOfListLen + listLen,
                                  "Invalid RLP long list."
                              );
                              return (1 + lenOfListLen, listLen, RLPItemType.LIST_ITEM);
                          }
                      }
                      /**
                       * Copies the bytes from a memory location.
                       * @param _src Pointer to the location to read from.
                       * @param _offset Offset to start reading from.
                       * @param _length Number of bytes to read.
                       * @return Copied bytes.
                       */
                      function _copy(
                          uint256 _src,
                          uint256 _offset,
                          uint256 _length
                      )
                          private
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          bytes memory out = new bytes(_length);
                          if (out.length == 0) {
                              return out;
                          }
                          uint256 src = _src + _offset;
                          uint256 dest;
                          assembly {
                              dest := add(out, 32)
                          }
                          // Copy over as many complete words as we can.
                          for (uint256 i = 0; i < _length / 32; i++) {
                              assembly {
                                  mstore(dest, mload(src))
                              }
                              src += 32;
                              dest += 32;
                          }
                          // Pick out the remaining bytes.
                          uint256 mask = 256 ** (32 - (_length % 32)) - 1;
                          assembly {
                              mstore(
                                  dest,
                                  or(
                                      and(mload(src), not(mask)),
                                      and(mload(dest), mask)
                                  )
                              )
                          }
                          return out;
                      }
                      /**
                       * Copies an RLP item into bytes.
                       * @param _in RLP item to copy.
                       * @return Copied bytes.
                       */
                      function _copy(
                          RLPItem memory _in
                      )
                          private
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          return _copy(_in.ptr, 0, _in.length);
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  pragma experimental ABIEncoderV2;
                  /**
                   * @title Lib_RLPWriter
                   * @author Bakaoh (with modifications)
                   */
                  library Lib_RLPWriter {
                      /**********************
                       * Internal Functions *
                       **********************/
                      /**
                       * RLP encodes a byte string.
                       * @param _in The byte string to encode.
                       * @return The RLP encoded string in bytes.
                       */
                      function writeBytes(
                          bytes memory _in
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          bytes memory encoded;
                          if (_in.length == 1 && uint8(_in[0]) < 128) {
                              encoded = _in;
                          } else {
                              encoded = abi.encodePacked(_writeLength(_in.length, 128), _in);
                          }
                          return encoded;
                      }
                      /**
                       * RLP encodes a list of RLP encoded byte byte strings.
                       * @param _in The list of RLP encoded byte strings.
                       * @return The RLP encoded list of items in bytes.
                       */
                      function writeList(
                          bytes[] memory _in
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          bytes memory list = _flatten(_in);
                          return abi.encodePacked(_writeLength(list.length, 192), list);
                      }
                      /**
                       * RLP encodes a string.
                       * @param _in The string to encode.
                       * @return The RLP encoded string in bytes.
                       */
                      function writeString(
                          string memory _in
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          return writeBytes(bytes(_in));
                      }
                      /**
                       * RLP encodes an address.
                       * @param _in The address to encode.
                       * @return The RLP encoded address in bytes.
                       */
                      function writeAddress(
                          address _in
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          return writeBytes(abi.encodePacked(_in));
                      }
                      /**
                       * RLP encodes a bytes32 value.
                       * @param _in The bytes32 to encode.
                       * @return _out The RLP encoded bytes32 in bytes.
                       */
                      function writeBytes32(
                          bytes32 _in
                      )
                          internal
                          pure
                          returns (
                              bytes memory _out
                          )
                      {
                          return writeBytes(abi.encodePacked(_in));
                      }
                      /**
                       * RLP encodes a uint.
                       * @param _in The uint256 to encode.
                       * @return The RLP encoded uint256 in bytes.
                       */
                      function writeUint(
                          uint256 _in
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          return writeBytes(_toBinary(_in));
                      }
                      /**
                       * RLP encodes a bool.
                       * @param _in The bool to encode.
                       * @return The RLP encoded bool in bytes.
                       */
                      function writeBool(
                          bool _in
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          bytes memory encoded = new bytes(1);
                          encoded[0] = (_in ? bytes1(0x01) : bytes1(0x80));
                          return encoded;
                      }
                      /*********************
                       * Private Functions *
                       *********************/
                      /**
                       * Encode the first byte, followed by the `len` in binary form if `length` is more than 55.
                       * @param _len The length of the string or the payload.
                       * @param _offset 128 if item is string, 192 if item is list.
                       * @return RLP encoded bytes.
                       */
                      function _writeLength(
                          uint256 _len,
                          uint256 _offset
                      )
                          private
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          bytes memory encoded;
                          if (_len < 56) {
                              encoded = new bytes(1);
                              encoded[0] = byte(uint8(_len) + uint8(_offset));
                          } else {
                              uint256 lenLen;
                              uint256 i = 1;
                              while (_len / i != 0) {
                                  lenLen++;
                                  i *= 256;
                              }
                              encoded = new bytes(lenLen + 1);
                              encoded[0] = byte(uint8(lenLen) + uint8(_offset) + 55);
                              for(i = 1; i <= lenLen; i++) {
                                  encoded[i] = byte(uint8((_len / (256**(lenLen-i))) % 256));
                              }
                          }
                          return encoded;
                      }
                      /**
                       * Encode integer in big endian binary form with no leading zeroes.
                       * @notice TODO: This should be optimized with assembly to save gas costs.
                       * @param _x The integer to encode.
                       * @return RLP encoded bytes.
                       */
                      function _toBinary(
                          uint256 _x
                      )
                          private
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          bytes memory b = abi.encodePacked(_x);
                          uint256 i = 0;
                          for (; i < 32; i++) {
                              if (b[i] != 0) {
                                  break;
                              }
                          }
                          bytes memory res = new bytes(32 - i);
                          for (uint256 j = 0; j < res.length; j++) {
                              res[j] = b[i++];
                          }
                          return res;
                      }
                      /**
                       * Copies a piece of memory to another location.
                       * @notice From: https://github.com/Arachnid/solidity-stringutils/blob/master/src/strings.sol.
                       * @param _dest Destination location.
                       * @param _src Source location.
                       * @param _len Length of memory to copy.
                       */
                      function _memcpy(
                          uint256 _dest,
                          uint256 _src,
                          uint256 _len
                      )
                          private
                          pure
                      {
                          uint256 dest = _dest;
                          uint256 src = _src;
                          uint256 len = _len;
                          for(; len >= 32; len -= 32) {
                              assembly {
                                  mstore(dest, mload(src))
                              }
                              dest += 32;
                              src += 32;
                          }
                          uint256 mask = 256 ** (32 - len) - 1;
                          assembly {
                              let srcpart := and(mload(src), not(mask))
                              let destpart := and(mload(dest), mask)
                              mstore(dest, or(destpart, srcpart))
                          }
                      }
                      /**
                       * Flattens a list of byte strings into one byte string.
                       * @notice From: https://github.com/sammayo/solidity-rlp-encoder/blob/master/RLPEncode.sol.
                       * @param _list List of byte strings to flatten.
                       * @return The flattened byte string.
                       */
                      function _flatten(
                          bytes[] memory _list
                      )
                          private
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          if (_list.length == 0) {
                              return new bytes(0);
                          }
                          uint256 len;
                          uint256 i = 0;
                          for (; i < _list.length; i++) {
                              len += _list[i].length;
                          }
                          bytes memory flattened = new bytes(len);
                          uint256 flattenedPtr;
                          assembly { flattenedPtr := add(flattened, 0x20) }
                          for(i = 0; i < _list.length; i++) {
                              bytes memory item = _list[i];
                              uint256 listPtr;
                              assembly { listPtr := add(item, 0x20)}
                              _memcpy(flattenedPtr, listPtr, item.length);
                              flattenedPtr += _list[i].length;
                          }
                          return flattened;
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  /**
                   * @title Lib_Byte32Utils
                   */
                  library Lib_Bytes32Utils {
                      /**********************
                       * Internal Functions *
                       **********************/
                      /**
                       * Converts a bytes32 value to a boolean. Anything non-zero will be converted to "true."
                       * @param _in Input bytes32 value.
                       * @return Bytes32 as a boolean.
                       */
                      function toBool(
                          bytes32 _in
                      )
                          internal
                          pure
                          returns (
                              bool
                          )
                      {
                          return _in != 0;
                      }
                      /**
                       * Converts a boolean to a bytes32 value.
                       * @param _in Input boolean value.
                       * @return Boolean as a bytes32.
                       */
                      function fromBool(
                          bool _in
                      )
                          internal
                          pure
                          returns (
                              bytes32
                          )
                      {
                          return bytes32(uint256(_in ? 1 : 0));
                      }
                      /**
                       * Converts a bytes32 value to an address. Takes the *last* 20 bytes.
                       * @param _in Input bytes32 value.
                       * @return Bytes32 as an address.
                       */
                      function toAddress(
                          bytes32 _in
                      )
                          internal
                          pure
                          returns (
                              address
                          )
                      {
                          return address(uint160(uint256(_in)));
                      }
                      /**
                       * Converts an address to a bytes32.
                       * @param _in Input address value.
                       * @return Address as a bytes32.
                       */
                      function fromAddress(
                          address _in
                      )
                          internal
                          pure
                          returns (
                              bytes32
                          )
                      {
                          return bytes32(uint256(_in));
                      }
                      /**
                       * Removes the leading zeros from a bytes32 value and returns a new (smaller) bytes value.
                       * @param _in Input bytes32 value.
                       * @return Bytes32 without any leading zeros.
                       */
                      function removeLeadingZeros(
                          bytes32 _in
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          bytes memory out;
                          assembly {
                              // Figure out how many leading zero bytes to remove.
                              let shift := 0
                              for { let i := 0 } and(lt(i, 32), eq(byte(i, _in), 0)) { i := add(i, 1) } {
                                  shift := add(shift, 1)
                              }
                              // Reserve some space for our output and fix the free memory pointer.
                              out := mload(0x40)
                              mstore(0x40, add(out, 0x40))
                              // Shift the value and store it into the output bytes.
                              mstore(add(out, 0x20), shl(mul(shift, 8), _in))
                              // Store the new size (with leading zero bytes removed) in the output byte size.
                              mstore(out, sub(32, shift))
                          }
                          return out;
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  /**
                   * @title Lib_BytesUtils
                   */
                  library Lib_BytesUtils {
                      /**********************
                       * Internal Functions *
                       **********************/
                      function slice(
                          bytes memory _bytes,
                          uint256 _start,
                          uint256 _length
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          require(_length + 31 >= _length, "slice_overflow");
                          require(_start + _length >= _start, "slice_overflow");
                          require(_bytes.length >= _start + _length, "slice_outOfBounds");
                          bytes memory tempBytes;
                          assembly {
                              switch iszero(_length)
                              case 0 {
                                  // Get a location of some free memory and store it in tempBytes as
                                  // Solidity does for memory variables.
                                  tempBytes := mload(0x40)
                                  // The first word of the slice result is potentially a partial
                                  // word read from the original array. To read it, we calculate
                                  // the length of that partial word and start copying that many
                                  // bytes into the array. The first word we copy will start with
                                  // data we don't care about, but the last `lengthmod` bytes will
                                  // land at the beginning of the contents of the new array. When
                                  // we're done copying, we overwrite the full first word with
                                  // the actual length of the slice.
                                  let lengthmod := and(_length, 31)
                                  // The multiplication in the next line is necessary
                                  // because when slicing multiples of 32 bytes (lengthmod == 0)
                                  // the following copy loop was copying the origin's length
                                  // and then ending prematurely not copying everything it should.
                                  let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
                                  let end := add(mc, _length)
                                  for {
                                      // The multiplication in the next line has the same exact purpose
                                      // as the one above.
                                      let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
                                  } lt(mc, end) {
                                      mc := add(mc, 0x20)
                                      cc := add(cc, 0x20)
                                  } {
                                      mstore(mc, mload(cc))
                                  }
                                  mstore(tempBytes, _length)
                                  //update free-memory pointer
                                  //allocating the array padded to 32 bytes like the compiler does now
                                  mstore(0x40, and(add(mc, 31), not(31)))
                              }
                              //if we want a zero-length slice let's just return a zero-length array
                              default {
                                  tempBytes := mload(0x40)
                                  //zero out the 32 bytes slice we are about to return
                                  //we need to do it because Solidity does not garbage collect
                                  mstore(tempBytes, 0)
                                  mstore(0x40, add(tempBytes, 0x20))
                              }
                          }
                          return tempBytes;
                      }
                      function slice(
                          bytes memory _bytes,
                          uint256 _start
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          if (_start >= _bytes.length) {
                              return bytes('');
                          }
                          return slice(_bytes, _start, _bytes.length - _start);
                      }
                      function toBytes32PadLeft(
                          bytes memory _bytes
                      )
                          internal
                          pure
                          returns (
                              bytes32
                          )
                      {
                          bytes32 ret;
                          uint256 len = _bytes.length <= 32 ? _bytes.length : 32;
                          assembly {
                              ret := shr(mul(sub(32, len), 8), mload(add(_bytes, 32)))
                          }
                          return ret;
                      }
                      function toBytes32(
                          bytes memory _bytes
                      )
                          internal
                          pure
                          returns (
                              bytes32
                          )
                      {
                          if (_bytes.length < 32) {
                              bytes32 ret;
                              assembly {
                                  ret := mload(add(_bytes, 32))
                              }
                              return ret;
                          }
                          return abi.decode(_bytes,(bytes32)); // will truncate if input length > 32 bytes
                      }
                      function toUint256(
                          bytes memory _bytes
                      )
                          internal
                          pure
                          returns (
                              uint256
                          )
                      {
                          return uint256(toBytes32(_bytes));
                      }
                      function toUint24(
                          bytes memory _bytes,
                          uint256 _start
                      )
                          internal
                          pure
                          returns (
                              uint24
                          )
                      {
                          require(_start + 3 >= _start, "toUint24_overflow");
                          require(_bytes.length >= _start + 3 , "toUint24_outOfBounds");
                          uint24 tempUint;
                          assembly {
                              tempUint := mload(add(add(_bytes, 0x3), _start))
                          }
                          return tempUint;
                      }
                      function toUint8(
                          bytes memory _bytes,
                          uint256 _start
                      )
                          internal
                          pure
                          returns (
                              uint8
                          )
                      {
                          require(_start + 1 >= _start, "toUint8_overflow");
                          require(_bytes.length >= _start + 1 , "toUint8_outOfBounds");
                          uint8 tempUint;
                          assembly {
                              tempUint := mload(add(add(_bytes, 0x1), _start))
                          }
                          return tempUint;
                      }
                      function toAddress(
                          bytes memory _bytes,
                          uint256 _start
                      )
                          internal
                          pure
                          returns (
                              address
                          )
                      {
                          require(_start + 20 >= _start, "toAddress_overflow");
                          require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
                          address tempAddress;
                          assembly {
                              tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
                          }
                          return tempAddress;
                      }
                      function toNibbles(
                          bytes memory _bytes
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          bytes memory nibbles = new bytes(_bytes.length * 2);
                          for (uint256 i = 0; i < _bytes.length; i++) {
                              nibbles[i * 2] = _bytes[i] >> 4;
                              nibbles[i * 2 + 1] = bytes1(uint8(_bytes[i]) % 16);
                          }
                          return nibbles;
                      }
                      function fromNibbles(
                          bytes memory _bytes
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          bytes memory ret = new bytes(_bytes.length / 2);
                          for (uint256 i = 0; i < ret.length; i++) {
                              ret[i] = (_bytes[i * 2] << 4) | (_bytes[i * 2 + 1]);
                          }
                          return ret;
                      }
                      function equal(
                          bytes memory _bytes,
                          bytes memory _other
                      )
                          internal
                          pure
                          returns (
                              bool
                          )
                      {
                          return keccak256(_bytes) == keccak256(_other);
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  pragma experimental ABIEncoderV2;
                  /**
                   * @title Lib_ErrorUtils
                   */
                  library Lib_ErrorUtils {
                      /**********************
                       * Internal Functions *
                       **********************/
                      /**
                       * Encodes an error string into raw solidity-style revert data.
                       * (i.e. ascii bytes, prefixed with bytes4(keccak("Error(string))"))
                       * Ref: https://docs.soliditylang.org/en/v0.8.2/control-structures.html?highlight=Error(string)#panic-via-assert-and-error-via-require
                       * @param _reason Reason for the reversion.
                       * @return Standard solidity revert data for the given reason.
                       */
                      function encodeRevertString(
                          string memory _reason
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          return abi.encodeWithSignature(
                              "Error(string)",
                              _reason
                          );
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // @unsupported: ovm
                  pragma solidity >0.5.0 <0.8.0;
                  pragma experimental ABIEncoderV2;
                  /* Library Imports */
                  import { Lib_RLPWriter } from "../rlp/Lib_RLPWriter.sol";
                  import { Lib_Bytes32Utils } from "./Lib_Bytes32Utils.sol";
                  /**
                   * @title Lib_EthUtils
                   */
                  library Lib_EthUtils {
                      /**********************
                       * Internal Functions *
                       **********************/
                      /**
                       * Gets the code for a given address.
                       * @param _address Address to get code for.
                       * @param _offset Offset to start reading from.
                       * @param _length Number of bytes to read.
                       * @return Code read from the contract.
                       */
                      function getCode(
                          address _address,
                          uint256 _offset,
                          uint256 _length
                      )
                          internal
                          view
                          returns (
                              bytes memory
                          )
                      {
                          bytes memory code;
                          assembly {
                              code := mload(0x40)
                              mstore(0x40, add(code, add(_length, 0x20)))
                              mstore(code, _length)
                              extcodecopy(_address, add(code, 0x20), _offset, _length)
                          }
                          return code;
                      }
                      /**
                       * Gets the full code for a given address.
                       * @param _address Address to get code for.
                       * @return Full code of the contract.
                       */
                      function getCode(
                          address _address
                      )
                          internal
                          view
                          returns (
                              bytes memory
                          )
                      {
                          return getCode(
                              _address,
                              0,
                              getCodeSize(_address)
                          );
                      }
                      /**
                       * Gets the size of a contract's code in bytes.
                       * @param _address Address to get code size for.
                       * @return Size of the contract's code in bytes.
                       */
                      function getCodeSize(
                          address _address
                      )
                          internal
                          view
                          returns (
                              uint256
                          )
                      {
                          uint256 codeSize;
                          assembly {
                              codeSize := extcodesize(_address)
                          }
                          return codeSize;
                      }
                      /**
                       * Gets the hash of a contract's code.
                       * @param _address Address to get a code hash for.
                       * @return Hash of the contract's code.
                       */
                      function getCodeHash(
                          address _address
                      )
                          internal
                          view
                          returns (
                              bytes32
                          )
                      {
                          bytes32 codeHash;
                          assembly {
                              codeHash := extcodehash(_address)
                          }
                          return codeHash;
                      }
                      /**
                       * Creates a contract with some given initialization code.
                       * @param _code Contract initialization code.
                       * @return Address of the created contract.
                       */
                      function createContract(
                          bytes memory _code
                      )
                          internal
                          returns (
                              address
                          )
                      {
                          address created;
                          assembly {
                              created := create(
                                  0,
                                  add(_code, 0x20),
                                  mload(_code)
                              )
                          }
                          return created;
                      }
                      /**
                       * Computes the address that would be generated by CREATE.
                       * @param _creator Address creating the contract.
                       * @param _nonce Creator's nonce.
                       * @return Address to be generated by CREATE.
                       */
                      function getAddressForCREATE(
                          address _creator,
                          uint256 _nonce
                      )
                          internal
                          pure
                          returns (
                              address
                          )
                      {
                          bytes[] memory encoded = new bytes[](2);
                          encoded[0] = Lib_RLPWriter.writeAddress(_creator);
                          encoded[1] = Lib_RLPWriter.writeUint(_nonce);
                          bytes memory encodedList = Lib_RLPWriter.writeList(encoded);
                          return Lib_Bytes32Utils.toAddress(keccak256(encodedList));
                      }
                      /**
                       * Computes the address that would be generated by CREATE2.
                       * @param _creator Address creating the contract.
                       * @param _bytecode Bytecode of the contract to be created.
                       * @param _salt 32 byte salt value mixed into the hash.
                       * @return Address to be generated by CREATE2.
                       */
                      function getAddressForCREATE2(
                          address _creator,
                          bytes memory _bytecode,
                          bytes32 _salt
                      )
                          internal
                          pure
                          returns (
                              address
                          )
                      {
                          bytes32 hashedData = keccak256(abi.encodePacked(
                              byte(0xff),
                              _creator,
                              _salt,
                              keccak256(_bytecode)
                          ));
                          return Lib_Bytes32Utils.toAddress(hashedData);
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  /**
                   * @title Lib_MerkleTree
                   * @author River Keefer
                   */
                  library Lib_MerkleTree {
                      /**********************
                       * Internal Functions *
                       **********************/
                      /**
                       * Calculates a merkle root for a list of 32-byte leaf hashes.  WARNING: If the number
                       * of leaves passed in is not a power of two, it pads out the tree with zero hashes.
                       * If you do not know the original length of elements for the tree you are verifying,
                       * then this may allow empty leaves past _elements.length to pass a verification check down the line.
                       * Note that the _elements argument is modified, therefore it must not be used again afterwards
                       * @param _elements Array of hashes from which to generate a merkle root.
                       * @return Merkle root of the leaves, with zero hashes for non-powers-of-two (see above).
                       */
                      function getMerkleRoot(
                          bytes32[] memory _elements
                      )
                          internal
                          pure
                          returns (
                              bytes32
                          )
                      {
                          require(
                              _elements.length > 0,
                              "Lib_MerkleTree: Must provide at least one leaf hash."
                          );
                          if (_elements.length == 1) {
                              return _elements[0];
                          }
                          uint256[16] memory defaults = [
                              0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563,
                              0x633dc4d7da7256660a892f8f1604a44b5432649cc8ec5cb3ced4c4e6ac94dd1d,
                              0x890740a8eb06ce9be422cb8da5cdafc2b58c0a5e24036c578de2a433c828ff7d,
                              0x3b8ec09e026fdc305365dfc94e189a81b38c7597b3d941c279f042e8206e0bd8,
                              0xecd50eee38e386bd62be9bedb990706951b65fe053bd9d8a521af753d139e2da,
                              0xdefff6d330bb5403f63b14f33b578274160de3a50df4efecf0e0db73bcdd3da5,
                              0x617bdd11f7c0a11f49db22f629387a12da7596f9d1704d7465177c63d88ec7d7,
                              0x292c23a9aa1d8bea7e2435e555a4a60e379a5a35f3f452bae60121073fb6eead,
                              0xe1cea92ed99acdcb045a6726b2f87107e8a61620a232cf4d7d5b5766b3952e10,
                              0x7ad66c0a68c72cb89e4fb4303841966e4062a76ab97451e3b9fb526a5ceb7f82,
                              0xe026cc5a4aed3c22a58cbd3d2ac754c9352c5436f638042dca99034e83636516,
                              0x3d04cffd8b46a874edf5cfae63077de85f849a660426697b06a829c70dd1409c,
                              0xad676aa337a485e4728a0b240d92b3ef7b3c372d06d189322bfd5f61f1e7203e,
                              0xa2fca4a49658f9fab7aa63289c91b7c7b6c832a6d0e69334ff5b0a3483d09dab,
                              0x4ebfd9cd7bca2505f7bef59cc1c12ecc708fff26ae4af19abe852afe9e20c862,
                              0x2def10d13dd169f550f578bda343d9717a138562e0093b380a1120789d53cf10
                          ];
                          // Reserve memory space for our hashes.
                          bytes memory buf = new bytes(64);
                          // We'll need to keep track of left and right siblings.
                          bytes32 leftSibling;
                          bytes32 rightSibling;
                          // Number of non-empty nodes at the current depth.
                          uint256 rowSize = _elements.length;
                          // Current depth, counting from 0 at the leaves
                          uint256 depth = 0;
                          // Common sub-expressions
                          uint256 halfRowSize;         // rowSize / 2
                          bool rowSizeIsOdd;           // rowSize % 2 == 1
                          while (rowSize > 1) {
                              halfRowSize = rowSize / 2;
                              rowSizeIsOdd = rowSize % 2 == 1;
                              for (uint256 i = 0; i < halfRowSize; i++) {
                                  leftSibling  = _elements[(2 * i)    ];
                                  rightSibling = _elements[(2 * i) + 1];
                                  assembly {
                                      mstore(add(buf, 32), leftSibling )
                                      mstore(add(buf, 64), rightSibling)
                                  }
                                  _elements[i] = keccak256(buf);
                              }
                              if (rowSizeIsOdd) {
                                  leftSibling  = _elements[rowSize - 1];
                                  rightSibling = bytes32(defaults[depth]);
                                  assembly {
                                      mstore(add(buf, 32), leftSibling)
                                      mstore(add(buf, 64), rightSibling)
                                  }
                                  _elements[halfRowSize] = keccak256(buf);
                              }
                              rowSize = halfRowSize + (rowSizeIsOdd ? 1 : 0);
                              depth++;
                          }
                          return _elements[0];
                      }
                      /**
                       * Verifies a merkle branch for the given leaf hash.  Assumes the original length
                       * of leaves generated is a known, correct input, and does not return true for indices
                       * extending past that index (even if _siblings would be otherwise valid.)
                       * @param _root The Merkle root to verify against.
                       * @param _leaf The leaf hash to verify inclusion of.
                       * @param _index The index in the tree of this leaf.
                       * @param _siblings Array of sibline nodes in the inclusion proof, starting from depth 0 (bottom of the tree).
                       * @param _totalLeaves The total number of leaves originally passed into.
                       * @return Whether or not the merkle branch and leaf passes verification.
                       */
                      function verify(
                          bytes32 _root,
                          bytes32 _leaf,
                          uint256 _index,
                          bytes32[] memory _siblings,
                          uint256 _totalLeaves
                      )
                          internal
                          pure
                          returns (
                              bool
                          )
                      {
                          require(
                              _totalLeaves > 0,
                              "Lib_MerkleTree: Total leaves must be greater than zero."
                          );
                          require(
                              _index < _totalLeaves,
                              "Lib_MerkleTree: Index out of bounds."
                          );
                          require(
                              _siblings.length == _ceilLog2(_totalLeaves),
                              "Lib_MerkleTree: Total siblings does not correctly correspond to total leaves."
                          );
                          bytes32 computedRoot = _leaf;
                          for (uint256 i = 0; i < _siblings.length; i++) {
                              if ((_index & 1) == 1) {
                                  computedRoot = keccak256(
                                      abi.encodePacked(
                                          _siblings[i],
                                          computedRoot
                                      )
                                  );
                              } else {
                                  computedRoot = keccak256(
                                      abi.encodePacked(
                                          computedRoot,
                                          _siblings[i]
                                      )
                                  );
                              }
                              _index >>= 1;
                          }
                          return _root == computedRoot;
                      }
                      /*********************
                       * Private Functions *
                       *********************/
                      /**
                       * Calculates the integer ceiling of the log base 2 of an input.
                       * @param _in Unsigned input to calculate the log.
                       * @return ceil(log_base_2(_in))
                       */
                      function _ceilLog2(
                          uint256 _in
                      )
                          private
                          pure
                          returns (
                              uint256
                          )
                      {
                          require(
                              _in > 0,
                              "Lib_MerkleTree: Cannot compute ceil(log_2) of 0."
                          );
                          if (_in == 1) {
                              return 0;
                          }
                          // Find the highest set bit (will be floor(log_2)).
                          // Borrowed with <3 from https://github.com/ethereum/solidity-examples
                          uint256 val = _in;
                          uint256 highest = 0;
                          for (uint256 i = 128; i >= 1; i >>= 1) {
                              if (val & (uint(1) << i) - 1 << i != 0) {
                                  highest += i;
                                  val >>= i;
                              }
                          }
                          // Increment by one if this is not a perfect logarithm.
                          if ((uint(1) << highest) != _in) {
                              highest += 1;
                          }
                          return highest;
                      }
                  }
                  

                  File 3 of 7: Lib_ResolvedDelegateProxy
                  // SPDX-License-Identifier: MIT
                  pragma solidity >=0.6.0 <0.8.0;
                  import "../utils/Context.sol";
                  /**
                   * @dev Contract module which provides a basic access control mechanism, where
                   * there is an account (an owner) that can be granted exclusive access to
                   * specific functions.
                   *
                   * By default, the owner account will be the one that deploys the contract. This
                   * can later be changed with {transferOwnership}.
                   *
                   * This module is used through inheritance. It will make available the modifier
                   * `onlyOwner`, which can be applied to your functions to restrict their use to
                   * the owner.
                   */
                  abstract contract Ownable is Context {
                      address private _owner;
                      event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                      /**
                       * @dev Initializes the contract setting the deployer as the initial owner.
                       */
                      constructor () internal {
                          address msgSender = _msgSender();
                          _owner = msgSender;
                          emit OwnershipTransferred(address(0), msgSender);
                      }
                      /**
                       * @dev Returns the address of the current owner.
                       */
                      function owner() public view virtual returns (address) {
                          return _owner;
                      }
                      /**
                       * @dev Throws if called by any account other than the owner.
                       */
                      modifier onlyOwner() {
                          require(owner() == _msgSender(), "Ownable: caller is not the owner");
                          _;
                      }
                      /**
                       * @dev Leaves the contract without owner. It will not be possible to call
                       * `onlyOwner` functions anymore. Can only be called by the current owner.
                       *
                       * NOTE: Renouncing ownership will leave the contract without an owner,
                       * thereby removing any functionality that is only available to the owner.
                       */
                      function renounceOwnership() public virtual onlyOwner {
                          emit OwnershipTransferred(_owner, address(0));
                          _owner = 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");
                          emit OwnershipTransferred(_owner, newOwner);
                          _owner = newOwner;
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >=0.6.0 <0.8.0;
                  /*
                   * @dev Provides information about the current execution context, including the
                   * sender of the transaction and its data. While these are generally available
                   * via msg.sender and msg.data, they should not be accessed in such a direct
                   * manner, since when dealing with GSN meta-transactions the account sending and
                   * paying for execution may not be the actual sender (as far as an application
                   * is concerned).
                   *
                   * This contract is only required for intermediate, library-like contracts.
                   */
                  abstract contract Context {
                      function _msgSender() internal view virtual returns (address payable) {
                          return msg.sender;
                      }
                      function _msgData() internal view virtual returns (bytes memory) {
                          this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                          return msg.data;
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  /* External Imports */
                  import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
                  /**
                   * @title Lib_AddressManager
                   */
                  contract Lib_AddressManager is Ownable {
                      /**********
                       * Events *
                       **********/
                      event AddressSet(
                          string indexed _name,
                          address _newAddress,
                          address _oldAddress
                      );
                      /*************
                       * Variables *
                       *************/
                      mapping (bytes32 => address) private addresses;
                      /********************
                       * Public Functions *
                       ********************/
                      /**
                       * Changes the address associated with a particular name.
                       * @param _name String name to associate an address with.
                       * @param _address Address to associate with the name.
                       */
                      function setAddress(
                          string memory _name,
                          address _address
                      )
                          external
                          onlyOwner
                      {
                          bytes32 nameHash = _getNameHash(_name);
                          address oldAddress = addresses[nameHash];
                          addresses[nameHash] = _address;
                          emit AddressSet(
                              _name,
                              _address,
                              oldAddress
                          );
                      }
                      /**
                       * Retrieves the address associated with a given name.
                       * @param _name Name to retrieve an address for.
                       * @return Address associated with the given name.
                       */
                      function getAddress(
                          string memory _name
                      )
                          external
                          view
                          returns (
                              address
                          )
                      {
                          return addresses[_getNameHash(_name)];
                      }
                      /**********************
                       * Internal Functions *
                       **********************/
                      /**
                       * Computes the hash of a name.
                       * @param _name Name to compute a hash for.
                       * @return Hash of the given name.
                       */
                      function _getNameHash(
                          string memory _name
                      )
                          internal
                          pure
                          returns (
                              bytes32
                          )
                      {
                          return keccak256(abi.encodePacked(_name));
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  /* Library Imports */
                  import { Lib_AddressManager } from "./Lib_AddressManager.sol";
                  /**
                   * @title Lib_ResolvedDelegateProxy
                   */
                  contract Lib_ResolvedDelegateProxy {
                      /*************
                       * Variables *
                       *************/
                      // Using mappings to store fields to avoid overwriting storage slots in the
                      // implementation contract. For example, instead of storing these fields at
                      // storage slot `0` & `1`, they are stored at `keccak256(key + slot)`.
                      // See: https://solidity.readthedocs.io/en/v0.7.0/internals/layout_in_storage.html
                      // NOTE: Do not use this code in your own contract system.
                      //      There is a known flaw in this contract, and we will remove it from the repository
                      //      in the near future. Due to the very limited way that we are using it, this flaw is
                      //      not an issue in our system.
                      mapping (address => string) private implementationName;
                      mapping (address => Lib_AddressManager) private addressManager;
                      /***************
                       * Constructor *
                       ***************/
                      /**
                       * @param _libAddressManager Address of the Lib_AddressManager.
                       * @param _implementationName implementationName of the contract to proxy to.
                       */
                      constructor(
                          address _libAddressManager,
                          string memory _implementationName
                      ) {
                          addressManager[address(this)] = Lib_AddressManager(_libAddressManager);
                          implementationName[address(this)] = _implementationName;
                      }
                      /*********************
                       * Fallback Function *
                       *********************/
                      fallback()
                          external
                          payable
                      {
                          address target = addressManager[address(this)].getAddress(
                              (implementationName[address(this)])
                          );
                          require(
                              target != address(0),
                              "Target address must be initialized."
                          );
                          (bool success, bytes memory returndata) = target.delegatecall(msg.data);
                          if (success == true) {
                              assembly {
                                  return(add(returndata, 0x20), mload(returndata))
                              }
                          } else {
                              assembly {
                                  revert(add(returndata, 0x20), mload(returndata))
                              }
                          }
                      }
                  }
                  

                  File 4 of 7: OVM_L1StandardBridge
                  // SPDX-License-Identifier: MIT
                  pragma solidity >=0.6.0 <0.8.0;
                  /**
                   * @dev Wrappers over Solidity's arithmetic operations with added overflow
                   * checks.
                   *
                   * Arithmetic operations in Solidity wrap on overflow. This can easily result
                   * in bugs, because programmers usually assume that an overflow raises an
                   * error, which is the standard behavior in high level programming languages.
                   * `SafeMath` restores this intuition by reverting the transaction when an
                   * operation overflows.
                   *
                   * Using this library instead of the unchecked operations eliminates an entire
                   * class of bugs, so it's recommended to use it always.
                   */
                  library SafeMath {
                      /**
                       * @dev Returns the addition of two unsigned integers, with an overflow flag.
                       *
                       * _Available since v3.4._
                       */
                      function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                          uint256 c = a + b;
                          if (c < a) return (false, 0);
                          return (true, c);
                      }
                      /**
                       * @dev Returns the substraction of two unsigned integers, with an overflow flag.
                       *
                       * _Available since v3.4._
                       */
                      function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                          if (b > a) return (false, 0);
                          return (true, a - b);
                      }
                      /**
                       * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
                       *
                       * _Available since v3.4._
                       */
                      function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                          // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                          // benefit is lost if 'b' is also tested.
                          // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                          if (a == 0) return (true, 0);
                          uint256 c = a * b;
                          if (c / a != b) return (false, 0);
                          return (true, c);
                      }
                      /**
                       * @dev Returns the division of two unsigned integers, with a division by zero flag.
                       *
                       * _Available since v3.4._
                       */
                      function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                          if (b == 0) return (false, 0);
                          return (true, a / b);
                      }
                      /**
                       * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
                       *
                       * _Available since v3.4._
                       */
                      function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                          if (b == 0) return (false, 0);
                          return (true, a % b);
                      }
                      /**
                       * @dev Returns the addition of two unsigned integers, reverting on
                       * overflow.
                       *
                       * Counterpart to Solidity's `+` operator.
                       *
                       * Requirements:
                       *
                       * - Addition cannot overflow.
                       */
                      function add(uint256 a, uint256 b) internal pure returns (uint256) {
                          uint256 c = a + b;
                          require(c >= a, "SafeMath: addition overflow");
                          return c;
                      }
                      /**
                       * @dev Returns the subtraction of two unsigned integers, reverting on
                       * overflow (when the result is negative).
                       *
                       * Counterpart to Solidity's `-` operator.
                       *
                       * Requirements:
                       *
                       * - Subtraction cannot overflow.
                       */
                      function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                          require(b <= a, "SafeMath: subtraction overflow");
                          return a - b;
                      }
                      /**
                       * @dev Returns the multiplication of two unsigned integers, reverting on
                       * overflow.
                       *
                       * Counterpart to Solidity's `*` operator.
                       *
                       * Requirements:
                       *
                       * - Multiplication cannot overflow.
                       */
                      function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                          if (a == 0) return 0;
                          uint256 c = a * b;
                          require(c / a == b, "SafeMath: multiplication overflow");
                          return c;
                      }
                      /**
                       * @dev Returns the integer division of two unsigned integers, reverting on
                       * division by zero. The result is rounded towards zero.
                       *
                       * Counterpart to Solidity's `/` operator. Note: this function uses a
                       * `revert` opcode (which leaves remaining gas untouched) while Solidity
                       * uses an invalid opcode to revert (consuming all remaining gas).
                       *
                       * Requirements:
                       *
                       * - The divisor cannot be zero.
                       */
                      function div(uint256 a, uint256 b) internal pure returns (uint256) {
                          require(b > 0, "SafeMath: division by zero");
                          return a / b;
                      }
                      /**
                       * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                       * reverting when dividing by zero.
                       *
                       * Counterpart to Solidity's `%` operator. This function uses a `revert`
                       * opcode (which leaves remaining gas untouched) while Solidity uses an
                       * invalid opcode to revert (consuming all remaining gas).
                       *
                       * Requirements:
                       *
                       * - The divisor cannot be zero.
                       */
                      function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                          require(b > 0, "SafeMath: modulo by zero");
                          return a % b;
                      }
                      /**
                       * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
                       * overflow (when the result is negative).
                       *
                       * CAUTION: This function is deprecated because it requires allocating memory for the error
                       * message unnecessarily. For custom revert reasons use {trySub}.
                       *
                       * Counterpart to Solidity's `-` operator.
                       *
                       * Requirements:
                       *
                       * - Subtraction cannot overflow.
                       */
                      function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                          require(b <= a, errorMessage);
                          return a - b;
                      }
                      /**
                       * @dev Returns the integer division of two unsigned integers, reverting with custom message on
                       * division by zero. The result is rounded towards zero.
                       *
                       * CAUTION: This function is deprecated because it requires allocating memory for the error
                       * message unnecessarily. For custom revert reasons use {tryDiv}.
                       *
                       * Counterpart to Solidity's `/` operator. Note: this function uses a
                       * `revert` opcode (which leaves remaining gas untouched) while Solidity
                       * uses an invalid opcode to revert (consuming all remaining gas).
                       *
                       * Requirements:
                       *
                       * - The divisor cannot be zero.
                       */
                      function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                          require(b > 0, errorMessage);
                          return a / b;
                      }
                      /**
                       * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                       * reverting with custom message when dividing by zero.
                       *
                       * CAUTION: This function is deprecated because it requires allocating memory for the error
                       * message unnecessarily. For custom revert reasons use {tryMod}.
                       *
                       * Counterpart to Solidity's `%` operator. This function uses a `revert`
                       * opcode (which leaves remaining gas untouched) while Solidity uses an
                       * invalid opcode to revert (consuming all remaining gas).
                       *
                       * Requirements:
                       *
                       * - The divisor cannot be zero.
                       */
                      function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                          require(b > 0, errorMessage);
                          return a % b;
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >=0.6.0 <0.8.0;
                  /**
                   * @dev Interface of the ERC20 standard as defined in the EIP.
                   */
                  interface IERC20 {
                      /**
                       * @dev Returns the amount of tokens in existence.
                       */
                      function totalSupply() external view returns (uint256);
                      /**
                       * @dev Returns the amount of tokens owned by `account`.
                       */
                      function balanceOf(address account) external view returns (uint256);
                      /**
                       * @dev Moves `amount` tokens from the caller's account to `recipient`.
                       *
                       * Returns a boolean value indicating whether the operation succeeded.
                       *
                       * Emits a {Transfer} event.
                       */
                      function transfer(address recipient, uint256 amount) external returns (bool);
                      /**
                       * @dev Returns the remaining number of tokens that `spender` will be
                       * allowed to spend on behalf of `owner` through {transferFrom}. This is
                       * zero by default.
                       *
                       * This value changes when {approve} or {transferFrom} are called.
                       */
                      function allowance(address owner, address spender) external view returns (uint256);
                      /**
                       * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                       *
                       * Returns a boolean value indicating whether the operation succeeded.
                       *
                       * IMPORTANT: Beware that changing an allowance with this method brings the risk
                       * that someone may use both the old and the new allowance by unfortunate
                       * transaction ordering. One possible solution to mitigate this race
                       * condition is to first reduce the spender's allowance to 0 and set the
                       * desired value afterwards:
                       * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                       *
                       * Emits an {Approval} event.
                       */
                      function approve(address spender, uint256 amount) external returns (bool);
                      /**
                       * @dev Moves `amount` tokens from `sender` to `recipient` using the
                       * allowance mechanism. `amount` is then deducted from the caller's
                       * allowance.
                       *
                       * Returns a boolean value indicating whether the operation succeeded.
                       *
                       * Emits a {Transfer} event.
                       */
                      function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
                      /**
                       * @dev Emitted when `value` tokens are moved from one account (`from`) to
                       * another (`to`).
                       *
                       * Note that `value` may be zero.
                       */
                      event Transfer(address indexed from, address indexed to, uint256 value);
                      /**
                       * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                       * a call to {approve}. `value` is the new allowance.
                       */
                      event Approval(address indexed owner, address indexed spender, uint256 value);
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >=0.6.0 <0.8.0;
                  import "./IERC20.sol";
                  import "../../math/SafeMath.sol";
                  import "../../utils/Address.sol";
                  /**
                   * @title SafeERC20
                   * @dev Wrappers around ERC20 operations that throw on failure (when the token
                   * contract returns false). Tokens that return no value (and instead revert or
                   * throw on failure) are also supported, non-reverting calls are assumed to be
                   * successful.
                   * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
                   * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
                   */
                  library SafeERC20 {
                      using SafeMath for uint256;
                      using Address for address;
                      function safeTransfer(IERC20 token, address to, uint256 value) internal {
                          _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                      }
                      function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                          _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                      }
                      /**
                       * @dev Deprecated. This function has issues similar to the ones found in
                       * {IERC20-approve}, and its usage is discouraged.
                       *
                       * Whenever possible, use {safeIncreaseAllowance} and
                       * {safeDecreaseAllowance} instead.
                       */
                      function safeApprove(IERC20 token, address spender, uint256 value) internal {
                          // safeApprove should only be called when setting an initial allowance,
                          // or when resetting it to zero. To increase and decrease it, use
                          // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                          // solhint-disable-next-line max-line-length
                          require((value == 0) || (token.allowance(address(this), spender) == 0),
                              "SafeERC20: approve from non-zero to non-zero allowance"
                          );
                          _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                      }
                      function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                          uint256 newAllowance = token.allowance(address(this), spender).add(value);
                          _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                      }
                      function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                          uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
                          _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                      }
                      /**
                       * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                       * on the return value: the return value is optional (but if data is returned, it must not be false).
                       * @param token The token targeted by the call.
                       * @param data The call data (encoded using abi.encode or one of its variants).
                       */
                      function _callOptionalReturn(IERC20 token, bytes memory data) private {
                          // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                          // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
                          // the target address contains contract code and also asserts for success in the low-level call.
                          bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                          if (returndata.length > 0) { // Return data is optional
                              // solhint-disable-next-line max-line-length
                              require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                          }
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >=0.6.2 <0.8.0;
                  /**
                   * @dev Collection of functions related to the address type
                   */
                  library Address {
                      /**
                       * @dev Returns true if `account` is a contract.
                       *
                       * [IMPORTANT]
                       * ====
                       * It is unsafe to assume that an address for which this function returns
                       * false is an externally-owned account (EOA) and not a contract.
                       *
                       * Among others, `isContract` will return false for the following
                       * types of addresses:
                       *
                       *  - an externally-owned account
                       *  - a contract in construction
                       *  - an address where a contract will be created
                       *  - an address where a contract lived, but was destroyed
                       * ====
                       */
                      function isContract(address account) internal view returns (bool) {
                          // This method relies on extcodesize, which returns 0 for contracts in
                          // construction, since the code is only stored at the end of the
                          // constructor execution.
                          uint256 size;
                          // solhint-disable-next-line no-inline-assembly
                          assembly { size := extcodesize(account) }
                          return size > 0;
                      }
                      /**
                       * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                       * `recipient`, forwarding all available gas and reverting on errors.
                       *
                       * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                       * of certain opcodes, possibly making contracts go over the 2300 gas limit
                       * imposed by `transfer`, making them unable to receive funds via
                       * `transfer`. {sendValue} removes this limitation.
                       *
                       * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                       *
                       * IMPORTANT: because control is transferred to `recipient`, care must be
                       * taken to not create reentrancy vulnerabilities. Consider using
                       * {ReentrancyGuard} or the
                       * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                       */
                      function sendValue(address payable recipient, uint256 amount) internal {
                          require(address(this).balance >= amount, "Address: insufficient balance");
                          // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                          (bool success, ) = recipient.call{ value: amount }("");
                          require(success, "Address: unable to send value, recipient may have reverted");
                      }
                      /**
                       * @dev Performs a Solidity function call using a low level `call`. A
                       * plain`call` is an unsafe replacement for a function call: use this
                       * function instead.
                       *
                       * If `target` reverts with a revert reason, it is bubbled up by this
                       * function (like regular Solidity function calls).
                       *
                       * Returns the raw returned data. To convert to the expected return value,
                       * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                       *
                       * Requirements:
                       *
                       * - `target` must be a contract.
                       * - calling `target` with `data` must not revert.
                       *
                       * _Available since v3.1._
                       */
                      function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                        return functionCall(target, data, "Address: low-level call failed");
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                       * `errorMessage` as a fallback revert reason when `target` reverts.
                       *
                       * _Available since v3.1._
                       */
                      function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                          return functionCallWithValue(target, data, 0, errorMessage);
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                       * but also transferring `value` wei to `target`.
                       *
                       * Requirements:
                       *
                       * - the calling contract must have an ETH balance of at least `value`.
                       * - the called Solidity function must be `payable`.
                       *
                       * _Available since v3.1._
                       */
                      function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                          return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                      }
                      /**
                       * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                       * with `errorMessage` as a fallback revert reason when `target` reverts.
                       *
                       * _Available since v3.1._
                       */
                      function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
                          require(address(this).balance >= value, "Address: insufficient balance for call");
                          require(isContract(target), "Address: call to non-contract");
                          // solhint-disable-next-line avoid-low-level-calls
                          (bool success, bytes memory returndata) = target.call{ value: value }(data);
                          return _verifyCallResult(success, returndata, errorMessage);
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                       * but performing a static call.
                       *
                       * _Available since v3.3._
                       */
                      function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                          return functionStaticCall(target, data, "Address: low-level static call failed");
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                       * but performing a static call.
                       *
                       * _Available since v3.3._
                       */
                      function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
                          require(isContract(target), "Address: static call to non-contract");
                          // solhint-disable-next-line avoid-low-level-calls
                          (bool success, bytes memory returndata) = target.staticcall(data);
                          return _verifyCallResult(success, returndata, errorMessage);
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                       * but performing a delegate call.
                       *
                       * _Available since v3.4._
                       */
                      function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                          return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                       * but performing a delegate call.
                       *
                       * _Available since v3.4._
                       */
                      function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                          require(isContract(target), "Address: delegate call to non-contract");
                          // solhint-disable-next-line avoid-low-level-calls
                          (bool success, bytes memory returndata) = target.delegatecall(data);
                          return _verifyCallResult(success, returndata, errorMessage);
                      }
                      function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
                          if (success) {
                              return returndata;
                          } else {
                              // Look for revert reason and bubble it up if present
                              if (returndata.length > 0) {
                                  // The easiest way to bubble the revert reason is using memory via assembly
                                  // solhint-disable-next-line no-inline-assembly
                                  assembly {
                                      let returndata_size := mload(returndata)
                                      revert(add(32, returndata), returndata_size)
                                  }
                              } else {
                                  revert(errorMessage);
                              }
                          }
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // @unsupported: ovm
                  pragma solidity >0.5.0 <0.8.0;
                  pragma experimental ABIEncoderV2;
                  /* Interface Imports */
                  import { iOVM_L1StandardBridge } from "../../../iOVM/bridge/tokens/iOVM_L1StandardBridge.sol";
                  import { iOVM_L1ERC20Bridge } from "../../../iOVM/bridge/tokens/iOVM_L1ERC20Bridge.sol";
                  import { iOVM_L2ERC20Bridge } from "../../../iOVM/bridge/tokens/iOVM_L2ERC20Bridge.sol";
                  import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
                  /* Library Imports */
                  import { OVM_CrossDomainEnabled } from "../../../libraries/bridge/OVM_CrossDomainEnabled.sol";
                  import { Lib_PredeployAddresses } from "../../../libraries/constants/Lib_PredeployAddresses.sol";
                  import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
                  import { Address } from "@openzeppelin/contracts/utils/Address.sol";
                  import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
                  /**
                   * @title OVM_L1StandardBridge
                   * @dev The L1 ETH and ERC20 Bridge is a contract which stores deposited L1 funds and standard tokens that are in use on L2.
                   * It synchronizes a corresponding L2 Bridge, informing it of deposits, and listening to it for newly finalized withdrawals.
                   *
                   * Compiler used: solc
                   * Runtime target: EVM
                   */
                  contract OVM_L1StandardBridge is iOVM_L1StandardBridge, OVM_CrossDomainEnabled {
                      using SafeMath for uint;
                      using SafeERC20 for IERC20;
                      /********************************
                       * External Contract References *
                       ********************************/
                      address public l2TokenBridge;
                      // Maps L1 token to L2 token to balance of the L1 token deposited
                      mapping(address => mapping (address => uint256)) public deposits;
                      /***************
                       * Constructor *
                       ***************/
                      // This contract lives behind a proxy, so the constructor parameters will go unused.
                      constructor()
                          OVM_CrossDomainEnabled(address(0))
                      {}
                      /******************
                       * Initialization *
                       ******************/
                      /**
                       * @param _l1messenger L1 Messenger address being used for cross-chain communications.
                       * @param _l2TokenBridge L2 standard bridge address.
                       */
                      function initialize(
                          address _l1messenger,
                          address _l2TokenBridge
                      )
                          public
                      {
                          require(messenger == address(0), "Contract has already been initialized.");
                          messenger = _l1messenger;
                          l2TokenBridge = _l2TokenBridge;
                      }
                      /**************
                       * Depositing *
                       **************/
                      /// @dev Modifier requiring sender to be EOA.  This check could be bypassed by a malicious contract via initcode, but it takes care of the user error we want to avoid.
                      modifier onlyEOA() {
                          // Used to stop deposits from contracts (avoid accidentally lost tokens)
                          require(!Address.isContract(msg.sender), "Account not EOA");
                          _;
                      }
                      /**
                       * @dev This function can be called with no data
                       * to deposit an amount of ETH to the caller's balance on L2.
                       * Since the receive function doesn't take data, a conservative
                       * default amount is forwarded to L2.
                       */
                      receive()
                          external
                          payable
                          onlyEOA()
                      {
                          _initiateETHDeposit(
                              msg.sender,
                              msg.sender,
                              1_300_000,
                              bytes("")
                          );
                      }
                      /**
                       * @inheritdoc iOVM_L1StandardBridge
                       */
                      function depositETH(
                          uint32 _l2Gas,
                          bytes calldata _data
                      )
                          external
                          override
                          payable
                          onlyEOA()
                      {
                          _initiateETHDeposit(
                              msg.sender,
                              msg.sender,
                              _l2Gas,
                              _data
                          );
                      }
                      /**
                       * @inheritdoc iOVM_L1StandardBridge
                       */
                      function depositETHTo(
                          address _to,
                          uint32 _l2Gas,
                          bytes calldata _data
                      )
                          external
                          override
                          payable
                      {
                          _initiateETHDeposit(
                              msg.sender,
                              _to,
                              _l2Gas,
                              _data
                          );
                      }
                      /**
                       * @dev Performs the logic for deposits by storing the ETH and informing the L2 ETH Gateway of the deposit.
                       * @param _from Account to pull the deposit from on L1.
                       * @param _to Account to give the deposit to on L2.
                       * @param _l2Gas Gas limit required to complete the deposit on L2.
                       * @param _data Optional data to forward to L2. This data is provided
                       *        solely as a convenience for external contracts. Aside from enforcing a maximum
                       *        length, these contracts provide no guarantees about its content.
                       */
                      function _initiateETHDeposit(
                          address _from,
                          address _to,
                          uint32 _l2Gas,
                          bytes memory _data
                      )
                          internal
                      {
                          // Construct calldata for finalizeDeposit call
                          bytes memory message =
                              abi.encodeWithSelector(
                                  iOVM_L2ERC20Bridge.finalizeDeposit.selector,
                                  address(0),
                                  Lib_PredeployAddresses.OVM_ETH,
                                  _from,
                                  _to,
                                  msg.value,
                                  _data
                              );
                          // Send calldata into L2
                          sendCrossDomainMessage(
                              l2TokenBridge,
                              _l2Gas,
                              message
                          );
                          emit ETHDepositInitiated(_from, _to, msg.value, _data);
                      }
                      /**
                       * @inheritdoc iOVM_L1ERC20Bridge
                       */
                      function depositERC20(
                          address _l1Token,
                          address _l2Token,
                          uint256 _amount,
                          uint32 _l2Gas,
                          bytes calldata _data
                      )
                          external
                          override
                          virtual
                          onlyEOA()
                      {
                          _initiateERC20Deposit(_l1Token, _l2Token, msg.sender, msg.sender, _amount, _l2Gas, _data);
                      }
                       /**
                       * @inheritdoc iOVM_L1ERC20Bridge
                       */
                      function depositERC20To(
                          address _l1Token,
                          address _l2Token,
                          address _to,
                          uint256 _amount,
                          uint32 _l2Gas,
                          bytes calldata _data
                      )
                          external
                          override
                          virtual
                      {
                          _initiateERC20Deposit(_l1Token, _l2Token, msg.sender, _to, _amount, _l2Gas, _data);
                      }
                      /**
                       * @dev Performs the logic for deposits by informing the L2 Deposited Token
                       * contract of the deposit and calling a handler to lock the L1 funds. (e.g. transferFrom)
                       *
                       * @param _l1Token Address of the L1 ERC20 we are depositing
                       * @param _l2Token Address of the L1 respective L2 ERC20
                       * @param _from Account to pull the deposit from on L1
                       * @param _to Account to give the deposit to on L2
                       * @param _amount Amount of the ERC20 to deposit.
                       * @param _l2Gas Gas limit required to complete the deposit on L2.
                       * @param _data Optional data to forward to L2. This data is provided
                       *        solely as a convenience for external contracts. Aside from enforcing a maximum
                       *        length, these contracts provide no guarantees about its content.
                       */
                      function _initiateERC20Deposit(
                          address _l1Token,
                          address _l2Token,
                          address _from,
                          address _to,
                          uint256 _amount,
                          uint32 _l2Gas,
                          bytes calldata _data
                      )
                          internal
                      {
                          // When a deposit is initiated on L1, the L1 Bridge transfers the funds to itself for future withdrawals.
                          // safeTransferFrom also checks if the contract has code, so this will fail if _from is an EOA or address(0).
                          IERC20(_l1Token).safeTransferFrom(
                              _from,
                              address(this),
                              _amount
                          );
                          // Construct calldata for _l2Token.finalizeDeposit(_to, _amount)
                          bytes memory message = abi.encodeWithSelector(
                              iOVM_L2ERC20Bridge.finalizeDeposit.selector,
                              _l1Token,
                              _l2Token,
                              _from,
                              _to,
                              _amount,
                              _data
                          );
                          // Send calldata into L2
                          sendCrossDomainMessage(
                              l2TokenBridge,
                              _l2Gas,
                              message
                          );
                          deposits[_l1Token][_l2Token] = deposits[_l1Token][_l2Token].add(_amount);
                          emit ERC20DepositInitiated(_l1Token, _l2Token, _from, _to, _amount, _data);
                      }
                      /*************************
                       * Cross-chain Functions *
                       *************************/
                       /**
                       * @inheritdoc iOVM_L1StandardBridge
                       */
                      function finalizeETHWithdrawal(
                          address _from,
                          address _to,
                          uint256 _amount,
                          bytes calldata _data
                      )
                          external
                          override
                          onlyFromCrossDomainAccount(l2TokenBridge)
                      {
                          (bool success, ) = _to.call{value: _amount}(new bytes(0));
                          require(success, 'TransferHelper::safeTransferETH: ETH transfer failed');
                          emit ETHWithdrawalFinalized(_from, _to, _amount, _data);
                      }
                      /**
                       * @inheritdoc iOVM_L1ERC20Bridge
                       */
                      function finalizeERC20Withdrawal(
                          address _l1Token,
                          address _l2Token,
                          address _from,
                          address _to,
                          uint256 _amount,
                          bytes calldata _data
                      )
                          external
                          override
                          onlyFromCrossDomainAccount(l2TokenBridge)
                      {
                          deposits[_l1Token][_l2Token] = deposits[_l1Token][_l2Token].sub(_amount);
                          // When a withdrawal is finalized on L1, the L1 Bridge transfers the funds to the withdrawer.
                          IERC20(_l1Token).safeTransfer(_to, _amount);
                          emit ERC20WithdrawalFinalized(_l1Token, _l2Token, _from, _to, _amount, _data);
                      }
                      /*****************************
                       * Temporary - Migrating ETH *
                       *****************************/
                      /**
                       * @dev Adds ETH balance to the account. This is meant to allow for ETH
                       * to be migrated from an old gateway to a new gateway.
                       * NOTE: This is left for one upgrade only so we are able to receive the migrated ETH from the old contract
                       */
                      function donateETH() external payable {}
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  pragma experimental ABIEncoderV2;
                  /**
                   * @title iOVM_CrossDomainMessenger
                   */
                  interface iOVM_CrossDomainMessenger {
                      /**********
                       * Events *
                       **********/
                      event SentMessage(bytes message);
                      event RelayedMessage(bytes32 msgHash);
                      event FailedRelayedMessage(bytes32 msgHash);
                      /*************
                       * Variables *
                       *************/
                      function xDomainMessageSender() external view returns (address);
                      /********************
                       * Public Functions *
                       ********************/
                      /**
                       * Sends a cross domain message to the target messenger.
                       * @param _target Target contract address.
                       * @param _message Message to send to the target.
                       * @param _gasLimit Gas limit for the provided message.
                       */
                      function sendMessage(
                          address _target,
                          bytes calldata _message,
                          uint32 _gasLimit
                      ) external;
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0;
                  pragma experimental ABIEncoderV2;
                  /**
                   * @title iOVM_L1ERC20Bridge
                   */
                  interface iOVM_L1ERC20Bridge {
                      /**********
                       * Events *
                       **********/
                      event ERC20DepositInitiated (
                          address indexed _l1Token,
                          address indexed _l2Token,
                          address indexed _from,
                          address _to,
                          uint256 _amount,
                          bytes _data
                      );
                      event ERC20WithdrawalFinalized (
                          address indexed _l1Token,
                          address indexed _l2Token,
                          address indexed _from,
                          address _to,
                          uint256 _amount,
                          bytes _data
                      );
                      /********************
                       * Public Functions *
                       ********************/
                      /**
                       * @dev deposit an amount of the ERC20 to the caller's balance on L2.
                       * @param _l1Token Address of the L1 ERC20 we are depositing
                       * @param _l2Token Address of the L1 respective L2 ERC20
                       * @param _amount Amount of the ERC20 to deposit
                       * @param _l2Gas Gas limit required to complete the deposit on L2.
                       * @param _data Optional data to forward to L2. This data is provided
                       *        solely as a convenience for external contracts. Aside from enforcing a maximum
                       *        length, these contracts provide no guarantees about its content.
                       */
                      function depositERC20 (
                          address _l1Token,
                          address _l2Token,
                          uint _amount,
                          uint32 _l2Gas,
                          bytes calldata _data
                      )
                          external;
                      /**
                       * @dev deposit an amount of ERC20 to a recipient's balance on L2.
                       * @param _l1Token Address of the L1 ERC20 we are depositing
                       * @param _l2Token Address of the L1 respective L2 ERC20
                       * @param _to L2 address to credit the withdrawal to.
                       * @param _amount Amount of the ERC20 to deposit.
                       * @param _l2Gas Gas limit required to complete the deposit on L2.
                       * @param _data Optional data to forward to L2. This data is provided
                       *        solely as a convenience for external contracts. Aside from enforcing a maximum
                       *        length, these contracts provide no guarantees about its content.
                       */
                      function depositERC20To (
                          address _l1Token,
                          address _l2Token,
                          address _to,
                          uint _amount,
                          uint32 _l2Gas,
                          bytes calldata _data
                      )
                          external;
                      /*************************
                       * Cross-chain Functions *
                       *************************/
                      /**
                       * @dev Complete a withdrawal from L2 to L1, and credit funds to the recipient's balance of the
                       * L1 ERC20 token.
                       * This call will fail if the initialized withdrawal from L2 has not been finalized.
                       *
                       * @param _l1Token Address of L1 token to finalizeWithdrawal for.
                       * @param _l2Token Address of L2 token where withdrawal was initiated.
                       * @param _from L2 address initiating the transfer.
                       * @param _to L1 address to credit the withdrawal to.
                       * @param _amount Amount of the ERC20 to deposit.
                       * @param _data Data provided by the sender on L2. This data is provided
                       *   solely as a convenience for external contracts. Aside from enforcing a maximum
                       *   length, these contracts provide no guarantees about its content.
                       */
                      function finalizeERC20Withdrawal (
                          address _l1Token,
                          address _l2Token,
                          address _from,
                          address _to,
                          uint _amount,
                          bytes calldata _data
                      )
                          external;
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0;
                  pragma experimental ABIEncoderV2;
                  import './iOVM_L1ERC20Bridge.sol';
                  /**
                   * @title iOVM_L1StandardBridge
                   */
                  interface iOVM_L1StandardBridge is iOVM_L1ERC20Bridge {
                      /**********
                       * Events *
                       **********/
                      event ETHDepositInitiated (
                          address indexed _from,
                          address indexed _to,
                          uint256 _amount,
                          bytes _data
                      );
                      event ETHWithdrawalFinalized (
                          address indexed _from,
                          address indexed _to,
                          uint256 _amount,
                          bytes _data
                      );
                      /********************
                       * Public Functions *
                       ********************/
                       
                      /**
                       * @dev Deposit an amount of the ETH to the caller's balance on L2.
                       * @param _l2Gas Gas limit required to complete the deposit on L2.
                       * @param _data Optional data to forward to L2. This data is provided
                       *        solely as a convenience for external contracts. Aside from enforcing a maximum
                       *        length, these contracts provide no guarantees about its content.
                       */
                      function depositETH (
                          uint32 _l2Gas,
                          bytes calldata _data
                      )
                          external
                          payable;
                      /**
                       * @dev Deposit an amount of ETH to a recipient's balance on L2.
                       * @param _to L2 address to credit the withdrawal to.
                       * @param _l2Gas Gas limit required to complete the deposit on L2.
                       * @param _data Optional data to forward to L2. This data is provided
                       *        solely as a convenience for external contracts. Aside from enforcing a maximum
                       *        length, these contracts provide no guarantees about its content.
                       */
                      function depositETHTo (
                          address _to,
                          uint32 _l2Gas,
                          bytes calldata _data
                      )
                          external
                          payable;
                      /*************************
                       * Cross-chain Functions *
                       *************************/
                      /**
                       * @dev Complete a withdrawal from L2 to L1, and credit funds to the recipient's balance of the
                       * L1 ETH token.
                       * Since only the xDomainMessenger can call this function, it will never be called before the withdrawal is finalized.
                       * @param _from L2 address initiating the transfer.
                       * @param _to L1 address to credit the withdrawal to.
                       * @param _amount Amount of the ERC20 to deposit.
                       * @param _data Optional data to forward to L2. This data is provided
                       *        solely as a convenience for external contracts. Aside from enforcing a maximum
                       *        length, these contracts provide no guarantees about its content.
                       */
                      function finalizeETHWithdrawal (
                          address _from,
                          address _to,
                          uint _amount,
                          bytes calldata _data
                      )
                          external;
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0;
                  pragma experimental ABIEncoderV2;
                  /**
                   * @title iOVM_L2ERC20Bridge
                   */
                  interface iOVM_L2ERC20Bridge {
                      /**********
                       * Events *
                       **********/
                      event WithdrawalInitiated (
                          address indexed _l1Token,
                          address indexed _l2Token,
                          address indexed _from,
                          address _to,
                          uint256 _amount,
                          bytes _data
                      );
                      event DepositFinalized (
                          address indexed _l1Token,
                          address indexed _l2Token,
                          address indexed _from,
                          address _to,
                          uint256 _amount,
                          bytes _data
                      );
                      event DepositFailed (
                          address indexed _l1Token,
                          address indexed _l2Token,
                          address indexed _from,
                          address _to,
                          uint256 _amount,
                          bytes _data
                      );
                      /********************
                       * Public Functions *
                       ********************/
                      /**
                       * @dev initiate a withdraw of some tokens to the caller's account on L1
                       * @param _l2Token Address of L2 token where withdrawal was initiated.
                       * @param _amount Amount of the token to withdraw.
                       * param _l1Gas Unused, but included for potential forward compatibility considerations.
                       * @param _data Optional data to forward to L1. This data is provided
                       *        solely as a convenience for external contracts. Aside from enforcing a maximum
                       *        length, these contracts provide no guarantees about its content.
                       */
                      function withdraw (
                          address _l2Token,
                          uint _amount,
                          uint32 _l1Gas,
                          bytes calldata _data
                      )
                          external;
                      /**
                       * @dev initiate a withdraw of some token to a recipient's account on L1.
                       * @param _l2Token Address of L2 token where withdrawal is initiated.
                       * @param _to L1 adress to credit the withdrawal to.
                       * @param _amount Amount of the token to withdraw.
                       * param _l1Gas Unused, but included for potential forward compatibility considerations.
                       * @param _data Optional data to forward to L1. This data is provided
                       *        solely as a convenience for external contracts. Aside from enforcing a maximum
                       *        length, these contracts provide no guarantees about its content.
                       */
                      function withdrawTo (
                          address _l2Token,
                          address _to,
                          uint _amount,
                          uint32 _l1Gas,
                          bytes calldata _data
                      )
                          external;
                      /*************************
                       * Cross-chain Functions *
                       *************************/
                      /**
                       * @dev Complete a deposit from L1 to L2, and credits funds to the recipient's balance of this
                       * L2 token.
                       * This call will fail if it did not originate from a corresponding deposit in OVM_l1TokenGateway.
                       * @param _l1Token Address for the l1 token this is called with
                       * @param _l2Token Address for the l2 token this is called with
                       * @param _from Account to pull the deposit from on L2.
                       * @param _to Address to receive the withdrawal at
                       * @param _amount Amount of the token to withdraw
                       * @param _data Data provider by the sender on L1. This data is provided
                       *        solely as a convenience for external contracts. Aside from enforcing a maximum
                       *        length, these contracts provide no guarantees about its content.
                       */
                      function finalizeDeposit (
                          address _l1Token,
                          address _l2Token,
                          address _from,
                          address _to,
                          uint _amount,
                          bytes calldata _data
                      )
                          external;
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  /* Interface Imports */
                  import { iOVM_CrossDomainMessenger } from "../../iOVM/bridge/messaging/iOVM_CrossDomainMessenger.sol";
                  /**
                   * @title OVM_CrossDomainEnabled
                   * @dev Helper contract for contracts performing cross-domain communications
                   *
                   * Compiler used: defined by inheriting contract
                   * Runtime target: defined by inheriting contract
                   */
                  contract OVM_CrossDomainEnabled {
                      /*************
                       * Variables *
                       *************/
                      // Messenger contract used to send and recieve messages from the other domain.
                      address public messenger;
                      /***************
                       * Constructor *
                       ***************/
                      /**
                       * @param _messenger Address of the CrossDomainMessenger on the current layer.
                       */
                      constructor(
                          address _messenger
                      ) {
                          messenger = _messenger;
                      }
                      /**********************
                       * Function Modifiers *
                       **********************/
                      /**
                       * Enforces that the modified function is only callable by a specific cross-domain account.
                       * @param _sourceDomainAccount The only account on the originating domain which is
                       *  authenticated to call this function.
                       */
                      modifier onlyFromCrossDomainAccount(
                          address _sourceDomainAccount
                      ) {
                          require(
                              msg.sender == address(getCrossDomainMessenger()),
                              "OVM_XCHAIN: messenger contract unauthenticated"
                          );
                          require(
                              getCrossDomainMessenger().xDomainMessageSender() == _sourceDomainAccount,
                              "OVM_XCHAIN: wrong sender of cross-domain message"
                          );
                          _;
                      }
                      /**********************
                       * Internal Functions *
                       **********************/
                      /**
                       * Gets the messenger, usually from storage. This function is exposed in case a child contract
                       * needs to override.
                       * @return The address of the cross-domain messenger contract which should be used.
                       */
                      function getCrossDomainMessenger()
                          internal
                          virtual
                          returns (
                              iOVM_CrossDomainMessenger
                          )
                      {
                          return iOVM_CrossDomainMessenger(messenger);
                      }
                      /**
                       * Sends a message to an account on another domain
                       * @param _crossDomainTarget The intended recipient on the destination domain
                       * @param _message The data to send to the target (usually calldata to a function with
                       *  `onlyFromCrossDomainAccount()`)
                       * @param _gasLimit The gasLimit for the receipt of the message on the target domain.
                       */
                      function sendCrossDomainMessage(
                          address _crossDomainTarget,
                          uint32 _gasLimit,
                          bytes memory _message
                      )
                          internal
                      {
                          getCrossDomainMessenger().sendMessage(_crossDomainTarget, _message, _gasLimit);
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  /**
                   * @title Lib_PredeployAddresses
                   */
                  library Lib_PredeployAddresses {
                      address internal constant L2_TO_L1_MESSAGE_PASSER = 0x4200000000000000000000000000000000000000;
                      address internal constant L1_MESSAGE_SENDER = 0x4200000000000000000000000000000000000001;
                      address internal constant DEPLOYER_WHITELIST = 0x4200000000000000000000000000000000000002;
                      address internal constant ECDSA_CONTRACT_ACCOUNT = 0x4200000000000000000000000000000000000003;
                      address internal constant SEQUENCER_ENTRYPOINT = 0x4200000000000000000000000000000000000005;
                      address payable internal constant OVM_ETH = 0x4200000000000000000000000000000000000006;
                      address internal constant L2_CROSS_DOMAIN_MESSENGER = 0x4200000000000000000000000000000000000007;
                      address internal constant LIB_ADDRESS_MANAGER = 0x4200000000000000000000000000000000000008;
                      address internal constant PROXY_EOA = 0x4200000000000000000000000000000000000009;
                      address internal constant EXECUTION_MANAGER_WRAPPER = 0x420000000000000000000000000000000000000B;
                      address internal constant SEQUENCER_FEE_WALLET = 0x4200000000000000000000000000000000000011;
                      address internal constant ERC1820_REGISTRY = 0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24;
                      address internal constant L2_STANDARD_BRIDGE = 0x4200000000000000000000000000000000000010;
                  }
                  

                  File 5 of 7: Lib_AddressManager
                  // SPDX-License-Identifier: MIT
                  pragma solidity >=0.6.0 <0.8.0;
                  import "../utils/Context.sol";
                  /**
                   * @dev Contract module which provides a basic access control mechanism, where
                   * there is an account (an owner) that can be granted exclusive access to
                   * specific functions.
                   *
                   * By default, the owner account will be the one that deploys the contract. This
                   * can later be changed with {transferOwnership}.
                   *
                   * This module is used through inheritance. It will make available the modifier
                   * `onlyOwner`, which can be applied to your functions to restrict their use to
                   * the owner.
                   */
                  abstract contract Ownable is Context {
                      address private _owner;
                      event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                      /**
                       * @dev Initializes the contract setting the deployer as the initial owner.
                       */
                      constructor () internal {
                          address msgSender = _msgSender();
                          _owner = msgSender;
                          emit OwnershipTransferred(address(0), msgSender);
                      }
                      /**
                       * @dev Returns the address of the current owner.
                       */
                      function owner() public view virtual returns (address) {
                          return _owner;
                      }
                      /**
                       * @dev Throws if called by any account other than the owner.
                       */
                      modifier onlyOwner() {
                          require(owner() == _msgSender(), "Ownable: caller is not the owner");
                          _;
                      }
                      /**
                       * @dev Leaves the contract without owner. It will not be possible to call
                       * `onlyOwner` functions anymore. Can only be called by the current owner.
                       *
                       * NOTE: Renouncing ownership will leave the contract without an owner,
                       * thereby removing any functionality that is only available to the owner.
                       */
                      function renounceOwnership() public virtual onlyOwner {
                          emit OwnershipTransferred(_owner, address(0));
                          _owner = 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");
                          emit OwnershipTransferred(_owner, newOwner);
                          _owner = newOwner;
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >=0.6.0 <0.8.0;
                  /*
                   * @dev Provides information about the current execution context, including the
                   * sender of the transaction and its data. While these are generally available
                   * via msg.sender and msg.data, they should not be accessed in such a direct
                   * manner, since when dealing with GSN meta-transactions the account sending and
                   * paying for execution may not be the actual sender (as far as an application
                   * is concerned).
                   *
                   * This contract is only required for intermediate, library-like contracts.
                   */
                  abstract contract Context {
                      function _msgSender() internal view virtual returns (address payable) {
                          return msg.sender;
                      }
                      function _msgData() internal view virtual returns (bytes memory) {
                          this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                          return msg.data;
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  /* External Imports */
                  import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
                  /**
                   * @title Lib_AddressManager
                   */
                  contract Lib_AddressManager is Ownable {
                      /**********
                       * Events *
                       **********/
                      event AddressSet(
                          string indexed _name,
                          address _newAddress,
                          address _oldAddress
                      );
                      /*************
                       * Variables *
                       *************/
                      mapping (bytes32 => address) private addresses;
                      /********************
                       * Public Functions *
                       ********************/
                      /**
                       * Changes the address associated with a particular name.
                       * @param _name String name to associate an address with.
                       * @param _address Address to associate with the name.
                       */
                      function setAddress(
                          string memory _name,
                          address _address
                      )
                          external
                          onlyOwner
                      {
                          bytes32 nameHash = _getNameHash(_name);
                          address oldAddress = addresses[nameHash];
                          addresses[nameHash] = _address;
                          emit AddressSet(
                              _name,
                              _address,
                              oldAddress
                          );
                      }
                      /**
                       * Retrieves the address associated with a given name.
                       * @param _name Name to retrieve an address for.
                       * @return Address associated with the given name.
                       */
                      function getAddress(
                          string memory _name
                      )
                          external
                          view
                          returns (
                              address
                          )
                      {
                          return addresses[_getNameHash(_name)];
                      }
                      /**********************
                       * Internal Functions *
                       **********************/
                      /**
                       * Computes the hash of a name.
                       * @param _name Name to compute a hash for.
                       * @return Hash of the given name.
                       */
                      function _getNameHash(
                          string memory _name
                      )
                          internal
                          pure
                          returns (
                              bytes32
                          )
                      {
                          return keccak256(abi.encodePacked(_name));
                      }
                  }
                  

                  File 6 of 7: OVM_L1CrossDomainMessenger
                  // SPDX-License-Identifier: MIT
                  pragma solidity >=0.6.0 <0.8.0;
                  import "../utils/ContextUpgradeable.sol";
                  import "../proxy/Initializable.sol";
                  /**
                   * @dev Contract module which provides a basic access control mechanism, where
                   * there is an account (an owner) that can be granted exclusive access to
                   * specific functions.
                   *
                   * By default, the owner account will be the one that deploys the contract. This
                   * can later be changed with {transferOwnership}.
                   *
                   * This module is used through inheritance. It will make available the modifier
                   * `onlyOwner`, which can be applied to your functions to restrict their use to
                   * the owner.
                   */
                  abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
                      address private _owner;
                      event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                      /**
                       * @dev Initializes the contract setting the deployer as the initial owner.
                       */
                      function __Ownable_init() internal initializer {
                          __Context_init_unchained();
                          __Ownable_init_unchained();
                      }
                      function __Ownable_init_unchained() internal initializer {
                          address msgSender = _msgSender();
                          _owner = msgSender;
                          emit OwnershipTransferred(address(0), msgSender);
                      }
                      /**
                       * @dev Returns the address of the current owner.
                       */
                      function owner() public view virtual returns (address) {
                          return _owner;
                      }
                      /**
                       * @dev Throws if called by any account other than the owner.
                       */
                      modifier onlyOwner() {
                          require(owner() == _msgSender(), "Ownable: caller is not the owner");
                          _;
                      }
                      /**
                       * @dev Leaves the contract without owner. It will not be possible to call
                       * `onlyOwner` functions anymore. Can only be called by the current owner.
                       *
                       * NOTE: Renouncing ownership will leave the contract without an owner,
                       * thereby removing any functionality that is only available to the owner.
                       */
                      function renounceOwnership() public virtual onlyOwner {
                          emit OwnershipTransferred(_owner, address(0));
                          _owner = 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");
                          emit OwnershipTransferred(_owner, newOwner);
                          _owner = newOwner;
                      }
                      uint256[49] private __gap;
                  }
                  // SPDX-License-Identifier: MIT
                  // solhint-disable-next-line compiler-version
                  pragma solidity >=0.4.24 <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 a proxied contract can't have 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 {UpgradeableProxy-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.
                   */
                  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() {
                          require(_initializing || _isConstructor() || !_initialized, "Initializable: contract is already initialized");
                          bool isTopLevelCall = !_initializing;
                          if (isTopLevelCall) {
                              _initializing = true;
                              _initialized = true;
                          }
                          _;
                          if (isTopLevelCall) {
                              _initializing = false;
                          }
                      }
                      /// @dev Returns true if and only if the function is running in the constructor
                      function _isConstructor() private view returns (bool) {
                          return !AddressUpgradeable.isContract(address(this));
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >=0.6.2 <0.8.0;
                  /**
                   * @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
                       * ====
                       */
                      function isContract(address account) internal view returns (bool) {
                          // This method relies on extcodesize, which returns 0 for contracts in
                          // construction, since the code is only stored at the end of the
                          // constructor execution.
                          uint256 size;
                          // solhint-disable-next-line no-inline-assembly
                          assembly { size := extcodesize(account) }
                          return size > 0;
                      }
                      /**
                       * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                       * `recipient`, forwarding all available gas and reverting on errors.
                       *
                       * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                       * of certain opcodes, possibly making contracts go over the 2300 gas limit
                       * imposed by `transfer`, making them unable to receive funds via
                       * `transfer`. {sendValue} removes this limitation.
                       *
                       * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                       *
                       * IMPORTANT: because control is transferred to `recipient`, care must be
                       * taken to not create reentrancy vulnerabilities. Consider using
                       * {ReentrancyGuard} or the
                       * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                       */
                      function sendValue(address payable recipient, uint256 amount) internal {
                          require(address(this).balance >= amount, "Address: insufficient balance");
                          // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                          (bool success, ) = recipient.call{ value: amount }("");
                          require(success, "Address: unable to send value, recipient may have reverted");
                      }
                      /**
                       * @dev Performs a Solidity function call using a low level `call`. A
                       * plain`call` is an unsafe replacement for a function call: use this
                       * function instead.
                       *
                       * If `target` reverts with a revert reason, it is bubbled up by this
                       * function (like regular Solidity function calls).
                       *
                       * Returns the raw returned data. To convert to the expected return value,
                       * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                       *
                       * Requirements:
                       *
                       * - `target` must be a contract.
                       * - calling `target` with `data` must not revert.
                       *
                       * _Available since v3.1._
                       */
                      function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                        return functionCall(target, data, "Address: low-level call failed");
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                       * `errorMessage` as a fallback revert reason when `target` reverts.
                       *
                       * _Available since v3.1._
                       */
                      function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                          return functionCallWithValue(target, data, 0, errorMessage);
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                       * but also transferring `value` wei to `target`.
                       *
                       * Requirements:
                       *
                       * - the calling contract must have an ETH balance of at least `value`.
                       * - the called Solidity function must be `payable`.
                       *
                       * _Available since v3.1._
                       */
                      function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                          return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                      }
                      /**
                       * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                       * with `errorMessage` as a fallback revert reason when `target` reverts.
                       *
                       * _Available since v3.1._
                       */
                      function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
                          require(address(this).balance >= value, "Address: insufficient balance for call");
                          require(isContract(target), "Address: call to non-contract");
                          // solhint-disable-next-line avoid-low-level-calls
                          (bool success, bytes memory returndata) = target.call{ value: value }(data);
                          return _verifyCallResult(success, returndata, errorMessage);
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                       * but performing a static call.
                       *
                       * _Available since v3.3._
                       */
                      function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                          return functionStaticCall(target, data, "Address: low-level static call failed");
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                       * but performing a static call.
                       *
                       * _Available since v3.3._
                       */
                      function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
                          require(isContract(target), "Address: static call to non-contract");
                          // solhint-disable-next-line avoid-low-level-calls
                          (bool success, bytes memory returndata) = target.staticcall(data);
                          return _verifyCallResult(success, returndata, errorMessage);
                      }
                      function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
                          if (success) {
                              return returndata;
                          } else {
                              // Look for revert reason and bubble it up if present
                              if (returndata.length > 0) {
                                  // The easiest way to bubble the revert reason is using memory via assembly
                                  // solhint-disable-next-line no-inline-assembly
                                  assembly {
                                      let returndata_size := mload(returndata)
                                      revert(add(32, returndata), returndata_size)
                                  }
                              } else {
                                  revert(errorMessage);
                              }
                          }
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >=0.6.0 <0.8.0;
                  import "../proxy/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 GSN meta-transactions the account sending and
                   * paying for execution may not be the actual sender (as far as an application
                   * is concerned).
                   *
                   * This contract is only required for intermediate, library-like contracts.
                   */
                  abstract contract ContextUpgradeable is Initializable {
                      function __Context_init() internal initializer {
                          __Context_init_unchained();
                      }
                      function __Context_init_unchained() internal initializer {
                      }
                      function _msgSender() internal view virtual returns (address payable) {
                          return msg.sender;
                      }
                      function _msgData() internal view virtual returns (bytes memory) {
                          this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                          return msg.data;
                      }
                      uint256[50] private __gap;
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >=0.6.0 <0.8.0;
                  import "./ContextUpgradeable.sol";
                  import "../proxy/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 initializer {
                          __Context_init_unchained();
                          __Pausable_init_unchained();
                      }
                      function __Pausable_init_unchained() internal initializer {
                          _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());
                      }
                      uint256[49] private __gap;
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >=0.6.0 <0.8.0;
                  import "../proxy/Initializable.sol";
                  /**
                   * @dev Contract module that helps prevent reentrant calls to a function.
                   *
                   * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
                   * available, which can be applied to functions to make sure there are no nested
                   * (reentrant) calls to them.
                   *
                   * Note that because there is a single `nonReentrant` guard, functions marked as
                   * `nonReentrant` may not call one another. This can be worked around by making
                   * those functions `private`, and then adding `external` `nonReentrant` entry
                   * points to them.
                   *
                   * TIP: If you would like to learn more about reentrancy and alternative ways
                   * to protect against it, check out our blog post
                   * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
                   */
                  abstract contract ReentrancyGuardUpgradeable is Initializable {
                      // Booleans are more expensive than uint256 or any type that takes up a full
                      // word because each write operation emits an extra SLOAD to first read the
                      // slot's contents, replace the bits taken up by the boolean, and then write
                      // back. This is the compiler's defense against contract upgrades and
                      // pointer aliasing, and it cannot be disabled.
                      // The values being non-zero value makes deployment a bit more expensive,
                      // but in exchange the refund on every call to nonReentrant will be lower in
                      // amount. Since refunds are capped to a percentage of the total
                      // transaction's gas, it is best to keep them low in cases like this one, to
                      // increase the likelihood of the full refund coming into effect.
                      uint256 private constant _NOT_ENTERED = 1;
                      uint256 private constant _ENTERED = 2;
                      uint256 private _status;
                      function __ReentrancyGuard_init() internal initializer {
                          __ReentrancyGuard_init_unchained();
                      }
                      function __ReentrancyGuard_init_unchained() internal initializer {
                          _status = _NOT_ENTERED;
                      }
                      /**
                       * @dev Prevents a contract from calling itself, directly or indirectly.
                       * Calling a `nonReentrant` function from another `nonReentrant`
                       * function is not supported. It is possible to prevent this from happening
                       * by making the `nonReentrant` function external, and make it call a
                       * `private` function that does the actual work.
                       */
                      modifier nonReentrant() {
                          // On the first call to nonReentrant, _notEntered will be true
                          require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
                          // Any calls to nonReentrant after this point will fail
                          _status = _ENTERED;
                          _;
                          // By storing the original value once again, a refund is triggered (see
                          // https://eips.ethereum.org/EIPS/eip-2200)
                          _status = _NOT_ENTERED;
                      }
                      uint256[49] private __gap;
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >=0.6.0 <0.8.0;
                  import "../utils/Context.sol";
                  /**
                   * @dev Contract module which provides a basic access control mechanism, where
                   * there is an account (an owner) that can be granted exclusive access to
                   * specific functions.
                   *
                   * By default, the owner account will be the one that deploys the contract. This
                   * can later be changed with {transferOwnership}.
                   *
                   * This module is used through inheritance. It will make available the modifier
                   * `onlyOwner`, which can be applied to your functions to restrict their use to
                   * the owner.
                   */
                  abstract contract Ownable is Context {
                      address private _owner;
                      event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                      /**
                       * @dev Initializes the contract setting the deployer as the initial owner.
                       */
                      constructor () internal {
                          address msgSender = _msgSender();
                          _owner = msgSender;
                          emit OwnershipTransferred(address(0), msgSender);
                      }
                      /**
                       * @dev Returns the address of the current owner.
                       */
                      function owner() public view virtual returns (address) {
                          return _owner;
                      }
                      /**
                       * @dev Throws if called by any account other than the owner.
                       */
                      modifier onlyOwner() {
                          require(owner() == _msgSender(), "Ownable: caller is not the owner");
                          _;
                      }
                      /**
                       * @dev Leaves the contract without owner. It will not be possible to call
                       * `onlyOwner` functions anymore. Can only be called by the current owner.
                       *
                       * NOTE: Renouncing ownership will leave the contract without an owner,
                       * thereby removing any functionality that is only available to the owner.
                       */
                      function renounceOwnership() public virtual onlyOwner {
                          emit OwnershipTransferred(_owner, address(0));
                          _owner = 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");
                          emit OwnershipTransferred(_owner, newOwner);
                          _owner = newOwner;
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >=0.6.0 <0.8.0;
                  /*
                   * @dev Provides information about the current execution context, including the
                   * sender of the transaction and its data. While these are generally available
                   * via msg.sender and msg.data, they should not be accessed in such a direct
                   * manner, since when dealing with GSN meta-transactions the account sending and
                   * paying for execution may not be the actual sender (as far as an application
                   * is concerned).
                   *
                   * This contract is only required for intermediate, library-like contracts.
                   */
                  abstract contract Context {
                      function _msgSender() internal view virtual returns (address payable) {
                          return msg.sender;
                      }
                      function _msgData() internal view virtual returns (bytes memory) {
                          this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                          return msg.data;
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  pragma experimental ABIEncoderV2;
                  /* Library Imports */
                  import { Lib_AddressResolver } from "../../../libraries/resolver/Lib_AddressResolver.sol";
                  import { Lib_OVMCodec } from "../../../libraries/codec/Lib_OVMCodec.sol";
                  import { Lib_AddressManager } from "../../../libraries/resolver/Lib_AddressManager.sol";
                  import { Lib_SecureMerkleTrie } from "../../../libraries/trie/Lib_SecureMerkleTrie.sol";
                  import { Lib_PredeployAddresses } from "../../../libraries/constants/Lib_PredeployAddresses.sol";
                  import { Lib_CrossDomainUtils } from "../../../libraries/bridge/Lib_CrossDomainUtils.sol";
                  /* Interface Imports */
                  import { iOVM_L1CrossDomainMessenger } from "../../../iOVM/bridge/messaging/iOVM_L1CrossDomainMessenger.sol";
                  import { iOVM_CanonicalTransactionChain } from "../../../iOVM/chain/iOVM_CanonicalTransactionChain.sol";
                  import { iOVM_StateCommitmentChain } from "../../../iOVM/chain/iOVM_StateCommitmentChain.sol";
                  /* External Imports */
                  import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
                  import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
                  import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
                  /**
                   * @title OVM_L1CrossDomainMessenger
                   * @dev The L1 Cross Domain Messenger contract sends messages from L1 to L2, and relays messages
                   * from L2 onto L1. In the event that a message sent from L1 to L2 is rejected for exceeding the L2
                   * epoch gas limit, it can be resubmitted via this contract's replay function.
                   *
                   * Compiler used: solc
                   * Runtime target: EVM
                   */
                  contract OVM_L1CrossDomainMessenger is
                          iOVM_L1CrossDomainMessenger,
                          Lib_AddressResolver,
                          OwnableUpgradeable,
                          PausableUpgradeable,
                          ReentrancyGuardUpgradeable
                  {
                      /**********
                       * Events *
                       **********/
                      event MessageBlocked(
                          bytes32 indexed _xDomainCalldataHash
                      );
                      event MessageAllowed(
                          bytes32 indexed _xDomainCalldataHash
                      );
                      /*************
                       * Constants *
                       *************/
                      // The default x-domain message sender being set to a non-zero value makes
                      // deployment a bit more expensive, but in exchange the refund on every call to
                      // `relayMessage` by the L1 and L2 messengers will be higher.
                      address internal constant DEFAULT_XDOMAIN_SENDER = 0x000000000000000000000000000000000000dEaD;
                      /**********************
                       * Contract Variables *
                       **********************/
                      mapping (bytes32 => bool) public blockedMessages;
                      mapping (bytes32 => bool) public relayedMessages;
                      mapping (bytes32 => bool) public successfulMessages;
                      address internal xDomainMsgSender = DEFAULT_XDOMAIN_SENDER;
                      /***************
                       * Constructor *
                       ***************/
                      /**
                       * This contract is intended to be behind a delegate proxy.
                       * We pass the zero address to the address resolver just to satisfy the constructor.
                       * We still need to set this value in initialize().
                       */
                      constructor()
                          Lib_AddressResolver(address(0))
                      {}
                      /**********************
                       * Function Modifiers *
                       **********************/
                      /**
                       * Modifier to enforce that, if configured, only the OVM_L2MessageRelayer contract may
                       * successfully call a method.
                       */
                      modifier onlyRelayer() {
                          address relayer = resolve("OVM_L2MessageRelayer");
                          if (relayer != address(0)) {
                              require(
                                  msg.sender == relayer,
                                  "Only OVM_L2MessageRelayer can relay L2-to-L1 messages."
                              );
                          }
                          _;
                      }
                      /********************
                       * Public Functions *
                       ********************/
                      /**
                       * @param _libAddressManager Address of the Address Manager.
                       */
                      function initialize(
                          address _libAddressManager
                      )
                          public
                          initializer
                      {
                          require(
                              address(libAddressManager) == address(0),
                              "L1CrossDomainMessenger already intialized."
                          );
                          libAddressManager = Lib_AddressManager(_libAddressManager);
                          xDomainMsgSender = DEFAULT_XDOMAIN_SENDER;
                          // Initialize upgradable OZ contracts
                          __Context_init_unchained(); // Context is a dependency for both Ownable and Pausable
                          __Ownable_init_unchained();
                          __Pausable_init_unchained();
                          __ReentrancyGuard_init_unchained();
                      }
                      /**
                       * Pause relaying.
                       */
                      function pause()
                          external
                          onlyOwner
                      {
                          _pause();
                      }
                      /**
                       * Block a message.
                       * @param _xDomainCalldataHash Hash of the message to block.
                       */
                      function blockMessage(
                          bytes32 _xDomainCalldataHash
                      )
                          external
                          onlyOwner
                      {
                          blockedMessages[_xDomainCalldataHash] = true;
                          emit MessageBlocked(_xDomainCalldataHash);
                      }
                      /**
                       * Allow a message.
                       * @param _xDomainCalldataHash Hash of the message to block.
                       */
                      function allowMessage(
                          bytes32 _xDomainCalldataHash
                      )
                          external
                          onlyOwner
                      {
                          blockedMessages[_xDomainCalldataHash] = false;
                          emit MessageAllowed(_xDomainCalldataHash);
                      }
                      function xDomainMessageSender()
                          public
                          override
                          view
                          returns (
                              address
                          )
                      {
                          require(xDomainMsgSender != DEFAULT_XDOMAIN_SENDER, "xDomainMessageSender is not set");
                          return xDomainMsgSender;
                      }
                      /**
                       * Sends a cross domain message to the target messenger.
                       * @param _target Target contract address.
                       * @param _message Message to send to the target.
                       * @param _gasLimit Gas limit for the provided message.
                       */
                      function sendMessage(
                          address _target,
                          bytes memory _message,
                          uint32 _gasLimit
                      )
                          override
                          public
                      {
                          address ovmCanonicalTransactionChain = resolve("OVM_CanonicalTransactionChain");
                          // Use the CTC queue length as nonce
                          uint40 nonce = iOVM_CanonicalTransactionChain(ovmCanonicalTransactionChain).getQueueLength();
                          bytes memory xDomainCalldata = Lib_CrossDomainUtils.encodeXDomainCalldata(
                              _target,
                              msg.sender,
                              _message,
                              nonce
                          );
                          address l2CrossDomainMessenger = resolve("OVM_L2CrossDomainMessenger");
                          _sendXDomainMessage(ovmCanonicalTransactionChain, l2CrossDomainMessenger, xDomainCalldata, _gasLimit);
                          emit SentMessage(xDomainCalldata);
                      }
                      /**
                       * Relays a cross domain message to a contract.
                       * @inheritdoc iOVM_L1CrossDomainMessenger
                       */
                      function relayMessage(
                          address _target,
                          address _sender,
                          bytes memory _message,
                          uint256 _messageNonce,
                          L2MessageInclusionProof memory _proof
                      )
                          override
                          public
                          nonReentrant
                          onlyRelayer
                          whenNotPaused
                      {
                          bytes memory xDomainCalldata = Lib_CrossDomainUtils.encodeXDomainCalldata(
                              _target,
                              _sender,
                              _message,
                              _messageNonce
                          );
                          require(
                              _verifyXDomainMessage(
                                  xDomainCalldata,
                                  _proof
                              ) == true,
                              "Provided message could not be verified."
                          );
                          bytes32 xDomainCalldataHash = keccak256(xDomainCalldata);
                          require(
                              successfulMessages[xDomainCalldataHash] == false,
                              "Provided message has already been received."
                          );
                          require(
                              blockedMessages[xDomainCalldataHash] == false,
                              "Provided message has been blocked."
                          );
                          require(
                              _target != resolve("OVM_CanonicalTransactionChain"),
                              "Cannot send L2->L1 messages to L1 system contracts."
                          );
                          xDomainMsgSender = _sender;
                          (bool success, ) = _target.call(_message);
                          xDomainMsgSender = DEFAULT_XDOMAIN_SENDER;
                          // Mark the message as received if the call was successful. Ensures that a message can be
                          // relayed multiple times in the case that the call reverted.
                          if (success == true) {
                              successfulMessages[xDomainCalldataHash] = true;
                              emit RelayedMessage(xDomainCalldataHash);
                          } else {
                              emit FailedRelayedMessage(xDomainCalldataHash);
                          }
                          // Store an identifier that can be used to prove that the given message was relayed by some
                          // user. Gives us an easy way to pay relayers for their work.
                          bytes32 relayId = keccak256(
                              abi.encodePacked(
                                  xDomainCalldata,
                                  msg.sender,
                                  block.number
                              )
                          );
                          relayedMessages[relayId] = true;
                      }
                      /**
                       * Replays a cross domain message to the target messenger.
                       * @inheritdoc iOVM_L1CrossDomainMessenger
                       */
                      function replayMessage(
                          address _target,
                          address _sender,
                          bytes memory _message,
                          uint256 _queueIndex,
                          uint32 _gasLimit
                      )
                          override
                          public
                      {
                          // Verify that the message is in the queue:
                          address canonicalTransactionChain = resolve("OVM_CanonicalTransactionChain");
                          Lib_OVMCodec.QueueElement memory element = iOVM_CanonicalTransactionChain(canonicalTransactionChain).getQueueElement(_queueIndex);
                          address l2CrossDomainMessenger = resolve("OVM_L2CrossDomainMessenger");
                          // Compute the transactionHash
                          bytes32 transactionHash = keccak256(
                              abi.encode(
                                  address(this),
                                  l2CrossDomainMessenger,
                                  _gasLimit,
                                  _message
                              )
                          );
                          require(
                              transactionHash == element.transactionHash,
                              "Provided message has not been enqueued."
                          );
                          bytes memory xDomainCalldata = Lib_CrossDomainUtils.encodeXDomainCalldata(
                              _target,
                              _sender,
                              _message,
                              _queueIndex
                          );
                          _sendXDomainMessage(canonicalTransactionChain, l2CrossDomainMessenger, xDomainCalldata, _gasLimit);
                      }
                      /**********************
                       * Internal Functions *
                       **********************/
                      /**
                       * Verifies that the given message is valid.
                       * @param _xDomainCalldata Calldata to verify.
                       * @param _proof Inclusion proof for the message.
                       * @return Whether or not the provided message is valid.
                       */
                      function _verifyXDomainMessage(
                          bytes memory _xDomainCalldata,
                          L2MessageInclusionProof memory _proof
                      )
                          internal
                          view
                          returns (
                              bool
                          )
                      {
                          return (
                              _verifyStateRootProof(_proof)
                              && _verifyStorageProof(_xDomainCalldata, _proof)
                          );
                      }
                      /**
                       * Verifies that the state root within an inclusion proof is valid.
                       * @param _proof Message inclusion proof.
                       * @return Whether or not the provided proof is valid.
                       */
                      function _verifyStateRootProof(
                          L2MessageInclusionProof memory _proof
                      )
                          internal
                          view
                          returns (
                              bool
                          )
                      {
                          iOVM_StateCommitmentChain ovmStateCommitmentChain = iOVM_StateCommitmentChain(
                              resolve("OVM_StateCommitmentChain")
                          );
                          return (
                              ovmStateCommitmentChain.insideFraudProofWindow(_proof.stateRootBatchHeader) == false
                              && ovmStateCommitmentChain.verifyStateCommitment(
                                  _proof.stateRoot,
                                  _proof.stateRootBatchHeader,
                                  _proof.stateRootProof
                              )
                          );
                      }
                      /**
                       * Verifies that the storage proof within an inclusion proof is valid.
                       * @param _xDomainCalldata Encoded message calldata.
                       * @param _proof Message inclusion proof.
                       * @return Whether or not the provided proof is valid.
                       */
                      function _verifyStorageProof(
                          bytes memory _xDomainCalldata,
                          L2MessageInclusionProof memory _proof
                      )
                          internal
                          view
                          returns (
                              bool
                          )
                      {
                          bytes32 storageKey = keccak256(
                              abi.encodePacked(
                                  keccak256(
                                      abi.encodePacked(
                                          _xDomainCalldata,
                                          resolve("OVM_L2CrossDomainMessenger")
                                      )
                                  ),
                                  uint256(0)
                              )
                          );
                          (
                              bool exists,
                              bytes memory encodedMessagePassingAccount
                          ) = Lib_SecureMerkleTrie.get(
                              abi.encodePacked(Lib_PredeployAddresses.L2_TO_L1_MESSAGE_PASSER),
                              _proof.stateTrieWitness,
                              _proof.stateRoot
                          );
                          require(
                              exists == true,
                              "Message passing predeploy has not been initialized or invalid proof provided."
                          );
                          Lib_OVMCodec.EVMAccount memory account = Lib_OVMCodec.decodeEVMAccount(
                              encodedMessagePassingAccount
                          );
                          return Lib_SecureMerkleTrie.verifyInclusionProof(
                              abi.encodePacked(storageKey),
                              abi.encodePacked(uint8(1)),
                              _proof.storageTrieWitness,
                              account.storageRoot
                          );
                      }
                      /**
                       * Sends a cross domain message.
                       * @param _canonicalTransactionChain Address of the OVM_CanonicalTransactionChain instance.
                       * @param _l2CrossDomainMessenger Address of the OVM_L2CrossDomainMessenger instance.
                       * @param _message Message to send.
                       * @param _gasLimit OVM gas limit for the message.
                       */
                      function _sendXDomainMessage(
                          address _canonicalTransactionChain,
                          address _l2CrossDomainMessenger,
                          bytes memory _message,
                          uint256 _gasLimit
                      )
                          internal
                      {
                          iOVM_CanonicalTransactionChain(_canonicalTransactionChain).enqueue(
                              _l2CrossDomainMessenger,
                              _gasLimit,
                              _message
                          );
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  pragma experimental ABIEncoderV2;
                  /**
                   * @title iOVM_CrossDomainMessenger
                   */
                  interface iOVM_CrossDomainMessenger {
                      /**********
                       * Events *
                       **********/
                      event SentMessage(bytes message);
                      event RelayedMessage(bytes32 msgHash);
                      event FailedRelayedMessage(bytes32 msgHash);
                      /*************
                       * Variables *
                       *************/
                      function xDomainMessageSender() external view returns (address);
                      /********************
                       * Public Functions *
                       ********************/
                      /**
                       * Sends a cross domain message to the target messenger.
                       * @param _target Target contract address.
                       * @param _message Message to send to the target.
                       * @param _gasLimit Gas limit for the provided message.
                       */
                      function sendMessage(
                          address _target,
                          bytes calldata _message,
                          uint32 _gasLimit
                      ) external;
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  pragma experimental ABIEncoderV2;
                  /* Library Imports */
                  import { Lib_OVMCodec } from "../../../libraries/codec/Lib_OVMCodec.sol";
                  /* Interface Imports */
                  import { iOVM_CrossDomainMessenger } from "./iOVM_CrossDomainMessenger.sol";
                  /**
                   * @title iOVM_L1CrossDomainMessenger
                   */
                  interface iOVM_L1CrossDomainMessenger is iOVM_CrossDomainMessenger {
                      /*******************
                       * Data Structures *
                       *******************/
                      struct L2MessageInclusionProof {
                          bytes32 stateRoot;
                          Lib_OVMCodec.ChainBatchHeader stateRootBatchHeader;
                          Lib_OVMCodec.ChainInclusionProof stateRootProof;
                          bytes stateTrieWitness;
                          bytes storageTrieWitness;
                      }
                      /********************
                       * Public Functions *
                       ********************/
                      /**
                       * Relays a cross domain message to a contract.
                       * @param _target Target contract address.
                       * @param _sender Message sender address.
                       * @param _message Message to send to the target.
                       * @param _messageNonce Nonce for the provided message.
                       * @param _proof Inclusion proof for the given message.
                       */
                      function relayMessage(
                          address _target,
                          address _sender,
                          bytes memory _message,
                          uint256 _messageNonce,
                          L2MessageInclusionProof memory _proof
                      ) external;
                      /**
                       * Replays a cross domain message to the target messenger.
                       * @param _target Target contract address.
                       * @param _sender Original sender address.
                       * @param _message Message to send to the target.
                       * @param _queueIndex CTC Queue index for the message to replay.
                       * @param _gasLimit Gas limit for the provided message.
                       */
                      function replayMessage(
                          address _target,
                          address _sender,
                          bytes memory _message,
                          uint256 _queueIndex,
                          uint32 _gasLimit
                      ) external;
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  pragma experimental ABIEncoderV2;
                  /* Library Imports */
                  import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
                  /* Interface Imports */
                  import { iOVM_ChainStorageContainer } from "./iOVM_ChainStorageContainer.sol";
                  /**
                   * @title iOVM_CanonicalTransactionChain
                   */
                  interface iOVM_CanonicalTransactionChain {
                      /**********
                       * Events *
                       **********/
                      event TransactionEnqueued(
                          address _l1TxOrigin,
                          address _target,
                          uint256 _gasLimit,
                          bytes _data,
                          uint256 _queueIndex,
                          uint256 _timestamp
                      );
                      event QueueBatchAppended(
                          uint256 _startingQueueIndex,
                          uint256 _numQueueElements,
                          uint256 _totalElements
                      );
                      event SequencerBatchAppended(
                          uint256 _startingQueueIndex,
                          uint256 _numQueueElements,
                          uint256 _totalElements
                      );
                      event TransactionBatchAppended(
                          uint256 indexed _batchIndex,
                          bytes32 _batchRoot,
                          uint256 _batchSize,
                          uint256 _prevTotalElements,
                          bytes _extraData
                      );
                      /***********
                       * Structs *
                       ***********/
                      struct BatchContext {
                          uint256 numSequencedTransactions;
                          uint256 numSubsequentQueueTransactions;
                          uint256 timestamp;
                          uint256 blockNumber;
                      }
                      /********************
                       * Public Functions *
                       ********************/
                      /**
                       * Accesses the batch storage container.
                       * @return Reference to the batch storage container.
                       */
                      function batches()
                          external
                          view
                          returns (
                              iOVM_ChainStorageContainer
                          );
                      /**
                       * Accesses the queue storage container.
                       * @return Reference to the queue storage container.
                       */
                      function queue()
                          external
                          view
                          returns (
                              iOVM_ChainStorageContainer
                          );
                      /**
                       * Retrieves the total number of elements submitted.
                       * @return _totalElements Total submitted elements.
                       */
                      function getTotalElements()
                          external
                          view
                          returns (
                              uint256 _totalElements
                          );
                      /**
                       * Retrieves the total number of batches submitted.
                       * @return _totalBatches Total submitted batches.
                       */
                      function getTotalBatches()
                          external
                          view
                          returns (
                              uint256 _totalBatches
                          );
                      /**
                       * Returns the index of the next element to be enqueued.
                       * @return Index for the next queue element.
                       */
                      function getNextQueueIndex()
                          external
                          view
                          returns (
                              uint40
                          );
                      /**
                       * Gets the queue element at a particular index.
                       * @param _index Index of the queue element to access.
                       * @return _element Queue element at the given index.
                       */
                      function getQueueElement(
                          uint256 _index
                      )
                          external
                          view
                          returns (
                              Lib_OVMCodec.QueueElement memory _element
                          );
                      /**
                       * Returns the timestamp of the last transaction.
                       * @return Timestamp for the last transaction.
                       */
                      function getLastTimestamp()
                          external
                          view
                          returns (
                              uint40
                          );
                      /**
                       * Returns the blocknumber of the last transaction.
                       * @return Blocknumber for the last transaction.
                       */
                      function getLastBlockNumber()
                          external
                          view
                          returns (
                              uint40
                          );
                      /**
                       * Get the number of queue elements which have not yet been included.
                       * @return Number of pending queue elements.
                       */
                      function getNumPendingQueueElements()
                          external
                          view
                          returns (
                              uint40
                          );
                      /**
                       * Retrieves the length of the queue, including
                       * both pending and canonical transactions.
                       * @return Length of the queue.
                       */
                      function getQueueLength()
                          external
                          view
                          returns (
                              uint40
                          );
                      /**
                       * Adds a transaction to the queue.
                       * @param _target Target contract to send the transaction to.
                       * @param _gasLimit Gas limit for the given transaction.
                       * @param _data Transaction data.
                       */
                      function enqueue(
                          address _target,
                          uint256 _gasLimit,
                          bytes memory _data
                      )
                          external;
                      /**
                       * Appends a given number of queued transactions as a single batch.
                       * @param _numQueuedTransactions Number of transactions to append.
                       */
                      function appendQueueBatch(
                          uint256 _numQueuedTransactions
                      )
                          external;
                      /**
                       * Allows the sequencer to append a batch of transactions.
                       * @dev This function uses a custom encoding scheme for efficiency reasons.
                       * .param _shouldStartAtElement Specific batch we expect to start appending to.
                       * .param _totalElementsToAppend Total number of batch elements we expect to append.
                       * .param _contexts Array of batch contexts.
                       * .param _transactionDataFields Array of raw transaction data.
                       */
                      function appendSequencerBatch(
                          // uint40 _shouldStartAtElement,
                          // uint24 _totalElementsToAppend,
                          // BatchContext[] _contexts,
                          // bytes[] _transactionDataFields
                      )
                          external;
                      /**
                       * Verifies whether a transaction is included in the chain.
                       * @param _transaction Transaction to verify.
                       * @param _txChainElement Transaction chain element corresponding to the transaction.
                       * @param _batchHeader Header of the batch the transaction was included in.
                       * @param _inclusionProof Inclusion proof for the provided transaction chain element.
                       * @return True if the transaction exists in the CTC, false if not.
                       */
                      function verifyTransaction(
                          Lib_OVMCodec.Transaction memory _transaction,
                          Lib_OVMCodec.TransactionChainElement memory _txChainElement,
                          Lib_OVMCodec.ChainBatchHeader memory _batchHeader,
                          Lib_OVMCodec.ChainInclusionProof memory _inclusionProof
                      )
                          external
                          view
                          returns (
                              bool
                          );
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  /**
                   * @title iOVM_ChainStorageContainer
                   */
                  interface iOVM_ChainStorageContainer {
                      /********************
                       * Public Functions *
                       ********************/
                      /**
                       * Sets the container's global metadata field. We're using `bytes27` here because we use five
                       * bytes to maintain the length of the underlying data structure, meaning we have an extra
                       * 27 bytes to store arbitrary data.
                       * @param _globalMetadata New global metadata to set.
                       */
                      function setGlobalMetadata(
                          bytes27 _globalMetadata
                      )
                          external;
                      /**
                       * Retrieves the container's global metadata field.
                       * @return Container global metadata field.
                       */
                      function getGlobalMetadata()
                          external
                          view
                          returns (
                              bytes27
                          );
                      /**
                       * Retrieves the number of objects stored in the container.
                       * @return Number of objects in the container.
                       */
                      function length()
                          external
                          view
                          returns (
                              uint256
                          );
                      /**
                       * Pushes an object into the container.
                       * @param _object A 32 byte value to insert into the container.
                       */
                      function push(
                          bytes32 _object
                      )
                          external;
                      /**
                       * Pushes an object into the container. Function allows setting the global metadata since
                       * we'll need to touch the "length" storage slot anyway, which also contains the global
                       * metadata (it's an optimization).
                       * @param _object A 32 byte value to insert into the container.
                       * @param _globalMetadata New global metadata for the container.
                       */
                      function push(
                          bytes32 _object,
                          bytes27 _globalMetadata
                      )
                          external;
                      /**
                       * Retrieves an object from the container.
                       * @param _index Index of the particular object to access.
                       * @return 32 byte object value.
                       */
                      function get(
                          uint256 _index
                      )
                          external
                          view
                          returns (
                              bytes32
                          );
                      /**
                       * Removes all objects after and including a given index.
                       * @param _index Object index to delete from.
                       */
                      function deleteElementsAfterInclusive(
                          uint256 _index
                      )
                          external;
                      /**
                       * Removes all objects after and including a given index. Also allows setting the global
                       * metadata field.
                       * @param _index Object index to delete from.
                       * @param _globalMetadata New global metadata for the container.
                       */
                      function deleteElementsAfterInclusive(
                          uint256 _index,
                          bytes27 _globalMetadata
                      )
                          external;
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  pragma experimental ABIEncoderV2;
                  /* Library Imports */
                  import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
                  /**
                   * @title iOVM_StateCommitmentChain
                   */
                  interface iOVM_StateCommitmentChain {
                      /**********
                       * Events *
                       **********/
                      event StateBatchAppended(
                          uint256 indexed _batchIndex,
                          bytes32 _batchRoot,
                          uint256 _batchSize,
                          uint256 _prevTotalElements,
                          bytes _extraData
                      );
                      event StateBatchDeleted(
                          uint256 indexed _batchIndex,
                          bytes32 _batchRoot
                      );
                      /********************
                       * Public Functions *
                       ********************/
                      /**
                       * Retrieves the total number of elements submitted.
                       * @return _totalElements Total submitted elements.
                       */
                      function getTotalElements()
                          external
                          view
                          returns (
                              uint256 _totalElements
                          );
                      /**
                       * Retrieves the total number of batches submitted.
                       * @return _totalBatches Total submitted batches.
                       */
                      function getTotalBatches()
                          external
                          view
                          returns (
                              uint256 _totalBatches
                          );
                      /**
                       * Retrieves the timestamp of the last batch submitted by the sequencer.
                       * @return _lastSequencerTimestamp Last sequencer batch timestamp.
                       */
                      function getLastSequencerTimestamp()
                          external
                          view
                          returns (
                              uint256 _lastSequencerTimestamp
                          );
                      /**
                       * Appends a batch of state roots to the chain.
                       * @param _batch Batch of state roots.
                       * @param _shouldStartAtElement Index of the element at which this batch should start.
                       */
                      function appendStateBatch(
                          bytes32[] calldata _batch,
                          uint256 _shouldStartAtElement
                      )
                          external;
                      /**
                       * Deletes all state roots after (and including) a given batch.
                       * @param _batchHeader Header of the batch to start deleting from.
                       */
                      function deleteStateBatch(
                          Lib_OVMCodec.ChainBatchHeader memory _batchHeader
                      )
                          external;
                      /**
                       * Verifies a batch inclusion proof.
                       * @param _element Hash of the element to verify a proof for.
                       * @param _batchHeader Header of the batch in which the element was included.
                       * @param _proof Merkle inclusion proof for the element.
                       */
                      function verifyStateCommitment(
                          bytes32 _element,
                          Lib_OVMCodec.ChainBatchHeader memory _batchHeader,
                          Lib_OVMCodec.ChainInclusionProof memory _proof
                      )
                          external
                          view
                          returns (
                              bool _verified
                          );
                      /**
                       * Checks whether a given batch is still inside its fraud proof window.
                       * @param _batchHeader Header of the batch to check.
                       * @return _inside Whether or not the batch is inside the fraud proof window.
                       */
                      function insideFraudProofWindow(
                          Lib_OVMCodec.ChainBatchHeader memory _batchHeader
                      )
                          external
                          view
                          returns (
                              bool _inside
                          );
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  pragma experimental ABIEncoderV2;
                  /* Library Imports */
                  import { Lib_RLPReader } from "../rlp/Lib_RLPReader.sol";
                  /**
                   * @title Lib_CrossDomainUtils
                   */
                  library Lib_CrossDomainUtils {
                      /**
                       * Generates the correct cross domain calldata for a message.
                       * @param _target Target contract address.
                       * @param _sender Message sender address.
                       * @param _message Message to send to the target.
                       * @param _messageNonce Nonce for the provided message.
                       * @return ABI encoded cross domain calldata.
                       */
                      function encodeXDomainCalldata(
                          address _target,
                          address _sender,
                          bytes memory _message,
                          uint256 _messageNonce
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          return abi.encodeWithSignature(
                              "relayMessage(address,address,bytes,uint256)",
                              _target,
                              _sender,
                              _message,
                              _messageNonce
                          );
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  pragma experimental ABIEncoderV2;
                  /* Library Imports */
                  import { Lib_RLPReader } from "../rlp/Lib_RLPReader.sol";
                  import { Lib_RLPWriter } from "../rlp/Lib_RLPWriter.sol";
                  import { Lib_BytesUtils } from "../utils/Lib_BytesUtils.sol";
                  import { Lib_Bytes32Utils } from "../utils/Lib_Bytes32Utils.sol";
                  /**
                   * @title Lib_OVMCodec
                   */
                  library Lib_OVMCodec {
                      /*********
                       * Enums *
                       *********/
                      enum QueueOrigin {
                          SEQUENCER_QUEUE,
                          L1TOL2_QUEUE
                      }
                      /***********
                       * Structs *
                       ***********/
                      struct Account {
                          uint256 nonce;
                          uint256 balance;
                          bytes32 storageRoot;
                          bytes32 codeHash;
                          address ethAddress;
                          bool isFresh;
                      }
                      struct EVMAccount {
                          uint256 nonce;
                          uint256 balance;
                          bytes32 storageRoot;
                          bytes32 codeHash;
                      }
                      struct ChainBatchHeader {
                          uint256 batchIndex;
                          bytes32 batchRoot;
                          uint256 batchSize;
                          uint256 prevTotalElements;
                          bytes extraData;
                      }
                      struct ChainInclusionProof {
                          uint256 index;
                          bytes32[] siblings;
                      }
                      struct Transaction {
                          uint256 timestamp;
                          uint256 blockNumber;
                          QueueOrigin l1QueueOrigin;
                          address l1TxOrigin;
                          address entrypoint;
                          uint256 gasLimit;
                          bytes data;
                      }
                      struct TransactionChainElement {
                          bool isSequenced;
                          uint256 queueIndex;  // QUEUED TX ONLY
                          uint256 timestamp;   // SEQUENCER TX ONLY
                          uint256 blockNumber; // SEQUENCER TX ONLY
                          bytes txData;        // SEQUENCER TX ONLY
                      }
                      struct QueueElement {
                          bytes32 transactionHash;
                          uint40 timestamp;
                          uint40 blockNumber;
                      }
                      /**********************
                       * Internal Functions *
                       **********************/
                      /**
                       * Encodes a standard OVM transaction.
                       * @param _transaction OVM transaction to encode.
                       * @return Encoded transaction bytes.
                       */
                      function encodeTransaction(
                          Transaction memory _transaction
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          return abi.encodePacked(
                              _transaction.timestamp,
                              _transaction.blockNumber,
                              _transaction.l1QueueOrigin,
                              _transaction.l1TxOrigin,
                              _transaction.entrypoint,
                              _transaction.gasLimit,
                              _transaction.data
                          );
                      }
                      /**
                       * Hashes a standard OVM transaction.
                       * @param _transaction OVM transaction to encode.
                       * @return Hashed transaction
                       */
                      function hashTransaction(
                          Transaction memory _transaction
                      )
                          internal
                          pure
                          returns (
                              bytes32
                          )
                      {
                          return keccak256(encodeTransaction(_transaction));
                      }
                      /**
                       * Converts an OVM account to an EVM account.
                       * @param _in OVM account to convert.
                       * @return Converted EVM account.
                       */
                      function toEVMAccount(
                          Account memory _in
                      )
                          internal
                          pure
                          returns (
                              EVMAccount memory
                          )
                      {
                          return EVMAccount({
                              nonce: _in.nonce,
                              balance: _in.balance,
                              storageRoot: _in.storageRoot,
                              codeHash: _in.codeHash
                          });
                      }
                      /**
                       * @notice RLP-encodes an account state struct.
                       * @param _account Account state struct.
                       * @return RLP-encoded account state.
                       */
                      function encodeEVMAccount(
                          EVMAccount memory _account
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          bytes[] memory raw = new bytes[](4);
                          // Unfortunately we can't create this array outright because
                          // Lib_RLPWriter.writeList will reject fixed-size arrays. Assigning
                          // index-by-index circumvents this issue.
                          raw[0] = Lib_RLPWriter.writeBytes(
                              Lib_Bytes32Utils.removeLeadingZeros(
                                  bytes32(_account.nonce)
                              )
                          );
                          raw[1] = Lib_RLPWriter.writeBytes(
                              Lib_Bytes32Utils.removeLeadingZeros(
                                  bytes32(_account.balance)
                              )
                          );
                          raw[2] = Lib_RLPWriter.writeBytes(abi.encodePacked(_account.storageRoot));
                          raw[3] = Lib_RLPWriter.writeBytes(abi.encodePacked(_account.codeHash));
                          return Lib_RLPWriter.writeList(raw);
                      }
                      /**
                       * @notice Decodes an RLP-encoded account state into a useful struct.
                       * @param _encoded RLP-encoded account state.
                       * @return Account state struct.
                       */
                      function decodeEVMAccount(
                          bytes memory _encoded
                      )
                          internal
                          pure
                          returns (
                              EVMAccount memory
                          )
                      {
                          Lib_RLPReader.RLPItem[] memory accountState = Lib_RLPReader.readList(_encoded);
                          return EVMAccount({
                              nonce: Lib_RLPReader.readUint256(accountState[0]),
                              balance: Lib_RLPReader.readUint256(accountState[1]),
                              storageRoot: Lib_RLPReader.readBytes32(accountState[2]),
                              codeHash: Lib_RLPReader.readBytes32(accountState[3])
                          });
                      }
                      /**
                       * Calculates a hash for a given batch header.
                       * @param _batchHeader Header to hash.
                       * @return Hash of the header.
                       */
                      function hashBatchHeader(
                          Lib_OVMCodec.ChainBatchHeader memory _batchHeader
                      )
                          internal
                          pure
                          returns (
                              bytes32
                          )
                      {
                          return keccak256(
                              abi.encode(
                                  _batchHeader.batchRoot,
                                  _batchHeader.batchSize,
                                  _batchHeader.prevTotalElements,
                                  _batchHeader.extraData
                              )
                          );
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  /**
                   * @title Lib_PredeployAddresses
                   */
                  library Lib_PredeployAddresses {
                      address internal constant L2_TO_L1_MESSAGE_PASSER = 0x4200000000000000000000000000000000000000;
                      address internal constant L1_MESSAGE_SENDER = 0x4200000000000000000000000000000000000001;
                      address internal constant DEPLOYER_WHITELIST = 0x4200000000000000000000000000000000000002;
                      address internal constant ECDSA_CONTRACT_ACCOUNT = 0x4200000000000000000000000000000000000003;
                      address internal constant SEQUENCER_ENTRYPOINT = 0x4200000000000000000000000000000000000005;
                      address payable internal constant OVM_ETH = 0x4200000000000000000000000000000000000006;
                      address internal constant L2_CROSS_DOMAIN_MESSENGER = 0x4200000000000000000000000000000000000007;
                      address internal constant LIB_ADDRESS_MANAGER = 0x4200000000000000000000000000000000000008;
                      address internal constant PROXY_EOA = 0x4200000000000000000000000000000000000009;
                      address internal constant EXECUTION_MANAGER_WRAPPER = 0x420000000000000000000000000000000000000B;
                      address internal constant SEQUENCER_FEE_WALLET = 0x4200000000000000000000000000000000000011;
                      address internal constant ERC1820_REGISTRY = 0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24;
                      address internal constant L2_STANDARD_BRIDGE = 0x4200000000000000000000000000000000000010;
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  /* External Imports */
                  import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
                  /**
                   * @title Lib_AddressManager
                   */
                  contract Lib_AddressManager is Ownable {
                      /**********
                       * Events *
                       **********/
                      event AddressSet(
                          string indexed _name,
                          address _newAddress,
                          address _oldAddress
                      );
                      /*************
                       * Variables *
                       *************/
                      mapping (bytes32 => address) private addresses;
                      /********************
                       * Public Functions *
                       ********************/
                      /**
                       * Changes the address associated with a particular name.
                       * @param _name String name to associate an address with.
                       * @param _address Address to associate with the name.
                       */
                      function setAddress(
                          string memory _name,
                          address _address
                      )
                          external
                          onlyOwner
                      {
                          bytes32 nameHash = _getNameHash(_name);
                          address oldAddress = addresses[nameHash];
                          addresses[nameHash] = _address;
                          emit AddressSet(
                              _name,
                              _address,
                              oldAddress
                          );
                      }
                      /**
                       * Retrieves the address associated with a given name.
                       * @param _name Name to retrieve an address for.
                       * @return Address associated with the given name.
                       */
                      function getAddress(
                          string memory _name
                      )
                          external
                          view
                          returns (
                              address
                          )
                      {
                          return addresses[_getNameHash(_name)];
                      }
                      /**********************
                       * Internal Functions *
                       **********************/
                      /**
                       * Computes the hash of a name.
                       * @param _name Name to compute a hash for.
                       * @return Hash of the given name.
                       */
                      function _getNameHash(
                          string memory _name
                      )
                          internal
                          pure
                          returns (
                              bytes32
                          )
                      {
                          return keccak256(abi.encodePacked(_name));
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  /* Library Imports */
                  import { Lib_AddressManager } from "./Lib_AddressManager.sol";
                  /**
                   * @title Lib_AddressResolver
                   */
                  abstract contract Lib_AddressResolver {
                      /*************
                       * Variables *
                       *************/
                      Lib_AddressManager public libAddressManager;
                      /***************
                       * Constructor *
                       ***************/
                      /**
                       * @param _libAddressManager Address of the Lib_AddressManager.
                       */
                      constructor(
                          address _libAddressManager
                      ) {
                          libAddressManager = Lib_AddressManager(_libAddressManager);
                      }
                      /********************
                       * Public Functions *
                       ********************/
                      /**
                       * Resolves the address associated with a given name.
                       * @param _name Name to resolve an address for.
                       * @return Address associated with the given name.
                       */
                      function resolve(
                          string memory _name
                      )
                          public
                          view
                          returns (
                              address
                          )
                      {
                          return libAddressManager.getAddress(_name);
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  /**
                   * @title Lib_RLPReader
                   * @dev Adapted from "RLPReader" by Hamdi Allam ([email protected]).
                   */
                  library Lib_RLPReader {
                      /*************
                       * Constants *
                       *************/
                      uint256 constant internal MAX_LIST_LENGTH = 32;
                      /*********
                       * Enums *
                       *********/
                      enum RLPItemType {
                          DATA_ITEM,
                          LIST_ITEM
                      }
                      /***********
                       * Structs *
                       ***********/
                      struct RLPItem {
                          uint256 length;
                          uint256 ptr;
                      }
                      /**********************
                       * Internal Functions *
                       **********************/
                      /**
                       * Converts bytes to a reference to memory position and length.
                       * @param _in Input bytes to convert.
                       * @return Output memory reference.
                       */
                      function toRLPItem(
                          bytes memory _in
                      )
                          internal
                          pure
                          returns (
                              RLPItem memory
                          )
                      {
                          uint256 ptr;
                          assembly {
                              ptr := add(_in, 32)
                          }
                          return RLPItem({
                              length: _in.length,
                              ptr: ptr
                          });
                      }
                      /**
                       * Reads an RLP list value into a list of RLP items.
                       * @param _in RLP list value.
                       * @return Decoded RLP list items.
                       */
                      function readList(
                          RLPItem memory _in
                      )
                          internal
                          pure
                          returns (
                              RLPItem[] memory
                          )
                      {
                          (
                              uint256 listOffset,
                              ,
                              RLPItemType itemType
                          ) = _decodeLength(_in);
                          require(
                              itemType == RLPItemType.LIST_ITEM,
                              "Invalid RLP list value."
                          );
                          // Solidity in-memory arrays can't be increased in size, but *can* be decreased in size by
                          // writing to the length. Since we can't know the number of RLP items without looping over
                          // the entire input, we'd have to loop twice to accurately size this array. It's easier to
                          // simply set a reasonable maximum list length and decrease the size before we finish.
                          RLPItem[] memory out = new RLPItem[](MAX_LIST_LENGTH);
                          uint256 itemCount = 0;
                          uint256 offset = listOffset;
                          while (offset < _in.length) {
                              require(
                                  itemCount < MAX_LIST_LENGTH,
                                  "Provided RLP list exceeds max list length."
                              );
                              (
                                  uint256 itemOffset,
                                  uint256 itemLength,
                              ) = _decodeLength(RLPItem({
                                  length: _in.length - offset,
                                  ptr: _in.ptr + offset
                              }));
                              out[itemCount] = RLPItem({
                                  length: itemLength + itemOffset,
                                  ptr: _in.ptr + offset
                              });
                              itemCount += 1;
                              offset += itemOffset + itemLength;
                          }
                          // Decrease the array size to match the actual item count.
                          assembly {
                              mstore(out, itemCount)
                          }
                          return out;
                      }
                      /**
                       * Reads an RLP list value into a list of RLP items.
                       * @param _in RLP list value.
                       * @return Decoded RLP list items.
                       */
                      function readList(
                          bytes memory _in
                      )
                          internal
                          pure
                          returns (
                              RLPItem[] memory
                          )
                      {
                          return readList(
                              toRLPItem(_in)
                          );
                      }
                      /**
                       * Reads an RLP bytes value into bytes.
                       * @param _in RLP bytes value.
                       * @return Decoded bytes.
                       */
                      function readBytes(
                          RLPItem memory _in
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          (
                              uint256 itemOffset,
                              uint256 itemLength,
                              RLPItemType itemType
                          ) = _decodeLength(_in);
                          require(
                              itemType == RLPItemType.DATA_ITEM,
                              "Invalid RLP bytes value."
                          );
                          return _copy(_in.ptr, itemOffset, itemLength);
                      }
                      /**
                       * Reads an RLP bytes value into bytes.
                       * @param _in RLP bytes value.
                       * @return Decoded bytes.
                       */
                      function readBytes(
                          bytes memory _in
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          return readBytes(
                              toRLPItem(_in)
                          );
                      }
                      /**
                       * Reads an RLP string value into a string.
                       * @param _in RLP string value.
                       * @return Decoded string.
                       */
                      function readString(
                          RLPItem memory _in
                      )
                          internal
                          pure
                          returns (
                              string memory
                          )
                      {
                          return string(readBytes(_in));
                      }
                      /**
                       * Reads an RLP string value into a string.
                       * @param _in RLP string value.
                       * @return Decoded string.
                       */
                      function readString(
                          bytes memory _in
                      )
                          internal
                          pure
                          returns (
                              string memory
                          )
                      {
                          return readString(
                              toRLPItem(_in)
                          );
                      }
                      /**
                       * Reads an RLP bytes32 value into a bytes32.
                       * @param _in RLP bytes32 value.
                       * @return Decoded bytes32.
                       */
                      function readBytes32(
                          RLPItem memory _in
                      )
                          internal
                          pure
                          returns (
                              bytes32
                          )
                      {
                          require(
                              _in.length <= 33,
                              "Invalid RLP bytes32 value."
                          );
                          (
                              uint256 itemOffset,
                              uint256 itemLength,
                              RLPItemType itemType
                          ) = _decodeLength(_in);
                          require(
                              itemType == RLPItemType.DATA_ITEM,
                              "Invalid RLP bytes32 value."
                          );
                          uint256 ptr = _in.ptr + itemOffset;
                          bytes32 out;
                          assembly {
                              out := mload(ptr)
                              // Shift the bytes over to match the item size.
                              if lt(itemLength, 32) {
                                  out := div(out, exp(256, sub(32, itemLength)))
                              }
                          }
                          return out;
                      }
                      /**
                       * Reads an RLP bytes32 value into a bytes32.
                       * @param _in RLP bytes32 value.
                       * @return Decoded bytes32.
                       */
                      function readBytes32(
                          bytes memory _in
                      )
                          internal
                          pure
                          returns (
                              bytes32
                          )
                      {
                          return readBytes32(
                              toRLPItem(_in)
                          );
                      }
                      /**
                       * Reads an RLP uint256 value into a uint256.
                       * @param _in RLP uint256 value.
                       * @return Decoded uint256.
                       */
                      function readUint256(
                          RLPItem memory _in
                      )
                          internal
                          pure
                          returns (
                              uint256
                          )
                      {
                          return uint256(readBytes32(_in));
                      }
                      /**
                       * Reads an RLP uint256 value into a uint256.
                       * @param _in RLP uint256 value.
                       * @return Decoded uint256.
                       */
                      function readUint256(
                          bytes memory _in
                      )
                          internal
                          pure
                          returns (
                              uint256
                          )
                      {
                          return readUint256(
                              toRLPItem(_in)
                          );
                      }
                      /**
                       * Reads an RLP bool value into a bool.
                       * @param _in RLP bool value.
                       * @return Decoded bool.
                       */
                      function readBool(
                          RLPItem memory _in
                      )
                          internal
                          pure
                          returns (
                              bool
                          )
                      {
                          require(
                              _in.length == 1,
                              "Invalid RLP boolean value."
                          );
                          uint256 ptr = _in.ptr;
                          uint256 out;
                          assembly {
                              out := byte(0, mload(ptr))
                          }
                          require(
                              out == 0 || out == 1,
                              "Lib_RLPReader: Invalid RLP boolean value, must be 0 or 1"
                          );
                          return out != 0;
                      }
                      /**
                       * Reads an RLP bool value into a bool.
                       * @param _in RLP bool value.
                       * @return Decoded bool.
                       */
                      function readBool(
                          bytes memory _in
                      )
                          internal
                          pure
                          returns (
                              bool
                          )
                      {
                          return readBool(
                              toRLPItem(_in)
                          );
                      }
                      /**
                       * Reads an RLP address value into a address.
                       * @param _in RLP address value.
                       * @return Decoded address.
                       */
                      function readAddress(
                          RLPItem memory _in
                      )
                          internal
                          pure
                          returns (
                              address
                          )
                      {
                          if (_in.length == 1) {
                              return address(0);
                          }
                          require(
                              _in.length == 21,
                              "Invalid RLP address value."
                          );
                          return address(readUint256(_in));
                      }
                      /**
                       * Reads an RLP address value into a address.
                       * @param _in RLP address value.
                       * @return Decoded address.
                       */
                      function readAddress(
                          bytes memory _in
                      )
                          internal
                          pure
                          returns (
                              address
                          )
                      {
                          return readAddress(
                              toRLPItem(_in)
                          );
                      }
                      /**
                       * Reads the raw bytes of an RLP item.
                       * @param _in RLP item to read.
                       * @return Raw RLP bytes.
                       */
                      function readRawBytes(
                          RLPItem memory _in
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          return _copy(_in);
                      }
                      /*********************
                       * Private Functions *
                       *********************/
                      /**
                       * Decodes the length of an RLP item.
                       * @param _in RLP item to decode.
                       * @return Offset of the encoded data.
                       * @return Length of the encoded data.
                       * @return RLP item type (LIST_ITEM or DATA_ITEM).
                       */
                      function _decodeLength(
                          RLPItem memory _in
                      )
                          private
                          pure
                          returns (
                              uint256,
                              uint256,
                              RLPItemType
                          )
                      {
                          require(
                              _in.length > 0,
                              "RLP item cannot be null."
                          );
                          uint256 ptr = _in.ptr;
                          uint256 prefix;
                          assembly {
                              prefix := byte(0, mload(ptr))
                          }
                          if (prefix <= 0x7f) {
                              // Single byte.
                              return (0, 1, RLPItemType.DATA_ITEM);
                          } else if (prefix <= 0xb7) {
                              // Short string.
                              uint256 strLen = prefix - 0x80;
                              require(
                                  _in.length > strLen,
                                  "Invalid RLP short string."
                              );
                              return (1, strLen, RLPItemType.DATA_ITEM);
                          } else if (prefix <= 0xbf) {
                              // Long string.
                              uint256 lenOfStrLen = prefix - 0xb7;
                              require(
                                  _in.length > lenOfStrLen,
                                  "Invalid RLP long string length."
                              );
                              uint256 strLen;
                              assembly {
                                  // Pick out the string length.
                                  strLen := div(
                                      mload(add(ptr, 1)),
                                      exp(256, sub(32, lenOfStrLen))
                                  )
                              }
                              require(
                                  _in.length > lenOfStrLen + strLen,
                                  "Invalid RLP long string."
                              );
                              return (1 + lenOfStrLen, strLen, RLPItemType.DATA_ITEM);
                          } else if (prefix <= 0xf7) {
                              // Short list.
                              uint256 listLen = prefix - 0xc0;
                              require(
                                  _in.length > listLen,
                                  "Invalid RLP short list."
                              );
                              return (1, listLen, RLPItemType.LIST_ITEM);
                          } else {
                              // Long list.
                              uint256 lenOfListLen = prefix - 0xf7;
                              require(
                                  _in.length > lenOfListLen,
                                  "Invalid RLP long list length."
                              );
                              uint256 listLen;
                              assembly {
                                  // Pick out the list length.
                                  listLen := div(
                                      mload(add(ptr, 1)),
                                      exp(256, sub(32, lenOfListLen))
                                  )
                              }
                              require(
                                  _in.length > lenOfListLen + listLen,
                                  "Invalid RLP long list."
                              );
                              return (1 + lenOfListLen, listLen, RLPItemType.LIST_ITEM);
                          }
                      }
                      /**
                       * Copies the bytes from a memory location.
                       * @param _src Pointer to the location to read from.
                       * @param _offset Offset to start reading from.
                       * @param _length Number of bytes to read.
                       * @return Copied bytes.
                       */
                      function _copy(
                          uint256 _src,
                          uint256 _offset,
                          uint256 _length
                      )
                          private
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          bytes memory out = new bytes(_length);
                          if (out.length == 0) {
                              return out;
                          }
                          uint256 src = _src + _offset;
                          uint256 dest;
                          assembly {
                              dest := add(out, 32)
                          }
                          // Copy over as many complete words as we can.
                          for (uint256 i = 0; i < _length / 32; i++) {
                              assembly {
                                  mstore(dest, mload(src))
                              }
                              src += 32;
                              dest += 32;
                          }
                          // Pick out the remaining bytes.
                          uint256 mask = 256 ** (32 - (_length % 32)) - 1;
                          assembly {
                              mstore(
                                  dest,
                                  or(
                                      and(mload(src), not(mask)),
                                      and(mload(dest), mask)
                                  )
                              )
                          }
                          return out;
                      }
                      /**
                       * Copies an RLP item into bytes.
                       * @param _in RLP item to copy.
                       * @return Copied bytes.
                       */
                      function _copy(
                          RLPItem memory _in
                      )
                          private
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          return _copy(_in.ptr, 0, _in.length);
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  pragma experimental ABIEncoderV2;
                  /**
                   * @title Lib_RLPWriter
                   * @author Bakaoh (with modifications)
                   */
                  library Lib_RLPWriter {
                      /**********************
                       * Internal Functions *
                       **********************/
                      /**
                       * RLP encodes a byte string.
                       * @param _in The byte string to encode.
                       * @return The RLP encoded string in bytes.
                       */
                      function writeBytes(
                          bytes memory _in
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          bytes memory encoded;
                          if (_in.length == 1 && uint8(_in[0]) < 128) {
                              encoded = _in;
                          } else {
                              encoded = abi.encodePacked(_writeLength(_in.length, 128), _in);
                          }
                          return encoded;
                      }
                      /**
                       * RLP encodes a list of RLP encoded byte byte strings.
                       * @param _in The list of RLP encoded byte strings.
                       * @return The RLP encoded list of items in bytes.
                       */
                      function writeList(
                          bytes[] memory _in
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          bytes memory list = _flatten(_in);
                          return abi.encodePacked(_writeLength(list.length, 192), list);
                      }
                      /**
                       * RLP encodes a string.
                       * @param _in The string to encode.
                       * @return The RLP encoded string in bytes.
                       */
                      function writeString(
                          string memory _in
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          return writeBytes(bytes(_in));
                      }
                      /**
                       * RLP encodes an address.
                       * @param _in The address to encode.
                       * @return The RLP encoded address in bytes.
                       */
                      function writeAddress(
                          address _in
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          return writeBytes(abi.encodePacked(_in));
                      }
                      /**
                       * RLP encodes a bytes32 value.
                       * @param _in The bytes32 to encode.
                       * @return _out The RLP encoded bytes32 in bytes.
                       */
                      function writeBytes32(
                          bytes32 _in
                      )
                          internal
                          pure
                          returns (
                              bytes memory _out
                          )
                      {
                          return writeBytes(abi.encodePacked(_in));
                      }
                      /**
                       * RLP encodes a uint.
                       * @param _in The uint256 to encode.
                       * @return The RLP encoded uint256 in bytes.
                       */
                      function writeUint(
                          uint256 _in
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          return writeBytes(_toBinary(_in));
                      }
                      /**
                       * RLP encodes a bool.
                       * @param _in The bool to encode.
                       * @return The RLP encoded bool in bytes.
                       */
                      function writeBool(
                          bool _in
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          bytes memory encoded = new bytes(1);
                          encoded[0] = (_in ? bytes1(0x01) : bytes1(0x80));
                          return encoded;
                      }
                      /*********************
                       * Private Functions *
                       *********************/
                      /**
                       * Encode the first byte, followed by the `len` in binary form if `length` is more than 55.
                       * @param _len The length of the string or the payload.
                       * @param _offset 128 if item is string, 192 if item is list.
                       * @return RLP encoded bytes.
                       */
                      function _writeLength(
                          uint256 _len,
                          uint256 _offset
                      )
                          private
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          bytes memory encoded;
                          if (_len < 56) {
                              encoded = new bytes(1);
                              encoded[0] = byte(uint8(_len) + uint8(_offset));
                          } else {
                              uint256 lenLen;
                              uint256 i = 1;
                              while (_len / i != 0) {
                                  lenLen++;
                                  i *= 256;
                              }
                              encoded = new bytes(lenLen + 1);
                              encoded[0] = byte(uint8(lenLen) + uint8(_offset) + 55);
                              for(i = 1; i <= lenLen; i++) {
                                  encoded[i] = byte(uint8((_len / (256**(lenLen-i))) % 256));
                              }
                          }
                          return encoded;
                      }
                      /**
                       * Encode integer in big endian binary form with no leading zeroes.
                       * @notice TODO: This should be optimized with assembly to save gas costs.
                       * @param _x The integer to encode.
                       * @return RLP encoded bytes.
                       */
                      function _toBinary(
                          uint256 _x
                      )
                          private
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          bytes memory b = abi.encodePacked(_x);
                          uint256 i = 0;
                          for (; i < 32; i++) {
                              if (b[i] != 0) {
                                  break;
                              }
                          }
                          bytes memory res = new bytes(32 - i);
                          for (uint256 j = 0; j < res.length; j++) {
                              res[j] = b[i++];
                          }
                          return res;
                      }
                      /**
                       * Copies a piece of memory to another location.
                       * @notice From: https://github.com/Arachnid/solidity-stringutils/blob/master/src/strings.sol.
                       * @param _dest Destination location.
                       * @param _src Source location.
                       * @param _len Length of memory to copy.
                       */
                      function _memcpy(
                          uint256 _dest,
                          uint256 _src,
                          uint256 _len
                      )
                          private
                          pure
                      {
                          uint256 dest = _dest;
                          uint256 src = _src;
                          uint256 len = _len;
                          for(; len >= 32; len -= 32) {
                              assembly {
                                  mstore(dest, mload(src))
                              }
                              dest += 32;
                              src += 32;
                          }
                          uint256 mask = 256 ** (32 - len) - 1;
                          assembly {
                              let srcpart := and(mload(src), not(mask))
                              let destpart := and(mload(dest), mask)
                              mstore(dest, or(destpart, srcpart))
                          }
                      }
                      /**
                       * Flattens a list of byte strings into one byte string.
                       * @notice From: https://github.com/sammayo/solidity-rlp-encoder/blob/master/RLPEncode.sol.
                       * @param _list List of byte strings to flatten.
                       * @return The flattened byte string.
                       */
                      function _flatten(
                          bytes[] memory _list
                      )
                          private
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          if (_list.length == 0) {
                              return new bytes(0);
                          }
                          uint256 len;
                          uint256 i = 0;
                          for (; i < _list.length; i++) {
                              len += _list[i].length;
                          }
                          bytes memory flattened = new bytes(len);
                          uint256 flattenedPtr;
                          assembly { flattenedPtr := add(flattened, 0x20) }
                          for(i = 0; i < _list.length; i++) {
                              bytes memory item = _list[i];
                              uint256 listPtr;
                              assembly { listPtr := add(item, 0x20)}
                              _memcpy(flattenedPtr, listPtr, item.length);
                              flattenedPtr += _list[i].length;
                          }
                          return flattened;
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  /* Library Imports */
                  import { Lib_BytesUtils } from "../utils/Lib_BytesUtils.sol";
                  import { Lib_RLPReader } from "../rlp/Lib_RLPReader.sol";
                  import { Lib_RLPWriter } from "../rlp/Lib_RLPWriter.sol";
                  /**
                   * @title Lib_MerkleTrie
                   */
                  library Lib_MerkleTrie {
                      /*******************
                       * Data Structures *
                       *******************/
                      enum NodeType {
                          BranchNode,
                          ExtensionNode,
                          LeafNode
                      }
                      struct TrieNode {
                          bytes encoded;
                          Lib_RLPReader.RLPItem[] decoded;
                      }
                      /**********************
                       * Contract Constants *
                       **********************/
                      // TREE_RADIX determines the number of elements per branch node.
                      uint256 constant TREE_RADIX = 16;
                      // Branch nodes have TREE_RADIX elements plus an additional `value` slot.
                      uint256 constant BRANCH_NODE_LENGTH = TREE_RADIX + 1;
                      // Leaf nodes and extension nodes always have two elements, a `path` and a `value`.
                      uint256 constant LEAF_OR_EXTENSION_NODE_LENGTH = 2;
                      // Prefixes are prepended to the `path` within a leaf or extension node and
                      // allow us to differentiate between the two node types. `ODD` or `EVEN` is
                      // determined by the number of nibbles within the unprefixed `path`. If the
                      // number of nibbles if even, we need to insert an extra padding nibble so
                      // the resulting prefixed `path` has an even number of nibbles.
                      uint8 constant PREFIX_EXTENSION_EVEN = 0;
                      uint8 constant PREFIX_EXTENSION_ODD = 1;
                      uint8 constant PREFIX_LEAF_EVEN = 2;
                      uint8 constant PREFIX_LEAF_ODD = 3;
                      // Just a utility constant. RLP represents `NULL` as 0x80.
                      bytes1 constant RLP_NULL = bytes1(0x80);
                      bytes constant RLP_NULL_BYTES = hex'80';
                      bytes32 constant internal KECCAK256_RLP_NULL_BYTES = keccak256(RLP_NULL_BYTES);
                      /**********************
                       * Internal Functions *
                       **********************/
                      /**
                       * @notice Verifies a proof that a given key/value pair is present in the
                       * Merkle trie.
                       * @param _key Key of the node to search for, as a hex string.
                       * @param _value Value of the node to search for, as a hex string.
                       * @param _proof Merkle trie inclusion proof for the desired node. Unlike
                       * traditional Merkle trees, this proof is executed top-down and consists
                       * of a list of RLP-encoded nodes that make a path down to the target node.
                       * @param _root Known root of the Merkle trie. Used to verify that the
                       * included proof is correctly constructed.
                       * @return _verified `true` if the k/v pair exists in the trie, `false` otherwise.
                       */
                      function verifyInclusionProof(
                          bytes memory _key,
                          bytes memory _value,
                          bytes memory _proof,
                          bytes32 _root
                      )
                          internal
                          pure
                          returns (
                              bool _verified
                          )
                      {
                          (
                              bool exists,
                              bytes memory value
                          ) = get(_key, _proof, _root);
                          return (
                              exists && Lib_BytesUtils.equal(_value, value)
                          );
                      }
                      /**
                       * @notice Updates a Merkle trie and returns a new root hash.
                       * @param _key Key of the node to update, as a hex string.
                       * @param _value Value of the node to update, as a hex string.
                       * @param _proof Merkle trie inclusion proof for the node *nearest* the
                       * target node. If the key exists, we can simply update the value.
                       * Otherwise, we need to modify the trie to handle the new k/v pair.
                       * @param _root Known root of the Merkle trie. Used to verify that the
                       * included proof is correctly constructed.
                       * @return _updatedRoot Root hash of the newly constructed trie.
                       */
                      function update(
                          bytes memory _key,
                          bytes memory _value,
                          bytes memory _proof,
                          bytes32 _root
                      )
                          internal
                          pure
                          returns (
                              bytes32 _updatedRoot
                          )
                      {
                          // Special case when inserting the very first node.
                          if (_root == KECCAK256_RLP_NULL_BYTES) {
                              return getSingleNodeRootHash(_key, _value);
                          }
                          TrieNode[] memory proof = _parseProof(_proof);
                          (uint256 pathLength, bytes memory keyRemainder, ) = _walkNodePath(proof, _key, _root);
                          TrieNode[] memory newPath = _getNewPath(proof, pathLength, _key, keyRemainder, _value);
                          return _getUpdatedTrieRoot(newPath, _key);
                      }
                      /**
                       * @notice Retrieves the value associated with a given key.
                       * @param _key Key to search for, as hex bytes.
                       * @param _proof Merkle trie inclusion proof for the key.
                       * @param _root Known root of the Merkle trie.
                       * @return _exists Whether or not the key exists.
                       * @return _value Value of the key if it exists.
                       */
                      function get(
                          bytes memory _key,
                          bytes memory _proof,
                          bytes32 _root
                      )
                          internal
                          pure
                          returns (
                              bool _exists,
                              bytes memory _value
                          )
                      {
                          TrieNode[] memory proof = _parseProof(_proof);
                          (uint256 pathLength, bytes memory keyRemainder, bool isFinalNode) = _walkNodePath(proof, _key, _root);
                          bool exists = keyRemainder.length == 0;
                          require(
                              exists || isFinalNode,
                              "Provided proof is invalid."
                          );
                          bytes memory value = exists ? _getNodeValue(proof[pathLength - 1]) : bytes('');
                          return (
                              exists,
                              value
                          );
                      }
                      /**
                       * Computes the root hash for a trie with a single node.
                       * @param _key Key for the single node.
                       * @param _value Value for the single node.
                       * @return _updatedRoot Hash of the trie.
                       */
                      function getSingleNodeRootHash(
                          bytes memory _key,
                          bytes memory _value
                      )
                          internal
                          pure
                          returns (
                              bytes32 _updatedRoot
                          )
                      {
                          return keccak256(_makeLeafNode(
                              Lib_BytesUtils.toNibbles(_key),
                              _value
                          ).encoded);
                      }
                      /*********************
                       * Private Functions *
                       *********************/
                      /**
                       * @notice Walks through a proof using a provided key.
                       * @param _proof Inclusion proof to walk through.
                       * @param _key Key to use for the walk.
                       * @param _root Known root of the trie.
                       * @return _pathLength Length of the final path
                       * @return _keyRemainder Portion of the key remaining after the walk.
                       * @return _isFinalNode Whether or not we've hit a dead end.
                       */
                      function _walkNodePath(
                          TrieNode[] memory _proof,
                          bytes memory _key,
                          bytes32 _root
                      )
                          private
                          pure
                          returns (
                              uint256 _pathLength,
                              bytes memory _keyRemainder,
                              bool _isFinalNode
                          )
                      {
                          uint256 pathLength = 0;
                          bytes memory key = Lib_BytesUtils.toNibbles(_key);
                          bytes32 currentNodeID = _root;
                          uint256 currentKeyIndex = 0;
                          uint256 currentKeyIncrement = 0;
                          TrieNode memory currentNode;
                          // Proof is top-down, so we start at the first element (root).
                          for (uint256 i = 0; i < _proof.length; i++) {
                              currentNode = _proof[i];
                              currentKeyIndex += currentKeyIncrement;
                              // Keep track of the proof elements we actually need.
                              // It's expensive to resize arrays, so this simply reduces gas costs.
                              pathLength += 1;
                              if (currentKeyIndex == 0) {
                                  // First proof element is always the root node.
                                  require(
                                      keccak256(currentNode.encoded) == currentNodeID,
                                      "Invalid root hash"
                                  );
                              } else if (currentNode.encoded.length >= 32) {
                                  // Nodes 32 bytes or larger are hashed inside branch nodes.
                                  require(
                                      keccak256(currentNode.encoded) == currentNodeID,
                                      "Invalid large internal hash"
                                  );
                              } else {
                                  // Nodes smaller than 31 bytes aren't hashed.
                                  require(
                                      Lib_BytesUtils.toBytes32(currentNode.encoded) == currentNodeID,
                                      "Invalid internal node hash"
                                  );
                              }
                              if (currentNode.decoded.length == BRANCH_NODE_LENGTH) {
                                  if (currentKeyIndex == key.length) {
                                      // We've hit the end of the key, meaning the value should be within this branch node.
                                      break;
                                  } else {
                                      // We're not at the end of the key yet.
                                      // Figure out what the next node ID should be and continue.
                                      uint8 branchKey = uint8(key[currentKeyIndex]);
                                      Lib_RLPReader.RLPItem memory nextNode = currentNode.decoded[branchKey];
                                      currentNodeID = _getNodeID(nextNode);
                                      currentKeyIncrement = 1;
                                      continue;
                                  }
                              } else if (currentNode.decoded.length == LEAF_OR_EXTENSION_NODE_LENGTH) {
                                  bytes memory path = _getNodePath(currentNode);
                                  uint8 prefix = uint8(path[0]);
                                  uint8 offset = 2 - prefix % 2;
                                  bytes memory pathRemainder = Lib_BytesUtils.slice(path, offset);
                                  bytes memory keyRemainder = Lib_BytesUtils.slice(key, currentKeyIndex);
                                  uint256 sharedNibbleLength = _getSharedNibbleLength(pathRemainder, keyRemainder);
                                  if (prefix == PREFIX_LEAF_EVEN || prefix == PREFIX_LEAF_ODD) {
                                      if (
                                          pathRemainder.length == sharedNibbleLength &&
                                          keyRemainder.length == sharedNibbleLength
                                      ) {
                                          // The key within this leaf matches our key exactly.
                                          // Increment the key index to reflect that we have no remainder.
                                          currentKeyIndex += sharedNibbleLength;
                                      }
                                      // We've hit a leaf node, so our next node should be NULL.
                                      currentNodeID = bytes32(RLP_NULL);
                                      break;
                                  } else if (prefix == PREFIX_EXTENSION_EVEN || prefix == PREFIX_EXTENSION_ODD) {
                                      if (sharedNibbleLength != pathRemainder.length) {
                                          // Our extension node is not identical to the remainder.
                                          // We've hit the end of this path, updates will need to modify this extension.
                                          currentNodeID = bytes32(RLP_NULL);
                                          break;
                                      } else {
                                          // Our extension shares some nibbles.
                                          // Carry on to the next node.
                                          currentNodeID = _getNodeID(currentNode.decoded[1]);
                                          currentKeyIncrement = sharedNibbleLength;
                                          continue;
                                      }
                                  } else {
                                      revert("Received a node with an unknown prefix");
                                  }
                              } else {
                                  revert("Received an unparseable node.");
                              }
                          }
                          // If our node ID is NULL, then we're at a dead end.
                          bool isFinalNode = currentNodeID == bytes32(RLP_NULL);
                          return (pathLength, Lib_BytesUtils.slice(key, currentKeyIndex), isFinalNode);
                      }
                      /**
                       * @notice Creates new nodes to support a k/v pair insertion into a given Merkle trie path.
                       * @param _path Path to the node nearest the k/v pair.
                       * @param _pathLength Length of the path. Necessary because the provided path may include
                       *  additional nodes (e.g., it comes directly from a proof) and we can't resize in-memory
                       *  arrays without costly duplication.
                       * @param _key Full original key.
                       * @param _keyRemainder Portion of the initial key that must be inserted into the trie.
                       * @param _value Value to insert at the given key.
                       * @return _newPath A new path with the inserted k/v pair and extra supporting nodes.
                       */
                      function _getNewPath(
                          TrieNode[] memory _path,
                          uint256 _pathLength,
                          bytes memory _key,
                          bytes memory _keyRemainder,
                          bytes memory _value
                      )
                          private
                          pure
                          returns (
                              TrieNode[] memory _newPath
                          )
                      {
                          bytes memory keyRemainder = _keyRemainder;
                          // Most of our logic depends on the status of the last node in the path.
                          TrieNode memory lastNode = _path[_pathLength - 1];
                          NodeType lastNodeType = _getNodeType(lastNode);
                          // Create an array for newly created nodes.
                          // We need up to three new nodes, depending on the contents of the last node.
                          // Since array resizing is expensive, we'll keep track of the size manually.
                          // We're using an explicit `totalNewNodes += 1` after insertions for clarity.
                          TrieNode[] memory newNodes = new TrieNode[](3);
                          uint256 totalNewNodes = 0;
                          // Reference: https://github.com/ethereumjs/merkle-patricia-tree/blob/c0a10395aab37d42c175a47114ebfcbd7efcf059/src/baseTrie.ts#L294-L313
                          bool matchLeaf = false;
                          if (lastNodeType == NodeType.LeafNode) {
                              uint256 l = 0;
                              if (_path.length > 0) {
                                  for (uint256 i = 0; i < _path.length - 1; i++) {
                                      if (_getNodeType(_path[i]) == NodeType.BranchNode) {
                                          l++;
                                      } else {
                                          l += _getNodeKey(_path[i]).length;
                                      }
                                  }
                              }
                              if (
                                  _getSharedNibbleLength(
                                      _getNodeKey(lastNode),
                                      Lib_BytesUtils.slice(Lib_BytesUtils.toNibbles(_key), l)
                                  ) == _getNodeKey(lastNode).length
                                  && keyRemainder.length == 0
                              ) {
                                  matchLeaf = true;
                              }
                          }
                          if (matchLeaf) {
                              // We've found a leaf node with the given key.
                              // Simply need to update the value of the node to match.
                              newNodes[totalNewNodes] = _makeLeafNode(_getNodeKey(lastNode), _value);
                              totalNewNodes += 1;
                          } else if (lastNodeType == NodeType.BranchNode) {
                              if (keyRemainder.length == 0) {
                                  // We've found a branch node with the given key.
                                  // Simply need to update the value of the node to match.
                                  newNodes[totalNewNodes] = _editBranchValue(lastNode, _value);
                                  totalNewNodes += 1;
                              } else {
                                  // We've found a branch node, but it doesn't contain our key.
                                  // Reinsert the old branch for now.
                                  newNodes[totalNewNodes] = lastNode;
                                  totalNewNodes += 1;
                                  // Create a new leaf node, slicing our remainder since the first byte points
                                  // to our branch node.
                                  newNodes[totalNewNodes] = _makeLeafNode(Lib_BytesUtils.slice(keyRemainder, 1), _value);
                                  totalNewNodes += 1;
                              }
                          } else {
                              // Our last node is either an extension node or a leaf node with a different key.
                              bytes memory lastNodeKey = _getNodeKey(lastNode);
                              uint256 sharedNibbleLength = _getSharedNibbleLength(lastNodeKey, keyRemainder);
                              if (sharedNibbleLength != 0) {
                                  // We've got some shared nibbles between the last node and our key remainder.
                                  // We'll need to insert an extension node that covers these shared nibbles.
                                  bytes memory nextNodeKey = Lib_BytesUtils.slice(lastNodeKey, 0, sharedNibbleLength);
                                  newNodes[totalNewNodes] = _makeExtensionNode(nextNodeKey, _getNodeHash(_value));
                                  totalNewNodes += 1;
                                  // Cut down the keys since we've just covered these shared nibbles.
                                  lastNodeKey = Lib_BytesUtils.slice(lastNodeKey, sharedNibbleLength);
                                  keyRemainder = Lib_BytesUtils.slice(keyRemainder, sharedNibbleLength);
                              }
                              // Create an empty branch to fill in.
                              TrieNode memory newBranch = _makeEmptyBranchNode();
                              if (lastNodeKey.length == 0) {
                                  // Key remainder was larger than the key for our last node.
                                  // The value within our last node is therefore going to be shifted into
                                  // a branch value slot.
                                  newBranch = _editBranchValue(newBranch, _getNodeValue(lastNode));
                              } else {
                                  // Last node key was larger than the key remainder.
                                  // We're going to modify some index of our branch.
                                  uint8 branchKey = uint8(lastNodeKey[0]);
                                  // Move on to the next nibble.
                                  lastNodeKey = Lib_BytesUtils.slice(lastNodeKey, 1);
                                  if (lastNodeType == NodeType.LeafNode) {
                                      // We're dealing with a leaf node.
                                      // We'll modify the key and insert the old leaf node into the branch index.
                                      TrieNode memory modifiedLastNode = _makeLeafNode(lastNodeKey, _getNodeValue(lastNode));
                                      newBranch = _editBranchIndex(newBranch, branchKey, _getNodeHash(modifiedLastNode.encoded));
                                  } else if (lastNodeKey.length != 0) {
                                      // We're dealing with a shrinking extension node.
                                      // We need to modify the node to decrease the size of the key.
                                      TrieNode memory modifiedLastNode = _makeExtensionNode(lastNodeKey, _getNodeValue(lastNode));
                                      newBranch = _editBranchIndex(newBranch, branchKey, _getNodeHash(modifiedLastNode.encoded));
                                  } else {
                                      // We're dealing with an unnecessary extension node.
                                      // We're going to delete the node entirely.
                                      // Simply insert its current value into the branch index.
                                      newBranch = _editBranchIndex(newBranch, branchKey, _getNodeValue(lastNode));
                                  }
                              }
                              if (keyRemainder.length == 0) {
                                  // We've got nothing left in the key remainder.
                                  // Simply insert the value into the branch value slot.
                                  newBranch = _editBranchValue(newBranch, _value);
                                  // Push the branch into the list of new nodes.
                                  newNodes[totalNewNodes] = newBranch;
                                  totalNewNodes += 1;
                              } else {
                                  // We've got some key remainder to work with.
                                  // We'll be inserting a leaf node into the trie.
                                  // First, move on to the next nibble.
                                  keyRemainder = Lib_BytesUtils.slice(keyRemainder, 1);
                                  // Push the branch into the list of new nodes.
                                  newNodes[totalNewNodes] = newBranch;
                                  totalNewNodes += 1;
                                  // Push a new leaf node for our k/v pair.
                                  newNodes[totalNewNodes] = _makeLeafNode(keyRemainder, _value);
                                  totalNewNodes += 1;
                              }
                          }
                          // Finally, join the old path with our newly created nodes.
                          // Since we're overwriting the last node in the path, we use `_pathLength - 1`.
                          return _joinNodeArrays(_path, _pathLength - 1, newNodes, totalNewNodes);
                      }
                      /**
                       * @notice Computes the trie root from a given path.
                       * @param _nodes Path to some k/v pair.
                       * @param _key Key for the k/v pair.
                       * @return _updatedRoot Root hash for the updated trie.
                       */
                      function _getUpdatedTrieRoot(
                          TrieNode[] memory _nodes,
                          bytes memory _key
                      )
                          private
                          pure
                          returns (
                              bytes32 _updatedRoot
                          )
                      {
                          bytes memory key = Lib_BytesUtils.toNibbles(_key);
                          // Some variables to keep track of during iteration.
                          TrieNode memory currentNode;
                          NodeType currentNodeType;
                          bytes memory previousNodeHash;
                          // Run through the path backwards to rebuild our root hash.
                          for (uint256 i = _nodes.length; i > 0; i--) {
                              // Pick out the current node.
                              currentNode = _nodes[i - 1];
                              currentNodeType = _getNodeType(currentNode);
                              if (currentNodeType == NodeType.LeafNode) {
                                  // Leaf nodes are already correctly encoded.
                                  // Shift the key over to account for the nodes key.
                                  bytes memory nodeKey = _getNodeKey(currentNode);
                                  key = Lib_BytesUtils.slice(key, 0, key.length - nodeKey.length);
                              } else if (currentNodeType == NodeType.ExtensionNode) {
                                  // Shift the key over to account for the nodes key.
                                  bytes memory nodeKey = _getNodeKey(currentNode);
                                  key = Lib_BytesUtils.slice(key, 0, key.length - nodeKey.length);
                                  // If this node is the last element in the path, it'll be correctly encoded
                                  // and we can skip this part.
                                  if (previousNodeHash.length > 0) {
                                      // Re-encode the node based on the previous node.
                                      currentNode = _editExtensionNodeValue(currentNode, previousNodeHash);
                                  }
                              } else if (currentNodeType == NodeType.BranchNode) {
                                  // If this node is the last element in the path, it'll be correctly encoded
                                  // and we can skip this part.
                                  if (previousNodeHash.length > 0) {
                                      // Re-encode the node based on the previous node.
                                      uint8 branchKey = uint8(key[key.length - 1]);
                                      key = Lib_BytesUtils.slice(key, 0, key.length - 1);
                                      currentNode = _editBranchIndex(currentNode, branchKey, previousNodeHash);
                                  }
                              }
                              // Compute the node hash for the next iteration.
                              previousNodeHash = _getNodeHash(currentNode.encoded);
                          }
                          // Current node should be the root at this point.
                          // Simply return the hash of its encoding.
                          return keccak256(currentNode.encoded);
                      }
                      /**
                       * @notice Parses an RLP-encoded proof into something more useful.
                       * @param _proof RLP-encoded proof to parse.
                       * @return _parsed Proof parsed into easily accessible structs.
                       */
                      function _parseProof(
                          bytes memory _proof
                      )
                          private
                          pure
                          returns (
                              TrieNode[] memory _parsed
                          )
                      {
                          Lib_RLPReader.RLPItem[] memory nodes = Lib_RLPReader.readList(_proof);
                          TrieNode[] memory proof = new TrieNode[](nodes.length);
                          for (uint256 i = 0; i < nodes.length; i++) {
                              bytes memory encoded = Lib_RLPReader.readBytes(nodes[i]);
                              proof[i] = TrieNode({
                                  encoded: encoded,
                                  decoded: Lib_RLPReader.readList(encoded)
                              });
                          }
                          return proof;
                      }
                      /**
                       * @notice Picks out the ID for a node. Node ID is referred to as the
                       * "hash" within the specification, but nodes < 32 bytes are not actually
                       * hashed.
                       * @param _node Node to pull an ID for.
                       * @return _nodeID ID for the node, depending on the size of its contents.
                       */
                      function _getNodeID(
                          Lib_RLPReader.RLPItem memory _node
                      )
                          private
                          pure
                          returns (
                              bytes32 _nodeID
                          )
                      {
                          bytes memory nodeID;
                          if (_node.length < 32) {
                              // Nodes smaller than 32 bytes are RLP encoded.
                              nodeID = Lib_RLPReader.readRawBytes(_node);
                          } else {
                              // Nodes 32 bytes or larger are hashed.
                              nodeID = Lib_RLPReader.readBytes(_node);
                          }
                          return Lib_BytesUtils.toBytes32(nodeID);
                      }
                      /**
                       * @notice Gets the path for a leaf or extension node.
                       * @param _node Node to get a path for.
                       * @return _path Node path, converted to an array of nibbles.
                       */
                      function _getNodePath(
                          TrieNode memory _node
                      )
                          private
                          pure
                          returns (
                              bytes memory _path
                          )
                      {
                          return Lib_BytesUtils.toNibbles(Lib_RLPReader.readBytes(_node.decoded[0]));
                      }
                      /**
                       * @notice Gets the key for a leaf or extension node. Keys are essentially
                       * just paths without any prefix.
                       * @param _node Node to get a key for.
                       * @return _key Node key, converted to an array of nibbles.
                       */
                      function _getNodeKey(
                          TrieNode memory _node
                      )
                          private
                          pure
                          returns (
                              bytes memory _key
                          )
                      {
                          return _removeHexPrefix(_getNodePath(_node));
                      }
                      /**
                       * @notice Gets the path for a node.
                       * @param _node Node to get a value for.
                       * @return _value Node value, as hex bytes.
                       */
                      function _getNodeValue(
                          TrieNode memory _node
                      )
                          private
                          pure
                          returns (
                              bytes memory _value
                          )
                      {
                          return Lib_RLPReader.readBytes(_node.decoded[_node.decoded.length - 1]);
                      }
                      /**
                       * @notice Computes the node hash for an encoded node. Nodes < 32 bytes
                       * are not hashed, all others are keccak256 hashed.
                       * @param _encoded Encoded node to hash.
                       * @return _hash Hash of the encoded node. Simply the input if < 32 bytes.
                       */
                      function _getNodeHash(
                          bytes memory _encoded
                      )
                          private
                          pure
                          returns (
                              bytes memory _hash
                          )
                      {
                          if (_encoded.length < 32) {
                              return _encoded;
                          } else {
                              return abi.encodePacked(keccak256(_encoded));
                          }
                      }
                      /**
                       * @notice Determines the type for a given node.
                       * @param _node Node to determine a type for.
                       * @return _type Type of the node; BranchNode/ExtensionNode/LeafNode.
                       */
                      function _getNodeType(
                          TrieNode memory _node
                      )
                          private
                          pure
                          returns (
                              NodeType _type
                          )
                      {
                          if (_node.decoded.length == BRANCH_NODE_LENGTH) {
                              return NodeType.BranchNode;
                          } else if (_node.decoded.length == LEAF_OR_EXTENSION_NODE_LENGTH) {
                              bytes memory path = _getNodePath(_node);
                              uint8 prefix = uint8(path[0]);
                              if (prefix == PREFIX_LEAF_EVEN || prefix == PREFIX_LEAF_ODD) {
                                  return NodeType.LeafNode;
                              } else if (prefix == PREFIX_EXTENSION_EVEN || prefix == PREFIX_EXTENSION_ODD) {
                                  return NodeType.ExtensionNode;
                              }
                          }
                          revert("Invalid node type");
                      }
                      /**
                       * @notice Utility; determines the number of nibbles shared between two
                       * nibble arrays.
                       * @param _a First nibble array.
                       * @param _b Second nibble array.
                       * @return _shared Number of shared nibbles.
                       */
                      function _getSharedNibbleLength(
                          bytes memory _a,
                          bytes memory _b
                      )
                          private
                          pure
                          returns (
                              uint256 _shared
                          )
                      {
                          uint256 i = 0;
                          while (_a.length > i && _b.length > i && _a[i] == _b[i]) {
                              i++;
                          }
                          return i;
                      }
                      /**
                       * @notice Utility; converts an RLP-encoded node into our nice struct.
                       * @param _raw RLP-encoded node to convert.
                       * @return _node Node as a TrieNode struct.
                       */
                      function _makeNode(
                          bytes[] memory _raw
                      )
                          private
                          pure
                          returns (
                              TrieNode memory _node
                          )
                      {
                          bytes memory encoded = Lib_RLPWriter.writeList(_raw);
                          return TrieNode({
                              encoded: encoded,
                              decoded: Lib_RLPReader.readList(encoded)
                          });
                      }
                      /**
                       * @notice Utility; converts an RLP-decoded node into our nice struct.
                       * @param _items RLP-decoded node to convert.
                       * @return _node Node as a TrieNode struct.
                       */
                      function _makeNode(
                          Lib_RLPReader.RLPItem[] memory _items
                      )
                          private
                          pure
                          returns (
                              TrieNode memory _node
                          )
                      {
                          bytes[] memory raw = new bytes[](_items.length);
                          for (uint256 i = 0; i < _items.length; i++) {
                              raw[i] = Lib_RLPReader.readRawBytes(_items[i]);
                          }
                          return _makeNode(raw);
                      }
                      /**
                       * @notice Creates a new extension node.
                       * @param _key Key for the extension node, unprefixed.
                       * @param _value Value for the extension node.
                       * @return _node New extension node with the given k/v pair.
                       */
                      function _makeExtensionNode(
                          bytes memory _key,
                          bytes memory _value
                      )
                          private
                          pure
                          returns (
                              TrieNode memory _node
                          )
                      {
                          bytes[] memory raw = new bytes[](2);
                          bytes memory key = _addHexPrefix(_key, false);
                          raw[0] = Lib_RLPWriter.writeBytes(Lib_BytesUtils.fromNibbles(key));
                          raw[1] = Lib_RLPWriter.writeBytes(_value);
                          return _makeNode(raw);
                      }
                      /**
                       * Creates a new extension node with the same key but a different value.
                       * @param _node Extension node to copy and modify.
                       * @param _value New value for the extension node.
                       * @return New node with the same key and different value.
                       */
                      function _editExtensionNodeValue(
                          TrieNode memory _node,
                          bytes memory _value
                      )
                          private
                          pure
                          returns (
                              TrieNode memory
                          )
                      {
                          bytes[] memory raw = new bytes[](2);
                          bytes memory key = _addHexPrefix(_getNodeKey(_node), false);
                          raw[0] = Lib_RLPWriter.writeBytes(Lib_BytesUtils.fromNibbles(key));
                          if (_value.length < 32) {
                              raw[1] = _value;
                          } else {
                              raw[1] = Lib_RLPWriter.writeBytes(_value);
                          }
                          return _makeNode(raw);
                      }
                      /**
                       * @notice Creates a new leaf node.
                       * @dev This function is essentially identical to `_makeExtensionNode`.
                       * Although we could route both to a single method with a flag, it's
                       * more gas efficient to keep them separate and duplicate the logic.
                       * @param _key Key for the leaf node, unprefixed.
                       * @param _value Value for the leaf node.
                       * @return _node New leaf node with the given k/v pair.
                       */
                      function _makeLeafNode(
                          bytes memory _key,
                          bytes memory _value
                      )
                          private
                          pure
                          returns (
                              TrieNode memory _node
                          )
                      {
                          bytes[] memory raw = new bytes[](2);
                          bytes memory key = _addHexPrefix(_key, true);
                          raw[0] = Lib_RLPWriter.writeBytes(Lib_BytesUtils.fromNibbles(key));
                          raw[1] = Lib_RLPWriter.writeBytes(_value);
                          return _makeNode(raw);
                      }
                      /**
                       * @notice Creates an empty branch node.
                       * @return _node Empty branch node as a TrieNode struct.
                       */
                      function _makeEmptyBranchNode()
                          private
                          pure
                          returns (
                              TrieNode memory _node
                          )
                      {
                          bytes[] memory raw = new bytes[](BRANCH_NODE_LENGTH);
                          for (uint256 i = 0; i < raw.length; i++) {
                              raw[i] = RLP_NULL_BYTES;
                          }
                          return _makeNode(raw);
                      }
                      /**
                       * @notice Modifies the value slot for a given branch.
                       * @param _branch Branch node to modify.
                       * @param _value Value to insert into the branch.
                       * @return _updatedNode Modified branch node.
                       */
                      function _editBranchValue(
                          TrieNode memory _branch,
                          bytes memory _value
                      )
                          private
                          pure
                          returns (
                              TrieNode memory _updatedNode
                          )
                      {
                          bytes memory encoded = Lib_RLPWriter.writeBytes(_value);
                          _branch.decoded[_branch.decoded.length - 1] = Lib_RLPReader.toRLPItem(encoded);
                          return _makeNode(_branch.decoded);
                      }
                      /**
                       * @notice Modifies a slot at an index for a given branch.
                       * @param _branch Branch node to modify.
                       * @param _index Slot index to modify.
                       * @param _value Value to insert into the slot.
                       * @return _updatedNode Modified branch node.
                       */
                      function _editBranchIndex(
                          TrieNode memory _branch,
                          uint8 _index,
                          bytes memory _value
                      )
                          private
                          pure
                          returns (
                              TrieNode memory _updatedNode
                          )
                      {
                          bytes memory encoded = _value.length < 32 ? _value : Lib_RLPWriter.writeBytes(_value);
                          _branch.decoded[_index] = Lib_RLPReader.toRLPItem(encoded);
                          return _makeNode(_branch.decoded);
                      }
                      /**
                       * @notice Utility; adds a prefix to a key.
                       * @param _key Key to prefix.
                       * @param _isLeaf Whether or not the key belongs to a leaf.
                       * @return _prefixedKey Prefixed key.
                       */
                      function _addHexPrefix(
                          bytes memory _key,
                          bool _isLeaf
                      )
                          private
                          pure
                          returns (
                              bytes memory _prefixedKey
                          )
                      {
                          uint8 prefix = _isLeaf ? uint8(0x02) : uint8(0x00);
                          uint8 offset = uint8(_key.length % 2);
                          bytes memory prefixed = new bytes(2 - offset);
                          prefixed[0] = bytes1(prefix + offset);
                          return abi.encodePacked(prefixed, _key);
                      }
                      /**
                       * @notice Utility; removes a prefix from a path.
                       * @param _path Path to remove the prefix from.
                       * @return _unprefixedKey Unprefixed key.
                       */
                      function _removeHexPrefix(
                          bytes memory _path
                      )
                          private
                          pure
                          returns (
                              bytes memory _unprefixedKey
                          )
                      {
                          if (uint8(_path[0]) % 2 == 0) {
                              return Lib_BytesUtils.slice(_path, 2);
                          } else {
                              return Lib_BytesUtils.slice(_path, 1);
                          }
                      }
                      /**
                       * @notice Utility; combines two node arrays. Array lengths are required
                       * because the actual lengths may be longer than the filled lengths.
                       * Array resizing is extremely costly and should be avoided.
                       * @param _a First array to join.
                       * @param _aLength Length of the first array.
                       * @param _b Second array to join.
                       * @param _bLength Length of the second array.
                       * @return _joined Combined node array.
                       */
                      function _joinNodeArrays(
                          TrieNode[] memory _a,
                          uint256 _aLength,
                          TrieNode[] memory _b,
                          uint256 _bLength
                      )
                          private
                          pure
                          returns (
                              TrieNode[] memory _joined
                          )
                      {
                          TrieNode[] memory ret = new TrieNode[](_aLength + _bLength);
                          // Copy elements from the first array.
                          for (uint256 i = 0; i < _aLength; i++) {
                              ret[i] = _a[i];
                          }
                          // Copy elements from the second array.
                          for (uint256 i = 0; i < _bLength; i++) {
                              ret[i + _aLength] = _b[i];
                          }
                          return ret;
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  pragma experimental ABIEncoderV2;
                  /* Library Imports */
                  import { Lib_MerkleTrie } from "./Lib_MerkleTrie.sol";
                  /**
                   * @title Lib_SecureMerkleTrie
                   */
                  library Lib_SecureMerkleTrie {
                      /**********************
                       * Internal Functions *
                       **********************/
                      /**
                       * @notice Verifies a proof that a given key/value pair is present in the
                       * Merkle trie.
                       * @param _key Key of the node to search for, as a hex string.
                       * @param _value Value of the node to search for, as a hex string.
                       * @param _proof Merkle trie inclusion proof for the desired node. Unlike
                       * traditional Merkle trees, this proof is executed top-down and consists
                       * of a list of RLP-encoded nodes that make a path down to the target node.
                       * @param _root Known root of the Merkle trie. Used to verify that the
                       * included proof is correctly constructed.
                       * @return _verified `true` if the k/v pair exists in the trie, `false` otherwise.
                       */
                      function verifyInclusionProof(
                          bytes memory _key,
                          bytes memory _value,
                          bytes memory _proof,
                          bytes32 _root
                      )
                          internal
                          pure
                          returns (
                              bool _verified
                          )
                      {
                          bytes memory key = _getSecureKey(_key);
                          return Lib_MerkleTrie.verifyInclusionProof(key, _value, _proof, _root);
                      }
                      /**
                       * @notice Updates a Merkle trie and returns a new root hash.
                       * @param _key Key of the node to update, as a hex string.
                       * @param _value Value of the node to update, as a hex string.
                       * @param _proof Merkle trie inclusion proof for the node *nearest* the
                       * target node. If the key exists, we can simply update the value.
                       * Otherwise, we need to modify the trie to handle the new k/v pair.
                       * @param _root Known root of the Merkle trie. Used to verify that the
                       * included proof is correctly constructed.
                       * @return _updatedRoot Root hash of the newly constructed trie.
                       */
                      function update(
                          bytes memory _key,
                          bytes memory _value,
                          bytes memory _proof,
                          bytes32 _root
                      )
                          internal
                          pure
                          returns (
                              bytes32 _updatedRoot
                          )
                      {
                          bytes memory key = _getSecureKey(_key);
                          return Lib_MerkleTrie.update(key, _value, _proof, _root);
                      }
                      /**
                       * @notice Retrieves the value associated with a given key.
                       * @param _key Key to search for, as hex bytes.
                       * @param _proof Merkle trie inclusion proof for the key.
                       * @param _root Known root of the Merkle trie.
                       * @return _exists Whether or not the key exists.
                       * @return _value Value of the key if it exists.
                       */
                      function get(
                          bytes memory _key,
                          bytes memory _proof,
                          bytes32 _root
                      )
                          internal
                          pure
                          returns (
                              bool _exists,
                              bytes memory _value
                          )
                      {
                          bytes memory key = _getSecureKey(_key);
                          return Lib_MerkleTrie.get(key, _proof, _root);
                      }
                      /**
                       * Computes the root hash for a trie with a single node.
                       * @param _key Key for the single node.
                       * @param _value Value for the single node.
                       * @return _updatedRoot Hash of the trie.
                       */
                      function getSingleNodeRootHash(
                          bytes memory _key,
                          bytes memory _value
                      )
                          internal
                          pure
                          returns (
                              bytes32 _updatedRoot
                          )
                      {
                          bytes memory key = _getSecureKey(_key);
                          return Lib_MerkleTrie.getSingleNodeRootHash(key, _value);
                      }
                      /*********************
                       * Private Functions *
                       *********************/
                      /**
                       * Computes the secure counterpart to a key.
                       * @param _key Key to get a secure key from.
                       * @return _secureKey Secure version of the key.
                       */
                      function _getSecureKey(
                          bytes memory _key
                      )
                          private
                          pure
                          returns (
                              bytes memory _secureKey
                          )
                      {
                          return abi.encodePacked(keccak256(_key));
                      }
                  }// SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  /**
                   * @title Lib_Byte32Utils
                   */
                  library Lib_Bytes32Utils {
                      /**********************
                       * Internal Functions *
                       **********************/
                      /**
                       * Converts a bytes32 value to a boolean. Anything non-zero will be converted to "true."
                       * @param _in Input bytes32 value.
                       * @return Bytes32 as a boolean.
                       */
                      function toBool(
                          bytes32 _in
                      )
                          internal
                          pure
                          returns (
                              bool
                          )
                      {
                          return _in != 0;
                      }
                      /**
                       * Converts a boolean to a bytes32 value.
                       * @param _in Input boolean value.
                       * @return Boolean as a bytes32.
                       */
                      function fromBool(
                          bool _in
                      )
                          internal
                          pure
                          returns (
                              bytes32
                          )
                      {
                          return bytes32(uint256(_in ? 1 : 0));
                      }
                      /**
                       * Converts a bytes32 value to an address. Takes the *last* 20 bytes.
                       * @param _in Input bytes32 value.
                       * @return Bytes32 as an address.
                       */
                      function toAddress(
                          bytes32 _in
                      )
                          internal
                          pure
                          returns (
                              address
                          )
                      {
                          return address(uint160(uint256(_in)));
                      }
                      /**
                       * Converts an address to a bytes32.
                       * @param _in Input address value.
                       * @return Address as a bytes32.
                       */
                      function fromAddress(
                          address _in
                      )
                          internal
                          pure
                          returns (
                              bytes32
                          )
                      {
                          return bytes32(uint256(_in));
                      }
                      /**
                       * Removes the leading zeros from a bytes32 value and returns a new (smaller) bytes value.
                       * @param _in Input bytes32 value.
                       * @return Bytes32 without any leading zeros.
                       */
                      function removeLeadingZeros(
                          bytes32 _in
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          bytes memory out;
                          assembly {
                              // Figure out how many leading zero bytes to remove.
                              let shift := 0
                              for { let i := 0 } and(lt(i, 32), eq(byte(i, _in), 0)) { i := add(i, 1) } {
                                  shift := add(shift, 1)
                              }
                              // Reserve some space for our output and fix the free memory pointer.
                              out := mload(0x40)
                              mstore(0x40, add(out, 0x40))
                              // Shift the value and store it into the output bytes.
                              mstore(add(out, 0x20), shl(mul(shift, 8), _in))
                              // Store the new size (with leading zero bytes removed) in the output byte size.
                              mstore(out, sub(32, shift))
                          }
                          return out;
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  /**
                   * @title Lib_BytesUtils
                   */
                  library Lib_BytesUtils {
                      /**********************
                       * Internal Functions *
                       **********************/
                      function slice(
                          bytes memory _bytes,
                          uint256 _start,
                          uint256 _length
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          require(_length + 31 >= _length, "slice_overflow");
                          require(_start + _length >= _start, "slice_overflow");
                          require(_bytes.length >= _start + _length, "slice_outOfBounds");
                          bytes memory tempBytes;
                          assembly {
                              switch iszero(_length)
                              case 0 {
                                  // Get a location of some free memory and store it in tempBytes as
                                  // Solidity does for memory variables.
                                  tempBytes := mload(0x40)
                                  // The first word of the slice result is potentially a partial
                                  // word read from the original array. To read it, we calculate
                                  // the length of that partial word and start copying that many
                                  // bytes into the array. The first word we copy will start with
                                  // data we don't care about, but the last `lengthmod` bytes will
                                  // land at the beginning of the contents of the new array. When
                                  // we're done copying, we overwrite the full first word with
                                  // the actual length of the slice.
                                  let lengthmod := and(_length, 31)
                                  // The multiplication in the next line is necessary
                                  // because when slicing multiples of 32 bytes (lengthmod == 0)
                                  // the following copy loop was copying the origin's length
                                  // and then ending prematurely not copying everything it should.
                                  let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
                                  let end := add(mc, _length)
                                  for {
                                      // The multiplication in the next line has the same exact purpose
                                      // as the one above.
                                      let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
                                  } lt(mc, end) {
                                      mc := add(mc, 0x20)
                                      cc := add(cc, 0x20)
                                  } {
                                      mstore(mc, mload(cc))
                                  }
                                  mstore(tempBytes, _length)
                                  //update free-memory pointer
                                  //allocating the array padded to 32 bytes like the compiler does now
                                  mstore(0x40, and(add(mc, 31), not(31)))
                              }
                              //if we want a zero-length slice let's just return a zero-length array
                              default {
                                  tempBytes := mload(0x40)
                                  //zero out the 32 bytes slice we are about to return
                                  //we need to do it because Solidity does not garbage collect
                                  mstore(tempBytes, 0)
                                  mstore(0x40, add(tempBytes, 0x20))
                              }
                          }
                          return tempBytes;
                      }
                      function slice(
                          bytes memory _bytes,
                          uint256 _start
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          if (_start >= _bytes.length) {
                              return bytes('');
                          }
                          return slice(_bytes, _start, _bytes.length - _start);
                      }
                      function toBytes32PadLeft(
                          bytes memory _bytes
                      )
                          internal
                          pure
                          returns (
                              bytes32
                          )
                      {
                          bytes32 ret;
                          uint256 len = _bytes.length <= 32 ? _bytes.length : 32;
                          assembly {
                              ret := shr(mul(sub(32, len), 8), mload(add(_bytes, 32)))
                          }
                          return ret;
                      }
                      function toBytes32(
                          bytes memory _bytes
                      )
                          internal
                          pure
                          returns (
                              bytes32
                          )
                      {
                          if (_bytes.length < 32) {
                              bytes32 ret;
                              assembly {
                                  ret := mload(add(_bytes, 32))
                              }
                              return ret;
                          }
                          return abi.decode(_bytes,(bytes32)); // will truncate if input length > 32 bytes
                      }
                      function toUint256(
                          bytes memory _bytes
                      )
                          internal
                          pure
                          returns (
                              uint256
                          )
                      {
                          return uint256(toBytes32(_bytes));
                      }
                      function toUint24(
                          bytes memory _bytes,
                          uint256 _start
                      )
                          internal
                          pure
                          returns (
                              uint24
                          )
                      {
                          require(_start + 3 >= _start, "toUint24_overflow");
                          require(_bytes.length >= _start + 3 , "toUint24_outOfBounds");
                          uint24 tempUint;
                          assembly {
                              tempUint := mload(add(add(_bytes, 0x3), _start))
                          }
                          return tempUint;
                      }
                      function toUint8(
                          bytes memory _bytes,
                          uint256 _start
                      )
                          internal
                          pure
                          returns (
                              uint8
                          )
                      {
                          require(_start + 1 >= _start, "toUint8_overflow");
                          require(_bytes.length >= _start + 1 , "toUint8_outOfBounds");
                          uint8 tempUint;
                          assembly {
                              tempUint := mload(add(add(_bytes, 0x1), _start))
                          }
                          return tempUint;
                      }
                      function toAddress(
                          bytes memory _bytes,
                          uint256 _start
                      )
                          internal
                          pure
                          returns (
                              address
                          )
                      {
                          require(_start + 20 >= _start, "toAddress_overflow");
                          require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
                          address tempAddress;
                          assembly {
                              tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
                          }
                          return tempAddress;
                      }
                      function toNibbles(
                          bytes memory _bytes
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          bytes memory nibbles = new bytes(_bytes.length * 2);
                          for (uint256 i = 0; i < _bytes.length; i++) {
                              nibbles[i * 2] = _bytes[i] >> 4;
                              nibbles[i * 2 + 1] = bytes1(uint8(_bytes[i]) % 16);
                          }
                          return nibbles;
                      }
                      function fromNibbles(
                          bytes memory _bytes
                      )
                          internal
                          pure
                          returns (
                              bytes memory
                          )
                      {
                          bytes memory ret = new bytes(_bytes.length / 2);
                          for (uint256 i = 0; i < ret.length; i++) {
                              ret[i] = (_bytes[i * 2] << 4) | (_bytes[i * 2 + 1]);
                          }
                          return ret;
                      }
                      function equal(
                          bytes memory _bytes,
                          bytes memory _other
                      )
                          internal
                          pure
                          returns (
                              bool
                          )
                      {
                          return keccak256(_bytes) == keccak256(_other);
                      }
                  }
                  

                  File 7 of 7: OVM_ChainStorageContainer
                  // SPDX-License-Identifier: MIT
                  pragma solidity >=0.6.0 <0.8.0;
                  import "../utils/Context.sol";
                  /**
                   * @dev Contract module which provides a basic access control mechanism, where
                   * there is an account (an owner) that can be granted exclusive access to
                   * specific functions.
                   *
                   * By default, the owner account will be the one that deploys the contract. This
                   * can later be changed with {transferOwnership}.
                   *
                   * This module is used through inheritance. It will make available the modifier
                   * `onlyOwner`, which can be applied to your functions to restrict their use to
                   * the owner.
                   */
                  abstract contract Ownable is Context {
                      address private _owner;
                      event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                      /**
                       * @dev Initializes the contract setting the deployer as the initial owner.
                       */
                      constructor () internal {
                          address msgSender = _msgSender();
                          _owner = msgSender;
                          emit OwnershipTransferred(address(0), msgSender);
                      }
                      /**
                       * @dev Returns the address of the current owner.
                       */
                      function owner() public view virtual returns (address) {
                          return _owner;
                      }
                      /**
                       * @dev Throws if called by any account other than the owner.
                       */
                      modifier onlyOwner() {
                          require(owner() == _msgSender(), "Ownable: caller is not the owner");
                          _;
                      }
                      /**
                       * @dev Leaves the contract without owner. It will not be possible to call
                       * `onlyOwner` functions anymore. Can only be called by the current owner.
                       *
                       * NOTE: Renouncing ownership will leave the contract without an owner,
                       * thereby removing any functionality that is only available to the owner.
                       */
                      function renounceOwnership() public virtual onlyOwner {
                          emit OwnershipTransferred(_owner, address(0));
                          _owner = 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");
                          emit OwnershipTransferred(_owner, newOwner);
                          _owner = newOwner;
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >=0.6.0 <0.8.0;
                  /*
                   * @dev Provides information about the current execution context, including the
                   * sender of the transaction and its data. While these are generally available
                   * via msg.sender and msg.data, they should not be accessed in such a direct
                   * manner, since when dealing with GSN meta-transactions the account sending and
                   * paying for execution may not be the actual sender (as far as an application
                   * is concerned).
                   *
                   * This contract is only required for intermediate, library-like contracts.
                   */
                  abstract contract Context {
                      function _msgSender() internal view virtual returns (address payable) {
                          return msg.sender;
                      }
                      function _msgData() internal view virtual returns (bytes memory) {
                          this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                          return msg.data;
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  /* Library Imports */
                  import { Lib_Buffer } from "../../libraries/utils/Lib_Buffer.sol";
                  import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
                  /* Interface Imports */
                  import { iOVM_ChainStorageContainer } from "../../iOVM/chain/iOVM_ChainStorageContainer.sol";
                  /**
                   * @title OVM_ChainStorageContainer
                   * @dev The Chain Storage Container provides its owner contract with read, write and delete functionality.
                   * This provides gas efficiency gains by enabling it to overwrite storage slots which can no longer be used
                   * in a fraud proof due to the fraud window having passed, and the associated chain state or
                   * transactions being finalized.
                   * Three distinct Chain Storage Containers will be deployed on Layer 1:
                   * 1. Stores transaction batches for the Canonical Transaction Chain
                   * 2. Stores queued transactions for the Canonical Transaction Chain
                   * 3. Stores chain state batches for the State Commitment Chain
                   *
                   * Compiler used: solc
                   * Runtime target: EVM
                   */
                  contract OVM_ChainStorageContainer is iOVM_ChainStorageContainer, Lib_AddressResolver {
                      /*************
                       * Libraries *
                       *************/
                      using Lib_Buffer for Lib_Buffer.Buffer;
                      /*************
                       * Variables *
                       *************/
                      string public owner;
                      Lib_Buffer.Buffer internal buffer;
                      /***************
                       * Constructor *
                       ***************/
                      /**
                       * @param _libAddressManager Address of the Address Manager.
                       * @param _owner Name of the contract that owns this container (will be resolved later).
                       */
                      constructor(
                          address _libAddressManager,
                          string memory _owner
                      )
                          Lib_AddressResolver(_libAddressManager)
                      {
                          owner = _owner;
                      }
                      /**********************
                       * Function Modifiers *
                       **********************/
                      modifier onlyOwner() {
                          require(
                              msg.sender == resolve(owner),
                              "OVM_ChainStorageContainer: Function can only be called by the owner."
                          );
                          _;
                      }
                      /********************
                       * Public Functions *
                       ********************/
                      /**
                       * @inheritdoc iOVM_ChainStorageContainer
                       */
                      function setGlobalMetadata(
                          bytes27 _globalMetadata
                      )
                          override
                          public
                          onlyOwner
                      {
                          return buffer.setExtraData(_globalMetadata);
                      }
                      /**
                       * @inheritdoc iOVM_ChainStorageContainer
                       */
                      function getGlobalMetadata()
                          override
                          public
                          view
                          returns (
                              bytes27
                          )
                      {
                          return buffer.getExtraData();
                      }
                      /**
                       * @inheritdoc iOVM_ChainStorageContainer
                       */
                      function length()
                          override
                          public
                          view
                          returns (
                              uint256
                          )
                      {
                          return uint256(buffer.getLength());
                      }
                      /**
                       * @inheritdoc iOVM_ChainStorageContainer
                       */
                      function push(
                          bytes32 _object
                      )
                          override
                          public
                          onlyOwner
                      {
                          buffer.push(_object);
                      }
                      /**
                       * @inheritdoc iOVM_ChainStorageContainer
                       */
                      function push(
                          bytes32 _object,
                          bytes27 _globalMetadata
                      )
                          override
                          public
                          onlyOwner
                      {
                          buffer.push(_object, _globalMetadata);
                      }
                      /**
                       * @inheritdoc iOVM_ChainStorageContainer
                       */
                      function get(
                          uint256 _index
                      )
                          override
                          public
                          view
                          returns (
                              bytes32
                          )
                      {
                          return buffer.get(uint40(_index));
                      }
                      /**
                       * @inheritdoc iOVM_ChainStorageContainer
                       */
                      function deleteElementsAfterInclusive(
                          uint256 _index
                      )
                          override
                          public
                          onlyOwner
                      {
                          buffer.deleteElementsAfterInclusive(
                              uint40(_index)
                          );
                      }
                      /**
                       * @inheritdoc iOVM_ChainStorageContainer
                       */
                      function deleteElementsAfterInclusive(
                          uint256 _index,
                          bytes27 _globalMetadata
                      )
                          override
                          public
                          onlyOwner
                      {
                          buffer.deleteElementsAfterInclusive(
                              uint40(_index),
                              _globalMetadata
                          );
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  /**
                   * @title iOVM_ChainStorageContainer
                   */
                  interface iOVM_ChainStorageContainer {
                      /********************
                       * Public Functions *
                       ********************/
                      /**
                       * Sets the container's global metadata field. We're using `bytes27` here because we use five
                       * bytes to maintain the length of the underlying data structure, meaning we have an extra
                       * 27 bytes to store arbitrary data.
                       * @param _globalMetadata New global metadata to set.
                       */
                      function setGlobalMetadata(
                          bytes27 _globalMetadata
                      )
                          external;
                      /**
                       * Retrieves the container's global metadata field.
                       * @return Container global metadata field.
                       */
                      function getGlobalMetadata()
                          external
                          view
                          returns (
                              bytes27
                          );
                      /**
                       * Retrieves the number of objects stored in the container.
                       * @return Number of objects in the container.
                       */
                      function length()
                          external
                          view
                          returns (
                              uint256
                          );
                      /**
                       * Pushes an object into the container.
                       * @param _object A 32 byte value to insert into the container.
                       */
                      function push(
                          bytes32 _object
                      )
                          external;
                      /**
                       * Pushes an object into the container. Function allows setting the global metadata since
                       * we'll need to touch the "length" storage slot anyway, which also contains the global
                       * metadata (it's an optimization).
                       * @param _object A 32 byte value to insert into the container.
                       * @param _globalMetadata New global metadata for the container.
                       */
                      function push(
                          bytes32 _object,
                          bytes27 _globalMetadata
                      )
                          external;
                      /**
                       * Retrieves an object from the container.
                       * @param _index Index of the particular object to access.
                       * @return 32 byte object value.
                       */
                      function get(
                          uint256 _index
                      )
                          external
                          view
                          returns (
                              bytes32
                          );
                      /**
                       * Removes all objects after and including a given index.
                       * @param _index Object index to delete from.
                       */
                      function deleteElementsAfterInclusive(
                          uint256 _index
                      )
                          external;
                      /**
                       * Removes all objects after and including a given index. Also allows setting the global
                       * metadata field.
                       * @param _index Object index to delete from.
                       * @param _globalMetadata New global metadata for the container.
                       */
                      function deleteElementsAfterInclusive(
                          uint256 _index,
                          bytes27 _globalMetadata
                      )
                          external;
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  /* External Imports */
                  import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
                  /**
                   * @title Lib_AddressManager
                   */
                  contract Lib_AddressManager is Ownable {
                      /**********
                       * Events *
                       **********/
                      event AddressSet(
                          string indexed _name,
                          address _newAddress,
                          address _oldAddress
                      );
                      /*************
                       * Variables *
                       *************/
                      mapping (bytes32 => address) private addresses;
                      /********************
                       * Public Functions *
                       ********************/
                      /**
                       * Changes the address associated with a particular name.
                       * @param _name String name to associate an address with.
                       * @param _address Address to associate with the name.
                       */
                      function setAddress(
                          string memory _name,
                          address _address
                      )
                          external
                          onlyOwner
                      {
                          bytes32 nameHash = _getNameHash(_name);
                          address oldAddress = addresses[nameHash];
                          addresses[nameHash] = _address;
                          emit AddressSet(
                              _name,
                              _address,
                              oldAddress
                          );
                      }
                      /**
                       * Retrieves the address associated with a given name.
                       * @param _name Name to retrieve an address for.
                       * @return Address associated with the given name.
                       */
                      function getAddress(
                          string memory _name
                      )
                          external
                          view
                          returns (
                              address
                          )
                      {
                          return addresses[_getNameHash(_name)];
                      }
                      /**********************
                       * Internal Functions *
                       **********************/
                      /**
                       * Computes the hash of a name.
                       * @param _name Name to compute a hash for.
                       * @return Hash of the given name.
                       */
                      function _getNameHash(
                          string memory _name
                      )
                          internal
                          pure
                          returns (
                              bytes32
                          )
                      {
                          return keccak256(abi.encodePacked(_name));
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  /* Library Imports */
                  import { Lib_AddressManager } from "./Lib_AddressManager.sol";
                  /**
                   * @title Lib_AddressResolver
                   */
                  abstract contract Lib_AddressResolver {
                      /*************
                       * Variables *
                       *************/
                      Lib_AddressManager public libAddressManager;
                      /***************
                       * Constructor *
                       ***************/
                      /**
                       * @param _libAddressManager Address of the Lib_AddressManager.
                       */
                      constructor(
                          address _libAddressManager
                      ) {
                          libAddressManager = Lib_AddressManager(_libAddressManager);
                      }
                      /********************
                       * Public Functions *
                       ********************/
                      /**
                       * Resolves the address associated with a given name.
                       * @param _name Name to resolve an address for.
                       * @return Address associated with the given name.
                       */
                      function resolve(
                          string memory _name
                      )
                          public
                          view
                          returns (
                              address
                          )
                      {
                          return libAddressManager.getAddress(_name);
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  pragma solidity >0.5.0 <0.8.0;
                  /**
                   * @title Lib_Buffer
                   * @dev This library implements a bytes32 storage array with some additional gas-optimized
                   * functionality. In particular, it encodes its length as a uint40, and tightly packs this with an
                   * overwritable "extra data" field so we can store more information with a single SSTORE.
                   */
                  library Lib_Buffer {
                      /*************
                       * Libraries *
                       *************/
                      using Lib_Buffer for Buffer;
                      /***********
                       * Structs *
                       ***********/
                      struct Buffer {
                          bytes32 context;
                          mapping (uint256 => bytes32) buf;
                      }
                      struct BufferContext {
                          // Stores the length of the array. Uint40 is way more elements than we'll ever reasonably
                          // need in an array and we get an extra 27 bytes of extra data to play with.
                          uint40 length;
                          // Arbitrary extra data that can be modified whenever the length is updated. Useful for
                          // squeezing out some gas optimizations.
                          bytes27 extraData;
                      }
                      /**********************
                       * Internal Functions *
                       **********************/
                      /**
                       * Pushes a single element to the buffer.
                       * @param _self Buffer to access.
                       * @param _value Value to push to the buffer.
                       * @param _extraData Global extra data.
                       */
                      function push(
                          Buffer storage _self,
                          bytes32 _value,
                          bytes27 _extraData
                      )
                          internal
                      {
                          BufferContext memory ctx = _self.getContext();
                          _self.buf[ctx.length] = _value;
                          // Bump the global index and insert our extra data, then save the context.
                          ctx.length++;
                          ctx.extraData = _extraData;
                          _self.setContext(ctx);
                      }
                      /**
                       * Pushes a single element to the buffer.
                       * @param _self Buffer to access.
                       * @param _value Value to push to the buffer.
                       */
                      function push(
                          Buffer storage _self,
                          bytes32 _value
                      )
                          internal
                      {
                          BufferContext memory ctx = _self.getContext();
                          _self.push(
                              _value,
                              ctx.extraData
                          );
                      }
                      /**
                       * Retrieves an element from the buffer.
                       * @param _self Buffer to access.
                       * @param _index Element index to retrieve.
                       * @return Value of the element at the given index.
                       */
                      function get(
                          Buffer storage _self,
                          uint256 _index
                      )
                          internal
                          view
                          returns (
                              bytes32
                          )
                      {
                          BufferContext memory ctx = _self.getContext();
                          require(
                              _index < ctx.length,
                              "Index out of bounds."
                          );
                          return _self.buf[_index];
                      }
                      /**
                       * Deletes all elements after (and including) a given index.
                       * @param _self Buffer to access.
                       * @param _index Index of the element to delete from (inclusive).
                       * @param _extraData Optional global extra data.
                       */
                      function deleteElementsAfterInclusive(
                          Buffer storage _self,
                          uint40 _index,
                          bytes27 _extraData
                      )
                          internal
                      {
                          BufferContext memory ctx = _self.getContext();
                          require(
                              _index < ctx.length,
                              "Index out of bounds."
                          );
                          // Set our length and extra data, save the context.
                          ctx.length = _index;
                          ctx.extraData = _extraData;
                          _self.setContext(ctx);
                      }
                      /**
                       * Deletes all elements after (and including) a given index.
                       * @param _self Buffer to access.
                       * @param _index Index of the element to delete from (inclusive).
                       */
                      function deleteElementsAfterInclusive(
                          Buffer storage _self,
                          uint40 _index
                      )
                          internal
                      {
                          BufferContext memory ctx = _self.getContext();
                          _self.deleteElementsAfterInclusive(
                              _index,
                              ctx.extraData
                          );
                      }
                      /**
                       * Retrieves the current global index.
                       * @param _self Buffer to access.
                       * @return Current global index.
                       */
                      function getLength(
                          Buffer storage _self
                      )
                          internal
                          view
                          returns (
                              uint40
                          )
                      {
                          BufferContext memory ctx = _self.getContext();
                          return ctx.length;
                      }
                      /**
                       * Changes current global extra data.
                       * @param _self Buffer to access.
                       * @param _extraData New global extra data.
                       */
                      function setExtraData(
                          Buffer storage _self,
                          bytes27 _extraData
                      )
                          internal
                      {
                          BufferContext memory ctx = _self.getContext();
                          ctx.extraData = _extraData;
                          _self.setContext(ctx);
                      }
                      /**
                       * Retrieves the current global extra data.
                       * @param _self Buffer to access.
                       * @return Current global extra data.
                       */
                      function getExtraData(
                          Buffer storage _self
                      )
                          internal
                          view
                          returns (
                              bytes27
                          )
                      {
                          BufferContext memory ctx = _self.getContext();
                          return ctx.extraData;
                      }
                      /**
                       * Sets the current buffer context.
                       * @param _self Buffer to access.
                       * @param _ctx Current buffer context.
                       */
                      function setContext(
                          Buffer storage _self,
                          BufferContext memory _ctx
                      )
                          internal
                      {
                          bytes32 context;
                          uint40 length = _ctx.length;
                          bytes27 extraData = _ctx.extraData;
                          assembly {
                              context := length
                              context := or(context, extraData)
                          }
                          if (_self.context != context) {
                              _self.context = context;
                          }
                      }
                      /**
                       * Retrieves the current buffer context.
                       * @param _self Buffer to access.
                       * @return Current buffer context.
                       */
                      function getContext(
                          Buffer storage _self
                      )
                          internal
                          view
                          returns (
                              BufferContext memory
                          )
                      {
                          bytes32 context = _self.context;
                          uint40 length;
                          bytes27 extraData;
                          assembly {
                              length    := and(context, 0x000000000000000000000000000000000000000000000000000000FFFFFFFFFF)
                              extraData := and(context, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000)
                          }
                          return BufferContext({
                              length: length,
                              extraData: extraData
                          });
                      }
                  }