7Block Labs
Blockchain Technology

ByAUJay

Summary: Absolutely--if you set up your architecture correctly, you can easily swap in new ZK proof formats without having to redeploy your main verifier contract. In this post, we'll dive into practical, ready-for-production patterns like router registries, proxy upgrades, zkVM wrappers, and recursive “outer” SNARKs. We’ll also cover the exact gas costs on Ethereum after Pectra and share some tried-and-true governance controls to keep your verification future-proof 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 are often wondering: How can we stay on top of the lightning-fast evolution of zero-knowledge technology without having to constantly redeploy verifiers or migrate state? The quick answer is to design for “pluggability” right from the start.

In this post, you’ll find solid patterns, practical code tips, and the latest info about the ecosystem--like Ethereum’s BLS12‑381 precompiles, KZG precompile, zkVM verifier gateways, and the ongoing standardization efforts. All of this will help you make sure your protocol’s verification layer is ready for the future.

  • Ethereum's Pectra (Prague/Electra) went live on May 7, 2025 (epoch 364032) and it brings some exciting enhancements with EIP‑2537. This update adds native BLS12‑381 precompiles at addresses 0x0b-0x11. What’s the big deal? Well, now you can verify BLS12‑381‑based SNARKs on the mainnet much more efficiently. This opens up a whole new world of proof systems and makes “format swapping” super simple. You can dive into the details here: (blog.ethereum.org).
  • On top of that, with Dencun (EIP‑4844), Ethereum has also introduced the KZG point‑evaluation precompile at 0x0a, along with a BLOBHASH opcode for blob commitments. This is super handy for proof-carrying data flows and data-availability attestations. If you're curious, check out more about it here: (eips.ethereum.org).

Here are five solid architectures that allow you to integrate new proof systems without needing to redeploy your core contracts--or with minimal redeploys. Plus, we've included a handy, copy-pasteable router interface, some governance patterns, and a migration playbook to help you out.


What “future‑proof” means for verifiers

  • You can easily add support for a new proof system (like switching from Groth16 to PLONK, moving from BN254 to BLS12‑381, or creating a wrapper for STARK and SNARK) in a few different ways:
    • Upgrade a pluggable module that's linked to a stable contract address.
    • Register a new verifier implementation and direct traffic to it.
    • Stick with a single, universal on-chain verifier (like a zkVM proof) and handle the inner verification process off-chain.
  • This way, you won’t mess up storage layouts, addresses, or app integrations. Your app’s “verify(...)” entrypoint remains nice and steady.

A simple and stable “router” contract has just one main function you’ll use: verify(bytes proof, bytes publicInputs, bytes32 formatId). This function sends the request over to the registered verifier modules, which are organized by the formatId.

  • To roll out support for a new proof format, just deploy a fresh verifier module and register it in the router. This means no need to redeploy the core app or migrate any storage.
  • Leverage CREATE2 to deploy verifiers at predictable addresses based on the formatId and VK hash. This approach makes managing allowlists and keeping audit trails so much easier. (eips.ethereum.org)

Why It Works Now:

  • With Post‑Pectra, you can mix in BLS12‑381 verifiers for Groth16/PLONK alongside the older BN254 verifiers. You can easily route based on the proof’s curve or system identifier. (eips.ethereum.org)
  • For those blob‑anchored statements, the router's got your back; it can validate KZG openings through 0x0a in the relevant verifier module. (eips.ethereum.org)

Production precedents:

  • zkVM teams are rolling out “gateway” verifiers that smartly route across different versions and systems. For example, Succinct’s SP1VerifierGateway acts as a straightforward, version-aware router. You just need to point your app to one address, and they’ll handle the rest by adding new verifier implementations underneath. You can apply the same concept for your in-house formats. Check it out here: (docs.succinct.xyz)
  • The RISC Zero ecosystem is all about building dynamic verifier routers as essential infrastructure on other chains. Their design does a great job of keeping routing, pausing, and module upgrades separate, which is something you might want to consider mirroring on EVM. Dive into their work here: (github.com)

