ETH Price: $2,948.62 (+1.24%)

Contract

0x65B299f6d365F34a04B67f602B89f6Fab094a012

Overview

ETH Balance

0 ETH

ETH Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Block
From
To

There are no matching entries

1 Internal Transaction found.

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To
385067742026-01-24 11:45:332 hrs ago1769255133  Contract Creation0 ETH

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
SP1Verifier

Compiler Version
v0.8.30+commit.73712a01

Optimization Enabled:
Yes with 200 runs

Other Settings:
cancun EvmVersion
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {ISP1Verifier, ISP1VerifierWithHash} from "../ISP1Verifier.sol";
import {Groth16Verifier} from "./Groth16Verifier.sol";

/// @title SP1 Verifier
/// @author Succinct Labs
/// @notice This contracts implements a solidity verifier for SP1.
contract SP1Verifier is Groth16Verifier, ISP1VerifierWithHash {
    /// @notice Thrown when the verifier selector from this proof does not match the one in this
    /// verifier. This indicates that this proof was sent to the wrong verifier.
    /// @param received The verifier selector from the first 4 bytes of the proof.
    /// @param expected The verifier selector from the first 4 bytes of the VERIFIER_HASH().
    error WrongVerifierSelector(bytes4 received, bytes4 expected);

    /// @notice Thrown when the proof is invalid.
    error InvalidProof();

    function VERSION() external pure returns (string memory) {
        return "v5.0.0";
    }

    /// @inheritdoc ISP1VerifierWithHash
    function VERIFIER_HASH() public pure returns (bytes32) {
        return 0xa4594c59bbc142f3b81c3ecb7f50a7c34bc9af7c4c444b5d48b795427e285913;
    }

    /// @notice Hashes the public values to a field elements inside Bn254.
    /// @param publicValues The public values.
    function hashPublicValues(
        bytes calldata publicValues
    ) public pure returns (bytes32) {
        return sha256(publicValues) & bytes32(uint256((1 << 253) - 1));
    }

    /// @notice Verifies a proof with given public values and vkey.
    /// @param programVKey The verification key for the RISC-V program.
    /// @param publicValues The public values encoded as bytes.
    /// @param proofBytes The proof of the program execution the SP1 zkVM encoded as bytes.
    function verifyProof(
        bytes32 programVKey,
        bytes calldata publicValues,
        bytes calldata proofBytes
    ) external view {
        bytes4 receivedSelector = bytes4(proofBytes[:4]);
        bytes4 expectedSelector = bytes4(VERIFIER_HASH());
        if (receivedSelector != expectedSelector) {
            revert WrongVerifierSelector(receivedSelector, expectedSelector);
        }

        bytes32 publicValuesDigest = hashPublicValues(publicValues);
        uint256[2] memory inputs;
        inputs[0] = uint256(programVKey);
        inputs[1] = uint256(publicValuesDigest);
        uint256[8] memory proof = abi.decode(proofBytes[4:], (uint256[8]));
        this.Verify(proof, inputs);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/// @title SP1 Verifier Interface
/// @author Succinct Labs
/// @notice This contract is the interface for the SP1 Verifier.
interface ISP1Verifier {
    /// @notice Verifies a proof with given public values and vkey.
    /// @dev It is expected that the first 4 bytes of proofBytes must match the first 4 bytes of
    /// target verifier's VERIFIER_HASH.
    /// @param programVKey The verification key for the RISC-V program.
    /// @param publicValues The public values encoded as bytes.
    /// @param proofBytes The proof of the program execution the SP1 zkVM encoded as bytes.
    function verifyProof(
        bytes32 programVKey,
        bytes calldata publicValues,
        bytes calldata proofBytes
    ) external view;
}

interface ISP1VerifierWithHash is ISP1Verifier {
    /// @notice Returns the hash of the verifier.
    function VERIFIER_HASH() external pure returns (bytes32);
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

/// @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.
contract 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 = 20491192805390485299153009773594534940189261866228447918068658471970481763042;
    uint256 constant ALPHA_Y = 9383485363053290200918347156157836566562967994039712273449902621266178545958;

    // Groth16 beta point in G2 in powers of i
    uint256 constant BETA_NEG_X_0 = 6375614351688725206403948262868962793625744043794305715222011528459656738731;
    uint256 constant BETA_NEG_X_1 = 4252822878758300859123897981450591353533073413197771768651442665752259397132;
    uint256 constant BETA_NEG_Y_0 = 11383000245469012944693504663162918391286475477077232690815866754273895001727;
    uint256 constant BETA_NEG_Y_1 = 41207766310529818958173054109690360505148424997958324311878202295167071904;

    // Groth16 gamma point in G2 in powers of i
    uint256 constant GAMMA_NEG_X_0 = 10857046999023057135944570762232829481370756359578518086990519993285655852781;
    uint256 constant GAMMA_NEG_X_1 = 11559732032986387107991004021392285783925812861821192530917403151452391805634;
    uint256 constant GAMMA_NEG_Y_0 = 13392588948715843804641432497768002650278120570034223513918757245338268106653;
    uint256 constant GAMMA_NEG_Y_1 = 17805874995975841540914202342111839520379459829704422454583296818431106115052;

    // Groth16 delta point in G2 in powers of i
    uint256 constant DELTA_NEG_X_0 = 1807939758600928081661535078044266309701426477869595321608690071623627252461;
    uint256 constant DELTA_NEG_X_1 = 13017767206419180294867239590191240882490168779777616723978810680471506089190;
    uint256 constant DELTA_NEG_Y_0 = 11385252965472363874004017020523979267854101512663014352368174256411716100034;
    uint256 constant DELTA_NEG_Y_1 = 707821308472421780425082520239282952693670279239989952629124761519869475067;

    // Constant and public input points
    uint256 constant CONSTANT_X = 17203997695518370725253383800612862082040222186834248316724952811913305748878;
    uint256 constant CONSTANT_Y = 282619892079818506885924724237935832196325815176482254129420869757043108110;
    uint256 constant PUB_0_X = 2763789253671512309630211343474627955637016507408470052385640371173442321228;
    uint256 constant PUB_0_Y = 7070003421332099028511324531870215047017050364545890942981741487547942466073;
    uint256 constant PUB_1_X = 2223923876691923064813371578678400285087400227347901303400514986210692294428;
    uint256 constant PUB_1_Y = 3228708299174762375496115493137156328822199374794870011715145604387710550517;

    /// 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 whether 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[2] calldata 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 reduce commitments(if any) with constants as the first point argument to ECADD.
        // 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 :=  calldataload(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 :=  calldataload(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))

            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] calldata proof)
    public 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[2] calldata input
    ) public view {
        uint256[24] memory pairings;

        {
            (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.
            // 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 Verify(
        uint256[8] calldata proof,
        uint256[2] calldata input
    ) public 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, -δ).
            calldatacopy(f, proof, 0x100)

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

Settings
{
  "remappings": [
    "@hyperbridge/core/=node_modules/@hyperbridge/core/contracts/",
    "@openzeppelin/=node_modules/@openzeppelin/",
    "@polytope-labs/=node_modules/@polytope-labs/",
    "@uniswap/=node_modules/@uniswap/",
    "stringutils/=lib/solidity-stringutils/src/",
    "@sp1-contracts/=lib/sp1-contracts/contracts/src/",
    "ds-test/=lib/solidity-stringutils/lib/ds-test/src/",
    "erc4626-tests/=lib/sp1-contracts/contracts/lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts/=lib/sp1-contracts/contracts/lib/openzeppelin-contracts/",
    "solidity-stringutils/=lib/solidity-stringutils/",
    "sp1-contracts/=lib/sp1-contracts/contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": true
}

Contract Security Audit

Contract ABI

API
[{"inputs":[],"name":"InvalidProof","type":"error"},{"inputs":[],"name":"ProofInvalid","type":"error"},{"inputs":[],"name":"PublicInputNotInField","type":"error"},{"inputs":[{"internalType":"bytes4","name":"received","type":"bytes4"},{"internalType":"bytes4","name":"expected","type":"bytes4"}],"name":"WrongVerifierSelector","type":"error"},{"inputs":[],"name":"VERIFIER_HASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256[8]","name":"proof","type":"uint256[8]"},{"internalType":"uint256[2]","name":"input","type":"uint256[2]"}],"name":"Verify","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[8]","name":"proof","type":"uint256[8]"}],"name":"compressProof","outputs":[{"internalType":"uint256[4]","name":"compressed","type":"uint256[4]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"publicValues","type":"bytes"}],"name":"hashPublicValues","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256[4]","name":"compressedProof","type":"uint256[4]"},{"internalType":"uint256[2]","name":"input","type":"uint256[2]"}],"name":"verifyCompressedProof","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"programVKey","type":"bytes32"},{"internalType":"bytes","name":"publicValues","type":"bytes"},{"internalType":"bytes","name":"proofBytes","type":"bytes"}],"name":"verifyProof","outputs":[],"stateMutability":"view","type":"function"}]

