ETH Price: $2,164.97 (+0.80%)

Transaction Decoder

Block:
17069861 at Apr-17-2023 11:51:59 PM +UTC
Transaction Fee:
0.050444133001126368 ETH $109.21
Gas Used:
1,289,104 Gas / 39.131158542 Gwei

Emitted Events:

219 DiamondProxy.0x8f2916b2f2d78cc5890ead36c06c0f6d5d112c7e103589947e8e2f0d6eddb763( 0x8f2916b2f2d78cc5890ead36c06c0f6d5d112c7e103589947e8e2f0d6eddb763, 0x000000000000000000000000000000000000000000000000000000000000447e, 0xaeae1a66285cad7cb1e2aa2c58bd256e149bc91b5519cbc2086cdff968cdb672, 0x9a0c4ebddf4135ad0450cee0314181a32a307e0245c0b7d058e0827740318585 )

Account State Difference:

  Address   Before After State Difference Code
0x112200Ea...d56B82211
39.226430093117599009 Eth
Nonce: 35975
39.175985960116472641 Eth
Nonce: 35976
0.050444133001126368
0x32400084...60a000324
(zkSync Era: Diamond Proxy)
0x3dB52cE0...F2360d919
(zkSync Era: Validator Timelock)
(builder0x69)
2.202456261851233861 Eth2.202615217567034485 Eth0.000158955715800624

Execution Trace

