---
eip: 8084
title: Zero-knowledge proof metadata
description: ZKmeta is a method to discover ZK circuit identifiers, semantic versions, verification keys, public input schemas.
author: cococay (@zwowo1997)
discussions-to: https://ethereum-magicians.org/t/eip-0000-standardized-zk-metadata-interface/26511
status: Draft
type: Standards Track
category: ERC
created: 2025-11-10
---

## Abstract

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](./eip-1923.md)), 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](https://www.rfc-editor.org/rfc/rfc2119) and [RFC 8174](https://www.rfc-editor.org/rfc/rfc8174).

### Interface

```solidity
/// @title ZKMeta Interface
interface IZKMetadata {
    /// Emitted when the stored circuit metadata is modified.
    event CircuitMetadataUpdated(bytes32 indexed circuitId, uint64 circuitVersion, bytes4 proofSystem);

    /// Content-addressed identifier of the circuit definition artifact.
    function circuitId() external view returns (bytes32);

    /// Monotonically increasing semver-style version (major.minor -> uint32.uint32 packed into uint64).
    function circuitVersion() external view returns (uint64);

    /// Hash of the canonical public-input schema document.
    function publicInputsSchemaHash() external view returns (bytes32);

    /// URI of the canonical public-input schema document.
    function publicInputsSchemaURI() external view returns (string memory);

    /// URI for verification key discovery (content-addressed or URI with trailing #hash).
    function verificationKeyURI() external view returns (string memory);

    /// Proof-system identifier (e.g., 0x0001 Groth16, 0x0002 Plonk, 0x0003 Halo2).
    function proofSystem() external view returns (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

1. **Content addressing**  
   Prefer IPFS/Arweave/Swarm CIDs or HTTPS with `#hash` to ensure immutability and reproducibility.

2. **Versioning discipline**  
   Treat schema or circuit constraint changes as *major*; cosmetic or doc updates as *minor*.

3. **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

```solidity
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.20;

interface IZKMetadata {
    event CircuitMetadataUpdated(bytes32 indexed circuitId, uint64 circuitVersion, bytes4 proofSystem);
    function circuitId() external view returns (bytes32);
    function circuitVersion() external view returns (uint64);
    function publicInputsSchemaHash() external view returns (bytes32);
    function publicInputsSchemaURI() external view returns (string memory);
    function verificationKeyURI() external view returns (string memory);
    function proofSystem() external view returns (bytes4);
}

contract ZKMetadataAdapter is IZKMetadata {
    bytes32 private _cid;
    uint64  private _version;
    bytes32 private _schemaHash;
    string  private _schemaURI;
    string  private _vkURI;
    bytes4  private _ps;

    constructor(
        bytes32 cid,
        uint64 version,
        bytes32 schemaHash,
        string memory schemaURI,
        string memory vkURI,
        bytes4 proofSystemId
    ) {
        _cid = cid;
        _version = version;
        _schemaHash = schemaHash;
        _schemaURI = schemaURI;
        _vkURI = vkURI;
        _ps = proofSystemId;
        emit CircuitMetadataUpdated(_cid, _version, _ps);
    }

    function circuitId() external view returns (bytes32) { return _cid; }
    function circuitVersion() external view returns (uint64) { return _version; }
    function publicInputsSchemaHash() external view returns (bytes32) { return _schemaHash; }
    function publicInputsSchemaURI() external view returns (string memory) { return _schemaURI; }
    function verificationKeyURI() external view returns (string memory) { return _vkURI; }
    function proofSystem() external view returns (bytes4) { return _ps; }

    /// Example admin update; replace with proper access control in production.
    function _adminUpdate(
        bytes32 cid,
        uint64 version,
        bytes32 schemaHash,
        string calldata schemaURI,
        string calldata vkURI,
        bytes4 proofSystemId
    ) external {
        _cid = cid;
        _version = version;
        _schemaHash = schemaHash;
        _schemaURI = schemaURI;
        _vkURI = vkURI;
        _ps = proofSystemId;
        emit CircuitMetadataUpdated(_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.

## Copyright

Copyright and related rights waived via [CC0](../LICENSE.md).