Gas Reality

So, here's the lowdown:

  • BN254 pairings (EIP-1108) run about 34,000·k + 45,000 gas. When you’ve got a Groth16 verifier needing three pairings, that formula really takes the lead. On the flip side, BLS12-381 has got those native pairings at 0x0f, and you can check out the specific gas details in EIP-2537. Just make sure to go with whatever curve your prover is aiming for and route it accordingly. (eip.directory)

Pattern 2 -- Upgradable proxy around the verifier set (UUPS/Diamond)

If you’d rather stick with a single “verifier component” instead of juggling multiple modules, consider putting it behind a UUPS proxy or a Diamond (EIP‑2535). This way, as proof formats change and improve, you can effortlessly upgrade the logic while keeping that proxy address consistent.

  • The UUPS upgrade pattern (ERC‑1822/1967) is all about providing a streamlined, gas-efficient way to upgrade your contracts. Plus, you can secure those upgrades with role-based access and a timelock. Check it out here: (docs.openzeppelin.com).
  • The Diamond standard (EIP‑2535) really shines when you're dealing with a ton of verifier facets. Whether you need different facets for Groth16‑BN254, Groth16‑BLS12‑381, PLONK‑BLS12‑381, SP1‑Groth16, or more, it helps you avoid the pesky 24KB bytecode limit. Dive into the details here: (eips.ethereum.org).

Tradeoffs:

  • Proxies make integration smoother since you only have to deal with one address, but they also put all your change risk in one spot--so be sure to use strict upgrade gates (check out the Governance section for more on this).
  • Diamonds might complicate things a bit organizationally, but they really shine when it comes to scaling with a bunch of different formats.

Pattern 3 -- “Universal” outer verifier: zkVM wrapper with fixed on‑chain VK

You can completely skip redeploys by just verifying a single zkVM proof on-chain (with a fixed verifier and a fixed verifying key). The cool part is that the zkVM program you run off-chain can handle any inner proof format, which means adding support for a new proof type is just an off-chain tweak.

How It’s Shipping Today:

  • SP1: You kick things off by generating an SP1 proof and checking it on-chain using a stable verifier, either Groth16 or the PLONK wrapper. Succinct makes this easier by setting up a gateway that routes different versions for you. Your contract will call ISP1Verifier.verifyProof(programVKey, publicValues, proofBytes). If you want to add a new inner proof format, you’ll need to tweak the SP1 guest program off-chain, but don’t worry--the on-chain verifier stays the same. Check out more about it here.
  • RISC Zero: Their setup supports STARK proofs with recursion, plus a Groth16 "receipt" for EVM verification. Just like with SP1, your on-chain verifier remains steady while you adapt your guest logic to handle various inner proof formats. For a deeper dive, head over to this link.

When to pick this option:

  • If you think you'll be changing proof systems often (like switching from PLONK to Plonky3 to Folded SNARKs).
  • If you prefer having just one fixed verifier address and would rather handle all the flexibility off-chain.

Notes:

  • Look out for on-chain verification costs that usually hover around a few hundred thousand gas for standard zkVM receipts. Teams generally set around ~280k-gas targets for contract-call proofs following the recursion/SNARK wrapping process. Make sure to benchmark this based on your specific context. (github.com)

Pattern 4 -- Recursive “outer SNARK” that wraps your changing inner proofs

If you’re using a STARK-based prover (or any other speedy, evolving method), consider adding a recursion layer. This can help pack those proofs into a more stable outer SNARK, like Groth16 or PLONK, using either BN254 or BLS12-381. Then, your on-chain contract will just need to verify that outer proof.

  • After Post‑Pectra, it’s worth checking out BLS12‑381 for the outer SNARK--having that extra security margin and the now-native precompiles at 0x0b-0x11 really shifts the game a bit. BN254 is still affordable (thanks to EIP‑1108) and super popular, so it makes sense to support both through routing. (eips.ethereum.org)
  • Libraries: Don’t forget about PSE’s snark‑verifier/halo2‑solidity‑verifier and gnark’s Solidity verifiers; these gems are the core components for many production verifiers. (github.com)