ValidatorTimelock.commitBlocks( [{name:blockNumber, type:uint64, order:1, indexed:false, value:17533, valueString:17533}, {name:blockHash, type:bytes32, order:2, indexed:false, value:7166EC118299774131C02911FA75D5AA001BC057ED78204776F63591F764DBCA, valueString:7166EC118299774131C02911FA75D5AA001BC057ED78204776F63591F764DBCA}, {name:indexRepeatedStorageChanges, type:uint64, order:3, indexed:false, value:10668656, valueString:10668656}, {name:numberOfLayer1Txs, type:uint256, order:4, indexed:false, value:34, valueString:34}, {name:priorityOperationsHash, type:bytes32, order:5, indexed:false, value:3CE6B7959056CB130AFFAB50AABF6A36B63D1B58BC02C54FADABF42E298C2E97, valueString:3CE6B7959056CB130AFFAB50AABF6A36B63D1B58BC02C54FADABF42E298C2E97}, {name:l2LogsTreeRoot, type:bytes32, order:6, indexed:false, value:D7788F2BD0545B0CBF25AB107F6972A013BA2F4399368F7C0F4A2B1D6FC29EFB, valueString:D7788F2BD0545B0CBF25AB107F6972A013BA2F4399368F7C0F4A2B1D6FC29EFB}, {name:timestamp, type:uint256, order:7, indexed:false, value:1681774157, valueString:1681774157}, {name:commitment, type:bytes32, order:8, indexed:false, value:53005F19AD8976195B76C8828BE0558219CA5535A8CB89D2EA750B1E896781C7, valueString:53005F19AD8976195B76C8828BE0558219CA5535A8CB89D2EA750B1E896781C7}], _newBlocksData= )
  • DiamondProxy.0c4dd810( )
    • ExecutorFacet.commitBlocks( _lastCommittedBlockData=[{name:blockNumber, type:uint64, order:1, indexed:false, value:17533, valueString:17533}, {name:blockHash, type:bytes32, order:2, indexed:false, value:7166EC118299774131C02911FA75D5AA001BC057ED78204776F63591F764DBCA, valueString:7166EC118299774131C02911FA75D5AA001BC057ED78204776F63591F764DBCA}, {name:indexRepeatedStorageChanges, type:uint64, order:3, indexed:false, value:10668656, valueString:10668656}, {name:numberOfLayer1Txs, type:uint256, order:4, indexed:false, value:34, valueString:34}, {name:priorityOperationsHash, type:bytes32, order:5, indexed:false, value:3CE6B7959056CB130AFFAB50AABF6A36B63D1B58BC02C54FADABF42E298C2E97, valueString:3CE6B7959056CB130AFFAB50AABF6A36B63D1B58BC02C54FADABF42E298C2E97}, {name:l2LogsTreeRoot, type:bytes32, order:6, indexed:false, value:D7788F2BD0545B0CBF25AB107F6972A013BA2F4399368F7C0F4A2B1D6FC29EFB, valueString:D7788F2BD0545B0CBF25AB107F6972A013BA2F4399368F7C0F4A2B1D6FC29EFB}, {name:timestamp, type:uint256, order:7, indexed:false, value:1681774157, valueString:1681774157}, {name:commitment, type:bytes32, order:8, indexed:false, value:53005F19AD8976195B76C8828BE0558219CA5535A8CB89D2EA750B1E896781C7, valueString:53005F19AD8976195B76C8828BE0558219CA5535A8CB89D2EA750B1E896781C7}], _newBlocksData= )
      File 1 of 3: ValidatorTimelock
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
      pragma solidity ^0.8.0;
      import "../utils/Context.sol";
      /**
       * @dev Contract module which provides a basic access control mechanism, where
       * there is an account (an owner) that can be granted exclusive access to
       * specific functions.
       *
       * By default, the owner account will be the one that deploys the contract. This
       * can later be changed with {transferOwnership}.
       *
       * This module is used through inheritance. It will make available the modifier
       * `onlyOwner`, which can be applied to your functions to restrict their use to
       * the owner.
       */
      abstract contract Ownable is Context {
          address private _owner;
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
          /**
           * @dev Initializes the contract setting the deployer as the initial owner.
           */
          constructor() {
              _transferOwnership(_msgSender());
          }
          /**
           * @dev Throws if called by any account other than the owner.
           */
          modifier onlyOwner() {
              _checkOwner();
              _;
          }
          /**
           * @dev Returns the address of the current owner.
           */
          function owner() public view virtual returns (address) {
              return _owner;
          }
          /**
           * @dev Throws if the sender is not the owner.
           */
          function _checkOwner() internal view virtual {
              require(owner() == _msgSender(), "Ownable: caller is not the owner");
          }
          /**
           * @dev Leaves the contract without owner. It will not be possible to call
           * `onlyOwner` functions anymore. Can only be called by the current owner.
           *
           * NOTE: Renouncing ownership will leave the contract without an owner,
           * thereby removing any functionality that is only available to the owner.
           */
          function renounceOwnership() public virtual onlyOwner {
              _transferOwnership(address(0));
          }
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`).
           * Can only be called by the current owner.
           */
          function transferOwnership(address newOwner) public virtual onlyOwner {
              require(newOwner != address(0), "Ownable: new owner is the zero address");
              _transferOwnership(newOwner);
          }
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`).
           * Internal function without access restriction.
           */
          function _transferOwnership(address newOwner) internal virtual {
              address oldOwner = _owner;
              _owner = newOwner;
              emit OwnershipTransferred(oldOwner, newOwner);
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.8.0) (access/Ownable2Step.sol)
      pragma solidity ^0.8.0;
      import "./Ownable.sol";
      /**
       * @dev Contract module which provides 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} and {acceptOwnership}.
       *
       * This module is used through inheritance. It will make available all functions
       * from parent (Ownable).
       */
      abstract contract Ownable2Step is Ownable {
          address private _pendingOwner;
          event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
          /**
           * @dev Returns the address of the pending owner.
           */
          function pendingOwner() public view virtual returns (address) {
              return _pendingOwner;
          }
          /**
           * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
           * Can only be called by the current owner.
           */
          function transferOwnership(address newOwner) public virtual override onlyOwner {
              _pendingOwner = newOwner;
              emit OwnershipTransferStarted(owner(), newOwner);
          }
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
           * Internal function without access restriction.
           */
          function _transferOwnership(address newOwner) internal virtual override {
              delete _pendingOwner;
              super._transferOwnership(newOwner);
          }
          /**
           * @dev The new owner accepts the ownership transfer.
           */
          function acceptOwnership() external {
              address sender = _msgSender();
              require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
              _transferOwnership(sender);
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Provides information about the current execution context, including the
       * sender of the transaction and its data. While these are generally available
       * via msg.sender and msg.data, they should not be accessed in such a direct
       * manner, since when dealing with meta-transactions the account sending and
       * paying for execution may not be the actual sender (as far as an application
       * is concerned).
       *
       * This contract is only required for intermediate, library-like contracts.
       */
      abstract contract Context {
          function _msgSender() internal view virtual returns (address) {
              return msg.sender;
          }
          function _msgData() internal view virtual returns (bytes calldata) {
              return msg.data;
          }
      }
      pragma solidity ^0.8.13;
      // SPDX-License-Identifier: MIT
      interface IExecutor {
          /// @notice Rollup block stored data
          /// @param blockNumber Rollup block number
          /// @param blockHash Hash of L2 block
          /// @param indexRepeatedStorageChanges The serial number of the shortcut index that's used as a unique identifier for storage keys that were used twice or more
          /// @param numberOfLayer1Txs Number of priority operations to be processed
          /// @param priorityOperationsHash Hash of all priority operations from this block
          /// @param l2LogsTreeRoot Root hash of tree that contains L2 -> L1 messages from this block
          /// @param timestamp Rollup block timestamp, have the same format as Ethereum block constant
          /// @param commitment Verified input for the zkSync circuit
          struct StoredBlockInfo {
              uint64 blockNumber;
              bytes32 blockHash;
              uint64 indexRepeatedStorageChanges;
              uint256 numberOfLayer1Txs;
              bytes32 priorityOperationsHash;
              bytes32 l2LogsTreeRoot;
              uint256 timestamp;
              bytes32 commitment;
          }
          /// @notice Data needed to commit new block
          /// @param blockNumber Number of the committed block
          /// @param timestamp Unix timestamp denoting the start of the block execution
          /// @param indexRepeatedStorageChanges The serial number of the shortcut index that's used as a unique identifier for storage keys that were used twice or more
          /// @param newStateRoot The state root of the full state tree
          /// @param numberOfLayer1Txs Number of priority operations to be processed
          /// @param l2LogsTreeRoot The root hash of the tree that contains all L2 -> L1 logs in the block
          /// @param priorityOperationsHash Hash of all priority operations from this block
          /// @param initialStorageChanges Storage write access as a concatenation key-value
          /// @param repeatedStorageChanges Storage write access as a concatenation index-value
          /// @param l2Logs concatenation of all L2 -> L1 logs in the block
          /// @param l2ArbitraryLengthMessages array of hash preimages that were sent as value of L2 logs by special system L2 contract
          /// @param factoryDeps array of l2 bytecodes that were marked as known on L2
          struct CommitBlockInfo {
              uint64 blockNumber;
              uint64 timestamp;
              uint64 indexRepeatedStorageChanges;
              bytes32 newStateRoot;
              uint256 numberOfLayer1Txs;
              bytes32 l2LogsTreeRoot;
              bytes32 priorityOperationsHash;
              bytes initialStorageChanges;
              bytes repeatedStorageChanges;
              bytes l2Logs;
              bytes[] l2ArbitraryLengthMessages;
              bytes[] factoryDeps;
          }
          /// @notice Recursive proof input data (individual commitments are constructed onchain)
          struct ProofInput {
              uint256[] recursiveAggregationInput;
              uint256[] serializedProof;
          }
          function commitBlocks(StoredBlockInfo calldata _lastCommittedBlockData, CommitBlockInfo[] calldata _newBlocksData)
              external;
          function proveBlocks(
              StoredBlockInfo calldata _prevBlock,
              StoredBlockInfo[] calldata _committedBlocks,
              ProofInput calldata _proof
          ) external;
          function executeBlocks(StoredBlockInfo[] calldata _blocksData) external;
          function revertBlocks(uint256 _newLastBlock) external;
          /// @notice Event emitted when a block is committed
          event BlockCommit(uint256 indexed blockNumber, bytes32 indexed blockHash, bytes32 indexed commitment);
          /// @notice Event emitted when blocks are verified
          event BlocksVerification(uint256 indexed previousLastVerifiedBlock, uint256 indexed currentLastVerifiedBlock);
          /// @notice Event emitted when a block is executed
          event BlockExecution(uint256 indexed blockNumber, bytes32 indexed blockHash, bytes32 indexed commitment);
          /// @notice Event emitted when blocks are reverted
          event BlocksRevert(uint256 totalBlocksCommitted, uint256 totalBlocksVerified, uint256 totalBlocksExecuted);
      }
      pragma solidity ^0.8.13;
      // SPDX-License-Identifier: MIT
      import "@openzeppelin/contracts/access/Ownable2Step.sol";
      import "./interfaces/IExecutor.sol";
      /// @author Matter Labs
      /// @notice Intermediate smart contract between the validator EOA account and the zkSync smart contract.
      /// @dev The primary purpose of this contract is to provide a trustless means of delaying block execution without
      /// modifying the main zkSync contract. As such, even if this contract is compromised, it will not impact the main contract.
      /// @dev zkSync actively monitors the chain activity and reacts to any suspicious activity by freezing the chain.
      /// This allows time for investigation and mitigation before resuming normal operations.
      /// @dev The contract overloads all of the 4 methods, that are used in state transition. When the block is committed, the
      /// timestamp is stored for it. Later, when the owner calls the block execution, the contract checks that block
      /// was committed not earlier than X time ago.
      contract ValidatorTimelock is IExecutor, Ownable2Step {
          /// @notice The delay between committing and executing blocks is changed.
          event NewExecutionDelay(uint256 _newExecutionDelay);
          /// @notice The validator address is changed.
          event NewValidator(address _oldValidator, address _newValidator);
          /// @dev The main zkSync smart contract.
          address public immutable zkSyncContract;
          /// @dev The mapping of L2 block number => timestamp when it was commited.
          mapping(uint256 => uint256) public committedBlockTimestamp;
          /// @dev The address that can commit/revert/validate/execute blocks.
          address public validator;
          /// @dev The delay between committing and executing blocks.
          uint256 public executionDelay;
          constructor(
              address _initialOwner,
              address _zkSyncContract,
              uint256 _executionDelay,
              address _validator
          ) {
              _transferOwnership(_initialOwner);
              zkSyncContract = _zkSyncContract;
              executionDelay = _executionDelay;
              validator = _validator;
          }
          /// @dev Set new validator address.
          function setValidator(address _newValidator) external onlyOwner {
              address oldValidator = validator;
              validator = _newValidator;
              emit NewValidator(oldValidator, _newValidator);
          }
          /// @dev Set the delay between committing and executing blocks.
          function setExecutionDelay(uint256 _executionDelay) external onlyOwner {
              executionDelay = _executionDelay;
              emit NewExecutionDelay(_executionDelay);
          }
          /// @notice Checks if the caller is a validator.
          modifier onlyValidator() {
              require(msg.sender == validator, "8h");
              _;
          }
          /// @dev Records the timestamp for all provided committed blocks and make
          /// a call to the zkSync contract with the same calldata.
          function commitBlocks(StoredBlockInfo calldata, CommitBlockInfo[] calldata _newBlocksData) external onlyValidator {
              for (uint256 i = 0; i < _newBlocksData.length; ++i) {
                  committedBlockTimestamp[_newBlocksData[i].blockNumber] = block.timestamp;
              }
              _propagateToZkSync();
          }
          /// @dev Make a call to the zkSync contract with the same calldata.
          /// Note: If the block is reverted, it needs to be committed first before the execution.
          /// So it's safe to not override the committed blocks.
          function revertBlocks(uint256) external onlyValidator {
              _propagateToZkSync();
          }
          /// @dev Make a call to the zkSync contract with the same calldata.
          /// Note: We don't track the time when blocks are proven, since all information about
          /// the block is known on the commit stage and the proved is not finalized (may be reverted).
          function proveBlocks(
              StoredBlockInfo calldata,
              StoredBlockInfo[] calldata,
              ProofInput calldata
          ) external onlyValidator {
              _propagateToZkSync();
          }
          /// @dev Check that blocks were committed at least X time ago and
          /// make a call to the zkSync contract with the same calldata.
          function executeBlocks(StoredBlockInfo[] calldata _newBlocksData) external onlyValidator {
              for (uint256 i = 0; i < _newBlocksData.length; ++i) {
                  uint256 commitBlockTimestamp = committedBlockTimestamp[_newBlocksData[i].blockNumber];
                  // Note: if the `commitBlockTimestamp` is zero, that means either:
                  // * The block was committed, but not though this contract.
                  // * The block wasn't committed at all, so execution will fail in the zkSync contract.
                  // We allow executing such blocks.
                  require(block.timestamp > commitBlockTimestamp + executionDelay, "5c"); // The delay is not passed
              }
              _propagateToZkSync();
          }
          /// @dev Call the zkSync contract with the same calldata as this contract was called.
          /// Note: it is called the zkSync contract, not delegatecalled!
          function _propagateToZkSync() internal {
              address contractAddress = zkSyncContract;
              assembly {
                  // Copy function signature and arguments from calldata at zero position into memory at pointer position
                  calldatacopy(0, 0, calldatasize())
                  // Call method of the zkSync contract returns 0 on error
                  let result := call(gas(), contractAddress, 0, 0, calldatasize(), 0, 0)
                  // Get the size of the last return data
                  let size := returndatasize()
                  // Copy the size length of bytes from return data at zero position to pointer position
                  returndatacopy(0, 0, size)
                  // Depending on the result value
                  switch result
                  case 0 {
                      // End execution and revert state changes
                      revert(0, size)
                  }
                  default {
                      // Return data with length of size at pointers position
                      return(0, size)
                  }
              }
          }
      }
      

      File 2 of 3: DiamondProxy
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol)
      // This file was procedurally generated from scripts/generate/templates/SafeCast.js.
      pragma solidity ^0.8.0;
      /**
       * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
       * checks.
       *
       * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
       * easily result in undesired exploitation or bugs, since developers usually
       * assume that overflows raise errors. `SafeCast` restores this intuition by
       * reverting the transaction when such 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.
       *
       * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
       * all math on `uint256` and `int256` and then downcasting.
       */
      library SafeCast {
          /**
           * @dev Returns the downcasted uint248 from uint256, reverting on
           * overflow (when the input is greater than largest uint248).
           *
           * Counterpart to Solidity's `uint248` operator.
           *
           * Requirements:
           *
           * - input must fit into 248 bits
           *
           * _Available since v4.7._
           */
          function toUint248(uint256 value) internal pure returns (uint248) {
              require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
              return uint248(value);
          }
          /**
           * @dev Returns the downcasted uint240 from uint256, reverting on
           * overflow (when the input is greater than largest uint240).
           *
           * Counterpart to Solidity's `uint240` operator.
           *
           * Requirements:
           *
           * - input must fit into 240 bits
           *
           * _Available since v4.7._
           */
          function toUint240(uint256 value) internal pure returns (uint240) {
              require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
              return uint240(value);
          }
          /**
           * @dev Returns the downcasted uint232 from uint256, reverting on
           * overflow (when the input is greater than largest uint232).
           *
           * Counterpart to Solidity's `uint232` operator.
           *
           * Requirements:
           *
           * - input must fit into 232 bits
           *
           * _Available since v4.7._
           */
          function toUint232(uint256 value) internal pure returns (uint232) {
              require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
              return uint232(value);
          }
          /**
           * @dev Returns the downcasted uint224 from uint256, reverting on
           * overflow (when the input is greater than largest uint224).
           *
           * Counterpart to Solidity's `uint224` operator.
           *
           * Requirements:
           *
           * - input must fit into 224 bits
           *
           * _Available since v4.2._
           */
          function toUint224(uint256 value) internal pure returns (uint224) {
              require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
              return uint224(value);
          }
          /**
           * @dev Returns the downcasted uint216 from uint256, reverting on
           * overflow (when the input is greater than largest uint216).
           *
           * Counterpart to Solidity's `uint216` operator.
           *
           * Requirements:
           *
           * - input must fit into 216 bits
           *
           * _Available since v4.7._
           */
          function toUint216(uint256 value) internal pure returns (uint216) {
              require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
              return uint216(value);
          }
          /**
           * @dev Returns the downcasted uint208 from uint256, reverting on
           * overflow (when the input is greater than largest uint208).
           *
           * Counterpart to Solidity's `uint208` operator.
           *
           * Requirements:
           *
           * - input must fit into 208 bits
           *
           * _Available since v4.7._
           */
          function toUint208(uint256 value) internal pure returns (uint208) {
              require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
              return uint208(value);
          }
          /**
           * @dev Returns the downcasted uint200 from uint256, reverting on
           * overflow (when the input is greater than largest uint200).
           *
           * Counterpart to Solidity's `uint200` operator.
           *
           * Requirements:
           *
           * - input must fit into 200 bits
           *
           * _Available since v4.7._
           */
          function toUint200(uint256 value) internal pure returns (uint200) {
              require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
              return uint200(value);
          }
          /**
           * @dev Returns the downcasted uint192 from uint256, reverting on
           * overflow (when the input is greater than largest uint192).
           *
           * Counterpart to Solidity's `uint192` operator.
           *
           * Requirements:
           *
           * - input must fit into 192 bits
           *
           * _Available since v4.7._
           */
          function toUint192(uint256 value) internal pure returns (uint192) {
              require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
              return uint192(value);
          }
          /**
           * @dev Returns the downcasted uint184 from uint256, reverting on
           * overflow (when the input is greater than largest uint184).
           *
           * Counterpart to Solidity's `uint184` operator.
           *
           * Requirements:
           *
           * - input must fit into 184 bits
           *
           * _Available since v4.7._
           */
          function toUint184(uint256 value) internal pure returns (uint184) {
              require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
              return uint184(value);
          }
          /**
           * @dev Returns the downcasted uint176 from uint256, reverting on
           * overflow (when the input is greater than largest uint176).
           *
           * Counterpart to Solidity's `uint176` operator.
           *
           * Requirements:
           *
           * - input must fit into 176 bits
           *
           * _Available since v4.7._
           */
          function toUint176(uint256 value) internal pure returns (uint176) {
              require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
              return uint176(value);
          }
          /**
           * @dev Returns the downcasted uint168 from uint256, reverting on
           * overflow (when the input is greater than largest uint168).
           *
           * Counterpart to Solidity's `uint168` operator.
           *
           * Requirements:
           *
           * - input must fit into 168 bits
           *
           * _Available since v4.7._
           */
          function toUint168(uint256 value) internal pure returns (uint168) {
              require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
              return uint168(value);
          }
          /**
           * @dev Returns the downcasted uint160 from uint256, reverting on
           * overflow (when the input is greater than largest uint160).
           *
           * Counterpart to Solidity's `uint160` operator.
           *
           * Requirements:
           *
           * - input must fit into 160 bits
           *
           * _Available since v4.7._
           */
          function toUint160(uint256 value) internal pure returns (uint160) {
              require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
              return uint160(value);
          }
          /**
           * @dev Returns the downcasted uint152 from uint256, reverting on
           * overflow (when the input is greater than largest uint152).
           *
           * Counterpart to Solidity's `uint152` operator.
           *
           * Requirements:
           *
           * - input must fit into 152 bits
           *
           * _Available since v4.7._
           */
          function toUint152(uint256 value) internal pure returns (uint152) {
              require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
              return uint152(value);
          }
          /**
           * @dev Returns the downcasted uint144 from uint256, reverting on
           * overflow (when the input is greater than largest uint144).
           *
           * Counterpart to Solidity's `uint144` operator.
           *
           * Requirements:
           *
           * - input must fit into 144 bits
           *
           * _Available since v4.7._
           */
          function toUint144(uint256 value) internal pure returns (uint144) {
              require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
              return uint144(value);
          }
          /**
           * @dev Returns the downcasted uint136 from uint256, reverting on
           * overflow (when the input is greater than largest uint136).
           *
           * Counterpart to Solidity's `uint136` operator.
           *
           * Requirements:
           *
           * - input must fit into 136 bits
           *
           * _Available since v4.7._
           */
          function toUint136(uint256 value) internal pure returns (uint136) {
              require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
              return uint136(value);
          }
          /**
           * @dev Returns the downcasted uint128 from uint256, reverting on
           * overflow (when the input is greater than largest uint128).
           *
           * Counterpart to Solidity's `uint128` operator.
           *
           * Requirements:
           *
           * - input must fit into 128 bits
           *
           * _Available since v2.5._
           */
          function toUint128(uint256 value) internal pure returns (uint128) {
              require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
              return uint128(value);
          }
          /**
           * @dev Returns the downcasted uint120 from uint256, reverting on
           * overflow (when the input is greater than largest uint120).
           *
           * Counterpart to Solidity's `uint120` operator.
           *
           * Requirements:
           *
           * - input must fit into 120 bits
           *
           * _Available since v4.7._
           */
          function toUint120(uint256 value) internal pure returns (uint120) {
              require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
              return uint120(value);
          }
          /**
           * @dev Returns the downcasted uint112 from uint256, reverting on
           * overflow (when the input is greater than largest uint112).
           *
           * Counterpart to Solidity's `uint112` operator.
           *
           * Requirements:
           *
           * - input must fit into 112 bits
           *
           * _Available since v4.7._
           */
          function toUint112(uint256 value) internal pure returns (uint112) {
              require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
              return uint112(value);
          }
          /**
           * @dev Returns the downcasted uint104 from uint256, reverting on
           * overflow (when the input is greater than largest uint104).
           *
           * Counterpart to Solidity's `uint104` operator.
           *
           * Requirements:
           *
           * - input must fit into 104 bits
           *
           * _Available since v4.7._
           */
          function toUint104(uint256 value) internal pure returns (uint104) {
              require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
              return uint104(value);
          }
          /**
           * @dev Returns the downcasted uint96 from uint256, reverting on
           * overflow (when the input is greater than largest uint96).
           *
           * Counterpart to Solidity's `uint96` operator.
           *
           * Requirements:
           *
           * - input must fit into 96 bits
           *
           * _Available since v4.2._
           */
          function toUint96(uint256 value) internal pure returns (uint96) {
              require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
              return uint96(value);
          }
          /**
           * @dev Returns the downcasted uint88 from uint256, reverting on
           * overflow (when the input is greater than largest uint88).
           *
           * Counterpart to Solidity's `uint88` operator.
           *
           * Requirements:
           *
           * - input must fit into 88 bits
           *
           * _Available since v4.7._
           */
          function toUint88(uint256 value) internal pure returns (uint88) {
              require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
              return uint88(value);
          }
          /**
           * @dev Returns the downcasted uint80 from uint256, reverting on
           * overflow (when the input is greater than largest uint80).
           *
           * Counterpart to Solidity's `uint80` operator.
           *
           * Requirements:
           *
           * - input must fit into 80 bits
           *
           * _Available since v4.7._
           */
          function toUint80(uint256 value) internal pure returns (uint80) {
              require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
              return uint80(value);
          }
          /**
           * @dev Returns the downcasted uint72 from uint256, reverting on
           * overflow (when the input is greater than largest uint72).
           *
           * Counterpart to Solidity's `uint72` operator.
           *
           * Requirements:
           *
           * - input must fit into 72 bits
           *
           * _Available since v4.7._
           */
          function toUint72(uint256 value) internal pure returns (uint72) {
              require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
              return uint72(value);
          }
          /**
           * @dev Returns the downcasted uint64 from uint256, reverting on
           * overflow (when the input is greater than largest uint64).
           *
           * Counterpart to Solidity's `uint64` operator.
           *
           * Requirements:
           *
           * - input must fit into 64 bits
           *
           * _Available since v2.5._
           */
          function toUint64(uint256 value) internal pure returns (uint64) {
              require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
              return uint64(value);
          }
          /**
           * @dev Returns the downcasted uint56 from uint256, reverting on
           * overflow (when the input is greater than largest uint56).
           *
           * Counterpart to Solidity's `uint56` operator.
           *
           * Requirements:
           *
           * - input must fit into 56 bits
           *
           * _Available since v4.7._
           */
          function toUint56(uint256 value) internal pure returns (uint56) {
              require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
              return uint56(value);
          }
          /**
           * @dev Returns the downcasted uint48 from uint256, reverting on
           * overflow (when the input is greater than largest uint48).
           *
           * Counterpart to Solidity's `uint48` operator.
           *
           * Requirements:
           *
           * - input must fit into 48 bits
           *
           * _Available since v4.7._
           */
          function toUint48(uint256 value) internal pure returns (uint48) {
              require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
              return uint48(value);
          }
          /**
           * @dev Returns the downcasted uint40 from uint256, reverting on
           * overflow (when the input is greater than largest uint40).
           *
           * Counterpart to Solidity's `uint40` operator.
           *
           * Requirements:
           *
           * - input must fit into 40 bits
           *
           * _Available since v4.7._
           */
          function toUint40(uint256 value) internal pure returns (uint40) {
              require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
              return uint40(value);
          }
          /**
           * @dev Returns the downcasted uint32 from uint256, reverting on
           * overflow (when the input is greater than largest uint32).
           *
           * Counterpart to Solidity's `uint32` operator.
           *
           * Requirements:
           *
           * - input must fit into 32 bits
           *
           * _Available since v2.5._
           */
          function toUint32(uint256 value) internal pure returns (uint32) {
              require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
              return uint32(value);
          }
          /**
           * @dev Returns the downcasted uint24 from uint256, reverting on
           * overflow (when the input is greater than largest uint24).
           *
           * Counterpart to Solidity's `uint24` operator.
           *
           * Requirements:
           *
           * - input must fit into 24 bits
           *
           * _Available since v4.7._
           */
          function toUint24(uint256 value) internal pure returns (uint24) {
              require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
              return uint24(value);
          }
          /**
           * @dev Returns the downcasted uint16 from uint256, reverting on
           * overflow (when the input is greater than largest uint16).
           *
           * Counterpart to Solidity's `uint16` operator.
           *
           * Requirements:
           *
           * - input must fit into 16 bits
           *
           * _Available since v2.5._
           */
          function toUint16(uint256 value) internal pure returns (uint16) {
              require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
              return uint16(value);
          }
          /**
           * @dev Returns the downcasted uint8 from uint256, reverting on
           * overflow (when the input is greater than largest uint8).
           *
           * Counterpart to Solidity's `uint8` operator.
           *
           * Requirements:
           *
           * - input must fit into 8 bits
           *
           * _Available since v2.5._
           */
          function toUint8(uint256 value) internal pure returns (uint8) {
              require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
              return uint8(value);
          }
          /**
           * @dev Converts a signed int256 into an unsigned uint256.
           *
           * Requirements:
           *
           * - input must be greater than or equal to 0.
           *
           * _Available since v3.0._
           */
          function toUint256(int256 value) internal pure returns (uint256) {
              require(value >= 0, "SafeCast: value must be positive");
              return uint256(value);
          }
          /**
           * @dev Returns the downcasted int248 from int256, reverting on
           * overflow (when the input is less than smallest int248 or
           * greater than largest int248).
           *
           * Counterpart to Solidity's `int248` operator.
           *
           * Requirements:
           *
           * - input must fit into 248 bits
           *
           * _Available since v4.7._
           */
          function toInt248(int256 value) internal pure returns (int248 downcasted) {
              downcasted = int248(value);
              require(downcasted == value, "SafeCast: value doesn't fit in 248 bits");
          }
          /**
           * @dev Returns the downcasted int240 from int256, reverting on
           * overflow (when the input is less than smallest int240 or
           * greater than largest int240).
           *
           * Counterpart to Solidity's `int240` operator.
           *
           * Requirements:
           *
           * - input must fit into 240 bits
           *
           * _Available since v4.7._
           */
          function toInt240(int256 value) internal pure returns (int240 downcasted) {
              downcasted = int240(value);
              require(downcasted == value, "SafeCast: value doesn't fit in 240 bits");
          }
          /**
           * @dev Returns the downcasted int232 from int256, reverting on
           * overflow (when the input is less than smallest int232 or
           * greater than largest int232).
           *
           * Counterpart to Solidity's `int232` operator.
           *
           * Requirements:
           *
           * - input must fit into 232 bits
           *
           * _Available since v4.7._
           */
          function toInt232(int256 value) internal pure returns (int232 downcasted) {
              downcasted = int232(value);
              require(downcasted == value, "SafeCast: value doesn't fit in 232 bits");
          }
          /**
           * @dev Returns the downcasted int224 from int256, reverting on
           * overflow (when the input is less than smallest int224 or
           * greater than largest int224).
           *
           * Counterpart to Solidity's `int224` operator.
           *
           * Requirements:
           *
           * - input must fit into 224 bits
           *
           * _Available since v4.7._
           */
          function toInt224(int256 value) internal pure returns (int224 downcasted) {
              downcasted = int224(value);
              require(downcasted == value, "SafeCast: value doesn't fit in 224 bits");
          }
          /**
           * @dev Returns the downcasted int216 from int256, reverting on
           * overflow (when the input is less than smallest int216 or
           * greater than largest int216).
           *
           * Counterpart to Solidity's `int216` operator.
           *
           * Requirements:
           *
           * - input must fit into 216 bits
           *
           * _Available since v4.7._
           */
          function toInt216(int256 value) internal pure returns (int216 downcasted) {
              downcasted = int216(value);
              require(downcasted == value, "SafeCast: value doesn't fit in 216 bits");
          }
          /**
           * @dev Returns the downcasted int208 from int256, reverting on
           * overflow (when the input is less than smallest int208 or
           * greater than largest int208).
           *
           * Counterpart to Solidity's `int208` operator.
           *
           * Requirements:
           *
           * - input must fit into 208 bits
           *
           * _Available since v4.7._
           */
          function toInt208(int256 value) internal pure returns (int208 downcasted) {
              downcasted = int208(value);
              require(downcasted == value, "SafeCast: value doesn't fit in 208 bits");
          }
          /**
           * @dev Returns the downcasted int200 from int256, reverting on
           * overflow (when the input is less than smallest int200 or
           * greater than largest int200).
           *
           * Counterpart to Solidity's `int200` operator.
           *
           * Requirements:
           *
           * - input must fit into 200 bits
           *
           * _Available since v4.7._
           */
          function toInt200(int256 value) internal pure returns (int200 downcasted) {
              downcasted = int200(value);
              require(downcasted == value, "SafeCast: value doesn't fit in 200 bits");
          }
          /**
           * @dev Returns the downcasted int192 from int256, reverting on
           * overflow (when the input is less than smallest int192 or
           * greater than largest int192).
           *
           * Counterpart to Solidity's `int192` operator.
           *
           * Requirements:
           *
           * - input must fit into 192 bits
           *
           * _Available since v4.7._
           */
          function toInt192(int256 value) internal pure returns (int192 downcasted) {
              downcasted = int192(value);
              require(downcasted == value, "SafeCast: value doesn't fit in 192 bits");
          }
          /**
           * @dev Returns the downcasted int184 from int256, reverting on
           * overflow (when the input is less than smallest int184 or
           * greater than largest int184).
           *
           * Counterpart to Solidity's `int184` operator.
           *
           * Requirements:
           *
           * - input must fit into 184 bits
           *
           * _Available since v4.7._
           */
          function toInt184(int256 value) internal pure returns (int184 downcasted) {
              downcasted = int184(value);
              require(downcasted == value, "SafeCast: value doesn't fit in 184 bits");
          }
          /**
           * @dev Returns the downcasted int176 from int256, reverting on
           * overflow (when the input is less than smallest int176 or
           * greater than largest int176).
           *
           * Counterpart to Solidity's `int176` operator.
           *
           * Requirements:
           *
           * - input must fit into 176 bits
           *
           * _Available since v4.7._
           */
          function toInt176(int256 value) internal pure returns (int176 downcasted) {
              downcasted = int176(value);
              require(downcasted == value, "SafeCast: value doesn't fit in 176 bits");
          }
          /**
           * @dev Returns the downcasted int168 from int256, reverting on
           * overflow (when the input is less than smallest int168 or
           * greater than largest int168).
           *
           * Counterpart to Solidity's `int168` operator.
           *
           * Requirements:
           *
           * - input must fit into 168 bits
           *
           * _Available since v4.7._
           */
          function toInt168(int256 value) internal pure returns (int168 downcasted) {
              downcasted = int168(value);
              require(downcasted == value, "SafeCast: value doesn't fit in 168 bits");
          }
          /**
           * @dev Returns the downcasted int160 from int256, reverting on
           * overflow (when the input is less than smallest int160 or
           * greater than largest int160).
           *
           * Counterpart to Solidity's `int160` operator.
           *
           * Requirements:
           *
           * - input must fit into 160 bits
           *
           * _Available since v4.7._
           */
          function toInt160(int256 value) internal pure returns (int160 downcasted) {
              downcasted = int160(value);
              require(downcasted == value, "SafeCast: value doesn't fit in 160 bits");
          }
          /**
           * @dev Returns the downcasted int152 from int256, reverting on
           * overflow (when the input is less than smallest int152 or
           * greater than largest int152).
           *
           * Counterpart to Solidity's `int152` operator.
           *
           * Requirements:
           *
           * - input must fit into 152 bits
           *
           * _Available since v4.7._
           */
          function toInt152(int256 value) internal pure returns (int152 downcasted) {
              downcasted = int152(value);
              require(downcasted == value, "SafeCast: value doesn't fit in 152 bits");
          }
          /**
           * @dev Returns the downcasted int144 from int256, reverting on
           * overflow (when the input is less than smallest int144 or
           * greater than largest int144).
           *
           * Counterpart to Solidity's `int144` operator.
           *
           * Requirements:
           *
           * - input must fit into 144 bits
           *
           * _Available since v4.7._
           */
          function toInt144(int256 value) internal pure returns (int144 downcasted) {
              downcasted = int144(value);
              require(downcasted == value, "SafeCast: value doesn't fit in 144 bits");
          }
          /**
           * @dev Returns the downcasted int136 from int256, reverting on
           * overflow (when the input is less than smallest int136 or
           * greater than largest int136).
           *
           * Counterpart to Solidity's `int136` operator.
           *
           * Requirements:
           *
           * - input must fit into 136 bits
           *
           * _Available since v4.7._
           */
          function toInt136(int256 value) internal pure returns (int136 downcasted) {
              downcasted = int136(value);
              require(downcasted == value, "SafeCast: value doesn't fit in 136 bits");
          }
          /**
           * @dev Returns the downcasted int128 from int256, reverting on
           * overflow (when the input is less than smallest int128 or
           * greater than largest int128).
           *
           * Counterpart to Solidity's `int128` operator.
           *
           * Requirements:
           *
           * - input must fit into 128 bits
           *
           * _Available since v3.1._
           */
          function toInt128(int256 value) internal pure returns (int128 downcasted) {
              downcasted = int128(value);
              require(downcasted == value, "SafeCast: value doesn't fit in 128 bits");
          }
          /**
           * @dev Returns the downcasted int120 from int256, reverting on
           * overflow (when the input is less than smallest int120 or
           * greater than largest int120).
           *
           * Counterpart to Solidity's `int120` operator.
           *
           * Requirements:
           *
           * - input must fit into 120 bits
           *
           * _Available since v4.7._
           */
          function toInt120(int256 value) internal pure returns (int120 downcasted) {
              downcasted = int120(value);
              require(downcasted == value, "SafeCast: value doesn't fit in 120 bits");
          }
          /**
           * @dev Returns the downcasted int112 from int256, reverting on
           * overflow (when the input is less than smallest int112 or
           * greater than largest int112).
           *
           * Counterpart to Solidity's `int112` operator.
           *
           * Requirements:
           *
           * - input must fit into 112 bits
           *
           * _Available since v4.7._
           */
          function toInt112(int256 value) internal pure returns (int112 downcasted) {
              downcasted = int112(value);
              require(downcasted == value, "SafeCast: value doesn't fit in 112 bits");
          }
          /**
           * @dev Returns the downcasted int104 from int256, reverting on
           * overflow (when the input is less than smallest int104 or
           * greater than largest int104).
           *
           * Counterpart to Solidity's `int104` operator.
           *
           * Requirements:
           *
           * - input must fit into 104 bits
           *
           * _Available since v4.7._
           */
          function toInt104(int256 value) internal pure returns (int104 downcasted) {
              downcasted = int104(value);
              require(downcasted == value, "SafeCast: value doesn't fit in 104 bits");
          }
          /**
           * @dev Returns the downcasted int96 from int256, reverting on
           * overflow (when the input is less than smallest int96 or
           * greater than largest int96).
           *
           * Counterpart to Solidity's `int96` operator.
           *
           * Requirements:
           *
           * - input must fit into 96 bits
           *
           * _Available since v4.7._
           */
          function toInt96(int256 value) internal pure returns (int96 downcasted) {
              downcasted = int96(value);
              require(downcasted == value, "SafeCast: value doesn't fit in 96 bits");
          }
          /**
           * @dev Returns the downcasted int88 from int256, reverting on
           * overflow (when the input is less than smallest int88 or
           * greater than largest int88).
           *
           * Counterpart to Solidity's `int88` operator.
           *
           * Requirements:
           *
           * - input must fit into 88 bits
           *
           * _Available since v4.7._
           */
          function toInt88(int256 value) internal pure returns (int88 downcasted) {
              downcasted = int88(value);
              require(downcasted == value, "SafeCast: value doesn't fit in 88 bits");
          }
          /**
           * @dev Returns the downcasted int80 from int256, reverting on
           * overflow (when the input is less than smallest int80 or
           * greater than largest int80).
           *
           * Counterpart to Solidity's `int80` operator.
           *
           * Requirements:
           *
           * - input must fit into 80 bits
           *
           * _Available since v4.7._
           */
          function toInt80(int256 value) internal pure returns (int80 downcasted) {
              downcasted = int80(value);
              require(downcasted == value, "SafeCast: value doesn't fit in 80 bits");
          }
          /**
           * @dev Returns the downcasted int72 from int256, reverting on
           * overflow (when the input is less than smallest int72 or
           * greater than largest int72).
           *
           * Counterpart to Solidity's `int72` operator.
           *
           * Requirements:
           *
           * - input must fit into 72 bits
           *
           * _Available since v4.7._
           */
          function toInt72(int256 value) internal pure returns (int72 downcasted) {
              downcasted = int72(value);
              require(downcasted == value, "SafeCast: value doesn't fit in 72 bits");
          }
          /**
           * @dev Returns the downcasted int64 from int256, reverting on
           * overflow (when the input is less than smallest int64 or
           * greater than largest int64).
           *
           * Counterpart to Solidity's `int64` operator.
           *
           * Requirements:
           *
           * - input must fit into 64 bits
           *
           * _Available since v3.1._
           */
          function toInt64(int256 value) internal pure returns (int64 downcasted) {
              downcasted = int64(value);
              require(downcasted == value, "SafeCast: value doesn't fit in 64 bits");
          }
          /**
           * @dev Returns the downcasted int56 from int256, reverting on
           * overflow (when the input is less than smallest int56 or
           * greater than largest int56).
           *
           * Counterpart to Solidity's `int56` operator.
           *
           * Requirements:
           *
           * - input must fit into 56 bits
           *
           * _Available since v4.7._
           */
          function toInt56(int256 value) internal pure returns (int56 downcasted) {
              downcasted = int56(value);
              require(downcasted == value, "SafeCast: value doesn't fit in 56 bits");
          }
          /**
           * @dev Returns the downcasted int48 from int256, reverting on
           * overflow (when the input is less than smallest int48 or
           * greater than largest int48).
           *
           * Counterpart to Solidity's `int48` operator.
           *
           * Requirements:
           *
           * - input must fit into 48 bits
           *
           * _Available since v4.7._
           */
          function toInt48(int256 value) internal pure returns (int48 downcasted) {
              downcasted = int48(value);
              require(downcasted == value, "SafeCast: value doesn't fit in 48 bits");
          }
          /**
           * @dev Returns the downcasted int40 from int256, reverting on
           * overflow (when the input is less than smallest int40 or
           * greater than largest int40).
           *
           * Counterpart to Solidity's `int40` operator.
           *
           * Requirements:
           *
           * - input must fit into 40 bits
           *
           * _Available since v4.7._
           */
          function toInt40(int256 value) internal pure returns (int40 downcasted) {
              downcasted = int40(value);
              require(downcasted == value, "SafeCast: value doesn't fit in 40 bits");
          }
          /**
           * @dev Returns the downcasted int32 from int256, reverting on
           * overflow (when the input is less than smallest int32 or
           * greater than largest int32).
           *
           * Counterpart to Solidity's `int32` operator.
           *
           * Requirements:
           *
           * - input must fit into 32 bits
           *
           * _Available since v3.1._
           */
          function toInt32(int256 value) internal pure returns (int32 downcasted) {
              downcasted = int32(value);
              require(downcasted == value, "SafeCast: value doesn't fit in 32 bits");
          }
          /**
           * @dev Returns the downcasted int24 from int256, reverting on
           * overflow (when the input is less than smallest int24 or
           * greater than largest int24).
           *
           * Counterpart to Solidity's `int24` operator.
           *
           * Requirements:
           *
           * - input must fit into 24 bits
           *
           * _Available since v4.7._
           */
          function toInt24(int256 value) internal pure returns (int24 downcasted) {
              downcasted = int24(value);
              require(downcasted == value, "SafeCast: value doesn't fit in 24 bits");
          }
          /**
           * @dev Returns the downcasted int16 from int256, reverting on
           * overflow (when the input is less than smallest int16 or
           * greater than largest int16).
           *
           * Counterpart to Solidity's `int16` operator.
           *
           * Requirements:
           *
           * - input must fit into 16 bits
           *
           * _Available since v3.1._
           */
          function toInt16(int256 value) internal pure returns (int16 downcasted) {
              downcasted = int16(value);
              require(downcasted == value, "SafeCast: value doesn't fit in 16 bits");
          }
          /**
           * @dev Returns the downcasted int8 from int256, reverting on
           * overflow (when the input is less than smallest int8 or
           * greater than largest int8).
           *
           * Counterpart to Solidity's `int8` operator.
           *
           * Requirements:
           *
           * - input must fit into 8 bits
           *
           * _Available since v3.1._
           */
          function toInt8(int256 value) internal pure returns (int8 downcasted) {
              downcasted = int8(value);
              require(downcasted == value, "SafeCast: value doesn't fit in 8 bits");
          }
          /**
           * @dev Converts an unsigned uint256 into a signed int256.
           *
           * Requirements:
           *
           * - input must be less than or equal to maxInt256.
           *
           * _Available since v3.0._
           */
          function toInt256(uint256 value) internal pure returns (int256) {
              // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
              require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
              return int256(value);
          }
      }
      pragma solidity ^0.8.0;
      // SPDX-License-Identifier: MIT
      library UncheckedMath {
          function uncheckedInc(uint256 _number) internal pure returns (uint256) {
              unchecked {
                  return _number + 1;
              }
          }
          function uncheckedAdd(uint256 _lhs, uint256 _rhs) internal pure returns (uint256) {
              unchecked {
                  return _lhs + _rhs;
              }
          }
      }
      pragma solidity ^0.8.0;
      // SPDX-License-Identifier: MIT
      import "./libraries/Diamond.sol";
      /// @title Diamond Proxy Contract (EIP-2535)
      /// @author Matter Labs
      contract DiamondProxy {
          constructor(uint256 _chainId, Diamond.DiamondCutData memory _diamondCut) {
              // Check that the contract is deployed on the expected chain.
              // Thus, the contract deployed by the same Create2 factory on the different chain will have different addresses!
              require(_chainId == block.chainid, "pr");
              Diamond.diamondCut(_diamondCut);
          }
          /// @dev 1. Find the facet for the function that is called.
          /// @dev 2. Delegate the execution to the found facet via `delegatecall`.
          fallback() external payable {
              Diamond.DiamondStorage storage diamondStorage = Diamond.getDiamondStorage();
              // Check whether the data contains a "full" selector or it is empty.
              // Required because Diamond proxy finds a facet by function signature,
              // which is not defined for data length in range [1, 3].
              require(msg.data.length >= 4 || msg.data.length == 0, "Ut");
              // Get facet from function selector
              Diamond.SelectorToFacet memory facet = diamondStorage.selectorToFacet[msg.sig];
              address facetAddress = facet.facetAddress;
              require(facetAddress != address(0), "F"); // Proxy has no facet for this selector
              require(!diamondStorage.isFrozen || !facet.isFreezable, "q1"); // Facet is frozen
              assembly {
                  // The pointer to the free memory slot
                  let ptr := mload(0x40)
                  // Copy function signature and arguments from calldata at zero position into memory at pointer position
                  calldatacopy(ptr, 0, calldatasize())
                  // Delegatecall method of the implementation contract returns 0 on error
                  let result := delegatecall(gas(), facetAddress, ptr, calldatasize(), 0, 0)
                  // Get the size of the last return data
                  let size := returndatasize()
                  // Copy the size length of bytes from return data at zero position to pointer position
                  returndatacopy(ptr, 0, size)
                  // Depending on the result value
                  switch result
                  case 0 {
                      // End execution and revert state changes
                      revert(ptr, size)
                  }
                  default {
                      // Return data with length of size at pointers position
                      return(ptr, size)
                  }
              }
          }
      }
      pragma solidity ^0.8.0;
      // SPDX-License-Identifier: MIT
      import "@openzeppelin/contracts/utils/math/SafeCast.sol";
      import "../../common/libraries/UncheckedMath.sol";
      /// @author Matter Labs
      /// @notice The helper library for managing the EIP-2535 diamond proxy.
      library Diamond {
          using UncheckedMath for uint256;
          using SafeCast for uint256;
          /// @dev Magic value that should be returned by diamond cut initialize contracts.
          /// @dev Used to distinguish calls to contracts that were supposed to be used as diamond initializer from other contracts.
          bytes32 constant DIAMOND_INIT_SUCCESS_RETURN_VALUE =
              0x33774e659306e47509050e97cb651e731180a42d458212294d30751925c551a2; // keccak256("diamond.zksync.init") - 1
          /// @dev Storage position of `DiamondStorage` structure.
          bytes32 constant DIAMOND_STORAGE_POSITION = 0xc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131b; // keccak256("diamond.standard.diamond.storage") - 1;
          event DiamondCut(FacetCut[] facetCuts, address initAddress, bytes initCalldata);
          /// @dev Utility struct that contains associated facet & meta information of selector
          /// @param facetAddress address of the facet which is connected with selector
          /// @param selectorPosition index in `FacetToSelectors.selectors` array, where is selector stored
          /// @param isFreezable denotes whether the selector can be frozen.
          struct SelectorToFacet {
              address facetAddress;
              uint16 selectorPosition;
              bool isFreezable;
          }
          /// @dev Utility struct that contains associated selectors & meta information of facet
          /// @param selectors list of all selectors that belong to the facet
          /// @param facetPosition index in `DiamondStorage.facets` array, where is facet stored
          struct FacetToSelectors {
              bytes4[] selectors;
              uint16 facetPosition;
          }
          /// @notice The structure that holds all diamond proxy associated parameters
          /// @dev According to the EIP-2535 should be stored on a special storage key - `DIAMOND_STORAGE_POSITION`
          /// @param selectorToFacet A mapping from the selector to the facet address and its meta information
          /// @param facetToSelectors A mapping from facet address to its selector with meta information
          /// @param facets The array of all unique facet addresses that belong to the diamond proxy
          /// @param isFrozen Denotes whether the diamond proxy is frozen and all freezable facets are not accessible
          struct DiamondStorage {
              mapping(bytes4 => SelectorToFacet) selectorToFacet;
              mapping(address => FacetToSelectors) facetToSelectors;
              address[] facets;
              bool isFrozen;
          }
          /// @dev Parameters for diamond changes that touch one of the facets
          /// @param facet The address of facet that's affected by the cut
          /// @param action The action that is made on the facet
          /// @param isFreezable Denotes whether the facet & all their selectors can be frozen
          /// @param selectors An array of unique selectors that belongs to the facet address
          struct FacetCut {
              address facet;
              Action action;
              bool isFreezable;
              bytes4[] selectors;
          }
          /// @dev Structure of the diamond proxy changes
          /// @param facetCuts The set of changes (adding/removing/replacement) of implementation contracts
          /// @param initAddress The address that's delegate called after setting up new facet changes
          /// @param initCalldata Calldata for the delegate call to `initAddress`
          struct DiamondCutData {
              FacetCut[] facetCuts;
              address initAddress;
              bytes initCalldata;
          }
          /// @dev Type of change over diamond: add/replace/remove facets
          enum Action {
              Add,
              Replace,
              Remove
          }
          /// @return diamondStorage The pointer to the storage where all specific diamond proxy parameters stored
          function getDiamondStorage() internal pure returns (DiamondStorage storage diamondStorage) {
              bytes32 position = DIAMOND_STORAGE_POSITION;
              assembly {
                  diamondStorage.slot := position
              }
          }
          /// @dev Add/replace/remove any number of selectors and optionally execute a function with delegatecall
          /// @param _diamondCut Diamond's facet changes and the parameters to optional initialization delegatecall
          function diamondCut(DiamondCutData memory _diamondCut) internal {
              FacetCut[] memory facetCuts = _diamondCut.facetCuts;
              address initAddress = _diamondCut.initAddress;
              bytes memory initCalldata = _diamondCut.initCalldata;
              uint256 facetCutsLength = facetCuts.length;
              for (uint256 i = 0; i < facetCutsLength; i = i.uncheckedInc()) {
                  Action action = facetCuts[i].action;
                  address facet = facetCuts[i].facet;
                  bool isFacetFreezable = facetCuts[i].isFreezable;
                  bytes4[] memory selectors = facetCuts[i].selectors;
                  require(selectors.length > 0, "B"); // no functions for diamond cut
                  if (action == Action.Add) {
                      _addFunctions(facet, selectors, isFacetFreezable);
                  } else if (action == Action.Replace) {
                      _replaceFunctions(facet, selectors, isFacetFreezable);
                  } else if (action == Action.Remove) {
                      _removeFunctions(facet, selectors);
                  } else {
                      revert("C"); // undefined diamond cut action
                  }
              }
              _initializeDiamondCut(initAddress, initCalldata);
              emit DiamondCut(facetCuts, initAddress, initCalldata);
          }
          /// @dev Add new functions to the diamond proxy
          /// NOTE: expect but NOT enforce that `_selectors` is NON-EMPTY array
          function _addFunctions(
              address _facet,
              bytes4[] memory _selectors,
              bool _isFacetFreezable
          ) private {
              DiamondStorage storage ds = getDiamondStorage();
              require(_facet != address(0), "G"); // facet with zero address cannot be added
              // Add facet to the list of facets if the facet address is new one
              _saveFacetIfNew(_facet);
              uint256 selectorsLength = _selectors.length;
              for (uint256 i = 0; i < selectorsLength; i = i.uncheckedInc()) {
                  bytes4 selector = _selectors[i];
                  SelectorToFacet memory oldFacet = ds.selectorToFacet[selector];
                  require(oldFacet.facetAddress == address(0), "J"); // facet for this selector already exists
                  _addOneFunction(_facet, selector, _isFacetFreezable);
              }
          }
          /// @dev Change associated facets to already known function selectors
          /// NOTE: expect but NOT enforce that `_selectors` is NON-EMPTY array
          function _replaceFunctions(
              address _facet,
              bytes4[] memory _selectors,
              bool _isFacetFreezable
          ) private {
              DiamondStorage storage ds = getDiamondStorage();
              require(_facet != address(0), "K"); // cannot replace facet with zero address
              uint256 selectorsLength = _selectors.length;
              for (uint256 i = 0; i < selectorsLength; i = i.uncheckedInc()) {
                  bytes4 selector = _selectors[i];
                  SelectorToFacet memory oldFacet = ds.selectorToFacet[selector];
                  require(oldFacet.facetAddress != address(0), "L"); // it is impossible to replace the facet with zero address
                  _removeOneFunction(oldFacet.facetAddress, selector);
                  // Add facet to the list of facets if the facet address is a new one
                  _saveFacetIfNew(_facet);
                  _addOneFunction(_facet, selector, _isFacetFreezable);
              }
          }
          /// @dev Remove association with function and facet
          /// NOTE: expect but NOT enforce that `_selectors` is NON-EMPTY array
          function _removeFunctions(address _facet, bytes4[] memory _selectors) private {
              DiamondStorage storage ds = getDiamondStorage();
              require(_facet == address(0), "a1"); // facet address must be zero
              uint256 selectorsLength = _selectors.length;
              for (uint256 i = 0; i < selectorsLength; i = i.uncheckedInc()) {
                  bytes4 selector = _selectors[i];
                  SelectorToFacet memory oldFacet = ds.selectorToFacet[selector];
                  require(oldFacet.facetAddress != address(0), "a2"); // Can't delete a non-existent facet
                  _removeOneFunction(oldFacet.facetAddress, selector);
              }
          }
          /// @dev Add address to the list of known facets if it is not on the list yet
          /// NOTE: should be called ONLY before adding a new selector associated with the address
          function _saveFacetIfNew(address _facet) private {
              DiamondStorage storage ds = getDiamondStorage();
              uint256 selectorsLength = ds.facetToSelectors[_facet].selectors.length;
              // If there are no selectors associated with facet then save facet as new one
              if (selectorsLength == 0) {
                  ds.facetToSelectors[_facet].facetPosition = ds.facets.length.toUint16();
                  ds.facets.push(_facet);
              }
          }
          /// @dev Add one function to the already known facet
          /// NOTE: It is expected but NOT enforced that:
          /// - `_facet` is NON-ZERO address
          /// - `_facet` is already stored address in `DiamondStorage.facets`
          /// - `_selector` is NOT associated by another facet
          function _addOneFunction(
              address _facet,
              bytes4 _selector,
              bool _isSelectorFreezable
          ) private {
              DiamondStorage storage ds = getDiamondStorage();
              uint16 selectorPosition = (ds.facetToSelectors[_facet].selectors.length).toUint16();
              // if selectorPosition is nonzero, it means it is not a new facet
              // so the freezability of the first selector must be matched to _isSelectorFreezable
              // so all the selectors in a facet will have the same freezability
              if (selectorPosition != 0) {
                  bytes4 selector0 = ds.facetToSelectors[_facet].selectors[0];
                  require(_isSelectorFreezable == ds.selectorToFacet[selector0].isFreezable, "J1");
              }
              ds.selectorToFacet[_selector] = SelectorToFacet({
                  facetAddress: _facet,
                  selectorPosition: selectorPosition,
                  isFreezable: _isSelectorFreezable
              });
              ds.facetToSelectors[_facet].selectors.push(_selector);
          }
          /// @dev Remove one associated function with facet
          /// NOTE: It is expected but NOT enforced that `_facet` is NON-ZERO address
          function _removeOneFunction(address _facet, bytes4 _selector) private {
              DiamondStorage storage ds = getDiamondStorage();
              // Get index of `FacetToSelectors.selectors` of the selector and last element of array
              uint256 selectorPosition = ds.selectorToFacet[_selector].selectorPosition;
              uint256 lastSelectorPosition = ds.facetToSelectors[_facet].selectors.length - 1;
              // If the selector is not at the end of the array then move the last element to the selector position
              if (selectorPosition != lastSelectorPosition) {
                  bytes4 lastSelector = ds.facetToSelectors[_facet].selectors[lastSelectorPosition];
                  ds.facetToSelectors[_facet].selectors[selectorPosition] = lastSelector;
                  ds.selectorToFacet[lastSelector].selectorPosition = selectorPosition.toUint16();
              }
              // Remove last element from the selectors array
              ds.facetToSelectors[_facet].selectors.pop();
              // Finally, clean up the association with facet
              delete ds.selectorToFacet[_selector];
              // If there are no selectors for facet then remove the facet from the list of known facets
              if (lastSelectorPosition == 0) {
                  _removeFacet(_facet);
              }
          }
          /// @dev remove facet from the list of known facets
          /// NOTE: It is expected but NOT enforced that there are no selectors associated with `_facet`
          function _removeFacet(address _facet) private {
              DiamondStorage storage ds = getDiamondStorage();
              // Get index of `DiamondStorage.facets` of the facet and last element of array
              uint256 facetPosition = ds.facetToSelectors[_facet].facetPosition;
              uint256 lastFacetPosition = ds.facets.length - 1;
              // If the facet is not at the end of the array then move the last element to the facet position
              if (facetPosition != lastFacetPosition) {
                  address lastFacet = ds.facets[lastFacetPosition];
                  ds.facets[facetPosition] = lastFacet;
                  ds.facetToSelectors[lastFacet].facetPosition = facetPosition.toUint16();
              }
              // Remove last element from the facets array
              ds.facets.pop();
          }
          /// @dev Delegates call to the initialization address with provided calldata
          /// @dev Used as a final step of diamond cut to execute the logic of the initialization for changed facets
          function _initializeDiamondCut(address _init, bytes memory _calldata) private {
              if (_init == address(0)) {
                  require(_calldata.length == 0, "H"); // Non-empty calldata for zero address
              } else {
                  // Do not check whether `_init` is a contract since later we check that it returns data.
                  (bool success, bytes memory data) = _init.delegatecall(_calldata);
                  require(success, "I"); // delegatecall failed
                  // Check that called contract returns magic value to make sure that contract logic
                  // supposed to be used as diamond cut initializer.
                  require(data.length == 32, "lp");
                  require(abi.decode(data, (bytes32)) == DIAMOND_INIT_SUCCESS_RETURN_VALUE, "lp1");
              }
          }
      }
      

      File 3 of 3: ExecutorFacet
      pragma solidity ^0.8.13;
      // SPDX-License-Identifier: MIT
      import "./interfaces/IAllowList.sol";
      /// @author Matter Labs
      abstract contract AllowListed {
          modifier senderCanCallFunction(IAllowList _allowList) {
              // Preventing the stack too deep error
              {
                  require(_allowList.canCall(msg.sender, address(this), msg.sig), "nr");
              }
              _;
          }
      }
      pragma solidity ^0.8.13;
      // SPDX-License-Identifier: MIT
      interface IAllowList {
          /*//////////////////////////////////////////////////////////////
                                  EVENTS
          //////////////////////////////////////////////////////////////*/
          /// @notice Access mode of target contract is changed
          event UpdateAccessMode(address indexed target, AccessMode previousMode, AccessMode newMode);
          /// @notice Permission to call is changed
          event UpdateCallPermission(address indexed caller, address indexed target, bytes4 indexed functionSig, bool status);
          /// @notice Type of access to a specific contract includes three different modes
          /// @param Closed No one has access to the contract
          /// @param SpecialAccessOnly Any address with granted special access can interact with a contract (see `hasSpecialAccessToCall`)
          /// @param Public Everyone can interact with a contract
          enum AccessMode {
              Closed,
              SpecialAccessOnly,
              Public
          }
          /// @dev A struct that contains deposit limit data of a token
          /// @param depositLimitation Whether any deposit limitation is placed or not
          /// @param depositCap The maximum amount that can be deposited.
          struct Deposit {
              bool depositLimitation;
              uint256 depositCap;
          }
          /*//////////////////////////////////////////////////////////////
                                  GETTERS
          //////////////////////////////////////////////////////////////*/
          function getAccessMode(address _target) external view returns (AccessMode);
          function hasSpecialAccessToCall(
              address _caller,
              address _target,
              bytes4 _functionSig
          ) external view returns (bool);
          function canCall(
              address _caller,
              address _target,
              bytes4 _functionSig
          ) external view returns (bool);
          function getTokenDepositLimitData(address _l1Token) external view returns (Deposit memory);
          /*//////////////////////////////////////////////////////////////
                                 ALLOW LIST LOGIC
          //////////////////////////////////////////////////////////////*/
          function setBatchAccessMode(address[] calldata _targets, AccessMode[] calldata _accessMode) external;
          function setAccessMode(address _target, AccessMode _accessMode) external;
          function setBatchPermissionToCall(
              address[] calldata _callers,
              address[] calldata _targets,
              bytes4[] calldata _functionSigs,
              bool[] calldata _enables
          ) external;
          function setPermissionToCall(
              address _caller,
              address _target,
              bytes4 _functionSig,
              bool _enable
          ) external;
          /*//////////////////////////////////////////////////////////////
                                 DEPOSIT LIMIT LOGIC
          //////////////////////////////////////////////////////////////*/
          function setDepositLimit(
              address _l1Token,
              bool _depositLimitation,
              uint256 _depositCap
          ) external;
      }
      pragma solidity ^0.8.0;
      // SPDX-License-Identifier: MIT
      /// @dev The address of the L2 deployer system contract.
      address constant L2_DEPLOYER_SYSTEM_CONTRACT_ADDR = address(0x8006);
      /// @dev The special reserved L2 address. It is located in the system contracts space but doesn't have deployed bytecode.
      /// @dev The L2 deployer system contract allows changing bytecodes on any address if the `msg.sender` is this address.
      /// @dev So, whenever the governor wants to redeploy system contracts, it just initiates the L1 upgrade call deployer system contract
      /// via the L1 -> L2 transaction with `sender == L2_FORCE_DEPLOYER_ADDR`. For more details see the `diamond-initializers` contracts.
      address constant L2_FORCE_DEPLOYER_ADDR = address(0x8007);
      /// @dev The address of the special smart contract that can send arbitrary length message as an L2 log
      address constant L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR = address(0x8008);
      /// @dev The formal address of the initial program of the system: the bootloader
      address constant L2_BOOTLOADER_ADDRESS = address(0x8001);
      /// @dev The address of the eth token system contract
      address constant L2_ETH_TOKEN_SYSTEM_CONTRACT_ADDR = address(0x800a);
      /// @dev The address of the known code storage system contract
      address constant L2_KNOWN_CODE_STORAGE_SYSTEM_CONTRACT_ADDR = address(0x8004);
      /// @dev The address of the context system contract
      address constant L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT_ADDR = address(0x800b);
      pragma solidity ^0.8.13;
      // SPDX-License-Identifier: MIT
      /**
       * @author Matter Labs
       * @notice Helper library for working with L2 contracts on L1.
       */
      library L2ContractHelper {
          /// @dev The prefix used to create CREATE2 addresses.
          bytes32 constant CREATE2_PREFIX = keccak256("zksyncCreate2");
          /// @notice Validate the bytecode format and calculate its hash.
          /// @param _bytecode The bytecode to hash.
          /// @return hashedBytecode The 32-byte hash of the bytecode.
          /// Note: The function reverts the execution if the bytecode has non expected format:
          /// - Bytecode bytes length is not a multiple of 32
          /// - Bytecode bytes length is not less than 2^21 bytes (2^16 words)
          /// - Bytecode words length is not odd
          function hashL2Bytecode(bytes memory _bytecode) internal pure returns (bytes32 hashedBytecode) {
              // Note that the length of the bytecode must be provided in 32-byte words.
              require(_bytecode.length % 32 == 0, "po");
              uint256 bytecodeLenInWords = _bytecode.length / 32;
              require(bytecodeLenInWords < 2**16, "pp"); // bytecode length must be less than 2^16 words
              require(bytecodeLenInWords % 2 == 1, "pr"); // bytecode length in words must be odd
              hashedBytecode = sha256(_bytecode) & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
              // Setting the version of the hash
              hashedBytecode = (hashedBytecode | bytes32(uint256(1 << 248)));
              // Setting the length
              hashedBytecode = hashedBytecode | bytes32(bytecodeLenInWords << 224);
          }
          /// @notice Validates the format of the given bytecode hash.
          /// @dev Due to the specification of the L2 bytecode hash, not every 32 bytes could be a legit bytecode hash.
          /// @dev The function reverts on invalid bytecode hash formam.
          /// @param _bytecodeHash The hash of the bytecode to validate.
          function validateBytecodeHash(bytes32 _bytecodeHash) internal pure {
              uint8 version = uint8(_bytecodeHash[0]);
              require(version == 1 && _bytecodeHash[1] == bytes1(0), "zf"); // Incorrectly formatted bytecodeHash
              require(_bytecodeLen(_bytecodeHash) % 2 == 1, "uy"); // Code length in words must be odd
          }
          /// @notice Returns the length of the bytecode associated with the given hash.
          /// @param _bytecodeHash The hash of the bytecode.
          /// @return codeLengthInWords The length of the bytecode in words.
          function _bytecodeLen(bytes32 _bytecodeHash) private pure returns (uint256 codeLengthInWords) {
              codeLengthInWords = uint256(uint8(_bytecodeHash[2])) * 256 + uint256(uint8(_bytecodeHash[3]));
          }
          /// @notice Computes the create2 address for a Layer 2 contract.
          /// @param _sender The address of the sender.
          /// @param _salt The salt value to use in the create2 address computation.
          /// @param _bytecodeHash The contract bytecode hash.
          /// @param _constructorInputHash The hash of the constructor input data.
          /// @return The create2 address of the contract.
          /// NOTE: L2 create2 derivation is different from L1 derivation!
          function computeCreate2Address(
              address _sender,
              bytes32 _salt,
              bytes32 _bytecodeHash,
              bytes32 _constructorInputHash
          ) internal pure returns (address) {
              bytes32 senderBytes = bytes32(uint256(uint160(_sender)));
              bytes32 data = keccak256(
                  bytes.concat(CREATE2_PREFIX, senderBytes, _salt, _bytecodeHash, _constructorInputHash)
              );
              return address(uint160(uint256(data)));
          }
      }
      pragma solidity ^0.8.13;
      // SPDX-License-Identifier: MIT
      library UncheckedMath {
          function uncheckedInc(uint256 _number) internal pure returns (uint256) {
              unchecked {
                  return _number + 1;
              }
          }
          function uncheckedAdd(uint256 _lhs, uint256 _rhs) internal pure returns (uint256) {
              unchecked {
                  return _lhs + _rhs;
              }
          }
      }
      pragma solidity ^0.8.13;
      // SPDX-License-Identifier: MIT
      /**
       * @author Matter Labs
       * @dev The library provides a set of functions that help read data from an "abi.encodePacked" byte array.
       * @dev Each of the functions accepts the `bytes memory` and the offset where data should be read and returns a value of a certain type.
       *
       * @dev WARNING!
       * 1) Functions don't check the length of the bytes array, so it can go out of bounds.
       * The user of the library must check for bytes length before using any functions from the library!
       *
       * 2) Read variables are not cleaned up - https://docs.soliditylang.org/en/v0.8.16/internals/variable_cleanup.html.
       * Using data in inline assembly can lead to unexpected behavior!
       */
      library UnsafeBytes {
          function readUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32 result, uint256 offset) {
              assembly {
                  offset := add(_start, 4)
                  result := mload(add(_bytes, offset))
              }
          }
          function readAddress(bytes memory _bytes, uint256 _start) internal pure returns (address result, uint256 offset) {
              assembly {
                  offset := add(_start, 20)
                  result := mload(add(_bytes, offset))
              }
          }
          function readUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256 result, uint256 offset) {
              assembly {
                  offset := add(_start, 32)
                  result := mload(add(_bytes, offset))
              }
          }
          function readBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32 result, uint256 offset) {
              assembly {
                  offset := add(_start, 32)
                  result := mload(add(_bytes, offset))
              }
          }
      }
      pragma solidity ^0.8.13;
      // SPDX-License-Identifier: MIT
      /**
       * @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].
       *
       * _Since v2.5.0:_ this module is now much more gas efficient, given net gas
       * metering changes introduced in the Istanbul hardfork.
       */
      abstract contract ReentrancyGuard {
          /// @dev Address of lock flag variable.
          /// @dev Flag is placed at random memory location to not interfere with Storage contract.
          uint256 private constant LOCK_FLAG_ADDRESS = 0x8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4; // keccak256("ReentrancyGuard") - 1;
          // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/566a774222707e424896c0c390a84dc3c13bdcb2/contracts/security/ReentrancyGuard.sol
          // 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;
          modifier reentrancyGuardInitializer() {
              _initializeReentrancyGuard();
              _;
          }
          function _initializeReentrancyGuard() private {
              uint256 lockSlotOldValue;
              // Storing an initial non-zero value makes deployment a bit more
              // expensive but in exchange every call to nonReentrant
              // will be cheaper.
              assembly {
                  lockSlotOldValue := sload(LOCK_FLAG_ADDRESS)
                  sstore(LOCK_FLAG_ADDRESS, _NOT_ENTERED)
              }
              // Check that storage slot for reentrancy guard is empty to rule out possibility of slot conflict
              require(lockSlotOldValue == 0, "1B");
          }
          /**
           * @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() {
              uint256 _status;
              assembly {
                  _status := sload(LOCK_FLAG_ADDRESS)
              }
              // On the first call to nonReentrant, _notEntered will be true
              require(_status == _NOT_ENTERED, "r1");
              // Any calls to nonReentrant after this point will fail
              assembly {
                  sstore(LOCK_FLAG_ADDRESS, _ENTERED)
              }
              _;
              // By storing the original value once again, a refund is triggered (see
              // https://eips.ethereum.org/EIPS/eip-2200)
              assembly {
                  sstore(LOCK_FLAG_ADDRESS, _NOT_ENTERED)
              }
          }
      }
      pragma solidity ^0.8.13;
      // SPDX-License-Identifier: MIT
      /// @dev `keccak256("")`
      bytes32 constant EMPTY_STRING_KECCAK = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
      /// @dev Bytes in raw L2 log
      /// @dev Equal to the bytes size of the tuple - (uint8 ShardId, bool isService, uint16 txNumberInBlock, address sender, bytes32 key, bytes32 value)
      uint256 constant L2_TO_L1_LOG_SERIALIZE_SIZE = 88;
      /// @dev The maximum length of the bytes array with L2 -> L1 logs
      uint256 constant MAX_L2_TO_L1_LOGS_COMMITMENT_BYTES = 4 + L2_TO_L1_LOG_SERIALIZE_SIZE * 512;
      /// @dev L2 -> L1 logs Merkle tree height
      uint256 constant L2_TO_L1_LOG_MERKLE_TREE_HEIGHT = 9;
      /// @dev The value of default leaf hash for L2 -> L1 logs Merkle tree
      /// @dev An incomplete fixed-size tree is filled with this value to be a full binary tree
      /// @dev Actually equal to the `keccak256(new bytes(L2_TO_L1_LOG_SERIALIZE_SIZE))`
      bytes32 constant L2_L1_LOGS_TREE_DEFAULT_LEAF_HASH = 0x72abee45b59e344af8a6e520241c4744aff26ed411f4c4b00f8af09adada43ba;
      /// @dev Number of bytes in a one initial storage change
      /// @dev Equal to the bytes size of the tuple - (bytes32 key, bytes32 value)
      uint256 constant INITIAL_STORAGE_CHANGE_SERIALIZE_SIZE = 64;
      /// @dev The maximum length of the bytes array with initial storage changes
      uint256 constant MAX_INITIAL_STORAGE_CHANGES_COMMITMENT_BYTES = 4 + INITIAL_STORAGE_CHANGE_SERIALIZE_SIZE * 4765;
      /// @dev Number of bytes in a one repeated storage change
      /// @dev Equal to the bytes size of the tuple - (bytes8 key, bytes32 value)
      uint256 constant REPEATED_STORAGE_CHANGE_SERIALIZE_SIZE = 40;
      /// @dev The maximum length of the bytes array with repeated storage changes
      uint256 constant MAX_REPEATED_STORAGE_CHANGES_COMMITMENT_BYTES = 4 + REPEATED_STORAGE_CHANGE_SERIALIZE_SIZE * 7564;
      // TODO: change constant to the real root hash of empty Merkle tree (SMA-184)
      bytes32 constant DEFAULT_L2_LOGS_TREE_ROOT_HASH = bytes32(0);
      /// @dev Denotes the first byte of the zkSync transaction that came from L1.
      uint256 constant PRIORITY_OPERATION_L2_TX_TYPE = 255;
      /// @dev The amount of time in seconds the validator has to process the priority transaction
      /// NOTE: The constant is set to zero for the Alpha release period
      uint256 constant PRIORITY_EXPIRATION = 0 days;
      /// @dev Notice period before activation preparation status of upgrade mode (in seconds)
      /// @dev NOTE: we must reserve for users enough time to send full exit operation, wait maximum time for processing this operation and withdraw funds from it.
      uint256 constant UPGRADE_NOTICE_PERIOD = 0;
      /// @dev Timestamp - seconds since unix epoch
      uint256 constant COMMIT_TIMESTAMP_NOT_OLDER = 365 days;
      /// @dev Maximum available error between real commit block timestamp and analog used in the verifier (in seconds)
      /// @dev Must be used cause miner's `block.timestamp` value can differ on some small value (as we know - 15 seconds)
      uint256 constant COMMIT_TIMESTAMP_APPROXIMATION_DELTA = 365 days;
      /// @dev Bit mask to apply for verifier public input before verifying.
      uint256 constant INPUT_MASK = 452312848583266388373324160190187140051835877600158453279131187530910662655;
      /// @dev The maximum number of L2 gas that a user can request for an L2 transaction
      uint256 constant L2_TX_MAX_GAS_LIMIT = 80000000;
      /// @dev The maximum number of the pubdata an L2 operation should be allowed to use.
      uint256 constant MAX_PUBDATA_PER_BLOCK = 110000;
      /// @dev The maximum number of the pubdata an priority operation should be allowed to use.
      /// For now, it is somewhat lower than the maximum number of pubdata allowed for an L2 transaction,
      /// to ensure that the transaction is definitely processable on L2 despite any potential overhead.
      uint256 constant PRIORITY_TX_MAX_PUBDATA = 99000;
      /// @dev The default price per L2 gas to be used for L1->L2 transactions
      uint256 constant FAIR_L2_GAS_PRICE = 500000000;
      /// @dev Even though the price for 1 byte of pubdata is 16 L1 gas, we have a slightly increased
      /// value.
      uint256 constant L1_GAS_PER_PUBDATA_BYTE = 17;
      /// @dev The computational overhead of processing an L2 block.
      uint256 constant BLOCK_OVERHEAD_L2_GAS = 1200000;
      /// @dev The overhead in L1 gas of interacting with the L1
      uint256 constant BLOCK_OVERHEAD_L1_GAS = 1000000;
      /// @dev The equivalent in L1 pubdata of L1 gas used for working with L1
      uint256 constant BLOCK_OVERHEAD_PUBDATA = BLOCK_OVERHEAD_L1_GAS / L1_GAS_PER_PUBDATA_BYTE;
      /// @dev The maximum number of transactions in L2 block:
      uint256 constant MAX_TRANSACTIONS_IN_BLOCK = 1024;
      /// @dev The size of the bootloader memory dedicated to the encodings of transactions
      uint256 constant BOOTLOADER_TX_ENCODING_SPACE = 485225;
      /// @dev The intrinsic cost of the L1->l2 transaction in computational L2 gas
      uint256 constant L1_TX_INTRINSIC_L2_GAS = 167157;
      /// @dev The intrinsic cost of the L1->l2 transaction in pubdata
      uint256 constant L1_TX_INTRINSIC_PUBDATA = 88;
      /// @dev The minimal base price for L1 transaction
      uint256 constant L1_TX_MIN_L2_GAS_BASE = 173484;
      /// @dev The number of L2 gas the transaction starts costing more with each 544 bytes of encoding
      uint256 constant L1_TX_DELTA_544_ENCODING_BYTES = 1656;
      /// @dev The number of L2 gas an L1->L2 transaction gains with each new factory dependency
      uint256 constant L1_TX_DELTA_FACTORY_DEPS_L2_GAS = 2473;
      /// @dev The number of L2 gas an L1->L2 transaction gains with each new factory dependency
      uint256 constant L1_TX_DELTA_FACTORY_DEPS_PUBDATA = 64;
      /// @dev The number of pubdata an L1->L2 transaction requires with each new factory dependency
      uint256 constant MAX_NEW_FACTORY_DEPS = 32;
      /// @dev The L2 gasPricePerPubdata required to be used in bridges.
      uint256 constant REQUIRED_L2_GAS_PRICE_PER_PUBDATA = 800;
      pragma solidity ^0.8.13;
      // SPDX-License-Identifier: MIT
      import "../Storage.sol";
      import "../../common/ReentrancyGuard.sol";
      import "../../common/AllowListed.sol";
      /// @title Base contract containing functions accessible to the other facets.
      /// @author Matter Labs
      contract Base is ReentrancyGuard, AllowListed {
          AppStorage internal s;
          /// @notice Checks that the message sender is an active governor
          modifier onlyGovernor() {
              require(msg.sender == s.governor, "1g"); // only by governor
              _;
          }
          /// @notice Checks if validator is active
          modifier onlyValidator() {
              require(s.validators[msg.sender], "1h"); // validator is not active
              _;
          }
          modifier onlySecurityCouncil() {
              require(msg.sender == s.upgrades.securityCouncil, "a9"); // not a security council
              _;
          }
      }
      pragma solidity ^0.8.13;
      // SPDX-License-Identifier: MIT
      import "./Base.sol";
      import "../Config.sol";
      import "../interfaces/IExecutor.sol";
      import "../libraries/PairingsBn254.sol";
      import "../libraries/PriorityQueue.sol";
      import "../../common/libraries/UncheckedMath.sol";
      import "../../common/libraries/UnsafeBytes.sol";
      import "../../common/libraries/L2ContractHelper.sol";
      import "../../common/L2ContractAddresses.sol";
      /// @title zkSync Executor contract capable of processing events emitted in the zkSync protocol.
      /// @author Matter Labs
      contract ExecutorFacet is Base, IExecutor {
          using UncheckedMath for uint256;
          using PriorityQueue for PriorityQueue.Queue;
          /// @dev Process one block commit using the previous block StoredBlockInfo
          /// @dev returns new block StoredBlockInfo
          /// @notice Does not change storage
          function _commitOneBlock(StoredBlockInfo memory _previousBlock, CommitBlockInfo calldata _newBlock)
              internal
              view
              returns (StoredBlockInfo memory)
          {
              require(_newBlock.blockNumber == _previousBlock.blockNumber + 1, "f"); // only commit next block
              // Check that block contain all meta information for L2 logs.
              // Get the chained hash of priority transaction hashes.
              (
                  uint256 expectedNumberOfLayer1Txs,
                  bytes32 expectedPriorityOperationsHash,
                  bytes32 previousBlockHash,
                  uint256 l2BlockTimestamp
              ) = _processL2Logs(_newBlock);
              require(_previousBlock.blockHash == previousBlockHash, "l");
              // Check that the priority operation hash in the L2 logs is as expected
              require(expectedPriorityOperationsHash == _newBlock.priorityOperationsHash, "t");
              // Check that the number of processed priority operations is as expected
              require(expectedNumberOfLayer1Txs == _newBlock.numberOfLayer1Txs, "ta");
              // Check that the timestamp that came from the Bootloader is expected
              require(l2BlockTimestamp == _newBlock.timestamp, "tb");
              // Preventing "stack too deep error"
              {
                  // Check the timestamp of the new block
                  bool timestampNotTooSmall = block.timestamp - COMMIT_TIMESTAMP_NOT_OLDER <= l2BlockTimestamp;
                  bool timestampNotTooBig = l2BlockTimestamp <= block.timestamp + COMMIT_TIMESTAMP_APPROXIMATION_DELTA;
                  require(timestampNotTooSmall, "h"); // New block timestamp is too small
                  require(timestampNotTooBig, "h1"); // New block timestamp is too big
                  // Check the index of repeated storage writes
                  uint256 newStorageChangesIndexes = uint256(uint32(bytes4(_newBlock.initialStorageChanges[:4])));
                  require(
                      _previousBlock.indexRepeatedStorageChanges + newStorageChangesIndexes ==
                          _newBlock.indexRepeatedStorageChanges,
                      "yq"
                  );
                  // NOTE: We don't check that _newBlock.timestamp > _previousBlock.timestamp, it is checked inside the L2
              }
              // Create block commitment for the proof verification
              bytes32 commitment = _createBlockCommitment(_newBlock);
              return
                  StoredBlockInfo(
                      _newBlock.blockNumber,
                      _newBlock.newStateRoot,
                      _newBlock.indexRepeatedStorageChanges,
                      _newBlock.numberOfLayer1Txs,
                      _newBlock.priorityOperationsHash,
                      _newBlock.l2LogsTreeRoot,
                      _newBlock.timestamp,
                      commitment
                  );
          }
          /// @dev Check that L2 logs are proper and block contain all meta information for them
          function _processL2Logs(CommitBlockInfo calldata _newBlock)
              internal
              pure
              returns (
                  uint256 numberOfLayer1Txs,
                  bytes32 chainedPriorityTxsHash,
                  bytes32 previousBlockHash,
                  uint256 blockTimestamp
              )
          {
              // Copy L2 to L1 logs into memory.
              bytes memory emittedL2Logs = _newBlock.l2Logs[4:];
              bytes[] calldata l2Messages = _newBlock.l2ArbitraryLengthMessages;
              uint256 currentMessage;
              // Auxiliary variable that is needed to enforce that `previousBlockHash` and `blockTimestamp` was read exactly one time
              bool isSystemContextLogProcessed;
              bytes[] calldata factoryDeps = _newBlock.factoryDeps;
              uint256 currentBytecode;
              chainedPriorityTxsHash = EMPTY_STRING_KECCAK;
              // linear traversal of the logs
              for (uint256 i = 0; i < emittedL2Logs.length; i = i.uncheckedAdd(L2_TO_L1_LOG_SERIALIZE_SIZE)) {
                  (address logSender, ) = UnsafeBytes.readAddress(emittedL2Logs, i + 4);
                  // show preimage for hashed message stored in log
                  if (logSender == L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR) {
                      (bytes32 hashedMessage, ) = UnsafeBytes.readBytes32(emittedL2Logs, i + 56);
                      require(keccak256(l2Messages[currentMessage]) == hashedMessage, "k2");
                      currentMessage = currentMessage.uncheckedInc();
                  } else if (logSender == L2_BOOTLOADER_ADDRESS) {
                      (bytes32 canonicalTxHash, ) = UnsafeBytes.readBytes32(emittedL2Logs, i + 24);
                      chainedPriorityTxsHash = keccak256(abi.encode(chainedPriorityTxsHash, canonicalTxHash));
                      // Overflow is not realistic
                      numberOfLayer1Txs = numberOfLayer1Txs.uncheckedInc();
                  } else if (logSender == L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT_ADDR) {
                      // Make sure that the system context log wasn't processed yet, to
                      // avoid accident double reading `blockTimestamp` and `previousBlockHash`
                      require(!isSystemContextLogProcessed, "fx");
                      (blockTimestamp, ) = UnsafeBytes.readUint256(emittedL2Logs, i + 24);
                      (previousBlockHash, ) = UnsafeBytes.readBytes32(emittedL2Logs, i + 56);
                      // Mark system context log as processed
                      isSystemContextLogProcessed = true;
                  } else if (logSender == L2_KNOWN_CODE_STORAGE_SYSTEM_CONTRACT_ADDR) {
                      (bytes32 bytecodeHash, ) = UnsafeBytes.readBytes32(emittedL2Logs, i + 24);
                      require(bytecodeHash == L2ContractHelper.hashL2Bytecode(factoryDeps[currentBytecode]), "k3");
                      currentBytecode = currentBytecode.uncheckedInc();
                  }
              }
              // To check that only relevant preimages have been included in the calldata
              require(currentBytecode == factoryDeps.length, "ym");
              require(currentMessage == l2Messages.length, "pl");
              // `blockTimestamp` and `previousBlockHash` wasn't read from L2 logs
              require(isSystemContextLogProcessed, "by");
          }
          /// @notice Commit block
          /// @notice 1. Checks timestamp.
          /// @notice 2. Process L2 logs.
          /// @notice 3. Store block commitments.
          function commitBlocks(StoredBlockInfo memory _lastCommittedBlockData, CommitBlockInfo[] calldata _newBlocksData)
              external
              override
              nonReentrant
              onlyValidator
          {
              // Check that we commit blocks after last committed block
              require(s.storedBlockHashes[s.totalBlocksCommitted] == _hashStoredBlockInfo(_lastCommittedBlockData), "i"); // incorrect previous block data
              uint256 blocksLength = _newBlocksData.length;
              for (uint256 i = 0; i < blocksLength; i = i.uncheckedInc()) {
                  _lastCommittedBlockData = _commitOneBlock(_lastCommittedBlockData, _newBlocksData[i]);
                  s.storedBlockHashes[_lastCommittedBlockData.blockNumber] = _hashStoredBlockInfo(_lastCommittedBlockData);
                  emit BlockCommit(
                      _lastCommittedBlockData.blockNumber,
                      _lastCommittedBlockData.blockHash,
                      _lastCommittedBlockData.commitment
                  );
              }
              s.totalBlocksCommitted = s.totalBlocksCommitted + blocksLength;
          }
          /// @dev Pops the priority operations from the priority queue and returns a rolling hash of operations
          function _collectOperationsFromPriorityQueue(uint256 _nPriorityOps) internal returns (bytes32 concatHash) {
              concatHash = EMPTY_STRING_KECCAK;
              for (uint256 i = 0; i < _nPriorityOps; i = i.uncheckedInc()) {
                  PriorityOperation memory priorityOp = s.priorityQueue.popFront();
                  concatHash = keccak256(abi.encode(concatHash, priorityOp.canonicalTxHash));
              }
          }
          /// @dev Executes one block
          /// @dev 1. Processes all pending operations (Complete priority requests)
          /// @dev 2. Finalizes block on Ethereum
          /// @dev _executedBlockIdx is an index in the array of the blocks that we want to execute together
          function _executeOneBlock(StoredBlockInfo memory _storedBlock, uint256 _executedBlockIdx) internal {
              uint256 currentBlockNumber = _storedBlock.blockNumber;
              require(currentBlockNumber == s.totalBlocksExecuted + _executedBlockIdx + 1, "k"); // Execute blocks in order
              require(
                  _hashStoredBlockInfo(_storedBlock) == s.storedBlockHashes[currentBlockNumber],
                  "exe10" // executing block should be committed
              );
              bytes32 priorityOperationsHash = _collectOperationsFromPriorityQueue(_storedBlock.numberOfLayer1Txs);
              require(priorityOperationsHash == _storedBlock.priorityOperationsHash, "x"); // priority operations hash does not match to expected
              // Save root hash of L2 -> L1 logs tree
              s.l2LogsRootHashes[currentBlockNumber] = _storedBlock.l2LogsTreeRoot;
          }
          /// @notice Execute blocks, complete priority operations and process withdrawals.
          /// @notice 1. Processes all pending operations (Complete priority requests)
          /// @notice 2. Finalizes block on Ethereum
          function executeBlocks(StoredBlockInfo[] calldata _blocksData) external nonReentrant onlyValidator {
              uint256 nBlocks = _blocksData.length;
              for (uint256 i = 0; i < nBlocks; i = i.uncheckedInc()) {
                  _executeOneBlock(_blocksData[i], i);
                  emit BlockExecution(_blocksData[i].blockNumber, _blocksData[i].blockHash, _blocksData[i].commitment);
              }
              s.totalBlocksExecuted = s.totalBlocksExecuted + nBlocks;
              require(s.totalBlocksExecuted <= s.totalBlocksVerified, "n"); // Can't execute blocks more than committed and proven currently.
          }
          /// @notice Blocks commitment verification.
          /// @notice Only verifies block commitments without any other processing
          function proveBlocks(
              StoredBlockInfo calldata _prevBlock,
              StoredBlockInfo[] calldata _committedBlocks,
              ProofInput calldata _proof
          ) external nonReentrant onlyValidator {
              // Save the variables into the stack to save gas on reading them later
              uint256 currentTotalBlocksVerified = s.totalBlocksVerified;
              uint256 committedBlocksLength = _committedBlocks.length;
              // Save the variable from the storage to memory to save gas
              VerifierParams memory verifierParams = s.verifierParams;
              // Initialize the array, that will be used as public input to the ZKP
              uint256[] memory proofPublicInput = new uint256[](committedBlocksLength);
              // Check that the block passed by the validator is indeed the first unverified block
              require(_hashStoredBlockInfo(_prevBlock) == s.storedBlockHashes[currentTotalBlocksVerified], "t1");
              bytes32 prevBlockCommitment = _prevBlock.commitment;
              for (uint256 i = 0; i < committedBlocksLength; i = i.uncheckedInc()) {
                  currentTotalBlocksVerified = currentTotalBlocksVerified.uncheckedInc();
                  require(_hashStoredBlockInfo(_committedBlocks[i]) == s.storedBlockHashes[currentTotalBlocksVerified], "o1");
                  bytes32 currentBlockCommitment = _committedBlocks[i].commitment;
                  proofPublicInput[i] = _getBlockProofPublicInput(
                      prevBlockCommitment,
                      currentBlockCommitment,
                      _proof,
                      verifierParams
                  );
                  prevBlockCommitment = currentBlockCommitment;
              }
              require(currentTotalBlocksVerified <= s.totalBlocksCommitted, "q");
              bool successVerifyProof = s.verifier.verify_serialized_proof(proofPublicInput, _proof.serializedProof);
              require(successVerifyProof, "p"); // Proof verification fail
              // Verify the recursive part that was given to us through the public input
              bool successProofAggregation = _verifyRecursivePartOfProof(_proof.recursiveAggregationInput);
              require(successProofAggregation, "hh"); // Proof aggregation must be valid
              emit BlocksVerification(s.totalBlocksVerified, currentTotalBlocksVerified);
              s.totalBlocksVerified = currentTotalBlocksVerified;
          }
          /// @dev Gets zk proof public input
          function _getBlockProofPublicInput(
              bytes32 _prevBlockCommitment,
              bytes32 _currentBlockCommitment,
              ProofInput calldata _proof,
              VerifierParams memory _verifierParams
          ) internal pure returns (uint256) {
              return
                  uint256(
                      keccak256(
                          abi.encodePacked(
                              _prevBlockCommitment,
                              _currentBlockCommitment,
                              _verifierParams.recursionNodeLevelVkHash,
                              _verifierParams.recursionLeafLevelVkHash,
                              _verifierParams.recursionCircuitsSetVksHash,
                              _proof.recursiveAggregationInput
                          )
                      )
                  ) & INPUT_MASK;
          }
          /// @dev Verify a part of the zkp, that is responsible for the aggregation
          function _verifyRecursivePartOfProof(uint256[] calldata _recursiveAggregationInput) internal view returns (bool) {
              require(_recursiveAggregationInput.length == 4, "vr");
              PairingsBn254.G1Point memory pairWithGen = PairingsBn254.new_g1_checked(
                  _recursiveAggregationInput[0],
                  _recursiveAggregationInput[1]
              );
              PairingsBn254.G1Point memory pairWithX = PairingsBn254.new_g1_checked(
                  _recursiveAggregationInput[2],
                  _recursiveAggregationInput[3]
              );
              PairingsBn254.G2Point memory g2Gen = PairingsBn254.new_g2(
                  [
                      0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2,
                      0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed
                  ],
                  [
                      0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b,
                      0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa
                  ]
              );
              PairingsBn254.G2Point memory g2X = PairingsBn254.new_g2(
                  [
                      0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1,
                      0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0
                  ],
                  [
                      0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4,
                      0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55
                  ]
              );
              return PairingsBn254.pairingProd2(pairWithGen, g2Gen, pairWithX, g2X);
          }
          /// @notice Reverts unexecuted blocks
          /// @param _newLastBlock block number after which blocks should be reverted
          /// NOTE: Doesn't delete the stored data about blocks, but only decreases
          /// counters that are responsible for the number of blocks
          function revertBlocks(uint256 _newLastBlock) external nonReentrant onlyValidator {
              require(s.totalBlocksCommitted > _newLastBlock, "v1"); // The last committed block is less than new last block
              uint256 newTotalBlocksCommitted = _maxU256(_newLastBlock, s.totalBlocksExecuted);
              if (newTotalBlocksCommitted < s.totalBlocksVerified) {
                  s.totalBlocksVerified = newTotalBlocksCommitted;
              }
              s.totalBlocksCommitted = newTotalBlocksCommitted;
              emit BlocksRevert(s.totalBlocksCommitted, s.totalBlocksVerified, s.totalBlocksExecuted);
          }
          /// @notice Returns larger of two values
          function _maxU256(uint256 a, uint256 b) internal pure returns (uint256) {
              return a < b ? b : a;
          }
          /// @dev Creates block commitment from its data
          function _createBlockCommitment(CommitBlockInfo calldata _newBlockData) internal view returns (bytes32) {
              bytes32 passThroughDataHash = keccak256(_blockPassThroughData(_newBlockData));
              bytes32 metadataHash = keccak256(_blockMetaParameters());
              bytes32 auxiliaryOutputHash = keccak256(_blockAuxiliaryOutput(_newBlockData));
              return keccak256(abi.encode(passThroughDataHash, metadataHash, auxiliaryOutputHash));
          }
          function _blockPassThroughData(CommitBlockInfo calldata _block) internal pure returns (bytes memory) {
              return
                  abi.encodePacked(
                      _block.indexRepeatedStorageChanges,
                      _block.newStateRoot,
                      uint64(0), // index repeated storage changes in zkPorter
                      bytes32(0) // zkPorter block hash
                  );
          }
          function _blockMetaParameters() internal view returns (bytes memory) {
              return abi.encodePacked(s.zkPorterIsAvailable, s.l2BootloaderBytecodeHash, s.l2DefaultAccountBytecodeHash);
          }
          function _blockAuxiliaryOutput(CommitBlockInfo calldata _block) internal pure returns (bytes memory) {
              require(_block.initialStorageChanges.length <= MAX_INITIAL_STORAGE_CHANGES_COMMITMENT_BYTES, "pf");
              require(_block.repeatedStorageChanges.length <= MAX_REPEATED_STORAGE_CHANGES_COMMITMENT_BYTES, "py");
              require(_block.l2Logs.length <= MAX_L2_TO_L1_LOGS_COMMITMENT_BYTES, "pu");
              bytes32 initialStorageChangesHash = keccak256(_block.initialStorageChanges);
              bytes32 repeatedStorageChangesHash = keccak256(_block.repeatedStorageChanges);
              bytes32 l2ToL1LogsHash = keccak256(_block.l2Logs);
              return abi.encode(_block.l2LogsTreeRoot, l2ToL1LogsHash, initialStorageChangesHash, repeatedStorageChangesHash);
          }
          /// @notice Returns the keccak hash of the ABI-encoded StoredBlockInfo
          function _hashStoredBlockInfo(StoredBlockInfo memory _storedBlockInfo) internal pure returns (bytes32) {
              return keccak256(abi.encode(_storedBlockInfo));
          }
      }
      pragma solidity ^0.8.13;
      // SPDX-License-Identifier: MIT
      interface IExecutor {
          /// @notice Rollup block stored data
          /// @param blockNumber Rollup block number
          /// @param blockHash Hash of L2 block
          /// @param indexRepeatedStorageChanges The serial number of the shortcut index that's used as a unique identifier for storage keys that were used twice or more
          /// @param numberOfLayer1Txs Number of priority operations to be processed
          /// @param priorityOperationsHash Hash of all priority operations from this block
          /// @param l2LogsTreeRoot Root hash of tree that contains L2 -> L1 messages from this block
          /// @param timestamp Rollup block timestamp, have the same format as Ethereum block constant
          /// @param commitment Verified input for the zkSync circuit
          struct StoredBlockInfo {
              uint64 blockNumber;
              bytes32 blockHash;
              uint64 indexRepeatedStorageChanges;
              uint256 numberOfLayer1Txs;
              bytes32 priorityOperationsHash;
              bytes32 l2LogsTreeRoot;
              uint256 timestamp;
              bytes32 commitment;
          }
          /// @notice Data needed to commit new block
          /// @param blockNumber Number of the committed block
          /// @param timestamp Unix timestamp denoting the start of the block execution
          /// @param indexRepeatedStorageChanges The serial number of the shortcut index that's used as a unique identifier for storage keys that were used twice or more
          /// @param newStateRoot The state root of the full state tree
          /// @param numberOfLayer1Txs Number of priority operations to be processed
          /// @param l2LogsTreeRoot The root hash of the tree that contains all L2 -> L1 logs in the block
          /// @param priorityOperationsHash Hash of all priority operations from this block
          /// @param initialStorageChanges Storage write access as a concatenation key-value
          /// @param repeatedStorageChanges Storage write access as a concatenation index-value
          /// @param l2Logs concatenation of all L2 -> L1 logs in the block
          /// @param l2ArbitraryLengthMessages array of hash preimages that were sent as value of L2 logs by special system L2 contract
          /// @param factoryDeps array of l2 bytecodes that were marked as known on L2
          struct CommitBlockInfo {
              uint64 blockNumber;
              uint64 timestamp;
              uint64 indexRepeatedStorageChanges;
              bytes32 newStateRoot;
              uint256 numberOfLayer1Txs;
              bytes32 l2LogsTreeRoot;
              bytes32 priorityOperationsHash;
              bytes initialStorageChanges;
              bytes repeatedStorageChanges;
              bytes l2Logs;
              bytes[] l2ArbitraryLengthMessages;
              bytes[] factoryDeps;
          }
          /// @notice Recursive proof input data (individual commitments are constructed onchain)
          struct ProofInput {
              uint256[] recursiveAggregationInput;
              uint256[] serializedProof;
          }
          function commitBlocks(StoredBlockInfo calldata _lastCommittedBlockData, CommitBlockInfo[] calldata _newBlocksData)
              external;
          function proveBlocks(
              StoredBlockInfo calldata _prevBlock,
              StoredBlockInfo[] calldata _committedBlocks,
              ProofInput calldata _proof
          ) external;
          function executeBlocks(StoredBlockInfo[] calldata _blocksData) external;
          function revertBlocks(uint256 _newLastBlock) external;
          /// @notice Event emitted when a block is committed
          event BlockCommit(uint256 indexed blockNumber, bytes32 indexed blockHash, bytes32 indexed commitment);
          /// @notice Event emitted when blocks are verified
          event BlocksVerification(uint256 indexed previousLastVerifiedBlock, uint256 indexed currentLastVerifiedBlock);
          /// @notice Event emitted when a block is executed
          event BlockExecution(uint256 indexed blockNumber, bytes32 indexed blockHash, bytes32 indexed commitment);
          /// @notice Event emitted when blocks are reverted
          event BlocksRevert(uint256 totalBlocksCommitted, uint256 totalBlocksVerified, uint256 totalBlocksExecuted);
      }
      pragma solidity ^0.8.13;
      // SPDX-License-Identifier: MIT
      library PairingsBn254 {
          uint256 constant q_mod = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
          uint256 constant r_mod = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
          uint256 constant bn254_b_coeff = 3;
          struct G1Point {
              uint256 X;
              uint256 Y;
          }
          struct Fr {
              uint256 value;
          }
          function new_fr(uint256 fr) internal pure returns (Fr memory) {
              require(fr < r_mod);
              return Fr({value: fr});
          }
          function copy(Fr memory self) internal pure returns (Fr memory n) {
              n.value = self.value;
          }
          function assign(Fr memory self, Fr memory other) internal pure {
              self.value = other.value;
          }
          function inverse(Fr memory fr) internal view returns (Fr memory) {
              require(fr.value != 0);
              return pow(fr, r_mod - 2);
          }
          function add_assign(Fr memory self, Fr memory other) internal pure {
              self.value = addmod(self.value, other.value, r_mod);
          }
          function sub_assign(Fr memory self, Fr memory other) internal pure {
              self.value = addmod(self.value, r_mod - other.value, r_mod);
          }
          function mul_assign(Fr memory self, Fr memory other) internal pure {
              self.value = mulmod(self.value, other.value, r_mod);
          }
          function pow(Fr memory self, uint256 power) internal view returns (Fr memory) {
              uint256[6] memory input = [32, 32, 32, self.value, power, r_mod];
              uint256[1] memory result;
              bool success;
              assembly {
                  success := staticcall(gas(), 0x05, input, 0xc0, result, 0x20)
              }
              require(success);
              return Fr({value: result[0]});
          }
          // Encoding of field elements is: X[0] * z + X[1]
          struct G2Point {
              uint256[2] X;
              uint256[2] Y;
          }
          function P1() internal pure returns (G1Point memory) {
              return G1Point(1, 2);
          }
          function new_g1(uint256 x, uint256 y) internal pure returns (G1Point memory) {
              return G1Point(x, y);
          }
          // function new_g1_checked(uint256 x, uint256 y) internal pure returns (G1Point memory) {
          function new_g1_checked(uint256 x, uint256 y) internal pure returns (G1Point memory) {
              if (x == 0 && y == 0) {
                  // point of infinity is (0,0)
                  return G1Point(x, y);
              }
              // check encoding
              require(x < q_mod, "x axis isn't valid");
              require(y < q_mod, "y axis isn't valid");
              // check on curve
              uint256 lhs = mulmod(y, y, q_mod); // y^2
              uint256 rhs = mulmod(x, x, q_mod); // x^2
              rhs = mulmod(rhs, x, q_mod); // x^3
              rhs = addmod(rhs, bn254_b_coeff, q_mod); // x^3 + b
              require(lhs == rhs, "is not on curve");
              return G1Point(x, y);
          }
          function new_g2(uint256[2] memory x, uint256[2] memory y) internal pure returns (G2Point memory) {
              return G2Point(x, y);
          }
          function copy_g1(G1Point memory self) internal pure returns (G1Point memory result) {
              result.X = self.X;
              result.Y = self.Y;
          }
          function P2() internal pure returns (G2Point memory) {
              // for some reason ethereum expects to have c1*v + c0 form
              return
                  G2Point(
                      [
                          0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2,
                          0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed
                      ],
                      [
                          0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b,
                          0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa
                      ]
                  );
          }
          function negate(G1Point memory self) internal pure {
              // The prime q in the base field F_q for G1
              if (self.Y == 0) {
                  require(self.X == 0);
                  return;
              }
              self.Y = q_mod - self.Y;
          }
          function point_add(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) {
              point_add_into_dest(p1, p2, r);
              return r;
          }
          function point_add_assign(G1Point memory p1, G1Point memory p2) internal view {
              point_add_into_dest(p1, p2, p1);
          }
          function point_add_into_dest(
              G1Point memory p1,
              G1Point memory p2,
              G1Point memory dest
          ) internal view {
              if (p2.X == 0 && p2.Y == 0) {
                  // we add zero, nothing happens
                  dest.X = p1.X;
                  dest.Y = p1.Y;
                  return;
              } else if (p1.X == 0 && p1.Y == 0) {
                  // we add into zero, and we add non-zero point
                  dest.X = p2.X;
                  dest.Y = p2.Y;
                  return;
              } else {
                  uint256[4] memory input;
                  input[0] = p1.X;
                  input[1] = p1.Y;
                  input[2] = p2.X;
                  input[3] = p2.Y;
                  bool success;
                  assembly {
                      success := staticcall(gas(), 6, input, 0x80, dest, 0x40)
                  }
                  require(success);
              }
          }
          function point_sub_assign(G1Point memory p1, G1Point memory p2) internal view {
              point_sub_into_dest(p1, p2, p1);
          }
          function point_sub_into_dest(
              G1Point memory p1,
              G1Point memory p2,
              G1Point memory dest
          ) internal view {
              if (p2.X == 0 && p2.Y == 0) {
                  // we subtracted zero, nothing happens
                  dest.X = p1.X;
                  dest.Y = p1.Y;
                  return;
              } else if (p1.X == 0 && p1.Y == 0) {
                  // we subtract from zero, and we subtract non-zero point
                  dest.X = p2.X;
                  dest.Y = q_mod - p2.Y;
                  return;
              } else {
                  uint256[4] memory input;
                  input[0] = p1.X;
                  input[1] = p1.Y;
                  input[2] = p2.X;
                  input[3] = q_mod - p2.Y;
                  bool success = false;
                  assembly {
                      success := staticcall(gas(), 6, input, 0x80, dest, 0x40)
                  }
                  require(success);
              }
          }
          function point_mul(G1Point memory p, Fr memory s) internal view returns (G1Point memory r) {
              // https://eips.ethereum.org/EIPS/eip-197
              // Elliptic curve points are encoded as a Jacobian pair (X, Y) where the point at infinity is encoded as (0, 0)
              if (p.X == 0 && p.Y == 1) {
                  p.Y = 0;
              }
              point_mul_into_dest(p, s, r);
              return r;
          }
          function point_mul_assign(G1Point memory p, Fr memory s) internal view {
              point_mul_into_dest(p, s, p);
          }
          function point_mul_into_dest(
              G1Point memory p,
              Fr memory s,
              G1Point memory dest
          ) internal view {
              uint256[3] memory input;
              input[0] = p.X;
              input[1] = p.Y;
              input[2] = s.value;
              bool success;
              assembly {
                  success := staticcall(gas(), 7, input, 0x60, dest, 0x40)
              }
              require(success);
          }
          function pairing(G1Point[] memory p1, G2Point[] memory p2) internal view returns (bool) {
              require(p1.length == p2.length);
              uint256 elements = p1.length;
              uint256 inputSize = elements * 6;
              uint256[] memory input = new uint256[](inputSize);
              for (uint256 i = 0; i < elements; ) {
                  input[i * 6 + 0] = p1[i].X;
                  input[i * 6 + 1] = p1[i].Y;
                  input[i * 6 + 2] = p2[i].X[0];
                  input[i * 6 + 3] = p2[i].X[1];
                  input[i * 6 + 4] = p2[i].Y[0];
                  input[i * 6 + 5] = p2[i].Y[1];
                  unchecked {
                      ++i;
                  }
              }
              uint256[1] memory out;
              bool success;
              assembly {
                  success := staticcall(gas(), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
              }
              require(success);
              return out[0] != 0;
          }
          /// Convenience method for a pairing check for two pairs.
          function pairingProd2(
              G1Point memory a1,
              G2Point memory a2,
              G1Point memory b1,
              G2Point memory b2
          ) internal view returns (bool) {
              G1Point[] memory p1 = new G1Point[](2);
              G2Point[] memory p2 = new G2Point[](2);
              p1[0] = a1;
              p1[1] = b1;
              p2[0] = a2;
              p2[1] = b2;
              return pairing(p1, p2);
          }
      }
      pragma solidity ^0.8.13;
      // SPDX-License-Identifier: MIT
      /// @notice The structure that contains meta information of the L2 transaction that was requested from L1
      /// @dev The weird size of fields was selected specifically to minimize the structure storage size
      /// @param canonicalTxHash Hashed L2 transaction data that is needed to process it
      /// @param expirationTimestamp Expiration timestamp for this request (must be satisfied before)
      /// @param layer2Tip Additional payment to the validator as an incentive to perform the operation
      struct PriorityOperation {
          bytes32 canonicalTxHash;
          uint64 expirationTimestamp;
          uint192 layer2Tip;
      }
      /// @author Matter Labs
      /// @dev The library provides the API to interact with the priority queue container
      /// @dev Order of processing operations from queue - FIFO (Fist in - first out)
      library PriorityQueue {
          using PriorityQueue for Queue;
          /// @notice Container that stores priority operations
          /// @param data The inner mapping that saves priority operation by its index
          /// @param head The pointer to the first unprocessed priority operation, equal to the tail if the queue is empty
          /// @param tail The pointer to the free slot
          struct Queue {
              mapping(uint256 => PriorityOperation) data;
              uint256 tail;
              uint256 head;
          }
          /// @notice Returns zero if and only if no operations were processed from the queue
          /// @return Index of the oldest priority operation that wasn't processed yet
          function getFirstUnprocessedPriorityTx(Queue storage _queue) internal view returns (uint256) {
              return _queue.head;
          }
          /// @return The total number of priority operations that were added to the priority queue, including all processed ones
          function getTotalPriorityTxs(Queue storage _queue) internal view returns (uint256) {
              return _queue.tail;
          }
          /// @return The total number of unprocessed priority operations in a priority queue
          function getSize(Queue storage _queue) internal view returns (uint256) {
              return uint256(_queue.tail - _queue.head);
          }
          /// @return Whether the priority queue contains no operations
          function isEmpty(Queue storage _queue) internal view returns (bool) {
              return _queue.tail == _queue.head;
          }
          /// @notice Add the priority operation to the end of the priority queue
          function pushBack(Queue storage _queue, PriorityOperation memory _operation) internal {
              // Save value into the stack to avoid double reading from the storage
              uint256 tail = _queue.tail;
              _queue.data[tail] = _operation;
              _queue.tail = tail + 1;
          }
          /// @return The first unprocessed priority operation from the queue
          function front(Queue storage _queue) internal view returns (PriorityOperation memory) {
              require(!_queue.isEmpty(), "D"); // priority queue is empty
              return _queue.data[_queue.head];
          }
          /// @notice Remove the first unprocessed priority operation from the queue
          /// @return priorityOperation that was popped from the priority queue
          function popFront(Queue storage _queue) internal returns (PriorityOperation memory priorityOperation) {
              require(!_queue.isEmpty(), "s"); // priority queue is empty
              // Save value into the stack to avoid double reading from the storage
              uint256 head = _queue.head;
              priorityOperation = _queue.data[head];
              delete _queue.data[head];
              _queue.head = head + 1;
          }
      }
      pragma solidity ^0.8.13;
      // SPDX-License-Identifier: MIT
      import "./PairingsBn254.sol";
      library TranscriptLib {
          // flip                    0xe000000000000000000000000000000000000000000000000000000000000000;
          uint256 constant FR_MASK = 0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
          uint32 constant DST_0 = 0;
          uint32 constant DST_1 = 1;
          uint32 constant DST_CHALLENGE = 2;
          struct Transcript {
              bytes32 state_0;
              bytes32 state_1;
              uint32 challenge_counter;
          }
          function new_transcript() internal pure returns (Transcript memory t) {
              t.state_0 = bytes32(0);
              t.state_1 = bytes32(0);
              t.challenge_counter = 0;
          }
          function update_with_u256(Transcript memory self, uint256 value) internal pure {
              bytes32 old_state_0 = self.state_0;
              self.state_0 = keccak256(abi.encodePacked(DST_0, old_state_0, self.state_1, value));
              self.state_1 = keccak256(abi.encodePacked(DST_1, old_state_0, self.state_1, value));
          }
          function update_with_fr(Transcript memory self, PairingsBn254.Fr memory value) internal pure {
              update_with_u256(self, value.value);
          }
          function update_with_g1(Transcript memory self, PairingsBn254.G1Point memory p) internal pure {
              update_with_u256(self, p.X);
              update_with_u256(self, p.Y);
          }
          function get_challenge(Transcript memory self) internal pure returns (PairingsBn254.Fr memory challenge) {
              bytes32 query = keccak256(abi.encodePacked(DST_CHALLENGE, self.state_0, self.state_1, self.challenge_counter));
              self.challenge_counter += 1;
              challenge = PairingsBn254.Fr({value: uint256(query) & FR_MASK});
          }
      }
      pragma solidity ^0.8.13;
      // SPDX-License-Identifier: MIT
      import "./libraries/PairingsBn254.sol";
      import "./libraries/TranscriptLib.sol";
      import "../common/libraries/UncheckedMath.sol";
      uint256 constant STATE_WIDTH = 4;
      uint256 constant NUM_G2_ELS = 2;
      struct VerificationKey {
          uint256 domain_size;
          uint256 num_inputs;
          PairingsBn254.Fr omega;
          PairingsBn254.G1Point[2] gate_selectors_commitments;
          PairingsBn254.G1Point[8] gate_setup_commitments;
          PairingsBn254.G1Point[STATE_WIDTH] permutation_commitments;
          PairingsBn254.G1Point lookup_selector_commitment;
          PairingsBn254.G1Point[4] lookup_tables_commitments;
          PairingsBn254.G1Point lookup_table_type_commitment;
          PairingsBn254.Fr[STATE_WIDTH - 1] non_residues;
          PairingsBn254.G2Point[NUM_G2_ELS] g2_elements;
      }
      contract Plonk4VerifierWithAccessToDNext {
          using PairingsBn254 for PairingsBn254.G1Point;
          using PairingsBn254 for PairingsBn254.G2Point;
          using PairingsBn254 for PairingsBn254.Fr;
          using TranscriptLib for TranscriptLib.Transcript;
          using UncheckedMath for uint256;
          struct Proof {
              uint256[] input_values;
              // commitments
              PairingsBn254.G1Point[STATE_WIDTH] state_polys_commitments;
              PairingsBn254.G1Point copy_permutation_grand_product_commitment;
              PairingsBn254.G1Point[STATE_WIDTH] quotient_poly_parts_commitments;
              // openings
              PairingsBn254.Fr[STATE_WIDTH] state_polys_openings_at_z;
              PairingsBn254.Fr[1] state_polys_openings_at_z_omega;
              PairingsBn254.Fr[1] gate_selectors_openings_at_z;
              PairingsBn254.Fr[STATE_WIDTH - 1] copy_permutation_polys_openings_at_z;
              PairingsBn254.Fr copy_permutation_grand_product_opening_at_z_omega;
              PairingsBn254.Fr quotient_poly_opening_at_z;
              PairingsBn254.Fr linearization_poly_opening_at_z;
              // lookup commitments
              PairingsBn254.G1Point lookup_s_poly_commitment;
              PairingsBn254.G1Point lookup_grand_product_commitment;
              // lookup openings
              PairingsBn254.Fr lookup_s_poly_opening_at_z_omega;
              PairingsBn254.Fr lookup_grand_product_opening_at_z_omega;
              PairingsBn254.Fr lookup_t_poly_opening_at_z;
              PairingsBn254.Fr lookup_t_poly_opening_at_z_omega;
              PairingsBn254.Fr lookup_selector_poly_opening_at_z;
              PairingsBn254.Fr lookup_table_type_poly_opening_at_z;
              PairingsBn254.G1Point opening_proof_at_z;
              PairingsBn254.G1Point opening_proof_at_z_omega;
          }
          struct PartialVerifierState {
              PairingsBn254.Fr zero;
              PairingsBn254.Fr alpha;
              PairingsBn254.Fr beta;
              PairingsBn254.Fr gamma;
              PairingsBn254.Fr[9] alpha_values;
              PairingsBn254.Fr eta;
              PairingsBn254.Fr beta_lookup;
              PairingsBn254.Fr gamma_lookup;
              PairingsBn254.Fr beta_plus_one;
              PairingsBn254.Fr beta_gamma;
              PairingsBn254.Fr v;
              PairingsBn254.Fr u;
              PairingsBn254.Fr z;
              PairingsBn254.Fr z_omega;
              PairingsBn254.Fr z_minus_last_omega;
              PairingsBn254.Fr l_0_at_z;
              PairingsBn254.Fr l_n_minus_one_at_z;
              PairingsBn254.Fr t;
              PairingsBn254.G1Point tp;
          }
          function evaluate_l0_at_point(uint256 domain_size, PairingsBn254.Fr memory at)
              internal
              view
              returns (PairingsBn254.Fr memory num)
          {
              PairingsBn254.Fr memory one = PairingsBn254.new_fr(1);
              PairingsBn254.Fr memory size_fe = PairingsBn254.new_fr(domain_size);
              PairingsBn254.Fr memory den = at.copy();
              den.sub_assign(one);
              den.mul_assign(size_fe);
              den = den.inverse();
              num = at.pow(domain_size);
              num.sub_assign(one);
              num.mul_assign(den);
          }
          function evaluate_lagrange_poly_out_of_domain(
              uint256 poly_num,
              uint256 domain_size,
              PairingsBn254.Fr memory omega,
              PairingsBn254.Fr memory at
          ) internal view returns (PairingsBn254.Fr memory res) {
              // (omega^i / N) / (X - omega^i) * (X^N - 1)
              require(poly_num < domain_size);
              PairingsBn254.Fr memory one = PairingsBn254.new_fr(1);
              PairingsBn254.Fr memory omega_power = omega.pow(poly_num);
              res = at.pow(domain_size);
              res.sub_assign(one);
              require(res.value != 0); // Vanishing polynomial can not be zero at point `at`
              res.mul_assign(omega_power);
              PairingsBn254.Fr memory den = PairingsBn254.copy(at);
              den.sub_assign(omega_power);
              den.mul_assign(PairingsBn254.new_fr(domain_size));
              den = den.inverse();
              res.mul_assign(den);
          }
          function evaluate_vanishing(uint256 domain_size, PairingsBn254.Fr memory at)
              internal
              view
              returns (PairingsBn254.Fr memory res)
          {
              res = at.pow(domain_size);
              res.sub_assign(PairingsBn254.new_fr(1));
          }
          function initialize_transcript(Proof memory proof, VerificationKey memory vk)
              internal
              pure
              returns (PartialVerifierState memory state)
          {
              TranscriptLib.Transcript memory transcript = TranscriptLib.new_transcript();
              for (uint256 i = 0; i < vk.num_inputs; i = i.uncheckedInc()) {
                  transcript.update_with_u256(proof.input_values[i]);
              }
              for (uint256 i = 0; i < STATE_WIDTH; i = i.uncheckedInc()) {
                  transcript.update_with_g1(proof.state_polys_commitments[i]);
              }
              state.eta = transcript.get_challenge();
              transcript.update_with_g1(proof.lookup_s_poly_commitment);
              state.beta = transcript.get_challenge();
              state.gamma = transcript.get_challenge();
              transcript.update_with_g1(proof.copy_permutation_grand_product_commitment);
              state.beta_lookup = transcript.get_challenge();
              state.gamma_lookup = transcript.get_challenge();
              transcript.update_with_g1(proof.lookup_grand_product_commitment);
              state.alpha = transcript.get_challenge();
              for (uint256 i = 0; i < proof.quotient_poly_parts_commitments.length; i = i.uncheckedInc()) {
                  transcript.update_with_g1(proof.quotient_poly_parts_commitments[i]);
              }
              state.z = transcript.get_challenge();
              transcript.update_with_fr(proof.quotient_poly_opening_at_z);
              for (uint256 i = 0; i < proof.state_polys_openings_at_z.length; i = i.uncheckedInc()) {
                  transcript.update_with_fr(proof.state_polys_openings_at_z[i]);
              }
              for (uint256 i = 0; i < proof.state_polys_openings_at_z_omega.length; i = i.uncheckedInc()) {
                  transcript.update_with_fr(proof.state_polys_openings_at_z_omega[i]);
              }
              for (uint256 i = 0; i < proof.gate_selectors_openings_at_z.length; i = i.uncheckedInc()) {
                  transcript.update_with_fr(proof.gate_selectors_openings_at_z[i]);
              }
              for (uint256 i = 0; i < proof.copy_permutation_polys_openings_at_z.length; i = i.uncheckedInc()) {
                  transcript.update_with_fr(proof.copy_permutation_polys_openings_at_z[i]);
              }
              state.z_omega = state.z.copy();
              state.z_omega.mul_assign(vk.omega);
              transcript.update_with_fr(proof.copy_permutation_grand_product_opening_at_z_omega);
              transcript.update_with_fr(proof.lookup_t_poly_opening_at_z);
              transcript.update_with_fr(proof.lookup_selector_poly_opening_at_z);
              transcript.update_with_fr(proof.lookup_table_type_poly_opening_at_z);
              transcript.update_with_fr(proof.lookup_s_poly_opening_at_z_omega);
              transcript.update_with_fr(proof.lookup_grand_product_opening_at_z_omega);
              transcript.update_with_fr(proof.lookup_t_poly_opening_at_z_omega);
              transcript.update_with_fr(proof.linearization_poly_opening_at_z);
              state.v = transcript.get_challenge();
              transcript.update_with_g1(proof.opening_proof_at_z);
              transcript.update_with_g1(proof.opening_proof_at_z_omega);
              state.u = transcript.get_challenge();
          }
          // compute some powers of challenge alpha([alpha^1, .. alpha^8])
          function compute_powers_of_alpha(PartialVerifierState memory state) public pure {
              require(state.alpha.value != 0);
              state.alpha_values[0] = PairingsBn254.new_fr(1);
              state.alpha_values[1] = state.alpha.copy();
              PairingsBn254.Fr memory current_alpha = state.alpha.copy();
              for (uint256 i = 2; i < state.alpha_values.length; i = i.uncheckedInc()) {
                  current_alpha.mul_assign(state.alpha);
                  state.alpha_values[i] = current_alpha.copy();
              }
          }
          function verify(Proof memory proof, VerificationKey memory vk) internal view returns (bool) {
              // we initialize all challenges beforehand, we can draw each challenge in its own place
              PartialVerifierState memory state = initialize_transcript(proof, vk);
              if (verify_quotient_evaluation(vk, proof, state) == false) {
                  return false;
              }
              require(proof.state_polys_openings_at_z_omega.length == 1);
              PairingsBn254.G1Point memory quotient_result = proof.quotient_poly_parts_commitments[0].copy_g1();
              {
                  // block scope
                  PairingsBn254.Fr memory z_in_domain_size = state.z.pow(vk.domain_size);
                  PairingsBn254.Fr memory current_z = z_in_domain_size.copy();
                  PairingsBn254.G1Point memory tp;
                  // start from i =1
                  for (uint256 i = 1; i < proof.quotient_poly_parts_commitments.length; i = i.uncheckedInc()) {
                      tp = proof.quotient_poly_parts_commitments[i].copy_g1();
                      tp.point_mul_assign(current_z);
                      quotient_result.point_add_assign(tp);
                      current_z.mul_assign(z_in_domain_size);
                  }
              }
              Queries memory queries = prepare_queries(vk, proof, state);
              queries.commitments_at_z[0] = quotient_result;
              queries.values_at_z[0] = proof.quotient_poly_opening_at_z;
              queries.commitments_at_z[1] = aggregated_linearization_commitment(vk, proof, state);
              queries.values_at_z[1] = proof.linearization_poly_opening_at_z;
              require(queries.commitments_at_z.length == queries.values_at_z.length);
              PairingsBn254.G1Point memory aggregated_commitment_at_z = queries.commitments_at_z[0];
              PairingsBn254.Fr memory aggregated_opening_at_z = queries.values_at_z[0];
              PairingsBn254.Fr memory aggregation_challenge = PairingsBn254.new_fr(1);
              PairingsBn254.G1Point memory scaled;
              for (uint256 i = 1; i < queries.commitments_at_z.length; i = i.uncheckedInc()) {
                  aggregation_challenge.mul_assign(state.v);
                  scaled = queries.commitments_at_z[i].point_mul(aggregation_challenge);
                  aggregated_commitment_at_z.point_add_assign(scaled);
                  state.t = queries.values_at_z[i];
                  state.t.mul_assign(aggregation_challenge);
                  aggregated_opening_at_z.add_assign(state.t);
              }
              aggregation_challenge.mul_assign(state.v);
              PairingsBn254.G1Point memory aggregated_commitment_at_z_omega = queries.commitments_at_z_omega[0].point_mul(
                  aggregation_challenge
              );
              PairingsBn254.Fr memory aggregated_opening_at_z_omega = queries.values_at_z_omega[0];
              aggregated_opening_at_z_omega.mul_assign(aggregation_challenge);
              for (uint256 i = 1; i < queries.commitments_at_z_omega.length; i = i.uncheckedInc()) {
                  aggregation_challenge.mul_assign(state.v);
                  scaled = queries.commitments_at_z_omega[i].point_mul(aggregation_challenge);
                  aggregated_commitment_at_z_omega.point_add_assign(scaled);
                  state.t = queries.values_at_z_omega[i];
                  state.t.mul_assign(aggregation_challenge);
                  aggregated_opening_at_z_omega.add_assign(state.t);
              }
              return
                  final_pairing(
                      vk.g2_elements,
                      proof,
                      state,
                      aggregated_commitment_at_z,
                      aggregated_commitment_at_z_omega,
                      aggregated_opening_at_z,
                      aggregated_opening_at_z_omega
                  );
          }
          function verify_quotient_evaluation(
              VerificationKey memory vk,
              Proof memory proof,
              PartialVerifierState memory state
          ) internal view returns (bool) {
              uint256[] memory lagrange_poly_numbers = new uint256[](vk.num_inputs);
              for (uint256 i = 0; i < lagrange_poly_numbers.length; i = i.uncheckedInc()) {
                  lagrange_poly_numbers[i] = i;
              }
              require(vk.num_inputs > 0);
              PairingsBn254.Fr memory inputs_term = PairingsBn254.new_fr(0);
              for (uint256 i = 0; i < vk.num_inputs; i = i.uncheckedInc()) {
                  state.t = evaluate_lagrange_poly_out_of_domain(i, vk.domain_size, vk.omega, state.z);
                  state.t.mul_assign(PairingsBn254.new_fr(proof.input_values[i]));
                  inputs_term.add_assign(state.t);
              }
              inputs_term.mul_assign(proof.gate_selectors_openings_at_z[0]);
              PairingsBn254.Fr memory result = proof.linearization_poly_opening_at_z.copy();
              result.add_assign(inputs_term);
              // compute powers of alpha
              compute_powers_of_alpha(state);
              PairingsBn254.Fr memory factor = state.alpha_values[4].copy();
              factor.mul_assign(proof.copy_permutation_grand_product_opening_at_z_omega);
              // - alpha_0 * (a + perm(z) * beta + gamma)*()*(d + gamma) * z(z*omega)
              require(proof.copy_permutation_polys_openings_at_z.length == STATE_WIDTH - 1);
              PairingsBn254.Fr memory t; // TMP;
              for (uint256 i = 0; i < proof.copy_permutation_polys_openings_at_z.length; i = i.uncheckedInc()) {
                  t = proof.copy_permutation_polys_openings_at_z[i].copy();
                  t.mul_assign(state.beta);
                  t.add_assign(proof.state_polys_openings_at_z[i]);
                  t.add_assign(state.gamma);
                  factor.mul_assign(t);
              }
              t = proof.state_polys_openings_at_z[3].copy();
              t.add_assign(state.gamma);
              factor.mul_assign(t);
              result.sub_assign(factor);
              // - L_0(z) * alpha_1
              PairingsBn254.Fr memory l_0_at_z = evaluate_l0_at_point(vk.domain_size, state.z);
              l_0_at_z.mul_assign(state.alpha_values[4 + 1]);
              result.sub_assign(l_0_at_z);
              PairingsBn254.Fr memory lookup_quotient_contrib = lookup_quotient_contribution(vk, proof, state);
              result.add_assign(lookup_quotient_contrib);
              PairingsBn254.Fr memory lhs = proof.quotient_poly_opening_at_z.copy();
              lhs.mul_assign(evaluate_vanishing(vk.domain_size, state.z));
              return lhs.value == result.value;
          }
          function lookup_quotient_contribution(
              VerificationKey memory vk,
              Proof memory proof,
              PartialVerifierState memory state
          ) internal view returns (PairingsBn254.Fr memory result) {
              PairingsBn254.Fr memory t;
              PairingsBn254.Fr memory one = PairingsBn254.new_fr(1);
              state.beta_plus_one = state.beta_lookup.copy();
              state.beta_plus_one.add_assign(one);
              state.beta_gamma = state.beta_plus_one.copy();
              state.beta_gamma.mul_assign(state.gamma_lookup);
              // (s'*beta + gamma)*(zw')*alpha
              t = proof.lookup_s_poly_opening_at_z_omega.copy();
              t.mul_assign(state.beta_lookup);
              t.add_assign(state.beta_gamma);
              t.mul_assign(proof.lookup_grand_product_opening_at_z_omega);
              t.mul_assign(state.alpha_values[6]);
              // (z - omega^{n-1}) for this part
              PairingsBn254.Fr memory last_omega = vk.omega.pow(vk.domain_size - 1);
              state.z_minus_last_omega = state.z.copy();
              state.z_minus_last_omega.sub_assign(last_omega);
              t.mul_assign(state.z_minus_last_omega);
              result.add_assign(t);
              // - alpha_1 * L_{0}(z)
              state.l_0_at_z = evaluate_lagrange_poly_out_of_domain(0, vk.domain_size, vk.omega, state.z);
              t = state.l_0_at_z.copy();
              t.mul_assign(state.alpha_values[6 + 1]);
              result.sub_assign(t);
              // - alpha_2 * beta_gamma_powered L_{n-1}(z)
              PairingsBn254.Fr memory beta_gamma_powered = state.beta_gamma.pow(vk.domain_size - 1);
              state.l_n_minus_one_at_z = evaluate_lagrange_poly_out_of_domain(
                  vk.domain_size - 1,
                  vk.domain_size,
                  vk.omega,
                  state.z
              );
              t = state.l_n_minus_one_at_z.copy();
              t.mul_assign(beta_gamma_powered);
              t.mul_assign(state.alpha_values[6 + 2]);
              result.sub_assign(t);
          }
          function aggregated_linearization_commitment(
              VerificationKey memory vk,
              Proof memory proof,
              PartialVerifierState memory state
          ) internal view returns (PairingsBn254.G1Point memory result) {
              // qMain*(Q_a * A + Q_b * B + Q_c * C + Q_d * D + Q_m * A*B + Q_const + Q_dNext * D_next)
              result = PairingsBn254.new_g1(0, 0);
              // Q_a * A
              PairingsBn254.G1Point memory scaled = vk.gate_setup_commitments[0].point_mul(
                  proof.state_polys_openings_at_z[0]
              );
              result.point_add_assign(scaled);
              // Q_b * B
              scaled = vk.gate_setup_commitments[1].point_mul(proof.state_polys_openings_at_z[1]);
              result.point_add_assign(scaled);
              // Q_c * C
              scaled = vk.gate_setup_commitments[2].point_mul(proof.state_polys_openings_at_z[2]);
              result.point_add_assign(scaled);
              // Q_d * D
              scaled = vk.gate_setup_commitments[3].point_mul(proof.state_polys_openings_at_z[3]);
              result.point_add_assign(scaled);
              // Q_m* A*B or Q_ab*A*B
              PairingsBn254.Fr memory t = proof.state_polys_openings_at_z[0].copy();
              t.mul_assign(proof.state_polys_openings_at_z[1]);
              scaled = vk.gate_setup_commitments[4].point_mul(t);
              result.point_add_assign(scaled);
              // Q_AC* A*C
              t = proof.state_polys_openings_at_z[0].copy();
              t.mul_assign(proof.state_polys_openings_at_z[2]);
              scaled = vk.gate_setup_commitments[5].point_mul(t);
              result.point_add_assign(scaled);
              // Q_const
              result.point_add_assign(vk.gate_setup_commitments[6]);
              // Q_dNext * D_next
              scaled = vk.gate_setup_commitments[7].point_mul(proof.state_polys_openings_at_z_omega[0]);
              result.point_add_assign(scaled);
              result.point_mul_assign(proof.gate_selectors_openings_at_z[0]);
              PairingsBn254.G1Point
                  memory rescue_custom_gate_linearization_contrib = rescue_custom_gate_linearization_contribution(
                      vk,
                      proof,
                      state
                  );
              result.point_add_assign(rescue_custom_gate_linearization_contrib);
              require(vk.non_residues.length == STATE_WIDTH - 1);
              PairingsBn254.Fr memory one = PairingsBn254.new_fr(1);
              PairingsBn254.Fr memory factor = state.alpha_values[4].copy();
              for (uint256 i = 0; i < proof.state_polys_openings_at_z.length; ) {
                  t = state.z.copy();
                  if (i == 0) {
                      t.mul_assign(one);
                  } else {
                      t.mul_assign(vk.non_residues[i - 1]);
                  }
                  t.mul_assign(state.beta);
                  t.add_assign(state.gamma);
                  t.add_assign(proof.state_polys_openings_at_z[i]);
                  factor.mul_assign(t);
                  unchecked {
                      ++i;
                  }
              }
              scaled = proof.copy_permutation_grand_product_commitment.point_mul(factor);
              result.point_add_assign(scaled);
              // - (a(z) + beta*perm_a + gamma)*()*()*z(z*omega) * beta * perm_d(X)
              factor = state.alpha_values[4].copy();
              factor.mul_assign(state.beta);
              factor.mul_assign(proof.copy_permutation_grand_product_opening_at_z_omega);
              for (uint256 i = 0; i < STATE_WIDTH - 1; i = i.uncheckedInc()) {
                  t = proof.copy_permutation_polys_openings_at_z[i].copy();
                  t.mul_assign(state.beta);
                  t.add_assign(state.gamma);
                  t.add_assign(proof.state_polys_openings_at_z[i]);
                  factor.mul_assign(t);
              }
              scaled = vk.permutation_commitments[3].point_mul(factor);
              result.point_sub_assign(scaled);
              // + L_0(z) * Z(x)
              state.l_0_at_z = evaluate_lagrange_poly_out_of_domain(0, vk.domain_size, vk.omega, state.z);
              require(state.l_0_at_z.value != 0);
              factor = state.l_0_at_z.copy();
              factor.mul_assign(state.alpha_values[4 + 1]);
              scaled = proof.copy_permutation_grand_product_commitment.point_mul(factor);
              result.point_add_assign(scaled);
              PairingsBn254.G1Point memory lookup_linearization_contrib = lookup_linearization_contribution(proof, state);
              result.point_add_assign(lookup_linearization_contrib);
          }
          function rescue_custom_gate_linearization_contribution(
              VerificationKey memory vk,
              Proof memory proof,
              PartialVerifierState memory state
          ) public view returns (PairingsBn254.G1Point memory result) {
              PairingsBn254.Fr memory t;
              PairingsBn254.Fr memory intermediate_result;
              // a^2 - b = 0
              t = proof.state_polys_openings_at_z[0].copy();
              t.mul_assign(t);
              t.sub_assign(proof.state_polys_openings_at_z[1]);
              // t.mul_assign(challenge1);
              t.mul_assign(state.alpha_values[1]);
              intermediate_result.add_assign(t);
              // b^2 - c = 0
              t = proof.state_polys_openings_at_z[1].copy();
              t.mul_assign(t);
              t.sub_assign(proof.state_polys_openings_at_z[2]);
              t.mul_assign(state.alpha_values[1 + 1]);
              intermediate_result.add_assign(t);
              // c*a - d = 0;
              t = proof.state_polys_openings_at_z[2].copy();
              t.mul_assign(proof.state_polys_openings_at_z[0]);
              t.sub_assign(proof.state_polys_openings_at_z[3]);
              t.mul_assign(state.alpha_values[1 + 2]);
              intermediate_result.add_assign(t);
              result = vk.gate_selectors_commitments[1].point_mul(intermediate_result);
          }
          function lookup_linearization_contribution(Proof memory proof, PartialVerifierState memory state)
              internal
              view
              returns (PairingsBn254.G1Point memory result)
          {
              PairingsBn254.Fr memory zero = PairingsBn254.new_fr(0);
              PairingsBn254.Fr memory t;
              PairingsBn254.Fr memory factor;
              // s(x) from the Z(x*omega)*(\\gamma*(1 + \\beta) + s(x) + \\beta * s(x*omega)))
              factor = proof.lookup_grand_product_opening_at_z_omega.copy();
              factor.mul_assign(state.alpha_values[6]);
              factor.mul_assign(state.z_minus_last_omega);
              PairingsBn254.G1Point memory scaled = proof.lookup_s_poly_commitment.point_mul(factor);
              result.point_add_assign(scaled);
              // Z(x) from - alpha_0 * Z(x) * (\\beta + 1) * (\\gamma + f(x)) * (\\gamma(1 + \\beta) + t(x) + \\beta * t(x*omega))
              // + alpha_1 * Z(x) * L_{0}(z) + alpha_2 * Z(x) * L_{n-1}(z)
              // accumulate coefficient
              factor = proof.lookup_t_poly_opening_at_z_omega.copy();
              factor.mul_assign(state.beta_lookup);
              factor.add_assign(proof.lookup_t_poly_opening_at_z);
              factor.add_assign(state.beta_gamma);
              // (\\gamma + f(x))
              PairingsBn254.Fr memory f_reconstructed;
              PairingsBn254.Fr memory current = PairingsBn254.new_fr(1);
              PairingsBn254.Fr memory tmp0;
              for (uint256 i = 0; i < STATE_WIDTH - 1; i = i.uncheckedInc()) {
                  tmp0 = proof.state_polys_openings_at_z[i].copy();
                  tmp0.mul_assign(current);
                  f_reconstructed.add_assign(tmp0);
                  current.mul_assign(state.eta);
              }
              // add type of table
              t = proof.lookup_table_type_poly_opening_at_z.copy();
              t.mul_assign(current);
              f_reconstructed.add_assign(t);
              f_reconstructed.mul_assign(proof.lookup_selector_poly_opening_at_z);
              f_reconstructed.add_assign(state.gamma_lookup);
              // end of (\\gamma + f(x)) part
              factor.mul_assign(f_reconstructed);
              factor.mul_assign(state.beta_plus_one);
              t = zero.copy();
              t.sub_assign(factor);
              factor = t;
              factor.mul_assign(state.alpha_values[6]);
              // Multiply by (z - omega^{n-1})
              factor.mul_assign(state.z_minus_last_omega);
              // L_{0}(z) in front of Z(x)
              t = state.l_0_at_z.copy();
              t.mul_assign(state.alpha_values[6 + 1]);
              factor.add_assign(t);
              // L_{n-1}(z) in front of Z(x)
              t = state.l_n_minus_one_at_z.copy();
              t.mul_assign(state.alpha_values[6 + 2]);
              factor.add_assign(t);
              scaled = proof.lookup_grand_product_commitment.point_mul(factor);
              result.point_add_assign(scaled);
          }
          struct Queries {
              PairingsBn254.G1Point[13] commitments_at_z;
              PairingsBn254.Fr[13] values_at_z;
              PairingsBn254.G1Point[6] commitments_at_z_omega;
              PairingsBn254.Fr[6] values_at_z_omega;
          }
          function prepare_queries(
              VerificationKey memory vk,
              Proof memory proof,
              PartialVerifierState memory state
          ) public view returns (Queries memory queries) {
              // we set first two items in calee side so start idx from 2
              uint256 idx = 2;
              for (uint256 i = 0; i < STATE_WIDTH; i = i.uncheckedInc()) {
                  queries.commitments_at_z[idx] = proof.state_polys_commitments[i];
                  queries.values_at_z[idx] = proof.state_polys_openings_at_z[i];
                  idx = idx.uncheckedInc();
              }
              require(proof.gate_selectors_openings_at_z.length == 1);
              queries.commitments_at_z[idx] = vk.gate_selectors_commitments[0];
              queries.values_at_z[idx] = proof.gate_selectors_openings_at_z[0];
              idx = idx.uncheckedInc();
              for (uint256 i = 0; i < STATE_WIDTH - 1; i = i.uncheckedInc()) {
                  queries.commitments_at_z[idx] = vk.permutation_commitments[i];
                  queries.values_at_z[idx] = proof.copy_permutation_polys_openings_at_z[i];
                  idx = idx.uncheckedInc();
              }
              queries.commitments_at_z_omega[0] = proof.copy_permutation_grand_product_commitment;
              queries.commitments_at_z_omega[1] = proof.state_polys_commitments[STATE_WIDTH - 1];
              queries.values_at_z_omega[0] = proof.copy_permutation_grand_product_opening_at_z_omega;
              queries.values_at_z_omega[1] = proof.state_polys_openings_at_z_omega[0];
              PairingsBn254.G1Point memory lookup_t_poly_commitment_aggregated = vk.lookup_tables_commitments[0];
              PairingsBn254.Fr memory current_eta = state.eta.copy();
              for (uint256 i = 1; i < vk.lookup_tables_commitments.length; i = i.uncheckedInc()) {
                  state.tp = vk.lookup_tables_commitments[i].point_mul(current_eta);
                  lookup_t_poly_commitment_aggregated.point_add_assign(state.tp);
                  current_eta.mul_assign(state.eta);
              }
              queries.commitments_at_z[idx] = lookup_t_poly_commitment_aggregated;
              queries.values_at_z[idx] = proof.lookup_t_poly_opening_at_z;
              idx = idx.uncheckedInc();
              queries.commitments_at_z[idx] = vk.lookup_selector_commitment;
              queries.values_at_z[idx] = proof.lookup_selector_poly_opening_at_z;
              idx = idx.uncheckedInc();
              queries.commitments_at_z[idx] = vk.lookup_table_type_commitment;
              queries.values_at_z[idx] = proof.lookup_table_type_poly_opening_at_z;
              queries.commitments_at_z_omega[2] = proof.lookup_s_poly_commitment;
              queries.values_at_z_omega[2] = proof.lookup_s_poly_opening_at_z_omega;
              queries.commitments_at_z_omega[3] = proof.lookup_grand_product_commitment;
              queries.values_at_z_omega[3] = proof.lookup_grand_product_opening_at_z_omega;
              queries.commitments_at_z_omega[4] = lookup_t_poly_commitment_aggregated;
              queries.values_at_z_omega[4] = proof.lookup_t_poly_opening_at_z_omega;
          }
          function final_pairing(
              // VerificationKey memory vk,
              PairingsBn254.G2Point[NUM_G2_ELS] memory g2_elements,
              Proof memory proof,
              PartialVerifierState memory state,
              PairingsBn254.G1Point memory aggregated_commitment_at_z,
              PairingsBn254.G1Point memory aggregated_commitment_at_z_omega,
              PairingsBn254.Fr memory aggregated_opening_at_z,
              PairingsBn254.Fr memory aggregated_opening_at_z_omega
          ) internal view returns (bool) {
              // q(x) = f(x) - f(z) / (x - z)
              // q(x) * (x-z)  = f(x) - f(z)
              // f(x)
              PairingsBn254.G1Point memory pair_with_generator = aggregated_commitment_at_z.copy_g1();
              aggregated_commitment_at_z_omega.point_mul_assign(state.u);
              pair_with_generator.point_add_assign(aggregated_commitment_at_z_omega);
              // - f(z)*g
              PairingsBn254.Fr memory aggregated_value = aggregated_opening_at_z_omega.copy();
              aggregated_value.mul_assign(state.u);
              aggregated_value.add_assign(aggregated_opening_at_z);
              PairingsBn254.G1Point memory tp = PairingsBn254.P1().point_mul(aggregated_value);
              pair_with_generator.point_sub_assign(tp);
              // +z * q(x)
              tp = proof.opening_proof_at_z.point_mul(state.z);
              PairingsBn254.Fr memory t = state.z_omega.copy();
              t.mul_assign(state.u);
              PairingsBn254.G1Point memory t1 = proof.opening_proof_at_z_omega.point_mul(t);
              tp.point_add_assign(t1);
              pair_with_generator.point_add_assign(tp);
              // rhs
              PairingsBn254.G1Point memory pair_with_x = proof.opening_proof_at_z_omega.point_mul(state.u);
              pair_with_x.point_add_assign(proof.opening_proof_at_z);
              pair_with_x.negate();
              // Pairing precompile expects points to be in a `i*x[1] + x[0]` form instead of `x[0] + i*x[1]`
              // so we handle it in code generation step
              PairingsBn254.G2Point memory first_g2 = g2_elements[0];
              PairingsBn254.G2Point memory second_g2 = g2_elements[1];
              return PairingsBn254.pairingProd2(pair_with_generator, first_g2, pair_with_x, second_g2);
          }
      }
      pragma solidity ^0.8.13;
      // SPDX-License-Identifier: MIT
      import "./Verifier.sol";
      import "../common/interfaces/IAllowList.sol";
      import "./libraries/PriorityQueue.sol";
      /// @notice Indicates whether an upgrade is initiated and if yes what type
      /// @param None Upgrade is NOT initiated
      /// @param Transparent Fully transparent upgrade is initiated, upgrade data is publicly known
      /// @param Shadow Shadow upgrade is initiated, upgrade data is hidden
      enum UpgradeState {
          None,
          Transparent,
          Shadow
      }
      /// @dev Logically separated part of the storage structure, which is responsible for everything related to proxy upgrades and diamond cuts
      /// @param proposedUpgradeHash The hash of the current upgrade proposal, zero if there is no active proposal
      /// @param state Indicates whether an upgrade is initiated and if yes what type
      /// @param securityCouncil Address which has the permission to approve instant upgrades (expected to be a Gnosis multisig)
      /// @param approvedBySecurityCouncil Indicates whether the security council has approved the upgrade
      /// @param proposedUpgradeTimestamp The timestamp when the upgrade was proposed, zero if there are no active proposals
      /// @param currentProposalId The serial number of proposed upgrades, increments when proposing a new one
      struct UpgradeStorage {
          bytes32 proposedUpgradeHash;
          UpgradeState state;
          address securityCouncil;
          bool approvedBySecurityCouncil;
          uint40 proposedUpgradeTimestamp;
          uint40 currentProposalId;
      }
      /// @dev The log passed from L2
      /// @param l2ShardId The shard identifier, 0 - rollup, 1 - porter. All other values are not used but are reserved for the future
      /// @param isService A boolean flag that is part of the log along with `key`, `value`, and `sender` address.
      /// This field is required formally but does not have any special meaning.
      /// @param txNumberInBlock The L2 transaction number in a block, in which the log was sent
      /// @param sender The L2 address which sent the log
      /// @param key The 32 bytes of information that was sent in the log
      /// @param value The 32 bytes of information that was sent in the log
      // Both `key` and `value` are arbitrary 32-bytes selected by the log sender
      struct L2Log {
          uint8 l2ShardId;
          bool isService;
          uint16 txNumberInBlock;
          address sender;
          bytes32 key;
          bytes32 value;
      }
      /// @dev An arbitrary length message passed from L2
      /// @notice Under the hood it is `L2Log` sent from the special system L2 contract
      /// @param txNumberInBlock The L2 transaction number in a block, in which the message was sent
      /// @param sender The address of the L2 account from which the message was passed
      /// @param data An arbitrary length message
      struct L2Message {
          uint16 txNumberInBlock;
          address sender;
          bytes data;
      }
      /// @notice Part of the configuration parameters of ZKP circuits
      struct VerifierParams {
          bytes32 recursionNodeLevelVkHash;
          bytes32 recursionLeafLevelVkHash;
          bytes32 recursionCircuitsSetVksHash;
      }
      /// @dev storing all storage variables for zkSync facets
      /// NOTE: It is used in a proxy, so it is possible to add new variables to the end
      /// NOTE: but NOT to modify already existing variables or change their order
      /// NOTE: DiamondCutStorage is unused, but it must remain a member of AppStorage to not have storage collision
      /// NOTE: instead UpgradeStorage is used that is appended to the end of the AppStorage struct
      struct AppStorage {
          /// @dev Storage of variables needed for deprecated diamond cut facet
          uint256[7] __DEPRECATED_diamondCutStorage;
          /// @notice Address which will exercise governance over the network i.e. change validator set, conduct upgrades
          address governor;
          /// @notice Address that the governor proposed as one that will replace it
          address pendingGovernor;
          /// @notice List of permitted validators
          mapping(address => bool) validators;
          /// @dev Verifier contract. Used to verify aggregated proof for blocks
          Verifier verifier;
          /// @notice Total number of executed blocks i.e. blocks[totalBlocksExecuted] points at the latest executed block (block 0 is genesis)
          uint256 totalBlocksExecuted;
          /// @notice Total number of proved blocks i.e. blocks[totalBlocksProved] points at the latest proved block
          uint256 totalBlocksVerified;
          /// @notice Total number of committed blocks i.e. blocks[totalBlocksCommitted] points at the latest committed block
          uint256 totalBlocksCommitted;
          /// @dev Stored hashed StoredBlock for block number
          mapping(uint256 => bytes32) storedBlockHashes;
          /// @dev Stored root hashes of L2 -> L1 logs
          mapping(uint256 => bytes32) l2LogsRootHashes;
          /// @dev Container that stores transactions requested from L1
          PriorityQueue.Queue priorityQueue;
          /// @dev The smart contract that manages the list with permission to call contract functions
          IAllowList allowList;
          /// @notice Part of the configuration parameters of ZKP circuits. Used as an input for the verifier smart contract
          VerifierParams verifierParams;
          /// @notice Bytecode hash of bootloader program.
          /// @dev Used as an input to zkp-circuit.
          bytes32 l2BootloaderBytecodeHash;
          /// @notice Bytecode hash of default account (bytecode for EOA).
          /// @dev Used as an input to zkp-circuit.
          bytes32 l2DefaultAccountBytecodeHash;
          /// @dev Indicates that the porter may be touched on L2 transactions.
          /// @dev Used as an input to zkp-circuit.
          bool zkPorterIsAvailable;
          /// @dev The maximum number of the L2 gas that a user can request for L1 -> L2 transactions
          /// @dev This is the maximum number of L2 gas that is available for the "body" of the transaction, i.e.
          /// without overhead for proving the block.
          uint256 priorityTxMaxGasLimit;
          /// @dev Storage of variables needed for upgrade facet
          UpgradeStorage upgrades;
          /// @dev A mapping L2 block number => message number => flag.
          /// @dev The L2 -> L1 log is sent for every withdrawal, so this mapping is serving as
          /// a flag to indicate that the message was already processed.
          /// @dev Used to indicate that eth withdrawal was already processed
          mapping(uint256 => mapping(uint256 => bool)) isEthWithdrawalFinalized;
          /// @dev The most recent withdrawal time and amount reset
          uint256 __DEPRECATED_lastWithdrawalLimitReset;
          /// @dev The accumulated withdrawn amount during the withdrawal limit window
          uint256 __DEPRECATED_withdrawnAmountInWindow;
          /// @dev A mapping user address => the total deposited amount by the user
          mapping(address => uint256) totalDepositedAmountPerUser;
      }
      pragma solidity ^0.8.13;
      // SPDX-License-Identifier: MIT
      import "./Plonk4VerifierWithAccessToDNext.sol";
      import "../common/libraries/UncheckedMath.sol";
      contract Verifier is Plonk4VerifierWithAccessToDNext {
          using UncheckedMath for uint256;
          function get_verification_key() public pure returns (VerificationKey memory vk) {
              vk.num_inputs = 1;
              vk.domain_size = 67108864;
              vk.omega = PairingsBn254.new_fr(0x1dba8b5bdd64ef6ce29a9039aca3c0e524395c43b9227b96c75090cc6cc7ec97);
              // coefficients
              vk.gate_setup_commitments[0] = PairingsBn254.new_g1(
                  0x08fa9d6f0dd6ac1cbeb94ae20fe7a23df05cb1095df66fb561190e615a4037ef,
                  0x196dcc8692fe322d21375920559944c12ba7b1ba8b732344cf4ba2e3aa0fc8b4
              );
              vk.gate_setup_commitments[1] = PairingsBn254.new_g1(
                  0x0074aaf5d97bd57551311a8b3e4aa7840bc55896502020b2f43ad6a98d81a443,
                  0x2d275a3ad153dc9d89ebb9c9b6a0afd2dde82470554e9738d905c328fbb4c8bc
              );
              vk.gate_setup_commitments[2] = PairingsBn254.new_g1(
                  0x287f1975a9aeaef5d2bb0767b5ef538f76e82f7da01c0cb6db8c6f920818ec4f,
                  0x2fff6f53594129f794a7731d963d27e72f385c5c6d8e08829e6f66a9d29a12ea
              );
              vk.gate_setup_commitments[3] = PairingsBn254.new_g1(
                  0x038809fa3d4b7320d43e023454194f0a7878baa7e73a295d2d105260f1c34cbc,
                  0x25418b1105cf45b2a3da6c349bab1d9caaf145eaf24d1e8fb92c11654c000781
              );
              vk.gate_setup_commitments[4] = PairingsBn254.new_g1(
                  0x0561cafd527ac3f0bc550db77d87cd1c63938f7ec051e62ebf84a5bbe07f9840,
                  0x28f87201b4cbe19f1517a1c29ca6d6cb074502ccfed4c31c8931c6992c3eea43
              );
              vk.gate_setup_commitments[5] = PairingsBn254.new_g1(
                  0x27e0af572bac6e36d31c33808cb44c0ef8ceee5e2850e916fb01f3747db72491,
                  0x1da20087ba61c59366b21e31e4ac6889d357cf11bf16b94d875f94f41525c427
              );
              vk.gate_setup_commitments[6] = PairingsBn254.new_g1(
                  0x2c2bcafea8f93d07f96874f470985a8d272c09c8ed49373f36497ee80bd8da17,
                  0x299276cf6dca1a7e3780f6276c5d067403f6e024e83e0cc1ab4c5f7252b7f653
              );
              vk.gate_setup_commitments[7] = PairingsBn254.new_g1(
                  0x0ba9d4a53e050da25b8410045b634f1ca065ff74acd35bab1a72bf1f20047ef3,
                  0x1f1eefc8b0507a08f852f554bd7abcbd506e52de390ca127477a678d212abfe5
              );
              // gate selectors
              vk.gate_selectors_commitments[0] = PairingsBn254.new_g1(
                  0x1c6b68d9920620012d85a4850dad9bd6d03ae8bbc7a08b827199e85dba1ef2b1,
                  0x0f6380560d1b585628ed259289cec19d3a7c70c60e66bbfebfcb70c8c312d91e
              );
              vk.gate_selectors_commitments[1] = PairingsBn254.new_g1(
                  0x0dfead780e5067181aae631ff734a33fca302773472997daca58ba49dbd20dcc,
                  0x00f13fa6e356f525d2fd1c533acf2858c0d2b9f0a9b3180f94e1543929c75073
              );
              // permutation
              vk.permutation_commitments[0] = PairingsBn254.new_g1(
                  0x1df0747c787934650d99c5696f9273088ad07ec3e0825c9d39685a9b9978ebed,
                  0x2ace2a277becbc69af4e89518eb50960a733d9d71354845ea43d2e65c8e0e4cb
              );
              vk.permutation_commitments[1] = PairingsBn254.new_g1(
                  0x06598c8236a5f5045cd7444dc87f3e1f66f99bf01251e13be4dc0ab1f7f1af4b,
                  0x14ca234fe9b3bb1e5517fc60d6b90f8ad44b0899a2d4f71a64c9640b3142ce8b
              );
              vk.permutation_commitments[2] = PairingsBn254.new_g1(
                  0x01889e2c684caefde60471748f4259196ecf4209a735ccdf7b1816f05bafa50a,
                  0x092d287a080bfe2fd40ad392ff290e462cd0e347b8fd9d05b90af234ce77a11b
              );
              vk.permutation_commitments[3] = PairingsBn254.new_g1(
                  0x0dd98eeb5bc12c221da969398b67750a8774dbdd37a78da52367f9fc0e566d5c,
                  0x06750ceb40c9fb87fc424df9599340938b7552b759914a90cb0e41d3915c945b
              );
              // lookup table commitments
              vk.lookup_selector_commitment = PairingsBn254.new_g1(
                  0x2f491c662ae53ceb358f57a868dc00b89befa853bd9a449127ea2d46820995bd,
                  0x231fe6538634ff8b6fa21ca248fb15e7f43d82eb0bfa705490d24ddb3e3cad77
              );
              vk.lookup_tables_commitments[0] = PairingsBn254.new_g1(
                  0x0ebe0de4a2f39df3b903da484c1641ffdffb77ff87ce4f9508c548659eb22d3c,
                  0x12a3209440242d5662729558f1017ed9dcc08fe49a99554dd45f5f15da5e4e0b
              );
              vk.lookup_tables_commitments[1] = PairingsBn254.new_g1(
                  0x1b7d54f8065ca63bed0bfbb9280a1011b886d07e0c0a26a66ecc96af68c53bf9,
                  0x2c51121fff5b8f58c302f03c74e0cb176ae5a1d1730dec4696eb9cce3fe284ca
              );
              vk.lookup_tables_commitments[2] = PairingsBn254.new_g1(
                  0x0138733c5faa9db6d4b8df9748081e38405999e511fb22d40f77cf3aef293c44,
                  0x269bee1c1ac28053238f7fe789f1ea2e481742d6d16ae78ed81e87c254af0765
              );
              vk.lookup_tables_commitments[3] = PairingsBn254.new_g1(
                  0x1b1be7279d59445065a95f01f16686adfa798ec4f1e6845ffcec9b837e88372e,
                  0x057c90cb96d8259238ed86b05f629efd55f472a721efeeb56926e979433e6c0e
              );
              vk.lookup_table_type_commitment = PairingsBn254.new_g1(
                  0x12cd873a6f18a4a590a846d9ebf61565197edf457efd26bc408eb61b72f37b59,
                  0x19890cbdac892682e7a5910ca6c238c082130e1c71f33d0c9c901153377770d1
              );
              // non residues
              vk.non_residues[0] = PairingsBn254.new_fr(0x0000000000000000000000000000000000000000000000000000000000000005);
              vk.non_residues[1] = PairingsBn254.new_fr(0x0000000000000000000000000000000000000000000000000000000000000007);
              vk.non_residues[2] = PairingsBn254.new_fr(0x000000000000000000000000000000000000000000000000000000000000000a);
              // g2 elements
              vk.g2_elements[0] = PairingsBn254.new_g2(
                  [
                      0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2,
                      0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed
                  ],
                  [
                      0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b,
                      0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa
                  ]
              );
              vk.g2_elements[1] = PairingsBn254.new_g2(
                  [
                      0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1,
                      0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0
                  ],
                  [
                      0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4,
                      0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55
                  ]
              );
          }
          function deserialize_proof(uint256[] calldata public_inputs, uint256[] calldata serialized_proof)
              internal
              pure
              returns (Proof memory proof)
          {
              require(serialized_proof.length == 44);
              proof.input_values = new uint256[](public_inputs.length);
              for (uint256 i = 0; i < public_inputs.length; i = i.uncheckedInc()) {
                  proof.input_values[i] = public_inputs[i];
              }
              uint256 j;
              for (uint256 i = 0; i < STATE_WIDTH; i = i.uncheckedInc()) {
                  proof.state_polys_commitments[i] = PairingsBn254.new_g1_checked(
                      serialized_proof[j],
                      serialized_proof[j.uncheckedInc()]
                  );
                  j = j.uncheckedAdd(2);
              }
              proof.copy_permutation_grand_product_commitment = PairingsBn254.new_g1_checked(
                  serialized_proof[j],
                  serialized_proof[j.uncheckedInc()]
              );
              j = j.uncheckedAdd(2);
              proof.lookup_s_poly_commitment = PairingsBn254.new_g1_checked(
                  serialized_proof[j],
                  serialized_proof[j.uncheckedInc()]
              );
              j = j.uncheckedAdd(2);
              proof.lookup_grand_product_commitment = PairingsBn254.new_g1_checked(
                  serialized_proof[j],
                  serialized_proof[j.uncheckedInc()]
              );
              j = j.uncheckedAdd(2);
              for (uint256 i = 0; i < proof.quotient_poly_parts_commitments.length; i = i.uncheckedInc()) {
                  proof.quotient_poly_parts_commitments[i] = PairingsBn254.new_g1_checked(
                      serialized_proof[j],
                      serialized_proof[j.uncheckedInc()]
                  );
                  j = j.uncheckedAdd(2);
              }
              for (uint256 i = 0; i < proof.state_polys_openings_at_z.length; i = i.uncheckedInc()) {
                  proof.state_polys_openings_at_z[i] = PairingsBn254.new_fr(serialized_proof[j]);
                  j = j.uncheckedInc();
              }
              for (uint256 i = 0; i < proof.state_polys_openings_at_z_omega.length; i = i.uncheckedInc()) {
                  proof.state_polys_openings_at_z_omega[i] = PairingsBn254.new_fr(serialized_proof[j]);
                  j = j.uncheckedInc();
              }
              for (uint256 i = 0; i < proof.gate_selectors_openings_at_z.length; i = i.uncheckedInc()) {
                  proof.gate_selectors_openings_at_z[i] = PairingsBn254.new_fr(serialized_proof[j]);
                  j = j.uncheckedInc();
              }
              for (uint256 i = 0; i < proof.copy_permutation_polys_openings_at_z.length; i = i.uncheckedInc()) {
                  proof.copy_permutation_polys_openings_at_z[i] = PairingsBn254.new_fr(serialized_proof[j]);
                  j = j.uncheckedInc();
              }
              proof.copy_permutation_grand_product_opening_at_z_omega = PairingsBn254.new_fr(serialized_proof[j]);
              j = j.uncheckedInc();
              proof.lookup_s_poly_opening_at_z_omega = PairingsBn254.new_fr(serialized_proof[j]);
              j = j.uncheckedInc();
              proof.lookup_grand_product_opening_at_z_omega = PairingsBn254.new_fr(serialized_proof[j]);
              j = j.uncheckedInc();
              proof.lookup_t_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]);
              j = j.uncheckedInc();
              proof.lookup_t_poly_opening_at_z_omega = PairingsBn254.new_fr(serialized_proof[j]);
              j = j.uncheckedInc();
              proof.lookup_selector_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]);
              j = j.uncheckedInc();
              proof.lookup_table_type_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]);
              j = j.uncheckedInc();
              proof.quotient_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]);
              j = j.uncheckedInc();
              proof.linearization_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]);
              j = j.uncheckedInc();
              proof.opening_proof_at_z = PairingsBn254.new_g1_checked(
                  serialized_proof[j],
                  serialized_proof[j.uncheckedInc()]
              );
              j = j.uncheckedAdd(2);
              proof.opening_proof_at_z_omega = PairingsBn254.new_g1_checked(
                  serialized_proof[j],
                  serialized_proof[j.uncheckedInc()]
              );
          }
          function verify_serialized_proof(uint256[] calldata public_inputs, uint256[] calldata serialized_proof)
              public
              view
              returns (bool)
          {
              VerificationKey memory vk = get_verification_key();
              require(vk.num_inputs == public_inputs.length);
              Proof memory proof = deserialize_proof(public_inputs, serialized_proof);
              return verify(proof, vk);
          }
      }