{"file_path":"contracts/state-transition/verifiers/EraVerifierFflonk.sol","creation_status":"success","source_code":"// SPDX-License-Identifier: MIT\n\npragma solidity 0.8.28;\n\nimport {IVerifierV2} from \"../chain-interfaces/IVerifierV2.sol\";\n\n/// @title Fflonk Verifier Implementation\n/// @author Matter Labs\n/// @notice FFT inspired version of PlonK to optimize on-chain gas cost\n/// @dev For better understanding of the protocol follow the below papers:\n/// * Fflonk Paper: https://eprint.iacr.org/2021/1167\n/// @dev Contract was generated from a verification key with a hash of 0xd89b62ac599b15d9dc47860c8ebca5964c99e7b177e9b8ea38a0c13714dc32bb\n/// @custom:security-contact security@matterlabs.dev\ncontract EraVerifierFflonk is IVerifierV2 {\n    // ================Constants================\n    uint32 internal constant DST_0 = 0;\n    uint32 internal constant DST_1 = 1;\n    uint32 internal constant DST_CHALLENGE = 2;\n    uint256 internal constant FR_MASK = 0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;\n    uint256 internal constant Q_MOD = 21888242871839275222246405745257275088696311157297823662689037894645226208583;\n    uint256 internal constant R_MOD = 21888242871839275222246405745257275088548364400416034343698204186575808495617;\n    uint256 internal constant BN254_B_COEFF = 3;\n\n    // ================Verification Key================\n    uint256 internal constant VK_NUM_INPUTS = 1;\n    // [C0]1 = qL(X^8)+ X*qR(X^8)+ X^2*qO(X^8)+ X^3*qM(X^8)+ X^4*qC(X^8)+ X^5*Sσ1(X^8)+ X^6*Sσ2(X^8)+ X^7*Sσ3(X^8)\n    uint256 internal constant VK_C0_G1_X = 0x2d61513640465d1ae53659963bae24b4fd7fed16847f5c74641fecd2869f5310;\n    uint256 internal constant VK_C0_G1_Y = 0x1a8ca8d060ed719a135103d35efc5de2484294b58ed7a32c600262b757b63c5e;\n\n    // k1 = 5, k2 = 7\n    uint256 internal constant VK_NON_RESIDUES_0 = 0x05;\n    uint256 internal constant VK_NON_RESIDUES_1 = 0x07;\n\n    // G2 Elements = [1]_2, [s]_2\n    uint256 internal constant VK_G2_ELEMENT_0_X1 = 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2;\n    uint256 internal constant VK_G2_ELEMENT_0_X2 = 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed;\n    uint256 internal constant VK_G2_ELEMENT_0_Y1 = 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b;\n    uint256 internal constant VK_G2_ELEMENT_0_Y2 = 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa;\n    uint256 internal constant VK_G2_ELEMENT_1_X1 = 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1;\n    uint256 internal constant VK_G2_ELEMENT_1_X2 = 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0;\n    uint256 internal constant VK_G2_ELEMENT_1_Y1 = 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4;\n    uint256 internal constant VK_G2_ELEMENT_1_Y2 = 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55;\n\n    // Memory slots from 0x000 to 0x200 are reserved for intermediate computations and call to precompiles.\n\n    // ================Transcript================\n    // ================Constants================\n    uint256 internal constant ONE = 1;\n    uint256 internal constant DOMAIN_SIZE = 8388608;\n    uint256 internal constant OMEGA = 0x1283ba6f4b7b1a76ba2008fe823128bea4adb9269cbfd7c41c223be65bc60863;\n    // =========================================\n    uint256 internal constant TRANSCRIPT_BEGIN_SLOT = 0x200;\n    uint256 internal constant TRANSCRIPT_DST_BYTE_SLOT = 0x203;\n    uint256 internal constant TRANSCRIPT_STATE_0_SLOT = 0x204;\n    uint256 internal constant TRANSCRIPT_STATE_1_SLOT = 0x224;\n    uint256 internal constant TRANSCRIPT_CHALLENGE_SLOT = 0x244;\n\n    // ================PartialVerifierState================\n    // copy-permutation challenges\n    uint256 internal constant PVS_BETA = 0x264 + 0x00;\n    uint256 internal constant PVS_GAMMA = 0x264 + 0x20;\n    // evaluation challenges\n    uint256 internal constant PVS_R = 0x264 + 0x40;\n    uint256 internal constant PVS_Z = 0x264 + 0x60;\n    uint256 internal constant PVS_Z_OMEGA = 0x264 + 0x80;\n    // aggregation challenge\n    uint256 internal constant PVS_ALPHA_0 = 0x264 + 0xa0;\n    uint256 internal constant PVS_ALPHA_1 = 0x264 + 0xc0;\n    // final evaluation challenge\n    uint256 internal constant PVS_Y = 0x264 + 0xe0;\n    // convenience\n    uint256 internal constant PVS_VANISHING_AT_Z = 0x264 + 0x100;\n    uint256 internal constant PVS_VANISHING_AT_Z_INV = 0x264 + 0x120;\n    uint256 internal constant PVS_L_0_AT_Z = 0x264 + 0x140;\n    uint256 internal constant MAIN_GATE_QUOTIENT_AT_Z = 0x264 + 0x160;\n    uint256 internal constant COPY_PERM_FIRST_QUOTIENT_AT_Z = 0x264 + 0x180;\n    uint256 internal constant COPY_PERM_SECOND_QUOTIENT_AT_Z = 0x264 + 0x1a0;\n    // ================Opening State================\n    // h0, h1, h2, h2_shifted\n    uint256 internal constant OPS_OPENING_POINTS = 0x264 + 0x1c0 + 0x00; // 4 slots\n    uint256 internal constant OPS_Y_POWS = 0x264 + 0x1c0 + 0x80; // 9 SLOTS\n\n    // ================Pairing State================\n\n    uint256 internal constant PS_VANISHING_AT_Y = 0x264 + 0x1c0 + 0x1a0;\n    uint256 internal constant PS_INV_ZTS0_AT_Y = 0x264 + 0x1c0 + 0x1c0;\n    uint256 internal constant PS_SET_DIFFERENCES_AT_Y = 0x264 + 0x1c0 + 0x1e0; // 3 slots\n    uint256 internal constant PS_MINUS_Z = 0x264 + 0x1c0 + 0x240; // 2 slots\n    uint256 internal constant PS_R_EVALS = 0x264 + 0x1c0 + 0x280; // 3 slots\n\n    // ================In Memory(from Proof)================\n    uint256 internal constant MEM_PROOF_PUBLIC_INPUT_SLOT = 0x264 + 0x1c0 + 0x2e0;\n\n    uint256 internal constant MEM_PROOF_COMMITMENT_0_G1_X = 0x264 + 0x1c0 + 0x2e0 + 0x20;\n    uint256 internal constant MEM_PROOF_COMMITMENT_0_G1_Y = 0x264 + 0x1c0 + 0x2e0 + 0x40;\n    uint256 internal constant MEM_PROOF_COMMITMENT_1_G1_X = 0x264 + 0x1c0 + 0x2e0 + 0x60;\n    uint256 internal constant MEM_PROOF_COMMITMENT_1_G1_Y = 0x264 + 0x1c0 + 0x2e0 + 0x80;\n    uint256 internal constant MEM_PROOF_COMMITMENT_2_G1_X = 0x264 + 0x1c0 + 0x2e0 + 0xa0;\n    uint256 internal constant MEM_PROOF_COMMITMENT_2_G1_Y = 0x264 + 0x1c0 + 0x2e0 + 0xc0;\n    uint256 internal constant MEM_PROOF_COMMITMENT_3_G1_X = 0x264 + 0x1c0 + 0x2e0 + 0xe0;\n    uint256 internal constant MEM_PROOF_COMMITMENT_3_G1_Y = 0x264 + 0x1c0 + 0x2e0 + 0x100;\n\n    uint256 internal constant MEM_PROOF_EVALUATIONS = 0x264 + 0x1c0 + 0x2e0 + 0x120; // 15 slots\n\n    uint256 internal constant MEM_PROOF_MONTGOMERY_LAGRANGE_BASIS_INVERSE = 0x264 + 0x1c0 + 0x2e0 + 0x120 + 0x1e0; // 1 slots\n\n    uint256 internal constant MEM_LAGRANGE_BASIS_DENOMS = 0x264 + 0x1c0 + 0x2e0 + 0x120 + 0x200; //18 slots\n    uint256 internal constant MEM_LAGRANGE_BASIS_DENOM_PRODUCTS = 0x264 + 0x1c0 + 0x2e0 + 0x120 + 0x440; // 18 slots\n    uint256 internal constant MEM_PROOF_LAGRANGE_BASIS_EVALS = 0x264 + 0x1c0 + 0x2e0 + 0x120 + 0x680; // 18 Slots\n\n    // ================Constants================\n    uint256 internal constant PROOF_PUBLIC_INPUTS_LENGTH = 1;\n    uint256 internal constant PROOF_LENGTH = 24;\n    uint256 internal constant PROOF_EVALUATIONS_LENGTH = 15;\n    uint256 internal constant TOTAL_LAGRANGE_BASIS_INVERSES_LENGTH = 18;\n\n    /// @inheritdoc IVerifierV2\n    function verificationKeyHash() external pure returns (bytes32) {\n        return\n            keccak256(\n                // solhint-disable-next-line func-named-parameters\n                abi.encodePacked(\n                    VK_NUM_INPUTS,\n                    VK_C0_G1_X,\n                    VK_C0_G1_Y,\n                    VK_NON_RESIDUES_0,\n                    VK_NON_RESIDUES_1,\n                    _getG2Elements()\n                )\n            );\n    }\n\n    /// @dev This breakdown is done to avoid stack-too-deep error\n    /// @return bytes memory The G2 elements\n    function _getG2Elements() internal pure returns (bytes memory) {\n        return\n            // solhint-disable-next-line func-named-parameters\n            abi.encodePacked(\n                VK_G2_ELEMENT_0_X1,\n                VK_G2_ELEMENT_0_X2,\n                VK_G2_ELEMENT_0_Y1,\n                VK_G2_ELEMENT_0_Y2,\n                VK_G2_ELEMENT_1_X1,\n                VK_G2_ELEMENT_1_X2,\n                VK_G2_ELEMENT_1_Y1,\n                VK_G2_ELEMENT_1_Y2\n            );\n    }\n\n    /// @inheritdoc IVerifierV2\n    function verify(\n        uint256[] calldata, // _publicInputs\n        uint256[] calldata // _proof\n    ) external view virtual returns (bool) {\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            // load public inputs and proof from the calldata\n            load_inputs()\n            initialize_transcript()\n            // identities at verifier's point\n            compute_main_gate_quotient()\n            compute_copy_permutation_quotients()\n            // openings\n            initialize_opening_state()\n            // final pairing\n            let result := check_openings()\n            mstore(0, result)\n            return(0, 0x20)\n\n            function load_inputs() {\n                // 1. Load public inputs\n                let publicInputOffset := calldataload(0x04)\n                let publicInputLengthInWords := calldataload(add(publicInputOffset, 0x04))\n                // We expect only one public input\n                if iszero(eq(publicInputLengthInWords, PROOF_PUBLIC_INPUTS_LENGTH)) {\n                    revertWithMessage(32, \"public input length is incorrect\")\n                }\n                mstore(MEM_PROOF_PUBLIC_INPUT_SLOT, mod(calldataload(add(publicInputOffset, 0x24)), R_MOD))\n\n                // 2. Load proof\n                let proofLengthOffset := calldataload(0x24)\n                let proofLengthInWords := calldataload(add(proofLengthOffset, 0x04))\n\n                if iszero(eq(proofLengthInWords, PROOF_LENGTH)) {\n                    revertWithMessage(25, \"proof length is incorrect\")\n                }\n                let proofOffset := add(proofLengthOffset, 0x24)\n                // Note: We don't accept the point-at-infinity as a valid input for the commitments considering the security risks involved,\n                // as it may aid in proof manipulation and final pairing computation.\n                {\n                    let x := mod(calldataload(proofOffset), Q_MOD)\n                    let y := mod(calldataload(add(proofOffset, 0x20)), Q_MOD)\n                    let xx := mulmod(x, x, Q_MOD)\n                    if iszero(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD))) {\n                        revertWithMessage(28, \"commitment 0 is not on curve\")\n                    }\n                    mstore(MEM_PROOF_COMMITMENT_0_G1_Y, y)\n                    mstore(MEM_PROOF_COMMITMENT_0_G1_X, x)\n                }\n                {\n                    let x := mod(calldataload(add(proofOffset, 0x40)), Q_MOD)\n                    let y := mod(calldataload(add(proofOffset, 0x60)), Q_MOD)\n                    let xx := mulmod(x, x, Q_MOD)\n                    if iszero(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD))) {\n                        revertWithMessage(28, \"commitment 1 is not on curve\")\n                    }\n                    mstore(MEM_PROOF_COMMITMENT_1_G1_Y, y)\n                    mstore(MEM_PROOF_COMMITMENT_1_G1_X, x)\n                }\n                {\n                    let x := mod(calldataload(add(proofOffset, 0x80)), Q_MOD)\n                    let y := mod(calldataload(add(proofOffset, 0xa0)), Q_MOD)\n                    let xx := mulmod(x, x, Q_MOD)\n                    if iszero(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD))) {\n                        revertWithMessage(28, \"commitment 2 is not on curve\")\n                    }\n                    mstore(MEM_PROOF_COMMITMENT_2_G1_Y, y)\n                    mstore(MEM_PROOF_COMMITMENT_2_G1_X, x)\n                }\n                {\n                    let x := mod(calldataload(add(proofOffset, 0xc0)), Q_MOD)\n                    let y := mod(calldataload(add(proofOffset, 0xe0)), Q_MOD)\n                    let xx := mulmod(x, x, Q_MOD)\n                    if iszero(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD))) {\n                        revertWithMessage(28, \"commitment 3 is not on curve\")\n                    }\n                    mstore(MEM_PROOF_COMMITMENT_3_G1_Y, y)\n                    mstore(MEM_PROOF_COMMITMENT_3_G1_X, x)\n                }\n                proofOffset := add(proofOffset, 0x100)\n\n                for {\n                    let i := 0\n                } lt(i, PROOF_EVALUATIONS_LENGTH) {\n                    i := add(i, 1)\n                } {\n                    let eval := mod(calldataload(add(proofOffset, mul(i, 0x20))), R_MOD)\n                    let slot := add(MEM_PROOF_EVALUATIONS, mul(i, 0x20))\n                    mstore(slot, eval)\n                }\n                proofOffset := add(proofOffset, mul(PROOF_EVALUATIONS_LENGTH, 0x20))\n\n                mstore(MEM_PROOF_MONTGOMERY_LAGRANGE_BASIS_INVERSE, mod(calldataload(proofOffset), R_MOD))\n            }\n\n            /**\n             * @dev Commits data in the transcript then gets the challenges\n             * @notice that at this point, the transcript only has public inputs\n             * But luckily prover doesn't need any randomness in the first round\n             * so that prover has no control over the values because quotients are\n             * separated(there is no quotient aggregation neither in this round nor all rounds)\n             *\n             * w = 0x1283ba6f4b7b1a76ba2008fe823128bea4adb9269cbfd7c41c223be65bc60863\n             */\n            function initialize_transcript() {\n                if iszero(lt(DOMAIN_SIZE, R_MOD)) {\n                    revertWithMessage(26, \"Domain size >= R_MOD [ITS]\")\n                }\n                if iszero(lt(OMEGA, R_MOD)) {\n                    revertWithMessage(20, \"Omega >= R_MOD [ITS]\")\n                }\n                for {\n                    let i := 0\n                } lt(i, VK_NUM_INPUTS) {\n                    i := add(i, 1)\n                } {\n                    update_transcript(mload(add(MEM_PROOF_PUBLIC_INPUT_SLOT, mul(i, 0x20))))\n                }\n                // commit first round commitment: preprocessed polynomials\n                update_transcript(VK_C0_G1_X)\n                update_transcript(VK_C0_G1_Y)\n\n                // commit second round commitment: witnesses and gate identities\n                update_transcript(mload(MEM_PROOF_COMMITMENT_0_G1_X))\n                update_transcript(mload(MEM_PROOF_COMMITMENT_0_G1_Y))\n\n                // copy-permutation challenges\n                mstore(PVS_BETA, get_challenge(0))\n                mstore(PVS_GAMMA, get_challenge(1))\n                // commit third round commitment: copy-perm\n                update_transcript(mload(MEM_PROOF_COMMITMENT_1_G1_X))\n                update_transcript(mload(MEM_PROOF_COMMITMENT_1_G1_Y))\n                // get evaluation challenge\n                // all system polynomials will be evaluated at z\n                // then combined polynomials will be opened at h_i = r^power_i\n                // then it becomes e.g C_i(X) = f_0(x^2) + x*f(x^2) in case of two polynomials\n                mstore(PVS_R, get_challenge(2))\n                // commit all evaluations\n                for {\n                    let i := 0\n                } lt(i, PROOF_EVALUATIONS_LENGTH) {\n                    i := add(i, 1)\n                } {\n                    update_transcript(mload(add(MEM_PROOF_EVALUATIONS, mul(i, 0x20))))\n                }\n                // get aggregation challenge\n                mstore(PVS_ALPHA_0, get_challenge(3))\n                mstore(PVS_ALPHA_1, mulmod(mload(PVS_ALPHA_0), mload(PVS_ALPHA_0), R_MOD))\n                // commit w(X)\n                update_transcript(mload(MEM_PROOF_COMMITMENT_2_G1_X))\n                update_transcript(mload(MEM_PROOF_COMMITMENT_2_G1_Y))\n                // opening challenge\n                mstore(PVS_Y, get_challenge(4))\n                mstore(PVS_Z, modexp(mload(PVS_R), 24))\n                // grand product of copy-permutation needs to be opened at shifted position\n                mstore(PVS_Z_OMEGA, mulmod(mload(PVS_Z), OMEGA, R_MOD))\n                // Z_h(z) = X^N - 1\n                mstore(PVS_VANISHING_AT_Z, addmod(modexp(mload(PVS_Z), DOMAIN_SIZE), sub(R_MOD, ONE), R_MOD))\n                // L0(z) = 1/(N*(X-1)) * (X^N - 1)\n                mstore(\n                    PVS_L_0_AT_Z,\n                    modexp(mulmod(addmod(mload(PVS_Z), sub(R_MOD, ONE), R_MOD), DOMAIN_SIZE, R_MOD), sub(R_MOD, 2))\n                )\n                mstore(PVS_L_0_AT_Z, mulmod(mload(PVS_L_0_AT_Z), mload(PVS_VANISHING_AT_Z), R_MOD))\n                mstore(PVS_VANISHING_AT_Z_INV, modexp(mload(PVS_VANISHING_AT_Z), sub(R_MOD, 2)))\n            }\n\n            /**\n             * @dev Computes main gate quotient T0(ζ)\n             * T0(ζ) = (qm(ζ)*a(ζ)*b(ζ) + qa(ζ)*a(ζ) + qb(ζ)*b(ζ) + qc(ζ)*c(ζ) + qconst(ζ) + PI*L0(ζ)) * ZH(ζ)^-1\n             */\n            function compute_main_gate_quotient() {\n                // q_const\n                let rhs := mload(add(MEM_PROOF_EVALUATIONS, mul(4, 0x20)))\n                rhs := addmod(rhs, mulmod(mload(PVS_L_0_AT_Z), mload(MEM_PROOF_PUBLIC_INPUT_SLOT), R_MOD), R_MOD)\n                for {\n                    let i := 0\n                } lt(i, 3) {\n                    i := add(i, 1)\n                } {\n                    rhs := addmod(\n                        rhs,\n                        mulmod(\n                            mload(add(MEM_PROOF_EVALUATIONS, mul(i, 0x20))),\n                            mload(add(MEM_PROOF_EVALUATIONS, mul(add(8, i), 0x20))),\n                            R_MOD\n                        ),\n                        R_MOD\n                    )\n                }\n                // q_m*A*B\n                rhs := mulmod(\n                    addmod(\n                        rhs,\n                        mulmod(\n                            mulmod(\n                                mload(add(MEM_PROOF_EVALUATIONS, mul(3, 0x20))),\n                                mload(add(MEM_PROOF_EVALUATIONS, mul(8, 0x20))),\n                                R_MOD\n                            ),\n                            mload(add(MEM_PROOF_EVALUATIONS, mul(9, 0x20))),\n                            R_MOD\n                        ),\n                        R_MOD\n                    ),\n                    mload(PVS_VANISHING_AT_Z_INV),\n                    R_MOD\n                )\n                mstore(MAIN_GATE_QUOTIENT_AT_Z, rhs)\n            }\n\n            /**\n             * @dev Computes copy permutation quotients T1(ζ) & T2(ζ)\n             * T1(ζ) = ((z(ζ) * (a(ζ)+β*ζ+γ) * (b(ζ)+k1*β*ζ+γ) * (c(ζ)+k2*β*ζ+γ))\n             *           −(z(ζω) * (a(ζ)+β*sσ1(ζ)+γ) * (b(ζ)+β*sσ2(ζ)+γ) * (c(ζ)+β*sσ3(ζ)+γ)) * ZH(ζ)^-1\n             * T2(ζ) = (z(ζ)−1)*L0(ζ)*ZH(ζ)^-1\n             */\n            function compute_copy_permutation_quotients() {\n                let tmp\n                let tmp2\n                // (c(ζ)+k2*β*ζ+γ)\n                let rhs := addmod(\n                    addmod(\n                        mulmod(mulmod(mload(PVS_BETA), mload(PVS_Z), R_MOD), VK_NON_RESIDUES_1, R_MOD),\n                        mload(PVS_GAMMA),\n                        R_MOD\n                    ),\n                    mload(add(MEM_PROOF_EVALUATIONS, mul(add(8, 2), 0x20))),\n                    R_MOD\n                )\n                // (b(ζ)+k1*β*ζ+γ)\n                tmp := addmod(\n                    addmod(\n                        mulmod(mulmod(mload(PVS_BETA), mload(PVS_Z), R_MOD), VK_NON_RESIDUES_0, R_MOD),\n                        mload(PVS_GAMMA),\n                        R_MOD\n                    ),\n                    mload(add(MEM_PROOF_EVALUATIONS, mul(add(8, 1), 0x20))),\n                    R_MOD\n                )\n                // (b(ζ)+k1*β*ζ+γ) * (c(ζ)+k2*β*ζ+γ)\n                rhs := mulmod(rhs, tmp, R_MOD)\n                // (z(ζ) * (a(ζ)+β*ζ+γ) * (b(ζ)+k1*β*ζ+γ) * (c(ζ)+k2*β*ζ+γ)\n                rhs := mulmod(\n                    mulmod(\n                        rhs,\n                        addmod(\n                            addmod(mulmod(mload(PVS_BETA), mload(PVS_Z), R_MOD), mload(PVS_GAMMA), R_MOD),\n                            mload(add(MEM_PROOF_EVALUATIONS, mul(8, 0x20))),\n                            R_MOD\n                        ),\n                        R_MOD\n                    ),\n                    mload(add(MEM_PROOF_EVALUATIONS, mul(11, 0x20))),\n                    R_MOD\n                )\n\n                // (z(ζω) * (b(ζ)+β*sσ2(ζ)+γ) * (c(ζ)+β*sσ3(ζ)+γ))\n                tmp2 := mulmod(\n                    mulmod(\n                        addmod(\n                            addmod(\n                                mulmod(mload(PVS_BETA), mload(add(MEM_PROOF_EVALUATIONS, mul(add(5, 2), 0x20))), R_MOD),\n                                mload(PVS_GAMMA),\n                                R_MOD\n                            ),\n                            mload(add(MEM_PROOF_EVALUATIONS, mul(add(8, 2), 0x20))),\n                            R_MOD\n                        ),\n                        mload(add(MEM_PROOF_EVALUATIONS, mul(12, 0x20))),\n                        R_MOD\n                    ),\n                    addmod(\n                        addmod(\n                            mulmod(mload(PVS_BETA), mload(add(MEM_PROOF_EVALUATIONS, mul(add(5, 1), 0x20))), R_MOD),\n                            mload(PVS_GAMMA),\n                            R_MOD\n                        ),\n                        mload(add(MEM_PROOF_EVALUATIONS, mul(add(8, 1), 0x20))),\n                        R_MOD\n                    ),\n                    R_MOD\n                )\n                // (a(ζ)+β*sσ1(ζ)+γ)\n                tmp := addmod(\n                    addmod(\n                        mulmod(mload(PVS_BETA), mload(add(MEM_PROOF_EVALUATIONS, mul(5, 0x20))), R_MOD),\n                        mload(PVS_GAMMA),\n                        R_MOD\n                    ),\n                    mload(add(MEM_PROOF_EVALUATIONS, mul(8, 0x20))),\n                    R_MOD\n                )\n                // z(ζω) * (a(ζ)+β*sσ1(ζ)+γ) * (b(ζ)+β*sσ2(ζ)+γ) * (c(ζ)+β*sσ3(ζ)+γ)\n                tmp2 := mulmod(tmp2, tmp, R_MOD)\n                // −(z(ζω) * (a(ζ)+β*sσ1(ζ)+γ) * (b(ζ)+β*sσ2(ζ)+γ) * (c(ζ)+β*sσ3(ζ)+γ))\n                tmp2 := sub(R_MOD, tmp2)\n                // ((z(ζ) * (a(ζ)+β*ζ+γ) * (b(ζ)+k1*β*ζ+γ) * (c(ζ)+k2*β*ζ+γ)) − (z(ζω) * (a(ζ)+β*sσ1(ζ)+γ) * (b(ζ)+β*sσ2(ζ)+γ) * (c(ζ)+β*sσ3(ζ)+γ)) * ZH(ζ)^-1\n                rhs := mulmod(addmod(rhs, tmp2, R_MOD), mload(PVS_VANISHING_AT_Z_INV), R_MOD)\n                mstore(COPY_PERM_FIRST_QUOTIENT_AT_Z, rhs)\n\n                // (z(ζ)−1)*L0(ζ)*ZH(ζ)^-1\n                rhs := mulmod(\n                    mulmod(\n                        addmod(mload(add(MEM_PROOF_EVALUATIONS, mul(11, 0x20))), sub(R_MOD, 1), R_MOD),\n                        mload(PVS_L_0_AT_Z),\n                        R_MOD\n                    ),\n                    mload(PVS_VANISHING_AT_Z_INV),\n                    R_MOD\n                )\n                mstore(COPY_PERM_SECOND_QUOTIENT_AT_Z, rhs)\n            }\n\n            /**\n             * @dev Computes partial lagrange basis evaluations Li(y)_numerator {i = [start..(start+num_polys))} using montgomery lagrange basis inverses sent with proof.\n             * Li(y)_numerator = (w_i * (y^{num_polys} - h^{num_polys}))\n             * Li(y)_denominator = (num_polys * h^{num_polys-1} * (y - (h * w_i)))\n             * Li(y) = Li(y)_numerator / Li(y)_denominator = (w_i * (y^{num_polys} - h^{num_polys})) / (num_polys * h^{num_polys-1} * (y - (h * w_i)))\n             *\n             * Also calculates the products of the denominators of the lagrange basis evaluations:\n             * Li(y)_denominators_product = Li(y)_previous_denominators_product * (∏(Li(y)_denominator {i = [start..(start+num_polys))}))\n             */\n            function precompute_partial_lagrange_basis_evaluations(start, num_polys, y, omega, h, product)\n                -> interim_product\n            {\n                if gt(add(start, num_polys), TOTAL_LAGRANGE_BASIS_INVERSES_LENGTH) {\n                    revertWithMessage(31, \"Precompute Eval. Error [PLBEI1]\")\n                }\n                let tmp := h\n                let loop_length := sub(num_polys, 2)\n                // h^{num_polys-1}\n                for {\n                    let i := 0\n                } lt(i, loop_length) {\n                    i := add(i, 1)\n                } {\n                    tmp := mulmod(tmp, h, R_MOD)\n                }\n                // num_polys * h^{num_polys-1}\n                let constant_part := mulmod(num_polys, tmp, R_MOD)\n\n                // y^{num_polys}\n                let y_pow := mload(add(OPS_Y_POWS, mul(num_polys, 0x20)))\n                // h^{num_polys}\n                let num_at_y := mulmod(tmp, h, R_MOD)\n                // -h^{num_polys}\n                num_at_y := sub(R_MOD, num_at_y)\n                // (y^{num_polys} - h^{num_polys})\n                num_at_y := addmod(num_at_y, y_pow, R_MOD)\n\n                let current_omega := 1\n                for {\n                    let i := 0\n                } lt(i, num_polys) {\n                    i := add(i, 1)\n                } {\n                    // h*w_i\n                    tmp := mulmod(current_omega, h, R_MOD)\n                    // -h*w_i\n                    tmp := sub(R_MOD, tmp)\n                    // y-(h*w_i)\n                    tmp := addmod(tmp, y, R_MOD)\n                    // (num_polys * h^{num_polys-1} * (y - (h * w_i)))\n                    tmp := mulmod(tmp, constant_part, R_MOD)\n\n                    mstore(add(MEM_LAGRANGE_BASIS_DENOMS, mul(add(start, i), 0x20)), tmp)\n\n                    product := mulmod(product, tmp, R_MOD)\n\n                    mstore(add(MEM_LAGRANGE_BASIS_DENOM_PRODUCTS, mul(add(start, i), 0x20)), product)\n                    // Li(y) = (W_i * (y^{num_polys} - h^{num_polys}))\n                    mstore(\n                        add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(start, i), 0x20)),\n                        mulmod(num_at_y, current_omega, R_MOD)\n                    )\n\n                    // w_i {i = i+1}\n                    current_omega := mulmod(current_omega, omega, R_MOD)\n                }\n\n                interim_product := product\n            }\n\n            /**\n             * sma: TODO add description\n             */\n            function temp_name_inner_function(num_polys, h, h_shifted) -> constant_parts_0, constant_parts_1, t_0, t_1 {\n                let h_pows_0 := h\n                let h_pows_1 := h_shifted\n                let loop_length := sub(num_polys, 2)\n                // h^{num_polys-1} & h_s^{num_polys-1}\n                for {\n                    let i := 0\n                } lt(i, loop_length) {\n                    i := add(i, 1)\n                } {\n                    h_pows_0 := mulmod(h_pows_0, h, R_MOD)\n                    h_pows_1 := mulmod(h_pows_1, h_shifted, R_MOD)\n                }\n                constant_parts_0 := h_pows_0\n                constant_parts_1 := h_pows_1\n                // h^{num_polys}\n                h_pows_0 := mulmod(h_pows_0, h, R_MOD)\n                // h_s^{num_polys}\n                h_pows_1 := mulmod(h_pows_1, h_shifted, R_MOD)\n\n                // h^{num_polys-1} * h_s^{num_polys}\n                constant_parts_0 := mulmod(constant_parts_0, h_pows_1, R_MOD)\n                // -h^{num_polys-1} * h_s^{num_polys}\n                constant_parts_0 := sub(R_MOD, constant_parts_0)\n                // h_s^{num_polys-1} * h^{num_polys}\n                constant_parts_1 := mulmod(constant_parts_1, h_pows_0, R_MOD)\n                // -h_s^{num_polys-1} * h^{num_polys}\n                constant_parts_1 := sub(R_MOD, constant_parts_1)\n\n                // y^{num_polys}\n                let t_2 := mload(add(OPS_Y_POWS, mul(num_polys, 0x20)))\n                // h^{num_polys} * h_s^{num_polys}\n                t_1 := mulmod(h_pows_0, h_pows_1, R_MOD)\n                // h^{num_polys} + h_s^{num_polys}\n                t_0 := addmod(h_pows_0, h_pows_1, R_MOD)\n                // y^{num_polys} * (h^{num_polys} + h_s^{num_polys})\n                t_0 := mulmod(t_0, t_2, R_MOD)\n                // - (y^{num_polys} * (h^{num_polys} + h_s^{num_polys}))\n                t_0 := sub(R_MOD, t_0)\n                // h^{num_polys} * h_s^{num_polys} - (y^{num_polys} * (h^{num_polys} + h_s^{num_polys}))\n                t_1 := addmod(t_1, t_0, R_MOD)\n                // y^{2*num_polys}\n                t_2 := mulmod(t_2, t_2, R_MOD)\n                // y^{2*num_polys} + (h^{num_polys} * h_s^{num_polys}) - (y^{num_polys} * (h^{num_polys} + h_s^{num_polys}))\n                t_1 := addmod(t_1, t_2, R_MOD)\n                loop_length := sub(num_polys, 1)\n                // h^{(2*num_polys)-1} & h_s^{(2*num_polys)-1}\n                for {\n                    let i := 0\n                } lt(i, loop_length) {\n                    i := add(i, 1)\n                } {\n                    h_pows_0 := mulmod(h_pows_0, h, R_MOD)\n                    h_pows_1 := mulmod(h_pows_1, h_shifted, R_MOD)\n                }\n                // h^{(2*num_polys)-1}-(h^{num_polys-1} * h_s^{num_polys})\n                constant_parts_0 := addmod(constant_parts_0, h_pows_0, R_MOD)\n                // num_polys * (h^{(2*num_polys)-1}-(h^{num_polys-1} * h_s^{num_polys}))\n                constant_parts_0 := mulmod(constant_parts_0, num_polys, R_MOD)\n                // h_s^{(2*num_polys)-1}-(h_s^{num_polys-1} * h^{num_polys})\n                constant_parts_1 := addmod(constant_parts_1, h_pows_1, R_MOD)\n                // num_polys * (h_s^{(2*num_polys)-1}-(h_s^{num_polys-1} * h^{num_polys}))\n                constant_parts_1 := mulmod(constant_parts_1, num_polys, R_MOD)\n            }\n\n            /**\n             * @dev Computes partial lagrange basis evaluations Li(y)_numerator = {i = [start..(start+num_polys))} & Li(y)_numerator {i = [(start+num_polys)..(start+(2*num_polys)))} using montgomery lagrange basis inverses sent with proof.\n             * For Li(y)_numerator{i = [start..(start+num_polys))}:\n             * Li(y)_numerator = w_i * (y^{2*num_polys} + (h^{num_polys} * h_s^{num_polys}) - (y^{num_polys} * (h^{num_polys} + h_s^{num_polys})))\n             * Li(y)_denominator = (num_polys * (h^{(2*num_polys)-1}-(h^{num_polys-1} * h_s^{num_polys})) * (y-(h*w_i)))\n             * Li(y) = Li(y)_numerator / Li(y)_denominator =  (w_i * (y^{2*num_polys} + (h^{num_polys} * h_s^{num_polys}) - (y^{num_polys} * (h^{num_polys} + h_s^{num_polys})))) / (num_polys * (h^{(2*num_polys)-1}-(h^{num_polys-1} * h_s^{num_polys})) * (y-(h*w_i)))\n             *\n             * For Li(y)_numerator{i = [(start+num_polys)..(start+(2*num_polys)))}\n             * Li(y)_numerator = w_i * (y^{2*num_polys} + (h^{num_polys} * h_s^{num_polys}) - (y^{num_polys} * (h^{num_polys} + h_s^{num_polys})))\n             * Li(y)_denominator = (num_polys * (h_s^{(2*num_polys)-1}-(h_s^{num_polys-1} * h^{num_polys})) * (y-(h_s*w_i)))\n             * Li(y) = Li(y)_numerator / Li(y)_denominator =  (w_i * (y^{2*num_polys} + (h^{num_polys} * h_s^{num_polys}) - (y^{num_polys} * (h^{num_polys} + h_s^{num_polys}))) ) / (num_polys * (h_s^{(2*num_polys)-1}-(h_s^{num_polys-1} * h^{num_polys})) * (y-(h_s*w_i)))\n             *\n             * Also calculates the products of the denominators of the lagrange basis evaluations:\n             * Li(y)_denominators_product = Li(y)_previous_denominators_product * (∏(Li(y)_denominator {i = [start..(start+num_polys))})) * (∏(Li(y)_denominator {i = [(start+num_polys)..(start+(2*num_polys)))}))\n             */\n\n            function precompute_partial_lagrange_basis_evaluations_for_union_set(\n                start,\n                num_polys,\n                y,\n                omega,\n                h,\n                h_shifted,\n                interim_product\n            ) -> final_product {\n                if gt(add(start, mul(2, num_polys)), TOTAL_LAGRANGE_BASIS_INVERSES_LENGTH) {\n                    revertWithMessage(32, \"Precompute Eval. Error [PLBEIU1]\")\n                }\n\n                let constant_parts_0, constant_parts_1, t_0, t_1 := temp_name_inner_function(num_polys, h, h_shifted)\n\n                let current_omega := 1\n                for {\n                    let i := 0\n                } lt(i, num_polys) {\n                    i := add(i, 1)\n                } {\n                    t_0 := mulmod(current_omega, h, R_MOD)\n                    t_0 := sub(R_MOD, t_0)\n                    t_0 := addmod(t_0, y, R_MOD)\n                    // (num_polys * (h^{(2*num_polys)-1}-(h^{num_polys-1} * h_s^{num_polys})) * (y-(h*w_i)))\n                    t_0 := mulmod(t_0, constant_parts_0, R_MOD)\n\n                    mstore(add(MEM_LAGRANGE_BASIS_DENOMS, mul(add(start, i), 0x20)), t_0)\n\n                    interim_product := mulmod(interim_product, t_0, R_MOD)\n\n                    mstore(add(MEM_LAGRANGE_BASIS_DENOM_PRODUCTS, mul(add(start, i), 0x20)), interim_product)\n                    // w_i * (y^{2*num_polys} + (h^{num_polys} * h_s^{num_polys}) - (y^{num_polys} * (h^{num_polys} + h_s^{num_polys})))\n                    mstore(\n                        add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(start, i), 0x20)),\n                        mulmod(t_1, current_omega, R_MOD)\n                    )\n                    // w_i {i = i+1}\n                    current_omega := mulmod(current_omega, omega, R_MOD)\n                }\n\n                current_omega := 1\n                for {\n                    let i := 0\n                } lt(i, num_polys) {\n                    i := add(i, 1)\n                } {\n                    t_0 := mulmod(current_omega, h_shifted, R_MOD)\n                    t_0 := sub(R_MOD, t_0)\n                    t_0 := addmod(t_0, y, R_MOD)\n                    // (num_polys * (h_s^{(2*num_polys)-1}-(h_s^{num_polys-1} * h^{num_polys})) * (y-(h_s*w_i)))\n                    t_0 := mulmod(t_0, constant_parts_1, R_MOD)\n\n                    mstore(add(MEM_LAGRANGE_BASIS_DENOMS, mul(add(add(start, num_polys), i), 0x20)), t_0)\n\n                    interim_product := mulmod(interim_product, t_0, R_MOD)\n\n                    mstore(\n                        add(MEM_LAGRANGE_BASIS_DENOM_PRODUCTS, mul(add(add(start, num_polys), i), 0x20)),\n                        interim_product\n                    )\n                    // w_i * (y^{2*num_polys} + (h^{num_polys} * h_s^{num_polys}) - (y^{num_polys} * (h^{num_polys} + h_s^{num_polys})))\n                    mstore(\n                        add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(add(start, num_polys), i), 0x20)),\n                        mulmod(t_1, current_omega, R_MOD)\n                    )\n                    // w_i {i = i+1}\n                    current_omega := mulmod(current_omega, omega, R_MOD)\n                }\n\n                final_product := interim_product\n            }\n\n            /**\n             * @dev Computes lagrange basis evaluations using montgomery lagrange basis inverses sent with proof.\n             * @notice Check individual functions for more details\n             */\n            function precompute_all_lagrange_basis_evaluations_from_inverses() {\n                let y := mload(PVS_Y)\n                // w8 = 0x2b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e80\n                // w4 = 0x30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f703636\n                // w3 = 0x0000000000000000b3c4d79d41a917585bfc41088d8daaa78b17ea66b99c90dd\n                let product_0_7 := precompute_partial_lagrange_basis_evaluations(\n                    0,\n                    8,\n                    y,\n                    0x2b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e80,\n                    mload(add(OPS_OPENING_POINTS, mul(0, 0x20))),\n                    1\n                )\n                let product_0_11 := precompute_partial_lagrange_basis_evaluations(\n                    8,\n                    4,\n                    y,\n                    0x30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f703636,\n                    mload(add(OPS_OPENING_POINTS, mul(1, 0x20))),\n                    product_0_7\n                )\n                let product_0_17 := precompute_partial_lagrange_basis_evaluations_for_union_set(\n                    add(8, 4),\n                    3,\n                    y,\n                    0x0000000000000000b3c4d79d41a917585bfc41088d8daaa78b17ea66b99c90dd,\n                    mload(add(OPS_OPENING_POINTS, mul(2, 0x20))),\n                    mload(add(OPS_OPENING_POINTS, mul(3, 0x20))),\n                    product_0_11\n                )\n\n                let montgomery_inverse := mload(MEM_PROOF_MONTGOMERY_LAGRANGE_BASIS_INVERSE)\n\n                if iszero(eq(mulmod(product_0_17, montgomery_inverse, R_MOD), 1)) {\n                    revertWithMessage(30, \"Precompute Eval. Error [PALBE]\")\n                }\n                let temp := montgomery_inverse\n                let loop_length := sub(TOTAL_LAGRANGE_BASIS_INVERSES_LENGTH, 1)\n                for {\n                    let i := loop_length\n                } gt(i, 0) {\n                    i := sub(i, 1)\n                } {\n                    mstore(\n                        add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(i, 0x20)),\n                        mulmod(\n                            mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(i, 0x20))),\n                            mulmod(mload(add(MEM_LAGRANGE_BASIS_DENOM_PRODUCTS, mul(sub(i, 1), 0x20))), temp, R_MOD),\n                            R_MOD\n                        )\n                    )\n                    temp := mulmod(temp, mload(add(MEM_LAGRANGE_BASIS_DENOMS, mul(i, 0x20))), R_MOD)\n                }\n                mstore(\n                    add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(0, 0x20)),\n                    mulmod(mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(0, 0x20))), temp, R_MOD)\n                )\n            }\n\n            /**\n             * @dev Computes opening points h0, h1, h2, h3\n             */\n            function compute_opening_points() {\n                // h = r^{power/num_polys}\n                let pvs_r := mload(PVS_R)\n                let r_2 := mulmod(pvs_r, pvs_r, R_MOD)\n                let r_3 := mulmod(r_2, pvs_r, R_MOD)\n                let r_6 := mulmod(r_3, r_3, R_MOD)\n                let r_8 := mulmod(r_6, r_2, R_MOD)\n                // h0 = pvs_r^3\n                mstore(add(OPS_OPENING_POINTS, mul(0, 0x20)), r_3)\n                // h1 = pvs_r^6\n                mstore(add(OPS_OPENING_POINTS, mul(1, 0x20)), r_6)\n                // h2 = pvs_r^8\n                mstore(add(OPS_OPENING_POINTS, mul(2, 0x20)), r_8)\n\n                // h3 (only round 2 needs opening at shifted point)\n                mstore(\n                    add(OPS_OPENING_POINTS, mul(3, 0x20)),\n                    mulmod(r_8, 0x0925f0bd364638ec3084b45fc27895f8f3f6f079096600fe946c8e9db9a47124, R_MOD)\n                )\n            }\n\n            /**\n             * @dev Initializes opening state OPS_Y_POWS[i] = y^i\n             * @notice only 9 powers are computed since the rest stay unused.\n             */\n            function initialize_opening_state() {\n                compute_opening_points()\n                let acc := 1\n                for {\n                    let i := 0\n                } lt(i, 9) {\n                    i := add(i, 1)\n                } {\n                    mstore(add(OPS_Y_POWS, mul(i, 0x20)), acc)\n                    acc := mulmod(acc, mload(PVS_Y), R_MOD)\n                }\n                precompute_all_lagrange_basis_evaluations_from_inverses()\n            }\n\n            /**\n             * @dev Computes r polynomial evaluations utilizing horner method\n             * (r*w)^{i}:{1, w*r, (w*r)^2, .. , (w*r)^{k-1}}\n             * horner: c0 + c1*(rw) + c2*(rw)^2 + c3*(rw)^3  -> (c0 + (rw)*(c1 + (rw)*(c2 + c3*(rw))))\n             */\n            function evaluate_r_polys_at_point_unrolled(\n                main_gate_quotient_at_z,\n                copy_perm_first_quotient_at_z,\n                copy_perm_second_quotient_at_z\n            ) {\n                let omega_h\n                let c\n\n                // setup round\n                // r\n\n                // w8^1 = 0x2b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e80\n                // w8^2 = 0x30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f703636\n                // w8^3 = 0x1d59376149b959ccbd157ac850893a6f07c2d99b3852513ab8d01be8e846a566\n                // w8^4 = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000\n                // w8^5 = 0x0530d09118705106cbb4a786ead16926d5d174e181a26686af5448492e42a181\n                // w8^6 = 0x0000000000000000b3c4d79d41a91758cb49c3517c4604a520cff123608fc9cb\n                // w8^7 = 0x130b17119778465cfb3acaee30f81dee20710ead41671f568b11d9ab07b95a9b\n                omega_h := mload(add(OPS_OPENING_POINTS, mul(0, 0x20)))\n                c := mulmod(mload(add(MEM_PROOF_EVALUATIONS, mul(7, 0x20))), omega_h, R_MOD)\n                for {\n                    let i := 1\n                } lt(i, 7) {\n                    i := add(i, 1)\n                } {\n                    c := mulmod(\n                        addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(7, i), 0x20))), R_MOD),\n                        omega_h,\n                        R_MOD\n                    )\n                }\n                c := addmod(c, mload(MEM_PROOF_EVALUATIONS), R_MOD)\n                mstore(\n                    PS_R_EVALS,\n                    addmod(\n                        mload(PS_R_EVALS),\n                        mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(0, 0x20))), R_MOD),\n                        R_MOD\n                    )\n                )\n                omega_h := mulmod(\n                    0x2b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e80,\n                    mload(add(OPS_OPENING_POINTS, mul(0, 0x20))),\n                    R_MOD\n                )\n\n                c := mulmod(mload(add(MEM_PROOF_EVALUATIONS, mul(7, 0x20))), omega_h, R_MOD)\n                for {\n                    let i := 1\n                } lt(i, 7) {\n                    i := add(i, 1)\n                } {\n                    c := mulmod(\n                        addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(7, i), 0x20))), R_MOD),\n                        omega_h,\n                        R_MOD\n                    )\n                }\n                c := addmod(c, mload(MEM_PROOF_EVALUATIONS), R_MOD)\n                mstore(\n                    PS_R_EVALS,\n                    addmod(\n                        mload(PS_R_EVALS),\n                        mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(1, 0x20))), R_MOD),\n                        R_MOD\n                    )\n                )\n                omega_h := mulmod(\n                    0x30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f703636,\n                    mload(add(OPS_OPENING_POINTS, mul(0, 0x20))),\n                    R_MOD\n                )\n\n                c := mulmod(mload(add(MEM_PROOF_EVALUATIONS, mul(7, 0x20))), omega_h, R_MOD)\n                for {\n                    let i := 1\n                } lt(i, 7) {\n                    i := add(i, 1)\n                } {\n                    c := mulmod(\n                        addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(7, i), 0x20))), R_MOD),\n                        omega_h,\n                        R_MOD\n                    )\n                }\n                c := addmod(c, mload(MEM_PROOF_EVALUATIONS), R_MOD)\n                mstore(\n                    PS_R_EVALS,\n                    addmod(\n                        mload(PS_R_EVALS),\n                        mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(2, 0x20))), R_MOD),\n                        R_MOD\n                    )\n                )\n                omega_h := mulmod(\n                    0x1d59376149b959ccbd157ac850893a6f07c2d99b3852513ab8d01be8e846a566,\n                    mload(add(OPS_OPENING_POINTS, mul(0, 0x20))),\n                    R_MOD\n                )\n\n                c := mulmod(mload(add(MEM_PROOF_EVALUATIONS, mul(7, 0x20))), omega_h, R_MOD)\n                for {\n                    let i := 1\n                } lt(i, 7) {\n                    i := add(i, 1)\n                } {\n                    c := mulmod(\n                        addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(7, i), 0x20))), R_MOD),\n                        omega_h,\n                        R_MOD\n                    )\n                }\n                c := addmod(c, mload(MEM_PROOF_EVALUATIONS), R_MOD)\n                mstore(\n                    PS_R_EVALS,\n                    addmod(\n                        mload(PS_R_EVALS),\n                        mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(3, 0x20))), R_MOD),\n                        R_MOD\n                    )\n                )\n                omega_h := mulmod(\n                    0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000,\n                    mload(add(OPS_OPENING_POINTS, mul(0, 0x20))),\n                    R_MOD\n                )\n\n                c := mulmod(mload(add(MEM_PROOF_EVALUATIONS, mul(7, 0x20))), omega_h, R_MOD)\n                for {\n                    let i := 1\n                } lt(i, 7) {\n                    i := add(i, 1)\n                } {\n                    c := mulmod(\n                        addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(7, i), 0x20))), R_MOD),\n                        omega_h,\n                        R_MOD\n                    )\n                }\n                c := addmod(c, mload(MEM_PROOF_EVALUATIONS), R_MOD)\n                mstore(\n                    PS_R_EVALS,\n                    addmod(\n                        mload(PS_R_EVALS),\n                        mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(4, 0x20))), R_MOD),\n                        R_MOD\n                    )\n                )\n                omega_h := mulmod(\n                    0x0530d09118705106cbb4a786ead16926d5d174e181a26686af5448492e42a181,\n                    mload(add(OPS_OPENING_POINTS, mul(0, 0x20))),\n                    R_MOD\n                )\n\n                c := mulmod(mload(add(MEM_PROOF_EVALUATIONS, mul(7, 0x20))), omega_h, R_MOD)\n                for {\n                    let i := 1\n                } lt(i, 7) {\n                    i := add(i, 1)\n                } {\n                    c := mulmod(\n                        addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(7, i), 0x20))), R_MOD),\n                        omega_h,\n                        R_MOD\n                    )\n                }\n                c := addmod(c, mload(MEM_PROOF_EVALUATIONS), R_MOD)\n                mstore(\n                    PS_R_EVALS,\n                    addmod(\n                        mload(PS_R_EVALS),\n                        mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(5, 0x20))), R_MOD),\n                        R_MOD\n                    )\n                )\n                omega_h := mulmod(\n                    0x0000000000000000b3c4d79d41a91758cb49c3517c4604a520cff123608fc9cb,\n                    mload(add(OPS_OPENING_POINTS, mul(0, 0x20))),\n                    R_MOD\n                )\n\n                c := mulmod(mload(add(MEM_PROOF_EVALUATIONS, mul(7, 0x20))), omega_h, R_MOD)\n                for {\n                    let i := 1\n                } lt(i, 7) {\n                    i := add(i, 1)\n                } {\n                    c := mulmod(\n                        addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(7, i), 0x20))), R_MOD),\n                        omega_h,\n                        R_MOD\n                    )\n                }\n                c := addmod(c, mload(MEM_PROOF_EVALUATIONS), R_MOD)\n                mstore(\n                    PS_R_EVALS,\n                    addmod(\n                        mload(PS_R_EVALS),\n                        mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(6, 0x20))), R_MOD),\n                        R_MOD\n                    )\n                )\n                omega_h := mulmod(\n                    0x130b17119778465cfb3acaee30f81dee20710ead41671f568b11d9ab07b95a9b,\n                    mload(add(OPS_OPENING_POINTS, mul(0, 0x20))),\n                    R_MOD\n                )\n\n                c := mulmod(mload(add(MEM_PROOF_EVALUATIONS, mul(7, 0x20))), omega_h, R_MOD)\n                for {\n                    let i := 1\n                } lt(i, 7) {\n                    i := add(i, 1)\n                } {\n                    c := mulmod(\n                        addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(7, i), 0x20))), R_MOD),\n                        omega_h,\n                        R_MOD\n                    )\n                }\n                c := addmod(c, mload(MEM_PROOF_EVALUATIONS), R_MOD)\n                mstore(\n                    PS_R_EVALS,\n                    addmod(\n                        mload(PS_R_EVALS),\n                        mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(7, 0x20))), R_MOD),\n                        R_MOD\n                    )\n                )\n\n                // first round\n                // r\n\n                // w4^1 = 0x30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f703636\n                // w4^2 = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000\n                // w4^3 = 0x0000000000000000b3c4d79d41a91758cb49c3517c4604a520cff123608fc9cb\n                omega_h := mload(add(OPS_OPENING_POINTS, mul(1, 0x20)))\n\n                c := mulmod(main_gate_quotient_at_z, omega_h, R_MOD)\n                for {\n                    let i := 1\n                } lt(i, 3) {\n                    i := add(i, 1)\n                } {\n                    c := mulmod(\n                        addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(sub(add(8, 4), i), 1), 0x20))), R_MOD),\n                        omega_h,\n                        R_MOD\n                    )\n                }\n                c := addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(sub(add(8, 4), 3), 1), 0x20))), R_MOD)\n\n                mstore(\n                    add(PS_R_EVALS, mul(1, 0x20)),\n                    addmod(\n                        mload(add(PS_R_EVALS, mul(1, 0x20))),\n                        mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(8, 0), 0x20))), R_MOD),\n                        R_MOD\n                    )\n                )\n                omega_h := mulmod(\n                    0x30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f703636,\n                    mload(add(OPS_OPENING_POINTS, mul(1, 0x20))),\n                    R_MOD\n                )\n\n                c := mulmod(main_gate_quotient_at_z, omega_h, R_MOD)\n                for {\n                    let i := 1\n                } lt(i, 3) {\n                    i := add(i, 1)\n                } {\n                    c := mulmod(\n                        addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(sub(add(8, 4), i), 1), 0x20))), R_MOD),\n                        omega_h,\n                        R_MOD\n                    )\n                }\n                c := addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(sub(add(8, 4), 3), 1), 0x20))), R_MOD)\n                mstore(\n                    add(PS_R_EVALS, mul(1, 0x20)),\n                    addmod(\n                        mload(add(PS_R_EVALS, mul(1, 0x20))),\n                        mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(8, 1), 0x20))), R_MOD),\n                        R_MOD\n                    )\n                )\n                omega_h := mulmod(\n                    0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000,\n                    mload(add(OPS_OPENING_POINTS, mul(1, 0x20))),\n                    R_MOD\n                )\n\n                c := mulmod(main_gate_quotient_at_z, omega_h, R_MOD)\n                for {\n                    let i := 1\n                } lt(i, 3) {\n                    i := add(i, 1)\n                } {\n                    c := mulmod(\n                        addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(sub(add(8, 4), i), 1), 0x20))), R_MOD),\n                        omega_h,\n                        R_MOD\n                    )\n                }\n                c := addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(sub(add(8, 4), 3), 1), 0x20))), R_MOD)\n                mstore(\n                    add(PS_R_EVALS, mul(1, 0x20)),\n                    addmod(\n                        mload(add(PS_R_EVALS, mul(1, 0x20))),\n                        mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(8, 2), 0x20))), R_MOD),\n                        R_MOD\n                    )\n                )\n                omega_h := mulmod(\n                    0x0000000000000000b3c4d79d41a91758cb49c3517c4604a520cff123608fc9cb,\n                    mload(add(OPS_OPENING_POINTS, mul(1, 0x20))),\n                    R_MOD\n                )\n\n                c := mulmod(main_gate_quotient_at_z, omega_h, R_MOD)\n                for {\n                    let i := 1\n                } lt(i, 3) {\n                    i := add(i, 1)\n                } {\n                    c := mulmod(\n                        addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(sub(add(8, 4), i), 1), 0x20))), R_MOD),\n                        omega_h,\n                        R_MOD\n                    )\n                }\n                c := addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(sub(add(8, 4), 3), 1), 0x20))), R_MOD)\n                mstore(\n                    add(PS_R_EVALS, mul(1, 0x20)),\n                    addmod(\n                        mload(add(PS_R_EVALS, mul(1, 0x20))),\n                        mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(8, 3), 0x20))), R_MOD),\n                        R_MOD\n                    )\n                )\n\n                // second round\n                // c2\n                // r\n                omega_h := mload(add(OPS_OPENING_POINTS, mul(2, 0x20)))\n                let omega_h_shifted := mload(add(OPS_OPENING_POINTS, mul(3, 0x20)))\n                c := mulmod(copy_perm_second_quotient_at_z, omega_h, R_MOD)\n                c := mulmod(addmod(c, copy_perm_first_quotient_at_z, R_MOD), omega_h, R_MOD)\n                c := addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(11, 0x20))), R_MOD)\n                mstore(\n                    add(PS_R_EVALS, mul(2, 0x20)),\n                    addmod(\n                        mload(add(PS_R_EVALS, mul(2, 0x20))),\n                        mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(add(8, 4), 0), 0x20))), R_MOD),\n                        R_MOD\n                    )\n                )\n                // c2 shifted\n                c := mulmod(mload(add(MEM_PROOF_EVALUATIONS, mul(add(12, 2), 0x20))), omega_h_shifted, R_MOD)\n                c := mulmod(\n                    addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(add(12, 1), 0x20))), R_MOD),\n                    omega_h_shifted,\n                    R_MOD\n                )\n                c := addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(12, 0x20))), R_MOD)\n                mstore(\n                    add(PS_R_EVALS, mul(2, 0x20)),\n                    addmod(\n                        mload(add(PS_R_EVALS, mul(2, 0x20))),\n                        mulmod(\n                            c,\n                            mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(add(add(8, 4), 3), 0), 0x20))),\n                            R_MOD\n                        ),\n                        R_MOD\n                    )\n                )\n                // c2\n                omega_h := mulmod(\n                    0x0000000000000000b3c4d79d41a917585bfc41088d8daaa78b17ea66b99c90dd,\n                    mload(add(OPS_OPENING_POINTS, mul(2, 0x20))),\n                    R_MOD\n                )\n                omega_h_shifted := mulmod(\n                    0x0000000000000000b3c4d79d41a917585bfc41088d8daaa78b17ea66b99c90dd,\n                    mload(add(OPS_OPENING_POINTS, mul(3, 0x20))),\n                    R_MOD\n                )\n\n                c := mulmod(copy_perm_second_quotient_at_z, omega_h, R_MOD)\n                c := mulmod(addmod(c, copy_perm_first_quotient_at_z, R_MOD), omega_h, R_MOD)\n                c := addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(11, 0x20))), R_MOD)\n                mstore(\n                    add(PS_R_EVALS, mul(2, 0x20)),\n                    addmod(\n                        mload(add(PS_R_EVALS, mul(2, 0x20))),\n                        mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(add(8, 4), 1), 0x20))), R_MOD),\n                        R_MOD\n                    )\n                )\n                // c2 shifted\n                c := mulmod(mload(add(MEM_PROOF_EVALUATIONS, mul(add(12, 2), 0x20))), omega_h_shifted, R_MOD)\n                c := mulmod(\n                    addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(add(12, 1), 0x20))), R_MOD),\n                    omega_h_shifted,\n                    R_MOD\n                )\n                c := addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(12, 0x20))), R_MOD)\n                mstore(\n                    add(PS_R_EVALS, mul(2, 0x20)),\n                    addmod(\n                        mload(add(PS_R_EVALS, mul(2, 0x20))),\n                        mulmod(\n                            c,\n                            mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(add(add(8, 4), 3), 1), 0x20))),\n                            R_MOD\n                        ),\n                        R_MOD\n                    )\n                )\n                // c2\n                omega_h := mulmod(\n                    0x30644e72e131a029048b6e193fd84104cc37a73fec2bc5e9b8ca0b2d36636f23,\n                    mload(add(OPS_OPENING_POINTS, mul(2, 0x20))),\n                    R_MOD\n                )\n                omega_h_shifted := mulmod(\n                    0x30644e72e131a029048b6e193fd84104cc37a73fec2bc5e9b8ca0b2d36636f23,\n                    mload(add(OPS_OPENING_POINTS, mul(3, 0x20))),\n                    R_MOD\n                )\n\n                c := mulmod(copy_perm_second_quotient_at_z, omega_h, R_MOD)\n                c := mulmod(addmod(c, copy_perm_first_quotient_at_z, R_MOD), omega_h, R_MOD)\n                c := addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(11, 0x20))), R_MOD)\n                mstore(\n                    add(PS_R_EVALS, mul(2, 0x20)),\n                    addmod(\n                        mload(add(PS_R_EVALS, mul(2, 0x20))),\n                        mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(add(8, 4), 2), 0x20))), R_MOD),\n                        R_MOD\n                    )\n                )\n                // c2 shifted\n                c := mulmod(mload(add(MEM_PROOF_EVALUATIONS, mul(add(12, 2), 0x20))), omega_h_shifted, R_MOD)\n                c := mulmod(\n                    addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(add(12, 1), 0x20))), R_MOD),\n                    omega_h_shifted,\n                    R_MOD\n                )\n                c := addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(12, 0x20))), R_MOD)\n\n                mstore(\n                    add(PS_R_EVALS, mul(2, 0x20)),\n                    addmod(\n                        mload(add(PS_R_EVALS, mul(2, 0x20))),\n                        mulmod(\n                            c,\n                            mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(add(add(8, 4), 3), 2), 0x20))),\n                            R_MOD\n                        ),\n                        R_MOD\n                    )\n                )\n            }\n\n            /**\n             * @dev Computes the openings and returns the result of pairing computation\n             */\n            function check_openings() -> out {\n                //  f(X) = (Z_{T\\S0}(y) * (C0(X) - r0(y))) + (alpha*(Z_{T\\S1}(y)*(C1(X) - r1(y)))) + (alpha^{2}*(Z_{T\\S2}(y)*(C2(X) - r2(y))))\n                // Note that, in our case set differences(Z_T\\{S_i}) are:\n                // - Z_{T\\S0}(y): (y^{k1}-ζ)*(y^{k2}-ζ)*(y^{k2}-(ζ*w))\n                // - Z_{T\\S1}(y): (y^{k0}-ζ)*(y^{k2}-ζ)*(y^{k2}-(ζ*w))\n                // - Z_{T\\S2}(y): (y^{k0}-ζ)*(y^{k1}-ζ) where\n                // k0=8, k1=4, and k2=3 are number of the polynomials for setup, first and second round respectively\n\n                let tmp\n                evaluate_r_polys_at_point_unrolled(\n                    mload(MAIN_GATE_QUOTIENT_AT_Z),\n                    mload(COPY_PERM_FIRST_QUOTIENT_AT_Z),\n                    mload(COPY_PERM_SECOND_QUOTIENT_AT_Z)\n                )\n\n                // -ζ\n                mstore(add(PS_MINUS_Z, mul(0, 0x20)), sub(R_MOD, mload(PVS_Z)))\n                // -(ζ*w)\n                mstore(add(PS_MINUS_Z, mul(1, 0x20)), sub(R_MOD, mload(PVS_Z_OMEGA)))\n\n                // Z_{T\\S0}(y)\n                mstore(\n                    add(PS_SET_DIFFERENCES_AT_Y, mul(0, 0x20)),\n                    addmod(mload(add(OPS_Y_POWS, mul(3, 0x20))), mload(add(PS_MINUS_Z, mul(1, 0x20))), R_MOD)\n                )\n                tmp := addmod(mload(add(OPS_Y_POWS, mul(3, 0x20))), mload(add(PS_MINUS_Z, mul(0, 0x20))), R_MOD)\n                mstore(\n                    add(PS_SET_DIFFERENCES_AT_Y, mul(0, 0x20)),\n                    mulmod(mload(add(PS_SET_DIFFERENCES_AT_Y, mul(0, 0x20))), tmp, R_MOD)\n                )\n                tmp := addmod(mload(add(OPS_Y_POWS, mul(4, 0x20))), mload(add(PS_MINUS_Z, mul(0, 0x20))), R_MOD)\n                mstore(\n                    add(PS_SET_DIFFERENCES_AT_Y, mul(0, 0x20)),\n                    mulmod(mload(add(PS_SET_DIFFERENCES_AT_Y, mul(0, 0x20))), tmp, R_MOD)\n                )\n                mstore(PS_VANISHING_AT_Y, mload(add(PS_SET_DIFFERENCES_AT_Y, mul(0, 0x20))))\n                mstore(PS_INV_ZTS0_AT_Y, modexp(mload(add(PS_SET_DIFFERENCES_AT_Y, mul(0, 0x20))), sub(R_MOD, 2)))\n\n                // Z_{T\\S1}(y)\n                mstore(\n                    add(PS_SET_DIFFERENCES_AT_Y, mul(1, 0x20)),\n                    addmod(mload(add(OPS_Y_POWS, mul(3, 0x20))), mload(add(PS_MINUS_Z, mul(1, 0x20))), R_MOD)\n                )\n                tmp := addmod(mload(add(OPS_Y_POWS, mul(3, 0x20))), mload(add(PS_MINUS_Z, mul(0, 0x20))), R_MOD)\n                mstore(\n                    add(PS_SET_DIFFERENCES_AT_Y, mul(1, 0x20)),\n                    mulmod(mload(add(PS_SET_DIFFERENCES_AT_Y, mul(1, 0x20))), tmp, R_MOD)\n                )\n                tmp := addmod(mload(add(OPS_Y_POWS, mul(8, 0x20))), mload(add(PS_MINUS_Z, mul(0, 0x20))), R_MOD)\n                mstore(\n                    add(PS_SET_DIFFERENCES_AT_Y, mul(1, 0x20)),\n                    mulmod(mload(add(PS_SET_DIFFERENCES_AT_Y, mul(1, 0x20))), tmp, R_MOD)\n                )\n                mstore(PS_VANISHING_AT_Y, mulmod(mload(PS_VANISHING_AT_Y), tmp, R_MOD))\n\n                // // Z_{T\\S2}(y)\n                mstore(\n                    add(PS_SET_DIFFERENCES_AT_Y, mul(2, 0x20)),\n                    addmod(mload(add(OPS_Y_POWS, mul(4, 0x20))), mload(add(PS_MINUS_Z, mul(0, 0x20))), R_MOD)\n                )\n                tmp := addmod(mload(add(OPS_Y_POWS, mul(8, 0x20))), mload(add(PS_MINUS_Z, mul(0, 0x20))), R_MOD)\n                mstore(\n                    add(PS_SET_DIFFERENCES_AT_Y, mul(2, 0x20)),\n                    mulmod(mload(add(PS_SET_DIFFERENCES_AT_Y, mul(2, 0x20))), tmp, R_MOD)\n                )\n\n                // W(X) = f(X) / Z_T(y) where Z_T(y) = (y^{k0}-ζ)*(y^{k1}-ζ)*(y^{k2}-ζ)*(y^{k2}-(ζ*w))\n                // we need to check that\n                // f(X) - W(X) * Z_T(y) = 0\n\n                // W'(X) = L(X) / (Z_{T\\S0}(y)*(X-y))\n                // L(X)/Z_{T\\S0}(y) = (C0(X) - r0(y)) + (alpha*(Z_{T\\S1}(y)/Z_{T\\S0}(y))*(C1(X) - r1(y))) + (alpha^{2}*(Z_{T\\S2}(y)/Z_{T\\S0}(y))*(C2(X) - r2(y))) - ((Z_T(y)/Z_{T\\S0}(y))*W(X))\n\n                // the identity check is reduced into following\n                // L(X) - W'(X)*Z_{T\\S0}(y)(X-y) == 0\n                // verifier has commitments to the C_i(X) polynomials\n                // verifier also recomputed r_i(y)\n                // group constant and commitment parts\n                // first prepare L(X)/Z_{T\\S0}(y)\n                // C(X) = C0(X) + ((alpha*Z_{T\\S1}(y)/Z_{T\\S0}(y))*C1(X)) + ((alpha^2*Z_{T\\S2}(y)/Z_{T\\S0}(y))*C2(X))\n                // r(y) = r0(y) + ((alpha*Z_{T\\S1}(y)/Z_{T\\S0}(y))*r1(y)) + ((alpha^2*Z_{T\\S2}(y)/Z_{T\\S0}(y))*r2(y))\n                // now construct\n                // L(X)/Z_{T\\S0}(y) = C(X) - r(y) - ((Z_T(y)/Z_{T\\S0}(y))*W(X))\n                // now check following identity\n                // C(X) - r(y) - ((Z_t(y)/Z_{T\\S0}(y))*W(X)) - (W'(X)*(X-y)) = 0\n                // [C(X)] - [r(y)*G1] - (Z_T(y)/Z_{T\\S0}(y))*[W] - [(X-y)*W'] = 0\n                // [C(X)] - [r(y)*G1] - (Z_T(y)/Z_{T\\S0}(y))*[W] - [X*W'] + [y*W]' = 0\n                // [C(X)] - [r(y)*G1] - (Z_T(y)/Z_{T\\S0}(y))*[W] + [y*W'] - [X*W'] = 0\n                // points with X will be multiplied in the exponent via pairing\n                // so final pairing would ne\n                // e([C(X)] - [r(y)*G1] - [Z_T(y)/(Z_{T\\S0}(y)*W)] + [y*W'], G2)*e(-W', X*G2) = 1\n\n                // C0\n                let ps_aggregated_commitment_g1_x := VK_C0_G1_X\n                let ps_aggregated_commitment_g1_y := VK_C0_G1_Y\n\n                // ((alpha^{2}*Z_{T\\S2}(y))/Z_{T\\S0}(y))\n                let aggregated_r_at_y := mulmod(\n                    mload(add(PS_SET_DIFFERENCES_AT_Y, mul(2, 0x20))),\n                    mload(PS_INV_ZTS0_AT_Y),\n                    R_MOD\n                )\n                aggregated_r_at_y := mulmod(aggregated_r_at_y, mload(PVS_ALPHA_1), R_MOD)\n\n                // ((alpha^{2}*Z_{T\\S2}(y))/Z_{T\\S0}(y))*C2\n                let tp_g1_x, tp_g1_y := point_mul(\n                    mload(MEM_PROOF_COMMITMENT_1_G1_X),\n                    mload(MEM_PROOF_COMMITMENT_1_G1_Y),\n                    aggregated_r_at_y\n                )\n                // c0 + (((alpha^{2}*Z_{T\\S2}(y))/Z_{T\\S0}(y))*C2)\n                ps_aggregated_commitment_g1_x, ps_aggregated_commitment_g1_y := point_add(\n                    ps_aggregated_commitment_g1_x,\n                    ps_aggregated_commitment_g1_y,\n                    tp_g1_x,\n                    tp_g1_y\n                )\n                // ((alpha^{2}*Z_{T\\S2}(y))/Z_{T\\S0}(y))*r2\n                aggregated_r_at_y := mulmod(aggregated_r_at_y, mload(add(PS_R_EVALS, mul(2, 0x20))), R_MOD)\n\n                // (alpha*Z_{T\\S1}(y)/Z_{T\\S0}(y))\n                tmp := mulmod(mload(add(PS_SET_DIFFERENCES_AT_Y, mul(1, 0x20))), mload(PS_INV_ZTS0_AT_Y), R_MOD)\n                tmp := mulmod(tmp, mload(PVS_ALPHA_0), R_MOD)\n\n                // (alpha*Z_{T\\S1}(y)/Z_{T\\S0}(y))*C1\n                tp_g1_x, tp_g1_y := point_mul(\n                    mload(MEM_PROOF_COMMITMENT_0_G1_X),\n                    mload(MEM_PROOF_COMMITMENT_0_G1_Y),\n                    tmp\n                )\n                // c0 + ((alpha*Z_{T\\S1}(y)/Z_{T\\S0}(y))*C1) + (((alpha^{2}*Z_{T\\S2}(y))/Z_{T\\S0}(y))*C2)\n                ps_aggregated_commitment_g1_x, ps_aggregated_commitment_g1_y := point_add(\n                    ps_aggregated_commitment_g1_x,\n                    ps_aggregated_commitment_g1_y,\n                    tp_g1_x,\n                    tp_g1_y\n                )\n                // (alpha*Z_{T\\S1}(y)/Z_{T\\S0}(y))*r1\n                tmp := mulmod(tmp, mload(add(PS_R_EVALS, mul(1, 0x20))), R_MOD)\n                // ((alpha*Z_{T\\S1}(y)/Z_{T\\S0}(y))*r1) + ((alpha^{2}*Z_{T\\S2}(y)/Z_{T\\S0}(y))*r2)\n                aggregated_r_at_y := addmod(aggregated_r_at_y, tmp, R_MOD)\n                // r0 + (alpha*Z_{T\\S1}(y)/Z_{T\\S0}(y))*r1 + ((alpha^{2}*Z_{T\\S2}(y)/Z_{T\\S0}(y))*r2)\n                aggregated_r_at_y := addmod(aggregated_r_at_y, mload(PS_R_EVALS), R_MOD)\n                tp_g1_x, tp_g1_y := point_mul(1, 2, aggregated_r_at_y)\n                ps_aggregated_commitment_g1_x, ps_aggregated_commitment_g1_y := point_sub(\n                    ps_aggregated_commitment_g1_x,\n                    ps_aggregated_commitment_g1_y,\n                    tp_g1_x,\n                    tp_g1_y\n                )\n                // - ((Z_T(y)/Z_{T\\S0}(y))*W(X))\n                mstore(PS_VANISHING_AT_Y, mulmod(mload(PS_VANISHING_AT_Y), mload(PS_INV_ZTS0_AT_Y), R_MOD))\n                tp_g1_x, tp_g1_y := point_mul(\n                    mload(MEM_PROOF_COMMITMENT_2_G1_X),\n                    mload(MEM_PROOF_COMMITMENT_2_G1_Y),\n                    mload(PS_VANISHING_AT_Y)\n                )\n                ps_aggregated_commitment_g1_x, ps_aggregated_commitment_g1_y := point_sub(\n                    ps_aggregated_commitment_g1_x,\n                    ps_aggregated_commitment_g1_y,\n                    tp_g1_x,\n                    tp_g1_y\n                )\n                // L(X)/Z_{T\\S0}(y) is aggregated\n\n                // Now check W'(X) = L(X) / (Z_{T\\S0}(y)*(x-y))\n                // L(X)/Z_{T\\S0}(y) + (y*W'(X)) - (x*W'(X)) = 0\n                tp_g1_x, tp_g1_y := point_mul(\n                    mload(MEM_PROOF_COMMITMENT_3_G1_X),\n                    mload(MEM_PROOF_COMMITMENT_3_G1_Y),\n                    mload(PVS_Y)\n                )\n                ps_aggregated_commitment_g1_x, ps_aggregated_commitment_g1_y := point_add(\n                    ps_aggregated_commitment_g1_x,\n                    ps_aggregated_commitment_g1_y,\n                    tp_g1_x,\n                    tp_g1_y\n                )\n                let is_zero_commitment\n                if iszero(mload(MEM_PROOF_COMMITMENT_3_G1_Y)) {\n                    if gt(mload(MEM_PROOF_COMMITMENT_3_G1_X), 0) {\n                        revertWithMessage(21, \"non zero x value [CO]\")\n                    }\n                    is_zero_commitment := 1\n                }\n\n                out := pairing_check(ps_aggregated_commitment_g1_x, ps_aggregated_commitment_g1_y, is_zero_commitment)\n            }\n\n            /**\n             * @dev Generates the rolling hash using `val` and updates the transcript.\n             * The computation is done as follows:\n             * new_state_0 = keccak256(uint32(0) || old_state_0 || old_state_1 || value)\n             * new_state_1 = keccak256(uint32(1) || old_state_0 || old_state_1 || value)\n             *\n             * @notice The computation assumes that the memory slots 0x200 - 0x202 are clean and doesn't explicitly clean them\n             */\n            function update_transcript(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            /**\n             * @dev Generates a new challenge with (uint32(2) || state_0 || state_1 || uint32(challenge_counter))\n             * The challenge_counter is incremented after every challenge\n             */\n            function get_challenge(challenge_counter) -> challenge {\n                mstore8(TRANSCRIPT_DST_BYTE_SLOT, 0x02)\n                mstore(TRANSCRIPT_CHALLENGE_SLOT, shl(224, challenge_counter))\n                challenge := and(keccak256(TRANSCRIPT_BEGIN_SLOT, 0x48), FR_MASK)\n            }\n\n            /**\n             * @dev Performs scalar multiplication: point * scalar -> t\n             * @notice Stores values starting from the initial free memory pointer i.e., 0x80.\n             * The free memory pointer is not updated as it stays unused throughout the code execution.\n             */\n            function point_mul(p_x, p_y, s) -> t_x, t_y {\n                mstore(0x80, p_x)\n                mstore(0xa0, p_y)\n                mstore(0xc0, s)\n\n                let success := staticcall(gas(), 7, 0x80, 0x60, 0x80, 0x40)\n                if iszero(success) {\n                    revertWithMessage(27, \"point multiplication failed\")\n                }\n                t_x := mload(0x80)\n                t_y := mload(add(0x80, 0x20))\n            }\n\n            /**\n             * @dev Performs point addition: point 1 + point 2 -> t\n             * @notice Stores values starting from the initial free memory pointer i.e., 0x80.\n             * The free memory pointer is not updated as it stays unused throughout the code execution.\n             */\n            function point_add(p1_x, p1_y, p2_x, p2_y) -> t_x, t_y {\n                mstore(0x80, p1_x)\n                mstore(0xa0, p1_y)\n                mstore(0xc0, p2_x)\n                mstore(0xe0, p2_y)\n\n                let success := staticcall(gas(), 6, 0x80, 0x80, 0x80, 0x40)\n                if iszero(success) {\n                    revertWithMessage(21, \"point addition failed\")\n                }\n\n                t_x := mload(0x80)\n                t_y := mload(add(0x80, 0x20))\n            }\n\n            /**\n             * @dev Performs point subtraction: point 1 + point 2 -> t\n             * @notice Stores values starting from the initial free memory pointer i.e., 0x80.\n             * The free memory pointer is not updated as it stays unused throughout the code execution.\n             * @notice We don't consider the highly unlikely case where p2 can be a point-at-infinity and the function would revert.\n             */\n            function point_sub(p1_x, p1_y, p2_x, p2_y) -> t_x, t_y {\n                mstore(0x80, p1_x)\n                mstore(0xa0, p1_y)\n                mstore(0xc0, p2_x)\n                mstore(0xe0, sub(Q_MOD, p2_y))\n\n                let success := staticcall(gas(), 6, 0x80, 0x80, 0x80, 0x40)\n                if iszero(success) {\n                    revertWithMessage(24, \"point subtraction failed\")\n                }\n\n                t_x := mload(0x80)\n                t_y := mload(add(0x80, 0x20))\n            }\n\n            /**\n             * @dev Calculates EC Pairing result following the EIP-197: https://eips.ethereum.org/EIPS/eip-197\n             * Performs point negation before pairing calculation, if the flag `is_zero_commitment` is true\n             *\n             * @notice Stores values starting from the initial free memory pointer i.e., 0x80.\n             * The free memory pointer is not updated as it stays unused throughout the code execution.\n             * While code reformatting consider not to overwrite the first constant-defined memory location, which is currently\n             * TRANSCRIPT_BEGIN_SLOT = 0x200\n             */\n            function pairing_check(p1_x, p1_y, is_zero_commitment) -> res {\n                mstore(0x80, p1_x)\n                mstore(0xa0, p1_y)\n                mstore(0xc0, VK_G2_ELEMENT_0_X1)\n                mstore(0xe0, VK_G2_ELEMENT_0_X2)\n                mstore(0x100, VK_G2_ELEMENT_0_Y1)\n                mstore(0x120, VK_G2_ELEMENT_0_Y2)\n                mstore(0x140, mload(MEM_PROOF_COMMITMENT_3_G1_X))\n                mstore(0x160, mload(MEM_PROOF_COMMITMENT_3_G1_Y))\n                if iszero(is_zero_commitment) {\n                    mstore(0x160, sub(Q_MOD, mload(MEM_PROOF_COMMITMENT_3_G1_Y)))\n                }\n                mstore(0x180, VK_G2_ELEMENT_1_X1)\n                mstore(0x1a0, VK_G2_ELEMENT_1_X2)\n                mstore(0x1c0, VK_G2_ELEMENT_1_Y1)\n                mstore(0x1e0, VK_G2_ELEMENT_1_Y2)\n\n                let success := staticcall(gas(), 8, 0x80, mul(12, 0x20), 0x80, 0x20)\n\n                if iszero(success) {\n                    revertWithMessage(20, \"pairing check failed\")\n                }\n                res := mload(0x80)\n            }\n\n            /**\n             * @dev Reverts with the desired custom error string.\n             * @notice Stores values starting from the initial free memory pointer i.e., 0x80.\n             * The free memory pointer is not updated as it stays unused throughout the code execution.\n             */\n            function revertWithMessage(len, reason) {\n                // \"Error(string)\" signature: bytes32(bytes4(keccak256(\"Error(string)\")))\n                mstore(0x80, 0x08c379a000000000000000000000000000000000000000000000000000000000)\n                // Data offset\n                mstore(0x84, 0x0000000000000000000000000000000000000000000000000000000000000020)\n                // Length of revert string\n                mstore(0xa4, len)\n                // Revert reason\n                mstore(0xc4, reason)\n                // Revert\n                revert(0x80, 0x64)\n            }\n\n            /**\n             * @dev Performs modular exponentiation using the formula (value ^ power) mod R_MOD.\n             * @notice Stores values starting from the 0x00 memory slot.\n             * The free memory pointer is not updated as it stays unused throughout the code execution.\n             */\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    }\n}\n","deployed_bytecode":"0x608060405234801561000f575f5ffd5b5060043610610034575f3560e01c80639e8945d214610038578063b864f5a914610053575b5f5ffd5b610040610076565b6040519081526020015b60405180910390f35b610066610061366004613891565b610240565b604051901515815260200161004a565b5f60017f2d61513640465d1ae53659963bae24b4fd7fed16847f5c74641fecd2869f53107f1a8ca8d060ed719a135103d35efc5de2484294b58ed7a32c600262b757b63c5e60056007610210604080517f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c260208201527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed818301527f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b60608201527f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa60808201527f260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c160a08201527f0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b060c08201527f04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe460e08201527f22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55610100808301919091528251808303909101815261012090910190915290565b604051602001610225969594939291906138fd565b60405160208183030381529060405280519060200120905090565b5f610249610279565b610251610802565b610259610b14565b610261610bdf565b610269611a4f565b610271612eab565b805f5260205ff35b6004356004810135600181146102b4576102b47f7075626c696320696e707574206c656e67746820697320696e636f72726563746020613796565b507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160249182013506610704523560048101356018811461031a5761031a7f70726f6f66206c656e67746820697320696e636f7272656374000000000000006019613796565b506024810190507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd478135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476020830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd478283097f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47838609087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd478384091461042c5761042c7f636f6d6d69746d656e742030206973206e6f74206f6e20637572766500000000601c613796565b508061074452508061072452507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476040820135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476060830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd478283097f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47838609087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4783840914610547576105477f636f6d6d69746d656e742031206973206e6f74206f6e20637572766500000000601c613796565b508061078452508061076452507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476080820135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760a0830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd478283097f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47838609087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4783840914610662576106627f636f6d6d69746d656e742032206973206e6f74206f6e20637572766500000000601c613796565b50806107c45250806107a452507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760c0820135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760e0830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd478283097f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47838609087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd478384091461077d5761077d7f636f6d6d69746d656e742033206973206e6f74206f6e20637572766500000000601c613796565b50610804526107e452610100015f5b600f8110156107d1577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016020820283810135919091066108249091015260010161078c565b507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101e0919091013506610a0452565b5f5b60018110156108265761081e602082026107040151613463565b600101610804565b506108507f2d61513640465d1ae53659963bae24b4fd7fed16847f5c74641fecd2869f5310613463565b6108797f1a8ca8d060ed719a135103d35efc5de2484294b58ed7a32c600262b757b63c5e613463565b61088561072451613463565b61089161074451613463565b61089a5f613488565b610264526108a86001613488565b610284526108b861076451613463565b6108c461078451613463565b6108ce6002613488565b6102a4525f5b600f8110156108f6576108ee602082026108240151613463565b6001016108d4565b506109016003613488565b610304527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016103045161030451096103245261093f6107a451613463565b61094b6107c451613463565b6109556004613488565b6103445261096760186102a4516137ce565b6102c4527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f1283ba6f4b7b1a76ba2008fe823128bea4adb9269cbfd7c41c223be65bc608636102c451096102e4527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103610a0b628000006102c4516137ce565b0861036452610aac60027f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001037f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001628000007f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001036102c45108096137ce565b6103a4527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610364516103a451096103a452610b0e60027f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103610364516137ce565b61038452565b6108a451610704516103a4517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000191829109820890505f5b6003811015610b98577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001806020836008010261082401516020840261082401510983089150600101610b4b565b50610384516109445161092451610884517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000193928492839283919009098408096103c45250565b5f5f7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001602060026008010261082401517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610284517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160077f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016102c45161026451090908087f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001602060016008010261082401517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610284517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160057f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016102c451610264510909080892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016020600b0261082401517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180602060080261082401517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610284517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016102c4516102645109080884090961094451610284516108e451610264519394507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000193849392849290918391900908086109a451610964516102845161090451610264517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019493859390928492839190090808090961092451610284516108c451610264519395507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000193849291839109080892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001838309610384517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001918203935081848408099050806103e4527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610384517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016103a4517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001036020600b02610824015108090961040452505050565b5f60128383011115610ff657610ff67f507265636f6d70757465204576616c2e204572726f72205b504c424549315d00601f613796565b85600284035f5b81811015611033577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018984099250600101610ffd565b50507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001818509602085026104a401517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018984097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103908282089050600191505f5b878110156111ce577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018b84097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103955089860894507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001848609945084602082890102610a2401527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001858d099b508b602082890102610c6401527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001838309602082890102610ea401527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018a840992506001016110b6565b50999a9950505050505050505050565b5f5f5f5f8587600287035f5b81811015611246577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018a850993507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018b840992506001016111ea565b508296508195507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000189840992507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018a830991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018288097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190810397508387096104a460208a0201517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000191820397509083850994507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183850895507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018187097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103965086860894507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018186089450506001880390505f5b81811015611441577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018a850993507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018b840992506001016113e5565b50507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182870895507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000187870995507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001818608945050507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001858409925093509350935093565b5f60128360020283011115611520576115207f507265636f6d70757465204576616c2e204572726f72205b504c42454955315d6020613796565b61152b8787856111de565b60015f5b88811015611647577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018c83097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190810394508a850893507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018685099350836020828a0102610a2401527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001848f099d508d6020828a0102610c6401527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018284096020828a0102610ea401527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018b8309915060010161152f565b50600190505f94505b87851015611771577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018c82097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103935089840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018484099250826020868a8a010102610a2401527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001838e099c508c6020868a8a010102610c6401527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018183096020868a8a010102610ea401527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018a82099050600185019450611650565b509a9b9a5050505050505050505050565b61034451610424516117bb906001907f2b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e808460085f610fbf565b610444516117f09082907f30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f7036368560046008610fbf565b61048451610464519192506118259183919077b3c4d79d41a917585bfc41088d8daaa78b17ea66b99c90dd866003600c6114e6565b915050610a045160017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182840914611882576118827f507265636f6d70757465204576616c2e204572726f72205b50414c42455d0000601e613796565b90506011805b8015611926577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001808460206001850302610c6401510960208302610ea401510960208202610ea401527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160208202610a240151840992507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01611888565b5050610ea4517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190829009610ea45250565b6102a4517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018182097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182820991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018283097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018282096104248490526104449190915261046481905290507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f0925f0bd364638ec3084b45fc27895f8f3f6f079096600fe946c8e9db9a471248209610484525050565b611a57611958565b60015f5b6009811015611a9f5781602082026104a401527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016103445183099150600101611a5b565b5050611aa9611782565b565b61042451610904515f907f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190839009905060015b6007811015611b44577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016020846007030261082401518508099150600101611adf565b507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610824518208610ea4519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819083096106a451086106a452610424517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001907f2b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e8009610904519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190839009905060015b6007811015611c80577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016020846007030261082401518508099150600101611c1b565b507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610824518208610ec4519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819083096106a451086106a452610424517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001907f30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f70363609610904519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190839009905060015b6007811015611dbc577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016020846007030261082401518508099150600101611d57565b507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610824518208610ee4519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819083096106a451086106a452610424517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001907f1d59376149b959ccbd157ac850893a6f07c2d99b3852513ab8d01be8e846a56609610904519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190839009905060015b6007811015611ef8577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016020846007030261082401518508099150600101611e93565b507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610824518208610f04519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819083096106a451086106a452610424517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001907f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000009610904519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190839009905060015b6007811015612034577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016020846007030261082401518508099150600101611fcf565b507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610824518208610f24519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819083096106a451086106a452610424517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001907f0530d09118705106cbb4a786ead16926d5d174e181a26686af5448492e42a18109610904519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190839009905060015b6007811015612170577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001602084600703026108240151850809915060010161210b565b507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610824518208610f44519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819083096106a451086106a452610424517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019077b3c4d79d41a91758cb49c3517c4604a520cff123608fc9cb09610904519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190839009905060015b60078110156122a4577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001602084600703026108240151850809915060010161223f565b507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610824518208610f64519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819083096106a451086106a452610424517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001907f130b17119778465cfb3acaee30f81dee20710ead41671f568b11d9ab07b95a9b09610904519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190839009905060015b60078110156123e0577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001602084600703026108240151850809915060010161237b565b507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610824518208610f84519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819083096106a451086106a4526104445191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001828409905060015b60038110156124d8577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001602060018560046008010303026108240151850809915060010161246d565b50610924517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908208610fa4519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819083096106c451086106c452610444517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001907f30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f7036360991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001828409905060015b6003811015612615577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160206001856004600801030302610824015185080991506001016125aa565b50610924517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908208610fc4519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819083096106c451086106c452610444517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001907f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000000991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001828409905060015b6003811015612752577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160206001856004600801030302610824015185080991506001016126e7565b50610924517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908208610fe4519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819083096106c451086106c452610444517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019077b3c4d79d41a91758cb49c3517c4604a520cff123608fc9cb0991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001828409905060015b6003811015612887577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001602060018560046008010303026108240151850809915060010161281c565b50610924517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908208611004519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819083096106c451086106c45261046451610484519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183870991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000187850809610984519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908308611024519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819084096106e451086106e4526109e4517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908290096109c4519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190829082908508096109a4519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908308611084519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819084096106e451086106e452610464517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019077b3c4d79d41a917585bfc41088d8daaa78b17ea66b99c90dd09610484519093507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019077b3c4d79d41a917585bfc41088d8daaa78b17ea66b99c90dd0990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183870991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000187850809610984519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908308611044519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819084096106e451086106e4526109e4517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908290096109c4519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190829082908508096109a4519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019083086110a4519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819084096106e451086106e452610464517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001907f30644e72e131a029048b6e193fd84104cc37a73fec2bc5e9b8ca0b2d36636f2309610484519093507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001907f30644e72e131a029048b6e193fd84104cc37a73fec2bc5e9b8ca0b2d36636f230990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183870991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000187850809610984519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908308611064519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819084096106e451086106e4526109e4517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908290096109c4519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190829082908508096109a4519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001915082086110c4519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819083096106e451086106e4525050505050565b5f5f612ec1610404516103e4516103c451611aab565b6102c4517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103610664526102e451810361068481905261050451086106045261066451610504517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001919008610604519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908290096106045261066451610524517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001919008610604519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908290096106048190526105c4819052612fed907f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffff906137ce565b6105e45261068451610504517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019190086106245261066451610504517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001919008610624519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019082900961062452610664516105a4517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001919008610624519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190829009610624526105c4517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908290096105c45261066451610524517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000191900861064452610664516105a4517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001919008610644519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908290096106448190526105e4517f2d61513640465d1ae53659963bae24b4fd7fed16847f5c74641fecd2869f5310917f1a8ca8d060ed719a135103d35efc5de2484294b58ed7a32c600262b757b63c5e917f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000191097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161032451820990506132398161078451610764516134c0565b6132458183868861351c565b6106e45191965094507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019084096105e451610624519194507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001910995507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161030451870995506132dc8661074451610724516134c0565b915091506132ec8183868861351c565b6106c45191965094507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190870995507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000186840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016106a4518408925061337783600260016134c0565b915091506133878183868861356d565b945094507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016105e4516105c451096105c4526133cd6105c4516107c4516107a4516134c0565b915091506133dd8183868861356d565b945094506133f561034451610804516107e4516134c0565b915091506134058183868861351c565b945094505050505f925061080451613450576107e4511561344b5761344b7f6e6f6e207a65726f20782076616c7565205b434f5d00000000000000000000006015613796565b600192505b61345b8382846135e0565b935050505090565b5f61020353610244526064610200206001610203536064610200206102245261020452565b60026102035360e01b610244526048610200207f1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1690565b80608052508060a052508060c052505f5f604060806060608060075afa8061350d5761350d7f706f696e74206d756c7469706c69636174696f6e206661696c65640000000000601b613796565b505060805160a0519092909150565b80608052508060a052508060c052508060e052505f5f6040608080608060065afa8061350d5761350d7f706f696e74206164646974696f6e206661696c656400000000000000000000006015613796565b80608052508060a052508060c05250807f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd470360e052505f5f6040608080608060065afa8061350d5761350d7f706f696e74207375627472616374696f6e206661696c656400000000000000006018613796565b60805260a0527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c260c0527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed60e0527f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b610100527f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa610120526107e4516101405261080451610160525f816136b957610804517f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4703610160525b7f260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1610180527f0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b06101a0527f04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe46101c0527f22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e556101e052602060806020600c02608060085afa8061378c5761378c7f70616972696e6720636865636b206661696c65640000000000000000000000006014613796565b5050608051919050565b7f08c379a00000000000000000000000000000000000000000000000000000000060805260206084528060a452508060c45260646080fd5b60205f52602080526020604052806060525080608052507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160a0525f60205f60c05f60055afa613843576138437f6d6f6465787020707265636f6d70696c65206661696c656400000000000000006018613796565b505f5190565b5f5f83601f840112613859575f5ffd5b50813567ffffffffffffffff811115613870575f5ffd5b6020830191508360208260051b850101111561388a575f5ffd5b9250929050565b5f5f5f5f604085870312156138a4575f5ffd5b843567ffffffffffffffff8111156138ba575f5ffd5b6138c687828801613849565b909550935050602085013567ffffffffffffffff8111156138e5575f5ffd5b6138f187828801613849565b95989497509550505050565b8681528560208201528460408201528360608201528260808201525f8251806020850160a085015e5f920160a00191825250969550505050505056fea2646970667358221220dfd5f49f0401e9e739eec67e7d593a8f0f2ee7ff4a1760a0ca7d1d231f5dad2864736f6c634300081c0033","optimization_enabled":true,"verified_twin_address_hash":null,"is_verified":true,"compiler_settings":{"evmVersion":"prague","libraries":{},"metadata":{"appendCBOR":true,"bytecodeHash":"ipfs","useLiteralContent":false},"optimizer":{"enabled":true,"runs":9999999},"outputSelection":{"*":{"":["*"],"*":["*"]}},"remappings":["@ensdomains/=node_modules/@ensdomains/","ds-test/=lib/forge-std/lib/ds-test/src/","eth-gas-reporter/=node_modules/eth-gas-reporter/","forge-std/=lib/forge-std/src/","hardhat/=node_modules/hardhat/","murky/=lib/murky/src/","foundry-test/=test/foundry/","l2-contracts/=../l2-contracts/contracts/","system-contracts/=../system-contracts/","@openzeppelin/contracts-v4/=lib/openzeppelin-contracts-v4/contracts/","@openzeppelin/contracts-upgradeable-v4/=lib/openzeppelin-contracts-upgradeable-v4/contracts/","test-utils/=test/foundry/l1/integration/utils/","openzeppelin-contracts-upgradeable-v4/=lib/openzeppelin-contracts-upgradeable-v4/","openzeppelin-contracts-v4/=lib/openzeppelin-contracts-v4/"],"viaIR":false},"optimization_runs":9999999,"sourcify_repo_url":null,"decoded_constructor_args":null,"compiler_version":"v0.8.28+commit.7893614a","is_verified_via_verifier_alliance":false,"verified_at":"2026-08-15T03:44:17.094171Z","implementations":[],"proxy_type":null,"external_libraries":[],"creation_bytecode":"0x6080604052348015600e575f5ffd5b5061396f8061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610034575f3560e01c80639e8945d214610038578063b864f5a914610053575b5f5ffd5b610040610076565b6040519081526020015b60405180910390f35b610066610061366004613891565b610240565b604051901515815260200161004a565b5f60017f2d61513640465d1ae53659963bae24b4fd7fed16847f5c74641fecd2869f53107f1a8ca8d060ed719a135103d35efc5de2484294b58ed7a32c600262b757b63c5e60056007610210604080517f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c260208201527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed818301527f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b60608201527f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa60808201527f260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c160a08201527f0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b060c08201527f04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe460e08201527f22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55610100808301919091528251808303909101815261012090910190915290565b604051602001610225969594939291906138fd565b60405160208183030381529060405280519060200120905090565b5f610249610279565b610251610802565b610259610b14565b610261610bdf565b610269611a4f565b610271612eab565b805f5260205ff35b6004356004810135600181146102b4576102b47f7075626c696320696e707574206c656e67746820697320696e636f72726563746020613796565b507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160249182013506610704523560048101356018811461031a5761031a7f70726f6f66206c656e67746820697320696e636f7272656374000000000000006019613796565b506024810190507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd478135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476020830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd478283097f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47838609087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd478384091461042c5761042c7f636f6d6d69746d656e742030206973206e6f74206f6e20637572766500000000601c613796565b508061074452508061072452507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476040820135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476060830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd478283097f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47838609087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4783840914610547576105477f636f6d6d69746d656e742031206973206e6f74206f6e20637572766500000000601c613796565b508061078452508061076452507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476080820135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760a0830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd478283097f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47838609087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4783840914610662576106627f636f6d6d69746d656e742032206973206e6f74206f6e20637572766500000000601c613796565b50806107c45250806107a452507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760c0820135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760e0830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd478283097f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47838609087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd478384091461077d5761077d7f636f6d6d69746d656e742033206973206e6f74206f6e20637572766500000000601c613796565b50610804526107e452610100015f5b600f8110156107d1577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016020820283810135919091066108249091015260010161078c565b507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101e0919091013506610a0452565b5f5b60018110156108265761081e602082026107040151613463565b600101610804565b506108507f2d61513640465d1ae53659963bae24b4fd7fed16847f5c74641fecd2869f5310613463565b6108797f1a8ca8d060ed719a135103d35efc5de2484294b58ed7a32c600262b757b63c5e613463565b61088561072451613463565b61089161074451613463565b61089a5f613488565b610264526108a86001613488565b610284526108b861076451613463565b6108c461078451613463565b6108ce6002613488565b6102a4525f5b600f8110156108f6576108ee602082026108240151613463565b6001016108d4565b506109016003613488565b610304527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016103045161030451096103245261093f6107a451613463565b61094b6107c451613463565b6109556004613488565b6103445261096760186102a4516137ce565b6102c4527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f1283ba6f4b7b1a76ba2008fe823128bea4adb9269cbfd7c41c223be65bc608636102c451096102e4527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103610a0b628000006102c4516137ce565b0861036452610aac60027f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001037f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001628000007f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001036102c45108096137ce565b6103a4527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610364516103a451096103a452610b0e60027f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103610364516137ce565b61038452565b6108a451610704516103a4517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000191829109820890505f5b6003811015610b98577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001806020836008010261082401516020840261082401510983089150600101610b4b565b50610384516109445161092451610884517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000193928492839283919009098408096103c45250565b5f5f7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001602060026008010261082401517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610284517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160077f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016102c45161026451090908087f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001602060016008010261082401517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610284517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160057f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016102c451610264510909080892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016020600b0261082401517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180602060080261082401517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610284517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016102c4516102645109080884090961094451610284516108e451610264519394507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000193849392849290918391900908086109a451610964516102845161090451610264517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019493859390928492839190090808090961092451610284516108c451610264519395507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000193849291839109080892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001838309610384517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001918203935081848408099050806103e4527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610384517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016103a4517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001036020600b02610824015108090961040452505050565b5f60128383011115610ff657610ff67f507265636f6d70757465204576616c2e204572726f72205b504c424549315d00601f613796565b85600284035f5b81811015611033577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018984099250600101610ffd565b50507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001818509602085026104a401517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018984097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103908282089050600191505f5b878110156111ce577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018b84097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103955089860894507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001848609945084602082890102610a2401527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001858d099b508b602082890102610c6401527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001838309602082890102610ea401527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018a840992506001016110b6565b50999a9950505050505050505050565b5f5f5f5f8587600287035f5b81811015611246577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018a850993507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018b840992506001016111ea565b508296508195507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000189840992507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018a830991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018288097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190810397508387096104a460208a0201517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000191820397509083850994507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183850895507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018187097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103965086860894507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018186089450506001880390505f5b81811015611441577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018a850993507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018b840992506001016113e5565b50507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182870895507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000187870995507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001818608945050507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001858409925093509350935093565b5f60128360020283011115611520576115207f507265636f6d70757465204576616c2e204572726f72205b504c42454955315d6020613796565b61152b8787856111de565b60015f5b88811015611647577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018c83097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190810394508a850893507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018685099350836020828a0102610a2401527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001848f099d508d6020828a0102610c6401527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018284096020828a0102610ea401527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018b8309915060010161152f565b50600190505f94505b87851015611771577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018c82097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103935089840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018484099250826020868a8a010102610a2401527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001838e099c508c6020868a8a010102610c6401527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018183096020868a8a010102610ea401527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018a82099050600185019450611650565b509a9b9a5050505050505050505050565b61034451610424516117bb906001907f2b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e808460085f610fbf565b610444516117f09082907f30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f7036368560046008610fbf565b61048451610464519192506118259183919077b3c4d79d41a917585bfc41088d8daaa78b17ea66b99c90dd866003600c6114e6565b915050610a045160017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182840914611882576118827f507265636f6d70757465204576616c2e204572726f72205b50414c42455d0000601e613796565b90506011805b8015611926577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001808460206001850302610c6401510960208302610ea401510960208202610ea401527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160208202610a240151840992507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01611888565b5050610ea4517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190829009610ea45250565b6102a4517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018182097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182820991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018283097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018282096104248490526104449190915261046481905290507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f0925f0bd364638ec3084b45fc27895f8f3f6f079096600fe946c8e9db9a471248209610484525050565b611a57611958565b60015f5b6009811015611a9f5781602082026104a401527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016103445183099150600101611a5b565b5050611aa9611782565b565b61042451610904515f907f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190839009905060015b6007811015611b44577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016020846007030261082401518508099150600101611adf565b507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610824518208610ea4519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819083096106a451086106a452610424517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001907f2b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e8009610904519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190839009905060015b6007811015611c80577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016020846007030261082401518508099150600101611c1b565b507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610824518208610ec4519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819083096106a451086106a452610424517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001907f30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f70363609610904519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190839009905060015b6007811015611dbc577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016020846007030261082401518508099150600101611d57565b507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610824518208610ee4519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819083096106a451086106a452610424517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001907f1d59376149b959ccbd157ac850893a6f07c2d99b3852513ab8d01be8e846a56609610904519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190839009905060015b6007811015611ef8577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016020846007030261082401518508099150600101611e93565b507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610824518208610f04519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819083096106a451086106a452610424517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001907f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000009610904519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190839009905060015b6007811015612034577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016020846007030261082401518508099150600101611fcf565b507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610824518208610f24519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819083096106a451086106a452610424517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001907f0530d09118705106cbb4a786ead16926d5d174e181a26686af5448492e42a18109610904519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190839009905060015b6007811015612170577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001602084600703026108240151850809915060010161210b565b507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610824518208610f44519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819083096106a451086106a452610424517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019077b3c4d79d41a91758cb49c3517c4604a520cff123608fc9cb09610904519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190839009905060015b60078110156122a4577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001602084600703026108240151850809915060010161223f565b507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610824518208610f64519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819083096106a451086106a452610424517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001907f130b17119778465cfb3acaee30f81dee20710ead41671f568b11d9ab07b95a9b09610904519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190839009905060015b60078110156123e0577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001602084600703026108240151850809915060010161237b565b507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610824518208610f84519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819083096106a451086106a4526104445191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001828409905060015b60038110156124d8577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001602060018560046008010303026108240151850809915060010161246d565b50610924517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908208610fa4519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819083096106c451086106c452610444517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001907f30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f7036360991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001828409905060015b6003811015612615577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160206001856004600801030302610824015185080991506001016125aa565b50610924517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908208610fc4519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819083096106c451086106c452610444517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001907f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000000991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001828409905060015b6003811015612752577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160206001856004600801030302610824015185080991506001016126e7565b50610924517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908208610fe4519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819083096106c451086106c452610444517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019077b3c4d79d41a91758cb49c3517c4604a520cff123608fc9cb0991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001828409905060015b6003811015612887577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001602060018560046008010303026108240151850809915060010161281c565b50610924517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908208611004519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819083096106c451086106c45261046451610484519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183870991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000187850809610984519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908308611024519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819084096106e451086106e4526109e4517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908290096109c4519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190829082908508096109a4519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908308611084519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819084096106e451086106e452610464517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019077b3c4d79d41a917585bfc41088d8daaa78b17ea66b99c90dd09610484519093507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019077b3c4d79d41a917585bfc41088d8daaa78b17ea66b99c90dd0990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183870991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000187850809610984519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908308611044519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819084096106e451086106e4526109e4517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908290096109c4519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190829082908508096109a4519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019083086110a4519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819084096106e451086106e452610464517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001907f30644e72e131a029048b6e193fd84104cc37a73fec2bc5e9b8ca0b2d36636f2309610484519093507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001907f30644e72e131a029048b6e193fd84104cc37a73fec2bc5e9b8ca0b2d36636f230990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183870991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000187850809610984519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908308611064519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819084096106e451086106e4526109e4517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908290096109c4519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190829082908508096109a4519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001915082086110c4519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190819083096106e451086106e4525050505050565b5f5f612ec1610404516103e4516103c451611aab565b6102c4517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103610664526102e451810361068481905261050451086106045261066451610504517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001919008610604519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908290096106045261066451610524517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001919008610604519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908290096106048190526105c4819052612fed907f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffff906137ce565b6105e45261068451610504517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019190086106245261066451610504517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001919008610624519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019082900961062452610664516105a4517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001919008610624519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190829009610624526105c4517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908290096105c45261066451610524517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000191900861064452610664516105a4517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001919008610644519091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908290096106448190526105e4517f2d61513640465d1ae53659963bae24b4fd7fed16847f5c74641fecd2869f5310917f1a8ca8d060ed719a135103d35efc5de2484294b58ed7a32c600262b757b63c5e917f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000191097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161032451820990506132398161078451610764516134c0565b6132458183868861351c565b6106e45191965094507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019084096105e451610624519194507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001910995507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161030451870995506132dc8661074451610724516134c0565b915091506132ec8183868861351c565b6106c45191965094507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190870995507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000186840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016106a4518408925061337783600260016134c0565b915091506133878183868861356d565b945094507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016105e4516105c451096105c4526133cd6105c4516107c4516107a4516134c0565b915091506133dd8183868861356d565b945094506133f561034451610804516107e4516134c0565b915091506134058183868861351c565b945094505050505f925061080451613450576107e4511561344b5761344b7f6e6f6e207a65726f20782076616c7565205b434f5d00000000000000000000006015613796565b600192505b61345b8382846135e0565b935050505090565b5f61020353610244526064610200206001610203536064610200206102245261020452565b60026102035360e01b610244526048610200207f1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1690565b80608052508060a052508060c052505f5f604060806060608060075afa8061350d5761350d7f706f696e74206d756c7469706c69636174696f6e206661696c65640000000000601b613796565b505060805160a0519092909150565b80608052508060a052508060c052508060e052505f5f6040608080608060065afa8061350d5761350d7f706f696e74206164646974696f6e206661696c656400000000000000000000006015613796565b80608052508060a052508060c05250807f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd470360e052505f5f6040608080608060065afa8061350d5761350d7f706f696e74207375627472616374696f6e206661696c656400000000000000006018613796565b60805260a0527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c260c0527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed60e0527f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b610100527f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa610120526107e4516101405261080451610160525f816136b957610804517f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4703610160525b7f260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1610180527f0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b06101a0527f04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe46101c0527f22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e556101e052602060806020600c02608060085afa8061378c5761378c7f70616972696e6720636865636b206661696c65640000000000000000000000006014613796565b5050608051919050565b7f08c379a00000000000000000000000000000000000000000000000000000000060805260206084528060a452508060c45260646080fd5b60205f52602080526020604052806060525080608052507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160a0525f60205f60c05f60055afa613843576138437f6d6f6465787020707265636f6d70696c65206661696c656400000000000000006018613796565b505f5190565b5f5f83601f840112613859575f5ffd5b50813567ffffffffffffffff811115613870575f5ffd5b6020830191508360208260051b850101111561388a575f5ffd5b9250929050565b5f5f5f5f604085870312156138a4575f5ffd5b843567ffffffffffffffff8111156138ba575f5ffd5b6138c687828801613849565b909550935050602085013567ffffffffffffffff8111156138e5575f5ffd5b6138f187828801613849565b95989497509550505050565b8681528560208201528460408201528360608201528260808201525f8251806020850160a085015e5f920160a00191825250969550505050505056fea2646970667358221220dfd5f49f0401e9e739eec67e7d593a8f0f2ee7ff4a1760a0ca7d1d231f5dad2864736f6c634300081c0033","name":"EraVerifierFflonk","is_blueprint":false,"license_type":"none","is_fully_verified":true,"is_verified_via_eth_bytecode_db":true,"language":"solidity","evm_version":"prague","can_be_visualized_via_sol2uml":true,"is_verified_via_sourcify":false,"additional_sources":[{"file_path":"contracts/state-transition/chain-interfaces/IVerifierV2.sol","source_code":"// SPDX-License-Identifier: MIT\n// We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.\npragma solidity ^0.8.21;\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 IVerifierV2 {\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(uint256[] calldata _publicInputs, uint256[] calldata _proof) 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 view returns (bytes32);\n}\n"}],"certified":false,"conflicting_implementations":null,"abi":[{"inputs":[],"name":"verificationKeyHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"}],"name":"verify","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}],"is_changed_bytecode":false,"is_partially_verified":false,"constructor_args":null}