ETH Price: $2,443.27 (-1.27%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
0x60806040194897812024-03-22 11:10:59228 days ago1711105859IN
 Create: PlonkVerifierForMultiTypeDataAggregation
0 ETH0.0563370223.85082389

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
203359722024-07-18 21:03:59109 days ago1721336639
0x8AB45503...dcAd24Cc4
0 ETH
203359722024-07-18 21:03:59109 days ago1721336639
0x8AB45503...dcAd24Cc4
0 ETH
203359722024-07-18 21:03:59109 days ago1721336639
0x8AB45503...dcAd24Cc4
0 ETH
203359722024-07-18 21:03:59109 days ago1721336639
0x8AB45503...dcAd24Cc4
0 ETH
203359722024-07-18 21:03:59109 days ago1721336639
0x8AB45503...dcAd24Cc4
0 ETH
203359722024-07-18 21:03:59109 days ago1721336639
0x8AB45503...dcAd24Cc4
0 ETH
203359722024-07-18 21:03:59109 days ago1721336639
0x8AB45503...dcAd24Cc4
0 ETH
203359722024-07-18 21:03:59109 days ago1721336639
0x8AB45503...dcAd24Cc4
0 ETH
203359722024-07-18 21:03:59109 days ago1721336639
0x8AB45503...dcAd24Cc4
0 ETH
203359722024-07-18 21:03:59109 days ago1721336639
0x8AB45503...dcAd24Cc4
0 ETH
203359722024-07-18 21:03:59109 days ago1721336639
0x8AB45503...dcAd24Cc4
0 ETH
203359722024-07-18 21:03:59109 days ago1721336639
0x8AB45503...dcAd24Cc4
0 ETH
203359722024-07-18 21:03:59109 days ago1721336639
0x8AB45503...dcAd24Cc4
0 ETH
203359722024-07-18 21:03:59109 days ago1721336639
0x8AB45503...dcAd24Cc4
0 ETH
203359722024-07-18 21:03:59109 days ago1721336639
0x8AB45503...dcAd24Cc4
0 ETH
203359722024-07-18 21:03:59109 days ago1721336639
0x8AB45503...dcAd24Cc4
0 ETH
203359722024-07-18 21:03:59109 days ago1721336639
0x8AB45503...dcAd24Cc4
0 ETH
203359722024-07-18 21:03:59109 days ago1721336639
0x8AB45503...dcAd24Cc4
0 ETH
203359722024-07-18 21:03:59109 days ago1721336639
0x8AB45503...dcAd24Cc4
0 ETH
203359722024-07-18 21:03:59109 days ago1721336639
0x8AB45503...dcAd24Cc4
0 ETH
203359722024-07-18 21:03:59109 days ago1721336639
0x8AB45503...dcAd24Cc4
0 ETH
203359722024-07-18 21:03:59109 days ago1721336639
0x8AB45503...dcAd24Cc4
0 ETH
203359722024-07-18 21:03:59109 days ago1721336639
0x8AB45503...dcAd24Cc4
0 ETH
203359722024-07-18 21:03:59109 days ago1721336639
0x8AB45503...dcAd24Cc4
0 ETH
203359722024-07-18 21:03:59109 days ago1721336639
0x8AB45503...dcAd24Cc4
0 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
PlonkVerifierForMultiTypeDataAggregation

Compiler Version
v0.8.24+commit.e11b9ed9

Optimization Enabled:
Yes with 100000 runs

Other Settings:
cancun EvmVersion
File 1 of 1 : PlonkVerifierForMultiTypeDataAggregation.sol
// SPDX-License-Identifier: Apache-2.0

// Copyright 2023 Consensys Software Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Code generated by gnark DO NOT EDIT

pragma solidity 0.8.24;

contract PlonkVerifierForMultiTypeDataAggregation {
  uint256 private constant R_MOD = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
  uint256 private constant R_MOD_MINUS_ONE =
    21888242871839275222246405745257275088548364400416034343698204186575808495616;
  uint256 private constant P_MOD = 21888242871839275222246405745257275088696311157297823662689037894645226208583;

  uint256 private constant G2_SRS_0_X_0 = 11559732032986387107991004021392285783925812861821192530917403151452391805634;
  uint256 private constant G2_SRS_0_X_1 = 10857046999023057135944570762232829481370756359578518086990519993285655852781;
  uint256 private constant G2_SRS_0_Y_0 = 4082367875863433681332203403145435568316851327593401208105741076214120093531;
  uint256 private constant G2_SRS_0_Y_1 = 8495653923123431417604973247489272438418190587263600148770280649306958101930;

  uint256 private constant G2_SRS_1_X_0 = 15805639136721018565402881920352193254830339253282065586954346329754995870280;
  uint256 private constant G2_SRS_1_X_1 = 19089565590083334368588890253123139704298730990782503769911324779715431555531;
  uint256 private constant G2_SRS_1_Y_0 = 9779648407879205346559610309258181044130619080926897934572699915909528404984;
  uint256 private constant G2_SRS_1_Y_1 = 6779728121489434657638426458390319301070371227460768374343986326751507916979;

  uint256 private constant G1_SRS_X = 14312776538779914388377568895031746459131577658076416373430523308756343304251;
  uint256 private constant G1_SRS_Y = 11763105256161367503191792604679297387056316997144156930871823008787082098465;

  // ----------------------- vk ---------------------
  uint256 private constant VK_NB_PUBLIC_INPUTS = 1;
  uint256 private constant VK_DOMAIN_SIZE = 67108864;
  uint256 private constant VK_INV_DOMAIN_SIZE =
    21888242545679039938882419398440172875981108180010270949818755658014750055173;
  uint256 private constant VK_OMEGA = 7419588552507395652481651088034484897579724952953562618697845598160172257810;
  uint256 private constant VK_QL_COM_X = 10351727079213613483195244711380391114216864939895330479839658146840276882262;
  uint256 private constant VK_QL_COM_Y = 1501263231399727366873163221832273693721713537024621645003902818474756423636;
  uint256 private constant VK_QR_COM_X = 1755715254584130553316219200699510678407150977562385913444299354328290732750;
  uint256 private constant VK_QR_COM_Y = 6035531587949530257895257270136742232493729835720286559296644264685184728912;
  uint256 private constant VK_QM_COM_X = 6670623518536262361990885473297545797912654796530243540828937537306084236619;
  uint256 private constant VK_QM_COM_Y = 5527371248161028440961109065969946893828350552620073674472390679136677719041;
  uint256 private constant VK_QO_COM_X = 11535322182238154858975480929690827734408299766285738385246540057519307052131;
  uint256 private constant VK_QO_COM_Y = 9406943571372495410630473023640476438948478281577790053634611025904513130385;
  uint256 private constant VK_QK_COM_X = 16992489263408597431365807756861820824519715637825097480883210502244678717323;
  uint256 private constant VK_QK_COM_Y = 6316566310580488254674066482502540967149399935076269761204826568302310471641;

  uint256 private constant VK_S1_COM_X = 7526958838811434733988160694607832192518841690427020181558356036914871067815;
  uint256 private constant VK_S1_COM_Y = 21601335937159467341823511823826796792260553831478229612173961272875997241217;

  uint256 private constant VK_S2_COM_X = 11840692868198724727479994514745732568183318032409011322354798500766847323606;
  uint256 private constant VK_S2_COM_Y = 15744552220798376594850739946958929988596017970330564656423765550023713765969;

  uint256 private constant VK_S3_COM_X = 9136251636064909542462317382444135433432587842307255314711928769032387448189;
  uint256 private constant VK_S3_COM_Y = 19967154923631086378606582363409133980564320499521903515170070514301406348885;

  uint256 private constant VK_COSET_SHIFT = 5;

  uint256 private constant VK_QCP_0_X = 14248271088937116299667760094406306756783830312220769724227434338672147875233;
  uint256 private constant VK_QCP_0_Y = 12069861204650129302745976264365857070025160064577630615914061145100142015961;

  uint256 private constant VK_INDEX_COMMIT_API0 = 23360636;
  uint256 private constant VK_NB_CUSTOM_GATES = 1;

  // ------------------------------------------------

  // offset proof
  uint256 private constant PROOF_L_COM_X = 0x00;
  uint256 private constant PROOF_L_COM_Y = 0x20;
  uint256 private constant PROOF_R_COM_X = 0x40;
  uint256 private constant PROOF_R_COM_Y = 0x60;
  uint256 private constant PROOF_O_COM_X = 0x80;
  uint256 private constant PROOF_O_COM_Y = 0xa0;

  // h = h_0 + x^{n+2}h_1 + x^{2(n+2)}h_2
  uint256 private constant PROOF_H_0_X = 0xc0;
  uint256 private constant PROOF_H_0_Y = 0xe0;
  uint256 private constant PROOF_H_1_X = 0x100;
  uint256 private constant PROOF_H_1_Y = 0x120;
  uint256 private constant PROOF_H_2_X = 0x140;
  uint256 private constant PROOF_H_2_Y = 0x160;

  // wire values at zeta
  uint256 private constant PROOF_L_AT_ZETA = 0x180;
  uint256 private constant PROOF_R_AT_ZETA = 0x1a0;
  uint256 private constant PROOF_O_AT_ZETA = 0x1c0;

  //uint256[STATE_WIDTH-1] permutation_polynomials_at_zeta; // Sσ1(zeta),Sσ2(zeta)
  uint256 private constant PROOF_S1_AT_ZETA = 0x1e0; // Sσ1(zeta)
  uint256 private constant PROOF_S2_AT_ZETA = 0x200; // Sσ2(zeta)

  //Bn254.G1Point grand_product_commitment;                 // [z(x)]
  uint256 private constant PROOF_GRAND_PRODUCT_COMMITMENT_X = 0x220;
  uint256 private constant PROOF_GRAND_PRODUCT_COMMITMENT_Y = 0x240;

  uint256 private constant PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA = 0x260; // z(w*zeta)
  uint256 private constant PROOF_QUOTIENT_POLYNOMIAL_AT_ZETA = 0x280; // t(zeta)
  uint256 private constant PROOF_LINEARISED_POLYNOMIAL_AT_ZETA = 0x2a0; // r(zeta)

  // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp
  uint256 private constant PROOF_BATCH_OPENING_AT_ZETA_X = 0x2c0; // [Wzeta]
  uint256 private constant PROOF_BATCH_OPENING_AT_ZETA_Y = 0x2e0;

  uint256 private constant PROOF_OPENING_AT_ZETA_OMEGA_X = 0x300;
  uint256 private constant PROOF_OPENING_AT_ZETA_OMEGA_Y = 0x320;

  uint256 private constant PROOF_OPENING_QCP_AT_ZETA = 0x340;
  uint256 private constant PROOF_COMMITMENTS_WIRES_CUSTOM_GATES = 0x360;

  // -> next part of proof is
  // [ openings_selector_commits || commitments_wires_commit_api]

  // -------- offset state

  // challenges to check the claimed quotient
  uint256 private constant STATE_ALPHA = 0x00;
  uint256 private constant STATE_BETA = 0x20;
  uint256 private constant STATE_GAMMA = 0x40;
  uint256 private constant STATE_ZETA = 0x60;

  // reusable value
  uint256 private constant STATE_ALPHA_SQUARE_LAGRANGE_0 = 0x80;

  // commitment to H
  uint256 private constant STATE_FOLDED_H_X = 0xa0;
  uint256 private constant STATE_FOLDED_H_Y = 0xc0;

  // commitment to the linearised polynomial
  uint256 private constant STATE_LINEARISED_POLYNOMIAL_X = 0xe0;
  uint256 private constant STATE_LINEARISED_POLYNOMIAL_Y = 0x100;

  // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp
  uint256 private constant STATE_FOLDED_CLAIMED_VALUES = 0x120;

  // folded digests of H, linearised poly, l, r, o, s_1, s_2, qcp
  uint256 private constant STATE_FOLDED_DIGESTS_X = 0x140;
  uint256 private constant STATE_FOLDED_DIGESTS_Y = 0x160;

  uint256 private constant STATE_PI = 0x180;

  uint256 private constant STATE_ZETA_POWER_N_MINUS_ONE = 0x1a0;

  uint256 private constant STATE_GAMMA_KZG = 0x1c0;

  uint256 private constant STATE_SUCCESS = 0x1e0;
  uint256 private constant STATE_CHECK_VAR = 0x200; // /!\ this slot is used for debugging only

  uint256 private constant STATE_LAST_MEM = 0x220;

  // -------- errors
  uint256 private constant ERROR_STRING_ID = 0x08c379a000000000000000000000000000000000000000000000000000000000; // selector for function Error(string)

  // -------- utils (for hash_fr)
  uint256 private constant HASH_FR_BB = 340282366920938463463374607431768211456; // 2**128
  uint256 private constant HASH_FR_ZERO_UINT256 = 0;

  uint8 private constant HASH_FR_LEN_IN_BYTES = 48;
  uint8 private constant HASH_FR_SIZE_DOMAIN = 11;
  uint8 private constant HASH_FR_ONE = 1;
  uint8 private constant HASH_FR_TWO = 2;

  /// Verify a Plonk proof.
  /// Reverts if the proof or the public inputs are malformed.
  /// @param proof serialised plonk proof (using gnark's MarshalSolidity)
  /// @param public_inputs (must be reduced)
  /// @return success true if the proof passes false otherwise
  function Verify(bytes calldata proof, uint256[] calldata public_inputs) public view returns (bool success) {
    assembly {
      let mem := mload(0x40)
      let freeMem := add(mem, STATE_LAST_MEM)

      // sanity checks
      check_number_of_public_inputs(public_inputs.length)
      check_inputs_size(public_inputs.length, public_inputs.offset)
      check_proof_size(proof.length)
      check_proof_openings_size(proof.offset)

      // compute the challenges
      let prev_challenge_non_reduced
      prev_challenge_non_reduced := derive_gamma(proof.offset, public_inputs.length, public_inputs.offset)
      prev_challenge_non_reduced := derive_beta(prev_challenge_non_reduced)
      prev_challenge_non_reduced := derive_alpha(proof.offset, prev_challenge_non_reduced)
      derive_zeta(proof.offset, prev_challenge_non_reduced)

      // evaluation of Z=Xⁿ-1 at ζ, we save this value
      let zeta := mload(add(mem, STATE_ZETA))
      let zeta_power_n_minus_one := addmod(pow(zeta, VK_DOMAIN_SIZE, freeMem), sub(R_MOD, 1), R_MOD)
      mstore(add(mem, STATE_ZETA_POWER_N_MINUS_ONE), zeta_power_n_minus_one)

      // public inputs contribution
      let l_pi := sum_pi_wo_api_commit(public_inputs.offset, public_inputs.length, freeMem)
      let l_wocommit := sum_pi_commit(proof.offset, public_inputs.length, freeMem)
      l_pi := addmod(l_wocommit, l_pi, R_MOD)
      mstore(add(mem, STATE_PI), l_pi)

      compute_alpha_square_lagrange_0()
      verify_quotient_poly_eval_at_zeta(proof.offset)
      fold_h(proof.offset)
      compute_commitment_linearised_polynomial(proof.offset)
      compute_gamma_kzg(proof.offset)
      fold_state(proof.offset)
      batch_verify_multi_points(proof.offset)

      success := mload(add(mem, STATE_SUCCESS))

      // Beginning errors -------------------------------------------------

      function error_nb_public_inputs() {
        let ptError := mload(0x40)
        mstore(ptError, ERROR_STRING_ID) // selector for function Error(string)
        mstore(add(ptError, 0x4), 0x20)
        mstore(add(ptError, 0x24), 0x1d)
        mstore(add(ptError, 0x44), "wrong number of public inputs")
        revert(ptError, 0x64)
      }

      /// Called when an operation on Bn254 fails
      /// @dev for instance when calling EcMul on a point not on Bn254.
      function error_ec_op() {
        let ptError := mload(0x40)
        mstore(ptError, ERROR_STRING_ID) // selector for function Error(string)
        mstore(add(ptError, 0x4), 0x20)
        mstore(add(ptError, 0x24), 0x12)
        mstore(add(ptError, 0x44), "error ec operation")
        revert(ptError, 0x64)
      }

      /// Called when one of the public inputs is not reduced.
      function error_inputs_size() {
        let ptError := mload(0x40)
        mstore(ptError, ERROR_STRING_ID) // selector for function Error(string)
        mstore(add(ptError, 0x4), 0x20)
        mstore(add(ptError, 0x24), 0x18)
        mstore(add(ptError, 0x44), "inputs are bigger than r")
        revert(ptError, 0x64)
      }

      /// Called when the size proof is not as expected
      /// @dev to avoid overflow attack for instance
      function error_proof_size() {
        let ptError := mload(0x40)
        mstore(ptError, ERROR_STRING_ID) // selector for function Error(string)
        mstore(add(ptError, 0x4), 0x20)
        mstore(add(ptError, 0x24), 0x10)
        mstore(add(ptError, 0x44), "wrong proof size")
        revert(ptError, 0x64)
      }

      /// Called when one the openings is bigger than r
      /// The openings are the claimed evalutions of a polynomial
      /// in a Kzg proof.
      function error_proof_openings_size() {
        let ptError := mload(0x40)
        mstore(ptError, ERROR_STRING_ID) // selector for function Error(string)
        mstore(add(ptError, 0x4), 0x20)
        mstore(add(ptError, 0x24), 0x16)
        mstore(add(ptError, 0x44), "openings bigger than r")
        revert(ptError, 0x64)
      }

      function error_verify() {
        let ptError := mload(0x40)
        mstore(ptError, ERROR_STRING_ID) // selector for function Error(string)
        mstore(add(ptError, 0x4), 0x20)
        mstore(add(ptError, 0x24), 0xc)
        mstore(add(ptError, 0x44), "error verify")
        revert(ptError, 0x64)
      }

      function error_random_generation() {
        let ptError := mload(0x40)
        mstore(ptError, ERROR_STRING_ID) // selector for function Error(string)
        mstore(add(ptError, 0x4), 0x20)
        mstore(add(ptError, 0x24), 0x14)
        mstore(add(ptError, 0x44), "error random gen kzg")
        revert(ptError, 0x64)
      }
      // end errors -------------------------------------------------

      // Beginning checks -------------------------------------------------

      /// @param s actual number of public inputs
      function check_number_of_public_inputs(s) {
        if iszero(eq(s, VK_NB_PUBLIC_INPUTS)) {
          error_nb_public_inputs()
        }
      }

      /// Checks that the public inputs are < R_MOD.
      /// @param s number of public inputs
      /// @param p pointer to the public inputs array
      function check_inputs_size(s, p) {
        for {
          let i
        } lt(i, s) {
          i := add(i, 1)
        } {
          if gt(calldataload(p), R_MOD_MINUS_ONE) {
            error_inputs_size()
          }
          p := add(p, 0x20)
        }
      }

      /// Checks if the proof is of the correct size
      /// @param actual_proof_size size of the proof (not the expected size)
      function check_proof_size(actual_proof_size) {
        let expected_proof_size := add(0x340, mul(VK_NB_CUSTOM_GATES, 0x60))
        if iszero(eq(actual_proof_size, expected_proof_size)) {
          error_proof_size()
        }
      }

      /// Checks if the multiple openings of the polynomials are < R_MOD.
      /// @param aproof pointer to the beginning of the proof
      /// @dev the 'a' prepending proof is to have a local name
      function check_proof_openings_size(aproof) {
        // linearised polynomial at zeta
        let p := add(aproof, PROOF_LINEARISED_POLYNOMIAL_AT_ZETA)
        if gt(calldataload(p), R_MOD_MINUS_ONE) {
          error_proof_openings_size()
        }

        // quotient polynomial at zeta
        p := add(aproof, PROOF_QUOTIENT_POLYNOMIAL_AT_ZETA)
        if gt(calldataload(p), R_MOD_MINUS_ONE) {
          error_proof_openings_size()
        }

        // PROOF_L_AT_ZETA
        p := add(aproof, PROOF_L_AT_ZETA)
        if gt(calldataload(p), R_MOD_MINUS_ONE) {
          error_proof_openings_size()
        }

        // PROOF_R_AT_ZETA
        p := add(aproof, PROOF_R_AT_ZETA)
        if gt(calldataload(p), R_MOD_MINUS_ONE) {
          error_proof_openings_size()
        }

        // PROOF_O_AT_ZETA
        p := add(aproof, PROOF_O_AT_ZETA)
        if gt(calldataload(p), R_MOD_MINUS_ONE) {
          error_proof_openings_size()
        }

        // PROOF_S1_AT_ZETA
        p := add(aproof, PROOF_S1_AT_ZETA)
        if gt(calldataload(p), R_MOD_MINUS_ONE) {
          error_proof_openings_size()
        }

        // PROOF_S2_AT_ZETA
        p := add(aproof, PROOF_S2_AT_ZETA)
        if gt(calldataload(p), R_MOD_MINUS_ONE) {
          error_proof_openings_size()
        }

        // PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA
        p := add(aproof, PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA)
        if gt(calldataload(p), R_MOD_MINUS_ONE) {
          error_proof_openings_size()
        }

        // PROOF_OPENING_QCP_AT_ZETA

        p := add(aproof, PROOF_OPENING_QCP_AT_ZETA)
        for {
          let i := 0
        } lt(i, VK_NB_CUSTOM_GATES) {
          i := add(i, 1)
        } {
          if gt(calldataload(p), R_MOD_MINUS_ONE) {
            error_proof_openings_size()
          }
          p := add(p, 0x20)
        }
      }
      // end checks -------------------------------------------------

      // Beginning challenges -------------------------------------------------

      /// Derive gamma as Sha256(<transcript>)
      /// @param aproof pointer to the proof
      /// @param nb_pi number of public inputs
      /// @param pi pointer to the array of public inputs
      /// @return the challenge gamma, not reduced
      /// @notice The transcript is the concatenation (in this order) of:
      /// * the word "gamma" in ascii, equal to [0x67,0x61,0x6d, 0x6d, 0x61] and encoded as a uint256.
      /// * the commitments to the permutation polynomials S1, S2, S3, where we concatenate the coordinates of those points
      /// * the commitments of Ql, Qr, Qm, Qo, Qk
      /// * the public inputs
      /// * the commitments of the wires related to the custom gates (commitments_wires_commit_api)
      /// * commitments to L, R, O (proof_<l,r,o>_com_<x,y>)
      /// The data described above is written starting at mPtr. "gamma" lies on 5 bytes,
      /// and is encoded as a uint256 number n. In basis b = 256, the number looks like this
      /// [0 0 0 .. 0x67 0x61 0x6d, 0x6d, 0x61]. The first non zero entry is at position 27=0x1b
      /// Gamma reduced (the actual challenge) is stored at add(state, state_gamma)
      function derive_gamma(aproof, nb_pi, pi) -> gamma_not_reduced {
        let state := mload(0x40)
        let mPtr := add(state, STATE_LAST_MEM)

        // gamma
        // gamma in ascii is [0x67,0x61,0x6d, 0x6d, 0x61]
        // (same for alpha, beta, zeta)
        mstore(mPtr, 0x67616d6d61) // "gamma"

        mstore(add(mPtr, 0x20), VK_S1_COM_X)
        mstore(add(mPtr, 0x40), VK_S1_COM_Y)
        mstore(add(mPtr, 0x60), VK_S2_COM_X)
        mstore(add(mPtr, 0x80), VK_S2_COM_Y)
        mstore(add(mPtr, 0xa0), VK_S3_COM_X)
        mstore(add(mPtr, 0xc0), VK_S3_COM_Y)
        mstore(add(mPtr, 0xe0), VK_QL_COM_X)
        mstore(add(mPtr, 0x100), VK_QL_COM_Y)
        mstore(add(mPtr, 0x120), VK_QR_COM_X)
        mstore(add(mPtr, 0x140), VK_QR_COM_Y)
        mstore(add(mPtr, 0x160), VK_QM_COM_X)
        mstore(add(mPtr, 0x180), VK_QM_COM_Y)
        mstore(add(mPtr, 0x1a0), VK_QO_COM_X)
        mstore(add(mPtr, 0x1c0), VK_QO_COM_Y)
        mstore(add(mPtr, 0x1e0), VK_QK_COM_X)
        mstore(add(mPtr, 0x200), VK_QK_COM_Y)

        mstore(add(mPtr, 0x220), VK_QCP_0_X)
        mstore(add(mPtr, 0x240), VK_QCP_0_Y)

        // public inputs
        let _mPtr := add(mPtr, 0x260)
        let size_pi_in_bytes := mul(nb_pi, 0x20)
        calldatacopy(_mPtr, pi, size_pi_in_bytes)
        _mPtr := add(_mPtr, size_pi_in_bytes)

        // commitments to l, r, o
        let size_commitments_lro_in_bytes := 0xc0
        calldatacopy(_mPtr, aproof, size_commitments_lro_in_bytes)
        _mPtr := add(_mPtr, size_commitments_lro_in_bytes)

        // total size is :
        // sizegamma(=0x5) + 11*64(=0x2c0)
        // + nb_public_inputs*0x20
        // + nb_custom gates*0x40
        let size := add(0x2c5, size_pi_in_bytes)

        size := add(size, mul(VK_NB_CUSTOM_GATES, 0x40))
        let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1b), size, mPtr, 0x20) //0x1b -> 000.."gamma"
        if iszero(l_success) {
          error_verify()
        }
        gamma_not_reduced := mload(mPtr)
        mstore(add(state, STATE_GAMMA), mod(gamma_not_reduced, R_MOD))
      }

      /// derive beta as Sha256<transcript>
      /// @param gamma_not_reduced the previous challenge (gamma) not reduced
      /// @return beta_not_reduced the next challenge, beta, not reduced
      /// @notice the transcript consists of the previous challenge only.
      /// The reduced version of beta is stored at add(state, state_beta)
      function derive_beta(gamma_not_reduced) -> beta_not_reduced {
        let state := mload(0x40)
        let mPtr := add(mload(0x40), STATE_LAST_MEM)

        // beta
        mstore(mPtr, 0x62657461) // "beta"
        mstore(add(mPtr, 0x20), gamma_not_reduced)
        let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1c), 0x24, mPtr, 0x20) //0x1b -> 000.."gamma"
        if iszero(l_success) {
          error_verify()
        }
        beta_not_reduced := mload(mPtr)
        mstore(add(state, STATE_BETA), mod(beta_not_reduced, R_MOD))
      }

      /// derive alpha as sha256<transcript>
      /// @param aproof pointer to the proof object
      /// @param beta_not_reduced the previous challenge (beta) not reduced
      /// @return alpha_not_reduced the next challenge, alpha, not reduced
      /// @notice the transcript consists of the previous challenge (beta)
      /// not reduced, the commitments to the wires associated to the QCP_i,
      /// and the commitment to the grand product polynomial
      function derive_alpha(aproof, beta_not_reduced) -> alpha_not_reduced {
        let state := mload(0x40)
        let mPtr := add(mload(0x40), STATE_LAST_MEM)
        let full_size := 0x65 // size("alpha") + 0x20 (previous challenge)

        // alpha
        mstore(mPtr, 0x616C706861) // "alpha"
        let _mPtr := add(mPtr, 0x20)
        mstore(_mPtr, beta_not_reduced)
        _mPtr := add(_mPtr, 0x20)

        // Bsb22Commitments
        let proof_bsb_commitments := add(aproof, PROOF_COMMITMENTS_WIRES_CUSTOM_GATES)
        let size_bsb_commitments := mul(0x40, VK_NB_CUSTOM_GATES)
        calldatacopy(_mPtr, proof_bsb_commitments, size_bsb_commitments)
        _mPtr := add(_mPtr, size_bsb_commitments)
        full_size := add(full_size, size_bsb_commitments)

        // [Z], the commitment to the grand product polynomial
        calldatacopy(_mPtr, add(aproof, PROOF_GRAND_PRODUCT_COMMITMENT_X), 0x40)
        let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1b), full_size, mPtr, 0x20)
        if iszero(l_success) {
          error_verify()
        }

        alpha_not_reduced := mload(mPtr)
        mstore(add(state, STATE_ALPHA), mod(alpha_not_reduced, R_MOD))
      }

      /// derive zeta as sha256<transcript>
      /// @param aproof pointer to the proof object
      /// @param alpha_not_reduced the previous challenge (alpha) not reduced
      /// The transcript consists of the previous challenge and the commitment to
      /// the quotient polynomial h.
      function derive_zeta(aproof, alpha_not_reduced) {
        let state := mload(0x40)
        let mPtr := add(mload(0x40), STATE_LAST_MEM)

        // zeta
        mstore(mPtr, 0x7a657461) // "zeta"
        mstore(add(mPtr, 0x20), alpha_not_reduced)
        calldatacopy(add(mPtr, 0x40), add(aproof, PROOF_H_0_X), 0xc0)
        let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1c), 0xe4, mPtr, 0x20)
        if iszero(l_success) {
          error_verify()
        }
        let zeta_not_reduced := mload(mPtr)
        mstore(add(state, STATE_ZETA), mod(zeta_not_reduced, R_MOD))
      }
      // END challenges -------------------------------------------------

      // BEGINNING compute_pi -------------------------------------------------

      /// sum_pi_wo_api_commit computes the public inputs contributions,
      /// except for the public inputs coming from the custom gate
      /// @param ins pointer to the public inputs
      /// @param n number of public inputs
      /// @param mPtr free memory
      /// @return pi_wo_commit public inputs contribution (except the public inputs coming from the custom gate)
      function sum_pi_wo_api_commit(ins, n, mPtr) -> pi_wo_commit {
        let state := mload(0x40)
        let z := mload(add(state, STATE_ZETA))
        let zpnmo := mload(add(state, STATE_ZETA_POWER_N_MINUS_ONE))

        let li := mPtr
        batch_compute_lagranges_at_z(z, zpnmo, n, li)

        let tmp := 0
        for {
          let i := 0
        } lt(i, n) {
          i := add(i, 1)
        } {
          tmp := mulmod(mload(li), calldataload(ins), R_MOD)
          pi_wo_commit := addmod(pi_wo_commit, tmp, R_MOD)
          li := add(li, 0x20)
          ins := add(ins, 0x20)
        }
      }

      /// batch_compute_lagranges_at_z computes [L_0(z), .., L_{n-1}(z)]
      /// @param z point at which the Lagranges are evaluated
      /// @param zpnmo ζⁿ-1
      /// @param n number of public inputs (number of Lagranges to compute)
      /// @param mPtr pointer to which the results are stored
      function batch_compute_lagranges_at_z(z, zpnmo, n, mPtr) {
        let zn := mulmod(zpnmo, VK_INV_DOMAIN_SIZE, R_MOD) // 1/n * (ζⁿ - 1)

        let _w := 1
        let _mPtr := mPtr
        for {
          let i := 0
        } lt(i, n) {
          i := add(i, 1)
        } {
          mstore(_mPtr, addmod(z, sub(R_MOD, _w), R_MOD))
          _w := mulmod(_w, VK_OMEGA, R_MOD)
          _mPtr := add(_mPtr, 0x20)
        }
        batch_invert(mPtr, n, _mPtr)
        _mPtr := mPtr
        _w := 1
        for {
          let i := 0
        } lt(i, n) {
          i := add(i, 1)
        } {
          mstore(_mPtr, mulmod(mulmod(mload(_mPtr), zn, R_MOD), _w, R_MOD))
          _mPtr := add(_mPtr, 0x20)
          _w := mulmod(_w, VK_OMEGA, R_MOD)
        }
      }

      /// @notice Montgomery trick for batch inversion mod R_MOD
      /// @param ins pointer to the data to batch invert
      /// @param number of elements to batch invert
      /// @param mPtr free memory
      function batch_invert(ins, nb_ins, mPtr) {
        mstore(mPtr, 1)
        let offset := 0
        for {
          let i := 0
        } lt(i, nb_ins) {
          i := add(i, 1)
        } {
          let prev := mload(add(mPtr, offset))
          let cur := mload(add(ins, offset))
          cur := mulmod(prev, cur, R_MOD)
          offset := add(offset, 0x20)
          mstore(add(mPtr, offset), cur)
        }
        ins := add(ins, sub(offset, 0x20))
        mPtr := add(mPtr, offset)
        let inv := pow(mload(mPtr), sub(R_MOD, 2), add(mPtr, 0x20))
        for {
          let i := 0
        } lt(i, nb_ins) {
          i := add(i, 1)
        } {
          mPtr := sub(mPtr, 0x20)
          let tmp := mload(ins)
          let cur := mulmod(inv, mload(mPtr), R_MOD)
          mstore(ins, cur)
          inv := mulmod(inv, tmp, R_MOD)
          ins := sub(ins, 0x20)
        }
      }

      /// Public inputs (the ones coming from the custom gate) contribution
      /// @param aproof pointer to the proof
      /// @param nb_public_inputs number of public inputs
      /// @param mPtr pointer to free memory
      /// @return pi_commit custom gate public inputs contribution
      function sum_pi_commit(aproof, nb_public_inputs, mPtr) -> pi_commit {
        let state := mload(0x40)
        let z := mload(add(state, STATE_ZETA))
        let zpnmo := mload(add(state, STATE_ZETA_POWER_N_MINUS_ONE))

        let p := add(aproof, PROOF_COMMITMENTS_WIRES_CUSTOM_GATES)

        let h_fr, ith_lagrange

        h_fr := hash_fr(calldataload(p), calldataload(add(p, 0x20)), mPtr)
        ith_lagrange := compute_ith_lagrange_at_z(z, zpnmo, add(nb_public_inputs, VK_INDEX_COMMIT_API0), mPtr)
        pi_commit := addmod(pi_commit, mulmod(h_fr, ith_lagrange, R_MOD), R_MOD)
        p := add(p, 0x40)
      }

      /// Computes L_i(zeta) =  ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where:
      /// @param z zeta
      /// @param zpmno ζⁿ-1
      /// @param i i-th lagrange
      /// @param mPtr free memory
      /// @return res = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ)
      function compute_ith_lagrange_at_z(z, zpnmo, i, mPtr) -> res {
        let w := pow(VK_OMEGA, i, mPtr) // w**i
        i := addmod(z, sub(R_MOD, w), R_MOD) // z-w**i
        w := mulmod(w, VK_INV_DOMAIN_SIZE, R_MOD) // w**i/n
        i := pow(i, sub(R_MOD, 2), mPtr) // (z-w**i)**-1
        w := mulmod(w, i, R_MOD) // w**i/n*(z-w)**-1
        res := mulmod(w, zpnmo, R_MOD)
      }

      /// @dev https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-5.2
      /// @param x x coordinate of a point on Bn254(𝔽_p)
      /// @param y y coordinate of a point on Bn254(𝔽_p)
      /// @param mPtr free memory
      /// @return res an element mod R_MOD
      function hash_fr(x, y, mPtr) -> res {
        // [0x00, .. , 0x00 || x, y, || 0, 48, 0, dst, HASH_FR_SIZE_DOMAIN]
        // <-  64 bytes  ->  <-64b -> <-       1 bytes each     ->

        // [0x00, .., 0x00] 64 bytes of zero
        mstore(mPtr, HASH_FR_ZERO_UINT256)
        mstore(add(mPtr, 0x20), HASH_FR_ZERO_UINT256)

        // msg =  x || y , both on 32 bytes
        mstore(add(mPtr, 0x40), x)
        mstore(add(mPtr, 0x60), y)

        // 0 || 48 || 0 all on 1 byte
        mstore8(add(mPtr, 0x80), 0)
        mstore8(add(mPtr, 0x81), HASH_FR_LEN_IN_BYTES)
        mstore8(add(mPtr, 0x82), 0)

        // "BSB22-Plonk" = [42, 53, 42, 32, 32, 2d, 50, 6c, 6f, 6e, 6b,]
        mstore8(add(mPtr, 0x83), 0x42)
        mstore8(add(mPtr, 0x84), 0x53)
        mstore8(add(mPtr, 0x85), 0x42)
        mstore8(add(mPtr, 0x86), 0x32)
        mstore8(add(mPtr, 0x87), 0x32)
        mstore8(add(mPtr, 0x88), 0x2d)
        mstore8(add(mPtr, 0x89), 0x50)
        mstore8(add(mPtr, 0x8a), 0x6c)
        mstore8(add(mPtr, 0x8b), 0x6f)
        mstore8(add(mPtr, 0x8c), 0x6e)
        mstore8(add(mPtr, 0x8d), 0x6b)

        // size domain
        mstore8(add(mPtr, 0x8e), HASH_FR_SIZE_DOMAIN)

        let l_success := staticcall(gas(), 0x2, mPtr, 0x8f, mPtr, 0x20)
        if iszero(l_success) {
          error_verify()
        }

        let b0 := mload(mPtr)

        // [b0         || one || dst || HASH_FR_SIZE_DOMAIN]
        // <-64bytes ->  <-    1 byte each      ->
        mstore8(add(mPtr, 0x20), HASH_FR_ONE) // 1

        mstore8(add(mPtr, 0x21), 0x42) // dst
        mstore8(add(mPtr, 0x22), 0x53)
        mstore8(add(mPtr, 0x23), 0x42)
        mstore8(add(mPtr, 0x24), 0x32)
        mstore8(add(mPtr, 0x25), 0x32)
        mstore8(add(mPtr, 0x26), 0x2d)
        mstore8(add(mPtr, 0x27), 0x50)
        mstore8(add(mPtr, 0x28), 0x6c)
        mstore8(add(mPtr, 0x29), 0x6f)
        mstore8(add(mPtr, 0x2a), 0x6e)
        mstore8(add(mPtr, 0x2b), 0x6b)

        mstore8(add(mPtr, 0x2c), HASH_FR_SIZE_DOMAIN) // size domain
        l_success := staticcall(gas(), 0x2, mPtr, 0x2d, mPtr, 0x20)
        if iszero(l_success) {
          error_verify()
        }

        // b1 is located at mPtr. We store b2 at add(mPtr, 0x20)

        // [b0^b1      || two || dst || HASH_FR_SIZE_DOMAIN]
        // <-64bytes ->  <-    1 byte each      ->
        mstore(add(mPtr, 0x20), xor(mload(mPtr), b0))
        mstore8(add(mPtr, 0x40), HASH_FR_TWO)

        mstore8(add(mPtr, 0x41), 0x42) // dst
        mstore8(add(mPtr, 0x42), 0x53)
        mstore8(add(mPtr, 0x43), 0x42)
        mstore8(add(mPtr, 0x44), 0x32)
        mstore8(add(mPtr, 0x45), 0x32)
        mstore8(add(mPtr, 0x46), 0x2d)
        mstore8(add(mPtr, 0x47), 0x50)
        mstore8(add(mPtr, 0x48), 0x6c)
        mstore8(add(mPtr, 0x49), 0x6f)
        mstore8(add(mPtr, 0x4a), 0x6e)
        mstore8(add(mPtr, 0x4b), 0x6b)

        mstore8(add(mPtr, 0x4c), HASH_FR_SIZE_DOMAIN) // size domain

        let offset := add(mPtr, 0x20)
        l_success := staticcall(gas(), 0x2, offset, 0x2d, offset, 0x20)
        if iszero(l_success) {
          error_verify()
        }

        // at this point we have mPtr = [ b1 || b2] where b1 is on 32byes and b2 in 16bytes.
        // we interpret it as a big integer mod r in big endian (similar to regular decimal notation)
        // the result is then 2**(8*16)*mPtr[32:] + mPtr[32:48]
        res := mulmod(mload(mPtr), HASH_FR_BB, R_MOD) // <- res = 2**128 * mPtr[:32]
        let b1 := shr(128, mload(add(mPtr, 0x20))) // b1 <- [0, 0, .., 0 ||  b2[:16] ]
        res := addmod(res, b1, R_MOD)
      }

      // END compute_pi -------------------------------------------------

      /// @notice compute α² * 1/n * (ζ{n}-1)/(ζ - 1) where
      /// *  α = challenge derived in derive_gamma_beta_alpha_zeta
      /// * n = vk_domain_size
      /// * ω = vk_omega (generator of the multiplicative cyclic group of order n in (ℤ/rℤ)*)
      /// * ζ = zeta (challenge derived with Fiat Shamir)
      function compute_alpha_square_lagrange_0() {
        let state := mload(0x40)
        let mPtr := add(mload(0x40), STATE_LAST_MEM)

        let res := mload(add(state, STATE_ZETA_POWER_N_MINUS_ONE))
        let den := addmod(mload(add(state, STATE_ZETA)), sub(R_MOD, 1), R_MOD)
        den := pow(den, sub(R_MOD, 2), mPtr)
        den := mulmod(den, VK_INV_DOMAIN_SIZE, R_MOD)
        res := mulmod(den, res, R_MOD)

        let l_alpha := mload(add(state, STATE_ALPHA))
        res := mulmod(res, l_alpha, R_MOD)
        res := mulmod(res, l_alpha, R_MOD)
        mstore(add(state, STATE_ALPHA_SQUARE_LAGRANGE_0), res)
      }

      /// @notice follows alg. p.13 of https://eprint.iacr.org/2019/953.pdf
      /// with t₁ = t₂ = 1, and the proofs are ([digest] + [quotient] +purported evaluation):
      /// * [state_folded_state_digests], [proof_batch_opening_at_zeta_x], state_folded_evals
      /// * [proof_grand_product_commitment], [proof_opening_at_zeta_omega_x], [proof_grand_product_at_zeta_omega]
      /// @param aproof pointer to the proof
      function batch_verify_multi_points(aproof) {
        let state := mload(0x40)
        let mPtr := add(state, STATE_LAST_MEM)

        // derive a random number. As there is no random generator, we
        // do an FS like challenge derivation, depending on both digests and
        // ζ to ensure that the prover cannot control the random numger.
        // Note: adding the other point ζω is not needed, as ω is known beforehand.
        mstore(mPtr, mload(add(state, STATE_FOLDED_DIGESTS_X)))
        mstore(add(mPtr, 0x20), mload(add(state, STATE_FOLDED_DIGESTS_Y)))
        mstore(add(mPtr, 0x40), calldataload(add(aproof, PROOF_BATCH_OPENING_AT_ZETA_X)))
        mstore(add(mPtr, 0x60), calldataload(add(aproof, PROOF_BATCH_OPENING_AT_ZETA_Y)))
        mstore(add(mPtr, 0x80), calldataload(add(aproof, PROOF_GRAND_PRODUCT_COMMITMENT_X)))
        mstore(add(mPtr, 0xa0), calldataload(add(aproof, PROOF_GRAND_PRODUCT_COMMITMENT_Y)))
        mstore(add(mPtr, 0xc0), calldataload(add(aproof, PROOF_OPENING_AT_ZETA_OMEGA_X)))
        mstore(add(mPtr, 0xe0), calldataload(add(aproof, PROOF_OPENING_AT_ZETA_OMEGA_Y)))
        mstore(add(mPtr, 0x100), mload(add(state, STATE_ZETA)))
        mstore(add(mPtr, 0x120), mload(add(state, STATE_GAMMA_KZG)))
        let random := staticcall(gas(), 0x2, mPtr, 0x140, mPtr, 0x20)
        if iszero(random) {
          error_random_generation()
        }
        random := mod(mload(mPtr), R_MOD) // use the same variable as we are one variable away from getting stack-too-deep error...

        let folded_quotients := mPtr
        mPtr := add(folded_quotients, 0x40)
        mstore(folded_quotients, calldataload(add(aproof, PROOF_BATCH_OPENING_AT_ZETA_X)))
        mstore(add(folded_quotients, 0x20), calldataload(add(aproof, PROOF_BATCH_OPENING_AT_ZETA_Y)))
        point_acc_mul_calldata(folded_quotients, add(aproof, PROOF_OPENING_AT_ZETA_OMEGA_X), random, mPtr)

        let folded_digests := add(state, STATE_FOLDED_DIGESTS_X)
        point_acc_mul_calldata(folded_digests, add(aproof, PROOF_GRAND_PRODUCT_COMMITMENT_X), random, mPtr)

        let folded_evals := add(state, STATE_FOLDED_CLAIMED_VALUES)
        fr_acc_mul_calldata(folded_evals, add(aproof, PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA), random)

        let folded_evals_commit := mPtr
        mPtr := add(folded_evals_commit, 0x40)
        mstore(folded_evals_commit, G1_SRS_X)
        mstore(add(folded_evals_commit, 0x20), G1_SRS_Y)
        mstore(add(folded_evals_commit, 0x40), mload(folded_evals))
        let check_staticcall := staticcall(gas(), 7, folded_evals_commit, 0x60, folded_evals_commit, 0x40)
        if iszero(check_staticcall) {
          error_verify()
        }

        let folded_evals_commit_y := add(folded_evals_commit, 0x20)
        mstore(folded_evals_commit_y, sub(P_MOD, mload(folded_evals_commit_y)))
        point_add(folded_digests, folded_digests, folded_evals_commit, mPtr)

        let folded_points_quotients := mPtr
        mPtr := add(mPtr, 0x40)
        point_mul_calldata(
          folded_points_quotients,
          add(aproof, PROOF_BATCH_OPENING_AT_ZETA_X),
          mload(add(state, STATE_ZETA)),
          mPtr
        )
        let zeta_omega := mulmod(mload(add(state, STATE_ZETA)), VK_OMEGA, R_MOD)
        random := mulmod(random, zeta_omega, R_MOD)
        point_acc_mul_calldata(folded_points_quotients, add(aproof, PROOF_OPENING_AT_ZETA_OMEGA_X), random, mPtr)

        point_add(folded_digests, folded_digests, folded_points_quotients, mPtr)

        let folded_quotients_y := add(folded_quotients, 0x20)
        mstore(folded_quotients_y, sub(P_MOD, mload(folded_quotients_y)))

        mstore(mPtr, mload(folded_digests))
        mstore(add(mPtr, 0x20), mload(add(folded_digests, 0x20)))
        mstore(add(mPtr, 0x40), G2_SRS_0_X_0) // the 4 lines are the canonical G2 point on BN254
        mstore(add(mPtr, 0x60), G2_SRS_0_X_1)
        mstore(add(mPtr, 0x80), G2_SRS_0_Y_0)
        mstore(add(mPtr, 0xa0), G2_SRS_0_Y_1)
        mstore(add(mPtr, 0xc0), mload(folded_quotients))
        mstore(add(mPtr, 0xe0), mload(add(folded_quotients, 0x20)))
        mstore(add(mPtr, 0x100), G2_SRS_1_X_0)
        mstore(add(mPtr, 0x120), G2_SRS_1_X_1)
        mstore(add(mPtr, 0x140), G2_SRS_1_Y_0)
        mstore(add(mPtr, 0x160), G2_SRS_1_Y_1)
        check_pairing_kzg(mPtr)
      }

      /// @notice check_pairing_kzg checks the result of the final pairing product of the batched
      /// kzg verification. The purpose of this function is to avoid exhausting the stack
      /// in the function batch_verify_multi_points.
      /// @param mPtr pointer storing the tuple of pairs
      function check_pairing_kzg(mPtr) {
        let state := mload(0x40)

        // TODO test the staticcall using the method from audit_4-5
        let l_success := staticcall(gas(), 8, mPtr, 0x180, 0x00, 0x20)
        let res_pairing := mload(0x00)
        let s_success := mload(add(state, STATE_SUCCESS))
        res_pairing := and(and(res_pairing, l_success), s_success)
        mstore(add(state, STATE_SUCCESS), res_pairing)
      }

      /// @notice Fold the opening proofs at ζ:
      /// * at state+state_folded_digest we store: [H] + γ[Linearised_polynomial]+γ²[L] + γ³[R] + γ⁴[O] + γ⁵[S₁] +γ⁶[S₂] + ∑ᵢγ⁶⁺ⁱ[Pi_{i}]
      /// * at state+state_folded_claimed_values we store: H(ζ) + γLinearised_polynomial(ζ)+γ²L(ζ) + γ³R(ζ)+ γ⁴O(ζ) + γ⁵S₁(ζ) +γ⁶S₂(ζ) + ∑ᵢγ⁶⁺ⁱPi_{i}(ζ)
      /// @param aproof pointer to the proof
      /// acc_gamma stores the γⁱ
      function fold_state(aproof) {
        let state := mload(0x40)
        let mPtr := add(mload(0x40), STATE_LAST_MEM)
        let mPtr20 := add(mPtr, 0x20)
        let mPtr40 := add(mPtr, 0x40)

        let l_gamma_kzg := mload(add(state, STATE_GAMMA_KZG))
        let acc_gamma := l_gamma_kzg
        let state_folded_digests := add(state, STATE_FOLDED_DIGESTS_X)

        mstore(add(state, STATE_FOLDED_DIGESTS_X), mload(add(state, STATE_FOLDED_H_X)))
        mstore(add(state, STATE_FOLDED_DIGESTS_Y), mload(add(state, STATE_FOLDED_H_Y)))
        mstore(add(state, STATE_FOLDED_CLAIMED_VALUES), calldataload(add(aproof, PROOF_QUOTIENT_POLYNOMIAL_AT_ZETA)))

        point_acc_mul(state_folded_digests, add(state, STATE_LINEARISED_POLYNOMIAL_X), acc_gamma, mPtr)
        fr_acc_mul_calldata(
          add(state, STATE_FOLDED_CLAIMED_VALUES),
          add(aproof, PROOF_LINEARISED_POLYNOMIAL_AT_ZETA),
          acc_gamma
        )

        acc_gamma := mulmod(acc_gamma, l_gamma_kzg, R_MOD)
        point_acc_mul_calldata(add(state, STATE_FOLDED_DIGESTS_X), add(aproof, PROOF_L_COM_X), acc_gamma, mPtr)
        fr_acc_mul_calldata(add(state, STATE_FOLDED_CLAIMED_VALUES), add(aproof, PROOF_L_AT_ZETA), acc_gamma)

        acc_gamma := mulmod(acc_gamma, l_gamma_kzg, R_MOD)
        point_acc_mul_calldata(state_folded_digests, add(aproof, PROOF_R_COM_X), acc_gamma, mPtr)
        fr_acc_mul_calldata(add(state, STATE_FOLDED_CLAIMED_VALUES), add(aproof, PROOF_R_AT_ZETA), acc_gamma)

        acc_gamma := mulmod(acc_gamma, l_gamma_kzg, R_MOD)
        point_acc_mul_calldata(state_folded_digests, add(aproof, PROOF_O_COM_X), acc_gamma, mPtr)
        fr_acc_mul_calldata(add(state, STATE_FOLDED_CLAIMED_VALUES), add(aproof, PROOF_O_AT_ZETA), acc_gamma)

        acc_gamma := mulmod(acc_gamma, l_gamma_kzg, R_MOD)
        mstore(mPtr, VK_S1_COM_X)
        mstore(mPtr20, VK_S1_COM_Y)
        point_acc_mul(state_folded_digests, mPtr, acc_gamma, mPtr40)
        fr_acc_mul_calldata(add(state, STATE_FOLDED_CLAIMED_VALUES), add(aproof, PROOF_S1_AT_ZETA), acc_gamma)

        acc_gamma := mulmod(acc_gamma, l_gamma_kzg, R_MOD)
        mstore(mPtr, VK_S2_COM_X)
        mstore(mPtr20, VK_S2_COM_Y)
        point_acc_mul(state_folded_digests, mPtr, acc_gamma, mPtr40)
        fr_acc_mul_calldata(add(state, STATE_FOLDED_CLAIMED_VALUES), add(aproof, PROOF_S2_AT_ZETA), acc_gamma)
        let poscaz := add(aproof, PROOF_OPENING_QCP_AT_ZETA)

        acc_gamma := mulmod(acc_gamma, l_gamma_kzg, R_MOD)
        mstore(mPtr, VK_QCP_0_X)
        mstore(mPtr20, VK_QCP_0_Y)
        point_acc_mul(state_folded_digests, mPtr, acc_gamma, mPtr40)
        fr_acc_mul_calldata(add(state, STATE_FOLDED_CLAIMED_VALUES), poscaz, acc_gamma)
        poscaz := add(poscaz, 0x20)
      }

      /// @notice generate the challenge (using Fiat Shamir) to fold the opening proofs
      /// at ζ.
      /// The process for deriving γ is the same as in derive_gamma but this time the inputs are
      /// in this order (the [] means it's a commitment):
      /// * ζ
      /// * [H] ( = H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ )
      /// * [Linearised polynomial]
      /// * [L], [R], [O]
      /// * [S₁] [S₂]
      /// * [Pi_{i}] (wires associated to custom gates)
      /// Then there are the purported evaluations of the previous committed polynomials:
      /// * H(ζ)
      /// * Linearised_polynomial(ζ)
      /// * L(ζ), R(ζ), O(ζ), S₁(ζ), S₂(ζ)
      /// * Pi_{i}(ζ)
      /// * Z(ζω)
      /// @param aproof pointer to the proof
      function compute_gamma_kzg(aproof) {
        let state := mload(0x40)
        let mPtr := add(mload(0x40), STATE_LAST_MEM)
        mstore(mPtr, 0x67616d6d61) // "gamma"
        mstore(add(mPtr, 0x20), mload(add(state, STATE_ZETA)))
        mstore(add(mPtr, 0x40), mload(add(state, STATE_FOLDED_H_X)))
        mstore(add(mPtr, 0x60), mload(add(state, STATE_FOLDED_H_Y)))
        mstore(add(mPtr, 0x80), mload(add(state, STATE_LINEARISED_POLYNOMIAL_X)))
        mstore(add(mPtr, 0xa0), mload(add(state, STATE_LINEARISED_POLYNOMIAL_Y)))
        calldatacopy(add(mPtr, 0xc0), add(aproof, PROOF_L_COM_X), 0xc0)
        mstore(add(mPtr, 0x180), VK_S1_COM_X)
        mstore(add(mPtr, 0x1a0), VK_S1_COM_Y)
        mstore(add(mPtr, 0x1c0), VK_S2_COM_X)
        mstore(add(mPtr, 0x1e0), VK_S2_COM_Y)

        let offset := 0x200

        mstore(add(mPtr, offset), VK_QCP_0_X)
        mstore(add(mPtr, add(offset, 0x20)), VK_QCP_0_Y)
        offset := add(offset, 0x40)

        mstore(add(mPtr, offset), calldataload(add(aproof, PROOF_QUOTIENT_POLYNOMIAL_AT_ZETA)))
        mstore(add(mPtr, add(offset, 0x20)), calldataload(add(aproof, PROOF_LINEARISED_POLYNOMIAL_AT_ZETA)))
        mstore(add(mPtr, add(offset, 0x40)), calldataload(add(aproof, PROOF_L_AT_ZETA)))
        mstore(add(mPtr, add(offset, 0x60)), calldataload(add(aproof, PROOF_R_AT_ZETA)))
        mstore(add(mPtr, add(offset, 0x80)), calldataload(add(aproof, PROOF_O_AT_ZETA)))
        mstore(add(mPtr, add(offset, 0xa0)), calldataload(add(aproof, PROOF_S1_AT_ZETA)))
        mstore(add(mPtr, add(offset, 0xc0)), calldataload(add(aproof, PROOF_S2_AT_ZETA)))

        let _mPtr := add(mPtr, add(offset, 0xe0))

        let _poscaz := add(aproof, PROOF_OPENING_QCP_AT_ZETA)
        for {
          let i := 0
        } lt(i, VK_NB_CUSTOM_GATES) {
          i := add(i, 1)
        } {
          mstore(_mPtr, calldataload(_poscaz))
          _poscaz := add(_poscaz, 0x20)
          _mPtr := add(_mPtr, 0x20)
        }

        mstore(_mPtr, calldataload(add(aproof, PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA)))

        let start_input := 0x1b // 00.."gamma"
        let size_input := add(0x17, mul(VK_NB_CUSTOM_GATES, 3)) // number of 32bytes elmts = 0x17 (zeta+2*7+7 for the digests+openings) + 2*VK_NB_CUSTOM_GATES (for the commitments of the selectors) + VK_NB_CUSTOM_GATES (for the openings of the selectors)
        size_input := add(0x5, mul(size_input, 0x20)) // size in bytes: 15*32 bytes + 5 bytes for gamma
        let check_staticcall := staticcall(
          gas(),
          0x2,
          add(mPtr, start_input),
          size_input,
          add(state, STATE_GAMMA_KZG),
          0x20
        )
        if iszero(check_staticcall) {
          error_verify()
        }
        mstore(add(state, STATE_GAMMA_KZG), mod(mload(add(state, STATE_GAMMA_KZG)), R_MOD))
      }

      function compute_commitment_linearised_polynomial_ec(aproof, s1, s2) {
        let state := mload(0x40)
        let mPtr := add(mload(0x40), STATE_LAST_MEM)

        mstore(mPtr, VK_QL_COM_X)
        mstore(add(mPtr, 0x20), VK_QL_COM_Y)
        point_mul(
          add(state, STATE_LINEARISED_POLYNOMIAL_X),
          mPtr,
          calldataload(add(aproof, PROOF_L_AT_ZETA)),
          add(mPtr, 0x40)
        )

        mstore(mPtr, VK_QR_COM_X)
        mstore(add(mPtr, 0x20), VK_QR_COM_Y)
        point_acc_mul(
          add(state, STATE_LINEARISED_POLYNOMIAL_X),
          mPtr,
          calldataload(add(aproof, PROOF_R_AT_ZETA)),
          add(mPtr, 0x40)
        )

        let rl := mulmod(calldataload(add(aproof, PROOF_L_AT_ZETA)), calldataload(add(aproof, PROOF_R_AT_ZETA)), R_MOD)
        mstore(mPtr, VK_QM_COM_X)
        mstore(add(mPtr, 0x20), VK_QM_COM_Y)
        point_acc_mul(add(state, STATE_LINEARISED_POLYNOMIAL_X), mPtr, rl, add(mPtr, 0x40))

        mstore(mPtr, VK_QO_COM_X)
        mstore(add(mPtr, 0x20), VK_QO_COM_Y)
        point_acc_mul(
          add(state, STATE_LINEARISED_POLYNOMIAL_X),
          mPtr,
          calldataload(add(aproof, PROOF_O_AT_ZETA)),
          add(mPtr, 0x40)
        )

        mstore(mPtr, VK_QK_COM_X)
        mstore(add(mPtr, 0x20), VK_QK_COM_Y)
        point_add(
          add(state, STATE_LINEARISED_POLYNOMIAL_X),
          add(state, STATE_LINEARISED_POLYNOMIAL_X),
          mPtr,
          add(mPtr, 0x40)
        )

        let commits_api_at_zeta := add(aproof, PROOF_OPENING_QCP_AT_ZETA)
        let commits_api := add(aproof, PROOF_COMMITMENTS_WIRES_CUSTOM_GATES)
        for {
          let i := 0
        } lt(i, VK_NB_CUSTOM_GATES) {
          i := add(i, 1)
        } {
          mstore(mPtr, calldataload(commits_api))
          mstore(add(mPtr, 0x20), calldataload(add(commits_api, 0x20)))
          point_acc_mul(
            add(state, STATE_LINEARISED_POLYNOMIAL_X),
            mPtr,
            calldataload(commits_api_at_zeta),
            add(mPtr, 0x40)
          )
          commits_api_at_zeta := add(commits_api_at_zeta, 0x20)
          commits_api := add(commits_api, 0x40)
        }

        mstore(mPtr, VK_S3_COM_X)
        mstore(add(mPtr, 0x20), VK_S3_COM_Y)
        point_acc_mul(add(state, STATE_LINEARISED_POLYNOMIAL_X), mPtr, s1, add(mPtr, 0x40))

        mstore(mPtr, calldataload(add(aproof, PROOF_GRAND_PRODUCT_COMMITMENT_X)))
        mstore(add(mPtr, 0x20), calldataload(add(aproof, PROOF_GRAND_PRODUCT_COMMITMENT_Y)))
        point_acc_mul(add(state, STATE_LINEARISED_POLYNOMIAL_X), mPtr, s2, add(mPtr, 0x40))
      }

      /// @notice Compute the commitment to the linearized polynomial equal to
      ///	L(ζ)[Qₗ]+r(ζ)[Qᵣ]+R(ζ)L(ζ)[Qₘ]+O(ζ)[Qₒ]+[Qₖ]+Σᵢqc'ᵢ(ζ)[BsbCommitmentᵢ] +
      ///	α*( Z(μζ)(L(ζ)+β*S₁(ζ)+γ)*(R(ζ)+β*S₂(ζ)+γ)[S₃]-[Z](L(ζ)+β*id_{1}(ζ)+γ)*(R(ζ)+β*id_{2(ζ)+γ)*(O(ζ)+β*id_{3}(ζ)+γ) ) +
      ///	α²*L₁(ζ)[Z]
      /// where
      /// * id_1 = id, id_2 = vk_coset_shift*id, id_3 = vk_coset_shift^{2}*id
      /// * the [] means that it's a commitment (i.e. a point on Bn254(F_p))
      /// @param aproof pointer to the proof
      function compute_commitment_linearised_polynomial(aproof) {
        let state := mload(0x40)
        let l_beta := mload(add(state, STATE_BETA))
        let l_gamma := mload(add(state, STATE_GAMMA))
        let l_zeta := mload(add(state, STATE_ZETA))
        let l_alpha := mload(add(state, STATE_ALPHA))

        let u := mulmod(calldataload(add(aproof, PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA)), l_beta, R_MOD)
        let v := mulmod(l_beta, calldataload(add(aproof, PROOF_S1_AT_ZETA)), R_MOD)
        v := addmod(v, calldataload(add(aproof, PROOF_L_AT_ZETA)), R_MOD)
        v := addmod(v, l_gamma, R_MOD)

        let w := mulmod(l_beta, calldataload(add(aproof, PROOF_S2_AT_ZETA)), R_MOD)
        w := addmod(w, calldataload(add(aproof, PROOF_R_AT_ZETA)), R_MOD)
        w := addmod(w, l_gamma, R_MOD)

        let s1 := mulmod(u, v, R_MOD)
        s1 := mulmod(s1, w, R_MOD)
        s1 := mulmod(s1, l_alpha, R_MOD)

        let coset_square := mulmod(VK_COSET_SHIFT, VK_COSET_SHIFT, R_MOD)
        let betazeta := mulmod(l_beta, l_zeta, R_MOD)
        u := addmod(betazeta, calldataload(add(aproof, PROOF_L_AT_ZETA)), R_MOD)
        u := addmod(u, l_gamma, R_MOD)

        v := mulmod(betazeta, VK_COSET_SHIFT, R_MOD)
        v := addmod(v, calldataload(add(aproof, PROOF_R_AT_ZETA)), R_MOD)
        v := addmod(v, l_gamma, R_MOD)

        w := mulmod(betazeta, coset_square, R_MOD)
        w := addmod(w, calldataload(add(aproof, PROOF_O_AT_ZETA)), R_MOD)
        w := addmod(w, l_gamma, R_MOD)

        let s2 := mulmod(u, v, R_MOD)
        s2 := mulmod(s2, w, R_MOD)
        s2 := sub(R_MOD, s2)
        s2 := mulmod(s2, l_alpha, R_MOD)
        s2 := addmod(s2, mload(add(state, STATE_ALPHA_SQUARE_LAGRANGE_0)), R_MOD)

        // at this stage:
        // * s₁ = α*Z(μζ)(l(ζ)+β*s₁(ζ)+γ)*(r(ζ)+β*s₂(ζ)+γ)*β
        // * s₂ = -α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) + α²*L₁(ζ)

        compute_commitment_linearised_polynomial_ec(aproof, s1, s2)
      }

      /// @notice compute H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ and store the result at
      /// state + state_folded_h
      /// @param aproof pointer to the proof
      function fold_h(aproof) {
        let state := mload(0x40)
        let n_plus_two := add(VK_DOMAIN_SIZE, 2)
        let mPtr := add(mload(0x40), STATE_LAST_MEM)
        let zeta_power_n_plus_two := pow(mload(add(state, STATE_ZETA)), n_plus_two, mPtr)
        point_mul_calldata(add(state, STATE_FOLDED_H_X), add(aproof, PROOF_H_2_X), zeta_power_n_plus_two, mPtr)
        point_add_calldata(add(state, STATE_FOLDED_H_X), add(state, STATE_FOLDED_H_X), add(aproof, PROOF_H_1_X), mPtr)
        point_mul(add(state, STATE_FOLDED_H_X), add(state, STATE_FOLDED_H_X), zeta_power_n_plus_two, mPtr)
        point_add_calldata(add(state, STATE_FOLDED_H_X), add(state, STATE_FOLDED_H_X), add(aproof, PROOF_H_0_X), mPtr)
      }

      /// @notice check that
      ///	L(ζ)Qₗ(ζ)+r(ζ)Qᵣ(ζ)+R(ζ)L(ζ)Qₘ(ζ)+O(ζ)Qₒ(ζ)+Qₖ(ζ)+Σᵢqc'ᵢ(ζ)BsbCommitmentᵢ(ζ) +
      ///  α*( Z(μζ)(l(ζ)+β*s₁(ζ)+γ)*(r(ζ)+β*s₂(ζ)+γ)*β*s₃(X)-Z(X)(l(ζ)+β*id_1(ζ)+γ)*(r(ζ)+β*id_2(ζ)+γ)*(o(ζ)+β*id_3(ζ)+γ) ) )
      /// + α²*L₁(ζ) =
      /// (ζⁿ-1)H(ζ)
      /// @param aproof pointer to the proof
      function verify_quotient_poly_eval_at_zeta(aproof) {
        let state := mload(0x40)

        // (l(ζ)+β*s1(ζ)+γ)
        let s1 := add(mload(0x40), STATE_LAST_MEM)
        mstore(s1, mulmod(calldataload(add(aproof, PROOF_S1_AT_ZETA)), mload(add(state, STATE_BETA)), R_MOD))
        mstore(s1, addmod(mload(s1), mload(add(state, STATE_GAMMA)), R_MOD))
        mstore(s1, addmod(mload(s1), calldataload(add(aproof, PROOF_L_AT_ZETA)), R_MOD))

        // (r(ζ)+β*s2(ζ)+γ)
        let s2 := add(s1, 0x20)
        mstore(s2, mulmod(calldataload(add(aproof, PROOF_S2_AT_ZETA)), mload(add(state, STATE_BETA)), R_MOD))
        mstore(s2, addmod(mload(s2), mload(add(state, STATE_GAMMA)), R_MOD))
        mstore(s2, addmod(mload(s2), calldataload(add(aproof, PROOF_R_AT_ZETA)), R_MOD))
        // _s2 := mload(s2)

        // (o(ζ)+γ)
        let o := add(s1, 0x40)
        mstore(o, addmod(calldataload(add(aproof, PROOF_O_AT_ZETA)), mload(add(state, STATE_GAMMA)), R_MOD))

        //  α*(Z(μζ))*(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(o(ζ)+γ)
        mstore(s1, mulmod(mload(s1), mload(s2), R_MOD))
        mstore(s1, mulmod(mload(s1), mload(o), R_MOD))
        mstore(s1, mulmod(mload(s1), mload(add(state, STATE_ALPHA)), R_MOD))
        mstore(s1, mulmod(mload(s1), calldataload(add(aproof, PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA)), R_MOD))

        let computed_quotient := add(s1, 0x60)

        // linearizedpolynomial + pi(zeta)
        mstore(
          computed_quotient,
          addmod(calldataload(add(aproof, PROOF_LINEARISED_POLYNOMIAL_AT_ZETA)), mload(add(state, STATE_PI)), R_MOD)
        )
        mstore(computed_quotient, addmod(mload(computed_quotient), mload(s1), R_MOD))
        mstore(
          computed_quotient,
          addmod(mload(computed_quotient), sub(R_MOD, mload(add(state, STATE_ALPHA_SQUARE_LAGRANGE_0))), R_MOD)
        )
        mstore(
          s2,
          mulmod(
            calldataload(add(aproof, PROOF_QUOTIENT_POLYNOMIAL_AT_ZETA)),
            mload(add(state, STATE_ZETA_POWER_N_MINUS_ONE)),
            R_MOD
          )
        )

        mstore(add(state, STATE_SUCCESS), eq(mload(computed_quotient), mload(s2)))
      }

      // BEGINNING utils math functions -------------------------------------------------

      /// @param dst pointer storing the result
      /// @param p pointer to the first point
      /// @param q pointer to the second point
      /// @param mPtr pointer to free memory
      function point_add(dst, p, q, mPtr) {
        let state := mload(0x40)
        mstore(mPtr, mload(p))
        mstore(add(mPtr, 0x20), mload(add(p, 0x20)))
        mstore(add(mPtr, 0x40), mload(q))
        mstore(add(mPtr, 0x60), mload(add(q, 0x20)))
        let l_success := staticcall(gas(), 6, mPtr, 0x80, dst, 0x40)
        if iszero(l_success) {
          error_ec_op()
        }
      }

      /// @param dst pointer storing the result
      /// @param p pointer to the first point (calldata)
      /// @param q pointer to the second point (calladata)
      /// @param mPtr pointer to free memory
      function point_add_calldata(dst, p, q, mPtr) {
        let state := mload(0x40)
        mstore(mPtr, mload(p))
        mstore(add(mPtr, 0x20), mload(add(p, 0x20)))
        mstore(add(mPtr, 0x40), calldataload(q))
        mstore(add(mPtr, 0x60), calldataload(add(q, 0x20)))
        let l_success := staticcall(gas(), 6, mPtr, 0x80, dst, 0x40)
        if iszero(l_success) {
          error_ec_op()
        }
      }

      /// @parma dst pointer storing the result
      /// @param src pointer to a point on Bn254(𝔽_p)
      /// @param s scalar
      /// @param mPtr free memory
      function point_mul(dst, src, s, mPtr) {
        let state := mload(0x40)
        mstore(mPtr, mload(src))
        mstore(add(mPtr, 0x20), mload(add(src, 0x20)))
        mstore(add(mPtr, 0x40), s)
        let l_success := staticcall(gas(), 7, mPtr, 0x60, dst, 0x40)
        if iszero(l_success) {
          error_ec_op()
        }
      }

      /// @parma dst pointer storing the result
      /// @param src pointer to a point on Bn254(𝔽_p) on calldata
      /// @param s scalar
      /// @param mPtr free memory
      function point_mul_calldata(dst, src, s, mPtr) {
        let state := mload(0x40)
        mstore(mPtr, calldataload(src))
        mstore(add(mPtr, 0x20), calldataload(add(src, 0x20)))
        mstore(add(mPtr, 0x40), s)
        let l_success := staticcall(gas(), 7, mPtr, 0x60, dst, 0x40)
        if iszero(l_success) {
          error_ec_op()
        }
      }

      /// @notice dst <- dst + [s]src (Elliptic curve)
      /// @param dst pointer accumulator point storing the result
      /// @param src pointer to the point to multiply and add
      /// @param s scalar
      /// @param mPtr free memory
      function point_acc_mul(dst, src, s, mPtr) {
        let state := mload(0x40)
        mstore(mPtr, mload(src))
        mstore(add(mPtr, 0x20), mload(add(src, 0x20)))
        mstore(add(mPtr, 0x40), s)
        let l_success := staticcall(gas(), 7, mPtr, 0x60, mPtr, 0x40)
        mstore(add(mPtr, 0x40), mload(dst))
        mstore(add(mPtr, 0x60), mload(add(dst, 0x20)))
        l_success := and(l_success, staticcall(gas(), 6, mPtr, 0x80, dst, 0x40))
        if iszero(l_success) {
          error_ec_op()
        }
      }

      /// @notice dst <- dst + [s]src (Elliptic curve)
      /// @param dst pointer accumulator point storing the result
      /// @param src pointer to the point to multiply and add (on calldata)
      /// @param s scalar
      /// @mPtr free memory
      function point_acc_mul_calldata(dst, src, s, mPtr) {
        let state := mload(0x40)
        mstore(mPtr, calldataload(src))
        mstore(add(mPtr, 0x20), calldataload(add(src, 0x20)))
        mstore(add(mPtr, 0x40), s)
        let l_success := staticcall(gas(), 7, mPtr, 0x60, mPtr, 0x40)
        mstore(add(mPtr, 0x40), mload(dst))
        mstore(add(mPtr, 0x60), mload(add(dst, 0x20)))
        l_success := and(l_success, staticcall(gas(), 6, mPtr, 0x80, dst, 0x40))
        if iszero(l_success) {
          error_ec_op()
        }
      }

      /// @notice dst <- dst + src*s (Fr) dst,src are addresses, s is a value
      /// @param dst pointer storing the result
      /// @param src pointer to the scalar to multiply and add (on calldata)
      /// @param s scalar
      function fr_acc_mul_calldata(dst, src, s) {
        let tmp := mulmod(calldataload(src), s, R_MOD)
        mstore(dst, addmod(mload(dst), tmp, R_MOD))
      }

      /// @param x element to exponentiate
      /// @param e exponent
      /// @param mPtr free memory
      /// @return res x ** e mod r
      function pow(x, e, mPtr) -> res {
        mstore(mPtr, 0x20)
        mstore(add(mPtr, 0x20), 0x20)
        mstore(add(mPtr, 0x40), 0x20)
        mstore(add(mPtr, 0x60), x)
        mstore(add(mPtr, 0x80), e)
        mstore(add(mPtr, 0xa0), R_MOD)
        let check_staticcall := staticcall(gas(), 0x05, mPtr, 0xc0, mPtr, 0x20)
        if eq(check_staticcall, 0) {
          error_verify()
        }
        res := mload(mPtr)
      }
    }
  }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 100000
  },
  "evmVersion": "cancun",
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "metadata": {
    "useLiteralContent": true
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"bytes","name":"proof","type":"bytes"},{"internalType":"uint256[]","name":"public_inputs","type":"uint256[]"}],"name":"Verify","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"view","type":"function"}]

