Quick Start Examples

Treasury Pays for Minting (TS/JS + Fetch)

This example requires several utility and configuration files. Create the following files in the same directory as your main script.

Need to create wallets?

You can use the Anvil Wallet CLI to generate the required wallet JSON files for these examples.

// See Authentication page for API key details.
export const X_API_KEY = "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9";
export const NETWORK = "preprod";
export const API_URL = `https://${NETWORK}.api.ada-anvil.app/v2/services`;
export const HEADERS = {
  "Content-Type": "application/json",
  "x-api-key": X_API_KEY,
};

You will also need the shared utility functions, which you can find in the utils/shared.ts file in the examples repository.

This example uses the Wallet Endpoints to fetch UTXOs from the treasury wallet.

Now, you can run the main script:

/**
 * CIP-68 Treasury Pays Example
 *
 * This script demonstrates how to mint CIP-68 tokens (a reference token and a user token)
 * where the transaction fees are paid from a designated treasury wallet.
 *
 * This involves:
 * 1. Loading customer, treasury, policy, and metadata manager wallets.
 * 2. Creating a time-locked native script signed by the policy wallet.
 * 3. Defining the asset metadata for both the reference (label 100) and user (label 222) tokens.
 * 4. Fetching UTXOs from the treasury wallet to cover transaction fees.
 * 5. Building the transaction payload with the mint operation and preloaded native script.
 * 6. Signing the transaction with both the treasury and metadata manager private keys.
 * 7. Submitting the transaction to the Anvil API.
 *
 * Required environment variables: ANVIL_API_KEY (set in utils/constant.ts)
 * Run: deno run --allow-all --env-file=.env cip68-treasury-pays.ts
 */

import { Buffer } from "node:buffer";
import {
  FixedTransaction,
  PrivateKey,
} from "npm:@emurgo/cardano-serialization-lib-nodejs@14.1.1";
import {
  timeToSlot,
  getKeyhash,
  createNativeScript,
} from "../utils/shared.ts";
import { API_URL, HEADERS } from "../utils/constant.ts";

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

// 2. Create native script (expires 2027-01-01, signed by policy wallet)
const counter = new Date().getTime();
const slot = await timeToSlot(new Date("2027-01-01"));
const keyhash = await getKeyhash(policyWallet.base_address_preprod);

if (!keyhash) {
  throw new Error("Unable to get key hash for policy wallet.");
}
const nativeScript = await createNativeScript(keyhash, slot);

// 3. Define assets to be minted
const assets = [
  {
    // Reference Token (label 100) -> sent to Metadata Manager Address
    version: "cip68",
    assetName: { name: `anvilapicip68_${counter}`, format: "utf8", label: 100 },
    metadata: {
      name: `anvil-api-${counter}`,
      image: "ipfs://QmQZJ1J3J3J3J3J3J3J3J3J3J3J3J3J3J3J3J3J3J",
      mediaType: "image/png",
      description: "Anvil API CIP-68 Treasury Pays Example",
    },
    policyId: nativeScript.hash,
    quantity: 1,
    destAddress: metaManagerWallet.base_address_preprod,
  },
  {
    // User Token (label 222) -> sent to Customer Address
    version: "cip68",
    assetName: { name: `anvilapicip68_${counter}`, format: "utf8", label: 222 },
    policyId: nativeScript.hash,
    quantity: 1,
    destAddress: customerWallet.base_address_preprod,
  },
];

// 4. Get UTXOs from the treasury wallet using Anvil API
const utxosResponse = await fetch(
  `${API_URL}/wallets/utxos?address=${treasuryWallet.base_address_preprod}`,
  { headers: HEADERS }
);
if (!utxosResponse.ok) {
  throw new Error(`Failed to fetch UTXOs: ${await utxosResponse.text()}`);
}
const utxos: string[] = await utxosResponse.json();
console.log(`Found ${utxos.length} UTXOs in treasury wallet.`);

// 5. Build the transaction payload
const buildBody = {
  changeAddress: treasuryWallet.base_address_preprod,
  utxos,
  mint: assets,
  preloadedScripts: [nativeScript],
};

console.log("Build Body: ", buildBody);

// 6. Build the transaction
const buildResponse = await fetch(`${API_URL}/transactions/build`, {
  method: "POST",
  body: JSON.stringify(buildBody),
  headers: HEADERS,
});

if (!buildResponse.ok) {
  throw new Error(`Failed to build transaction: ${await buildResponse.text()}`);
}
const buildJson = await buildResponse.json();
console.log("Build Output:", buildJson);

// 7. Sign the transaction
const transaction = FixedTransaction.from_bytes(Buffer.from(buildJson.complete, "hex"));

// Sign with Treasury wallet (pays for the transaction)
transaction.sign_and_add_vkey_signature(PrivateKey.from_bech32(treasuryWallet.skey));

// Sign with Metadata Manager wallet (controls the reference token)
transaction.sign_and_add_vkey_signature(PrivateKey.from_bech32(policyWallet.skey));

