Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
PlonkVerifierFull
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 100000 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// 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;
}
}{
"optimizer": {
"enabled": true,
"runs": 100000
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
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"}]Contract Creation Code
608060405234801561001057600080fd5b50613280806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80637e4f7a8a14610030575b600080fd5b61004361003e366004612e5b565b610057565b604051901515815260200160405180910390f35b6000610062826117d8565b61006b83611896565b6100748361191e565b6000806000806100848787611a4a565b92965090945092509050600061009b87838a611f61565b905060008060009050604051856000820152876040820152846060820152866020820152836101808201526100ce610179565b6100d78b611352565b6100e08b6112e2565b6100e98b610efc565b6100f28b61091f565b6100fb8b6106ac565b6101048b610324565b6101e081015191506102008101519250506117c9565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f6572726f722076657269667900000000000000000000000000000000000000006044820152606481fd5b604051610220604051016101968163040000006060850151611761565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000082089050806101a08401527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103606085015108610259837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffff83611761565b90507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e66c81e03716be83b486d6feabcc7ddd0fe6cbf5e72d585d142f7829b05820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018282098451935091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001905082820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018282099050806080840152505050565b60405161022081017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016020832006816040810192506102e08501518152610300850151602082015261037c83836103208801846116b1565b610140840161039184846102408901846116b1565b61012085016103a58461028089018361170f565b7f1fa4be93b5e7f7e674d5059b63554fab99638b304ed8310e9fa44c281ac9b03b85527f1a01ae7fac6228e39d3cb5a5e71fd31160f3241e79a5f48ffb3737e6c389b7216020860152805160408087019182529095908160608160076107d05a03fa9150816104165761041661011a565b60208101915081517f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4703825261044e86828586611630565b50508360408501945061046b8560608801516102e08a0184611676565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f1067569af1ff73b20113eff9b8d89d4a605b52b63d68f9ae1c79bd572f4e921260608801510995507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000186850993506104eb85856103208a01846116b1565b6104f785828485611630565b50602082810180517f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd470381528251865291810151908501527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c260408501527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed60608501527f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b60808501527f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa60a0850152905160c0840152805160e08401527f22f1acbb03c4508760c2430af35865e7cdf9f3eb1224504fdcc3708ddb954a486101008401527f2a344fad01c2ed0ed73142ae1752429eaea515c6f3f6b941103cc21c2308e1cb6101208401527f159f15b842ba9c8449aa3268f981010d4c7142e5193473d80b464e964845c3f86101408401527f0efd30ac7b6f8d0d3ccbc2207587c2acbad1532dc0293f0d034cf8258cd428b3610160840152925061067e905081610683565b505050565b604051602060006101808460086107d05a03fa6000516101e09290920180519190921616905250565b604051610220604051016101c082015180604060030261020001808401604085015161014087015260608501516101608701526102a08701516101208701526106fe8184608088016101408a016116b1565b610711836102c08901610120890161170f565b826102008701527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018484099250610751818460c088016101408a016116b1565b50610765826101a08801610120880161170f565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183830991506107a181850183610100870161014089016116b1565b6107b4826101c08801610120880161170f565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183830991506107f081850183610140870161014089016116b1565b610803826101e08801610120880161170f565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001838309915061083f81850183610180870161014089016116b1565b610852826102008801610120880161170f565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001838309915061088e818501836101c0870161014089016116b1565b6108a1826102208801610120880161170f565b6103608601610200850160005b6003811015610914577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000186860994506108ef84880186846101408c016116b1565b6108fe85846101208b0161170f565b60209290920191604091909101906001016108ae565b505050505050505050565b604080516467616d6d6161022080830191825260608084015161024085015260a08085015161026086015260c08086015161028087015260e08601516102a0808801919091526101008701516102c08089019190915260208a01516102e0890152978901516103008801529288015161032087015260808801516103408701529087015161036080870191909152908701516103808601527f1f84d6f80352f2fc7729113df20d64103bf3eae4204ce683c4b29834a19235606103a08601527f2e837b96567130647c44cbfdc512df731f0d589d87c364505b5d8bf52f326f546103c08601527f0240cbafaea4c9e736b868b0d9095395f8713efb4436b300ffa4cf87664317e26103e08601527f01157cddb161e79d457e57d106481f3aa7ade784703b5b29eda8083f032e709c6104008601527f302bc66f2018948d97055fb3a7514e487907e5e5e9e51be44364ccf77201cfa26104208601527f23a69ea5749b5e0a77ebee412a7fc5cad7e6b4cbcc43e3724d61dbee91555ed06104408601527f14b71eb4e8b16bb39ef3181b796737f9f7f3e1a1e61a5b3f5d5ba75aa3690cd06104608601527f2d1bfdbc4aa010cc9248ffa1b7b75dd1c758494451a97310de4e03ee420822d46104808601527f215a4aa132d8397759a1f4b4510e813338a3111fef6a78700ec0a50c5ee308d86104a08601527f156ed649c13a1ce23db99bdbea2392a0c3deb1286a7d4d792fc1e01163833adc6104c0860152908601516104e0850152938501516105008401526101a08501516105208401526101c08501516105408401526101e08501516105608401526102008501516105808401528401516105a083015290916105c0830190840160005b6003811015610bb6578151835260209283019290910190600101610b97565b50601b91506103e5905060206101c085018285850160026107d05a03fa9250505060008103610be757610be761011a565b506101c00180517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019006905250565b604051610220604051017f1de747c8b08f6e8135b238f73f2136d5dd9b23dcc5382f08c4130149c8ab4f5881527f24aaeb1ab69d8d9baf5146abaa1cfaffd933a0142b006a4beea229a5486a826f6020820152610c80604082016101a08501518360e08601611676565b7f1f47ced88f79e9a283779f60440e4b544dceb61cf7b0dc400ae00c87823deae581527f1e9a6e10cea071afe356efe7bbd87641f3aa129a0a14997640e06e0102b66cf96020820152610ce0604082016101c08501518360e086016116b1565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101c08401516101a0850151097f1de5b062e1c03586e2609cc3edda81bb8dd47468442c9d0a83d4043317cc449182527f1dd50545f38cf0b0c17eaa7c0646e6faa05a8f31d2d27e5835ec0461f50c84b16020830152610d6960408301828460e087016116b1565b507f0f9c3581e08299d620cd366d1473887a2f7623dfc2803bb53398a93a29c8cea581527f25e09ffadafa8cb36dd495dbc07a755617a4d7ad790893ad396904d9fb5eda916020820152610dca604082016101e08501518360e086016116b1565b7f21a42d83c31cff7c252fdbe33ed5d37e519ed002253a314b1ffe1374c7351be281527f1b878ed0d031c3333f1ae0cfbb47690afb6f4457e1dfd784bcd7ec22b952e6ec6020820152610e25604082018260e0850180611630565b61036083016103c0840160005b6003811015610e72578151845260208201516020850152610e5c6040850184518660e089016116b1565b6020929092019160409190910190600101610e32565b5050507f0690a2dbb7358db0cb77d75c272330856af7e51382a386ca0b67bb03e96b48f481527f2397e85bb92816bd9c0a98fc6d9c38255cc0358c1b656a463438821223d18d1a6020820152610ed060408201858360e086016116b1565b61024083015181526102608301516020820152610ef560408201868360e086016116b1565b5050505050565b60405160208101516040820151606083015160008401517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000184610280880151097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161020088015186097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101a0890151820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161022089015187097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101c08a0151820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000186820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018284097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001600580097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001878a0998507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101a08c01518a0894507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000188860894507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160058a0993507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101c08c0151850893507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000188850893507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001818a099250507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101e08b0151830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000187830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183850997507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018289097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103985085890997507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160808a0151890897506112d688828c610c16565b50505050505050505050565b60405160026304000000016102206040510161130381836060860151611761565b91506113188183610160870160a08701611676565b61132b81610120860160a0860180611630565b61133a818360a0860180611676565b61134c8160e0860160a0860180611630565b50505050565b604051610220604051017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160208301516102008501510981527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001604083015182510881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101a08401518251088152602081017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160208401516102208601510981527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001604084015182510881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101c08501518251088152604082017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160408501516101e08701510881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001825184510983527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001815184510980845284517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019250900982527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016102808501518351098252606082017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101808501516102c08701510881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001835182510881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160808501517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000010382510881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101a08501516102a087015109825281518151146101e08501525050505050565b6040518251855260208301516020860152835160408601526020840151606086015260408260808760066107d05a03fa6101e082015181166101e0830152505050505050565b604051825185526020830151602086015283604086015260408260608760076107d05a03fa6101e082015181166101e0830152505050505050565b604051825185526020830151602086015283604086015260408560608760076107d05a03fa825160408701526020830151606087015260408360808860066107d05a03fa811690506101e082015181166101e0830152505050505050565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001838351097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181835108825250505050565b602083526020808401526020604084015280606084015250806080830152507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160a0820152600060208260c08460056107d05a03fa806117c3576117c361011a565b50505190565b96505050505050505b92915050565b80516001906020830160005b828110156118235781517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000011193909316926020909101906001016117e4565b50505080611892576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f736f6d6520696e707574732061726520626967676572207468616e207200000060448201526064015b60405180910390fd5b5050565b60006118a460036060612f60565b6118b090610340612f77565b825190915080821461067e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f77726f6e672070726f6f662073697a65000000000000000000000000000000006044820152606401611889565b6102c08101516102a08201516101a08301516101c08401516101e08501516102008601516102208701516102808801517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908111918111928111938111948111958111968111971096909616949094169290921616161616600116610360820160005b60038110156119e15781517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000011192909216916020909101906001016119a1565b505080611892576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f736f6d65206f70656e696e67732061726520626967676572207468616e2072006044820152606401611889565b600080600080600080600080604051611a638a8c611b26565b80519450611a71858c611e68565b80519350611a7f848c611e99565b80519250611a8d838c611ee0565b517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000194859006949384900693928390069290069050611f51565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f6572726f722073746174696363616c6c20736861322d323536000000000000006044820152606481fd5b6040516467616d6d6181527f1f84d6f80352f2fc7729113df20d64103bf3eae4204ce683c4b29834a192356060208201527f2e837b96567130647c44cbfdc512df731f0d589d87c364505b5d8bf52f326f5460408201527f0240cbafaea4c9e736b868b0d9095395f8713efb4436b300ffa4cf87664317e260608201527f01157cddb161e79d457e57d106481f3aa7ade784703b5b29eda8083f032e709c60808201527f0690a2dbb7358db0cb77d75c272330856af7e51382a386ca0b67bb03e96b48f460a08201527f2397e85bb92816bd9c0a98fc6d9c38255cc0358c1b656a463438821223d18d1a60c08201527f1de747c8b08f6e8135b238f73f2136d5dd9b23dcc5382f08c4130149c8ab4f5860e08201527f24aaeb1ab69d8d9baf5146abaa1cfaffd933a0142b006a4beea229a5486a826f6101008201527f1f47ced88f79e9a283779f60440e4b544dceb61cf7b0dc400ae00c87823deae56101208201527f1e9a6e10cea071afe356efe7bbd87641f3aa129a0a14997640e06e0102b66cf96101408201527f1de5b062e1c03586e2609cc3edda81bb8dd47468442c9d0a83d4043317cc44916101608201527f1dd50545f38cf0b0c17eaa7c0646e6faa05a8f31d2d27e5835ec0461f50c84b16101808201527f0f9c3581e08299d620cd366d1473887a2f7623dfc2803bb53398a93a29c8cea56101a08201527f25e09ffadafa8cb36dd495dbc07a755617a4d7ad790893ad396904d9fb5eda916101c08201527f21a42d83c31cff7c252fdbe33ed5d37e519ed002253a314b1ffe1374c7351be26101e08201527f1b878ed0d031c3333f1ae0cfbb47690afb6f4457e1dfd784bcd7ec22b952e6ec61020082015260208301610220820160005b8551811015611dc5578251825260209283019290910190600101611da6565b506103c08401915060005b6003811015611df957825182526020808401519083015260409283019290910190600101611dd0565b506020840151815260408401516020820152606084015160408201526080840151606082015260a0840151608082015260c084015160a0820152505060208351026102c50160406003028101905060208282601b850160026107d05a03fa915081905061067e5761067e611ac7565b50604051636265746181528160208201526020816024601c840160026107d05a03fa90508061189257611892611ac7565b60405164616c7068618152826020820152610240820151604082015261026082015160608201526020816065601b840160026107d05a03fa90508061067e5761067e611ac7565b604051637a657461815282602082015260e082015160408201526101008201516060820152610120820151608082015261014082015160a082015261016082015160c082015261018082015160e082015260208160e4601c840160026107d05a03fa90508061067e5761067e611ac7565b9299919850965090945092505050565b6000806000611fca565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f6572726f722073746174696363616c6c206d6f646578700000000000000000006044820152606481fd5b611fd985875160208901611fe3565b50604051516123fa565b604051611ff181848661206c565b60008060005b85811015612060577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001855185510991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182840860209586019594909401939250600101611ff7565b50506040515250505050565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000006120bd85630400000085612398565b087f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e66c81e03716be83b486d6feabcc7ddd0fe6cbf5e72d585d142f7829b058209905060018460005b858110156121af577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103860882527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f1067569af1ff73b20113eff9b8d89d4a605b52b63d68f9ae1c79bd572f4e9212840992506020919091019060010161210a565b506121bb818688612273565b50600190508460005b8581101561226a577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001868551090982526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f1067569af1ff73b20113eff9b8d89d4a605b52b63d68f9ae1c79bd572f4e9212840992506001016121c4565b50505050505050565b600183526000805b838110156122c95781850151828401517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830990506020840193508084880152505060018101905061227b565b50602081038201915080840193505061230a6020840160027f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001038551612398565b60005b83811015610ef55760208503945082517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018651840984527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018184097fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09094019392505060010161230d565b602083526020808401526020604084015280606084015250806080830152507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160a0820152600060208260c08460056107d05a03fa806117c3576117c3611f6b565b61240b604051630400000087612398565b91507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000830860408051600380825260808201909252919350600091906020820160608036833701905050630159089160208201526301a3a4c6604082015263039c844f6060820152905060606124a460036002612f60565b67ffffffffffffffff8111156124bc576124bc612d5d565b6040519080825280602002602001820160405280156124e5578160200160208202803683370190505b5090506124f28187612600565b60005b60038110156125f357600061255d8361250f846002612f60565b8151811061251f5761251f612f8a565b6020026020010151848460026125359190612f60565b612540906001612f77565b8151811061255057612550612f8a565b6020026020010151612635565b905060006125908a8c5187868151811061257957612579612f8a565b602002602001015161258b9190612f77565b6127a0565b90507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018187089550505080806125eb90612fb9565b9150506124f5565b5091979650505050505050565b602082016103c0820160005b6003811015610ef55781518352602080830151908401526040928301929091019060010161260c565b60008061264284846129a6565b905060005b60208110156126a05761265b816008612f60565b8261266783602f612ff1565b6030811061267757612677612f8a565b602002015160ff16901b8361268c9190612f77565b92508061269881612fb9565b915050612647565b506126cb7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183613004565b91506000805b601081101561272a576126e5816008612f60565b836126f183600f612ff1565b6030811061270157612701612f8a565b602002015160ff16901b826127169190612f77565b91508061272281612fb9565b9150506126d1565b507f0e0a77c19a07df2f666ea36f7879462e36fc76959f60cd29ac96341c4ffffffb7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018285089695505050505050565b600080612819565b600060405160208152602080820152602060408201528260608201528360808201527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160a08201526020600060c08360056107d05a03fa90508061280e5761280e611f6b565b505060005192915050565b612843837f1067569af1ff73b20113eff9b8d89d4a605b52b63d68f9ae1c79bd572f4e92126127a8565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001817f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103860893506128996304000000866127a8565b94507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000860894507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e66c81e03716be83b486d6feabcc7ddd0fe6cbf5e72d585d142f7829b05820990506129517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffff856127a8565b93507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000184820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185820995945050505050565b6129ae612d3e565b60408051808201909152600b8082527f42534232322d506c6f6e6b0000000000000000000000000000000000000000006020830152606090600090603090825b6040811015612a2e578484604051602001612a0a929190613063565b60405160208183030381529060405294508080612a2690612fb9565b9150506129ee565b508388888585878a87604051602001612a4e9897969594939291906130ad565b60405160208183030381529060405293506000600285604051612a719190613143565b602060405180830381855afa158015612a8e573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190612ab1919061315f565b90508060018784604051602001612acb9493929190613178565b60405160208183030381529060405294506000600286604051612aee9190613143565b602060405180830381855afa158015612b0b573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190612b2e919061315f565b905060005b6020811015612b8057818160208110612b4e57612b4e612f8a565b1a898260308110612b6157612b61612f8a565b60ff909216602092909202015280612b7881612fb9565b915050612b33565b50604051600083811a9083901a1860f81b7fff00000000000000000000000000000000000000000000000000000000000000166020820152602101604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052955060015b6020811015612c5a5786828260208110612c0a57612c0a612f8a565b1a848360208110612c1d57612c1d612f8a565b1a60f81b60f81c18604051602001612c36929190613063565b60405160208183030381529060405296508080612c5290612fb9565b915050612bee565b508560028885604051602001612c7394939291906131da565b6040516020818303038152906040529550600286604051612c949190613143565b602060405180830381855afa158015612cb1573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190612cd4919061315f565b905060005b6010811015612d3057818160208110612cf457612cf4612f8a565b1a89612d01836020612f77565b60308110612d1157612d11612f8a565b60ff909216602092909202015280612d2881612fb9565b915050612cd9565b505050505050505092915050565b6040518061060001604052806030906020820280368337509192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715612dd357612dd3612d5d565b604052919050565b600082601f830112612dec57600080fd5b8135602067ffffffffffffffff821115612e0857612e08612d5d565b8160051b612e17828201612d8c565b9283528481018201928281019087851115612e3157600080fd5b83870192505b84831015612e5057823582529183019190830190612e37565b979650505050505050565b60008060408385031215612e6e57600080fd5b823567ffffffffffffffff80821115612e8657600080fd5b818501915085601f830112612e9a57600080fd5b8135602082821115612eae57612eae612d5d565b612ede817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f85011601612d8c565b8281528882848701011115612ef257600080fd5b82828601838301376000928101820192909252909450850135915080821115612f1a57600080fd5b50612f2785828601612ddb565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80820281158282048414176117d2576117d2612f31565b808201808211156117d2576117d2612f31565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203612fea57612fea612f31565b5060010190565b818103818111156117d2576117d2612f31565b60008261303a577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500690565b60005b8381101561305a578181015183820152602001613042565b50506000910152565b6000835161307581846020880161303f565b60f89390931b7fff00000000000000000000000000000000000000000000000000000000000000169190920190815260010192915050565b600089516130bf818460208e0161303f565b80830190508981528860208201527fff00000000000000000000000000000000000000000000000000000000000000808960f81b166040830152808860f81b166041830152808760f81b1660428301528551613122816043850160208a0161303f565b60f89590951b16930160438101939093525050604401979650505050505050565b6000825161315581846020870161303f565b9190910192915050565b60006020828403121561317157600080fd5b5051919050565b84815260007fff00000000000000000000000000000000000000000000000000000000000000808660f81b16602084015284516131bc81602186016020890161303f565b60f89490941b16919092016021810191909152602201949350505050565b600085516131ec818460208a0161303f565b80830190507fff00000000000000000000000000000000000000000000000000000000000000808760f81b168252855161322d816001850160208a0161303f565b60f89590951b16930160018101939093525050600201939250505056fea26469706673582212203fde081adb4570983a31bf0bcf69ab7d1e3179c31f4352e851cfe5518626906064736f6c63430008130033
Deployed Bytecode
0x608060405234801561001057600080fd5b506004361061002b5760003560e01c80637e4f7a8a14610030575b600080fd5b61004361003e366004612e5b565b610057565b604051901515815260200160405180910390f35b6000610062826117d8565b61006b83611896565b6100748361191e565b6000806000806100848787611a4a565b92965090945092509050600061009b87838a611f61565b905060008060009050604051856000820152876040820152846060820152866020820152836101808201526100ce610179565b6100d78b611352565b6100e08b6112e2565b6100e98b610efc565b6100f28b61091f565b6100fb8b6106ac565b6101048b610324565b6101e081015191506102008101519250506117c9565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f6572726f722076657269667900000000000000000000000000000000000000006044820152606481fd5b604051610220604051016101968163040000006060850151611761565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000082089050806101a08401527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103606085015108610259837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffff83611761565b90507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e66c81e03716be83b486d6feabcc7ddd0fe6cbf5e72d585d142f7829b05820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018282098451935091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001905082820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018282099050806080840152505050565b60405161022081017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016020832006816040810192506102e08501518152610300850151602082015261037c83836103208801846116b1565b610140840161039184846102408901846116b1565b61012085016103a58461028089018361170f565b7f1fa4be93b5e7f7e674d5059b63554fab99638b304ed8310e9fa44c281ac9b03b85527f1a01ae7fac6228e39d3cb5a5e71fd31160f3241e79a5f48ffb3737e6c389b7216020860152805160408087019182529095908160608160076107d05a03fa9150816104165761041661011a565b60208101915081517f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4703825261044e86828586611630565b50508360408501945061046b8560608801516102e08a0184611676565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f1067569af1ff73b20113eff9b8d89d4a605b52b63d68f9ae1c79bd572f4e921260608801510995507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000186850993506104eb85856103208a01846116b1565b6104f785828485611630565b50602082810180517f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd470381528251865291810151908501527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c260408501527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed60608501527f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b60808501527f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa60a0850152905160c0840152805160e08401527f22f1acbb03c4508760c2430af35865e7cdf9f3eb1224504fdcc3708ddb954a486101008401527f2a344fad01c2ed0ed73142ae1752429eaea515c6f3f6b941103cc21c2308e1cb6101208401527f159f15b842ba9c8449aa3268f981010d4c7142e5193473d80b464e964845c3f86101408401527f0efd30ac7b6f8d0d3ccbc2207587c2acbad1532dc0293f0d034cf8258cd428b3610160840152925061067e905081610683565b505050565b604051602060006101808460086107d05a03fa6000516101e09290920180519190921616905250565b604051610220604051016101c082015180604060030261020001808401604085015161014087015260608501516101608701526102a08701516101208701526106fe8184608088016101408a016116b1565b610711836102c08901610120890161170f565b826102008701527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018484099250610751818460c088016101408a016116b1565b50610765826101a08801610120880161170f565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183830991506107a181850183610100870161014089016116b1565b6107b4826101c08801610120880161170f565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183830991506107f081850183610140870161014089016116b1565b610803826101e08801610120880161170f565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001838309915061083f81850183610180870161014089016116b1565b610852826102008801610120880161170f565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001838309915061088e818501836101c0870161014089016116b1565b6108a1826102208801610120880161170f565b6103608601610200850160005b6003811015610914577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000186860994506108ef84880186846101408c016116b1565b6108fe85846101208b0161170f565b60209290920191604091909101906001016108ae565b505050505050505050565b604080516467616d6d6161022080830191825260608084015161024085015260a08085015161026086015260c08086015161028087015260e08601516102a0808801919091526101008701516102c08089019190915260208a01516102e0890152978901516103008801529288015161032087015260808801516103408701529087015161036080870191909152908701516103808601527f1f84d6f80352f2fc7729113df20d64103bf3eae4204ce683c4b29834a19235606103a08601527f2e837b96567130647c44cbfdc512df731f0d589d87c364505b5d8bf52f326f546103c08601527f0240cbafaea4c9e736b868b0d9095395f8713efb4436b300ffa4cf87664317e26103e08601527f01157cddb161e79d457e57d106481f3aa7ade784703b5b29eda8083f032e709c6104008601527f302bc66f2018948d97055fb3a7514e487907e5e5e9e51be44364ccf77201cfa26104208601527f23a69ea5749b5e0a77ebee412a7fc5cad7e6b4cbcc43e3724d61dbee91555ed06104408601527f14b71eb4e8b16bb39ef3181b796737f9f7f3e1a1e61a5b3f5d5ba75aa3690cd06104608601527f2d1bfdbc4aa010cc9248ffa1b7b75dd1c758494451a97310de4e03ee420822d46104808601527f215a4aa132d8397759a1f4b4510e813338a3111fef6a78700ec0a50c5ee308d86104a08601527f156ed649c13a1ce23db99bdbea2392a0c3deb1286a7d4d792fc1e01163833adc6104c0860152908601516104e0850152938501516105008401526101a08501516105208401526101c08501516105408401526101e08501516105608401526102008501516105808401528401516105a083015290916105c0830190840160005b6003811015610bb6578151835260209283019290910190600101610b97565b50601b91506103e5905060206101c085018285850160026107d05a03fa9250505060008103610be757610be761011a565b506101c00180517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019006905250565b604051610220604051017f1de747c8b08f6e8135b238f73f2136d5dd9b23dcc5382f08c4130149c8ab4f5881527f24aaeb1ab69d8d9baf5146abaa1cfaffd933a0142b006a4beea229a5486a826f6020820152610c80604082016101a08501518360e08601611676565b7f1f47ced88f79e9a283779f60440e4b544dceb61cf7b0dc400ae00c87823deae581527f1e9a6e10cea071afe356efe7bbd87641f3aa129a0a14997640e06e0102b66cf96020820152610ce0604082016101c08501518360e086016116b1565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101c08401516101a0850151097f1de5b062e1c03586e2609cc3edda81bb8dd47468442c9d0a83d4043317cc449182527f1dd50545f38cf0b0c17eaa7c0646e6faa05a8f31d2d27e5835ec0461f50c84b16020830152610d6960408301828460e087016116b1565b507f0f9c3581e08299d620cd366d1473887a2f7623dfc2803bb53398a93a29c8cea581527f25e09ffadafa8cb36dd495dbc07a755617a4d7ad790893ad396904d9fb5eda916020820152610dca604082016101e08501518360e086016116b1565b7f21a42d83c31cff7c252fdbe33ed5d37e519ed002253a314b1ffe1374c7351be281527f1b878ed0d031c3333f1ae0cfbb47690afb6f4457e1dfd784bcd7ec22b952e6ec6020820152610e25604082018260e0850180611630565b61036083016103c0840160005b6003811015610e72578151845260208201516020850152610e5c6040850184518660e089016116b1565b6020929092019160409190910190600101610e32565b5050507f0690a2dbb7358db0cb77d75c272330856af7e51382a386ca0b67bb03e96b48f481527f2397e85bb92816bd9c0a98fc6d9c38255cc0358c1b656a463438821223d18d1a6020820152610ed060408201858360e086016116b1565b61024083015181526102608301516020820152610ef560408201868360e086016116b1565b5050505050565b60405160208101516040820151606083015160008401517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000184610280880151097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161020088015186097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101a0890151820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161022089015187097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101c08a0151820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000186820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018284097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001600580097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001878a0998507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101a08c01518a0894507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000188860894507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160058a0993507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101c08c0151850893507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000188850893507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001818a099250507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101e08b0151830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000187830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183850997507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018289097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103985085890997507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160808a0151890897506112d688828c610c16565b50505050505050505050565b60405160026304000000016102206040510161130381836060860151611761565b91506113188183610160870160a08701611676565b61132b81610120860160a0860180611630565b61133a818360a0860180611676565b61134c8160e0860160a0860180611630565b50505050565b604051610220604051017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160208301516102008501510981527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001604083015182510881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101a08401518251088152602081017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160208401516102208601510981527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001604084015182510881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101c08501518251088152604082017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160408501516101e08701510881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001825184510983527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001815184510980845284517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019250900982527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016102808501518351098252606082017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101808501516102c08701510881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001835182510881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160808501517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000010382510881527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101a08501516102a087015109825281518151146101e08501525050505050565b6040518251855260208301516020860152835160408601526020840151606086015260408260808760066107d05a03fa6101e082015181166101e0830152505050505050565b604051825185526020830151602086015283604086015260408260608760076107d05a03fa6101e082015181166101e0830152505050505050565b604051825185526020830151602086015283604086015260408560608760076107d05a03fa825160408701526020830151606087015260408360808860066107d05a03fa811690506101e082015181166101e0830152505050505050565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001838351097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181835108825250505050565b602083526020808401526020604084015280606084015250806080830152507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160a0820152600060208260c08460056107d05a03fa806117c3576117c361011a565b50505190565b96505050505050505b92915050565b80516001906020830160005b828110156118235781517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000011193909316926020909101906001016117e4565b50505080611892576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f736f6d6520696e707574732061726520626967676572207468616e207200000060448201526064015b60405180910390fd5b5050565b60006118a460036060612f60565b6118b090610340612f77565b825190915080821461067e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f77726f6e672070726f6f662073697a65000000000000000000000000000000006044820152606401611889565b6102c08101516102a08201516101a08301516101c08401516101e08501516102008601516102208701516102808801517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908111918111928111938111948111958111968111971096909616949094169290921616161616600116610360820160005b60038110156119e15781517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000011192909216916020909101906001016119a1565b505080611892576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f736f6d65206f70656e696e67732061726520626967676572207468616e2072006044820152606401611889565b600080600080600080600080604051611a638a8c611b26565b80519450611a71858c611e68565b80519350611a7f848c611e99565b80519250611a8d838c611ee0565b517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000194859006949384900693928390069290069050611f51565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f6572726f722073746174696363616c6c20736861322d323536000000000000006044820152606481fd5b6040516467616d6d6181527f1f84d6f80352f2fc7729113df20d64103bf3eae4204ce683c4b29834a192356060208201527f2e837b96567130647c44cbfdc512df731f0d589d87c364505b5d8bf52f326f5460408201527f0240cbafaea4c9e736b868b0d9095395f8713efb4436b300ffa4cf87664317e260608201527f01157cddb161e79d457e57d106481f3aa7ade784703b5b29eda8083f032e709c60808201527f0690a2dbb7358db0cb77d75c272330856af7e51382a386ca0b67bb03e96b48f460a08201527f2397e85bb92816bd9c0a98fc6d9c38255cc0358c1b656a463438821223d18d1a60c08201527f1de747c8b08f6e8135b238f73f2136d5dd9b23dcc5382f08c4130149c8ab4f5860e08201527f24aaeb1ab69d8d9baf5146abaa1cfaffd933a0142b006a4beea229a5486a826f6101008201527f1f47ced88f79e9a283779f60440e4b544dceb61cf7b0dc400ae00c87823deae56101208201527f1e9a6e10cea071afe356efe7bbd87641f3aa129a0a14997640e06e0102b66cf96101408201527f1de5b062e1c03586e2609cc3edda81bb8dd47468442c9d0a83d4043317cc44916101608201527f1dd50545f38cf0b0c17eaa7c0646e6faa05a8f31d2d27e5835ec0461f50c84b16101808201527f0f9c3581e08299d620cd366d1473887a2f7623dfc2803bb53398a93a29c8cea56101a08201527f25e09ffadafa8cb36dd495dbc07a755617a4d7ad790893ad396904d9fb5eda916101c08201527f21a42d83c31cff7c252fdbe33ed5d37e519ed002253a314b1ffe1374c7351be26101e08201527f1b878ed0d031c3333f1ae0cfbb47690afb6f4457e1dfd784bcd7ec22b952e6ec61020082015260208301610220820160005b8551811015611dc5578251825260209283019290910190600101611da6565b506103c08401915060005b6003811015611df957825182526020808401519083015260409283019290910190600101611dd0565b506020840151815260408401516020820152606084015160408201526080840151606082015260a0840151608082015260c084015160a0820152505060208351026102c50160406003028101905060208282601b850160026107d05a03fa915081905061067e5761067e611ac7565b50604051636265746181528160208201526020816024601c840160026107d05a03fa90508061189257611892611ac7565b60405164616c7068618152826020820152610240820151604082015261026082015160608201526020816065601b840160026107d05a03fa90508061067e5761067e611ac7565b604051637a657461815282602082015260e082015160408201526101008201516060820152610120820151608082015261014082015160a082015261016082015160c082015261018082015160e082015260208160e4601c840160026107d05a03fa90508061067e5761067e611ac7565b9299919850965090945092505050565b6000806000611fca565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f6572726f722073746174696363616c6c206d6f646578700000000000000000006044820152606481fd5b611fd985875160208901611fe3565b50604051516123fa565b604051611ff181848661206c565b60008060005b85811015612060577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001855185510991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182840860209586019594909401939250600101611ff7565b50506040515250505050565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000006120bd85630400000085612398565b087f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e66c81e03716be83b486d6feabcc7ddd0fe6cbf5e72d585d142f7829b058209905060018460005b858110156121af577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103860882527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f1067569af1ff73b20113eff9b8d89d4a605b52b63d68f9ae1c79bd572f4e9212840992506020919091019060010161210a565b506121bb818688612273565b50600190508460005b8581101561226a577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001868551090982526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f1067569af1ff73b20113eff9b8d89d4a605b52b63d68f9ae1c79bd572f4e9212840992506001016121c4565b50505050505050565b600183526000805b838110156122c95781850151828401517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830990506020840193508084880152505060018101905061227b565b50602081038201915080840193505061230a6020840160027f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001038551612398565b60005b83811015610ef55760208503945082517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018651840984527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018184097fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09094019392505060010161230d565b602083526020808401526020604084015280606084015250806080830152507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160a0820152600060208260c08460056107d05a03fa806117c3576117c3611f6b565b61240b604051630400000087612398565b91507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000830860408051600380825260808201909252919350600091906020820160608036833701905050630159089160208201526301a3a4c6604082015263039c844f6060820152905060606124a460036002612f60565b67ffffffffffffffff8111156124bc576124bc612d5d565b6040519080825280602002602001820160405280156124e5578160200160208202803683370190505b5090506124f28187612600565b60005b60038110156125f357600061255d8361250f846002612f60565b8151811061251f5761251f612f8a565b6020026020010151848460026125359190612f60565b612540906001612f77565b8151811061255057612550612f8a565b6020026020010151612635565b905060006125908a8c5187868151811061257957612579612f8a565b602002602001015161258b9190612f77565b6127a0565b90507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018187089550505080806125eb90612fb9565b9150506124f5565b5091979650505050505050565b602082016103c0820160005b6003811015610ef55781518352602080830151908401526040928301929091019060010161260c565b60008061264284846129a6565b905060005b60208110156126a05761265b816008612f60565b8261266783602f612ff1565b6030811061267757612677612f8a565b602002015160ff16901b8361268c9190612f77565b92508061269881612fb9565b915050612647565b506126cb7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183613004565b91506000805b601081101561272a576126e5816008612f60565b836126f183600f612ff1565b6030811061270157612701612f8a565b602002015160ff16901b826127169190612f77565b91508061272281612fb9565b9150506126d1565b507f0e0a77c19a07df2f666ea36f7879462e36fc76959f60cd29ac96341c4ffffffb7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018285089695505050505050565b600080612819565b600060405160208152602080820152602060408201528260608201528360808201527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160a08201526020600060c08360056107d05a03fa90508061280e5761280e611f6b565b505060005192915050565b612843837f1067569af1ff73b20113eff9b8d89d4a605b52b63d68f9ae1c79bd572f4e92126127a8565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001817f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103860893506128996304000000866127a8565b94507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000860894507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e66c81e03716be83b486d6feabcc7ddd0fe6cbf5e72d585d142f7829b05820990506129517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffff856127a8565b93507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000184820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185820995945050505050565b6129ae612d3e565b60408051808201909152600b8082527f42534232322d506c6f6e6b0000000000000000000000000000000000000000006020830152606090600090603090825b6040811015612a2e578484604051602001612a0a929190613063565b60405160208183030381529060405294508080612a2690612fb9565b9150506129ee565b508388888585878a87604051602001612a4e9897969594939291906130ad565b60405160208183030381529060405293506000600285604051612a719190613143565b602060405180830381855afa158015612a8e573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190612ab1919061315f565b90508060018784604051602001612acb9493929190613178565b60405160208183030381529060405294506000600286604051612aee9190613143565b602060405180830381855afa158015612b0b573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190612b2e919061315f565b905060005b6020811015612b8057818160208110612b4e57612b4e612f8a565b1a898260308110612b6157612b61612f8a565b60ff909216602092909202015280612b7881612fb9565b915050612b33565b50604051600083811a9083901a1860f81b7fff00000000000000000000000000000000000000000000000000000000000000166020820152602101604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052955060015b6020811015612c5a5786828260208110612c0a57612c0a612f8a565b1a848360208110612c1d57612c1d612f8a565b1a60f81b60f81c18604051602001612c36929190613063565b60405160208183030381529060405296508080612c5290612fb9565b915050612bee565b508560028885604051602001612c7394939291906131da565b6040516020818303038152906040529550600286604051612c949190613143565b602060405180830381855afa158015612cb1573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190612cd4919061315f565b905060005b6010811015612d3057818160208110612cf457612cf4612f8a565b1a89612d01836020612f77565b60308110612d1157612d11612f8a565b60ff909216602092909202015280612d2881612fb9565b915050612cd9565b505050505050505092915050565b6040518061060001604052806030906020820280368337509192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715612dd357612dd3612d5d565b604052919050565b600082601f830112612dec57600080fd5b8135602067ffffffffffffffff821115612e0857612e08612d5d565b8160051b612e17828201612d8c565b9283528481018201928281019087851115612e3157600080fd5b83870192505b84831015612e5057823582529183019190830190612e37565b979650505050505050565b60008060408385031215612e6e57600080fd5b823567ffffffffffffffff80821115612e8657600080fd5b818501915085601f830112612e9a57600080fd5b8135602082821115612eae57612eae612d5d565b612ede817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f85011601612d8c565b8281528882848701011115612ef257600080fd5b82828601838301376000928101820192909252909450850135915080821115612f1a57600080fd5b50612f2785828601612ddb565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80820281158282048414176117d2576117d2612f31565b808201808211156117d2576117d2612f31565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203612fea57612fea612f31565b5060010190565b818103818111156117d2576117d2612f31565b60008261303a577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500690565b60005b8381101561305a578181015183820152602001613042565b50506000910152565b6000835161307581846020880161303f565b60f89390931b7fff00000000000000000000000000000000000000000000000000000000000000169190920190815260010192915050565b600089516130bf818460208e0161303f565b80830190508981528860208201527fff00000000000000000000000000000000000000000000000000000000000000808960f81b166040830152808860f81b166041830152808760f81b1660428301528551613122816043850160208a0161303f565b60f89590951b16930160438101939093525050604401979650505050505050565b6000825161315581846020870161303f565b9190910192915050565b60006020828403121561317157600080fd5b5051919050565b84815260007fff00000000000000000000000000000000000000000000000000000000000000808660f81b16602084015284516131bc81602186016020890161303f565b60f89490941b16919092016021810191909152602201949350505050565b600085516131ec818460208a0161303f565b80830190507fff00000000000000000000000000000000000000000000000000000000000000808760f81b168252855161322d816001850160208a0161303f565b60f89590951b16930160018101939093525050600201939250505056fea26469706673582212203fde081adb4570983a31bf0bcf69ab7d1e3179c31f4352e851cfe5518626906064736f6c63430008130033
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
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.