ETH Price: $2,158.48 (+5.37%)

Contract

0xc01E6807DB9Fb9cC75E9Fe622ba8e7f3eB9f2B32
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
PlonkVerifierFull

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 100000 runs

Other Settings:
default evmVersion
File 1 of 2 : PlonkVerifierFull.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.19;

import { Utils } from "./Utils.sol";

contract PlonkVerifierFull {
  using Utils for *;
  uint256 private constant r_mod = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
  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;

  // ----------------------- vk ---------------------
  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 = 13525709715452455298954926042894212564628036321399789360566793694715937771352;
  uint256 private constant vk_ql_com_y = 16585249180591675138941559203780511979245575412908714775636958360987383792239;
  uint256 private constant vk_qr_com_x = 14148572045886251945792876173080813556116314088794324579574660284762914941669;
  uint256 private constant vk_qr_com_y = 13842239550691491428464191545817225601597189810026701643462638680503957744889;
  uint256 private constant vk_qm_com_x = 13522897959955119652865784834835976361490133655994054434262671514193248404625;
  uint256 private constant vk_qm_com_y = 13493447428329441420851370602159948706574248532218034239617820929323277518001;
  uint256 private constant vk_qo_com_x = 7060690164887603180822709561299613895672597716510049606680499562908505132709;
  uint256 private constant vk_qo_com_y = 17132453280808170663140286188647213203791535181032438058770464109374337899153;
  uint256 private constant vk_qk_com_x = 15216401052757890774219228156239863879308845760439647233724618177408901913570;
  uint256 private constant vk_qk_com_y = 12451956943068853170449619051211352834650857556519559497734074793907719431916;

  uint256 private constant vk_s1_com_x = 14256405778767051587276133608837021203518255961898101394303537378002866419040;
  uint256 private constant vk_s1_com_y = 21038700968212297875313747694611083193187035592187569106720411730639905255252;

  uint256 private constant vk_s2_com_x = 1019109700203543492729048562641359612628812305695299058467746496950485325794;
  uint256 private constant vk_s2_com_y = 490278430319379230242443848913755358662702057338304277498471180434097991836;

  uint256 private constant vk_s3_com_x = 2969427075258365849777260383285616082152301055041585820485498518017437026548;
  uint256 private constant vk_s3_com_y = 16099347285202727295431946911322450887583714417016216619148080599766330871066;

  uint256 private constant vk_coset_shift = 5;

  uint256 private constant vk_selector_commitments_commit_api_0_x =
    21788360697493106829785246939901744021456836473452930837314992369057900122018;
  uint256 private constant vk_selector_commitments_commit_api_0_y =
    16125341249761668633747631072985421243468892447965413965887015388783528206032;

  uint256 private constant vk_selector_commitments_commit_api_1_x =
    9369801914206014217445122279591521815449013590744042826207321226401456655568;
  uint256 private constant vk_selector_commitments_commit_api_1_y =
    20403534275150672051547282308343985824042675377185536764459718189767081861844;

  uint256 private constant vk_selector_commitments_commit_api_2_x =
    15085855314213485493120090850643571722324905485327671636383375594981366630616;
  uint256 private constant vk_selector_commitments_commit_api_2_y =
    9694401959517714837257605548316168182563966590362894961003011860886804708060;

  function load_vk_commitments_indices_commit_api(uint256[] memory v) internal pure {
    assembly {
      let _v := add(v, 0x20)

      mstore(_v, 22612113)
      _v := add(_v, 0x20)

      mstore(_v, 27501766)
      _v := add(_v, 0x20)

      mstore(_v, 60589135)
      _v := add(_v, 0x20)
    }
  }

  uint256 private constant vk_nb_commitments_commit_api = 3;

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

  // offset proof
  uint256 private constant proof_l_com_x = 0x20;
  uint256 private constant proof_l_com_y = 0x40;
  uint256 private constant proof_r_com_x = 0x60;
  uint256 private constant proof_r_com_y = 0x80;
  uint256 private constant proof_o_com_x = 0xa0;
  uint256 private constant proof_o_com_y = 0xc0;

  // h = h_0 + x^{n+2}h_1 + x^{2(n+2)}h_2
  uint256 private constant proof_h_0_x = 0xe0;
  uint256 private constant proof_h_0_y = 0x100;
  uint256 private constant proof_h_1_x = 0x120;
  uint256 private constant proof_h_1_y = 0x140;
  uint256 private constant proof_h_2_x = 0x160;
  uint256 private constant proof_h_2_y = 0x180;

  // wire values at zeta
  uint256 private constant proof_l_at_zeta = 0x1a0;
  uint256 private constant proof_r_at_zeta = 0x1c0;
  uint256 private constant proof_o_at_zeta = 0x1e0;

  //uint256[STATE_WIDTH-1] permutation_polynomials_at_zeta; // Sσ1(zeta),Sσ2(zeta)
  uint256 private constant proof_s1_at_zeta = 0x200; // Sσ1(zeta)
  uint256 private constant proof_s2_at_zeta = 0x220; // Sσ2(zeta)

  //Bn254.G1Point grand_product_commitment;                 // [z(x)]
  uint256 private constant proof_grand_product_commitment_x = 0x240;
  uint256 private constant proof_grand_product_commitment_y = 0x260;

  uint256 private constant proof_grand_product_at_zeta_omega = 0x280; // z(w*zeta)
  uint256 private constant proof_quotient_polynomial_at_zeta = 0x2a0; // t(zeta)
  uint256 private constant proof_linearised_polynomial_at_zeta = 0x2c0; // 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 = 0x2e0; // [Wzeta]
  uint256 private constant proof_batch_opening_at_zeta_y = 0x300;

  //Bn254.G1Point opening_at_zeta_omega_proof;      // [Wzeta*omega]
  uint256 private constant proof_opening_at_zeta_omega_x = 0x320;
  uint256 private constant proof_opening_at_zeta_omega_y = 0x340;

  uint256 private constant proof_openings_selector_commit_api_at_zeta = 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
  // Bn254.G1Point folded_digests;
  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)

  // read the commitments to the wires related to the commit api and store them in wire_commitments.
  // The commitments are points on Bn254(Fp) so they are stored on 2 uint256.
  function load_wire_commitments_commit_api(uint256[] memory wire_commitments, bytes memory proof) internal pure {
    assembly {
      let w := add(wire_commitments, 0x20)
      let p := add(proof, proof_openings_selector_commit_api_at_zeta)
      p := add(p, mul(vk_nb_commitments_commit_api, 0x20))
      for {
        let i := 0
      } lt(i, vk_nb_commitments_commit_api) {
        i := add(i, 1)
      } {
        // x coordinate
        mstore(w, mload(p))
        w := add(w, 0x20)
        p := add(p, 0x20)

        // y coordinate
        mstore(w, mload(p))
        w := add(w, 0x20)
        p := add(p, 0x20)
      }
    }
  }

  function derive_gamma_beta_alpha_zeta(
    bytes memory proof,
    uint256[] memory public_inputs
  ) internal view returns (uint256, uint256, uint256, uint256) {
    uint256 gamma;
    uint256 beta;
    uint256 alpha;
    uint256 zeta;

    assembly {
      let mem := mload(0x40)

      derive_gamma(proof, public_inputs)
      gamma := mload(mem)

      derive_beta(proof, gamma)
      beta := mload(mem)

      derive_alpha(proof, beta)
      alpha := mload(mem)

      derive_zeta(proof, alpha)
      zeta := mload(mem)

      gamma := mod(gamma, r_mod)
      beta := mod(beta, r_mod)
      alpha := mod(alpha, r_mod)
      zeta := mod(zeta, r_mod)

      function error_sha2_256() {
        let ptError := mload(0x40)
        mstore(ptError, error_string_id) // selector for function Error(string)
        mstore(add(ptError, 0x4), 0x20)
        mstore(add(ptError, 0x24), 0x19)
        mstore(add(ptError, 0x44), "error staticcall sha2-256")
        revert(ptError, 0x64)
      }

      // Derive gamma as Sha256(<transcript>)
      // where 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
      function derive_gamma(aproof, pub_inputs) {
        let mPtr := mload(0x40)

        // 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)

        let pi := add(pub_inputs, 0x20)
        let _mPtr := add(mPtr, 0x220)
        for {
          let i := 0
        } lt(i, mload(pub_inputs)) {
          i := add(i, 1)
        } {
          mstore(_mPtr, mload(pi))
          pi := add(pi, 0x20)
          _mPtr := add(_mPtr, 0x20)
        }

        let _proof := add(aproof, proof_openings_selector_commit_api_at_zeta)
        _proof := add(_proof, mul(vk_nb_commitments_commit_api, 0x20))
        for {
          let i := 0
        } lt(i, vk_nb_commitments_commit_api) {
          i := add(i, 1)
        } {
          mstore(_mPtr, mload(_proof))
          mstore(add(_mPtr, 0x20), mload(add(_proof, 0x20)))
          _mPtr := add(_mPtr, 0x40)
          _proof := add(_proof, 0x40)
        }

        mstore(_mPtr, mload(add(aproof, proof_l_com_x)))
        mstore(add(_mPtr, 0x20), mload(add(aproof, proof_l_com_y)))
        mstore(add(_mPtr, 0x40), mload(add(aproof, proof_r_com_x)))
        mstore(add(_mPtr, 0x60), mload(add(aproof, proof_r_com_y)))
        mstore(add(_mPtr, 0x80), mload(add(aproof, proof_o_com_x)))
        mstore(add(_mPtr, 0xa0), mload(add(aproof, proof_o_com_y)))

        let size := add(0x2c5, mul(mload(pub_inputs), 0x20)) // 0x2c5 = 22*32+5
        size := add(size, mul(vk_nb_commitments_commit_api, 0x40))
        let success := staticcall(sub(gas(), 2000), 0x2, add(mPtr, 0x1b), size, mPtr, 0x20) //0x1b -> 000.."gamma"
        if eq(success, 0) {
          error_sha2_256()
        }
      }

      function derive_beta(aproof, prev_challenge) {
        let mPtr := mload(0x40)
        // beta
        mstore(mPtr, 0x62657461) // "beta"
        mstore(add(mPtr, 0x20), prev_challenge)
        let success := staticcall(sub(gas(), 2000), 0x2, add(mPtr, 0x1c), 0x24, mPtr, 0x20) //0x1b -> 000.."gamma"
        if eq(success, 0) {
          error_sha2_256()
        }
      }

      // alpha depends on the previous challenge (beta) and on the commitment to the grand product polynomial
      function derive_alpha(aproof, prev_challenge) {
        let mPtr := mload(0x40)
        // alpha
        mstore(mPtr, 0x616C706861) // "alpha"
        mstore(add(mPtr, 0x20), prev_challenge)
        mstore(add(mPtr, 0x40), mload(add(aproof, proof_grand_product_commitment_x)))
        mstore(add(mPtr, 0x60), mload(add(aproof, proof_grand_product_commitment_y)))
        let success := staticcall(sub(gas(), 2000), 0x2, add(mPtr, 0x1b), 0x65, mPtr, 0x20) //0x1b -> 000.."gamma"
        if eq(success, 0) {
          error_sha2_256()
        }
      }

      // zeta depends on the previous challenge (alpha) and on the commitment to the quotient polynomial
      function derive_zeta(aproof, prev_challenge) {
        let mPtr := mload(0x40)
        // zeta
        mstore(mPtr, 0x7a657461) // "zeta"
        mstore(add(mPtr, 0x20), prev_challenge)
        mstore(add(mPtr, 0x40), mload(add(aproof, proof_h_0_x)))
        mstore(add(mPtr, 0x60), mload(add(aproof, proof_h_0_y)))
        mstore(add(mPtr, 0x80), mload(add(aproof, proof_h_1_x)))
        mstore(add(mPtr, 0xa0), mload(add(aproof, proof_h_1_y)))
        mstore(add(mPtr, 0xc0), mload(add(aproof, proof_h_2_x)))
        mstore(add(mPtr, 0xe0), mload(add(aproof, proof_h_2_y)))
        let success := staticcall(sub(gas(), 2000), 0x2, add(mPtr, 0x1c), 0xe4, mPtr, 0x20)
        if eq(success, 0) {
          error_sha2_256()
        }
      }
    }

    return (gamma, beta, alpha, zeta);
  }

  // Computes L_i(zeta) =  ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where:
  // * 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_ith_lagrange_at_z(uint256 zeta, uint256 i) internal view returns (uint256) {
    uint256 res;
    assembly {
      function error_pow_local() {
        let ptError := mload(0x40)
        mstore(ptError, error_string_id)
        mstore(add(ptError, 0x4), 0x20)
        mstore(add(ptError, 0x24), 0x17)
        mstore(add(ptError, 0x44), "error staticcall modexp")
        revert(ptError, 0x64)
      }

      // _n^_i [r]
      function pow_local(x, e) -> result {
        let mPtr := mload(0x40)
        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 success := staticcall(sub(gas(), 2000), 0x05, mPtr, 0xc0, 0x00, 0x20)
        if eq(success, 0) {
          error_pow_local()
        }
        result := mload(0x00)
      }

      let w := pow_local(vk_omega, i) // w**i
      i := addmod(zeta, sub(r_mod, w), r_mod) // z-w**i
      zeta := pow_local(zeta, vk_domain_size) // z**n
      zeta := addmod(zeta, sub(r_mod, 1), r_mod) // z**n-1
      w := mulmod(w, vk_inv_domain_size, r_mod) // w**i/n
      i := pow_local(i, sub(r_mod, 2)) // (z-w**i)**-1
      w := mulmod(w, i, r_mod) // w**i/n*(z-w)**-1
      res := mulmod(w, zeta, r_mod)
    }

    return res;
  }

  function compute_pi(
    uint256[] memory public_inputs,
    uint256 zeta,
    bytes memory proof
  ) internal view returns (uint256) {
    // evaluation of Z=Xⁿ⁻¹ at ζ
    // uint256 zeta_power_n_minus_one = Fr.pow(zeta, vk_domain_size);
    // zeta_power_n_minus_one = Fr.sub(zeta_power_n_minus_one, 1);
    uint256 zeta_power_n_minus_one;

    uint256 pi;

    assembly {
      function error_pow() {
        let ptError := mload(0x40)
        mstore(ptError, error_string_id) // selector for function Error(string)
        mstore(add(ptError, 0x4), 0x20)
        mstore(add(ptError, 0x24), 0x17)
        mstore(add(ptError, 0x44), "error staticcall modexp")
        revert(ptError, 0x64)
      }

      sum_pi_wo_api_commit(add(public_inputs, 0x20), mload(public_inputs), zeta)
      pi := mload(mload(0x40))

      function sum_pi_wo_api_commit(ins, n, z) {
        let li := mload(0x40)
        batch_compute_lagranges_at_z(z, n, li)
        let res := 0
        let tmp := 0
        for {
          let i := 0
        } lt(i, n) {
          i := add(i, 1)
        } {
          tmp := mulmod(mload(li), mload(ins), r_mod)
          res := addmod(res, tmp, r_mod)
          li := add(li, 0x20)
          ins := add(ins, 0x20)
        }
        mstore(mload(0x40), res)
      }

      // mPtr <- [L_0(z), .., L_{n-1}(z)]
      //
      // Here L_i(zeta) =  ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where:
      // * n = vk_domain_size
      // * ω = vk_omega (generator of the multiplicative cyclic group of order n in (ℤ/rℤ)*)
      // * ζ = zeta (challenge derived with Fiat Shamir)
      function batch_compute_lagranges_at_z(z, n, mPtr) {
        let zn := addmod(pow(z, vk_domain_size, mPtr), sub(r_mod, 1), r_mod)
        zn := mulmod(zn, vk_inv_domain_size, r_mod)
        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)
        }
      }

      // batch invert (modulo r) in place the nb_ins uint256 inputs starting at ins.
      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)
        }
      }

      // 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 success := staticcall(sub(gas(), 2000), 0x05, mPtr, 0xc0, mPtr, 0x20)
        if eq(success, 0) {
          error_pow()
        }
        res := mload(mPtr)
      }

      zeta_power_n_minus_one := pow(zeta, vk_domain_size, mload(0x40))
      zeta_power_n_minus_one := addmod(zeta_power_n_minus_one, sub(r_mod, 1), r_mod)
    }

    // compute the contribution of the public inputs whose indices are in commitment_indices,
    // and whose value is hash_fr of the corresponding commitme
    uint256[] memory commitment_indices = new uint256[](vk_nb_commitments_commit_api);
    load_vk_commitments_indices_commit_api(commitment_indices);

    uint256[] memory wire_committed_commitments;
    wire_committed_commitments = new uint256[](2 * vk_nb_commitments_commit_api);

    load_wire_commitments_commit_api(wire_committed_commitments, proof);

    for (uint256 i = 0; i < vk_nb_commitments_commit_api; i++) {
      uint256 hash_res = Utils.hash_fr(wire_committed_commitments[2 * i], wire_committed_commitments[2 * i + 1]);
      uint256 a = compute_ith_lagrange_at_z(zeta, commitment_indices[i] + public_inputs.length);
      assembly {
        a := mulmod(hash_res, a, r_mod)
        pi := addmod(pi, a, r_mod)
      }
    }

    return pi;
  }

  function check_inputs_size(uint256[] memory public_inputs) internal pure {
    bool input_checks = true;
    assembly {
      let s := mload(public_inputs)
      let p := add(public_inputs, 0x20)
      for {
        let i
      } lt(i, s) {
        i := add(i, 1)
      } {
        input_checks := and(input_checks, lt(mload(p), r_mod))
        p := add(p, 0x20)
      }
    }
    require(input_checks, "some inputs are bigger than r");
  }

  function check_proof_size(bytes memory proof) internal pure {
    uint256 expected_proof_size = 0x340 + vk_nb_commitments_commit_api * 0x60;
    uint256 actual_proof_size;
    assembly {
      actual_proof_size := mload(proof)
    }
    require(actual_proof_size == expected_proof_size, "wrong proof size");
  }

  function check_proof_openings_size(bytes memory proof) internal pure {
    bool openings_check = true;
    assembly {
      // linearised polynomial at zeta
      let p := add(proof, proof_linearised_polynomial_at_zeta)
      openings_check := and(openings_check, lt(mload(p), r_mod))

      // quotient polynomial at zeta
      p := add(proof, proof_quotient_polynomial_at_zeta)
      openings_check := and(openings_check, lt(mload(p), r_mod))

      // proof_l_at_zeta
      p := add(proof, proof_l_at_zeta)
      openings_check := and(openings_check, lt(mload(p), r_mod))

      // proof_r_at_zeta
      p := add(proof, proof_r_at_zeta)
      openings_check := and(openings_check, lt(mload(p), r_mod))

      // proof_o_at_zeta
      p := add(proof, proof_o_at_zeta)
      openings_check := and(openings_check, lt(mload(p), r_mod))

      // proof_s1_at_zeta
      p := add(proof, proof_s1_at_zeta)
      openings_check := and(openings_check, lt(mload(p), r_mod))

      // proof_s2_at_zeta
      p := add(proof, proof_s2_at_zeta)
      openings_check := and(openings_check, lt(mload(p), r_mod))

      // proof_grand_product_at_zeta_omega
      p := add(proof, proof_grand_product_at_zeta_omega)
      openings_check := and(openings_check, lt(mload(p), r_mod))

      // proof_openings_selector_commit_api_at_zeta

      p := add(proof, proof_openings_selector_commit_api_at_zeta)
      for {
        let i := 0
      } lt(i, vk_nb_commitments_commit_api) {
        i := add(i, 1)
      } {
        openings_check := and(openings_check, lt(mload(p), r_mod))
        p := add(p, 0x20)
      }
    }
    require(openings_check, "some openings are bigger than r");
  }

  function Verify(bytes memory proof, uint256[] memory public_inputs) public view returns (bool) {
    check_inputs_size(public_inputs);
    check_proof_size(proof);
    check_proof_openings_size(proof);

    uint256 gamma;
    uint256 beta;
    uint256 alpha;
    uint256 zeta;

    (gamma, beta, alpha, zeta) = derive_gamma_beta_alpha_zeta(proof, public_inputs);

    uint256 pi = compute_pi(public_inputs, zeta, proof);

    uint256 check;

    bool success = false;
    // uint256 success;

    assembly {
      let mem := mload(0x40)
      mstore(add(mem, state_alpha), alpha)
      mstore(add(mem, state_gamma), gamma)
      mstore(add(mem, state_zeta), zeta)
      mstore(add(mem, state_beta), beta)
      mstore(add(mem, state_pi), pi)

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

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

      check := mload(add(mem, state_check_var))

      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)
      }

      // 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)

        // zeta**n - 1
        let res := pow(mload(add(state, state_zeta)), vk_domain_size, mPtr)
        res := addmod(res, sub(r_mod, 1), r_mod)
        mstore(add(state, state_zeta_power_n_minus_one), res)

        // 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)
      }

      // 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]
      function batch_verify_multi_points(aproof) {
        let state := mload(0x40)
        let mPtr := add(state, state_last_mem)

        // here the random is not a challenge, hence no need to use Fiat Shamir, we just
        // need an unpredictible result.
        let random := mod(keccak256(state, 0x20), r_mod)

        let folded_quotients := mPtr
        mPtr := add(folded_quotients, 0x40)
        mstore(folded_quotients, mload(add(aproof, proof_batch_opening_at_zeta_x)))
        mstore(add(folded_quotients, 0x20), mload(add(aproof, proof_batch_opening_at_zeta_y)))
        point_acc_mul(folded_quotients, add(aproof, proof_opening_at_zeta_omega_x), random, mPtr)

        let folded_digests := add(state, state_folded_digests_x)
        point_acc_mul(folded_digests, add(aproof, proof_grand_product_commitment_x), random, mPtr)

        let folded_evals := add(state, state_folded_claimed_values)
        fr_acc_mul(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, 14312776538779914388377568895031746459131577658076416373430523308756343304251)
        mstore(
          add(folded_evals_commit, 0x20),
          11763105256161367503191792604679297387056316997144156930871823008787082098465
        )
        mstore(add(folded_evals_commit, 0x40), mload(folded_evals))
        let check_staticcall := staticcall(sub(gas(), 2000), 7, folded_evals_commit, 0x60, folded_evals_commit, 0x40)
        if eq(check_staticcall, 0) {
          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(
          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(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)
      }

      // check_pairing_kzg checks the result of the final pairing product of the batched
      // kzg verification. The purpose of this function is too avoid exhausting the stack
      // in the function batch_verify_multi_points.
      // 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(sub(gas(), 2000), 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)
      }

      // 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}(ζ)
      // acc_gamma stores the γⁱ
      function fold_state(aproof) {
        let state := mload(0x40)
        let mPtr := add(mload(0x40), state_last_mem)

        let l_gamma_kzg := mload(add(state, state_gamma_kzg))
        let acc_gamma := l_gamma_kzg

        let offset := add(0x200, mul(vk_nb_commitments_commit_api, 0x40)) // 0x40 = 2*0x20
        let mPtrOffset := add(mPtr, offset)

        mstore(add(state, state_folded_digests_x), mload(add(mPtr, 0x40)))
        mstore(add(state, state_folded_digests_y), mload(add(mPtr, 0x60)))
        mstore(add(state, state_folded_claimed_values), mload(add(aproof, proof_quotient_polynomial_at_zeta)))

        point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x80), acc_gamma, mPtrOffset)
        fr_acc_mul(add(state, state_folded_claimed_values), add(aproof, proof_linearised_polynomial_at_zeta), acc_gamma)
        mstore(add(state, state_check_var), acc_gamma)

        acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod)
        point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0xc0), acc_gamma, mPtrOffset)
        fr_acc_mul(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(add(state, state_folded_digests_x), add(mPtr, 0x100), acc_gamma, add(mPtr, offset))
        fr_acc_mul(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(add(state, state_folded_digests_x), add(mPtr, 0x140), acc_gamma, add(mPtr, offset))
        fr_acc_mul(add(state, state_folded_claimed_values), add(aproof, proof_o_at_zeta), acc_gamma)

        acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod)
        point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x180), acc_gamma, add(mPtr, offset))
        fr_acc_mul(add(state, state_folded_claimed_values), add(aproof, proof_s1_at_zeta), acc_gamma)

        acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod)
        point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x1c0), acc_gamma, add(mPtr, offset))
        fr_acc_mul(add(state, state_folded_claimed_values), add(aproof, proof_s2_at_zeta), acc_gamma)

        let poscaz := add(aproof, proof_openings_selector_commit_api_at_zeta)
        let opca := add(mPtr, 0x200) // offset_proof_commits_api
        for {
          let i := 0
        } lt(i, vk_nb_commitments_commit_api) {
          i := add(i, 1)
        } {
          acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod)
          point_acc_mul(add(state, state_folded_digests_x), opca, acc_gamma, add(mPtr, offset))
          fr_acc_mul(add(state, state_folded_claimed_values), poscaz, acc_gamma)
          poscaz := add(poscaz, 0x20)
          opca := add(opca, 0x40)
        }
      }

      // 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}(ζ)
      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)))
        mstore(add(mPtr, 0xc0), mload(add(aproof, proof_l_com_x)))
        mstore(add(mPtr, 0xe0), mload(add(aproof, proof_l_com_y)))
        mstore(add(mPtr, 0x100), mload(add(aproof, proof_r_com_x)))
        mstore(add(mPtr, 0x120), mload(add(aproof, proof_r_com_y)))
        mstore(add(mPtr, 0x140), mload(add(aproof, proof_o_com_x)))
        mstore(add(mPtr, 0x160), mload(add(aproof, proof_o_com_y)))
        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_selector_commitments_commit_api_0_x)
        mstore(add(mPtr, add(offset, 0x20)), vk_selector_commitments_commit_api_0_y)
        offset := add(offset, 0x40)

        mstore(add(mPtr, offset), vk_selector_commitments_commit_api_1_x)
        mstore(add(mPtr, add(offset, 0x20)), vk_selector_commitments_commit_api_1_y)
        offset := add(offset, 0x40)

        mstore(add(mPtr, offset), vk_selector_commitments_commit_api_2_x)
        mstore(add(mPtr, add(offset, 0x20)), vk_selector_commitments_commit_api_2_y)
        offset := add(offset, 0x40)

        mstore(add(mPtr, offset), mload(add(aproof, proof_quotient_polynomial_at_zeta)))
        mstore(add(mPtr, add(offset, 0x20)), mload(add(aproof, proof_linearised_polynomial_at_zeta)))
        mstore(add(mPtr, add(offset, 0x40)), mload(add(aproof, proof_l_at_zeta)))
        mstore(add(mPtr, add(offset, 0x60)), mload(add(aproof, proof_r_at_zeta)))
        mstore(add(mPtr, add(offset, 0x80)), mload(add(aproof, proof_o_at_zeta)))
        mstore(add(mPtr, add(offset, 0xa0)), mload(add(aproof, proof_s1_at_zeta)))
        mstore(add(mPtr, add(offset, 0xc0)), mload(add(aproof, proof_s2_at_zeta)))

        let _mPtr := add(mPtr, add(offset, 0xe0))
        let _poscaz := add(aproof, proof_openings_selector_commit_api_at_zeta)
        for {
          let i := 0
        } lt(i, vk_nb_commitments_commit_api) {
          i := add(i, 1)
        } {
          mstore(_mPtr, mload(_poscaz))
          _poscaz := add(_poscaz, 0x20)
          _mPtr := add(_mPtr, 0x20)
        }

        let start_input := 0x1b // 00.."gamma"
        let size_input := add(0x16, mul(vk_nb_commitments_commit_api, 3)) // number of 32bytes elmts = 0x16 (zeta+2*7+7 for the digests+openings) + 2*vk_nb_commitments_commit_api (for the commitments of the selectors) + vk_nb_commitments_commit_api (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(
          sub(gas(), 2000),
          0x2,
          add(mPtr, start_input),
          size_input,
          add(state, state_gamma_kzg),
          0x20
        )
        if eq(check_staticcall, 0) {
          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, mload(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,
          mload(add(aproof, proof_r_at_zeta)),
          add(mPtr, 0x40)
        )

        let rl := mulmod(mload(add(aproof, proof_l_at_zeta)), mload(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,
          mload(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_openings_selector_commit_api_at_zeta)
        let commits_api := add(
          aproof,
          add(proof_openings_selector_commit_api_at_zeta, mul(vk_nb_commitments_commit_api, 0x20))
        )
        for {
          let i := 0
        } lt(i, vk_nb_commitments_commit_api) {
          i := add(i, 1)
        } {
          mstore(mPtr, mload(commits_api))
          mstore(add(mPtr, 0x20), mload(add(commits_api, 0x20)))
          point_acc_mul(add(state, state_linearised_polynomial_x), mPtr, mload(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, mload(add(aproof, proof_grand_product_commitment_x)))
        mstore(add(mPtr, 0x20), mload(add(aproof, proof_grand_product_commitment_y)))
        point_acc_mul(add(state, state_linearised_polynomial_x), mPtr, s2, add(mPtr, 0x40))
      }

      // 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))
      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(mload(add(aproof, proof_grand_product_at_zeta_omega)), l_beta, r_mod)
        let v := mulmod(l_beta, mload(add(aproof, proof_s1_at_zeta)), r_mod)
        v := addmod(v, mload(add(aproof, proof_l_at_zeta)), r_mod)
        v := addmod(v, l_gamma, r_mod)

        let w := mulmod(l_beta, mload(add(aproof, proof_s2_at_zeta)), r_mod)
        w := addmod(w, mload(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, mload(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, mload(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, mload(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)
      }

      // compute H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ and store the result at
      // state + state_folded_h
      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(add(state, state_folded_h_x), add(aproof, proof_h_2_x), zeta_power_n_plus_two, mPtr)
        point_add(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(add(state, state_folded_h_x), add(state, state_folded_h_x), add(aproof, proof_h_0_x), mPtr)
      }

      // 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(ζ)
      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(mload(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), mload(add(aproof, proof_l_at_zeta)), r_mod))

        // (r(ζ)+β*s2(ζ)+γ)
        let s2 := add(s1, 0x20)
        mstore(s2, mulmod(mload(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), mload(add(aproof, proof_r_at_zeta)), r_mod))
        // _s2 := mload(s2)

        // (o(ζ)+γ)
        let o := add(s1, 0x40)
        mstore(o, addmod(mload(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), mload(add(aproof, proof_grand_product_at_zeta_omega)), r_mod))

        let computed_quotient := add(s1, 0x60)

        // linearizedpolynomial + pi(zeta)
        mstore(
          computed_quotient,
          addmod(mload(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(
            mload(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)))
      }

      function point_add(dst, p, q, mPtr) {
        // let mPtr := add(mload(0x40), state_last_mem)
        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(sub(gas(), 2000), 6, mPtr, 0x80, dst, 0x40)
        mstore(add(state, state_success), and(l_success, mload(add(state, state_success))))
      }

      // dst <- [s]src
      function point_mul(dst, src, s, mPtr) {
        // let mPtr := add(mload(0x40), state_last_mem)
        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(sub(gas(), 2000), 7, mPtr, 0x60, dst, 0x40)
        mstore(add(state, state_success), and(l_success, mload(add(state, state_success))))
      }

      // dst <- dst + [s]src (Elliptic curve)
      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(sub(gas(), 2000), 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(sub(gas(), 2000), 6, mPtr, 0x80, dst, 0x40))
        mstore(add(state, state_success), and(l_success, mload(add(state, state_success))))
      }

      // dst <- dst + src (Fr) dst,src are addresses, s is a value
      function fr_acc_mul(dst, src, s) {
        let tmp := mulmod(mload(src), s, r_mod)
        mstore(dst, addmod(mload(dst), tmp, r_mod))
      }

      // dst <- x ** e mod r (x, e are values, not pointers)
      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(sub(gas(), 2000), 0x05, mPtr, 0xc0, mPtr, 0x20)
        if eq(check_staticcall, 0) {
          error_verify()
        }
        res := mload(mPtr)
      }
    }

    return success;
  }
}

// SPDX-License-Identifier: AGPL-3.0

// It has not been audited and is provided as-is, we make no guarantees or warranties to its safety and reliability.
//
// According to https://eprint.iacr.org/archive/2019/953/1585767119.pdf
pragma solidity ^0.8.19;

library Utils {
  uint256 private constant r_mod = 21888242871839275222246405745257275088548364400416034343698204186575808495617;

  /**
   * @dev ExpandMsgXmd expands msg to a slice of lenInBytes bytes.
   *      https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-5
   *      https://tools.ietf.org/html/rfc8017#section-4.1 (I2OSP/O2ISP)
   */
  function expand_msg(uint256 x, uint256 y) public pure returns (uint8[48] memory res) {
    string memory dst = "BSB22-Plonk";

    //uint8[64] memory pad; // 64 is sha256 block size.
    // sha256(pad || msg || (0 || 48 || 0) || dst || 11)
    bytes memory tmp;
    uint8 zero = 0;
    uint8 lenInBytes = 48;
    uint8 sizeDomain = 11; // size of dst

    for (uint i = 0; i < 64; i++) {
      tmp = abi.encodePacked(tmp, zero);
    }
    tmp = abi.encodePacked(tmp, x, y, zero, lenInBytes, zero, dst, sizeDomain);
    bytes32 b0 = sha256(tmp);

    tmp = abi.encodePacked(b0, uint8(1), dst, sizeDomain);
    bytes32 b1 = sha256(tmp);
    for (uint i = 0; i < 32; i++) {
      res[i] = uint8(b1[i]);
    }

    tmp = abi.encodePacked(uint8(b0[0]) ^ uint8(b1[0]));
    for (uint i = 1; i < 32; i++) {
      tmp = abi.encodePacked(tmp, uint8(b0[i]) ^ uint8(b1[i]));
    }

    tmp = abi.encodePacked(tmp, uint8(2), dst, sizeDomain);
    b1 = sha256(tmp);

    // TODO handle the size of the dst (check gnark-crypto)
    for (uint i = 0; i < 16; i++) {
      res[i + 32] = uint8(b1[i]);
    }

    return res;
  }

  /**
   * @dev cf https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-5.2
   * corresponds to https://github.com/ConsenSys/gnark-crypto/blob/develop/ecc/bn254/fr/element.go
   */
  function hash_fr(uint256 x, uint256 y) internal pure returns (uint256 res) {
    // interpret a as a bigEndian integer and reduce it mod r
    uint8[48] memory xmsg = expand_msg(x, y);
    // uint8[48] memory xmsg = [0x44, 0x74, 0xb5, 0x29, 0xd7, 0xfb, 0x29, 0x88, 0x3a, 0x7a, 0xc1, 0x65, 0xfd, 0x72, 0xce, 0xd0, 0xd4, 0xd1, 0x3f, 0x9e, 0x85, 0x8a, 0x3, 0x86, 0x1c, 0x90, 0x83, 0x1e, 0x94, 0xdc, 0xfc, 0x1d, 0x70, 0x82, 0xf5, 0xbf, 0x30, 0x3, 0x39, 0x87, 0x21, 0x38, 0x15, 0xed, 0x12, 0x75, 0x44, 0x6a];

    // reduce xmsg mod r, where xmsg is intrepreted in big endian
    // (as SetBytes does for golang's Big.Int library).
    for (uint i = 0; i < 32; i++) {
      res += uint256(xmsg[47 - i]) << (8 * i);
    }
    res = res % r_mod;
    uint256 tmp;
    for (uint i = 0; i < 16; i++) {
      tmp += uint256(xmsg[15 - i]) << (8 * i);
    }

    // 2**256%r
    uint256 b = 6350874878119819312338956282401532410528162663560392320966563075034087161851;
    assembly {
      tmp := mulmod(tmp, b, r_mod)
      res := addmod(res, tmp, r_mod)
    }

    return res;
  }
}

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

Contract Security Audit

Contract ABI

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

608060405234801561001057600080fd5b50613280806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80637e4f7a8a14610030575b600080fd5b61004361003e366004612e5b565b610057565b604051901515815260200160405180910390f35b6000610062826117d8565b61006b83611896565b6100748361191e565b6000806000806100848787611a4a565b92965090945092509050600061009b87838a611f61565b905060008060009050604051856000820152876040820152846060820152866020820152836101808201526100ce610179565b6100d78b611352565b6100e08b6112e2565b6100e98b610efc565b6100f28b61091f565b6100fb8b6106ac565b6101048b610324565b6101e081015191506102008101519250506117c9565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f6572726f722076657269667900000000000000000000000000000000000000006044820152606481fd5b604051610220604051016101968163040000006060850151611761565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000082089050806101a08401527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103606085015108610259837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffff83611761565b90507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e66c81e03716be83b486d6feabcc7ddd0fe6cbf5e72d585d142f7829b05820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018282098451935091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001905082820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018282099050806080840152505050565b60405161022081017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016020832006816040810192506102e08501518152610300850151602082015261037c83836103208801846116b1565b610140840161039184846102408901846116b1565b61012085016103a58461028089018361170f565b7f1fa4be93b5e7f7e674d5059b63554fab99638b304ed8310e9fa44c281ac9b03b85527f1a01ae7fac6228e39d3cb5a5e71fd31160f3241e79a5f48ffb3737e6c389b7216020860152805160408087019182529095908160608160076107d05a03fa9150816104165761041661011a565b60208101915081517f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4703825261044e86828586611630565b50508360408501945061046b8560608801516102e08a0184611676565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f1067569af1ff73b20113eff9b8d89d4a605b52b63d68f9ae1c79bd572f4e921260608801510995507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000186850993506104eb85856103208a01846116b1565b6104f785828485611630565b50602082810180517f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd470381528251865291810151908501527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c260408501527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed60608501527f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b60808501527f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa60a0850152905160c0840152805160e08401527f22f1acbb03c4508760c2430af35865e7cdf9f3eb1224504fdcc3708ddb954a486101008401527f2a344fad01c2ed0ed73142ae1752429eaea515c6f3f6b941103cc21c2308e1cb6101208401527f159f15b842ba9c8449aa3268f981010d4c7142e5193473d80b464e964845c3f86101408401527f0efd30ac7b6f8d0d3ccbc2207587c2acbad1532dc0293f0d034cf8258cd428b3610160840152925061067e905081610683565b505050565b604051602060006101808460086107d05a03fa6000516101e09290920180519190921616905250565b604051610220604051016101c082015180604060030261020001808401604085015161014087015260608501516101608701526102a08701516101208701526106fe8184608088016101408a016116b1565b610711836102c08901610120890161170f565b826102008701527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018484099250610751818460c088016101408a016116b1565b50610765826101a08801610120880161170f565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183830991506107a181850183610100870161014089016116b1565b6107b4826101c08801610120880161170f565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183830991506107f081850183610140870161014089016116b1565b610803826101e08801610120880161170f565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001838309915061083f81850183610180870161014089016116b1565b610852826102008801610120880161170f565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001838309915061088e818501836101c0870161014089016116b1565b6108a1826102208801610120880161170f565b6103608601610200850160005b6003811015610914577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000186860994506108ef84880186846101408c016116b1565b6108fe85846101208b0161170f565b60209290920191604091909101906001016108ae565b505050505050505050565b604080516467616d6d6161022080830191825260608084015161024085015260a08085015161026086015260c08086015161028087015260e08601516102a0808801919091526101008701516102c08089019190915260208a01516102e0890152978901516103008801529288015161032087015260808801516103408701529087015161036080870191909152908701516103808601527f1f84d6f80352f2fc7729113df20d64103bf3eae4204ce683c4b29834a19235606103a08601527f2e837b96567130647c44cbfdc512df731f0d589d87c364505b5d8bf52f326f546103c08601527f0240cbafaea4c9e736b868b0d9095395f8713efb4436b300ffa4cf87664317e26103e08601527f01157cddb161e79d457e57d106481f3aa7ade784703b5b29eda8083f032e709c6104008601527f302bc66f2018948d97055fb3a7514e487907e5e5e9e51be44364ccf77201cfa26104208601527f23a69ea5749b5e0a77ebee412a7fc5cad7e6b4cbcc43e3724d61dbee91555ed06104408601527f14b71eb4e8b16bb39ef3181b796737f9f7f3e1a1e61a5b3f5d5ba75aa3690cd06104608601527f2d1bfdbc4aa010cc9248ffa1b7b75dd1c758494451a97310de4e03ee420822d46104808601527f215a4aa132d8397759a1f4b4510e813338a3111fef6a78700ec0a50c5ee308d86104a08601527f156ed649c13a1ce23db99bdbea2392a0c3deb1286a7d4d792fc1e01163833adc6104c0860152908601516104e0850152938501516105008401526101a08501516105208401526101c08501516105408401526101e08501516105608401526102008501516105808401528401516105a083015290916105c0830190840160005b6003811015610bb6578151835260209283019290910190600101610b97565b50601b91506103e5905060206101c085018285850160026107d05a03fa9250505060008103610be757610be761011a565b506101c00180517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019006905250565b604051610220604051017f1de747c8b08f6e8135b238f73f2136d5dd9b23dcc5382f08c4130149c8ab4f5881527f24aaeb1ab69d8d9baf5146abaa1cfaffd933a0142b006a4beea229a5486a826f6020820152610c80604082016101a08501518360e08601611676565b7f1f47ced88f79e9a283779f60440e4b544dceb61cf7b0dc400ae00c87823deae581527f1e9a6e10cea071afe356efe7bbd87641f3aa129a0a14997640e06e0102b66cf96020820152610ce0604082016101c08501518360e086016116b1565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101c08401516101a0850151097f1de5b062e1c03586e2609cc3edda81bb8dd47468442c9d0a83d4043317cc449182527f1dd50545f38cf0b0c17eaa7c0646e6faa05a8f31d2d27e5835ec0461f50c84b16020830152610d6960408301828460e087016116b1565b507f0f9c3581e08299d620cd366d1473887a2f7623dfc2803bb53398a93a29c8cea581527f25e09ffadafa8cb36dd495dbc07a755617a4d7ad790893ad396904d9fb5eda916020820152610dca604082016101e08501518360e086016116b1565b7f21a42d83c31cff7c252fdbe33ed5d37e519ed002253a314b1ffe1374c7351be281527f1b878ed0d031c3333f1ae0cfbb47690afb6f4457e1dfd784bcd7ec22b952e6ec6020820152610e25604082018260e0850180611630565b61036083016103c0840160005b6003811015610e72578151845260208201516020850152610e5c6040850184518660e089016116b1565b6020929092019160409190910190600101610e32565b5050507f0690a2dbb7358db0cb77d75c272330856af7e51382a386ca0b67bb03e96b48f481527f2397e85bb92816bd9c0a98fc6d9c38255cc0358c1b656a463438821223d18d1a6020820152610ed060408201858360e086016116b1565b61024083015181526102608301516020820152610ef560408201868360e086016116b1565b5050505050565b60405160208101516040820151606083015160008401517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000184610280880151097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161020088015186097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101a0890151820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161022089015187097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101c08a0151820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000186820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018284097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001600580097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001878a0998507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101a08c01518a0894507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000188860894507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160058a0993507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101c08c0151850893507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000188850893507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001818a099250507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101e08b0151830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000187830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183850997507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018289097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103985085890997507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160808a0151890897506112d688828c610c16565b50505050505050505050565b60405160026304000000016102206040510161130381836060860151611761565b91506113188183610160870160a08701611676565b61132b81610120860160a0860180611630565b61133a818360a0860180611676565b61134c8160e0860160a0860180611630565b50505050565b604051610220604051017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160208301516102008501510981527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001604083015182510881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101a08401518251088152602081017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160208401516102208601510981527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001604084015182510881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101c08501518251088152604082017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160408501516101e08701510881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001825184510983527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001815184510980845284517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019250900982527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016102808501518351098252606082017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101808501516102c08701510881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001835182510881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160808501517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000010382510881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101a08501516102a087015109825281518151146101e08501525050505050565b6040518251855260208301516020860152835160408601526020840151606086015260408260808760066107d05a03fa6101e082015181166101e0830152505050505050565b604051825185526020830151602086015283604086015260408260608760076107d05a03fa6101e082015181166101e0830152505050505050565b604051825185526020830151602086015283604086015260408560608760076107d05a03fa825160408701526020830151606087015260408360808860066107d05a03fa811690506101e082015181166101e0830152505050505050565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001838351097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181835108825250505050565b602083526020808401526020604084015280606084015250806080830152507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160a0820152600060208260c08460056107d05a03fa806117c3576117c361011a565b50505190565b96505050505050505b92915050565b80516001906020830160005b828110156118235781517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000011193909316926020909101906001016117e4565b50505080611892576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f736f6d6520696e707574732061726520626967676572207468616e207200000060448201526064015b60405180910390fd5b5050565b60006118a460036060612f60565b6118b090610340612f77565b825190915080821461067e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f77726f6e672070726f6f662073697a65000000000000000000000000000000006044820152606401611889565b6102c08101516102a08201516101a08301516101c08401516101e08501516102008601516102208701516102808801517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908111918111928111938111948111958111968111971096909616949094169290921616161616600116610360820160005b60038110156119e15781517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000011192909216916020909101906001016119a1565b505080611892576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f736f6d65206f70656e696e67732061726520626967676572207468616e2072006044820152606401611889565b600080600080600080600080604051611a638a8c611b26565b80519450611a71858c611e68565b80519350611a7f848c611e99565b80519250611a8d838c611ee0565b517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000194859006949384900693928390069290069050611f51565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f6572726f722073746174696363616c6c20736861322d323536000000000000006044820152606481fd5b6040516467616d6d6181527f1f84d6f80352f2fc7729113df20d64103bf3eae4204ce683c4b29834a192356060208201527f2e837b96567130647c44cbfdc512df731f0d589d87c364505b5d8bf52f326f5460408201527f0240cbafaea4c9e736b868b0d9095395f8713efb4436b300ffa4cf87664317e260608201527f01157cddb161e79d457e57d106481f3aa7ade784703b5b29eda8083f032e709c60808201527f0690a2dbb7358db0cb77d75c272330856af7e51382a386ca0b67bb03e96b48f460a08201527f2397e85bb92816bd9c0a98fc6d9c38255cc0358c1b656a463438821223d18d1a60c08201527f1de747c8b08f6e8135b238f73f2136d5dd9b23dcc5382f08c4130149c8ab4f5860e08201527f24aaeb1ab69d8d9baf5146abaa1cfaffd933a0142b006a4beea229a5486a826f6101008201527f1f47ced88f79e9a283779f60440e4b544dceb61cf7b0dc400ae00c87823deae56101208201527f1e9a6e10cea071afe356efe7bbd87641f3aa129a0a14997640e06e0102b66cf96101408201527f1de5b062e1c03586e2609cc3edda81bb8dd47468442c9d0a83d4043317cc44916101608201527f1dd50545f38cf0b0c17eaa7c0646e6faa05a8f31d2d27e5835ec0461f50c84b16101808201527f0f9c3581e08299d620cd366d1473887a2f7623dfc2803bb53398a93a29c8cea56101a08201527f25e09ffadafa8cb36dd495dbc07a755617a4d7ad790893ad396904d9fb5eda916101c08201527f21a42d83c31cff7c252fdbe33ed5d37e519ed002253a314b1ffe1374c7351be26101e08201527f1b878ed0d031c3333f1ae0cfbb47690afb6f4457e1dfd784bcd7ec22b952e6ec61020082015260208301610220820160005b8551811015611dc5578251825260209283019290910190600101611da6565b506103c08401915060005b6003811015611df957825182526020808401519083015260409283019290910190600101611dd0565b506020840151815260408401516020820152606084015160408201526080840151606082015260a0840151608082015260c084015160a0820152505060208351026102c50160406003028101905060208282601b850160026107d05a03fa915081905061067e5761067e611ac7565b50604051636265746181528160208201526020816024601c840160026107d05a03fa90508061189257611892611ac7565b60405164616c7068618152826020820152610240820151604082015261026082015160608201526020816065601b840160026107d05a03fa90508061067e5761067e611ac7565b604051637a657461815282602082015260e082015160408201526101008201516060820152610120820151608082015261014082015160a082015261016082015160c082015261018082015160e082015260208160e4601c840160026107d05a03fa90508061067e5761067e611ac7565b9299919850965090945092505050565b6000806000611fca565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f6572726f722073746174696363616c6c206d6f646578700000000000000000006044820152606481fd5b611fd985875160208901611fe3565b50604051516123fa565b604051611ff181848661206c565b60008060005b85811015612060577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001855185510991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182840860209586019594909401939250600101611ff7565b50506040515250505050565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000006120bd85630400000085612398565b087f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e66c81e03716be83b486d6feabcc7ddd0fe6cbf5e72d585d142f7829b058209905060018460005b858110156121af577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103860882527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f1067569af1ff73b20113eff9b8d89d4a605b52b63d68f9ae1c79bd572f4e9212840992506020919091019060010161210a565b506121bb818688612273565b50600190508460005b8581101561226a577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001868551090982526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f1067569af1ff73b20113eff9b8d89d4a605b52b63d68f9ae1c79bd572f4e9212840992506001016121c4565b50505050505050565b600183526000805b838110156122c95781850151828401517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830990506020840193508084880152505060018101905061227b565b50602081038201915080840193505061230a6020840160027f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001038551612398565b60005b83811015610ef55760208503945082517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018651840984527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018184097fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09094019392505060010161230d565b602083526020808401526020604084015280606084015250806080830152507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160a0820152600060208260c08460056107d05a03fa806117c3576117c3611f6b565b61240b604051630400000087612398565b91507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000830860408051600380825260808201909252919350600091906020820160608036833701905050630159089160208201526301a3a4c6604082015263039c844f6060820152905060606124a460036002612f60565b67ffffffffffffffff8111156124bc576124bc612d5d565b6040519080825280602002602001820160405280156124e5578160200160208202803683370190505b5090506124f28187612600565b60005b60038110156125f357600061255d8361250f846002612f60565b8151811061251f5761251f612f8a565b6020026020010151848460026125359190612f60565b612540906001612f77565b8151811061255057612550612f8a565b6020026020010151612635565b905060006125908a8c5187868151811061257957612579612f8a565b602002602001015161258b9190612f77565b6127a0565b90507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018187089550505080806125eb90612fb9565b9150506124f5565b5091979650505050505050565b602082016103c0820160005b6003811015610ef55781518352602080830151908401526040928301929091019060010161260c565b60008061264284846129a6565b905060005b60208110156126a05761265b816008612f60565b8261266783602f612ff1565b6030811061267757612677612f8a565b602002015160ff16901b8361268c9190612f77565b92508061269881612fb9565b915050612647565b506126cb7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183613004565b91506000805b601081101561272a576126e5816008612f60565b836126f183600f612ff1565b6030811061270157612701612f8a565b602002015160ff16901b826127169190612f77565b91508061272281612fb9565b9150506126d1565b507f0e0a77c19a07df2f666ea36f7879462e36fc76959f60cd29ac96341c4ffffffb7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018285089695505050505050565b600080612819565b600060405160208152602080820152602060408201528260608201528360808201527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160a08201526020600060c08360056107d05a03fa90508061280e5761280e611f6b565b505060005192915050565b612843837f1067569af1ff73b20113eff9b8d89d4a605b52b63d68f9ae1c79bd572f4e92126127a8565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001817f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103860893506128996304000000866127a8565b94507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000860894507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e66c81e03716be83b486d6feabcc7ddd0fe6cbf5e72d585d142f7829b05820990506129517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffff856127a8565b93507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000184820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185820995945050505050565b6129ae612d3e565b60408051808201909152600b8082527f42534232322d506c6f6e6b0000000000000000000000000000000000000000006020830152606090600090603090825b6040811015612a2e578484604051602001612a0a929190613063565b60405160208183030381529060405294508080612a2690612fb9565b9150506129ee565b508388888585878a87604051602001612a4e9897969594939291906130ad565b60405160208183030381529060405293506000600285604051612a719190613143565b602060405180830381855afa158015612a8e573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190612ab1919061315f565b90508060018784604051602001612acb9493929190613178565b60405160208183030381529060405294506000600286604051612aee9190613143565b602060405180830381855afa158015612b0b573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190612b2e919061315f565b905060005b6020811015612b8057818160208110612b4e57612b4e612f8a565b1a898260308110612b6157612b61612f8a565b60ff909216602092909202015280612b7881612fb9565b915050612b33565b50604051600083811a9083901a1860f81b7fff00000000000000000000000000000000000000000000000000000000000000166020820152602101604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052955060015b6020811015612c5a5786828260208110612c0a57612c0a612f8a565b1a848360208110612c1d57612c1d612f8a565b1a60f81b60f81c18604051602001612c36929190613063565b60405160208183030381529060405296508080612c5290612fb9565b915050612bee565b508560028885604051602001612c7394939291906131da565b6040516020818303038152906040529550600286604051612c949190613143565b602060405180830381855afa158015612cb1573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190612cd4919061315f565b905060005b6010811015612d3057818160208110612cf457612cf4612f8a565b1a89612d01836020612f77565b60308110612d1157612d11612f8a565b60ff909216602092909202015280612d2881612fb9565b915050612cd9565b505050505050505092915050565b6040518061060001604052806030906020820280368337509192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715612dd357612dd3612d5d565b604052919050565b600082601f830112612dec57600080fd5b8135602067ffffffffffffffff821115612e0857612e08612d5d565b8160051b612e17828201612d8c565b9283528481018201928281019087851115612e3157600080fd5b83870192505b84831015612e5057823582529183019190830190612e37565b979650505050505050565b60008060408385031215612e6e57600080fd5b823567ffffffffffffffff80821115612e8657600080fd5b818501915085601f830112612e9a57600080fd5b8135602082821115612eae57612eae612d5d565b612ede817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f85011601612d8c565b8281528882848701011115612ef257600080fd5b82828601838301376000928101820192909252909450850135915080821115612f1a57600080fd5b50612f2785828601612ddb565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80820281158282048414176117d2576117d2612f31565b808201808211156117d2576117d2612f31565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203612fea57612fea612f31565b5060010190565b818103818111156117d2576117d2612f31565b60008261303a577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500690565b60005b8381101561305a578181015183820152602001613042565b50506000910152565b6000835161307581846020880161303f565b60f89390931b7fff00000000000000000000000000000000000000000000000000000000000000169190920190815260010192915050565b600089516130bf818460208e0161303f565b80830190508981528860208201527fff00000000000000000000000000000000000000000000000000000000000000808960f81b166040830152808860f81b166041830152808760f81b1660428301528551613122816043850160208a0161303f565b60f89590951b16930160438101939093525050604401979650505050505050565b6000825161315581846020870161303f565b9190910192915050565b60006020828403121561317157600080fd5b5051919050565b84815260007fff00000000000000000000000000000000000000000000000000000000000000808660f81b16602084015284516131bc81602186016020890161303f565b60f89490941b16919092016021810191909152602201949350505050565b600085516131ec818460208a0161303f565b80830190507fff00000000000000000000000000000000000000000000000000000000000000808760f81b168252855161322d816001850160208a0161303f565b60f89590951b16930160018101939093525050600201939250505056fea26469706673582212203fde081adb4570983a31bf0bcf69ab7d1e3179c31f4352e851cfe5518626906064736f6c63430008130033

Deployed Bytecode

0x608060405234801561001057600080fd5b506004361061002b5760003560e01c80637e4f7a8a14610030575b600080fd5b61004361003e366004612e5b565b610057565b604051901515815260200160405180910390f35b6000610062826117d8565b61006b83611896565b6100748361191e565b6000806000806100848787611a4a565b92965090945092509050600061009b87838a611f61565b905060008060009050604051856000820152876040820152846060820152866020820152836101808201526100ce610179565b6100d78b611352565b6100e08b6112e2565b6100e98b610efc565b6100f28b61091f565b6100fb8b6106ac565b6101048b610324565b6101e081015191506102008101519250506117c9565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f6572726f722076657269667900000000000000000000000000000000000000006044820152606481fd5b604051610220604051016101968163040000006060850151611761565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000082089050806101a08401527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103606085015108610259837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffff83611761565b90507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e66c81e03716be83b486d6feabcc7ddd0fe6cbf5e72d585d142f7829b05820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018282098451935091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001905082820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018282099050806080840152505050565b60405161022081017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016020832006816040810192506102e08501518152610300850151602082015261037c83836103208801846116b1565b610140840161039184846102408901846116b1565b61012085016103a58461028089018361170f565b7f1fa4be93b5e7f7e674d5059b63554fab99638b304ed8310e9fa44c281ac9b03b85527f1a01ae7fac6228e39d3cb5a5e71fd31160f3241e79a5f48ffb3737e6c389b7216020860152805160408087019182529095908160608160076107d05a03fa9150816104165761041661011a565b60208101915081517f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4703825261044e86828586611630565b50508360408501945061046b8560608801516102e08a0184611676565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f1067569af1ff73b20113eff9b8d89d4a605b52b63d68f9ae1c79bd572f4e921260608801510995507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000186850993506104eb85856103208a01846116b1565b6104f785828485611630565b50602082810180517f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd470381528251865291810151908501527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c260408501527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed60608501527f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b60808501527f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa60a0850152905160c0840152805160e08401527f22f1acbb03c4508760c2430af35865e7cdf9f3eb1224504fdcc3708ddb954a486101008401527f2a344fad01c2ed0ed73142ae1752429eaea515c6f3f6b941103cc21c2308e1cb6101208401527f159f15b842ba9c8449aa3268f981010d4c7142e5193473d80b464e964845c3f86101408401527f0efd30ac7b6f8d0d3ccbc2207587c2acbad1532dc0293f0d034cf8258cd428b3610160840152925061067e905081610683565b505050565b604051602060006101808460086107d05a03fa6000516101e09290920180519190921616905250565b604051610220604051016101c082015180604060030261020001808401604085015161014087015260608501516101608701526102a08701516101208701526106fe8184608088016101408a016116b1565b610711836102c08901610120890161170f565b826102008701527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018484099250610751818460c088016101408a016116b1565b50610765826101a08801610120880161170f565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183830991506107a181850183610100870161014089016116b1565b6107b4826101c08801610120880161170f565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183830991506107f081850183610140870161014089016116b1565b610803826101e08801610120880161170f565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001838309915061083f81850183610180870161014089016116b1565b610852826102008801610120880161170f565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001838309915061088e818501836101c0870161014089016116b1565b6108a1826102208801610120880161170f565b6103608601610200850160005b6003811015610914577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000186860994506108ef84880186846101408c016116b1565b6108fe85846101208b0161170f565b60209290920191604091909101906001016108ae565b505050505050505050565b604080516467616d6d6161022080830191825260608084015161024085015260a08085015161026086015260c08086015161028087015260e08601516102a0808801919091526101008701516102c08089019190915260208a01516102e0890152978901516103008801529288015161032087015260808801516103408701529087015161036080870191909152908701516103808601527f1f84d6f80352f2fc7729113df20d64103bf3eae4204ce683c4b29834a19235606103a08601527f2e837b96567130647c44cbfdc512df731f0d589d87c364505b5d8bf52f326f546103c08601527f0240cbafaea4c9e736b868b0d9095395f8713efb4436b300ffa4cf87664317e26103e08601527f01157cddb161e79d457e57d106481f3aa7ade784703b5b29eda8083f032e709c6104008601527f302bc66f2018948d97055fb3a7514e487907e5e5e9e51be44364ccf77201cfa26104208601527f23a69ea5749b5e0a77ebee412a7fc5cad7e6b4cbcc43e3724d61dbee91555ed06104408601527f14b71eb4e8b16bb39ef3181b796737f9f7f3e1a1e61a5b3f5d5ba75aa3690cd06104608601527f2d1bfdbc4aa010cc9248ffa1b7b75dd1c758494451a97310de4e03ee420822d46104808601527f215a4aa132d8397759a1f4b4510e813338a3111fef6a78700ec0a50c5ee308d86104a08601527f156ed649c13a1ce23db99bdbea2392a0c3deb1286a7d4d792fc1e01163833adc6104c0860152908601516104e0850152938501516105008401526101a08501516105208401526101c08501516105408401526101e08501516105608401526102008501516105808401528401516105a083015290916105c0830190840160005b6003811015610bb6578151835260209283019290910190600101610b97565b50601b91506103e5905060206101c085018285850160026107d05a03fa9250505060008103610be757610be761011a565b506101c00180517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019006905250565b604051610220604051017f1de747c8b08f6e8135b238f73f2136d5dd9b23dcc5382f08c4130149c8ab4f5881527f24aaeb1ab69d8d9baf5146abaa1cfaffd933a0142b006a4beea229a5486a826f6020820152610c80604082016101a08501518360e08601611676565b7f1f47ced88f79e9a283779f60440e4b544dceb61cf7b0dc400ae00c87823deae581527f1e9a6e10cea071afe356efe7bbd87641f3aa129a0a14997640e06e0102b66cf96020820152610ce0604082016101c08501518360e086016116b1565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101c08401516101a0850151097f1de5b062e1c03586e2609cc3edda81bb8dd47468442c9d0a83d4043317cc449182527f1dd50545f38cf0b0c17eaa7c0646e6faa05a8f31d2d27e5835ec0461f50c84b16020830152610d6960408301828460e087016116b1565b507f0f9c3581e08299d620cd366d1473887a2f7623dfc2803bb53398a93a29c8cea581527f25e09ffadafa8cb36dd495dbc07a755617a4d7ad790893ad396904d9fb5eda916020820152610dca604082016101e08501518360e086016116b1565b7f21a42d83c31cff7c252fdbe33ed5d37e519ed002253a314b1ffe1374c7351be281527f1b878ed0d031c3333f1ae0cfbb47690afb6f4457e1dfd784bcd7ec22b952e6ec6020820152610e25604082018260e0850180611630565b61036083016103c0840160005b6003811015610e72578151845260208201516020850152610e5c6040850184518660e089016116b1565b6020929092019160409190910190600101610e32565b5050507f0690a2dbb7358db0cb77d75c272330856af7e51382a386ca0b67bb03e96b48f481527f2397e85bb92816bd9c0a98fc6d9c38255cc0358c1b656a463438821223d18d1a6020820152610ed060408201858360e086016116b1565b61024083015181526102608301516020820152610ef560408201868360e086016116b1565b5050505050565b60405160208101516040820151606083015160008401517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000184610280880151097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161020088015186097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101a0890151820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161022089015187097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101c08a0151820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000186820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018284097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001600580097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001878a0998507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101a08c01518a0894507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000188860894507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160058a0993507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101c08c0151850893507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000188850893507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001818a099250507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101e08b0151830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000187830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183850997507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018289097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103985085890997507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160808a0151890897506112d688828c610c16565b50505050505050505050565b60405160026304000000016102206040510161130381836060860151611761565b91506113188183610160870160a08701611676565b61132b81610120860160a0860180611630565b61133a818360a0860180611676565b61134c8160e0860160a0860180611630565b50505050565b604051610220604051017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160208301516102008501510981527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001604083015182510881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101a08401518251088152602081017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160208401516102208601510981527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001604084015182510881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101c08501518251088152604082017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160408501516101e08701510881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001825184510983527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001815184510980845284517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019250900982527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016102808501518351098252606082017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101808501516102c08701510881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001835182510881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160808501517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000010382510881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101a08501516102a087015109825281518151146101e08501525050505050565b6040518251855260208301516020860152835160408601526020840151606086015260408260808760066107d05a03fa6101e082015181166101e0830152505050505050565b604051825185526020830151602086015283604086015260408260608760076107d05a03fa6101e082015181166101e0830152505050505050565b604051825185526020830151602086015283604086015260408560608760076107d05a03fa825160408701526020830151606087015260408360808860066107d05a03fa811690506101e082015181166101e0830152505050505050565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001838351097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181835108825250505050565b602083526020808401526020604084015280606084015250806080830152507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160a0820152600060208260c08460056107d05a03fa806117c3576117c361011a565b50505190565b96505050505050505b92915050565b80516001906020830160005b828110156118235781517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000011193909316926020909101906001016117e4565b50505080611892576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f736f6d6520696e707574732061726520626967676572207468616e207200000060448201526064015b60405180910390fd5b5050565b60006118a460036060612f60565b6118b090610340612f77565b825190915080821461067e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f77726f6e672070726f6f662073697a65000000000000000000000000000000006044820152606401611889565b6102c08101516102a08201516101a08301516101c08401516101e08501516102008601516102208701516102808801517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908111918111928111938111948111958111968111971096909616949094169290921616161616600116610360820160005b60038110156119e15781517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000011192909216916020909101906001016119a1565b505080611892576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f736f6d65206f70656e696e67732061726520626967676572207468616e2072006044820152606401611889565b600080600080600080600080604051611a638a8c611b26565b80519450611a71858c611e68565b80519350611a7f848c611e99565b80519250611a8d838c611ee0565b517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000194859006949384900693928390069290069050611f51565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f6572726f722073746174696363616c6c20736861322d323536000000000000006044820152606481fd5b6040516467616d6d6181527f1f84d6f80352f2fc7729113df20d64103bf3eae4204ce683c4b29834a192356060208201527f2e837b96567130647c44cbfdc512df731f0d589d87c364505b5d8bf52f326f5460408201527f0240cbafaea4c9e736b868b0d9095395f8713efb4436b300ffa4cf87664317e260608201527f01157cddb161e79d457e57d106481f3aa7ade784703b5b29eda8083f032e709c60808201527f0690a2dbb7358db0cb77d75c272330856af7e51382a386ca0b67bb03e96b48f460a08201527f2397e85bb92816bd9c0a98fc6d9c38255cc0358c1b656a463438821223d18d1a60c08201527f1de747c8b08f6e8135b238f73f2136d5dd9b23dcc5382f08c4130149c8ab4f5860e08201527f24aaeb1ab69d8d9baf5146abaa1cfaffd933a0142b006a4beea229a5486a826f6101008201527f1f47ced88f79e9a283779f60440e4b544dceb61cf7b0dc400ae00c87823deae56101208201527f1e9a6e10cea071afe356efe7bbd87641f3aa129a0a14997640e06e0102b66cf96101408201527f1de5b062e1c03586e2609cc3edda81bb8dd47468442c9d0a83d4043317cc44916101608201527f1dd50545f38cf0b0c17eaa7c0646e6faa05a8f31d2d27e5835ec0461f50c84b16101808201527f0f9c3581e08299d620cd366d1473887a2f7623dfc2803bb53398a93a29c8cea56101a08201527f25e09ffadafa8cb36dd495dbc07a755617a4d7ad790893ad396904d9fb5eda916101c08201527f21a42d83c31cff7c252fdbe33ed5d37e519ed002253a314b1ffe1374c7351be26101e08201527f1b878ed0d031c3333f1ae0cfbb47690afb6f4457e1dfd784bcd7ec22b952e6ec61020082015260208301610220820160005b8551811015611dc5578251825260209283019290910190600101611da6565b506103c08401915060005b6003811015611df957825182526020808401519083015260409283019290910190600101611dd0565b506020840151815260408401516020820152606084015160408201526080840151606082015260a0840151608082015260c084015160a0820152505060208351026102c50160406003028101905060208282601b850160026107d05a03fa915081905061067e5761067e611ac7565b50604051636265746181528160208201526020816024601c840160026107d05a03fa90508061189257611892611ac7565b60405164616c7068618152826020820152610240820151604082015261026082015160608201526020816065601b840160026107d05a03fa90508061067e5761067e611ac7565b604051637a657461815282602082015260e082015160408201526101008201516060820152610120820151608082015261014082015160a082015261016082015160c082015261018082015160e082015260208160e4601c840160026107d05a03fa90508061067e5761067e611ac7565b9299919850965090945092505050565b6000806000611fca565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f6572726f722073746174696363616c6c206d6f646578700000000000000000006044820152606481fd5b611fd985875160208901611fe3565b50604051516123fa565b604051611ff181848661206c565b60008060005b85811015612060577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001855185510991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182840860209586019594909401939250600101611ff7565b50506040515250505050565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000006120bd85630400000085612398565b087f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e66c81e03716be83b486d6feabcc7ddd0fe6cbf5e72d585d142f7829b058209905060018460005b858110156121af577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103860882527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f1067569af1ff73b20113eff9b8d89d4a605b52b63d68f9ae1c79bd572f4e9212840992506020919091019060010161210a565b506121bb818688612273565b50600190508460005b8581101561226a577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001868551090982526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f1067569af1ff73b20113eff9b8d89d4a605b52b63d68f9ae1c79bd572f4e9212840992506001016121c4565b50505050505050565b600183526000805b838110156122c95781850151828401517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830990506020840193508084880152505060018101905061227b565b50602081038201915080840193505061230a6020840160027f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001038551612398565b60005b83811015610ef55760208503945082517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018651840984527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018184097fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09094019392505060010161230d565b602083526020808401526020604084015280606084015250806080830152507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160a0820152600060208260c08460056107d05a03fa806117c3576117c3611f6b565b61240b604051630400000087612398565b91507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000830860408051600380825260808201909252919350600091906020820160608036833701905050630159089160208201526301a3a4c6604082015263039c844f6060820152905060606124a460036002612f60565b67ffffffffffffffff8111156124bc576124bc612d5d565b6040519080825280602002602001820160405280156124e5578160200160208202803683370190505b5090506124f28187612600565b60005b60038110156125f357600061255d8361250f846002612f60565b8151811061251f5761251f612f8a565b6020026020010151848460026125359190612f60565b612540906001612f77565b8151811061255057612550612f8a565b6020026020010151612635565b905060006125908a8c5187868151811061257957612579612f8a565b602002602001015161258b9190612f77565b6127a0565b90507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018187089550505080806125eb90612fb9565b9150506124f5565b5091979650505050505050565b602082016103c0820160005b6003811015610ef55781518352602080830151908401526040928301929091019060010161260c565b60008061264284846129a6565b905060005b60208110156126a05761265b816008612f60565b8261266783602f612ff1565b6030811061267757612677612f8a565b602002015160ff16901b8361268c9190612f77565b92508061269881612fb9565b915050612647565b506126cb7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183613004565b91506000805b601081101561272a576126e5816008612f60565b836126f183600f612ff1565b6030811061270157612701612f8a565b602002015160ff16901b826127169190612f77565b91508061272281612fb9565b9150506126d1565b507f0e0a77c19a07df2f666ea36f7879462e36fc76959f60cd29ac96341c4ffffffb7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018285089695505050505050565b600080612819565b600060405160208152602080820152602060408201528260608201528360808201527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160a08201526020600060c08360056107d05a03fa90508061280e5761280e611f6b565b505060005192915050565b612843837f1067569af1ff73b20113eff9b8d89d4a605b52b63d68f9ae1c79bd572f4e92126127a8565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001817f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103860893506128996304000000866127a8565b94507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000860894507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e66c81e03716be83b486d6feabcc7ddd0fe6cbf5e72d585d142f7829b05820990506129517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffff856127a8565b93507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000184820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185820995945050505050565b6129ae612d3e565b60408051808201909152600b8082527f42534232322d506c6f6e6b0000000000000000000000000000000000000000006020830152606090600090603090825b6040811015612a2e578484604051602001612a0a929190613063565b60405160208183030381529060405294508080612a2690612fb9565b9150506129ee565b508388888585878a87604051602001612a4e9897969594939291906130ad565b60405160208183030381529060405293506000600285604051612a719190613143565b602060405180830381855afa158015612a8e573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190612ab1919061315f565b90508060018784604051602001612acb9493929190613178565b60405160208183030381529060405294506000600286604051612aee9190613143565b602060405180830381855afa158015612b0b573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190612b2e919061315f565b905060005b6020811015612b8057818160208110612b4e57612b4e612f8a565b1a898260308110612b6157612b61612f8a565b60ff909216602092909202015280612b7881612fb9565b915050612b33565b50604051600083811a9083901a1860f81b7fff00000000000000000000000000000000000000000000000000000000000000166020820152602101604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052955060015b6020811015612c5a5786828260208110612c0a57612c0a612f8a565b1a848360208110612c1d57612c1d612f8a565b1a60f81b60f81c18604051602001612c36929190613063565b60405160208183030381529060405296508080612c5290612fb9565b915050612bee565b508560028885604051602001612c7394939291906131da565b6040516020818303038152906040529550600286604051612c949190613143565b602060405180830381855afa158015612cb1573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190612cd4919061315f565b905060005b6010811015612d3057818160208110612cf457612cf4612f8a565b1a89612d01836020612f77565b60308110612d1157612d11612f8a565b60ff909216602092909202015280612d2881612fb9565b915050612cd9565b505050505050505092915050565b6040518061060001604052806030906020820280368337509192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715612dd357612dd3612d5d565b604052919050565b600082601f830112612dec57600080fd5b8135602067ffffffffffffffff821115612e0857612e08612d5d565b8160051b612e17828201612d8c565b9283528481018201928281019087851115612e3157600080fd5b83870192505b84831015612e5057823582529183019190830190612e37565b979650505050505050565b60008060408385031215612e6e57600080fd5b823567ffffffffffffffff80821115612e8657600080fd5b818501915085601f830112612e9a57600080fd5b8135602082821115612eae57612eae612d5d565b612ede817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f85011601612d8c565b8281528882848701011115612ef257600080fd5b82828601838301376000928101820192909252909450850135915080821115612f1a57600080fd5b50612f2785828601612ddb565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80820281158282048414176117d2576117d2612f31565b808201808211156117d2576117d2612f31565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203612fea57612fea612f31565b5060010190565b818103818111156117d2576117d2612f31565b60008261303a577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500690565b60005b8381101561305a578181015183820152602001613042565b50506000910152565b6000835161307581846020880161303f565b60f89390931b7fff00000000000000000000000000000000000000000000000000000000000000169190920190815260010192915050565b600089516130bf818460208e0161303f565b80830190508981528860208201527fff00000000000000000000000000000000000000000000000000000000000000808960f81b166040830152808860f81b166041830152808760f81b1660428301528551613122816043850160208a0161303f565b60f89590951b16930160438101939093525050604401979650505050505050565b6000825161315581846020870161303f565b9190910192915050565b60006020828403121561317157600080fd5b5051919050565b84815260007fff00000000000000000000000000000000000000000000000000000000000000808660f81b16602084015284516131bc81602186016020890161303f565b60f89490941b16919092016021810191909152602201949350505050565b600085516131ec818460208a0161303f565b80830190507fff00000000000000000000000000000000000000000000000000000000000000808760f81b168252855161322d816001850160208a0161303f565b60f89590951b16930160018101939093525050600201939250505056fea26469706673582212203fde081adb4570983a31bf0bcf69ab7d1e3179c31f4352e851cfe5518626906064736f6c63430008130033

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

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.