Course: Cardano Go PBL 2026Module: 203 β Applications & Smart Contracts
π― Learning Outcome
I can build a transaction that mints or burns tokens using a native script (no smart contract required).
01 β Background: What Is a Native Script?
On Cardano, every token belongs to a policy. A policy defines:
- Who can mint or burn tokens
- Under what conditions
Types of Minting Policies
Type
Description
Native Script
Declarative rule set evaluated by the node. No Plutus VM required. Supports signatures and time-locks.
Plutus Script (Validator)
Arbitrary logic executed in Plutus VM. Supports complex conditions and state checks.
π This lesson focuses on native scripts.
Key Concept
- A policy ID = hash of the script
- Token identity =
policyId + assetName
β οΈ Tokens are permanently bound to their policy.
02 β How Apollo Models Minting
API Methods
// Native scripts
MintAssets(mintUnit Unit) *Apollo
// Plutus scripts
MintAssetsWithRedeemer(mintUnit Unit, redeemer Redeemer) *Apollo
Unit Structure
unit := apollo.NewUnit(
"a1b2c3...", // policy ID
"MyToken", // asset name
1000, // quantity (+ mint, - burn)
)
β οΈ Minted tokens must be included in an output (
PayToAddressBech32) or the transaction will fail.
03 β Setup: Imports & Backend
package main
import (
"encoding/hex"
"fmt"
"github.com/Salvionied/apollo"
"github.com/Salvionied/apollo/constants"
"github.com/Salvionied/apollo/txBuilding/Backend/BlockFrostChainContext"
NativeScript "github.com/Salvionied/apollo/serialization/NativeScript"
Key "github.com/Salvionied/apollo/serialization/Key"
)
const (
BLOCKFROST_KEY = "previewXXXXXXXXXXXXXXXX"
MNEMONIC = "your mnemonic..."
)
04 β Native Script & Policy ID
func buildNativeScript(vkey Key.VerificationKey) (NativeScript.NativeScript, string) {
pkh := vkey.PaymentKeyHash()
script := NativeScript.NativeScript{
Type: NativeScript.ScriptPubkey,
KeyHash: pkh,
}
policyId := hex.EncodeToString(script.Hash())
return script, policyId
}
Optional: Time-Locked Policy
timeLocked := NativeScript.NativeScript{
Type: NativeScript.ScriptAll,
Scripts: []NativeScript.NativeScript{
{
Type: NativeScript.ScriptPubkey,
KeyHash: pkh,
},
{
Type: NativeScript.ScriptInvalidHereAfter,
Slot: 10_000_000,
},
},
}
05 β Complete Minting Transaction
func main() {
bfc, err := BlockFrostChainContext.NewBlockfrostChainContext(
constants.BLOCKFROST_BASE_URL_PREVIEW,
int(constants.PREVIEW),
BLOCKFROST_KEY,
)
if err != nil { panic(err) }
apollob := apollo.New(&bfc)
apollob, _ = apollob.SetWalletFromMnemonic(MNEMONIC, constants.PREVIEW)
apollob, _ = apollob.SetWalletAsChangeAddress()
utxos, _ := bfc.Utxos(*apollob.GetWallet().GetAddress())
vkey := apollob.GetWallet().GetVerificationKey()
script, policyId := buildNativeScript(vkey)
mintUnit := apollo.NewUnit(policyId, "GimbalToken", 1_000_000)
apollob, err = apollob.
AddLoadedUTxOs(utxos...).
MintAssets(mintUnit).
AttachNativeScript(script).
AddRequiredSignerFromBech32(
apollob.GetWallet().GetAddress().ToBech32(),
true, false,
).
PayToAddressBech32(
apollob.GetWallet().GetAddress().ToBech32(),
2_000_000,
mintUnit,
).
Complete()
if err != nil { panic(err) }
apollob = apollob.Sign()
txId, _ := apollob.Submit()
fmt.Println("Tx:", hex.EncodeToString(txId.Payload))
}
06 β Burning Tokens
burnUnit := apollo.NewUnit(policyId, "GimbalToken", -500_000)
tokenUtxo, _ := apollob.UtxoFromRef("txhash...", 0)
apollob, err = apollob.
AddLoadedUTxOs(utxos...).
AddInput(*tokenUtxo).
MintAssets(burnUnit).
AttachNativeScript(script).
AddRequiredSignerFromBech32(
apollob.GetWallet().GetAddress().ToBech32(),
true, false,
).
Complete()
β οΈ Burn amount must not exceed available tokens.
07 β Observing with Adder
{
"txHash": "abc123...",
"mint": {
"policyId...": {
"GimbalToken": 1000000
}
},
"outputs": [
{
"address": "addr_test1...",
"value": {
"lovelace": 2000000,
"policyId...GimbalToken": 1000000
}
}
]
}
08 β Step-by-Step Flow
- Set up Blockfrost context
- Load wallet
- Build native script β derive policy ID
- Create mint unit
- Build transaction
- Sign & submit
09 β Exercises
A β Mint Token
- Set up backend
- Derive policy ID
- Mint
1,000,000tokens - Send to your wallet
- Verify on explorer
B β Burn Half
- Locate token UTxO
- Burn
500,000 - Use same policy
- Verify negative mint
C β Time-Locked Policy
- Get current slot
- Create expiry policy
- Mint before deadline
- Try minting after (should fail)
10 β Knowledge Check
- Understand native vs Plutus policies
- Know how policy ID is derived
- Can construct
Unitcorrectly - Can build mint & burn transactions
- Understand why outputs are required
- Can interpret mint field
11 β Whatβs Next
Next: Plutus Minting Validators (203.3)
MintAssetsWithRedeemer- On-chain logic with Aiken
- Advanced minting rules