# 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)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-68/deno-and-fetch.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
