This standard formalizes ZKMeta, a minimal interface for contracts that verify or depend on zero-knowledge proofs. It defines how to expose a proof-system identifier, circuit identifier, circuit version, public-input schema (hash and URI), and verification-key URI. The goal is to enable interoperable wallet, relayer, explorer, rollup, and dApp tooling without prescribing any specific proof format.
Motivation
Zero-knowledge applications currently lack a standard way to publish the “shape” of their proofs. Projects using Groth16, Plonk, Halo2, or zkVMs each deploy custom artifacts, leaving integrators unable to reliably determine how to interact with a system without bespoke adapters.
Unlike previous attempts that aimed to standardize verification logic (e.g., ERC-1923), ZKMeta focuses solely on metadata discovery. By standardizing how to locate the verification key, public input schema, and circuit identity, this standard creates a “ZK ABI” that unlocks capabilities previously impossible in a fragmented ecosystem:
Universal ZK Explorers: Block explorers can automatically fetch input schemas to decode and display opaque proof inputs (e.g., “decoding” a ZK-tx’s public signals similar to how ABIs decode call data), rather than showing raw hex strings.
Decentralized Proving Markets: Solvers and prover networks can programmatically discover new jobs, fetch the required circuit artifacts (Wasm/zkey) via URI, and submit proofs without needing manual integration for every new dApp.
Automated Security Auditing: Security tooling can track circuitVersion changes to alert users if a protocol silently downgrades to an older, vulnerable circuit or changes constraints without announcement.
Specification
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119 and RFC 8174.
Interface
/// @title ZKMeta Interface
interfaceIZKMetadata{/// Emitted when the stored circuit metadata is modified.
eventCircuitMetadataUpdated(bytes32indexedcircuitId,uint64circuitVersion,bytes4proofSystem);/// Content-addressed identifier of the circuit definition artifact.
functioncircuitId()externalviewreturns(bytes32);/// Monotonically increasing semver-style version (major.minor -> uint32.uint32 packed into uint64).
functioncircuitVersion()externalviewreturns(uint64);/// Hash of the canonical public-input schema document.
functionpublicInputsSchemaHash()externalviewreturns(bytes32);/// URI of the canonical public-input schema document.
functionpublicInputsSchemaURI()externalviewreturns(stringmemory);/// URI for verification key discovery (content-addressed or URI with trailing #hash).
functionverificationKeyURI()externalviewreturns(stringmemory);/// Proof-system identifier (e.g., 0x0001 Groth16, 0x0002 Plonk, 0x0003 Halo2).
functionproofSystem()externalviewreturns(bytes4);}
Proof-System Registry
Identifier
Proof System
Notes
0x0001
Groth16
BN254 (e.g., snarkjs)
0x0002
Plonk
BN254 variant
0x0003
Halo2
Plonkish family
0x0004
zkSTARK
General STARK provers
0x0005
zkVM
e.g., RISC-V/Jolt/RISC Zero
Additional identifiers MUST be proposed in the discussion thread and MUST NOT collide. Unknown identifiers SHOULD be treated as unsupported by tooling.
Requirements
circuitId() MUST be a content hash (e.g., keccak256 or multihash) of the canonical circuit artifact used to derive the verification key.
circuitVersion() MUST increase upon any breaking change to constraints or public-input semantics. Projects SHOULD use the high 32 bits for major and low 32 bits for minor.
publicInputsSchemaHash() MUST match the document at publicInputsSchemaURI().
publicInputsSchemaURI() and verificationKeyURI() SHOULD be content-addressed (ipfs://, ar://, bzz://). If HTTPS is used, the URI MUST include a URL fragment containing the Keccak-256 hash of the resource content in hexadecimal format (e.g., https://example.com/schema.json#0x...) to ensure integrity.
CircuitMetadataUpdated SHOULD be emitted in the same transaction that makes new metadata observable.
Tooling MUST verify that publicInputsSchemaHash() matches the hash of the fetched schema document.
Tooling SHOULD verify that the verification key’s content hash matches verificationKeyURI()’s hash fragment.
CircuitMetadataUpdated MUST be emitted in the same transaction that makes new metadata observable to prevent indexer race conditions.
Recommendations
Content addressing
Prefer IPFS/Arweave/Swarm CIDs or HTTPS with #hash to ensure immutability and reproducibility.
Versioning discipline
Treat schema or circuit constraint changes as major; cosmetic or doc updates as minor.
Indexing
Indexers and explorers SHOULD subscribe to CircuitMetadataUpdated instead of polling getters.
Rationale
Hash + URI for schemas/keys enables automatic discovery while preserving integrity.
Event-based updates let tooling react to changes without polling.
Compact bytes4 proof-system code minimizes calldata while remaining extensible.
Proof-system neutrality avoids locking the ecosystem to any single proving stack.
Backwards Compatibility
Existing contracts may expose an adapter that implements IZKMetadata. Legacy systems can deploy a read-only facade or off-chain router for downstream tooling. No existing ERCs are modified.
Reference Implementation
// SPDX-License-Identifier: CC0-1.0
pragmasolidity^0.8.20;interfaceIZKMetadata{eventCircuitMetadataUpdated(bytes32indexedcircuitId,uint64circuitVersion,bytes4proofSystem);functioncircuitId()externalviewreturns(bytes32);functioncircuitVersion()externalviewreturns(uint64);functionpublicInputsSchemaHash()externalviewreturns(bytes32);functionpublicInputsSchemaURI()externalviewreturns(stringmemory);functionverificationKeyURI()externalviewreturns(stringmemory);functionproofSystem()externalviewreturns(bytes4);}contractZKMetadataAdapterisIZKMetadata{bytes32private_cid;uint64private_version;bytes32private_schemaHash;stringprivate_schemaURI;stringprivate_vkURI;bytes4private_ps;constructor(bytes32cid,uint64version,bytes32schemaHash,stringmemoryschemaURI,stringmemoryvkURI,bytes4proofSystemId){_cid=cid;_version=version;_schemaHash=schemaHash;_schemaURI=schemaURI;_vkURI=vkURI;_ps=proofSystemId;emitCircuitMetadataUpdated(_cid,_version,_ps);}functioncircuitId()externalviewreturns(bytes32){return_cid;}functioncircuitVersion()externalviewreturns(uint64){return_version;}functionpublicInputsSchemaHash()externalviewreturns(bytes32){return_schemaHash;}functionpublicInputsSchemaURI()externalviewreturns(stringmemory){return_schemaURI;}functionverificationKeyURI()externalviewreturns(stringmemory){return_vkURI;}functionproofSystem()externalviewreturns(bytes4){return_ps;}/// Example admin update; replace with proper access control in production.
function_adminUpdate(bytes32cid,uint64version,bytes32schemaHash,stringcalldataschemaURI,stringcalldatavkURI,bytes4proofSystemId)external{_cid=cid;_version=version;_schemaHash=schemaHash;_schemaURI=schemaURI;_vkURI=vkURI;_ps=proofSystemId;emitCircuitMetadataUpdated(_cid,_version,_ps);}}
Security Considerations
Schema Integrity
If tooling does not verify publicInputsSchemaHash() against the downloaded document, a malicious frontend or gateway could serve a modified schema. This would allow an attacker to mislabel public inputs (e.g., swapping “Token Amount” for “Nonce”), deceiving users about what the proof actually attests to.
Verification Key Mutability
Relying on HTTP URIs without hash fragments allows a server administrator to silently swap the verification key. This could enable the server admin to create fake proofs for a new (compromised) circuit while the contract still points to the old URI.
Indexer Race Conditions
If the CircuitMetadataUpdated event is not emitted atomically with the state change, off-chain indexers might read stale metadata values (e.g., an old verification key) while processing the update event, leading to widespread denial of service for proof generation.