Skip to Content
Scalus Club is now open! Join us to get an early access to new features 🎉

Minting and Burning Native Tokens on Cardano

This guide covers how to create (mint) and destroy (burn) native tokens on Cardano using TxBuilder.

Understanding Cardano Minting Policies

A minting policy is a Plutus script that controls when tokens can be created or destroyed. The policy ID (the script hash) becomes part of the token identifier.

Minting creates tokens but doesn’t automatically send them anywhere. You must explicitly add outputs to receive the newly minted tokens.

Minting Native Tokens

To mint tokens, use the mintAndAttach() method which combines minting with script attachment:

import scalus.builtin.ByteString.hex import scalus.cardano.ledger.{AssetName, PolicyId} val mintingPolicy: Script.PlutusV3 = // ... your minting policy script val assetName = AssetName(hex"4d795368696e79546f6b656e") // "MyShinyToken" in hex val redeemer = MintRedeemer(...) val assets = Map( assetName -> 1000L // Mint 1000 tokens ) TxBuilder(env) .spend(utxo) .mintAndAttach(redeemer, assets, mintingPolicy) .payTo(recipient, Value.asset(mintingPolicy.scriptHash, assetName, 1000L, Coin.ada(2))) .changeTo(changeAddress) .build()

This mints 1000 tokens and sends them to the recipient along with 2 ADA.

Minting Multiple NativeToken Types

Mint different tokens under the same policy:

val tokenA = AssetName(hex"546f6b656e41") // "TokenA" val tokenB = AssetName(hex"546f6b656e42") // "TokenB" val assets = Map( tokenA -> 500L, tokenB -> 300L ) TxBuilder(env) .spend(utxo) .mintAndAttach(redeemer, assets, mintingPolicy) .payTo( recipient, Value( Coin.ada(2), MultiAsset(SortedMap( mintingPolicy.scriptHash -> SortedMap( tokenA -> 500L, tokenB -> 300L ) )) ) ) .changeTo(changeAddress) .build()

Burning Native Tokens

To burn tokens, use negative amounts:

val assets = Map( assetName -> -100L // Burn 100 tokens ) TxBuilder(env) .spend(utxoWithTokens) // Must contain the tokens being burned .mintAndAttach(redeemer, assets, mintingPolicy) .payTo(recipient, Value.ada(5)) .changeTo(changeAddress) .build()

The tokens are removed from circulation and the UTxO containing them is consumed.

Using Pre-attached Scripts

If you’ve already attached the minting policy, use mint() instead:

val policyId = mintingPolicy.scriptHash TxBuilder(env) .spend(utxo) .attach(mintingPolicy) .mint(redeemer, policyId, assets) .payTo(recipient, Value.asset(policyId, assetName, 1000L, Coin.ada(2))) .changeTo(changeAddress) .build()

This is useful when minting under multiple policies in the same transaction.

Minting with Required Signers

If your minting policy requires specific signatures:

val requiredSigners = Set(pubKeyHash) val assets = Map(assetName -> 1000L) TxBuilder(env) .spend(utxo) .mintAndAttach(redeemer, assets, mintingPolicy, requiredSigners) .payTo(recipient, Value.asset(mintingPolicy.scriptHash, assetName, 1000L, Coin.ada(2))) .changeTo(changeAddress) .build() .sign(signer) // Must provide required signature

How to Mint NFTs on Cardano

Mint a unique NFT (1 token, typically with amount = 1):

val nftName = AssetName(hex"4d794e4654") // "MyNFT" val nftAssets = Map(nftName -> 1L) TxBuilder(env) .spend(utxo) .mintAndAttach(redeemer, nftAssets, mintingPolicy) .payTo( recipient, Value.asset(mintingPolicy.scriptHash, nftName, 1L, Coin.ada(2)) ) .changeTo(changeAddress) .build()

The minting policy typically ensures uniqueness by checking for a specific UTxO or enforcing a one-time mint.

Minting and Burning in One Transaction

You can mint and burn in the same transaction:

val assets = Map( tokenA -> 500L, // Mint 500 of tokenA tokenB -> -100L // Burn 100 of tokenB ) TxBuilder(env) .spend(utxoWithTokenB) // Must contain tokenB for burning .mintAndAttach(redeemer, assets, mintingPolicy) .payTo( recipient, Value.asset(mintingPolicy.scriptHash, tokenA, 500L, Coin.ada(2)) ) .changeTo(changeAddress) .build()

Multiple Policies

Mint tokens under different policies by chaining multiple mintAndAttach() calls:

TxBuilder(env) .spend(utxo) .mintAndAttach(redeemer1, assets1, policy1) .mintAndAttach(redeemer2, assets2, policy2) .payTo(recipient, combinedValue) .changeTo(changeAddress) .build()

Each policy is evaluated independently with its own redeemer.

Next Steps

Last updated on