60808060405234601557611345908161001a8239f35b5f80fdfe6080806040526004361015610012575f80fd5b5f905f3560e01c9081632a51043614610bb55750806341493c6014610a0857806344f636921461096d5780636b61d8e714610928578063eddf243c14610553578063f11817b2146100f05763ffa1ad741461006b575f80fd5b346100ed57806003193601126100ed576040516040810181811067ffffffffffffffff8211176100d957906040918252600681526020810165076352e302e360d41b81528251938492602084525180928160208601528585015e828201840152601f01601f19168101030190f35b634e487b7160e01b83526041600452602483fd5b80fd5b50346100ed5760c03660031901126100ed57366084116100ed573660c4116100ed57604051906103006101238184610c1b565b80368437610132600435610f6c565b610143602495929535604435610fd7565b91939290610152606435610f6c565b9390926040519660408801967f26091e1cafb0ad8a4ea0a694cd3743ebf524779233db734c451d28b58aa9758e895288600160208201997e9ff50a6b8b11c3ca6fdb2690a124f8ce25489fefa65a3e782e7ba70b66690e8b527f061c3fd0fd3da25d2607c227d090cca750ed36c6ec878755e537c1c48951fb4c81527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001604060608501927f0fa17ae9c2033379df7b5c65eff0e107055e9a273e6119a212dd09eb5170721984527f07236256d21c60d02f0bdbf95cff83e03ea9e16fca56b18d5544b0889a65c1f560843596836080820198808a5286828660608160075afa9110169160808160065afa16947f04eab241388a79817fe0e0e2ead0b2ec4ffdec51a16028dee020634fd129e71c83525260a43580965260608160075afa931016161660408a60808160065afa169851975198156105445760209a9b9c8a528a8a015260408901526060880152608087015260a086015260c085015260e08401527f1cc7cb8de715675f21f01ecc9b46d236e0865e0cc020024521998269845f74e66101008401527f03ff41f4ba0c37fe2caf27354d28e4b8f83d3b76777a63b327d736bffb0122ed6101208401527f01909cd7827e0278e6b60843a4abc7b111d7f8b2725cd5902a6b20da7a2938fb6101408401527f192bd3274441670227b4f69a44005b8711266e474227c6439ca25ca8e1ec1fc26101608401527f2d4d9aa7e302d9df41749d5507949d05dbea33fbb16c643b22f599a2be6df2e26101808401527f14bedd503c37ceb061d8ec60209fe345ce89830a19230301f076caff004d19266101a08401527f0967032fcbf776d1afc985f88877f182d38480a653f2decaa9794cbc3bf3060c6101c08401527f0e187847ad4c798374d0d6732bf501847dd68bc0e071241e0213bc7fc13db7ab6101e08401527e1752a100a72fdf1e5a5d6ea841cc20ec838bccfcf7bd559e79f1c9c759b6a06102008401527f192a8cc13cd9f762871f21e43451c6ca9eeab2cb2987c4e366a185c25dac2e7f6102208401526102408301526102608201527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c26102808201527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed6102a08201527f275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec6102c08201527f1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d6102e082015260405192839161050f8484610c1b565b8336843760085afa15908115610537575b506105285780f35b631ff3747d60e21b8152600490fd5b600191505114155f610520565b63a54f8e2760e01b8c5260048cfd5b50346100ed576101403660031901126100ed5736610104116100ed5736610144116100ed5760405160408101907f26091e1cafb0ad8a4ea0a694cd3743ebf524779233db734c451d28b58aa9758e815260208101917e9ff50a6b8b11c3ca6fdb2690a124f8ce25489fefa65a3e782e7ba70b66690e83527f061c3fd0fd3da25d2607c227d090cca750ed36c6ec878755e537c1c48951fb4c81526001606083017f0fa17ae9c2033379df7b5c65eff0e107055e9a273e6119a212dd09eb5170721981527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001604061010435947f07236256d21c60d02f0bdbf95cff83e03ea9e16fca56b18d5544b0889a65c1f5608088019680885284848460608160075afa911016838960808160065afa16947f04eab241388a79817fe0e0e2ead0b2ec4ffdec51a16028dee020634fd129e71c8352526101243580965260608160075afa931016161660408260808160065afa169051915190156109195760405191610100600484377f1cc7cb8de715675f21f01ecc9b46d236e0865e0cc020024521998269845f74e66101008401527f03ff41f4ba0c37fe2caf27354d28e4b8f83d3b76777a63b327d736bffb0122ed6101208401527f01909cd7827e0278e6b60843a4abc7b111d7f8b2725cd5902a6b20da7a2938fb6101408401527f192bd3274441670227b4f69a44005b8711266e474227c6439ca25ca8e1ec1fc26101608401527f2d4d9aa7e302d9df41749d5507949d05dbea33fbb16c643b22f599a2be6df2e26101808401527f14bedd503c37ceb061d8ec60209fe345ce89830a19230301f076caff004d19266101a08401527f0967032fcbf776d1afc985f88877f182d38480a653f2decaa9794cbc3bf3060c6101c08401527f0e187847ad4c798374d0d6732bf501847dd68bc0e071241e0213bc7fc13db7ab6101e08401527e1752a100a72fdf1e5a5d6ea841cc20ec838bccfcf7bd559e79f1c9c759b6a06102008401527f192a8cc13cd9f762871f21e43451c6ca9eeab2cb2987c4e366a185c25dac2e7f6102208401526102408301526102608201527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c26102808201527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed6102a08201527f275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec6102c08201527f1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d6102e08201526020816103008160085afa905116156105285780f35b63a54f8e2760e01b8352600483fd5b50346100ed5760203660031901126100ed576004359067ffffffffffffffff82116100ed57602061096561095f3660048601610bed565b90610c51565b604051908152f35b50346100ed576101003660031901126100ed5736610104116100ed5760405190610998608083610c1b565b60803683376109ab602435600435610c88565b82526109c160843560a435604435606435610d38565b602084015260408301526109d960e43560c435610c88565b60608301526040519190825b600482106109f257608084f35b60208060019285518152019301910190916109e5565b5034610b84576060366003190112610b845760243567ffffffffffffffff8111610b8457610a3a903690600401610bed565b60443567ffffffffffffffff8111610b8457610a5a903690600401610bed565b91909282600411610b845783356001600160e01b031916635ba6b3a760e01b8101610b98575090610a8a91610c51565b604091825191610a9a8484610c1b565b833684376004358352602083015283016101008482036003190112610b845780602385011215610b8457825193610ad361010086610c1b565b84906101048101928311610b8457600401905b828210610b8857505050303b15610b84578151633b77c90f60e21b8152925f600485015b60088210610b6e5750505061010483015f905b60028210610b58575050505f8261014481305afa908115610b4f5750610b41575080f35b610b4d91505f90610c1b565b005b513d5f823e3d90fd5b6020806001928551815201930191019091610b1d565b6020806001928551815201930191019091610b0a565b5f80fd5b8135815260209182019101610ae6565b63988066a160e01b5f5260045263a4594c5960e01b60245260445ffd5b34610b84575f366003190112610b8457807fa4594c59bbc142f3b81c3ecb7f50a7c34bc9af7c4c444b5d48b795427e28591360209252f35b9181601f84011215610b845782359167ffffffffffffffff8311610b845760208381860195010111610b8457565b90601f8019910116810190811067ffffffffffffffff821117610c3d57604052565b634e487b7160e01b5f52604160045260245ffd5b6020915f918160405192839283378101838152039060025afa15610c7d575f516001600160fd1b031690565b6040513d5f823e3d90fd5b905f5160206112f05f395f51905f528210801590610d21575b610d0457811580610d19575b610d1357610cd15f5160206112f05f395f51905f5260038185818180090908611110565b818103610ce057505060011b90565b5f5160206112f05f395f51905f52809106810306145f14610d0457600190811b1790565b631ff3747d60e21b5f5260045ffd5b50505f90565b508015610cad565b505f5160206112f05f395f51905f52811015610ca1565b919093925f5160206112f05f395f51905f528310801590610f55575b8015610f3e575b8015610f27575b610d04578082868517171715610f1c57908291610e7f5f5160206112f05f395f51905f5280808080888180808f9d7f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd448f839290839109099d8e0981848181800909087f2b149d40ceb8aaae81be18991be06ac3b5b4c5e559dbefa33267e6dc24a138e5089a09818c8181800909087f2fcd3ac2a640a154eb23960892a85a68f031ca0c8344b23a577dcf1052b9e7750806810306945f5160206112f05f395f51905f527f183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea481610e5981808b80098187800908611110565b8408095f5160206112f05f395f51905f52610e7382611287565b80091415958691611133565b929080821480610f13575b15610eb15750505050905f14610ea95760ff60025b169060021b179190565b60ff5f610e9f565b5f5160206112f05f395f51905f52809106810306149182610ef4575b505015610d045760019115610eec5760ff60025b169060021b17179190565b60ff5f610ee1565b5f5160206112f05f395f51905f52919250819006810306145f80610ecd565b50838314610e8a565b50505090505f905f90565b505f5160206112f05f395f51905f52811015610d62565b505f5160206112f05f395f51905f52821015610d5b565b505f5160206112f05f395f51905f52851015610d54565b8015610fd0578060011c915f5160206112f05f395f51905f52831015610d0457600180610faf5f5160206112f05f395f51905f5260038188818180090908611110565b931614610fb857565b905f5160206112f05f395f51905f5280910681030690565b505f905f90565b801580611108575b6110fc578060021c92825f5160206112f05f395f51905f5285108015906110e5575b610d045784815f5160206112f05f395f51905f5280808080808080807f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd44816110af9d8d0909998a0981898181800909087f2fcd3ac2a640a154eb23960892a85a68f031ca0c8344b23a577dcf1052b9e7750806810306936002808a16149509818a8181800909087f2b149d40ceb8aaae81be18991be06ac3b5b4c5e559dbefa33267e6dc24a138e508611133565b809291600180829616146110c1575050565b5f5160206112f05f395f51905f528093945080929550809106810306930681030690565b505f5160206112f05f395f51905f52811015611001565b50505f905f905f905f90565b508115610fdf565b9061111a82611287565b915f5160206112f05f395f51905f5283800903610d0457565b915f5160206112f05f395f51905f527f183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea48161118b9396949661117d82808a8009818a800908611110565b9061127b575b860809611110565b925f5160206112f05f395f51905f52600285096040519060208252602080830152602060408301528060608301527f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4560808301525f5160206112f05f395f51905f5260a083015260208260c08160055afa91519115610d04575f5160206112f05f395f51905f52826001920903610d04575f5160206112f05f395f51905f52908209925f5160206112f05f395f51905f52808080878009068103068187800908149081159161125c575b50610d0457565b90505f5160206112f05f395f51905f528084860960020914155f611255565b81809106810306611183565b9060405191602083526020808401526020604084015260608301527f0c19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f5260808301525f5160206112f05f395f51905f5260a083015260208260c08160055afa91519115610d045756fe30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47a264697066735822122001c620ffb412d56b4d9b44ff67323ea6677b033bd299e287737edf4302f781cd64736f6c634300081e0033

