Module M006
Minting Policies and NFT Development
DirectEd x CATS Hackathon
Aiken Development Workshop Series
Duration: 2 hours
Format: 1 hour lecture + 1 hour exercises
Minting Policies and NFT Development
DirectEd x CATS Hackathon
Aiken Development Workshop Series
Duration: 2 hours
Format: 1 hour lecture + 1 hour exercises
Until now, we've focused on spending validators. Now we shift to minting policies - validators that control token creation and destruction!
| Aspect | Spending Validator | Minting Policy |
|---|---|---|
| Purpose | Controls when UTxOs can be spent | Controls when tokens are minted/burned |
| Has Datum? | ✅ Yes - state attached to UTxO | ❌ No - no UTxO to attach to |
| Identifier | Script Address | PolicyId (script hash) |
| Parameters | datum, redeemer, own_ref, tx | redeemer, policy_id, tx |
| Real-World Analogy | Bank vault (controls locked funds) | Central bank (controls money supply) |
Minting policies have a different signature than spending validators.
Every token on Cardano has a unique identity made of two parts:
Hash of minting policy
Identifies the "collection"
Token identifier (ByteArray)
Identifies specific token
The tx.mint field shows what tokens are being created or destroyed.
For NFTs, we need guaranteed uniqueness - each token minted only ONCE, ever.
Problem: Owner can sign multiple transactions, minting the same token name repeatedly!
Use OutputReference - a UTxO that can only be spent ONCE.
An OutputReference is a unique identifier for every UTxO on Cardano.
Parameterize minting policy with a specific OutputReference that must be spent.
Alice has a UTxO she owns at tx_hash "abc123..." output index 0
Compile one_shot policy with that specific OutputReference as parameter
Transaction MUST include that UTxO as input + mint operation
✓ UTxO "abc123#0" is in inputs → Minting succeeds!
❌ UTxO "abc123#0" was already spent! Cannot include it again → Minting FAILS
How do we create an NFT collection with sequential numbering?
We want: "MyCoolNFT (0)", "MyCoolNFT (1)", "MyCoolNFT (2)", ...
Problem: How do we track the counter? Minting policies have no datum!
Minted exactly once, serves as reference token
Holds Oracle NFT + datum with collection state (count, price, admin)
Checks Oracle via reference input, mints with current count
The Oracle NFT is minted exactly once and serves as a reference token.
The Oracle validator holds the Oracle NFT and tracks collection state in its datum.
Reference inputs let you READ a UTxO without spending it!
Collection minting policy can READ the Oracle state (current count) via reference input, while the Oracle validator handles the actual spending and state update.
The actual NFT collection policy references the Oracle to get the current count.
One-time transaction mints Oracle NFT, locks it at Oracle address with initial datum (count: 0)
Inputs: Oracle UTxO (spending), User wallet
Reference Inputs: Oracle UTxO (read count = 0)
Outputs: Oracle back (count = 1), NFT "Collection (0)" to user, 5 ADA fee
Mint: +1 "Collection (0)"
Same pattern: Read count = 1, mint "Collection (1)", update to count = 2
Each mint reads current count, creates sequential NFT, increments Oracle
Token names are generated dynamically using the collection name + count.
Remember double-satisfaction from M004? It can happen here too!
Two Oracle inputs could both validate with same output!
The three validators are connected through parameters.
Parameter: utxo_ref (OutputReference)
Output: PolicyId (used by Oracle validator)
Parameter: oracle_nft_policy (PolicyId from Layer 1)
Output: Script Address (where Oracle NFT is locked)
Parameters: collection_name + oracle_nft_policy (PolicyId from Layer 1)
Uses: References Oracle to read state
Minting policies can also handle burning (destroying tokens).
| Minting | quantity > 0 |
| Burning | quantity < 0 |
Comprehensive testing ensures your NFT system is secure and functional.
• One-shot mint succeeds with UTxO
• Mint fails without UTxO
• Burn succeeds
• Counter increments correctly
• Fee payment enforced
• Value preservation
• Single input validation
• Admin stop with burn
• Correct token name passes
• Wrong token name fails
• Missing Oracle reference fails
• Burn succeeds
• Complete mint workflow
• Sequential minting
• State consistency
Time to build your NFT system! 🎨
Build basic minting policy with signature authorization
Implement OutputReference-based one-time minting
Create policy that validates token name prefix
Build mini Oracle NFT + Oracle validator with counter
Ensure mock transaction includes the exact UTxO ref used as parameter
Use correct policy_id when calling assets.tokens(tx.mint, policy_id)
Use proper casting: expect output_datum: OracleDatum = output_datum_data
Test token name generation function separately first
You can now:
✅ Understand minting policies vs spending validators
✅ Implement one-shot minting with OutputReference
✅ Understand the Oracle pattern for state management
✅ Create parameterized minting policies
✅ Use reference inputs effectively
✅ Build complete NFT collection systems
✅ Connect multiple validators through parameters
Three Validators Working Together
• One-shot minting (OutputReference)
• Mints exactly 1 reference token
• Can be burned by admin
• Holds Oracle NFT + state datum
• Tracks count, price, admin address
• Enforces single input (prevent double-satisfaction)
• Increments counter, validates fees
• Parameterized with collection name + Oracle policy
• References Oracle via reference input
• Reads current count from Oracle datum
• Generates token name: "Collection (N)"
• Mints exactly 1 NFT with sequential name
Building a real NFT platform requires additional features:
Module M006 Complete
You can now build complete NFT systems on Cardano!
Master the Oracle pattern 🏗️
Create production-ready NFT platforms 💎
See you in M007! 🚀