ERC-8382: Private Referable NFTs
Abstract This ERC extends ERC-721 with interoperable commitments to private NFT-to-NFT references. A token can publish one or more reference commitments without disclosing the referenced NFT, reference label, weight, or authorization material. Implementations expose a common discovery interface, may require a zero-knowledge or other privacy-preserving proof at mint time, and may support later selective disclosure of individual references. The core interface is proof-system-neutral. It does not require Groth16, a particular elliptic curve, Poseidon, a fixed number of references, or a specific authorization registry. Optional extensions standardize proof-gated minting, selective reveal, registration-snapshot owner authorization, canonical public policies, and rank-governed DAG insertion. Motivation ERC-721 standardizes ownership and transfer of unique tokens but does not standardize relationships between tokens. ERC-5521 adds public referring and referred relationships, enabling an NFT graph to be queried and indexed. Public references are useful for provenance, remix attribution, licensing, collaboration, and recommendation, but they can disclose commercially or socially sensitive information before the holder is ready to reveal it. A private reference should not be only an opaque hash. A relying contract may need evidence that: the hidden parent is an existing eligible NFT; a key registered by the parent NFT owner authorized the exact child and edge intent; the hidden label and weight satisfy a publicly auditable policy; the reference is not replayed; the insertion obeys a declared graph-ordering rule; and the committed reference can later be selectively disclosed and independently checked. Existing applications can implement these properties with custom contracts, but custom event formats and query surfaces prevent wallets, marketplaces, indexers, and provenance explorers from discovering private references consistently. This ERC standardizes the observable lifecycle while leaving the proof system and cryptographic profile replaceable. Private references may also be consumed by downstream royalty, revenue-sharing, licensing, reputation, or attribution systems. Cryptographic validity, owner authorization, and compliance with an application-defined policy establish that a reference satisfies the declared protocol conditions; they do not by themselves establish that the reference represents genuine creative contribution, an economically independent party, or an entitlement to payment. Applications that attach economic consequences to references therefore need separate payout-eligibility and contribution-authenticity rules. Specification The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119 and RFC 8174. Terminology Child: the ERC-721 token containing one or more private references. Parent: the token addressed by a reference opening. Reference commitment: a 32-byte commitment to a private reference and all profile-required binding context. Opening: the public semantic fields disclosed for a committed reference. Validation data: profile-specific bytes needed to verify an opening, such as nonces, key material, a proof, or an authentication path. Policy identifier: a 32-byte identifier for the policy under which a reference was accepted. Proof profile: a 32-byte identifier for a fully specified statement, encoding, verifier type, hash suite, and version. Authorization epoch: a registry-defined time or governance interval used to scope owner authorization. Rank namespace: a domain in which immutable token ranks are compared. Hidden state: a committed reference whose semantic opening has not been published through the selective-reveal interface. Revealed state: a committed reference whose opening has been verified and stored. General requirements A conforming core implementation: MUST implement ERC-721 and ERC-165 . MUST implement IERCXXXXPrivateReferences below. MUST assign each private reference a zero-based index local to the child token. MUST NOT change the commitment or policy identifier at an existing (tokenId, index) . MUST emit exactly one PrivateReferenceCommitted event when a reference is created. MUST return the same commitment and policy identifier through privateReference that were emitted at creation. MUST represent an existing unrevealed reference with status 0 and a revealed reference with status 1 . MUST revert when an index is greater than or equal to privateReferenceCount(tokenId) . MUST NOT expose the parent contract, parent token identifier, label, weight, secret nonce, owner public key, or authorization witness through the core interface while the reference remains hidden. MUST advertise the core interface through ERC-165 . A contract may create private references during mint or attach them through another application-defined lifecycle. If an implementation advertises IERCXXXXProofMint , it MUST follow the proof-gated mint requirements in this ERC. Interfaces and common data types The interfaces below inherit IERC165 so that supporting contracts can advertise compliance through ERC-165 . The core interface is mandatory; all other interfaces are optional extensions. // SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.0; struct ReferenceOpening { address parentContract; uint256 parentTokenId; uint256 parentRank; uint256 label; uint256 weight; } struct PrivateReferenceMintRequest { address recipient; bytes32 metadataHash; uint256 mintNonce; bytes32 childCommitment; bytes32 policyId; bytes32 authorizationRoot; uint64 authorizationEpoch; bytes32 rankNamespace; uint256 childRank; bytes32 externalNullifier; bytes32[] referenceCommitments; bytes32[] referenceNullifiers; } metadataHash is an application-selected digest bound by the proof profile. It does not replace ERC-721 tokenURI semantics. Core private-reference interface interface IERCXXXXPrivateReferences is IERC165 { /// status: 0 = hidden, 1 = revealed event PrivateReferenceCommitted( uint256 indexed tokenId, uint256 indexed index, bytes32 commitment, bytes32 indexed policyId ); event PrivateReferenceRevealed( uint256 indexed tokenId, uint256 indexed index, address indexed parentContract, uint256 parentTokenId, uint256 parentRank, uint256 label, uint256 weight ); function privateReferenceCount(uint256 tokenId) external view returns (uint256 count); function privateReference(uint256 tokenId, uint256 index) external view returns (bytes32 commitment, bytes32 policyId, uint8 status); function revealedReference(uint256 tokenId, uint256 index) external view returns (ReferenceOpening memory opening); } revealedReference MUST revert while the reference is hidden. It MUST return the opening accepted by the selective-reveal operation after the reference is revealed. Optional selective-reveal extension interface IERCXXXXSelectiveReveal is IERC165 { function canRevealPrivateReference( uint256 tokenId, uint256 index, address operator ) external view returns (bool); function revealPrivateReference( uint256 tokenId, uint256 index, ReferenceOpening calldata opening, bytes calldata validationData ) external; } An implementation advertising this extension: MUST verify that opening and validationData open the immutable commitment at (tokenId, index) under the active proof profile. MUST reject a second reveal for the same index. MUST reject a zero parentContract . MUST change status from hidden to revealed only after successful verification. MUST store the accepted ReferenceOpening and emit PrivateReferenceRevealed atomically. MUST define an authorization policy for reveal. At minimum, the current token owner and operators approved under ERC-721 SHOULD be supported unless the application deliberately uses a stricter policy. MUST NOT interpret caller authorization as proof that the opening is correct; authorization and commitment verification are separate checks. validationData is intentionally profile-specific. A profile MUST define its canonical encoding. For example, it may contain an edge nonce, approval nonce, owner public key, owner epoch, or a proof of opening. This ERC does not define when a hidden or revealed reference becomes economically eligible in a downstream application. Applications that attach royalties, revenue shares, licensing benefits, reputation, or other economic effects to references MUST define which lifecycle event controls eligibility, such as commitment time, authorization time, reveal time, listing time, or another application-defined event, and whether later reveal has retroactive economic effect. Optional proof-gated mint extension interface IERCXXXXProofMint is IERC165 { event PrivateReferenceMinted( uint256 indexed tokenId, address indexed recipient, bytes32 childCommitment, bytes32 indexed policyId, bytes32 proofProfile ); function proofProfile() external view returns (bytes32 profileId); function mintWithPrivateReferences( PrivateReferenceMintRequest calldata request, bytes calldata proof ) external returns (uint256 tokenId); } An implementation advertising this extension MUST, before minting: reject the zero recipient; check that referenceCommitments.length equals referenceNullifiers.length and is permitted by the proof profile; check that every reference nullifier is canonical, pairwise distinct within the request, and unspent; check that the child commitment has not previously been consumed if the profile defines child uniqueness; derive or validate all contract-bound public context required by the profile, including block.chainid , the accepting contract, action selector, proof-profile identifier, policy identifier, authorization epoch, and any external nullifier; validate the active authorization root and policy for the request epoch; validate any required rank reservation and rank namespace; verify proof against the exact canonical public input encoding defined by proofProfile() ; mark nullifiers and one-time commitments as consumed; mint the ERC-721 token, store every reference commitment and policy identifier, and emit one PrivateReferenceCommitted event per index; and emit PrivateReferenceMinted . All checks, replay-state updates, minting, storage writes, and events MUST be atomic. A failed operation MUST NOT consume a nullifier, reservation, or child commitment. The proof verifier MAY be embedded in the token contract or delegated to another contract. bytes proof is opaque to this ERC. The profile defines its format and verification algorithm. Proof profile requirements A profile identifier MUST commit to, or unambiguously identify, all consensus-relevant proof semantics, including: relation and schema versions; proof system and curve, if applicable; hash or commitment suite; domain-separation tags; field and byte encodings; public-signal names, order, widths, and canonical ranges; child commitment derivation; reference commitment derivation; nullifier derivation; owner-authorization statement; policy encoding; rank rules; batch-size rules; and verifier code or an immutable verifier identifier. Any change to one of these items MUST produce a different profile identifier. Implementations MUST NOT verify a proof produced for one profile as though it belonged to another profile. Profiles SHOULD derive commitments and nullifiers from a domain containing at least: (profileId, chainId, acceptingContract, actionSelector) Registry-scoped authorization leaves SHOULD additionally bind the registry address and authorization schema version. Optional canonical-policy extension interface IERCXXXXCanonicalPolicy is IERC165 { event CanonicalPolicyRegistered( bytes32 indexed policyId, uint64 indexed activationEpoch, bytes canonicalPolicy ); function canonicalPolicy(bytes32 policyId) external view returns ( bytes memory canonicalPolicy, uint64 activationEpoch, bool registered ); function isPolicyActive(bytes32 policyId, uint64 epoch) external view returns (bool); } An implementation advertising this extension: MUST make the canonical policy preimage publicly queryable. MUST document its canonical ABI encoding. MUST guarantee that policyId is the profile-defined digest of the returned preimage. MUST NOT change the preimage associated with a registered policyId . MUST expose enough information for an observer to determine whether the policy was active for a specified epoch. Publishing only an opaque accepted digest is not sufficient for this extension. Such a system may claim compliance with an accepted policy identifier, but not publicly auditable compliance with known policy fields. Policy compliance is not contribution attestation. A canonical policy may constrain admissible labels, weight ranges, namespaces, epochs, or other machine-checkable fields, but an in-range or otherwise policy-compliant value does not prove that the value faithfully represents real-world creative contribution or economic importance. Downstream applications that monetize labels or weights SHOULD use additional attestation, admission, moderation, dispute-resolution, or other application-specific mechanisms when such authenticity matters. Optional registration-snapshot owner-authorization extension interface IERCXXXXOwnerAuthorization is IERC165 { event OwnerAuthorizationRegistered( address indexed parentContract, uint256 indexed parentTokenId, bytes32 indexed authorizationLeaf, bytes32 root, uint64 epoch, bytes4 keyScheme, bytes publicKey, bytes32 rankNamespace, uint256 parentRank ); event OwnerAuthorizationRevoked( bytes32 indexed authorizationLeaf, uint64 indexed epoch ); event AuthorizationEpochAdvanced( uint64 indexed previousEpoch, uint64 indexed newEpoch ); function currentAuthorizationEpoch() external view returns (uint64); function isAuthorizationRootActive( bytes32 root, bytes32 policyId, uint64 epoch ) external view returns (bool); function registerOwnerAuthorization( address parentContract, uint256 parentTokenId, bytes4 keyScheme, bytes calldata publicKey, bytes32 rankNamespace ) external returns (bytes32 leaf, uint256 leafIndex, bytes32 newRoot); function revokeOwnerAuthorization( address parentContract, uint256 parentTokenId, bytes4 keyScheme, bytes calldata publicKey, bytes32 rankNamespace ) external returns (bytes32 leaf); } Registration MUST check the current ERC-721 owner through ownerOf(parentTokenId) . A profile MUST define how the parent contract, token identifier, public key, rank, rank namespace, epoch, chain, and registry are encoded in the authorization leaf. A root accepted for minting MUST be explicitly active for the current authorization epoch and applicable policy. An implementation MAY accept multiple activated roots within one epoch to preserve proof liveness during registry growth. If it does, activating a newer root MUST NOT silently deactivate older roots in that epoch. Roots from an older epoch MUST be rejected unless a separate carry-over or revocation relation is explicitly standardized by the profile. Revocation semantics MUST be documented. Epoch rollover revocation and immediate intra-epoch revocation are distinct. An implementation that only prevents future re-registration MUST NOT claim immediate invalidation of previously activated roots. Owner authorization establishes consent by the owner represented by the registry snapshot; it does not establish that the reference is economically independent, non-Sybil, or entitled to downstream compensation. In particular, an actor controlling both the child and parent assets may be able to satisfy authorization correctly. Applications that use authorized references for payments or other scarce benefits MUST treat authorization and economic eligibility as separate decisions. keyScheme identifies the signature/public-key encoding. A key scheme profile MUST define canonical public-key and signature validation. Elliptic-curve profiles MUST specify on-curve, non-identity, subgroup, and scalar-canonicality requirements. Optional ranked-DAG extension interface IERCXXXXRankedReferences is IERC165 { event ChildRankReserved( bytes32 indexed childPreCommitment, bytes32 indexed rankNamespace, uint256 childRank, bytes32 reservationId ); event TokenRankAssigned( address indexed tokenContract, uint256 indexed tokenId, bytes32 indexed rankNamespace, uint256 rank ); function rankOf( address tokenContract, uint256 tokenId, bytes32 rankNamespace ) external view returns (uint256 rank, bool assigned); function reserveChildRank( bytes32 childPreCommitment, bytes32 rankNamespace ) external returns (uint256 childRank, bytes32 reservationId); function rankReservation( bytes32 childPreCommitment, bytes32 rankNamespace ) external view returns ( uint256 childRank, bytes32 reservationId, bool active ); } An implementation advertising this extension: MUST assign at most one immutable rank to an asset in a rank namespace. MUST bind a reservation to the accepting adapter, child precommitment, and rank namespace. MUST prevent callers from substituting a different rank at mint. MUST consume the exact reservation atomically with mint. MUST enforce parentRank < childRank for every accepted ranked reference. MUST define who may assign ranks and prevent unauthorized pre-assignment or namespace griefing. MUST NOT claim arbitrary global dynamic acyclicity solely from the local inequality unless every relevant token has an immutable rank in the same namespace. A reservation is publicly linkable to the mint that consumes its child precommitment. Relayers or account-abstraction systems may reduce wallet-identity linkage, but they do not hide the protocol-level reservation-to-mint relation. ERC-165 requirement Implementations MUST return true for supportsInterface(type(IERCXXXXPrivateReferences).interfaceId) . Implementations MUST return true only for optional extension identifiers whose complete semantics they implement. Events and inherited functions are not included when calculating the interface identifiers. Additional interfaces SHOULD be forwarded through the implementation’s normal supportsInterface inheritance chain. Interface Interface identifier IERCXXXXPrivateReferences 0x9d2c065b IERCXXXXSelectiveReveal 0x15d38dfb IERCXXXXProofMint 0xa6a4cad2 IERCXXXXCanonicalPolicy 0x8f43c3aa IERCXXXXOwnerAuthorization 0x1a9dd556 IERCXXXXRankedReferences 0xce938bbe Key Components Structs ReferenceOpening contains the parent contract, parent token identifier, parent rank, label, and weight disclosed for one reference. PrivateReferenceMintRequest contains the public mint context, commitments, nullifiers, policy, authorization root and epoch, rank namespace, and child rank. Functions privateReferenceCount and privateReference provide batch-size-independent discovery of hidden or revealed references. revealedReference returns the immutable semantic opening after disclosure. revealPrivateReference verifies one indexed opening without revealing sibling references. mintWithPrivateReferences verifies a profile-specific proof and atomically mints the child token while consuming replay state. canonicalPolicy exposes the immutable policy preimage required for public semantic audit. registerOwnerAuthorization and revokeOwnerAuthorization manage registration-snapshot owner keys under documented epoch semantics. reserveChildRank , rankReservation , and rankOf support the optional ranked-DAG profile. Events PrivateReferenceCommitted announces a new indexed commitment without publishing its parent endpoint. PrivateReferenceRevealed publishes the verified opening for one index. PrivateReferenceMinted records the child commitment, policy, and proof profile used for mint. Policy, authorization, epoch, and rank events expose the governance transitions needed by provers and indexers. Reference lifecycle A proof-gated ranked implementation normally follows this lifecycle: A parent NFT owner registers an authorization key for the parent and current epoch. Governance or another specified mechanism activates one or more authorization roots and a canonical policy. A prospective child minter computes a child precommitment and reserves a rank. The parent owner signs an exact edge intent bound to the child commitment, policy, epoch, and fresh approval nonce. The prover constructs a proof that hidden parent eligibility, owner authorization, policy compliance, replay derivation, and rank ordering hold. mintWithPrivateReferences verifies the request and proof, consumes replay state and the reservation, mints the child, and stores commitments. Wallets and indexers discover hidden references through the core interface and commitment events. An authorized operator may later disclose one index. The contract verifies the opening and emits PrivateReferenceRevealed . After disclosure, an implementation may also publish a public relationship compatible with ERC-5521 . ERC-5521 interoperability Before reveal, a private reference has no public parent endpoint and therefore cannot populate an ERC-5521 referring or referred list without defeating privacy. After successful reveal, an implementation that also supports ERC-5521 MAY expose the reference through its ERC-5521 query surface. Such publication: MUST use the same parent contract and token identifier accepted by revealPrivateReference ; MUST NOT create a different semantic edge; MUST preserve any ERC-5521 authorization and cross-contract callback requirements; and SHOULD occur atomically with reveal when both interfaces are implemented by the same contract. This ERC does not require an unrevealed reverse index at the parent. A reverse index would reveal the hidden parent or require an additional privacy-preserving index protocol outside the scope of this ERC. Canonical encoding All bytes32 values are opaque at the core interface but canonical within a profile. When a profile maps a prime-field element into bytes32 , it MUST specify byte order and MUST reject non-canonical values greater than or equal to the field modulus. Addresses used inside field hashes MUST be encoded as their unsigned 160-bit value unless the profile specifies another unambiguous encoding. A uint256 token identifier split into limbs MUST specify limb width and order. Dynamic arrays MUST be bound with an unambiguous length and ordered index. A profile MUST NOT accept two byte encodings for the same public statement. 1 post - 1 participant Read full topic
DeFi Intel is an entity-graph aggregator: we curate, tag and link crypto news to a typed knowledge graph of protocols, tokens, people and incidents. We do not republish the full article body. Use the link above to read the original report at Ethereum_magicians.
Want the full article?
Continue reading on Ethereum_magicians →