Ecosystem Pulse

  • Polygon’s Plonky3 is ramping up for 2024 to 2025 as a slick, modular proving toolkit. It's getting integrated into various stacks, including SP1. With BLS12‑381 verification now a built-in feature, we're likely to see more teams jumping on the “outer SNARK” bandwagon, opting for Plonky3/PLONK or Groth16. Check out the details over at CoinDesk!

Pattern 5 -- Off‑chain verification networks as an escape valve

You can check out heavy or novel proofs using a specialized chain like zkVerify, and you can also bridge attestations on-chain. When setting up the router, think of it this way: a “formatId = ZKV/attestation” path is simply another module; updating the format just means making a quick registry update. Be sure to consider trust and latency in your evaluation. (github.com)


A minimal, production‑ready VerifierRouter interface

Stick to a stable interface and make sure to register verifiers for each format. Keep the router lightweight; save the heavy math for modules that call 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:

  • When you're handling BN254 operations, make sure to use the EIP‑196/197 precompiles along with the EIP‑1108 gas schedule. For BLS12‑381, you'll want to hit up 0x0b through 0x11 (with pairing specifically at 0x0f). Oh, and don’t forget to keep your byte encodings big-endian when necessary (thanks, EIP‑2537!). Check it out here: (eips.ethereum.org)
  • For blob‑anchored statements, you’ll need to call the point‑evaluation precompile at 0x0a using a 192‑byte input structure. Use BLOBHASH to grab those versioned hashes. You can find more info here: (eips.ethereum.org)
  • If you’re thinking about adding a zkVM wrapper, your “module” will function as the zkVM gateway (like SP1VerifierGateway, for example). Good news--your app’s interface will remain the same while the gateway team works on the behind-the-scenes support. For details, check this out: (docs.succinct.xyz)

Choosing curves and formats in 2026: a pragmatic matrix

  • BN254 Groth16

    • Pros: This one has had the lowest gas fees on EVM for years! Plus, it comes with some solid, mature tooling like gnark and circom. (eip.directory)
    • Cons: The security margin is around 80 bits, and the ecosystem is slowly shifting towards “forever” proofs, so that's something to consider.
  • BLS12-381 Groth16/PLONK (now practical on EVM)

    • Pros: It's now natively verifiable thanks to EIP-2537, has a higher security margin, and it syncs nicely with Ethereum's consensus crypto and the KZG world. (eips.ethereum.org)
    • Cons: If your setup is currently all about BN254, migrating to this will involve some effort.
  • STARK → SNARK wrapper (outer Groth16/PLONK)

    • Pros: You get the best of both worlds here! Fast proving combined with a tiny verification process, and you can easily switch out inner STARK flavors without fussing too much, all while having one stable outer verifier. (dev.risczero.com)
  • zkVM proof (SP1/RISC Zero)

    • Pros: This setup offers a single, fixed on-chain verifier, and it can handle any inner verifier program without needing redeployments. That's pretty convenient! (docs.succinct.xyz)

Bottom line for most teams:

  • If you've got BN254 Groth16 ready to go, ship it today! But make sure to set up a BLS12‑381 path behind your router. This way, you can pivot smoothly without messing up any interfaces. Remember, post-Pectra, BLS12‑381 is the way to go on Ethereum. (eips.ethereum.org)
  • Anticipating a lot of changes in proof systems? Then you’ll want to lean towards the zkVM wrapper pattern. It offers the best future-proofing with just one verifier address. (docs.succinct.xyz)

