ERC-8084 - Zero-knowledge proof metadata

Created 2025-11-10
Status Draft
Category ERC
Type Standards Track
Authors

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), 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:

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
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

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

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