608060405234801561000f575f80fd5b506129d18061001d5f395ff3fe608060405234801561000f575f80fd5b5060043610610029575f3560e01c80637e4f7a8a1461002d575b5f80fd5b61004061003b3660046128e1565b610054565b604051901515815260200160405180910390f35b5f604051610220810161006684610438565b610070858561044b565b6100798661049a565b610082876104b0565b5f61008e86868a6106b8565b9050610099816109ef565b90506100a58189610a55565b90506100b18189610ae3565b5060608201517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000061010884630400000085612876565b086101a08401525061011b818587610b4e565b61012682868a610ec3565b91507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018183086101808401525061015d9050611269565b610166866123ee565b61016f8661237e565b61017886611f99565b61018186611a9a565b61018a866117b2565b610193866113ba565b6101e0015190506128d9565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f77726f6e67206e756d626572206f66207075626c696320696e707574730000006044820152606481fd5b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f6572726f72206563206f7065726174696f6e00000000000000000000000000006044820152606481fd5b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f696e707574732061726520626967676572207468616e207200000000000000006044820152606481fd5b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f77726f6e672070726f6f662073697a65000000000000000000000000000000006044820152606481fd5b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f6f70656e696e677320626967676572207468616e2072000000000000000000006044820152606481fd5b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f6572726f722076657269667900000000000000000000000000000000000000006044820152606481fd5b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f6572726f722072616e646f6d2067656e206b7a670000000000000000000000006044820152606481fd5b600181146104485761044861019f565b50565b5f5b81811015610495577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000833511156104865761048661025d565b6020929092019160010161044d565b505050565b6103a08181146104ac576104ac6102bc565b5050565b6102a081017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000813511156104e6576104e661031b565b5061028081017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000008135111561051d5761051d61031b565b5061018081017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000813511156105545761055461031b565b506101a081017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000008135111561058b5761058b61031b565b506101c081017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000813511156105c2576105c261031b565b506101e081017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000813511156105f9576105f961031b565b5061020081017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000813511156106305761063061031b565b5061026081017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000813511156106675761066761031b565b5061034081015f5b6001811015610495577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000823511156106a9576106a961031b565b6020919091019060010161066f565b5f60405161022081016467616d6d6181527f10a41b94357abfb4a1e653c37120f36bf8a7afcd7380d5ee421b55ef5ce8e4a760208201527f2fc1ec413636a4db1b661562cd7ded6c503d3dd4d5422ce57e3a1390bc1c9f8160408201527f1a2d983c26b7449795edd7ff3869efcba9ae5aaf7f78d23b120ec32f9157cdd660608201527f22cf19cb5f3e6e86c3699628d6ac8f6e46cc8cc334506c5df3d9901f197c3a5160808201527f1432ef67a0902a5b932d0a2a6ee57c7f9fef8baf1857f850dc16dbceabd24d7d60a08201527f2c25025a8b6b98df5516c666a9319e710a548801f27088adbd7d74400231065560c08201527f16e2deb1864a00fa65f207862b4bddd8859e69af1823328b667802cef8dbd35660e08201527f0351af541df970fceb40ed48f5d82c75357ad777bf7cdb157094763300d03fd46101008201527f03e1b31a362ae2d5ac8b788c133ddbba11f7de90898fb064e9ec3ed0eaceaace6101208201527f0d57fd64e30c4863335cab8d61627831a48d32f0ea454bae7e36033b1ec3bf506101408201527f0ebf7069d6b83c8c545351949fcad4f0410bff394a6705a95b4c9cbc90c3dd4b6101608201527f0c38619a4ee0c0ce40ff7059d9888c00e98da79f9ef014671bfd2f166574ec016101808201527f1980c2cfe29bb04711e0ea063030259c8f1be74a96f2a5d58fa6a39c674b54636101a08201527f14cc24318a81d5063f3eb415de3e2d53bda3b8c27037fc46cf5cc75845d91f916101c08201527f25916878e9cad8031bf15e48d5ca40731197104a6fdb57d83bda5fe9d16b4f8b6101e08201527f0df70cc21ab378626113aa1c2e1f331266c3281605d743c7d2b2f896d42037d96102008201527f1f803c5398f400176982507a4120971333d538c5e0bad1b4cfbcafa1a4c2c5a16102208201527f1aaf4ca22eac5be5ba8cfffb4dc06e6971c24b34fb7f313033424d5b992075d9610240820152610260810160208602808883379081019060c0808784375061030501905060208282601b820160025afa9050806109b9576109b961037a565b5080519250507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182066040820152509392505050565b5f60405161022060405101636265746181528360208201526020816024601c840160025afa80610a2157610a2161037a565b5080519250507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018206602082015250919050565b5f60405161022060405101606564616c7068618252602082018681526020810190506103608601600160400280828437928301929190910190506040610220870182375060208282601b850160025afa905080610ab457610ab461037a565b50517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181069091529392505050565b60405161022060405101637a657461815283602082015260c0808401604083013760208160e4601c840160025afa80610b1e57610b1e61037a565b50517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019006606091909101525050565b5f60405160608101516101a0820151915085610b6c81878585610be7565b5f92505f91505b85821015610bdd577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001853582510992507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018385086020958601959094506001929092019101610b73565b5050509392505050565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e66c81e03716be83b486d6feabcc7ddd0fe6cbf5e72d585d142f7829b0583096001855f5b86811015610cd5577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103860882527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f1067569af1ff73b20113eff9b8d89d4a605b52b63d68f9ae1c79bd572f4e92128409925060209190910190600101610c30565b50610ce1818789610d99565b5060019050855f5b86811015610d8f577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001868551090982526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f1067569af1ff73b20113eff9b8d89d4a605b52b63d68f9ae1c79bd572f4e921284099250600101610ce9565b5050505050505050565b600183525f805b83811015610dee5781850151828401517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001818309905060208401935080848801525050600181019050610da0565b506020810382019150808401935050610e2f6020840160027f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001038551612876565b5f5b83811015610ebc5760208503945082517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018651840984527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018184097fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090940193925050600101610e31565b5050505050565b5f60405160608101516101a0820151915061036084015f80610eeb8960208501358535611070565b9150610eff89630164747c8a018787610f35565b90507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018082840987089998505050505050505050565b5f610f6185857f1067569af1ff73b20113eff9b8d89d4a605b52b63d68f9ae1c79bd572f4e9212612876565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001817f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103840894507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e66c81e03716be83b486d6feabcc7ddd0fe6cbf5e72d585d142f7829b058209905061101a867f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffff87612876565b94507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018482099695505050505050565b5f83525f602084015280604084015250806060830152505f6080820153603060818201535f60828201536042608382015360536084820153604260858201536032608682015360326087820153602d608882015360506089820153606c608a820153606f608b820153606e608c820153606b608d820153600b608e8201535f602082608f8460025afa806111065761110661037a565b8251600160208501536042602185015360536022850153604260238501536032602485015360326025850153602d602685015360506027850153606c6028850153606f6029850153606e602a850153606b602b850153600b602c850153602084602d8660025afa91508161117c5761117c61037a565b8351186020840152600260408401536042604184015360536042840153604260438401536032604484015360326045840153602d604684015360506047840153606c6048840153606f6049840153606e604a840153606b604b840153600b604c84015360208301602081602d8360025afa915050806111fd576111fd61037a565b507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017001000000000000000000000000000000008351099050602082015160801c7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018183089392505050565b604051610220604051016101a08201517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001036060850151086112ef837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffff83612876565b90507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e66c81e03716be83b486d6feabcc7ddd0fe6cbf5e72d585d142f7829b05820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018282098451935091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001905082820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018282099050806080840152505050565b6040516102208101610140820151815261016082015160208201526102c083013560408201526102e08301356060820152610220830135608082015261024083013560a082015261030083013560c082015261032083013560e082015260608201516101008201526101c08201516101208201526020816101408360025afa80611446576114466103d9565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018251069050816040810192506102c085013581526102e0850135602082015261149683836103008801846127dd565b61014084016114ab84846102208901846127dd565b61012085016114bf84610260890183612824565b7f1fa4be93b5e7f7e674d5059b63554fab99638b304ed8310e9fa44c281ac9b03b85527f1a01ae7fac6228e39d3cb5a5e71fd31160f3241e79a5f48ffb3737e6c389b7216020860152805160408087019182529095908160608160075afa91508161152c5761152c61037a565b60208101915081517f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47038252611564868285866126cc565b5050836040850194506115818560608801516102c08a018461276b565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f1067569af1ff73b20113eff9b8d89d4a605b52b63d68f9ae1c79bd572f4e921260608801510995507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001868509935061160185856103008a01846127dd565b61160d858284856126cc565b50602082810180517f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd470381528251865291810151908501527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c260408501527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed60608501527f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b60808501527f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa60a0850152905160c0840152805160e08401527f22f1acbb03c4508760c2430af35865e7cdf9f3eb1224504fdcc3708ddb954a486101008401527f2a344fad01c2ed0ed73142ae1752429eaea515c6f3f6b941103cc21c2308e1cb6101208401527f159f15b842ba9c8449aa3268f981010d4c7142e5193473d80b464e964845c3f86101408401527f0efd30ac7b6f8d0d3ccbc2207587c2acbad1532dc0293f0d034cf8258cd428b3610160840152925061049590508160405160205f6101808460085afa5f516101e09290920180519190921616905250565b6040516102206040510160208101604082016101c084015180610140860160a087015161014088015260c0870151610160880152610280880135610120880152611801868360e08a0184612796565b611814826102a08a016101208a01612824565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001838309915061184a86838a6101408b016127dd565b61185d826101808a016101208a01612824565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018383099150611892868360408b01846127dd565b6118a5826101a08a016101208a01612824565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183830991506118da868360808b01846127dd565b6118ed826101c08a016101208a01612824565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183830991507f10a41b94357abfb4a1e653c37120f36bf8a7afcd7380d5ee421b55ef5ce8e4a786527f2fc1ec413636a4db1b661562cd7ded6c503d3dd4d5422ce57e3a1390bc1c9f81855261196584838884612796565b611978826101e08a016101208a01612824565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183830991507f1a2d983c26b7449795edd7ff3869efcba9ae5aaf7f78d23b120ec32f9157cdd686527f22cf19cb5f3e6e86c3699628d6ac8f6e46cc8cc334506c5df3d9901f197c3a5185526119f084838884612796565b611a03826102008a016101208a01612824565b61034088017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000184840992507f1f803c5398f400176982507a4120971333d538c5e0bad1b4cfbcafa1a4c2c5a187527f1aaf4ca22eac5be5ba8cfffb4dc06e6971c24b34fb7f313033424d5b992075d98652611a8085848985612796565b611a8f83826101208b01612824565b505050505050505050565b6040516467616d6d616102208201908152606082015161024083015260a082015161026083015260c08083015161028084015260e08301516102a08401526101008301516102c0840152836102e08401377f10a41b94357abfb4a1e653c37120f36bf8a7afcd7380d5ee421b55ef5ce8e4a7610180828101919091527f2fc1ec413636a4db1b661562cd7ded6c503d3dd4d5422ce57e3a1390bc1c9f816101a0808401919091527f1a2d983c26b7449795edd7ff3869efcba9ae5aaf7f78d23b120ec32f9157cdd66101c0808501919091527f22cf19cb5f3e6e86c3699628d6ac8f6e46cc8cc334506c5df3d9901f197c3a516101e0808601919091527f1f803c5398f400176982507a4120971333d538c5e0bad1b4cfbcafa1a4c2c5a1610200808701919091527f1aaf4ca22eac5be5ba8cfffb4dc06e6971c24b34fb7f313033424d5b992075d9610220870152610280888101356102408801526102a0808a0135610260890152958901359087015292870135938501939093528501356102c0840152908401356102e0830152830135610300820152610320810161034084015f5b6001811015611c5d578135835260209283019290910190600101611c3e565b50506102608401359052601b61034560206101c085018285850160025afa9250505080611c8c57611c8c61037a565b506101c00180517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019006905250565b604051610220604051017f16e2deb1864a00fa65f207862b4bddd8859e69af1823328b667802cef8dbd35681527f0351af541df970fceb40ed48f5d82c75357ad777bf7cdb157094763300d03fd46020820152611d25604082016101808501358360e08601612740565b7f03e1b31a362ae2d5ac8b788c133ddbba11f7de90898fb064e9ec3ed0eaceaace81527f0d57fd64e30c4863335cab8d61627831a48d32f0ea454bae7e36033b1ec3bf506020820152611d85604082016101a08501358360e08601612796565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101a0840135610180850135097f0ebf7069d6b83c8c545351949fcad4f0410bff394a6705a95b4c9cbc90c3dd4b82527f0c38619a4ee0c0ce40ff7059d9888c00e98da79f9ef014671bfd2f166574ec016020830152611e0e60408301828460e08701612796565b507f1980c2cfe29bb04711e0ea063030259c8f1be74a96f2a5d58fa6a39c674b546381527f14cc24318a81d5063f3eb415de3e2d53bda3b8c27037fc46cf5cc75845d91f916020820152611e6f604082016101c08501358360e08601612796565b7f25916878e9cad8031bf15e48d5ca40731197104a6fdb57d83bda5fe9d16b4f8b81527f0df70cc21ab378626113aa1c2e1f331266c3281605d743c7d2b2f896d42037d96020820152611eca604082018260e08501806126cc565b610340830161036084015f5b6001811015611f16578135845260208201356020850152611f006040850184358660e08901612796565b6020929092019160409190910190600101611ed6565b5050507f1432ef67a0902a5b932d0a2a6ee57c7f9fef8baf1857f850dc16dbceabd24d7d81527f2c25025a8b6b98df5516c666a9319e710a548801f27088adbd7d7440023106556020820152611f7460408201858360e08601612796565b61022083013581526102408301356020820152610ebc60408201868360e08601612796565b6040516020810151604082015160608301515f8401517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000184610260880135097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101e088013586097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610180890135820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161020089013587097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101a08a0135820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000186820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018284097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001600580097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001878a0998507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101808c01358a0894507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000188860894507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160058a0993507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101a08c0135850893507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000188850893507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001818a099250507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101c08b0135830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000187830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183850997507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018289097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103985085890997507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160808a01518908975061237288828c611cbb565b50505050505050505050565b60405160026304000000016102206040510161239f81836060860151612876565b91506123b48183610140870160a0870161276b565b6123c781610100860160a0860180612706565b6123d6818360a0860180612740565b6123e88160c0860160a0860180612706565b50505050565b604051610220604051017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160208301516101e08501350981527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001604083015182510881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101808401358251088152602081017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160208401516102008601350981527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001604084015182510881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101a08501358251088152604082017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160408501516101c08701350881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001825184510983527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001815184510980845284517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019250900982527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016102608501358351098252606082017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101808501516102a08701350881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001835182510881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160808501517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000010382510881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101a085015161028087013509825281518151146101e08501525050505050565b604051508151845260208201516020850152825160408501526020830151606085015260408160808660065afa80610ebc57610ebc6101fe565b604051508151845260208201516020850152823560408501526020830135606085015260408160808660065afa80610ebc57610ebc6101fe565b815184526020808301519085015260408481018490528160608660075afa80610ebc57610ebc6101fe565b813584526020808301359085015260408481018490528160608660075afa80610ebc57610ebc6101fe565b815184526020808301519085015260408481018490528460608160075afa815160408601526020820151606086015260408260808760065afa1680610ebc57610ebc6101fe565b813584526020808301359085015260408481018490528460608160075afa815160408601526020820151606086015260408260808760065afa1680610ebc57610ebc6101fe565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001838335097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181835108825250505050565b602083526020808401526020604084015280606084015250806080830152507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160a08201525f60208260c08460055afa806128d3576128d361037a565b50505190565b949350505050565b5f805f80604085870312156128f4575f80fd5b843567ffffffffffffffff8082111561290b575f80fd5b818701915087601f83011261291e575f80fd5b81358181111561292c575f80fd5b88602082850101111561293d575f80fd5b602092830196509450908601359080821115612957575f80fd5b818701915087601f83011261296a575f80fd5b813581811115612978575f80fd5b8860208260051b850101111561298c575f80fd5b9598949750506020019450505056fea26469706673582212200a94bd014378d518cad5dac55779db659edbfcf02bc70e865fe544ee9558c39064736f6c63430008180033