Governance: upgrade without surprises

  • Update the gate router using a UUPS or Diamond governor along with a timelock. Make sure to emit ModuleSet events and publish VK hashes for every formatId. You can find more details here.
  • For off-chain updates, get them approved with EIP‑712 typed data. Don't forget to include formatId, impl, vkHash, and an expiry. When working on-chain, verify the signature from your governance key and set a delay. This approach gives you clear, replay-resistant upgrades that both users and integrators can keep an eye on. Check it out here.
  • Use CREATE2 to pre-commit addresses for your new verifier modules (salt = keccak256(formatId||vkHash)). This way, you can document the addresses before deployment, making life easier for auditors and counterparties. More info can be found here.

Security and auditability checklist

  • VK Pinning: We need to make sure the verify() module recalculates the verifying-key commitment and matches it up with the provided vkHash, then store that in the router. This is a key step in stopping “key-swapping” attacks.
  • Input Normalization: Let’s enforce endian-ness and subgroup checks just as the EIP specs say. The BLS12-381 precompiles have some strict encoding rules--it's all about sticking to the spec down to the last byte. Check it out here: (eips.ethereum.org)
  • DoS Hardening: To protect against denial of service attacks, we should cap the number of pairings allowed in each call and use STATICCALL for precompiles. Plus, let’s make sure we’re surfacing clear and precise error codes.
  • Event Trail: We should emit a single event for every verification (including formatId, vkHash, and witness hash) so we can keep track for analytics and quick incident responses.
  • Formal Verification: Keep an eye on ZKProof’s “Verified Verifier” work; let’s lean towards libraries and generators that come with formalization efforts or have undergone recent audits. For more info, check this out: (zkproof.org)

  1. Today: Right now, your rollup is set up with BN254 Groth16. The router's entry formatIdA is calculated as keccak256("groth16:bn254:abi-v1") → this links to the BN254Verifier module following EIP‑196/197+1108. You can check it out here.
  2. Tomorrow: You'll be adding formatIdB as keccak256("groth16:bls12-381:abi-v1") → which will incorporate a new module using EIP‑2537 pairings at 0x0f. The new implementation will be pushed through timelocked governance, but don’t worry, your app code will stay the same. More info here.
  3. Data‑availability: For those blob-backed statements, you’ll be registering formatIdC modules that reach out to 0x0a to open KZG commitments linked to BLOBHASH references. Get the details here.

If you want one verifier address for the next 3-5 years

Go for the zkVM Wrapper Route:

If you’re diving into the zkVM world, opting for the zkVM wrapper route is a solid choice. It provides a nice balance between power and usability. Here’s why you might want to consider it:

  • User-friendly: The wrapper simplifies many complex processes. You won’t have to deal with the nitty-gritty details of zkVM; it handles a lot for you.
  • Compatibility: It ensures that your setup plays nicely with various other tools and libraries. No one likes compatibility headaches, right?
  • Performance Gains: By using the wrapper, you can often achieve better performance metrics without diving deep into optimization strategies.

Getting Started:

To get rolling with the zkVM wrapper, here are the steps you'll want to follow:

  1. Install the Wrapper: Grab the latest version from the zkVM GitHub repository.

    git clone https://github.com/zkvm/zkvm.git
    cd zkvm
    npm install
  2. Configuration: Set up your configuration files according to your project requirements. You can find sample configs in the configs/ directory.
  3. Run Your First Project: Try out a quick demo project to get a feel for how the wrapper works. Follow the instructions in the README file.

Key Benefits:

  • Rapid Development: You’ll spend less time on setup and more time building.
  • Community Support: The zkVM community is thriving. You can always ask questions and share insights.
  • Evolving Features: The wrapper continues to receive updates, meaning you can look forward to new features and improvements regularly.

