ETH Price: $2,428.20 (+0.52%)

Transaction Decoder

Block:
11531806 at Dec-26-2020 09:39:42 PM +UTC
Transaction Fee:
0.010553396115775326 ETH $25.63
Gas Used:
269,359 Gas / 39.179667714 Gwei

Emitted Events:

157 Proxy.0x9b5478c99b5ca41beec4f6f6084126d6f9e26382d017b4bb67c37c9e8453a313( 0x9b5478c99b5ca41beec4f6f6084126d6f9e26382d017b4bb67c37c9e8453a313, 00000000000000000000000000000000000000000000000000000000000026f0, 0000000000000000000000000000000000000000000000000000000000002704 )

Account State Difference:

  Address   Before After State Difference Code
0x17f6D54a...F8636Db15 0.000336 Eth0.122336 Eth0.122
0x1dB9BCD2...4877DFA3C 0 Eth0.1003792 Eth0.1003792
754.857579278041938359 Eth754.868132674157713685 Eth0.010553396115775326
0x3846fd2d...049C8F4Be 0.242174471870971013 Eth0.503038571870971013 Eth0.2608641
0x46527788...2B30328ec 0.001792796 Eth0.012038435 Eth0.010245639
0x579923C2...d0AB093b4 0.138882307001362577 Eth0.179282607001362577 Eth0.0404003
0x57E307c0...CD8C8e2Ec 0.4512591 Eth0.488521200000000006 Eth0.037262100000000006
0x5Fb1076a...301a065bC 0.527612309565304596 Eth1.169435309565304596 Eth0.641823
0x64d6eA7b...2218e5cEE
0 Eth
Nonce: 0
0.5523644 Eth
Nonce: 0
0.5523644From: 0 To: 0
0x752D5682...1C006b3D0 0 Eth0.033441341 Eth0.033441341
0x84060C7F...1da18A860
0 Eth
Nonce: 0
0.09729919999999999 Eth
Nonce: 0
0.09729919999999999From: 0 To: 0
0x8CABDC90...e5ce6623C
0 Eth
Nonce: 0
0.68 Eth
Nonce: 0
0.68From: 0 To: 0
0x960c7cad...D87830286 0.000302499969361 Eth2.927302499969361 Eth2.927
0xaBEA9132...00777fBEF
(zkSync)
7,724.363565338922954706 Eth7,717.16094219792295471 Eth7.202623140999999996
0xAeCce82c...7253d2DcA 0.000003799138722 Eth0.076503799138722 Eth0.0765
0xC02B8bf9...480524034 6.553488453490339891 Eth6.940997553490339891 Eth0.3875091
0xd8ac8c13...0C97a4022 0.251188165931481383 Eth0.841378165931481383 Eth0.59019
0xda7357bB...1c5b4EaBb
(zkSync: Old L2 Operator)
115.177927177765059519 Eth
Nonce: 15339
115.167373781649284193 Eth
Nonce: 15340
0.010553396115775326
0xf3dEf151...9B5bd97EC 3.481476442641474977 Eth3.749387542641474977 Eth0.2679111
0xF51c2E90...Ae14CafA8 0.089351422 Eth0.157891062 Eth0.06853964
0xf6123EB9...F31Ccc09F 0.007343335016624599 Eth0.308382492016624599 Eth0.301039157
0xf92095B8...1FE455F51
0 Eth
Nonce: 0
0.007854864 Eth
Nonce: 0
0.007854864From: 0 To: 0

Execution Trace

