Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
270 changes: 270 additions & 0 deletions sips/sip-038/sip-enft-private-layer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
# SIP-038: Standard Trait Definition for Commitment-Based Private Metadata (Encrypted NFTs)

## Preamble

SIP Number: 038
Title: Standard Trait Definition for Commitment-Based Private Metadata (Encrypted NFTs)
Authors: Xenitron nortinex@gmail.com, Bogachev ogres2009anton@gmail.com
Status: Accepted
Consideration: Technical
Type: Standard
Created: 2026-01-10
License: CC0-1.0
Sign-off:
Layer: Trait
Discussions-To: https://forum.stacks.org/t/encrypted-nfts-on-stacks-a-standard-trait-for-commitment-based-private-layers-enft/18560

# Abstract

This SIP proposes a minimal, composable trait interface for “Encrypted NFTs” (eNFTs): NFTs that commit on-chain to a private off-chain payload (“private metadata”) while keeping the payload itself off-chain. The standard defines:

1) A public commitment interface,
2) An optional owner-gated envelope descriptor for wallet user experience.
3) Indexer-friendly event conventions.

This enables interoperable private-metadata NFT patterns across contracts, wallets, marketplaces, and indexers without requiring consensus changes.


# Copyright

This SIP is made available under the terms of the CC0-1.0 license.
This SIP’s copyright is waived to the extent possible under law.


## Introduction

“Encrypted metadata / private metadata” NFTs on Stacks are currently implemented as project-specific patterns. This fragmentation makes it difficult for wallets, marketplaces, and indexers to support such NFTs uniformly.

This SIP standardizes a minimal interface for commitment-based private metadata on SIP-009 NFTs:

- A **public commitment** (hash) to the payload (or ciphertext), enabling anyone to verify integrity after reveal.
- An optional **owner-gated envelope descriptor** to guide wallet user experience (e.g., show “Reveal” only to the owner).
- A recommended **event format** so indexers can detect updates efficiently.

### Relationship to SIP-009

This trait is designed to be implemented **alongside** SIP-009-compliant NFT contracts, not as a replacement. An eNFT contract implements SIP-009 for ownership and transfer, and additionally implements this trait to expose commitment-based private metadata.

## Goals

- Define a reusable trait for commitment-based private metadata for eNFTs.
- Make private-layer patterns discoverable and composable for wallets/indexers.
- Keep the standard minimal, additive, and backward-compatible.

## Non-Goals

- Full on-chain confidentiality (not achievable in a public read-only model).
- Mandating a specific storage network, encryption scheme, or reveal flow.
- Defining a full game/puzzle “answer verification” protocol (out of scope).

**Note on encryption flexibility:** This SIP intentionally does not mandate a specific encryption scheme. Wallets and indexers interact only with the commitment layer — they verify `hash(payload) == on-chain commitment` and never perform decryption. Decryption occurs off-chain between the application and the asset owner, outside the trait scope. Different projects require different threat models (server-assisted reveal vs. client-side decryption with embedded key blob vs. proxy re-encryption). To prevent fragmentation, implementers using any non-default algorithm id (other than `u0`) **must** publish a stable, publicly available algorithm specification. Wallets **may** support only `u0` (SHA-256) and treat unknown algorithm ids as unsupported for verification while still supporting discovery.

# Specification

## 1. Terminology

- **Payload**: The private off-chain data (JSON, image, encrypted blob, etc.).
- **Ciphertext**: An encrypted payload (still considered “payload bytes” for commitment purposes).
- **Commitment**: A hash over the payload bytes (often ciphertext bytes) recorded on-chain.
- **Envelope**: A small off-chain descriptor used for UX/access (e.g., retrieval URI, encrypted key blob, signed URL descriptor).
- **Reveal**: Off-chain delivery of payload/envelope to the user; the client verifies integrity by comparing hash(payload_bytes) to the on-chain commitment.
- **Algo ID**: An integer identifier that indicates how the commitment hash is computed.
- **Version**: A monotonically increasing version number for commitment/envelope encoding.
- **Token-id**: The SIP-009 NFT token identifier (`uint`). Each token-id uniquely identifies a single NFT within a contract.

### 2. Commitment Semantics

Implementations **must** expose a commitment for a given token-id.

- The commitment **must** be computed exactly according to the declared algorithm id.
- The commitment **must** be stable: the same payload bytes must produce the same commitment.
- This SIP does not mandate a single algorithm, but **recommends**:
- `algo = u0` => SHA-256 over raw payload bytes

### Algorithm Registry (Informative)

| Algo ID | Algorithm | Notes |
|---------|-----------|-------|
| u0 | SHA-256 | Recommended default |
| u1-u99 | Reserved | For future standardization |
| u100+ | Application-defined | Projects may define custom algorithms |

**Wallet implementation guidance:** Wallets and indexers are not required to support all algorithm ids. A conforming wallet may support only `u0` (SHA-256) and display a warning or informational notice for tokens using unrecognized algorithms. The registry enables organic ecosystem convergence without requiring a new SIP for each algorithm.

**Notes**
- If the payload is JSON, implementers should treat the committed object as raw bytes of a canonical encoding chosen by the application (this SIP does not standardize canonicalization). Consumers must verify against the exact bytes they receive on reveal.

---

### 3. Trait Interface (Clarity)

To make “optional” functionality actually optional in Clarity, this SIP defines **two traits**:

- A **required** commitment trait (baseline interoperability).
- An **optional** owner-envelope trait (wallet UX extension).

Contracts may implement the baseline trait alone, or both traits.

#### 3.1 Required Trait: Commitment

