Mint Logic
Understanding the smart contract - How to mint NFTs on Cardano (in accordance to CIP-68 standard) using Aiken smart contracts. Cardano interactions powered by Anvil API
You've chosen the Smart Contract approach for CIP-68 - excellent choice for projects needing more validation control and complex business rules!
What the Smart Contract Does
While the main guide explained what CIP-68 achieves (updatable NFT metadata), this guide explains how the smart contract ensures everything works correctly with the CIP-68 NFT standard.
The Smart Contract's Job:
β Enforces CIP-68 Rules: Ensures reference tokens (100) and user tokens (222) are always created together
β Validates Authorization: Only authorized admins can mint new token pairs
β Guarantees Metadata Integrity: Reference tokens contain valid CIP-68 metadata structure
β Enables Future Updates: Reference tokens can be spent later to update metadata
How It Works: The Validation Flow
Every CIP-68 mint using this smart contract follows the same validation sequence: Build Transaction β Validate Smart Contract β Submit Valid Transaction β Outputs.
Quick Setup Process:
Upload & Parameterize: Deploy the smart contract with admin keys via POST /blueprints endpoints
Build Transactions: Use the parameterized contract in POST /transactions/build calls
Let Validation Happen: Smart contract automatically enforces all CIP-68 rules
Submit Valid Transaction: Validated transaction is submitted to the Cardano network via the
POST /transactions/submit
endpoint.
The beauty of smart contracts: Once deployed and parameterized, they handle all the complex validation automatically. You just build transactions - the contract ensures everything follows CIP-68 standards, and you are creating the NFTs correctly.
Ready to implement? See the Mint Example for step-by-step code implementation.
Smart Contract Validation Flow
The diagram below shows how the CIP-68 smart contract processes your transaction. Follow the arrows to understand the validation sequence:
1. Build Transaction
Your transaction requires these inputs: For more information on transaction building, see the transaction building guide.
Required Transaction Inputs:
π° UTxOs (ADA for fees) - Sufficient ADA for transaction fees and minimum UTxO requirements
π·οΈ Reference NFT (100) - Token sent to smart contract address containing metadata
π¨ User Token (222) - Token sent to user as the actual NFT
π Admin Signature - Parameterized admin key authorization
π Blueprint with Applied Parameters - Smart contract with embedded admin key
2. POST /transactions/build (API Processing)
Once you build your transaction, it gets processed through the Anvil API's transaction builder endpoint:
POST /transactions/build
What happens at this step:
Your transaction details are received by the Anvil API
The API loads the parameterized smart contract from
preloadedScripts
Transaction structure is validated for basic requirements
The smart contract validation logic is triggered
If validation passes, a transaction is built and returned
Key Point: The smart contract validation (next section) happens during this API call, not separately.
3. Validate Smart Contract
The smart contract performs three validation checks:
β
Check Admin Signature
let is_signed = list.has(self.extra_signatories, signer)
Ensures only the parameterized admin can authorize mints.
β
Validate CIP-68 Tokens
The contract performs comprehensive validation of CIP-68 dual-token creation, metadata, and exact mint matching:
validator cip68examples(signer: VerificationKeyHash) {
mint(redeemer: MintRedeemer, policy_id: PolicyId, self: Transaction) {
// Step 1: Generate expected tokens based on redeemer instructions
let expected_mint =
generate_mint(redeemer.assetnames, policy_id, zero, self)
|> tokens(policy_id)
// Step 2: Get what's actually being minted in this transaction
let currently_minting = self.mint |> tokens(policy_id)
// Step 3: Ensure both admin signature AND exact token match
and {
is_signed?, // Admin must sign
(expected_mint == currently_minting)?, // Mint exactly what's expected, nothing more/less
}
}
}
How this validation works:
Expected vs Actual: The contract calculates what tokens should be minted based on the redeemer, then compares against what's actually being minted
No Surprises: If someone tries to mint extra tokens or different quantities, the validation fails
Dual Validation: Both admin signature AND exact token matching must pass
β
Validate Metadata
The contract ensures each reference token output contains valid CIP-68 metadata:
// Extract and validate datum from reference token output
expect Some(datum) = find_datum(output, self)
expect _: Cip68Metadata = datum
Metadata Structure:
pub type Cip68Metadata {
metadata: Pairs<Data, Data>,
version: Int,
extra_datum: MetadataUpdateExpiration,
}
What this validates:
Datum Presence: Reference token outputs must contain a datum
Structure Validation: Datum must conform to CIP-68 metadata specification
Version Control: Metadata includes version information for future updates
The generate_mint
function handles all token validation:
This function processes each asset name from the redeemer and validates the corresponding transaction outputs:
pub fn generate_mint(
tokens: List<(ByteArray, Int)>, // List of (asset_name, output_index) pairs
policy_id: PolicyId,
mint: Value, // Accumulator for expected mint value
self: Transaction,
) -> Value {
when tokens is {
[] -> mint // Base case: return accumulated mint value
[(assetname, index), ..rest] -> {
// Step 1: Generate the CIP-68 token pair names
let (ref_tok, user_tok) = generate_ref_and_user_token(assetname)
// Step 2: Find and validate the reference token output
expect Some(output) = self.outputs |> at(index) // Must exist at specified index
expect Some(datum) = find_datum(output, self) // Must have a datum
expect _: Cip68Metadata = datum // Datum must be valid CIP-68 metadata
// Step 3: Verify reference token goes to script address with correct value
let output_value = without_lovelace(output.value)
let expected_output_value = zero |> add(policy_id, ref_tok, 1) // Exactly 1 reference token
expect output_value == expected_output_value // No extra tokens in output
expect
when output.address.payment_credential is {
Script(hash) -> hash == policy_id // Must go to this script's address
_ -> False // Not a script address = fail
}
// Step 4: Add both tokens to mint and process remaining assets
generate_mint(
rest,
policy_id,
mint
|> add(policy_id, ref_tok, 1) // Add reference token to expected mint
|> add(policy_id, user_tok, 1), // Add user token to expected mint
self,
)
}
}
}
What each validation step ensures:
Output Existence: The transaction must have an output at the specified index
Metadata Presence: Reference token output must contain valid CIP-68 metadata
Address Validation: Reference token must be sent to the smart contract address
Value Precision: Output contains exactly 1 reference token, no extras
Recursive Processing: All asset names in the redeemer are validated
CIP-68 Token Generation:
/// (100) Reference Token Prefix
pub const prefix_100: ByteArray = #"000643b0"
/// (222) Non-Fungible Token Prefix
pub const prefix_222: ByteArray = #"000de140"
pub fn generate_ref_and_user_token(assetname: ByteArray) -> (ByteArray, ByteArray) {
(prefix_100 |> concat(assetname), prefix_222 |> concat(assetname))
}
Metadata Structure:
pub type Cip68Metadata {
metadata: Pairs<Data, Data>,
version: Int,
extra_datum: MetadataUpdateExpiration,
}
What this comprehensive validation ensures:
Token Pairs: Creates Reference (100) and User (222) tokens for each asset name
Metadata Validation: Reference tokens must have valid CIP-68 metadata datum
Output Placement: Reference tokens sent to script address, user tokens to specified address
Exact Matching: Only specified tokens are minted, no extras allowed
Label Prefixes: Proper CIP-68 label encoding (100 =
000643b0
, 222 =000de140
)
4. Submit Valid Transaction
Once all validations pass, the transaction is submitted to the Cardano network and CIP-68 token pairs are created on-chain.
5. Outputs
The successful transaction creates both tokens as validated above:
π·οΈ Reference NFT (100) - Sent to smart contract address with metadata datum
π¨ User Token (222) - Sent to user's wallet as the actual NFT
Security Model
Admin Authorization: Admin key hash is embedded in the contract; only they can authorize mints
Customer Control: Customers control their UTxOs and receive user tokens (222) directly
Metadata Protection: Reference tokens (100) are locked at script address with validated metadata
Next Steps
Now that you understand the smart contract validation logic, you can:
Ready to implement? See the practical minting example for step-by-step code
Want to build custom applications? Use this validation knowledge to create applications that interact with CIP-68 contracts
Technical References
Last updated
Was this helpful?