Deno & Fetch

A Deno-based script for minting CIP-68 assets on the Cardano blockchain using the Anvil API. This guide provides a complete example for building, signing, and submitting a transaction to mint a CIP-68

Quick-Start Example

This streamlined tutorial shows how to mint a CIP-68 NFT on Cardano using Deno + Fetch and the Anvil API.

This guide demonstrates the Native Script Validation approach using a Metadata-Manager Wallet. It's a straightforward way to create updatable NFTs without deploying a full smart contract. See the reasons why for more details.

We'll:

  1. Load wallets (Customer, Policy, Metadata Manager)

  2. Create a native script using our Native Script utilities

  3. Build a mint payload that creates both tokens (100 + 222) in the same transaction.

  4. Call transactions/build

  5. Sign with the Policy & Customer (skeys) keys. Customer in the backend for simplicity.

  6. Submit the transaction

  7. Verify on-chain that the assets are minted:

    • The reference token is sent to the metadata manager wallet

    • The user token is sent to the customer wallet


Prerequisites

Three Wallets – You'll need three wallets with these fields:

  • Customer Wallet: Pays fees, receives user token (label 222)

    • base_address_preprod: Testnet address (addr_test1...)

    • skey: Signing key (ed25519e_sk1...)

  • Policy Wallet: Controls minting policy

    • base_address_preprod: Testnet address (addr_test1...)

    • skey: Signing key (ed25519e_sk1...)

  • Metadata Manager Wallet: Receives reference token (label 100)

    • base_address_preprod: Testnet address (addr_test1...)

    • skey: Signing key (optional for this script. Used when updating metadata)

Create wallets with our Wallet CLI. Ensure customer wallet has ADA for fees.

Anvil API Key – A valid Anvil API key. See Authentication.

Utility Helpers – Import helpers from the utilities-functions guide to keep this file short.

Project Structure

For the absolute minimum setup, you only need:

your-project/
├── mint.ts                    # Main minting script
├── utils/
│   ├── shared.ts             # Utility functions (timeToSlot, getKeyhash, etc.)
│   └── constant.ts           # API configuration (URLs, headers)
├── wallet-customer.json      # Customer wallet (pays fees, receives user token)
├── wallet-policy.json        # Policy wallet (controls minting policy)
└── wallet-meta-manager.json  # Metadata manager wallet (receives reference token)

Quick-Start Script

import { Buffer } from "node:buffer";
import {
  FixedTransaction,
  PrivateKey,
} from "npm:@emurgo/[email protected]";
import {
  timeToSlot,
  getKeyhash,
  createNativeScript,
} from "../utils/shared.ts";
import { API_URL, HEADERS } from "../utils/constant.ts";

// Load wallets
const customerWallet = JSON.parse(Deno.readTextFileSync("wallet-customer.json"));
const policyWallet = JSON.parse(Deno.readTextFileSync("wallet-policy.json"));
const metaManagerWallet = JSON.parse(Deno.readTextFileSync("wallet-meta-manager.json"));

// Create native script
const slot = await timeToSlot(new Date("2026-01-01"));
const keyhash = await getKeyhash(policyWallet.base_address_preprod);
const nativeScript = await createNativeScript(keyhash!, slot);

const counter = Date.now();
const assetName = `anvilapicip68_${counter}`;

// Build mint payload
const buildBody = {
  changeAddress: customerWallet.base_address_preprod,
  mint: [
    {
      // Reference token - label 100, sent to Metadata-Manager wallet
      version: "cip68",
      assetName: { name: assetName, format: "utf8", label: 100 },
      metadata: {
        name: `anvil-api-${counter}`,
        image: "ipfs://YOUR_IPFS_HASH_HERE",
        mediaType: "image/png",
        description: "Anvil API CIP-68 Mint Example",
      },
      policyId: nativeScript.hash,
      quantity: 1,
      destAddress: metaManagerWallet.base_address_preprod,
    },
    {
      // User token - label 222, sent to Customer wallet
      version: "cip68",
      assetName: { name: assetName, format: "utf8", label: 222 },
      policyId: nativeScript.hash,
      quantity: 1,
      destAddress: customerWallet.base_address_preprod,
    },
  ],
  preloadedScripts: [nativeScript],
};

// Build transaction
const buildResult = await fetch(`${API_URL}/transactions/build`, {
  method: "POST",
  headers: HEADERS,
  body: JSON.stringify(buildBody),
});
const buildJson = await buildResult.json();
if (!buildResult.ok) {
  throw new Error(buildJson.message);
}

// Sign transaction
const tx = FixedTransaction.from_bytes(Buffer.from(buildJson.complete, "hex"));
tx.sign_and_add_vkey_signature(PrivateKey.from_bech32(policyWallet.skey));

// Sign with customer wallet. Normally this would come from the clientside via your customers browser extenstion
// This is here for simplicity. See full minting examples below.
tx.sign_and_add_vkey_signature(PrivateKey.from_bech32(customerWallet.skey));

// Submit transaction
const submitResult = await fetch(`${API_URL}/transactions/submit`, {
  method: "POST",
  headers: HEADERS,
  body: JSON.stringify({ transaction: tx.to_hex() }),
});
const submitJson = await submitResult.json();
if (!submitResult.ok) {
  throw new Error(submitJson.message);
}

console.log("Submitted Transaction Hash:", submitJson.txHash);

Utility Files Reference

You'll need to create the utility files. See the Utility Functions documentation for detailed API endpoint information and examples.

The utility files provide these key functions:

  • timeToSlot(date) - Converts timestamps to Cardano slots

  • getKeyhash(address) - Extracts payment key hash from addresses

  • createNativeScript(keyHash, ttl) - Creates time-locked native scripts

  • API configuration - Anvil API endpoints and headers

💡 Tip: The anvil-api-examples repository contains the most up-to-date utility functions and is the recommended source.


Running the Script

# Run the script
deno run --allow-all mint.ts

Troubleshooting

Common Issues

Error: "Module not found"

  • Check that your import paths match your project structure

  • Ensure utils/shared.ts and utils/constant.ts exist

Error: "Failed to get key hash for address"

  • Verify your wallet addresses are valid testnet addresses

  • Check that wallet files contain proper JSON format

  • Ensure addresses start with addr_test1

Error: "API call failed: Input validation failed"

  • Usually means wallet address parsing failed

  • Double-check wallet file format and addresses

Error: "Unable to build tx"

  • Ensure customer wallet has sufficient ADA for fees

  • Check that API key is valid

  • Verify network connectivity

CIP-68 Specific Notes

Two tokens are minted in the same transaction:

  • Reference token (label 100) - Contains metadata, sent to metadata manager wallet

  • User token (label 222) - The actual NFT, sent to customer wallet


Full Example Script

For complete, fully-annotated examples see the examples repository:

CIP-68 Example

CIP-68 Treasury Pays Example

Last updated

Was this helpful?