Deployed Bytecode

0x608060405234801561000f575f80fd5b5060043610610029575f3560e01c80637e4f7a8a1461002d575b5f80fd5b61004061003b3660046128e1565b610054565b604051901515815260200160405180910390f35b5f604051610220810161006684610438565b610070858561044b565b6100798661049a565b610082876104b0565b5f61008e86868a6106b8565b9050610099816109ef565b90506100a58189610a55565b90506100b18189610ae3565b5060608201517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000061010884630400000085612876565b086101a08401525061011b818587610b4e565b61012682868a610ec3565b91507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018183086101808401525061015d9050611269565b610166866123ee565b61016f8661237e565b61017886611f99565b61018186611a9a565b61018a866117b2565b610193866113ba565b6101e0015190506128d9565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f77726f6e67206e756d626572206f66207075626c696320696e707574730000006044820152606481fd5b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f6572726f72206563206f7065726174696f6e00000000000000000000000000006044820152606481fd5b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f696e707574732061726520626967676572207468616e207200000000000000006044820152606481fd5b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f77726f6e672070726f6f662073697a65000000000000000000000000000000006044820152606481fd5b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f6f70656e696e677320626967676572207468616e2072000000000000000000006044820152606481fd5b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f6572726f722076657269667900000000000000000000000000000000000000006044820152606481fd5b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f6572726f722072616e646f6d2067656e206b7a670000000000000000000000006044820152606481fd5b600181146104485761044861019f565b50565b5f5b81811015610495577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000833511156104865761048661025d565b6020929092019160010161044d565b505050565b6103a08181146104ac576104ac6102bc565b5050565b6102a081017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000813511156104e6576104e661031b565b5061028081017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000008135111561051d5761051d61031b565b5061018081017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000813511156105545761055461031b565b506101a081017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000008135111561058b5761058b61031b565b506101c081017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000813511156105c2576105c261031b565b506101e081017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000813511156105f9576105f961031b565b5061020081017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000813511156106305761063061031b565b5061026081017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000813511156106675761066761031b565b5061034081015f5b6001811015610495577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000823511156106a9576106a961031b565b6020919091019060010161066f565b5f60405161022081016467616d6d6181527f10a41b94357abfb4a1e653c37120f36bf8a7afcd7380d5ee421b55ef5ce8e4a760208201527f2fc1ec413636a4db1b661562cd7ded6c503d3dd4d5422ce57e3a1390bc1c9f8160408201527f1a2d983c26b7449795edd7ff3869efcba9ae5aaf7f78d23b120ec32f9157cdd660608201527f22cf19cb5f3e6e86c3699628d6ac8f6e46cc8cc334506c5df3d9901f197c3a5160808201527f1432ef67a0902a5b932d0a2a6ee57c7f9fef8baf1857f850dc16dbceabd24d7d60a08201527f2c25025a8b6b98df5516c666a9319e710a548801f27088adbd7d74400231065560c08201527f16e2deb1864a00fa65f207862b4bddd8859e69af1823328b667802cef8dbd35660e08201527f0351af541df970fceb40ed48f5d82c75357ad777bf7cdb157094763300d03fd46101008201527f03e1b31a362ae2d5ac8b788c133ddbba11f7de90898fb064e9ec3ed0eaceaace6101208201527f0d57fd64e30c4863335cab8d61627831a48d32f0ea454bae7e36033b1ec3bf506101408201527f0ebf7069d6b83c8c545351949fcad4f0410bff394a6705a95b4c9cbc90c3dd4b6101608201527f0c38619a4ee0c0ce40ff7059d9888c00e98da79f9ef014671bfd2f166574ec016101808201527f1980c2cfe29bb04711e0ea063030259c8f1be74a96f2a5d58fa6a39c674b54636101a08201527f14cc24318a81d5063f3eb415de3e2d53bda3b8c27037fc46cf5cc75845d91f916101c08201527f25916878e9cad8031bf15e48d5ca40731197104a6fdb57d83bda5fe9d16b4f8b6101e08201527f0df70cc21ab378626113aa1c2e1f331266c3281605d743c7d2b2f896d42037d96102008201527f1f803c5398f400176982507a4120971333d538c5e0bad1b4cfbcafa1a4c2c5a16102208201527f1aaf4ca22eac5be5ba8cfffb4dc06e6971c24b34fb7f313033424d5b992075d9610240820152610260810160208602808883379081019060c0808784375061030501905060208282601b820160025afa9050806109b9576109b961037a565b5080519250507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182066040820152509392505050565b5f60405161022060405101636265746181528360208201526020816024601c840160025afa80610a2157610a2161037a565b5080519250507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018206602082015250919050565b5f60405161022060405101606564616c7068618252602082018681526020810190506103608601600160400280828437928301929190910190506040610220870182375060208282601b850160025afa905080610ab457610ab461037a565b50517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181069091529392505050565b60405161022060405101637a657461815283602082015260c0808401604083013760208160e4601c840160025afa80610b1e57610b1e61037a565b50517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019006606091909101525050565b5f60405160608101516101a0820151915085610b6c81878585610be7565b5f92505f91505b85821015610bdd577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001853582510992507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018385086020958601959094506001929092019101610b73565b5050509392505050565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e66c81e03716be83b486d6feabcc7ddd0fe6cbf5e72d585d142f7829b0583096001855f5b86811015610cd5577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103860882527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f1067569af1ff73b20113eff9b8d89d4a605b52b63d68f9ae1c79bd572f4e92128409925060209190910190600101610c30565b50610ce1818789610d99565b5060019050855f5b86811015610d8f577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001868551090982526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f1067569af1ff73b20113eff9b8d89d4a605b52b63d68f9ae1c79bd572f4e921284099250600101610ce9565b5050505050505050565b600183525f805b83811015610dee5781850151828401517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001818309905060208401935080848801525050600181019050610da0565b506020810382019150808401935050610e2f6020840160027f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001038551612876565b5f5b83811015610ebc5760208503945082517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018651840984527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018184097fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090940193925050600101610e31565b5050505050565b5f60405160608101516101a0820151915061036084015f80610eeb8960208501358535611070565b9150610eff89630164747c8a018787610f35565b90507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018082840987089998505050505050505050565b5f610f6185857f1067569af1ff73b20113eff9b8d89d4a605b52b63d68f9ae1c79bd572f4e9212612876565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001817f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103840894507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e66c81e03716be83b486d6feabcc7ddd0fe6cbf5e72d585d142f7829b058209905061101a867f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffff87612876565b94507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018482099695505050505050565b5f83525f602084015280604084015250806060830152505f6080820153603060818201535f60828201536042608382015360536084820153604260858201536032608682015360326087820153602d608882015360506089820153606c608a820153606f608b820153606e608c820153606b608d820153600b608e8201535f602082608f8460025afa806111065761110661037a565b8251600160208501536042602185015360536022850153604260238501536032602485015360326025850153602d602685015360506027850153606c6028850153606f6029850153606e602a850153606b602b850153600b602c850153602084602d8660025afa91508161117c5761117c61037a565b8351186020840152600260408401536042604184015360536042840153604260438401536032604484015360326045840153602d604684015360506047840153606c6048840153606f6049840153606e604a840153606b604b840153600b604c84015360208301602081602d8360025afa915050806111fd576111fd61037a565b507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017001000000000000000000000000000000008351099050602082015160801c7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018183089392505050565b604051610220604051016101a08201517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001036060850151086112ef837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffff83612876565b90507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e66c81e03716be83b486d6feabcc7ddd0fe6cbf5e72d585d142f7829b05820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018282098451935091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001905082820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018282099050806080840152505050565b6040516102208101610140820151815261016082015160208201526102c083013560408201526102e08301356060820152610220830135608082015261024083013560a082015261030083013560c082015261032083013560e082015260608201516101008201526101c08201516101208201526020816101408360025afa80611446576114466103d9565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018251069050816040810192506102c085013581526102e0850135602082015261149683836103008801846127dd565b61014084016114ab84846102208901846127dd565b61012085016114bf84610260890183612824565b7f1fa4be93b5e7f7e674d5059b63554fab99638b304ed8310e9fa44c281ac9b03b85527f1a01ae7fac6228e39d3cb5a5e71fd31160f3241e79a5f48ffb3737e6c389b7216020860152805160408087019182529095908160608160075afa91508161152c5761152c61037a565b60208101915081517f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47038252611564868285866126cc565b5050836040850194506115818560608801516102c08a018461276b565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f1067569af1ff73b20113eff9b8d89d4a605b52b63d68f9ae1c79bd572f4e921260608801510995507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001868509935061160185856103008a01846127dd565b61160d858284856126cc565b50602082810180517f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd470381528251865291810151908501527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c260408501527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed60608501527f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b60808501527f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa60a0850152905160c0840152805160e08401527f22f1acbb03c4508760c2430af35865e7cdf9f3eb1224504fdcc3708ddb954a486101008401527f2a344fad01c2ed0ed73142ae1752429eaea515c6f3f6b941103cc21c2308e1cb6101208401527f159f15b842ba9c8449aa3268f981010d4c7142e5193473d80b464e964845c3f86101408401527f0efd30ac7b6f8d0d3ccbc2207587c2acbad1532dc0293f0d034cf8258cd428b3610160840152925061049590508160405160205f6101808460085afa5f516101e09290920180519190921616905250565b6040516102206040510160208101604082016101c084015180610140860160a087015161014088015260c0870151610160880152610280880135610120880152611801868360e08a0184612796565b611814826102a08a016101208a01612824565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001838309915061184a86838a6101408b016127dd565b61185d826101808a016101208a01612824565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018383099150611892868360408b01846127dd565b6118a5826101a08a016101208a01612824565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183830991506118da868360808b01846127dd565b6118ed826101c08a016101208a01612824565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183830991507f10a41b94357abfb4a1e653c37120f36bf8a7afcd7380d5ee421b55ef5ce8e4a786527f2fc1ec413636a4db1b661562cd7ded6c503d3dd4d5422ce57e3a1390bc1c9f81855261196584838884612796565b611978826101e08a016101208a01612824565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183830991507f1a2d983c26b7449795edd7ff3869efcba9ae5aaf7f78d23b120ec32f9157cdd686527f22cf19cb5f3e6e86c3699628d6ac8f6e46cc8cc334506c5df3d9901f197c3a5185526119f084838884612796565b611a03826102008a016101208a01612824565b61034088017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000184840992507f1f803c5398f400176982507a4120971333d538c5e0bad1b4cfbcafa1a4c2c5a187527f1aaf4ca22eac5be5ba8cfffb4dc06e6971c24b34fb7f313033424d5b992075d98652611a8085848985612796565b611a8f83826101208b01612824565b505050505050505050565b6040516467616d6d616102208201908152606082015161024083015260a082015161026083015260c08083015161028084015260e08301516102a08401526101008301516102c0840152836102e08401377f10a41b94357abfb4a1e653c37120f36bf8a7afcd7380d5ee421b55ef5ce8e4a7610180828101919091527f2fc1ec413636a4db1b661562cd7ded6c503d3dd4d5422ce57e3a1390bc1c9f816101a0808401919091527f1a2d983c26b7449795edd7ff3869efcba9ae5aaf7f78d23b120ec32f9157cdd66101c0808501919091527f22cf19cb5f3e6e86c3699628d6ac8f6e46cc8cc334506c5df3d9901f197c3a516101e0808601919091527f1f803c5398f400176982507a4120971333d538c5e0bad1b4cfbcafa1a4c2c5a1610200808701919091527f1aaf4ca22eac5be5ba8cfffb4dc06e6971c24b34fb7f313033424d5b992075d9610220870152610280888101356102408801526102a0808a0135610260890152958901359087015292870135938501939093528501356102c0840152908401356102e0830152830135610300820152610320810161034084015f5b6001811015611c5d578135835260209283019290910190600101611c3e565b50506102608401359052601b61034560206101c085018285850160025afa9250505080611c8c57611c8c61037a565b506101c00180517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019006905250565b604051610220604051017f16e2deb1864a00fa65f207862b4bddd8859e69af1823328b667802cef8dbd35681527f0351af541df970fceb40ed48f5d82c75357ad777bf7cdb157094763300d03fd46020820152611d25604082016101808501358360e08601612740565b7f03e1b31a362ae2d5ac8b788c133ddbba11f7de90898fb064e9ec3ed0eaceaace81527f0d57fd64e30c4863335cab8d61627831a48d32f0ea454bae7e36033b1ec3bf506020820152611d85604082016101a08501358360e08601612796565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101a0840135610180850135097f0ebf7069d6b83c8c545351949fcad4f0410bff394a6705a95b4c9cbc90c3dd4b82527f0c38619a4ee0c0ce40ff7059d9888c00e98da79f9ef014671bfd2f166574ec016020830152611e0e60408301828460e08701612796565b507f1980c2cfe29bb04711e0ea063030259c8f1be74a96f2a5d58fa6a39c674b546381527f14cc24318a81d5063f3eb415de3e2d53bda3b8c27037fc46cf5cc75845d91f916020820152611e6f604082016101c08501358360e08601612796565b7f25916878e9cad8031bf15e48d5ca40731197104a6fdb57d83bda5fe9d16b4f8b81527f0df70cc21ab378626113aa1c2e1f331266c3281605d743c7d2b2f896d42037d96020820152611eca604082018260e08501806126cc565b610340830161036084015f5b6001811015611f16578135845260208201356020850152611f006040850184358660e08901612796565b6020929092019160409190910190600101611ed6565b5050507f1432ef67a0902a5b932d0a2a6ee57c7f9fef8baf1857f850dc16dbceabd24d7d81527f2c25025a8b6b98df5516c666a9319e710a548801f27088adbd7d7440023106556020820152611f7460408201858360e08601612796565b61022083013581526102408301356020820152610ebc60408201868360e08601612796565b6040516020810151604082015160608301515f8401517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000184610260880135097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101e088013586097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610180890135820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161020089013587097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101a08a0135820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000186820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018284097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001600580097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001878a0998507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101808c01358a0894507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000188860894507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160058a0993507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101a08c0135850893507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000188850893507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001818a099250507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101c08b0135830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000187830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183850997507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018289097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103985085890997507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160808a01518908975061237288828c611cbb565b50505050505050505050565b60405160026304000000016102206040510161239f81836060860151612876565b91506123b48183610140870160a0870161276b565b6123c781610100860160a0860180612706565b6123d6818360a0860180612740565b6123e88160c0860160a0860180612706565b50505050565b604051610220604051017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160208301516101e08501350981527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001604083015182510881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101808401358251088152602081017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160208401516102008601350981527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001604084015182510881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101a08501358251088152604082017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160408501516101c08701350881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001825184510983527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001815184510980845284517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019250900982527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016102608501358351098252606082017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101808501516102a08701350881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001835182510881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160808501517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000010382510881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101a085015161028087013509825281518151146101e08501525050505050565b604051508151845260208201516020850152825160408501526020830151606085015260408160808660065afa80610ebc57610ebc6101fe565b604051508151845260208201516020850152823560408501526020830135606085015260408160808660065afa80610ebc57610ebc6101fe565b815184526020808301519085015260408481018490528160608660075afa80610ebc57610ebc6101fe565b813584526020808301359085015260408481018490528160608660075afa80610ebc57610ebc6101fe565b815184526020808301519085015260408481018490528460608160075afa815160408601526020820151606086015260408260808760065afa1680610ebc57610ebc6101fe565b813584526020808301359085015260408481018490528460608160075afa815160408601526020820151606086015260408260808760065afa1680610ebc57610ebc6101fe565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001838335097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181835108825250505050565b602083526020808401526020604084015280606084015250806080830152507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160a08201525f60208260c08460055afa806128d3576128d361037a565b50505190565b949350505050565b5f805f80604085870312156128f4575f80fd5b843567ffffffffffffffff8082111561290b575f80fd5b818701915087601f83011261291e575f80fd5b81358181111561292c575f80fd5b88602082850101111561293d575f80fd5b602092830196509450908601359080821115612957575f80fd5b818701915087601f83011261296a575f80fd5b813581811115612978575f80fd5b8860208260051b850101111561298c575f80fd5b9598949750506020019450505056fea26469706673582212200a94bd014378d518cad5dac55779db659edbfcf02bc70e865fe544ee9558c39064736f6c63430008180033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.