creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "contracts/state-transition/chain-interfaces/IVerifier.sol": {
        "content": "// SPDX-License-Identifier: MIT\n\npragma solidity 0.8.24;\n\n/// @notice Part of the configuration parameters of ZKP circuits\nstruct VerifierParams {\n    bytes32 recursionNodeLevelVkHash;\n    bytes32 recursionLeafLevelVkHash;\n    bytes32 recursionCircuitsSetVksHash;\n}\n\n/// @title The interface of the Verifier contract, responsible for the zero knowledge proof verification.\n/// @author Matter Labs\n/// @custom:security-contact security@matterlabs.dev\ninterface IVerifier {\n    /// @dev Verifies a zk-SNARK proof.\n    /// @return A boolean value indicating whether the zk-SNARK proof is valid.\n    /// Note: The function may revert execution instead of returning false in some cases.\n    function verify(\n        uint256[] calldata _publicInputs,\n        uint256[] calldata _proof,\n        uint256[] calldata _recursiveAggregationInput\n    ) external view returns (bool);\n\n    /// @notice Calculates a keccak256 hash of the runtime loaded verification keys.\n    /// @return vkHash The keccak256 hash of the loaded verification keys.\n    function verificationKeyHash() external pure returns (bytes32);\n}\n"
      },
      "contracts/state-transition/Verifier.sol": {
        "content": "// SPDX-License-Identifier: MIT\n\npragma solidity 0.8.24;\n\nimport {IVerifier} from \"./chain-interfaces/IVerifier.sol\";\n\n/* solhint-disable max-line-length */\n/// @author Matter Labs\n/// @notice Modified version of the Permutations over Lagrange-bases for Oecumenical Noninteractive arguments of\n/// Knowledge (PLONK) verifier.\n/// Modifications have been made to optimize the proof system for zkSync hyperchain circuits.\n/// @dev It uses a custom memory layout inside the inline assembly block. Each reserved memory cell is declared in the\n/// constants below.\n/// @dev For a better understanding of the verifier algorithm please refer to the following papers:\n/// * Original Plonk Article: https://eprint.iacr.org/2019/953.pdf\n/// * Original LookUp Article: https://eprint.iacr.org/2020/315.pdf\n/// * Plonk for zkSync v1.1: https://github.com/matter-labs/solidity_plonk_verifier/raw/recursive/bellman_vk_codegen_recursive/RecursivePlonkUnrolledForEthereum.pdf\n/// The notation used in the code is the same as in the papers.\n/* solhint-enable max-line-length */\ncontract Verifier is IVerifier {\n    /*//////////////////////////////////////////////////////////////\n                             Verification keys\n    //////////////////////////////////////////////////////////////*/\n\n    // Memory slots from 0x000 to 0x200 are reserved for intermediate computations and call to precompiles.\n\n    uint256 internal constant VK_GATE_SETUP_0_X_SLOT = 0x200 + 0x000;\n    uint256 internal constant VK_GATE_SETUP_0_Y_SLOT = 0x200 + 0x020;\n    uint256 internal constant VK_GATE_SETUP_1_X_SLOT = 0x200 + 0x040;\n    uint256 internal constant VK_GATE_SETUP_1_Y_SLOT = 0x200 + 0x060;\n    uint256 internal constant VK_GATE_SETUP_2_X_SLOT = 0x200 + 0x080;\n    uint256 internal constant VK_GATE_SETUP_2_Y_SLOT = 0x200 + 0x0a0;\n    uint256 internal constant VK_GATE_SETUP_3_X_SLOT = 0x200 + 0x0c0;\n    uint256 internal constant VK_GATE_SETUP_3_Y_SLOT = 0x200 + 0x0e0;\n    uint256 internal constant VK_GATE_SETUP_4_X_SLOT = 0x200 + 0x100;\n    uint256 internal constant VK_GATE_SETUP_4_Y_SLOT = 0x200 + 0x120;\n    uint256 internal constant VK_GATE_SETUP_5_X_SLOT = 0x200 + 0x140;\n    uint256 internal constant VK_GATE_SETUP_5_Y_SLOT = 0x200 + 0x160;\n    uint256 internal constant VK_GATE_SETUP_6_X_SLOT = 0x200 + 0x180;\n    uint256 internal constant VK_GATE_SETUP_6_Y_SLOT = 0x200 + 0x1a0;\n    uint256 internal constant VK_GATE_SETUP_7_X_SLOT = 0x200 + 0x1c0;\n    uint256 internal constant VK_GATE_SETUP_7_Y_SLOT = 0x200 + 0x1e0;\n\n    uint256 internal constant VK_GATE_SELECTORS_0_X_SLOT = 0x200 + 0x200;\n    uint256 internal constant VK_GATE_SELECTORS_0_Y_SLOT = 0x200 + 0x220;\n    uint256 internal constant VK_GATE_SELECTORS_1_X_SLOT = 0x200 + 0x240;\n    uint256 internal constant VK_GATE_SELECTORS_1_Y_SLOT = 0x200 + 0x260;\n\n    uint256 internal constant VK_PERMUTATION_0_X_SLOT = 0x200 + 0x280;\n    uint256 internal constant VK_PERMUTATION_0_Y_SLOT = 0x200 + 0x2a0;\n    uint256 internal constant VK_PERMUTATION_1_X_SLOT = 0x200 + 0x2c0;\n    uint256 internal constant VK_PERMUTATION_1_Y_SLOT = 0x200 + 0x2e0;\n    uint256 internal constant VK_PERMUTATION_2_X_SLOT = 0x200 + 0x300;\n    uint256 internal constant VK_PERMUTATION_2_Y_SLOT = 0x200 + 0x320;\n    uint256 internal constant VK_PERMUTATION_3_X_SLOT = 0x200 + 0x340;\n    uint256 internal constant VK_PERMUTATION_3_Y_SLOT = 0x200 + 0x360;\n\n    uint256 internal constant VK_LOOKUP_SELECTOR_X_SLOT = 0x200 + 0x380;\n    uint256 internal constant VK_LOOKUP_SELECTOR_Y_SLOT = 0x200 + 0x3a0;\n\n    uint256 internal constant VK_LOOKUP_TABLE_0_X_SLOT = 0x200 + 0x3c0;\n    uint256 internal constant VK_LOOKUP_TABLE_0_Y_SLOT = 0x200 + 0x3e0;\n    uint256 internal constant VK_LOOKUP_TABLE_1_X_SLOT = 0x200 + 0x400;\n    uint256 internal constant VK_LOOKUP_TABLE_1_Y_SLOT = 0x200 + 0x420;\n    uint256 internal constant VK_LOOKUP_TABLE_2_X_SLOT = 0x200 + 0x440;\n    uint256 internal constant VK_LOOKUP_TABLE_2_Y_SLOT = 0x200 + 0x460;\n    uint256 internal constant VK_LOOKUP_TABLE_3_X_SLOT = 0x200 + 0x480;\n    uint256 internal constant VK_LOOKUP_TABLE_3_Y_SLOT = 0x200 + 0x4a0;\n\n    uint256 internal constant VK_LOOKUP_TABLE_TYPE_X_SLOT = 0x200 + 0x4c0;\n    uint256 internal constant VK_LOOKUP_TABLE_TYPE_Y_SLOT = 0x200 + 0x4e0;\n\n    uint256 internal constant VK_RECURSIVE_FLAG_SLOT = 0x200 + 0x500;\n\n    /*//////////////////////////////////////////////////////////////\n                             Proof\n    //////////////////////////////////////////////////////////////*/\n\n    uint256 internal constant PROOF_PUBLIC_INPUT = 0x200 + 0x520 + 0x000;\n\n    uint256 internal constant PROOF_STATE_POLYS_0_X_SLOT = 0x200 + 0x520 + 0x020;\n    uint256 internal constant PROOF_STATE_POLYS_0_Y_SLOT = 0x200 + 0x520 + 0x040;\n    uint256 internal constant PROOF_STATE_POLYS_1_X_SLOT = 0x200 + 0x520 + 0x060;\n    uint256 internal constant PROOF_STATE_POLYS_1_Y_SLOT = 0x200 + 0x520 + 0x080;\n    uint256 internal constant PROOF_STATE_POLYS_2_X_SLOT = 0x200 + 0x520 + 0x0a0;\n    uint256 internal constant PROOF_STATE_POLYS_2_Y_SLOT = 0x200 + 0x520 + 0x0c0;\n    uint256 internal constant PROOF_STATE_POLYS_3_X_SLOT = 0x200 + 0x520 + 0x0e0;\n    uint256 internal constant PROOF_STATE_POLYS_3_Y_SLOT = 0x200 + 0x520 + 0x100;\n\n    uint256 internal constant PROOF_COPY_PERMUTATION_GRAND_PRODUCT_X_SLOT = 0x200 + 0x520 + 0x120;\n    uint256 internal constant PROOF_COPY_PERMUTATION_GRAND_PRODUCT_Y_SLOT = 0x200 + 0x520 + 0x140;\n\n    uint256 internal constant PROOF_LOOKUP_S_POLY_X_SLOT = 0x200 + 0x520 + 0x160;\n    uint256 internal constant PROOF_LOOKUP_S_POLY_Y_SLOT = 0x200 + 0x520 + 0x180;\n\n    uint256 internal constant PROOF_LOOKUP_GRAND_PRODUCT_X_SLOT = 0x200 + 0x520 + 0x1a0;\n    uint256 internal constant PROOF_LOOKUP_GRAND_PRODUCT_Y_SLOT = 0x200 + 0x520 + 0x1c0;\n\n    uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_0_X_SLOT = 0x200 + 0x520 + 0x1e0;\n    uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_0_Y_SLOT = 0x200 + 0x520 + 0x200;\n    uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_1_X_SLOT = 0x200 + 0x520 + 0x220;\n    uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_1_Y_SLOT = 0x200 + 0x520 + 0x240;\n    uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_2_X_SLOT = 0x200 + 0x520 + 0x260;\n    uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_2_Y_SLOT = 0x200 + 0x520 + 0x280;\n    uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_3_X_SLOT = 0x200 + 0x520 + 0x2a0;\n    uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_3_Y_SLOT = 0x200 + 0x520 + 0x2c0;\n\n    uint256 internal constant PROOF_STATE_POLYS_0_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x2e0;\n    uint256 internal constant PROOF_STATE_POLYS_1_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x300;\n    uint256 internal constant PROOF_STATE_POLYS_2_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x320;\n    uint256 internal constant PROOF_STATE_POLYS_3_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x340;\n\n    uint256 internal constant PROOF_STATE_POLYS_3_OPENING_AT_Z_OMEGA_SLOT = 0x200 + 0x520 + 0x360;\n    uint256 internal constant PROOF_GATE_SELECTORS_0_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x380;\n\n    uint256 internal constant PROOF_COPY_PERMUTATION_POLYS_0_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x3a0;\n    uint256 internal constant PROOF_COPY_PERMUTATION_POLYS_1_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x3c0;\n    uint256 internal constant PROOF_COPY_PERMUTATION_POLYS_2_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x3e0;\n\n    uint256 internal constant PROOF_COPY_PERMUTATION_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT = 0x200 + 0x520 + 0x400;\n    uint256 internal constant PROOF_LOOKUP_S_POLY_OPENING_AT_Z_OMEGA_SLOT = 0x200 + 0x520 + 0x420;\n    uint256 internal constant PROOF_LOOKUP_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT = 0x200 + 0x520 + 0x440;\n    uint256 internal constant PROOF_LOOKUP_T_POLY_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x460;\n    uint256 internal constant PROOF_LOOKUP_T_POLY_OPENING_AT_Z_OMEGA_SLOT = 0x200 + 0x520 + 0x480;\n    uint256 internal constant PROOF_LOOKUP_SELECTOR_POLY_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x4a0;\n    uint256 internal constant PROOF_LOOKUP_TABLE_TYPE_POLY_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x4c0;\n    uint256 internal constant PROOF_QUOTIENT_POLY_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x4e0;\n    uint256 internal constant PROOF_LINEARISATION_POLY_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x500;\n\n    uint256 internal constant PROOF_OPENING_PROOF_AT_Z_X_SLOT = 0x200 + 0x520 + 0x520;\n    uint256 internal constant PROOF_OPENING_PROOF_AT_Z_Y_SLOT = 0x200 + 0x520 + 0x540;\n    uint256 internal constant PROOF_OPENING_PROOF_AT_Z_OMEGA_X_SLOT = 0x200 + 0x520 + 0x560;\n    uint256 internal constant PROOF_OPENING_PROOF_AT_Z_OMEGA_Y_SLOT = 0x200 + 0x520 + 0x580;\n\n    uint256 internal constant PROOF_RECURSIVE_PART_P1_X_SLOT = 0x200 + 0x520 + 0x5a0;\n    uint256 internal constant PROOF_RECURSIVE_PART_P1_Y_SLOT = 0x200 + 0x520 + 0x5c0;\n\n    uint256 internal constant PROOF_RECURSIVE_PART_P2_X_SLOT = 0x200 + 0x520 + 0x5e0;\n    uint256 internal constant PROOF_RECURSIVE_PART_P2_Y_SLOT = 0x200 + 0x520 + 0x600;\n\n    /*//////////////////////////////////////////////////////////////\n                             Transcript slot\n    //////////////////////////////////////////////////////////////*/\n\n    uint256 internal constant TRANSCRIPT_BEGIN_SLOT = 0x200 + 0x520 + 0x620 + 0x00;\n    uint256 internal constant TRANSCRIPT_DST_BYTE_SLOT = 0x200 + 0x520 + 0x620 + 0x03;\n    uint256 internal constant TRANSCRIPT_STATE_0_SLOT = 0x200 + 0x520 + 0x620 + 0x04;\n    uint256 internal constant TRANSCRIPT_STATE_1_SLOT = 0x200 + 0x520 + 0x620 + 0x24;\n    uint256 internal constant TRANSCRIPT_CHALLENGE_SLOT = 0x200 + 0x520 + 0x620 + 0x44;\n\n    /*//////////////////////////////////////////////////////////////\n                             Partial verifier state\n    //////////////////////////////////////////////////////////////*/\n\n    uint256 internal constant STATE_ALPHA_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x000;\n    uint256 internal constant STATE_BETA_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x020;\n    uint256 internal constant STATE_GAMMA_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x040;\n    uint256 internal constant STATE_POWER_OF_ALPHA_2_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x060;\n    uint256 internal constant STATE_POWER_OF_ALPHA_3_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x080;\n    uint256 internal constant STATE_POWER_OF_ALPHA_4_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x0a0;\n    uint256 internal constant STATE_POWER_OF_ALPHA_5_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x0c0;\n    uint256 internal constant STATE_POWER_OF_ALPHA_6_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x0e0;\n    uint256 internal constant STATE_POWER_OF_ALPHA_7_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x100;\n    uint256 internal constant STATE_POWER_OF_ALPHA_8_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x120;\n    uint256 internal constant STATE_ETA_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x140;\n    uint256 internal constant STATE_BETA_LOOKUP_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x160;\n    uint256 internal constant STATE_GAMMA_LOOKUP_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x180;\n    uint256 internal constant STATE_BETA_PLUS_ONE_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x1a0;\n    uint256 internal constant STATE_BETA_GAMMA_PLUS_GAMMA_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x1c0;\n    uint256 internal constant STATE_V_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x1e0;\n    uint256 internal constant STATE_U_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x200;\n    uint256 internal constant STATE_Z_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x220;\n    uint256 internal constant STATE_Z_MINUS_LAST_OMEGA_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x240;\n    uint256 internal constant STATE_L_0_AT_Z_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x260;\n    uint256 internal constant STATE_L_N_MINUS_ONE_AT_Z_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x280;\n    uint256 internal constant STATE_Z_IN_DOMAIN_SIZE = 0x200 + 0x520 + 0x620 + 0x80 + 0x2a0;\n\n    /*//////////////////////////////////////////////////////////////\n                             Queries\n    //////////////////////////////////////////////////////////////*/\n\n    uint256 internal constant QUERIES_BUFFER_POINT_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x00;\n\n    uint256 internal constant QUERIES_AT_Z_0_X_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x40;\n    uint256 internal constant QUERIES_AT_Z_0_Y_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x60;\n    uint256 internal constant QUERIES_AT_Z_1_X_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x80;\n    uint256 internal constant QUERIES_AT_Z_1_Y_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0xa0;\n\n    uint256 internal constant QUERIES_T_POLY_AGGREGATED_X_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0xc0;\n    uint256 internal constant QUERIES_T_POLY_AGGREGATED_Y_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0xe0;\n\n    /*//////////////////////////////////////////////////////////////\n                             Aggregated commitment\n    //////////////////////////////////////////////////////////////*/\n\n    uint256 internal constant AGGREGATED_AT_Z_X_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x00;\n    uint256 internal constant AGGREGATED_AT_Z_Y_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x20;\n\n    uint256 internal constant AGGREGATED_AT_Z_OMEGA_X_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x40;\n    uint256 internal constant AGGREGATED_AT_Z_OMEGA_Y_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x60;\n\n    uint256 internal constant AGGREGATED_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x80;\n    uint256 internal constant AGGREGATED_OPENING_AT_Z_OMEGA_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0xa0;\n\n    /*//////////////////////////////////////////////////////////////\n                             Pairing data\n    //////////////////////////////////////////////////////////////*/\n\n    uint256 internal constant PAIRING_BUFFER_POINT_X_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0xc0 + 0x00;\n    uint256 internal constant PAIRING_BUFFER_POINT_Y_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0xc0 + 0x20;\n\n    uint256 internal constant PAIRING_PAIR_WITH_GENERATOR_X_SLOT =\n        0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0xc0 + 0x40;\n    uint256 internal constant PAIRING_PAIR_WITH_GENERATOR_Y_SLOT =\n        0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0xc0 + 0x60;\n\n    uint256 internal constant PAIRING_PAIR_WITH_X_X_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x100 + 0x80;\n    uint256 internal constant PAIRING_PAIR_WITH_X_Y_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x100 + 0xa0;\n\n    /*//////////////////////////////////////////////////////////////\n               Slots for scalar multiplication optimizations\n    //////////////////////////////////////////////////////////////*/\n\n    uint256 internal constant COPY_PERMUTATION_FIRST_AGGREGATED_COMMITMENT_COEFF =\n        0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x100 + 0xc0;\n    uint256 internal constant LOOKUP_GRAND_PRODUCT_FIRST_AGGREGATED_COMMITMENT_COEFF =\n        0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x100 + 0xe0;\n    uint256 internal constant LOOKUP_S_FIRST_AGGREGATED_COMMITMENT_COEFF =\n        0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x100 + 0x100;\n\n    /*//////////////////////////////////////////////////////////////\n                             Constants\n    //////////////////////////////////////////////////////////////*/\n\n    uint256 internal constant OMEGA = 0x1951441010b2b95a6e47a6075066a50a036f5ba978c050f2821df86636c0facb;\n    uint256 internal constant DOMAIN_SIZE = 0x1000000; // 2^24\n    uint256 internal constant Q_MOD = 21888242871839275222246405745257275088696311157297823662689037894645226208583;\n    uint256 internal constant R_MOD = 21888242871839275222246405745257275088548364400416034343698204186575808495617;\n\n    /// @dev flip of 0xe000000000000000000000000000000000000000000000000000000000000000;\n    uint256 internal constant FR_MASK = 0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;\n\n    // non residues\n    uint256 internal constant NON_RESIDUES_0 = 0x05;\n    uint256 internal constant NON_RESIDUES_1 = 0x07;\n    uint256 internal constant NON_RESIDUES_2 = 0x0a;\n\n    // trusted setup g2 elements\n    uint256 internal constant G2_ELEMENTS_0_X1 = 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2;\n    uint256 internal constant G2_ELEMENTS_0_X2 = 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed;\n    uint256 internal constant G2_ELEMENTS_0_Y1 = 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b;\n    uint256 internal constant G2_ELEMENTS_0_Y2 = 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa;\n    uint256 internal constant G2_ELEMENTS_1_X1 = 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1;\n    uint256 internal constant G2_ELEMENTS_1_X2 = 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0;\n    uint256 internal constant G2_ELEMENTS_1_Y1 = 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4;\n    uint256 internal constant G2_ELEMENTS_1_Y2 = 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55;\n\n    /// @inheritdoc IVerifier\n    function verificationKeyHash() external pure returns (bytes32 vkHash) {\n        _loadVerificationKey();\n\n        assembly {\n            let start := VK_GATE_SETUP_0_X_SLOT\n            let end := VK_RECURSIVE_FLAG_SLOT\n            let length := add(sub(end, start), 0x20)\n\n            vkHash := keccak256(start, length)\n        }\n    }\n\n    /// @notice Load verification keys to memory in runtime.\n    /// @dev The constants are loaded into memory in a specific layout declared in the constants starting from\n    /// `VK_` prefix.\n    /// NOTE: Function may corrupt the memory state if some memory was used before this function was called.\n    /// The VK consists of commitments to setup polynomials:\n    /// [q_a], [q_b], [q_c], [q_d],                  - main gate setup commitments\n    /// [q_{d_next}], [q_ab], [q_ac], [q_const]      /\n    /// [main_gate_selector], [custom_gate_selector] - gate selectors commitments\n    /// [sigma_0], [sigma_1], [sigma_2], [sigma_3]   - permutation polynomials commitments\n    /// [lookup_selector]                            - lookup selector commitment\n    /// [col_0], [col_1], [col_2], [col_3]           - lookup columns commitments\n    /// [table_type]                                 - lookup table type commitment\n    function _loadVerificationKey() internal pure virtual {\n        assembly {\n            // gate setup commitments\n            mstore(VK_GATE_SETUP_0_X_SLOT, 0x110deb1e0863737f9a3d7b4de641a03aa00a77bc9f1a05acc9d55b76ab9fdd4d)\n            mstore(VK_GATE_SETUP_0_Y_SLOT, 0x2c9dc252441e9298b7f6df6335a252517b7bccb924adf537b87c5cd3383fd7a9)\n            mstore(VK_GATE_SETUP_1_X_SLOT, 0x04659caf7b05471ba5ba85b1ab62267aa6c456836e625f169f7119d55b9462d2)\n            mstore(VK_GATE_SETUP_1_Y_SLOT, 0x0ea63403692148d2ad22189a1e5420076312f4d46e62036a043a6b0b84d5b410)\n            mstore(VK_GATE_SETUP_2_X_SLOT, 0x0e6696d09d65fce1e42805be03fca1f14aea247281f688981f925e77d4ce2291)\n            mstore(VK_GATE_SETUP_2_Y_SLOT, 0x0228f6cf8fe20c1e07e5b78bf8c41d50e55975a126d22a198d1e56acd4bbb3dd)\n            mstore(VK_GATE_SETUP_3_X_SLOT, 0x14685dafe340b1dec5eafcd5e7faddaf24f3781ddc53309cc25d0b42c00541dd)\n            mstore(VK_GATE_SETUP_3_Y_SLOT, 0x0e651cff9447cb360198899b80fa23e89ec13bc94ff161729aa841d2b55ea5be)\n            mstore(VK_GATE_SETUP_4_X_SLOT, 0x16e9ef76cb68f2750eb0ee72382dd9911a982308d0ab10ef94dada13c382ae73)\n            mstore(VK_GATE_SETUP_4_Y_SLOT, 0x22e404bc91350f3bc7daad1d1025113742436983c85eac5ab7b42221a181b81e)\n            mstore(VK_GATE_SETUP_5_X_SLOT, 0x0d9b29613037a5025655c82b143d2b7449c98f3aea358307c8529249cc54f3b9)\n            mstore(VK_GATE_SETUP_5_Y_SLOT, 0x15b3c4c946ad1babfc4c03ff7c2423fd354af3a9305c499b7fb3aaebe2fee746)\n            mstore(VK_GATE_SETUP_6_X_SLOT, 0x2a4cb6c495dbc7201142cc773da895ae2046e790073988fb850aca6aead27b8a)\n            mstore(VK_GATE_SETUP_6_Y_SLOT, 0x28ef9200c3cb67da82030520d640292014f5f7c2e2909da608812e04671a3acf)\n            mstore(VK_GATE_SETUP_7_X_SLOT, 0x283344a1ab3e55ecfd904d0b8e9f4faea338df5a4ead2fa9a42f0e103da40abc)\n            mstore(VK_GATE_SETUP_7_Y_SLOT, 0x223b37b83b9687512d322993edd70e508dd80adb10bcf7321a3cc8a44c269521)\n\n            // gate selectors commitments\n            mstore(VK_GATE_SELECTORS_0_X_SLOT, 0x1f67f0ba5f7e837bc680acb4e612ebd938ad35211aa6e05b96cad19e66b82d2d)\n            mstore(VK_GATE_SELECTORS_0_Y_SLOT, 0x2820641a84d2e8298ac2ac42bd4b912c0c37f768ecc83d3a29e7c720763d15a1)\n            mstore(VK_GATE_SELECTORS_1_X_SLOT, 0x0353257957562270292a17860ca8e8827703f828f440ee004848b1e23fdf9de2)\n            mstore(VK_GATE_SELECTORS_1_Y_SLOT, 0x305f4137fee253dff8b2bfe579038e8f25d5bd217865072af5d89fc8800ada24)\n\n            // permutation commitments\n            mstore(VK_PERMUTATION_0_X_SLOT, 0x13a600154b369ff3237706d00948e465ee1c32c7a6d3e18bccd9c4a15910f2e5)\n            mstore(VK_PERMUTATION_0_Y_SLOT, 0x138aa24fbf4cdddc75114811b3d59040394c218ecef3eb46ef9bd646f7e53776)\n            mstore(VK_PERMUTATION_1_X_SLOT, 0x277fff1f80c409357e2d251d79f6e3fd2164b755ce69cfd72de5c690289df662)\n            mstore(VK_PERMUTATION_1_Y_SLOT, 0x25235588e28c70eea3e35531c80deac25cd9b53ea3f98993f120108bc7abf670)\n            mstore(VK_PERMUTATION_2_X_SLOT, 0x0990e07a9b001048b947d0e5bd6157214c7359b771f01bf52bd771ba563a900e)\n            mstore(VK_PERMUTATION_2_Y_SLOT, 0x05e5fb090dd40914c8606d875e301167ae3047d684a02b44d9d36f1eaf43d0b4)\n            mstore(VK_PERMUTATION_3_X_SLOT, 0x1d4656690b33299db5631401a282afab3e16c78ee2c9ad9efea628171dcbc6bc)\n            mstore(VK_PERMUTATION_3_Y_SLOT, 0x0ebda2ebe582f601f813ec1e3970d13ef1500c742a85cce9b7f190f333de03b0)\n\n            // lookup tables commitments\n            mstore(VK_LOOKUP_TABLE_0_X_SLOT, 0x2c513ed74d9d57a5ec901e074032741036353a2c4513422e96e7b53b302d765b)\n            mstore(VK_LOOKUP_TABLE_0_Y_SLOT, 0x04dd964427e430f16004076d708c0cb21e225056cc1d57418cfbd3d472981468)\n            mstore(VK_LOOKUP_TABLE_1_X_SLOT, 0x1ea83e5e65c6f8068f4677e2911678cf329b28259642a32db1f14b8347828aac)\n            mstore(VK_LOOKUP_TABLE_1_Y_SLOT, 0x1d22bc884a2da4962a893ba8de13f57aaeb785ed52c5e686994839cab8f7475d)\n            mstore(VK_LOOKUP_TABLE_2_X_SLOT, 0x0b2e7212d0d9cff26d0bdf3d79b2cac029a25dfeb1cafdf49e2349d7db348d89)\n            mstore(VK_LOOKUP_TABLE_2_Y_SLOT, 0x1301f9b252419ea240eb67fda720ca0b16d92364027285f95e9b1349490fa283)\n            mstore(VK_LOOKUP_TABLE_3_X_SLOT, 0x02f7b99fdfa5b418548c2d777785820e02383cfc87e7085e280a375a358153bf)\n            mstore(VK_LOOKUP_TABLE_3_Y_SLOT, 0x09d004fe08dc4d19c382df36fad22ef676185663543703e6a4b40203e50fd8a6)\n\n            // lookup selector commitment\n            mstore(VK_LOOKUP_SELECTOR_X_SLOT, 0x2f4d347c7fb61daaadfff881e24f4b5dcfdc0d70a95bcb148168b90ef93e0007)\n            mstore(VK_LOOKUP_SELECTOR_Y_SLOT, 0x2322632465ba8e28cd0a4befd813ea85a972f4f6fa8e8603cf5d062dbcb14065)\n\n            // table type commitment\n            mstore(VK_LOOKUP_TABLE_TYPE_X_SLOT, 0x1e3c9fc98c118e4bc34f1f93d214a5d86898e980c40d8e2c180c6ada377a7467)\n            mstore(VK_LOOKUP_TABLE_TYPE_Y_SLOT, 0x2260a13535c35a15c173f5e5797d4b675b55d164a9995bfb7624971324bd84a8)\n\n            // flag for using recursive part\n            mstore(VK_RECURSIVE_FLAG_SLOT, 0)\n        }\n    }\n\n    /// @inheritdoc IVerifier\n    function verify(\n        uint256[] calldata, // _publicInputs\n        uint256[] calldata, // _proof\n        uint256[] calldata // _recursiveAggregationInput\n    ) public view virtual returns (bool) {\n        // No memory was accessed yet, so keys can be loaded into the right place and not corrupt any other memory.\n        _loadVerificationKey();\n\n        // Beginning of the big inline assembly block that makes all the verification work.\n        // Note: We use the custom memory layout, so the return value should be returned from the assembly, not\n        // Solidity code.\n        assembly {\n            /*//////////////////////////////////////////////////////////////\n                                    Utils\n            //////////////////////////////////////////////////////////////*/\n\n            /// @dev Reverts execution with a provided revert reason.\n            /// @param len The byte length of the error message string, which is expected to be no more than 32.\n            /// @param reason The 1-word revert reason string, encoded in ASCII.\n            function revertWithMessage(len, reason) {\n                // \"Error(string)\" signature: bytes32(bytes4(keccak256(\"Error(string)\")))\n                mstore(0x00, 0x08c379a000000000000000000000000000000000000000000000000000000000)\n                // Data offset\n                mstore(0x04, 0x0000000000000000000000000000000000000000000000000000000000000020)\n                // Length of revert string\n                mstore(0x24, len)\n                // Revert reason\n                mstore(0x44, reason)\n                // Revert\n                revert(0x00, 0x64)\n            }\n\n            /// @dev Performs modular exponentiation using the formula (value ^ power) mod R_MOD.\n            function modexp(value, power) -> res {\n                mstore(0x00, 0x20)\n                mstore(0x20, 0x20)\n                mstore(0x40, 0x20)\n                mstore(0x60, value)\n                mstore(0x80, power)\n                mstore(0xa0, R_MOD)\n                if iszero(staticcall(gas(), 5, 0, 0xc0, 0x00, 0x20)) {\n                    revertWithMessage(24, \"modexp precompile failed\")\n                }\n                res := mload(0x00)\n            }\n\n            /// @dev Performs a point multiplication operation and stores the result in a given memory destination.\n            function pointMulIntoDest(point, s, dest) {\n                mstore(0x00, mload(point))\n                mstore(0x20, mload(add(point, 0x20)))\n                mstore(0x40, s)\n                if iszero(staticcall(gas(), 7, 0, 0x60, dest, 0x40)) {\n                    revertWithMessage(30, \"pointMulIntoDest: ecMul failed\")\n                }\n            }\n\n            /// @dev Performs a point addition operation and stores the result in a given memory destination.\n            function pointAddIntoDest(p1, p2, dest) {\n                mstore(0x00, mload(p1))\n                mstore(0x20, mload(add(p1, 0x20)))\n                mstore(0x40, mload(p2))\n                mstore(0x60, mload(add(p2, 0x20)))\n                if iszero(staticcall(gas(), 6, 0x00, 0x80, dest, 0x40)) {\n                    revertWithMessage(30, \"pointAddIntoDest: ecAdd failed\")\n                }\n            }\n\n            /// @dev Performs a point subtraction operation and updates the first point with the result.\n            function pointSubAssign(p1, p2) {\n                mstore(0x00, mload(p1))\n                mstore(0x20, mload(add(p1, 0x20)))\n                mstore(0x40, mload(p2))\n                mstore(0x60, sub(Q_MOD, mload(add(p2, 0x20))))\n                if iszero(staticcall(gas(), 6, 0x00, 0x80, p1, 0x40)) {\n                    revertWithMessage(28, \"pointSubAssign: ecAdd failed\")\n                }\n            }\n\n            /// @dev Performs a point addition operation and updates the first point with the result.\n            function pointAddAssign(p1, p2) {\n                mstore(0x00, mload(p1))\n                mstore(0x20, mload(add(p1, 0x20)))\n                mstore(0x40, mload(p2))\n                mstore(0x60, mload(add(p2, 0x20)))\n                if iszero(staticcall(gas(), 6, 0x00, 0x80, p1, 0x40)) {\n                    revertWithMessage(28, \"pointAddAssign: ecAdd failed\")\n                }\n            }\n\n            /// @dev Performs a point multiplication operation and then adds the result to the destination point.\n            function pointMulAndAddIntoDest(point, s, dest) {\n                mstore(0x00, mload(point))\n                mstore(0x20, mload(add(point, 0x20)))\n                mstore(0x40, s)\n                let success := staticcall(gas(), 7, 0, 0x60, 0, 0x40)\n\n                mstore(0x40, mload(dest))\n                mstore(0x60, mload(add(dest, 0x20)))\n                success := and(success, staticcall(gas(), 6, 0x00, 0x80, dest, 0x40))\n\n                if iszero(success) {\n                    revertWithMessage(22, \"pointMulAndAddIntoDest\")\n                }\n            }\n\n            /// @dev Negates an elliptic curve point by changing the sign of the y-coordinate.\n            function pointNegate(point) {\n                let pY := mload(add(point, 0x20))\n                switch pY\n                case 0 {\n                    if mload(point) {\n                        revertWithMessage(26, \"pointNegate: invalid point\")\n                    }\n                }\n                default {\n                    mstore(add(point, 0x20), sub(Q_MOD, pY))\n                }\n            }\n\n            /*//////////////////////////////////////////////////////////////\n                                    Transcript helpers\n            //////////////////////////////////////////////////////////////*/\n\n            /// @dev Updates the transcript state with a new challenge value.\n            function updateTranscript(value) {\n                mstore8(TRANSCRIPT_DST_BYTE_SLOT, 0x00)\n                mstore(TRANSCRIPT_CHALLENGE_SLOT, value)\n                let newState0 := keccak256(TRANSCRIPT_BEGIN_SLOT, 0x64)\n                mstore8(TRANSCRIPT_DST_BYTE_SLOT, 0x01)\n                let newState1 := keccak256(TRANSCRIPT_BEGIN_SLOT, 0x64)\n                mstore(TRANSCRIPT_STATE_1_SLOT, newState1)\n                mstore(TRANSCRIPT_STATE_0_SLOT, newState0)\n            }\n\n            /// @dev Retrieves a transcript challenge.\n            function getTranscriptChallenge(numberOfChallenge) -> challenge {\n                mstore8(TRANSCRIPT_DST_BYTE_SLOT, 0x02)\n                mstore(TRANSCRIPT_CHALLENGE_SLOT, shl(224, numberOfChallenge))\n                challenge := and(keccak256(TRANSCRIPT_BEGIN_SLOT, 0x48), FR_MASK)\n            }\n\n            /*//////////////////////////////////////////////////////////////\n                                    1. Load Proof\n            //////////////////////////////////////////////////////////////*/\n\n            /// @dev This function loads a zk-SNARK proof, ensures it's properly formatted, and stores it in memory.\n            /// It ensures the number of inputs and the elliptic curve point's validity.\n            /// Note: It does NOT reject inputs that exceed these module sizes, but rather wraps them within the\n            /// module bounds.\n            /// The proof consists of:\n            /// 1. Public input: (1 field element from F_r)\n            ///\n            /// 2. Polynomial commitments (elliptic curve points over F_q):\n            ///     [a], [b], [c], [d]         - state polynomials commitments\n            ///     [z_perm]                   - copy-permutation grand product commitment\n            ///     [s]                        - polynomial for lookup argument commitment\n            ///     [z_lookup]                 - lookup grand product commitment\n            ///     [t_0], [t_1], [t_2], [t_3] - quotient polynomial parts commitments\n            ///     [W], [W']                  - proof openings commitments\n            ///\n            /// 3. Polynomial evaluations at z and z*omega (field elements from F_r):\n            ///     t(z)                                  - quotient polynomial opening\n            ///     a(z), b(z), c(z), d(z), d(z*omega)    - state polynomials openings\n            ///     main_gate_selector(z)                 - main gate selector opening\n            ///     sigma_0(z), sigma_1(z), sigma_2(z)    - permutation polynomials openings\n            ///     z_perm(z*omega)                       - copy-permutation grand product opening\n            ///     z_lookup(z*omega)                     - lookup grand product opening\n            ///     lookup_selector(z)                    - lookup selector opening\n            ///     s(x*omega), t(z*omega), table_type(z) - lookup argument polynomial openings\n            ///     r(z)                                  - linearisation polynomial opening\n            ///\n            /// 4. Recursive proof (0 or 2 elliptic curve points over F_q)\n            function loadProof() {\n                // 1. Load public input\n                let offset := calldataload(0x04)\n                let publicInputLengthInWords := calldataload(add(offset, 0x04))\n                let isValid := eq(publicInputLengthInWords, 1) // We expect only one public input\n                mstore(PROOF_PUBLIC_INPUT, and(calldataload(add(offset, 0x24)), FR_MASK))\n\n                // 2. Load the proof (except for the recursive part)\n                offset := calldataload(0x24)\n                let proofLengthInWords := calldataload(add(offset, 0x04))\n                isValid := and(eq(proofLengthInWords, 44), isValid)\n\n                // PROOF_STATE_POLYS_0\n                {\n                    let x := mod(calldataload(add(offset, 0x024)), Q_MOD)\n                    let y := mod(calldataload(add(offset, 0x044)), Q_MOD)\n                    let xx := mulmod(x, x, Q_MOD)\n                    isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid)\n                    mstore(PROOF_STATE_POLYS_0_X_SLOT, x)\n                    mstore(PROOF_STATE_POLYS_0_Y_SLOT, y)\n                }\n                // PROOF_STATE_POLYS_1\n                {\n                    let x := mod(calldataload(add(offset, 0x064)), Q_MOD)\n                    let y := mod(calldataload(add(offset, 0x084)), Q_MOD)\n                    let xx := mulmod(x, x, Q_MOD)\n                    isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid)\n                    mstore(PROOF_STATE_POLYS_1_X_SLOT, x)\n                    mstore(PROOF_STATE_POLYS_1_Y_SLOT, y)\n                }\n                // PROOF_STATE_POLYS_2\n                {\n                    let x := mod(calldataload(add(offset, 0x0a4)), Q_MOD)\n                    let y := mod(calldataload(add(offset, 0x0c4)), Q_MOD)\n                    let xx := mulmod(x, x, Q_MOD)\n                    isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid)\n                    mstore(PROOF_STATE_POLYS_2_X_SLOT, x)\n                    mstore(PROOF_STATE_POLYS_2_Y_SLOT, y)\n                }\n                // PROOF_STATE_POLYS_3\n                {\n                    let x := mod(calldataload(add(offset, 0x0e4)), Q_MOD)\n                    let y := mod(calldataload(add(offset, 0x104)), Q_MOD)\n                    let xx := mulmod(x, x, Q_MOD)\n                    isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid)\n                    mstore(PROOF_STATE_POLYS_3_X_SLOT, x)\n                    mstore(PROOF_STATE_POLYS_3_Y_SLOT, y)\n                }\n                // PROOF_COPY_PERMUTATION_GRAND_PRODUCT\n                {\n                    let x := mod(calldataload(add(offset, 0x124)), Q_MOD)\n                    let y := mod(calldataload(add(offset, 0x144)), Q_MOD)\n                    let xx := mulmod(x, x, Q_MOD)\n                    isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid)\n                    mstore(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_X_SLOT, x)\n                    mstore(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_Y_SLOT, y)\n                }\n                // PROOF_LOOKUP_S_POLY\n                {\n                    let x := mod(calldataload(add(offset, 0x164)), Q_MOD)\n                    let y := mod(calldataload(add(offset, 0x184)), Q_MOD)\n                    let xx := mulmod(x, x, Q_MOD)\n                    isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid)\n                    mstore(PROOF_LOOKUP_S_POLY_X_SLOT, x)\n                    mstore(PROOF_LOOKUP_S_POLY_Y_SLOT, y)\n                }\n                // PROOF_LOOKUP_GRAND_PRODUCT\n                {\n                    let x := mod(calldataload(add(offset, 0x1a4)), Q_MOD)\n                    let y := mod(calldataload(add(offset, 0x1c4)), Q_MOD)\n                    let xx := mulmod(x, x, Q_MOD)\n                    isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid)\n                    mstore(PROOF_LOOKUP_GRAND_PRODUCT_X_SLOT, x)\n                    mstore(PROOF_LOOKUP_GRAND_PRODUCT_Y_SLOT, y)\n                }\n                // PROOF_QUOTIENT_POLY_PARTS_0\n                {\n                    let x := mod(calldataload(add(offset, 0x1e4)), Q_MOD)\n                    let y := mod(calldataload(add(offset, 0x204)), Q_MOD)\n                    let xx := mulmod(x, x, Q_MOD)\n                    isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid)\n                    mstore(PROOF_QUOTIENT_POLY_PARTS_0_X_SLOT, x)\n                    mstore(PROOF_QUOTIENT_POLY_PARTS_0_Y_SLOT, y)\n                }\n                // PROOF_QUOTIENT_POLY_PARTS_1\n                {\n                    let x := mod(calldataload(add(offset, 0x224)), Q_MOD)\n                    let y := mod(calldataload(add(offset, 0x244)), Q_MOD)\n                    let xx := mulmod(x, x, Q_MOD)\n                    isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid)\n                    mstore(PROOF_QUOTIENT_POLY_PARTS_1_X_SLOT, x)\n                    mstore(PROOF_QUOTIENT_POLY_PARTS_1_Y_SLOT, y)\n                }\n                // PROOF_QUOTIENT_POLY_PARTS_2\n                {\n                    let x := mod(calldataload(add(offset, 0x264)), Q_MOD)\n                    let y := mod(calldataload(add(offset, 0x284)), Q_MOD)\n                    let xx := mulmod(x, x, Q_MOD)\n                    isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid)\n                    mstore(PROOF_QUOTIENT_POLY_PARTS_2_X_SLOT, x)\n                    mstore(PROOF_QUOTIENT_POLY_PARTS_2_Y_SLOT, y)\n                }\n                // PROOF_QUOTIENT_POLY_PARTS_3\n                {\n                    let x := mod(calldataload(add(offset, 0x2a4)), Q_MOD)\n                    let y := mod(calldataload(add(offset, 0x2c4)), Q_MOD)\n                    let xx := mulmod(x, x, Q_MOD)\n                    isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid)\n                    mstore(PROOF_QUOTIENT_POLY_PARTS_3_X_SLOT, x)\n                    mstore(PROOF_QUOTIENT_POLY_PARTS_3_Y_SLOT, y)\n                }\n\n                mstore(PROOF_STATE_POLYS_0_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x2e4)), R_MOD))\n                mstore(PROOF_STATE_POLYS_1_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x304)), R_MOD))\n                mstore(PROOF_STATE_POLYS_2_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x324)), R_MOD))\n                mstore(PROOF_STATE_POLYS_3_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x344)), R_MOD))\n\n                mstore(PROOF_STATE_POLYS_3_OPENING_AT_Z_OMEGA_SLOT, mod(calldataload(add(offset, 0x364)), R_MOD))\n                mstore(PROOF_GATE_SELECTORS_0_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x384)), R_MOD))\n\n                mstore(PROOF_COPY_PERMUTATION_POLYS_0_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x3a4)), R_MOD))\n                mstore(PROOF_COPY_PERMUTATION_POLYS_1_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x3c4)), R_MOD))\n                mstore(PROOF_COPY_PERMUTATION_POLYS_2_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x3e4)), R_MOD))\n\n                mstore(\n                    PROOF_COPY_PERMUTATION_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT,\n                    mod(calldataload(add(offset, 0x404)), R_MOD)\n                )\n                mstore(PROOF_LOOKUP_S_POLY_OPENING_AT_Z_OMEGA_SLOT, mod(calldataload(add(offset, 0x424)), R_MOD))\n                mstore(PROOF_LOOKUP_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT, mod(calldataload(add(offset, 0x444)), R_MOD))\n                mstore(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x464)), R_MOD))\n                mstore(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_OMEGA_SLOT, mod(calldataload(add(offset, 0x484)), R_MOD))\n                mstore(PROOF_LOOKUP_SELECTOR_POLY_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x4a4)), R_MOD))\n                mstore(PROOF_LOOKUP_TABLE_TYPE_POLY_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x4c4)), R_MOD))\n                mstore(PROOF_QUOTIENT_POLY_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x4e4)), R_MOD))\n                mstore(PROOF_LINEARISATION_POLY_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x504)), R_MOD))\n\n                // PROOF_OPENING_PROOF_AT_Z\n                {\n                    let x := mod(calldataload(add(offset, 0x524)), Q_MOD)\n                    let y := mod(calldataload(add(offset, 0x544)), Q_MOD)\n                    let xx := mulmod(x, x, Q_MOD)\n                    isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid)\n                    mstore(PROOF_OPENING_PROOF_AT_Z_X_SLOT, x)\n                    mstore(PROOF_OPENING_PROOF_AT_Z_Y_SLOT, y)\n                }\n                // PROOF_OPENING_PROOF_AT_Z_OMEGA\n                {\n                    let x := mod(calldataload(add(offset, 0x564)), Q_MOD)\n                    let y := mod(calldataload(add(offset, 0x584)), Q_MOD)\n                    let xx := mulmod(x, x, Q_MOD)\n                    isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid)\n                    mstore(PROOF_OPENING_PROOF_AT_Z_OMEGA_X_SLOT, x)\n                    mstore(PROOF_OPENING_PROOF_AT_Z_OMEGA_Y_SLOT, y)\n                }\n\n                // 3. Load the recursive part of the proof\n                offset := calldataload(0x44)\n                let recursiveProofLengthInWords := calldataload(add(offset, 0x04))\n\n                switch mload(VK_RECURSIVE_FLAG_SLOT)\n                case 0 {\n                    // recursive part should be empty\n                    isValid := and(iszero(recursiveProofLengthInWords), isValid)\n                }\n                default {\n                    // recursive part should be consist of 2 points\n                    isValid := and(eq(recursiveProofLengthInWords, 4), isValid)\n                    // PROOF_RECURSIVE_PART_P1\n                    {\n                        let x := mod(calldataload(add(offset, 0x024)), Q_MOD)\n                        let y := mod(calldataload(add(offset, 0x044)), Q_MOD)\n                        let xx := mulmod(x, x, Q_MOD)\n                        isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid)\n                        mstore(PROOF_RECURSIVE_PART_P1_X_SLOT, x)\n                        mstore(PROOF_RECURSIVE_PART_P1_Y_SLOT, y)\n                    }\n                    // PROOF_RECURSIVE_PART_P2\n                    {\n                        let x := mod(calldataload(add(offset, 0x064)), Q_MOD)\n                        let y := mod(calldataload(add(offset, 0x084)), Q_MOD)\n                        let xx := mulmod(x, x, Q_MOD)\n                        isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid)\n                        mstore(PROOF_RECURSIVE_PART_P2_X_SLOT, x)\n                        mstore(PROOF_RECURSIVE_PART_P2_Y_SLOT, y)\n                    }\n                }\n\n                // Revert if a proof is not valid\n                if iszero(isValid) {\n                    revertWithMessage(27, \"loadProof: Proof is invalid\")\n                }\n            }\n\n            /*//////////////////////////////////////////////////////////////\n                                    2. Transcript initialization\n            //////////////////////////////////////////////////////////////*/\n\n            /// @notice Recomputes all challenges\n            /// @dev The process is the following:\n            /// Commit:   PI, [a], [b], [c], [d]\n            /// Get:      eta\n            /// Commit:   [s]\n            /// Get:      beta, gamma\n            /// Commit:   [z_perm]\n            /// Get:      beta', gamma'\n            /// Commit:   [z_lookup]\n            /// Get:      alpha\n            /// Commit:   [t_0], [t_1], [t_2], [t_3]\n            /// Get:      z\n            /// Commit:   t(z), a(z), b(z), c(z), d(z), d(z*omega),\n            ///           main_gate_selector(z),\n            ///           sigma_0(z), sigma_1(z), sigma_2(z),\n            ///           z_perm(z*omega),\n            ///           t(z), lookup_selector(z), table_type(z),\n            ///           s(x*omega), z_lookup(z*omega), t(z*omega),\n            ///           r(z)\n            /// Get:      v\n            /// Commit:   [W], [W']\n            /// Get:      u\n            function initializeTranscript() {\n                // Round 1\n                updateTranscript(mload(PROOF_PUBLIC_INPUT))\n                updateTranscript(mload(PROOF_STATE_POLYS_0_X_SLOT))\n                updateTranscript(mload(PROOF_STATE_POLYS_0_Y_SLOT))\n                updateTranscript(mload(PROOF_STATE_POLYS_1_X_SLOT))\n                updateTranscript(mload(PROOF_STATE_POLYS_1_Y_SLOT))\n                updateTranscript(mload(PROOF_STATE_POLYS_2_X_SLOT))\n                updateTranscript(mload(PROOF_STATE_POLYS_2_Y_SLOT))\n                updateTranscript(mload(PROOF_STATE_POLYS_3_X_SLOT))\n                updateTranscript(mload(PROOF_STATE_POLYS_3_Y_SLOT))\n\n                mstore(STATE_ETA_SLOT, getTranscriptChallenge(0))\n\n                // Round 1.5\n                updateTranscript(mload(PROOF_LOOKUP_S_POLY_X_SLOT))\n                updateTranscript(mload(PROOF_LOOKUP_S_POLY_Y_SLOT))\n\n                mstore(STATE_BETA_SLOT, getTranscriptChallenge(1))\n                mstore(STATE_GAMMA_SLOT, getTranscriptChallenge(2))\n\n                // Round 2\n                updateTranscript(mload(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_X_SLOT))\n                updateTranscript(mload(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_Y_SLOT))\n\n                mstore(STATE_BETA_LOOKUP_SLOT, getTranscriptChallenge(3))\n                mstore(STATE_GAMMA_LOOKUP_SLOT, getTranscriptChallenge(4))\n\n                // Round 2.5\n                updateTranscript(mload(PROOF_LOOKUP_GRAND_PRODUCT_X_SLOT))\n                updateTranscript(mload(PROOF_LOOKUP_GRAND_PRODUCT_Y_SLOT))\n\n                mstore(STATE_ALPHA_SLOT, getTranscriptChallenge(5))\n\n                // Round 3\n                updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_0_X_SLOT))\n                updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_0_Y_SLOT))\n                updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_1_X_SLOT))\n                updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_1_Y_SLOT))\n                updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_2_X_SLOT))\n                updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_2_Y_SLOT))\n                updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_3_X_SLOT))\n                updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_3_Y_SLOT))\n\n                {\n                    let z := getTranscriptChallenge(6)\n\n                    mstore(STATE_Z_SLOT, z)\n                    mstore(STATE_Z_IN_DOMAIN_SIZE, modexp(z, DOMAIN_SIZE))\n                }\n\n                // Round 4\n                updateTranscript(mload(PROOF_QUOTIENT_POLY_OPENING_AT_Z_SLOT))\n\n                updateTranscript(mload(PROOF_STATE_POLYS_0_OPENING_AT_Z_SLOT))\n                updateTranscript(mload(PROOF_STATE_POLYS_1_OPENING_AT_Z_SLOT))\n                updateTranscript(mload(PROOF_STATE_POLYS_2_OPENING_AT_Z_SLOT))\n                updateTranscript(mload(PROOF_STATE_POLYS_3_OPENING_AT_Z_SLOT))\n\n                updateTranscript(mload(PROOF_STATE_POLYS_3_OPENING_AT_Z_OMEGA_SLOT))\n                updateTranscript(mload(PROOF_GATE_SELECTORS_0_OPENING_AT_Z_SLOT))\n\n                updateTranscript(mload(PROOF_COPY_PERMUTATION_POLYS_0_OPENING_AT_Z_SLOT))\n                updateTranscript(mload(PROOF_COPY_PERMUTATION_POLYS_1_OPENING_AT_Z_SLOT))\n                updateTranscript(mload(PROOF_COPY_PERMUTATION_POLYS_2_OPENING_AT_Z_SLOT))\n\n                updateTranscript(mload(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT))\n                updateTranscript(mload(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_SLOT))\n                updateTranscript(mload(PROOF_LOOKUP_SELECTOR_POLY_OPENING_AT_Z_SLOT))\n                updateTranscript(mload(PROOF_LOOKUP_TABLE_TYPE_POLY_OPENING_AT_Z_SLOT))\n                updateTranscript(mload(PROOF_LOOKUP_S_POLY_OPENING_AT_Z_OMEGA_SLOT))\n                updateTranscript(mload(PROOF_LOOKUP_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT))\n                updateTranscript(mload(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_OMEGA_SLOT))\n                updateTranscript(mload(PROOF_LINEARISATION_POLY_OPENING_AT_Z_SLOT))\n\n                mstore(STATE_V_SLOT, getTranscriptChallenge(7))\n\n                // Round 5\n                updateTranscript(mload(PROOF_OPENING_PROOF_AT_Z_X_SLOT))\n                updateTranscript(mload(PROOF_OPENING_PROOF_AT_Z_Y_SLOT))\n                updateTranscript(mload(PROOF_OPENING_PROOF_AT_Z_OMEGA_X_SLOT))\n                updateTranscript(mload(PROOF_OPENING_PROOF_AT_Z_OMEGA_Y_SLOT))\n\n                mstore(STATE_U_SLOT, getTranscriptChallenge(8))\n            }\n\n            /*//////////////////////////////////////////////////////////////\n                                    3. Verifying quotient evaluation\n            //////////////////////////////////////////////////////////////*/\n\n            /// @notice Compute linearisation polynomial's constant term: r_0\n            /// @dev To save a verifier scalar multiplication, we split linearisation polynomial\n            /// into its constant and non-constant terms. The constant term is computed with the formula:\n            ///\n            /// r_0 = alpha^0 * L_0(z) * PI * q_{main selector}(z) + r(z)         -- main gate contribution\n            ///\n            ///     - alpha^4 * z_perm(z*omega)(sigma_0(z) * beta + gamma + a(z)) \\\n            ///                           (sigma_1(z) * beta + gamma + b(z))      |\n            ///                           (sigma_2(z) * beta + gamma + c(z))      | - permutation contribution\n            ///                           (sigma_3(z) + gamma)                    |\n            ///     - alpha^5 * L_0(z)                                            /\n            ///\n            ///     + alpha^6 * (s(z*omega) * beta' + gamma' (beta' + 1))         \\\n            ///               * (z - omega^{n-1}) * z_lookup(z*omega)             | - lookup contribution\n            ///     - alpha^7 * L_0(z)                                            |\n            ///     - alpha^8 * L_{n-1}(z) * (gamma' (beta' + 1))^{n-1}           /\n            ///\n            /// In the end we should check that t(z)*Z_H(z) = r(z) + r_0!\n            function verifyQuotientEvaluation() {\n                // Compute power of alpha\n                {\n                    let alpha := mload(STATE_ALPHA_SLOT)\n                    let currentAlpha := mulmod(alpha, alpha, R_MOD)\n                    mstore(STATE_POWER_OF_ALPHA_2_SLOT, currentAlpha)\n                    currentAlpha := mulmod(currentAlpha, alpha, R_MOD)\n                    mstore(STATE_POWER_OF_ALPHA_3_SLOT, currentAlpha)\n                    currentAlpha := mulmod(currentAlpha, alpha, R_MOD)\n                    mstore(STATE_POWER_OF_ALPHA_4_SLOT, currentAlpha)\n                    currentAlpha := mulmod(currentAlpha, alpha, R_MOD)\n                    mstore(STATE_POWER_OF_ALPHA_5_SLOT, currentAlpha)\n                    currentAlpha := mulmod(currentAlpha, alpha, R_MOD)\n                    mstore(STATE_POWER_OF_ALPHA_6_SLOT, currentAlpha)\n                    currentAlpha := mulmod(currentAlpha, alpha, R_MOD)\n                    mstore(STATE_POWER_OF_ALPHA_7_SLOT, currentAlpha)\n                    currentAlpha := mulmod(currentAlpha, alpha, R_MOD)\n                    mstore(STATE_POWER_OF_ALPHA_8_SLOT, currentAlpha)\n                }\n\n                // z\n                let stateZ := mload(STATE_Z_SLOT)\n                // L_0(z)\n                mstore(STATE_L_0_AT_Z_SLOT, evaluateLagrangePolyOutOfDomain(0, stateZ))\n                // L_{n-1}(z)\n                mstore(STATE_L_N_MINUS_ONE_AT_Z_SLOT, evaluateLagrangePolyOutOfDomain(sub(DOMAIN_SIZE, 1), stateZ))\n                // L_0(z) * PI\n                let stateT := mulmod(mload(STATE_L_0_AT_Z_SLOT), mload(PROOF_PUBLIC_INPUT), R_MOD)\n\n                // Compute main gate contribution\n                let result := mulmod(stateT, mload(PROOF_GATE_SELECTORS_0_OPENING_AT_Z_SLOT), R_MOD)\n\n                // Compute permutation contribution\n                result := addmod(result, permutationQuotientContribution(), R_MOD)\n\n                // Compute lookup contribution\n                result := addmod(result, lookupQuotientContribution(), R_MOD)\n\n                // Check that r(z) + r_0 = t(z) * Z_H(z)\n                result := addmod(mload(PROOF_LINEARISATION_POLY_OPENING_AT_Z_SLOT), result, R_MOD)\n\n                let vanishing := addmod(mload(STATE_Z_IN_DOMAIN_SIZE), sub(R_MOD, 1), R_MOD)\n                let lhs := mulmod(mload(PROOF_QUOTIENT_POLY_OPENING_AT_Z_SLOT), vanishing, R_MOD)\n                if iszero(eq(lhs, result)) {\n                    revertWithMessage(27, \"invalid quotient evaluation\")\n                }\n            }\n\n            /// @notice Evaluating L_{polyNum}(at) out of domain\n            /// @dev L_i is a Lagrange polynomial for our domain such that:\n            /// L_i(omega^i) = 1 and L_i(omega^j) = 0 for all j != i\n            function evaluateLagrangePolyOutOfDomain(polyNum, at) -> res {\n                let omegaPower := 1\n                if polyNum {\n                    omegaPower := modexp(OMEGA, polyNum)\n                }\n\n                res := addmod(modexp(at, DOMAIN_SIZE), sub(R_MOD, 1), R_MOD)\n\n                // Vanishing polynomial can not be zero at point `at`\n                if iszero(res) {\n                    revertWithMessage(28, \"invalid vanishing polynomial\")\n                }\n                res := mulmod(res, omegaPower, R_MOD)\n                let denominator := addmod(at, sub(R_MOD, omegaPower), R_MOD)\n                denominator := mulmod(denominator, DOMAIN_SIZE, R_MOD)\n                denominator := modexp(denominator, sub(R_MOD, 2))\n                res := mulmod(res, denominator, R_MOD)\n            }\n\n            /// @notice Compute permutation contribution to linearisation polynomial's constant term\n            function permutationQuotientContribution() -> res {\n                // res = alpha^4 * z_perm(z*omega)\n                res := mulmod(\n                    mload(STATE_POWER_OF_ALPHA_4_SLOT),\n                    mload(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT),\n                    R_MOD\n                )\n\n                {\n                    let gamma := mload(STATE_GAMMA_SLOT)\n                    let beta := mload(STATE_BETA_SLOT)\n\n                    let factorMultiplier\n                    {\n                        // res *= sigma_0(z) * beta + gamma + a(z)\n                        factorMultiplier := mulmod(mload(PROOF_COPY_PERMUTATION_POLYS_0_OPENING_AT_Z_SLOT), beta, R_MOD)\n                        factorMultiplier := addmod(factorMultiplier, gamma, R_MOD)\n                        factorMultiplier := addmod(\n                            factorMultiplier,\n                            mload(PROOF_STATE_POLYS_0_OPENING_AT_Z_SLOT),\n                            R_MOD\n                        )\n                        res := mulmod(res, factorMultiplier, R_MOD)\n                    }\n                    {\n                        // res *= sigma_1(z) * beta + gamma + b(z)\n                        factorMultiplier := mulmod(mload(PROOF_COPY_PERMUTATION_POLYS_1_OPENING_AT_Z_SLOT), beta, R_MOD)\n                        factorMultiplier := addmod(factorMultiplier, gamma, R_MOD)\n                        factorMultiplier := addmod(\n                            factorMultiplier,\n                            mload(PROOF_STATE_POLYS_1_OPENING_AT_Z_SLOT),\n                            R_MOD\n                        )\n                        res := mulmod(res, factorMultiplier, R_MOD)\n                    }\n                    {\n                        // res *= sigma_2(z) * beta + gamma + c(z)\n                        factorMultiplier := mulmod(mload(PROOF_COPY_PERMUTATION_POLYS_2_OPENING_AT_Z_SLOT), beta, R_MOD)\n                        factorMultiplier := addmod(factorMultiplier, gamma, R_MOD)\n                        factorMultiplier := addmod(\n                            factorMultiplier,\n                            mload(PROOF_STATE_POLYS_2_OPENING_AT_Z_SLOT),\n                            R_MOD\n                        )\n                        res := mulmod(res, factorMultiplier, R_MOD)\n                    }\n\n                    // res *= sigma_3(z) + gamma\n                    res := mulmod(res, addmod(mload(PROOF_STATE_POLYS_3_OPENING_AT_Z_SLOT), gamma, R_MOD), R_MOD)\n                }\n\n                // res = -res\n                res := sub(R_MOD, res)\n\n                // -= L_0(z) * alpha^5\n                let l0AtZ := mload(STATE_L_0_AT_Z_SLOT)\n                l0AtZ := mulmod(l0AtZ, mload(STATE_POWER_OF_ALPHA_5_SLOT), R_MOD)\n                res := addmod(res, sub(R_MOD, l0AtZ), R_MOD)\n            }\n\n            /// @notice Compute lookup contribution to linearisation polynomial's constant term\n            function lookupQuotientContribution() -> res {\n                let betaLookup := mload(STATE_BETA_LOOKUP_SLOT)\n                let gammaLookup := mload(STATE_GAMMA_LOOKUP_SLOT)\n                let betaPlusOne := addmod(betaLookup, 1, R_MOD)\n                let betaGamma := mulmod(betaPlusOne, gammaLookup, R_MOD)\n\n                mstore(STATE_BETA_PLUS_ONE_SLOT, betaPlusOne)\n                mstore(STATE_BETA_GAMMA_PLUS_GAMMA_SLOT, betaGamma)\n\n                // res =  alpha^6 * (s(z*omega) * beta' + gamma' (beta' + 1)) * z_lookup(z*omega)\n                res := mulmod(mload(PROOF_LOOKUP_S_POLY_OPENING_AT_Z_OMEGA_SLOT), betaLookup, R_MOD)\n                res := addmod(res, betaGamma, R_MOD)\n                res := mulmod(res, mload(PROOF_LOOKUP_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT), R_MOD)\n                res := mulmod(res, mload(STATE_POWER_OF_ALPHA_6_SLOT), R_MOD)\n\n                // res *= z - omega^{n-1}\n                {\n                    let lastOmega := modexp(OMEGA, sub(DOMAIN_SIZE, 1))\n                    let zMinusLastOmega := addmod(mload(STATE_Z_SLOT), sub(R_MOD, lastOmega), R_MOD)\n                    mstore(STATE_Z_MINUS_LAST_OMEGA_SLOT, zMinusLastOmega)\n                    res := mulmod(res, zMinusLastOmega, R_MOD)\n                }\n\n                // res -= alpha^7 * L_{0}(z)\n                {\n                    let intermediateValue := mulmod(\n                        mload(STATE_L_0_AT_Z_SLOT),\n                        mload(STATE_POWER_OF_ALPHA_7_SLOT),\n                        R_MOD\n                    )\n                    res := addmod(res, sub(R_MOD, intermediateValue), R_MOD)\n                }\n\n                // res -= alpha^8 * L_{n-1}(z) * (gamma' (beta' + 1))^{n-1}\n                {\n                    let lnMinusOneAtZ := mload(STATE_L_N_MINUS_ONE_AT_Z_SLOT)\n                    let betaGammaPowered := modexp(betaGamma, sub(DOMAIN_SIZE, 1))\n                    let alphaPower8 := mload(STATE_POWER_OF_ALPHA_8_SLOT)\n\n                    let subtrahend := mulmod(mulmod(lnMinusOneAtZ, betaGammaPowered, R_MOD), alphaPower8, R_MOD)\n                    res := addmod(res, sub(R_MOD, subtrahend), R_MOD)\n                }\n            }\n\n            /// @notice Compute main gate contribution to linearisation polynomial commitment multiplied by v\n            function mainGateLinearisationContributionWithV(\n                dest,\n                stateOpening0AtZ,\n                stateOpening1AtZ,\n                stateOpening2AtZ,\n                stateOpening3AtZ\n            ) {\n                // += a(z) * [q_a]\n                pointMulIntoDest(VK_GATE_SETUP_0_X_SLOT, stateOpening0AtZ, dest)\n                // += b(z) * [q_b]\n                pointMulAndAddIntoDest(VK_GATE_SETUP_1_X_SLOT, stateOpening1AtZ, dest)\n                // += c(z) * [q_c]\n                pointMulAndAddIntoDest(VK_GATE_SETUP_2_X_SLOT, stateOpening2AtZ, dest)\n                // += d(z) * [q_d]\n                pointMulAndAddIntoDest(VK_GATE_SETUP_3_X_SLOT, stateOpening3AtZ, dest)\n                // += a(z) * b(z) * [q_ab]\n                pointMulAndAddIntoDest(VK_GATE_SETUP_4_X_SLOT, mulmod(stateOpening0AtZ, stateOpening1AtZ, R_MOD), dest)\n                // += a(z) * c(z) * [q_ac]\n                pointMulAndAddIntoDest(VK_GATE_SETUP_5_X_SLOT, mulmod(stateOpening0AtZ, stateOpening2AtZ, R_MOD), dest)\n                // += [q_const]\n                pointAddAssign(dest, VK_GATE_SETUP_6_X_SLOT)\n                // += d(z*omega) * [q_{d_next}]\n                pointMulAndAddIntoDest(VK_GATE_SETUP_7_X_SLOT, mload(PROOF_STATE_POLYS_3_OPENING_AT_Z_OMEGA_SLOT), dest)\n\n                // *= v * main_gate_selector(z)\n                let coeff := mulmod(mload(PROOF_GATE_SELECTORS_0_OPENING_AT_Z_SLOT), mload(STATE_V_SLOT), R_MOD)\n                pointMulIntoDest(dest, coeff, dest)\n            }\n\n            /// @notice Compute custom gate contribution to linearisation polynomial commitment multiplied by v\n            function addAssignRescueCustomGateLinearisationContributionWithV(\n                dest,\n                stateOpening0AtZ,\n                stateOpening1AtZ,\n                stateOpening2AtZ,\n                stateOpening3AtZ\n            ) {\n                let accumulator\n                let intermediateValue\n                //  = alpha * (a(z)^2 - b(z))\n                accumulator := mulmod(stateOpening0AtZ, stateOpening0AtZ, R_MOD)\n                accumulator := addmod(accumulator, sub(R_MOD, stateOpening1AtZ), R_MOD)\n                accumulator := mulmod(accumulator, mload(STATE_ALPHA_SLOT), R_MOD)\n                // += alpha^2 * (b(z)^2 - c(z))\n                intermediateValue := mulmod(stateOpening1AtZ, stateOpening1AtZ, R_MOD)\n                intermediateValue := addmod(intermediateValue, sub(R_MOD, stateOpening2AtZ), R_MOD)\n                intermediateValue := mulmod(intermediateValue, mload(STATE_POWER_OF_ALPHA_2_SLOT), R_MOD)\n                accumulator := addmod(accumulator, intermediateValue, R_MOD)\n                // += alpha^3 * (c(z) * a(z) - d(z))\n                intermediateValue := mulmod(stateOpening2AtZ, stateOpening0AtZ, R_MOD)\n                intermediateValue := addmod(intermediateValue, sub(R_MOD, stateOpening3AtZ), R_MOD)\n                intermediateValue := mulmod(intermediateValue, mload(STATE_POWER_OF_ALPHA_3_SLOT), R_MOD)\n                accumulator := addmod(accumulator, intermediateValue, R_MOD)\n\n                // *= v * [custom_gate_selector]\n                accumulator := mulmod(accumulator, mload(STATE_V_SLOT), R_MOD)\n                pointMulAndAddIntoDest(VK_GATE_SELECTORS_1_X_SLOT, accumulator, dest)\n            }\n\n            /// @notice Compute copy-permutation contribution to linearisation polynomial commitment multiplied by v\n            function addAssignPermutationLinearisationContributionWithV(\n                dest,\n                stateOpening0AtZ,\n                stateOpening1AtZ,\n                stateOpening2AtZ,\n                stateOpening3AtZ\n            ) {\n                // alpha^4\n                let factor := mload(STATE_POWER_OF_ALPHA_4_SLOT)\n                // Calculate the factor\n                {\n                    // *= (a(z) + beta * z + gamma)\n                    let zMulBeta := mulmod(mload(STATE_Z_SLOT), mload(STATE_BETA_SLOT), R_MOD)\n                    let gamma := mload(STATE_GAMMA_SLOT)\n\n                    let intermediateValue := addmod(addmod(zMulBeta, gamma, R_MOD), stateOpening0AtZ, R_MOD)\n                    factor := mulmod(factor, intermediateValue, R_MOD)\n\n                    // (b(z) + beta * z * k0 + gamma)\n                    intermediateValue := addmod(\n                        addmod(mulmod(zMulBeta, NON_RESIDUES_0, R_MOD), gamma, R_MOD),\n                        stateOpening1AtZ,\n                        R_MOD\n                    )\n                    factor := mulmod(factor, intermediateValue, R_MOD)\n\n                    // (c(z) + beta * z * k1 + gamma)\n                    intermediateValue := addmod(\n                        addmod(mulmod(zMulBeta, NON_RESIDUES_1, R_MOD), gamma, R_MOD),\n                        stateOpening2AtZ,\n                        R_MOD\n                    )\n                    factor := mulmod(factor, intermediateValue, R_MOD)\n\n                    // (d(z) + beta * z * k2 + gamma)\n                    intermediateValue := addmod(\n                        addmod(mulmod(zMulBeta, NON_RESIDUES_2, R_MOD), gamma, R_MOD),\n                        stateOpening3AtZ,\n                        R_MOD\n                    )\n                    factor := mulmod(factor, intermediateValue, R_MOD)\n                }\n\n                // += alpha^5 * L_0(z)\n                let l0AtZ := mload(STATE_L_0_AT_Z_SLOT)\n                factor := addmod(factor, mulmod(l0AtZ, mload(STATE_POWER_OF_ALPHA_5_SLOT), R_MOD), R_MOD)\n\n                // Here we can optimize one scalar multiplication by aggregating coefficients near [z_perm] during\n                // computing [F]\n                // We will sum them and add and make one scalar multiplication: (coeff1 + coeff2) * [z_perm]\n                factor := mulmod(factor, mload(STATE_V_SLOT), R_MOD)\n                mstore(COPY_PERMUTATION_FIRST_AGGREGATED_COMMITMENT_COEFF, factor)\n\n                // alpha^4 * beta * z_perm(z*omega)\n                factor := mulmod(mload(STATE_POWER_OF_ALPHA_4_SLOT), mload(STATE_BETA_SLOT), R_MOD)\n                factor := mulmod(factor, mload(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT), R_MOD)\n                {\n                    // *= (a(z) + beta * sigma_0(z) + gamma)\n                    let beta := mload(STATE_BETA_SLOT)\n                    let gamma := mload(STATE_GAMMA_SLOT)\n\n                    let intermediateValue := addmod(\n                        addmod(\n                            mulmod(mload(PROOF_COPY_PERMUTATION_POLYS_0_OPENING_AT_Z_SLOT), beta, R_MOD),\n                            gamma,\n                            R_MOD\n                        ),\n                        stateOpening0AtZ,\n                        R_MOD\n                    )\n                    factor := mulmod(factor, intermediateValue, R_MOD)\n\n                    // *= (b(z) + beta * sigma_1(z) + gamma)\n                    intermediateValue := addmod(\n                        addmod(\n                            mulmod(mload(PROOF_COPY_PERMUTATION_POLYS_1_OPENING_AT_Z_SLOT), beta, R_MOD),\n                            gamma,\n                            R_MOD\n                        ),\n                        stateOpening1AtZ,\n                        R_MOD\n                    )\n                    factor := mulmod(factor, intermediateValue, R_MOD)\n\n                    // *= (c(z) + beta * sigma_2(z) + gamma)\n                    intermediateValue := addmod(\n                        addmod(\n                            mulmod(mload(PROOF_COPY_PERMUTATION_POLYS_2_OPENING_AT_Z_SLOT), beta, R_MOD),\n                            gamma,\n                            R_MOD\n                        ),\n                        stateOpening2AtZ,\n                        R_MOD\n                    )\n                    factor := mulmod(factor, intermediateValue, R_MOD)\n                }\n\n                // *= v * [sigma_3]\n                factor := mulmod(factor, mload(STATE_V_SLOT), R_MOD)\n                pointMulIntoDest(VK_PERMUTATION_3_X_SLOT, factor, QUERIES_BUFFER_POINT_SLOT)\n\n                pointSubAssign(dest, QUERIES_BUFFER_POINT_SLOT)\n            }\n\n            /// @notice Compute lookup contribution to linearisation polynomial commitment multiplied by v\n            function addAssignLookupLinearisationContributionWithV(\n                dest,\n                stateOpening0AtZ,\n                stateOpening1AtZ,\n                stateOpening2AtZ\n            ) {\n                // alpha^6 * v * z_lookup(z*omega) * (z - omega^{n-1}) * [s]\n                let factor := mload(PROOF_LOOKUP_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT)\n                factor := mulmod(factor, mload(STATE_POWER_OF_ALPHA_6_SLOT), R_MOD)\n                factor := mulmod(factor, mload(STATE_Z_MINUS_LAST_OMEGA_SLOT), R_MOD)\n                factor := mulmod(factor, mload(STATE_V_SLOT), R_MOD)\n\n                // Here we can optimize one scalar multiplication by aggregating coefficients near [s] during\n                // computing [F]\n                // We will sum them and add and make one scalar multiplication: (coeff1 + coeff2) * [s]\n                mstore(LOOKUP_S_FIRST_AGGREGATED_COMMITMENT_COEFF, factor)\n\n                // gamma(1 + beta) + t(x) + beta * t(x*omega)\n                factor := mload(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_OMEGA_SLOT)\n                factor := mulmod(factor, mload(STATE_BETA_LOOKUP_SLOT), R_MOD)\n                factor := addmod(factor, mload(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_SLOT), R_MOD)\n                factor := addmod(factor, mload(STATE_BETA_GAMMA_PLUS_GAMMA_SLOT), R_MOD)\n\n                // *= (gamma + f(z))\n                // We should use fact that f(x) =\n                // lookup_selector(x) * (a(x) + eta * b(x) + eta^2 * c(x) + eta^3 * table_type(x))\n                // to restore f(z)\n                let fReconstructed\n                {\n                    fReconstructed := stateOpening0AtZ\n                    let eta := mload(STATE_ETA_SLOT)\n                    let currentEta := eta\n\n                    fReconstructed := addmod(fReconstructed, mulmod(currentEta, stateOpening1AtZ, R_MOD), R_MOD)\n                    currentEta := mulmod(currentEta, eta, R_MOD)\n                    fReconstructed := addmod(fReconstructed, mulmod(currentEta, stateOpening2AtZ, R_MOD), R_MOD)\n                    currentEta := mulmod(currentEta, eta, R_MOD)\n\n                    // add type of table\n                    fReconstructed := addmod(\n                        fReconstructed,\n                        mulmod(mload(PROOF_LOOKUP_TABLE_TYPE_POLY_OPENING_AT_Z_SLOT), currentEta, R_MOD),\n                        R_MOD\n                    )\n                    fReconstructed := mulmod(fReconstructed, mload(PROOF_LOOKUP_SELECTOR_POLY_OPENING_AT_Z_SLOT), R_MOD)\n                    fReconstructed := addmod(fReconstructed, mload(STATE_GAMMA_LOOKUP_SLOT), R_MOD)\n                }\n                // *= -alpha^6 * (beta + 1) * (z - omega^{n-1})\n                factor := mulmod(factor, fReconstructed, R_MOD)\n                factor := mulmod(factor, mload(STATE_BETA_PLUS_ONE_SLOT), R_MOD)\n                factor := sub(R_MOD, factor)\n                factor := mulmod(factor, mload(STATE_POWER_OF_ALPHA_6_SLOT), R_MOD)\n\n                factor := mulmod(factor, mload(STATE_Z_MINUS_LAST_OMEGA_SLOT), R_MOD)\n\n                // += alpha^7 * L_0(z)\n                factor := addmod(\n                    factor,\n                    mulmod(mload(STATE_L_0_AT_Z_SLOT), mload(STATE_POWER_OF_ALPHA_7_SLOT), R_MOD),\n                    R_MOD\n                )\n\n                // += alpha^8 * L_{n-1}(z)\n                factor := addmod(\n                    factor,\n                    mulmod(mload(STATE_L_N_MINUS_ONE_AT_Z_SLOT), mload(STATE_POWER_OF_ALPHA_8_SLOT), R_MOD),\n                    R_MOD\n                )\n\n                // Here we can optimize one scalar multiplication by aggregating coefficients near [z_lookup] during\n                // computing [F]\n                // We will sum them and add and make one scalar multiplication: (coeff1 + coeff2) * [z_lookup]\n                factor := mulmod(factor, mload(STATE_V_SLOT), R_MOD)\n                mstore(LOOKUP_GRAND_PRODUCT_FIRST_AGGREGATED_COMMITMENT_COEFF, factor)\n            }\n\n            /*//////////////////////////////////////////////////////////////\n                                    4. Prepare queries\n            //////////////////////////////////////////////////////////////*/\n\n            /// @dev Here we compute the first and second parts of batched polynomial commitment\n            /// We use the formula:\n            ///     [D0] = [t_0] + z^n * [t_1] + z^{2n} * [t_2] + z^{3n} * [t_3]\n            /// and\n            ///     [D1] = main_gate_selector(z) * (                                        \\\n            ///                a(z) * [q_a] + b(z) * [q_b] + c(z) * [q_c] + d(z) * [q_d] +  | - main gate contribution\n            ///                a(z) * b(z) * [q_ab] + a(z) * c(z) * [q_ac] +                |\n            ///                [q_const] + d(z*omega) * [q_{d_next}])                       /\n            ///\n            ///            + alpha * [custom_gate_selector] * (                             \\\n            ///                (a(z)^2 - b(z))              +                               | - custom gate contribution\n            ///                (b(z)^2 - c(z))    * alpha   +                               |\n            ///                (a(z)*c(z) - d(z)) * alpha^2 )                               /\n            ///\n            ///            + alpha^4 * [z_perm] *                                           \\\n            ///                (a(z) + beta * z      + gamma) *                             |\n            ///                (b(z) + beta * z * k0 + gamma) *                             |\n            ///                (c(z) + beta * z * k1 + gamma) *                             |\n            ///                (d(z) + beta * z * k2 + gamma)                               | - permutation contribution\n            ///            - alpha^4 * z_perm(z*omega) * beta * [sigma_3] *                 |\n            ///                (a(z) + beta * sigma_0(z) + gamma) *                         |\n            ///                (b(z) + beta * sigma_1(z) + gamma) *                         |\n            ///                (c(z) + beta * sigma_2(z) + gamma) *                         |\n            ///            + alpha^5 * L_0(z) * [z_perm]                                    /\n            ///\n            ///            - alpha^6 * (1 + beta') * (gamma' + f(z)) * (z - omega^{n-1}) *  \\\n            ///                (gamma'(1 + beta') + t(z) + beta' * t(z*omega)) * [z_lookup] |\n            ///            + alpha^6 * z_lookup(z*omega) * (z - omega^{n-1}) * [s]          | - lookup contribution\n            ///            + alpha^7 * L_0(z) * [z_lookup]                                  |\n            ///            + alpha^8 * L_{n-1}(z) * [z_lookup]                              /\n            function prepareQueries() {\n                // Calculate [D0]\n                {\n                    let zInDomainSize := mload(STATE_Z_IN_DOMAIN_SIZE)\n                    let currentZ := zInDomainSize\n\n                    mstore(QUERIES_AT_Z_0_X_SLOT, mload(PROOF_QUOTIENT_POLY_PARTS_0_X_SLOT))\n                    mstore(QUERIES_AT_Z_0_Y_SLOT, mload(PROOF_QUOTIENT_POLY_PARTS_0_Y_SLOT))\n\n                    pointMulAndAddIntoDest(PROOF_QUOTIENT_POLY_PARTS_1_X_SLOT, currentZ, QUERIES_AT_Z_0_X_SLOT)\n                    currentZ := mulmod(currentZ, zInDomainSize, R_MOD)\n\n                    pointMulAndAddIntoDest(PROOF_QUOTIENT_POLY_PARTS_2_X_SLOT, currentZ, QUERIES_AT_Z_0_X_SLOT)\n                    currentZ := mulmod(currentZ, zInDomainSize, R_MOD)\n\n                    pointMulAndAddIntoDest(PROOF_QUOTIENT_POLY_PARTS_3_X_SLOT, currentZ, QUERIES_AT_Z_0_X_SLOT)\n                }\n\n                // Calculate v * [D1]\n                // We are going to multiply all the points in the sum by v to save\n                // one scalar multiplication during [F] computation\n                {\n                    let stateOpening0AtZ := mload(PROOF_STATE_POLYS_0_OPENING_AT_Z_SLOT)\n                    let stateOpening1AtZ := mload(PROOF_STATE_POLYS_1_OPENING_AT_Z_SLOT)\n                    let stateOpening2AtZ := mload(PROOF_STATE_POLYS_2_OPENING_AT_Z_SLOT)\n                    let stateOpening3AtZ := mload(PROOF_STATE_POLYS_3_OPENING_AT_Z_SLOT)\n\n                    mainGateLinearisationContributionWithV(\n                        QUERIES_AT_Z_1_X_SLOT,\n                        stateOpening0AtZ,\n                        stateOpening1AtZ,\n                        stateOpening2AtZ,\n                        stateOpening3AtZ\n                    )\n\n                    addAssignRescueCustomGateLinearisationContributionWithV(\n                        QUERIES_AT_Z_1_X_SLOT,\n                        stateOpening0AtZ,\n                        stateOpening1AtZ,\n                        stateOpening2AtZ,\n                        stateOpening3AtZ\n                    )\n\n                    addAssignPermutationLinearisationContributionWithV(\n                        QUERIES_AT_Z_1_X_SLOT,\n                        stateOpening0AtZ,\n                        stateOpening1AtZ,\n                        stateOpening2AtZ,\n                        stateOpening3AtZ\n                    )\n\n                    addAssignLookupLinearisationContributionWithV(\n                        QUERIES_AT_Z_1_X_SLOT,\n                        stateOpening0AtZ,\n                        stateOpening1AtZ,\n                        stateOpening2AtZ\n                    )\n                }\n\n                // Also we should restore [t] for future computations\n                // [t] = [col_0] + eta*[col_1] + eta^2*[col_2] + eta^3*[col_3]\n                {\n                    mstore(QUERIES_T_POLY_AGGREGATED_X_SLOT, mload(VK_LOOKUP_TABLE_0_X_SLOT))\n                    mstore(QUERIES_T_POLY_AGGREGATED_Y_SLOT, mload(VK_LOOKUP_TABLE_0_Y_SLOT))\n\n                    let eta := mload(STATE_ETA_SLOT)\n                    let currentEta := eta\n\n                    pointMulAndAddIntoDest(VK_LOOKUP_TABLE_1_X_SLOT, currentEta, QUERIES_T_POLY_AGGREGATED_X_SLOT)\n                    currentEta := mulmod(currentEta, eta, R_MOD)\n\n                    pointMulAndAddIntoDest(VK_LOOKUP_TABLE_2_X_SLOT, currentEta, QUERIES_T_POLY_AGGREGATED_X_SLOT)\n                    currentEta := mulmod(currentEta, eta, R_MOD)\n\n                    pointMulAndAddIntoDest(VK_LOOKUP_TABLE_3_X_SLOT, currentEta, QUERIES_T_POLY_AGGREGATED_X_SLOT)\n                }\n            }\n\n            /*//////////////////////////////////////////////////////////////\n                                    5. Prepare aggregated commitment\n            //////////////////////////////////////////////////////////////*/\n\n            /// @dev Here we compute aggregated commitment for the final pairing\n            /// We use the formula:\n            /// [E] = ( t(z) + v * r(z)\n            ///       + v^2*a(z) + v^3*b(z) + v^4*c(z) + v^5*d(z)\n            ///       + v^6*main_gate_selector(z)\n            ///       + v^7*sigma_0(z) + v^8*sigma_1(z) + v^9*sigma_2(z)\n            ///       + v^10*t(z) + v^11*lookup_selector(z) + v^12*table_type(z)\n            ///       + u * (v^13*z_perm(z*omega) + v^14*d(z*omega)\n            ///           + v^15*s(z*omega) + v^16*z_lookup(z*omega) + v^17*t(z*omega)\n            ///       )\n            ///  ) * [1]\n            /// and\n            /// [F] = [D0] + v * [D1]\n            ///       + v^2*[a] + v^3*[b] + v^4*[c] + v^5*[d]\n            ///       + v^6*[main_gate_selector]\n            ///       + v^7*[sigma_0] + v^8*[sigma_1] + v^9*[sigma_2]\n            ///       + v^10*[t] + v^11*[lookup_selector] + v^12*[table_type]\n            ///       + u * ( v^13*[z_perm] + v^14*[d]\n            ///           + v^15*[s] + v^16*[z_lookup] + v^17*[t]\n            ///       )\n            function prepareAggregatedCommitment() {\n                // Here we compute parts of [E] and [F] without u multiplier\n                let aggregationChallenge := 1\n                let firstDCoeff\n                let firstTCoeff\n\n                mstore(AGGREGATED_AT_Z_X_SLOT, mload(QUERIES_AT_Z_0_X_SLOT))\n                mstore(AGGREGATED_AT_Z_Y_SLOT, mload(QUERIES_AT_Z_0_Y_SLOT))\n                let aggregatedOpeningAtZ := mload(PROOF_QUOTIENT_POLY_OPENING_AT_Z_SLOT)\n                {\n                    function updateAggregationChallenge(\n                        queriesCommitmentPoint,\n                        valueAtZ,\n                        curAggregationChallenge,\n                        curAggregatedOpeningAtZ\n                    ) -> newAggregationChallenge, newAggregatedOpeningAtZ {\n                        newAggregationChallenge := mulmod(curAggregationChallenge, mload(STATE_V_SLOT), R_MOD)\n                        pointMulAndAddIntoDest(queriesCommitmentPoint, newAggregationChallenge, AGGREGATED_AT_Z_X_SLOT)\n                        newAggregatedOpeningAtZ := addmod(\n                            curAggregatedOpeningAtZ,\n                            mulmod(newAggregationChallenge, mload(valueAtZ), R_MOD),\n                            R_MOD\n                        )\n                    }\n\n                    // We don't need to multiply by v, because we have already computed v * [D1]\n                    pointAddIntoDest(AGGREGATED_AT_Z_X_SLOT, QUERIES_AT_Z_1_X_SLOT, AGGREGATED_AT_Z_X_SLOT)\n                    aggregationChallenge := mulmod(aggregationChallenge, mload(STATE_V_SLOT), R_MOD)\n                    aggregatedOpeningAtZ := addmod(\n                        aggregatedOpeningAtZ,\n                        mulmod(aggregationChallenge, mload(PROOF_LINEARISATION_POLY_OPENING_AT_Z_SLOT), R_MOD),\n                        R_MOD\n                    )\n\n                    aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge(\n                        PROOF_STATE_POLYS_0_X_SLOT,\n                        PROOF_STATE_POLYS_0_OPENING_AT_Z_SLOT,\n                        aggregationChallenge,\n                        aggregatedOpeningAtZ\n                    )\n                    aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge(\n                        PROOF_STATE_POLYS_1_X_SLOT,\n                        PROOF_STATE_POLYS_1_OPENING_AT_Z_SLOT,\n                        aggregationChallenge,\n                        aggregatedOpeningAtZ\n                    )\n                    aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge(\n                        PROOF_STATE_POLYS_2_X_SLOT,\n                        PROOF_STATE_POLYS_2_OPENING_AT_Z_SLOT,\n                        aggregationChallenge,\n                        aggregatedOpeningAtZ\n                    )\n\n                    // Here we can optimize one scalar multiplication by aggregating coefficients near [d]\n                    // We will sum them and add and make one scalar multiplication: (coeff1 + coeff2) * [d]\n                    aggregationChallenge := mulmod(aggregationChallenge, mload(STATE_V_SLOT), R_MOD)\n                    firstDCoeff := aggregationChallenge\n                    aggregatedOpeningAtZ := addmod(\n                        aggregatedOpeningAtZ,\n                        mulmod(aggregationChallenge, mload(PROOF_STATE_POLYS_3_OPENING_AT_Z_SLOT), R_MOD),\n                        R_MOD\n                    )\n\n                    aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge(\n                        VK_GATE_SELECTORS_0_X_SLOT,\n                        PROOF_GATE_SELECTORS_0_OPENING_AT_Z_SLOT,\n                        aggregationChallenge,\n                        aggregatedOpeningAtZ\n                    )\n                    aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge(\n                        VK_PERMUTATION_0_X_SLOT,\n                        PROOF_COPY_PERMUTATION_POLYS_0_OPENING_AT_Z_SLOT,\n                        aggregationChallenge,\n                        aggregatedOpeningAtZ\n                    )\n                    aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge(\n                        VK_PERMUTATION_1_X_SLOT,\n                        PROOF_COPY_PERMUTATION_POLYS_1_OPENING_AT_Z_SLOT,\n                        aggregationChallenge,\n                        aggregatedOpeningAtZ\n                    )\n                    aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge(\n                        VK_PERMUTATION_2_X_SLOT,\n                        PROOF_COPY_PERMUTATION_POLYS_2_OPENING_AT_Z_SLOT,\n                        aggregationChallenge,\n                        aggregatedOpeningAtZ\n                    )\n\n                    // Here we can optimize one scalar multiplication by aggregating coefficients near [t]\n                    // We will sum them and add and make one scalar multiplication: (coeff1 + coeff2) * [t]\n                    aggregationChallenge := mulmod(aggregationChallenge, mload(STATE_V_SLOT), R_MOD)\n                    firstTCoeff := aggregationChallenge\n                    aggregatedOpeningAtZ := addmod(\n                        aggregatedOpeningAtZ,\n                        mulmod(aggregationChallenge, mload(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_SLOT), R_MOD),\n                        R_MOD\n                    )\n\n                    aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge(\n                        VK_LOOKUP_SELECTOR_X_SLOT,\n                        PROOF_LOOKUP_SELECTOR_POLY_OPENING_AT_Z_SLOT,\n                        aggregationChallenge,\n                        aggregatedOpeningAtZ\n                    )\n                    aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge(\n                        VK_LOOKUP_TABLE_TYPE_X_SLOT,\n                        PROOF_LOOKUP_TABLE_TYPE_POLY_OPENING_AT_Z_SLOT,\n                        aggregationChallenge,\n                        aggregatedOpeningAtZ\n                    )\n                }\n                mstore(AGGREGATED_OPENING_AT_Z_SLOT, aggregatedOpeningAtZ)\n\n                // Here we compute parts of [E] and [F] with u multiplier\n                aggregationChallenge := mulmod(aggregationChallenge, mload(STATE_V_SLOT), R_MOD)\n\n                let copyPermutationCoeff := addmod(\n                    mload(COPY_PERMUTATION_FIRST_AGGREGATED_COMMITMENT_COEFF),\n                    mulmod(aggregationChallenge, mload(STATE_U_SLOT), R_MOD),\n                    R_MOD\n                )\n\n                pointMulIntoDest(\n                    PROOF_COPY_PERMUTATION_GRAND_PRODUCT_X_SLOT,\n                    copyPermutationCoeff,\n                    AGGREGATED_AT_Z_OMEGA_X_SLOT\n                )\n                let aggregatedOpeningAtZOmega := mulmod(\n                    mload(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT),\n                    aggregationChallenge,\n                    R_MOD\n                )\n\n                {\n                    function updateAggregationChallenge(\n                        queriesCommitmentPoint,\n                        valueAtZ_Omega,\n                        previousCoeff,\n                        curAggregationChallenge,\n                        curAggregatedOpeningAtZ_Omega\n                    ) -> newAggregationChallenge, newAggregatedOpeningAtZ_Omega {\n                        newAggregationChallenge := mulmod(curAggregationChallenge, mload(STATE_V_SLOT), R_MOD)\n                        let finalCoeff := addmod(\n                            previousCoeff,\n                            mulmod(newAggregationChallenge, mload(STATE_U_SLOT), R_MOD),\n                            R_MOD\n                        )\n                        pointMulAndAddIntoDest(queriesCommitmentPoint, finalCoeff, AGGREGATED_AT_Z_OMEGA_X_SLOT)\n                        newAggregatedOpeningAtZ_Omega := addmod(\n                            curAggregatedOpeningAtZ_Omega,\n                            mulmod(newAggregationChallenge, mload(valueAtZ_Omega), R_MOD),\n                            R_MOD\n                        )\n                    }\n\n                    aggregationChallenge, aggregatedOpeningAtZOmega := updateAggregationChallenge(\n                        PROOF_STATE_POLYS_3_X_SLOT,\n                        PROOF_STATE_POLYS_3_OPENING_AT_Z_OMEGA_SLOT,\n                        firstDCoeff,\n                        aggregationChallenge,\n                        aggregatedOpeningAtZOmega\n                    )\n                    aggregationChallenge, aggregatedOpeningAtZOmega := updateAggregationChallenge(\n                        PROOF_LOOKUP_S_POLY_X_SLOT,\n                        PROOF_LOOKUP_S_POLY_OPENING_AT_Z_OMEGA_SLOT,\n                        mload(LOOKUP_S_FIRST_AGGREGATED_COMMITMENT_COEFF),\n                        aggregationChallenge,\n                        aggregatedOpeningAtZOmega\n                    )\n                    aggregationChallenge, aggregatedOpeningAtZOmega := updateAggregationChallenge(\n                        PROOF_LOOKUP_GRAND_PRODUCT_X_SLOT,\n                        PROOF_LOOKUP_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT,\n                        mload(LOOKUP_GRAND_PRODUCT_FIRST_AGGREGATED_COMMITMENT_COEFF),\n                        aggregationChallenge,\n                        aggregatedOpeningAtZOmega\n                    )\n                    aggregationChallenge, aggregatedOpeningAtZOmega := updateAggregationChallenge(\n                        QUERIES_T_POLY_AGGREGATED_X_SLOT,\n                        PROOF_LOOKUP_T_POLY_OPENING_AT_Z_OMEGA_SLOT,\n                        firstTCoeff,\n                        aggregationChallenge,\n                        aggregatedOpeningAtZOmega\n                    )\n                }\n                mstore(AGGREGATED_OPENING_AT_Z_OMEGA_SLOT, aggregatedOpeningAtZOmega)\n\n                // Now we can merge both parts and get [E] and [F]\n                let u := mload(STATE_U_SLOT)\n\n                // [F]\n                pointAddIntoDest(\n                    AGGREGATED_AT_Z_X_SLOT,\n                    AGGREGATED_AT_Z_OMEGA_X_SLOT,\n                    PAIRING_PAIR_WITH_GENERATOR_X_SLOT\n                )\n\n                // [E] = (aggregatedOpeningAtZ + u * aggregatedOpeningAtZOmega) * [1]\n                let aggregatedValue := addmod(\n                    mulmod(mload(AGGREGATED_OPENING_AT_Z_OMEGA_SLOT), u, R_MOD),\n                    mload(AGGREGATED_OPENING_AT_Z_SLOT),\n                    R_MOD\n                )\n\n                mstore(PAIRING_BUFFER_POINT_X_SLOT, 1)\n                mstore(PAIRING_BUFFER_POINT_Y_SLOT, 2)\n                pointMulIntoDest(PAIRING_BUFFER_POINT_X_SLOT, aggregatedValue, PAIRING_BUFFER_POINT_X_SLOT)\n            }\n\n            /*//////////////////////////////////////////////////////////////\n                                    5. Pairing\n            //////////////////////////////////////////////////////////////*/\n\n            /// @notice Checks the final pairing\n            /// @dev We should check the equation:\n            /// e([W] + u * [W'], [x]_2) = e(z * [W] + u * z * omega * [W'] + [F] - [E], [1]_2),\n            /// where [F] and [E] were computed previously\n            ///\n            /// Also we need to check that e([P1], [x]_2) = e([P2], [1]_2)\n            /// if we have the recursive part of the proof\n            /// where [P1] and [P2] are parts of the recursive proof\n            ///\n            /// We can aggregate both pairings into one for gas optimization:\n            /// e([W] + u * [W'] + u^2 * [P1], [x]_2) =\n            /// e(z * [W] + u * z * omega * [W'] + [F] - [E] + u^2 * [P2], [1]_2)\n            ///\n            /// u is a valid challenge for such aggregation,\n            /// because [P1] and [P2] are used in PI\n            function finalPairing() {\n                let u := mload(STATE_U_SLOT)\n                let z := mload(STATE_Z_SLOT)\n                let zOmega := mulmod(mload(STATE_Z_SLOT), OMEGA, R_MOD)\n\n                // [F] - [E]\n                pointSubAssign(PAIRING_PAIR_WITH_GENERATOR_X_SLOT, PAIRING_BUFFER_POINT_X_SLOT)\n\n                // +z * [W] + u * z * omega * [W']\n                pointMulAndAddIntoDest(PROOF_OPENING_PROOF_AT_Z_X_SLOT, z, PAIRING_PAIR_WITH_GENERATOR_X_SLOT)\n                pointMulAndAddIntoDest(\n                    PROOF_OPENING_PROOF_AT_Z_OMEGA_X_SLOT,\n                    mulmod(zOmega, u, R_MOD),\n                    PAIRING_PAIR_WITH_GENERATOR_X_SLOT\n                )\n\n                // [W] + u * [W']\n                mstore(PAIRING_PAIR_WITH_X_X_SLOT, mload(PROOF_OPENING_PROOF_AT_Z_X_SLOT))\n                mstore(PAIRING_PAIR_WITH_X_Y_SLOT, mload(PROOF_OPENING_PROOF_AT_Z_Y_SLOT))\n                pointMulAndAddIntoDest(PROOF_OPENING_PROOF_AT_Z_OMEGA_X_SLOT, u, PAIRING_PAIR_WITH_X_X_SLOT)\n                pointNegate(PAIRING_PAIR_WITH_X_X_SLOT)\n\n                // Add recursive proof part if needed\n                if mload(VK_RECURSIVE_FLAG_SLOT) {\n                    let uu := mulmod(u, u, R_MOD)\n                    pointMulAndAddIntoDest(PROOF_RECURSIVE_PART_P1_X_SLOT, uu, PAIRING_PAIR_WITH_GENERATOR_X_SLOT)\n                    pointMulAndAddIntoDest(PROOF_RECURSIVE_PART_P2_X_SLOT, uu, PAIRING_PAIR_WITH_X_X_SLOT)\n                }\n\n                // Calculate pairing\n                {\n                    mstore(0x000, mload(PAIRING_PAIR_WITH_GENERATOR_X_SLOT))\n                    mstore(0x020, mload(PAIRING_PAIR_WITH_GENERATOR_Y_SLOT))\n\n                    mstore(0x040, G2_ELEMENTS_0_X1)\n                    mstore(0x060, G2_ELEMENTS_0_X2)\n                    mstore(0x080, G2_ELEMENTS_0_Y1)\n                    mstore(0x0a0, G2_ELEMENTS_0_Y2)\n\n                    mstore(0x0c0, mload(PAIRING_PAIR_WITH_X_X_SLOT))\n                    mstore(0x0e0, mload(PAIRING_PAIR_WITH_X_Y_SLOT))\n\n                    mstore(0x100, G2_ELEMENTS_1_X1)\n                    mstore(0x120, G2_ELEMENTS_1_X2)\n                    mstore(0x140, G2_ELEMENTS_1_Y1)\n                    mstore(0x160, G2_ELEMENTS_1_Y2)\n\n                    let success := staticcall(gas(), 8, 0, 0x180, 0x00, 0x20)\n                    if iszero(success) {\n                        revertWithMessage(32, \"finalPairing: precompile failure\")\n                    }\n                    if iszero(mload(0)) {\n                        revertWithMessage(29, \"finalPairing: pairing failure\")\n                    }\n                }\n            }\n\n            /*//////////////////////////////////////////////////////////////\n                                    Verification\n            //////////////////////////////////////////////////////////////*/\n\n            // Step 1: Load the proof and check the correctness of its parts\n            loadProof()\n\n            // Step 2: Recompute all the challenges with the transcript\n            initializeTranscript()\n\n            // Step 3: Check the quotient equality\n            verifyQuotientEvaluation()\n\n            // Step 4: Compute queries [D0] and v * [D1]\n            prepareQueries()\n\n            // Step 5: Compute [E] and [F]\n            prepareAggregatedCommitment()\n\n            // Step 6: Check the final pairing with aggregated recursive proof\n            finalPairing()\n\n            mstore(0, true)\n            return(0, 32)\n        }\n    }\n}\n"
      }
    },
    "settings": {
      "optimizer": {
        "enabled": true,
        "runs": 9999999
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "evmVersion": "cancun",
      "libraries": {}
    }
  }}