What an NFT is on Cardano
Cardano tokens are native assets. The ledger tracks them directly, in the same UTxOs that hold ADA, so sending an NFT is an ordinary transaction. No token contract runs when it moves.
Every token is identified by two parts:
- Policy ID: the 28-byte (56 hex character) hash of the minting policy script. Because it is a hash of the rules, the rules are bound to the token forever.
- Asset name: up to 32 bytes that distinguish tokens under one policy. It can be empty.
Together they form the asset ID, policyId.assetName. An NFT is simply a token whose quantity is exactly 1 and whose policy guarantees that no second unit can ever be minted. The same asset name under a different policy is a different token.
The asset fingerprint (CIP-14)
Wallets and explorers often show a short asset1… string. CIP-14 defines it as a bech32 encoding of the blake2b-160 hash of policy ID and asset name concatenated. It is a convenient label, not a separate identity: two fingerprints match only if policy and name both match.
The policy ID is the authenticity anchor. Names, images and descriptions can be copied by anyone. Only the policy ID proves which issuer and which rules produced a token.
Minting policies: who can mint, when, and how many
Whenever a transaction mints or burns tokens under a policy, that policy's script runs and must succeed. Positive quantities mint; negative quantities burn. A policy can allow one and forbid the other.
Native scripts: signatures and time
The simplest policies need no smart contract. A native script combines signature and time conditions. The standard NFT pattern requires the issuer's signature and only works before a chosen slot:
{
"type": "all",
"scripts": [
{ "type": "before", "slot": 90000000 },
{ "type": "sig", "keyHash": "<policy key hash>" }
]
}
Once that slot passes, nobody can mint again under the policy, so the supply is provably fixed. Set the slot deliberately: a slot already in the past makes the policy unusable from the start.
Plutus policies: rules as code
When signatures and time are not enough, the policy is a validator written in a language such as Aiken and compiled to Plutus. Common patterns:
- One-shot: the policy requires one specific UTxO to be spent. A UTxO can be spent only once, so the policy can succeed only once in history. This is the protocol-level way to guarantee uniqueness.
- Parameterized: values such as that UTxO are baked into the script at build time, so each configuration gets its own policy ID.
- Stateful (state thread): a one-shot mint creates a single state token held at a script address; every later mint must spend and advance that state. This lets a finite, pre-committed collection be issued across many transactions. Data is Beautiful uses this pattern (see section 11).
| Policy | What it guarantees | What it does not |
|---|---|---|
| Signature only (native) | Only the key holder can mint or burn | Any supply limit: the key can mint again at any time |
| Signature + before slot (native) | No minting after the slot, so supply is fixed once it passes | What was minted before the deadline, or in what order |
| One-shot (Plutus) | Exactly one minting transaction, ever | Anything after that single transaction; the whole collection must fit in it |
| State thread / finite registry (Plutus) | A committed list of assets, each minted at most once, in the order the script allows, across many transactions | Anything the script does not check (metadata, for example) |
How a collector can check that a policy is closed
- Native script: get the script from the creator's own site, confirm its hash equals the policy ID, and confirm the
beforeslot is in the past. - Plutus policy: get the published source and blueprint, rebuild it, apply the published parameters, and confirm the resulting hash equals the policy ID. With Aiken that is
aiken build,aiken blueprint applyandaiken blueprint policy. Then read the code, or rely on an independent review, to understand what it allows.
Metadata standards: CIP-25, CIP-68 and CIP-27
CIP-25: metadata in the minting transaction
CIP-25 (the media token metadata standard) attaches name, image and other properties to the transaction that mints the token, under metadata label 721. Its rules:
nameandimageare required.imagemust be a full URI with a scheme, such asipfs://….mediaType,descriptionandfilesare optional; inside eachfilesentry,mediaTypeandsrcare required.- Every ledger metadata string is limited to 64 bytes. Longer values, such as long URIs, become an array of chunks that viewers join back together.
- Version 1 uses the policy ID and asset name as text map keys (the asset name must be UTF-8). Version 2 (
"version": 2) uses their raw bytes as keys, which suits asset names that are not readable text.
{
"721": {
"<policy_id>": {
"<asset_name>": {
"name": "Plate 1",
"image": ["ipfs://", "bafy…"],
"mediaType": "image/png",
"description": "One of one.",
"files": [{ "name": "Plate 1", "mediaType": "image/png", "src": ["ipfs://", "bafy…"] }]
}
},
"version": 2
}
}
In version 2 the two keys shown as placeholders are raw byte strings in the actual transaction, not hex text. Because the metadata lives in the minting transaction, it cannot be changed later.
CIP-68: metadata in a datum
CIP-68 mints two tokens under the same policy: a reference NFT (label 100) locked in an output whose datum holds the metadata, and a user token that the owner holds (label 222 for NFTs; 333 and 444 are the fungible and rich-fungible classes). If the reference NFT sits at a script address, that script decides whether and how the metadata can be updated. Scripts can also read the metadata on chain, which CIP-25 metadata cannot offer.
The labels follow CIP-67: each label is a 4-byte prefix at the start of the asset name. The reference NFT for label 100 starts with 000643b0 and a 222 user token starts with 000de140. That leaves 28 of the 32 asset-name bytes for the name itself.
| If you need | Consider |
|---|---|
| Fixed metadata written once at mint | CIP-25 |
| Metadata that can be updated under rules you define | CIP-68 with the reference NFT at a script |
| Metadata that other scripts can read on chain | CIP-68 |
| The whole 32-byte asset name for your own data (a hash, for example) | CIP-25 version 2, since CIP-68 labels use 4 bytes |
CIP-27: royalties
CIP-27 records a royalty rate and payout address under label 777, written to a token with an empty asset name under the collection's own policy. Marketplaces are told to honour only the first minted royalty token, so it must be minted before any other asset under that policy. After it is minted, burning it is recommended but not required. This is a community convention that marketplaces choose to follow; the ledger does not enforce payment.
Check marketplace support yourself. We have not verified which marketplaces display CIP-25 version 2 or CIP-68 tokens, or which honour CIP-27. Read each marketplace's own documentation and confirm with a test mint on preprod before you launch.
Media and storage
The token itself only carries a pointer to its image, so where that image lives decides how long the NFT stays viewable.
- Use a scheme. CIP-25 requires
ipfs://<CID>(orar://for Arweave), never a bare content identifier. Viewers may not find a bare CID. - Never split inside a CID. When a URI is longer than 64 bytes, split it into array chunks such as
["ipfs://", "<CID>"]. CIP-25 lists a CID cut in half as invalid. - CIDv0 or CIDv1. CIDv0 strings start with
Qm, use base58 and a fixed hash and codec. CIDv1, usuallybafy…in base32, describes its own hash and codec and is the more future-proof choice. Both are valid in anipfs://URI. - Pinning is what keeps it alive. A CID only names the content. Someone has to keep serving the bytes, so budget for pinning with more than one provider, or keep the bytes yourself.
- Fully on-chain media is possible with a
data:URI (base64 needs thedata:<mime_type>;base64,prefix). It must fit inside the 16,384-byte transaction together with everything else, so it only suits small SVGs and similar files. - Record a hash. Putting a SHA-256 of the image bytes in the metadata lets anyone check later that a gateway served the original file. Data is Beautiful records image hashes this way.
Costs and limits
These are live Cardano mainnet protocol parameters (Koios, epoch 657, checked 2026-09-26). Governance can change them, so read them from a provider when you build.
- Max transaction size
- 16,384 bytesEverything: inputs, outputs, scripts, witnesses and metadata.
- Max execution per transaction
- 16.5M memory / 10B stepsThe budget for all Plutus scripts in one transaction.
- Minimum ADA per output
- (160 + output bytes) × 4,310Lovelace per byte, per CIP-55.
- Transaction fee
- 44 × bytes + 155,381Lovelace, plus Plutus execution and reference-script fees.
| Parameter | Mainnet value | Why it matters |
|---|---|---|
| Execution prices | 0.0577 per memory unit, 0.0000721 per step | Plutus policies pay for the memory and CPU steps they use |
| Reference-script fee | 15 lovelace per byte (min_fee_ref_script_cost_per_byte) | Applies to scripts a transaction reads from a reference UTxO |
| Collateral | 150% of the fee, at most 3 collateral inputs | A Plutus transaction must post collateral, which it loses only if a script fails on chain |
| Max value size | 5,000 bytes | Caps how many different tokens one output can hold |
| Metadata strings | 64 bytes each | Longer text must be chunked (see CIP-25) |
| Asset name | 0 to 32 bytes | Set by the ledger rules |
Worked examples
An ADA-only output at a base address encodes to 65 bytes, so it needs at least (160 + 65) × 4,310 = 969,750 lovelace, about 0.97 ADA. The same output holding one NFT with a 32-byte name encodes to 133 bytes and needs 1,262,830 lovelace, about 1.26 ADA. That ADA travels with the NFT; it is not a fee. (Output sizes computed with the Cardano multiplatform library on 2026-09-27; wallets calculate this for you.)
One Data is Beautiful issuance, measured on a full-ledger emulator on 2026-09-26, with the Plutus script attached and a 1,618-byte CIP-25 version 2 metadata envelope:
| Measure | Value |
|---|---|
| Unsigned transaction size | 4,152 bytes (about a quarter of the limit) |
| Fee | 368,923 lovelace, about 0.37 ADA, covering the two signatures added afterwards |
| Script memory | 226,828 (mint) + 41,793 (spend) = 268,621, about 1.6% of the per-transaction memory limit |
| Script steps | 70,223,334 (mint) + 13,972,096 (spend) |
The fee checks out against the formula: the size component (44 × bytes + 155,381, with each signature adding roughly 100 bytes) plus execution priced at the rates above.
Wallets and signing
Browser wallets expose a standard bridge to web pages, CIP-30. A minting site uses a small set of calls:
| Call | What it does |
|---|---|
cardano.<wallet>.enable() | Asks the user to connect the wallet to the site |
getNetworkId() | 0 for testnets, 1 for mainnet |
getUtxos(), getChangeAddress() | The wallet's spendable outputs and where change should go, used to build the transaction |
signTx(tx, partialSign) | Asks the user to sign. With partialSign true the wallet signs what it can and returns only its signatures (a witness set) |
submitTx(tx) | Submits a fully signed transaction through the wallet |
What to check before you sign
- The network: a testnet mint should never be signed on a mainnet wallet, and the reverse.
- What leaves your wallet: the ADA amount and any tokens in the outputs.
- What is minted: the policy ID and asset name should match what the creator publishes.
- Your recovery phrase is never needed to mint or to connect. Anyone asking for it is trying to take your funds.
Cardano wallets include Lace, Eternl, VESPR and Yoroi. To connect to a minting site, a wallet needs a browser extension or in-app browser that supports CIP-30. Install wallets only from their official sites.
Tooling
| Tool | Use it for | Notes |
|---|---|---|
| cardano-cli | Native-script policies and manual transactions | Builds, signs and submits from the command line; the Cardano developer portal walks through a time-locked NFT mint |
| Aiken | Writing Plutus V3 policies and validators | Built-in unit and property tests; aiken build writes a CIP-57 blueprint (plutus.json) |
| Lucid Evolution | Building transactions in TypeScript | Includes an emulator, and by default evaluates Plutus scripts locally while building |
| Koios | Chain data and submission | Public API, free tier without a key; mainnet, preprod and preview endpoints |
| Blockfrost | Chain data, submission and IPFS pinning | Hosted API with a project key |
Provider endpoints you will use
- Submit: Koios
POST /submittxand BlockfrostPOST /tx/submit. Both take raw CBOR bytes withContent-Type: application/cbor. - Evaluate scripts: Blockfrost
POST /utils/txs/evaluate; Koios passes evaluation through to Ogmios atPOST /ogmios. - UTxOs: Koios
POST /address_utxos; BlockfrostGET /addresses/{address}/utxos. - Protocol parameters: Koios
POST /epoch_params; BlockfrostGET /epochs/latest/parameters.
Koios endpoints are RPC style (JSON bodies, mostly POST); Blockfrost uses REST paths. The preprod bases are https://preprod.koios.rest/api/v1 and https://cardano-preprod.blockfrost.io/api/v0.
Blueprints and multi-purpose scripts
CIP-57 defines the blueprint format that Aiken emits: each validator's compiled code, hash and parameters. Under CIP-69 a single Plutus V3 script can serve several purposes. The same script hash can be both a minting policy and a spending address, which is how a policy can guard its own state.
Pin your compiler. Aiken v1.1.24 and stdlib v4.0.0 were both released on 26 September 2026, and stdlib v4.0.0 renames the Value type to Assets. We build with Aiken v1.1.23 and stdlib v3.1.0, and move to newer versions deliberately. Whatever you choose, pin exact versions and record them with the blueprint.
Testing before anything is real
Test in three layers: fast unit tests, then a full-ledger emulator, then a public testnet.
- Unit tests against hand-built transactions, in Aiken itself. Fast, and good for covering every branch.
- Emulator tests that build real transactions with fees, minimum ADA, collateral and Plutus evaluation, for example with Lucid Evolution's emulator.
- Preprod or preview, the public testnets, with test ADA from the official testnet faucet. Only here do you see what real wallets, explorers and marketplaces display.
A lesson from our own build. In Aiken v1.1.23, one of our checks compared the minted tokens with minted == [Pair(name, 1)]. The unit tests passed because they built values in memory. In the emulator, with ledger-built data, the same comparison returned false and every valid mint was rejected. Pattern matching fixed it. Never accept unit tests alone: every branch must also pass the emulator.
Negative tests worth writing
A policy is only as good as what it refuses. Each of these should be rejected by the script itself, not only by your own front end:
- Minting out of order, or skipping ahead
- Minting the same asset twice
- Minting an asset that is not in the collection
- Minting quantity 2
- Minting without the required signatures
- Moving or stealing the state output, including through a burn
- Initializing the collection a second time
- Minting after the collection is closed
You can try our policy on preprod with your own wallet at the Data is Beautiful test mint.
Launch checklist
What should be settled, in writing, before a mainnet mint:
- Policy design decided, and reviewed by someone other than its author.
- Supply frozen: the exact list of assets or the closing slot, published before minting starts.
- Metadata validated byte-for-byte against the standard you chose, including chunking and key encoding.
- Media pinned and hashed, with more than one copy, and the hashes recorded in the metadata.
- Signing-key custody: who holds the keys, how many signatures are required, and how they are backed up.
- Preprod rehearsal of the whole lifecycle: initialization, several mints, closing, and a burn.
- Reference-script deployment, if you use one, so that each mint does not carry the full script.
- Marketplace display checks on the platforms you care about, using a test mint.
- A public verification page on your own domain that states the policy ID and how to check it.
- An incident plan: what you pause, what you tell collectors, and how you recover if a mint fails or a key is exposed.
Safety for collectors and creators
- Verify the policy ID on the creator's own website before buying. Look-alike collections can copy every name and image, but not the policy ID.
- Read what you sign. Check the network, the ADA and tokens leaving your wallet, and the policy of anything being minted.
- Never share a recovery phrase. No mint, airdrop or support agent needs it.
- Know whether the policy is locked. A signature-only policy can mint more at any time; a time-locked or one-shot policy cannot.
- Burning is permanent. If a policy allows holders to burn, a burned token is gone. Whether it could be minted again depends entirely on the policy.
- Creators: keep policy keys off internet-connected machines where you can, and never reuse a policy key for other purposes.
How Data is Beautiful mints
Data is Beautiful turns Cardano blocks into plates of art. Each NFT's asset name is the block's own 32-byte hash, so the collection needs CIP-25 version 2 and a policy that can issue a fixed list over many transactions. Our design:
- One Plutus V3 policy per finite volume. The script is parameterized by a one-shot seed UTxO, the ordered registry, its size and the issuer keys, so the policy ID commits to all of them.
- The ordered registry is a hash chain:
chain([]) = emptyandchain([n, ..rest]) = blake2b-256(n ++ chain(rest)). The policy stores only the chain of the unissued remainder. - One state token. Initialization spends the seed and locks a single state token with the full chain and a cursor of 0.
- Each mint reveals the next block hash. The script checks that hashing it with the rest of the chain matches the stored commitment, mints exactly one unit, and advances the cursor. Skipping, repeating, substituting or minting two units all fail.
- Closed forever after the last plate. The final state cannot advance, and the seed cannot be spent again to restart.
- Holders may burn their plates. Burning never touches the state, so a burned plate can never be minted again.
- Optional CIP-27 royalty token, allowed only at initialization so that it is the policy's first mint.
- Issuer custody is a required number of signatures from a set of keys (m-of-n).
- Metadata is CIP-25 version 2 with raw-byte keys. The script cannot see metadata, so the minting software checks it byte-for-byte before signing.
The policy passes 24 Aiken unit tests and 6 lifecycle tests on a full-ledger emulator, including every negative case listed in section 8.
Status. Mainnet minting is not open. Signing-key custody, an independent review of the policy and the launch terms have not been decided. Nothing on this page is an offer to sell.
Glossary
| Term | Meaning |
|---|---|
| UTxO | An unspent transaction output: ADA and tokens at an address, which a transaction spends whole and replaces with new outputs |
| Policy ID | The 28-byte hash of the minting policy script; the part of a token's identity that proves who could mint it |
| Asset name | Up to 32 bytes that distinguish tokens under one policy |
| Fingerprint | The asset1… label from CIP-14: a bech32 blake2b-160 hash of policy ID and asset name |
| Datum | Data attached to an output, which scripts can read; CIP-68 stores metadata there |
| Redeemer | The argument a transaction passes to a script, such as which action to take |
| Collateral | ADA a Plutus transaction pledges, and loses only if a script fails on chain |
| Reference script | A script stored in an output that other transactions can use without including its bytes |
| CIP | Cardano Improvement Proposal: the community process that publishes standards such as CIP-25 and CIP-30 |
Sources
Primary sources used for this guide. CIP pages were read from their original text.
- CIP-25, Media Token Metadata Standard (retrieved 2026-09-26)
- CIP-68, Datum Metadata Standard (retrieved 2026-09-26)
- CIP-67, Asset Name Label Registry (retrieved 2026-09-26)
- CIP-27, CNFT Community Royalties Standard (retrieved 2026-09-26)
- CIP-30, Cardano dApp-Wallet Web Bridge (retrieved 2026-09-26)
- CIP-14, User-Facing Asset Fingerprint (retrieved 2026-09-26)
- CIP-57, Plutus Contract Blueprint (retrieved 2026-09-26)
- CIP-69, Plutus Script Type Uniformization (retrieved 2026-09-26)
- CIP-55, minimum UTxO calculation (retrieved 2026-09-26)
- Conway ledger CDDL: asset name and metadata sizes (retrieved 2026-09-27)
- Cardano developer portal: what are native tokens (retrieved 2026-09-27)
- Cardano developer portal: minting policies (retrieved 2026-09-27)
- Cardano developer portal: mint an NFT (retrieved 2026-09-27)
- Aiken installation, validators, tests and design patterns (retrieved 2026-09-26)
- Aiken releases (retrieved 2026-09-26)
- Lucid Evolution documentation (retrieved 2026-09-26)
- Koios API, including the live
epoch_paramsquery for epoch 657 (retrieved 2026-09-26) - Blockfrost API documentation (retrieved 2026-09-26)
- IPFS content addressing and CIDs (retrieved 2026-09-26)
- Cardano testnet faucet (retrieved 2026-09-26)
