ByAUJay
Summary: Yes—with the right architecture you can swap in new ZK proof formats without redeploying your main verifier contract. This post lays out concrete, production-ready patterns (router registries, proxy upgrades, zkVM wrappers, recursive “outer” SNARKs), exact gas realities on Ethereum post-Pectra, and battle-tested governance controls to future‑proof verification on L1/L2.
Is There a Way to Future-Proof My Protocol So It Can Swap in New Proof Formats Without Redeploying the Verifier?
Decision-makers increasingly ask: How do we keep up with the breakneck pace of zero‑knowledge innovation without constantly redeploying verifiers and migrating state? The short answer: architect for “pluggability” from day one.
This post gives you concrete patterns, code‑level guidance, and up‑to‑date ecosystem facts (Ethereum’s BLS12‑381 precompiles; KZG precompile; zkVM verifier gateways; standardization efforts) to future‑proof your protocol’s verification layer.
- Ethereum’s Pectra (Prague/Electra) activated on May 7, 2025 (epoch 364032) and explicitly includes EIP‑2537, adding native BLS12‑381 precompiles at 0x0b–0x11. That matters because you can now verify BLS12‑381‑based SNARKs on mainnet efficiently, widening the set of feasible proof systems and making “format swapping” far easier. (blog.ethereum.org)
- Since Dencun (EIP‑4844), Ethereum also exposes the KZG point‑evaluation precompile at 0x0a and a BLOBHASH opcode for blob commitments—useful for proof‑carrying‑data flows and data-availability attestations. (eips.ethereum.org)
Below are five concrete architectures that let you adopt new proof systems with minimal or zero redeploys of your core contracts, followed by a practical, copy‑pasteable router interface, governance patterns, and a migration playbook.
What “future‑proof” means for verifiers
- You can add support for a new proof system (e.g., Groth16→PLONK; BN254→BLS12‑381; STARK→SNARK wrapper; zkVM X→zkVM Y) by:
- Upgrading a pluggable module behind a stable contract address, or
- Registering a new verifier implementation and routing to it, or
- Keeping a single, universal on‑chain verifier (e.g., zkVM proof) and swapping the inner verification program off‑chain.
- You avoid breaking storage layouts, addresses, and app integrations; your app’s “verify(...)” entrypoint stays stable.
Pattern 1 — Verifier Registry + Router (recommended default)
A minimal, stable “router” contract exposes a single verify(bytes proof, bytes publicInputs, bytes32 formatId) entrypoint. It dispatches to registered verifier modules keyed by formatId.
- Add support for a new proof format by deploying a new verifier module and registering it in the router. No core app redeploy. No storage migration.
- Use CREATE2 to deploy verifiers at deterministic addresses keyed by the formatId and VK hash, which simplifies allowlists and audit trails. (eips.ethereum.org)
Why it works now:
- Post‑Pectra, you can include BLS12‑381 verifiers for Groth16/PLONK beside legacy BN254 verifiers, and route based on the proof’s curve/system identifier. (eips.ethereum.org)
- For blob‑anchored statements, the router can validate KZG openings via 0x0a in the relevant verifier module. (eips.ethereum.org)
Production precedents:
- zkVM teams ship “gateway” verifiers that route across versions and systems. Succinct’s SP1VerifierGateway is a canonical, version‑aware router: you point your app to one address; they add new verifier implementations beneath it. Use the same idea for your in‑house formats. (docs.succinct.xyz)
- RISC Zero ecosystems build dynamic verifier routers as first‑class infra on other chains; the design cleanly separates routing, pausing, and module upgrades—mirror this on EVM. (github.com)
Gas reality:
- BN254 pairings (EIP‑1108) cost ~34,000·k + 45,000 gas; a Groth16 verifier doing three pairings is dominated by that formula. BLS12‑381 now has native pairings at 0x0f, with specified gas in EIP‑2537—use whichever curve your prover targets and route accordingly. (eip.directory)
Pattern 2 — Upgradable proxy around the verifier set (UUPS/Diamond)
If you prefer one “verifier component” instead of multiple modules, put it behind a UUPS proxy or Diamond (EIP‑2535). You can then upgrade logic as proof formats evolve, keeping the proxy address stable.
- UUPS (ERC‑1822/1967) gives a minimal, gas‑efficient upgrade path. Protect upgrades with role‑based access and a timelock. (docs.openzeppelin.com)
- Diamond (EIP‑2535) scales when you need many verifier facets (e.g., separate facets for Groth16‑BN254, Groth16‑BLS12‑381, PLONK‑BLS12‑381, SP1‑Groth16, etc.) without hitting the 24KB bytecode limit. (eips.ethereum.org)
Tradeoffs:
- Proxies simplify integration (one address) but concentrate change risk—use rigorous upgrade gates (see Governance section).
- Diamonds add organizational complexity but scale better for lots of formats.
Pattern 3 — “Universal” outer verifier: zkVM wrapper with fixed on‑chain VK
You can avoid redeploys entirely by verifying a single zkVM proof on‑chain (fixed verifier, fixed verifying key). The zkVM program you run off‑chain can itself verify any inner proof format, so supporting a new proof type is purely an off‑chain change.
How it’s shipping today:
- SP1: You generate an SP1 proof and verify it on‑chain via a stable verifier (Groth16 or PLONK wrapper). Succinct deploys a gateway that routes versions for you; your contract calls ISP1Verifier.verifyProof(programVKey, publicValues, proofBytes). Adding a new inner proof format means changing the SP1 guest program off‑chain; the on‑chain verifier stays the same. (docs.succinct.xyz)
- RISC Zero: Their stack supports STARK proofs with recursion and a Groth16 “receipt” for EVM verification; again, your on‑chain verifier is stable while you evolve guest logic to check different inner proof formats. (dev.risczero.com)
When to choose this:
- You expect frequent proof‑system churn (e.g., PLONK→Plonky3→Folded SNARKs).
- You want one immutable verifier address and to push all flexibility off‑chain.
Notes:
- Expect on‑chain verification around a few hundred thousand gas for typical zkVM receipts; teams publish ~280k-gas targets for contract‑call proofs after recursion/SNARK wrapping. Benchmark in your context. (github.com)
Pattern 4 — Recursive “outer SNARK” that wraps your changing inner proofs
If your prover is STARK‑based (or a fast, evolving scheme), add a recursion layer that compresses proofs into a stable outer SNARK (Groth16 or PLONK) over BN254 or BLS12‑381; your on‑chain contract only verifies the outer proof.
- Post‑Pectra, consider BLS12‑381 for the outer SNARK—security margin and now-native precompiles at 0x0b–0x11 change the calculus. BN254 remains cheap (EIP‑1108) and widely used; support both via routing. (eips.ethereum.org)
- Libraries: PSE’s snark‑verifier/halo2‑solidity‑verifier; gnark’s Solidity verifiers; these are the building blocks of many production verifiers. (github.com)
Ecosystem pulse:
- Polygon’s Plonky3 (2024→2025) matured as a modular, fast proving toolkit and is being embedded in stacks (including SP1). Expect more teams to standardize on an “outer SNARK” strategy with Plonky3/PLONK or Groth16 now that BLS12‑381 verification is native. (coindesk.com)
Pattern 5 — Off‑chain verification networks as an escape valve
You can verify heavy/novel proofs on a specialized chain (e.g., zkVerify) and bridge attestations on‑chain. Architect the router so a “formatId = ZKV/attestation” path is just another module; swapping formats remains a registry update. Evaluate trust and latency. (github.com)
A minimal, production‑ready VerifierRouter interface
Use a stable interface and register per‑format verifiers. Keep the router tiny; put heavy math in modules calling precompiles.
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; // Stable interface every verifier module must satisfy. interface IVerifierModule { // Implementations SHOULD validate that vkHash matches embedded VK and revert otherwise. function verify(bytes calldata proof, bytes calldata publicInputs, bytes32 vkHash) external view returns (bool); } contract VerifierRouter { // Format identifiers might encode scheme+curve+encoding, e.g. keccak256("groth16:bn254:abi-v1") // or keccak256("plonk:bls12-381:pse-vkey-v2") or keccak256("sp1:groth16:v5"). mapping(bytes32 => address) public module; // formatId -> verifier module mapping(bytes32 => bytes32) public allowedVkHash; // optional: (formatId) -> VK hash event ModuleSet(bytes32 indexed formatId, address indexed impl, bytes32 vkHash); // Governance-guarded in production; see Governance section. function setModule(bytes32 formatId, address impl, bytes32 vkHash) external /* onlyGov */ { module[formatId] = impl; if (vkHash != bytes32(0)) allowedVkHash[formatId] = vkHash; emit ModuleSet(formatId, impl, vkHash); } // Single stable entrypoint for callers. function verify(bytes calldata proof, bytes calldata publicInputs, bytes32 formatId) external view returns (bool) { address impl = module[formatId]; require(impl != address(0), "format not supported"); bytes32 vkHash = allowedVkHash[formatId]; // optional gating; zero means "any" return IVerifierModule(impl).verify(proof, publicInputs, vkHash); } }
Implementation tips:
- For BN254 operations, use EIP‑196/197 precompiles with EIP‑1108 gas schedule; for BLS12‑381, call 0x0b–0x11 (pairing at 0x0f). Keep byte encodings big‑endian where required (EIP‑2537). (eips.ethereum.org)
- For blob‑anchored statements, call the point‑evaluation precompile at 0x0a with the 192‑byte input structure and use BLOBHASH to load versioned hashes. (eips.ethereum.org)
- If you adopt a zkVM wrapper, your “module” becomes the zkVM gateway (e.g., SP1VerifierGateway). Your app’s interface stays unchanged while the gateway team adds support under the hood. (docs.succinct.xyz)
Choosing curves and formats in 2026: a pragmatic matrix
- BN254 Groth16
- Pros: Lowest gas on EVM for years; mature tooling (gnark, circom). (eip.directory)
- Cons: ~80‑bit security margin; ecosystem gradually moving up‑curve for “forever” proofs.
- BLS12‑381 Groth16/PLONK (newly practical on EVM)
- Pros: Now natively verifiable via EIP‑2537; higher security margin; aligns with Eth consensus crypto and KZG world. (eips.ethereum.org)
- Cons: Migration effort if your stack is all BN254 today.
- STARK → SNARK wrapper (outer Groth16/PLONK)
- Pros: Best of both worlds: fast proving + small verify; swap inner STARK flavors freely; one stable outer verifier. (dev.risczero.com)
- zkVM proof (SP1/RISC Zero)
- Pros: A single, fixed on‑chain verifier; support any inner verifier program without redeploys. (docs.succinct.xyz)
Bottom line for most teams:
- Ship BN254 Groth16 today if you already have it, but expose a BLS12‑381 path behind your router so you can pivot without breaking interfaces. Post‑Pectra, BLS12‑381 is first‑class on Ethereum. (eips.ethereum.org)
- If you expect rapid proof‑system churn, prefer the zkVM wrapper pattern; it future‑proofs the most with one verifier address. (docs.succinct.xyz)
Governance: upgrade without surprises
- Gate router updates via a UUPS or Diamond governor + timelock; emit ModuleSet events and publish VK hashes for each formatId. (docs.openzeppelin.com)
- Approve updates off‑chain with EIP‑712 typed data; include formatId, impl, vkHash, and an expiry. On‑chain, verify the signature from your governance key and enforce a delay. This yields transparent, replay‑resistant upgrades that users and integrators can monitor. (eips.ethereum.org)
- Use CREATE2 to pre‑commit addresses for new verifier modules (salt = keccak256(formatId||vkHash)). You can document addresses before deployment for auditors and counterparties. (eips.ethereum.org)
Security and auditability checklist
- VK pinning: Require module verify() to recompute the verifying‑key commitment and match the provided vkHash; store it in the router. This prevents “key‑swapping” attacks.
- Input normalization: Enforce endian‑ness and subgroup checks precisely per EIP specs; BLS12‑381 precompiles have strict encodings—follow the spec to the byte. (eips.ethereum.org)
- DoS hardening: Cap the number of pairings per call; use STATICCALL to precompiles; surface precise error codes.
- Event trail: Emit one event per verification (formatId, vkHash, witness hash) for analytics and incident response.
- Formal verification: Track ZKProof’s “Verified Verifier” work; prefer libraries and generators with formalization efforts or recent audits. (zkproof.org)
Practical example: enabling BLS12‑381 next to BN254 with no app changes
- Today: Your rollup uses BN254 Groth16. Router entry formatIdA = keccak256("groth16:bn254:abi-v1") → BN254Verifier module using EIP‑196/197+1108. (eips.ethereum.org)
- Tomorrow: You add formatIdB = keccak256("groth16:bls12-381:abi-v1") → new module using EIP‑2537 pairings at 0x0f; push the new impl via timelocked governance. App code unchanged. (eips.ethereum.org)
- Data‑availability: For blob‑backed statements, you register formatIdC modules that call 0x0a to open KZG commitments against BLOBHASH references. (eips.ethereum.org)
If you want one verifier address for the next 3–5 years
Pick the zkVM wrapper route:
- SP1: Point to the SP1VerifierGateway address; bind to your programVKey; when SP1 introduces a new proving backend or you switch your in‑guest verifier (e.g., from Groth16 to PLONK, or a new inner proof format), nothing changes on‑chain for you. (docs.succinct.xyz)
- RISC Zero: Use Groth16 receipts. Change your guest program to accept new inner formats; the on‑chain receipt verifier stays put. (dev.risczero.com)
Performance keeps getting better:
- SP1 Hypercube and peers are pushing “real‑time” proving of Ethereum blocks; while that’s about proving speed, the takeaway for product teams is: don’t over‑optimize on a single proof format today—design to swap. (theblock.co)
Standards and interop you can lean on
- ZKProof community is actively standardizing verifiers and PLONK‑ish ecosystems; monitor the “Verified Verifier” workstream for formally verified reference verifiers. This will make “plug‑and‑play” modules safer to adopt. (zkproof.org)
- Historical “zkInterface” work shows the feasibility of decoupling frontends/backends—use the same spirit in your on‑chain routing and VK normalization. (github.com)
Migration playbook: 2–4 weeks to future‑proof an existing app
Week 1
- Extract your current verifier into an IVerifierModule; measure gas; add precise input checks.
- Deploy VerifierRouter at a new address; have the app delegate verification calls to it (one PR).
- Implement governance: EIP‑712 admin, timelock, events.
Week 2
- Add a BLS12‑381 Groth16 or PLONK module (if applicable), using EIP‑2537 precompiles; fuzz against your prover’s test vectors. (eips.ethereum.org)
- If you rely on blob data, add a KZG opener module calling 0x0a; write end‑to‑end tests that pass BLOBHASH indices and verify points. (eips.ethereum.org)
Weeks 3–4
- Optional: add a zkVM path (SP1 or RISC Zero receipt) and run canary verifications for a subset of batches; design a controlled cutover. (docs.succinct.xyz)
- Ship dashboards over ModuleSet and verification events; publish a VK hash registry.
Common pitfalls to avoid
- Hard‑coding curve/system in the application contract. Always accept a formatId and route.
- Packing proof inputs incorrectly (endian mistakes) for BLS12‑381 precompiles; abide by EIP‑2537’s byte layouts. (eips.ethereum.org)
- Letting the verifier module load an arbitrary VK from calldata. Pin VKs (hashes) in storage or registry to prevent bait‑and‑switch.
- Ignoring code size. Use tiny Yul wrappers around precompiles; avoid monolithic generated verifiers when a router+module split will do. If you need many functions, consider Diamonds. (eips.ethereum.org)
What to budget for gas
- BN254 Groth16 pairing component ≈ 3 pairings → ~34,000·3 + 45,000 ≈ 147,000 gas plus field ops. Your total often lands ~200–230k gas. (eip.directory)
- BLS12‑381 Groth16/PLONK: post‑Pectra, use the 0x0f pairing precompile; expect similar order‑of‑magnitude with higher security margin. Profile your circuit. (eips.ethereum.org)
- zkVM receipts after recursion/SNARK wrap: ~280k gas is a reasonable target for “contract‑call proof” style flows; measure in your environment. (github.com)
- KZG point‑evaluation precompile (0x0a) is 50,000 gas per call; design blob‑opening modules accordingly. (eips.ethereum.org)
Best emerging practices we use with clients
- Ship BN254 today, enable BLS12‑381 tomorrow: keep both behind your router and let governance flip defaults once you’re confident, leveraging EIP‑2537. (eips.ethereum.org)
- Normalize VKs: store vkHash per formatId; require match in modules; log it on every success.
- Deterministic deployment with CREATE2: salt = keccak256(formatId||vkHash) so integrators can pre‑compute addresses. (eips.ethereum.org)
- Canary dual‑verification: For the first week after adding a new module, verify both the old and new formats for a small percentage of proofs and compare results on‑chain; auto‑revert on mismatch.
- Documentation: publish a living “Supported Formats Matrix” with formatId → curve → encoding → VK hash → module address. Include BLS12‑381 and BN254 entries post‑Pectra. (eips.ethereum.org)
- Track standardization: align your module interfaces to ZKProof “Verified Verifier” outputs as they mature. (zkproof.org)
FAQ
-
Do I need to wait for more precompiles?
No. Today you’ve got BN254 (mature) and BLS12‑381 (post‑Pectra) plus KZG. That’s enough to design a durable router across Groth16/PLONK/zkVM receipts. (eip.directory) -
What if I’m all‑in on STARKs?
Wrap with a stable outer SNARK or use a zkVM wrapper. Your app sees a steady interface while inner formats evolve. (dev.risczero.com) -
How do I keep bytecode under 24KB?
Prefer minimal Yul wrappers around precompiles and split modules. If still large, use Diamond facets. (eips.ethereum.org)
Key references you’ll want handy
- Pectra activation and meta‑EIP list (includes EIP‑2537). (blog.ethereum.org)
- EIP‑2537 (BLS12‑381 precompiles, addresses 0x0b–0x11). (eips.ethereum.org)
- EIP‑4844 (KZG point‑evaluation precompile at 0x0a, blob details). (eips.ethereum.org)
- EIP‑196/197 + EIP‑1108 (BN254 precompiles and gas). (eips.ethereum.org)
- SP1 verifier gateway and contracts (canonical on Ethereum + L2s). (docs.succinct.xyz)
- RISC Zero receipts and on‑chain Groth16 verification model. (dev.risczero.com)
- ZKProof “Verified Verifier” working group. (zkproof.org)
- CREATE2 and Singleton Factory for deterministic deployments. (eips.ethereum.org)
- OpenZeppelin guidance on safe upgrades (UUPS/1967). (docs.openzeppelin.com)
Bottom line: You don’t have to pick one proof system “forever.” With a router or universal zkVM wrapper, pinned VKs, deterministic module addresses, and post‑Pectra precompiles, you can adopt new formats in days—not quarters—without redeploying your core verifier or breaking integrators.
Like what you're reading? Let's build together.
Get a free 30‑minute consultation with our engineering team.

