Skip to main content
This is a transparent NFT contract. Every mint, transfer, and approval publishes the involved keys, token IDs, and (where applicable) URIs to the public ledger. The full ownership graph and provenance history is observable.For NFTs where ownership must stay private, use Midnight’s shielded primitives — they’re built on UTXOs and ZK commitments and don’t leak the ownership graph.
Authentication uses witness-derived keypairs, not ownPublicKey(). See Access Control for the rationale.

The Contract

How It Works

This ERC721 implementation provides standard non-fungible token functionality with witness-derived caller authentication.

Dual ownership view

  • _owners: tokenId → owner (who owns this specific NFT)
  • _balances: owner → count (how many NFTs that owner holds)
Both maps are kept in sync inside _update, mint, and adminBurn. Keeping the count denormalized lets balanceOf answer in O(1) without iterating _owners.

Authorization layers

Three ways to be authorized to move a token:
  1. You are the owner: owner == caller.
  2. The owner has approved you for this specific token: _getApproved(tokenId) == caller.
  3. The owner has approved you as operator for all their NFTs: isApprovedForAll(owner, caller).
_checkAuthorized evaluates all three; the union is the standard ERC721 authorization rule.

Admin mint / burn

mint and adminBurn are admin-only — they wrap assertAdmin() around the underlying state mutation. To delegate mint authority (e.g. to a separate “minter” role), follow the role-based pattern from Access Control.

Token URI

setTokenURI(tokenId, uri) is callable by the current owner. The URI is typically a pointer to off-chain metadata (IPFS, Arweave, HTTPS) and is stored as Opaque<"string"> since the contract doesn’t need to read its bytes.

What’s not here

  • safeTransferFrom and onERC721Received — Compact has no contract-to-contract call yet, so the unsafe-vs-safe distinction is moot.
  • ERC721 events — Compact has no event/log primitive yet (contract log events per MIP-0002 are arriving in newer toolchains). For now, off-chain indexers must read state directly.
  • Enumerable extension — Compact has no iteration over Map keys; if you need tokenOfOwnerByIndex, track an auxiliary list explicitly.

Try It Yourself

1. Create project structure:
2. Save the contract at contracts/my-nft.compact.3. Compile:
4. Deploy with (adminPublicKey, name, symbol). The DApp supplies getAdminSecret() and getUserSecret() witnesses.

What’s Next

ERC20 Token

Create fungible tokens

Multi-Token (ERC1155)

Manage multiple token types