Overview
MNT Balance
0 MNT
MNT Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 37,725 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Query NFT | 72213557 | 17 days ago | IN | 1.5 MNT | 0.01895488 | ||||
Query NFT | 71014810 | 45 days ago | IN | 1.5 MNT | 0.01886634 | ||||
Query NFT | 70473565 | 57 days ago | IN | 1.5 MNT | 0.02659794 | ||||
Query NFT | 70473537 | 57 days ago | IN | 1.5 MNT | 0.02659978 | ||||
Query NFT | 70473521 | 57 days ago | IN | 0.00045 MNT | 0.00662659 | ||||
Query NFT | 69020175 | 91 days ago | IN | 1.5 MNT | 0.01773495 | ||||
Query NFT | 68881376 | 94 days ago | IN | 1.5 MNT | 0.01822805 | ||||
Query NFT | 68881287 | 94 days ago | IN | 1.5 MNT | 0.01809722 | ||||
Query NFT | 68720529 | 98 days ago | IN | 1.5 MNT | 0.01741956 | ||||
Query NFT | 68720517 | 98 days ago | IN | 1.5 MNT | 0.01743066 | ||||
Query NFT | 68679844 | 99 days ago | IN | 1.5 MNT | 0.01765393 | ||||
Query NFT | 68650172 | 100 days ago | IN | 1.5 MNT | 0.00650663 | ||||
Query NFT | 68650145 | 100 days ago | IN | 1.5 MNT | 0.00650348 | ||||
Query NFT | 68650046 | 100 days ago | IN | 1.5 MNT | 0.00651342 | ||||
Query NFT | 68567367 | 102 days ago | IN | 1.5 MNT | 0.0209571 | ||||
Query NFT | 68545614 | 102 days ago | IN | 1.5 MNT | 0.01707061 | ||||
Query NFT | 68516633 | 103 days ago | IN | 1.5 MNT | 0.01699537 | ||||
Query NFT | 68512370 | 103 days ago | IN | 1.5 MNT | 0.01702816 | ||||
Query NFT | 68504510 | 103 days ago | IN | 1.5 MNT | 0.01695002 | ||||
Query NFT | 68500434 | 103 days ago | IN | 1.5 MNT | 0.01702712 | ||||
Query NFT | 68499780 | 103 days ago | IN | 1.51 MNT | 0.0187369 | ||||
Query NFT | 68494529 | 103 days ago | IN | 1.5 MNT | 0.01690472 | ||||
Query NFT | 68493892 | 103 days ago | IN | 1.5 MNT | 0.01695796 | ||||
Query NFT | 68493755 | 103 days ago | IN | 1.5 MNT | 0.02537403 | ||||
Query NFT | 68493718 | 103 days ago | IN | 1.5 MNT | 0.01695889 |
Latest 25 internal transactions (View All)
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
72213557 | 17 days ago | 1.5 MNT | ||||
71014810 | 45 days ago | 1.5 MNT | ||||
70473565 | 57 days ago | 1.5 MNT | ||||
70473537 | 57 days ago | 1.5 MNT | ||||
70473521 | 57 days ago | 0.00045 MNT | ||||
69020175 | 91 days ago | 1.5 MNT | ||||
68881376 | 94 days ago | 1.5 MNT | ||||
68881287 | 94 days ago | 1.5 MNT | ||||
68720529 | 98 days ago | 1.5 MNT | ||||
68720517 | 98 days ago | 1.5 MNT | ||||
68679844 | 99 days ago | 1.5 MNT | ||||
68650172 | 100 days ago | 1.5 MNT | ||||
68650145 | 100 days ago | 1.5 MNT | ||||
68650046 | 100 days ago | 1.5 MNT | ||||
68567367 | 102 days ago | 1.5 MNT | ||||
68545614 | 102 days ago | 1.5 MNT | ||||
68516633 | 103 days ago | 1.5 MNT | ||||
68512370 | 103 days ago | 1.5 MNT | ||||
68504510 | 103 days ago | 1.5 MNT | ||||
68500434 | 103 days ago | 1.5 MNT | ||||
68499780 | 103 days ago | 1.51 MNT | ||||
68494529 | 103 days ago | 1.5 MNT | ||||
68493892 | 103 days ago | 1.5 MNT | ||||
68493755 | 103 days ago | 1.5 MNT | ||||
68493718 | 103 days ago | 1.5 MNT |
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
LPNQueryV0
Compiler Version
v0.8.25+commit.b61c2a91
Contract Source Code (Solidity)
/** *Submitted for verification at mantlescan.xyz on 2024-06-27 */ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0 ^0.8.13; // src/Groth16Verifier.sol /// @title Groth16 verifier template. /// @author Remco Bloemen /// @notice Supports verifying Groth16 proofs. Proofs can be in uncompressed /// (256 bytes) and compressed (128 bytes) format. A view function is provided /// to compress proofs. /// @notice See <https://2π.com/23/bn254-compression> for further explanation. library Groth16Verifier { /// Some of the provided public input values are larger than the field modulus. /// @dev Public input elements are not automatically reduced, as this is can be /// a dangerous source of bugs. error PublicInputNotInField(); /// The proof is invalid. /// @dev This can mean that provided Groth16 proof points are not on their /// curves, that pairing equation fails, or that the proof is not for the /// provided public input. error ProofInvalid(); // Addresses of precompiles uint256 constant PRECOMPILE_MODEXP = 0x05; uint256 constant PRECOMPILE_ADD = 0x06; uint256 constant PRECOMPILE_MUL = 0x07; uint256 constant PRECOMPILE_VERIFY = 0x08; // Base field Fp order P and scalar field Fr order R. // For BN254 these are computed as follows: // t = 4965661367192848881 // P = 36⋅t⁴ + 36⋅t³ + 24⋅t² + 6⋅t + 1 // R = 36⋅t⁴ + 36⋅t³ + 18⋅t² + 6⋅t + 1 uint256 constant P = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47; uint256 constant R = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001; // Extension field Fp2 = Fp[i] / (i² + 1) // Note: This is the complex extension field of Fp with i² = -1. // Values in Fp2 are represented as a pair of Fp elements (a₀, a₁) as a₀ + a₁⋅i. // Note: The order of Fp2 elements is *opposite* that of the pairing contract, which // expects Fp2 elements in order (a₁, a₀). This is also the order in which // Fp2 elements are encoded in the public interface as this became convention. // Constants in Fp uint256 constant FRACTION_1_2_FP = 0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea4; uint256 constant FRACTION_27_82_FP = 0x2b149d40ceb8aaae81be18991be06ac3b5b4c5e559dbefa33267e6dc24a138e5; uint256 constant FRACTION_3_82_FP = 0x2fcd3ac2a640a154eb23960892a85a68f031ca0c8344b23a577dcf1052b9e775; // Exponents for inversions and square roots mod P uint256 constant EXP_INVERSE_FP = 0x30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD45; // P - 2 uint256 constant EXP_SQRT_FP = 0xC19139CB84C680A6E14116DA060561765E05AA45A1C72A34F082305B61F3F52; // (P + 1) / 4; // Groth16 alpha point in G1 uint256 constant ALPHA_X = 14203975404520260752093969438138389323271181511080380152435798100099745969379; uint256 constant ALPHA_Y = 18287204318794171332400073719746908786851491729756415454223442198102849074855; // Groth16 beta point in G2 in powers of i uint256 constant BETA_NEG_X_0 = 14182746057118932531119419775248655215999552851587880769894092919815318854079; uint256 constant BETA_NEG_X_1 = 12683390308506128326666583226496302601857318988227740948513564592565150412253; uint256 constant BETA_NEG_Y_0 = 17702544229660055425992089692161462261736050259607705457555294197497668133802; uint256 constant BETA_NEG_Y_1 = 21127033415818163472118538170889890527553241346229649624605221274417900855452; // Groth16 gamma point in G2 in powers of i uint256 constant GAMMA_NEG_X_0 = 499394079463924875962540958234756143708429577458159326946111281188934993028; uint256 constant GAMMA_NEG_X_1 = 21077090675402266397722954859415016654416356733606653058855662580372634814937; uint256 constant GAMMA_NEG_Y_0 = 21079311319027403275557387062295699330658827889470830916149494859737623436894; uint256 constant GAMMA_NEG_Y_1 = 17922207505503121146806922346995760187901822834219765373403832291996633569360; // Groth16 delta point in G2 in powers of i uint256 constant DELTA_NEG_X_0 = 20287536507502928815755415033200397197241347462498607141501277090035751617946; uint256 constant DELTA_NEG_X_1 = 20271679153223182686550786505203159428061009856639272167146423924973293551751; uint256 constant DELTA_NEG_Y_0 = 4646539917572385376126580834872916886805974994211311635641582655361809245150; uint256 constant DELTA_NEG_Y_1 = 8385798782071614272745500550950623661677313521612535590842438779503301227533; // Constant and public input points uint256 constant CONSTANT_X = 12972316725243678057073554578436389425980338930515051194131416297164538120316; uint256 constant CONSTANT_Y = 2467383993954176961469420008365922174939128553633466223895737162957587484853; uint256 constant PUB_0_X = 13485612424243073354768837647639370850516262424802561696539968552274236968416; uint256 constant PUB_0_Y = 16938032263268700169078617345222276997395080086877142413446124891951166401812; uint256 constant PUB_1_X = 9577871516163645227022497024355368800947618780467796129245411052446525718871; uint256 constant PUB_1_Y = 2255554114010427737405622218768298085159488821115248649813601084257761833871; uint256 constant PUB_2_X = 2729903753053334408029284154588476418397822073389430137379814638748981364113; uint256 constant PUB_2_Y = 8588614792037255042687646565980841920112454621626098637175634238186999562225; /// Negation in Fp. /// @notice Returns a number x such that a + x = 0 in Fp. /// @notice The input does not need to be reduced. /// @param a the base /// @return x the result function negate(uint256 a) internal pure returns (uint256 x) { unchecked { x = (P - (a % P)) % P; // Modulo is cheaper than branching } } /// Exponentiation in Fp. /// @notice Returns a number x such that a ^ e = x in Fp. /// @notice The input does not need to be reduced. /// @param a the base /// @param e the exponent /// @return x the result function exp(uint256 a, uint256 e) internal view returns (uint256 x) { bool success; assembly ("memory-safe") { let f := mload(0x40) mstore(f, 0x20) mstore(add(f, 0x20), 0x20) mstore(add(f, 0x40), 0x20) mstore(add(f, 0x60), a) mstore(add(f, 0x80), e) mstore(add(f, 0xa0), P) success := staticcall(gas(), PRECOMPILE_MODEXP, f, 0xc0, f, 0x20) x := mload(f) } if (!success) { // Exponentiation failed. // Should not happen. revert ProofInvalid(); } } /// Invertsion in Fp. /// @notice Returns a number x such that a * x = 1 in Fp. /// @notice The input does not need to be reduced. /// @notice Reverts with ProofInvalid() if the inverse does not exist /// @param a the input /// @return x the solution function invert_Fp(uint256 a) internal view returns (uint256 x) { x = exp(a, EXP_INVERSE_FP); if (mulmod(a, x, P) != 1) { // Inverse does not exist. // Can only happen during G2 point decompression. revert ProofInvalid(); } } /// Square root in Fp. /// @notice Returns a number x such that x * x = a in Fp. /// @notice Will revert with InvalidProof() if the input is not a square /// or not reduced. /// @param a the square /// @return x the solution function sqrt_Fp(uint256 a) internal view returns (uint256 x) { x = exp(a, EXP_SQRT_FP); if (mulmod(x, x, P) != a) { // Square root does not exist or a is not reduced. // Happens when G1 point is not on curve. revert ProofInvalid(); } } /// Square test in Fp. /// @notice Returns wheter a number x exists such that x * x = a in Fp. /// @notice Will revert with InvalidProof() if the input is not a square /// or not reduced. /// @param a the square /// @return x the solution function isSquare_Fp(uint256 a) internal view returns (bool) { uint256 x = exp(a, EXP_SQRT_FP); return mulmod(x, x, P) == a; } /// Square root in Fp2. /// @notice Fp2 is the complex extension Fp[i]/(i^2 + 1). The input is /// a0 + a1 ⋅ i and the result is x0 + x1 ⋅ i. /// @notice Will revert with InvalidProof() if /// * the input is not a square, /// * the hint is incorrect, or /// * the input coefficents are not reduced. /// @param a0 The real part of the input. /// @param a1 The imaginary part of the input. /// @param hint A hint which of two possible signs to pick in the equation. /// @return x0 The real part of the square root. /// @return x1 The imaginary part of the square root. function sqrt_Fp2(uint256 a0, uint256 a1, bool hint) internal view returns (uint256 x0, uint256 x1) { // If this square root reverts there is no solution in Fp2. uint256 d = sqrt_Fp(addmod(mulmod(a0, a0, P), mulmod(a1, a1, P), P)); if (hint) { d = negate(d); } // If this square root reverts there is no solution in Fp2. x0 = sqrt_Fp(mulmod(addmod(a0, d, P), FRACTION_1_2_FP, P)); x1 = mulmod(a1, invert_Fp(mulmod(x0, 2, P)), P); // Check result to make sure we found a root. // Note: this also fails if a0 or a1 is not reduced. if ( a0 != addmod(mulmod(x0, x0, P), negate(mulmod(x1, x1, P)), P) || a1 != mulmod(2, mulmod(x0, x1, P), P) ) { revert ProofInvalid(); } } /// Compress a G1 point. /// @notice Reverts with InvalidProof if the coordinates are not reduced /// or if the point is not on the curve. /// @notice The point at infinity is encoded as (0,0) and compressed to 0. /// @param x The X coordinate in Fp. /// @param y The Y coordinate in Fp. /// @return c The compresed point (x with one signal bit). function compress_g1(uint256 x, uint256 y) internal view returns (uint256 c) { if (x >= P || y >= P) { // G1 point not in field. revert ProofInvalid(); } if (x == 0 && y == 0) { // Point at infinity return 0; } // Note: sqrt_Fp reverts if there is no solution, i.e. the x coordinate is invalid. uint256 y_pos = sqrt_Fp(addmod(mulmod(mulmod(x, x, P), x, P), 3, P)); if (y == y_pos) { return (x << 1) | 0; } else if (y == negate(y_pos)) { return (x << 1) | 1; } else { // G1 point not on curve. revert ProofInvalid(); } } /// Decompress a G1 point. /// @notice Reverts with InvalidProof if the input does not represent a valid point. /// @notice The point at infinity is encoded as (0,0) and compressed to 0. /// @param c The compresed point (x with one signal bit). /// @return x The X coordinate in Fp. /// @return y The Y coordinate in Fp. function decompress_g1(uint256 c) internal view returns (uint256 x, uint256 y) { // Note that X = 0 is not on the curve since 0³ + 3 = 3 is not a square. // so we can use it to represent the point at infinity. if (c == 0) { // Point at infinity as encoded in EIP196 and EIP197. return (0, 0); } bool negate_point = c & 1 == 1; x = c >> 1; if (x >= P) { // G1 x coordinate not in field. revert ProofInvalid(); } // Note: (x³ + 3) is irreducible in Fp, so it can not be zero and therefore // y can not be zero. // Note: sqrt_Fp reverts if there is no solution, i.e. the point is not on the curve. y = sqrt_Fp(addmod(mulmod(mulmod(x, x, P), x, P), 3, P)); if (negate_point) { y = negate(y); } } /// Compress a G2 point. /// @notice Reverts with InvalidProof if the coefficients are not reduced /// or if the point is not on the curve. /// @notice The G2 curve is defined over the complex extension Fp[i]/(i^2 + 1) /// with coordinates (x0 + x1 ⋅ i, y0 + y1 ⋅ i). /// @notice The point at infinity is encoded as (0,0,0,0) and compressed to (0,0). /// @param x0 The real part of the X coordinate. /// @param x1 The imaginary poart of the X coordinate. /// @param y0 The real part of the Y coordinate. /// @param y1 The imaginary part of the Y coordinate. /// @return c0 The first half of the compresed point (x0 with two signal bits). /// @return c1 The second half of the compressed point (x1 unmodified). function compress_g2(uint256 x0, uint256 x1, uint256 y0, uint256 y1) internal view returns (uint256 c0, uint256 c1) { if (x0 >= P || x1 >= P || y0 >= P || y1 >= P) { // G2 point not in field. revert ProofInvalid(); } if ((x0 | x1 | y0 | y1) == 0) { // Point at infinity return (0, 0); } // Compute y^2 // Note: shadowing variables and scoping to avoid stack-to-deep. uint256 y0_pos; uint256 y1_pos; { uint256 n3ab = mulmod(mulmod(x0, x1, P), P - 3, P); uint256 a_3 = mulmod(mulmod(x0, x0, P), x0, P); uint256 b_3 = mulmod(mulmod(x1, x1, P), x1, P); y0_pos = addmod( FRACTION_27_82_FP, addmod(a_3, mulmod(n3ab, x1, P), P), P ); y1_pos = negate( addmod(FRACTION_3_82_FP, addmod(b_3, mulmod(n3ab, x0, P), P), P) ); } // Determine hint bit // If this sqrt fails the x coordinate is not on the curve. bool hint; { uint256 d = sqrt_Fp( addmod(mulmod(y0_pos, y0_pos, P), mulmod(y1_pos, y1_pos, P), P) ); hint = !isSquare_Fp(mulmod(addmod(y0_pos, d, P), FRACTION_1_2_FP, P)); } // Recover y (y0_pos, y1_pos) = sqrt_Fp2(y0_pos, y1_pos, hint); if (y0 == y0_pos && y1 == y1_pos) { c0 = (x0 << 2) | (hint ? 2 : 0) | 0; c1 = x1; } else if (y0 == negate(y0_pos) && y1 == negate(y1_pos)) { c0 = (x0 << 2) | (hint ? 2 : 0) | 1; c1 = x1; } else { // G1 point not on curve. revert ProofInvalid(); } } /// Decompress a G2 point. /// @notice Reverts with InvalidProof if the input does not represent a valid point. /// @notice The G2 curve is defined over the complex extension Fp[i]/(i^2 + 1) /// with coordinates (x0 + x1 ⋅ i, y0 + y1 ⋅ i). /// @notice The point at infinity is encoded as (0,0,0,0) and compressed to (0,0). /// @param c0 The first half of the compresed point (x0 with two signal bits). /// @param c1 The second half of the compressed point (x1 unmodified). /// @return x0 The real part of the X coordinate. /// @return x1 The imaginary poart of the X coordinate. /// @return y0 The real part of the Y coordinate. /// @return y1 The imaginary part of the Y coordinate. function decompress_g2(uint256 c0, uint256 c1) internal view returns (uint256 x0, uint256 x1, uint256 y0, uint256 y1) { // Note that X = (0, 0) is not on the curve since 0³ + 3/(9 + i) is not a square. // so we can use it to represent the point at infinity. if (c0 == 0 && c1 == 0) { // Point at infinity as encoded in EIP197. return (0, 0, 0, 0); } bool negate_point = c0 & 1 == 1; bool hint = c0 & 2 == 2; x0 = c0 >> 2; x1 = c1; if (x0 >= P || x1 >= P) { // G2 x0 or x1 coefficient not in field. revert ProofInvalid(); } uint256 n3ab = mulmod(mulmod(x0, x1, P), P - 3, P); uint256 a_3 = mulmod(mulmod(x0, x0, P), x0, P); uint256 b_3 = mulmod(mulmod(x1, x1, P), x1, P); y0 = addmod(FRACTION_27_82_FP, addmod(a_3, mulmod(n3ab, x1, P), P), P); y1 = negate( addmod(FRACTION_3_82_FP, addmod(b_3, mulmod(n3ab, x0, P), P), P) ); // Note: sqrt_Fp2 reverts if there is no solution, i.e. the point is not on the curve. // Note: (X³ + 3/(9 + i)) is irreducible in Fp2, so y can not be zero. // But y0 or y1 may still independently be zero. (y0, y1) = sqrt_Fp2(y0, y1, hint); if (negate_point) { y0 = negate(y0); y1 = negate(y1); } } /// Compute the public input linear combination. /// @notice Reverts with PublicInputNotInField if the input is not in the field. /// @notice Computes the multi-scalar-multiplication of the public input /// elements and the verification key including the constant term. /// @param input The public inputs. These are elements of the scalar field Fr. /// @return x The X coordinate of the resulting G1 point. /// @return y The Y coordinate of the resulting G1 point. function publicInputMSM(uint256[3] memory input) internal view returns (uint256 x, uint256 y) { // Note: The ECMUL precompile does not reject unreduced values, so we check this. // Note: Unrolling this loop does not cost much extra in code-size, the bulk of the // code-size is in the PUB_ constants. // ECMUL has input (x, y, scalar) and output (x', y'). // ECADD has input (x1, y1, x2, y2) and output (x', y'). // We call them such that ecmul output is already in the second point // argument to ECADD so we can have a tight loop. bool success = true; assembly ("memory-safe") { let f := mload(0x40) let g := add(f, 0x40) let s mstore(f, CONSTANT_X) mstore(add(f, 0x20), CONSTANT_Y) mstore(g, PUB_0_X) mstore(add(g, 0x20), PUB_0_Y) s := mload(input) mstore(add(g, 0x40), s) success := and(success, lt(s, R)) success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40)) success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40)) mstore(g, PUB_1_X) mstore(add(g, 0x20), PUB_1_Y) s := mload(add(input, 32)) mstore(add(g, 0x40), s) success := and(success, lt(s, R)) success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40)) success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40)) mstore(g, PUB_2_X) mstore(add(g, 0x20), PUB_2_Y) s := mload(add(input, 64)) mstore(add(g, 0x40), s) success := and(success, lt(s, R)) success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40)) success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40)) x := mload(f) y := mload(add(f, 0x20)) } if (!success) { // Either Public input not in field, or verification key invalid. // We assume the contract is correctly generated, so the verification key is valid. revert PublicInputNotInField(); } } /// Compress a proof. /// @notice Will revert with InvalidProof if the curve points are invalid, /// but does not verify the proof itself. /// @param proof The uncompressed Groth16 proof. Elements are in the same order as for /// verifyProof. I.e. Groth16 points (A, B, C) encoded as in EIP-197. /// @return compressed The compressed proof. Elements are in the same order as for /// verifyCompressedProof. I.e. points (A, B, C) in compressed format. function compressProof(uint256[8] memory proof) internal view returns (uint256[4] memory compressed) { compressed[0] = compress_g1(proof[0], proof[1]); (compressed[2], compressed[1]) = compress_g2(proof[3], proof[2], proof[5], proof[4]); compressed[3] = compress_g1(proof[6], proof[7]); } /// Verify a Groth16 proof with compressed points. /// @notice Reverts with InvalidProof if the proof is invalid or /// with PublicInputNotInField the public input is not reduced. /// @notice There is no return value. If the function does not revert, the /// proof was successfully verified. /// @param compressedProof the points (A, B, C) in compressed format /// matching the output of compressProof. /// @param input the public input field elements in the scalar field Fr. /// Elements must be reduced. function verifyCompressedProof( uint256[4] calldata compressedProof, uint256[3] memory input ) internal view { (uint256 Ax, uint256 Ay) = decompress_g1(compressedProof[0]); (uint256 Bx0, uint256 Bx1, uint256 By0, uint256 By1) = decompress_g2(compressedProof[2], compressedProof[1]); (uint256 Cx, uint256 Cy) = decompress_g1(compressedProof[3]); (uint256 Lx, uint256 Ly) = publicInputMSM(input); // Verify the pairing // Note: The precompile expects the F2 coefficients in big-endian order. // Note: The pairing precompile rejects unreduced values, so we won't check that here. uint256[24] memory pairings; // e(A, B) pairings[0] = Ax; pairings[1] = Ay; pairings[2] = Bx1; pairings[3] = Bx0; pairings[4] = By1; pairings[5] = By0; // e(C, -δ) pairings[6] = Cx; pairings[7] = Cy; pairings[8] = DELTA_NEG_X_1; pairings[9] = DELTA_NEG_X_0; pairings[10] = DELTA_NEG_Y_1; pairings[11] = DELTA_NEG_Y_0; // e(α, -β) pairings[12] = ALPHA_X; pairings[13] = ALPHA_Y; pairings[14] = BETA_NEG_X_1; pairings[15] = BETA_NEG_X_0; pairings[16] = BETA_NEG_Y_1; pairings[17] = BETA_NEG_Y_0; // e(L_pub, -γ) pairings[18] = Lx; pairings[19] = Ly; pairings[20] = GAMMA_NEG_X_1; pairings[21] = GAMMA_NEG_X_0; pairings[22] = GAMMA_NEG_Y_1; pairings[23] = GAMMA_NEG_Y_0; // Check pairing equation. bool success; uint256[1] memory output; assembly ("memory-safe") { success := staticcall(gas(), PRECOMPILE_VERIFY, pairings, 0x300, output, 0x20) } if (!success || output[0] != 1) { // Either proof or verification key invalid. // We assume the contract is correctly generated, so the verification key is valid. revert ProofInvalid(); } } /// Verify an uncompressed Groth16 proof. /// @notice Reverts with InvalidProof if the proof is invalid or /// with PublicInputNotInField the public input is not reduced. /// @notice There is no return value. If the function does not revert, the /// proof was successfully verified. /// @param proof the points (A, B, C) in EIP-197 format matching the output /// of compressProof. /// @param input the public input field elements in the scalar field Fr. /// Elements must be reduced. function verifyProof(uint256[8] memory proof, uint256[3] memory input) internal view { (uint256 x, uint256 y) = publicInputMSM(input); // Note: The precompile expects the F2 coefficients in big-endian order. // Note: The pairing precompile rejects unreduced values, so we won't check that here. bool success; assembly ("memory-safe") { let f := mload(0x40) // Free memory pointer. // Copy points (A, B, C) to memory. They are already in correct encoding. // This is pairing e(A, B) and G1 of e(C, -δ). mstore(f, mload(add(proof, 0x00))) mstore(add(f, 0x20), mload(add(proof, 0x20))) mstore(add(f, 0x40), mload(add(proof, 0x40))) mstore(add(f, 0x60), mload(add(proof, 0x60))) mstore(add(f, 0x80), mload(add(proof, 0x80))) mstore(add(f, 0xa0), mload(add(proof, 0xa0))) mstore(add(f, 0xc0), mload(add(proof, 0xc0))) mstore(add(f, 0xe0), mload(add(proof, 0xe0))) // Complete e(C, -δ) and write e(α, -β), e(L_pub, -γ) to memory. // OPT: This could be better done using a single codecopy, but // Solidity (unlike standalone Yul) doesn't provide a way to // to do this. mstore(add(f, 0x100), DELTA_NEG_X_1) mstore(add(f, 0x120), DELTA_NEG_X_0) mstore(add(f, 0x140), DELTA_NEG_Y_1) mstore(add(f, 0x160), DELTA_NEG_Y_0) mstore(add(f, 0x180), ALPHA_X) mstore(add(f, 0x1a0), ALPHA_Y) mstore(add(f, 0x1c0), BETA_NEG_X_1) mstore(add(f, 0x1e0), BETA_NEG_X_0) mstore(add(f, 0x200), BETA_NEG_Y_1) mstore(add(f, 0x220), BETA_NEG_Y_0) mstore(add(f, 0x240), x) mstore(add(f, 0x260), y) mstore(add(f, 0x280), GAMMA_NEG_X_1) mstore(add(f, 0x2a0), GAMMA_NEG_X_0) mstore(add(f, 0x2c0), GAMMA_NEG_Y_1) mstore(add(f, 0x2e0), GAMMA_NEG_Y_0) // Check pairing equation. success := staticcall(gas(), PRECOMPILE_VERIFY, f, 0x300, f, 0x20) // Also check returned value (both are either 1 or 0). success := and(success, mload(f)) } if (!success) { // Either proof or verification key invalid. // We assume the contract is correctly generated, so the verification key is valid. revert ProofInvalid(); } } bytes32 constant CIRCUIT_DIGEST = 0x299431a9262b45ab4993c36cd23c4cf37af4a66eb6207c49202a7c4176c60fbf; } // src/interfaces/ILPNRegistry.sol /// @title ILPNRegistry /// @notice Interface for the LPNRegistryV0 contract. interface ILPNRegistry { /// @notice Event emitted when a new client registers. /// @param storageContract The address of the smart contract to be indexed. /// @param client The address of the client who requested this contract to be indexed. /// @param mappingSlot The storage slot of the client's mapping to be computed and proved over. /// @param lengthSlot The storage slot of the variable storing the length of the client's mapping. event NewRegistration( address indexed storageContract, address indexed client, uint256 mappingSlot, uint256 lengthSlot ); /// @notice Event emitted when a new request is made. /// @param requestId The ID of the request. /// @param storageContract The address of the smart contract with the storage associated with the request. /// @param client The address of the client who made this request. /// @param params The query params associated with this query type. /// @param startBlock The starting block for the computation. /// @param endBlock The ending block for the computation. /// @param proofBlock The requested block for the proof to be computed against. /// Currently required for OP Stack chains event NewRequest( uint256 indexed requestId, address indexed storageContract, address indexed client, bytes32 params, uint256 startBlock, uint256 endBlock, uint256 offset, uint256 gasFee, uint256 proofBlock ); /// @notice Event emitted when a response is received. /// @param requestId The ID of the request. /// @param client The address of the client who made the matching request. /// @param results The computed results for the request. event NewResponse( uint256 indexed requestId, address indexed client, uint256[] results ); /// @notice The gas fee paid for on request to reimburse the response transaction. function gasFee() external returns (uint256); /// @notice Registers a client with the provided mapping and length slots. /// @param storageContract The address of the contract to be queried. /// @param mappingSlot The storage slot of the client's mapping to be computed and proved over. /// @param lengthSlot The storage slot of the variable storing the length of the client's mapping. function register( address storageContract, uint256 mappingSlot, uint256 lengthSlot ) external; /// @notice Submits a new request to the registry. /// @param storageContract The address of the smart contract with the storage associated with the request. /// @param params The query params associated with this query type. /// @param startBlock The starting block for the computation. /// @param endBlock The ending block for the computation. /// @return The ID of the newly created request. function request( address storageContract, bytes32 params, uint256 startBlock, uint256 endBlock ) external payable returns (uint256); /// @notice Submits a response to a specific request. /// @param requestId_ The ID of the request to respond to. /// @param data The proof, inputs, and public inputs to verify. /// - groth16_proof.proofs: 8 * U256 = 256 bytes /// - groth16_proof.inputs: 3 * U256 = 96 bytes /// - plonky2_proof.public_inputs: the little-endian bytes of public inputs exported by user /// @param blockNumber The block number of the block hash corresponding to the proof. function respond( uint256 requestId_, bytes32[] calldata data, uint256 blockNumber ) external; } // src/Groth16VerifierExtensions.sol library Groth16VerifierExtensions { // byteLen(uint160) / 4 uint32 constant PACKED_ADDRESS_LEN = 5; // byteLen(bytes32) / 4 uint32 constant PACKED_HASH_LEN = 8; // byteLen(uint256) / 4 uint32 constant PACKED_U256_LEN = 8; // Top 3 bits mask. uint256 constant TOP_THREE_BIT_MASK = ~(uint256(7) << 253); // Set the number of the NFT IDs. Each ID is an uint32. uint32 constant L = 5; // The start bytes32 offset of plonky2 public inputs in the whole data. // groth16_proof_number (8) + groth16_input_number (3) uint32 constant PLONKY2_PI_BYTES32_OFFSET = 11; // The total length of the plonky2 public inputs. Each input value is // serialized as an uint64. It's related with both the full proof // serialization and the wrapped circuit code. uint32 constant PI_TOTAL_LEN = (L + 41) * 8; // The min block number offset in the plonky2 public inputs. uint32 constant PI_MIN_BLOCK_NUM_OFFSET = 2 * 8; // The max block number offset in the plonky2 public inputs. uint32 constant PI_MAX_BLOCK_NUM_OFFSET = PI_MIN_BLOCK_NUM_OFFSET + 8; // The contract address offset in the plonky2 public inputs. uint32 constant PI_CONTRACT_ADDR_OFFSET = PI_MAX_BLOCK_NUM_OFFSET + 8; // The user address offset in the plonky2 public inputs. uint32 constant PI_USER_ADDR_OFFSET = PI_CONTRACT_ADDR_OFFSET + PACKED_ADDRESS_LEN * 8; // The NFT IDS offset in the plonky2 public inputs. uint32 constant PI_NFT_IDS_OFFSET = 16 * 8; // The block hash offset in the plonky2 public inputs. uint32 constant PI_BLOCK_HASH_OFFSET = PI_NFT_IDS_OFFSET + L * 8; // The rewards rate offset in the plonky2 public inputs. uint32 constant PI_REWARDS_RATE_OFFSET = PI_BLOCK_HASH_OFFSET + PACKED_U256_LEN * 8; // The ERC20 result offset in the plonky2 public inputs. uint32 constant PI_ERC20_RESULT_OFFSET = PI_REWARDS_RATE_OFFSET + PACKED_U256_LEN * 8; // The query identifier offset in the plonky2 public inputs. uint32 constant PI_QUERY_IDENTIFIER_OFFSET = PI_ERC20_RESULT_OFFSET + PACKED_U256_LEN * 8; // Supported query identifiers uint8 constant QUERY_IDENTIFIER_NFT = 67; uint8 constant QUERY_IDENTIFIER_ERC20 = 88; // The query struct used to check with the public inputs. struct Query { address contractAddress; uint96 minBlockNumber; address userAddress; uint96 maxBlockNumber; address clientAddress; uint88 rewardsRate; uint8 identifier; bytes32 blockHash; } // This processQuery function does the followings: // 1. Parse the Groth16 proofs (8 uint256) and inputs (3 uint256) from the `data` argument, and // call `verifyProof` function for Groth16 verification. // 2. Parse the plonky2 public inputs from the `data` argument. // 3. Calculate sha256 on the inputs to a hash value, and set the top 3 bits of this hash to 0. // Then asset this hash value must be equal to the last Groth16 input (groth16_inputs[2]). // 4. Parse a Query instance from the plonky2 public inputs, and asset it must be equal to the // expected `query` argument. // 5. Parse and return the query result from the plonky2 public inputs. function processQuery(bytes32[] calldata data, Query memory query) internal view returns (uint256[] memory) { // 1. Do Groth16 verification. uint256[3] memory groth16_inputs = verifyGroth16Proof(data); // 2. Parse the plonky2 public inputs. bytes memory pis = parsePlonky2Inputs(data); // 3. Ensure the hash of plonky2 public inputs must be equal to the last Groth16 input. verifyPlonky2Inputs(pis, groth16_inputs); // 4. Asset the query in plonky2 public inputs must be equal to expected `query` argument. verifyQuery(pis, query); // 5. Parse and return the query result. return parseQueryResult(pis, query.identifier); } // Parse the Groth16 proofs and inputs, and do verification. It returns the Groth16 inputs. function verifyGroth16Proof(bytes32[] calldata data) internal view returns (uint256[3] memory) { uint256[8] memory proofs; uint256[3] memory inputs; for (uint32 i = 0; i < 8; ++i) { proofs[i] = convertBytes32ToU256(data[i]); } for (uint32 i = 0; i < 3; ++i) { inputs[i] = convertBytes32ToU256(data[i + 8]); } // Require the sha256 hash equals to the last Groth16 input. require( inputs[0] == uint256(Groth16Verifier.CIRCUIT_DIGEST), "The first Groth16 input must be equal to the circuit digest" ); // Do Groth16 verification. Groth16Verifier.verifyProof(proofs, inputs); return inputs; } // Parse the plonky2 public inputs. function parsePlonky2Inputs(bytes32[] calldata data) internal pure returns (bytes memory) { bytes memory pis = new bytes(PI_TOTAL_LEN); uint32 bytes32_len = PI_TOTAL_LEN / 32; for (uint32 i = 0; i < bytes32_len; ++i) { bytes32 b = data[PLONKY2_PI_BYTES32_OFFSET + i]; for (uint32 j = 0; j < 32; ++j) { pis[i * 32 + j] = bytes1(b[j]); } } // Set the remaining bytes. bytes32 remaining_data = data[PLONKY2_PI_BYTES32_OFFSET + bytes32_len]; for (uint32 i = 0; i < PI_TOTAL_LEN % 32; ++i) { pis[bytes32_len * 32 + i] = remaining_data[i]; } return pis; } // Calculate sha256 on the plonky2 inputs, and asset it must be equal to the last Groth16 input. function verifyPlonky2Inputs( bytes memory pis, uint256[3] memory groth16_inputs ) internal pure { // Calculate sha256. bytes32 pis_hash_bytes = sha256(pis); uint256 pis_hash = uint256(pis_hash_bytes); // Set the top 3 bits of the hash value to 0. pis_hash = pis_hash & TOP_THREE_BIT_MASK; // Require the sha256 hash equals to the last Groth16 input. require( pis_hash == groth16_inputs[2], "The plonky2 public inputs hash must be equal to the last of the Groth16 inputs" ); } // Verify the plonky2 inputs with the expected Query instance. function verifyQuery(bytes memory pis, Query memory query) internal pure { uint32 minBlockNumber = convertToU32(pis, PI_MIN_BLOCK_NUM_OFFSET); require( minBlockNumber == query.minBlockNumber, "The parsed min block number must be equal to the expected one in query." ); uint32 maxBlockNumber = convertToU32(pis, PI_MAX_BLOCK_NUM_OFFSET); require( maxBlockNumber == query.maxBlockNumber, "The parsed max block number must be equal to the expected one in query." ); address contractAddress = convertToAddress(pis, PI_CONTRACT_ADDR_OFFSET); require( contractAddress == query.contractAddress, "The parsed contract address must be equal to the expected one in query." ); address userAddress = convertToAddress(pis, PI_USER_ADDR_OFFSET); require( userAddress == query.userAddress, "The parsed user address must be equal to the expected one in query." ); bytes32 blockHash = bytes32(convertToHash(pis, PI_BLOCK_HASH_OFFSET)); require( blockHash == query.blockHash, "The parsed block hash must be equal to the expected one in query." ); if (query.identifier == QUERY_IDENTIFIER_ERC20) { uint256 rewardsRate = convertByteSliceToU256(pis, PI_REWARDS_RATE_OFFSET); require( rewardsRate == query.rewardsRate, "The parsed rewards rate must be equal to the expected one in query." ); } require( uint8(pis[PI_QUERY_IDENTIFIER_OFFSET]) == query.identifier, "The parsed identifier must be equal to the expected one in query." ); } // Parse the query result from the plonky2 public inputs. function parseQueryResult(bytes memory pis, uint8 identifier) internal pure returns (uint256[] memory) { if (identifier == QUERY_IDENTIFIER_NFT) { return parseNftIds(pis); } else if (identifier == QUERY_IDENTIFIER_ERC20) { return parseErc20Result(pis); } else { revert("Unsupported query identifier"); } } // Parse the `L` NFT IDs from the plonky2 public inputs. function parseNftIds(bytes memory pis) internal pure returns (uint256[] memory) { uint256[] memory nft_ids = new uint256[](L); for (uint32 i = 0; i < L; ++i) { nft_ids[i] = uint256(convertToLeftPaddingU32(pis, PI_NFT_IDS_OFFSET + i * 8)); } return nft_ids; } // Parse the ERC20 result from the plonky2 public inputs. function parseErc20Result(bytes memory pis) internal pure returns (uint256[] memory) { uint256[] memory result = new uint256[](1); result[0] = convertByteSliceToU256(pis, PI_ERC20_RESULT_OFFSET); return result; } // Convert to an uint32 from a memory offset. function convertToU32(bytes memory data, uint32 offset) internal pure returns (uint32) { uint32 result; for (uint32 i = 0; i < 4; ++i) { result |= uint32(uint8(data[i + offset])) << (8 * i); } return result; } // Convert to an uint32 of left padding from a memory offset. function convertToLeftPaddingU32(bytes memory data, uint32 offset) internal pure returns (uint32) { uint32 result; for (uint32 i = 0; i < 4; ++i) { result |= uint32(uint8(data[i + offset])) << (8 * (3 - i)); } return result; } // Convert a bytes32 to an uint256. function convertBytes32ToU256(bytes32 b) internal pure returns (uint256) { uint256 result; for (uint32 i = 0; i < 32; i++) { result |= uint256(uint8(b[i])) << (8 * i); } return result; } // Convert the specified byte slice to an uint256. function convertByteSliceToU256(bytes memory pis, uint32 offset) internal pure returns (uint256) { uint256 result; for (uint32 i = 0; i < 8; ++i) { result |= uint256(convertToU32(pis, offset + i * 8)) << (32 * i); } return result; } // Convert to an address from a memory offset. function convertToAddress(bytes memory pis, uint32 offset) internal pure returns (address) { uint160 result; for (uint32 i = 0; i < PACKED_ADDRESS_LEN; ++i) { result |= uint160(convertToLeftPaddingU32(pis, offset + i * 8)) << (32 * (PACKED_ADDRESS_LEN - 1 - i)); } return address(result); } // Convert to a hash from a memory offset. function convertToHash(bytes memory pis, uint32 offset) internal pure returns (bytes32) { uint256 result; for (uint32 i = 0; i < PACKED_HASH_LEN; ++i) { result |= uint256(convertToLeftPaddingU32(pis, offset + i * 8)) << (32 * (PACKED_HASH_LEN - 1 - i)); } return bytes32(result); } } // src/interfaces/ILPNClient.sol /** * @title ILPNClient * @notice Interface for the LPNClientV0 contract. */ interface ILPNClient { /// @notice Callback function called by the LPNRegistry contract. /// @param requestId The ID of the request. /// @param results The result of the request. function lpnCallback(uint256 requestId, uint256[] calldata results) external; } // src/client/LPNClientV0.sol error CallbackNotAuthorized(); abstract contract LPNClientV0 is ILPNClient { ILPNRegistry public lpnRegistry; modifier onlyLagrangeRegistry() { if (msg.sender != address(lpnRegistry)) { revert CallbackNotAuthorized(); } _; } constructor(ILPNRegistry _lpnRegistry) { lpnRegistry = _lpnRegistry; } function lpnCallback(uint256 requestId, uint256[] calldata results) external onlyLagrangeRegistry { processCallback(requestId, results); } function processCallback(uint256 requestId, uint256[] calldata results) internal virtual; } // src/utils/QueryParams.sol uint8 constant NFT_QUERY_IDENTIFIER = uint8(Groth16VerifierExtensions.QUERY_IDENTIFIER_NFT); uint8 constant ERC20_QUERY_IDENTIFIER = uint8(Groth16VerifierExtensions.QUERY_IDENTIFIER_ERC20); /// @notice Error thrown when specifying params with an unknown query identifier. error UnsupportedParams(); /// @title Helper lib for constructing params to queries library QueryParams { /// @notice Calldata parameters for an NFT Query /// @param identifier The identifier for the query type /// @param userAddress The address of the user associated with the query /// @param offset The offset value for pagination or data fetching struct NFTQueryParams { uint8 identifier; address userAddress; uint88 offset; } /// @notice Calldata parameters for an ERC20 Query /// @param identifier The identifier for the query type /// @param userAddress The address of the user associated with the query /// @param rewardsRate The rewards rate for the ERC20 token struct ERC20QueryParams { uint8 identifier; address userAddress; uint88 rewardsRate; } /// @notice Combined structure of all possible query parameters /// @param identifier The identifier for the query type /// @param userAddress The address of the user associated with the query /// @param rewardsRate The rewards rate for the ERC20 token /// @param offset The offset value for pagination or data fetching struct CombinedParams { uint8 identifier; address userAddress; uint88 rewardsRate; uint256 offset; } function newNFTQueryParams(address userAddress, uint88 offset) internal pure returns (NFTQueryParams memory) { return NFTQueryParams(NFT_QUERY_IDENTIFIER, userAddress, offset); } function newERC20QueryParams(address userAddress, uint88 rewardsRate) internal pure returns (ERC20QueryParams memory) { return ERC20QueryParams(ERC20_QUERY_IDENTIFIER, userAddress, rewardsRate); } function toBytes32(NFTQueryParams memory params) internal pure returns (bytes32) { return bytes32( uint256(params.identifier) << 248 | uint256(uint160(params.userAddress)) << 88 | uint256(params.offset) ); } function toBytes32(ERC20QueryParams memory params) internal pure returns (bytes32) { return bytes32( uint256(params.identifier) << 248 | uint256(uint160(params.userAddress)) << 88 | uint256(params.rewardsRate) ); } function fromBytes32(NFTQueryParams memory, bytes32 params) internal pure returns (NFTQueryParams memory) { uint8 identifier = uint8(uint256(params) >> 248); address userAddress = address(uint160(uint256(params) >> 88)); uint88 offset = uint88(uint256(params)); return NFTQueryParams(identifier, userAddress, offset); } function fromBytes32(ERC20QueryParams memory, bytes32 params) internal pure returns (ERC20QueryParams memory) { uint8 identifier = uint8(uint256(params) >> 248); address userAddress = address(uint160(uint256(params) >> 88)); uint88 rewardsRate = uint88(uint256(params)); return ERC20QueryParams(identifier, userAddress, rewardsRate); } /// @notice Parse structured values from 32 bytes of params /// @param params 32-bytes of abi-encoded params values function combinedFromBytes32(bytes32 params) internal pure returns (CombinedParams memory) { CombinedParams memory cp = CombinedParams({ identifier: uint8(bytes1(params[0])), userAddress: address(0), rewardsRate: uint88(0), offset: uint88(0) }); if (cp.identifier == NFT_QUERY_IDENTIFIER) { NFTQueryParams memory p; p = fromBytes32(p, params); cp.userAddress = p.userAddress; cp.offset = p.offset; return cp; } if (cp.identifier == ERC20_QUERY_IDENTIFIER) { ERC20QueryParams memory p; p = fromBytes32(p, params); cp.userAddress = p.userAddress; cp.rewardsRate = p.rewardsRate; return cp; } revert UnsupportedParams(); } } // src/client/LPNQueryV0.sol /** * @title LPNQueryV0 * @dev A contract for querying NFT ownership using the Lagrange Euclid testnet. */ contract LPNQueryV0 is LPNClientV0 { using QueryParams for QueryParams.NFTQueryParams; using QueryParams for QueryParams.ERC20QueryParams; /** * @dev Struct to store metadata about a query request. * @param sender The address that sent the query request. * @param holder The address of the NFT holder being queried. */ struct RequestMetadata { address sender; address holder; } /** * @dev Mapping to store request metadata by request ID. */ mapping(uint256 requestId => RequestMetadata request) public requests; /** * @dev Event emitted when a query request is made. * @param sender The address that sent the query request. * @param storageContract The address of the NFT contract being queried. */ event Query(address indexed sender, address indexed storageContract); /** * @dev Event emitted when the result of a query is received. * @param requestId The ID of the query request. * @param sender The address that sent the query request. * @param holder The address of the NFT holder that was queried. * @param results The array of NFT IDs owned by the queried holder. */ event Result( uint256 indexed requestId, address indexed sender, address indexed holder, uint256[] results ); /** * @dev Constructor to initialize the LPNQueryV0 contract. * @param lpnRegistry The address of the LPN registry contract. */ constructor(ILPNRegistry lpnRegistry) LPNClientV0(lpnRegistry) {} /** * @dev Function to query the NFT IDs of a specific owner over a range of blocks. * @param storageContract The address of the NFT contract to query. * @param holder The address of the NFT holder to query. * @param startBlock The starting block number for the query range. * @param endBlock The ending block number for the query range. * @param offset The offset for pagination of results. */ function queryNFT( address storageContract, address holder, uint256 startBlock, uint256 endBlock, uint88 offset ) external payable { uint256 requestId = lpnRegistry.request{value: msg.value}( storageContract, QueryParams.newNFTQueryParams(holder, offset).toBytes32(), startBlock, endBlock ); requests[requestId] = RequestMetadata({sender: msg.sender, holder: holder}); emit Query(msg.sender, storageContract); } /** * @dev Function to query the proportionate erc20 balance of a specific token holder over a range of blocks. * @param storageContract The address of the NFT contract to query. * @param holder The address of the NFT holder to query. * @param startBlock The starting block number for the query range. * @param endBlock The ending block number for the query range. * @param rewardsRate The multiplier to apply for e.g. calculating rewards. */ function queryERC20( address storageContract, address holder, uint256 startBlock, uint256 endBlock, uint88 rewardsRate ) external payable { uint256 requestId = lpnRegistry.request{value: msg.value}( storageContract, QueryParams.newERC20QueryParams(holder, rewardsRate).toBytes32(), startBlock, endBlock ); requests[requestId] = RequestMetadata({sender: msg.sender, holder: holder}); emit Query(msg.sender, storageContract); } /** * @dev Internal function called by LPNClientV0 to provide the result of a query. * @param requestId The ID of the query request. * @param results The array of NFT IDs owned by the queried holder. */ function processCallback(uint256 requestId, uint256[] calldata results) internal override { RequestMetadata memory req = requests[requestId]; emit Result(requestId, req.sender, req.holder, results); delete requests[requestId]; } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"contract ILPNRegistry","name":"lpnRegistry","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CallbackNotAuthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"storageContract","type":"address"}],"name":"Query","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"holder","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"results","type":"uint256[]"}],"name":"Result","type":"event"},{"inputs":[{"internalType":"uint256","name":"requestId","type":"uint256"},{"internalType":"uint256[]","name":"results","type":"uint256[]"}],"name":"lpnCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lpnRegistry","outputs":[{"internalType":"contract ILPNRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"storageContract","type":"address"},{"internalType":"address","name":"holder","type":"address"},{"internalType":"uint256","name":"startBlock","type":"uint256"},{"internalType":"uint256","name":"endBlock","type":"uint256"},{"internalType":"uint88","name":"rewardsRate","type":"uint88"}],"name":"queryERC20","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"storageContract","type":"address"},{"internalType":"address","name":"holder","type":"address"},{"internalType":"uint256","name":"startBlock","type":"uint256"},{"internalType":"uint256","name":"endBlock","type":"uint256"},{"internalType":"uint88","name":"offset","type":"uint88"}],"name":"queryNFT","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"requestId","type":"uint256"}],"name":"requests","outputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"holder","type":"address"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
6080604052348015600e575f80fd5b50604051610628380380610628833981016040819052602b91604e565b5f80546001600160a01b0319166001600160a01b03929092169190911790556079565b5f60208284031215605d575f80fd5b81516001600160a01b03811681146072575f80fd5b9392505050565b6105a2806100865f395ff3fe608060405260043610610049575f3560e01c80633e8258f11461004d5780636337ec5a1461006e57806381d12c58146100a9578063de72e30214610108578063f0ce5ce11461011b575b5f80fd5b348015610058575f80fd5b5061006c610067366004610412565b61012e565b005b348015610079575f80fd5b505f5461008c906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156100b4575f80fd5b506100e86100c336600461048a565b600160208190525f918252604090912080549101546001600160a01b03918216911682565b604080516001600160a01b039384168152929091166020830152016100a0565b61006c6101163660046104bc565b610168565b61006c6101293660046104bc565b61030b565b5f546001600160a01b03163314610158576040516311016bf360e01b815260040160405180910390fd5b61016383838361036f565b505050565b5f80546001600160a01b0316638632627e34886102036101cc8a8860408051606080820183525f80835260208084018290529284015282519081018352604381526001600160a01b0394909416908401526001600160581b03919091169082015290565b5f81604001516001600160581b0316605883602001516001600160a01b0316901b60f8845f015160ff16901b17175f1b9050919050565b6040516001600160e01b031960e086901b1681526001600160a01b0390921660048301526024820152604481018890526064810187905260840160206040518083038185885af1158015610259573d5f803e3d5ffd5b50505050506040513d601f19601f8201168201806040525081019061027e919061051e565b604080518082018252338082526001600160a01b0389811660208085019182525f878152600191829052868120955186549085166001600160a01b03199182161787559251959091018054958416959092169490941790559251939450918916927f5904592568a4fbb78135ee4f11eab3d6638454d85eec4b0f4e1002f125fd66b29190a3505050505050565b5f80546001600160a01b0316638632627e34886102036101cc8a8860408051606080820183525f80835260208084018290529284015282519081018352605881526001600160a01b0394909416908401526001600160581b03919091169082015290565b5f838152600160208181526040928390208351808501855281546001600160a01b03908116808352929094015490931691830182905292519192909186907f96cd981e2886f10a0f0fdfa65634c1242f211bf9abdbaaf18e199aed945b6ff9906103dc9088908890610535565b60405180910390a45050505f90815260016020819052604090912080546001600160a01b03199081168255910180549091169055565b5f805f60408486031215610424575f80fd5b83359250602084013567ffffffffffffffff80821115610442575f80fd5b818601915086601f830112610455575f80fd5b813581811115610463575f80fd5b8760208260051b8501011115610477575f80fd5b6020830194508093505050509250925092565b5f6020828403121561049a575f80fd5b5035919050565b80356001600160a01b03811681146104b7575f80fd5b919050565b5f805f805f60a086880312156104d0575f80fd5b6104d9866104a1565b94506104e7602087016104a1565b9350604086013592506060860135915060808601356001600160581b0381168114610510575f80fd5b809150509295509295909350565b5f6020828403121561052e575f80fd5b5051919050565b602080825281018290525f6001600160fb1b03831115610553575f80fd5b8260051b8085604085013791909101604001939250505056fea2646970667358221220a3285985890ef12078da18e3d3bb7bdf7659eb71c387608473d3257018a1affa64736f6c634300081900330000000000000000000000002584665beff871534118aabae781bc267af142f9
Deployed Bytecode
0x608060405260043610610049575f3560e01c80633e8258f11461004d5780636337ec5a1461006e57806381d12c58146100a9578063de72e30214610108578063f0ce5ce11461011b575b5f80fd5b348015610058575f80fd5b5061006c610067366004610412565b61012e565b005b348015610079575f80fd5b505f5461008c906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156100b4575f80fd5b506100e86100c336600461048a565b600160208190525f918252604090912080549101546001600160a01b03918216911682565b604080516001600160a01b039384168152929091166020830152016100a0565b61006c6101163660046104bc565b610168565b61006c6101293660046104bc565b61030b565b5f546001600160a01b03163314610158576040516311016bf360e01b815260040160405180910390fd5b61016383838361036f565b505050565b5f80546001600160a01b0316638632627e34886102036101cc8a8860408051606080820183525f80835260208084018290529284015282519081018352604381526001600160a01b0394909416908401526001600160581b03919091169082015290565b5f81604001516001600160581b0316605883602001516001600160a01b0316901b60f8845f015160ff16901b17175f1b9050919050565b6040516001600160e01b031960e086901b1681526001600160a01b0390921660048301526024820152604481018890526064810187905260840160206040518083038185885af1158015610259573d5f803e3d5ffd5b50505050506040513d601f19601f8201168201806040525081019061027e919061051e565b604080518082018252338082526001600160a01b0389811660208085019182525f878152600191829052868120955186549085166001600160a01b03199182161787559251959091018054958416959092169490941790559251939450918916927f5904592568a4fbb78135ee4f11eab3d6638454d85eec4b0f4e1002f125fd66b29190a3505050505050565b5f80546001600160a01b0316638632627e34886102036101cc8a8860408051606080820183525f80835260208084018290529284015282519081018352605881526001600160a01b0394909416908401526001600160581b03919091169082015290565b5f838152600160208181526040928390208351808501855281546001600160a01b03908116808352929094015490931691830182905292519192909186907f96cd981e2886f10a0f0fdfa65634c1242f211bf9abdbaaf18e199aed945b6ff9906103dc9088908890610535565b60405180910390a45050505f90815260016020819052604090912080546001600160a01b03199081168255910180549091169055565b5f805f60408486031215610424575f80fd5b83359250602084013567ffffffffffffffff80821115610442575f80fd5b818601915086601f830112610455575f80fd5b813581811115610463575f80fd5b8760208260051b8501011115610477575f80fd5b6020830194508093505050509250925092565b5f6020828403121561049a575f80fd5b5035919050565b80356001600160a01b03811681146104b7575f80fd5b919050565b5f805f805f60a086880312156104d0575f80fd5b6104d9866104a1565b94506104e7602087016104a1565b9350604086013592506060860135915060808601356001600160581b0381168114610510575f80fd5b809150509295509295909350565b5f6020828403121561052e575f80fd5b5051919050565b602080825281018290525f6001600160fb1b03831115610553575f80fd5b8260051b8085604085013791909101604001939250505056fea2646970667358221220a3285985890ef12078da18e3d3bb7bdf7659eb71c387608473d3257018a1affa64736f6c63430008190033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000002584665beff871534118aabae781bc267af142f9
-----Decoded View---------------
Arg [0] : lpnRegistry (address): 0x2584665Beff871534118aAbAE781BC267Af142f9
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 0000000000000000000000002584665beff871534118aabae781bc267af142f9
Deployed Bytecode Sourcemap
49138:4254:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;43995:175;;;;;;;;;;-1:-1:-1;43995:175:0;;;;;:::i;:::-;;:::i;:::-;;43696:31;;;;;;;;;;-1:-1:-1;43696:31:0;;;;-1:-1:-1;;;;;43696:31:0;;;;;;-1:-1:-1;;;;;887:32:1;;;869:51;;857:2;842:18;43696:31:0;;;;;;;;49672:69;;;;;;;;;;-1:-1:-1;49672:69:0;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;49672:69:0;;;;;;;;;;;-1:-1:-1;;;;;1346:15:1;;;1328:34;;1398:15;;;;1393:2;1378:18;;1371:43;1263:18;49672:69:0;1116:304:1;51212:571:0;;;;;;:::i;:::-;;:::i;52281:585::-;;;;;;:::i;:::-;;:::i;43995:175::-;43805:11;;-1:-1:-1;;;;;43805:11:0;43783:10;:34;43779:97;;43841:23;;-1:-1:-1;;;43841:23:0;;;;;;;;;;;43779:97;44127:35:::1;44143:9;44154:7;;44127:15;:35::i;:::-;43995:175:::0;;;:::o;51212:571::-;51404:17;51424:11;;-1:-1:-1;;;;;51424:11:0;:19;51451:9;51476:15;51506:57;:45;51536:6;51544;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;46151:57:0;;;;;;;33387:2;46151:57;;-1:-1:-1;;;;;46151:57:0;;;;;;;;-1:-1:-1;;;;;46151:57:0;;;;;;;;;45992:224;51506:45;46586:7;46763:6;:13;;;-1:-1:-1;;;;;46755:22:0;46733:2;46709:6;:18;;;-1:-1:-1;;;;;46693:36:0;:42;;46670:3;46648:6;:17;;;46640:26;;:33;;:95;:137;46618:170;;46611:177;;46487:309;;;;51506:57;51424:198;;-1:-1:-1;;;;;;51424:198:0;;;;;;;-1:-1:-1;;;;;2433:32:1;;;51424:198:0;;;2415:51:1;2482:18;;;2475:34;2525:18;;;2518:34;;;2568:18;;;2561:34;;;2387:19;;51424:198:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;51670:53;;;;;;;;51695:10;51670:53;;;-1:-1:-1;;;;;51670:53:0;;;;;;;;;;-1:-1:-1;51635:19:0;;;:8;:19;;;;;;;:88;;;;;;;-1:-1:-1;;;;;;51635:88:0;;;;;;;;;;;;;;;;;;;;;;;;;;;51741:34;;51404:218;;-1:-1:-1;51741:34:0;;;;;;-1:-1:-1;51741:34:0;51393:390;51212:571;;;;;:::o;52281:585::-;52480:17;52500:11;;-1:-1:-1;;;;;52500:11:0;:19;52527:9;52552:15;52582:64;:52;52614:6;52622:11;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;46405:66:0;;;;;;;33436:2;46405:66;;-1:-1:-1;;;;;46405:66:0;;;;;;;;-1:-1:-1;;;;;46405:66:0;;;;;;;;;46224:255;53106:283;53230:26;53259:19;;;:8;:19;;;;;;;;;53230:48;;;;;;;;;-1:-1:-1;;;;;53230:48:0;;;;;;;;;;;;;;;;;;;;53294:50;;53230:48;;;;53268:9;;53294:50;;;;53336:7;;;;53294:50;:::i;:::-;;;;;;;;-1:-1:-1;;;53362:19:0;;;;:8;:19;;;;;;;;53355:26;;-1:-1:-1;;;;;;53355:26:0;;;;;;;;;;;;;;53106:283::o;14:683:1:-;109:6;117;125;178:2;166:9;157:7;153:23;149:32;146:52;;;194:1;191;184:12;146:52;230:9;217:23;207:33;;291:2;280:9;276:18;263:32;314:18;355:2;347:6;344:14;341:34;;;371:1;368;361:12;341:34;409:6;398:9;394:22;384:32;;454:7;447:4;443:2;439:13;435:27;425:55;;476:1;473;466:12;425:55;516:2;503:16;542:2;534:6;531:14;528:34;;;558:1;555;548:12;528:34;611:7;606:2;596:6;593:1;589:14;585:2;581:23;577:32;574:45;571:65;;;632:1;629;622:12;571:65;663:2;659;655:11;645:21;;685:6;675:16;;;;;14:683;;;;;:::o;931:180::-;990:6;1043:2;1031:9;1022:7;1018:23;1014:32;1011:52;;;1059:1;1056;1049:12;1011:52;-1:-1:-1;1082:23:1;;931:180;-1:-1:-1;931:180:1:o;1425:173::-;1493:20;;-1:-1:-1;;;;;1542:31:1;;1532:42;;1522:70;;1588:1;1585;1578:12;1522:70;1425:173;;;:::o;1603:576::-;1697:6;1705;1713;1721;1729;1782:3;1770:9;1761:7;1757:23;1753:33;1750:53;;;1799:1;1796;1789:12;1750:53;1822:29;1841:9;1822:29;:::i;:::-;1812:39;;1870:38;1904:2;1893:9;1889:18;1870:38;:::i;:::-;1860:48;;1955:2;1944:9;1940:18;1927:32;1917:42;;2006:2;1995:9;1991:18;1978:32;1968:42;;2060:3;2049:9;2045:19;2032:33;-1:-1:-1;;;;;2098:5:1;2094:36;2087:5;2084:47;2074:75;;2145:1;2142;2135:12;2074:75;2168:5;2158:15;;;1603:576;;;;;;;;:::o;2606:184::-;2676:6;2729:2;2717:9;2708:7;2704:23;2700:32;2697:52;;;2745:1;2742;2735:12;2697:52;-1:-1:-1;2768:16:1;;2606:184;-1:-1:-1;2606:184:1:o;2795:443::-;2984:2;2966:21;;;3003:18;;2996:34;;;-1:-1:-1;;;;;;3042:31:1;;3039:51;;;3086:1;3083;3076:12;3039:51;3120:6;3117:1;3113:14;3177:6;3169;3164:2;3153:9;3149:18;3136:48;3205:22;;;;3229:2;3201:31;;2795:443;-1:-1:-1;;;2795:443:1:o
Swarm Source
ipfs://a3285985890ef12078da18e3d3bb7bdf7659eb71c387608473d3257018a1affa
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.