Proxy.6a387fc9( )
  • ZkSync.completeWithdrawals( _n=20 )
    • ETH 0.037262100000000006 0x57e307c0f6ec88f413ffa630171274ccd8c8e2ec.CALL( )
    • ETH 0.641823 0x5fb1076acab34b439e887e358965419301a065bc.CALL( )
    • ETH 0.68 0x8cabdc90f7d767040c2fd2f8fb336a0e5ce6623c.CALL( )
    • ETH 0.5523644 0x64d6ea7bbeca9625d5526d69480fe042218e5cee.CALL( )
    • ETH 0.0404003 0x579923c2204c387895ea634d1381e66d0ab093b4.CALL( )
    • ETH 0.0765 0xaecce82c3f68206f976f8e219d706f37253d2dca.CALL( )
    • ETH 0.59019 0xd8ac8c13f988967c83039277fb7fbc10c97a4022.CALL( )
    • ETH 0.301039157 0xf6123eb9f3c63acc217a998d97d7f3ff31ccc09f.CALL( )
    • ETH 0.010245639 0x46527788c1b47c93d3971b2afdb22532b30328ec.CALL( )
    • ETH 0.122 0x17f6d54a936dcb2d010ed2bd022052ff8636db15.CALL( )
    • ETH 0.1003792 0x1db9bcd204a6f4d8e71d824a79f02af4877dfa3c.CALL( )
    • ETH 0.09729919999999999 0x84060c7faec2152a18a3345acd3e01a1da18a860.CALL( )
    • ETH 0.033441341 0x752d5682aa70a2b9e6e908371ee3e271c006b3d0.CALL( )
    • ETH 0.2608641 0x3846fd2d2651ea5cf5d34ad655319cc049c8f4be.CALL( )
    • ETH 0.2679111 0xf3def1510218fc985d476c4f40822df9b5bd97ec.CALL( )
    • ETH 0.007854864 0xf92095b83ec0c1f3f8566f148f4422a1fe455f51.CALL( )
    • ETH 0.3875091 0xc02b8bf9eed886e7625b8ab37257884480524034.CALL( )
    • ETH 0.06853964 0xf51c2e907650d4e10a998312becce18ae14cafa8.CALL( )
    • ETH 2.927 0x960c7cad9eda930897d429d42b3bc0fd87830286.CALL( )
      File 1 of 2: Proxy
      pragma solidity ^0.5.0;
      import "./Ownable.sol";
      import "./Upgradeable.sol";
      import "./UpgradeableMaster.sol";
      /// @title Proxy Contract
      /// @dev NOTICE: Proxy must implement UpgradeableMaster interface to prevent calling some function of it not by master of proxy
      /// @author Matter Labs
      contract Proxy is Upgradeable, UpgradeableMaster, Ownable {
          /// @notice Storage position of "target" (actual implementation address: keccak256('eip1967.proxy.implementation') - 1)
          bytes32 private constant targetPosition = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
          /// @notice Contract constructor
          /// @dev Calls Ownable contract constructor and initialize target
          /// @param target Initial implementation address
          /// @param targetInitializationParameters Target initialization parameters
          constructor(address target, bytes memory targetInitializationParameters) Ownable(msg.sender) public {
              setTarget(target);
              (bool initializationSuccess, ) = getTarget().delegatecall(
                  abi.encodeWithSignature("initialize(bytes)", targetInitializationParameters)
              );
              require(initializationSuccess, "uin11"); // uin11 - target initialization failed
          }
          /// @notice Intercepts initialization calls
          function initialize(bytes calldata) external pure {
              revert("ini11"); // ini11 - interception of initialization call
          }
          /// @notice Intercepts upgrade calls
          function upgrade(bytes calldata) external pure {
              revert("upg11"); // upg11 - interception of upgrade call
          }
          /// @notice Returns target of contract
          /// @return Actual implementation address
          function getTarget() public view returns (address target) {
              bytes32 position = targetPosition;
              assembly {
                  target := sload(position)
              }
          }
          /// @notice Sets new target of contract
          /// @param _newTarget New actual implementation address
          function setTarget(address _newTarget) internal {
              bytes32 position = targetPosition;
              assembly {
                  sstore(position, _newTarget)
              }
          }
          /// @notice Upgrades target
          /// @param newTarget New target
          /// @param newTargetUpgradeParameters New target upgrade parameters
          function upgradeTarget(address newTarget, bytes calldata newTargetUpgradeParameters) external {
              requireMaster(msg.sender);
              setTarget(newTarget);
              (bool upgradeSuccess, ) = getTarget().delegatecall(
                  abi.encodeWithSignature("upgrade(bytes)", newTargetUpgradeParameters)
              );
              require(upgradeSuccess, "ufu11"); // ufu11 - target upgrade failed
          }
          /// @notice Performs a delegatecall to the contract implementation
          /// @dev Fallback function allowing to perform a delegatecall to the given implementation
          /// This function will return whatever the implementation call returns
          function() external payable {
              address _target = getTarget();
              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, 0x0, calldatasize)
                  // Delegatecall method of the implementation contract, returns 0 on error
                  let result := delegatecall(
                      gas,
                      _target,
                      ptr,
                      calldatasize,
                      0x0,
                      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, 0x0, size)
                  // Depending on 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)
                  }
              }
          }
          /// UpgradeableMaster functions
          /// @notice Notice period before activation preparation status of upgrade mode
          function getNoticePeriod() external returns (uint) {
              (bool success, bytes memory result) = getTarget().delegatecall(abi.encodeWithSignature("getNoticePeriod()"));
              require(success, "unp11"); // unp11 - upgradeNoticePeriod delegatecall failed
              return abi.decode(result, (uint));
          }
          /// @notice Notifies proxy contract that notice period started
          function upgradeNoticePeriodStarted() external {
              requireMaster(msg.sender);
              (bool success, ) = getTarget().delegatecall(abi.encodeWithSignature("upgradeNoticePeriodStarted()"));
              require(success, "nps11"); // nps11 - upgradeNoticePeriodStarted delegatecall failed
          }
          /// @notice Notifies proxy contract that upgrade preparation status is activated
          function upgradePreparationStarted() external {
              requireMaster(msg.sender);
              (bool success, ) = getTarget().delegatecall(abi.encodeWithSignature("upgradePreparationStarted()"));
              require(success, "ups11"); // ups11 - upgradePreparationStarted delegatecall failed
          }
          /// @notice Notifies proxy contract that upgrade canceled
          function upgradeCanceled() external {
              requireMaster(msg.sender);
              (bool success, ) = getTarget().delegatecall(abi.encodeWithSignature("upgradeCanceled()"));
              require(success, "puc11"); // puc11 - upgradeCanceled delegatecall failed
          }
          /// @notice Notifies proxy contract that upgrade finishes
          function upgradeFinishes() external {
              requireMaster(msg.sender);
              (bool success, ) = getTarget().delegatecall(abi.encodeWithSignature("upgradeFinishes()"));
              require(success, "puf11"); // puf11 - upgradeFinishes delegatecall failed
          }
          /// @notice Checks that contract is ready for upgrade
          /// @return bool flag indicating that contract is ready for upgrade
          function isReadyForUpgrade() external returns (bool) {
              (bool success, bytes memory result) = getTarget().delegatecall(abi.encodeWithSignature("isReadyForUpgrade()"));
              require(success, "rfu11"); // rfu11 - readyForUpgrade delegatecall failed
              return abi.decode(result, (bool));
          }
      }
      pragma solidity ^0.5.0;
      /// @title Ownable Contract
      /// @author Matter Labs
      contract Ownable {
          /// @notice Storage position of the masters address (keccak256('eip1967.proxy.admin') - 1)
          bytes32 private constant masterPosition = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
          /// @notice Contract constructor
          /// @dev Sets msg sender address as masters address
          /// @param masterAddress Master address
          constructor(address masterAddress) public {
              setMaster(masterAddress);
          }
          /// @notice Check if specified address is master
          /// @param _address Address to check
          function requireMaster(address _address) internal view {
              require(_address == getMaster(), "oro11"); // oro11 - only by master
          }
          /// @notice Returns contract masters address
          /// @return Masters address
          function getMaster() public view returns (address master) {
              bytes32 position = masterPosition;
              assembly {
                  master := sload(position)
              }
          }
          /// @notice Sets new masters address
          /// @param _newMaster New masters address
          function setMaster(address _newMaster) internal {
              bytes32 position = masterPosition;
              assembly {
                  sstore(position, _newMaster)
              }
          }
          /// @notice Transfer mastership of the contract to new master
          /// @param _newMaster New masters address
          function transferMastership(address _newMaster) external {
              requireMaster(msg.sender);
              require(_newMaster != address(0), "otp11"); // otp11 - new masters address can't be zero address
              setMaster(_newMaster);
          }
      }
      pragma solidity ^0.5.0;
      /// @title Interface of the upgradeable contract
      /// @author Matter Labs
      interface Upgradeable {
          /// @notice Upgrades target of upgradeable contract
          /// @param newTarget New target
          /// @param newTargetInitializationParameters New target initialization parameters
          function upgradeTarget(address newTarget, bytes calldata newTargetInitializationParameters) external;
      }
      pragma solidity ^0.5.0;
      /// @title Interface of the upgradeable master contract (defines notice period duration and allows finish upgrade during preparation of it)
      /// @author Matter Labs
      interface UpgradeableMaster {
          /// @notice Notice period before activation preparation status of upgrade mode
          function getNoticePeriod() external returns (uint);
          /// @notice Notifies contract that notice period started
          function upgradeNoticePeriodStarted() external;
          /// @notice Notifies contract that upgrade preparation status is activated
          function upgradePreparationStarted() external;
          /// @notice Notifies contract that upgrade canceled
          function upgradeCanceled() external;
          /// @notice Notifies contract that upgrade finishes
          function upgradeFinishes() external;
          /// @notice Checks that contract is ready for upgrade
          /// @return bool flag indicating that contract is ready for upgrade
          function isReadyForUpgrade() external returns (bool);
      }
      

      File 2 of 2: ZkSync
      pragma solidity ^0.5.0;
      import "./ReentrancyGuard.sol";
      import "./SafeMath.sol";
      import "./SafeMathUInt128.sol";
      import "./SafeCast.sol";
      import "./Utils.sol";
      import "./Storage.sol";
      import "./Config.sol";
      import "./Events.sol";
      import "./Bytes.sol";
      import "./Operations.sol";
      import "./UpgradeableMaster.sol";
      /// @title zkSync main contract
      /// @author Matter Labs
      contract ZkSync is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard {
          using SafeMath for uint256;
          using SafeMathUInt128 for uint128;
          bytes32 public constant EMPTY_STRING_KECCAK = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
          // Upgrade functional
          /// @notice Notice period before activation preparation status of upgrade mode
          function getNoticePeriod() external returns (uint) {
              return UPGRADE_NOTICE_PERIOD;
          }
          /// @notice Notification that upgrade notice period started
          function upgradeNoticePeriodStarted() external {
          }
          /// @notice Notification that upgrade preparation status is activated
          function upgradePreparationStarted() external {
              upgradePreparationActive = true;
              upgradePreparationActivationTime = now;
          }
          /// @notice Notification that upgrade canceled
          function upgradeCanceled() external {
              upgradePreparationActive = false;
              upgradePreparationActivationTime = 0;
          }
          /// @notice Notification that upgrade finishes
          function upgradeFinishes() external {
              upgradePreparationActive = false;
              upgradePreparationActivationTime = 0;
          }
          /// @notice Checks that contract is ready for upgrade
          /// @return bool flag indicating that contract is ready for upgrade
          function isReadyForUpgrade() external returns (bool) {
              return !exodusMode;
          }
          /// @notice Franklin contract initialization. Can be external because Proxy contract intercepts illegal calls of this function.
          /// @param initializationParameters Encoded representation of initialization parameters:
          /// _governanceAddress The address of Governance contract
          /// _verifierAddress The address of Verifier contract
          /// _ // FIXME: remove _genesisAccAddress
          /// _genesisRoot Genesis blocks (first block) root
          function initialize(bytes calldata initializationParameters) external {
              initializeReentrancyGuard();
              (
              address _governanceAddress,
              address _verifierAddress,
              bytes32 _genesisRoot
              ) = abi.decode(initializationParameters, (address, address, bytes32));
              verifier = Verifier(_verifierAddress);
              governance = Governance(_governanceAddress);
              blocks[0].stateRoot = _genesisRoot;
          }
          /// @notice zkSync contract upgrade. Can be external because Proxy contract intercepts illegal calls of this function.
          /// @param upgradeParameters Encoded representation of upgrade parameters
          function upgrade(bytes calldata upgradeParameters) external {}
          /// @notice Sends tokens
          /// @dev NOTE: will revert if transfer call fails or rollup balance difference (before and after transfer) is bigger than _maxAmount
          /// @param _token Token address
          /// @param _to Address of recipient
          /// @param _amount Amount of tokens to transfer
          /// @param _maxAmount Maximum possible amount of tokens to transfer to this account
          function withdrawERC20Guarded(IERC20 _token, address _to, uint128 _amount, uint128 _maxAmount) external returns (uint128 withdrawnAmount) {
              require(msg.sender == address(this), "wtg10"); // wtg10 - can be called only from this contract as one "external" call (to revert all this function state changes if it is needed)
              uint256 balance_before = _token.balanceOf(address(this));
              require(Utils.sendERC20(_token, _to, _amount), "wtg11"); // wtg11 - ERC20 transfer fails
              uint256 balance_after = _token.balanceOf(address(this));
              uint256 balance_diff = balance_before.sub(balance_after);
              require(balance_diff <= _maxAmount, "wtg12"); // wtg12 - rollup balance difference (before and after transfer) is bigger than _maxAmount
              return SafeCast.toUint128(balance_diff);
          }
          /// @notice executes pending withdrawals
          /// @param _n The number of withdrawals to complete starting from oldest
          function completeWithdrawals(uint32 _n) external nonReentrant {
              // TODO: when switched to multi validators model we need to add incentive mechanism to call complete.
              uint32 toProcess = Utils.minU32(_n, numberOfPendingWithdrawals);
              uint32 startIndex = firstPendingWithdrawalIndex;
              numberOfPendingWithdrawals -= toProcess;
              firstPendingWithdrawalIndex += toProcess;
              for (uint32 i = startIndex; i < startIndex + toProcess; ++i) {
                  uint16 tokenId = pendingWithdrawals[i].tokenId;
                  address to = pendingWithdrawals[i].to;
                  // send fails are ignored hence there is always a direct way to withdraw.
                  delete pendingWithdrawals[i];
                  bytes22 packedBalanceKey = packAddressAndTokenId(to, tokenId);
                  uint128 amount = balancesToWithdraw[packedBalanceKey].balanceToWithdraw;
                  // amount is zero means funds has been withdrawn with withdrawETH or withdrawERC20
                  if (amount != 0) {
                      balancesToWithdraw[packedBalanceKey].balanceToWithdraw -= amount;
                      bool sent = false;
                      if (tokenId == 0) {
                          address payable toPayable = address(uint160(to));
                          sent = Utils.sendETHNoRevert(toPayable, amount);
                      } else {
                          address tokenAddr = governance.tokenAddresses(tokenId);
                          // we can just check that call not reverts because it wants to withdraw all amount
                          (sent, ) = address(this).call.gas(ERC20_WITHDRAWAL_GAS_LIMIT)(
                              abi.encodeWithSignature("withdrawERC20Guarded(address,address,uint128,uint128)", tokenAddr, to, amount, amount)
                          );
                      }
                      if (!sent) {
                          balancesToWithdraw[packedBalanceKey].balanceToWithdraw += amount;
                      }
                  }
              }
              if (toProcess > 0) {
                  emit PendingWithdrawalsComplete(startIndex, startIndex + toProcess);
              }
          }
          /// @notice Accrues users balances from deposit priority requests in Exodus mode
          /// @dev WARNING: Only for Exodus mode
          /// @dev Canceling may take several separate transactions to be completed
          /// @param _n number of requests to process
          function cancelOutstandingDepositsForExodusMode(uint64 _n) external nonReentrant {
              require(exodusMode, "coe01"); // exodus mode not active
              uint64 toProcess = Utils.minU64(totalOpenPriorityRequests, _n);
              require(toProcess > 0, "coe02"); // no deposits to process
              for (uint64 id = firstPriorityRequestId; id < firstPriorityRequestId + toProcess; id++) {
                  if (priorityRequests[id].opType == Operations.OpType.Deposit) {
                      Operations.Deposit memory op = Operations.readDepositPubdata(priorityRequests[id].pubData);
                      bytes22 packedBalanceKey = packAddressAndTokenId(op.owner, op.tokenId);
                      balancesToWithdraw[packedBalanceKey].balanceToWithdraw += op.amount;
                  }
                  delete priorityRequests[id];
              }
              firstPriorityRequestId += toProcess;
              totalOpenPriorityRequests -= toProcess;
          }
          /// @notice Deposit ETH to Layer 2 - transfer ether from user into contract, validate it, register deposit
          /// @param _franklinAddr The receiver Layer 2 address
          function depositETH(address _franklinAddr) external payable nonReentrant {
              requireActive();
              registerDeposit(0, SafeCast.toUint128(msg.value), _franklinAddr);
          }
          /// @notice Withdraw ETH to Layer 1 - register withdrawal and transfer ether to sender
          /// @param _amount Ether amount to withdraw
          function withdrawETH(uint128 _amount) external nonReentrant {
              registerWithdrawal(0, _amount, msg.sender);
              (bool success, ) = msg.sender.call.value(_amount)("");
              require(success, "fwe11"); // ETH withdraw failed
          }
          /// @notice Deposit ERC20 token to Layer 2 - transfer ERC20 tokens from user into contract, validate it, register deposit
          /// @param _token Token address
          /// @param _amount Token amount
          /// @param _franklinAddr Receiver Layer 2 address
          function depositERC20(IERC20 _token, uint104 _amount, address _franklinAddr) external nonReentrant {
              requireActive();
              // Get token id by its address
              uint16 tokenId = governance.validateTokenAddress(address(_token));
              uint256 balance_before = _token.balanceOf(address(this));
              require(Utils.transferFromERC20(_token, msg.sender, address(this), SafeCast.toUint128(_amount)), "fd012"); // token transfer failed deposit
              uint256 balance_after = _token.balanceOf(address(this));
              uint128 deposit_amount = SafeCast.toUint128(balance_after.sub(balance_before));
              registerDeposit(tokenId, deposit_amount, _franklinAddr);
          }
          /// @notice Withdraw ERC20 token to Layer 1 - register withdrawal and transfer ERC20 to sender
          /// @param _token Token address
          /// @param _amount amount to withdraw
          function withdrawERC20(IERC20 _token, uint128 _amount) external nonReentrant {
              uint16 tokenId = governance.validateTokenAddress(address(_token));
              bytes22 packedBalanceKey = packAddressAndTokenId(msg.sender, tokenId);
              uint128 balance = balancesToWithdraw[packedBalanceKey].balanceToWithdraw;
              uint128 withdrawnAmount = this.withdrawERC20Guarded(_token, msg.sender, _amount, balance);
              registerWithdrawal(tokenId, withdrawnAmount, msg.sender);
          }
          /// @notice Register full exit request - pack pubdata, add priority request
          /// @param _accountId Numerical id of the account
          /// @param _token Token address, 0 address for ether
          function fullExit (uint32 _accountId, address _token) external nonReentrant {
              requireActive();
              require(_accountId <= MAX_ACCOUNT_ID, "fee11");
              uint16 tokenId;
              if (_token == address(0)) {
                  tokenId = 0;
              } else {
                  tokenId = governance.validateTokenAddress(_token);
              }
              // Priority Queue request
              Operations.FullExit memory op = Operations.FullExit({
                  accountId:  _accountId,
                  owner:      msg.sender,
                  tokenId:    tokenId,
                  amount:     0 // unknown at this point
              });
              bytes memory pubData = Operations.writeFullExitPubdata(op);
              addPriorityRequest(Operations.OpType.FullExit, pubData);
              // User must fill storage slot of balancesToWithdraw(msg.sender, tokenId) with nonzero value
              // In this case operator should just overwrite this slot during confirming withdrawal
              bytes22 packedBalanceKey = packAddressAndTokenId(msg.sender, tokenId);
              balancesToWithdraw[packedBalanceKey].gasReserveValue = 0xff;
          }
          /// @notice Commit block - collect onchain operations, create its commitment, emit BlockCommit event
          /// @param _blockNumber Block number
          /// @param _feeAccount Account to collect fees
          /// @param _newBlockInfo New state of the block. (first element is the account tree root hash, rest of the array is reserved for the future)
          /// @param _publicData Operations pubdata
          /// @param _ethWitness Data passed to ethereum outside pubdata of the circuit.
          /// @param _ethWitnessSizes Amount of eth witness bytes for the corresponding operation.
          function commitBlock(
              uint32 _blockNumber,
              uint32 _feeAccount,
              bytes32[] calldata _newBlockInfo,
              bytes calldata _publicData,
              bytes calldata _ethWitness,
              uint32[] calldata _ethWitnessSizes
          ) external nonReentrant {
              requireActive();
              require(_blockNumber == totalBlocksCommitted + 1, "fck11"); // only commit next block
              governance.requireActiveValidator(msg.sender);
              require(_newBlockInfo.length == 1, "fck13"); // This version of the contract expects only account tree root hash
              bytes memory publicData = _publicData;
              // Unpack onchain operations and store them.
              // Get priority operations number for this block.
              uint64 prevTotalCommittedPriorityRequests = totalCommittedPriorityRequests;
              bytes32 withdrawalsDataHash = collectOnchainOps(_blockNumber, publicData, _ethWitness, _ethWitnessSizes);
              uint64 nPriorityRequestProcessed = totalCommittedPriorityRequests - prevTotalCommittedPriorityRequests;
              createCommittedBlock(_blockNumber, _feeAccount, _newBlockInfo[0], publicData, withdrawalsDataHash, nPriorityRequestProcessed);
              totalBlocksCommitted++;
              emit BlockCommit(_blockNumber);
          }
          /// @notice Block verification.
          /// @notice Verify proof -> process onchain withdrawals (accrue balances from withdrawals) -> remove priority requests
          /// @param _blockNumber Block number
          /// @param _proof Block proof
          /// @param _withdrawalsData Block withdrawals data
          function verifyBlock(uint32 _blockNumber, uint256[] calldata _proof, bytes calldata _withdrawalsData)
              external nonReentrant
          {
              requireActive();
              require(_blockNumber == totalBlocksVerified + 1, "fvk11"); // only verify next block
              governance.requireActiveValidator(msg.sender);
              require(verifier.verifyBlockProof(_proof, blocks[_blockNumber].commitment, blocks[_blockNumber].chunks), "fvk13"); // proof verification failed
              processOnchainWithdrawals(_withdrawalsData, blocks[_blockNumber].withdrawalsDataHash);
              deleteRequests(
                  blocks[_blockNumber].priorityOperations
              );
              totalBlocksVerified += 1;
              emit BlockVerification(_blockNumber);
          }
          /// @notice Reverts unverified blocks
          /// @param _maxBlocksToRevert the maximum number blocks that will be reverted (use if can't revert all blocks because of gas limit).
          function revertBlocks(uint32 _maxBlocksToRevert) external nonReentrant {
              require(isBlockCommitmentExpired(), "rbs11"); // trying to revert non-expired blocks.
              governance.requireActiveValidator(msg.sender);
              uint32 blocksCommited = totalBlocksCommitted;
              uint32 blocksToRevert = Utils.minU32(_maxBlocksToRevert, blocksCommited - totalBlocksVerified);
              uint64 revertedPriorityRequests = 0;
              for (uint32 i = totalBlocksCommitted - blocksToRevert + 1; i <= blocksCommited; i++) {
                  Block memory revertedBlock = blocks[i];
                  require(revertedBlock.committedAtBlock > 0, "frk11"); // block not found
                  revertedPriorityRequests += revertedBlock.priorityOperations;
                  delete blocks[i];
              }
              blocksCommited -= blocksToRevert;
              totalBlocksCommitted -= blocksToRevert;
              totalCommittedPriorityRequests -= revertedPriorityRequests;
              emit BlocksRevert(totalBlocksVerified, blocksCommited);
          }
          /// @notice Checks if Exodus mode must be entered. If true - enters exodus mode and emits ExodusMode event.
          /// @dev Exodus mode must be entered in case of current ethereum block number is higher than the oldest
          /// @dev of existed priority requests expiration block number.
          /// @return bool flag that is true if the Exodus mode must be entered.
          function triggerExodusIfNeeded() external returns (bool) {
              bool trigger = block.number >= priorityRequests[firstPriorityRequestId].expirationBlock &&
              priorityRequests[firstPriorityRequestId].expirationBlock != 0;
              if (trigger) {
                  if (!exodusMode) {
                      exodusMode = true;
                      emit ExodusMode();
                  }
                  return true;
              } else {
                  return false;
              }
          }
          /// @notice Withdraws token from Franklin to root chain in case of exodus mode. User must provide proof that he owns funds
          /// @param _accountId Id of the account in the tree
          /// @param _proof Proof
          /// @param _tokenId Verified token id
          /// @param _amount Amount for owner (must be total amount, not part of it)
          function exit(uint32 _accountId, uint16 _tokenId, uint128 _amount, uint256[] calldata _proof) external nonReentrant {
              bytes22 packedBalanceKey = packAddressAndTokenId(msg.sender, _tokenId);
              require(exodusMode, "fet11"); // must be in exodus mode
              require(!exited[_accountId][_tokenId], "fet12"); // already exited
              require(verifier.verifyExitProof(blocks[totalBlocksVerified].stateRoot, _accountId, msg.sender, _tokenId, _amount, _proof), "fet13"); // verification failed
              uint128 balance = balancesToWithdraw[packedBalanceKey].balanceToWithdraw;
              balancesToWithdraw[packedBalanceKey].balanceToWithdraw = balance.add(_amount);
              exited[_accountId][_tokenId] = true;
          }
          function setAuthPubkeyHash(bytes calldata _pubkey_hash, uint32 _nonce) external nonReentrant {
              require(_pubkey_hash.length == PUBKEY_HASH_BYTES, "ahf10"); // PubKeyHash should be 20 bytes.
              require(authFacts[msg.sender][_nonce] == bytes32(0), "ahf11"); // auth fact for nonce should be empty
              authFacts[msg.sender][_nonce] = keccak256(_pubkey_hash);
              emit FactAuth(msg.sender, _nonce, _pubkey_hash);
          }
          /// @notice Register deposit request - pack pubdata, add priority request and emit OnchainDeposit event
          /// @param _tokenId Token by id
          /// @param _amount Token amount
          /// @param _owner Receiver
          function registerDeposit(
              uint16 _tokenId,
              uint128 _amount,
              address _owner
          ) internal {
              // Priority Queue request
              Operations.Deposit memory op = Operations.Deposit({
                  accountId:  0, // unknown at this point
                  owner:      _owner,
                  tokenId:    _tokenId,
                  amount:     _amount
                  });
              bytes memory pubData = Operations.writeDepositPubdata(op);
              addPriorityRequest(Operations.OpType.Deposit, pubData);
              emit OnchainDeposit(
                  msg.sender,
                  _tokenId,
                  _amount,
                  _owner
              );
          }
          /// @notice Register withdrawal - update user balance and emit OnchainWithdrawal event
          /// @param _token - token by id
          /// @param _amount - token amount
          /// @param _to - address to withdraw to
          function registerWithdrawal(uint16 _token, uint128 _amount, address payable _to) internal {
              bytes22 packedBalanceKey = packAddressAndTokenId(_to, _token);
              uint128 balance = balancesToWithdraw[packedBalanceKey].balanceToWithdraw;
              balancesToWithdraw[packedBalanceKey].balanceToWithdraw = balance.sub(_amount);
              emit OnchainWithdrawal(
                  _to,
                  _token,
                  _amount
              );
          }
          /// @notice Store committed block structure to the storage.
          /// @param _nCommittedPriorityRequests - number of priority requests in block
          function createCommittedBlock(
              uint32 _blockNumber,
              uint32 _feeAccount,
              bytes32 _newRoot,
              bytes memory _publicData,
              bytes32 _withdrawalDataHash,
              uint64 _nCommittedPriorityRequests
          ) internal {
              require(_publicData.length % CHUNK_BYTES == 0, "cbb10"); // Public data size is not multiple of CHUNK_BYTES
              uint32 blockChunks = uint32(_publicData.length / CHUNK_BYTES);
              require(verifier.isBlockSizeSupported(blockChunks), "ccb11");
              // Create block commitment for verification proof
              bytes32 commitment = createBlockCommitment(
                  _blockNumber,
                  _feeAccount,
                  blocks[_blockNumber - 1].stateRoot,
                  _newRoot,
                  _publicData
              );
              blocks[_blockNumber] = Block(
                  uint32(block.number), // committed at
                  _nCommittedPriorityRequests, // number of priority onchain ops in block
                  blockChunks,
                  _withdrawalDataHash, // hash of onchain withdrawals data (will be used during checking block withdrawal data in verifyBlock function)
                  commitment, // blocks' commitment
                  _newRoot // new root
              );
          }
          function emitDepositCommitEvent(uint32 _blockNumber, Operations.Deposit memory depositData) internal {
              emit DepositCommit(_blockNumber, depositData.accountId, depositData.owner, depositData.tokenId, depositData.amount);
          }
          function emitFullExitCommitEvent(uint32 _blockNumber, Operations.FullExit memory fullExitData) internal {
              emit FullExitCommit(_blockNumber, fullExitData.accountId, fullExitData.owner, fullExitData.tokenId, fullExitData.amount);
          }
          /// @notice Gets operations packed in bytes array. Unpacks it and stores onchain operations.
          /// @param _blockNumber Franklin block number
          /// @param _publicData Operations packed in bytes array
          /// @param _ethWitness Eth witness that was posted with commit
          /// @param _ethWitnessSizes Amount of eth witness bytes for the corresponding operation.
          /// Priority operations must be committed in the same order as they are in the priority queue.
          function collectOnchainOps(uint32 _blockNumber, bytes memory _publicData, bytes memory _ethWitness, uint32[] memory _ethWitnessSizes)
              internal returns (bytes32 withdrawalsDataHash) {
              require(_publicData.length % CHUNK_BYTES == 0, "fcs11"); // pubdata length must be a multiple of CHUNK_BYTES
              uint64 currentPriorityRequestId = firstPriorityRequestId + totalCommittedPriorityRequests;
              uint256 pubDataPtr = 0;
              uint256 pubDataStartPtr = 0;
              uint256 pubDataEndPtr = 0;
              assembly { pubDataStartPtr := add(_publicData, 0x20) }
              pubDataPtr = pubDataStartPtr;
              pubDataEndPtr = pubDataStartPtr + _publicData.length;
              uint64 ethWitnessOffset = 0;
              uint16 processedOperationsRequiringEthWitness = 0;
              withdrawalsDataHash = EMPTY_STRING_KECCAK;
              while (pubDataPtr < pubDataEndPtr) {
                  Operations.OpType opType;
                  // read operation type from public data (the first byte per each operation)
                  assembly {
                      opType := shr(0xf8, mload(pubDataPtr))
                  }
                  // cheap operations processing
                  if (opType == Operations.OpType.Transfer) {
                      pubDataPtr += TRANSFER_BYTES;
                  } else if (opType == Operations.OpType.Noop) {
                      pubDataPtr += NOOP_BYTES;
                  } else if (opType == Operations.OpType.TransferToNew) {
                      pubDataPtr += TRANSFER_TO_NEW_BYTES;
                  } else {
                      // other operations processing
                      // calculation of public data offset
                      uint256 pubdataOffset = pubDataPtr - pubDataStartPtr;
                      if (opType == Operations.OpType.Deposit) {
                          bytes memory pubData = Bytes.slice(_publicData, pubdataOffset + 1, DEPOSIT_BYTES - 1);
                          Operations.Deposit memory depositData = Operations.readDepositPubdata(pubData);
                          emitDepositCommitEvent(_blockNumber, depositData);
                          OnchainOperation memory onchainOp = OnchainOperation(
                              Operations.OpType.Deposit,
                              pubData
                          );
                          commitNextPriorityOperation(onchainOp, currentPriorityRequestId);
                          currentPriorityRequestId++;
                          pubDataPtr += DEPOSIT_BYTES;
                      } else if (opType == Operations.OpType.PartialExit) {
                          Operations.PartialExit memory data = Operations.readPartialExitPubdata(_publicData, pubdataOffset + 1);
                          bool addToPendingWithdrawalsQueue = true;
                          withdrawalsDataHash = keccak256(abi.encode(withdrawalsDataHash, addToPendingWithdrawalsQueue, data.owner, data.tokenId, data.amount));
                          pubDataPtr += PARTIAL_EXIT_BYTES;
                      } else if (opType == Operations.OpType.ForcedExit) {
                          Operations.ForcedExit memory data = Operations.readForcedExitPubdata(_publicData, pubdataOffset + 1);
                          bool addToPendingWithdrawalsQueue = true;
                          withdrawalsDataHash = keccak256(abi.encode(withdrawalsDataHash, addToPendingWithdrawalsQueue, data.target, data.tokenId, data.amount));
                          pubDataPtr += FORCED_EXIT_BYTES;
                      } else if (opType == Operations.OpType.FullExit) {
                          bytes memory pubData = Bytes.slice(_publicData, pubdataOffset + 1, FULL_EXIT_BYTES - 1);
                          Operations.FullExit memory fullExitData = Operations.readFullExitPubdata(pubData);
                          emitFullExitCommitEvent(_blockNumber, fullExitData);
                          bool addToPendingWithdrawalsQueue = false;
                          withdrawalsDataHash = keccak256(abi.encode(withdrawalsDataHash, addToPendingWithdrawalsQueue, fullExitData.owner, fullExitData.tokenId, fullExitData.amount));
                          OnchainOperation memory onchainOp = OnchainOperation(
                              Operations.OpType.FullExit,
                              pubData
                          );
                          commitNextPriorityOperation(onchainOp, currentPriorityRequestId);
                          currentPriorityRequestId++;
                          pubDataPtr += FULL_EXIT_BYTES;
                      } else if (opType == Operations.OpType.ChangePubKey) {
                          require(processedOperationsRequiringEthWitness < _ethWitnessSizes.length, "fcs13"); // eth witness data malformed
                          Operations.ChangePubKey memory op = Operations.readChangePubKeyPubdata(_publicData, pubdataOffset + 1);
                          if (_ethWitnessSizes[processedOperationsRequiringEthWitness] != 0) {
                              bytes memory currentEthWitness = Bytes.slice(_ethWitness, ethWitnessOffset, _ethWitnessSizes[processedOperationsRequiringEthWitness]);
                              bool valid = verifyChangePubkeySignature(currentEthWitness, op.pubKeyHash, op.nonce, op.owner, op.accountId);
                              require(valid, "fpp15"); // failed to verify change pubkey hash signature
                          } else {
                              bool valid = authFacts[op.owner][op.nonce] == keccak256(abi.encodePacked(op.pubKeyHash));
                              require(valid, "fpp16"); // new pub key hash is not authenticated properly
                          }
                          ethWitnessOffset += _ethWitnessSizes[processedOperationsRequiringEthWitness];
                          processedOperationsRequiringEthWitness++;
                          pubDataPtr += CHANGE_PUBKEY_BYTES;
                      } else {
                          revert("fpp14"); // unsupported op
                      }
                  }
              }
              require(pubDataPtr == pubDataEndPtr, "fcs12"); // last chunk exceeds pubdata
              require(ethWitnessOffset == _ethWitness.length, "fcs14"); // _ethWitness was not used completely
              require(processedOperationsRequiringEthWitness == _ethWitnessSizes.length, "fcs15"); // _ethWitnessSizes was not used completely
              require(currentPriorityRequestId <= firstPriorityRequestId + totalOpenPriorityRequests, "fcs16"); // fcs16 - excess priority requests in pubdata
              totalCommittedPriorityRequests = currentPriorityRequestId - firstPriorityRequestId;
          }
          /// @notice Checks that signature is valid for pubkey change message
          /// @param _signature Signature
          /// @param _newPkHash New pubkey hash
          /// @param _nonce Nonce used for message
          /// @param _ethAddress Account's ethereum address
          /// @param _accountId Id of zkSync account
          function verifyChangePubkeySignature(bytes memory _signature, bytes20 _newPkHash, uint32 _nonce, address _ethAddress, uint32 _accountId) internal pure returns (bool) {
              bytes memory signedMessage = abi.encodePacked(
                  "\\x19Ethereum Signed Message:\
      152",
                  "Register zkSync pubkey:\
      \
      ",
                  Bytes.bytesToHexASCIIBytes(abi.encodePacked(_newPkHash)), "\
      ",
                  "nonce: 0x", Bytes.bytesToHexASCIIBytes(Bytes.toBytesFromUInt32(_nonce)), "\
      ",
                  "account id: 0x", Bytes.bytesToHexASCIIBytes(Bytes.toBytesFromUInt32(_accountId)),
                  "\
      \
      ",
                  "Only sign this message for a trusted client!"
              );
              address recoveredAddress = Utils.recoverAddressFromEthSignature(_signature, signedMessage);
              return recoveredAddress == _ethAddress;
          }
          /// @notice Creates block commitment from its data
          /// @param _blockNumber Block number
          /// @param _feeAccount Account to collect fees
          /// @param _oldRoot Old tree root
          /// @param _newRoot New tree root
          /// @param _publicData Operations pubdata
          /// @return block commitment
          function createBlockCommitment(
              uint32 _blockNumber,
              uint32 _feeAccount,
              bytes32 _oldRoot,
              bytes32 _newRoot,
              bytes memory _publicData
          ) internal view returns (bytes32 commitment) {
              bytes32 hash = sha256(
                  abi.encodePacked(uint256(_blockNumber), uint256(_feeAccount))
              );
              hash = sha256(abi.encodePacked(hash, uint256(_oldRoot)));
              hash = sha256(abi.encodePacked(hash, uint256(_newRoot)));
              /// The code below is equivalent to `commitment = sha256(abi.encodePacked(hash, _publicData))`
              /// We use inline assembly instead of this concise and readable code in order to avoid copying of `_publicData` (which saves ~90 gas per transfer operation).
              /// Specifically, we perform the following trick:
              /// First, replace the first 32 bytes of `_publicData` (where normally its length is stored) with the value of `hash`.
              /// Then, we call `sha256` precompile passing the `_publicData` pointer and the length of the concatenated byte buffer.
              /// Finally, we put the `_publicData.length` back to its original location (to the first word of `_publicData`).
              assembly {
                  let hashResult := mload(0x40)
                  let pubDataLen := mload(_publicData)
                  mstore(_publicData, hash)
                  // staticcall to the sha256 precompile at address 0x2
                  let success := staticcall(
                      gas,
                      0x2,
                      _publicData,
                      add(pubDataLen, 0x20),
                      hashResult,
                      0x20
                  )
                  mstore(_publicData, pubDataLen)
                  // Use "invalid" to make gas estimation work
                  switch success case 0 { invalid() }
                  commitment := mload(hashResult)
              }
          }
          /// @notice Checks that operation is same as operation in priority queue
          /// @param _onchainOp The operation
          /// @param _priorityRequestId Operation's id in priority queue
          function commitNextPriorityOperation(OnchainOperation memory _onchainOp, uint64 _priorityRequestId) internal view {
              Operations.OpType priorReqType = priorityRequests[_priorityRequestId].opType;
              bytes memory priorReqPubdata = priorityRequests[_priorityRequestId].pubData;
              require(priorReqType == _onchainOp.opType, "nvp12"); // incorrect priority op type
              if (_onchainOp.opType == Operations.OpType.Deposit) {
                  require(Operations.depositPubdataMatch(priorReqPubdata, _onchainOp.pubData), "vnp13");
              } else if (_onchainOp.opType == Operations.OpType.FullExit) {
                  require(Operations.fullExitPubdataMatch(priorReqPubdata, _onchainOp.pubData), "vnp14");
              } else {
                  revert("vnp15"); // invalid or non-priority operation
              }
          }
          /// @notice Processes onchain withdrawals. Full exit withdrawals will not be added to pending withdrawals queue
          /// @dev NOTICE: must process only withdrawals which hash matches with expectedWithdrawalsDataHash.
          /// @param withdrawalsData Withdrawals data
          /// @param expectedWithdrawalsDataHash Expected withdrawals data hash
          function processOnchainWithdrawals(bytes memory withdrawalsData, bytes32 expectedWithdrawalsDataHash) internal {
              require(withdrawalsData.length % ONCHAIN_WITHDRAWAL_BYTES == 0, "pow11"); // pow11 - withdrawalData length is not multiple of ONCHAIN_WITHDRAWAL_BYTES
              bytes32 withdrawalsDataHash = EMPTY_STRING_KECCAK;
              uint offset = 0;
              uint32 localNumberOfPendingWithdrawals = numberOfPendingWithdrawals;
              while (offset < withdrawalsData.length) {
                  (bool addToPendingWithdrawalsQueue, address _to, uint16 _tokenId, uint128 _amount) = Operations.readWithdrawalData(withdrawalsData, offset);
                  bytes22 packedBalanceKey = packAddressAndTokenId(_to, _tokenId);
                  uint128 balance = balancesToWithdraw[packedBalanceKey].balanceToWithdraw;
                  // after this all writes to this slot will cost 5k gas
                  balancesToWithdraw[packedBalanceKey] = BalanceToWithdraw({
                      balanceToWithdraw: balance.add(_amount),
                      gasReserveValue: 0xff
                  });
                  if (addToPendingWithdrawalsQueue) {
                      pendingWithdrawals[firstPendingWithdrawalIndex + localNumberOfPendingWithdrawals] = PendingWithdrawal(_to, _tokenId);
                      localNumberOfPendingWithdrawals++;
                  }
                  withdrawalsDataHash = keccak256(abi.encode(withdrawalsDataHash, addToPendingWithdrawalsQueue, _to, _tokenId, _amount));
                  offset += ONCHAIN_WITHDRAWAL_BYTES;
              }
              require(withdrawalsDataHash == expectedWithdrawalsDataHash, "pow12"); // pow12 - withdrawals data hash not matches with expected value
              if (numberOfPendingWithdrawals != localNumberOfPendingWithdrawals) {
                  emit PendingWithdrawalsAdd(firstPendingWithdrawalIndex + numberOfPendingWithdrawals, firstPendingWithdrawalIndex + localNumberOfPendingWithdrawals);
              }
              numberOfPendingWithdrawals = localNumberOfPendingWithdrawals;
          }
          /// @notice Checks whether oldest unverified block has expired
          /// @return bool flag that indicates whether oldest unverified block has expired
          function isBlockCommitmentExpired() internal view returns (bool) {
              return (
                  totalBlocksCommitted > totalBlocksVerified &&
                  blocks[totalBlocksVerified + 1].committedAtBlock > 0 &&
                  block.number > blocks[totalBlocksVerified + 1].committedAtBlock + EXPECT_VERIFICATION_IN
              );
          }
          /// @notice Checks that current state not is exodus mode
          function requireActive() internal view {
              require(!exodusMode, "fre11"); // exodus mode activated
          }
          // Priority queue
          /// @notice Saves priority request in storage
          /// @dev Calculates expiration block for request, store this request and emit NewPriorityRequest event
          /// @param _opType Rollup operation type
          /// @param _pubData Operation pubdata
          function addPriorityRequest(
              Operations.OpType _opType,
              bytes memory _pubData
          ) internal {
              // Expiration block is: current block number + priority expiration delta
              uint256 expirationBlock = block.number + PRIORITY_EXPIRATION;
              uint64 nextPriorityRequestId = firstPriorityRequestId + totalOpenPriorityRequests;
              priorityRequests[nextPriorityRequestId] = PriorityOperation({
                  opType: _opType,
                  pubData: _pubData,
                  expirationBlock: expirationBlock
              });
              emit NewPriorityRequest(
                  msg.sender,
                  nextPriorityRequestId,
                  _opType,
                  _pubData,
                  expirationBlock
              );
              totalOpenPriorityRequests++;
          }
          /// @notice Deletes processed priority requests
          /// @param _number The number of requests
          function deleteRequests(uint64 _number) internal {
              require(_number <= totalOpenPriorityRequests, "pcs21"); // number is higher than total priority requests number
              uint64 numberOfRequestsToClear = Utils.minU64(_number, MAX_PRIORITY_REQUESTS_TO_DELETE_IN_VERIFY);
              uint64 startIndex = firstPriorityRequestId;
              for (uint64 i = startIndex; i < startIndex + numberOfRequestsToClear; i++) {
                  delete priorityRequests[i];
              }
              totalOpenPriorityRequests -= _number;
              firstPriorityRequestId += _number;
              totalCommittedPriorityRequests -= _number;
          }
      }
      pragma solidity ^0.5.0;
      /**
       * @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.
       */
      contract ReentrancyGuard {
          /// Address of lock flag variable.
          /// Flag is placed at random memory location to not interfere with Storage contract.
          uint constant private LOCK_FLAG_ADDRESS = 0x8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4; // keccak256("ReentrancyGuard") - 1;
          function initializeReentrancyGuard () internal {
              // Storing an initial 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 percetange 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.
              assembly { sstore(LOCK_FLAG_ADDRESS, 1) }
          }
          /**
           * @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() {
              bool notEntered;
              assembly { notEntered := sload(LOCK_FLAG_ADDRESS) }
              // On the first call to nonReentrant, _notEntered will be true
              require(notEntered, "ReentrancyGuard: reentrant call");
              // Any calls to nonReentrant after this point will fail
              assembly { sstore(LOCK_FLAG_ADDRESS, 0) }
              _;
              // By storing the original value once again, a refund is triggered (see
              // https://eips.ethereum.org/EIPS/eip-2200)
              assembly { sstore(LOCK_FLAG_ADDRESS, 1) }
          }
      }
      pragma solidity ^0.5.0;
      /**
       * @dev Wrappers over Solidity's arithmetic operations with added overflow
       * checks.
       *
       * Arithmetic operations in Solidity wrap on overflow. This can easily result
       * in bugs, because programmers usually assume that an overflow raises an
       * error, which is the standard behavior in high level programming languages.
       * `SafeMath` restores this intuition by reverting the transaction when an
       * operation overflows.
       *
       * Using this library instead of the unchecked operations eliminates an entire
       * class of bugs, so it's recommended to use it always.
       */
      library SafeMath {
          /**
           * @dev Returns the addition of two unsigned integers, reverting on
           * overflow.
           *
           * Counterpart to Solidity's `+` operator.
           *
           * Requirements:
           * - Addition cannot overflow.
           */
          function add(uint256 a, uint256 b) internal pure returns (uint256) {
              uint256 c = a + b;
              require(c >= a, "SafeMath: addition overflow");
              return c;
          }
          /**
           * @dev Returns the subtraction of two unsigned integers, reverting on
           * overflow (when the result is negative).
           *
           * Counterpart to Solidity's `-` operator.
           *
           * Requirements:
           * - Subtraction cannot overflow.
           */
          function sub(uint256 a, uint256 b) internal pure returns (uint256) {
              return sub(a, b, "SafeMath: subtraction overflow");
          }
          /**
           * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
           * overflow (when the result is negative).
           *
           * Counterpart to Solidity's `-` operator.
           *
           * Requirements:
           * - Subtraction cannot overflow.
           *
           * _Available since v2.4.0._
           */
          function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              require(b <= a, errorMessage);
              uint256 c = a - b;
              return c;
          }
          /**
           * @dev Returns the multiplication of two unsigned integers, reverting on
           * overflow.
           *
           * Counterpart to Solidity's `*` operator.
           *
           * Requirements:
           * - Multiplication cannot overflow.
           */
          function mul(uint256 a, uint256 b) internal pure returns (uint256) {
              // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
              // benefit is lost if 'b' is also tested.
              // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
              if (a == 0) {
                  return 0;
              }
              uint256 c = a * b;
              require(c / a == b, "SafeMath: multiplication overflow");
              return c;
          }
          /**
           * @dev Returns the integer division of two unsigned integers. Reverts on
           * division by zero. The result is rounded towards zero.
           *
           * Counterpart to Solidity's `/` operator. Note: this function uses a
           * `revert` opcode (which leaves remaining gas untouched) while Solidity
           * uses an invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           * - The divisor cannot be zero.
           */
          function div(uint256 a, uint256 b) internal pure returns (uint256) {
              return div(a, b, "SafeMath: division by zero");
          }
          /**
           * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
           * division by zero. The result is rounded towards zero.
           *
           * Counterpart to Solidity's `/` operator. Note: this function uses a
           * `revert` opcode (which leaves remaining gas untouched) while Solidity
           * uses an invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           * - The divisor cannot be zero.
           *
           * _Available since v2.4.0._
           */
          function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              // Solidity only automatically asserts when dividing by 0
              require(b > 0, errorMessage);
              uint256 c = a / b;
              // assert(a == b * c + a % b); // There is no case in which this doesn't hold
              return c;
          }
          /**
           * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
           * Reverts when dividing by zero.
           *
           * Counterpart to Solidity's `%` operator. This function uses a `revert`
           * opcode (which leaves remaining gas untouched) while Solidity uses an
           * invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           * - The divisor cannot be zero.
           */
          function mod(uint256 a, uint256 b) internal pure returns (uint256) {
              return mod(a, b, "SafeMath: modulo by zero");
          }
          /**
           * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
           * Reverts with custom message when dividing by zero.
           *
           * Counterpart to Solidity's `%` operator. This function uses a `revert`
           * opcode (which leaves remaining gas untouched) while Solidity uses an
           * invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           * - The divisor cannot be zero.
           *
           * _Available since v2.4.0._
           */
          function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              require(b != 0, errorMessage);
              return a % b;
          }
      }
      pragma solidity ^0.5.0;
      /**
       * @dev Wrappers over Solidity's arithmetic operations with added overflow
       * checks.
       *
       * Arithmetic operations in Solidity wrap on overflow. This can easily result
       * in bugs, because programmers usually assume that an overflow raises an
       * error, which is the standard behavior in high level programming languages.
       * `SafeMath` restores this intuition by reverting the transaction when an
       * operation overflows.
       *
       * Using this library instead of the unchecked operations eliminates an entire
       * class of bugs, so it's recommended to use it always.
       */
      library SafeMathUInt128 {
          /**
           * @dev Returns the addition of two unsigned integers, reverting on
           * overflow.
           *
           * Counterpart to Solidity's `+` operator.
           *
           * Requirements:
           * - Addition cannot overflow.
           */
          function add(uint128 a, uint128 b) internal pure returns (uint128) {
              uint128 c = a + b;
              require(c >= a, "SafeMath: addition overflow");
              return c;
          }
          /**
           * @dev Returns the subtraction of two unsigned integers, reverting on
           * overflow (when the result is negative).
           *
           * Counterpart to Solidity's `-` operator.
           *
           * Requirements:
           * - Subtraction cannot overflow.
           */
          function sub(uint128 a, uint128 b) internal pure returns (uint128) {
              return sub(a, b, "SafeMath: subtraction overflow");
          }
          /**
           * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
           * overflow (when the result is negative).
           *
           * Counterpart to Solidity's `-` operator.
           *
           * Requirements:
           * - Subtraction cannot overflow.
           *
           * _Available since v2.4.0._
           */
          function sub(uint128 a, uint128 b, string memory errorMessage) internal pure returns (uint128) {
              require(b <= a, errorMessage);
              uint128 c = a - b;
              return c;
          }
          /**
           * @dev Returns the multiplication of two unsigned integers, reverting on
           * overflow.
           *
           * Counterpart to Solidity's `*` operator.
           *
           * Requirements:
           * - Multiplication cannot overflow.
           */
          function mul(uint128 a, uint128 b) internal pure returns (uint128) {
              // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
              // benefit is lost if 'b' is also tested.
              // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
              if (a == 0) {
                  return 0;
              }
              uint128 c = a * b;
              require(c / a == b, "SafeMath: multiplication overflow");
              return c;
          }
          /**
           * @dev Returns the integer division of two unsigned integers. Reverts on
           * division by zero. The result is rounded towards zero.
           *
           * Counterpart to Solidity's `/` operator. Note: this function uses a
           * `revert` opcode (which leaves remaining gas untouched) while Solidity
           * uses an invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           * - The divisor cannot be zero.
           */
          function div(uint128 a, uint128 b) internal pure returns (uint128) {
              return div(a, b, "SafeMath: division by zero");
          }
          /**
           * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
           * division by zero. The result is rounded towards zero.
           *
           * Counterpart to Solidity's `/` operator. Note: this function uses a
           * `revert` opcode (which leaves remaining gas untouched) while Solidity
           * uses an invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           * - The divisor cannot be zero.
           *
           * _Available since v2.4.0._
           */
          function div(uint128 a, uint128 b, string memory errorMessage) internal pure returns (uint128) {
              // Solidity only automatically asserts when dividing by 0
              require(b > 0, errorMessage);
              uint128 c = a / b;
              // assert(a == b * c + a % b); // There is no case in which this doesn't hold
              return c;
          }
          /**
           * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
           * Reverts when dividing by zero.
           *
           * Counterpart to Solidity's `%` operator. This function uses a `revert`
           * opcode (which leaves remaining gas untouched) while Solidity uses an
           * invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           * - The divisor cannot be zero.
           */
          function mod(uint128 a, uint128 b) internal pure returns (uint128) {
              return mod(a, b, "SafeMath: modulo by zero");
          }
          /**
           * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
           * Reverts with custom message when dividing by zero.
           *
           * Counterpart to Solidity's `%` operator. This function uses a `revert`
           * opcode (which leaves remaining gas untouched) while Solidity uses an
           * invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           * - The divisor cannot be zero.
           *
           * _Available since v2.4.0._
           */
          function mod(uint128 a, uint128 b, string memory errorMessage) internal pure returns (uint128) {
              require(b != 0, errorMessage);
              return a % b;
          }
      }
      pragma solidity ^0.5.0;
      /**
       * @dev Wrappers over Solidity's uintXX casting operators with added overflow
       * checks.
       *
       * Downcasting from uint256 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} to extend it to smaller types, by performing
       * all math on `uint256` and then downcasting.
       *
       * _Available since v2.5.0._
       */
      library SafeCast {
          /**
           * @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
           */
          function toUint128(uint256 value) internal pure returns (uint128) {
              require(value < 2**128, "SafeCast: value doesn\\'t fit in 128 bits");
              return uint128(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
           */
          function toUint64(uint256 value) internal pure returns (uint64) {
              require(value < 2**64, "SafeCast: value doesn\\'t fit in 64 bits");
              return uint64(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
           */
          function toUint32(uint256 value) internal pure returns (uint32) {
              require(value < 2**32, "SafeCast: value doesn\\'t fit in 32 bits");
              return uint32(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
           */
          function toUint16(uint256 value) internal pure returns (uint16) {
              require(value < 2**16, "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.
           */
          function toUint8(uint256 value) internal pure returns (uint8) {
              require(value < 2**8, "SafeCast: value doesn\\'t fit in 8 bits");
              return uint8(value);
          }
      }
      pragma solidity ^0.5.0;
      import "./IERC20.sol";
      import "./Bytes.sol";
      library Utils {
          /// @notice Returns lesser of two values
          function minU32(uint32 a, uint32 b) internal pure returns (uint32) {
              return a < b ? a : b;
          }
          /// @notice Returns lesser of two values
          function minU64(uint64 a, uint64 b) internal pure returns (uint64) {
              return a < b ? a : b;
          }
          /// @notice Sends tokens
          /// @dev NOTE: this function handles tokens that have transfer function not strictly compatible with ERC20 standard
          /// @dev NOTE: call `transfer` to this token may return (bool) or nothing
          /// @param _token Token address
          /// @param _to Address of recipient
          /// @param _amount Amount of tokens to transfer
          /// @return bool flag indicating that transfer is successful
          function sendERC20(IERC20 _token, address _to, uint256 _amount) internal returns (bool) {
              (bool callSuccess, bytes memory callReturnValueEncoded) = address(_token).call(
                  abi.encodeWithSignature("transfer(address,uint256)", _to, _amount)
              );
              // `transfer` method may return (bool) or nothing.
              bool returnedSuccess = callReturnValueEncoded.length == 0 || abi.decode(callReturnValueEncoded, (bool));
              return callSuccess && returnedSuccess;
          }
          /// @notice Transfers token from one address to another
          /// @dev NOTE: this function handles tokens that have transfer function not strictly compatible with ERC20 standard
          /// @dev NOTE: call `transferFrom` to this token may return (bool) or nothing
          /// @param _token Token address
          /// @param _from Address of sender
          /// @param _to Address of recipient
          /// @param _amount Amount of tokens to transfer
          /// @return bool flag indicating that transfer is successful
          function transferFromERC20(IERC20 _token, address _from, address _to, uint256 _amount) internal returns (bool) {
              (bool callSuccess, bytes memory callReturnValueEncoded) = address(_token).call(
                  abi.encodeWithSignature("transferFrom(address,address,uint256)", _from, _to, _amount)
              );
              // `transferFrom` method may return (bool) or nothing.
              bool returnedSuccess = callReturnValueEncoded.length == 0 || abi.decode(callReturnValueEncoded, (bool));
              return callSuccess && returnedSuccess;
          }
          /// @notice Sends ETH
          /// @param _to Address of recipient
          /// @param _amount Amount of tokens to transfer
          /// @return bool flag indicating that transfer is successful
          function sendETHNoRevert(address payable _to, uint256 _amount) internal returns (bool) {
              // TODO: Use constant from Config
              uint256 ETH_WITHDRAWAL_GAS_LIMIT = 10000;
              (bool callSuccess, ) = _to.call.gas(ETH_WITHDRAWAL_GAS_LIMIT).value(_amount)("");
              return callSuccess;
          }
          /// @notice Recovers signer's address from ethereum signature for given message
          /// @param _signature 65 bytes concatenated. R (32) + S (32) + V (1)
          /// @param _message signed message.
          /// @return address of the signer
          function recoverAddressFromEthSignature(bytes memory _signature, bytes memory _message) internal pure returns (address) {
              require(_signature.length == 65, "ves10"); // incorrect signature length
              bytes32 signR;
              bytes32 signS;
              uint offset = 0;
              (offset, signR) = Bytes.readBytes32(_signature, offset);
              (offset, signS) = Bytes.readBytes32(_signature, offset);
              uint8 signV = uint8(_signature[offset]);
              return ecrecover(keccak256(_message), signV, signR, signS);
          }
      }
      pragma solidity ^0.5.0;
      import "./IERC20.sol";
      import "./Governance.sol";
      import "./Verifier.sol";
      import "./Operations.sol";
      /// @title zkSync storage contract
      /// @author Matter Labs
      contract Storage {
          /// @notice Flag indicates that upgrade preparation status is active
          /// @dev Will store false in case of not active upgrade mode
          bool public upgradePreparationActive;
          /// @notice Upgrade preparation activation timestamp (as seconds since unix epoch)
          /// @dev Will be equal to zero in case of not active upgrade mode
          uint public upgradePreparationActivationTime;
          /// @notice Verifier contract. Used to verify block proof and exit proof
          Verifier internal verifier;
          /// @notice Governance contract. Contains the governor (the owner) of whole system, validators list, possible tokens list
          Governance internal governance;
          struct BalanceToWithdraw {
              uint128 balanceToWithdraw;
              uint8 gasReserveValue; // gives user opportunity to fill storage slot with nonzero value
          }
          /// @notice Root-chain balances (per owner and token id, see packAddressAndTokenId) to withdraw
          mapping(bytes22 => BalanceToWithdraw) public balancesToWithdraw;
          /// @notice verified withdrawal pending to be executed.
          struct PendingWithdrawal {
              address to;
              uint16 tokenId;
          }
          
          /// @notice Verified but not executed withdrawals for addresses stored in here (key is pendingWithdrawal's index in pending withdrawals queue)
          mapping(uint32 => PendingWithdrawal) public pendingWithdrawals;
          uint32 public firstPendingWithdrawalIndex;
          uint32 public numberOfPendingWithdrawals;
          /// @notice Total number of verified blocks i.e. blocks[totalBlocksVerified] points at the latest verified block (block 0 is genesis)
          uint32 public totalBlocksVerified;
          /// @notice Total number of committed blocks i.e. blocks[totalBlocksCommitted] points at the latest committed block
          uint32 public totalBlocksCommitted;
          /// @notice Rollup block data (once per block)
          /// @member validator Block producer
          /// @member committedAtBlock ETH block number at which this block was committed
          /// @member cumulativeOnchainOperations Total number of operations in this and all previous blocks
          /// @member priorityOperations Total number of priority operations for this block
          /// @member commitment Hash of the block circuit commitment
          /// @member stateRoot New tree root hash
          ///
          /// Consider memory alignment when changing field order: https://solidity.readthedocs.io/en/v0.4.21/miscellaneous.html
          struct Block {
              uint32 committedAtBlock;
              uint64 priorityOperations;
              uint32 chunks;
              bytes32 withdrawalsDataHash; /// can be restricted to 16 bytes to reduce number of required storage slots
              bytes32 commitment;
              bytes32 stateRoot;
          }
          /// @notice Blocks by Franklin block id
          mapping(uint32 => Block) public blocks;
          /// @notice Onchain operations - operations processed inside rollup blocks
          /// @member opType Onchain operation type
          /// @member amount Amount used in the operation
          /// @member pubData Operation pubdata
          struct OnchainOperation {
              Operations.OpType opType;
              bytes pubData;
          }
          /// @notice Flag indicates that a user has exited certain token balance (per account id and tokenId)
          mapping(uint32 => mapping(uint16 => bool)) public exited;
          /// @notice Flag indicates that exodus (mass exit) mode is triggered
          /// @notice Once it was raised, it can not be cleared again, and all users must exit
          bool public exodusMode;
          /// @notice User authenticated fact hashes for some nonce.
          mapping(address => mapping(uint32 => bytes32)) public authFacts;
          /// @notice Priority Operation container
          /// @member opType Priority operation type
          /// @member pubData Priority operation public data
          /// @member expirationBlock Expiration block number (ETH block) for this request (must be satisfied before)
          struct PriorityOperation {
              Operations.OpType opType;
              bytes pubData;
              uint256 expirationBlock;
          }
          /// @notice Priority Requests mapping (request id - operation)
          /// @dev Contains op type, pubdata and expiration block of unsatisfied requests.
          /// @dev Numbers are in order of requests receiving
          mapping(uint64 => PriorityOperation) public priorityRequests;
          /// @notice First open priority request id
          uint64 public firstPriorityRequestId;
          /// @notice Total number of requests
          uint64 public totalOpenPriorityRequests;
          /// @notice Total number of committed requests.
          /// @dev Used in checks: if the request matches the operation on Rollup contract and if provided number of requests is not too big
          uint64 public totalCommittedPriorityRequests;
          /// @notice Packs address and token id into single word to use as a key in balances mapping
          function packAddressAndTokenId(address _address, uint16 _tokenId) internal pure returns (bytes22) {
              return bytes22((uint176(_address) | (uint176(_tokenId) << 160)));
          }
          /// @notice Gets value from balancesToWithdraw
          function getBalanceToWithdraw(address _address, uint16 _tokenId) public view returns (uint128) {
              return balancesToWithdraw[packAddressAndTokenId(_address, _tokenId)].balanceToWithdraw;
          }
      }
      pragma solidity ^0.5.0;
      /// @title zkSync configuration constants
      /// @author Matter Labs
      contract Config {
          /// @notice ERC20 token withdrawal gas limit, used only for complete withdrawals
          uint256 constant ERC20_WITHDRAWAL_GAS_LIMIT = 250000;
          /// @notice ETH token withdrawal gas limit, used only for complete withdrawals
          uint256 constant ETH_WITHDRAWAL_GAS_LIMIT = 10000;
          /// @notice Bytes in one chunk
          uint8 constant CHUNK_BYTES = 9;
          /// @notice zkSync address length
          uint8 constant ADDRESS_BYTES = 20;
          uint8 constant PUBKEY_HASH_BYTES = 20;
          /// @notice Public key bytes length
          uint8 constant PUBKEY_BYTES = 32;
          /// @notice Ethereum signature r/s bytes length
          uint8 constant ETH_SIGN_RS_BYTES = 32;
          /// @notice Success flag bytes length
          uint8 constant SUCCESS_FLAG_BYTES = 1;
          /// @notice Max amount of tokens registered in the network (excluding ETH, which is hardcoded as tokenId = 0)
          uint16 constant MAX_AMOUNT_OF_REGISTERED_TOKENS = 128 - 1;
          /// @notice Max account id that could be registered in the network
          uint32 constant MAX_ACCOUNT_ID = (2 ** 24) - 1;
          /// @notice Expected average period of block creation
          uint256 constant BLOCK_PERIOD = 15 seconds;
          /// @notice ETH blocks verification expectation
          /// Blocks can be reverted if they are not verified for at least EXPECT_VERIFICATION_IN.
          /// If set to 0 validator can revert blocks at any time.
          uint256 constant EXPECT_VERIFICATION_IN = 0 hours / BLOCK_PERIOD;
          uint256 constant NOOP_BYTES = 1 * CHUNK_BYTES;
          uint256 constant DEPOSIT_BYTES = 6 * CHUNK_BYTES;
          uint256 constant TRANSFER_TO_NEW_BYTES = 6 * CHUNK_BYTES;
          uint256 constant PARTIAL_EXIT_BYTES = 6 * CHUNK_BYTES;
          uint256 constant TRANSFER_BYTES = 2 * CHUNK_BYTES;
          uint256 constant FORCED_EXIT_BYTES = 6 * CHUNK_BYTES;
          /// @notice Full exit operation length
          uint256 constant FULL_EXIT_BYTES = 6 * CHUNK_BYTES;
          /// @notice OnchainWithdrawal data length
          uint256 constant ONCHAIN_WITHDRAWAL_BYTES = 1 + 20 + 2 + 16; // (uint8 addToPendingWithdrawalsQueue, address _to, uint16 _tokenId, uint128 _amount)
          /// @notice ChangePubKey operation length
          uint256 constant CHANGE_PUBKEY_BYTES = 6 * CHUNK_BYTES;
          /// @notice Expiration delta for priority request to be satisfied (in seconds)
          /// NOTE: Priority expiration should be > (EXPECT_VERIFICATION_IN * BLOCK_PERIOD), otherwise incorrect block with priority op could not be reverted.
          uint256 constant PRIORITY_EXPIRATION_PERIOD = 3 days;
          /// @notice Expiration delta for priority request to be satisfied (in ETH blocks)
          uint256 constant PRIORITY_EXPIRATION = PRIORITY_EXPIRATION_PERIOD / BLOCK_PERIOD;
          /// @notice Maximum number of priority request to clear during verifying the block
          /// @dev Cause deleting storage slots cost 5k gas per each slot it's unprofitable to clear too many slots
          /// @dev Value based on the assumption of ~750k gas cost of verifying and 5 used storage slots per PriorityOperation structure
          uint64 constant MAX_PRIORITY_REQUESTS_TO_DELETE_IN_VERIFY = 6;
          /// @notice Reserved time for users to send full exit priority operation in case of an upgrade (in seconds)
          uint constant MASS_FULL_EXIT_PERIOD = 3 days;
          /// @notice Reserved time for users to withdraw funds from full exit priority operation in case of an upgrade (in seconds)
          uint constant TIME_TO_WITHDRAW_FUNDS_FROM_FULL_EXIT = 2 days;
          /// @notice Notice period before activation preparation status of upgrade mode (in seconds)
          // 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.
          uint constant UPGRADE_NOTICE_PERIOD = MASS_FULL_EXIT_PERIOD + PRIORITY_EXPIRATION_PERIOD + TIME_TO_WITHDRAW_FUNDS_FROM_FULL_EXIT;
      }
      pragma solidity ^0.5.0;
      import "./Upgradeable.sol";
      import "./Operations.sol";
      /// @title zkSync events
      /// @author Matter Labs
      interface Events {
          /// @notice Event emitted when a block is committed
          event BlockCommit(uint32 indexed blockNumber);
          /// @notice Event emitted when a block is verified
          event BlockVerification(uint32 indexed blockNumber);
          /// @notice Event emitted when user send a transaction to withdraw her funds from onchain balance
          event OnchainWithdrawal(
              address indexed owner,
              uint16 indexed tokenId,
              uint128 amount
          );
          /// @notice Event emitted when user send a transaction to deposit her funds
          event OnchainDeposit(
              address indexed sender,
              uint16 indexed tokenId,
              uint128 amount,
              address indexed owner
          );
          /// @notice Event emitted when user sends a authentication fact (e.g. pub-key hash)
          event FactAuth(
              address indexed sender,
              uint32 nonce,
              bytes fact
          );
          /// @notice Event emitted when blocks are reverted
          event BlocksRevert(
              uint32 totalBlocksVerified,
              uint32 totalBlocksCommitted
          );
          /// @notice Exodus mode entered event
          event ExodusMode();
          /// @notice New priority request event. Emitted when a request is placed into mapping
          event NewPriorityRequest(
              address sender,
              uint64 serialId,
              Operations.OpType opType,
              bytes pubData,
              uint256 expirationBlock
          );
          /// @notice Deposit committed event.
          event DepositCommit(
              uint32 indexed zkSyncBlockId,
              uint32 indexed accountId,
              address owner,
              uint16 indexed tokenId,
              uint128 amount
          );
          /// @notice Full exit committed event.
          event FullExitCommit(
              uint32 indexed zkSyncBlockId,
              uint32 indexed accountId,
              address owner,
              uint16 indexed tokenId,
              uint128 amount
          );
          /// @notice Pending withdrawals index range that were added in the verifyBlock operation.
          /// NOTE: processed indexes in the queue map are [queueStartIndex, queueEndIndex)
          event PendingWithdrawalsAdd(
              uint32 queueStartIndex,
              uint32 queueEndIndex
          );
          /// @notice Pending withdrawals index range that were executed in the completeWithdrawals operation.
          /// NOTE: processed indexes in the queue map are [queueStartIndex, queueEndIndex)
          event PendingWithdrawalsComplete(
              uint32 queueStartIndex,
              uint32 queueEndIndex
          );
      }
      /// @title Upgrade events
      /// @author Matter Labs
      interface UpgradeEvents {
          /// @notice Event emitted when new upgradeable contract is added to upgrade gatekeeper's list of managed contracts
          event NewUpgradable(
              uint indexed versionId,
              address indexed upgradeable
          );
          /// @notice Upgrade mode enter event
          event NoticePeriodStart(
              uint indexed versionId,
              address[] newTargets,
              uint noticePeriod // notice period (in seconds)
          );
          /// @notice Upgrade mode cancel event
          event UpgradeCancel(
              uint indexed versionId
          );
          /// @notice Upgrade mode preparation status event
          event PreparationStart(
              uint indexed versionId
          );
          /// @notice Upgrade mode complete event
          event UpgradeComplete(
              uint indexed versionId,
              address[] newTargets
          );
      }
      pragma solidity ^0.5.0;
      // Functions named bytesToX, except bytesToBytes20, where X is some type of size N < 32 (size of one word)
      // implements the following algorithm:
      // f(bytes memory input, uint offset) -> X out
      // where byte representation of out is N bytes from input at the given offset
      // 1) We compute memory location of the word W such that last N bytes of W is input[offset..offset+N]
      // W_address = input + 32 (skip stored length of bytes) + offset - (32 - N) == input + offset + N
      // 2) We load W from memory into out, last N bytes of W are placed into out
      library Bytes {
          function toBytesFromUInt16(uint16 self) internal pure returns (bytes memory _bts) {
              return toBytesFromUIntTruncated(uint(self), 2);
          }
          function toBytesFromUInt24(uint24 self) internal pure returns (bytes memory _bts) {
              return toBytesFromUIntTruncated(uint(self), 3);
          }
          function toBytesFromUInt32(uint32 self) internal pure returns (bytes memory _bts) {
              return toBytesFromUIntTruncated(uint(self), 4);
          }
          function toBytesFromUInt128(uint128 self) internal pure returns (bytes memory _bts) {
              return toBytesFromUIntTruncated(uint(self), 16);
          }
          // Copies 'len' lower bytes from 'self' into a new 'bytes memory'.
          // Returns the newly created 'bytes memory'. The returned bytes will be of length 'len'.
          function toBytesFromUIntTruncated(uint self, uint8 byteLength) private pure returns (bytes memory bts) {
              require(byteLength <= 32, "bt211");
              bts = new bytes(byteLength);
              // Even though the bytes will allocate a full word, we don't want
              // any potential garbage bytes in there.
              uint data = self << ((32 - byteLength) * 8);
              assembly {
                  mstore(add(bts, /*BYTES_HEADER_SIZE*/32), data)
              }
          }
          // Copies 'self' into a new 'bytes memory'.
          // Returns the newly created 'bytes memory'. The returned bytes will be of length '20'.
          function toBytesFromAddress(address self) internal pure returns (bytes memory bts) {
              bts = toBytesFromUIntTruncated(uint(self), 20);
          }
          // See comment at the top of this file for explanation of how this function works.
          // NOTE: theoretically possible overflow of (_start + 20)
          function bytesToAddress(bytes memory self, uint256 _start) internal pure returns (address addr) {
              uint256 offset = _start + 20;
              require(self.length >= offset, "bta11");
              assembly {
                  addr := mload(add(self, offset))
              }
          }
          // Reasoning about why this function works is similar to that of other similar functions, except NOTE below.
          // NOTE: that bytes1..32 is stored in the beginning of the word unlike other primitive types
          // NOTE: theoretically possible overflow of (_start + 20)
          function bytesToBytes20(bytes memory self, uint256 _start) internal pure returns (bytes20 r) {
              require(self.length >= (_start + 20), "btb20");
              assembly {
                  r := mload(add(add(self, 0x20), _start))
              }
          }
          // See comment at the top of this file for explanation of how this function works.
          // NOTE: theoretically possible overflow of (_start + 0x2)
          function bytesToUInt16(bytes memory _bytes, uint256 _start) internal pure returns (uint16 r) {
              uint256 offset = _start + 0x2;
              require(_bytes.length >= offset, "btu02");
              assembly {
                  r := mload(add(_bytes, offset))
              }
          }
          // See comment at the top of this file for explanation of how this function works.
          // NOTE: theoretically possible overflow of (_start + 0x3)
          function bytesToUInt24(bytes memory _bytes, uint256 _start) internal pure returns (uint24 r) {
              uint256 offset = _start + 0x3;
              require(_bytes.length >= offset, "btu03");
              assembly {
                  r := mload(add(_bytes, offset))
              }
          }
          // NOTE: theoretically possible overflow of (_start + 0x4)
          function bytesToUInt32(bytes memory _bytes, uint256 _start) internal pure returns (uint32 r) {
              uint256 offset = _start + 0x4;
              require(_bytes.length >= offset, "btu04");
              assembly {
                  r := mload(add(_bytes, offset))
              }
          }
          // NOTE: theoretically possible overflow of (_start + 0x10)
          function bytesToUInt128(bytes memory _bytes, uint256 _start) internal pure returns (uint128 r) {
              uint256 offset = _start + 0x10;
              require(_bytes.length >= offset, "btu16");
              assembly {
                  r := mload(add(_bytes, offset))
              }
          }
          // See comment at the top of this file for explanation of how this function works.
          // NOTE: theoretically possible overflow of (_start + 0x14)
          function bytesToUInt160(bytes memory _bytes, uint256 _start) internal pure returns (uint160 r) {
              uint256 offset = _start + 0x14;
              require(_bytes.length >= offset, "btu20");
              assembly {
                  r := mload(add(_bytes, offset))
              }
          }
          // NOTE: theoretically possible overflow of (_start + 0x20)
          function bytesToBytes32(bytes memory  _bytes, uint256 _start) internal pure returns (bytes32 r) {
              uint256 offset = _start + 0x20;
              require(_bytes.length >= offset, "btb32");
              assembly {
                  r := mload(add(_bytes, offset))
              }
          }
          // Original source code: https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol#L228
          // Get slice from bytes arrays
          // Returns the newly created 'bytes memory'
          // NOTE: theoretically possible overflow of (_start + _length)
          function slice(
              bytes memory _bytes,
              uint _start,
              uint _length
          )
              internal
              pure
              returns (bytes memory)
          {
              require(_bytes.length >= (_start + _length), "bse11"); // bytes length is less then start byte + length bytes
              bytes memory tempBytes = new bytes(_length);
              if (_length != 0) {
                  // TODO: Review this thoroughly.
                  assembly {
                      let slice_curr := add(tempBytes, 0x20)
                      let slice_end := add(slice_curr, _length)
                      for {
                          let array_current := add(_bytes, add(_start, 0x20))
                      } lt(slice_curr, slice_end) {
                          slice_curr := add(slice_curr, 0x20)
                          array_current := add(array_current, 0x20)
                      } {
                          mstore(slice_curr, mload(array_current))
                      }
                  }
              }
              return tempBytes;
          }
          /// Reads byte stream
          /// @return new_offset - offset + amount of bytes read
          /// @return data - actually read data
          // NOTE: theoretically possible overflow of (_offset + _length)
          function read(bytes memory _data, uint _offset, uint _length) internal pure returns (uint new_offset, bytes memory data) {
              data = slice(_data, _offset, _length);
              new_offset = _offset + _length;
          }
          // NOTE: theoretically possible overflow of (_offset + 1)
          function readBool(bytes memory _data, uint _offset) internal pure returns (uint new_offset, bool r) {
              new_offset = _offset + 1;
              r = uint8(_data[_offset]) != 0;
          }
          // NOTE: theoretically possible overflow of (_offset + 1)
          function readUint8(bytes memory _data, uint _offset) internal pure returns (uint new_offset, uint8 r) {
              new_offset = _offset + 1;
              r = uint8(_data[_offset]);
          }
          // NOTE: theoretically possible overflow of (_offset + 2)
          function readUInt16(bytes memory _data, uint _offset) internal pure returns (uint new_offset, uint16 r) {
              new_offset = _offset + 2;
              r = bytesToUInt16(_data, _offset);
          }
          // NOTE: theoretically possible overflow of (_offset + 3)
          function readUInt24(bytes memory _data, uint _offset) internal pure returns (uint new_offset, uint24 r) {
              new_offset = _offset + 3;
              r = bytesToUInt24(_data, _offset);
          }
          // NOTE: theoretically possible overflow of (_offset + 4)
          function readUInt32(bytes memory _data, uint _offset) internal pure returns (uint new_offset, uint32 r) {
              new_offset = _offset + 4;
              r = bytesToUInt32(_data, _offset);
          }
          // NOTE: theoretically possible overflow of (_offset + 16)
          function readUInt128(bytes memory _data, uint _offset) internal pure returns (uint new_offset, uint128 r) {
              new_offset = _offset + 16;
              r = bytesToUInt128(_data, _offset);
          }
          // NOTE: theoretically possible overflow of (_offset + 20)
          function readUInt160(bytes memory _data, uint _offset) internal pure returns (uint new_offset, uint160 r) {
              new_offset = _offset + 20;
              r = bytesToUInt160(_data, _offset);
          }
          // NOTE: theoretically possible overflow of (_offset + 20)
          function readAddress(bytes memory _data, uint _offset) internal pure returns (uint new_offset, address r) {
              new_offset = _offset + 20;
              r = bytesToAddress(_data, _offset);
          }
          // NOTE: theoretically possible overflow of (_offset + 20)
          function readBytes20(bytes memory _data, uint _offset) internal pure returns (uint new_offset, bytes20 r) {
              new_offset = _offset + 20;
              r = bytesToBytes20(_data, _offset);
          }
          // NOTE: theoretically possible overflow of (_offset + 32)
          function readBytes32(bytes memory _data, uint _offset) internal pure returns (uint new_offset, bytes32 r) {
              new_offset = _offset + 32;
              r = bytesToBytes32(_data, _offset);
          }
          // Helper function for hex conversion.
          function halfByteToHex(byte _byte) internal pure returns (byte _hexByte) {
              require(uint8(_byte) < 0x10, "hbh11");  // half byte's value is out of 0..15 range.
              // "FEDCBA9876543210" ASCII-encoded, shifted and automatically truncated.
              return byte (uint8 (0x66656463626139383736353433323130 >> (uint8 (_byte) * 8)));
          }
          // Convert bytes to ASCII hex representation
          function bytesToHexASCIIBytes(bytes memory  _input) internal pure returns (bytes memory _output) {
              bytes memory outStringBytes = new bytes(_input.length * 2);
              // code in `assembly` construction is equivalent of the next code:
              // for (uint i = 0; i < _input.length; ++i) {
              //     outStringBytes[i*2] = halfByteToHex(_input[i] >> 4);
              //     outStringBytes[i*2+1] = halfByteToHex(_input[i] & 0x0f);
              // }
              assembly {
                  let input_curr := add(_input, 0x20)
                  let input_end := add(input_curr, mload(_input))
                  for {
                      let out_curr := add(outStringBytes, 0x20)
                  } lt(input_curr, input_end) {
                      input_curr := add(input_curr, 0x01)
                      out_curr := add(out_curr, 0x02)
                  } {
                      let curr_input_byte := shr(0xf8, mload(input_curr))
                      // here outStringByte from each half of input byte calculates by the next:
                      //
                      // "FEDCBA9876543210" ASCII-encoded, shifted and automatically truncated.
                      // outStringByte = byte (uint8 (0x66656463626139383736353433323130 >> (uint8 (_byteHalf) * 8)))
                      mstore(out_curr,            shl(0xf8, shr(mul(shr(0x04, curr_input_byte), 0x08), 0x66656463626139383736353433323130)))
                      mstore(add(out_curr, 0x01), shl(0xf8, shr(mul(and(0x0f, curr_input_byte), 0x08), 0x66656463626139383736353433323130)))
                  }
              }
              return outStringBytes;
          }
          /// Trim bytes into single word
          function trim(bytes memory _data, uint _new_length) internal pure returns (uint r) {
              require(_new_length <= 0x20, "trm10");  // new_length is longer than word
              require(_data.length >= _new_length, "trm11");  // data is to short
              uint a;
              assembly {
                  a := mload(add(_data, 0x20)) // load bytes into uint256
              }
              return a >> ((0x20 - _new_length) * 8);
          }
      }
      pragma solidity ^0.5.0;
      import "./Bytes.sol";
      /// @title zkSync operations tools
      library Operations {
          // Circuit ops and their pubdata (chunks * bytes)
          /// @notice zkSync circuit operation type
          enum OpType {
              Noop,
              Deposit,
              TransferToNew,
              PartialExit,
              _CloseAccount, // used for correct op id offset
              Transfer,
              FullExit,
              ChangePubKey,
              ForcedExit
          }
          // Byte lengths
          uint8 constant TOKEN_BYTES = 2;
          uint8 constant PUBKEY_BYTES = 32;
          uint8 constant NONCE_BYTES = 4;
          uint8 constant PUBKEY_HASH_BYTES = 20;
          uint8 constant ADDRESS_BYTES = 20;
          /// @notice Packed fee bytes lengths
          uint8 constant FEE_BYTES = 2;
          /// @notice zkSync account id bytes lengths
          uint8 constant ACCOUNT_ID_BYTES = 4;
          uint8 constant AMOUNT_BYTES = 16;
          /// @notice Signature (for example full exit signature) bytes length
          uint8 constant SIGNATURE_BYTES = 64;
          // Deposit pubdata
          struct Deposit {
              uint32 accountId;
              uint16 tokenId;
              uint128 amount;
              address owner;
          }
          uint public constant PACKED_DEPOSIT_PUBDATA_BYTES = 
              ACCOUNT_ID_BYTES + TOKEN_BYTES + AMOUNT_BYTES + ADDRESS_BYTES;
          /// Deserialize deposit pubdata
          function readDepositPubdata(bytes memory _data) internal pure
              returns (Deposit memory parsed)
          {
              // NOTE: there is no check that variable sizes are same as constants (i.e. TOKEN_BYTES), fix if possible.
              uint offset = 0;
              (offset, parsed.accountId) = Bytes.readUInt32(_data, offset); // accountId
              (offset, parsed.tokenId) = Bytes.readUInt16(_data, offset);   // tokenId
              (offset, parsed.amount) = Bytes.readUInt128(_data, offset);   // amount
              (offset, parsed.owner) = Bytes.readAddress(_data, offset);    // owner
              require(offset == PACKED_DEPOSIT_PUBDATA_BYTES, "rdp10"); // reading invalid deposit pubdata size
          }
          /// Serialize deposit pubdata
          function writeDepositPubdata(Deposit memory op) internal pure returns (bytes memory buf) {
              buf = abi.encodePacked(
                  bytes4(0),   // accountId (ignored) (update when ACCOUNT_ID_BYTES is changed)
                  op.tokenId,  // tokenId
                  op.amount,   // amount
                  op.owner     // owner
              );
          }
          /// @notice Check that deposit pubdata from request and block matches
          function depositPubdataMatch(bytes memory _lhs, bytes memory _rhs) internal pure returns (bool) {
              // We must ignore `accountId` because it is present in block pubdata but not in priority queue
              bytes memory lhs_trimmed = Bytes.slice(_lhs, ACCOUNT_ID_BYTES, PACKED_DEPOSIT_PUBDATA_BYTES - ACCOUNT_ID_BYTES);
              bytes memory rhs_trimmed = Bytes.slice(_rhs, ACCOUNT_ID_BYTES, PACKED_DEPOSIT_PUBDATA_BYTES - ACCOUNT_ID_BYTES);
              return keccak256(lhs_trimmed) == keccak256(rhs_trimmed);
          }
          // FullExit pubdata
          struct FullExit {
              uint32 accountId;
              address owner;
              uint16 tokenId;
              uint128 amount;
          }
          uint public constant PACKED_FULL_EXIT_PUBDATA_BYTES = 
              ACCOUNT_ID_BYTES + ADDRESS_BYTES + TOKEN_BYTES + AMOUNT_BYTES;
          function readFullExitPubdata(bytes memory _data) internal pure
              returns (FullExit memory parsed)
          {
              // NOTE: there is no check that variable sizes are same as constants (i.e. TOKEN_BYTES), fix if possible.
              uint offset = 0;
              (offset, parsed.accountId) = Bytes.readUInt32(_data, offset);      // accountId
              (offset, parsed.owner) = Bytes.readAddress(_data, offset);         // owner
              (offset, parsed.tokenId) = Bytes.readUInt16(_data, offset);        // tokenId
              (offset, parsed.amount) = Bytes.readUInt128(_data, offset);        // amount
              require(offset == PACKED_FULL_EXIT_PUBDATA_BYTES, "rfp10"); // reading invalid full exit pubdata size
          }
          function writeFullExitPubdata(FullExit memory op) internal pure returns (bytes memory buf) {
              buf = abi.encodePacked(
                  op.accountId,  // accountId
                  op.owner,      // owner
                  op.tokenId,    // tokenId
                  op.amount      // amount
              );
          }
          /// @notice Check that full exit pubdata from request and block matches
          function fullExitPubdataMatch(bytes memory _lhs, bytes memory _rhs) internal pure returns (bool) {
              // `amount` is ignored because it is present in block pubdata but not in priority queue
              uint lhs = Bytes.trim(_lhs, PACKED_FULL_EXIT_PUBDATA_BYTES - AMOUNT_BYTES);
              uint rhs = Bytes.trim(_rhs, PACKED_FULL_EXIT_PUBDATA_BYTES - AMOUNT_BYTES);
              return lhs == rhs;
          }
          // PartialExit pubdata
          
          struct PartialExit {
              //uint32 accountId; -- present in pubdata, ignored at serialization
              uint16 tokenId;
              uint128 amount;
              //uint16 fee; -- present in pubdata, ignored at serialization
              address owner;
          }
          function readPartialExitPubdata(bytes memory _data, uint _offset) internal pure
              returns (PartialExit memory parsed)
          {
              // NOTE: there is no check that variable sizes are same as constants (i.e. TOKEN_BYTES), fix if possible.
              uint offset = _offset + ACCOUNT_ID_BYTES;                   // accountId (ignored)
              (offset, parsed.tokenId) = Bytes.readUInt16(_data, offset); // tokenId
              (offset, parsed.amount) = Bytes.readUInt128(_data, offset); // amount
              offset += FEE_BYTES;                                        // fee (ignored)
              (offset, parsed.owner) = Bytes.readAddress(_data, offset);  // owner
          }
          function writePartialExitPubdata(PartialExit memory op) internal pure returns (bytes memory buf) {
              buf = abi.encodePacked(
                  bytes4(0),  // accountId (ignored) (update when ACCOUNT_ID_BYTES is changed)
                  op.tokenId, // tokenId
                  op.amount,  // amount
                  bytes2(0),  // fee (ignored)  (update when FEE_BYTES is changed)
                  op.owner    // owner
              );
          }
          // ForcedExit pubdata
          
          struct ForcedExit {
              //uint32 initiatorAccountId; -- present in pubdata, ignored at serialization
              //uint32 targetAccountId; -- present in pubdata, ignored at serialization
              uint16 tokenId;
              uint128 amount;
              //uint16 fee; -- present in pubdata, ignored at serialization
              address target;
          }
          function readForcedExitPubdata(bytes memory _data, uint _offset) internal pure
              returns (ForcedExit memory parsed)
          {
              // NOTE: there is no check that variable sizes are same as constants (i.e. TOKEN_BYTES), fix if possible.
              uint offset = _offset + ACCOUNT_ID_BYTES * 2;               // initiatorAccountId + targetAccountId (ignored)
              (offset, parsed.tokenId) = Bytes.readUInt16(_data, offset); // tokenId
              (offset, parsed.amount) = Bytes.readUInt128(_data, offset); // amount
              offset += FEE_BYTES;                                        // fee (ignored)
              (offset, parsed.target) = Bytes.readAddress(_data, offset); // target
          }
          function writeForcedExitPubdata(ForcedExit memory op) internal pure returns (bytes memory buf) {
              buf = abi.encodePacked(
                  bytes4(0),  // initiatorAccountId (ignored) (update when ACCOUNT_ID_BYTES is changed)
                  bytes4(0),  // targetAccountId (ignored) (update when ACCOUNT_ID_BYTES is changed)
                  op.tokenId, // tokenId
                  op.amount,  // amount
                  bytes2(0),  // fee (ignored)  (update when FEE_BYTES is changed)
                  op.target   // target
              );
          }
          // ChangePubKey
          struct ChangePubKey {
              uint32 accountId;
              bytes20 pubKeyHash;
              address owner;
              uint32 nonce;
              //uint16 tokenId; -- present in pubdata, ignored at serialization
              //uint16 fee; -- present in pubdata, ignored at serialization
          }
          function readChangePubKeyPubdata(bytes memory _data, uint _offset) internal pure
              returns (ChangePubKey memory parsed)
          {
              uint offset = _offset;
              (offset, parsed.accountId) = Bytes.readUInt32(_data, offset);                // accountId
              (offset, parsed.pubKeyHash) = Bytes.readBytes20(_data, offset);              // pubKeyHash
              (offset, parsed.owner) = Bytes.readAddress(_data, offset);                   // owner
              (offset, parsed.nonce) = Bytes.readUInt32(_data, offset);                    // nonce
          }
          // Withdrawal data process
          function readWithdrawalData(bytes memory _data, uint _offset) internal pure
              returns (bool _addToPendingWithdrawalsQueue, address _to, uint16 _tokenId, uint128 _amount)
          {
              uint offset = _offset;
              (offset, _addToPendingWithdrawalsQueue) = Bytes.readBool(_data, offset);
              (offset, _to) = Bytes.readAddress(_data, offset);
              (offset, _tokenId) = Bytes.readUInt16(_data, offset);
              (offset, _amount) = Bytes.readUInt128(_data, offset);
          }
      }
      pragma solidity ^0.5.0;
      /// @title Interface of the upgradeable master contract (defines notice period duration and allows finish upgrade during preparation of it)
      /// @author Matter Labs
      interface UpgradeableMaster {
          /// @notice Notice period before activation preparation status of upgrade mode
          function getNoticePeriod() external returns (uint);
          /// @notice Notifies contract that notice period started
          function upgradeNoticePeriodStarted() external;
          /// @notice Notifies contract that upgrade preparation status is activated
          function upgradePreparationStarted() external;
          /// @notice Notifies contract that upgrade canceled
          function upgradeCanceled() external;
          /// @notice Notifies contract that upgrade finishes
          function upgradeFinishes() external;
          /// @notice Checks that contract is ready for upgrade
          /// @return bool flag indicating that contract is ready for upgrade
          function isReadyForUpgrade() external returns (bool);
      }
      pragma solidity ^0.5.0;
      /**
       * @dev Interface of the ERC20 standard as defined in the EIP. Does not include
       * the optional functions; to access them see {ERC20Detailed}.
       */
      interface IERC20 {
          /**
           * @dev Returns the amount of tokens in existence.
           */
          function totalSupply() external view returns (uint256);
          /**
           * @dev Returns the amount of tokens owned by `account`.
           */
          function balanceOf(address account) external view returns (uint256);
          /**
           * @dev Moves `amount` tokens from the caller's account to `recipient`.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transfer(address recipient, uint256 amount) external returns (bool);
          /**
           * @dev Returns the remaining number of tokens that `spender` will be
           * allowed to spend on behalf of `owner` through {transferFrom}. This is
           * zero by default.
           *
           * This value changes when {approve} or {transferFrom} are called.
           */
          function allowance(address owner, address spender) external view returns (uint256);
          /**
           * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * IMPORTANT: Beware that changing an allowance with this method brings the risk
           * that someone may use both the old and the new allowance by unfortunate
           * transaction ordering. One possible solution to mitigate this race
           * condition is to first reduce the spender's allowance to 0 and set the
           * desired value afterwards:
           * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
           *
           * Emits an {Approval} event.
           */
          function approve(address spender, uint256 amount) external returns (bool);
          /**
           * @dev Moves `amount` tokens from `sender` to `recipient` using the
           * allowance mechanism. `amount` is then deducted from the caller's
           * allowance.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
          /**
           * @dev Emitted when `value` tokens are moved from one account (`from`) to
           * another (`to`).
           *
           * Note that `value` may be zero.
           */
          event Transfer(address indexed from, address indexed to, uint256 value);
          /**
           * @dev Emitted when the allowance of a `spender` for an `owner` is set by
           * a call to {approve}. `value` is the new allowance.
           */
          event Approval(address indexed owner, address indexed spender, uint256 value);
      }
      pragma solidity ^0.5.0;
      import "./Config.sol";
      /// @title Governance Contract
      /// @author Matter Labs
      contract Governance is Config {
          /// @notice Token added to Franklin net
          event NewToken(
              address indexed token,
              uint16 indexed tokenId
          );
          /// @notice Governor changed
          event NewGovernor(
              address newGovernor
          );
          /// @notice Validator's status changed
          event ValidatorStatusUpdate(
              address indexed validatorAddress,
              bool isActive
          );
          /// @notice Address which will exercise governance over the network i.e. add tokens, change validator set, conduct upgrades
          address public networkGovernor;
          /// @notice Total number of ERC20 tokens registered in the network (excluding ETH, which is hardcoded as tokenId = 0)
          uint16 public totalTokens;
          /// @notice List of registered tokens by tokenId
          mapping(uint16 => address) public tokenAddresses;
          /// @notice List of registered tokens by address
          mapping(address => uint16) public tokenIds;
          /// @notice List of permitted validators
          mapping(address => bool) public validators;
          constructor() public {}
          /// @notice Governance contract initialization. Can be external because Proxy contract intercepts illegal calls of this function.
          /// @param initializationParameters Encoded representation of initialization parameters:
          ///     _networkGovernor The address of network governor
          function initialize(bytes calldata initializationParameters) external {
              address _networkGovernor = abi.decode(initializationParameters, (address));
              networkGovernor = _networkGovernor;
          }
          /// @notice Governance contract upgrade. Can be external because Proxy contract intercepts illegal calls of this function.
          /// @param upgradeParameters Encoded representation of upgrade parameters
          function upgrade(bytes calldata upgradeParameters) external {}
          /// @notice Change current governor
          /// @param _newGovernor Address of the new governor
          function changeGovernor(address _newGovernor) external {
              requireGovernor(msg.sender);
              if (networkGovernor != _newGovernor) {
                  networkGovernor = _newGovernor;
                  emit NewGovernor(_newGovernor);
              }
          }
          /// @notice Add token to the list of networks tokens
          /// @param _token Token address
          function addToken(address _token) external {
              requireGovernor(msg.sender);
              require(tokenIds[_token] == 0, "gan11"); // token exists
              require(totalTokens < MAX_AMOUNT_OF_REGISTERED_TOKENS, "gan12"); // no free identifiers for tokens
              totalTokens++;
              uint16 newTokenId = totalTokens; // it is not `totalTokens - 1` because tokenId = 0 is reserved for eth
              tokenAddresses[newTokenId] = _token;
              tokenIds[_token] = newTokenId;
              emit NewToken(_token, newTokenId);
          }
          /// @notice Change validator status (active or not active)
          /// @param _validator Validator address
          /// @param _active Active flag
          function setValidator(address _validator, bool _active) external {
              requireGovernor(msg.sender);
              if (validators[_validator] != _active) {
                  validators[_validator] = _active;
                  emit ValidatorStatusUpdate(_validator, _active);
              }
          }
          /// @notice Check if specified address is is governor
          /// @param _address Address to check
          function requireGovernor(address _address) public view {
              require(_address == networkGovernor, "grr11"); // only by governor
          }
          /// @notice Checks if validator is active
          /// @param _address Validator address
          function requireActiveValidator(address _address) external view {
              require(validators[_address], "grr21"); // validator is not active
          }
          /// @notice Validate token id (must be less than or equal to total tokens amount)
          /// @param _tokenId Token id
          /// @return bool flag that indicates if token id is less than or equal to total tokens amount
          function isValidTokenId(uint16 _tokenId) external view returns (bool) {
              return _tokenId <= totalTokens;
          }
          /// @notice Validate token address
          /// @param _tokenAddr Token address
          /// @return tokens id
          function validateTokenAddress(address _tokenAddr) external view returns (uint16) {
              uint16 tokenId = tokenIds[_tokenAddr];
              require(tokenId != 0, "gvs11"); // 0 is not a valid token
              return tokenId;
          }
      }
      pragma solidity ^0.5.0;
      import "./KeysWithPlonkVerifier.sol";
      // Hardcoded constants to avoid accessing store
      contract Verifier is KeysWithPlonkVerifier {
          bool constant DUMMY_VERIFIER = false;
          function initialize(bytes calldata) external {
          }
          /// @notice Verifier contract upgrade. Can be external because Proxy contract intercepts illegal calls of this function.
          /// @param upgradeParameters Encoded representation of upgrade parameters
          function upgrade(bytes calldata upgradeParameters) external {}
          function isBlockSizeSupported(uint32 _size) public pure returns (bool) {
              if (DUMMY_VERIFIER) {
                  return true;
              } else {
                  return isBlockSizeSupportedInternal(_size);
              }
          }
          function verifyBlockProof(
              uint256[] calldata _proof,
              bytes32 _commitment,
              uint32 _chunks
          ) external view returns (bool) {
              if (DUMMY_VERIFIER) {
                  uint oldGasValue = gasleft();
                  uint tmp;
                  while (gasleft() + 470000 > oldGasValue) {
                      tmp += 1;
                  }
                  return true;
              }
              uint256[] memory inputs = new uint256[](1);
              uint256 mask = (~uint256(0)) >> 3;
              inputs[0] = uint256(_commitment) & mask;
              Proof memory proof = deserialize_proof(inputs, _proof);
              VerificationKey memory vk = getVkBlock(_chunks);
              require(vk.num_inputs == inputs.length);
              return verify(proof, vk);
          }
          function verifyExitProof(
              bytes32 _rootHash,
              uint32 _accountId,
              address _owner,
              uint16 _tokenId,
              uint128 _amount,
              uint256[] calldata _proof
          ) external view returns (bool) {
              bytes32 commitment = sha256(abi.encodePacked(_rootHash, _accountId, _owner, _tokenId, _amount));
              uint256[] memory inputs = new uint256[](1);
              uint256 mask = (~uint256(0)) >> 3;
              inputs[0] = uint256(commitment) & mask;
              Proof memory proof = deserialize_proof(inputs, _proof);
              VerificationKey memory vk = getVkExit();
              require(vk.num_inputs == inputs.length);
              return verify(proof, vk);
          }
      }
      pragma solidity ^0.5.0;
      /// @title Interface of the upgradeable contract
      /// @author Matter Labs
      interface Upgradeable {
          /// @notice Upgrades target of upgradeable contract
          /// @param newTarget New target
          /// @param newTargetInitializationParameters New target initialization parameters
          function upgradeTarget(address newTarget, bytes calldata newTargetInitializationParameters) external;
      }
      
      pragma solidity >=0.5.0 <0.7.0;
      import "./PlonkCore.sol";
      // Hardcoded constants to avoid accessing store
      contract KeysWithPlonkVerifier is VerifierWithDeserialize {
          function isBlockSizeSupportedInternal(uint32 _size) internal pure returns (bool) {
              if (_size == uint32(6)) { return true; }
              else if (_size == uint32(30)) { return true; }
              else if (_size == uint32(74)) { return true; }
              else if (_size == uint32(150)) { return true; }
              else if (_size == uint32(334)) { return true; }
              else if (_size == uint32(678)) { return true; }
              else { return false; }
          }
          function getVkBlock(uint32 _chunks) internal pure returns (VerificationKey memory vk) {
              if (_chunks == uint32(6)) { return getVkBlock6(); }
              else if (_chunks == uint32(30)) { return getVkBlock30(); }
              else if (_chunks == uint32(74)) { return getVkBlock74(); }
              else if (_chunks == uint32(150)) { return getVkBlock150(); }
              else if (_chunks == uint32(334)) { return getVkBlock334(); }
              else if (_chunks == uint32(678)) { return getVkBlock678(); }
          }
          
          function getVkBlock6() internal pure returns(VerificationKey memory vk) {
              vk.domain_size = 2097152;
              vk.num_inputs = 1;
              vk.omega = PairingsBn254.new_fr(0x032750f8f3c2493d0828c7285d0258e1bdcaa463f4442a52747b5c96639659bb);
              vk.selector_commitments[0] = PairingsBn254.new_g1(
                  0x0af568a35305efe9043e30a66e5dbf46219e16a04c7681e0291759114257a9a4,
                  0x2f35e4f3c521dcd57b7f7cc1548df2a4877eda3d6bf6e47830b7b4c5c78247fa
              );
              vk.selector_commitments[1] = PairingsBn254.new_g1(
                  0x15facf3c62fdc8eb795512905e6756fbdab12f583e92f847fe04ebed1de2b0d9,
                  0x145ba3f0cd63989a960af1652ace370d8ebae9ccf8462780216625d812100623
              );
              vk.selector_commitments[2] = PairingsBn254.new_g1(
                  0x16d73cc25f2f549e265a5cc871d5350a340e53bfab24118d30d6dd3276b9edf5,
                  0x1eaf73c1e29c3c3a1702e2375bbee02458c04ae316a603c9509ac9f041bdf67e
              );
              vk.selector_commitments[3] = PairingsBn254.new_g1(
                  0x1f652d9f3fb289cfaff303e35b53e4a1915f2a4f631115e572cfb7dd7e72c9a8,
                  0x165827a3b413c30dd0e22f10b58e7e64774325e5a213821b953b20d26374b1b1
              );
              vk.selector_commitments[4] = PairingsBn254.new_g1(
                  0x0bb9329eaae8b9979ccf377d312778494b03642e3a1f629f1c4a78dcc759b348,
                  0x213616224ae180ef4c0010301e037e281689f84d5a9121191957eff36770d526
              );
              vk.selector_commitments[5] = PairingsBn254.new_g1(
                  0x0b478d136e36e67ef049746e8b452afa88c13547cdc341eef713fa7e42f6dcd6,
                  0x24ef9c90e617fcf3adf998dff4c3238f8fe564ba2da8d15ac3c673d0b16d9bd6
              );
              // we only have access to value of the d(x) witness polynomial on the next
              // trace step, so we only need one element here and deal with it in other places
              // by having this in mind
              vk.next_step_selector_commitments[0] = PairingsBn254.new_g1(
                  0x09a2c2eeb91944b93013a95e6a63a780e881f101249375d9732ba74c6e54186b,
                  0x2599f0b0d736bfb3f66cdff99c9f5557f7b82a1fa4029d0d5770d1d194019533
              );
               vk.permutation_commitments[0] = PairingsBn254.new_g1(
                  0x199f1e85e793132f1ce19e86967efb1ed606e68b7af0478532fa182163fefa6e,
                  0x21698d34ed8a715d0086ecab6c1b7fcf4d9a1d7995db29d517031084f2764f95
              );
              vk.permutation_commitments[1] = PairingsBn254.new_g1(
                  0x2389c84e9eaf7f61ad69dd4d19299530c4027f083c6976b5e7cc7f3b7cb57b55,
                  0x18ee0d9df2d37dda5e85a5764088e89ee8ce32eb7ff45173f0fd102c522d41e1
              );
              vk.permutation_commitments[2] = PairingsBn254.new_g1(
                  0x0f922b9348896b282f12aff0610e39dfa1b6066aaeb5a04f0a5a29d2bb0096c8,
                  0x1e24a9abbf50778a8d2fd51b37a8eae7836cde2c559740d6ec322c8584274442
              );
              vk.permutation_commitments[3] = PairingsBn254.new_g1(
                  0x2abf5027b8f2a88015873d2b3f97ae97da5f771e800acf89098c5d2228086cf1,
                  0x1e245aa8ee95af522f204a3e62b82cc62361cf604efac1dd27d49252d1d360c4
              );
              vk.permutation_non_residues[0] = PairingsBn254.new_fr(
                  0x0000000000000000000000000000000000000000000000000000000000000005
              );
              vk.permutation_non_residues[1] = PairingsBn254.new_fr(
                  0x0000000000000000000000000000000000000000000000000000000000000007
              );
              vk.permutation_non_residues[2] = PairingsBn254.new_fr(
                  0x000000000000000000000000000000000000000000000000000000000000000a
              );
              vk.g2_x = PairingsBn254.new_g2(
                  [0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1,
                   0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0],
                  [0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4,
                   0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55]
              );
          }
          
          function getVkBlock30() internal pure returns(VerificationKey memory vk) {
              vk.domain_size = 4194304;
              vk.num_inputs = 1;
              vk.omega = PairingsBn254.new_fr(0x18c95f1ae6514e11a1b30fd7923947c5ffcec5347f16e91b4dd654168326bede);
              vk.selector_commitments[0] = PairingsBn254.new_g1(
                  0x0dabeb092c842c9877aab11b2242490061cef35c2631e3c383f1ce13c386aaf3,
                  0x0d34932557f52b84c523dc2474e79eb343f84718d7f20e519a85d10bdb4611eb
              );
              vk.selector_commitments[1] = PairingsBn254.new_g1(
                  0x1c0ea096536ef84a9ee46457b44d4bf9f4b147e9cfd9157f9291d50e59de2512,
                  0x0b84d8085ef5989f16bc03822d3c3232c2d5df22a0d0a3ac80e6338094909b3b
              );
              vk.selector_commitments[2] = PairingsBn254.new_g1(
                  0x2f6dd701052fc5e95812f5c0da0bf96d5120d7dd5a60bfcc7705aeb212593949,
                  0x1275cd37c2e0b36830d7a0a3000668064b28c3ff4071614d5992e7a9720fe5a8
              );
              vk.selector_commitments[3] = PairingsBn254.new_g1(
                  0x1466533cc8c309aca62e5571d170e056b570358ba73bdf921d914a96deef85b1,
                  0x2f1d1375359dcd5c881b144b64698f15e8227d3f4cb9507f463eecb14173942d
              );
              vk.selector_commitments[4] = PairingsBn254.new_g1(
                  0x0d23903b411253d6e1ea85334f072b75da815db364e96b600003f3f95e3af56c,
                  0x1130d37d579a1c54aab11ac4e7b7e3fb12e2632682c41f40042cf5e0de646e32
              );
              vk.selector_commitments[5] = PairingsBn254.new_g1(
                  0x130a475c0d12c09535079832afded260636cea2d3acf638b3645f6f18b1defd8,
                  0x0bf9f1bc4fe3d87628e43c5f87634164bb4a7baedeb578e8b036e72bc5da9038
              );
              // we only have access to value of the d(x) witness polynomial on the next
              // trace step, so we only need one element here and deal with it in other places
              // by having this in mind
              vk.next_step_selector_commitments[0] = PairingsBn254.new_g1(
                  0x153b616b629aa926262a08d03f3626b2623b1a2aad95dba19d80878fe4d2701a,
                  0x0ce4c47b8656ea235b974df7b7ec7e3cb62a952704ebcb084ecf521da22c1549
              );
               vk.permutation_commitments[0] = PairingsBn254.new_g1(
                  0x0ec6a763e129c400eeaa8bf1d66498ff92286d1bed142f92c932f5ef8cf8c5e3,
                  0x23a13322172b50c6f624e9c7c924260e2894f84ab928dbb718d0c391b0d43abf
              );
              vk.permutation_commitments[1] = PairingsBn254.new_g1(
                  0x246a73716676323f05a5d6137eb98c7f6c8d6ca5f9b63c397271ce820175599e,
                  0x08ac8dc778bb4998b6d8440fb25463d7810986439aae3f3ddc6e24b0e8a8da2f
              );
              vk.permutation_commitments[2] = PairingsBn254.new_g1(
                  0x1174638606b9dc726499db27c34f317db4bd0475678827972fa0da4fab6da1f7,
                  0x17ceb003ecee92a35fa0ab0989de9d6aafedd821c6d89a0dcded8b096f5b45cb
              );
              vk.permutation_commitments[3] = PairingsBn254.new_g1(
                  0x1e7f3863aacbcbb3a43318c621b0abcae89a145bc950dd161fb793fb425ae8cb,
                  0x2980f2f25fd142c92a55560529f7080e7d55ed8c3cfbb1cd421186c3c3f799e7
              );
              vk.permutation_non_residues[0] = PairingsBn254.new_fr(
                  0x0000000000000000000000000000000000000000000000000000000000000005
              );
              vk.permutation_non_residues[1] = PairingsBn254.new_fr(
                  0x0000000000000000000000000000000000000000000000000000000000000007
              );
              vk.permutation_non_residues[2] = PairingsBn254.new_fr(
                  0x000000000000000000000000000000000000000000000000000000000000000a
              );
              vk.g2_x = PairingsBn254.new_g2(
                  [0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1,
                   0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0],
                  [0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4,
                   0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55]
              );
          }
          
          function getVkBlock74() internal pure returns(VerificationKey memory vk) {
              vk.domain_size = 8388608;
              vk.num_inputs = 1;
              vk.omega = PairingsBn254.new_fr(0x1283ba6f4b7b1a76ba2008fe823128bea4adb9269cbfd7c41c223be65bc60863);
              vk.selector_commitments[0] = PairingsBn254.new_g1(
                  0x1021fcff6a718826f54ecb1ed30b237b453a8d16a68c5d473ddd1a98ce4d3ffe,
                  0x1ff632b0f6b06f344c7790260938e21fefeda3c4428e4f3ffce28301de847934
              );
              vk.selector_commitments[1] = PairingsBn254.new_g1(
                  0x04d1cc2c538b6bc75450f955d21550a948cb38b8aec7c9775795a96aabdb412e,
                  0x159a35771ccd356ab60f186c9efc8767df370c28e2231ec98e6a674bc95f7612
              );
              vk.selector_commitments[2] = PairingsBn254.new_g1(
                  0x23eeccd095551b0357be6eea8bd9ecabd4a446cb7993c545c7193a2d5bb8657f,
                  0x00827f6f318c00d7dd2e4a7f3bd94810af906e62eb6844bd110e17ee1ec16f8d
              );
              vk.selector_commitments[3] = PairingsBn254.new_g1(
                  0x1d3bdf4f220278fc7fc8be20ced77647dc38be36f8d9b84e61ddf46e1d593d14,
                  0x2396a7d5704823939ead4a2bfc6510a7f6470e2a1f447072c9534d62372873f3
              );
              vk.selector_commitments[4] = PairingsBn254.new_g1(
                  0x040be274be43c2d83ae606ec3636fec5c4e7d8c99becf7d33b52adbd0d724b8a,
                  0x0dec58400efeed3381f71ad1e83582c139a8b728fa9e25ca61e92ef46a09e025
              );
              vk.selector_commitments[5] = PairingsBn254.new_g1(
                  0x0adf559b5270e352f9ab28f549922da636aef8bdba57d67f85434dc56e78c744,
                  0x2e70f0eda4beb23c457fb274b0aa553b82a94f07c6015ee589481cfa2b3496b1
              );
              // we only have access to value of the d(x) witness polynomial on the next
              // trace step, so we only need one element here and deal with it in other places
              // by having this in mind
              vk.next_step_selector_commitments[0] = PairingsBn254.new_g1(
                  0x2a8d0d37052e369ff5f5f03b3263deae82cbb555557050c6332488ec2be812ae,
                  0x2fa789399c26b85d1cf48961bbc44dca2eaf75016720f9e2ba78c1133fadf0bb
              );
               vk.permutation_commitments[0] = PairingsBn254.new_g1(
                  0x238b4d00fa2d36e7ab351a32f91a2125622a5bb0ae9af7fdbd9b60cf000e6e91,
                  0x08ff4499abe98d10e1b6b2fc77fa32333dd5f41cf726cdc71503e0eb8595f4de
              );
              vk.permutation_commitments[1] = PairingsBn254.new_g1(
                  0x0cd7e807d8ed7749d99f27e58c871f6feb2036ed6cfcc5a411dc38c7fd307be6,
                  0x292f00dd8d21c1ce8124bd9f82ab249dbbdb6f45c3696481ae38ee77b22f849b
              );
              vk.permutation_commitments[2] = PairingsBn254.new_g1(
                  0x2809b958f09357f3a419ce2245cc5b38e8faecc1ec767d5c868349e588fe5d44,
                  0x2624d43f0e037f39b0a6fb9f5ae4499849d54c54c0dc3ac8f9c292ac8551e6bc
              );
              vk.permutation_commitments[3] = PairingsBn254.new_g1(
                  0x276a858b024e5d82607fac4ee82e97719b25fae9853e2c394236ebc15bdc07ed,
                  0x11de57c72d139056394203bcac52a132a9d2a012edba72949518e3b986694a8e
              );
              vk.permutation_non_residues[0] = PairingsBn254.new_fr(
                  0x0000000000000000000000000000000000000000000000000000000000000005
              );
              vk.permutation_non_residues[1] = PairingsBn254.new_fr(
                  0x0000000000000000000000000000000000000000000000000000000000000007
              );
              vk.permutation_non_residues[2] = PairingsBn254.new_fr(
                  0x000000000000000000000000000000000000000000000000000000000000000a
              );
              vk.g2_x = PairingsBn254.new_g2(
                  [0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1,
                   0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0],
                  [0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4,
                   0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55]
              );
          }
          
          function getVkBlock150() internal pure returns(VerificationKey memory vk) {
              vk.domain_size = 16777216;
              vk.num_inputs = 1;
              vk.omega = PairingsBn254.new_fr(0x1951441010b2b95a6e47a6075066a50a036f5ba978c050f2821df86636c0facb);
              vk.selector_commitments[0] = PairingsBn254.new_g1(
                  0x2b980886069d87943728e229dd4c9e983a0ce1a319b5ab964fced0bc02e2cf96,
                  0x176f6a4a15b95fa93edb949de5510ee84c50040e05c5ee1e2b928ec013d2c0da
              );
              vk.selector_commitments[1] = PairingsBn254.new_g1(
                  0x251f54507ddd45d703e5a81b666217e0c3e9231fdbfd382188dafc03268931ce,
                  0x27d916677565037db4532f2846e10f42cd20499ec54989c42a996c86429786c0
              );
              vk.selector_commitments[2] = PairingsBn254.new_g1(
                  0x00e1d3e897a5f0fea282b120762ed656204c7b05c6716f92047c88991a6776f9,
                  0x1c83d49caa16f271c2f7250bbc4bba028d4dfd65ed880bc294005253ea7c846a
              );
              vk.selector_commitments[3] = PairingsBn254.new_g1(
                  0x29692360bdfa1c1fde3828cf2b903f6ec3853a1073368db46ab444edf5989cc4,
                  0x1fb7acc4736be1008144d100c5d447cc55d36c988e6ca974afb2d6039ad19c71
              );
              vk.selector_commitments[4] = PairingsBn254.new_g1(
                  0x2324d61f18207e8135bd2f290e4acd36fc9a977411da6c7e404702d120a4aa4a,
                  0x12f7ce81186f570986229da30c136c85473d552fe1c214a7eb3b2d305b7b2ae5
              );
              vk.selector_commitments[5] = PairingsBn254.new_g1(
                  0x1d1d3df125d46c06153985ada847816cdcafbf7c8f72d99ae779680bed23e935,
                  0x1685aa96e1c7d4be8e4993d2b50e8ea76fca9166c223749492f31ebf22915853
              );
              // we only have access to value of the d(x) witness polynomial on the next
              // trace step, so we only need one element here and deal with it in other places
              // by having this in mind
              vk.next_step_selector_commitments[0] = PairingsBn254.new_g1(
                  0x234111b09c5d38dd313eb1ef80a12cbbdc20bc6066310cd5109f93a4545852da,
                  0x02441d140d85197884cc9cce20f80670cd94daf51153e61d99381ad85f9d3421
              );
               vk.permutation_commitments[0] = PairingsBn254.new_g1(
                  0x02f194881a81ef07ab23dd4552157fb3b83a67df10ffd6916c6ac9f8f5a088ba,
                  0x0cfb413a65eb6880ffb16277e56b6e1f2474bbb5e2de0a71f06a94118f54bdab
              );
              vk.permutation_commitments[1] = PairingsBn254.new_g1(
                  0x1292198bff3ce83fc2410250e998a49ae2d15080555ab268e2714e7cd7e68078,
                  0x206789f5461397abcaed25062e0881928a9ad05d02f031944dc3a3c3b0955eec
              );
              vk.permutation_commitments[2] = PairingsBn254.new_g1(
                  0x2204220f2bfff0ff22d77c9c66c3fdc00b431e92e930dc65ba3a6b91a3350a98,
                  0x0e46f948f7b703fd7c100575ed47db8d559b93fba62cefaa6f65458249b1e52c
              );
              vk.permutation_commitments[3] = PairingsBn254.new_g1(
                  0x2b627b695c64b566e4f4b8f0be454d1de004cce7fa19e6f7fdcb2de2397e67d6,
                  0x264b1bb8361351d44e34c89162185f489f8e823c649dbbd1f65a1d10e3e196af
              );
              vk.permutation_non_residues[0] = PairingsBn254.new_fr(
                  0x0000000000000000000000000000000000000000000000000000000000000005
              );
              vk.permutation_non_residues[1] = PairingsBn254.new_fr(
                  0x0000000000000000000000000000000000000000000000000000000000000007
              );
              vk.permutation_non_residues[2] = PairingsBn254.new_fr(
                  0x000000000000000000000000000000000000000000000000000000000000000a
              );
              vk.g2_x = PairingsBn254.new_g2(
                  [0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1,
                   0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0],
                  [0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4,
                   0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55]
              );
          }
          
          function getVkBlock334() internal pure returns(VerificationKey memory vk) {
              vk.domain_size = 33554432;
              vk.num_inputs = 1;
              vk.omega = PairingsBn254.new_fr(0x0d94d63997367c97a8ed16c17adaae39262b9af83acb9e003f94c217303dd160);
              vk.selector_commitments[0] = PairingsBn254.new_g1(
                  0x29d9574cd4b98e563db05a14d1ecf9dd7b8e3cd5d901e140d04181c9f53db97e,
                  0x2ee352008474de4960ca513838e407cd27cbd5c5a8cffd67f67d8a49d4861279
              );
              vk.selector_commitments[1] = PairingsBn254.new_g1(
                  0x1b1dffc6fde1dd941557412626ddebedd2bcb6f9f8cc9c19bc1f1cca2f9635c7,
                  0x0f2a6292bb6dacecaa6cb3c71240504f417d8e45f8b345707486afb658fd9d4a
              );
              vk.selector_commitments[2] = PairingsBn254.new_g1(
                  0x0210cb0963ab20ff896d704feb4aadf889ebfe3c3fe1555744ec562fc8bc24b6,
                  0x156b1a7294328baadcb080d01237d031acf66f63c2d91659d16e1b80cbf3a890
              );
              vk.selector_commitments[3] = PairingsBn254.new_g1(
                  0x1c3228a3e68fe3ade8c48d516595407359570842d2ab66127b77dc076488be5b,
                  0x2497ee062b253369cdf12f373e8bd7c9bde6942074b7fea52d1751e9b0de7a24
              );
              vk.selector_commitments[4] = PairingsBn254.new_g1(
                  0x291088d66f3e2f19861c488ab28c619a8fb0ead716cbf1182be4c857a738e37b,
                  0x010eaf9bab2047a22c90b03c95a8d4f4f45ed0f3410777fc572ca249398017e5
              );
              vk.selector_commitments[5] = PairingsBn254.new_g1(
                  0x18c2e15408ba31f91aec85db8edf934f6ad294b1ef641109f026090c7ce788af,
                  0x215a339e53528e9c9247987610f93f0854de562fd78ba34aebd8e0e82d5a45a2
              );
              // we only have access to value of the d(x) witness polynomial on the next
              // trace step, so we only need one element here and deal with it in other places
              // by having this in mind
              vk.next_step_selector_commitments[0] = PairingsBn254.new_g1(
                  0x14a4455b1da8964b29fe75d6b19283f00fd58d3db10afce417cca2a69cd993ae,
                  0x12d468900ccdc72f0f2e7f41b9a29329c46dd8ba3b0344bf453e2d172a26bc9c
              );
               vk.permutation_commitments[0] = PairingsBn254.new_g1(
                  0x04a3e03c4f3e964d756e69a0de2d331c8679cfbdce806849931efe547d493b4b,
                  0x20871a71fdb6f7e12839bc53ff8b0559d30db42e523d1754121b2ee8f967361b
              );
              vk.permutation_commitments[1] = PairingsBn254.new_g1(
                  0x1e8f25a49a753a938da78263003a4dc0e68492940abd9b6294da149c7c927216,
                  0x0bcd44d08ffc48a289e87b0604c7a16b5e119e3c47b293c3c6c29762a4a5d326
              );
              vk.permutation_commitments[2] = PairingsBn254.new_g1(
                  0x2f3b23257c3437e10631f5dc5da61a622f17dd1516294e013fe484a3adf42462,
                  0x0b0a21cb5384dc0669f58d54671732385cf96065680255d46861f9a7456267f5
              );
              vk.permutation_commitments[3] = PairingsBn254.new_g1(
                  0x01ec6c4541fb1b4342d219856f1805bf7b94de582b261b096ea2b11b62205633,
                  0x05a9b67927c90079c45907f9ba67105b47b15dcf480b3bf3514582dc18d357bf
              );
              vk.permutation_non_residues[0] = PairingsBn254.new_fr(
                  0x0000000000000000000000000000000000000000000000000000000000000005
              );
              vk.permutation_non_residues[1] = PairingsBn254.new_fr(
                  0x0000000000000000000000000000000000000000000000000000000000000007
              );
              vk.permutation_non_residues[2] = PairingsBn254.new_fr(
                  0x000000000000000000000000000000000000000000000000000000000000000a
              );
              vk.g2_x = PairingsBn254.new_g2(
                  [0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1,
                   0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0],
                  [0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4,
                   0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55]
              );
          }
          
          function getVkBlock678() internal pure returns(VerificationKey memory vk) {
              vk.domain_size = 67108864;
              vk.num_inputs = 1;
              vk.omega = PairingsBn254.new_fr(0x1dba8b5bdd64ef6ce29a9039aca3c0e524395c43b9227b96c75090cc6cc7ec97);
              vk.selector_commitments[0] = PairingsBn254.new_g1(
                  0x10fac38e585fc150fa6f7470ff88f978bd906bd5454fd067381816c296f89870,
                  0x1b5424e03353a60155d057d5b0303c2b0d78410cd2f7b0abeb2928b76f808816
              );
              vk.selector_commitments[1] = PairingsBn254.new_g1(
                  0x0ff633c9b1ed5af3bd5882da5354dfcccd698066d4050ff0c7fd20aa9cd01218,
                  0x2ab1ee7db81f3e504032e3e36e297c38d15e55171a49cee01ff42d1c954d63a5
              );
              vk.selector_commitments[2] = PairingsBn254.new_g1(
                  0x03aafad8e4a648f6339fc48f229b8672c64dd64e7866263fa8c4e0e716961dea,
                  0x03bc02bc248d3d3aa917b9eec4a335dc7b1c21ae694c6166911b7246fc95a539
              );
              vk.selector_commitments[3] = PairingsBn254.new_g1(
                  0x303d788f44e34b61d5671389e8e4a7bfa4f13c02b8c2d345d0eba623e5a6f17f,
                  0x00a6d7d77556ccff73f1ce9fd0ddce9eb8940731dbdca250fad108ffbccb744d
              );
              vk.selector_commitments[4] = PairingsBn254.new_g1(
                  0x03cacd9bc51ff522d6cc654b17483cf5f044a15eec12f1837fcb9d7f717d5a67,
                  0x0de3f25d9a6865cd7cc72e529edd802a0cee06d1b45830a294bd6a2240d4bdd0
              );
              vk.selector_commitments[5] = PairingsBn254.new_g1(
                  0x02c54c3ac215172724f0b8138e212e793b28af7ae06b5b53c2f56b52cf32fff6,
                  0x25093d56e5e5dfad1b1c75b94250fcb4fc430ba214bba40989855d142dcf29b2
              );
              // we only have access to value of the d(x) witness polynomial on the next
              // trace step, so we only need one element here and deal with it in other places
              // by having this in mind
              vk.next_step_selector_commitments[0] = PairingsBn254.new_g1(
                  0x222cfccd491605b4b9e15a18b8b0d841c8c5104ed3f96a97d546b0b33cdc67db,
                  0x0f4ea5620594b707d6d37c4292df6889bd835574abec790b97fd0af88b1d1edd
              );
               vk.permutation_commitments[0] = PairingsBn254.new_g1(
                  0x230f568480422793e27ba60859477b363d50ae18c48ace23d6cfcf04abe29dd6,
                  0x1c6c663735ff13ab1332598f552fc3d01410b18cfa9c6a7bb88df553c79a38b0
              );
              vk.permutation_commitments[1] = PairingsBn254.new_g1(
                  0x0955c07d90bf6d48aa1aec00c060f9aec57f10fa76285684a16cd023192af01c,
                  0x290ff005de85504f475b596b72bcf1623b71b30534cd360576626d6737f1b763
              );
              vk.permutation_commitments[2] = PairingsBn254.new_g1(
                  0x0cac2104abcde1bf215788c18be6a5c2d73da416f8c5b6e0a2a2222a24deb32f,
                  0x02dde54e719bc243cda9febc88187582a0983ff1a85d6f888bfe13e4567d9aa5
              );
              vk.permutation_commitments[3] = PairingsBn254.new_g1(
                  0x27fce095aa4c68adbd01f5fd8e64864f6c1625cc577e13a2b80051947b2e8ff6,
                  0x2583c01600426f9b3873ffef651187c82c0e55a6e5de762355a458fc388f4585
              );
              vk.permutation_non_residues[0] = PairingsBn254.new_fr(
                  0x0000000000000000000000000000000000000000000000000000000000000005
              );
              vk.permutation_non_residues[1] = PairingsBn254.new_fr(
                  0x0000000000000000000000000000000000000000000000000000000000000007
              );
              vk.permutation_non_residues[2] = PairingsBn254.new_fr(
                  0x000000000000000000000000000000000000000000000000000000000000000a
              );
              vk.g2_x = PairingsBn254.new_g2(
                  [0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1,
                   0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0],
                  [0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4,
                   0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55]
              );
          }
          
          function getVkExit() internal pure returns(VerificationKey memory vk) {
              vk.domain_size = 262144;
              vk.num_inputs = 1;
              vk.omega = PairingsBn254.new_fr(0x0f60c8fe0414cb9379b2d39267945f6bd60d06a05216231b26a9fcf88ddbfebe);
              vk.selector_commitments[0] = PairingsBn254.new_g1(
                  0x117ebe939b7336d17b69b05d5530e30326af39da45a989b078bb3d607707bf3e,
                  0x18b16095a1c814fe2980170ff34490f1fd454e874caa87df2f739fb9c8d2e902
              );
              vk.selector_commitments[1] = PairingsBn254.new_g1(
                  0x05ac70a10fc569cc8358bfb708c184446966c6b6a3e0d7c25183ded97f9e7933,
                  0x0f6152282854e153588d45e784d216a423a624522a687741492ee0b807348e71
              );
              vk.selector_commitments[2] = PairingsBn254.new_g1(
                  0x03cfa9d8f9b40e565435bee3c5b0e855c8612c5a89623557cc30f4588617d7bd,
                  0x2292bb95c2cc2da55833b403a387e250a9575e32e4ce7d6caa954f12e6ce592a
              );
              vk.selector_commitments[3] = PairingsBn254.new_g1(
                  0x04d04f495c69127b6cc6ecbfd23f77f178e7f4e2d2de3eab3e583a4997744cd9,
                  0x09dcf5b3db29af5c5eef2759da26d3b6959cb8d80ada9f9b086f7cc39246ad2b
              );
              vk.selector_commitments[4] = PairingsBn254.new_g1(
                  0x01ebab991522d407cfd4e8a1740b64617f0dfca50479bba2707c2ec4159039fc,
                  0x2c8bd00a44c6120bbf8e57877013f2b5ee36b53eef4ea3b6748fd03568005946
              );
              vk.selector_commitments[5] = PairingsBn254.new_g1(
                  0x07a7124d1fece66bd5428fcce25c22a4a9d5ceaa1e632565d9a062c39f005b5e,
                  0x2044ae5306f0e114c48142b9b97001d94e3f2280db1b01a1e47ac1cf6bd5f99e
              );
              // we only have access to value of the d(x) witness polynomial on the next
              // trace step, so we only need one element here and deal with it in other places
              // by having this in mind
              vk.next_step_selector_commitments[0] = PairingsBn254.new_g1(
                  0x1dd1549a639f052c4fbc95b7b7a40acf39928cad715580ba2b38baa116dacd9c,
                  0x0f8e712990da1ce5195faaf80185ef0d5e430fdec9045a20af758cc8ecdac2e5
              );
               vk.permutation_commitments[0] = PairingsBn254.new_g1(
                  0x0026b64066e39a22739be37fed73308ace0a5f38a0e2292dcc2309c818e8c89c,
                  0x285101acca358974c2c7c9a8a3936e08fbd86779b877b416d9480c91518cb35b
              );
              vk.permutation_commitments[1] = PairingsBn254.new_g1(
                  0x2159265ac6fcd4d0257673c3a85c17f4cf3ea13a3c9fb51e404037b13778d56f,
                  0x25bf73e568ba3406ace2137195bb2176d9de87a48ae42520281aaef2ac2ef937
              );
              vk.permutation_commitments[2] = PairingsBn254.new_g1(
                  0x068f29af99fc8bbf8c00659d34b6d34e4757af6edc10fc7647476cbd0ea9be63,
                  0x2ef759b20cabf3da83d7f578d9e11ed60f7015440e77359db94475ddb303144d
              );
              vk.permutation_commitments[3] = PairingsBn254.new_g1(
                  0x22793db6e98b9e37a1c5d78fcec67a2d8c527d34c5e9c8c1ff15007d30a4c133,
                  0x1b683d60fd0750b3a45cdee5cbc4057204a02bd428e8071c92fe6694a40a5c1f
              );
              vk.permutation_non_residues[0] = PairingsBn254.new_fr(
                  0x0000000000000000000000000000000000000000000000000000000000000005
              );
              vk.permutation_non_residues[1] = PairingsBn254.new_fr(
                  0x0000000000000000000000000000000000000000000000000000000000000007
              );
              vk.permutation_non_residues[2] = PairingsBn254.new_fr(
                  0x000000000000000000000000000000000000000000000000000000000000000a
              );
              vk.g2_x = PairingsBn254.new_g2(
                  [0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1,
                   0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0],
                  [0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4,
                   0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55]
              );
          }
          
      }
      pragma solidity >=0.5.0 <0.7.0;
      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 {
              uint[2] X;
              uint[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) {
              if (x == 0 && y == 0) {
                  // point of infinity is (0,0)
                  return G1Point(x, y);
              }
              // check encoding
              require(x < q_mod);
              require(y < q_mod);
              // 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);
              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 = false;
                  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)
          {
              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
          {
              uint[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);
              uint elements = p1.length;
              uint inputSize = elements * 6;
              uint[] memory input = new uint[](inputSize);
              for (uint i = 0; i < elements; i++)
              {
                  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];
              }
              uint[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);
          }
      }
      library TranscriptLibrary {
          // 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});
          }
      }
      contract Plonk4VerifierWithAccessToDNext {
          using PairingsBn254 for PairingsBn254.G1Point;
          using PairingsBn254 for PairingsBn254.G2Point;
          using PairingsBn254 for PairingsBn254.Fr;
          using TranscriptLibrary for TranscriptLibrary.Transcript;
          uint256 constant STATE_WIDTH = 4;
          uint256 constant ACCESSIBLE_STATE_POLYS_ON_NEXT_STEP = 1;
          struct VerificationKey {
              uint256 domain_size;
              uint256 num_inputs;
              PairingsBn254.Fr omega;
              PairingsBn254.G1Point[STATE_WIDTH+2] selector_commitments; // STATE_WIDTH for witness + multiplication + constant
              PairingsBn254.G1Point[ACCESSIBLE_STATE_POLYS_ON_NEXT_STEP] next_step_selector_commitments;
              PairingsBn254.G1Point[STATE_WIDTH] permutation_commitments;
              PairingsBn254.Fr[STATE_WIDTH-1] permutation_non_residues;
              PairingsBn254.G2Point g2_x;
          }
          struct Proof {
              uint256[] input_values;
              PairingsBn254.G1Point[STATE_WIDTH] wire_commitments;
              PairingsBn254.G1Point grand_product_commitment;
              PairingsBn254.G1Point[STATE_WIDTH] quotient_poly_commitments;
              PairingsBn254.Fr[STATE_WIDTH] wire_values_at_z;
              PairingsBn254.Fr[ACCESSIBLE_STATE_POLYS_ON_NEXT_STEP] wire_values_at_z_omega;
              PairingsBn254.Fr grand_product_at_z_omega;
              PairingsBn254.Fr quotient_polynomial_at_z;
              PairingsBn254.Fr linearization_polynomial_at_z;
              PairingsBn254.Fr[STATE_WIDTH-1] permutation_polynomials_at_z;
              PairingsBn254.G1Point opening_at_z_proof;
              PairingsBn254.G1Point opening_at_z_omega_proof;
          }
          struct PartialVerifierState {
              PairingsBn254.Fr alpha;
              PairingsBn254.Fr beta;
              PairingsBn254.Fr gamma;
              PairingsBn254.Fr v;
              PairingsBn254.Fr u;
              PairingsBn254.Fr z;
              PairingsBn254.Fr[] cached_lagrange_evals;
          }
          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) {
              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 batch_evaluate_lagrange_poly_out_of_domain(
              uint256[] memory poly_nums,
              uint256 domain_size,
              PairingsBn254.Fr memory omega,
              PairingsBn254.Fr memory at
          ) internal view returns (PairingsBn254.Fr[] memory res) {
              PairingsBn254.Fr memory one = PairingsBn254.new_fr(1);
              PairingsBn254.Fr memory tmp_1 = PairingsBn254.new_fr(0);
              PairingsBn254.Fr memory tmp_2 = PairingsBn254.new_fr(domain_size);
              PairingsBn254.Fr memory vanishing_at_z = at.pow(domain_size);
              vanishing_at_z.sub_assign(one);
              // we can not have random point z be in domain
              require(vanishing_at_z.value != 0);
              PairingsBn254.Fr[] memory nums = new PairingsBn254.Fr[](poly_nums.length);
              PairingsBn254.Fr[] memory dens = new PairingsBn254.Fr[](poly_nums.length);
              // numerators in a form omega^i * (z^n - 1)
              // denoms in a form (z - omega^i) * N
              for (uint i = 0; i < poly_nums.length; i++) {
                  tmp_1 = omega.pow(poly_nums[i]); // power of omega
                  nums[i].assign(vanishing_at_z);
                  nums[i].mul_assign(tmp_1);
                  dens[i].assign(at); // (X - omega^i) * N
                  dens[i].sub_assign(tmp_1);
                  dens[i].mul_assign(tmp_2); // mul by domain size
              }
              PairingsBn254.Fr[] memory partial_products = new PairingsBn254.Fr[](poly_nums.length);
              partial_products[0].assign(PairingsBn254.new_fr(1));
              for (uint i = 1; i < dens.length - 1; i++) {
                  partial_products[i].assign(dens[i-1]);
                  partial_products[i].mul_assign(dens[i]);
              }
              tmp_2.assign(partial_products[partial_products.length - 1]);
              tmp_2.mul_assign(dens[dens.length - 1]);
              tmp_2 = tmp_2.inverse(); // tmp_2 contains a^-1 * b^-1 (with! the last one)
              for (uint i = dens.length - 1; i < dens.length; i--) {
                  dens[i].assign(tmp_2); // all inversed
                  dens[i].mul_assign(partial_products[i]); // clear lowest terms
                  tmp_2.mul_assign(dens[i]);
              }
              for (uint i = 0; i < nums.length; i++) {
                  nums[i].mul_assign(dens[i]);
              }
              return nums;
          }
          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 verify_at_z(
              PartialVerifierState memory state,
              Proof memory proof,
              VerificationKey memory vk
          ) internal view returns (bool) {
              PairingsBn254.Fr memory lhs = evaluate_vanishing(vk.domain_size, state.z);
              require(lhs.value != 0); // we can not check a polynomial relationship if point `z` is in the domain
              lhs.mul_assign(proof.quotient_polynomial_at_z);
              PairingsBn254.Fr memory quotient_challenge = PairingsBn254.new_fr(1);
              PairingsBn254.Fr memory rhs = PairingsBn254.copy(proof.linearization_polynomial_at_z);
              // public inputs
              PairingsBn254.Fr memory tmp = PairingsBn254.new_fr(0);
              for (uint256 i = 0; i < proof.input_values.length; i++) {
                  tmp.assign(state.cached_lagrange_evals[i]);
                  tmp.mul_assign(PairingsBn254.new_fr(proof.input_values[i]));
                  rhs.add_assign(tmp);
              }
              quotient_challenge.mul_assign(state.alpha);
              PairingsBn254.Fr memory z_part = PairingsBn254.copy(proof.grand_product_at_z_omega);
              for (uint256 i = 0; i < proof.permutation_polynomials_at_z.length; i++) {
                  tmp.assign(proof.permutation_polynomials_at_z[i]);
                  tmp.mul_assign(state.beta);
                  tmp.add_assign(state.gamma);
                  tmp.add_assign(proof.wire_values_at_z[i]);
                  z_part.mul_assign(tmp);
              }
              tmp.assign(state.gamma);
              // we need a wire value of the last polynomial in enumeration
              tmp.add_assign(proof.wire_values_at_z[STATE_WIDTH - 1]);
              z_part.mul_assign(tmp);
              z_part.mul_assign(quotient_challenge);
              rhs.sub_assign(z_part);
              quotient_challenge.mul_assign(state.alpha);
              tmp.assign(state.cached_lagrange_evals[0]);
              tmp.mul_assign(quotient_challenge);
              rhs.sub_assign(tmp);
              return lhs.value == rhs.value;
          }
          function reconstruct_d(
              PartialVerifierState memory state,
              Proof memory proof,
              VerificationKey memory vk
          ) internal view returns (PairingsBn254.G1Point memory res) {
              // we compute what power of v is used as a delinearization factor in batch opening of
              // commitments. Let's label W(x) = 1 / (x - z) *
              // [
              // t_0(x) + z^n * t_1(x) + z^2n * t_2(x) + z^3n * t_3(x) - t(z)
              // + v (r(x) - r(z))
              // + v^{2..5} * (witness(x) - witness(z))
              // + v^(6..8) * (permutation(x) - permutation(z))
              // ]
              // W'(x) = 1 / (x - z*omega) *
              // [
              // + v^9 (z(x) - z(z*omega)) <- we need this power
              // + v^10 * (d(x) - d(z*omega))
              // ]
              //
              // we pay a little for a few arithmetic operations to not introduce another constant
              uint256 power_for_z_omega_opening = 1 + 1 + STATE_WIDTH + STATE_WIDTH - 1;
              res = PairingsBn254.copy_g1(vk.selector_commitments[STATE_WIDTH + 1]);
              PairingsBn254.G1Point memory tmp_g1 = PairingsBn254.P1();
              PairingsBn254.Fr memory tmp_fr = PairingsBn254.new_fr(0);
              // addition gates
              for (uint256 i = 0; i < STATE_WIDTH; i++) {
                  tmp_g1 = vk.selector_commitments[i].point_mul(proof.wire_values_at_z[i]);
                  res.point_add_assign(tmp_g1);
              }
              // multiplication gate
              tmp_fr.assign(proof.wire_values_at_z[0]);
              tmp_fr.mul_assign(proof.wire_values_at_z[1]);
              tmp_g1 = vk.selector_commitments[STATE_WIDTH].point_mul(tmp_fr);
              res.point_add_assign(tmp_g1);
              // d_next
              tmp_g1 = vk.next_step_selector_commitments[0].point_mul(proof.wire_values_at_z_omega[0]);
              res.point_add_assign(tmp_g1);
              // z * non_res * beta + gamma + a
              PairingsBn254.Fr memory grand_product_part_at_z = PairingsBn254.copy(state.z);
              grand_product_part_at_z.mul_assign(state.beta);
              grand_product_part_at_z.add_assign(proof.wire_values_at_z[0]);
              grand_product_part_at_z.add_assign(state.gamma);
              for (uint256 i = 0; i < vk.permutation_non_residues.length; i++) {
                  tmp_fr.assign(state.z);
                  tmp_fr.mul_assign(vk.permutation_non_residues[i]);
                  tmp_fr.mul_assign(state.beta);
                  tmp_fr.add_assign(state.gamma);
                  tmp_fr.add_assign(proof.wire_values_at_z[i+1]);
                  grand_product_part_at_z.mul_assign(tmp_fr);
              }
              grand_product_part_at_z.mul_assign(state.alpha);
              tmp_fr.assign(state.cached_lagrange_evals[0]);
              tmp_fr.mul_assign(state.alpha);
              tmp_fr.mul_assign(state.alpha);
              grand_product_part_at_z.add_assign(tmp_fr);
              PairingsBn254.Fr memory grand_product_part_at_z_omega = state.v.pow(power_for_z_omega_opening);
              grand_product_part_at_z_omega.mul_assign(state.u);
              PairingsBn254.Fr memory last_permutation_part_at_z = PairingsBn254.new_fr(1);
              for (uint256 i = 0; i < proof.permutation_polynomials_at_z.length; i++) {
                  tmp_fr.assign(state.beta);
                  tmp_fr.mul_assign(proof.permutation_polynomials_at_z[i]);
                  tmp_fr.add_assign(state.gamma);
                  tmp_fr.add_assign(proof.wire_values_at_z[i]);
                  last_permutation_part_at_z.mul_assign(tmp_fr);
              }
              last_permutation_part_at_z.mul_assign(state.beta);
              last_permutation_part_at_z.mul_assign(proof.grand_product_at_z_omega);
              last_permutation_part_at_z.mul_assign(state.alpha);
              // add to the linearization
              tmp_g1 = proof.grand_product_commitment.point_mul(grand_product_part_at_z);
              tmp_g1.point_sub_assign(vk.permutation_commitments[STATE_WIDTH - 1].point_mul(last_permutation_part_at_z));
              res.point_add_assign(tmp_g1);
              res.point_mul_assign(state.v);
              res.point_add_assign(proof.grand_product_commitment.point_mul(grand_product_part_at_z_omega));
          }
          function verify_commitments(
              PartialVerifierState memory state,
              Proof memory proof,
              VerificationKey memory vk
          ) internal view returns (bool) {
              PairingsBn254.G1Point memory d = reconstruct_d(state, proof, vk);
              PairingsBn254.Fr memory z_in_domain_size = state.z.pow(vk.domain_size);
              PairingsBn254.G1Point memory tmp_g1 = PairingsBn254.P1();
              PairingsBn254.Fr memory aggregation_challenge = PairingsBn254.new_fr(1);
              PairingsBn254.G1Point memory commitment_aggregation = PairingsBn254.copy_g1(proof.quotient_poly_commitments[0]);
              PairingsBn254.Fr memory tmp_fr = PairingsBn254.new_fr(1);
              for (uint i = 1; i < proof.quotient_poly_commitments.length; i++) {
                  tmp_fr.mul_assign(z_in_domain_size);
                  tmp_g1 = proof.quotient_poly_commitments[i].point_mul(tmp_fr);
                  commitment_aggregation.point_add_assign(tmp_g1);
              }
              aggregation_challenge.mul_assign(state.v);
              commitment_aggregation.point_add_assign(d);
              for (uint i = 0; i < proof.wire_commitments.length; i++) {
                  aggregation_challenge.mul_assign(state.v);
                  tmp_g1 = proof.wire_commitments[i].point_mul(aggregation_challenge);
                  commitment_aggregation.point_add_assign(tmp_g1);
              }
              for (uint i = 0; i < vk.permutation_commitments.length - 1; i++) {
                  aggregation_challenge.mul_assign(state.v);
                  tmp_g1 = vk.permutation_commitments[i].point_mul(aggregation_challenge);
                  commitment_aggregation.point_add_assign(tmp_g1);
              }
              aggregation_challenge.mul_assign(state.v);
              aggregation_challenge.mul_assign(state.v);
              tmp_fr.assign(aggregation_challenge);
              tmp_fr.mul_assign(state.u);
              tmp_g1 = proof.wire_commitments[STATE_WIDTH - 1].point_mul(tmp_fr);
              commitment_aggregation.point_add_assign(tmp_g1);
              // collect opening values
              aggregation_challenge = PairingsBn254.new_fr(1);
              PairingsBn254.Fr memory aggregated_value = PairingsBn254.copy(proof.quotient_polynomial_at_z);
              aggregation_challenge.mul_assign(state.v);
              tmp_fr.assign(proof.linearization_polynomial_at_z);
              tmp_fr.mul_assign(aggregation_challenge);
              aggregated_value.add_assign(tmp_fr);
              for (uint i = 0; i < proof.wire_values_at_z.length; i++) {
                  aggregation_challenge.mul_assign(state.v);
                  tmp_fr.assign(proof.wire_values_at_z[i]);
                  tmp_fr.mul_assign(aggregation_challenge);
                  aggregated_value.add_assign(tmp_fr);
              }
              for (uint i = 0; i < proof.permutation_polynomials_at_z.length; i++) {
                  aggregation_challenge.mul_assign(state.v);
                  tmp_fr.assign(proof.permutation_polynomials_at_z[i]);
                  tmp_fr.mul_assign(aggregation_challenge);
                  aggregated_value.add_assign(tmp_fr);
              }
              aggregation_challenge.mul_assign(state.v);
              tmp_fr.assign(proof.grand_product_at_z_omega);
              tmp_fr.mul_assign(aggregation_challenge);
              tmp_fr.mul_assign(state.u);
              aggregated_value.add_assign(tmp_fr);
              aggregation_challenge.mul_assign(state.v);
              tmp_fr.assign(proof.wire_values_at_z_omega[0]);
              tmp_fr.mul_assign(aggregation_challenge);
              tmp_fr.mul_assign(state.u);
              aggregated_value.add_assign(tmp_fr);
              commitment_aggregation.point_sub_assign(PairingsBn254.P1().point_mul(aggregated_value));
              PairingsBn254.G1Point memory pair_with_generator = commitment_aggregation;
              pair_with_generator.point_add_assign(proof.opening_at_z_proof.point_mul(state.z));
              tmp_fr.assign(state.z);
              tmp_fr.mul_assign(vk.omega);
              tmp_fr.mul_assign(state.u);
              pair_with_generator.point_add_assign(proof.opening_at_z_omega_proof.point_mul(tmp_fr));
              PairingsBn254.G1Point memory pair_with_x = proof.opening_at_z_omega_proof.point_mul(state.u);
              pair_with_x.point_add_assign(proof.opening_at_z_proof);
              pair_with_x.negate();
              return PairingsBn254.pairingProd2(pair_with_generator, PairingsBn254.P2(), pair_with_x, vk.g2_x);
          }
          function verify_initial(
              PartialVerifierState memory state,
              Proof memory proof,
              VerificationKey memory vk
          ) internal view returns (bool) {
              require(proof.input_values.length == vk.num_inputs);
              require(vk.num_inputs >= 1);
              TranscriptLibrary.Transcript memory transcript = TranscriptLibrary.new_transcript();
              for (uint256 i = 0; i < vk.num_inputs; i++) {
                  transcript.update_with_u256(proof.input_values[i]);
              }
              for (uint256 i = 0; i < proof.wire_commitments.length; i++) {
                  transcript.update_with_g1(proof.wire_commitments[i]);
              }
              state.beta = transcript.get_challenge();
              state.gamma = transcript.get_challenge();
              transcript.update_with_g1(proof.grand_product_commitment);
              state.alpha = transcript.get_challenge();
              for (uint256 i = 0; i < proof.quotient_poly_commitments.length; i++) {
                  transcript.update_with_g1(proof.quotient_poly_commitments[i]);
              }
              state.z = transcript.get_challenge();
              uint256[] memory lagrange_poly_numbers = new uint256[](vk.num_inputs);
              for (uint256 i = 0; i < lagrange_poly_numbers.length; i++) {
                  lagrange_poly_numbers[i] = i;
              }
              state.cached_lagrange_evals = batch_evaluate_lagrange_poly_out_of_domain(
                  lagrange_poly_numbers,
                  vk.domain_size,
                  vk.omega, state.z
              );
              bool valid = verify_at_z(state, proof, vk);
              if (valid == false) {
                  return false;
              }
              for (uint256 i = 0; i < proof.wire_values_at_z.length; i++) {
                  transcript.update_with_fr(proof.wire_values_at_z[i]);
              }
              for (uint256 i = 0; i < proof.wire_values_at_z_omega.length; i++) {
                  transcript.update_with_fr(proof.wire_values_at_z_omega[i]);
              }
              for (uint256 i = 0; i < proof.permutation_polynomials_at_z.length; i++) {
                  transcript.update_with_fr(proof.permutation_polynomials_at_z[i]);
              }
              transcript.update_with_fr(proof.quotient_polynomial_at_z);
              transcript.update_with_fr(proof.linearization_polynomial_at_z);
              state.v = transcript.get_challenge();
              transcript.update_with_g1(proof.opening_at_z_proof);
              transcript.update_with_g1(proof.opening_at_z_omega_proof);
              state.u = transcript.get_challenge();
              return true;
          }
          // This verifier is for a PLONK with a state width 4
          // and main gate equation
          // q_a(X) * a(X) +
          // q_b(X) * b(X) +
          // q_c(X) * c(X) +
          // q_d(X) * d(X) +
          // q_m(X) * a(X) * b(X) +
          // q_constants(X)+
          // q_d_next(X) * d(X*omega)
          // where q_{}(X) are selectors a, b, c, d - state (witness) polynomials
          // q_d_next(X) "peeks" into the next row of the trace, so it takes
          // the same d(X) polynomial, but shifted
          function verify(Proof memory proof, VerificationKey memory vk) internal view returns (bool) {
              PartialVerifierState memory state;
              bool valid = verify_initial(state, proof, vk);
              if (valid == false) {
                  return false;
              }
              valid = verify_commitments(state, proof, vk);
              return valid;
          }
      }
      contract VerifierWithDeserialize is Plonk4VerifierWithAccessToDNext {
          uint256 constant SERIALIZED_PROOF_LENGTH = 33;
          function deserialize_proof(
              uint256[] memory public_inputs,
              uint256[] memory serialized_proof
          ) internal pure returns(Proof memory proof) {
              require(serialized_proof.length == SERIALIZED_PROOF_LENGTH);
              proof.input_values = new uint256[](public_inputs.length);
              for (uint256 i = 0; i < public_inputs.length; i++) {
                  proof.input_values[i] = public_inputs[i];
              }
              uint256 j = 0;
              for (uint256 i = 0; i < STATE_WIDTH; i++) {
                  proof.wire_commitments[i] = PairingsBn254.new_g1_checked(
                      serialized_proof[j],
                      serialized_proof[j+1]
                  );
                  j += 2;
              }
              proof.grand_product_commitment = PairingsBn254.new_g1_checked(
                  serialized_proof[j],
                  serialized_proof[j+1]
              );
              j += 2;
              for (uint256 i = 0; i < STATE_WIDTH; i++) {
                  proof.quotient_poly_commitments[i] = PairingsBn254.new_g1_checked(
                      serialized_proof[j],
                      serialized_proof[j+1]
                  );
                  j += 2;
              }
              for (uint256 i = 0; i < STATE_WIDTH; i++) {
                  proof.wire_values_at_z[i] = PairingsBn254.new_fr(
                      serialized_proof[j]
                  );
                  j += 1;
              }
              for (uint256 i = 0; i < proof.wire_values_at_z_omega.length; i++) {
                  proof.wire_values_at_z_omega[i] = PairingsBn254.new_fr(
                      serialized_proof[j]
                  );
                  j += 1;
              }
              proof.grand_product_at_z_omega = PairingsBn254.new_fr(
                  serialized_proof[j]
              );
              j += 1;
              proof.quotient_polynomial_at_z = PairingsBn254.new_fr(
                  serialized_proof[j]
              );
              j += 1;
              proof.linearization_polynomial_at_z = PairingsBn254.new_fr(
                  serialized_proof[j]
              );
              j += 1;
              for (uint256 i = 0; i < proof.permutation_polynomials_at_z.length; i++) {
                  proof.permutation_polynomials_at_z[i] = PairingsBn254.new_fr(
                      serialized_proof[j]
                  );
                  j += 1;
              }
              proof.opening_at_z_proof = PairingsBn254.new_g1_checked(
                  serialized_proof[j],
                  serialized_proof[j+1]
              );
              j += 2;
              proof.opening_at_z_omega_proof = PairingsBn254.new_g1_checked(
                  serialized_proof[j],
                  serialized_proof[j+1]
              );
          }
      }