Mint NFTs (CIP-68)
An overview of how to mint CIP-68 compliant NFTs and other native assets on Cardano using the Anvil API
What is CIP-68?
Cardano Improvement Proposal 68 (CIP-68) defines a way to mint native assets whose metadata can be updated after issuance. By storing the metadata on-chain and linking it to each token, creators can evolve NFTs—such as game items or collectibles—without burning and re-minting them.
How It Works
CIP-68 uses a dual-token system to achieve this functionality:
Reference Token (
label: 100
): This token holds the asset's metadata within an on-chain wallet (or smart contract script address).User Token (
label: 222
): This is the actual token held in a user's wallet. It acts as a pointer to the Reference Token and inherits its metadata.
Because the metadata lives with the Reference Token, creators can update an asset’s metadata simply by modifying that single token—no need to re-mint or burn the user-facing token.
Validation Approaches: Native Scripts vs. Smart Contracts
CIP-68's updatable metadata is managed by a Reference Token, which can be secured in two ways:
Metadata-Manager Wallet with a Native Script: This approach uses a standard wallet and a native script to control the Reference Token. It's straightforward and low-cost, making it ideal for projects that don't need complex on-chain logic. The trade-off is that it relies on trusting the wallet owner to manage metadata updates and token supply correctly.
Smart Contract (Plutus/Aiken): This method uses a full smart contract to govern the Reference Token. While someone still needs to trigger updates, the smart contract validates critical aspects like supply limits and reference token to user token ratios on-chain, requiring less trust than the wallet approach. It enables complex rules for metadata updates, supply caps, and other on-chain logic, but is more complex and expensive to develop and interact with.
The choice between them depends on your project's need for trustless validation versus simplicity and cost-effectiveness. If you need custom smart contract development, or are not sure which option to choose, we recommend reaching out at [email protected].
Minting a CIP-68 Asset
Minting a CIP-68 asset typically requires constructing a transaction using Anvil API's transactions/build
endpoint that creates both the Reference and User tokens simultaneously. Smart contracts usually enforce this simultaneous minting, but the tokens can technically be minted separately.
Prerequisites
Before you mint, make sure you have:
Anvil API key – Obtain one here to submit transactions.
Three wallets
Customer wallet – Pays fees and receives the NFT. Must have UTxOs to pay for fees.
Policy wallet – Signs the minting transaction due to the native script requirement. For secutiry reasons, make sure no UTxOs are on this address.
Metadata-manager wallet (or script address) – Holds the reference token and controls metadata updates. If you want to update the metadata, you need make sure you have some ADA on this address to pay for fees. Use our wallet CLI to create them.
NFT metadata – Name, description, image URL, etc., formatted per CIP-68.
Minting rules – Define who can sign and when:
For a metadata-manager wallet, see how to use native scripts.
For a smart contract, see how to use smart contracts.
Each guide below walks you through collecting these requirements.
Payload Example
Regardless of using a Metadata Manager Wallet or a Smart Contract, interacting with the API is almost the same. We need to start by building a transaction. This transaction defines what we want to submit to the blockchain. The native script, or smart contract defines how the transaction is validated.
Native Script vs Smart Contract Validation
Validation options
Native Script – Script that checks:
Signer – Required key-hash
Timing – Optional timelock Ideal for straightforward mint/burn rules. See Native Scripts.
Smart Contract (Plutus/Aiken) – Fully programmable logic. Verify payments, token ownership, game achievements, or any custom rule you write in Plutus or Aiken. Include a
scriptInteractions
section with validator, redeemer, and datum. See Smart Contract Documentation.
Choose based on complexity: • native scripts for quick, signature and time-lock validation rules • smart contracts for advanced, application-specific logic
{
// Customer wallet address receive any leftover UTxOs from the transaction.
"changeAddress": "addr_test1...",
// UTXOs from the customer wallet paying for fees (list of CBOR UTXOs)
"utxos": ["8282...", "8282..."],
"mint": [
{
// Reference Token - Label 100 -> Sent to Metadata Manager Wallet or Script Address
"version": "cip68",
//Name of the asset. Must be the same for both tokens
"assetName": { "name": "youruniquecip68asset", "format": "utf8", "label": 100 },
"metadata": {
"name": "Your NFT Name",
"image": "ipfs://your-image-hash",
"mediaType": "image/png",
"description": "A description for your CIP-68 asset"
},
"policyId": "8e024681ee83f54bd5f9a0334641...",
"quantity": 1,
"destAddress": "addr_test2..."
},
{
// User Token - Label 222 -> Sent to User Wallet
"version": "cip68",
// Asset name must be identical to the (100) token's assetName
"assetName": { "name": "youruniquecip68asset", "format": "utf8", "label": 222 },
"policyId": "8e024681ee83f54bd5f9a0334641...",
"quantity": 1,
"destAddress": "addr_test1..."
}
],
// The `preloadedScripts` array provides script data for minting authorization.
// - Native scripts: contains the actual native script
// - Plutus scripts: contains blueprint data (native script derived from blueprint)
// Optional but recommended for performance and latency. Required for new/unregistered scripts.
"preloadedScripts": [ /* ... script details ... */ ],
// The `scriptInteractions` array is ONLY required for smart contract validation.
// It is not used for the native script / Metadata-Manager Wallet approach.
// It is ONLY required for blueprints that are not registered yet.
// The full payload format is covered in the smart contract guide.
"scriptInteractions": [ /* ... validator, redeemer, datum ... */ ],
}
CIP-68 Guides
Deno & FetchLast updated
Was this helpful?