```clarity
(define-trait enft-commitment-trait
(
;; Public: returns the commitment to the private payload (often ciphertext bytes)
(get-private-commitment (uint)
(response
(tuple
(commitment (buff 32)) ;; hash of payload bytes
(algo uint) ;; algorithm id (e.g., u0 = sha256)
(mime (optional (string-ascii 64))) ;; e.g. "application/json", "image/png"
(size (optional uint)) ;; payload size in bytes (optional)
(commitment-version uint) ;; commitment scheme version
)
uint
)
)
)
)
```

#### 3.2 Optional Trait: Owner-Gated Envelope (Wallet UX Extension)

```clarity
(define-trait enft-owner-envelope-trait
(
;; user experience oriented: returns an owner-gated envelope descriptor.
;; Implementations SHOULD check ownership for consistent wallet UX.
(get-owner-envelope (uint)
(response
(tuple
(envelope-uri (string-ascii 256)) ;; retrieval location (NOT a secrecy guarantee)
(envelope-hash (buff 32)) ;; integrity check for envelope blob/response
(algo uint) ;; algorithm id for envelope-hash
(envelope-version uint) ;; envelope scheme version
)
uint
)
)
)
)
```

**Normative guidance**
- Contracts that claim eNFT support **must** implement `enft-commitment-trait`.
- Contracts **may** implement `enft-owner-envelope-trait` to provide a standardized “Reveal” UX path.
- Wallets/indexers should treat the presence of `enft-owner-envelope-trait` as an optional capability.

---

### 3.3 Error Codes (Recommended)

Implementations **may** choose their own error codes. This SIP **recommends**:

- `err u404` => token-id not found / no commitment set
- `err u401` => not authorized for `get-owner-envelope` (only relevant if `enft-owner-envelope-trait` is implemented)


### 4. Events (Indexer Conventions)

When a private-metadata commitment is set or updated, contracts **should** emit a `print` event with a predictable structure:

```clarity
(print {
notification: "enft-update",
token-id: token-id,
commitment: commitment,
algo: algo,
commitment-version: commitment-version
})
```

If an envelope descriptor is set/updated, contracts **may** emit:

```clarity
(print {
notification: "enft-envelope-update",
token-id: token-id,
envelope-hash: envelope-hash,
algo: algo,
envelope-version: envelope-version
})
```

**Notes**
- Events are intended for indexers/wallets discovery and user interface updates.
- `envelope-uri` is intentionally excluded from the event for safety; wallets should fetch it via the read-only function if needed.
- Indexers should treat these events as hints and verify by reading the latest on-chain state as required.

---

## Backwards Compatibility

This SIP introduces no breaking changes. It is additive and does not modify existing SIP-009 NFT contracts. Projects can implement these traits alongside existing token URI / metadata patterns. Wallets and indexers can support eNFTs incrementally.

---

## Security Considerations

### Read-only visibility and “owner gating”
`get-owner-envelope` ownership checks are primarily for **wallet UX consistency**, not cryptographic secrecy. Read-only calls can be simulated by any party running a node; therefore, **confidentiality must not rely solely on on-chain gating**.

If the envelope or payload must remain private, it **should** be protected off-chain via:
- encryption (e.g., encrypt payload/key per owner), and/or
- access control (signed challenges, bearer tokens, short-TTL URLs).

### Integrity and server non-equivocation
Assuming collision-resistant hash functions, the on-chain commitment ensures the server cannot swap the underlying private payload after minting without being detected. Clients **must** verify `hash(payload_bytes) == commitment` after reveal.

### Phishing / malicious URIs
Wallets should treat envelope URIs as untrusted input and apply standard URL safety practices (origin warnings, link previews sandboxing, and user confirmation flows where appropriate).

### MIME type validation
The `mime` field is informational and does not instruct clients to execute content. Clients **should** validate MIME types before processing payloads and **should** reject executable types (e.g., `application/x-executable`, `application/x-msdownload`) by default. This follows standard content-security practices.

---

## Reference Implementation (Informative)

A reference contract can implement:
- a mapping `token-id -> commitment tuple`
- optionally a mapping `token-id -> envelope tuple`
- setters restricted to contract-defined roles (e.g., token owner or authorized operator)
- `print` events as specified above

---

## Related Work (Informative)

Commitments and commit–reveal schemes are widely used across blockchains to prevent equivocation. Some NFT ecosystems support “encrypted metadata” patterns, but these are often application-specific and lack a shared interface for wallets/indexers.

To the best of our knowledge, Stacks lacks a minimal, interoperable trait and event convention for commitment-anchored private metadata on SIP-009 NFTs; this SIP defines such a standard in a composable, non-consensus, and implementation-agnostic manner.

---

## Activation

No consensus changes are required. This SIP is considered
Active when all of the following conditions are met before
Bitcoin block height (H₀ + 12,960), where H₀ is the Bitcoin
block height at the time this SIP is merged, and 12,960 blocks
represents approximately 3 months:

1) A reference trait contract implementing this specification is deployed on Stacks mainnet.

2) At least one production NFT contract on mainnet implements the trait(s) exactly as specified in this SIP.

3) At least one ecosystem integration — indexer, wallet, or marketplace — demonstrates consumption of the interface,
proving interoperability independent of the authors.

A trait that follows this specification will be made available
on mainnet upon status update to activation-in-progress, and its deployed address will be
recorded here.

Until activation, the SIP remains Draft, and implementers may
ship experimental contracts under the proposed interface.

## References

[1] [SIP-009 ](https://github.com/stacksgov/sips/blob/main/sips/sip-009/sip-009-nft-standard.md)

[2] [Forum discussion thread](https://forum.stacks.org/t/encrypted-nfts-on-stacks-a-standard-trait-for-commitment-based-private-layers-enft/18560)