By going with the zkVM wrapper route, you're setting yourself up for a smoother experience in the world of zero-knowledge proofs, all while keeping things straightforward and efficient. Happy coding!

  • SP1: Just point to the SP1VerifierGateway address and link it up with your programVKey. When SP1 rolls out a new proving backend or if you decide to switch your in-guest verifier (like moving from Groth16 to PLONK, or trying out a new inner proof format), you won't have to worry about making any changes on-chain. Check out the details here.
  • RISC Zero: Stick with Groth16 receipts for this one. If you're looking to update your guest program to handle new inner formats, the on-chain receipt verifier will remain unchanged. Get more info here.

Performance Keeps Getting Better

  • The SP1 Hypercube and its peers are stepping up the game with “real-time” proving of Ethereum blocks. While this mainly focuses on how fast we can prove things, the key point for product teams is to avoid getting too hung up on one proof format right now--make sure your design can adapt and swap when needed. (theblock.co)

Standards and interop you can lean on

  • The ZKProof community is doing some great work in standardizing verifiers and PLONK-ish ecosystems. Keep an eye on the “Verified Verifier” workstream for those formally verified reference verifiers. This way, those “plug-and-play” modules will be a lot safer to use. Check it out here: (zkproof.org)
  • The historical work on “zkInterface” shows that it’s totally possible to decouple frontends and backends. You might want to apply the same approach for your on-chain routing and VK normalization. Dive into it here: (github.com)

Migration playbook: 2-4 weeks to future‑proof an existing app

Week 1

  • Break out your existing verifier into an IVerifierModule, track the gas usage, and make sure to include some detailed input validations.
  • Deploy the VerifierRouter at a fresh address; update the app to route verification calls to it (all in one PR).
  • Set up governance: incorporate EIP‑712 for admin functions, add a timelock, and ensure the events are covered.

Week 2

  • Go ahead and integrate a BLS12‑381 Groth16 or PLONK module if it fits your needs. Make sure to use the EIP‑2537 precompiles and do some fuzz testing with your prover’s test vectors. You can check out more about it here.
  • If you're working with blob data, it's time to add a KZG opener module that calls 0x0a. Don’t forget to write some end-to-end tests that pass BLOBHASH indices and verify those points. More details can be found here.

Weeks 3-4

  • If you feel like it, you can add a zkVM path (like SP1 or a RISC Zero receipt) and give canary verifications a shot for a few batches. Just make sure to plan a smooth cutover. (docs.succinct.xyz)
  • Get those dashboards out there for ModuleSet and verification events, and don't forget to roll out a VK hash registry.

Common pitfalls to avoid

  • Don't hard-code the curve/system in the application contract. Always make sure to accept a formatId and a route.
  • Be careful with proof inputs! Avoid endian mistakes for BLS12‑381 precompiles and stick to EIP‑2537’s byte layouts. Check it out here: (eips.ethereum.org)
  • Watch out for letting the verifier module load any random VK from calldata. It’s a good idea to pin VKs (hashes) in storage or a registry to stop any bait-and-switch tactics.
  • Don't overlook the code size. Use compact Yul wrappers around precompiles; it’s better than having bulky generated verifiers when a router+module setup can work just fine. If you've got a lot of functions to deal with, consider using Diamonds. You can find more info here: (eips.ethereum.org)

What to budget for gas

  • For the BN254 Groth16 pairing component, you're looking at roughly 3 pairings, which adds up to about 34,000·3 + 45,000, landing you around 147,000 gas, plus some extra for field ops. So in total, you’ll often see yourself hitting around 200-230k gas. You can check out more details on this here.
  • When it comes to BLS12‑381 Groth16/PLONK, especially after the Pectra updates, you should switch to the 0x0f pairing precompile. You can expect similar gas costs but with a better security margin. Just remember to profile your circuit to optimize it! More info is available here.
  • If you’re working with zkVM receipts after recursion or SNARK wrapping, aiming for around 280k gas is a solid target for your “contract‑call proof” style flows. It’s best to measure it out in your specific environment, though! You can find the relevant details here.
  • Lastly, the KZG point‑evaluation precompile (0x0a) will cost you 50,000 gas per call, so make sure to design your blob‑opening modules with that in mind. You can look deeper into this here.

