> For the complete documentation index, see [llms.txt](https://dev.ada-anvil.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-68/deno-and-fetch.md).

# Native Script Minting

**Quick-Start Example**

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

{% hint style="info" %}
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](/guides/nft-and-ft/mint-nft-cip-68.md#validation-approaches-native-scripts-vs-smart-contracts) for more details.
{% endhint %}

We'll:

1. Load wallets (Customer, Policy, Metadata Manager)
2. Create a native script using our [Native Script utilities](/developer-tools/utility-functions.md)
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](/developer-tools/wallet-cli.md). **Ensure customer wallet has ADA for fees.**

**Anvil API Key** – A valid Anvil API key. See [Authentication](/anvil-api/authentication.md).

**Utility Helpers** – Import helpers from the [utilities-functions](/developer-tools/utility-functions.md) 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

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

// 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](/developer-tools/utility-functions.md) 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](https://github.com/Cardano-Forge/anvil-api-examples/blob/main/utils/shared.ts) contains the most up-to-date utility functions and is the recommended source.

***

## Running the Script

```bash
# 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](https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/cip68.ts)

[CIP-68 Treasury Pays Example](https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/cip68-treasury-pays.ts)