Deployed Bytecode

0x6080806040526004361015610012575f80fd5b5f905f3560e01c9081632a51043614610bb55750806341493c6014610a0857806344f636921461096d5780636b61d8e714610928578063eddf243c14610553578063f11817b2146100f05763ffa1ad741461006b575f80fd5b346100ed57806003193601126100ed576040516040810181811067ffffffffffffffff8211176100d957906040918252600681526020810165076352e302e360d41b81528251938492602084525180928160208601528585015e828201840152601f01601f19168101030190f35b634e487b7160e01b83526041600452602483fd5b80fd5b50346100ed5760c03660031901126100ed57366084116100ed573660c4116100ed57604051906103006101238184610c1b565b80368437610132600435610f6c565b610143602495929535604435610fd7565b91939290610152606435610f6c565b9390926040519660408801967f26091e1cafb0ad8a4ea0a694cd3743ebf524779233db734c451d28b58aa9758e895288600160208201997e9ff50a6b8b11c3ca6fdb2690a124f8ce25489fefa65a3e782e7ba70b66690e8b527f061c3fd0fd3da25d2607c227d090cca750ed36c6ec878755e537c1c48951fb4c81527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001604060608501927f0fa17ae9c2033379df7b5c65eff0e107055e9a273e6119a212dd09eb5170721984527f07236256d21c60d02f0bdbf95cff83e03ea9e16fca56b18d5544b0889a65c1f560843596836080820198808a5286828660608160075afa9110169160808160065afa16947f04eab241388a79817fe0e0e2ead0b2ec4ffdec51a16028dee020634fd129e71c83525260a43580965260608160075afa931016161660408a60808160065afa169851975198156105445760209a9b9c8a528a8a015260408901526060880152608087015260a086015260c085015260e08401527f1cc7cb8de715675f21f01ecc9b46d236e0865e0cc020024521998269845f74e66101008401527f03ff41f4ba0c37fe2caf27354d28e4b8f83d3b76777a63b327d736bffb0122ed6101208401527f01909cd7827e0278e6b60843a4abc7b111d7f8b2725cd5902a6b20da7a2938fb6101408401527f192bd3274441670227b4f69a44005b8711266e474227c6439ca25ca8e1ec1fc26101608401527f2d4d9aa7e302d9df41749d5507949d05dbea33fbb16c643b22f599a2be6df2e26101808401527f14bedd503c37ceb061d8ec60209fe345ce89830a19230301f076caff004d19266101a08401527f0967032fcbf776d1afc985f88877f182d38480a653f2decaa9794cbc3bf3060c6101c08401527f0e187847ad4c798374d0d6732bf501847dd68bc0e071241e0213bc7fc13db7ab6101e08401527e1752a100a72fdf1e5a5d6ea841cc20ec838bccfcf7bd559e79f1c9c759b6a06102008401527f192a8cc13cd9f762871f21e43451c6ca9eeab2cb2987c4e366a185c25dac2e7f6102208401526102408301526102608201527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c26102808201527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed6102a08201527f275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec6102c08201527f1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d6102e082015260405192839161050f8484610c1b565b8336843760085afa15908115610537575b506105285780f35b631ff3747d60e21b8152600490fd5b600191505114155f610520565b63a54f8e2760e01b8c5260048cfd5b50346100ed576101403660031901126100ed5736610104116100ed5736610144116100ed5760405160408101907f26091e1cafb0ad8a4ea0a694cd3743ebf524779233db734c451d28b58aa9758e815260208101917e9ff50a6b8b11c3ca6fdb2690a124f8ce25489fefa65a3e782e7ba70b66690e83527f061c3fd0fd3da25d2607c227d090cca750ed36c6ec878755e537c1c48951fb4c81526001606083017f0fa17ae9c2033379df7b5c65eff0e107055e9a273e6119a212dd09eb5170721981527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001604061010435947f07236256d21c60d02f0bdbf95cff83e03ea9e16fca56b18d5544b0889a65c1f5608088019680885284848460608160075afa911016838960808160065afa16947f04eab241388a79817fe0e0e2ead0b2ec4ffdec51a16028dee020634fd129e71c8352526101243580965260608160075afa931016161660408260808160065afa169051915190156109195760405191610100600484377f1cc7cb8de715675f21f01ecc9b46d236e0865e0cc020024521998269845f74e66101008401527f03ff41f4ba0c37fe2caf27354d28e4b8f83d3b76777a63b327d736bffb0122ed6101208401527f01909cd7827e0278e6b60843a4abc7b111d7f8b2725cd5902a6b20da7a2938fb6101408401527f192bd3274441670227b4f69a44005b8711266e474227c6439ca25ca8e1ec1fc26101608401527f2d4d9aa7e302d9df41749d5507949d05dbea33fbb16c643b22f599a2be6df2e26101808401527f14bedd503c37ceb061d8ec60209fe345ce89830a19230301f076caff004d19266101a08401527f0967032fcbf776d1afc985f88877f182d38480a653f2decaa9794cbc3bf3060c6101c08401527f0e187847ad4c798374d0d6732bf501847dd68bc0e071241e0213bc7fc13db7ab6101e08401527e1752a100a72fdf1e5a5d6ea841cc20ec838bccfcf7bd559e79f1c9c759b6a06102008401527f192a8cc13cd9f762871f21e43451c6ca9eeab2cb2987c4e366a185c25dac2e7f6102208401526102408301526102608201527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c26102808201527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed6102a08201527f275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec6102c08201527f1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d6102e08201526020816103008160085afa905116156105285780f35b63a54f8e2760e01b8352600483fd5b50346100ed5760203660031901126100ed576004359067ffffffffffffffff82116100ed57602061096561095f3660048601610bed565b90610c51565b604051908152f35b50346100ed576101003660031901126100ed5736610104116100ed5760405190610998608083610c1b565b60803683376109ab602435600435610c88565b82526109c160843560a435604435606435610d38565b602084015260408301526109d960e43560c435610c88565b60608301526040519190825b600482106109f257608084f35b60208060019285518152019301910190916109e5565b5034610b84576060366003190112610b845760243567ffffffffffffffff8111610b8457610a3a903690600401610bed565b60443567ffffffffffffffff8111610b8457610a5a903690600401610bed565b91909282600411610b845783356001600160e01b031916635ba6b3a760e01b8101610b98575090610a8a91610c51565b604091825191610a9a8484610c1b565b833684376004358352602083015283016101008482036003190112610b845780602385011215610b8457825193610ad361010086610c1b565b84906101048101928311610b8457600401905b828210610b8857505050303b15610b84578151633b77c90f60e21b8152925f600485015b60088210610b6e5750505061010483015f905b60028210610b58575050505f8261014481305afa908115610b4f5750610b41575080f35b610b4d91505f90610c1b565b005b513d5f823e3d90fd5b6020806001928551815201930191019091610b1d565b6020806001928551815201930191019091610b0a565b5f80fd5b8135815260209182019101610ae6565b63988066a160e01b5f5260045263a4594c5960e01b60245260445ffd5b34610b84575f366003190112610b8457807fa4594c59bbc142f3b81c3ecb7f50a7c34bc9af7c4c444b5d48b795427e28591360209252f35b9181601f84011215610b845782359167ffffffffffffffff8311610b845760208381860195010111610b8457565b90601f8019910116810190811067ffffffffffffffff821117610c3d57604052565b634e487b7160e01b5f52604160045260245ffd5b6020915f918160405192839283378101838152039060025afa15610c7d575f516001600160fd1b031690565b6040513d5f823e3d90fd5b905f5160206112f05f395f51905f528210801590610d21575b610d0457811580610d19575b610d1357610cd15f5160206112f05f395f51905f5260038185818180090908611110565b818103610ce057505060011b90565b5f5160206112f05f395f51905f52809106810306145f14610d0457600190811b1790565b631ff3747d60e21b5f5260045ffd5b50505f90565b508015610cad565b505f5160206112f05f395f51905f52811015610ca1565b919093925f5160206112f05f395f51905f528310801590610f55575b8015610f3e575b8015610f27575b610d04578082868517171715610f1c57908291610e7f5f5160206112f05f395f51905f5280808080888180808f9d7f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd448f839290839109099d8e0981848181800909087f2b149d40ceb8aaae81be18991be06ac3b5b4c5e559dbefa33267e6dc24a138e5089a09818c8181800909087f2fcd3ac2a640a154eb23960892a85a68f031ca0c8344b23a577dcf1052b9e7750806810306945f5160206112f05f395f51905f527f183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea481610e5981808b80098187800908611110565b8408095f5160206112f05f395f51905f52610e7382611287565b80091415958691611133565b929080821480610f13575b15610eb15750505050905f14610ea95760ff60025b169060021b179190565b60ff5f610e9f565b5f5160206112f05f395f51905f52809106810306149182610ef4575b505015610d045760019115610eec5760ff60025b169060021b17179190565b60ff5f610ee1565b5f5160206112f05f395f51905f52919250819006810306145f80610ecd565b50838314610e8a565b50505090505f905f90565b505f5160206112f05f395f51905f52811015610d62565b505f5160206112f05f395f51905f52821015610d5b565b505f5160206112f05f395f51905f52851015610d54565b8015610fd0578060011c915f5160206112f05f395f51905f52831015610d0457600180610faf5f5160206112f05f395f51905f5260038188818180090908611110565b931614610fb857565b905f5160206112f05f395f51905f5280910681030690565b505f905f90565b801580611108575b6110fc578060021c92825f5160206112f05f395f51905f5285108015906110e5575b610d045784815f5160206112f05f395f51905f5280808080808080807f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd44816110af9d8d0909998a0981898181800909087f2fcd3ac2a640a154eb23960892a85a68f031ca0c8344b23a577dcf1052b9e7750806810306936002808a16149509818a8181800909087f2b149d40ceb8aaae81be18991be06ac3b5b4c5e559dbefa33267e6dc24a138e508611133565b809291600180829616146110c1575050565b5f5160206112f05f395f51905f528093945080929550809106810306930681030690565b505f5160206112f05f395f51905f52811015611001565b50505f905f905f905f90565b508115610fdf565b9061111a82611287565b915f5160206112f05f395f51905f5283800903610d0457565b915f5160206112f05f395f51905f527f183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea48161118b9396949661117d82808a8009818a800908611110565b9061127b575b860809611110565b925f5160206112f05f395f51905f52600285096040519060208252602080830152602060408301528060608301527f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4560808301525f5160206112f05f395f51905f5260a083015260208260c08160055afa91519115610d04575f5160206112f05f395f51905f52826001920903610d04575f5160206112f05f395f51905f52908209925f5160206112f05f395f51905f52808080878009068103068187800908149081159161125c575b50610d0457565b90505f5160206112f05f395f51905f528084860960020914155f611255565b81809106810306611183565b9060405191602083526020808401526020604084015260608301527f0c19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f5260808301525f5160206112f05f395f51905f5260a083015260208260c08160055afa91519115610d045756fe30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47a264697066735822122001c620ffb412d56b4d9b44ff67323ea6677b033bd299e287737edf4302f781cd64736f6c634300081e0033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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