Best emerging practices we use with clients

  • Ship BN254 today, and let's roll out BLS12‑381 tomorrow! Keep both of these tucked behind your router, and once you're feeling good about it, let governance switch the defaults using EIP‑2537. Check it out here.
  • Normalize those VKs! Store the vkHash for each formatId, make sure they match in the modules, and log it every time there's a success.
  • For deterministic deployment, use CREATE2: set the salt as keccak256(formatId||vkHash) so integrators can figure out addresses ahead of time. More info here.
  • Implement a canary dual‑verification: During the first week after introducing a new module, check both the old and new formats for a small percentage of proofs. Then, compare the results on-chain and automatically revert if there's a mismatch.
  • Documentation is key! Publish a living “Supported Formats Matrix” that includes formatId → curve → encoding → VK hash → module address. Don't forget to add BLS12‑381 and BN254 entries after Pectra. You can find more details here.
  • Keep an eye on standardization: align your module interfaces with ZKProof “Verified Verifier” outputs as they develop. Discover more here.

FAQ

  • Do I need to wait for more precompiles?
    Nope! Right now, you’ve got BN254 (which is mature) and BLS12‑381 (post‑Pectra), along with KZG. That’s plenty to create a robust router for Groth16/PLONK/zkVM receipts. (eip.directory)
  • What if I'm fully committed to STARKs?
    You can either wrap it with a stable outer SNARK or go for a zkVM wrapper. This way, your app keeps a consistent interface even as the inner formats change. Check it out here: (dev.risczero.com)
  • How can I keep my bytecode under 24KB?
    Aim for lightweight Yul wrappers around precompiles and consider breaking things into smaller modules. If you’re still running into size issues, look into using Diamond facets. (eips.ethereum.org)

Key references you’ll want handy

  • Check out the Pectra activation and the meta‑EIP list, which now includes EIP‑2537. (blog.ethereum.org)
  • Here's some info on EIP‑2537, which deals with the BLS12‑381 precompiles. You can find it at addresses 0x0b-0x11. (eips.ethereum.org)
  • Don’t miss EIP‑4844! It's all about the KZG point‑evaluation precompile located at 0x0a, along with blob details. (eips.ethereum.org)
  • EIP‑196/197 and EIP‑1108 cover the BN254 precompiles and gas issues. Worth checking out! (eips.ethereum.org)
  • Got to know about the SP1 verifier gateway and contracts? They’re canonical on Ethereum and L2s. (docs.succinct.xyz)
  • Dive into RISC Zero receipts and their on‑chain Groth16 verification model. It's pretty interesting! (dev.risczero.com)
  • The ZKProof “Verified Verifier” working group is working hard on some cool stuff. Check it out! (zkproof.org)
  • CREATE2 and Singleton Factory are here to help with deterministic deployments. Definitely a useful addition! (eips.ethereum.org)
  • Lastly, OpenZeppelin has some great guidance on safe upgrades, specifically for UUPS/1967. Don’t miss it! (docs.openzeppelin.com)

Bottom line: You don’t have to stick with one proof system “forever.” Thanks to a router or universal zkVM wrapper, pinned VKs, deterministic module addresses, and post‑Pectra precompiles, you can switch to new formats in just days--not quarters--without needing to redeploy your core verifier or causing headaches for integrators.

Like what you're reading? Let's build together.

Get a free 30-minute consultation with our engineering team.

7BlockLabs

Full-stack blockchain product studio: DeFi, dApps, audits, integrations.

7Block Labs is a trading name of JAYANTH TECHNOLOGIES LIMITED.

Registered in England and Wales (Company No. 16589283).

Registered Office address: Office 13536, 182-184 High Street North, East Ham, London, E6 2JA.

© 2026 7BlockLabs. All rights reserved.