ByAUJay
Are There SDKs That Abstract Multiple Proof Systems (Groth16, Plonk, zkVM) and Output a Single Verifier Friendly to Ethereum Calldata Limits?
Short answer: yes, but not by having one “universal Solidity verifier” for all proof systems. The practical pattern in 2026 is to verify/aggregate heterogeneous proofs off-chain or inside a zkVM recursion circuit, then emit a single, EVM-friendly SNARK (usually Groth16 or Plonk on BN254) that fits Ethereum’s calldata and gas budgets. (eips.ethereum.org)
Executive summary (description)
A growing set of SDKs let teams mix proof systems (STARK-based zkVMs, Groth16, Plonk) and output one small, cheap-to-verify proof on Ethereum. The most production-ready paths today use recursive proving/wrapping to Groth16 or Plonk over BN254, achieving ~260–900 byte proofs and ~270–300k gas verification—well within calldata and gas constraints. (docs.succinct.xyz)
The short answer for decision‑makers
- There is no widely deployed, single Solidity contract that directly accepts arbitrary Groth16, Plonk, STARK, etc., proofs with one interface; verification math differs per system. Instead, leading SDKs provide:
- A multi-prover or recursion layer that ingests heterogeneous proofs, then
- A single “final” proof (Groth16 or Plonk on BN254) verified on-chain via a small, stable verifier contract or gateway. (docs.succinct.xyz)
- This model minimizes calldata/gas thanks to BN254 precompiles (EIP‑196/197) and tiny final proofs; blobs from EIP‑4844 cannot be read by the EVM and therefore cannot carry the proof to the verifier. (eips.ethereum.org)
Why a “single verifier for everything” isn’t how Ethereum works
- Ethereum has precompiles for BN254 addition, multiplication, and pairings (EIP-196/197). These power today’s efficient on-chain SNARK verification. There is no mainnet precompile for BLS12‑381 (EIP‑2537 remains an EIP), so universal verifiers across curves/schemes aren’t practical on L1 without large custom code and gas. (eips.ethereum.org)
- EVM calldata is still metered per byte (4 gas for zero, 16 gas non‑zero as per EIP‑2028), and although EIP‑4844 added cheap blobs, blob data is not accessible by smart contracts, only the commitment is. You must pass the proof via calldata to the verifier. (eips.ethereum.org)
Implication: build with a “final proof type” the EVM verifies cheaply—BN254 Groth16 or Plonk—then do your multi-proof abstraction in recursion off-chain or inside a zkVM.
What “EVM‑friendly” actually means in 2026
- Groth16 over BN254: ~200–300 bytes proof size, O(1) verification, minimal calldata/gas; commonly ~260 bytes and ~270k gas in modern stacks. (dple.github.io)
- Plonk over BN254: still small, typically ~0.8–1.3KB proofs, ~300k gas with tuned verifiers. (docs.succinct.xyz)
- STARK proofs: much larger (hundreds of KB); almost always wrapped to a succinct SNARK for on-chain verification. (dev.risczero.com)
SDKs and stacks that already abstract multiple proof systems into one EVM‑friendly verifier
Below are concrete, production‑leaning options we’ve used or audited for clients.
1) Succinct SP1 zkVM: STARK recursion with Groth16/Plonk final proofs
- What it does
- You write Rust programs; SP1 proves them with STARK recursion and then wraps to a final Groth16 or Plonk proof over BN254. On-chain verification costs ~270–300k gas. (docs.succinct.xyz)
- SP1 exposes proof types “Groth16” (~260 bytes) and “Plonk” (~868 bytes). You can pick per deployment. (docs.succinct.xyz)
- Canonical SP1 verifier gateways are deployed on Ethereum and major L2s; you submit a single proof+public inputs to one contract. (docs.succinct.xyz)
- Why it’s multi‑proof friendly
- Inside SP1, you can verify Groth16 and Plonk proofs produced by other stacks (e.g., gnark) using their BN254 verifier crate, then output one SP1 Groth16/Plonk proof to the chain. This is the “unify many proofs to one” pattern. (docs.rs)
- Practical numbers
- On‑chain verification ≈ 270k gas (Groth16) or ≈ 300k gas (Plonk) via the Solidity SDK/gateways. (docs.succinct.xyz)
- Implementation sketch
- Verify external Groth16/Plonk proofs inside an SP1 program using sp1‑verifier or the bn254 verifier, compute your application logic, then call client.prove(...).groth16() and submit to the SP1VerifierGateway on mainnet/L2. (docs.rs)
When to choose: you need a general‑purpose zkVM with proven recursion, minimal on‑chain cost, and the flexibility to pull in arbitrary SNARKs and compress them to one EVM‑friendly proof.
2) RISC Zero zkVM: STARK recursion with a Groth16 “receipt” for EVM
- What it does
- RISC Zero proves RISC‑V execution with STARKs, recursively compresses them, and finally runs a STARK‑to‑SNARK circuit to emit a Groth16Receipt that your Solidity verifier can check. (dev.risczero.com)
- Why it’s multi‑proof friendly
- You can compose/aggregate multiple program segments inside RISC Zero recursion and end with one Groth16 proof, minimizing calldata. (dev.risczero.com)
- Developer tip
- The docs expose Prover::prove_with_opts to choose composite/succinct/groth16 receipts; use groth16 for EVM. (dev.risczero.com)
When to choose: you prefer RISC‑V VM semantics, want a STARK stack but need tiny on‑chain proofs.
3) Noir (ACIR) + backends: language‑level abstraction, pick your final SNARK
- What it does
- Noir compiles to ACIR, then you choose a backend (e.g., Aztec’s Barretenberg/UltraPlonk, or community backends like gnark). Noir’s tooling can generate Solidity verifiers per backend. It’s “proof system agnostic” at the language level. (noir-lang.org)
- Why it helps
- Teams can iterate circuits once and target different final proof systems. To get a single verifier, still wrap/aggregate to Groth16/Plonk BN254 for EVM. (noir-lang.org)
When to choose: product teams standardize on Noir for safety and portability, but want flexibility in the final verifier.
4) gnark (Go): one circuit, multiple backends, Solidity exports
- What it does
- gnark supports Groth16 and Plonk on several curves and can export a Solidity verifier for BN254 Groth16. You can keep one circuit and switch backends, but on-chain you deploy one verifier per backend. (github.com)
- Multi‑proof unification approach
- Use gnark for your “classical” SNARK circuits; verify those proofs inside SP1 or another recursion system; emit one Groth16 proof to Ethereum. (docs.rs)
5) Off‑chain/mid‑chain verifiers for heterogeneous proofs (zkVerify)
- What it does
- zkVerify is a specialized network that verifies multiple proof types (e.g., RISC Zero, Groth16, Fflonk) and issues attestations; useful if you prefer minimal L1 footprint and cross‑ecosystem verification. (blog.zkverify.io)
When to choose: you want cross‑chain proof validation or to keep Ethereum interaction minimal via attestation bridges.
Calldata constraints you must design around
- Calldata cost: 4 gas per zero byte, 16 gas per non‑zero byte since EIP‑2028; large proofs hurt gas. SNARK proofs minimize payload. (eips.ethereum.org)
- Blobs (EIP‑4844) are cheaper DA but not EVM‑readable; you cannot pass the proof via blob to a Solidity verifier. Only commitments are visible. (eips.ethereum.org)
- Best practice: pick BN254 Groth16 when acceptable (trusted setup tradeoff), or BN254 Plonk if you prefer universal setup; both keep calldata very small and verification ~270–300k gas. (docs.succinct.xyz)
Practical example: unifying Groth16 + Plonk + zkVM into one proof verified on Ethereum
Goal: Your product emits:
- a gnark Groth16 proof for a legacy circuit,
- a gnark Plonk proof for a newer circuit,
- a zkVM proof for heavy computation, and you want a single EVM transaction with one small proof.
A minimal architecture:
- Build a recursion “aggregator” program in SP1:
- Include sp1‑verifier to check SP1 Groth16/Plonk inside the VM. (docs.rs)
- Include a BN254 verifier for gnark Groth16/Plonk proofs (compatibility with gnark formats is documented). (github.com)
- The program:
- Verifies the gnark Groth16 proof’s vkey+pub‑inputs.
- Verifies the gnark Plonk proof’s vkey+pub‑inputs.
- Verifies your prior SP1 subproof(s) if any.
- Merklizes or hashes all public outputs into one digest to keep final on‑chain public inputs tiny.
- Prove the aggregator program with SP1 and select Groth16 mode:
- client.prove(...).groth16() yields a ~260‑byte proof. (docs.succinct.xyz)
- Submit to SP1’s VerifierGateway on Ethereum:
- One call, ~270k gas, single verifier contract maintained by Succinct. (docs.succinct.xyz)
This pattern gives you a single verifier “surface” on L1 with predictable gas and minimal calldata, while keeping upstream proof systems swappable and future‑proof.
RISC Zero variant: STARK to Groth16Receipt
- Compose as many STARK receipts as you need with lift/join recursion inside RISC Zero; finally compress() to a Groth16Receipt. Deploy RISC Zero’s Groth16 verifier on Ethereum, submit one receipt and minimal public inputs. (dev.risczero.com)
Emerging techniques to watch
- Groth16 aggregation (SnarkPack): research and deployments show logarithmic verification for many Groth16 proofs; useful if you have only Groth16 proofs to aggregate. It still ends in a single SNARK verified on-chain. (research.protocol.ai)
- New “final SNARK” candidates: research like Polymath proposes even shorter arguments than Groth16 for the final wrapper; promising, but not yet standard on Ethereum. (eprint.iacr.org)
- Modular zkVMs (OpenVM): a framework to mix proof system components and verify Halo2 proofs on-chain in ~<330k gas via a Solidity SDK; you still pick one final verifier contract. (blog.openvm.dev)
Best practices we recommend to clients
- Target BN254 for the final proof on Ethereum
- It uses EIP‑196/197 precompiles and ensures cheap pairing checks; BLS12‑381 precompile (EIP‑2537) is not live on mainnet as of Jan 7, 2026. (eips.ethereum.org)
- Keep public inputs tiny
- Hash large outputs to a single field element (e.g., Poseidon/Keccak‑compatible) and publish only the digest as public input to the final verifier. This shrinks calldata and gas.
- Prefer a proven SDK with maintained on‑chain verifier contracts
- SP1 contracts and gateways are actively maintained; use them rather than rolling your own verifier unless you need bespoke logic. (docs.succinct.xyz)
- If you must support multiple proof systems long‑term, abstract at the recursion layer, not the Solidity layer
- Use SP1 or RISC Zero to verify upstream Groth16/Plonk/STARK proofs inside the VM; the Solidity layer only ever sees one Groth16/Plonk verifier gateway call. (docs.rs)
- Don’t rely on blobs for verification
- Blobs are DA only; contracts can’t read blob bytes. Always deliver the final proof and public inputs via calldata. (eips.ethereum.org)
- Measure gas and calldata early
- Groth16 will usually give you the smallest calldata footprint (~260 bytes proof), with ~270k gas verification; Plonk may be ~868 bytes and ~300k gas—budget accordingly. (docs.succinct.xyz)
What to choose when
- You want the smallest possible on‑chain footprint and ability to mix proof systems:
- SP1 zkVM with Groth16 final proof. (docs.succinct.xyz)
- You prefer a STARK‑native stack with a clean EVM bridge:
- RISC Zero with Groth16Receipt. (dev.risczero.com)
- Your team wants a language‑level abstraction and flexibility across backends:
- Noir (ACIR) + Barretenberg/gnark backends; then wrap to a BN254 Groth16/Plonk verifier. (noir-lang.org)
- Multiple Groth16 proofs only:
- Explore SnarkPack aggregation to compress many Groth16 proofs before on‑chain verification. (research.protocol.ai)
Implementation notes and gotchas
- Trusted setup tradeoff: Groth16’s small proofs come with per‑circuit setup (or usage of community ceremonies like Aztec’s Ignition used by some stacks). If that’s undesirable, choose Plonk with universal setup and accept a slightly larger proof. (docs.succinct.xyz)
- Curve mismatch: Don’t produce BLS12‑381 proofs for L1 verification unless you deploy custom heavy verifiers; BN254 is the path of least resistance on EVM. (eips.ethereum.org)
- Tooling details:
- Circom/snarkJS can generate Groth16/Plonk verifiers; good for point solutions but not a multi‑proof abstraction by itself. (docs.circom.io)
- hardhat‑circom can auto‑generate per‑circuit verifiers; you can even template a “multi‑verifier” but you’ll still manage protocol‑specific paths. (github.com)
Bottom line
- If your question is “Can we build once and accept Groth16, Plonk, and zkVM proofs but post just one tiny, cheap proof to Ethereum?”—the answer is yes, by doing the abstraction in recursion and outputting a final Groth16/Plonk proof on BN254.
- The most product‑ready routes today are SP1 and RISC Zero; Noir and gnark round out the circuit/back‑end layer; zkVerify can help if you prefer attestations. This approach keeps your on‑chain surface small, costs predictable, and choices open as ZK tech evolves. (docs.succinct.xyz)
References and further reading
- BN254 precompiles for EVM (EIP‑196/197). (eips.ethereum.org)
- Calldata pricing (EIP‑2028) and blobs (EIP‑4844) are the key L1 constraints. (eips.ethereum.org)
- SP1 proof types, gas and SDK; Solidity verifier gateways. (docs.succinct.xyz)
- RISC Zero recursion and Groth16Receipt path. (dev.risczero.com)
- Noir ACIR backend-agnostic docs and guides. (noir-lang.org)
- gnark backends and Solidity export. (github.com)
- Groth16 aggregation (SnarkPack) and newer “final SNARK” research (Polymath). (research.protocol.ai)
Like what you're reading? Let's build together.
Get a free 30‑minute consultation with our engineering team.