// 8. Submit the transaction
const submitResponse = await fetch(`${API_URL}/transactions/submit`, {
  method: "POST",
  body: JSON.stringify({
    transaction: transaction.to_hex(),
  }),
  headers: HEADERS,
});

const submitJson = await submitResponse.json();

if (!submitResponse.ok) {
  console.error("Submission failed. Response:", submitJson);
  throw new Error(`Failed to submit transaction: ${submitResponse.statusText}`);
}

console.log("Submitted Transaction:", JSON.stringify(submitJson, null, 2));

/*
Sample Output

Found 3 UTXOs in treasury wallet.
Build Body:  {
  changeAddress: "addr_test1qpxpar5s94wv6n76jvmvkr8uhnrflge6fatjzlkuujw3m33mk0alnas44ptzrkqqp3kdhplmcwys5mlht5w6seefrtls2r748e",
  utxos: [
    "82825820cbdd371aca5fef27267d5a41d9af6c6d9c9ba46b16aa5501f6fae91d3415a1f700825839004c1e8e902d5ccd4fda9336cb0cfcbcc69fa33a4f57217edce49d1dc63bb3fbf9f615a85621d8000c6cdb87fbc3890a6ff75d1da867291aff1a05f5e100",
    "82825820f3c3344a570042a0ad5753bd6af8a6ff360dbc41ea35c77f6502da89427efc9c00825839004c1e8e902d5ccd4fda9336cb0cfcbcc69fa33a4f57217edce49d1dc63bb3fbf9f615a85621d8000c6cdb87fbc3890a6ff75d1da867291aff1a01312d00",
    "828258203bbfdc451b2d12a3388146113b9411da603c205a835579a6e1397e696e04f0df02825839004c1e8e902d5ccd4fda9336cb0cfcbcc69fa33a4f57217edce49d1dc63bb3fbf9f615a85621d8000c6cdb87fbc3890a6ff75d1da867291aff821a0979d543a3581c38fdf893232baf9703e2f051ff9083a2756063c8bce3d80eb8d5eb3fa148000de1407465737401581c5e3817eacf8b6c393307fa61b00d546678e24414380dc7a2d879bc78a148000de1407465737401581cf8e024681ee83f54bd5f9a033464168f619970289017a9f53454d950a148000de1407465737401"
  ],
  mint: [
    {
      version: "cip68",
      assetName: {
        name: "anvilapicip68_1752704257434",
        format: "utf8",
        label: 100
      },
      metadata: {
        name: "anvil-api-1752704257434",
        image: "ipfs://QmQZJ1J3J3J3J3J3J3J3J3J3J3J3J3J3J3J3J3J3J",
        mediaType: "image/png",
        description: "Anvil API CIP-68 Treasury Pays Example"
      },
      policyId: "4d5bd6249f0d9e4b2762ce334e2973dc7fd414ec1e08b4b0c2159bfb",
      quantity: 1,
      destAddress: "addr_test1qz7jcka6zwj0tz52rl73te7y7avl2ckzmkfguwl42h728jeqzph40hhsxq32p2t4r3qcnhsrk9nnq5m2yv04dhuf3hfsuqphqk"
    },
    {
      version: "cip68",
      assetName: {
        name: "anvilapicip68_1752704257434",
        format: "utf8",
        label: 222
      },
      policyId: "4d5bd6249f0d9e4b2762ce334e2973dc7fd414ec1e08b4b0c2159bfb",
      quantity: 1,
      destAddress: "addr_test1qqwadht4defe0cqem6a56ytwz96g4y7j7z0a02y5aj8c4unm5k2xfvhpn4kukwhsfakq22dy94yf9y8nt9dddzpkmuys4r5q8h"
    }
  ],
  preloadedScripts: [
    {
      type: "simple",
      script: { type: "all", scripts: [ [Object], [Object] ] },
      hash: "4d5bd6249f0d9e4b2762ce334e2973dc7fd414ec1e08b4b0c2159bfb"
    }
  ]
}
Build Output: {
  hash: "6689bd7030901e036490e510d95f6535c7717345fe8a5541c3f6efe5efa5d65c",
  complete: "84a700d90102818258203bbfdc451b2d12a3388146113b9411da603c205a835579a6e1397e696e04f0df02018483583900bd2c5bba13a4f58a8a1ffd15e7c4f759f562c2dd928e3bf555fca3cb20106f57def03022a0a9751c4189de03b16730536a231f56df898dd3821a00157084a1581c4d5bd6249f0d9e4b2762ce334e2973dc7fd414ec1e08b4b0c2159bfba1581f000643b0616e76696c61706963697036385f3137353237303432353734333401582080cbf5aab266792d95a836241f88b8a9e4e4e58f897884343f01dcbf57529f8f825839001dd6dd756e5397e019debb4d116e11748a93d2f09fd7a894ec8f8af27ba59464b2e19d6dcb3af04f6c0529a42d489290f3595ad68836df09821a00133418a1581c4d5bd6249f0d9e4b2762ce334e2973dc7fd414ec1e08b4b0c2159bfba1581f000de140616e76696c61706963697036385f313735323730343235373433340182583900e4c0e5e76be5cf9ea49b889cb6e54baed815859c35b2d9fe84b04b222435fcd97a6b0717fbd3658537d3dfd3360267449dcd2cd4de5928e01a0020ce70825839004c1e8e902d5ccd4fda9336cb0cfcbcc69fa33a4f57217edce49d1dc63bb3fbf9f615a85621d8000c6cdb87fbc3890a6ff75d1da867291aff821a092ce9faa3581c38fdf893232baf9703e2f051ff9083a2756063c8bce3d80eb8d5eb3fa148000de1407465737401581c5e3817eacf8b6c393307fa61b00d546678e24414380dc7a2d879bc78a148000de1407465737401581cf8e024681ee83f54bd5f9a033464168f619970289017a9f53454d950a148000de1407465737401021a0003783d031a05c88894081a05c86c7409a1581c4d5bd6249f0d9e4b2762ce334e2973dc7fd414ec1e08b4b0c2159bfba2581f000643b0616e76696c61706963697036385f3137353237303432353734333401581f000de140616e76696c61706963697036385f31373532373034323537343334010b58204587ac46d0a5802afe974301ec122e228c20b19fbd15ba26eaf9c1770d687d0da201d90102818201828200581c3910f0db63599cd9cb1484d5591491629470761a89cde0473e4b94f682051a06a6008004d901029fd8799fa4446e616d6557616e76696c2d6170692d3137353237303432353734333445696d6167655830697066733a2f2f516d515a4a314a334a334a334a334a334a334a334a334a334a334a334a334a334a334a334a334a334a496d656469615479706549696d6167652f706e674b6465736372697074696f6e5826416e76696c20415049204349502d36382054726561737572792050617973204578616d706c6501fffff5f6",
  stripped: "84a700d90102818258203bbfdc451b2d12a3388146113b9411da603c205a835579a6e1397e696e04f0df02018483583900bd2c5bba13a4f58a8a1ffd15e7c4f759f562c2dd928e3bf555fca3cb20106f57def03022a0a9751c4189de03b16730536a231f56df898dd3821a00157084a1581c4d5bd6249f0d9e4b2762ce334e2973dc7fd414ec1e08b4b0c2159bfba1581f000643b0616e76696c61706963697036385f3137353237303432353734333401582080cbf5aab266792d95a836241f88b8a9e4e4e58f897884343f01dcbf57529f8f825839001dd6dd756e5397e019debb4d116e11748a93d2f09fd7a894ec8f8af27ba59464b2e19d6dcb3af04f6c0529a42d489290f3595ad68836df09821a00133418a1581c4d5bd6249f0d9e4b2762ce334e2973dc7fd414ec1e08b4b0c2159bfba1581f000de140616e76696c61706963697036385f313735323730343235373433340182583900e4c0e5e76be5cf9ea49b889cb6e54baed815859c35b2d9fe84b04b222435fcd97a6b0717fbd3658537d3dfd3360267449dcd2cd4de5928e01a0020ce70825839004c1e8e902d5ccd4fda9336cb0cfcbcc69fa33a4f57217edce49d1dc63bb3fbf9f615a85621d8000c6cdb87fbc3890a6ff75d1da867291aff821a092ce9faa3581c38fdf893232baf9703e2f051ff9083a2756063c8bce3d80eb8d5eb3fa148000de1407465737401581c5e3817eacf8b6c393307fa61b00d546678e24414380dc7a2d879bc78a148000de1407465737401581cf8e024681ee83f54bd5f9a033464168f619970289017a9f53454d950a148000de1407465737401021a0003783d031a05c88894081a05c86c7409a1581c4d5bd6249f0d9e4b2762ce334e2973dc7fd414ec1e08b4b0c2159bfba2581f000643b0616e76696c61706963697036385f3137353237303432353734333401581f000de140616e76696c61706963697036385f31373532373034323537343334010b58204587ac46d0a5802afe974301ec122e228c20b19fbd15ba26eaf9c1770d687d0da0f5f6",
  witnessSet: "a201d90102818201828200581c3910f0db63599cd9cb1484d5591491629470761a89cde0473e4b94f682051a06a6008004d901029fd8799fa4446e616d6557616e76696c2d6170692d3137353237303432353734333445696d6167655830697066733a2f2f516d515a4a314a334a334a334a334a334a334a334a334a334a334a334a334a334a334a334a334a334a496d656469615479706549696d6167652f706e674b6465736372697074696f6e5826416e76696c20415049204349502d36382054726561737572792050617973204578616d706c6501ffff"
}
Submitted Transaction: {
  "txHash": "6689bd7030901e036490e510d95f6535c7717345fe8a5541c3f6efe5efa5d65c"
}
*/

Learn More

For a step-by-step explanation, see the Mint a CIP-68 NFT Guide.