Deno & Fetch

A Deno-based script for minting CIP-25 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-2

Quick-Start Example

This streamlined tutorial shows how to mint a CIP-25 NFT on Cardano using Deno + Fetch and the Anvil API. Everything lives in a single file (mint.ts) so you can copy-paste and run.

We'll:

  1. Load wallets (Customer, Policy)

  2. Create a native script using our Native Script utilities

  3. Build a mint payload for one CIP-25 asset

  4. Call transactions/build

  5. Sign with the Policy & Customer keys (customer signature is done in the backend for simplicity)

  6. Submit the transaction

  7. Verify the NFT on-chain


Prerequisites

Two Wallets – You'll need two wallets with these fields:

  • Customer Wallet: Pays fees, receives the minted NFT

    • 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...)

New to Cardano? A "signing key" (skey) is your wallet's private key that proves you own the wallet. The "base address" is like your bank account number where ADA is stored. If you don't have wallets, see the Wallet CLI documentation THIS SHOULD BE STORED SECURELY AND NEVER SHARED OR EXPOSED. See the documentation for best practices.

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, createNativeScript)
β”‚   └── constant.ts        # API configuration (URLs, headers)
β”œβ”€β”€ wallet-customer.json   # Customer wallet (pays fees, receives NFT)
└── wallet-policy.json     # Policy wallet (controls minting policy)

Quick-Start Script

import { Buffer } from "node:buffer";
import {
  FixedTransaction,
  PrivateKey,
} from "npm:@emurgo/[email protected]";
import {
  createNativeScript,
  timeToSlot,
  getKeyhash,
} 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"));

// 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);

// Prepare mint payload
const counter = Date.now();
const assetName = `anvilapicip25_${counter}`;

// Build transaction
const buildBody = {
  changeAddress: customerWallet.base_address_preprod,
  mint: [
    {
      // CIP-25 asset with Metadata
      version: "cip25",
      assetName: { name: assetName, format: "utf8" },
      metadata: {
        name: `anvil-api-${counter}`,
        image: `ipfs://YOUR_IPFS_HASH_HERE`,
        mediaType: "image/png",
        description: "Anvil API CIP-25 Mint Example",
      },
      policyId: nativeScript.hash,
      quantity: 1,
    },
  ],
  preloadedScripts: [nativeScript],
};

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"));

// Sign with policy wallet
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: Date): Promise<number> - Converts timestamps to Cardano slots

  • getKeyhash(address: string): Promise<string> - Extracts payment key hash from addresses

  • createNativeScript(keyHash: string, ttl: number): Promise<{policyId: string, script: string}> - 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


Full Example Script

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

Deno Example

HTMX + Hono + Weld Example

Next.js + Weld Example

Last updated

Was this helpful?