# Anvil API

Simplify your Cardano integration with a powerful, developer-friendly API

The Anvil API empowers developers to rapidly build and deploy Cardano blockchain applications through a unified, developer-friendly interface. Whether you're creating a wallet with multi-signature capabilities, minting and managing NFT collections, implementing governance features, or interacting with smart contracts, Anvil eliminates the need to master complex low-level Cardano specifications while providing enterprise-grade reliability and performance.

## Key Benefits

### Reduced Complexity

Anvil handles the detailed protocol specifics, enabling developers to create transactions and smart contracts without mastering low-level details.

* **Simple transaction building**: Abstracted functions replace complex manual assembly.
* **Multi-signature support**: Define required signatures for transactions to allow shared control over assets.
* **Accessible learning curve**: Start building immediately, focusing on logic rather than specifications.

```json
// Instead of complex manual transaction assembly,
// Anvil API lets you create transactions with a clean, simple interface
// The following example shows how to send ADA and assets to a single participant easily.
{
  outputs: [
    {
      address: "addr...",  // Destination address
      lovelace: 10000000, // Send 10 ADA
      assets: [ // Assets to send
        {
          policyId: "policy_id_here",
          assetName: "asset_name",
          quantity: 1
        }
      ]
    }
  ],
  changeAddress: "addr..."  // Sender Address
}
```

### Faster Wallet Integration

Quickly integrate reliable transaction functionalities into wallets or payment apps.

* **Minimal coding effort**: Achieve common tasks like ADA transfers, asset transfers, delegation, minting, staking, or smart contract interactions through simple API endpoints.
* **Reliable and tested**: Consistent results reduce debugging and enhance product stability. Maintenance-free integration with automatic hard fork updates handled seamlessly.

### Streamlined NFT Minting

Efficiently mint NFTs through user-friendly "vending machine" style transactions and secure multisig minting capabilities.

* **Single-step minting**: Automate complex NFT minting, metadata handling, and delivery through intuitive endpoints.
* **Scalable & robust**: Supports both CIP-25 and CIP-68 token standards with minimal configuration.

```json
// Simplified NFT minting without needing to understand the underlying protocols
{
  mint: [
    {
      type: "simple",
      version: "cip25",
      assetName: { name: "MyNFT", format: "utf8" },
      policyId: "policy_id_here",
      quantity: 1,
      metadata: {
        name: "My NFT",
        description: "My first NFT minted with Anvil API",
        image: "ipfs://..."
      }
    }
  ]
}
```

### Smart Contract Simplification

Interact with Cardano smart contracts without deep Plutus knowledge.

* **Validator blueprints**: Use CIP-57 compliant blueprints to easily create and interact with Plutus scripts.
* **Automated datum handling**: Parse, construct, and validate complex Plutus data types with ease.
* **Contract evaluation**: Test your smart contract logic before deployment to ensure correct behavior.

```json
// Easy validation of Plutus contract data
// Instead of manual CBOR encoding/decoding
{
  hash: "validator_hash",
  type: "datum",
  purpose: "spend",
  data: { hex: "your_datum_hex" }
}
```

### Automatic Protocol Updates

Anvil stays updated with Cardano's protocol changes, eliminating maintenance burdens.

* **Zero downtime upgrades**: Continuous compatibility with the latest Cardano updates.
* **Maintenance-free**: Focus on feature development instead of ongoing technical upkeep.

## Features

Anvil API provides a comprehensive set of features to support your Cardano development needs:

1. **Multi-output Transaction Building**: Create complex transactions with multiple recipients
2. **Multi-signature Support**: Governance and shared control over assets with ease
3. **Token Minting**: Handles minting, editing, and burning for CIP-25 and CIP-68 tokens
4. **Staking Operations**: Streamlined delegations and certificates to stake pools and DReps
5. **Rewards Management**: Simplified reward withdrawal process
6. **Governance Support**: Delegate to DReps for voting in Cardano's governance system
7. **Transaction Optimization**: Automatic balancing and fee optimization
8. **Script Management**: Simplified on-chain script uploading
9. **Smart Contract Integration**: Contract evaluation and interaction via CIP-57 blueprints
10. **Language Agnostic API**: Build with your preferred technology stack through flexible API endpoints
11. **Multiple Protocol Support**: Choose between RESTful API and tRPC interfaces based on your development needs

## Next Steps

Ready to build with Anvil API? Choose your path:

New to Cardano?:

{% content-ref url="/pages/jaIfWg9f4Zu2YqvGtl2A" %}
[Cardano Basics](/anvil-api/cardano-basics)
{% endcontent-ref %}

If you are a developer familiar with Cardano:

{% content-ref url="/pages/RCnVmsz8IY84fmYcqVQX" %}
[Create Basic Transaction](/guides/transaction/create-basic-transaction)
{% endcontent-ref %}

{% content-ref url="/pages/Lls69ihKSdT5g8fcGOz5" %}
[Fees](/anvil-api/fees)
{% endcontent-ref %}


# Cardano Basics

## Getting Started with Cardano: A Comprehensive Guide

Welcome to Cardano! Whether you're a seasoned developer exploring Cardano's blockchain or a newcomer eager to dive into the ecosystem, this guide will help you set up, test, and understand the fundamentals of the network. Our goal is to simplify the onboarding process, offering clear recommendations and actionable steps to get started.

### Goals of This Page

1. Provide a clear and intuitive roadmap for setting up your first Cardano wallet.
2. Recommend the best wallets based on ease of use, features, and compatibility.
3. Offer actionable steps for testing and exploring Cardano using the Preprod Testnet.
4. Clarify the differences between Cardano environments (Mainnet, Preprod, Preview).
5. Introduce the core concepts of UTXOs and blockchain exploration tools.

### Step 1: Setting Up Your Wallet

To interact with the Cardano blockchain, you'll need a wallet to store funds, manage assets, and connect to decentralized applications (dApps). Below is a comprehensive overview of compatible Cardano wallets:

#### Recommended Browser Extension Wallets

1. [**Lace**](https://www.lace.io/):
   * Developed by IOG (creators of Cardano)
   * Multi-delegation staking capabilities
   * Clean, minimalist interface focused on user experience
2. [**Eternl**](https://eternl.io/) (Formerly CCVault):
   * Feature-rich with multi-address support and hardware wallet integration
   * Advanced UTXO management and token handling capabilities
   * Available as browser extension, web app, and mobile app
3. [**VESPR**](https://vespr.xyz/):
   * Fast mobile-focused wallet with excellent dApp support
   * Optimized for speed and user experience
   * Advanced Rewards Dashboard with epoch tracking and rewards forecasting

#### Full-Node Desktop Wallets

1. [**Daedalus**](https://daedaluswallet.io/):
   * Official full-node desktop wallet by Cardano Foundation
   * Downloads and validates the entire blockchain
   * Comprehensive security but requires significant system resources
   * Available for Windows, macOS, and Linux

### Step 2: Testing and Learning on Cardano

Testing is one of the best ways to understand how Cardano works without risking real funds. The **Preprod Testnet** is perfect for this because it mirrors the Mainnet but uses fake currency called **TestAda (TAda)**. Here's how you can get started:

#### Why Use the Preprod Testnet?

* **Low-Risk Environment:** Test transactions and smart contracts without fear of loss.
* **Realistic Experience:** It behaves like the Mainnet, making it a great learning and testing ground.

#### Getting TestAda for the Testnet

1. **Obtain Your Wallet Address**:\
   Open your wallet (e.g., Eternl, Nami, Lace) and go to the **Receive** section. Copy your public address—it will look like a long string of letters and numbers.
2. **Visit the Faucet Tool**:\
   Use a testnet faucet like [this one](https://docs.cardano.org/cardano-testnets/tools/faucet) to request free TAda. Paste your wallet address into the tool, and you’ll receive TestAda in minutes.
3. **Try Basic Operations:**
   * Send TAda to another wallet to see how transactions work.
   * Experiment with staking or interacting with decentralized applications (dApps).

### Step 3: Understanding Cardano and the EUTXO Model

#### What Is Cardano?

Cardano is a blockchain platform built on cutting-edge research and designed for scalability, sustainability, and interoperability. It’s one of the most secure and innovative platforms available, ideal for decentralized applications and digital assets.

#### What Is the EUTXO Model?

Cardano uses an Extended Unspent Transaction Output (EUTXO) model, which might sound complicated but is easy to grasp with the right analogy.

#### Analogy: Cash vs. Credit

* **EUTXO (Cardano):** Think of it like paying with **cash**.
  * If you want to pay $50, you hand over a $50 bill or a combination of smaller bills.
  * The recipient receives the cash, and any change comes back to you as a new "output."
  * This system is direct and secure because there's an actual "exchange" of funds.
* **EVM (Ethereum):** Think of it like paying with **credit**.
  * When you pay with a credit card, the transaction gets logged to a ledger.
  * There’s no direct transfer—just an update to your account balance.
  * It’s flexible but less deterministic because balances can change dynamically.

#### Why EUTXO Is Better for Certain Use Cases

* Predictable execution: Transactions are processed deterministically, reducing the risk of failure.
* Scalable: Cardano can handle more transactions simultaneously without congestion.

If you want a deeper dive, visit Cardano's EUTXO guide.

### Step 4: Choosing Your Environment

Cardano operates on three environments, each serving a specific purpose:

1. **Mainnet (Live Network):**
   * The active network where real transactions happen.
   * Use this when you’re ready for real-world blockchain interactions.
2. **Preprod (Testnet):**
   * Simulates Mainnet and allows you to test applications risk-free.
   * Ideal for experimenting with wallets, sending transactions, or testing dApps.
3. **Preview (Experimental Testnet):**
   * Used to test upcoming features before they’re deployed on Mainnet.
   * Often used by developers to ensure compatibility with future updates.

#### Which Environment Should I Use?

If you’re new to Cardano, start with **Preprod** to gain confidence. Once you’ve mastered the basics, move to Mainnet for real-world interactions.

### Step 5: Exploring the Blockchain

Once you've sent your first transaction or interacted with a dApp, you might wonder: **Where can I see my activity on the blockchain?** This is where blockchain explorers come in.

#### What Is a Blockchain Explorer?

A blockchain explorer is like a search engine for the blockchain. You can use it to:

* Look up your wallet address to see balances and transactions.
* Verify the details of any transaction.
* Explore staking pools and network statistics.

#### Recommended Explorers

* **For Preprod (Testnet):**
  * [Preprod CExplorer](https://preprod.cexplorer.io/)
  * [Preprod Cardano Scan](https://preprod.cardanoscan.io/)
* **For Mainnet (Live Network):**
  * [CExplorer](https://cexplorer.io/)
  * [Cardano Scan](https://cardanoscan.io/)
* **For Preview (Experimental Network):**
  * [Preview CExplorer](https://preview.cexplorer.io/)
  * [Preview Cardano Scan](https://preview.cardanoscan.io/)

#### How to Use an Explorer

1. **Search Your Wallet Address:** Copy your public wallet address and paste it into the search bar.
2. **View Transactions:** Click on individual transactions to see details like inputs, outputs, and fees.
3. **Explore Staking Pools:** Use the explorer to find staking pools and their performance metrics.

Cardano offers an innovative and user-friendly blockchain experience. With these tools and insights, you’re well-equipped to start your journey confidently.

Now, it’s time to choose a wallet and start building on Cardano! 🚀


# Usage

## Usage

Welcome to the **Anvil API Usage** page! This section outlines the different environments available to interact with the Cardano blockchain, utilizing Anvil’s infrastructure. Each environment caters to a specific stage of development, or type of deployment, so you can choose the one that aligns best with your goals.

### Why Multiple Environments?

Cardano supports multiple network “environments,” each intended for different purposes. These environments allow you to test without risking native ADA, preview upcoming features, or run production-grade services on the live (Mainnet) network.

Anvil provides separate endpoints for each environment to seamlessly move from experimentation to production while keeping the same API methods.

***

## Overview of Anvil Environments

Below is a list of our available endpoints and their corresponding Cardano environments, along with links to detailed documentation and interactive API reference (Swagger UI).

### Available Endpoints

| Environment                      | Endpoint                                         |
| -------------------------------- | ------------------------------------------------ |
| Mainnet                          | `https://prod.api.ada-anvil.app/v2/services/`    |
| Preprod                          | `https://preprod.api.ada-anvil.app/v2/services/` |
| Preview                          | `https://preview.api.ada-anvil.app/v2/services/` |
| Staging (Not for public use)     | `https://staging.api.ada-anvil.app/v2/services/` |
| Development (Not for public use) | `https://dev.api.ada-anvil.app/v2/services/`     |
| Test (Not for public use)        | `https://test.api.ada-anvil.app/v2/services/`    |
| Private Cluster                  | Contact Us for details                           |

**Note**: To access any of these endpoints, you must include your **API Key** in the request header. See the next page on Authentication for details on generating and using your API Key.

### Documentation Links

| Environment                      | Cardano Documentation                                                       | OpenAPI (Swagger)                                                      |
| -------------------------------- | --------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
| Mainnet                          | [Mainnet Documentation](https://book.play.dev.cardano.org/env-mainnet.html) | [Swagger UI](https://prod.api.ada-anvil.app/v2/services/swagger/ui)    |
| Preprod                          | [Preprod Documentation](https://book.play.dev.cardano.org/env-preprod.html) | [Swagger UI](https://preprod.api.ada-anvil.app/v2/services/swagger/ui) |
| Preview                          | [Preview Documentation](https://book.play.dev.cardano.org/env-preview.html) | [Swagger UI](https://preview.api.ada-anvil.app/v2/services/swagger/ui) |
| Staging (Not for public use)     | -                                                                           | [Swagger UI](https://staging.api.ada-anvil.app/v2/services/swagger/ui) |
| Development (Not for public use) | -                                                                           | [Swagger UI](https://dev.api.ada-anvil.app/v2/services/swagger/ui)     |
| Test (Not for public use)        | -                                                                           | [Swagger UI](https://test.api.ada-anvil.app/v2/services/swagger/ui)    |
| Private Cluster                  | -                                                                           | -                                                                      |

***

### Choosing the Right Environment

* **Mainnet**
  * **Who It’s For**: Production-ready dApps, applications handling real ADA or NFTs, and live user interactions.
  * **Key Benefits**: Fully decentralized, real economic value, backed by Cardano’s production network.
  * **Potential Risks**: Transactions have real financial implications and network fees.
* **Preprod**
  * **Who It’s For**: Users testing smart contracts, token minting, or new features without risking actual ADA.
  * **Key Benefits**: Closely mimics Mainnet behavior, using test tokens (TestAda).
  * **Ideal Use Cases**: Finalizing a project’s functionality before going live.
* **Preview**
  * **Who It’s For**: Developers who want to experiment with upcoming Cardano features.
  * **Key Benefits**: See how new Cardano protocol changes may impact your code.
  * **Ideal Use Cases**: Testing compatibility with future network updates.
* **Private Cluster**
  * **Who It’s For**: Enterprise solutions requiring a dedicated environment, or specialized compliance needs.
  * **Key Benefits**: Customizable settings, enhanced privacy, and dedicated resources.
  * **How to Access**: Reach out to Anvil to discuss your specific requirements.
* **Development, Staging & Test (Internal)**
  * **Who It’s For**: Anvil’s internal testing, staging, and QA processes.
  * **Availability**: Not publicly accessible—used for advanced in-house development.

***

## Getting Started: Your First API Call

Once you have chosen an environment and [obtained your API key](/anvil-api/authentication), you can make your first request.

1. **Construct the Request**: Use the correct endpoint for your chosen environment from the table above.
2. **Include Your API Key**: Add the `X-Api-Key` header with your key.

Here is a basic example using cURL to check the health of the Preprod environment:

```bash
curl -X GET \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: YOUR_API_KEY_HERE" \
  https://preprod.api.ada-anvil.app/v2/services/health
```

A successful request will return a JSON response confirming the service is operational.

***

## Next Steps

Now that you've made a successful call, you're ready to explore more complex features:

* **Dive Into Guides**: Check out the detailed guides (e.g., **Minting**, **Transactions**) in the sidebar.
* **Review Best Practices**: Read through our [Troubleshooting](/anvil-api/troubleshooting) and [FAQ](/anvil-api/faq) pages for common tips and solutions.

***

## Common Pitfalls

* **Mixing Environments**: Ensure your transaction details (like UTXOs), and wallets match the environment you are calling (e.g., don't use Mainnet assets on Preprod).
* **Forgetting the API Key**: Requests without a valid `X-Api-Key` header will be rejected.


# Authentication

How to access the Anvil API

## Authentication

### Overview

To interact with the Anvil API, you’ll need an **API Key**. Think of the API Key as your personal pass to our Cardano infrastructure—each key enforces daily usage limits and burst configurations tailored to your project’s needs.

**Important**: Treat your API Key like a password. If someone else gains access to it, they can use your quota and potentially access your resources.

***

### How to Get Your API Key

Visit our **self-serve portal** at [ada-anvil.io/api-keys](https://ada-anvil.io/api-keys) to generate your keys instantly. If the portal is unavailable, please email us at `hello@ada-anvil.io`.

***

## How to Use Your API Key

You will receive two API keys from Anvil:

* **Testnet Key**: Use this key for all non-production Cardano environments, including **Preprod** and **Preview**.
* **Mainnet Key**: Use this key exclusively for the Cardano **Mainnet** environment.

To authenticate your requests, include the appropriate key in the `X-Api-Key` header.

### Basic cURL Example

```bash
curl -X GET \
    -H "Content-Type: application/json" \
    -H "X-Api-Key: YOUR_API_KEY_HERE" \
    https://prod.api.ada-anvil.app/v2/services/health
```

**Endpoint**: Replace with the environment you want to interact with (e.g., `preprod.api.ada-anvil.app`, `preview.api.ada-anvil.app`, etc.).

**Method**: Varies depending on the API call (GET, POST, etc.).

**Headers**: Always include your `X-Api-Key`.

### Basic NodeJS Example

```typescript
async function checkHealth() {
  try {
    const response = await fetch(
      "https://prod.api.ada-anvil.app/v2/services/health",
      {
        headers: {
          "X-Api-Key": "YOUR_API_KEY_HERE",
        },
      },
    );
    console.log("Health Check:", await response.json());
  } catch (error) {
    console.error("Error:", error);
  }
}

(async () => {
  await checkHealth();
})();
```

### Python (Requests) Example

```python
import requests

def check_health():
    url = "https://prod.api.ada-anvil.app/v2/services/health"
    headers = {
        "X-Api-Key": "YOUR_API_KEY_HERE",
        "Content-Type": "application/json"
    }

    response = requests.get(url, headers=headers)
    if response.status_code == 200:
        print("Health Check:", response.json())
    else:
        print("Error:", response.status_code, response.text)

check_health()
```

***

## Rate Limits and Quotas

Every API Key has **usage limits** that define:

* **Maximum requests per day**
* **Requests per second (RPS)**
* **Burst limits** for short-term high-traffic events

These are set during onboarding. If you anticipate large spikes, contact us to adjust your limits.

**Tip**: Exceeding your rate limits may result in 429 (Throttled, please try again later) errors. In such cases, either reduce your request frequency or upgrade your quota.

***

## Best Practices

1. **Store Keys Securely**

* Use environment variables or a secure key management service.
* Avoid committing keys to public Git repositories.

2. **Rotate Keys Periodically**

* If you suspect your key has been exposed, email us immediately for revocation and re-issuance.
* Regular key rotation is a good security measure.

3. **Use Separate Keys for Each Environment**

* Keep your Preprod and Mainnet usage separate for better debugging and resource tracking.

4. **Handle Errors Gracefully**

* Look for `401 Unauthorized` (invalid or missing API key) or `Throttled, please try again later.`.
* Implement retry logic with exponential backoff to avoid overwhelming the service.

***

## Common Troubleshooting

### `401 Unauthorized`

Check that you set the `X-Api-Key` header exactly as provided.

Confirm the key belongs to the specific environment you’re calling.

### `Throttled, please try again later.`

You may have hit your burst or daily limit. Throttle your requests or contact support for higher limits.

### `Incorrect Endpoint`

Verify you’re using the correct URL (Mainnet vs. Preprod vs. Preview). Refer to the Usage page for correct endpoints.

***

## Moving Forward

Once your key is active, you can start exploring all the functionality that **Anvil’s Cardano APIs** offer—everything from minting tokens to submitting transactions and checking balances. Need guidance on environment selection? Head back to our Usage page for a refresher.

**Next Steps:**

* Dive deeper into Select Cardano Environment to ensure you’re building on the right network for your needs.
* Explore UTXOs vs. Change Address to understand Cardano’s transaction model.
* Build your first transaction or NFT mint using our step-by-step guides in the sidebar.

With your **API Key** in hand and the right environment chosen, you’re ready to harness the power of Cardano through Anvil—securely and efficiently!


# Fees

This page outlines all fees associated with using the Anvil API on the Cardano blockchain.

### Fee Structure

Anvil API charges the following fees for operations on the Cardano blockchain: These fees are paid by the end-customer and not the developers building on top of the Anvil API.

| Operation                  | Fee      |
| -------------------------- | -------- |
| Transaction                | 0.15 ADA |
| Smart Contract Interaction | 0.15 ADA |
| Minting                    | 1 ADA    |

#### Fee Details

**Processing Fee**

A fee of **0.15 ADA** is charged to the end user for each Anvil API interaction processed. This covers the cost of processing and validating the interaction on the Cardano blockchain.

**Smart Contract Interaction Fee**

A fee of **0.15 ADA** is charged to the end user for each smart contract interaction processed. This includes contract deployment and any other operations that involve interacting with smart contracts on the Cardano blockchain.

**Minting Fee**

A fee of **1 ADA** is charged to the end user for each minting operation. This covers the additional resources required for token creation and metadata management when minting NFTs or fungible tokens.

> **UI/UX Note for Developers**: To ensure a transparent user experience, applications should clearly communicate the total cost of interaction to end users before they approve transactions. This includes explaining that Anvil API fees will appear as charges from an address that is not your application's address.

### Billing and Payment

Fees are automatically calculated and deducted during the respective operations. No additional action is required from your side to process these payments.

### Questions

If you have any questions about our fee structure or billing process, please contact our support team for assistance at <hello@ada-anvil.io>


# FAQ

## Getting Started

<details>

<summary><strong>How to test the Anvil API?</strong></summary>

To get started with the Anvil API:

1. **Get Your API Keys**: Visit our self-serve portal at [ada-anvil.io/api-keys](https://ada-anvil.io/api-keys) to generate your keys instantly. If the portal is unavailable, please email us at `hello@ada-anvil.io`.
   * A Preprod key for development and testing
   * A Mainnet key with lower rate limits for basic production use
2. **Make Your First API Call**: Include your API key in the X-Api-Key header:

   ```bash
   curl -X GET \
       -H "Content-Type: application/json" \
       -H "X-Api-Key: YOUR_API_KEY_HERE" \
       https://preprod.api.ada-anvil.app/v2/services/health
   ```
3. **Explore the Documentation**: See our [Build Your First Transaction](/guides/transaction/create-basic-transaction) guide or [Cardano Basics](/anvil-api/cardano-basics) if you're new to Cardano.

For more details about authentication, see our [Authentication](/anvil-api/authentication) documentation.

</details>

<details>

<summary><strong>What do I need to test on different Cardano environments?</strong></summary>

> You need two types of API keys: one for testnet environments and one for mainnet, along with the correct endpoint for each network.

The correct endpoints are:

* Preprod: `https://preprod.api.ada-anvil.app/v2/services`
* Preview: `https://preview.api.ada-anvil.app/v2/services`
* Mainnet: `https://prod.api.ada-anvil.app/v2/services`

</details>

<details>

<summary><strong>Is there a difference between testnet and pre-prod, or are they the same thing?</strong></summary>

They are related but distinct:

* **Testnet** refers to any non-production Cardano network. Your testnet API key works across all testnet environments (both Preprod and Preview).
* **Preprod** is a specific testnet environment that closely mirrors mainnet's parameters.
* **Preview** is another testnet environment focused on testing future protocol changes.

Both Preprod and Preview use test ADA (not real value), but they may be running different protocol versions. For most testing purposes, Preprod is recommended.

</details>

<details>

<summary><strong>Is my tech compatible with the API?</strong></summary>

The Anvil API is designed for maximum flexibility and compatibility across technology stacks:

* **Language Agnostic**: Works with any programming language that can make HTTP requests (JavaScript, Python, Ruby, Go, Java, etc.)
* **Multiple Interface Options**: Supports both REST API and tRPC for type-safe operations
* **Standard JSON Format**: Uses standard JSON for request and response bodies
* **Simple Authentication**: Uses straightforward API key authentication via HTTP headers

This means you can easily integrate the Anvil API with:

* Web applications (React, Vue, Angular, etc.)
* Mobile apps (React Native, Flutter, native iOS/Android)
* Backend servers (Node.js, Python, PHP, Ruby, Java, Rust, Go, C#, etc.)
* Command-line tools and scripts

</details>

## Transaction Building

<details>

<summary><strong>Can I build and send Cardano transactions entirely from the client-side (browser)?</strong></summary>

Yes, but with important security considerations:

* **Never expose your API key in client-side code** - API keys should always be kept on your server side
* **Be aware of transaction privacy** - Direct client-side communication can expose transaction details to network observers or browser extensions

The recommended approach is a hybrid architecture:

1. **Frontend (Browser)**: Use [Weld](https://github.com/Cardano-Forge/weld) to connect wallets and handle transaction signing

   ```javascript
   const wallet = useWallet();
   if (wallet.isConnected) {
     // Frontend only handles wallet connection and signing
     const signature = await wallet.handler.signTx(txCbor);
   }
   ```
2. **Backend (Your Server)**: Create a secure backend service that:
   * Holds your Anvil API key securely
   * Communicates with Anvil API to build transactions
   * Sends the transaction CBOR to your frontend for signing
   * Receives the signature and submits the completed transaction

This architecture ensures your API key remains secure while still leveraging client-side wallet capabilities.

For testing purposes only, you can use the browser console to interact directly with a wallet:

```javascript
const w = await window.cardano.eternl.enable();
await w.signTx("Your transaction CBOR", true);
```

</details>

<details>

<summary><strong>How do I sign a transaction to send ADA or NFTs?</strong></summary>

For signing transactions:

1. **Browser/Frontend**: Use Weld to connect to the user's wallet and request signatures

   ```javascript
   // First connect to the wallet using Weld
   const wallet = useWallet();

   if (wallet.isConnected) {
     // Get the wallet to sign your transaction CBOR
     const signature = await wallet.handler.signTx(txCbor);
     // Submit the signed transaction
     // ...
   }
   ```

For detailed backend signing examples in multiple languages, see our comprehensive [Sign Transaction guide](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/transaction/signing-transaction.md).

</details>

<details>

<summary><strong>Do I need to provide UTXOs when building transactions?</strong></summary>

Yes, you need to provide UTXOs when building transactions.

* **For Testing ONLY (Preprod/Preview)**: You can optionally let Anvil handle UTXO selection automatically.

  ```javascript
  // Just provide a change address in test environments
  const response = await fetch('https://preprod.api.ada-anvil.app/v2/services/transactions/build', {
    // ...
    body: JSON.stringify({
      changeAddress: 'addr_test...',
      // No UTXOs needed for testing!
      outputs: [/* ... */]
    })
  });
  ```
* **For Production (Mainnet)**: You must explicitly provide UTXOs

  ```javascript
  // UTXOs are required in production
  const response = await fetch('https://prod.api.ada-anvil.app/v2/services/transactions/build', {
    // ...
    body: JSON.stringify({
      utxos: ["8282...", "8282..."],
      changeAddress: 'addr_...',
      outputs: [/* ... */]
    })
  });
  ```

See [Selecting UTXOs for Transactions](/guides/transaction/selecting-utxos) for more details.

</details>

## Development Workflow

<details>

<summary><strong>What's the recommended workflow for developing with Anvil API?</strong></summary>

1. **Start with Preprod**: Use test ADA from the faucet to develop and test your application
2. **Test edge cases**: Ensure your code handles various scenarios correctly
3. **Optional: Test on Preview**: If you need to test with upcoming protocol features
4. **Move to Mainnet**: Once thoroughly tested, switch to Mainnet endpoints and API key

Remember to keep your environments and API keys separate to avoid accidentally using real ADA during testing.

</details>

<details>

<summary><strong>How do I handle wallet integration in my application?</strong></summary>

We strongly recommend using [Weld](https://github.com/Cardano-Forge/weld) for wallet integration. It provides:

* A unified API for multiple Cardano wallets (Eternl, Lace, Flint, etc.)
* Simple hooks for React applications
* Comprehensive wallet state management
* Utilities for common wallet operations

Check our [Transaction Overview](/guides/transaction) for a complete flow using Weld and Anvil API.

</details>

## Smart Contracts and Plutus Scripts

<details>

<summary><strong>Does the API support smart contract interaction and Plutus scripts?</strong></summary>

Yes, Anvil API fully supports Plutus smart contract interactions:

1. **Blueprint Management (CIP-57)**: Upload your compiled Plutus scripts as blueprints:

   ```bash
   # Example: Create a blueprint from an Aiken compiler output
   curl -X POST \
       -H "Content-Type: application/json" \
       -H "X-Api-Key: YOUR_API_KEY" \
       https://preprod.api.ada-anvil.app/v2/services/blueprints \
       -d '{"blueprint": {...}}'  # Your Plutus script blueprint
   ```
2. **Script Interactions**: Once uploaded, you can interact with the contract in transactions by referencing the blueprint and providing required datum/redeemer data.

See our guides on [Blueprint Management](/guides/smart-contract/blueprint-management) and example smart contracts like our [Simple Mint Smart Contract](/guides/smart-contract/mint-smart-contract) for practical demonstrations.

</details>

## API Technical Details

<details>

<summary><strong>Are there rate limits or usage quotas?</strong></summary>

Yes, every API key comes with defined usage limits that protect both you and the service:

* **Daily Request Limits**: Maximum number of requests per 24-hour period, 300k requests per day per key
* **Requests Per Second (RPS)**: Maximum throughput rate, 100 requests per second per key

These limits provide important security and stability benefits:

1. **Preventing Unauthorized Use**: If your API key is compromised, rate limits minimize potential abuse by capping usage
2. **Cost Control**: Helps avoid unexpected charges by setting clear boundaries on API consumption
3. **Service Reliability**: Ensures no single client can overload the infrastructure, maintaining high availability for everyone
4. **Attack Mitigation**: Protects against DDoS and brute force attacks by throttling excessive requests

If you exceed these limits, the API will respond with `Throttled, please try again later.`. Your exact quotas are configured when your API key is issued and can be tailored to your application's specific needs.

For higher limits, please contact <hello@ada-anvil.io>.

</details>

<details>

<summary><strong>How is the transaction builder versioned?</strong></summary>

The Anvil API transaction builder uses request payload versioning to maintain backward compatibility:

```json
{
  "version": 1,
  // Other request parameters
}
```

Endpoints that support versioning accept a `version` field in the request body. This allows endpoints to evolve their functionality while maintaining backward compatibility with existing integrations.

</details>

<details>

<summary><strong>How does Anvil handle blockchain upgrades and protocol changes?</strong></summary>

Anvil's infrastructure is updated automatically to support every Cardano protocol upgrade (hard forks, new ledger rules, Plutus enhancements) with zero downtime. You don't need to change your integration—your existing endpoints continue working as the underlying node software is upgraded behind the scenes.

For testing upcoming features, you can point to the Preview endpoints, which run the latest protocol version before it hits Mainnet, so you can verify compatibility in advance.

</details>

<details>

<summary><strong>How do I query blockchain data like transaction history or UTXOs?</strong></summary>

The Anvil API focuses primarily on transaction building and submission rather than data querying.

* **UTXO Management**: In test environments, automatic UTXO selection is available
* **Production Requirements**: For mainnet, you must explicitly provide UTXOs
* **Data Querying**: For blockchain exploration (transaction history, balances), we recommend using dedicated blockchain explorers or data APIs like BlockFrost

Note: While not part of our standard API, we do provide specialized blockchain querying and indexing services for enterprise customers. If you need comprehensive blockchain data access for your application, please contact <hello@ada-anvil.io> to discuss your requirements.

See [Selecting UTXOs for Transactions](/guides/transaction/selecting-utxos) for more information about working with UTXOs in Anvil API.

</details>

## Support and Feedback

<details>

<summary><strong>I need help, I have a question or feedback</strong></summary>

Send an email to: <hello@ada-anvil.io>

Join the conversation in Discourse: (coming soon)

</details>


# Troubleshooting

Common Errors and Troubleshooting.

When building on the Anvil API (Cardano), you may encounter various errors related to transaction submission, UTXOs, API components, or authorization. This page documents common error messages, explains their causes (Cardano-specific vs Anvil-specific), and provides solutions or workarounds for each.

## Transaction Submission Failures

These errors occur when a transaction fails Cardano's validation rules during building or submission. They are typically Cardano-specific (originating from the Cardano node/ledger) but can surface through the Anvil API's responses.

<details>

<summary><strong>Missing Required Signatures (MissingVKeyWitnessesUTXOW)</strong></summary>

**What it means**: The node is telling you that one or more required transaction signatures (vkey witnesses) are missing. The error MissingVKeyWitnessesUTXOW appears when the transaction hasn't been signed by all the necessary keys needed to validate the transaction.

**Cardano or Anvil**: Cardano-specific (signature validation failure). Anvil passes along the node's error.

**Solution**:

1. **Include all signatures**: Ensure all required signatures are present in the transaction:
   * For ADA spending: Include the address's payment signing key
   * For minting/burning: Include the native script's keyhash (minting/burning tokens specifically requires the policy signature)
   * For script interactions: Include any required script witnesses
2. **Complete frontend signing**: When building via API and signing on frontend, verify the signed payload contains all required signatures

</details>

<details>

<summary><strong>Transaction Validity Interval Error (OutsideValidityIntervalUTxO)</strong></summary>

**What it means**: The transaction's validity interval is outside the current blockchain slot range. This error indicates that the transaction is either expired (current slot > end slot/TTL) or not yet valid (current slot < start slot).

**Cardano or Anvil**: Cardano-specific (timing validation).

**API Details**:

* In Anvil's API, you can specify the validity interval using the `validityInterval` parameter:

  ```json
  {
    "validityInterval": {
      "start": number | false, // POSIX timestamp or slot number (optional)
      "end": number | false   // POSIX timestamp or slot number (optional)
    }
  }
  ```
* **Default values** (when not specified):
  * `start`: Current blockchain slot (from chain indexer)
  * `end`: Current blockchain slot + 2 hours (from chain indexer)
* Set a boundary to `false` to disable it entirely

**Common scenarios**:

* Transaction submitted after its TTL has expired
* Transaction submitted before its start time is reached
* Clock differences between systems
* Chain indexer issues causing incorrect validity interval calculations

**Solution**:

1. **Explicitly set validity interval**: Instead of relying on defaults, specify both `start` and `end`:
   * For immediate transactions: Set `start` to a recent slot and `end` to at least 30 minutes in the future
   * For scheduled transactions: Set appropriate future `start` time and ensure `end` provides enough window
2. **Use relative values**: Calculate values relative to current time rather than using absolute slots
3. **Chain indexer issues**: If you've verified your validity interval is correct but still get this error, the chain indexer may be out of sync. In this case contact Anvil support if the issue persists

**Note on terminology**: Cardano error messages use `invalidBefore` (= Anvil's `start`) and `invalidHereafter` (= Anvil's `end`).

</details>

<details>

<summary><strong>Smart Contract Script Failure (Validation Error 3010/3012)</strong></summary>

**What it means**: Your smart contract returned `False` during validation or encountered an execution error. Errors typically appear with codes 3010 ("Some scripts of the transaction terminated with errors") or 3012 ("Some scripts failed to evaluate to a positive outcome").

**Cardano or Anvil**: Cardano-specific (on-chain script validation).

**Common causes**:

* Contract logic rejected the transaction parameters
* Incorrect datum or redeemer values
* Missing required inputs or signatures
* Execution units exceeded

**Solution**:

1. **Review error traces**: The `data.validationError` and `data.traces` fields contain valuable debugging information
2. **For Aiken contracts**: Rebuild your contract with verbose mode to get detailed execution traces:

   ```
   aiken build --verbose
   ```
3. **Check parameters**: Verify all datum values, redeemers, and required signatures

This error is specific to your smart contract's logic - the solution depends entirely on the contract code and the validation conditions it requires.

</details>

<details>

<summary><strong>Malformed Transaction Submission (Raw CBOR Decode Error)</strong></summary>

**What it means**: The node cannot decode your transaction because the CBOR format is incorrect. Error messages include "DecoderErrorDeserialiseFailure" or "RawCborDecodeError".

**Cardano or Anvil**: Cardano error (node-level validation).

**Common causes**:

1. **Incorrect encoding**: Transaction must be CBOR Hex Encoded
2. **Missing signatures**: Incomplete transaction witness data
3. **Improper signing workflow**: Incorrect signature appending

**Solution**:

1. **Use proper encoding**: When submitting via REST, ensure you're using CBOR Hex Encoded format
2. **Verify complete signatures**: Make sure all required signatures are included
3. **Follow correct signing flow**: When using Anvil's API where the backend returns an unsigned tx hash to be signed by the frontend, append signatures only in the designated witness fields

</details>

## UTXO/Input Issues

Errors in this category relate to UTXOs (Unspent Transaction Outputs) and inputs selection for your transactions. They might be Cardano errors triggered by incorrect input usage or Anvil-specific validations about how inputs are provided.

<details>

<summary><strong>Insufficient Inputs (Balance Shortage)</strong></summary>

**What it means**: Your transaction inputs don't have enough value to cover all outputs and fees. The error typically shows: "Insufficient input in transaction. shortage: {ada in inputs: X, ada in outputs: Y, fee: Z}".

**Key requirement**: The rule "ada in inputs" must be >= ("ada in outputs" + fee) before adding change.

**Cardano or Anvil**: Cardano-specific (transaction balancing).

**Common causes**:

* Insufficient ADA for outputs, fees, and minimum deposit requirements
* Missing tokens you're trying to send
* Wrong network (using testnet addresses when on mainnet or vice versa)

**Solution**:

1. **Add more inputs**: Include additional UTXOs from your wallet
2. **Check token presence**: Verify the UTXO contains the specific tokens (NFTs, FTs, and/or ADA) you're sending
3. **Verify network**: Ensure you're using addresses on the correct network

This error can apply to both ADA and native assets (NFTs and tokens).

</details>

<details>

<summary><strong>Using a Spent or Invalid UTXO (BadInputsUTxO / ValueNotConservedUTxO)</strong></summary>

**What it means**: This error combo occurs when one of the UTXO inputs you included is not actually available to spend. BadInputsUTxO means the transaction is referencing an input that the ledger finds invalid – typically because it's already spent (or doesn't exist).

**Cardano or Anvil**: Cardano-specific (UTXO set validation). Anvil just surfaces it.

**Solution**:

1. **Check UTXO status**: If you recently used a UTXO in a transaction, consider it already spent
2. **Refresh your wallet**: Always fetch the latest UTXOs from the address by refreshing your wallet before building a new transaction
3. **Use different UTXOs**: For multiple back-to-back transactions, use separate UTXOs for each transaction
4. **Inspect error details**: If the error persists, check the specific TxIn hash mentioned in the error message

</details>

<details>

<summary><strong>UTXO Fragmentation (UtxoNotEnoughFragmented / "Wallet fragmentation error")</strong></summary>

**What it means**: "Wallet fragmentation" refers to the distribution of funds across UTXOs. An error like UtxoNotEnoughFragmented means you don't have enough separate UTXOs to fulfill all the outputs you are trying to create.

**Cardano or Anvil**: This is more of a higher-level Cardano wallet limitation/feature.

**Solution**:

1. **Consolidate and re-split**: If your funds are in a single UTXO, first create a transaction to split it into multiple smaller UTXOs
2. **Reduce batch size**: Break large multi-recipient transactions into smaller batches with fewer outputs
3. **Check UTXO count**: Use wallet tools to verify your UTXO count matches your transaction requirements
4. **Verify balance**: Ensure you have sufficient funds available if the error is actually related to balance

</details>

<details>

<summary><strong>Providing UTXOs vs. Using an Address (Blockfrost "Component not found")</strong></summary>

**What it means**: No UTXOs found for the provided address. The error appears as "The requested component has not been found" with internal code -32603 or HTTP status 500 from Blockfrost.

**Cardano or Anvil**: Blockfrost response error when querying address UTXOs.

**Address format support**:

* Anvil API supports both bech32 addresses (addr1...) and hex-encoded addresses
* The address must be valid and properly formatted

**Solution**:

1. **Fund the address first**: Ensure the address has received at least one transaction
2. **Use explicit UTXOs**:

   * **Frontend**: Connect through wallet interfaces (e.g., [Weld](https://github.com/Cardano-Forge/weld)) to access UTXOs directly from the user's wallet
   * **Backend**: Fetch UTXOs using Blockfrost or other Cardano service providers and provide them in the `utxos` field of your build request
   * **Note**: Backend UTXO handling for your address may vary based on wallet settings (e.g., Eternl multi-account configuration)

   See [Selecting UTXOs](/guides/transaction/selecting-utxos) for more detailed information.
3. **Verify address format**: Double-check that you're using a valid bech32 or hex-encoded address format

This error simply means there are no UTXOs associated with the address you provided.

</details>

<details>

<summary><strong>Mainnet UTXO Selection Requirement ("Utxos must be set on mainnet")</strong></summary>

**What it means**: This is an Anvil API validation error (HTTP 400) that explicitly requires you to provide UTXO inputs in the request when operating on mainnet. The error message Input validation failed ... "Utxos must be set on mainnet" is telling you that unlike test environments, the mainnet API will not automatically pick UTXOs for you.

**Cardano or Anvil**: Anvil-specific. This is not coming from Cardano node, but from Anvil's API input checker.

**Solution**:

1. **Retrieve UTXOs**: First, fetch the UTXOs for the wallet address you want to spend from
2. **Include UTXOs in request**: Add the `utxos` field to your build transaction request, listing each UTXO you want to use as input. See [Selecting UTXOs](/guides/transaction/selecting-utxos) for more details.
3. **Verify network**: Confirm you're correctly pointing to mainnet

This requirement exists to ensure developers make deliberate decisions about which UTXOs to spend when operating on mainnet.

</details>

<details>

<summary><strong>Policy Script Hash Mismatch (UNPROCESSABLE_CONTENT)</strong></summary>

**What it means**: The error message "Failed to build transaction > Unable to get policy scripts > Policy script and policy id do not match" indicates that the native script content doesn't match the policy ID hash being used.

**Cardano or Anvil**: This is primarily a Cardano validation error that Anvil surfaces.

**Common causes**:

* The native script JSON in the preloaded script section was altered but the policy ID wasn't updated accordingly
* The policy ID was manually entered or calculated incorrectly
* Using the wrong script version for a specific policy ID

**Solution**:

1. **Verify script content**: The native script (the json in the preloaded script section) is different from the hash being used. You should double check that you didn't alter the native script and generate a new hash if necessary.
2. **Regenerate hash if needed**: If you intentionally modified the script, you need to regenerate the hash to match.
3. **Use consistent script**: Ensure you're using exactly the same script content throughout your application.

</details>

<details>

<summary><strong>Invalid Address Format (ScriptWitnessNotValidatingUTXOW)</strong></summary>

**What it means**: When you see a transaction submission error containing "ConwayUtxoValidationError" and "ScriptWitnessNotValidatingUTXOW" followed by "missing human-readable separator", it typically indicates an invalid address format is being used. The error message appears as: "Failed to submit tx > {"contents":{"contents":{"era":"ShelleyBasedEraConway","error":{"ConwayUtxoValidationError (ScriptWitnessNotValidatingUTXOW ...", code: "UNPROCESSABLE\_CONTENT", error: 'missing human-readable separator, "!"'

**Cardano or Anvil**: This is a Cardano validation error related to address format checking.

**Common causes**:

* Using a malformed address that's neither a valid hex-encoded nor bech32 format
* Truncated or corrupted address strings
* Mixing address formats from different networks (testnets vs mainnet)

**Solution**:

1. **Validate address format**: Ensure all addresses in your transaction are either valid bech32 format (starting with addr1..., addr\_test1..., etc.) or properly hex-encoded
2. **Check for truncation**: Verify that addresses haven't been truncated during processing
3. **Use network-appropriate addresses**: Confirm you're using addresses appropriate for the network you're targeting (mainnet vs testnet)

</details>

## Component or Endpoint Errors

These errors are related to specific components of the Anvil platform or external endpoints it relies on (like Blockfrost).

<details>

<summary><strong>External Data Not Yet Available (Policy Script/CBOR Not Found)</strong></summary>

**What it means**: This error occurs when Anvil tries to retrieve on-chain data that hasn't settled yet. The error message typically looks like: "Unable to get policy scripts > Failed to get cbor for tx hash > The requested component has not been found" with code 422.

**Cardano or Anvil**: This is a timing issue related to blockchain confirmation times.

**Common causes**:

1. **Transaction not yet settled**: The transaction containing the native script hasn't been fully confirmed

**Solution**:

1. **Wait for on-chain settlement**: Allow more time for the transaction to be confirmed before trying to use its data
2. **Implement retry logic**: If building an application, add a delay and retry mechanism
3. **Check network**: Verify you're looking for the transaction on the same network where it was submitted

</details>

## Authorization/Access Issues

These errors relate to authentication (API keys) or account-level access permissions in Anvil.

<details>

<summary><strong>Invalid or Missing API Key (401 Unauthorized)</strong></summary>

**What it means**: A 401 Unauthorized response indicates the request did not have proper authentication. The Anvil API requires an API key (in the X-Api-Key header) for every request.

**Solution**:

1. **Obtain API key**: Request an API key from Anvil if you don't already have one
2. **Use correct header format**: Include the header exactly as documented: `X-Api-Key: <YOUR_API_KEY>`
3. **Verify environment URL**: Ensure you're calling the correct base URL for your key's environment (testnet vs mainnet)
4. **Secure your key**: Store your API key securely and don't expose it in client-side code

</details>

<details>

<summary><strong>Throttled, please try again later.</strong></summary>

**What it means**: A Throttled response occurs when you're making requests faster than the allowed burst rate limit.

**Cardano or Anvil**: Anvil-specific. This is our API rate limiting control.

**Solution**:

1. **Implement backoff strategy**: Add exponential backoff with retries when you detect a Throttled response
2. **Space out requests**: Ensure your application doesn't send too many requests in quick succession
3. **Batch operations**: Where possible, combine multiple operations into single API calls
4. **Monitor usage**: Track your API consumption to anticipate potential throttling issues

</details>

<details>

<summary><strong>Daily quota exceeded, please contact Anvil support.</strong></summary>

**What it means**: This error occurs when you've exceeded your daily API quota allocation.

**Cardano or Anvil**: Anvil-specific. This is an API usage quota control.

**Solution**:

1. **Review API usage**: Analyze your daily API usage patterns to identify optimization opportunities
2. **Optimize requests**: Reduce unnecessary API calls in your application code
3. **Request quota increase**: If your use case legitimately requires a higher quota, contact Anvil support at <hello@ada-anvil.io>
4. **Implement caching**: Where appropriate, cache API responses to reduce the number of calls

</details>

<details>

<summary><strong>Blueprint Validator Ownership Conflict (Validators in Another Blueprint)</strong></summary>

**What it means**: This error message: "Failed to upsert blueprint: Some validators are associated with another blueprint" (500 Internal Server Error) occurs when you attempt to register or update a smart contract blueprint (CIP-57 blueprint) that contains validators (scripts) which are already tied to a different blueprint.

**Cardano or Anvil**: Anvil-specific – this is about Anvil's blueprint management component, not a Cardano on-chain error.

**Solution**:

1. **Contact support**: Reach out to Anvil support with details about your account, the blueprint ID/name, and the specific script hashes
2. **Verify ownership**: Be prepared to verify your identity and project ownership as requested
3. **Blueprint resolution**: The Anvil team may clear or merge blueprint records so you can upload your blueprint again

</details>


# Support

Support options and resources for Anvil API users.

## Self-Help Resources

Before contacting our support team, please check if your question or issue is already addressed in our documentation:

* Check our [FAQ](/anvil-api/faq) for answers to common questions
* Review the [Troubleshooting](/anvil-api/troubleshooting) guide for solutions to known errors
* Explore our developer guides for detailed implementation instructions

{% content-ref url="/pages/raAU0hxw2CxBoaKyEvn7" %}
[FAQ](/anvil-api/faq)
{% endcontent-ref %}

{% content-ref url="/pages/KNlZ93TVVmJYM7nML8Iz" %}
[Troubleshooting](/anvil-api/troubleshooting)
{% endcontent-ref %}

## Contact Support

If you couldn't find a solution in our documentation, please reach out to us through one of the following channels:

### Email Support

For technical issues or account-related questions:

* Email: <hello@ada-anvil.io>
* Please include detailed information about your issue, including error messages and steps to reproduce

## Support Hours

Our team is available to respond to inquiries 24/7. However depending on your API tier, you may experience longer response times.

## When Contacting Support

To help us resolve your issue more quickly, please include:

1. Your API key identifier or email address associated with your API key
2. Environment (mainnet, preprod, preview, etc.)
3. Context of what you are building. (e.g. a Minting dApp, a DeFi application, etc.)
4. Detailed description of the issue
5. Relevant error messages and timestamps
6. Steps to reproduce the problem
7. Any code samples (with sensitive information removed)


# Transaction

This section provides quick-start examples for creating, customizing, and submitting transactions using the Anvil API.

{% content-ref url="/pages/YunxTthcOz1f9g2fAacM" %}
[Basic Transaction](/quick-start-examples/transaction/basic-transaction)
{% endcontent-ref %}

{% content-ref url="/pages/ZQnPQECRcKijIBeb2nqZ" %}
[Custom Transaction](/quick-start-examples/transaction/custom-transaction)
{% endcontent-ref %}

{% content-ref url="/pages/22J9NZvLWtNTOi2mgXRU" %}
[Submit Transaction](/quick-start-examples/transaction/submit-transaction)
{% endcontent-ref %}

{% content-ref url="/pages/Fh9nifBSb4m05si5fRpq" %}
[Transaction Metadata (CIP-20)](/quick-start-examples/transaction/cip-20)
{% endcontent-ref %}


# Basic Transaction

This section provides examples of how to create a basic transaction using Anvil API in various programming languages. Select a language from the navigation to see the specific implementation.

{% content-ref url="/pages/2jWquIuhQ14yN8uyFn3U" %}
[TS/JS + Fetch](/quick-start-examples/transaction/basic-transaction/typescript)
{% endcontent-ref %}

{% content-ref url="/pages/fBibRw1Fl7wwSuUCvnUP" %}
[Python + Requests](/quick-start-examples/transaction/basic-transaction/python)
{% endcontent-ref %}

{% content-ref url="/pages/yENkvNWrA2VP5vMhPV0t" %}
[Go + net/http](/quick-start-examples/transaction/basic-transaction/go)
{% endcontent-ref %}

{% content-ref url="/pages/dzaVn3oZZ69BRvz36N0F" %}
[Elixir + HTTPoison](/quick-start-examples/transaction/basic-transaction/elixir)
{% endcontent-ref %}

{% content-ref url="/pages/f5B4OFnK86zQXbZIm13d" %}
[Rust + reqwest](/quick-start-examples/transaction/basic-transaction/rust)
{% endcontent-ref %}

{% content-ref url="/pages/27sJnqOyJ3LSb5PINuEB" %}
[Dart + http](/quick-start-examples/transaction/basic-transaction/dart)
{% endcontent-ref %}

{% content-ref url="/pages/oV5So6KJzsJxF3DvgrHB" %}
[PHP + cURL](/quick-start-examples/transaction/basic-transaction/php)
{% endcontent-ref %}

{% content-ref url="/pages/xBm8F5davhLsoruaL6fc" %}
[Swift + URLSession](/quick-start-examples/transaction/basic-transaction/swift)
{% endcontent-ref %}

{% content-ref url="/pages/jWPKOxUbcdodzHjiCUIS" %}
[C++ + libcurl](/quick-start-examples/transaction/basic-transaction/cpp)
{% endcontent-ref %}

{% content-ref url="/pages/LOVFFEu2ulphYtrapNzO" %}
[ESP32 + HTTPClient](/quick-start-examples/transaction/basic-transaction/esp32)
{% endcontent-ref %}

{% content-ref url="/pages/paa3SNY5XAkj8aMMVAoR" %}
[C# + HttpClient](/quick-start-examples/transaction/basic-transaction/csharp)
{% endcontent-ref %}

{% content-ref url="/pages/FJs17gXWfBDXm61byRlI" %}
[Kotlin + HttpClient](/quick-start-examples/transaction/basic-transaction/kotlin)
{% endcontent-ref %}

{% content-ref url="/pages/nT1NRfhpJ6zzR6Hm8smy" %}
[Java + HttpClient](/quick-start-examples/transaction/basic-transaction/java)
{% endcontent-ref %}


# TS/JS + Fetch

Learn how to create a basic Cardano transaction using TypeScript or JavaScript with the Anvil API. This guide provides a complete code example for building a transaction.

{% hint style="info" %}
**Need a wallet address?**

You can use the [Anvil Wallet CLI](/developer-tools/wallet-cli) to generate a wallet and get an address for these examples.
{% endhint %}

{% @github-files/github-code-block title="utils/constant.ts" url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/utils/constant.ts>" %}

{% @github-files/github-code-block url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/basic-tx.ts>" %}

{% hint style="success" %}
**Learn More**

For a detailed overview of the transaction lifecycle, see the [Transaction Guide](/guides/transaction).
{% endhint %}


# Python + Requests

Learn how to create a basic Cardano transaction using Python with the \`requests\` library and the Anvil API. This guide provides a complete code example for building a transaction.

{% hint style="info" %}
**Need a wallet address?**

You can use the [Anvil Wallet CLI](/developer-tools/wallet-cli) to generate a wallet and get an address for these examples.
{% endhint %}

{% @github-files/github-code-block url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/basic-tx.py>" %}

{% hint style="success" %}
**Learn More**

For a detailed overview of the transaction lifecycle, see the [Transaction Guide](/guides/transaction).
{% endhint %}


# Go + net/http

Learn how to create a basic Cardano transaction using Go with the \`net/http\` package and the Anvil API. This guide provides a complete code example for building a transaction.

{% hint style="info" %}
**Need a wallet address?**

You can use the [Anvil Wallet CLI](/developer-tools/wallet-cli) to generate a wallet and get an address for these examples.
{% endhint %}

{% @github-files/github-code-block url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/basic-tx.go>" %}

{% hint style="success" %}
**Learn More**

For a detailed overview of the transaction lifecycle, see the [Transaction Guide](/guides/transaction).
{% endhint %}


# Elixir + HTTPoison

Learn how to create a basic Cardano transaction using Elixir with the \`HTTPoison\` library and the Anvil API. This guide provides a complete code example for building a transaction.

{% hint style="info" %}
**Need a wallet address?**

You can use the [Anvil Wallet CLI](/developer-tools/wallet-cli) to generate a wallet and get an address for these examples.
{% endhint %}

{% @github-files/github-code-block url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/basic-tx-elixir/lib/basic-ts.ex>" %}

{% hint style="success" %}
**Learn More**

For a detailed overview of the transaction lifecycle, see the [Transaction Guide](/guides/transaction).
{% endhint %}


# Rust + reqwest

Learn how to create a basic Cardano transaction using Rust with the \`reqwest\` crate and the Anvil API. This guide provides a complete code example for building a transaction.

{% hint style="info" %}
**Need a wallet address?**

You can use the [Anvil Wallet CLI](/developer-tools/wallet-cli) to generate a wallet and get an address for these examples.
{% endhint %}

{% @github-files/github-code-block url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/basic-tx-rust/src/main.rs>" %}

{% hint style="success" %}
**Learn More**

For a detailed overview of the transaction lifecycle, see the [Transaction Guide](/guides/transaction).
{% endhint %}


# Dart + http

Learn how to create a basic Cardano transaction using Dart with the \`http\` package and the Anvil API. This guide provides a complete code example for building a transaction.

{% hint style="info" %}
**Need a wallet address?**

You can use the [Anvil Wallet CLI](/developer-tools/wallet-cli) to generate a wallet and get an address for these examples.
{% endhint %}

{% @github-files/github-code-block url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/basic-tx-dart/basic-tx.dart>" %}

{% hint style="success" %}
**Learn More**

For a detailed overview of the transaction lifecycle, see the [Transaction Guide](/guides/transaction).
{% endhint %}


# PHP + cURL

Learn how to create a basic Cardano transaction using PHP with \`cURL\` and the Anvil API. This guide provides a complete code example for building a transaction.

{% hint style="info" %}
**Need a wallet address?**

You can use the [Anvil Wallet CLI](/developer-tools/wallet-cli) to generate a wallet and get an address for these examples.
{% endhint %}

{% @github-files/github-code-block url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/basic-tx.php>" %}

{% hint style="success" %}
**Learn More**

For a detailed overview of the transaction lifecycle, see the [Transaction Guide](/guides/transaction).
{% endhint %}


# Swift + URLSession

Learn how to create a basic Cardano transaction using Swift with the Anvil API. This guide provides a complete code example for building a transaction.

{% hint style="info" %}
**Need a wallet address?**

You can use the [Anvil Wallet CLI](/developer-tools/wallet-cli) to generate a wallet and get an address for these examples.
{% endhint %}

{% @github-files/github-code-block url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/basic-tx.swift>" %}

{% hint style="success" %}
**Learn More**

For a detailed overview of the transaction lifecycle, see the [Transaction Guide](/guides/transaction).
{% endhint %}


# C++ + libcurl

Learn how to create a basic Cardano transaction using C++ with the Anvil API. This guide provides a complete code example for building a transaction.

{% hint style="info" %}
**Need a wallet address?**

You can use the [Anvil Wallet CLI](/developer-tools/wallet-cli) to generate a wallet and get an address for these examples.
{% endhint %}

{% @github-files/github-code-block url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/basic-tx.cpp>" %}

{% hint style="success" %}
**Learn More**

For a detailed overview of the transaction lifecycle, see the [Transaction Guide](/guides/transaction).
{% endhint %}


# ESP32 + HTTPClient

Learn how to create a basic Cardano transaction using ESP32 with the Anvil API. This guide provides a complete code example for IoT blockchain integration.

{% hint style="info" %}
**Need a wallet address?**

You can use the [Anvil Wallet CLI](/developer-tools/wallet-cli) to generate a wallet and get an address for these examples.
{% endhint %}

{% @github-files/github-code-block url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/basic-tx-esp32.cpp>" %}

{% hint style="success" %}
**Learn More**

For a detailed overview of the transaction lifecycle, see the [Transaction Guide](/guides/transaction).
{% endhint %}


# C# + HttpClient

Learn how to create a basic Cardano transaction using C# with the Anvil API. This guide provides a complete code example for building a transaction.

{% hint style="info" %}
**Need a wallet address?**

You can use the [Anvil Wallet CLI](/developer-tools/wallet-cli) to generate a wallet and get an address for these examples.
{% endhint %}

{% @github-files/github-code-block url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/basic-tx.cs>" %}

{% hint style="success" %}
**Learn More**

For a detailed overview of the transaction lifecycle, see the [Transaction Guide](/guides/transaction).
{% endhint %}


# Kotlin + HttpClient

Learn how to create a basic Cardano transaction using Kotlin with the Anvil API. This guide provides a complete code example for building a transaction.

{% hint style="info" %}
**Need a wallet address?**

You can use the [Anvil Wallet CLI](/developer-tools/wallet-cli) to generate a wallet and get an address for these examples.
{% endhint %}

{% @github-files/github-code-block url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/basic-tx.kt>" %}

{% hint style="success" %}
**Learn More**

For a detailed overview of the transaction lifecycle, see the [Transaction Guide](/guides/transaction).
{% endhint %}


# Java + HttpClient

Learn how to create a basic Cardano transaction using Java with the Anvil API. This guide provides a complete code example for building a transaction.

{% hint style="info" %}
**Need a wallet address?**

You can use the [Anvil Wallet CLI](/developer-tools/wallet-cli) to generate a wallet and get an address for these examples.
{% endhint %}

{% @github-files/github-code-block url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/BasicTx.java>" %}

{% hint style="success" %}
**Learn More**

For a detailed overview of the transaction lifecycle, see the [Transaction Guide](/guides/transaction).
{% endhint %}


# Custom Transaction

This section provides examples of how to create a custom transaction using Anvil API. A custom transaction gives you full control over the inputs, outputs, and other transaction parameters.

{% content-ref url="/pages/gUPsi9WGSSo3MY10eoZ0" %}
[Bash & cURL](/quick-start-examples/transaction/custom-transaction/bash-and-curl)
{% endcontent-ref %}

{% content-ref url="/pages/l6NxFE4shC4NBnfgUQJb" %}
[TS/JS + Fetch](/quick-start-examples/transaction/custom-transaction/typescript)
{% endcontent-ref %}


# Bash & cURL

Learn how to create a custom Cardano transaction using Bash and cURL with the Anvil API.

{% hint style="info" %}
**Need a wallet address?**

You can use the [Anvil Wallet CLI](/developer-tools/wallet-cli) to generate a wallet and get an address for these examples.
{% endhint %}

{% @github-files/github-code-block url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/custom-tx.sh>" %}

{% hint style="success" %}
**Learn More**

For a detailed overview of the transaction lifecycle, see the [Transaction Guide](/guides/transaction).
{% endhint %}


# TS/JS + Fetch

Learn how to create a custom Cardano transaction using TypeScript or JavaScript with the Anvil API.

{% hint style="info" %}
**Need a wallet address?**

You can use the [Anvil Wallet CLI](/developer-tools/wallet-cli) to generate a wallet and get an address for these examples.
{% endhint %}

{% @github-files/github-code-block title="utils/constant.ts" url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/utils/constant.ts>" %}

{% @github-files/github-code-block url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/custom-tx.ts>" %}

{% hint style="success" %}
**Learn More**

For a detailed overview of the transaction lifecycle, see the [Transaction Guide](/guides/transaction).
{% endhint %}


# Expiring Transaction

This section explains how to create a Cardano transaction that automatically expires after a specific time using a Time-to-Live (TTL) value. This is useful for time-sensitive operations where a transaction should not be valid indefinitely.

Select an implementation from the navigation to see a complete code example.

{% content-ref url="/pages/RFZfxshIaXd1B2plw57b" %}
[TS/JS + Fetch](/quick-start-examples/transaction/expiring-transaction/typescript)
{% endcontent-ref %}


# TS/JS + Fetch

Learn how to create a Cardano transaction that expires after a certain time (TTL) using TypeScript or JavaScript with the Anvil API.

{% @github-files/github-code-block title="utils/constant.ts" url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/utils/constant.ts>" %}

{% @github-files/github-code-block url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/tx-with-validity-interval.ts>" %}

{% hint style="success" %}
**Learn More**

For a detailed overview of the transaction lifecycle, see the [Transaction Guide](/guides/transaction).
{% endhint %}


# Submit Transaction

This section provides examples of how to submit a signed transaction to the Cardano network using the Anvil API.

{% content-ref url="/pages/GIx2LXV2XzCUOJJ3jA8V" %}
[TS/JS + Fetch](/quick-start-examples/transaction/submit-transaction/typescript)
{% endcontent-ref %}

{% content-ref url="/pages/yeqQGPANxAooOOxWsTMr" %}
[Bash & cURL](/quick-start-examples/transaction/submit-transaction/bash-and-curl)
{% endcontent-ref %}


# TS/JS + Fetch

Learn how to submit a signed Cardano transaction using TypeScript or JavaScript and the Anvil API.

{% @github-files/github-code-block title="utils/constant.ts" url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/utils/constant.ts>" %}

{% @github-files/github-code-block url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/submit-tx.ts>" %}

{% hint style="success" %}
**Learn More**

For a detailed overview of the transaction lifecycle, see the [Transaction Guide](/guides/transaction).
{% endhint %}


# Bash & cURL

Learn how to submit a signed Cardano transaction using Bash, cURL, and the Anvil API.

{% @github-files/github-code-block url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/submit-tx.sh>" %}

{% hint style="success" %}
**Learn More**

For a detailed overview of the transaction lifecycle, see the [Transaction Guide](/guides/transaction).
{% endhint %}


# Transaction Metadata (CIP-20)

Learn how to add metadata to a Cardano transaction according to CIP-20 using the Anvil API.

{% @github-files/github-code-block title="utils/constant.ts" url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/utils/constant.ts>" %}

{% @github-files/github-code-block url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/cip-20.ts>" %}

{% hint style="success" %}
**Learn More**

For a detailed overview of the transaction lifecycle, see the [Transaction Guide](/guides/transaction).
{% endhint %}


# Delegation

This section provides examples of how to delegate your stake to a Stake Pool or a DRep using the Anvil API.

{% content-ref url="/pages/VmyHauq28mkQfdmE1cgZ" %}
[DRep (Bash & cURL)](/quick-start-examples/delegation/delegate-drep-sh)
{% endcontent-ref %}

{% content-ref url="/pages/uT0hfQmEF4hoJcuHVotu" %}
[DRep (TS/JS + Fetch)](/quick-start-examples/delegation/delegate-drep-ts)
{% endcontent-ref %}

{% content-ref url="/pages/VRhHsIWhGCIiCiXTDEuA" %}
[Stake Pool (Bash & cURL)](/quick-start-examples/delegation/delegate-pool-sh)
{% endcontent-ref %}

{% content-ref url="/pages/TBttl7zrbcHW138DGhbd" %}
[Stake Pool (TS/JS + Fetch)](/quick-start-examples/delegation/delegate-pool-ts)
{% endcontent-ref %}


# DRep (Bash & cURL)

Learn how to delegate to a DRep using Bash, cURL, and the Anvil API.

{% @github-files/github-code-block url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/delegate-drep.sh>" %}

{% hint style="success" %}
**Learn More**

For a step-by-step explanation, see the [Delegate to a DRep Guide](/guides/delegations/delegate-to-a-drep).
{% endhint %}


# DRep (TS/JS + Fetch)

Learn how to delegate to a DRep using TypeScript or JavaScript and the Anvil API.

{% @github-files/github-code-block title="utils/constant.ts" url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/utils/constant.ts>" %}

{% @github-files/github-code-block url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/delegate-drep.ts>" %}

{% hint style="success" %}
**Learn More**

For a step-by-step explanation, see the [Delegate to a DRep Guide](/guides/delegations/delegate-to-a-drep).
{% endhint %}


# Stake Pool (Bash & cURL)

Learn how to delegate to a Stake Pool using Bash, cURL, and the Anvil API.

{% @github-files/github-code-block url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/delegate-pool.sh>" %}

{% hint style="success" %}
**Learn More**

For a step-by-step explanation, see the [Delegate to a Stake Pool Guide](/guides/delegations/delegate-to-a-pool).
{% endhint %}


# Stake Pool (TS/JS + Fetch)

Learn how to delegate to a Stake Pool using TypeScript or JavaScript and the Anvil API.

{% @github-files/github-code-block title="utils/constant.ts" url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/utils/constant.ts>" %}

{% @github-files/github-code-block url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/delegate-pool.ts>" %}

{% hint style="success" %}
**Learn More**

For a step-by-step explanation, see the [Delegate to a Stake Pool Guide](/guides/delegations/delegate-to-a-pool).
{% endhint %}


# Minting (CIP-25)

[CIP-25](https://cips.cardano.org/cip/cip-25) is the original and most widely adopted standard for creating NFTs on Cardano. It defines a metadata structure that is stored off-chain and linked in the minting transaction.

This section provides examples of how to mint NFTs according to the CIP-25 standard, which is ideal for traditional, immutable NFTs.

{% content-ref url="/pages/N4yaLumdBgmyJw1JdT7R" %}
[TS/JS + Fetch](/quick-start-examples/cip-25/cip-25)
{% endcontent-ref %}

{% content-ref url="/pages/DDFHfNhZgynP0E7Nw4YQ" %}
[CLI (TS/JS + Fetch)](/quick-start-examples/cip-25/cip-25-cli)
{% endcontent-ref %}


# TS/JS + Fetch

Learn how to mint a standard CIP-25 NFT using TypeScript or JavaScript with the Anvil API.

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

{% hint style="info" %}
**Need to create wallets?**

You can use the [Anvil Wallet CLI](/developer-tools/wallet-cli) to generate the required wallet JSON files for these examples.
{% endhint %}

{% @github-files/github-code-block title="utils/constant.ts" url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/utils/constant.ts>" %}

You will also need the shared utility functions, which you can find in the [`utils/shared.ts`](https://github.com/Cardano-Forge/anvil-api-examples/blob/main/utils/shared.ts) file in the examples repository.

{% @github-files/github-code-block title="metatemplate.json" url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/metatemplate.json>" %}

Now, you can run the main script:

{% @github-files/github-code-block url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/cip25.ts>" %}

{% hint style="success" %}
**Learn More**

For a step-by-step explanation, see the [Mint a CIP-25 NFT Guide](/guides/nft-and-ft/mint-nft-cip-25).
{% endhint %}


# CLI (TS/JS + Fetch)

Learn how to mint a standard CIP-25 NFT using the Anvil API wallet CLI with TypeScript or JavaScript.

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

{% hint style="info" %}
**Need to create wallets?**

You can use the [Anvil Wallet CLI](/developer-tools/wallet-cli) to generate the required wallet JSON files for these examples.
{% endhint %}

{% @github-files/github-code-block title="utils/constant.ts" url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/utils/constant.ts>" %}

You will also need the shared utility functions, which you can find in the [`utils/shared.ts`](https://github.com/Cardano-Forge/anvil-api-examples/blob/main/utils/shared.ts) file in the examples repository.

{% @github-files/github-code-block title="metatemplate.json" url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/metatemplate.json>" %}

Now, you can run the main script:

{% @github-files/github-code-block url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/mint-cli-cip25.ts>" %}

{% hint style="success" %}
**Learn More**

For a step-by-step explanation, see the [Mint a CIP-25 NFT Guide](/guides/nft-and-ft/mint-nft-cip-25).
{% endhint %}


# Minting (CIP-68)

[CIP-68](https://cips.cardano.org/cip/cip-68) is a newer standard that enhances NFTs by storing metadata directly on-chain within a datum. This allows for more dynamic and programmable assets whose metadata can be read and updated by smart contracts.

This section provides examples of how to mint NFTs according to the CIP-68 standard, which allows for dynamic, updatable metadata stored on-chain.

{% content-ref url="/pages/JTpMhp2AmvpIDTJSR9k6" %}
[TS/JS + Fetch](/quick-start-examples/cip-68/cip-68)
{% endcontent-ref %}

{% content-ref url="/pages/5vQNPUmgNblNkZKqBaYl" %}
[Treasury Pays for Minting (TS/JS + Fetch)](/quick-start-examples/cip-68/treasury-pays)
{% endcontent-ref %}

{% content-ref url="/pages/oTqj0IPyNp9JRaIaes3s" %}
[Treasury Updates Metadata (TS/JS + Fetch)](/quick-start-examples/cip-68/treasury-updates-metadata)
{% endcontent-ref %}


# TS/JS + Fetch

Learn how to mint a CIP-68 NFT, which stores metadata on-chain, using TypeScript or JavaScript with the Anvil API.

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

{% hint style="info" %}
**Need to create wallets?**

You can use the [Anvil Wallet CLI](/developer-tools/wallet-cli) to generate the required wallet JSON files for these examples.
{% endhint %}

{% @github-files/github-code-block title="utils/constant.ts" url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/utils/constant.ts>" %}

You will also need the shared utility functions, which you can find in the [`utils/shared.ts`](https://github.com/Cardano-Forge/anvil-api-examples/blob/main/utils/shared.ts) file in the examples repository.

Now, you can run the main script:

{% @github-files/github-code-block url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/cip68.ts>" %}

{% hint style="success" %}
**Learn More**

For a step-by-step explanation, see the [Mint a CIP-68 NFT Guide](/guides/nft-and-ft/mint-nft-cip-68).
{% endhint %}


# Treasury Pays for Minting (TS/JS + Fetch)

An example of a CIP-68 mint where a central treasury wallet pays the transaction fees, using TypeScript or JavaScript.

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

{% hint style="info" %}
**Need to create wallets?**

You can use the [Anvil Wallet CLI](/developer-tools/wallet-cli) to generate the required wallet JSON files for these examples.
{% endhint %}

{% @github-files/github-code-block title="utils/constant.ts" url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/utils/constant.ts>" %}

You will also need the shared utility functions, which you can find in the [`utils/shared.ts`](https://github.com/Cardano-Forge/anvil-api-examples/blob/main/utils/shared.ts) file in the examples repository.

This example uses the [Wallet Endpoints](/developer-tools/wallet-endpoints) to fetch UTXOs from the treasury wallet.

Now, you can run the main script:

{% @github-files/github-code-block url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/cip68-treasury-pays.ts>" %}

{% hint style="success" %}
**Learn More**

For a step-by-step explanation, see the [Mint a CIP-68 NFT Guide](/guides/nft-and-ft/mint-nft-cip-68).
{% endhint %}


# Treasury Updates Metadata (TS/JS + Fetch)

An example of a CIP-68 NFT where a central treasury wallet updates the on-chain metadata, using TypeScript or JavaScript.

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

{% hint style="info" %}
**Need to create wallets?**

You can use the [Anvil Wallet CLI](/developer-tools/wallet-cli) to generate the required wallet JSON files for these examples.
{% endhint %}

{% @github-files/github-code-block title="utils/constant.ts" url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/utils/constant.ts>" %}

You will also need the shared utility functions, which you can find in the [`utils/shared.ts`](https://github.com/Cardano-Forge/anvil-api-examples/blob/main/utils/shared.ts) file in the examples repository.

This example uses the [Wallet Endpoints](/developer-tools/wallet-endpoints) to fetch UTXOs from the treasury wallet.

Now, you can run the main script:

{% @github-files/github-code-block url="<https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/cip68-treasury-update-meta.ts>" %}

{% hint style="success" %}
**Learn More**

For a step-by-step explanation, see the [Update CIP-68 Metadata Guide](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/nft-and-ft/update-metadata-cip-68.md).
{% endhint %}


# Select Cardano Environment

Testnets and Mainnet

## Overview

The Anvil API works across multiple Cardano environments—each serving a unique purpose in your development lifecycle. Whether you’re testing new features with fake ADA or running a full production dApp with real ADA, you’ll point your calls to different base URLs. The API behavior remains consistent, but the domain changes to reflect which network you’re on.

## Key Environments:

* **Mainnet**: Real ADA, real economic value, live user interactions.
* **Preprod**: A Cardano testnet for safe experimentation (e.g., CIP-68 tokens, CIP-25 minting) without risking real funds.
* **Preview**: Another testnet, often used to test upcoming features or protocol changes.

## Choosing an Environment

### **Mainnet**

* **Base URL**: `https://prod.api.ada-anvil.app`
* **Use Case**: Production-ready calls for real Cardano usage (minting real tokens, CIP-25/68 assets for live dApps).
* **Risks**: Mistakes cost actual ADA. Validate thoroughly on a testnet before going live.

### **Preprod**

* **Base URL**: `https://preprod.api.ada-anvil.app`
* **Use Case**: Primary test environment. Ideal for verifying transaction building, new CIP-25 or CIP-68 scripts, multi-sig flows, etc.
* **Safe**: Uses test ADA from a faucet. Great for repeated trials or when you’re unsure about advanced features.

### **Preview**

* **Base URL**: `https://preview.api.ada-anvil.app`
* **Use Case**: Testing future Cardano protocol changes, ensuring your code stays compatible with upcoming releases.
* **Similar to Preprod**: Uses test ADA but may differ in protocol versions or feature flags.

***

**Network IDs (CSL/Technical Details)**

If you’re working at a lower level (for instance, with Cardano Serialization Library—CSL), you might see references to **network IDs**:

* **id**: `0` means Testnet (applies to Preprod, Preview, or private networks).
* **id**: `1` means Mainnet.

Anvil’s high-level APIs typically handle network IDs for you. However, if you’re constructing transactions yourself or using raw CSL calls, you may need to specify the correct ID to ensure your transactions are valid.

**Note**: We’re continually refining Anvil’s tools to **abstract away** these complexities, so you can focus on your application logic rather than the intricacies of Cardano’s underlying protocol.

***

**Getting an Environment-Specific API Key**

Each environment requires its own API Key.

* **Mainnet Key**: For real ADA transactions.
* **Preprod Key**: For test ADA in the pre-production environment.
* **Preview Key**: For experimenting with upcoming protocol changes.

**Why separate keys?** This ensures you won’t accidentally spend real funds when you intended to test, and it keeps usage metrics and rate limits distinct across different project stages.

**Steps to acquire keys:**

1. **Email** `hello@ada-anvil.io` to request keys for each environment.
2. You’ll receive separate credentials and rate-limit policies for each.
3. **Include the correct `X-Api-Key` in your headers** when sending requests (see Authentication guide for details).

***

**Development Workflow**

1. **Local Development**

* Start with the Preprod environment to mint tokens, test staking or delegate, and ensure your code works as expected.
* Use tADA from the faucet to simulate transactions.

2. **Integration Testing**

* Move to the Preview environment if you need to test features not yet live on Mainnet.
* Validate that your application still performs correctly under new protocol rules.

3. **Production Deployment**

* Once everything is stable and thoroughly tested, switch to the **Mainnet** domain and use your Mainnet key.
* Ensure your contracts, UTXO handling, and address generation are thoroughly tested on the testnets first.

***

**Code Example: Switching Environments Dynamically**

Here’s a Node.js snippet showing how you might toggle between environments based on a simple config variable:

```typescript
// Adjust this to 'preprod', 'preview', or 'mainnet'
const environment = process.env.CARDANO_ENV || "preprod";

const baseUrls = {
  preprod: "https://preprod.api.ada-anvil.app/v2/services",
  preview: "https://preview.api.ada-anvil.app/v2/services",
  mainnet: "https://prod.api.ada-anvil.app/v2/services",
};

// Suppose each environment requires a distinct API key
const apiKeys = {
  preprod: process.env.PREPROD_API_KEY,
  preview: process.env.PREVIEW_API_KEY,
  mainnet: process.env.MAINNET_API_KEY,
};

async function getHealth() {
  const url = `${baseUrls[environment]}/health`;
  const apiKey = apiKeys[environment];

  try {
    const response = await fetch(url, {
      headers: { "X-Api-Key": apiKey },
    });
    console.log(`[${environment.toUpperCase()}] Health:`, await response.json());
  } catch (error) {
    console.error("Error:", error);
  }
}

(async () => {
  await getHealth();
})();
```

**Explanation**:

* We store **domain URLs** and **API Keys** for each environment.
* We pick the environment via a config variable (`CARDANO_ENV`).
* If we wanted to run a test suite for both environments, we can easily toggle by changing `CARDANO_ENV`.

***

**FAQ and Troubleshooting**

1. **Do I need different code for each environment?**

* Generally **no**—only your **base URL** and **API Key** change. The rest of your logic remains the same.

2. **Why am I getting `401` errors on the testnet but not on Mainnet?**

* You might be using your Mainnet key on a testnet endpoint (or vice versa). Ensure you match the key to the environment.

3. **What if I need a private cluster?**

* Contact us to discuss setting up a **private** or **enterprise** environment. We can tailor configurations (network IDs, access controls, node settings) to your needs.

4. **Is there any difference in UTXO structure between testnet and Mainnet?**

* No—Cardano’s UTXO model works identically across environments. Only the tokens and network ID differ.

***

**Next Steps**

* **Dive into UTXOs vs. Change Address**
  * Learn how Cardano’s transaction model differs from account-based blockchains.
* **Check Out Our Utilities**
  * See how Anvil simplifies common Cardano functions (address derivation, transaction serialization, etc.).
* **Ready to Build**
  * Head over to our Transaction or NFT & FT guides to start coding real (or test) transactions on Cardano.


# Transaction

Overview of Anvil API Cardano Transaction Builder capabilities

## Introduction

Cardano **transactions** are the fundamental way to move value and interact with the blockchain, whether it's moving ADA, native tokens, and NFTs. Or it's interacting with smart contracts. The **Anvil API** simplifies transaction creation with a flexible interface that supports:

* **Simple to complex outputs** - from basic ADA transfers to multi-asset transactions with multiple recipients
* **Input methods** - using `changeAddress` and `UTXOs` to source funds
* **Complete transaction lifecycle** - building, signing, and submitting in a streamlined workflow

This documentation covers the various **transaction types**, **payload structures**, and **best practices** for creating transactions with the Anvil API.

## The Basic Flow

1. **BUILD**: We structure a simple payload with `changeAddress`, `utxos`, and `outputs`
2. **SIGN**: We sign the transaction with your preferred signing method (See [Signing Transactions](/guides/transaction/signing-transaction))
3. **SUBMIT**: We send the signed transaction to the Anvil API's transaction submit endpoint
4. **CONFIRM**: We handle the response from the submit endpoint

## Transaction Flow Overview

Below is a sequence diagram that illustrates the basic flow of a transaction between your application and the Anvil API.

{% @mermaid/diagram content="---
config:
theme: neo-dark
---------------

sequenceDiagram
actor A1 as Application User
participant User as Your Application
participant Anvil as Anvil API
participant Cardano as Cardano Network

```
Note over A1, Cardano: Anvil API Transaction Workflow

rect rgba(255, 255, 255, 0.1)
    A1->>+User: Start Transaction
    User->>+Anvil: 1. BUILD<br/>POST '/transaction/build' endpoint<br/>(with payload)
    Anvil-->>-User: 2. RECEIVE<br/>(unsigned transaction)
end

rect rgba(87, 173, 87, 0.25)
    Note over A1, User: 3. SIGN<br/>Using Weld<br/>(CIP-30 browser wallet)
    User->>A1: Prompt to Sign Transaction
    A1->>User: Confirm and Sign Transaction
end

rect rgba(255, 255, 255, 0.1)
    User->>+Anvil: 4. SUBMIT<br/>POST '/transaction/submit' endpoint<br/>(signed transaction)
    Anvil->>Cardano: Submit to blockchain
    Cardano-->>Anvil: Return transaction status
    Anvil-->>-User: 5. CONFIRM<br/>(transaction hash & status)
    User-->>-A1: Show Transaction Confirmation
end" %}
```

## Breakdown of the Basic Flow

The above flow diagram illustrates the basic flow of a transaction. The following sections will break down each step in detail. As you can see there are two API calls that are involved in the basic flow: **Build** and **Submit**. The **Build** API call is used to build a transaction, and the **Submit** API call is used to submit a transaction after the user or application has **signed** it.

### 1. Building Transaction: Defining Addresses, UTXOs, and Outputs

The **Build** (`POST /transaction/build`)API call is used to build a transaction. This call requires a payload that includes the sender address for change, UTXOs for inputs, and outputs.

All transactions need UTXOs for inputs. See: [Selecting UTXOs](/guides/transaction/selecting-utxos) for more information on how to do this for production applications.

```typescript
// Structure for the transaction build request
const buildBody = {
  // Sender address for change
  changeAddress: "addr_sender...",
  
  // UTXOs for inputs (either address or array of CBOR-encoded UTXOs)
  // For CIP-30 wallets: await wallet.getUtxos()
  utxos: ["8282...", "8282..."],

  // Optional: Force the inclusion of specific UTXOs, often required for smart contract interactions.
  // This is not required, but is useful if you need to force the spending of a specific UTXO.
  requiredInputs: ["8282...", "8282..."],
  
  // Where to send the ADA, assets, and amounts
  outputs: [
    {
      address: "addr_recipient...",
      lovelace: 10_000_000, // 10 ADA
    },
  ],
  // Optional bounds that determine when the transaction is valid. 
  // False can be used to disable a bound.
  // If transactions are submitted outside of the validity interval, they will be rejected.
  validityInterval: {
    start: 1234567890, // POSIX timestamp or slot number (optional)
    end: 1234598765    // POSIX timestamp or slot number (optional)
  }
  //Add additional parameters here.
};

// Send the build request
const buildResponse = await fetch(
  "https://preprod.api.ada-anvil.app/v2/services/transactions/build",
  {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-Api-Key": "YOUR_API_KEY",
    },
    body: JSON.stringify(buildBody),
  }
);

const buildData = await buildResponse.json();
```

### Build Response Structure

```json
// After successful build
{
  "hash": "TRANSACTION_HASH",

  // Complete transaction is preferred for signing most transactions.
  "complete": "TRANSACTION_CBOR", 
  
  // Stripped transaction is preferred for minting operations (NFTs/tokens).
  // This is because we want to hide metadata from the client. 
  "stripped": "STRIPPED_CBOR", 
  
  // The witness data component
  "witnessSet": "WITNESS_SET_CBOR"
}
```

> **Important Warning:**
>
> * Use the `complete` format when you don't care about hiding metadata from the frontend client.
> * For minting operations (NFTs/tokens), only exposing the `stripped` format of your `build` response helps hide metadata from the frontend client. The `complete` format will include those attributes allowing users to see the metadata before the transaction is submitted.

### 2. Sign the Transaction

**Sign** the transaction with your preferred signing method (See [Signing Transactions](/guides/transaction/signing-transaction)). This can be done using a CIP-30 compatible wallet, or by using a private key in your application, or by using a hardware wallet. We recommend using the [@ada-anvil/weld](https://www.npmjs.com/package/@ada-anvil/weld) wallet connector for a seamless experience handling interactions with Cardano wallets.

### 3. Submit the Signed Transaction

Once you have the signatures, we can **submit** `POST /transaction/submit` the transaction in two ways:

1. **Embed signatures in transaction**: You can embed the signatures in the transaction. This is helpful when you are signing a transaction in a backend server (using CSL). Otherwise, option 2 is recommended.
2. **Separate transaction and signatures**: You can separate the transaction and signatures and send them to the API separately.

```typescript
// Option 1: Submit with signatures embedded in transaction.
const submitBody = {
  transaction: "SIGNED_TRANSACTION_CBOR" 
};

// Option 2: Submit with separate transaction and signatures
const submitBody = {
  transaction: "UNSIGNED_TRANSACTION_CBOR", 
  signatures: ["SIGNATURE_1", "SIGNATURE_2"] // Array of signatures
};
```

### Submit Response Structure

```json
// After successful submit
{
  "hash": "TRANSACTION_HASH" // The transaction ID on the blockchain
}
```

> **Cardano is Deterministic:** The transaction hash returned by the build endpoint is the same hash that will appear on-chain. Unlike some other blockchains, Cardano's eUTXO model allows for deterministic transaction creation - the transaction ID is calculated before submission and remains unchanged when confirmed on the blockchain. This feature enables reliable transaction tracking from creation through confirmation.

***

## Best Practices

* **Test on Preprod** first to avoid risking real ADA.
* **Handle Errors gracefully**: watch for `400 Bad Request`, `401 Unauthorized`, or `429 Throttled, please try again later.` responses.
* **Use CIP-30 for a user-friendly Web3 experience**: fetch `utxos` and `changeAddress` directly from the user's connected wallet.
* **Validate JSON**: Ensure your payload is correctly formed—typos in keys like lovelace can cause failures.
* **Include sufficient ADA**: When sending native assets, include at least 2 ADA per output (due to min-UTXO requirements).
* **Verify addresses**: Always double-check recipient addresses before submitting transactions.
* **Keep API keys secure**: Never expose your API keys in client-side code.

***

### Transaction Builder Examples and Next Steps

Now that you have a basic understanding of how to send a transaction, explore how the Anvil API is flexible and can handle many types of transactions. Wether you are sending ADA and/or assets to multiple recipients, adding metadata to transactions, or handling multi-signature transactions, the Anvil API takes advantage of the full power of transactions on Cardano.

Review our guides below for more details. They provide practical examples of how to use the Anvil API transaction builder:

{% content-ref url="/pages/RCnVmsz8IY84fmYcqVQX" %}
[Create Basic Transaction](/guides/transaction/create-basic-transaction)
{% endcontent-ref %}

{% content-ref url="/pages/M2vi5OOciGkAyxbcASyw" %}
[Create Custom Transaction](/guides/transaction/create-custom-transaction)
{% endcontent-ref %}

{% content-ref url="/pages/tL14zSGDJ3WgB5F3i5S4" %}
[Create Transaction with Metadata (CIP-20)](/guides/transaction/create-transaction-with-metadata-cip-20)
{% endcontent-ref %}


# Create Basic Transaction

Send ADA from one address to another

## Introduction

This guide demonstrates how to create a **basic transaction** to send ADA from one address to another. The basic transaction is the simplest form of Cardano transaction, focused solely on transferring the native currency (ADA/Lovelace) **without** additional assets or metadata. With the Anvil API, you can easily create, sign, and submit these transactions with minimal configuration.

For more information on **transactions**, please refer to the [Transaction Overview](/guides/transaction).

## Key Features

* Simple ADA transfers between addresses
* Minimal configuration required
* Clean and straightforward API payload

## Requirements

* A Cardano wallet with sufficient ADA
* Recipient wallet address
* Valid API key for authentication

## Full Examples

### Using Next.js and Weld

{% content-ref url="/pages/yONC6pSZfkeixPNmEzsd" %}
[Next.js & Weld](/guides/transaction/create-basic-transaction/nextjs-with-weld)
{% endcontent-ref %}

### Using Bash and cURL

{% content-ref url="/pages/4DiIM6JnxjjclE7TtZUp" %}
[Bash & cURL](/guides/transaction/create-basic-transaction/bash-and-curl)
{% endcontent-ref %}

### Using TS/JS Fetch

{% content-ref url="/pages/rmXUvXA1P9a3qIeQexoT" %}
[Deno & Fetch](/guides/transaction/create-basic-transaction/node-and-fetch)
{% endcontent-ref %}

***

## **Specifications**

### API Endpoint

```
POST /transactions/build
```

### Request Structure

```json
{
  "changeAddress": "<sender_address>",
  "utxos": ['8282...', '8282...', '8282...'],
  "outputs": [
    {
      "address": "<receiver_address>",
      "lovelace": <lovelace_amount>
    }
  ]
}
```

## Best Practices

* Always verify the recipient address before submitting transactions
* Remember that 1 ADA = 1,000,000 Lovelace
* Always provide UTXOs in production See [Selecting UTXOs for Transactions](/guides/transaction/selecting-utxos)
* Test transactions on testnet before moving to mainnet
* Consider transaction fees when planning amounts to send
* Keep your API key secure and never expose it in client-side code


# Bash & cURL

Using bash & cURL to build, sign\* and submit a transaction

### Introduction

There are two ways to create a transaction, the first one is by passing the Cardano wallet address for the utxos (as the changeAddress), and the second one usually used with a frontend (wallet extension), requires to get a list of all cbor utxos to be used for the transaction.

The following examples are covering both approaches.

### Objectives

This example sends 10ADA from one wallet to another.

### Requirements

* A Cardano wallet with ADA
* A wallet to send ADA to
* A wallet extension to sign the transaction
* An API key

The wallets are on Preprod network and using Eternl (So the signature is done in the browser) See [Signing Transactions](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/transaction/signing-transaction.md) for more details.

### Using the Address to fetch Utxos

**Payload (Using an address only)**

> Important: This demo is meant for educational and testing purposes only. In production, please pass the UTXO list for your wallet. We recommend using the [@ada-anvil/weld](https://www.npmjs.com/package/@ada-anvil/weld) wallet connector to easily retrieve UTXOs from connected wallets. In this example, the API will fetch the UTXO list for you and automatically determine the UTXO(s) to be used for the transaction.

*You can use this payload when you cannot fetch the utxos, for example, when you don't have a wallet extension.*

```json
{
  "changeAddress": "addr_sender...",
  "outputs": [
    {
      "address": "addr_recipient...",
      "lovelace": 10_000_000
    }
  ]
}
```

**Example (Using cURL)**

*Using a preprod wallet.*

{% code overflow="wrap" %}

```sh
SENDER_ADDRESS="addr_test1qrvx8wgdndrk98qf62vka3q4fglchk7h940vepdtgcv9fuu0e0aeuac6j2xhz77esaaudku68ha89qesqvd29pmuzw6qk8xkcn"
RECEIVER_ADDRESS="addr_test1qztayr885vqrx6w0j946lvtxl622flxx4asj2z4ludm3y2rewu7hmazv8tm78tvphzlream22pp6zhk0rrsa84nf6qxsrua9nh"
# 10 ADA => 1ADA = 1'000'000LOVELACE
LOVELACE_AMOUNT=10_000_000
# See Authentication page for API key details.
X_API_KEY="testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9"
```

{% endcode %}

{% code overflow="wrap" %}

```sh
curl -X POST \
     -H "Content-Type: application/json" \
     -H "X-Api-Key: ${X_API_KEY}" \
     -d '{
           "changeAddress": "'${SENDER_ADDRESS}'",
           "outputs": [
             {
               "address": "'${RECEIVER_ADDRESS}'",
               "lovelace": 10000000
             }
           ]
         }' \
     https://preprod.api.ada-anvil.app/v2/services/transactions/build
```

{% endcode %}

**Output**

{% code overflow="wrap" fullWidth="false" %}

```json
{
  "complete": "84a400d901028182582059f796dba33ec94cd07dd609d175a6ef239a2590e974b73ec052eccb8daf53f30101828258390097d20ce7a3003369cf916bafb166fe94a4fcc6af61250abfe377122879773d7df44c3af7e3ad81b8be3cf76a5043a15ecf18e1d3d669d00d1a0098968082583900d863b90d9b47629c09d2996ec4154a3f8bdbd72d5ecc85ab461854f38fcbfb9e771a928d717bd9877bc6db9a3dfa728330031aa2877c13b41a1de51e21021a0003cb05031a04e05deea0f5f6",
  "stripped": "84a400d901028182582059f796dba33ec94cd07dd609d175a6ef239a2590e974b73ec052eccb8daf53f30101828258390097d20ce7a3003369cf916bafb166fe94a4fcc6af61250abfe377122879773d7df44c3af7e3ad81b8be3cf76a5043a15ecf18e1d3d669d00d1a0098968082583900d863b90d9b47629c09d2996ec4154a3f8bdbd72d5ecc85ab461854f38fcbfb9e771a928d717bd9877bc6db9a3dfa728330031aa2877c13b41a1de51e21021a0003cb05031a04e05deea0f5f6",
  "witnessSet": "a0"
}
```

{% endcode %}

### Using a list of UTXOs

**Payload**

*You can use this payload when you can fetch the utxos, for example, when you have a wallet extension.*

```json
{
  "utxos": ["8282...", "8282...", "..."],
  "changeAddress": "addr_sender...",
  "outputs": [
    {
      "address": "addr_recipient...",
      "lovelace": 10000000,
    }
  ]
}
```

<mark style="color:red;">TBD, do the example as above using an UTXOs list</mark>


# Deno & Fetch

Using Deno (or Node) & fetch to build, sign\* and submit a transaction

## Introduction

There are two ways to create a transaction, the first one is by passing the Cardano wallet address for the utxos (only available on testnet networks), and the second one (required for production) usually used with a frontend (wallet extension), requires to get a list of all cbor utxos to be used for the transaction.

## Objectives

This example sends 10ADA from one wallet to another.

The wallets are on Preprod network and using a CIP-30 compatible wallet (So the signature is done in the browser)

## Requirements

* A Cardano wallet with ADA
* A wallet to send ADA to
* A wallet extension to sign the transaction
* An API key

## API Request Structure

### Payload Format (Using an address as utxos)

> Important: This demo is meant for educational and testing purposes only. In production, please pass the UTXO list for your wallet. We recommend using the [@ada-anvil/weld](https://www.npmjs.com/package/@ada-anvil/weld) wallet connector to easily retrieve UTXOs from connected wallets. In this example, the API will fetch the UTXO list for you and automatically determine the UTXO(s) to be used for the transaction.

```json
{
  "changeAddress": "addr_sender...",
  "outputs": [
    {
      "address": "addr_recipient...",
      "lovelace": 10_000_000
    }
  ]
}
```

## Implementation

### Configuration and Parameters (using Deno and Fetch)

*Using a preprod wallet all value needed for a transaction*

```typescript
const SENDER_ADDRESS = "addr_test1qrydyk6uw6cehk5u3zspyz3dhnwzmhfls2fp42vv5dv9g2z3885pg4kpkn30ptezc855lu3w5ey93zcr5lrezjmwkftqg8xvge";
const RECEIVER_ADDRESS = "addr_test1qr0tkwvlln0v5fljdxceudmlpt5y6szc84vpj4skm836tgn4hsqaesgg97l8ppy5rsn0alj8pth6lqe20fdyydsdgw6sr74cyt";
// 10 ADA => 1ADA = 1'000'000LOVELACE
const LOVELACE_AMOUNT = 10_000_000;
// See Authentication page for API key details.
const X_API_KEY = "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9";
```

### Request Body

*Body Structure for creating a basic transaction using the previously collected values.*

```typescript
const BODY = {
  changeAddress: SENDER_ADDRESS,
  outputs: [
    {
      address: RECEIVER_ADDRESS,
      lovelace: LOVELACE_AMOUNT,
    },
  ],
};
```

### API Call (using Deno and Fetch)

Basic POST call with Fetch

{% code title="basic-transaction.ts" %}

```typescript
const response = await fetch(
    `https://preprod.api.ada-anvil.app/v2/services/transactions/build`,
    {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
        "x-api-key": X_API_KEY,
      },
      body: JSON.stringify(BODY),
    }
  );

  console.log(await response.json());
```

{% endcode %}

### Deno Command

```bash
deno run --allow-net basic-transaction.ts
```

### Output

{% code overflow="wrap" %}

```json
{
  "hash": "b1f159d88e9dece4d327352c03a6fbb9484533dee60207367a2689f632650c9c",
  "complete": "84a400d9010281825820c83c3a8285c7b08c2cec3abf0c91dcec5afee531d6a0eeef002dd6d75456927d01018282583900debb399ffcdeca27f269b19e377f0ae84d40583d58195616d9e3a5a275bc01dcc1082fbe7084941c26fefe470aefaf832a7a5a42360d43b51a0098968082583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a43c6a5fc021a0003cb05031a04ebdd50a0f5f6",
  "stripped": "84a400d9010281825820c83c3a8285c7b08c2cec3abf0c91dcec5afee531d6a0eeef002dd6d75456927d01018282583900debb399ffcdeca27f269b19e377f0ae84d40583d58195616d9e3a5a275bc01dcc1082fbe7084941c26fefe470aefaf832a7a5a42360d43b51a0098968082583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a43c6a5fc021a0003cb05031a04ebdd50a0f5f6",
  "witnessSet": "a0"
}
```

{% endcode %}

## The Whole File (Deno Version)

<details>

<summary>basic-transaction.ts</summary>

{% code overflow="wrap" %}

```typescript
const SENDER_ADDRESS =
  "addr_test1qrydyk6uw6cehk5u3zspyz3dhnwzmhfls2fp42vv5dv9g2z3885pg4kpkn30ptezc855lu3w5ey93zcr5lrezjmwkftqg8xvge";
const RECEIVER_ADDRESS =
  "addr_test1qr0tkwvlln0v5fljdxceudmlpt5y6szc84vpj4skm836tgn4hsqaesgg97l8ppy5rsn0alj8pth6lqe20fdyydsdgw6sr74cyt";

// 10 ADA => 1ADA = 1'000'000LOVELACE
const LOVELACE_AMOUNT = 10_000_000;

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

const BODY = {
  changeAddress: SENDER_ADDRESS,
  outputs: [
    {
      address: RECEIVER_ADDRESS,
      lovelace: LOVELACE_AMOUNT,
    },
  ],
};

const response = await fetch(`${API_URL}/transactions/build`, {
  method: "POST",
  headers: HEADERS,
  body: JSON.stringify(BODY),
});

console.log(await response.json());

export {};
```

{% endcode %}

</details>

### Using Fetched Utxos

**Payload (Using a list of utxos)**

*You can use this payload when you can fetch the utxos, for example, when you have a wallet extension*

```json
{
  "utxos": ["8282...", "8282...", "..."],
  "changeAddress": "addr_sender...",
  "outputs": [
    {
      "address": "addr_recipient...",
      "lovelace": 10_000_000
    }
  ]
}
```

*Add the required UTXOs for your transaction. (usually all of them)*

### Get UTXO

*You can retrieve your UTXOs from your wallet using these functions in the browser console. (Example: Chrome console with Eternl)*

{% code overflow="wrap" %}

```typescript
const wallet = await window.cardano.eternl.enabled();
const utxos = await wallet.getUtxos();
console.log(utxos);
```

{% endcode %}

*Then, copy your UTXO array and include it as a parameter in the request body, as shown below.*

### Params

{% code overflow="wrap" %}

```typescript
const UTXOS = [
  "828258200599b9572ee291d70e2ffe6ac876c1e814c952a1633e4c48ac58e39726123d9d0082583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a002dc6c0",
  "8282582020932c96eccc8ffdde138945a492a568942f7ef9181b1805eaae9a4bfdede28a0282583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a3b9a6a05",
  "8282582020932c96eccc8ffdde138945a492a568942f7ef9181b1805eaae9a4bfdede28a0382583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a3b9a6a05",
  "8282582020932c96eccc8ffdde138945a492a568942f7ef9181b1805eaae9a4bfdede28a0482583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a1dcd3507",
  "8282582020932c96eccc8ffdde138945a492a568942f7ef9181b1805eaae9a4bfdede28a0582583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a1dcd3502",
  "8282582020932c96eccc8ffdde138945a492a568942f7ef9181b1805eaae9a4bfdede28a0682583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a1dcd3502",
  "828258209856940b54a9048607a5b081e8718602571e57f59de745c562ce7b74776c89a90082583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb256821a0011f436a1581c1af660e4c58514a2f0ea167deca340381e55bed4aea60bc09c211417a14d416e76696c546573743133353601",
  "82825820a2cf6441b06a025f92d32d3e5faa17227d8f1d2310c0a1081ceaaa72d2dd6aa90182583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a1dca3e37",
  "82825820e1cd7eee71dcf9de2ea133f6932552d922ff6cc0a34908cdec8c0eae8c9664d80582583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a000f4240",
  "82825820c83c3a8285c7b08c2cec3abf0c91dcec5afee531d6a0eeef002dd6d75456927d0182583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a44630781",
  "828258203aeb86b1388189192281b6b2c114235cec91b4a1fc3aeb4d73b1721855b93f3c0082583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561b00000002540be400",
];
const SENDER_ADDRESS = "addr_test1qrydyk6uw6cehk5u3zspyz3dhnwzmhfls2fp42vv5dv9g2z3885pg4kpkn30ptezc855lu3w5ey93zcr5lrezjmwkftqg8xvge";
const RECEIVER_ADDRESS = "addr_test1qr0tkwvlln0v5fljdxceudmlpt5y6szc84vpj4skm836tgn4hsqaesgg97l8ppy5rsn0alj8pth6lqe20fdyydsdgw6sr74cyt";
// 10 ADA => 1ADA = 1'000'000LOVELACE
const LOVELACE_AMOUNT = 10_000_000;
// See Authentication page for API key details.
const X_API_KEY = "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9";
```

{% endcode %}

#### API POST Request Body

*Body Structure for creating a basic transaction using the previously collected values.*

```typescript
const BODY = {
  utxos: UTXOS,
  changeAddress: SENDER_ADDRESS,
  outputs: [
    {
      address: RECEIVER_ADDRESS,
      lovelace: LOVELACE_AMOUNT,
    },
  ],
};
```

#### Fetch Command with Deno

Basic POST call with Fetch

{% code title="" %}

```typescript
const response = await fetch(
  `${API_URL}/transactions/build`,
  {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-api-key": X_API_KEY,
    },
    body: JSON.stringify(BODY),
  }
);

console.log(await response.json());
```

{% endcode %}

### Deno Command

```bash
deno run --allow-net basic-transaction.ts
```

### Output

{% code overflow="wrap" %}

```json
{
  hash: "b1f159d88e9dece4d327352c03a6fbb9484533dee60207367a2689f632650c9c",
  complete: "84a400d9010281825820c83c3a8285c7b08c2cec3abf0c91dcec5afee531d6a0eeef002dd6d75456927d01018282583900debb399ffcdeca27f269b19e377f0ae84d40583d58195616d9e3a5a275bc01dcc1082fbe7084941c26fefe470aefaf832a7a5a42360d43b51a0098968082583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a43c6a5fc021a0003cb05031a04ebdd50a0f5f6",
  stripped: "84a400d9010281825820c83c3a8285c7b08c2cec3abf0c91dcec5afee531d6a0eeef002dd6d75456927d01018282583900debb399ffcdeca27f269b19e377f0ae84d40583d58195616d9e3a5a275bc01dcc1082fbe7084941c26fefe470aefaf832a7a5a42360d43b51a0098968082583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a43c6a5fc021a0003cb05031a04ebdd50a0f5f6",
  witnessSet: "a0"
}
```

{% endcode %}

***

### Sign and Submit the Transaction

The **transaction** can be **sign** and **submitted.**

{% content-ref url="<https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/transaction/signing-transaction.md>" %}
<https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/transaction/signing-transaction.md>
{% endcontent-ref %}

{% content-ref url="/pages/8vAn0uMN5WO2rFy6d0Cn" %}
[Submit Transaction](/guides/transaction/submit-transaction)
{% endcontent-ref %}

## The Whole File (Deno Version)

<details>

<summary>basic-transaction.ts</summary>

{% code overflow="wrap" %}

```typescript
const UTXOS = [
  "828258200599b9572ee291d70e2ffe6ac876c1e814c952a1633e4c48ac58e39726123d9d0082583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a002dc6c0",
  "8282582020932c96eccc8ffdde138945a492a568942f7ef9181b1805eaae9a4bfdede28a0282583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a3b9a6a05",
  "8282582020932c96eccc8ffdde138945a492a568942f7ef9181b1805eaae9a4bfdede28a0382583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a3b9a6a05",
  "8282582020932c96eccc8ffdde138945a492a568942f7ef9181b1805eaae9a4bfdede28a0482583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a1dcd3507",
  "8282582020932c96eccc8ffdde138945a492a568942f7ef9181b1805eaae9a4bfdede28a0582583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a1dcd3502",
  "8282582020932c96eccc8ffdde138945a492a568942f7ef9181b1805eaae9a4bfdede28a0682583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a1dcd3502",
  "828258209856940b54a9048607a5b081e8718602571e57f59de745c562ce7b74776c89a90082583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb256821a0011f436a1581c1af660e4c58514a2f0ea167deca340381e55bed4aea60bc09c211417a14d416e76696c546573743133353601",
  "82825820a2cf6441b06a025f92d32d3e5faa17227d8f1d2310c0a1081ceaaa72d2dd6aa90182583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a1dca3e37",
  "82825820e1cd7eee71dcf9de2ea133f6932552d922ff6cc0a34908cdec8c0eae8c9664d80582583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a000f4240",
  "82825820c83c3a8285c7b08c2cec3abf0c91dcec5afee531d6a0eeef002dd6d75456927d0182583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a44630781",
  "828258203aeb86b1388189192281b6b2c114235cec91b4a1fc3aeb4d73b1721855b93f3c0082583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561b00000002540be400",
];
const SENDER_ADDRESS = "addr_test1qrydyk6uw6cehk5u3zspyz3dhnwzmhfls2fp42vv5dv9g2z3885pg4kpkn30ptezc855lu3w5ey93zcr5lrezjmwkftqg8xvge";
const RECEIVER_ADDRESS = "addr_test1qr0tkwvlln0v5fljdxceudmlpt5y6szc84vpj4skm836tgn4hsqaesgg97l8ppy5rsn0alj8pth6lqe20fdyydsdgw6sr74cyt";
// 10 ADA => 1ADA = 1'000'000LOVELACE
const LOVELACE_AMOUNT = 10_000_000;
// See Authentication page for API key details.
const X_API_KEY = "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9";
const API_URL = "https://preprod.api.ada-anvil.app/v2/services";

const BODY = {
  utxos: UTXOS,
  changeAddress: SENDER_ADDRESS,
  outputs: [
    {
      address: RECEIVER_ADDRESS,
      lovelace: LOVELACE_AMOUNT,
    },
  ],
};

const response = await fetch(
  `${API_URL}/transactions/build`,
  {
    method: "POST",
    headers: { "Content-Type": "application/json", "x-api-key": X_API_KEY },
    body: JSON.stringify(BODY),
  }
);

console.log(await response.json());

export {};
```

{% endcode %}

</details>


# Next.js & Weld

Learn how to build a complete Cardano dApp that sends transactions using Next.js, React, and Weld wallet integration.

## Overview

In this tutorial, you'll build a modern Next.js application designed for interacting with the Cardano blockchain through the Anvil API. Users will be able to connect their wallets, check balances, and easily create, sign, and submit ADA transactions—all powered by the robust Anvil API and Weld wallet connector.

### What You'll Create

A complete transaction application that features:

* Server-side rendering (SSR) and [API Routes](https://nextjs.org/docs/pages/building-your-application/routing/api-routes) using Next.js
* Smooth wallet integration using [Weld](https://github.com/Cardano-Forge/weld)
* Robust transaction workflows with Anvil API (build, sign, and submit)
* Clear error handling and user feedback powered by Anvil API responses
* A clean, responsive user interface

<figure><img src="/files/tDD2nmVZFi6zLxqnKwaF" alt="Completed Cardano Transaction App with Next.js and Weld"><figcaption><p>Completed transaction application with wallet connection and ADA transfer functionality</p></figcaption></figure>

## Guide Breakdown

To make your journey easier, this guide is structured in three clear sections:

### [Part 1: Project Setup](/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-setup)

In the first part, you'll set up your development environment and create the basic Next.js application:

* Creating a new Next.js project
* Installing dependencies (Weld, Tailwind CSS, etc.)
* Configuring environment variables (Anvil API key, network)
* Setting up the project structure

By the end of Part 1, you'll have a working Next.js application ready for wallet integration.

### [Part 2: Wallet Integration](/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-wallet)

The second part focuses on integrating Cardano wallets using Weld:

* Creating a Weld provider with SSR support
* Building a wallet connector component
* Handling wallet state management
* Testing wallet connection functionality

When you complete Part 2, your application will be able to connect to Cardano wallets, display wallet information, and maintain connection state.

### [Part 3: Building Transactions](/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-transactions)

In the final part, you'll implement transaction functionality using the Anvil API:

* Creating server-side functions to communicate with Anvil API endpoints
* Building API routes that leverage Anvil API for transaction building and submission
* Implementing the Anvil transaction flow (build → sign → submit)
* Handling Anvil API responses and error conditions

After finishing Part 3, you'll have a complete Cardano transaction application that uses Anvil API to build, sign, and submit transactions to the Cardano network.

## Prerequisites

Before starting this guide, ensure you have:

* Node.js 18+ installed
* Basic knowledge of React and Next.js
* Basic knowledge of Cardano, Cardano Networks,and wallets. See the [Anvil API Getting Started guide](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/anvil-api/getting-started.md)
* An Anvil API key: ([sign up at Anvil](https://ada-anvil.io/auth/signup))
* A Cardano wallet extension installed in your browser (Eternl, Lace, etc.)

## Getting Started

Ready to build your Cardano transaction application? Begin with [Part 1: Project Setup](/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-setup).


# Part 1: Project Setup

This is Part 1 of our guide to building a Cardano transaction application with Next.js and Weld. In this section, you'll set up the project structure and dependencies.

## Introduction

In this first part, we'll create a Next.js application and configure it to work with Anvil API. By the end of this section, you'll have a working development environment ready for wallet integration.

## Prerequisites

Before you begin, ensure you have:

* Node.js 18+ installed
* Basic familiarity with React and Next.js
* An Anvil API key: ([sign up at Anvil](https://ada-anvil.io/auth/signup))

## Project Creation

### 1. Create a new Next.js project

Start by creating a new Next.js application using the create-next-app tool:

```bash
npx create-next-app@latest my-basic-transaction-app
cd my-basic-transaction-app
```

When prompted for options, select the following:

* TypeScript: Yes
* ESLint: Yes
* Tailwind CSS: Yes
* `src/` directory: Yes
* App Router: Yes
* Use TurboPack: (Optional)
* Custom Import Alias (@/\*): No

### 2. Install Required Dependencies

Next, install Weld and other required dependencies:

```bash
npm install @ada-anvil/weld
```

This single package includes everything needed for the client-side wallet integration. Other dependencies (e.g., Next.js, Tailwind CSS) should already be installed as part of the project creation. See the [Weld Documentation](https://github.com/Cardano-Forge/weld) for more information.

{% hint style="info" %}
The `@ada-anvil/weld` package includes all the necessary functionality for wallet integration. The React-specific hooks are exported from the package via the `/react` subpath: `@ada-anvil/weld/react`.
{% endhint %}

### 3. Environment Configuration

Create a `.env.example` file to document the required environment variables for other developers:

```env
NEXT_PUBLIC_ANVIL_API_URL=https://preprod.api.ada-anvil.app/v2/services
ANVIL_API_KEY=testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9
NEXT_PUBLIC_NETWORK=preprod
```

This file can be safely committed to your repository.

Next, create a `.env.local` file at the root of your project with your actual values

{% hint style="warning" %}
Never commit your `.env.local` file to version control. Add it to your `.gitignore` file to keep your API key secure.
{% endhint %}

### 4. Project Structure

The project will be built incrementally across all three parts of this guide. Here's a preview of the complete project structure you'll create:

```
my-basic-transaction-app/
├── src/
│   ├── app/
│   │   ├── api/                     # API routes
│   │   │   └── transaction/
│   │   │       ├── build/
│   │   │       │   └── route.ts     # Transaction Build
│   │   │       └── submit/
│   │   │           └── route.ts     # Transaction Submit
│   │   ├── globals.css              # Global styles
│   │   ├── layout.tsx               # Main Layout
│   │   └── page.tsx                 # Main Page
│   ├── components/                  # Component files
│   │   ├── WalletConnector.tsx      # Wallet Connector
│   │   ├── TransactionForm.tsx      # Transaction Form
│   │   └── WeldProvider.tsx         # Weld Provider
│   ├── hooks/                       # Custom React hooks
│   │   └── useTransactionSubmission.ts  # Transaction Submission Hook
│   └── utils/                       # Utility functions
│       └── anvil-api.ts             # Anvil API
├── public/                          # Static assets
├── .env.local                       # Environment variables
├── .env.example                     # Environment variables
├── package.json                     # Package configuration
└── other project config files       # Project configuration
```

In Part 1 (this guide), we'll focus on setting up the basic Next.js application structure, configuring environment variables, and adding styles. The implementation of specific components and API routes will come in Parts 2 and 3.

### 5. Basic Styling

Create a consistent look and feel by adding basic styles to your `src/app/globals.css` file: We are using custom styles for the paper, button, and custom-rounded classes. You can modify these styles to match your desired design. Or you can use Tailwind's built-in utilities to create your own styles. For now, copy the following code into your `src/app/globals.css` file:

```css
/* src/app/globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

:root {
  --bg-primary: #333;
  --bg-secondary: #ebebeb;
  --bg-accent: #212121;
  --fg-primary: #fff;
  --fg-secondary: #000;
  --fg-variant: #333;
  --white: #fff;
  --black: #000;
  --font:
    system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}

.paper {
  --bg-primary: #000;
  --bg-secondary: #dfd0b9;
  --bg-accent: #000;
  --fg-primary: #fff;
  --fg-secondary: #000;
  --fg-variant: #edede7;
  --white: #edede7;
  --black: #000;
}

@layer base {
  body {
    font-family: var(--font);
    background-color: var(--white);
    color: var(--black);
  }

  p,
  span {
    color: var(--fg-secondary);
  }
}

@layer components {
  .w-300 {
    max-width: 18.75rem;
    min-width: 18.75rem;
    width: 18.75rem;
  }

  .break {
    word-break: break-word;
  }

  .custom-border {
    border: 0.125rem solid var(--fg-secondary);
  }

  .custom-rounded {
    border-radius: 1rem;
  }

  section,
  article {
    box-shadow: 0.25rem 0.25rem var(--black);
    padding: 1rem;
    border: 0.125rem solid var(--fg-secondary);
    border-radius: 1rem;
    margin-bottom: 1rem;
  }

  .btn {
    border: 0.125rem solid var(--fg-secondary);
    border-radius: 1rem;
    box-shadow: 0.25rem 0.25rem;
    background-color: var(--white);
    color: var(--fg-secondary);
    padding: 1rem;
    font-family: var(--font);
    font-weight: bold;
    font-size: medium;
    transition: 0.1s;
    cursor: pointer;
  }

  .btn:hover {
    translate: 0.25rem 0.25rem;
    box-shadow: none;
    background-color: var(--bg-secondary);
  }

  .btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }

  input[type="text"],
  input[type="number"],
  select {
    width: 100%;
    padding: 10px;
    margin: 8px 0;
    display: block;
    border: 2px solid var(--black);
    background-color: var(--white);
    color: var(--black);
    font-size: 16px;
    box-sizing: border-box;
    border-radius: 1rem;
  }

  input[type="text"]:focus,
  input[type="number"]:focus,
  select:focus {
    outline: none;
    border-color: var(--black);
  }

  .container-spacing {
    padding: 1rem;
  }

  .container-spacing > * {
    margin-bottom: 1rem;
  }

  .container-spacing > *:last-child {
    margin-bottom: 0;
  }
}

```

## Verify Your Setup

Let's make sure your setup is working correctly:

1. Modify the existing home page at `src/app/page.tsx`. Next.js has already created this file during setup. Replace its contents with:

```tsx
export default function Home() {
  return (
    <main className="container mx-auto p-4">
      <h1 className="text-2xl font-bold mb-6">Cardano Transaction App</h1>
      <p>Welcome to the Cardano Transaction App!</p>
    </main>
  );
}
```

2. Start your development server:

```bash
npm run dev
```

3. Navigate to <http://localhost:3000> in your browser. You should see the welcome message.

{% hint style="success" %}
Congratulations! You've completed Part 1 of the guide. You now have a Next.js project ready for Cardano wallet integration.
{% endhint %}

## What's Next?

Now that you have a basic Next.js application set up, you're ready to integrate the Weld wallet connector. In [Part 2: Wallet Integration](/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-wallet), we'll implement the wallet connection functionality.


# Part 2: Wallet Integration

This is Part 2 of our guide to building a Cardano transaction application with Next.js and Weld. In this section, you'll implement wallet connectivity with proper SSR support.

## Weld Wallet Integration Overview

Weld is a universal Cardano wallet connector library that simplifies wallet integration by providing:

* A unified interface for multiple Cardano wallets
* TypeScript support with full type safety
* React hooks for state management
* Server-side rendering compatibility

See the [Weld documentation](https://github.com/Cardano-Forge/weld) for more information.

## Implementation Steps

### 1. Create the Weld Provider Component

First, we need to create a provider component that will make Weld available throughout your application. This component will handle wallet connectivity state and server/client hydration:

```tsx
// src/components/WeldProvider.tsx
"use client";

import { WeldProvider, type WeldProviderProps } from "@ada-anvil/weld/react";

export function ClientWeldProvider({
  children,
  lastConnectedWallet,
}: {
  children: React.ReactNode;
  lastConnectedWallet?: NonNullable<
    WeldProviderProps["wallet"]
  >["tryToReconnectTo"];
}) {
  return (
    <WeldProvider
      updateInterval={30_000} // 30 seconds
      wallet={{ tryToReconnectTo: lastConnectedWallet }} // Restore wallet connection state
    >
      {children}
    </WeldProvider>
  );
}
```

{% hint style="info" %}
The `updateInterval` option (set to 30 seconds) helps maintain active wallet connections during longer user sessions.
{% endhint %}

### 2. Integrate the Provider in Your Layout

Next, we'll update the app layout to use our `ClientWeldProvider`. Notice that we include the `lastConnectedWallet` prop to restore wallet connection state between server and client. This is important for maintaining wallet connectivity during page navigation, and preventing React hydration errors due to server-side rendering being inconsistent with client-side rendering.

See the [Server-Side Rendering (SSR) section in the Weld documentation](https://github.com/Cardano-Forge/weld?tab=readme-ov-file#server-side-rendering) for more information.

```tsx
// src/app/layout.tsx
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";

// Add the three imports.
import { ClientWeldProvider } from "@/components/WeldProvider";
import { cookies } from "next/headers";
import { STORAGE_KEYS } from "@ada-anvil/weld/server";

const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
});

const geistMono = Geist_Mono({
  variable: "--font-geist-mono",
  subsets: ["latin"],
});

export const metadata: Metadata = {
  title: "Cardano Transaction App",
  description: "Send Cardano transactions using the Anvil API",
};

export default async function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {

  // Add the cookie retrieval logic.
  const cookieStore = await cookies();
  const wallet = cookieStore.get(STORAGE_KEYS.connectedWallet)?.value;
  const changeAddress = cookieStore.get(STORAGE_KEYS.connectedChange)?.value;
  const stakeAddress = cookieStore.get(STORAGE_KEYS.connectedStake)?.value;
  const lastConnectedWallet = wallet ? { wallet, changeAddress, stakeAddress } : undefined;

  return (
    <html lang="en">
      <body
        className={`${geistSans.variable} ${geistMono.variable} antialiased`}
      >
        {/* Wrap the children with the ClientWeldProvider component. */}
        <ClientWeldProvider lastConnectedWallet={lastConnectedWallet}>
          {children}
        </ClientWeldProvider>
      </body>
    </html>
  );
}
```

### 3. Understanding SSR with Weld

Our implementation addresses three critical aspects of wallet integration in Next.js with SSR:

1. **Hydration Consistency**: By retrieving wallet connection data from cookies during server-side rendering, we ensure the initial client-side state matches what was rendered on the server, preventing React hydration errors.
2. **Connection Persistence**: The `updateInterval` configuration keeps an active wallet connection stable through periodic state checks.
3. **Seamless Reconnection**: The `tryToReconnectTo` property provides a smooth user experience by maintaining wallet connections across page refreshes.

### 4. Create a Wallet Connector Component

Now let's create a UI component that users will interact with to connect their wallets:

```tsx
// src/components/WalletConnector.tsx
"use client";
import { useState } from "react";

// Import Weld hooks to get wallet and user extensions. 
import { useWallet, useExtensions } from "@ada-anvil/weld/react";

// Import supported wallets from Weld.
import { SUPPORTED_WALLETS } from "@ada-anvil/weld";

// Helper function to truncate address for display
const truncateAddress = (address: string) => {
  if (!address) return "";
  return `${address.slice(0, 8)}...${address.slice(-8)}`;
};

// Component to display wallet info
const WalletInfo = ({ label, value }: { label: string; value: string }) => (
  <div>
    <span>{label} </span>
    <span>
      <b>{value}</b>
    </span>
  </div>
);

export default function WalletConnector() {
  const wallet = useWallet();
  const { supportedMap: installedWallets, isLoading } = useExtensions(
    "supportedMap",
    "isLoading",
  );
  const availableWallets = SUPPORTED_WALLETS.filter((w) =>
    installedWallets.has(w.key),
  );
  const [selectedWallet, setSelectedWallet] = useState<string>("");

  // Reset connection state if wallet selection changes
  const handleWalletSelection = (value: string) => {
    if (wallet.isConnectingTo && value !== wallet.isConnectingTo) {
      // Cancel any pending connection
      wallet
        .disconnect()
        .catch((err) => console.error("Failed to disconnect wallet:", err));
    }
    setSelectedWallet(value);
  };

  const handleConnect = async (walletKey?: string) => {
    if (!walletKey) return;
    try {
      await wallet.connectAsync(walletKey);
    } catch (error) {
      console.error("Failed to connect wallet:", error);
    }
  };

  return (
    <section className="paper">
      <h2>Wallet</h2>

      {wallet.isConnected ? (
        // Connected state - show wallet info and disconnect button
        <>
          <WalletInfo label="Connected to:" value={wallet.displayName || ""} />
          <WalletInfo
            label="Address:"
            value={truncateAddress(wallet.changeAddressBech32 || "")}
          />
          <WalletInfo
            label="Balance:"
            value={`${wallet.balanceAda?.toFixed(2) || "0.00"} ADA`}
          />

          <button
            onClick={() =>
              wallet
                .disconnect()
                .catch((err) =>
                  console.error("Failed to disconnect wallet:", err),
                )
            }
            className="btn mt-4"
          >
            Disconnect
          </button>
        </>
      ) : // Disconnected state - show wallet selector and connect button
      isLoading ? (
        <div>Detecting wallet extensions...</div>
      ) : (
        <div>
          <select
            className="custom-rounded mb-4"
            name="wallet-key"
            value={selectedWallet}
            onChange={(e) => handleWalletSelection(e.target.value)}
          >
            {availableWallets.length === 0 ? (
              <option value="">No wallets</option>
            ) : (
              <>
                <option value="">Select a wallet</option>
                {availableWallets.map((w) => (
                  <option key={w.key} value={w.key}>
                    {w.displayName}
                  </option>
                ))}
              </>
            )}
          </select>
          <button
            onClick={() => selectedWallet && handleConnect(selectedWallet)}
            className="btn text-center"
            disabled={wallet.isConnecting || availableWallets.length === 0}
          >
            {wallet.isConnecting
              ? `Connecting to ${wallet.isConnectingTo}...`
              : selectedWallet
                ? "Connect Wallet"
                : "Select a Wallet"}
          </button>
        </div>
      )}
    </section>
  );
}
```

### 5. Update the Home Page

Now that we have created the component. Lets update your home page to include the wallet connector component:

```tsx
// src/app/page.tsx
import WalletConnector from "@/components/WalletConnector";

export default function Home() {
  return (
    <main className="container mx-auto p-4">
      <h1 className="text-2xl font-bold mb-6">Cardano Transaction App</h1>
      <div className="max-w-md mx-auto">
        <WalletConnector />
      </div>
    </main>
  );
}
```

## Testing Your Wallet Integration

Now let's test the wallet integration to ensure it's working correctly:

1. **Start your development server**:

```bash
npm run dev
```

2. **Navigate to your application** (usually at <http://localhost:3000>)
3. **Test the wallet connection flow**:
   * Verify that available wallets are correctly detected in the dropdown
   * Select a wallet and click "Connect Wallet"
   * Confirm that the wallet popup appears requesting connection
   * After approving, verify that wallet information is displayed:
     * Connected wallet name
     * Truncated wallet address
     * ADA balance
4. **Test disconnection**:
   * Click the "Disconnect" button
   * Verify that the UI returns to the wallet selection state

{% hint style="warning" %}
If wallet connection fails, check your browser console for errors. Common issues include:

* Wallet extension not properly installed
* Wallet needs to be enabled for dApp interactions.
* Wallet locked (needs to be unlocked first)
* Incompatible wallet versions
  {% endhint %}

## Troubleshooting

### No Wallets Detected

If no wallets are appearing in the dropdown list:

1. Make sure you have wallet extensions installed (Eternl, Lace, etc.)
2. Refresh the page after installing a new wallet extension
3. Check your browser console for any errors

## What's Next?

Now that you have a working wallet integration, you're ready to implement transaction functionality. In [Part 3: Building Transactions](/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-transactions), we'll create the components and API routes needed to build and submit Cardano transactions.

{% hint style="success" %}
Congratulations! You've completed Part 2 of the guide. Your application can now detect wallets, connect to them, and display wallet information.
{% endhint %}


# Part 3: Building Transactions

This is Part 3 of our guide to building a Cardano transaction application with Next.js and Weld. In this section, you'll implement transaction building and submission functionality.

## Introduction

Now for the best part: With our wallet integration in place, we're now ready to implement the transaction functionality. This involves creating API endpoints for transaction building and submission, as well as building the user interface components for transaction input and status feedback.

## Transaction Flow Overview

For a comprehensive explanation of Cardano transaction concepts, please refer to our [Transaction Guide](/guides/transaction). In this tutorial, we'll focus on implementing the following streamlined transaction flow:

1. **Get Wallet Data**: Retrieve the user's wallet address and UTXOs
2. **Build Transaction**: Call Anvil API to construct the transaction
3. **Sign Transaction**: Use the connected wallet to sign the transaction
4. **Submit Transaction**: Send the signed transaction to the Cardano network
5. **Display Result**: Show the transaction ID and success/error feedback

## Implementation Steps

### 1. Create Anvil API Utility Functions

Instead of calling the Anvil API directly, we'll create utility functions that offer several advantages including type safety, error handling, and secure API key management through Next.js server components.

Our implementation will include three key functions:

* **callAnvilApi**: A generic request handler with proper error management
* **buildTransaction**: Creates transactions with sender addresses and payment details
* **submitTransaction**: Sends signed transactions to the Cardano network

Let's implement these utilities:

```typescript
// src/utils/anvil-api.ts
"use server";

const BASE_URL = process.env.NEXT_PUBLIC_ANVIL_API_URL;

interface AnvilApiConfig<T = Record<string, unknown>> {
  endpoint: string;
  method?: "GET" | "POST";
  body?: T;
  apiKey?: string;
}

export async function callAnvilApi<T, B = Record<string, unknown>>({
  endpoint,
  method = "POST",
  body,
  apiKey,
}: AnvilApiConfig<B>): Promise<T> {
  if (!BASE_URL) {
    throw new Error("Anvil API base URL is not configured");
  }

  const key = apiKey || process.env.ANVIL_API_KEY;
  if (!key) {
    throw new Error("API key is required for Anvil API");
  }

  const headers = {
    "Content-Type": "application/json",
    "X-Api-Key": key,
  };

  try {
    const response = await fetch(`${BASE_URL}/${endpoint}`, {
      method,
      headers,
      body: body ? JSON.stringify(body) : undefined,
    });

    if (!response.ok) {
      const errorData = await response
        .json()
        .catch(() => ({ message: "Unknown error" }));
      throw new Error(
        `Anvil API Error (${response.status}): ${errorData.message || "Unknown error"}`,
      );
    }

    return (await response.json()) as T;
  } catch (error) {
    console.error("Anvil API request failed:", error);
    throw error;
  }
}

export interface BuildTransactionParams {
  changeAddress: string;
  utxos: string[];
  outputs: {
    address: string;
    lovelace: number;
    assets?: Record<string, number>;
  }[];
}

export interface TransactionBuildResult {
  hash: string;
  complete: string; // CBOR
  stripped: string; // CBOR
  witnessSet: string; // CBOR
}

export async function buildTransaction(
  params: BuildTransactionParams,
): Promise<TransactionBuildResult> {
  return callAnvilApi<TransactionBuildResult, BuildTransactionParams>({
    endpoint: "transactions/build",
    body: params,
  });
}

export interface SubmitTransactionParams {
  transaction: string; // CBOR
  signatures?: string[]; // CBOR
}

export interface TransactionSubmitResult {
  txHash: string;
}

export async function submitTransaction(
  params: SubmitTransactionParams,
): Promise<TransactionSubmitResult> {
  return callAnvilApi<TransactionSubmitResult, SubmitTransactionParams>({
    endpoint: "transactions/submit",
    body: params,
  });
}
```

### 2. Create API Endpoints for Transaction Building and Submission

Next.js offers a built-in API routes system through its App Router architecture that allows us to create serverless functions. For this application, we'll create two API endpoints to handle transaction building and submission.

See the [Next.js Route Handlers documentation](https://nextjs.org/docs/app/building-your-application/routing/route-handlers) for more details on how these API routes work.

#### Transaction Building Endpoint

First, let's create the endpoint that builds a transaction with our Anvil API utility:

```typescript
// src/app/api/transaction/build/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { buildTransaction, BuildTransactionParams } from '@/utils/anvil-api';

/**
 * Build a Cardano transaction using Anvil API
 * 
 * This endpoint builds a transaction with the provided inputs and outputs
 * It requires:
 * - changeAddress: The sender's address for change
 * - utxos: Array of UTXOs from the wallet
 * - outputs: Where to send the ADA/assets with amounts
 */
export async function POST(request: NextRequest) {
  try {
    const body = await request.json();
    const { changeAddress, utxos, outputs } = body as BuildTransactionParams;
    
    if (!changeAddress || !utxos || !outputs) {
      return NextResponse.json(
        { error: "Missing required parameters" },
        { status: 400 }
      );
    }

    // Call utility function to build the transaction
    const transactionData = await buildTransaction({ changeAddress, utxos, outputs });

    // Return the transaction data for signing
    return NextResponse.json({
      hash: transactionData.hash,
      complete: transactionData.complete,
      stripped: transactionData.stripped,
      witnessSet: transactionData.witnessSet
    });
  } catch (error) {
    console.error("Error building transaction:", error);
    
    return NextResponse.json(
      { error: error instanceof Error ? error.message : "Failed to build transaction" },
      { status: 500 }
    );
  }
}
```

#### Transaction Submission Endpoint

Next, let's create the endpoint that submits a signed transaction to the Cardano network:

```typescript
// src/app/api/transaction/submit/route.ts
import { NextRequest, NextResponse } from "next/server";
import { submitTransaction, SubmitTransactionParams } from "@/utils/anvil-api";

/**
 * Submit a signed Cardano transaction to the blockchain using Anvil API
 *
 * This endpoint accepts:
 * - transaction: The transaction CBOR (can be unsigned)
 * - signatures: Optional array of signatures from the wallet
 */
export async function POST(request: NextRequest) {
  try {
    const body = await request.json();
    const { transaction, signatures } = body as SubmitTransactionParams;

    if (!transaction) {
      return NextResponse.json(
        { error: "Missing transaction" },
        { status: 400 },
      );
    }

    // Call utility function to submit the transaction
    const submissionData = await submitTransaction({ transaction, signatures });

    // Return the transaction hash and success message
    return NextResponse.json({
      hash: submissionData.txHash,
      message: "Transaction submitted successfully",
    });
  } catch (error) {
    console.error("Error submitting transaction:", error);

    return NextResponse.json(
      {
        error:
          error instanceof Error
            ? error.message
            : "Failed to submit transaction",
      },
      { status: 500 },
    );
  }
}
```

### 3. Create a Custom Hook for Transaction Management

To cleanly manage our transaction logic, we'll create a reusable custom hook that:

* Tracks the entire transaction lifecycle with multiple status states
* Manages API calls to our transaction endpoints
* Handles wallet integration for signing
* Provides consistent error handling and user feedback
* Exposes a simple interface for components to use

This pattern keeps our UI components focused on presentation rather than complex transaction logic.

```typescript
// src/hooks/useTransactionSubmission.ts
"use client";

import { useState, useCallback } from "react";
import { useWallet } from "@ada-anvil/weld/react";

// Transaction states for managing the flow
export type TransactionStatus =
  | "idle"
  | "building"
  | "signing"
  | "submitting"
  | "success"
  | "error";

export interface TransactionParams {
  recipient: string;
  ada: number;
}

export function useTransactionSubmission() {
  const [status, setStatus] = useState<TransactionStatus>("idle");
  const [txHash, setTxHash] = useState<string | null>(null);
  const [error, setError] = useState<string | null>(null);

  const wallet = useWallet(); // Use Weld's wallet hook

  const submitTransaction = useCallback(
    async ({ recipient, ada }: TransactionParams) => {
      // Convert ADA to lovelace (1 ADA = 1,000,000 lovelace)
      const lovelace = Math.floor(ada * 1_000_000);

      try {
        // Reset states
        setError(null);
        setTxHash(null);
        setStatus("building");

        // Validate wallet connection
        if (
          !wallet.isConnected ||
          !wallet.changeAddressBech32 ||
          !wallet.handler
        ) {
          throw new Error("Please connect your wallet first");
        }

        // Step 1: Build transaction
        const buildResponse = await fetch("/api/transaction/build", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            changeAddress: wallet.changeAddressBech32, // Sender's address from Weld
            utxos: await wallet.handler.getUtxos(), // Get available UTXOs from Weld
            outputs: [{ address: recipient, lovelace }],
          }),
        });

        if (!buildResponse.ok) {
          const errorData = await buildResponse.json();
          throw new Error(errorData.error || "Failed to build transaction");
        }

        const buildData = await buildResponse.json();

        // Step 2: Sign transaction
        setStatus("signing");
        const signature = await wallet.handler.signTx(buildData.complete);

        if (!signature) {
          throw new Error("Failed to sign transaction");
        }

        // Step 3: Submit transaction
        setStatus("submitting");
        const submitResponse = await fetch("/api/transaction/submit", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            transaction: buildData.complete,
            signatures: [signature],
          }),
        });

        if (!submitResponse.ok) {
          const errorData = await submitResponse.json();
          throw new Error(errorData.error || "Failed to submit transaction");
        }

        const submitData = await submitResponse.json();

        setTxHash(submitData.hash);
        setStatus("success");
        return submitData.hash;
      } catch (err) {
        setError(err instanceof Error ? err.message : "Unknown error");
        setStatus("error");
        return null;
      }
    },
    [wallet],
  );

  const reset = useCallback(() => {
    setStatus("idle");
    setTxHash(null);
    setError(null);
  }, []);

  return {
    status,
    txHash,
    error,
    submitTransaction,
    reset,
    isProcessing:
      status !== "idle" && status !== "error" && status !== "success",
  };
}
```

### 4. Create the Transaction Form Component

Now, let's create the UI component for transactions. This component will:

* Provide inputs for recipient address and ADA amount
* Display transaction status and results
* Handle form validation and submission
* Show a link to a blockchain explorer after successful transactions

The form integrates with our custom hook from the previous step to manage transaction state and processing. It also demonstrates proper error handling and user feedback throughout the transaction flow.

```tsx
// src/components/TransactionForm.tsx
"use client";

import { useState, useCallback } from "react";
import { useWallet } from "@ada-anvil/weld/react";
import { useTransactionSubmission } from "@/hooks/useTransactionSubmission";
const NETWORK = process.env.NEXT_PUBLIC_NETWORK || "preprod";

const getExplorerUrl = (txHash: string) => {
  return NETWORK === "mainnet"
    ? `https://cexplorer.io/tx/${txHash}`
    : `https://${NETWORK}.cexplorer.io/tx/${txHash}`;
};

function getButtonText(status: string, adaAmount: number): string {
  if (status === "building") return "Building...";
  if (status === "signing") return "Signing...";
  if (status === "submitting") return "Submitting...";
  if (adaAmount === 0) return "Send ADA";
  return `Send ${adaAmount} ADA`;
}

export default function TransactionForm() {
  // Form state
  const [recipient, setRecipient] = useState("");
  const [adaAmount, setAdaAmount] = useState(0);

  // Use our custom hook for transaction management
  const {
    status,
    txHash,
    error,
    submitTransaction,
    reset: resetTransaction,
  } = useTransactionSubmission();

  // Get wallet information from the Weld hook
  const wallet = useWallet();

  // Handle the transaction submission process
  const handleSendTransaction = useCallback(
    async (e: React.FormEvent) => {
      e.preventDefault();
      await submitTransaction({ recipient, ada: adaAmount });
    },
    [submitTransaction, recipient, adaAmount],
  );

  // Render success state if transaction completed
  if (status === "success" && txHash) {
    return (
      <section className="paper">
        <h2>Transaction Successful</h2>
        <span>Transaction ID:</span>
        <br />
        <a
          href={getExplorerUrl(txHash)}
          target="_blank"
          rel="noopener noreferrer"
          className="break mt-1 font-mono text-sm text-blue-600 underline"
        >
          <b>{txHash}</b>
        </a>
        <div className="mt-6">
          <button onClick={resetTransaction} className="btn">
            Send Another
          </button>
        </div>
      </section>
    );
  }

  const isFormDisabled =
    !wallet.isConnected || (status !== "idle" && status !== "error");

  // Render transaction form
  return (
    <section className="paper">
      <h2>Send Transaction</h2>
      <form onSubmit={handleSendTransaction}>
        {/* Wallet connection status */}
        {!wallet.isConnected && (
          <div className="break mb-2 text-red-600">
            Please connect your wallet first.
          </div>
        )}

        {/* Recipient address input */}
        <div className="mb-2">
          <label htmlFor="recipient" className="block mb-1 font-medium">
            Recipient Address
          </label>
          <input
            className="custom-rounded"
            name="recipient"
            id="recipient"
            value={recipient}
            onChange={(e) => setRecipient(e.target.value)}
            type="text"
            placeholder="addr..."
            required
            disabled={isFormDisabled}
          />
        </div>

        {/* ADA amount input */}
        <div className="mb-4">
          <label htmlFor="ada-amount" className="block mb-1 font-medium">
            Amount (ADA)
          </label>
          <input
            className="custom-rounded"
            name="ada-amount"
            id="ada-amount"
            value={adaAmount || ""}
            onChange={(e) => {
              const value = parseFloat(e.target.value);
              setAdaAmount(isNaN(value) ? 0 : value);
            }}
            type="number"
            min="1"
            step="1"
            placeholder="Amount in ADA"
            required
            disabled={isFormDisabled}
          />
        </div>

        {/* Submit button */}
        <button type="submit" className="btn" disabled={isFormDisabled}>
          {getButtonText(status, adaAmount)}
        </button>

        {error && (
          <div className="break mt-2 text-red-600">
            <strong>Error:</strong> {error}
          </div>
        )}
      </form>
    </section>
  );
}
```

### 5. Update the Home Page to Include the Transaction Form

Finally, let's update the home page to include both the wallet connector and transaction form:

```tsx
// src/app/page.tsx
import WalletConnector from "@/components/WalletConnector";
import TransactionForm from "@/components/TransactionForm";

export default function Home() {
  return (
    <main className="container mx-auto p-4">
      <h1 className="text-2xl font-bold mb-6">Cardano Transaction App</h1>
      
      <div className="grid md:grid-cols-2 gap-6">
        <WalletConnector />
        {/* Add the TransactionForm component below */}
        <TransactionForm /> 
      </div>
    </main>
  );
}
```

## Testing Your Transaction Flow

1. **Start your development server**:

```bash
npm run dev
```

2. **Navigate to your application** (usually at <http://localhost:3000>)
3. **Connect your wallet** following the instructions from Part 2
4. **Test the transaction flow**:
   * Enter a valid recipient address (use a testnet address for testing)
   * Enter a small amount of ADA (e.g., 1-2 ADA)
   * Click the "Send" button
   * Observe the transaction states: Building → Signing → Submitting → Success
   * Verify the transaction ID appears and links to a block explorer

{% hint style="warning" %}
Make sure you're using a testnet wallet with testnet ADA for testing. Never use real ADA or mainnet addresses during development and testing.
{% endhint %}

## Troubleshooting

### Common Transaction Errors

1. **Insufficient Funds**
   * Ensure your wallet has enough testnet ADA for the transaction and fees
   * Try sending a smaller amount
2. **Invalid Address**
   * Verify you're using a valid Cardano address format
   * Check that you're using a testnet address when testing on testnet
3. **Signature Failure**
   * Make sure your wallet is unlocked
   * Try reconnecting your wallet if signing fails
   * Check that your wallet is enabled for dApp interactions
4. **Network Issues**
   * Check your internet connection
   * Verify the Anvil API is available and responding (Use `/health` endpoint)

## Deployment Considerations

When deploying your application to production:

1. **Environment Variables**: Ensure your production environment has the correct API URLs and keys
2. **Network Selection**: Update the `NEXT_PUBLIC_NETWORK` to `mainnet` for production use
3. **Error Handling**: Implement more robust error handling and user feedback
4. **Security**: Ensure your API keys are properly secured and not exposed to clients

## Conclusion

Congratulations! You've successfully built a complete Cardano transaction application using Next.js and Weld. Your application can now:

* Connect to Cardano wallets
* Build and sign transactions
* Submit transactions to the Cardano network
* Display transaction results

<figure><img src="/files/tDD2nmVZFi6zLxqnKwaF" alt="Completed Cardano Transaction App with Next.js and Weld"><figcaption><p>Your completed Cardano transaction application</p></figcaption></figure>

{% hint style="success" %}
You've completed all three parts of this guide! You now have a working Cardano transaction application that demonstrates the core functionality needed for Cardano dApp development.
{% endhint %}

## Next Steps

To further enhance your application, consider exploring:

* [Creating Custom Transactions](https://github.com/Cardano-Forge/anvil-api/blob/main/guides/transaction/create-custom-transaction/README.md) for more complex use cases
* [Adding Transaction Metadata](https://github.com/Cardano-Forge/anvil-api/blob/main/guides/transaction/create-transaction-with-metadata-cip-20/README.md) following CIP-20
* [NFT & FT Minting](https://github.com/Cardano-Forge/anvil-api/blob/main/guides/nft-and-ft/README.md) to add token functionality
* [Smart Contract Integration](https://github.com/Cardano-Forge/anvil-api/blob/main/guides/smart-contract/README.md) for advanced use cases


# Select UTXOs

How to provide UTXOs when building transactions with the Anvil API.

Unspent Transaction Outputs (UTXOs) are Cardano's way of tracking value. Unlike account-based blockchains, Cardano tracks individual outputs from previous transactions that haven't been spent yet. When building a transaction, you need to specify which UTXOs to use as inputs.

## Test vs Production

In **test environments** (preprod, preview), the `utxos` parameter is optional—Anvil will automatically fetch and select UTXOs from the `changeAddress`:

```typescript
// Test environment: No UTXOs needed
const response = await fetch('https://preprod.api.ada-anvil.app/v2/services/transactions/build', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Api-Key': 'YOUR_API_KEY'
  },
  body: JSON.stringify({
    changeAddress: 'addr_test...',
    outputs: [{ address: 'addr_test...', lovelace: 5_000_000 }]
  })
});
```

{% hint style="warning" %}
**Production requires explicit UTXOs.** You must provide the `utxos` parameter when building transactions on mainnet. Multi-account wallets (e.g., Eternl) also require explicit UTXOs in all environments.
{% endhint %}

The transaction builder accepts two UTXO-related parameters:

* **`utxos`** — Available UTXOs (hex-CBOR encoded) for the builder to select from
* **`requiredInputs`** — Specific UTXOs that *must* be consumed (useful for smart contracts where a particular UTXO unlocks state or rewards)

```typescript
// Production: Explicit UTXOs with a required input
body: JSON.stringify({
  changeAddress: 'addr...',
  utxos: ['8282...', '8282...'],        // Pool of available UTXOs
  requiredInputs: ['8282...'],          // Must be spent (e.g., script UTXO)
  outputs: [{ address: 'addr...', lovelace: 5_000_000 }]
})
```

## Fetching UTXOs

Choose your approach based on where your code runs:

| Scenario                           | Approach                            | Why                                        |
| ---------------------------------- | ----------------------------------- | ------------------------------------------ |
| Browser dApp with connected wallet | [**Weld**](#frontend-weld)          | UTXOs from user's wallet, seamless signing |
| Backend service or automation      | [**Anvil API**](#backend-anvil-api) | No browser needed, query any address       |
| Hardware wallet signing            | [**Anvil API**](#backend-anvil-api) | Fetch UTXOs server-side, sign offline      |

### Frontend: Weld

[Weld](https://github.com/Cardano-Forge/weld) provides a unified interface for CIP-30 browser wallets (Eternl, Lace, Nami, etc.). UTXOs come directly from the user's connected wallet and are always current.

```typescript
import { useWallet } from "@ada-anvil/weld";

const wallet = useWallet("isConnected", "handler");

const buildTransaction = async () => {
  if (!wallet.isConnected) {
    return;
  }

  const utxos = await wallet.handler.getUtxos();
  const changeAddress = wallet.changeAddressBech32;

  const response = await fetch('https://preprod.api.ada-anvil.app/v2/services/transactions/build', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', 'X-Api-Key': 'YOUR_API_KEY' },
    body: JSON.stringify({
      changeAddress,
      utxos,
      outputs: [{ address: 'addr_test1qy...', lovelace: 5_000_000 }]
    })
  });

  const { complete } = await response.json();
  const signature = await wallet.handler.signTx(complete);
  // Submit signed transaction...
};
```

### Backend: Anvil API

For server-side applications, use Anvil's `/wallets/utxos` endpoint. UTXOs are returned in the exact hex-CBOR format needed for transaction building.

```typescript
const API_BASE = 'https://preprod.api.ada-anvil.app/v2/services';
const API_KEY = 'YOUR_API_KEY';

async function getUtxos(address: string): Promise<string[]> {
  const response = await fetch(`${API_BASE}/wallets/utxos?address=${address}`, {
    headers: { 'X-Api-Key': API_KEY }
  });
  return response.json();
}

const address = 'addr_test1qz...';
const utxos = await getUtxos(address);

const response = await fetch(`${API_BASE}/transactions/build`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'X-Api-Key': API_KEY },
  body: JSON.stringify({
    changeAddress: address,
    utxos,
    outputs: [{ address: 'addr_test1qy...', lovelace: 5_000_000 }]
  })
});
```

Additional options: query by `stakeAddress` to get UTXOs across all derived addresses, or enable `includeMempool` for chained transactions. See [Wallet Endpoints](/developer-tools/wallet-endpoints) for full parameter documentation.

{% hint style="info" %}
**Other UTXO sources** (BlockFrost, Ogmios, Dolos, etc.) work with Anvil's transaction builder as long as UTXOs are hex-CBOR encoded.
{% endhint %}

***

**Related:** [Transaction Building](/guides/transaction) · [Wallet Endpoints](/developer-tools/wallet-endpoints)


# Sign Transaction

Signing a transaction is a critical step that authorizes the movement of funds or interaction with smart contracts on the Cardano blockchain. This guide covers the primary methods for signing: on the frontend using a browser-based wallet, and on the backend using a server-side script.

## Frontend (Client-Side) Signing

Frontend signing is the most common method for user-centric applications. It leverages the user's installed browser wallet (like Eternl or Lace) to request a signature, ensuring that the user's private keys never leave their secure environment.

### Using Weld

We recommend using our [Weld](https://github.com/Cardano-Forge/weld) library to simplify interaction with browser wallets. Weld provides a unified API to connect to various wallets and request transaction signatures.

### Using the Browser Developer Console

For quick testing, you can directly use the CIP-30 wallet API exposed in the browser's developer console.

1. Go to any website with a connected Cardano wallet (e.g., `https://ada-anvil.io`).
2. Open the Developer Console.
3. Execute the following JavaScript to enable the wallet and request a signature:

   ```javascript
   // Example using Eternl wallet
   const w = await window.cardano.eternl.enable();

   // Replace with your actual transaction CBOR
   const signedTx = await w.signTx("Your unsigned transaction CBOR", true);

   console.log(signedTx);
   ```
4. Some wallets will prompt you to authorize your dApp to interact with the wallet.
5. Your wallet will then prompt you to sign the transaction.
6. The signed transaction CBOR, including the witness set, will be printed to the console.


# Submit Transaction

Submit a signed transaction to the Cardano network

## Introduction

Once you have a signed transaction. You are ready to submit it to the Cardano network

## Usage

### Payload

In order to submit the transaction, you need at least one valid signature with the provided transaction.

This approach supports multi signature (*multi sig*) as well.

{% code overflow="wrap" %}

```json
{
  "transaction": "84a4... *The complete transaction*",
  "signatures": ["a100...", "a100..."]
}
```

{% endcode %}

### Submit the Transaction

**You can refer to this page to see how to create a transaction:**

{% content-ref url="/pages/RCnVmsz8IY84fmYcqVQX" %}
[Create Basic Transaction](/guides/transaction/create-basic-transaction)
{% endcontent-ref %}

Once **signed**, you can submit it using the following endpoint:

#### With Bash and cURL

{% content-ref url="/pages/Q5ZDYBTN4CqEsW0t1QOB" %}
[Broken mention](broken://pages/Q5ZDYBTN4CqEsW0t1QOB)
{% endcontent-ref %}

#### With Fetch

{% content-ref url="/pages/t980xDOXeLRPzIf38hdn" %}
[Broken mention](broken://pages/t980xDOXeLRPzIf38hdn)
{% endcontent-ref %}

**Cardano Explorer**

You can get your transaction on explorer: <https://preprod.cexplorer.io/tx/b7969bf21b1125938bf112df7d932b66a519b32f0e94e36eb7d3e98cc4ef0c98>


# Create Custom Transaction

Send ADA and assets to multiple recipients in a single transaction

## Introduction

This guide demonstrates how to create a **custom transaction** that sends ADA and assets to multiple recipient addresses in a single transaction. This powerful feature allows you to efficiently distribute funds and tokens to several wallets at once, reducing transaction costs and improving throughput. Simply include **multiple outputs** in the payload to each recipient, and define what ADA and assets to send, and Anvil API will handle building the transaction for you.

For more information on **transactions**, please refer to the [Transaction Overview](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/transaction/transaction/README.md).

## Key Features

* Send different amounts of ADA to multiple recipients
* Distribute different native assets to different addresses
* Customize each output independently
* Handle complex transactions in a single API call

## Requirements

* A Cardano wallet with sufficient ADA
* Native assets in your wallet (if sending assets)
* Recipient wallet addresses
* Valid API key for authentication

## Full Examples

### Using Bash and cURL

{% content-ref url="/pages/CYJa1JzvztabyqOES1AF" %}
[Bash & cURL](/guides/transaction/create-custom-transaction/bash-and-curl)
{% endcontent-ref %}

### Using TS/JS Fetch

{% content-ref url="/pages/LYTHGUePMPudmg8XfRpX" %}
[Deno & Fetch](/guides/transaction/create-custom-transaction/node-and-fetch)
{% endcontent-ref %}

***

## **Specifications**

### API Endpoint

```
POST /transactions/build
```

### Request Structure

```json
{
  "changeAddress": "<sender_address>",
  "outputs": [
    {
      "address": "<receiver_address_1>",
      "lovelace": <lovelace_amount>,
      "assets": [
        {
          "policyId": "<policy_id>",
          "assetName": "<asset_name>",
          "quantity": <lovelace_amount>
        }
      ]
    },
    {
      "address": "<receiver_address_2>",
      "lovelace": <lovelace_amount>,
      "assets": [
        {
          "policyId": "<policy_id>",
          "assetName": "<asset_name>",
          "quantity": <lovelace_amount>
        }
      ]
    }
    // Add as many outputs as needed
  ],
  // Optional bounds that determine when the transaction is valid. 
  // False can be used to disable a bound.
  // If transactions are submitted outside of the validity interval, they will be rejected.
  "validityInterval": {
    "start": <posix_timestamp_or_slot_number>, // Optional, default: current slot
    "end": <posix_timestamp_or_slot_number>    // Optional, default: current slot + 2 hours
  }
}
```

## Best Practices

* Always verify recipient addresses before submitting transactions
* Include sufficient ADA in each output (minimum 2 ADA if sending native assets)
* Test transactions on testnet before moving to mainnet
* Consider transaction fees when planning outputs
* Keep your API key secure and never expose it in client-side code


# Bash & cURL

Using bash & cURL to build, sign\* and submit a custom transaction

## Introduction

This guide will show you how to create a custom transaction using bash and cURL.

## Objectives

This guide shows the robustness of the Anvil API transaction builder, by sending two assets and ADA to multiple recipients all within a single transaction.

## Requirements

* A Cardano wallet with 2 assets and at a minimum 20ADA
* Two Cardano wallets to send assets and ADA to
* A wallet extension to sign the transaction
* An API key

## Payload

**Send 10 ADA + 1 asset to address#1 and Send 5 ADA + 1 asset to address#2**

```json
{
  "changeAddress": "<sender_address>",
  "outputs": [
    {
      "address": "<receiver_address_1>",
      "lovelace": 10_000_000,
      "assets": [
        {
          "policyId": "<policy_id>",
          "assetName": "<asset_name>",
          "quantity": 1
        }
      ]
    },
    {
      "address": "<receiver_address_2>",
      "lovelace": 5_000_000,
      "assets": [
        {
          "policyId": "<policy_id>",
          "assetName": "<asset_name>",
          "quantity": 1
        }
      ]
    }

  ]
}
```

## Usage

Using Bash/cURL

### Parameters

{% code overflow="wrap" %}

```bash
# Wallets
SENDER_ADDRESS="addr_test1qq7fc3ke49nkcsfglltut7apa9t3gdul4utwhxt6j2hdrw7pg4vk6erdshyhdj5xeq0vh8qdy34cpdfstvc8l9su8hgq679eew"
RECEIVER_ADDRESS_1="addr_test1qrydyk6uw6cehk5u3zspyz3dhnwzmhfls2fp42vv5dv9g2z3885pg4kpkn30ptezc855lu3w5ey93zcr5lrezjmwkftqg8xvge"
RECEIVER_ADDRESS_2="addr_test1qr0tkwvlln0v5fljdxceudmlpt5y6szc84vpj4skm836tgn4hsqaesgg97l8ppy5rsn0alj8pth6lqe20fdyydsdgw6sr74cyt"
# Resources
POLICY_ID="360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13"
ASSET_NAME_1="616e76696c61706963697032355f333837393837393739"
ASSET_NAME_2="616e76696c61706963697032355f333837393732"
# See Authentication page for API key details.
X_API_KEY="testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9"
API_URL="https://preprod.api.ada-anvil.app/v2/services"
```

{% endcode %}

### Code

```bash
curl -X POST \
    -H "Content-Type: application/json" \
    -H "X-Api-Key: ${X_API_KEY}" \
    -d '{
        "changeAddress": "'${SENDER_ADDRESS}'",
        "outputs": [
            {
                "address": "'${RECEIVER_ADDRESS_1}'",
                "lovelace": 10000000,
                "assets": [{
                    "assetName": "'${ASSET_NAME_1}'",
                    "policyId": "'${POLICY_ID}'",
                    "quantity": 1
                }]
            },
            {
                "address": "'${RECEIVER_ADDRESS_2}'",
                "lovelace": 5000000,
                "assets": [{
                    "assetName": "'${ASSET_NAME_2}'",
                    "policyId": "'${POLICY_ID}'",
                    "quantity": 1
                }]
            }
        ]
    }' \
    ${API_URL}/transactions/build
```

### Expected Response

{% code overflow="wrap" %}

```json
{
  hash: 
    "7befd78854418b5465d505fa853a0631c1e8165bb0557b15ec6f194fcacd19a9",

  complete:
  "84a600d90102838258205bf3681e7bfe3322c5e05cda80a5a784177d3239f1aae6d929c8a86be3474198018258206a6bb5dafa917de09a2b727d4050c785f59f39f1b819e43a34570c8a537cadf00825820e490036d4c93cca91ada4365a2ea99d6a6b030338d041c02ba0a77e91c417eac01018482583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca3585428513981456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb256821a00989680a1581c360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13a157616e76696c6170696369702355f3338373938373937390182583900debb399ffcdeca27f269b19e377f0ae84d40583d58195616d9e3a5a275bc01dcc1082fbe7084941c26fefe470aefaf832a7a5a42360d43b5821a00c4b40a1581c360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13a154616e76696c61706963697032355f33383739373201a300581d605d2018b32a3752c252a8f08f5d34af8ff8993da9c30d5bf94e26522011a00155cc0028201d8184a49616e76696c2d746167825839003c9c46d9a9676c4128ffd7c5fba1e95714379faf16eb997a92aed1bbc145596d646d85c96ca86c81ecb9c0d246b80b5305b307f961c3dd0821a1c5c8558a1581c360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13a3581b616e76696c61706963697032355f313743130333330373738303401581b616e76696c61706963697032355f3137343130333331383531353001581b616e76696c61706963697032355f3137343133363330393135333801021a000329d031a0522114b081a0521f52b0ed9010281581c5d2018b32a3752c252a8f08f5d34daf8ff8993da9c30d5bf94e26522a100d9010281825820292af64334964e4e37e09a65aa7b14bf91a26236817ccc13acc660ccd2f3b725840003c389bf132640f78158f180b14f057a9919924b79c5e4771a9bccb0c225cde1bf3f1e5da4aac0649d8d905aa6dbe8734fa9da0053af103b1c0e480bb25503f5f6",
  stripped:"84a600d90102838258205bf3681e7bfe3322c5e05cda80a5a784177d3239f1aae6d929c8a86be3474198018258206a6bb5dafa917de09a2b727d4050c785f59f39f1b819e43a34570c8a537cadf00825820e490036d4c93cca91ada4365a2ea99d6a6b030338d041c02ba0a77e91c417eac01018482583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca3585428513981456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb256821a00989680a1581c360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13a157616e76696c6170696369702355f3338373938373937390182583900debb399ffcdeca27f269b19e377f0ae84d40583d58195616d9e3a5a275bc01dcc1082fbe7084941c26fefe470aefaf832a7a5a42360d43b5821a00c4b40a1581c360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13a154616e76696c61706963697032355f33383739373201a300581d605d2018b32a3752c252a8f08f5d34af8ff8993da9c30d5bf94e26522011a00155cc0028201d8184a49616e76696c2d746167825839003c9c46d9a9676c4128ffd7c5fba1e95714379faf16eb997a92aed1bbc145596d646d85c96ca86c81ecb9c0d246b80b5305b307f961c3dd0821a1c5c8558a1581c360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13a3581b616e76696c61706963697032355f313743130333330373738303401581b616e76696c61706963697032355f3137343130333331383531353001581b616e76696c61706963697032355f3137343133363330393135333801021a000329d031a0522114b081a0521f52b0ed9010281581c5d2018b32a3752c252a8f08f5d34daf8ff8993da9c30d5bf94e26522a0f5f6",

  witnessSet:"a100d9010281825820292af64334964e4e37e09a65aa7b14bf91a286236817ccc13acc660ccd2f3b725840003c389bf132640f78158f180b14f057a9919924b79c5e4771a9bccb0c225cdebf3f1e5da4aac0649d8d905aa6dbe8734fa9da0053af103b1c0e480fbb25503"
}
```

{% endcode %}


# Deno & Fetch

## Requirements

* A Cardano wallet with sufficient ADA and assets
* Multiple recipient Cardano wallet addresses
* Node.js environment (v14+) or Deno runtime
* Valid API key for authentication

## Objectives

This guide demonstrates the flexibility of the Anvil API for creating complex transactions.

**What You'll Accomplish:**

* Send different amounts of ADA to multiple recipients
* Distribute different native assets to different addresses
* Handle everything in a single transaction

## API Request Structure

### Payload Format

**Send 10 ADA + 1 asset to address#1 and Send 5 ADA + 1 asset to address#2**

```json
{
  "changeAddress": "addr...",
  "outputs": [
    {
      "address": "addr...1",
      "lovelace": 10_000_000, // 10 ADA
      "assets": [
        {
          "policyId": "<policy_id>",
          "assetName": "<asset_name>",
          "quantity": 1
        }
      ]
    },
    {
      "address": "addr...2",
      "lovelace": 5_000_000, // 5 ADA
      "assets": [
        {
          "policyId": "<policy_id>",
          "assetName": "<asset_name>",
          "quantity": 1
        }
      ]
    }
    // Add as many outputs as needed
  ]
}
```

## Implementation

### Configuration and Parameters

{% code overflow="wrap" %}

```typescript
// Wallet addresses
const SENDER_ADDRESS =
  "addr_test1qq7fc3ke49nkcsfglltut7apa9t3gdul4utwhxt6j2hdrw7pg4vk6erdshyhdj5xeq0vh8qdy34cpdfstvc8l9su8hgq679eew";
const RECEIVER_ADDRESS_1 =
  "addr_test1qrydyk6uw6cehk5u3zspyz3dhnwzmhfls2fp42vv5dv9g2z3885pg4kpkn30ptezc855lu3w5ey93zcr5lrezjmwkftqg8xvge";
const RECEIVER_ADDRESS_2 =
  "addr_test1qr0tkwvlln0v5fljdxceudmlpt5y6szc84vpj4skm836tgn4hsqaesgg97l8ppy5rsn0alj8pth6lqe20fdyydsdgw6sr74cyt";

// Asset information
const POLICY_ID = "360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13";
const ASSET_NAME_1 = "616e76696c61706963697032355f333837393837393739";
const ASSET_NAME_2 = "616e76696c61706963697032355f333837393732";

// API configuration
const X_API_KEY = "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9";
const API_URL = "https://preprod.api.ada-anvil.app/v2/services";
const HEADERS = { "Content-Type": "application/json", "x-api-key": X_API_KEY };
```

{% endcode %}

### Request Body

*Body Structure for creating a custom transaction using the previously collected values.*

{% code overflow="wrap" %}

```typescript
const BODY = {
  changeAddress: SENDER_ADDRESS,
  outputs: [
    {
      address: RECEIVER_ADDRESS_1,
      lovelace: 10_000_000, // 10 ADA
      assets: [
        {
          assetName: ASSET_NAME_1,
          policyId: POLICY_ID,
          quantity: 1,
        },
      ],
    },
    {
      address: RECEIVER_ADDRESS_2,
      lovelace: 5_000_000, // 5 ADA
      assets: [
        {
          assetName: ASSET_NAME_2,
          policyId: POLICY_ID,
          quantity: 1,
        },
      ],
    },
  ],
};
```

{% endcode %}

### API Call (using Node.js and Fetch)

Basic POST call with Fetch

{% code title="custom-transaction.ts" overflow="wrap" %}

```typescript
const response = await fetch(`${API_URL}/transactions/build`, {
  method: "POST",
  headers: HEADERS,
  body: JSON.stringify(BODY),
});

const result = await response.json();
console.log(result);
```

{% endcode %}

### Deno Command

```bash
deno run --allow-net custom-transaction.ts
```

### Output

{% code overflow="wrap" %}

```json
{
  "hash": "7befd78854418b5465d505fa853a0631c1e8165bb0557b15ec6f194fcacd19a9",
  "complete": "84a600d90102838258205bf3681e7bfe3322c5e05cda80a5a784177d3239f1aae6d929c8a86be3474198018258206a6bb5dafa917de09a2b727d4050c785f59f39f1b819e43a34570c8a5377cadf00825820e490036d4c93cca91ada4365a2ea99d6a6b030338d041c02ba0a77e91c417eac01018482583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb256821a00989680a1581c360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13a157616e76696c61706963697032355f3338373938373937390182583900debb399ffcdeca27f269b19e377f0ae84d40583d58195616d9e3a5a275bc01dcc1082fbe7084941c26fefe470aefaf832a7a5a42360d43b5821a004c4b40a1581c360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13a154616e76696c61706963697032355f33383739373201a300581d605d2018b32a3752c252a8f08f5d34daf8ff8993da9c30d5bf94e26522011a00155cc0028201d8184a49616e76696c2d746167825839003c9c46d9a9676c4128ffd7c5fba1e95714379faf16eb997a92aed1bbc145596d646d85c976ca86c81ecb9c0d246b80b5305b307f961c3dd0821a1c5c8558a1581c360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13a3581b616e76696c61706963697032355f3137343130333330373738303401581b616e76696c61706963697032355f3137343130333331383531353001581b616e76696c61706963697032355f3137343133363330393135333801021a0003429d031a0522114b081a0521f52b0ed9010281581c5d2018b32a3752c252a8f08f5d34daf8ff8993da9c30d5bf94e26522a100d9010281825820292af64334964e4e37e09a65aa7b14bf91a286236817ccc13acc660ccd2f3b725840003c389bf132640f78158f180b14f057a9919924b79c5e4771a9bccb0c225cde1bf3f1e5da4aac0649d8d905aa6dbe8734fa9da0053af103b1c0e480fbb25503",
  "stripped": "84a600d90102838258205bf3681e7bfe3322c5e05cda80a5a784177d3239f1aae6d929c8a86be3474198018258206a6bb5dafa917de09a2b727d4050c785f59f39f1b819e43a34570c8a5377cadf00825820e490036d4c93cca91ada4365a2ea99d6a6b030338d041c02ba0a77e91c417eac01018482583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb256821a00989680a1581c360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13a157616e76696c61706963697032355f3338373938373937390182583900debb399ffcdeca27f269b19e377f0ae84d40583d58195616d9e3a5a275bc01dcc1082fbe7084941c26fefe470aefaf832a7a5a42360d43b5821a004c4b40a1581c360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13a154616e76696c61706963697032355f33383739373201a300581d605d2018b32a3752c252a8f08f5d34daf8ff8993da9c30d5bf94e26522011a00155cc0028201d8184a49616e76696c2d746167825839003c9c46d9a9676c4128ffd7c5fba1e95714379faf16eb997a92aed1bbc145596d646d85c976ca86c81ecb9c0d246b80b5305b307f961c3dd0821a1c5c8558a1581c360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13a3581b616e76696c61706963697032355f3137343130333330373738303401581b616e76696c61706963697032355f3137343130333331383531353001581b616e76696c61706963697032355f3137343133363330393135333801021a0003429d031a0522114b081a0521f52b0ed9010281581c5d2018b32a3752c252a8f08f5d34daf8ff8993da9c30d5bf94e26522a0f5f6",
  "witnessSet": "a100d9010281825820292af64334964e4e37e09a65aa7b14bf91a286236817ccc13acc660ccd2f3b725840003c389bf132640f78158f180b14f057a9919924b79c5e4771a9bccb0c225cde1bf3f1e5da4aac0649d8d905aa6dbe8734fa9da0053af103b1c0e480fbb25503"
}
```

{% endcode %}

## Complete Example (Deno/Node.js)

<details>

<summary>custom-transaction.ts</summary>

{% code overflow="wrap" %}

```typescript
// Wallet addresses
const SENDER_ADDRESS =
  "addr_test1qq7fc3ke49nkcsfglltut7apa9t3gdul4utwhxt6j2hdrw7pg4vk6erdshyhdj5xeq0vh8qdy34cpdfstvc8l9su8hgq679eew";
const RECEIVER_ADDRESS_1 =
  "addr_test1qrydyk6uw6cehk5u3zspyz3dhnwzmhfls2fp42vv5dv9g2z3885pg4kpkn30ptezc855lu3w5ey93zcr5lrezjmwkftqg8xvge";
const RECEIVER_ADDRESS_2 =
  "addr_test1qr0tkwvlln0v5fljdxceudmlpt5y6szc84vpj4skm836tgn4hsqaesgg97l8ppy5rsn0alj8pth6lqe20fdyydsdgw6sr74cyt";

// Asset information
const POLICY_ID = "360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13";
const ASSET_NAME_1 = "616e76696c61706963697032355f333837393837393739";
const ASSET_NAME_2 = "616e76696c61706963697032355f333837393732";

// API configuration
const X_API_KEY = "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9";
const API_URL = "https://preprod.api.ada-anvil.app/v2/services";
const HEADERS = { "Content-Type": "application/json", "x-api-key": X_API_KEY };

const requestBody = {
  changeAddress: SENDER_ADDRESS,
  outputs: [
    {
      address: RECEIVER_ADDRESS_1,
      lovelace: 10_000_000, // 10 ADA
      assets: [
        {
          assetName: ASSET_NAME_1,
          policyId: POLICY_ID,
          quantity: 1,
        },
      ],
    },
    {
      address: RECEIVER_ADDRESS_2,
      lovelace: 5_000_000, // 5 ADA
      assets: [
        {
          assetName: ASSET_NAME_2,
          policyId: POLICY_ID,
          quantity: 1,
        },
      ],
    },
  ],
};

const response = await fetch(`${API_URL}/transactions/build`, {
  method: "POST",
  headers: HEADERS,
  body: JSON.stringify(requestBody),
});

const result = await response.json();
console.log(result);
```

{% endcode %}

</details>

## Running the Example

### Node.js

```bash
node custom-transaction.js
```

### Deno

```bash
deno run --allow-net custom-transaction.ts
```


# Multi-signature Transaction

TBD

<mark style="color:red;">TBD</mark>

1. Create the transaction as usual (Using the other guides)
2. Using the application backend,

* Sign it with your keys (in case of a minting platform)
* Save the transaction in a database and Setup a custom flow for users to fetch and sign it.
  * It requires custom flow to save signatures as well

3. Once all the required signatures are collected for a transaction (Requires a custom flow in the application backend)
4. Using the Submit endpoint (TODO), it is possible to send the TX with all of the signatures and anvil backend will build the full TX and send it on chain.


# Create Transaction with Metadata (CIP-20)

Adding human-readable messages to transactions (CIP-20)

## Overview

Sometimes you want to attach a message, comment, or memo to a Cardano transaction—whether it's a receipt reference, a note for future auditing, or just user-friendly text. **CIP-20** standardizes how these messages appear on-chain by using **label 674** in transaction metadata. With the Anvil API, you can easily attach messages to your transactions with minimal configuration.

The Anvil API lets you supply a simple message or array of messages. Under the hood, Anvil auto-formats them for CIP-20 compliance:

* **Single string** → `["Your message"]`
* **Array of strings** → `["Line 1", "Line 2"]`
* **Long string** → Automatically split across multiple array entries if it exceeds **64 bytes**

For more information on **transactions**, please refer to the [Transaction Overview](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/transaction/transaction/README.md).

## Key Features

* Simple message attachment to transactions
* Automatic CIP-20 compliance formatting
* Support for single strings or arrays of strings
* Automatic handling of messages exceeding 64 bytes

## Requirements

* A Cardano wallet with sufficient ADA
* Valid API key for authentication

## Full Examples

### Using Bash and cURL

{% content-ref url="/pages/w6SsgSQBjROumM1l6E4g" %}
[Bash & cURL](/guides/transaction/create-transaction-with-metadata-cip-20/bash-and-curl)
{% endcontent-ref %}

### Using TS/JS Fetch

{% content-ref url="/pages/Rf3mUgGbN5yjRYdr18LE" %}
[Deno & Fetch](/guides/transaction/create-transaction-with-metadata-cip-20/node-and-fetch)
{% endcontent-ref %}

***

## **Specifications**

### API Endpoint

```
POST /transactions/build
```

### Request Structure

```json
{
  "changeAddress": "<sender_address>",
  "message": "Your message" // String or array of strings
}
```

### CIP-20 Formatting

The Anvil API automatically formats your messages for CIP-20 compliance:

* **Single string** → `["Your message"]`
* **Array of strings** → `["Line 1", "Line 2"]`
* **Long string** → Automatically split across multiple array entries if it exceeds **64 bytes**

## Best Practices

* Keep messages concise and relevant
* Consider privacy implications of on-chain messages
* Test transactions on testnet before moving to mainnet
* Remember that all on-chain metadata is publicly visible
* Keep your API key secure and never expose it in client-side code


# Bash & cURL

Using Bash & cURL to create a transaction with CIP-20 metadata

## Introduction

This example demonstrates how to create a transaction with [CIP-20](https://cips.cardano.org/cip/CIP-20) metadata using the Anvil API. CIP-20 provides a standardized way to include human-readable messages in Cardano transactions using metadata label 674.

For more information about transactions, please refer to the [Transaction Overview](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/transaction/transaction/README.md).

## Objectives

This example creates a transaction with a CIP-20 compliant message on the Preprod network.

## Requirements

* A Cardano wallet with ADA
* A valid API key
* Bash shell environment

## API Request Structure

### Payload Format

```json
{
  "changeAddress": "addr_sender...",
  "message": "Your message" // String or array of strings
}
```

## Message Format Options

### Single String Message

```json
{
  "changeAddress": "addr_sender...",
  "message": "Receipt 42"
}
```

### Array of Strings

```json
{
  "changeAddress": "addr_sender...",
  "message": ["Receipt 42", "Minted by Anvil"]
}
```

### Auto-split for Long Messages

For messages exceeding 64 bytes, the API automatically splits them:

```json
{
  "changeAddress": "addr_sender...",
  "message": "A very long message to demonstrate the auto-split feature. I need more words"
}
```

**Will be formatted as:**

```json
{
  "msg": [
    "A very long message to demonstrate the auto-split feature. I nee",
    "d more words"
  ]
}
```

## Implementation

### Configuration and Parameters (using Bash and cURL)

*Using a preprod wallet for the transaction*

{% code overflow="wrap" %}

```sh
ADDRESS="addr_test1qrvx8wgdndrk98qf62vka3q4fglchk7h940vepdtgcv9fuu0e0aeuac6j2xhz77esaaudku68ha89qesqvd29pmuzw6qk8xkcn"
# See Authentication page for API key details.
X_API_KEY="testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9"
API_URL="https://preprod.api.ada-anvil.app/v2/services"
```

{% endcode %}

### API Call (using cURL)

Basic POST call with cURL

{% code overflow="wrap" %}

```sh
curl -XPOST \
    -H "Content-Type: application/json" \
    -H "X-Api-Key: ${X_API_KEY}" \
    -d '{"changeAddress": "'${ADDRESS}'", "message":["Receipt 42", "2025-01-24"]}' \
    ${API_URL}/transactions/build
```

{% endcode %}

### Expected Output

{% code overflow="wrap" %}

```json
{
  "hash": "28bdc13d5f532a10347f9671e5d3cd1aae9fae6a760e96808902f7e0baba878e",
  "complete": "84a500d9010281825820022ddfa716301851bb582acb30ff6d8be5718ea9565b87d0be70e5615a77a7cd01018182583900d863b90d9b47629c09d2996ec4154a3f8bdbd72d5ecc85ab461854f38fcbfb9e771a928d717bd9877bc6db9a3dfa728330031aa2877c13b41a1de152f0021a0003cb31031a04e44f940758201bd3787203ca834841a6e263f524989c7a2d8d340ae67098cc9ee225e7bb5df2a0f5a11902a2a1636d7367826a526563656970742034326a323032352d30312d3234",
  "stripped": "84a500d9010281825820022ddfa716301851bb582acb30ff6d8be5718ea9565b87d0be70e5615a77a7cd01018182583900d863b90d9b47629c09d2996ec4154a3f8bdbd72d5ecc85ab461854f38fcbfb9e771a928d717bd9877bc6db9a3dfa728330031aa2877c13b41a1de152f0021a0003cb31031a04e44f940758201bd3787203ca834841a6e263f524989c7a2d8d340ae67098cc9ee225e7bb5df2a0f5f6",
  "witnessSet": "a0",
  "auxiliaryData": "a11902a2a1636d7367826a526563656970742034326a323032352d30312d3234"
}
```

{% endcode %}

## The Whole Script (Bash Version)

<details>

<summary>cip-20.sh</summary>

{% code overflow="wrap" %}

```sh
#!/bin/bash

# Run with: bash cip-20.sh

ADDRESS="addr_test1qrvx8wgdndrk98qf62vka3q4fglchk7h940vepdtgcv9fuu0e0aeuac6j2xhz77esaaudku68ha89qesqvd29pmuzw6qk8xkcn"
# See Authentication page for API key details.
X_API_KEY="testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9"
API_URL="https://preprod.api.ada-anvil.app/v2/services"

curl -XPOST \
    -H "Content-Type: application/json" \
    -H "X-Api-Key: ${X_API_KEY}" \
    -d '{"changeAddress": "'${ADDRESS}'", "message":["Receipt 42", "2025-01-24"]}' \
    ${API_URL}/transactions/build
```

{% endcode %}

</details>


# Deno & Fetch

Using Deno (or Node) & fetch to create a transaction with CIP-20 metadata

## Introduction

This example demonstrates how to create a transaction with [CIP-20](https://cips.cardano.org/cip/CIP-20) metadata using the Anvil API. CIP-20 provides a standardized way to include human-readable messages in Cardano transactions using metadata label 674.

For more information about transactions, please refer to the [Transaction Overview](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/transaction/transaction/README.md).

## Objectives

This example creates a transaction with a CIP-20 compliant message on the Preprod network.

## Requirements

* A Cardano wallet with ADA
* A valid API key
* Deno or Node.js environment

## API Request Structure

### Payload Format

```json
{
  "changeAddress": "addr_sender...",
  "message": "Your message" // String or array of strings
}
```

## Message Format Options

### Single String Message

```json
{
  "changeAddress": "addr_sender...",
  "message": "Receipt 42"
}
```

### Array of Strings

```json
{
  "changeAddress": "addr_sender...",
  "message": ["Receipt 42", "Minted by Anvil"]
}
```

### Auto-split for Long Messages

For messages exceeding 64 bytes, the API automatically splits them:

```json
{
  "changeAddress": "addr_sender...",
  "message": "A very long message to demonstrate the auto-split feature. I need more words"
}
```

**Will be formatted as:**

```json
{
  "msg": [
    "A very long message to demonstrate the auto-split feature. I nee",
    "d more words"
  ]
}
```

## Implementation

### Configuration and Parameters (using Deno and Fetch)

*Using a preprod wallet for the transaction*

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

### Request Body

*Body Structure for creating a transaction with CIP-20 metadata*

```typescript
const BODY = {
  changeAddress: ADDRESS,
  message: ["Receipt 42", "2025-01-24"],
};
```

### API Call (using Deno and Fetch)

Basic POST call with Fetch

{% code title="cip-20.ts" overflow="wrap" %}

```typescript
const response = await fetch(`${API_URL}/transactions/build`, {
  method: "POST",
  headers: HEADERS,
  body: JSON.stringify(BODY),
});

console.log(await response.json());
```

{% endcode %}

### Deno Command

```bash
deno run --allow-net cip-20.ts
```

### Expected Output

{% code overflow="wrap" %}

```json
{
  "hash": "b2431ba925d55f3a117b4dd7d388b21277c6f89a27bfdc3895c10cae1dfcca7f",
  "complete": "84a700d9010282825820557a0804947569e286b0c98d859858bb5fc0697dc8fbb86b1c5d28ff7152a2a001825820593ad1e4983410a61b092d5119f92bd9b66d4a4eb4bdaf61c8cad6f8b9dffc1c010182a300581d6001687d507bb21217905bb35686ae6b373b26ce1ebfe2bc6db1caad5f011a00155cc0028201d8184a49616e76696c2d74616782583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561b000000018d343f3e021a0003098d031a052213630758201bd3787203ca834841a6e263f524989c7a2d8d340ae67098cc9ee225e7bb5df2081a0521f7430ed9010281581c01687d507bb21217905bb35686ae6b373b26ce1ebfe2bc6db1caad5fa100d9010281825820cfd1cfc82efa73c76302b9103f81b4c1fb2e9dc0ed39ab45039fb6d4c3fbe7a35840f32d960934a6511cb120a61dd8283a60ded05026e4b9e682aa3583a3c7d6be84b0749c3e86e4f226f5aa461869b8b9dc2fca81684a872f101691eedbbcd92c0af5a11902a2a1636d7367826a526563656970742034326a323032352d30312d3234",
  "stripped": "84a700d9010282825820557a0804947569e286b0c98d859858bb5fc0697dc8fbb86b1c5d28ff7152a2a001825820593ad1e4983410a61b092d5119f92bd9b66d4a4eb4bdaf61c8cad6f8b9dffc1c010182a300581d6001687d507bb21217905bb35686ae6b373b26ce1ebfe2bc6db1caad5f011a00155cc0028201d8184a49616e76696c2d74616782583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561b000000018d343f3e021a0003098d031a052213630758201bd3787203ca834841a6e263f524989c7a2d8d340ae67098cc9ee225e7bb5df2081a0521f7430ed9010281581c01687d507bb21217905bb35686ae6b373b26ce1ebfe2bc6db1caad5fa0f5f6",
  "witnessSet": "a100d9010281825820cfd1cfc82efa73c76302b9103f81b4c1fb2e9dc0ed39ab45039fb6d4c3fbe7a35840f32d960934a6511cb120a61dd8283a60ded05026e4b9e682aa3583a3c7d6be84b0749c3e86e4f226f5aa461869b8b9dc2fca81684a872f101691eedbbcd92c0a",
  "auxiliaryData": "a11902a2a1636d7367826a526563656970742034326a323032352d30312d3234"
}
```

{% endcode %}

## The Whole File (Deno Version)

<details>

<summary>cip-20.ts</summary>

{% code overflow="wrap" %}

```typescript
// deno run --allow-net cip-20.ts
const ADDRESS =
  "addr_test1qrydyk6uw6cehk5u3zspyz3dhnwzmhfls2fp42vv5dv9g2z3885pg4kpkn30ptezc855lu3w5ey93zcr5lrezjmwkftqg8xvge";
// See Authentication page for API key details.
const X_API_KEY = "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9";
const API_URL = "https://preprod.api.ada-anvil.app/v2/services";
const HEADERS = { "Content-Type": "application/json", "x-api-key": X_API_KEY };
const BODY = {
  changeAddress: ADDRESS,
  message: ["Receipt 42", "2025-03-12"],
};
const response = await fetch(`${API_URL}/transactions/build`, {
  method: "POST",
  headers: HEADERS,
  body: JSON.stringify(BODY),
});
console.log(await response.json());
```

{% endcode %}

</details>


# Metadata

## Overview

On-chain metadata is a powerful feature of the Cardano blockchain that enables storing data within transactions (CIP-25) or datum in UTXOs (CIP-68). The Anvil API provides flexible options for including metadata in your transactions through a streamlined interface.

Metadata serves several key purposes in the Cardano ecosystem:

* **NFT Information** (CIP-25, CIP-68): Store details about tokens like name, description, and image links
* **Transaction Messages** (CIP-20): Include human-readable messages or technical information such as:
  * Payment references or receipt identifiers
  * Invoice numbers for business transactions
  * Short notes explaining the purpose of transfers
  * DEX transaction identifiers for tracking operations
* **Protocol-specific Data**: Support for custom protocols through specialized metadata formats

## How Anvil Handles Metadata

The Anvil API processes metadata through the `meta` array in your transaction payload. Each entry in this array can contain a `label` and `data`.

### Supported Label Mappings

Anvil supports the following predefined friendly labels that map to Cardano metadata specifications:

| Friendly Label | Numeric Label | Purpose                       |
| -------------- | ------------- | ----------------------------- |
| `"message"`    | `674`         | Transaction messages (CIP-20) |
| `"token"`      | `721`         | NFT metadata (CIP-25, CIP-68) |

If you use a friendly label, Anvil will automatically convert it to the corresponding numeric value.

### Metadata Structure

> **Note**: You can use the predefined friendly labels above, any numeric label, or no label at all.

You can use the following structure to define metadata in your transaction payload:

```javascript
// Complete transaction payload with metadata
const transactionPayload = {
  // Other transaction fields like utxos, changeAddress, etc.
  
  // Metadata array - this is where you define your metadata entries
  meta: [
    // Option 1: Using a predefined friendly string label
    {
      label: "message", // Must be one of: "message" or "token"
      data: { 
        msg: ["This is a transaction message"] 
      }
    },
    
    // Option 2: Using a numeric label
    {
      label: 674, // Can be any number (674, 721, or custom numeric value)
      data: { 
        msg: ["This is another transaction message"] 
      }
    },
    
    // Option 3: No label, direct structure
    {
      data: {
        674: { 
          msg: ["This is a message with direct structure"] 
        }
      }
    }
  ]
};
```

Behind the scenes, Anvil employs sophisticated metadata handling with these features:

* **Friendly Labels**: Convert human-readable labels to their numeric counterparts (e.g., `"token"` → `721`)
* **Structure Merging**: Similar metadata entries are intelligently combined
* **Automatic String Chunking**: Long strings exceeding Cardano's 64-byte limit are split into arrays
* **Format Validation**: Ensures compliance with Cardano transaction specifications

<details>

<summary>Example: How Anvil API does Structure Merging</summary>

When multiple metadata entries use the same label, Anvil merges them instead of overwriting or duplicating:

```javascript
// Your input: Two separate metadata entries
const data = {
  meta: [
    {
      label: "token",
      data: { 
        "policyId_placeholder": { 
          asset1: { name: "Asset1", description: "First asset" } 
        } 
      }
    },
    {
      label: "token",
      data: { 
        "policyId_placeholder": { 
          asset2: { name: "Asset2", description: "Second asset" } 
        } 
      }
    }
  ]
};

// Transformed by Anvil for on-chain storage: Combined into a single structure
{
  "721": {
    "policyId_placeholder": {
      "asset1": { "name": "Asset1", "description": "First asset" },
      "asset2": { "name": "Asset2", "description": "Second asset" }
    }
  }
}
```

This allows you to build complex metadata incrementally from different parts of your application.

</details>

<details>

<summary>Example: How Anvil API does Automatic String Chunking</summary>

When a string exceeds Cardano's 64-byte limit, Anvil automatically converts it to an array of shorter strings:

```javascript
// Your input
{
  data: {
    description: "This is a very long description that exceeds the 64-byte limit imposed by Cardano transaction metadata specifications..."
  }
}

// Transformed by Anvil for on-chain storage
{
  data: {
    description: [
      "This is a very long description that exceeds the 64-byte limit ",
      "imposed by Cardano transaction metadata specifications..."
    ]
  }
}
```

</details>

## Working with CIP-25 and CIP-68 Metadata (Label 721)

> **IMPORTANT WARNING**: Metadata is only one part of the NFT minting process. For proper CIP-25 and CIP-68 implementation, you should follow the complete minting documentation. Attempting to implement NFT minting by manually constructing transactions with just the metadata structure below will lead to complexity and likely errors. Please refer to the [CIP-25 NFT Minting Guide](/guides/nft-and-ft/mint-nft-cip-25) or [CIP-68 NFT Minting Guide](/guides/nft-and-ft/mint-nft-cip-68) for the complete implementation.

CIP-25 established the original NFT metadata standard for Cardano, while CIP-68 expanded on this with additional capabilities for evolving NFTs. Most applications support both standards with the following characteristics:

* **Hierarchical Structure**: Organized by policy ID and asset name
* **Rich Content Support**: Name, description, image, and other media attributes
* **Extensibility**: Support for custom attributes beyond the core specification

> **Note on CIP-68 Implementation**: While both CIP-25 and CIP-68 use the same metadata structure under label `721`, CIP-68 requires additional implementation steps beyond metadata definition. CIP-68 uses a two-token model (reference NFT label `100` + user token label `222`) with the metadata stored in a datum. For complete CIP-68 implementation, see the [CIP-68 NFT Minting Guide](/guides/nft-and-ft/mint-nft-cip-68).

[CIP-25 Specification](https://cips.cardano.org/cip/CIP-25) | [CIP-68 Specification](https://cips.cardano.org/cip/CIP-68)

Anvil provides three equivalent ways to define CIP-25/CIP-68 metadata:

1. **Using the friendly `"token"` label**
2. **Using the numeric `721` label directly or any other numeric label**
3. **Using no label with direct structure**

All three approaches result in identical on-chain metadata.

### Example

```javascript
const asset = {
  metadata: {
    name: "Placeholder",
    image: [
      "https://ada-anvil.s3.ca-central-1.amazonaws.com/",
      "logo_pres_V2_3.png",
    ],
    mediaType: "image/png",
    description: "Testing CIP-25 using anvil API",
  },
};

const data = {
  meta: [
    // Option 1: Using friendly "token" label
    {
      label: "token",
      data: {
        "policyId_placeholder": {
          "Placeholder label token": {
            ...asset.metadata,
            name: "Placeholder label token",
          },
        },
      },
    },
    // Option 2: Using numeric 721 label
    {
      label: 721,
      data: {
        "policyId_placeholder": {
          "Placeholder label 721": {
            ...asset.metadata,
            name: "Placeholder label 721",
          },
        },
      },
    },
    // Option 3: No label with direct structure
    {
      data: {
        721: {
          "policyId_placeholder": {
            "Placeholder no label": {
              ...asset.metadata,
              name: "Placeholder no label",
            },
          },
        },
      },
    },
  ],
  changeAddress: "CHANGE_ADDRESS", // Replace with your wallet address
  utxos: "UTXOS", // Replace with your UTXOs
};

```

## Working with CIP-20 Transaction Messages (Label 674)

[CIP-20](https://cips.cardano.org/cip/CIP-20) defines a standard for including human-readable messages in transactions using label `674`. These messages are ideal for providing context, receipts, or references within transactions.

Anvil provides four equivalent ways to define CIP-20 transaction messages:

1. **Using the friendly `"message"` label** - Simplest approach that automatically converts to label `674` on-chain
2. **Using the numeric `674` label directly** - Same outcome as the friendly label approach
3. **Using no label with direct structure** - Maximum control over the final structure
4. **Using the simplified `message` property** - A special Anvil shorthand that handles all the formatting

All approaches result in correctly formatted message metadata on-chain, with each message appearing as an entry in the `msg` array under label `674`.

### Example

```javascript
// Transaction build payload with all four message approaches
const data = {
  meta: [
    // Option 1: Using friendly "message" label
    {
      label: "message",
      data: {
        msg: ["This is the first approach with the friendly label"]
      }
    },
    // Option 2: Using numeric 674 label
    {
      label: 674,
      data: {
        msg: ["This is the second approach with the numeric label"]
      }
    },
    // Option 3: No label with direct structure
    {
      data: {
        674: {
          msg: ["This is the third approach with direct structure"]
        }
      }
    },
  ],
  // Option 4: Using the simplified message property
  message: ["This is the fourth approach with the message property"],
  changeAddress: "CHANGE_ADDRESS", // Replace with your wallet address
  utxos: "UTXOS", // Replace with your UTXOs
};

```

## Conclusion

The Anvil API provides a flexible system for incorporating metadata in your Cardano transactions:

* Use the **CIP-25/CIP-68 format** (label `721`) for NFT metadata
* Use the **CIP-20 format** (label `674`) for transaction messages
* Choose the definition approach that best fits your workflow - friendly labels or direct numeric labels

In all cases, the resulting on-chain metadata follows the respective CIP standards, ensuring compatibility with wallets, explorers, and applications that interact with the Cardano blockchain.

### Further Resources

{% content-ref url="/pages/iAwcXYiYVbyg8oowsitJ" %}
[Mint NFTs (CIP-25)](/guides/nft-and-ft/mint-nft-cip-25)
{% endcontent-ref %}

{% content-ref url="/pages/zsxCSPtQt82XAHIlsZw4" %}
[Mint NFTs (CIP-68)](/guides/nft-and-ft/mint-nft-cip-68)
{% endcontent-ref %}


# Native Assets (NFTs/FTs)

## Introduction

Cardano supports native assets—both Fungible Tokens (FTs) (like a custom currency) and Non-Fungible Tokens (NFTs) (unique collectibles). Anvil provides straightforward ways to mint, send, or burn these assets, whether they follow CIP-25 (the classic NFT metadata standard) or CIP-68 (a more advanced approach allowing evolving or updatable data).

## In this section

* We’ll show how CIP-25 metadata might be integrated in a transaction payload.
* We’ll highlight how you can combine CIP-68 references similarly (should you want dynamic or updatable NFTs).
* We’ll note how to incorporate these tokens into your transaction using mint or outputs.

## NFTs vs. FTs: Dollar Bills vs. Art

### Fungible Tokens (FT)

**Analogy:** Think of them like dollar bills—each bill has the same value and is interchangeable with any other.

**1 FT = 1 FT, regardless of its “serial number.”**

**Use Cases:**

* In-Game Currencies: A consistent token players can trade.
* Community Tokens: Reward points or governance tokens.
* Stablecoins: Pegged to fiat currency.

### Non-Fungible Tokens (NFT)

**Analogy:** Think of them like a one-of-a-kind painting—unique art with no duplicates.

**1 NFT cannot simply be swapped for another NFT of the same “collection” because each is different.**

**Use Cases:**

* Digital Art & Collectibles: Rare items or artwork minted under a policy.
* Event Tickets: Each ticket is unique, can’t be replaced by another.
* Real-World Asset Tokens: Tokenizing property deeds, certificates, or identity credentials.

***

## Policy IDs: The Artist’s Collection

Tokens on Cardano—whether NFT or FT—are grouped under a **Policy ID**.

* **Policy ID** = A unique hash generated from a minting script or policy. See [Native Scripts](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/native-scripts/README.md).
* **Collection**: If an artist mints multiple NFTs under the same policy ID, they effectively form a “series” or “collection.”
* **Locking**: The policy can be time-locked or multi-signature locked, controlling whether more tokens can be minted in the future.

**In our analogy:** Policy ID = The “brand” or “collection” label that says “all these paintings or bills come from the same source.”

***

## CIP-25 / CIP-68 Metadata (for NFTs)

When you mint an NFT, you usually include metadata that describes the token’s name, image, description, attributes, etc. The most common metadata standards:

* **CIP-25 (Label 721):** Legacy standard for NFT metadata, widely supported by wallets and marketplaces.
* **CIP-68:** Newer approach that places more data in the on-chain datum, allowing dynamic NFTs or advanced features. Many still store a CIP-25–style JSON under label 721 for backward compatibility.

### Example:

```json
{
  "version": "cip-25",
  "assetName": {"name": "my_collection_nft_01", "format": "utf8"},
  "metadata": {
    "name": "My Collection NFT #1",
    "image": "ipfs://QmExampleHash",
    "mediaType": "image/png",
    "description": "Unique artwork minted with the Anvil API",
    "quantity": 1
  }
}
```

Depending on your approach, you embed this in your transaction’s metadata—Anvil can help you structure that under label 721.

***

## CIP-25 Payload

**Minting**

```json
{
  // Address to pay for the TX and receive any leftover ADA from the transaction. 
  "changeAddress": "addr_test1...",
  // UTXOs from the wallet paying for fees (list of CBOR UTXOs)
  "utxos": ["8282...", "8282..."],

  // Message to include in the transaction
  "message": "Asset minted with anvil api",

  // Array of assets to mint
  "mint": [
    {
      "version": "cip-25",
      "assetName": {"name": "anvil_api_cip_25", "format": "utf8"},
      "metadata": {
        "name": "anvil_api_cip_25 #1",
        "image": [
        		"https://ada-anvil.s3.ca-central-1.amazonaws.com/",
        		"logo_pres_V2_3.png"
       	],
       	"mediaType": "image/png",
       	"description": "Testing CIP-25 using anvil API",
      },
      "policyId": "4d5bd6249f0d9e4b2762ce334e2973dc7fd414ec1e08b4b0c2159bfb",
      "quantity": 1,
      // Destination to send minted asset.
      "destAddress": "addr_test1..."
    }
  ],

  // Optional but recommended for performance and latency. Required for new/unregistered scripts.
  "preloadedScripts": [
    {
      "type": "simple",
      "script": {
       	"type": "all",
       	"scripts": [
        		{
              // Defines the policy wallet that will sign the transaction
         			"type": "sig",
         			"keyHash": "fdf151b600df2492005221876c7d7e33056496572c7363c33a1e3609",
        		},
        		{
              // Defines the slot number that this policy can mint/burn tokens
         			"type": "before",
         			"slot": "100000000",
        		},
       	],
      },
      // The hash of the native script, which is the Policy ID.
      "hash": "4d5bd6249f0d9e4b2762ce334e2973dc7fd414ec1e08b4b0c2159bfb",
    }
  ],

}
```

## CIP-68 Payload

```json

{
  // Address receive any leftover UTxOs from the transaction. Usually the 
  "changeAddress": "addr_test1...",
  
  // UTXOs from the wallet paying for fees (list of CBOR UTXOs)
  "utxos": ["8282...", "8282..."],

  "mint": [
    {
      // Reference Token - Label 100 -> Sent to Metadata Manager Wallet or Script Address
      "version": "cip68",
      //Name of the asset. Must be the same for both tokens
      "assetName": { "name": "youruniquecip68asset", "format": "utf8", "label": 100 },
      "metadata": {
        "name": "Your NFT Name",
        "image": "ipfs://your-image-hash",
        "mediaType": "image/png",
        "description": "A description for your CIP-68 asset"
      },
      "policyId": "8e024681ee83f54bd5f9a0334641...",
      "quantity": 1,
      // Destination to send minted asset.
      "destAddress": "addr_test2..."
    },
    {
      // User Token - Label 222 -> Sent to User Wallet
      "version": "cip68",
      // Asset name must be identical to the (100) token's assetName
      "assetName": { "name": "youruniquecip68asset", "format": "utf8", "label": 222 },
      "policyId": "8e024681ee83f54bd5f9a033464...",
      "quantity": 1,
      // Destination to send minted asset.
      "destAddress": "addr_test1..."
    }
  ],
  
  // The `preloadedScripts` array provides script data for minting authorization.
  // - Native scripts: contains the actual native script
  // - Plutus scripts: contains blueprint data (native script derived from blueprint)
  // Optional but recommended for performance and latency. Required for new/unregistered scripts.
  "preloadedScripts": [ /* ... script details ... */ ],

  // The `scriptInteractions` array is ONLY required for smart contract validation.
  // It is not used for the native script / Metadata-Manager Wallet approach.
  // It is ONLY required for blueprints that are not registered yet. 
  // The full payload format is covered in the smart contract guide.
  "scriptInteractions": [ /* ... validator, redeemer, datum ... */ ],
}
```

***

## Explanation

* **changeAddress:** Where leftover funds or minted assets return if they’re not explicitly assigned.
* **message:** This sets CIP-20–style text if you want a short on-chain memo.
* **mint array:**
  * **version:** "cip-25" tells Anvil you’re embedding CIP-25–compliant metadata.
  * **assetName:** The token’s name as a JSON object
    * **name:** The name of the asset.
    * **format:** The format of the name. Can be "utf8" or "hex".
    * **Example:** {name: "MyAssetName", format: "utf8"}
  * **metadata:** CIP-25 fields like name, image, mediaType, description.
  * **policyId:** The unique 56-hex string for your minting native script.
  * **quantity:** How many tokens to mint (1 for a single NFT).
  * **destAddress:** If you want the minted asset to go to a specific address different from your changeAddress.
* **preloadedScripts:** Example of a native script using `type": "all"` (ScriptAll). Includes a signature requirement and time-lock.

***

## Minting vs. Sending

* **Mint:** Use the `mint` array. If you want to send additional tokens after minting, include them in the `outputs[]`.
* **Transferring an existing token:** Just specify it under `outputs[].assets[]`. No need to define `mint`.

***

## Key Steps to Use

1. Assemble the payload: `changeAddress` or `utxos` + the `mint` array if creating tokens.
2. Optionally add `CIP-20` or `CIP-25` metadata under `metadata` or `message`.
3. Call `POST /transactions/build`.
4. Sign the resulting transaction with your policy key and/or user’s key.
5. Submit the signed transaction to `POST /transactions/submit`.

***

## Conclusion

NFT & FT usage on Cardano with Anvil revolves around two primary tasks:

* **Minting tokens** by specifying them in the `mint` array (plus a reference native script if needed).
* **Transferring tokens** by listing them in `outputs[].assets[]`.

For CIP-25 or CIP-68 metadata, you embed JSON describing your asset (NFT name, image, etc.) so wallets and explorers can parse it. The snippet above with preloadedScripts and policyId is a typical CIP-25 single NFT example.

Whether you’re distributing a fungible currency or a one-of-a-kind collectible, the approach is the same—CIP standards define the metadata, and Anvil’s transaction builder handles the heavy lifting.


# Native Scripts

Comprehensive guide to Cardano Native scripts, their structure, components, and best practices when minting tokens with the Anvil API.

### Introduction

Native scripts are a fundamental component of the Cardano blockchain that define who has permission to mint or burn assets under a specific policy ID. These scripts create the cryptographic foundation of your NFT or token collection and establish the rules for minting authority. This guide explains how native scripts work, their structure, and best practices for implementation.

### What is a Native Script?

A native script is a logical expression that defines the conditions in which assets can be minted or burned under a specific policy ID. The policy ID is derived by hashing the native script, creating a unique identifier for your asset collection on the blockchain.

When included in a minting or burning transaction, native scripts are evaluated by the Cardano network to verify that the transaction satisfies all conditions before allowing new assets to be created or burned.

For a detailed, step-by-step guide on creating native scripts using the Anvil API, see our [Creating Native Scripts](/guides/nft-and-ft/native-scripts/create-native-script) guide.

### Native Script Structure

Native scripts in the Anvil API follow a specific JSON structure that maps to Cardano's native script format. Each script uses a discriminated union with a `type` field that determines the script's behavior:

```json
// Base structure for all native scripts
{
  "type": "sig" | "before" | "after" | "all" | "any" | "atLeast",
  // Additional properties based on the type
}
```

The underlying zod schema validates the following structures:

```typescript
// Main Native Script Schema
export const nativeScriptSchema = z.discriminatedUnion("type", [
  // Signature requirement
  z.object({ type: z.literal("sig"), keyHash: z.string() }),
  
  // Time constraints
  z.object({ type: z.enum(["before", "after"]), slot: z.number() }),
  
  // Logical AND/OR
  z.object({ 
    type: z.enum(["any", "all"]), 
    scripts: z.array(/*recursive native scripts*/) 
  }),
  
  // At least N of M
  z.object({ 
    type: z.literal("atLeast"), 
    required: z.number(),
    scripts: z.array(/*recursive native scripts*/) 
  }),
]);
```

#### Script Types

**Signature Scripts**

Signature scripts (`sig`) require a specific key to sign the transaction. This is the most common constraint used to ensure only authorized parties can mint tokens:

```json
{
  "type": "sig",
  "keyHash": "KEY_HASH_HERE"
}
```

The `keyHash` is the hash of the verification key that must sign the transaction.

**Time Constraints**

Time constraints limit minting to specific time windows using slot numbers:

```json
{
  "type": "before",
  "slot": 100000000
}
```

or

```json
{
  "type": "after",
  "slot": 50000000
}
```

* `before`: The transaction must be submitted before the specified slot number
* `after`: The transaction must be submitted after the specified slot number

**Logical Operators**

> **In Simple Terms**: `all` means "AND" (everything must be true), `any` means "OR" (at least one thing must be true). So if you want to allow either Alice OR Bob to sign, use `any`. If you need both Alice AND Bob to sign, use `all`.

Logical operators combine multiple constraints:

```json
{
  "type": "all",
  "scripts": [
    // Array of other scripts that must ALL evaluate to true
  ]
}
```

or

```json
{
  "type": "any",
  "scripts": [
    // Array of other scripts where at least ONE must evaluate to true
  ]
}
```

**Threshold Requirements**

The `atLeast` operator allows you to specify a minimum number of conditions that must be met:

```json
{
  "type": "atLeast",
  "required": 2,
  "scripts": [
    // Array of scripts where at least 'required' number must evaluate to true
  ]
}
```

This is particularly useful for multi-signature scenarios where you might want to require, for example, at least 2 out of 3 possible signers to authorize minting.

### Common Native Script Patterns

#### Basic Single-Signature Policy

```json
{
  "type": "sig",
  "keyHash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
}
```

#### Time-Limited Single-Signature Policy (Recommended for NFTs)

```json
{
  "type": "all",
  "scripts": [
    {
      "type": "sig",
      "keyHash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
    },
    {
      "type": "before",
      "slot": 98765432
    }
  ]
}
```

#### Multi-Signature Policy (Any of Two Keys)

```json
{
  "type": "any",
  "scripts": [
    {
      "type": "sig",
      "keyHash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
    },
    {
      "type": "sig",
      "keyHash": "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210"
    }
  ]
}
```

#### Multi-Signature Policy with Threshold (2 of 3 Keys Required)

```json
{
  "type": "atLeast",
  "required": 2,
  "scripts": [
    {
      "type": "sig",
      "keyHash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
    },
    {
      "type": "sig",
      "keyHash": "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210"
    },
    {
      "type": "sig",
      "keyHash": "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
    }
  ]
}
```

### Best Practices

#### Security Considerations

1. **Always Include a Signature Requirement**: While technically optional, you should always include at least one signature constraint in your native scripts for security. A native script without signature requirements allows anyone to mint tokens with your native script ID.
2. **Time-Limited Policies**: For NFT collections, consider implementing time-limited minting windows using both signature and time constraints. This pattern ensures your collection becomes immutable after a certain date, which is important for NFT value and trust.
3. **Multi-Signature Requirements**: For projects with multiple stakeholders, consider using nested logical operators to require multiple approvals.

#### Implications for Token Operations

**Time-Limited Policies and Token Operations**

When using time-limited policies (with a `before` constraint), understand the implications for various operations:

1. **Minting New Tokens**:
   * Only possible before the specified slot time
   * Attempting to mint after this time will fail validation
   * Keep track of your policy's deadline to avoid transaction failures
2. **Burning Tokens**:
   * Requires the same native script verification as minting
   * Cannot burn tokens after the time limit expires
   * To burn tokens, use a negative quantity in your mint request
3. **Metadata Updates**:
   * On-chain metadata (label 721) cannot be modified after being written
   * For updatable NFTs, consider using CIP-68 which stores data in datums
   * Time-limitations affect your ability to issue updated versions of tokens

> **Important**: If your use case requires future updates to tokens, avoid time-limited policies or ensure the time window is sufficient for your project's lifecycle.

### Using Native Scripts with the Anvil API

#### When to Include Native Scripts

Native scripts are provided via the `preloadedScripts` array in your Anvil API request:

* **Required**: When minting tokens with a new policy ID that hasn't been used before
* **Optional**: When minting additional tokens with an existing policy ID

> **Performance Note**: Including the native script even for existing policies improves performance. Without it, the Anvil API will automatically fetch the script from the blockchain in the background, which adds processing time to your request.

#### Script Verification

When you provide a native script, the Anvil API performs several validations:

1. The native script is parsed and its hash is computed
2. The computed hash is compared to the policy ID specified in the `policyId` field
3. If the hashes don't match, the transaction is rejected with a `Policy script and policy Id do not match` error

#### Integration with Transaction Building

Native scripts are provided in the `preloadedScripts` array and automatically resolved during the transaction preparation phase. The system:

1. Identifies required policy IDs from mint assets
2. Looks for matching scripts in `preloadedScripts`
3. Falls back to blockchain lookup if not found
4. Validates script hash matches policy ID
5. Uses resolved scripts for transaction building

### Common Errors and Troubleshooting

* **Unable to retrieve script for policy**: The policy doesn't exist on-chain and wasn't provided in `preloadedScripts`
* **Policy script and policy ID do not match**: The provided script's hash doesn't match the specified policy ID
* **Missing policy scripts**: Required for first-time minting but wasn't included in the request

### References

* [Creating Native Scripts](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/nft-and-ft/create-native-script/README.md)
* [Cardano Developers - Native Scripts](https://developers.cardano.org/docs/get-started/cardano-cli/simple-scripts/)
* [CIP-25 Specification](https://cips.cardano.org/cip/CIP-25)
* [CIP-68 Specification](https://cips.cardano.org/cip/CIP-68)


# Creating Native Scripts

Comprehensive guide to creating and managing Native scripts for creating rules for minting tokens on Cardano using the Anvil API.

## Introduction

This guide provides step-by-step instructions for creating native scripts using the Anvil API's utility endpoints. For conceptual information about native scripts, their structure, and common patterns, see the [Native Scripts Overview](/guides/nft-and-ft/native-scripts).

## Workflow Overview

The process of creating and using native scripts with the Anvil API follows these key steps:

1. Parse Wallet Address - obtain key hash(es) for signature requirements (supports multisig with multiple addresses)
2. Convert DateTime to Slot - create a time constraint (optional but recommended)
3. Create JSON Native Script - combine key hash(s) and time constraints
4. Convert to Native Script and get policy ID - use the Anvil API to create a machine-readable format
5. Use in Transaction - include the script when minting tokens

## Prerequisites

* An Anvil API key
* A wallet address for signing transactions
* Basic understanding of [native scripts](/guides/nft-and-ft/native-scripts) and their role in minting tokens

## Using the Utils Endpoints

The Anvil API provides several utility endpoints to assist with creating and managing native scripts:

| Endpoint                          | Description                                                          |
| --------------------------------- | -------------------------------------------------------------------- |
| `/utils/addresses/parse`          | Parses an address and returns its payment and stake credentials      |
| `/utils/native-scripts/parse`     | Returns information about a provided native script (e.g., policy ID) |
| `/utils/native-scripts/serialize` | Creates a native script from it's JSON schema definition             |
| `/utils/network/time-to-slot`     | Converts a date/time to a Cardano slot number                        |
| `/utils/network/slot-to-time`     | Converts a Cardano slot number to a date/time                        |

## Step-by-Step Guide to Creating a Native Script for Minting rules.

### Step 1: Obtain a key used for policy actions.

To create a signature-based native script, you first need to obtain the key hash of the wallet that will sign the minting transactions. You can get this by parsing a wallet address using the address parsing endpoint:

```typescript
// Example request
await fetch('https://preprod.api.ada-anvil.app/v2/services/utils/addresses/parse', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Api-Key': 'YOUR_API_KEY'
  },
  body: JSON.stringify({
    address: 'stake_address_or_payment_address_here'
  })
});

// Example response
{
  "payment": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
  "stake": "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210"
}
```

The `payment` value from the response is what you'll use as the `keyHash` in your native script.

### Step 2: Convert Time to Slot (Optional)

To add a time constraint to your native script, convert a future DateTime to a Cardano slot number:

```typescript
// Example request
await fetch('https://preprod.api.ada-anvil.app/v2/services/utils/network/time-to-slot', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Api-Key': 'YOUR_API_KEY'
  },
  body: JSON.stringify({
    timestamp: '2030-01-01T00:00:00Z'
  })
});

// Example response
{
  "slot": 98765432
}
```

This slot number will be used in your native script to create a time-limited minting policy.

### Step 3: Create JSON Native Script

Now that you have both the key hash (from Step 1) and optionally a slot number for time constraint (from Step 2), you can create the JSON structure for your native script.

Native scripts in the Anvil API follow a specific JSON structure that maps to Cardano's native script format. Each script uses a discriminated union with a `type` field that determines the script's behavior.

Here's how to combine the signature requirement and time constraint into a complete native script:

```json
{
  "type": "all",
  "scripts": [
    {
      "type": "sig",
      // Required Key hash from Step 1
      "keyHash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
    },
    {
      "type": "before",
      // Required Slot number from Step 2
      "slot": 98765432
    }
  ]
}
```

This JSON structure represents a native script that requires both the signature from the specified key hash AND the transaction to be submitted before the specified slot number.

### Step 4: Serialize the Native Script via API

While the JSON representation is a clear way to define the native script, it must be serialized into a specific hex format to be included in a transaction. The Anvil API provides a utility endpoint to handle this conversion. Here's how you can call the API with the JSON native script from Step 3:

```typescript
// The JSON native script from Step 3
const nativeScriptJSON = {
  type: "all",
  scripts: [
    {
      type: "sig",
      // Required Key hash from Step 1
      keyHash: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
    },
    {
      type: "before",
      // Required Slot number from Step 2
      slot: 98765432
    }
  ]
};

async function serializeNativeScript(nativeScriptJSON) {
  const response = await fetch("https://preprod.api.ada-anvil.app/v2/services/utils/native-scripts/serialize", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-Api-Key": "YOUR_API_KEY"
    },
    body: JSON.stringify({ schema: nativeScriptJSON }),
  });

  if (!response.ok) {
    const errorText = await response.text();
    throw new Error(`API call failed: ${errorText}`);
  }

  const { script, policyId } = await response.json();
    
  console.log("CBOR Hex-encoded Script:", script);
  console.log("Policy ID:", policyId);

  return { script, policyId };
}

// Get the serialized script and policy ID
const { script: hexEncodedScript, policyId } = await serializeNativeScript(nativeScriptJSON);
```

The API returns two crucial pieces of information:

1. **`script`**: The CBOR hex-encoded native script. This is what you will include in the transaction body.
2. **`policyId`**: The unique identifier for your native script, derived from hashing the script.

### Step 5: Use in Transaction

The final step is to use your native script when building transactions that mint or burn assets. You'll include the native script in the `preloadedScripts` section of your transaction request:

```typescript
const data = {
  changeAddress: "addr_test...",
  utxos: ["8282...", "8282..."],
  mint: [
    {
      version: "cip25",
      assetName: { name: "MyAsset", format: "utf8" },
      metadata: {
        name: "My Asset",
        image: ["ipfs://Qm..."],
        // Additional metadata fields
      },
      policyId, // The policy ID from Step 4
      quantity: 1,

      // Destination address for the minted asset
      destAddress: "addr_test..."
    }
  ],
  // This is required for the first mint transaction. Anvil API will fetch the scripts from the provided policy ID for any subsequent transactions.
  preloadedScripts: [
    {
      type: "simple",
      script: {
        // Your native script from Step 3
        type: "all",
        scripts: [
          {
            type: "sig",
            keyHash: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
          },
          {
            type: "before",
            slot: 98765432
          }
        ]
      },
      hash: policyId // The policy ID from Step 4
    }
  ]
};

const tx = await fetch(`https://preprod.api.ada-anvil.app/v2/services/transactions/build`, {
  method: "POST",
  body: JSON.stringify(data),
  headers: {
    "Content-Type": "application/json",
    "X-Api-Key": "YOUR_API_KEY"
  }
});
```

This transaction, when signed and submitted, will mint a new asset under the policy ID you generated.

## Additional Native Script Patterns

For more native script patterns including multi-signature policies, threshold requirements, and complex logical combinations, see the [Native Scripts Overview](/guides/nft-and-ft/native-scripts#common-native-script-patterns).

## Conclusion

You now have a complete native script that can be used for minting tokens. The script includes both signature requirements and time constraints, providing security and immutability for your token collection.

For security considerations, best practices, and additional script patterns, refer to the [Native Scripts Overview](/guides/nft-and-ft/native-scripts).

## Related Resources

* [Native Scripts](/guides/nft-and-ft/native-scripts)
* [Minting a CIP-25 NFT](/guides/nft-and-ft/mint-nft-cip-25)
* [Minting a CIP-68 NFT](/guides/nft-and-ft/mint-nft-cip-68)


# Policy Units

Represents the policy\_id + asset\_name in HEX format

In Cardano, "unit" typically refers to the policy ID plus the asset name in a single hex string that identifies a native token or NFT. Because you can have thousands of different tokens, each must be uniquely identified by a combination of:

* **Policy ID** (28 bytes, 56 hex characters).
* **Asset Name** (up to 32 bytes, variable hex length).

For CIP-25 or CIP-68 NFTs, you might see `unit` used in your minted assets or outputs. Similarly, when listing assets in an output, you specify:

```json
"unit": "<policyId+assetName>"
```

For example, if your `policyId` is:

```
1af660e4c58514a2f0ea167deca340381e55bed4aea60bc09c211417
```

and your asset name (in hex) is:

```
416e76696c54657374312038
```

the **Unit** becomes:

```
1af660e4c58514a2f0ea167deca340381e55bed4aea60bc09c211417416e76696c54657374312038
```

***

**Why Do Policy IDs and Asset Names Matter?**

1. **Token Gating and Access Control**

* Imagine you run a membership site or event. You want to grant access only to users who hold a specific NFT (like a “hotel keycard”).
* To validate their NFT, you check the **policy ID** (the “hotel”) and the **asset name** (the “room”). If a user’s wallet has the exact combination, they get in.

2. **Verification**

* If someone claims they hold a certain collectible or token, you can confirm by comparing their token’s **Unit** to the official “Unit” you expect.
* This prevents duplicates or fakes.

3. **Metadata and Searching**

* Explorers like preprod.cexplorer.io allow you to search by policy ID or asset name, letting you track how many tokens are minted, who holds them, and more.

4. **Minting and Burning**

* To mint or burn tokens, the blockchain needs to know exactly **which** asset under **which** policy you’re referring to.
* The policy ID and asset name form that unique identifier.

***

**How to Extract the Policy ID and Asset Name from a Unit**

If you have the **full unit string** (`policyId + assetName` in hex), you can slice it into two parts:

```typescript
const unit = "1af660e4c58514a2f0ea167deca340381e55bed4aea60bc09c211417416e76696c54657374312038";

// First 56 characters (28 bytes in hex) is the policy ID:
const policyId = unit.slice(0, 56);

// The remaining portion is the asset name in hex:
const encodedAssetName = unit.slice(56);

// Optionally, you can convert the asset name from hex to plain text:
const assetName = Buffer.from(encodedAssetName, "hex").toString();
```

**Tip**: Some asset names contain non-ASCII characters, so you might see garbled text if you convert from hex incorrectly or if it’s truly a binary representation. Always check the token’s known format.

***

**Where to Find Your Policy ID and Asset Name**

**1. From a Blockchain Explorer**

1. Go to an explorer like Preprod CExplorer (for testnet) or CExplorer.io (for mainnet).
2. Paste the asset name or policy ID in the search bar (e.g., “AnvilTest1208”).
3. On the asset’s detail page, you’ll typically see:

* **Encoded Asset Name** (hex)
* **Policy ID** (hex string)

Once you have both, concatenate them to get the **unit**.

**2. From a Wallet (e.g., Eternl)**

1. Open your wallet interface.
2. Locate the NFT or token in question.
3. The wallet interface usually displays both:

* **Policy ID**
* **Asset Name** (in either plain text or hex)

Some wallets (like Eternl) also display the **unit** or a direct link to an explorer page.

***

TBD: Add practical example.

***

**Additional Tips and Best Practices**

1. **Hex vs. ASCII**

* Always confirm whether your asset name is stored in **hex** or **ASCII**.
* If you do comparisons in your code, stay consistent—comparing hex to ASCII won’t match.

2. **Policy Expiration**

* Some policies allow minting indefinitely; others have an expiration. Check the policy’s script if you plan to handle or mint new tokens under it.

3. **Handling Special Characters**

* Asset names may include emojis or non-English text. The hex representation ensures compatibility, but the plain text might look odd.

4. **Batching**

* For dApps dealing with numerous tokens, consider how you’ll fetch all these units efficiently (paging, caching, etc.).

5. **Don’t Overlook Testnets**

* If you’re new to token gating or minted assets, test your approach on **Preprod** or **Preview** first. Real ADA isn’t at risk there.

***

**Summary**

* `Unit = policyId + assetName` (in hex).
* **Why It Matters**: It’s the unique identifier for any Cardano-native asset, crucial for gating, verification, minting, and more.
* **Where to Get Them**: Use explorers, your wallet, or direct blockchain APIs.
* **Hotel Keycard Analogy**: The policy ID is the “hotel,” and the asset name is the “room.” Checking them together ensures you grant access to the right person holding the right token.

With a solid grasp of **Units**, **Policy IDs**, and **Asset Names**, you can confidently build sophisticated Cardano applications—whether that’s token-gated dApps, NFT marketplaces, or membership sites—knowing exactly which assets users hold and how to verify them securely.

***

**Next Steps**

Learn about Utilities Functions to simplify asset handling (e.g., `unitToName()`, `nameToUnit()`, etc.). Jump into Transaction for guidance on how to send assets, sign transactions, or mint new tokens with Anvil. Explore advanced NFT & FT documentation to create, distribute, and manage your custom assets.


# Update Metadata (CIP-25)


# Mint and Burn NFTs or FTs


# Mint NFTs (CIP-68)

An overview of how to mint CIP-68 compliant NFTs and other native assets on Cardano using the Anvil API

#### What is CIP-68?

Cardano Improvement Proposal 68 (CIP-68) is a metadata standard that revolutionized how NFT metadata is managed on Cardano. It enabled **truly updatable metadata after minting** through a dual-token architecture, perfect for dynamic NFTs like game items, evolving collectibles, or interactive art.

#### Why CIP-68 Was Created

**CIP-25 Limitations:**

* Metadata inaccessible to smart contracts (stored in transaction metadata).
* Complex and high cost metadata updates requiring mint/burn cycles with double-mint periods.
* Metadata resolution requires scanning blockchain history to find the "last minted transaction" for each token.
* Permanent immutability once policy is time-locked.

**CIP-68 Solution:** A dual-token architecture separating metadata storage from ownership, enabling seamless updates, smart contract integration, and enhanced security through datum-based metadata storage. Unlike CIP-25, metadata updates remain possible even after the minting policy is time-locked, as updates are controlled by the reference token's spending script rather than the minting policy.

**CIP-68 Trade-offs:**

* Higher initial minting costs (requires creating two tokens instead of one)
* Increased complexity managing dual-token architecture, creating additional UTxO overhead for reference token storage.

#### How It Works

CIP-68 uses an innovative **dual-token system** where each NFT consists of two linked tokens:

* **Reference Token (Label 100)**: Stores the updatable metadata at a script address or Admin wallet
* **User Token (Label 222)**: Resides in the user's wallet and inherits metadata from the reference token

This separation allows metadata updates without affecting user ownership or requiring token transfers.

#### Validation Approaches: Native Scripts vs. Smart Contracts

CIP-68's updatable metadata is managed by a Reference Token, which can be secured in two ways:

1. **Metadata-Manager Wallet with a Native Script:** This approach uses a standard wallet and a native script to control the Reference Token. It's straightforward and low-cost, making it ideal for projects that don't need complex on-chain logic. The trade-off is that it relies on trusting the wallet owner to manage metadata updates and token supply correctly.
2. **Smart Contract (Plutus/Aiken):** This method uses a full smart contract to govern the Reference Token. While someone still needs to trigger updates, the smart contract validates critical aspects like supply limits and reference token to user token ratios on-chain, requiring less trust than the wallet approach. It enables complex rules for metadata updates, supply caps, and other on-chain logic, but is more complex and expensive to develop and interact with.

The choice between them depends on your project's need for trustless validation versus simplicity and cost-effectiveness. If you need custom smart contract development, or are not sure which option to choose, we recommend reaching out at <hello@ada-anvil.io>.

### Prerequisites

Before you start, make sure you have:

* **Anvil API key** – Get one [here](/anvil-api/authentication)
* **Cardano wallets** – Use our [wallet CLI](/developer-tools/wallet-cli) to create them
* **Basic understanding** of CIP-68's two token system. See [CIP-68 Standard Reference](/guides/nft-and-ft/mint-nft-cip-68/cip-68-standard)

### Choose Your Approach

CIP-68 offers two main validation approaches for securing reference tokens:

Choose your approach based on your specific needs:

| Criteria                           | Native Scripts                         | Smart Contracts                       |
| ---------------------------------- | -------------------------------------- | ------------------------------------- |
| **Validation Logic Customization** | Simple signature + timelock validation | Complex programmable business rules   |
| **Metadata Updates**               | Manual wallet-based updates            | Automated rule-based validation       |
| **Development Complexity**         | Low - JSON configuration               | High - Aiken Development              |
| **Cardano Transaction Costs**      | Lower                                  | Higher (\~2x ADA)                     |
| **Use Cases**                      | Simple collections, personal projects  | Gaming, DeFi, enterprise applications |

### All Guides

#### Getting Started

{% content-ref url="/pages/XUCgyiqYozlRhiFHziFc" %}
[Native Script Minting](/guides/nft-and-ft/mint-nft-cip-68/deno-and-fetch)
{% endcontent-ref %}

#### Smart Contract Guides

{% content-ref url="<https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/nft-and-ft/mint-nft-cip-68/smart-contract/logic.md>" %}
<https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/nft-and-ft/mint-nft-cip-68/smart-contract/logic.md>
{% endcontent-ref %}

{% content-ref url="/pages/gTfFEvJFTCraDlihyDEG" %}
[Mint Example](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/mint-example)
{% endcontent-ref %}

{% content-ref url="<https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/nft-and-ft/mint-nft-cip-68/smart-contract/update-example.md>" %}
<https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/nft-and-ft/mint-nft-cip-68/smart-contract/update-example.md>
{% endcontent-ref %}

### References & Further Reading

#### Official CIP Specifications

* [CIP-68: Datum Metadata Standard](https://cips.cardano.org/cip/CIP-68) - The official specification for updatable NFT metadata
* [CIP-25: NFT Metadata Standard](https://github.com/cardano-foundation/CIPs/tree/master/CIP-0025) - Traditional NFT metadata standard
* [CIP-86: NFT Metadata Update Oracles](https://cips.cardano.org/cip/CIP-86) - Extension for CIP-25 metadata updates


# CIP-68 Standard Reference

Detailed technical reference for the CIP-68 standard including token relationships, lifecycle workflows, datum structure, and implementation specifications for developers.

This page provides detailed technical information about the CIP-68 standard for developers implementing CIP-68 tokens.

## Token Relationship Overview

### CIP-68 Dual-Token Model

CIP-68 uses a dual-token system where each NFT consists of two linked tokens:

* **🏷️ Reference Token (label 100)**: Contains the metadata and lives at a script address. This is the "data storage" token that can be updated by the creator.
* **🎨 User Token (label 222)**: The actual NFT that users own and trade. This token inherits its metadata from the Reference Token.

Both tokens share the same Policy ID and base asset name, but have different label prefixes. When dApps need to display an NFT, they:

1. Find the User Token (222) in the user's wallet
2. Derive the Reference Token name by converting `(222)Name → (100)Name`
3. Query the blockchain for the Reference Token to get current metadata
4. Display the NFT with the inherited metadata

### Token Lifecycle Workflow

{% @mermaid/diagram content="sequenceDiagram
participant Creator as 👨‍💻 NFT Creator
participant RefToken as 🏷️ Reference Token (100)<br/>Script Address
participant UserToken as 🎨 User Token (222)<br/>User Wallet
participant dApp as 📱 dApp/Wallet

```
Note over Creator,dApp: 1. Minting Phase
Creator->>RefToken: Mint with CIP-68 datum<br/>(metadata, version, extra)
Creator->>UserToken: Mint to user wallet<br/>(same base name)
Note over RefToken,UserToken: Both tokens linked by Policy ID + Name

Note over Creator,dApp: 2. Discovery & Display Phase
dApp->>UserToken: Query user's wallet
UserToken-->>dApp: Return User Token (222)
Note over dApp: Convert (222)Name → (100)Name
dApp->>RefToken: Lookup Reference Token for metadata
RefToken-->>dApp: Return current metadata from datum
dApp->>UserToken: Display NFT with inherited metadata

Note over Creator,dApp: 3. Metadata Update Phase
Creator->>RefToken: Update datum with new metadata<br/>(same token, new data)
Note over RefToken: Reference Token updated on-chain
Note over UserToken: User Token unchanged
dApp->>RefToken: Query for latest metadata
RefToken-->>dApp: Return updated metadata
dApp->>UserToken: Display NFT with new metadata
Note over UserToken: User sees updated NFT<br/>without token moving" %}
```

## Datum Structure

CIP-68 tokens use a standardized datum structure: `[metadata, version, extra]`

* **`metadata`**: CIP-25 compatible metadata (name, image, description, etc.)
* **`version`**: Integer version field indicating supported token standards:
  * **Version 1**: NFTs (222) and Fungible Tokens (333)
  * **Version 2**: Rich Fungible Tokens (444) - fractionalized NFTs
  * **Version 3**: Reserved for future token standards
* **`extra`**: Optional custom Plutus data for application-specific use

The version field enables different token types and maintains backward compatibility. Higher versions support all features from lower versions.

{% hint style="success" %}
**Recommended Tool: Meta Draft**

For creating and validating CIP-68 metadata, we recommend using [Meta Draft](https://meta-draft.vercel.app/) - a free metadata validation tool built specifically for Cardano. It provides a complete 4-step workflow to create consistent metadata for your collection:

**1. Metadata Structure** - Define your collection's JSON structure with consistent formatting **2. Rules Selection** - Choose from 23+ validation rules to prevent common errors:

* Duplicate detection (names, images, asset names)
* Key formatting (camelCase, snake\_case, Title Case)
* Required fields validation (name, image)
* Character limits and data type validation
* Attribute consistency checks

**3. NFTs Data Validation** - Validate each NFT against your selected rules **4. Summary & Export** - Review and export your validated metadata as JSON

**Perfect for CIP-68 Collections:**

* **Prevents minting errors** by catching issues before blockchain deployment
* **Ensures consistency** across your entire collection
  {% endhint %}

## Token Labels (CIP-67 Encoding)

CIP-68 uses labels to identify token purposes:

| Token Type          | Label | Hex Prefix | Purpose                                  |
| ------------------- | ----- | ---------- | ---------------------------------------- |
| **Reference Token** | 100   | `000643b0` | Holds metadata, locked at script address |
| **User Token**      | 222   | `000de140` | Actual NFT in user's wallet              |

{% hint style="info" %}
**Anvil Handles Label Encoding Automatically**

Anvil automatically handles CIP-68 label encoding when you specify the `label` field:

```json
{
  "assetName": { "name": "mytoken", "format": "utf8", "label": 100 },
  // Anvil converts this to: "000643b0" + hex("mytoken")
}
```

* **`label: 100`** → Anvil prefixes with `000643b0` (Reference Token)
* **`label: 222`** → Anvil prefixes with `000de140` (User Token)
* **`format: "utf8"`** → Anvil converts the name to hex automatically
* **`format: "hex"`** → Anvil uses the name as-is (already hex encoded)

This ensures proper CIP-68 compliance without manual hex encoding.
{% endhint %}

## Supported URI Schemes for Metadata

Per the [official CIP-68 specification](https://cips.cardano.org/cip/CIP-68), these URI schemes are supported for metadata images and files:

* **`https://`** - Standard web URLs (e.g., `https://example.com/image.png`)
* **`ipfs://`** - [IPFS](https://ipfs.tech/) content addressing (e.g., `ipfs://QmHash...`)
* **`ar://`** - [Arweave](https://arweave.org/) permanent storage (e.g., `ar://TransactionId`)
* **`data:`** - [RFC2397](https://tools.ietf.org/html/rfc2397) on-chain data URLs (e.g., `data:image/png;base64,iVBOR...`)

### Best Practices

* Use IPFS for decentralized, content-addressed storage
* Use Arweave for permanent, immutable storage
* Use HTTPS only for trusted, long-term available sources
* Use data URLs sparingly due to transaction size limits

## Transaction Payload Structure

Here's the basic structure for minting CIP-68 tokens with Anvil:

{% code overflow="wrap" %}

```json
{
  // Customer wallet address receive any leftover UTxOs from the transaction. 
  "changeAddress": "addr_test1...",
  
  // UTXOs from the customer wallet paying for fees (list of CBOR UTXOs)
  "utxos": ["8282...", "8282..."],

  "mint": [
    {
      // Reference Token - Label 100 -> Sent to Metadata Manager Wallet or Script Address
      "version": "cip68",
      //Name of the asset. Must be the same for both tokens
      "assetName": { "name": "youruniquecip68asset", "format": "utf8", "label": 100 },
      "metadata": {
        "name": "Your NFT Name",
        "image": "ipfs://your-image-hash",
        "description": "Your NFT description",
        "mediaType": "image/png"
      },
      "policyId": "8e024681ee83f54bd5f9a0334641...",
      "quantity": 1,
      "destAddress": "addr_test1..."
    },
    {
      // User Token - Label 222 -> Sent to User Wallet
      "version": "cip68",
      // Asset name must be identical to the (100) token's assetName
      "assetName": { "name": "youruniquecip68asset", "format": "utf8", "label": 222 },
      "policyId": "8e024681ee83f54bd5f9a0334641...",
      "quantity": 1,
      "destAddress": "addr_test1..."
    }
  ],
  
  // The `preloadedScripts` array provides script data for minting authorization.
  // - Native scripts: contains the actual native script
  // - Plutus scripts: contains blueprint data (native script derived from blueprint)
  // Optional but recommended for performance and latency. Required for new/unregistered scripts.
  "preloadedScripts": [ /* ... script details ... */ ],

  // The `scriptInteractions` array is ONLY required for smart contract validation.
  // It is not used for the native script / Metadata-Manager Wallet approach.
  // It is ONLY required for blueprints that are not registered yet. 
  // The full payload format is covered in the smart contract guide.
  "scriptInteractions": [ /* ... validator, redeemer, datum ... */ ],
}
```

{% endcode %}

## References

* [Official CIP-68 Specification](https://cips.cardano.org/cip/CIP-68)
* [CIP-25 Metadata Standard](https://cips.cardano.org/cip/CIP-25)
* [CIP-67 Asset Name Label Registry](https://cips.cardano.org/cip/CIP-67)


# Native Script Minting

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**.

{% 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#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)
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). **Ensure customer wallet has ADA for fees.**

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

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

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


# Smart Contract Guides

Guide to CIP-68 NFT minting using smart contracts with Aiken validators for advanced validation logic, programmable metadata management on Cardano.

This section covers CIP-68 NFT minting using **smart contracts** for advanced validation logic and programmable metadata management.

{% hint style="warning" %}
**Smart Contract Complexity:** Smart contract development on Cardano requires a solid understanding of the UTXO model and can be particularly challenging for developers coming from account-based blockchains, as it demands careful consideration of concurrency patterns and state management across multiple UTXOs.
{% endhint %}

{% hint style="success" %}
**We've Got You Covered:** While Cardano smart contracts can be complex, we've broken down the concepts and implementation steps to help you get a minting project off the ground successfully.
{% endhint %}

### When to Use Smart Contracts

Choose smart contracts over native scripts when you need:

* **Advanced validation logic** beyond simple signature/time constraints
* **Programmable metadata updates** with custom business rules. (i.e. Only the owner and the Admin can update the metadata)
* **Complex authorization patterns** (multi-sig, role-based access, etc.)

### Guide Structure

#### 1. Understanding Smart Contract Logic

[**Mint Logic**](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/mint-logic)

**Conceptual Guide** - Understanding how the mint validator creates CIP-68 token pairs and enforces compliance.

**Topics Covered:**

* Token pair creation (reference + user tokens)
* CIP-68 metadata validation
* Smart contract parameterization

[**Update Logic**](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/update-logic)

**Conceptual Guide** - Understanding how the spend validator enables metadata updates with authorization controls.

**Topics Covered:**

* User vs admin authorization paths
* Fee mechanisms and ownership verification
* Metadata transformation logic
* Security model and validation flow

{% hint style="info" %}
**Choose Your Learning Path:**

🎓 **Conceptual Path**: Start with logic guides to understand validation → Then implement 📋 **Implementation Path**: Jump to examples for quick setup → Return to logic when needed

**New to Cardano smart contracts?** Take the conceptual path for better understanding. **Experienced developer?** The implementation path gets you running quickly.
{% endhint %}

#### 2. Implementation Examples

[**Mint Example**](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/mint-example)

**Implementation Guide** - Step-by-step Deno implementation for minting CIP-68 NFTs with smart contracts.

**Topics Covered:**

* Using `POST /blueprints/apply-params` to apply validator parameters
* Using `POST /transactions/build` to build mint transactions
* Dual-wallet signing process
* Complete working code examples

[**User Update Example**](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/user-update-example)

**Implementation Guide** - Complete user-driven metadata update implementation.

**Topics Covered:**

* Dynamic UTXO discovery via Blockfrost
* User token ownership verification
* Fee payment to admin (1 ADA)
* Limited metadata field updates

[**Admin Update Example**](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/admin-update-example)

**Implementation Guide** - Complete admin-driven metadata update implementation.

**Topics Covered:**

* Admin signature authorization
* Full metadata update permissions
* No fee requirements
* Complete metadata structure modifications


# Mint Logic

Understanding the smart contract - How to mint NFTs on Cardano (in accordance to CIP-68 standard) using Aiken smart contracts. Cardano interactions powered by Anvil API

You've chosen the **Smart Contract approach** for CIP-68 - excellent choice for projects needing more validation control and complex business rules!

{% hint style="info" %}
**Don't worry about Aiken syntax!** This guide focuses on *what* the smart contract validates and *why*, not how to write Aiken code. You can successfully use CIP-68 smart contracts through the Anvil API without being an Aiken expert.

**Ready to implement?** Skip to the [practical minting example](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/mint-example) and return here when you want to understand the underlying logic.
{% endhint %}

### What the Smart Contract Does

While the [main guide](/guides/nft-and-ft/mint-nft-cip-68) explained *what* CIP-68 achieves (updatable NFT metadata), this guide explains *how* the smart contract ensures everything works correctly with the CIP-68 NFT standard.

**The Smart Contract's Job:**

* ✅ **Enforces CIP-68 Rules**: Ensures reference tokens (100) and user tokens (222) are always created together
* ✅ **Validates Authorization**: Only authorized admins can mint new token pairs
* ✅ **Guarantees Metadata Integrity**: Reference tokens contain valid CIP-68 metadata structure
* ✅ **Enables Future Updates**: Reference tokens can be spent later to update metadata

### How It Works: The Validation Flow

Every CIP-68 mint using this smart contract follows the same validation sequence: **Build Transaction** → **Validate Smart Contract** → **Submit Valid Transaction** → **Outputs**.

**Quick Setup Process:**

1. **Upload & Parameterize**: Deploy the smart contract with admin keys via [POST /blueprints](/guides/smart-contract/blueprint-management) endpoints
2. **Build Transactions**: Use the parameterized contract in [POST /transactions/build](/guides/transaction) calls
3. **Let Validation Happen**: Smart contract automatically enforces all CIP-68 rules
4. **Submit Valid Transaction**: Validated transaction is submitted to the Cardano network via the `POST /transactions/submit` endpoint.

{% hint style="success" %}
**The beauty of smart contracts**: Once deployed and parameterized, they handle all the complex validation automatically. You just build transactions - the contract ensures everything follows CIP-68 standards, and you are creating the NFTs correctly.

**Ready to implement?** See the [Mint Example](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/mint-example) for step-by-step code implementation.
{% endhint %}

### Smart Contract Validation Flow

The diagram below shows how the CIP-68 smart contract processes your transaction. Follow the arrows to understand the validation sequence:

{% hint style="info" %}
**How to read this diagram**: Start with "Build Transaction" (what you provide), follow the arrows through "Validate Smart Contract" (validation checks), and end at "Submit Valid Transaction" (what gets created). Each validation step must pass for the transaction to succeed.
{% endhint %}

{% @mermaid/diagram content="flowchart TD
subgraph inputs\["<b>Build Transaction</b>"]
utxos\["💰 <b>UTxOs</b> (ADA for fees)"]
user\_input\["<b><b>Reference NFT (100)</b></b><br>(Label 100 → sent to SC)"]
ref\_input\["<b><b>User Token (222)</b></b><br>(Label 222 -> sent to User)"]
admin\_sig\["🔑 <b>Admin Signature</b>"]
applied\_script\["📋 <b>Blueprint</b> with Applied Parameters"]
end
subgraph validation\["<b>Validate Smart Contract</b>"]
check\_sig\["✅ <b>Check Admin Signature<br></b>list.has(extra\_signatories, admin\_key)"]
check\_tokens\["✅ <b>Validate CIP-68 Tokens<br></b>Token pairs, metadata, and exact mint matching"]
check\_datum@{ label: "✅ <b>Validate Metadata<br></b>Metadata is valid CIP-68 format and the token's names match" }
end
subgraph outputs\["📤 Outputs"]
ref\_token\["🏷️ Reference Token (100)<br>→ Script Address<br>→ Contains Metadata Datum"]
user\_token\["🎨 User Token (222)<br>→ Any Address<br>→ The Actual NFT"]
end
subgraph submission\["📤 Submit Valid Transaction"]
n2\["💰 <b>UTxOs</b> (ADA for fees)"]
admin\["🔑 <b>Admin Signature</b>"]
user\["🔑 <b>User Signature</b>"]
n1\["✅ Valid Transaction"]
n2\["💰 <b>UTxOs</b> (ADA for fees)"]
end
inputs -- <b>'POST transactions/build'</b> --> validation
check\_sig --> check\_tokens
check\_tokens --> check\_datum
validation -- All Checks Pass --> success\["✅ Transaction Valid"]
validation -- Any Check Fails --> failure\["❌ Transaction Rejected"]
success --> submission
submission --> outputs

```
check_datum@{ shape: rect}
n1@{ shape: rect}
n2@{ shape: rect}
 utxos:::inputStyle
 user_input:::inputStyle
 ref_input:::inputStyle
 admin_sig:::inputStyle
 applied_script:::inputStyle
 check_sig:::validationStyle
 check_tokens:::validationStyle
 check_datum:::validationStyle
 ref_token:::outputStyle
 user_token:::outputStyle
 admin:::inputStyle
 user:::inputStyle
 n1:::successStyle
 inputs:::inputStyle
 validation:::validationStyle
 success:::successStyle
 failure:::failureStyle
 submission:::inputStyle
 outputs:::outputStyle
 n2:::inputStyle
classDef inputStyle fill:#e1f5fe,stroke:#0277bd,stroke-width:2px
classDef validationStyle fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
classDef outputStyle fill:#e8f5e8,stroke:#388e3c,stroke-width:2px
classDef successStyle fill:#c8e6c9,stroke:#4caf50,stroke-width:3px
classDef failureStyle fill:#ffcdd2,stroke:#f44336,stroke-width:3px" %}
```

### 1. Build Transaction

Your transaction requires these inputs: For more information on transaction building, see the [transaction building guide](/guides/transaction).

**Required Transaction Inputs:**

* 💰 **UTxOs (ADA for fees)** - Sufficient ADA for transaction fees and minimum UTxO requirements
* 🏷️ **Reference NFT (100)** - Token sent to smart contract address containing metadata
* 🎨 **User Token (222)** - Token sent to user as the actual NFT
* 🔑 **Admin Signature** - Parameterized admin key authorization
* 📋 **Blueprint with Applied Parameters** - Smart contract with embedded admin key

### 2. POST /transactions/build (API Processing)

Once you build your transaction, it gets processed through the Anvil API's transaction builder endpoint:

```http
POST /transactions/build
```

**What happens at this step:**

* Your transaction details are received by the Anvil API
* The API loads the parameterized smart contract from `preloadedScripts`
* Transaction structure is validated for basic requirements
* The smart contract validation logic is triggered
* If validation passes, a transaction is built and returned

**Key Point:** The smart contract validation (next section) happens *during* this API call, not separately.

{% hint style="info" %}
**Want to understand transaction building in detail?** This overview focuses on smart contract validation, but for comprehensive transaction construction details, see the [transaction building guide](/guides/transaction).
{% endhint %}

### 3. Validate Smart Contract

The smart contract performs three validation checks:

#### ✅ Check Admin Signature

```typescript
let is_signed = list.has(self.extra_signatories, signer)
```

Ensures only the parameterized admin can authorize mints.

#### ✅ Validate CIP-68 Tokens

The contract performs comprehensive validation of CIP-68 dual-token creation, metadata, and exact mint matching:

```typescript
validator cip68examples(signer: VerificationKeyHash) {
  mint(redeemer: MintRedeemer, policy_id: PolicyId, self: Transaction) {
    // Step 1: Generate expected tokens based on redeemer instructions
    let expected_mint =
      generate_mint(redeemer.assetnames, policy_id, zero, self)
        |> tokens(policy_id)

    // Step 2: Get what's actually being minted in this transaction
    let currently_minting = self.mint |> tokens(policy_id)

    // Step 3: Ensure both admin signature AND exact token match
    and {
      is_signed?,                              // Admin must sign
      (expected_mint == currently_minting)?,   // Mint exactly what's expected, nothing more/less
    }
  }
}
```

**How this validation works:**

1. **Expected vs Actual**: The contract calculates what tokens *should* be minted based on the redeemer, then compares against what's *actually* being minted
2. **No Surprises**: If someone tries to mint extra tokens or different quantities, the validation fails
3. **Dual Validation**: Both admin signature AND exact token matching must pass

#### ✅ Validate Metadata

The contract ensures each reference token output contains valid CIP-68 metadata:

```typescript
// Extract and validate datum from reference token output
expect Some(datum) = find_datum(output, self)
expect _: Cip68Metadata = datum
```

**Metadata Structure:**

```typescript
pub type Cip68Metadata {
  metadata: Pairs<Data, Data>,
  version: Int,
  extra_datum: MetadataUpdateExpiration,
}
```

**What this validates:**

* **Datum Presence**: Reference token outputs must contain a datum
* **Structure Validation**: Datum must conform to CIP-68 metadata specification
* **Version Control**: Metadata includes version information for future updates

**The `generate_mint` function handles all token validation:**

This function processes each asset name from the redeemer and validates the corresponding transaction outputs:

```typescript
pub fn generate_mint(
  tokens: List<(ByteArray, Int)>,  // List of (asset_name, output_index) pairs
  policy_id: PolicyId,
  mint: Value,                     // Accumulator for expected mint value
  self: Transaction,
) -> Value {
  when tokens is {
    [] -> mint  // Base case: return accumulated mint value
    [(assetname, index), ..rest] -> {
      // Step 1: Generate the CIP-68 token pair names
      let (ref_tok, user_tok) = generate_ref_and_user_token(assetname)
      
      // Step 2: Find and validate the reference token output
      expect Some(output) = self.outputs |> at(index)  // Must exist at specified index
      expect Some(datum) = find_datum(output, self)     // Must have a datum
      expect _: Cip68Metadata = datum                   // Datum must be valid CIP-68 metadata
      
      // Step 3: Verify reference token goes to script address with correct value
      let output_value = without_lovelace(output.value)
      let expected_output_value = zero |> add(policy_id, ref_tok, 1)  // Exactly 1 reference token
      expect output_value == expected_output_value                    // No extra tokens in output
      expect
        when output.address.payment_credential is {
          Script(hash) -> hash == policy_id  // Must go to this script's address
          _ -> False                         // Not a script address = fail
        }
      
      // Step 4: Add both tokens to mint and process remaining assets
      generate_mint(
        rest,
        policy_id,
        mint
          |> add(policy_id, ref_tok, 1)   // Add reference token to expected mint
          |> add(policy_id, user_tok, 1), // Add user token to expected mint
        self,
      )
    }
  }
}
```

**What each validation step ensures:**

* **Output Existence**: The transaction must have an output at the specified index
* **Metadata Presence**: Reference token output must contain valid CIP-68 metadata
* **Address Validation**: Reference token must be sent to the smart contract address
* **Value Precision**: Output contains exactly 1 reference token, no extras
* **Recursive Processing**: All asset names in the redeemer are validated

**CIP-68 Token Generation:**

```typescript
/// (100) Reference Token Prefix
pub const prefix_100: ByteArray = #"000643b0"
/// (222) Non-Fungible Token Prefix  
pub const prefix_222: ByteArray = #"000de140"

pub fn generate_ref_and_user_token(assetname: ByteArray) -> (ByteArray, ByteArray) {
  (prefix_100 |> concat(assetname), prefix_222 |> concat(assetname))
}
```

**Metadata Structure:**

```typescript
pub type Cip68Metadata {
  metadata: Pairs<Data, Data>,
  version: Int,
  extra_datum: MetadataUpdateExpiration,
}
```

**What this comprehensive validation ensures:**

* **Token Pairs**: Creates Reference (100) and User (222) tokens for each asset name
* **Metadata Validation**: Reference tokens must have valid CIP-68 metadata datum
* **Output Placement**: Reference tokens sent to script address, user tokens to specified address
* **Exact Matching**: Only specified tokens are minted, no extras allowed
* **Label Prefixes**: Proper CIP-68 label encoding (100 = `000643b0`, 222 = `000de140`)

### 4. Submit Valid Transaction

Once all validations pass, the transaction is submitted to the Cardano network and CIP-68 token pairs are created on-chain.

### 5. Outputs

The successful transaction creates both tokens as validated above:

* 🏷️ **Reference NFT (100)** - Sent to smart contract address with metadata datum
* 🎨 **User Token (222)** - Sent to user's wallet as the actual NFT

### Security Model

* **Admin Authorization**: Admin key hash is embedded in the contract; only they can authorize mints
* **Customer Control**: Customers control their UTxOs and receive user tokens (222) directly
* **Metadata Protection**: Reference tokens (100) are locked at script address with validated metadata

### Next Steps

Now that you understand the smart contract validation logic, you can:

* **Ready to implement?** See the [practical minting example](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/mint-example) for step-by-step code
* **Want to build custom applications?** Use this validation knowledge to create applications that interact with CIP-68 contracts

### Technical References

* [CIP-68 Specification](https://cips.cardano.org/cips/cip68/)
* [Aiken Smart Contract Language](https://aiken-lang.org/)


# Update Logic

Understanding the smart contract - How to update CIP-68 NFT metadata on Cardano using Aiken spend validators. Deep dive into user vs admin update paths, fee mechanisms, and validation logic.

You've successfully minted CIP-68 tokens and now want to understand how metadata updates work through the **spend validator**!

{% hint style="info" %}
**Prerequisites**: This guide assumes you understand [CIP-68 minting logic](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/mint-logic) and have successfully minted tokens using the [minting example](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/mint-example). The spend validator builds upon the mint validator's foundation.

**Ready to implement?** Skip to the [admin update example](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/admin-update-example) or [user update example](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/user-update-example) and return here when you want to understand the underlying validation logic.
{% endhint %}

### Conceptual Foundation

#### What the Spend Validator Does

While the [mint validator](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/mint-logic) creates CIP-68 token pairs, the **spend validator** enables metadata updates by controlling how reference tokens can be spent and recreated.

**Core Responsibilities:**

* ✅ **Authorizes Updates**: Two paths - admin signature OR user token ownership + fee
* ✅ **Preserves Token Integrity**: Reference tokens must stay at smart contract address
* ✅ **Maintains CIP-68 Compliance**: Updated metadata follows proper structure

#### The Update Process

Every CIP-68 metadata update follows this sequence:

1. **Spend Existing**: Consume the current reference token UTXO
2. **Validate Authority**: Check authorization (admin OR user + fee)
3. **Transform Metadata**: Apply authorized changes to the datum
4. **Recreate Token**: Send updated reference token back to smart contract

{% hint style="success" %}
**Key Insight**: Updates don't modify tokens in-place. They spend the old reference token and create a new one with updated metadata.

**See it in action**: Check out [Admin Update Example](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/admin-update-example) and [User Update Example](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/user-update-example) for complete implementations.
{% endhint %}

#### Two Authorization Paths

**👤 User Updates:**

* Must own user token (222) and include it as transaction input
* Must pay exactly 1 ADA fee to admin address
* Can only update specific fields (e.g., "nickname")
* Other metadata fields are preserved

**👑 Admin Updates:**

* Admin must sign the transaction with their private key
* No fee payment required
* Can update any metadata fields, can modify entire metadata structure and version

#### Validation Flow Overview

The diagram below shows how the spend validator processes both authorization paths:

{% @mermaid/diagram content="flowchart TD
subgraph inputs\["📥 Transaction Inputs"]
ref\_token\["🏷️<b> Reference Token UTxO</b><br>(Current metadata)"]
user\_token\["👤 <b>User Token UTxO</b><br>(Used for Ownership Check)"]
auth\_method\["<b>🔑 Authorization<br></b>(User OR Admin)"]
new\_metadata\["📝<b> New Metadata</b><br>(Updated content)"]
end
subgraph validation\["🔍 Validation Logic"]
check\_type{"Update Type?"}
user\_path\["👤 User Path"]
admin\_path\["👑 Admin Path"]
end
subgraph user\_checks\["👤 User Validation"]
check\_ownership\["✅ User Token Ownership"]
check\_fee\["✅ 1 ADA Fee Payment"]
check\_limited\["✅ Limited Field Updates"]
end
subgraph admin\_checks\["👑 Admin Validation"]
check\_signature\["✅ Admin Signature"]
check\_full\["✅ Full Metadata Update Permissions"]
end
subgraph core\["🛡️ Core Validation"]
check\_address\["✅ Ensure Reference token stays at SC address"]
check\_integrity\["✅ Token Integrity"]
check\_format\["✅ CIP-68 Format"]
end
subgraph outputs\["📤 Transaction Outputs"]
updated\_token\["🏷️ Updated Reference Token<br>→ Same address<br>→ New metadata"]
fee\_output\["💰 Fee Payment<br>(User updates only)"]
end
inputs --> validation
check\_type -- UserUpdate --> user\_path
check\_type -- AdminUpdate --> admin\_path
user\_path --> user\_checks
admin\_path --> admin\_checks
user\_checks --> core
admin\_checks --> core
core --> success\["✅ Validation Passed"] & failure\["❌ Validation Failed"]
success --> outputs

```
 ref_token:::inputStyle
 user_token:::inputStyle
 auth_method:::inputStyle
 new_metadata:::inputStyle
 check_type:::validationStyle
 user_path:::userStyle
 admin_path:::adminStyle
 check_ownership:::userStyle
 check_fee:::userStyle
 check_limited:::userStyle
 check_signature:::adminStyle
 check_full:::adminStyle
 check_address:::coreStyle
 check_integrity:::coreStyle
 check_format:::coreStyle
 updated_token:::outputStyle
 fee_output:::outputStyle
 success:::successStyle
 failure:::failureStyle
classDef inputStyle fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
classDef validationStyle fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
classDef userStyle fill:#fff3e0,stroke:#f57c00,stroke-width:2px
classDef adminStyle fill:#e8f5e8,stroke:#388e3c,stroke-width:2px
classDef coreStyle fill:#fce4ec,stroke:#c2185b,stroke-width:2px
classDef outputStyle fill:#e8f5e8,stroke:#388e3c,stroke-width:2px
classDef successStyle fill:#c8e6c9,stroke:#4caf50,stroke-width:3px
classDef failureStyle fill:#ffcdd2,stroke:#f44336,stroke-width:3px" %}
```

### Authorization Logic Deep Dive

The spend validator implements two distinct authorization paths, each with specific validation requirements:

#### 👤 User Update Path

Users prove ownership and pay fees to update limited metadata fields:

```typescript
// Validation logic for user updates
UserUpdate { nickname, fee_output_index, output_ref_user_token } -> {
  // Verify 1 ADA fee payment to admin
  let fee_output = self.outputs |> at(fee_output_index)
  let lovelace_paid = fee_output.value |> quantity_of("", "")
  let is_paid_to_admin = fee_output.address.payment_credential == admin_key
  
  // Verify user token ownership
  let user_token_input = find_input(self.inputs, output_ref_user_token)
  let user_token_quantity = user_token_input.output.value |> quantity_of(policy_id, user_token)
  
  // Apply selective metadata changes (only "nickname" allowed)
  let expected_datum = selective_metadata_update(datum, nickname)
  
  // All conditions must pass
  and {
    (lovelace_paid == 1000000)?,
    is_paid_to_admin?,
    (current_datum == expected_datum)?,
    (user_token_quantity == 1)?
  }
}
```

**Requirements**: User token ownership + 1 ADA fee + limited field updates

#### 👑 Admin Update Path

Admins use signature authorization for full metadata control:

```typescript
// Simple signature check for admin updates
AdminUpdate -> list.has(self.extra_signatories, signer)
```

**Requirements**: Admin signature only + full metadata permissions

#### Core Validation (Both Paths)

All updates must also pass these fundamental checks:

* **Address Preservation**: `input.address == output.address`
* **Token Integrity**: `input.value == output.value` (same token, updated metadata)
* **Datum Structure**: Valid CIP-68 metadata format

### Implementation Examples

#### User Update Transaction Structure

```typescript
{
  scriptInteractions: [{
    purpose: "spend",
    // Reference token UTXO to update
    outputRef: "d330e666c15e2f19e54b49aff64e69aa134d25242b7dadd95d6aba570a7c1861#0",
    redeemer: {
      output_index: 0,
      update: {
        // New nickname as hex-encoded ByteArray
        nickname: Buffer.from("my_new_nickname").toString("hex"),
        // Output index for 1 ADA fee payment
        fee_output_index: 1,
        // User token UTXO reference (proves ownership)
        output_ref_user_token: {
          transaction_id: "1750a1c191646ade084c911fd9fd8a7c6b8372923e2305c4f4983663c2b236161",
          output_index: 1
        }
      }
    }
  }],
  
  outputs: [{
    // Updated reference token (same address)
    address: "addr_test1wpw62p4shrvu0kwly378dp9w4k8tdldvrjlm9htucv7kfxggmdaj6",
    assets: [{
      assetName: { name: "test_token", label: 100, format: "utf8" },
      policyId: "4983663c2b236161ad8e26c36dff9aee709a6adef53be2cc33d6499",
      quantity: 1
    }],
    datum: {
      type: "inline",
      value: {
        metadata: [["name", "test_token"], ["nickname", "my_new_nickname"]],
        version: 1
      }
    }
  }, {
    // 1 ADA fee to admin
    address: "addr_test1qpw62p4shrvu0kwly378dp9w4k8tdldvrjlm9htucv7kfxggmdaj6",
    value: { lovelace: 1000000 }
  }],
  
  // Include user token as input
  requiredInputs: ["828258201750a1c191646ade084c911fd9fd8a7c6b8372923e2305c4f4983663c2b236161015820..."]
}
```

#### Admin Update Transaction Structure

```typescript
{
  scriptInteractions: [{
    purpose: "spend",
    outputRef: "d330e666c15e2f19e54b49aff64e69aa134d25242b7dadd95d6aba570a7c1861#0",
    redeemer: {
      output_index: 0,
      update: "AdminUpdate"  // Simple string, no additional fields
    }
  }],
  
  outputs: [{
    address: "addr_test1wpw62p4shrvu0kwly378dp9w4k8tdldvrjlm9htucv7kfxggmdaj6",
    assets: [{
      assetName: { name: "test_token", label: 100, format: "utf8" },
      policyId: "4983663c2b236161ad8e26c36dff9aee709a6adef53be2cc33d6499",
      quantity: 1
    }],
    datum: {
      type: "inline",
      value: {
        // Admin can update any fields and version
        metadata: [["name", "test_token"], ["description", "Updated by admin"], ["rarity", "legendary"]],
        version: 1
      }
    }
  }],
  
  // Admin signature required
  requiredSigners: ["a1b2c3d4e5f6789012345678901234567890123456789012345678901234"]
}
```

### Production Considerations

#### Security Model

* **User Updates**: Token ownership + 1 ADA fee + limited permissions
* **Admin Updates**: Signature authorization + full permissions
* **Token Integrity**: Reference tokens locked at smart contract address
* **Audit Trail**: All metadata changes recorded on-chain

#### Common Errors

**User Update Failures:**

* `user_token_quantity != 1` - User doesn't own the token
* `lovelace_paid != 1000000` - Incorrect fee amount
* `current_datum != expected_datum` - Unauthorized metadata field update

**Admin Update Failures:**

* `!list.has(extra_signatories, signer)` - Missing admin signature
* `input.address != output.address` - Address preservation failed

**Core Validation Failures:**

* `datum not Cip68Metadata` - Invalid metadata structure
* `input.value != output.value` - Token integrity violated

### Next Steps

{% content-ref url="/pages/NMtrkrYlVgEYyBzBdAwS" %}
[User Update Example](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/user-update-example)
{% endcontent-ref %}

{% content-ref url="/pages/WA0GZ6gX6U76AGpzBGYD" %}
[Admin Update Example](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/admin-update-example)
{% endcontent-ref %}

### Technical References

* [CIP-68 Specification](https://cips.cardano.org/cips/cip68/) - Official standard
* [Aiken Smart Contract Language](https://aiken-lang.org/) - Contract development


# Mint Example

This guide provides a complete Deno implementation for minting CIP-68 NFTs using parameterized smart contracts with the Anvil API.

{% hint style="info" %}
**Want to understand the smart contract validation logic first?** Check out the [Mint Logic Guide](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/mint-logic) to learn how the contract ensures CIP-68 compliance. Otherwise, you can dive right into the implementation below!
{% endhint %}

### Full Code Example

{% hint style="info" %}
**Want to see the complete code first?** Check out the [full working implementation](https://github.com/Cardano-Forge/anvil-api-examples/blob/main/smart-contracts/cip-68/mint-cip68/create-mint.ts) on GitHub, then come back here for the step-by-step breakdown.
{% endhint %}

### Implementation Overview

This example demonstrates:

* Applying parameters to uploaded blueprints
* Building transactions with preloaded scripts
* Dual-wallet signing for smart contract authorization
* Complete end-to-end minting workflow

### Prerequisites

Before running this example, ensure you have:

1. **Deno installed** - [Download here](https://deno.land/)
2. **Anvil API key** - Get one from the [Anvil dashboard](/anvil-api/authentication)
3. **Two wallets with testnet ADA**: (See [Wallet CLI](/developer-tools/wallet-cli))
   * Customer wallet (pays fees, receives user token)
   * Admin wallet (provides key\_hash parameter, signs transaction)
4. **Uploaded CIP-68 blueprint** - The original blueprint must be uploaded to the Anvil API first. See [blueprint Management](/guides/smart-contract/blueprint-management)
5. **Understanding of parameterized contracts** - Parameters are applied at transaction time, not deployment time

### Step-by-Step Implementation

Let's build a parameterized CIP-68 minting script step by step. Each section will explain the concept and show the corresponding code.

#### Step 1: Setup and Imports

First, we need to import the required dependencies and load our configuration files.

Create `cip68-parameterized-mint.ts`:

```typescript
import { Buffer } from "node:buffer";
import {
  FixedTransaction,
  PrivateKey,
} from "npm:@emurgo/cardano-serialization-lib-nodejs@14.1.1";

// Import wallet configurations and blueprint
import customer from "./wallet-customer.json" with { type: "json" };
import adminWallet from "./wallet-mint-sc-policy.json" with { type: "json" };
import blueprint from "../aiken-mint-cip-68/plutus.json" with { type: "json" };
import { API_URL, HEADERS } from "../../../utils/constant.ts";

// Configuration - Customize these values for your project
const CUSTOMER_ADDRESS = customer.base_address_preprod; // Wallet that pays fees and receives user token
const ADMIN_KEY_HASH = adminWallet.key_hash; // Admin key for smart contract authorization
const ASSET_NAME = "test"; // Base name for your CIP-68 token pair
```

**What's happening here:**

* We import Cardano Serialization Library for transaction signing
* Load wallet files (customer pays fees, admin provides authorization)
* Load the original blueprint (compiled from Aiken smart contract)
* Set up API configuration and asset name

#### Step 2: Apply Parameters to Blueprint

The blueprint that we uploaded is generic. In order to make it custom for this collection. We need to apply the parameters to the smart contract, parameterized contracts are customized at transaction time. We apply the admin's key hash to create a personalized version of the contract:

```typescript

console.log("🔄 Applying parameters to blueprint...");
const originalHash = blueprint.validators[0].hash;
const applyParamsResponse = await fetch(
  `${API_URL}/blueprints/apply-params`,
  {
    method: "POST",
    headers: HEADERS,
    body: JSON.stringify({
      params: { [originalHash]: [ADMIN_KEY_HASH] }, // Apply admin key to parameterize contract
      blueprint: blueprint
    }),
  },
);

if (!applyParamsResponse.ok) {
  console.error("❌ Failed to apply parameters:", applyParamsResponse.status, applyParamsResponse.statusText);
  const errorText = await applyParamsResponse.text();
  console.error("Error details:", errorText);
  Deno.exit(1);
}

const applyParamsResult = await applyParamsResponse.json();
console.log("✅ Parameters applied successfully");

// Extract the parameterized script and address
const parameterizedScript = applyParamsResult.preloadedScript;
const newHashes = Object.keys(applyParamsResult.addresses);
const SCRIPT_HASH = newHashes[0];
const SC_ADDRESS = applyParamsResult.addresses[SCRIPT_HASH].bech32;

console.log("Parameterized script hash:", SCRIPT_HASH);
console.log("Script address:", SC_ADDRESS);
```

**What's happening here:**

* We call `/blueprints/apply-params` with the original blueprint and admin key hash
* The endpoint generates a new script with the admin key "baked in" as a parameter
* We get back a new script hash, address, and complete parameterized script
* **Important**: This parameterized script is NOT saved to the database - we must use it directly

**Expected output:**

```
🔄 Applying parameters to blueprint...
✅ Parameters applied successfully
Parameterized script hash: 5da506b0b8d9c7d9df247c7684aead8eb6fdac1cbfb2dd7cc33d6499
Script address: addr_test1wpw62p4shrvu0kwly378dp9w4k8tdldvrjlm9htucv7kfxggmdaj6
```

#### Step 3: Build the Transaction Payload

Now we build the CIP-68 minting transaction. The key insight is that we include the parameterized script as a `preloadedScript` so the tx-builder can validate it:

```typescript

const input = {
  changeAddress: CUSTOMER_ADDRESS,
  //utxos: [], // Required for LIVE network - provide actual UTxOs
  message: "Cip68 example",
  
  // Mint both reference and user tokens
  mint: [
    {
      // Reference token -> This is the token that will be spent to update metadata
      version: "cip68",
      assetName: {name: assetName, label: 100, format: "utf8"}, 
      policyId: SCRIPT_HASH,
      type: "plutus",
      quantity: 1,
    },
    {
      // User token -> This is the token that will be sent to the user
      version: "cip68",
      assetName: {name: assetName, label: 222, format: "utf8"}, 
      policyId: SCRIPT_HASH,
      type: "plutus",
      quantity: 1,
    },
  ],

  // Script Interactions - Specify redeemer data for smart contract validation during minting
  scriptInteractions: [
    {
      purpose: "mint",
      hash: SCRIPT_HASH,
      redeemer: {
        type: "json",
        value: {
          // Specify the asset name to be minted - Used to validate that the minting transaction
          // is minting the intended CIP-68 assets.
          assetnames: [[Buffer.from(assetName).toString("hex"), 0]]
        },
      },
    },
  ],
  
  // Override Mint Array's 'reference token' (Label 100) output   
  // to modify the datum as spendable in case of a metadata update.
  // This will be sent to the smart contract address.
  outputs: [
    {
      address: SC_ADDRESS, 
      assets: [
        {
          assetName: { name: ASSET_NAME, label: 100, format: "utf8" }, // Reference token (100)
          policyId: SCRIPT_HASH,
          quantity: 1,
        },
      ],
      datum: {
        type: "inline",
        shape: {
          validatorHash: SCRIPT_HASH,
          purpose: "spend",
        },
        value: {
          metadata: [
            ["name", ASSET_NAME], // Must match asset name for CIP-68 compliance
            ["nickname", "test_nickname"], // Custom metadata field
          ],
          version: 1
        }
      },
    },
  ],
  
  requiredSigners: [ADMIN_KEY_HASH], // Admin must sign
  preloadedScripts: [parameterizedScript]
};
```

**What's happening here:**

* **Mint array**: Creates both reference (100) and user (222) tokens with the same base name
* **Script interactions**: Tells the smart contract which assets we're minting via the redeemer
* **Outputs**: Explicitly routes the reference token to the script address with metadata datum
* **Required signers**: Ensures the admin (whose key was used in parameters) signs the transaction
* **Preloaded scripts**: Includes our parameterized script so tx-builder can validate it

**Key insight**: The user token (222) automatically goes to the change address, while the reference token (100) needs an explicit output with custom datum in order to go to the script address.

#### Step 4: Build the Transaction

Finally, we build the transaction:

```typescript
// Build the transaction
console.log("🔨 Building transaction...");
const contractDeployed = await fetch(`${API_URL}/transactions/build`, {
  method: "POST",
  headers: HEADERS,
  body: JSON.stringify(input),
});

if (!contractDeployed.ok) {
  console.error("❌ Failed to build transaction:", contractDeployed.status, contractDeployed.statusText);
  const errorText = await contractDeployed.text();
  console.error("Error details:", errorText);
  Deno.exit(1);
}

const contractResult = await contractDeployed.json();
console.log("✅ Transaction built successfully");
```

**What's happening here:**

* The tx-builder validates our payload and creates a Cardano transaction
* It includes the parameterized script from our `preloadedScripts`
* The transaction is returned as a hex-encoded string ready for signing

#### Step 5: Sign with Both Wallets

CIP-68 smart contracts require signatures from both the customer (who pays fees) and the admin (who authorizes minting):

```typescript
// Sign the transaction using CSL.
const txToSubmitOnChain = FixedTransaction.from_bytes(Buffer.from(contractResult.complete, "hex"));
txToSubmitOnChain.sign_and_add_vkey_signature(PrivateKey.from_bech32(customer.skey));
txToSubmitOnChain.sign_and_add_vkey_signature(PrivateKey.from_bech32(adminWallet.skey));

console.log("✅ Transaction signed with both keys");
```

**What's happening here:**

* We deserialize the transaction from hex format
* Load both private keys (customer and admin)
* Add both signatures to the witness set
* Create the final signed transaction

#### Step 6: Submit to Network

Finally, we submit the signed transaction to the Cardano network:

```typescript
// Submit to network
console.log("📡 Submitting to network...");
const submitResponse = await fetch(`${API_URL}/transactions/submit`, {
  method: "POST",
  headers: HEADERS,
  body: JSON.stringify({ transaction: signedTx.to_hex() }),
});

if (!submitResponse.ok) {
  console.error("❌ Failed to submit transaction:", submitResponse.status, submitResponse.statusText);
  const errorText = await submitResponse.text();
  console.error("Error details:", errorText);
  Deno.exit(1);
}

const submitResult = await submitResponse.json();
console.log("🎉 Transaction submitted successfully!");
console.log("Transaction hash:", submitResult.transactionId);
console.log("Reference Token (100):", `${SCRIPT_HASH}.${Buffer.from(assetName).toString("hex")}64`);
console.log("User Token (222):", `${SCRIPT_HASH}.${Buffer.from(assetName).toString("hex")}de`);
```

**What's happening here:**

* The signed transaction is submitted to the Cardano network
* On success, we get back a transaction hash for tracking
* Both CIP-68 tokens are now minted and distributed according to our rules

**Expected output:**

```
📡 Submitting to network...
🎉 Transaction submitted successfully!
Transaction hash: a1b2c3d4e5f6789...
Reference Token (100): 5da506b0b8d9c7d9df247c7684aead8eb6fdac1cbfb2dd7cc33d649974657374064
User Token (222): 5da506b0b8d9c7d9df247c7684aead8eb6fdac1cbfb2dd7cc33d649974657374de
```

### Running the Example

#### Prerequisites Setup

1. **Upload the blueprint first** using a separate upload script:

   ```bash
   deno run --allow-all upload-blueprint.ts --blueprint=../aiken-mint-cip-68/plutus.json
   ```
2. **Set up wallet files**:
   * `wallet-customer.json` - Customer wallet (pays fees, receives user token)
   * `wallet-mint-sc-policy.json` - Admin wallet (provides key\_hash parameter)
3. **Update configuration**:
   * Ensure `API_URL` and `HEADERS` are configured in your constants file
   * Modify `assetName` and metadata as desired
4. **Run the parameterized minting script**:

   ```bash
   deno run --allow-all cip68-parameterized-mint.ts
   ```

#### Expected Output

```
🔄 Applying parameters to blueprint...
✅ Parameters applied successfully
Parameterized script hash: 5da506b0b8d9c7d9df247c7684aead8eb6fdac1cbfb2dd7cc33d6499
Script address: addr_test1wpw62p4shrvu0kwly378dp9w4k8tdldvrjlm9htucv7kfxggmdaj6
✅ Transaction built successfully
🎉 CIP-68 Asset Minted Successfully!
Transaction Hash: 74dcb95427b9eee36137803617a900460b5d58ed266598ebf4146d659b613520
Reference Token (100): 5da506b0b8d9c7d9df247c7684aead8eb6fdac1cbfb2dd7cc33d6499.74657374064
User Token (222): 5da506b0b8d9c7d9df247c7684aead8eb6fdac1cbfb2dd7cc33d6499.746573740de
✅ Transaction submitted successfully
```

### Next Steps

This example demonstrates the complete parameterized CIP-68 workflow. For production use:

1. **Add robust error handling** for apply-params and transaction failures
2. **Implement parameter validation** to ensure correct admin keys
3. **Add transaction monitoring** to track minting status on-chain
4. **Create reusable functions** for parameter application and script generation
5. **Implement batch operations** for multiple asset minting

### References

* [Apply-params endpoint implementation](/guides/smart-contract/smart-contract-utilities#apply-parameters-to-script)
* [Transaction builder mint logic](/guides/transaction)
* [Working example implementation](https://github.com/Cardano-Forge/anvil-api-examples/blob/main/smart-contracts/cip-68/mint-cip68/create-mint.ts)


# Admin Update Example

This guide provides a complete Deno implementation for updating CIP-68 NFT metadata using spend validators with parameterized smart contracts and the Anvil API.

{% hint style="info" %}
**Want to understand the smart contract validation logic first?** Check out the [Update Logic Guide](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/update-logic) to learn how the contract ensures proper authorization for metadata updates. Otherwise, you can dive right into the implementation below!
{% endhint %}

### Full Code Example

{% hint style="info" %}
**Want to see the complete code first?** Check out the [full working implementation](https://github.com/Cardano-Forge/anvil-api-examples/blob/main/smart-contracts/cip-68/mint-cip68/admin-update.ts) on GitHub, then come back here for the step-by-step breakdown.
{% endhint %}

### Implementation Overview

This example demonstrates:

* Spending existing reference tokens for metadata updates
* Using spend validators with proper redeemer structures
* Admin authorization patterns for metadata changes
* Complete end-to-end update workflow

### Prerequisites

This guide assumes you have completed the [Minting Example](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/mint-example). Additionally, you need:

1. **Existing CIP-68 tokens** - Reference and user tokens from a successful mint
2. **Current reference token UTXO** - Transaction hash and output index where the reference token currently resides
3. **Understanding of spend validators** - Updates consume and recreate reference tokens with new metadata

### Step-by-Step Implementation

Let's build a CIP-68 metadata update script step by step. Each section will explain the concept and show the corresponding code.

#### Step 1: Setup and Configuration

Using the same setup from the minting example:

```typescript
import { Buffer } from "node:buffer";
import { FixedTransaction, PrivateKey } from "npm:@emurgo/cardano-serialization-lib-nodejs@14.1.1";
import customer from "./wallet-customer.json" with { type: "json" };
import adminWallet from "./wallet-mint-sc-policy.json" with { type: "json" };
import blueprint from "../aiken-mint-cip-68/plutus.json" with { type: "json" };
import { API_URL, HEADERS } from "../../../utils/constant.ts";

// Configuration - Update these values for your specific tokens
const CUSTOMER_ADDRESS = customer.base_address_preprod; // Change address for transaction
const ADMIN_KEY_HASH = adminWallet.key_hash; // Admin authorization key
const ASSET_NAME = "test"; // Name of the CIP-68 token to update

// NEW: Reference token UTXO to update
const TX_HASH = "d330e666c15e2f19e54b49aff64e69aa134d25242b7dadd95d6aba570a7c1861";
const TX_OUTPUT_INDEX = "0";
```

**Key difference from minting:**

* **TX\_HASH and TX\_OUTPUT\_INDEX** specify which existing reference token to update

#### Step 2: Apply Parameters to Blueprint

The apply-params process is identical to the minting example:

```typescript
const applyParamsResult = await fetch(`${API_URL}/blueprints/apply-params`, {
  method: "POST",
  headers: HEADERS,
  body: JSON.stringify({
    params: { [blueprint.validators[0].hash]: [ADMIN_KEY_HASH] },
    blueprint: blueprint
  }),
}).then(res => res.json());

const parameterizedScript = applyParamsResult.preloadedScript;
const SCRIPT_HASH = Object.keys(applyParamsResult.addresses)[0];
const SC_ADDRESS = applyParamsResult.addresses[SCRIPT_HASH].bech32;
```

**Result:** Same parameterized script and address as your original mint

#### Step 3: Build the Update Transaction

Now we build the spend transaction that will update the metadata. The key insight is that we **spend** the existing reference token and **recreate** it with new metadata:

```typescript
const input = {
  changeAddress: CUSTOMER_ADDRESS,
  message: "CIP-68 Admin Update Example",
  
  // Script Interactions - Define how to interact with the smart contract during transaction execution
  scriptInteractions: [
    {
      // Purpose: "spend" tells Cardano this is spending a UTXO locked at a smart contract address
      purpose: "spend",
      
      // Hash: The parameterized script hash of our CIP-68 validator
      hash: SCRIPT_HASH,
      
      // OutputRef: Specifies exactly which UTXO we want to spend (the reference token)
      outputRef: {
        txHash: TX_HASH,        // Transaction hash containing the reference token UTXO
        index: TX_OUTPUT_INDEX, // Output index within that transaction (usually 0)
      },
      
      // Redeemer: The data passed to the smart contract's spend validator for authorization
      redeemer: {
        type: "json",
        value: {
          // output_index: Tells the validator which transaction output contains the updated reference token
          output_index: 0,
          
          // update: "AdminUpdate" triggers admin authorization path in the smart contract
          // This makes the validator check that the transaction is signed by the admin key
          // (as opposed to "UserUpdate" which would require fee payment)
          update: "AdminUpdate"
        },
      },
    },
  ],
```

**What's happening here:**

* **Script interactions**: Tells the smart contract we're spending a UTXO locked at the contract address
* **OutputRef**: Specifies exactly which reference token UTXO we want to update
* **Redeemer**: Contains the authorization data - "AdminUpdate" means admin signature is required

#### Step 4: Define the Updated Output

We need to send the reference token back to the smart contract with updated metadata:

```typescript
  outputs: [
    {
      // Send the updated reference token back to the smart contract address with the new metadata
      address: SC_ADDRESS,
      assets: [
        {
          assetName: {name: assetName, label: 100, format: "utf8"},
          policyId: SCRIPT_HASH,
          quantity: 1,
        },
      ],
      // Details how and where the datum is stored to be used later.
      datum: {
        type: "inline",
        shape: {
          validatorHash: SCRIPT_HASH,
          purpose: "spend", // Marked as spend purpose so the spend validator can update the metadata later.
        },
        value: {
          // Updated CIP-68 Metadata goes here. Follows official CIP-68 spec metadata format. (metadata, version, datum)
          metadata: [
            ["name", "test"],
            ["nickname", "admin_updated"] // Updated nickname to show the admin update
          ],
          version: 1
        }
      },
    },
  ],
  
  // Admin must sign to pass smart contract validation
  requiredSigners: [META_MANAGER_KEY_HASH],
  preloadedScripts: [parameterizedScript]
};
```

**What's happening here:**

* **Output**: The reference token is sent back to the smart contract with updated metadata
* **Datum**: Contains the new metadata following CIP-68 specification
* **Required signers**: Admin must sign the transaction for authorization
* **Preloaded scripts**: Include the parameterized script for validation

#### Step 5: Build and Submit Transaction

Now we build, sign, and submit the transaction:

```typescript
const contractDeployed = await fetch(`${API_URL}/transactions/build`, {
  method: "POST",
  headers: HEADERS,
  body: JSON.stringify(input),
});

if (!contractDeployed.ok) {
  const errorText = await contractDeployed.text();
  console.error("❌ Transaction build failed:", errorText);
  Deno.exit(1);
}

const transaction = await contractDeployed.json();
console.log("✅ Transaction built successfully");

// Sign the transaction using CSL.
const txToSubmitOnChain = FixedTransaction.from_bytes(Buffer.from(transaction.complete, "hex"));
txToSubmitOnChain.sign_and_add_vkey_signature(PrivateKey.from_bech32(adminWallet.skey));

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

const output = await submitted.json();
if (!submitted.ok) {
  console.error("❌ Transaction submission failed:", output);
  Deno.exit(1);
}

console.log("✅ Admin update transaction submitted successfully");
console.log("Transaction hash:", output.transactionId);
```

**What's happening here:**

* The tx-builder validates our spend transaction and creates a Cardano transaction
* We sign with the admin's private key (required for "AdminUpdate" authorization)
* The transaction is submitted to the Cardano network
* On success, we get back a transaction hash for tracking

### Running the Example

1. **Find your reference token UTXO**:
   * Use CardanoScan to search for your policy ID from the mint
   * Locate the reference token (label 100) at the smart contract address
   * Update `TX_HASH` and `TX_OUTPUT_INDEX` in your script
2. **Modify the metadata** in the `datum.value.metadata` section as desired
3. **Run the update script**:

   ```bash
   deno run --allow-all admin-update.ts
   ```

#### Expected Output

```
🔄 Applying parameters to blueprint...
✅ Parameters applied successfully
Parameterized script hash: 5da506b0b8d9c7d9df247c7684aead8eb6fdac1cbfb2dd7cc33d6499
Script address: addr_test1wpw62p4shrvu0kwly378dp9w4k8tdldvrjlm9htucv7kfxggmdaj6
✅ Transaction built successfully
✅ Admin update transaction submitted successfully
Transaction hash: d330e666c15e2f19e54b49aff64e69aa134d25242b7dadd95d6aba570a7c1861
```

### Understanding the Update Process

#### Spend Validator Logic

The CIP-68 update process uses a **spend validator** that:

1. **Validates Authorization**: Checks that the admin has signed the transaction
2. **Preserves Token Integrity**: Ensures the reference token stays at the smart contract
3. **Allows Metadata Changes**: Permits updates to the inline datum containing metadata
4. **Maintains CIP-68 Compliance**: Enforces proper metadata structure and versioning

#### Authorization Patterns

**Admin Update (shown in this example):**

* Requires admin signature
* No fee payment needed
* Full metadata update permissions

**User Update (alternative pattern):**

* Requires user token ownership proof
* Fee payment to admin
* Limited metadata update permissions

#### Transaction Flow

1. **Input**: Spend the existing reference token UTXO
2. **Validation**: Smart contract validates admin signature
3. **Output**: Create new reference token UTXO with updated metadata
4. **Result**: Metadata is updated while preserving token integrity

### Key Requirements

* **Track UTXO locations**: Reference tokens move with each update
* **Use same script hash**: From your original mint parameterization
* **Admin signature required**: Only the minting admin can perform admin updates

### Next Steps

This example demonstrates the complete CIP-68 admin update workflow. For production use:

1. **Add UTXO discovery** to automatically find current reference token locations
2. **Implement user updates** with fee payment and user token validation
3. **Add batch operations** for updating multiple tokens
4. **Create metadata validation** to ensure proper format and constraints
5. **Implement update history tracking** for audit trails

### References

* [CIP-68 Specification](https://cips.cardano.org/cip/CIP-68) - Official standard
* [Smart Contract Logic Guide](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/nft-and-ft/mint-nft-cip-68/smart-contract/logic.md) - Validator implementation details
* [Minting Example](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/mint-example) - How to create CIP-68 tokens
* [Working example implementation](https://github.com/Cardano-Forge/anvil-api-examples/blob/main/smart-contracts/cip-68/mint-cip68/admin-update.ts)


# User Update Example

This guide provides a complete Deno implementation for user-driven CIP-68 NFT metadata updates using spend validators, fee payments, and token ownership verification with the Anvil API.

{% hint style="info" %}
**Want to understand the smart contract validation logic first?** Check out the [Smart Contract Logic Guide](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/update-logic) to learn how the contract validates user updates. Otherwise, you can dive right into the implementation below!
{% endhint %}

### Full Code Example

{% hint style="info" %}
**Want to see the complete code first?** Check out the [full working implementation](https://github.com/Cardano-Forge/anvil-api-examples/blob/main/smart-contracts/cip-68/mint-cip68/customer-update.ts) on GitHub, then come back here for the step-by-step breakdown.
{% endhint %}

### Implementation Overview

This example demonstrates **user-driven metadata updates** where token holders can update their own NFT metadata by:

* Proving ownership of the user token (222)
* Paying a 1 ADA fee to the admin
* Updating limited metadata fields (e.g., nickname)
* Following the spend validator's authorization rules

### Prerequisites

This guide assumes you have completed the [Minting Example](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/mint-example) and have:

1. **Existing CIP-68 tokens** - Reference and user tokens from a successful mint
2. **User token ownership** - The user token (222) must be in the customer's wallet
3. **Reference token location** - Current UTXO of the reference token at the script address
4. **Blockfrost API access** - For dynamic UTXO discovery
5. **Understanding of user updates** - Review [update logic](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/update-logic) for validation details

### Step-by-Step Implementation

Let's build a user-driven CIP-68 metadata update script step by step. Each section explains the concept and shows the corresponding code.

#### Step 1: Setup and Configuration

Create update file with the required imports and configuration:

```typescript
import { Buffer } from "node:buffer";
import {
  FixedTransaction,
  PrivateKey,
} from "npm:@emurgo/cardano-serialization-lib-nodejs@14.1.1";

// Import wallet files and constants
import customer from "./wallet-customer.json" with { type: "json" };
import adminWallet from "./wallet-mint-sc-policy.json" with { type: "json" };
import { API_URL, HEADERS } from "../../../utils/constant.ts";

// Configuration
const CUSTOMER_ADDRESS = customer.base_address_preprod;
const ADMIN_KEY_HASH = adminWallet.key_hash;
const ADMIN_ADDRESS = adminWallet.base_address_preprod;
const assetName = "cip68_1753999309623"; // Your minted asset name
const nickname = "customer_updated"; // New nickname to set

// Import Blockfrost utility for dynamic UTXO fetching
import { 
  getUtxos, 
  findUserTokenUTXO, 
  findReferenceTokenUTXO 
} from "../../../fetch-utxos-from-the-backend/utxos/blockfrost.ts";

// Blockfrost configuration
const BLOCKFROST_BASE_URL = "https://cardano-preprod.blockfrost.io/api/v0";
const BLOCKFROST_API_KEY = Deno.env.get("BLOCKFROST_PROJECT_ID");

if (!BLOCKFROST_API_KEY) {
  console.error("❌ BLOCKFROST_PROJECT_ID environment variable is required");
  Deno.exit(1);
}
```

**Key differences from admin updates:**

* **Customer wallet** signs the transaction (not admin)
* **Admin address** receives the 1 ADA fee payment
* **Blockfrost integration** for dynamic UTXO discovery
* **Asset name** must match the originally minted token

#### Step 2: Apply Parameters and Find UTXOs

Apply parameters to the blueprint and locate the required UTXOs:

```typescript
// Load blueprint and apply parameters (same as other examples)
import blueprint from "../aiken-mint-cip-68/plutus.json" with { type: "json" };

console.log("🔄 Applying parameters to blueprint...");
const applyParamsResponse = await fetch(`${API_URL}/blueprints/apply-params`, {
  method: "POST",
  headers: HEADERS,
  body: JSON.stringify({
    params: { [blueprint.validators[0].hash]: [ADMIN_KEY_HASH] },
    blueprint: blueprint
  }),
});

const applyParamsResult = await applyParamsResponse.json();
const parameterizedScript = applyParamsResult.preloadedScript;
const POLICY_ID = Object.keys(applyParamsResult.addresses)[0];
const SC_ADDRESS = applyParamsResult.addresses[POLICY_ID].bech32;

console.log("Parameterized script hash:", POLICY_ID);
console.log("Script address:", SC_ADDRESS);

// Find reference token UTXO at script address
const referenceTokenUTXO = await findReferenceTokenUTXO(
  BLOCKFROST_BASE_URL,
  BLOCKFROST_API_KEY,
  POLICY_ID,
  assetName,
  SC_ADDRESS
);

// Find user token UTXO at customer address
const userTokenUTXO = await findUserTokenUTXO(
  BLOCKFROST_BASE_URL,
  BLOCKFROST_API_KEY,
  POLICY_ID,
  assetName,
  CUSTOMER_ADDRESS
);

// Get user token UTXO in string format for requiredInputs
const utxos = await getUtxos(BLOCKFROST_BASE_URL, BLOCKFROST_API_KEY, CUSTOMER_ADDRESS);
const utxoString = `000de140${Buffer.from(assetName).toString("hex")}`;
const userUtxo = utxos.find((utxo) => utxo.includes(utxoString));

if (!userUtxo) {
  console.error("❌ User token UTXO not found at customer address");
  Deno.exit(1);
}

console.log(`Reference token: ${referenceTokenUTXO.transaction_id}#${referenceTokenUTXO.output_index}`);
console.log(`User token: ${userTokenUTXO.transaction_id}#${userTokenUTXO.output_index}`);
```

**What's happening here:**

* **Reference token discovery**: Finds the current reference token UTXO at the script address
* **User token discovery**: Locates the user token in the customer's wallet (proves ownership)
* **UTXO string format**: Gets the hex-encoded UTXO string needed for `requiredInputs`
* **Validation**: Ensures both tokens exist before proceeding

#### Step 3: Build the User Update Transaction

Now we build the spend transaction with user authorization:

```typescript
const input = {
  changeAddress: CUSTOMER_ADDRESS,
  message: "CIP-68 Customer Update Example",
  
  // SCRIPT INTERACTIONS: Tell the validator how to spend the reference token
  scriptInteractions: [
    {
      purpose: "spend", // Spending a UTXO locked at smart contract address
      
      // REFERENCE TOKEN UTXO: The UTXO containing the reference token to update
      outputRef: {
        txHash: referenceTokenUTXO.transaction_id,
        index: referenceTokenUTXO.output_index,
      },
      
      // REDEEMER: Authorization data for the spend validator
      redeemer: {
        type: "json",
        value: {
          output_index: 0, // Index of output containing updated reference token
          update: {
            // USER UPDATE: Triggers UserUpdate validation branch
            nickname: Buffer.from(nickname).toString("hex"), // New nickname in hex
            fee_output_index: 1, // Index of output paying 1 ADA fee to admin
            
            // USER TOKEN REFERENCE: Validator uses this to verify ownership
            output_ref_user_token: {
              transaction_id: userTokenUTXO.transaction_id,
              output_index: userTokenUTXO.output_index,
            },
          },
        },
      },
    },
  ],
```

**Key redeemer components:**

* **nickname**: The updated value in hex format (limited user permission)
* **fee\_output\_index**: Points to the 1 ADA fee payment output
* **output\_ref\_user\_token**: References the user token for ownership verification

#### Step 4: Define Transaction Outputs

Create the outputs for the updated reference token and fee payment:

```typescript
  // TRANSACTION OUTPUTS: What the transaction creates
  outputs: [
    {
      // OUTPUT 0: Updated reference token back to script address
      address: SC_ADDRESS,
      assets: [
        {
          assetName: { name: assetName, label: 100, format: "utf8" },
          policyId: POLICY_ID,
          quantity: 1,
        },
      ],
      // UPDATED DATUM: Contains new metadata with updated nickname
      datum: {
        type: "inline",
        shape: {
          validatorHash: POLICY_ID,
          purpose: "spend",
        },
        value: {
          metadata: [
            ["name", assetName], // Must match original minted asset name
            ["nickname", "customer_updated"], // Updated field
          ],
          version: 1,
        },
      },
    },
    {
      // OUTPUT 1: 1 ADA fee payment to admin
      address: ADMIN_ADDRESS,
      lovelace: "1000000", // Exactly 1 ADA - validator verifies this amount
    },
  ],

  requiredSigners: [], // No admin signature required for user updates
  referenceInputs: [],
  requiredInputs: [userUtxo!], // Include user token as input (proves ownership)
  preloadedScripts: [parameterizedScript],
};
```

**Critical output requirements:**

* **Output 0**: Updated reference token with new metadata at script address
* **Output 1**: Exactly 1 ADA fee payment to admin address
* **Datum preservation**: Original asset name must be preserved
* **User token input**: Must be included via `requiredInputs` for ownership proof

#### Step 5: Build, Sign, and Submit Transaction

Build and submit the user update transaction:

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

if (!contractDeployed.ok) {
  const errorText = await contractDeployed.text();
  console.error("❌ Transaction build failed:", errorText);
  Deno.exit(1);
}

const transaction = await contractDeployed.json();
console.log("✅ Transaction built successfully");

// Sign with customer's private key (not admin)
const txToSubmitOnChain = FixedTransaction.from_bytes(
  Buffer.from(transaction.complete, "hex")
);
txToSubmitOnChain.sign_and_add_vkey_signature(
  PrivateKey.from_bech32(customer.skey)
);

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

const output = await submitted.json();
if (!submitted.ok) {
  console.error("❌ Transaction submission failed:", output);
  Deno.exit(1);
}

console.log("✅ Customer update transaction submitted successfully");
console.log("Transaction hash:", output.transactionId);
```

**What's happening here:**

* **Customer signature**: Only the customer signs (proves they own the user token)
* **No admin signature**: Admin authorization comes from fee payment, not signature
* **Validation on-chain**: Smart contract validates ownership and fee payment

### Running the Example

#### Prerequisites Setup

1. **Complete the minting example first** to have CIP-68 tokens available
2. **Set up environment variables**:

   ```bash
   # PowerShell/Windows
   $env:BLOCKFROST_PROJECT_ID='your_blockfrost_api_key'

   # Bash/macOS/Linux
   export BLOCKFROST_PROJECT_ID='your_blockfrost_api_key'
   ```
3. **Update configuration**:
   * Set `assetName` to match your minted token
   * Modify `nickname` to your desired update value
   * Ensure wallet files are properly configured
4. **Run the user update script**:

   ```bash
   deno run --allow-all user-update.ts
   ```

#### Expected Output

```
🔄 Applying parameters to blueprint...
✅ Parameters applied successfully
Parameterized script hash: 4983663c2b236161ad8e26c36dff9aee709a6adef53be2...
Script address: addr_test1wpycxe3u9v3kzcdd3cnvxm0lnth8pxn2mm6nhch9f6pagxq...
Reference token: 1750a1c191646ade084c911fd9fd8a7c6b8372923e2305c4f4...#0
User token: 1750a1c191646ade084c911fd9fd8a7c6b8372923e2305c4f4...#1
✅ Transaction built successfully
✅ Customer update transaction submitted successfully
Transaction hash: b2cb92868e58238edb5646775847fca911c46c4d2206bb66b64c4df0af06187b
```

### User vs Admin Updates

This user update approach differs from admin updates in three key ways:

| Aspect            | User Updates                    | Admin Updates        |
| ----------------- | ------------------------------- | -------------------- |
| **Authorization** | Token ownership + 1 ADA fee     | Admin signature only |
| **Permissions**   | Limited fields (e.g., nickname) | Full metadata access |
| **Cost**          | 1 ADA fee to admin              | Free for admin       |

The economic barrier prevents spam while giving users control over their metadata.

### Troubleshooting

#### Common Validation Failures

If your transaction fails validation, check these common issues:

* **`user_token_quantity != 1`** - User token not included in transaction inputs
* **`lovelace_paid != 1000000`** - Fee amount is not exactly 1 ADA
* **`!is_paid_to_admin`** - Fee payment sent to wrong address
* **`current_datum != expected_datum`** - Attempting to update restricted metadata fields

{% hint style="info" %}
For detailed validator logic, see the [Update Logic Guide](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/update-logic).
{% endhint %}

### Key Requirements

* **Include user token** in `requiredInputs` for ownership proof
* **Pay exactly 1 ADA** (1,000,000 lovelace) to admin address
* **Update only permitted fields** as defined by the smart contract
* **Track UTXO locations** dynamically (tokens move with each update)

### Next Steps

You now have a complete user-driven update implementation. For production applications, consider:

* **Batch Updates**: Multiple metadata fields in one transaction
* **Update History**: Track metadata changes for audit trails
* **Fee Estimation**: Display costs before transaction submission
* **User Interface**: Build a frontend for non-technical users

### References

* [Update Logic Guide](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/update-logic) - Deep dive into Aiken implementation
* [Admin Update Example](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/admin-update-example) - Admin-driven updates
* [CIP-68 Specification](https://cips.cardano.org/cips/cip68/) - Official standard
* [Working Implementation](https://github.com/Cardano-Forge/anvil-api-examples/blob/main/smart-contracts/cip-68/mint-cip68/customer-update.ts) - Complete code


# Mint NFTs (CIP-25)

Overview for minting CIP-25 compliant NFTs/Native Assets on Cardano using the Anvil API's \`transactions/build\` endpoint. This document specifies the required JSON format for both new and existing poli

## Introduction

The `transactions/build` endpoint using the `mint` type allows for the creation of unique digital assets (NFTs/Native Assets) following the CIP-25 metadata standard. This guide demonstrates how to structure API requests when working with the Anvil platform to mint these assets, whether you're establishing a new collection or adding to an existing one.

### CIP-25 Mint with New Policy

#### Prerequisites

Before minting a new NFT collection, ensure you have the following:

1. A valid ADA wallet address (change address)
2. A list of UTXOs (Unspent Transaction Outputs) from your wallet. See [UTXOs](/guides/transaction/selecting-utxos)
3. A unique asset name (UTF-8 or hex format)
4. Custom metadata for your NFT (Use <https://metadraft.io> to generate accurate metadata easily.)
5. A native script registered on the Cardano blockchain. (See [Creating Native Scripts](/guides/nft-and-ft/native-scripts/create-native-script) for more information.)

For more information about native scripts, please see:

{% content-ref url="/pages/oLM0BLwLeb3jtJ9aLFaj" %}
[Native Scripts](/guides/nft-and-ft/native-scripts)
{% endcontent-ref %}

#### Request Structure

**Payload**

{% code overflow="wrap" %}

```json
{
  // Specifies which address should receive any leftover ADA (change) 
  // after the transaction is completed and fees are paid
  "changeAddress": "addr_test1qqwadht4defe...",

  // An array of UTXO strings from the wallet paying for the transaction.
  "utxos": ["8282...", "8282..."],

  // An array of assets to be minted.
  "mint": [
    {
      "version": "cip25",
      // UTF-8 or hex format - Must be unique within the collection.
      "assetName": { "name": "anvilapicip25", "format": "utf8" },

      // Custom Metadata for your NFT. Replace with your own CIP-25 standard values.
      // Use https://metadraft.io to generate accurate metadata easily.
      "metadata": {
        "name": "YOUR_NFT_NAME_HERE",
        "image": [
          "https://your-storage-provider.com/", // URL to your image
          "image-file.png" // File name of your image
        ],
        "mediaType": "image/png", // Media type of your image
        "description": "Anvil API CIP-25 Mint Example" // Description of your NFT
      },
      // The policy ID of the collection.
      "policyId": "4d5bd6249f0d9e4b2762ce334e2973dc7fd414ec1e08b4b0c2159bfb",
      "quantity": 1 // Accepts integers > 0. Negative numbers will burn assets.
    }
  ],

  // An array of native scripts that represent the policy that needs to be pre-loaded for validation.
  // This is required for the initial mint. once the policy is registered this is optional.
  // Including it saves resources and reduces latency by avoiding blockchain fetches.
  "preloadedScripts": [
    {
      "type": "simple",
      // The native script details.
      "script": {
        "type": "all",
        "scripts": [
          {
            "type": "sig",
            "keyHash": "fdf151b600df2492005221876c7d7e33056496572c7363c33a1e3609"
          },
          {
            "type": "before",
            "slot": 100000000
          }
        ]
      },
      // The hash of the native script, which is the Policy ID.
      "hash": "4d5bd6249f0d9e4b2762ce334e2973dc7fd414ec1e08b4b0c2159bfb"
    }
  ]
}
```

{% endcode %}

## Examples

In order to reduce page content, the use cases and examples are split into a dedicated repository.

* [Vanilla JavaScript CIP-25 Minting Platform](https://github.com/Cardano-Forge/anvil-api-examples/tree/main/minting-platform-cip25)
* [Next.js CIP-25 Minting Platform](https://github.com/Cardano-Forge/anvil-api-examples/tree/main/nextjs-minting-platform-cip25)

## Troubleshooting

### Fixes and Best Practices

1. Use the [metadata generation tool](https://metadraft.io) to ensure your metadata follows the CIP-25 standard
2. For multiple assets under the same policy, ensure each asset name is unique
3. Double-check that your time constraints in your native script have not expired

## Specifications

* [CIP-25 Specification](https://cips.cardano.org/cip/CIP-25)


# CLI Tool to Mint Assets

Create a custom CLI Tool, using Deno and Fetch to connect to anvil API, taking few configurable parameters, it is possible to mint a collection using a binary with a custom implementation.

This a a very specific example, the goal is to show how flexible and versatile the Anvil API is.

**This example will be entirely done into one file named `index.ts`**

## **Objectives**

* Build a simple CLI tool to mint assets
* Create a Policy (an NFT Collection)
* Create a custom function to customize the asset metadata
* Create 2 wallets, one for the policy and one to act as the customer
* Submit transaction to the network
* This is only an example showing the anvil api versatility
* The example is built on Cardano Preprod

### **Dependencies**

{% code overflow="wrap" %}

```typescript
import { Buffer } from "node:buffer";
import {
  Credential,
  type Ed25519KeyHash,
  FixedTransaction,
  NativeScript,
  NativeScripts,
  PrivateKey,
  ScriptAll,
  ScriptPubkey,
  TimelockExpiry,
} from "npm:@emurgo/cardano-serialization-lib-nodejs@13.2.0";
import { parseArgs } from "jsr:@std/cli/parse-args";
```

{% endcode %}

### Cardano Wallets

We have this utility to generate wallets: <https://github.com/Cardano-Forge/cardano-wallet-cli/releases>

```bash
~/Downloads/cardano-wallet-macos-latest --name policy --mnemonic
~/Downloads/cardano-wallet-macos-latest --name customer --mnemonic
```

You'll need to add some tADA to both wallets—100 should be more than enough.

### Import Helper Functions

To reduce the amount of content in this guide, you only have to import all functions defined here:

{% content-ref url="<https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/utilities-functions/README.md>" %}
<https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/utilities-functions/README.md>
{% endcontent-ref %}

### CLI

{% code overflow="wrap" %}

```typescript
const args: {
  _: [];
  expireDate: string;
  customerWalletPath: string;
  policyWalletPath: string;
  metadataTemplatePath: string;
  counter: number;
  submit: boolean;
} = parseArgs(Deno.args);
```

{% endcode %}

Parse and prepare the policy and wallets:

{% code overflow="wrap" %}

```typescript
// NOTE: Be sure to send ADA in this address, it will be used to pay the tx fee.
const customerWallet = JSON.parse(
  Deno.readTextFileSync(args.customerWalletPath)
);
// Wallet to create the policy with, no ADA is required for this one.
const policyWallet = JSON.parse(Deno.readTextFileSync(args.policyWalletPath));

// Expiration date, you can interact with the policy until this DateTime is reached.
// After that the policy is locked.
const slot = timeToSlot(new Date(args.expireDate));

const keyhash = get_keyhash(policyWallet.skey);
if (!keyhash) {
  throw new Error("Unable to get key hash for policy, missing or invalid skey");
}
const policyAnvilApi = create_policy_script(keyhash, slot);
```

{% endcode %}

### Collection configurations

This example requires 2 rules.

{% code overflow="wrap" %}

```typescript
const policyAnvilApiScript = {
  type: "all",
  scripts: [
    {
      type: "sig",
      keyHash: keyhash.to_hex(),
    },
    {
      type: "before",
      slot: slot,
    },
  ],
};
```

{% endcode %}

Meaning that the policy has to be signed by the wallet defined in the policy wallet path parameter AND all mutations must be done before the DateTime defined.

### Create Metadata

This function is where you have to define your own data and configuration per asset.

CIP-25 enforces few fields as mandatory see here: <https://cips.cardano.org/cip/CIP-25>

You can also use our powerful metadata validator: <https://metadraft.io>

**For example**

{% code overflow="wrap" %}

```typescript
const assets: {
  version: string;
  assetName: string;
  metadata: {
    name: string;
    image: string | string[];
    mediaType: string;
    description: string;
    epoch: number;
  };
  policyId: string;
  quantity: 1;
}[] = [];
const assetMetadataTemplate = JSON.parse(
  Deno.readTextFileSync(args.metadataTemplatePath)
);
const counter = args.counter;

// Simulate use case
assets.push({
  version: "cip25",
  assetName: `anvilapicip25_${counter}`,
  metadata: {
    ...assetMetadataTemplate,
    // Adding custom data just to test the flow
    name: `anvil-api-${counter}`,
    epoch: new Date().getTime(), // dummy data
  },
  policyId: get_policy_id(policyAnvilApi.mint_script),
  quantity: 1,
});
```

{% endcode %}

The counter is used to be sure that the `assetName` remains unique.

<mark style="color:red;">TBD: Show an example output (screenshot or link to preprod url or all of them)</mark>

### The transaction

{% code overflow="wrap" %}

```typescript
const data = {
  changeAddress: customerWallet.enterprise_address_preprod,
  mint: assets,
  preloadedScripts: [
    {
      type: "simple",
      script: policyAnvilApiScript,
      hash: get_policy_id(policyAnvilApi.mint_script),
    },
  ],
};

const urlTX =
  "https://preprod.api.ada-anvil.app/v2/services/transactions/build";
const transactionToSignWithPolicyKey = await fetch(urlTX, {
  method: "POST",
  body: JSON.stringify(data),
  headers: {
    "Content-Type": "application/json",
    "X-Api-Key": "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9",
  },
}).then((res) => res.json());
```

{% endcode %}

### Sign with the policy wallet

{% code overflow="wrap" %}

```typescript
// Sign transaction with policy key
const transactionToSignWithCustomerKey = FixedTransaction.from_bytes(
  Buffer.from(transactionToSignWithPolicyKey.complete, "hex")
);
transactionToSignWithCustomerKey.sign_and_add_vkey_signature(
  PrivateKey.from_bech32(policyWallet.skey)
);
```

{% endcode %}

### Sign with the customer wallet

*usually done using the browser extension*

{% code overflow="wrap" %}

```typescript
const txToSubmitOnChain = FixedTransaction.from_bytes(
  Buffer.from(transactionToSignWithCustomerKey.to_hex(), "hex")
);
// This sign the tx and add vkeys to the txToSubmitOnChain, so in submit we don't need to provide signautres
txToSubmitOnChain.sign_and_add_vkey_signature(
  PrivateKey.from_bech32(customerWallet.skey)
);
```

{% endcode %}

### Submit Transaction

{% code overflow="wrap" %}

```typescript
if (args.submit) {
  const urlSubmit =
    "https://preprod.api.ada-anvil.app/v2/services/transactions/submit";

  const submitted = await fetch(urlSubmit, {
    method: "POST",
    body: JSON.stringify({
      signatures: [], // This empty because the txToSubmitOnChain has the vkeys
      transaction: txToSubmitOnChain.to_hex(),
    }),
    headers: {
      "Content-Type": "application/json",
      "X-Api-Key": "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9",
    },
  }).then((res) => res.json());

  console.debug(submitted);
} else {
  console.log(txToSubmitOnChain.to_hex());
}
```

{% endcode %}

***

### Using the CLI

```sh
deno compile --allow-read --allow-write --allow-net index.ts
```

*Dont forget to increase the counter, otherwise you will double mint.*

```sh
./index --expireDate=2025-01-01 \
  --customerWalletPath=customer.json \
  --policyWalletPath=policy.json \
  --metadataTemplatePath=metatemplate.json \
  --counter=2 \
  --submit
```

<details>

<summary>File Content - (customer.json, policy.json, metatemplate.json)</summary>

**customer.json**

{% code overflow="wrap" %}

```json
{
  "skey": "REDACTED",
  "skey_hex": "REDACTED",
  "pkey": "REDACTED",
  "pkey_hex": "REDACTED",
  "key_hash": "REDACTED",
  "base_address_preview": "addr_test1qqc0f6py9qeyp5k2vme7pxct0t0ut5mczmq8yfumx8cfpfx7kdf62usuu92g463pyumktcckmd9x7nmrvfxc9w04dnfqz7ycf9",
  "base_address_preprod": "addr_test1qqc0f6py9qeyp5k2vme7pxct0t0ut5mczmq8yfumx8cfpfx7kdf62usuu92g463pyumktcckmd9x7nmrvfxc9w04dnfqz7ycf9",
  "base_address_mainnet": "addr1qyc0f6py9qeyp5k2vme7pxct0t0ut5mczmq8yfumx8cfpfx7kdf62usuu92g463pyumktcckmd9x7nmrvfxc9w04dnfqpgec96",
  "enterprise_address_mainnet": "addr1vyc0f6py9qeyp5k2vme7pxct0t0ut5mczmq8yfumx8cfpfq5xuxsn",
  "enterprise_address_preview": "addr_test1vqc0f6py9qeyp5k2vme7pxct0t0ut5mczmq8yfumx8cfpfq0wg6lk",
  "enterprise_address_preprod": "addr_test1vqc0f6py9qeyp5k2vme7pxct0t0ut5mczmq8yfumx8cfpfq0wg6lk",
  "reward_address_mainnet": "stake1u80tx5a9wgwwz4y2agsjwdm9uvtdkjn0fa3kynvzh86ke5se4shkt",
  "reward_address_preview": "stake_test1ur0tx5a9wgwwz4y2agsjwdm9uvtdkjn0fa3kynvzh86ke5s7l64jk",
  "reward_address_preprod": "stake_test1ur0tx5a9wgwwz4y2agsjwdm9uvtdkjn0fa3kynvzh86ke5s7l64jk",
  "mnemonic": "REDACTED"
}
```

{% endcode %}

**policy.json**

{% code overflow="wrap" %}

```json
{
  "skey": "REDACTED",
  "skey_hex": "REDACTED",
  "pkey": "REDACTED",
  "pkey_hex": "REDACTED",
  "key_hash": "REDACTED",
  "base_address_preview": "addr_test1qzmdaaddmhut3e3kyjsv5f3nsws0mcxk7tu2gpwql8f8pfschsxexl9yakag0rdpf7m4rgg4dvlwuzklghq7trdr4qkqpckdye",
  "base_address_preprod": "addr_test1qzmdaaddmhut3e3kyjsv5f3nsws0mcxk7tu2gpwql8f8pfschsxexl9yakag0rdpf7m4rgg4dvlwuzklghq7trdr4qkqpckdye",
  "base_address_mainnet": "addr1qxmdaaddmhut3e3kyjsv5f3nsws0mcxk7tu2gpwql8f8pfschsxexl9yakag0rdpf7m4rgg4dvlwuzklghq7trdr4qkqzwtdgx",
  "enterprise_address_mainnet": "addr1vxmdaaddmhut3e3kyjsv5f3nsws0mcxk7tu2gpwql8f8pfsc3x6e8",
  "enterprise_address_testnet": "addr_test1vzmdaaddmhut3e3kyjsv5f3nsws0mcxk7tu2gpwql8f8pfsrejxkz",
  "reward_address_mainnet": "stake1uyvtcrvn0jjwmw583ks5ld635y2kk0hwpt05ts093k36stqfn5cxe",
  "reward_address_testnet": "stake_test1uqvtcrvn0jjwmw583ks5ld635y2kk0hwpt05ts093k36stqwe76zy",
  "mnemonic": "REDACTED"
}
```

{% endcode %}

**metatemplate.json**

{% code overflow="wrap" %}

```json
{
  "name": "TBD",
  "image": [
    "https://ada-anvil.s3.ca-central-1.amazonaws.com/",
    "logo_pres_V2_3.png"
  ],
  "mediaType": "image/png",
  "description": "Testing CIP-25 using anvil API"
}
```

{% endcode %}

*The code will override the name key*

</details>

## Explorer

* <https://preprod.cexplorer.io/policy/fa16f906cb86e6b147683c0bede27215b85550f8b91a3f677292eabb>

## The Whole File (Deno Version)

<details>

<summary>index.ts</summary>

{% code overflow="wrap" %}

```typescript
import { Buffer } from "node:buffer";
import {
  Credential,
  type Ed25519KeyHash,
  FixedTransaction,
  NativeScript,
  NativeScripts,
  PrivateKey,
  ScriptAll,
  ScriptPubkey,
  TimelockExpiry,
} from "npm:@emurgo/cardano-serialization-lib-nodejs@13.2.0";
import { parseArgs } from "jsr:@std/cli/parse-args";

const args: {
  _: [];
  expireDate: string;
  customerWalletPath: string;
  policyWalletPath: string;
  metadataTemplatePath: string;
  counter: number;
  submit: boolean;
} = parseArgs(Deno.args);
// deno run -A steps.ts \
//  --expireDate=2026-01-01 \
//  --customerWalletPath=customer.json \
//  --policyWalletPath=policy.json \
//  --metadataTemplatePath=metatemplate.json \
//  --counter=20250117 \
//  --submit

const timeToSlot = (date: Date) => {
  return Math.floor(date.getTime() / 1000) - 1596491091 + 4924800;
};

export function get_keyhash(private_key: string): Ed25519KeyHash | undefined {
  return Credential.from_keyhash(
    PrivateKey.from_bech32(private_key).to_public().hash()
  ).to_keyhash();
}

export function create_policy_script(
  policy_key_hash: Ed25519KeyHash,
  ttl: number,
  with_timelock = true
): { mint_script: NativeScript; policy_ttl: number } {
  const scripts = NativeScripts.new();
  const key_hash_script = NativeScript.new_script_pubkey(
    ScriptPubkey.new(policy_key_hash)
  );
  scripts.add(key_hash_script);

  const policy_ttl: number = ttl;

  if (with_timelock) {
    const timelock = TimelockExpiry.new(policy_ttl);
    const timelock_script = NativeScript.new_timelock_expiry(timelock);
    scripts.add(timelock_script);
  }

  const mint_script = NativeScript.new_script_all(ScriptAll.new(scripts));

  return { mint_script, policy_ttl };
}

export function bytes_to_hex(input: Uint8Array): string {
  return Buffer.from(input).toString("hex");
}

export function get_policy_id(mint_script: NativeScript): string {
  return bytes_to_hex(mint_script.hash().to_bytes());
}

// NOTE: Be sure to send ADA in this address, it will be used to pay the tx fee.
const customerWallet = JSON.parse(
  Deno.readTextFileSync(args.customerWalletPath)
);
const policyWallet = JSON.parse(Deno.readTextFileSync(args.policyWalletPath));

// DateTime before you can interact with the policy
const slot = timeToSlot(new Date(args.expireDate));
const keyhash = get_keyhash(policyWallet.skey);
if (!keyhash) {
  throw new Error("Unable to get key hash for policy, missing or invalid skey");
}
const policyAnvilApi = create_policy_script(keyhash, slot);

//
// CUSTOM FOR EACH MINT COLLECTION
//
// not the best way to handle this, a config file would be better
const policyAnvilApiScript = {
  type: "all",
  scripts: [
    {
      type: "sig",
      keyHash: keyhash.to_hex(),
    },
    {
      type: "before",
      slot: slot,
    },
  ],
};

const assets: {
  version: string;
  assetName: string;
  metadata: {
    name: string;
    image: string | string[];
    mediaType: string;
    description: string;
    epoch: number;
  };
  policyId: string;
  quantity: 1;
}[] = [];
const assetMetadataTemplate = JSON.parse(
  Deno.readTextFileSync(args.metadataTemplatePath)
);
const counter = args.counter;

// Simulate use case
assets.push({
  version: "cip25",
  assetName: `anvilapicip25_${counter}`,
  metadata: {
    ...assetMetadataTemplate,
    // Adding custom data just to test the flow
    name: `anvil-api-${counter}`,
    epoch: new Date().getTime(), // dummy data
  },
  policyId: get_policy_id(policyAnvilApi.mint_script),
  quantity: 1,
});

//
// Generic calls to create, sign and submit tx
//
const data = {
  changeAddress: customerWallet.enterprise_address_preprod,
  mint: assets,
  preloadedScripts: [
    {
      type: "simple",
      script: policyAnvilApiScript,
      hash: get_policy_id(policyAnvilApi.mint_script),
    },
  ],
};

const urlTX =
  "https://preprod.api.ada-anvil.app/v2/services/transactions/build";
const transactionToSignWithPolicyKey = await fetch(urlTX, {
  method: "POST",
  body: JSON.stringify(data),
  headers: {
    "Content-Type": "application/json",
    "X-Api-Key": "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9",
  },
}).then((res) => res.json());

console.debug(
  "transactionToSignWithPolicyKey: ",
  transactionToSignWithPolicyKey
);
//
// policy signature
//
const transactionToSignWithCustomerKey = FixedTransaction.from_bytes(
  Buffer.from(transactionToSignWithPolicyKey.complete, "hex")
);
transactionToSignWithCustomerKey.sign_and_add_vkey_signature(
  PrivateKey.from_bech32(policyWallet.skey)
);

console.debug(
  "transactionToSignWithCustomerKey: ",
  transactionToSignWithCustomerKey.to_hex()
);

//
// customer signature
//
const txToSubmitOnChain = FixedTransaction.from_bytes(
  Buffer.from(transactionToSignWithCustomerKey.to_hex(), "hex")
);
console.debug("Customer Wallet", customerWallet);
// This sign the tx and add vkeys to the txToSubmitOnChain, so in submit we don't need to provide signautres
txToSubmitOnChain.sign_and_add_vkey_signature(
  PrivateKey.from_bech32(customerWallet.skey)
);

console.debug("txToSubmitOnChain: ", txToSubmitOnChain.to_hex());
console.debug(
  "txToSubmitOnChain JSON: ",
  txToSubmitOnChain.witness_set().to_json()
);

//
// Submit tx
//
if (args.submit) {
  const urlSubmit =
    "https://preprod.api.ada-anvil.app/v2/services/transactions/submit";

  const submitted = await fetch(urlSubmit, {
    method: "POST",
    body: JSON.stringify({
      signatures: [], // This empty because the txToSubmitOnChain has the vkeys
      transaction: txToSubmitOnChain.to_hex(),
    }),
    headers: {
      "Content-Type": "application/json",
      "X-Api-Key": "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9",
    },
  }).then((res) => res.json());

  console.debug(submitted);
} else {
  console.log(txToSubmitOnChain.to_hex());
}
```

{% endcode %}

</details>


# 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](/developer-tools/utility-functions)
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...`)

{% hint style="info" %}
**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](/developer-tools/wallet-cli) THIS SHOULD BE STORED SECURELY AND NEVER SHARED OR EXPOSED. See the documentation for best practices.
{% endhint %}

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

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

```ts
import { Buffer } from "node:buffer";
import {
  FixedTransaction,
  PrivateKey,
} from "npm:@emurgo/cardano-serialization-lib-nodejs@14.1.1";
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](/developer-tools/utility-functions) 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](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

***

### Full Example Script

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

[Deno Example](https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/cip25.ts)

[HTMX + Hono + Weld Example](https://github.com/Cardano-Forge/anvil-api-examples/tree/main/minting-platform-cip25)

[Next.js + Weld Example](https://github.com/Cardano-Forge/anvil-api-examples/tree/main/nextjs-minting-platform-cip25)


# Delegations

## API Overview

Delegations in Cardano enable users to participate in the network without running infrastructure themselves. Stake pool delegations allow users to earn rewards by supporting the network's security, while DRep delegations empower users to participate in governance decisions. The Anvil API simplifies implementing both delegation types through a unified interface, letting applications offer these essential features without complex blockchain interactions.

| Type   | Purpose                            | Certificate Created |
| ------ | ---------------------------------- | ------------------- |
| `pool` | Delegate to stake pool for rewards | `StakeDelegation`   |
| `drep` | Delegate to DRep for governance    | `VoteDelegation`    |

## API Endpoint

**URL**: `https://preprod.api.ada-anvil.app/v2/services/transactions/build`\
**Method**: POST\
**Headers**: `Content-Type: application/json`, `x-api-key: YOUR_API_KEY`

## Request Format

```json
{
  "changeAddress": "addr_test1...", 
  "delegations": [
    {
      "type": "pool" | "drep",
      "address": "addr_test1...",
      "keyHash": "pool_id_or_drep_id"
    }
  ]
}
```

## Parameters

| Parameter       | Description                       | Required            |
| --------------- | --------------------------------- | ------------------- |
| `type`          | Either `"pool"` or `"drep"`       | Yes                 |
| `address`       | User's full address               | Yes                 |
| `keyHash`       | Pool ID or DRep ID                | Yes                 |
| `changeAddress` | Address for change                | Yes                 |
| `utxos`         | Array of UTXOs in CBOR hex format | Yes (in production) |

## Implementation Guides

{% content-ref url="/pages/9N5ko1j3NXCrFh8NoX5C" %}
[Delegate to a DRep](/guides/delegations/delegate-to-a-drep)
{% endcontent-ref %}

{% content-ref url="/pages/nph9znb6kA1L9wLPrwd0" %}
[Delegate to a Stake Pool](/guides/delegations/delegate-to-a-pool)
{% endcontent-ref %}

## Transaction Workflow

1. **Prepare**: Include delegation certificates in transaction
2. **Build & Sign**: Process transaction with proper certificates and signatures
3. **Submit**: Send to network (changes effective after current epoch)

## Best Practices

* **Verification**: Confirm pool/DRep IDs before delegation
* **Testing**: Use testnet before mainnet operations
* **Error Handling**: Implement robust handling for:
  * Invalid address formats
  * Unknown pool/DRep IDs
  * Insufficient funds
  * Missing signers

## Related Resources

* [Signing Transactions](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/transaction/signing-transaction.md)
* [Submitting Transactions](/guides/transaction/submit-transaction)


# Delegate to a DRep

DRep delegation with Anvil API

DRep (Delegation Representative) delegation enables users to participate in Cardano's governance system by assigning their voting power to chosen representatives. By integrating DRep delegation into your application, you empower users to influence the blockchain's future without requiring them to actively participate in every governance decision.

## API Endpoint

**URL**: `https://preprod.api.ada-anvil.app/v2/services/transactions/build`\
**Method**: POST\
**Headers**: `Content-Type: application/json`, `x-api-key: YOUR_API_KEY`

## Request Format

```json
{
  "changeAddress": "addr_test1...",
  "utxos": ["8282...", "8282..."],
  "delegations": [
    {
      "type": "drep",
      "address": "addr_test1...",
      "keyHash": "drep13d6sxkyz6st9h65qqrzd8ukpywhr8swe9f6357qntgjqye0gttd"
    }
  ]
}
```

## Parameter Details

| Parameter | Description                       | Example                  |
| --------- | --------------------------------- | ------------------------ |
| `type`    | Must be `"drep"`                  | `"drep"`                 |
| `address` | User's full address               | `"addr_test1..."`        |
| `keyHash` | DRep ID to delegate to            | `"drep13d6s..."`         |
| `utxos`   | Array of UTXOs in CBOR hex format | `["8282...", "8282..."]` |

## Implementation Examples

**Using Fetch API:**

{% content-ref url="/pages/91YG6z42M04LInALjboG" %}
[Deno & Fetch](/guides/delegations/delegate-to-a-drep/deno-and-fetch)
{% endcontent-ref %}

**Using cURL:**

{% content-ref url="/pages/s4ycPbUoRrJNj7KPhj0x" %}
[Bash & cURL](/guides/delegations/delegate-to-a-drep/bash-and-curl)
{% endcontent-ref %}

## Transaction Signing

After building the transaction, it must be signed before submission:

{% content-ref url="<https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/transaction/signing-transaction.md>" %}
<https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/transaction/signing-transaction.md>
{% endcontent-ref %}

## User Interface Examples

<figure><img src="/files/aciYKy4GbwODcmxC7y2c" alt="Eternl Vote Delegation"><figcaption><p>Eternl's DRep delegation interface provides a good reference</p></figcaption></figure>


# Bash & cURL

### Prepare Delegation Transaction

**Payload**

{% code overflow="wrap" %}

```json
{
  "changeAddress": "addr...",
  "delegations": [
    {
      "type": "drep",
      "address": "addr..",
      "keyHash": "drep..."
    }
  ]
}
```

{% endcode %}

**Example**

{% code overflow="wrap" %}

```sh
DREP_ID="drep13d6sxkyz6st9h65qqrzd8ukpywhr8swe9f6357qntgjqye0gttd"
ADDR="addr_test1qztayr885vqrx6w0j946lvtxl622flxx4asj2z4ludm3y2rewu7hmazv8tm78tvphzlream22pp6zhk0rrsa84nf6qxsrua9nh"
# See Authentication page for API key details.
X_API_KEY="testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9"
```

{% endcode %}

{% code overflow="wrap" %}

```sh
curl -X POST \
     -H "Content-Type: application/json" \
     -H "X-Api-Key: ${X_API_KEY}" \
     -d '{
           "changeAddress": "'${ADDR}'",
           "delegations": [
             {
               "type": "drep",
               "address": "'${ADDR}'",
               "keyHash": "'${DREP_ID}'"
             }
           ]
         }' \
     https://preprod.api.ada-anvil.app/v2/services/transactions/build
```

{% endcode %}

**Output**

{% code overflow="wrap" %}

```json
{
  "complete": "84a600d90102818258200dbd6bf8170148df5ed6ba8722bbd3bc69e3a825db811413c4461b947923ab830001818258390097d20ce7a3003369cf916bafb166fe94a4fcc6af61250abfe377122879773d7df44c3af7e3ad81b8be3cf76a5043a15ecf18e1d3d669d00d1a0d2fd63e021a0003e36d031a04e05acb04d901028183098200581c79773d7df44c3af7e3ad81b8be3cf76a5043a15ecf18e1d3d669d00d8200581c8b75035882d4165bea8000c4d3f2c123ae33c1d92a751a78135a24020ed9010281581c79773d7df44c3af7e3ad81b8be3cf76a5043a15ecf18e1d3d669d00da0f5f6",
  "stripped": "84a600d90102818258200dbd6bf8170148df5ed6ba8722bbd3bc69e3a825db811413c4461b947923ab830001818258390097d20ce7a3003369cf916bafb166fe94a4fcc6af61250abfe377122879773d7df44c3af7e3ad81b8be3cf76a5043a15ecf18e1d3d669d00d1a0d2fd63e021a0003e36d031a04e05acb04d901028183098200581c79773d7df44c3af7e3ad81b8be3cf76a5043a15ecf18e1d3d669d00d8200581c8b75035882d4165bea8000c4d3f2c123ae33c1d92a751a78135a24020ed9010281581c79773d7df44c3af7e3ad81b8be3cf76a5043a15ecf18e1d3d669d00da0f5f6",
  "witnessSet": "a0"
}
```

{% endcode %}


# Deno & Fetch

Using Deno & fetch to delegate to a DRep.

## Prepare Delegation Transaction

**Payload**

{% code overflow="wrap" %}

```json
{
  "changeAddress": "addr...",
  "delegations": [
    {
      "type": "drep",
      "address": "addr..",
      "keyHash": "drep..."
    }
  ]
}
```

{% endcode %}

### Example (using Deno & fetch)

*Using a preprod wallet all value needed for a delegation*

{% code overflow="wrap" %}

```typescript
const DREP_ID = "drep13d6sxkyz6st9h65qqrzd8ukpywhr8swe9f6357qntgjqye0gttd";
const ADDR = "addr_test1qq7fc3ke49nkcsfglltut7apa9t3gdul4utwhxt6j2hdrw7pg4vk6erdshyhdj5xeq0vh8qdy34cpdfstvc8l9su8hgq679eew";
// See Authentication page for API key details.
const X_API_KEY = "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9";
```

{% endcode %}

#### API POST Request Body

*Body Structure for a delegation to a DRep using the previously collected values.*

```typescript
const BODY = {
  changeAddress: ADDR,
  delegations: [
    {
      type: "drep",
      address: ADDR,
      keyHash: DREP_ID,
    },
  ],
};
```

#### Fetch Command with Deno

POST call with Fetch

{% code overflow="wrap" %}

```typescript
const response = await fetch(
  `https://preprod.api.ada-anvil.app/v2/services/transactions/build`,
  {
    method: "POST",
    headers: { "Content-Type": "application/json", "x-api-key": X_API_KEY },
    body: JSON.stringify(BODY),
  }
);

console.log(await response.json());
```

{% endcode %}

### Deno Command

```bash
deno run --allow-net delegate-drep.ts
```

### Output

{% code overflow="wrap" %}

```json
{
  "hash": "309afc6f9e06e70a0431eb929f4bacdfff463a0879c5d7daaef3357a4e6dbf64",
  "complete": "84a600d9010281825820f115e0c125b4023379833df656b5a5535d715f3eabb31e5bd5697d2604866b46000181825839003c9c46d9a9676c4128ffd7c5fba1e95714379faf16eb997a92aed1bbc145596d646d85c976ca86c81ecb9c0d246b80b5305b307f961c3dd0821a0ea25013a1581c360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13a1581b616e76696c61706963697032355f3137333838363236313738393001021a0003f419031a04f5649904d901028282008200581cc145596d646d85c976ca86c81ecb9c0d246b80b5305b307f961c3dd083098200581cc145596d646d85c976ca86c81ecb9c0d246b80b5305b307f961c3dd08200581c8b75035882d4165bea8000c4d3f2c123ae33c1d92a751a78135a24020ed9010281581cc145596d646d85c976ca86c81ecb9c0d246b80b5305b307f961c3dd0a0f5f6",
  "stripped": "84a600d9010281825820f115e0c125b4023379833df656b5a5535d715f3eabb31e5bd5697d2604866b46000181825839003c9c46d9a9676c4128ffd7c5fba1e95714379faf16eb997a92aed1bbc145596d646d85c976ca86c81ecb9c0d246b80b5305b307f961c3dd0821a0ea25013a1581c360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13a1581b616e76696c61706963697032355f3137333838363236313738393001021a0003f419031a04f5649904d901028282008200581cc145596d646d85c976ca86c81ecb9c0d246b80b5305b307f961c3dd083098200581cc145596d646d85c976ca86c81ecb9c0d246b80b5305b307f961c3dd08200581c8b75035882d4165bea8000c4d3f2c123ae33c1d92a751a78135a24020ed9010281581cc145596d646d85c976ca86c81ecb9c0d246b80b5305b307f961c3dd0a0f5f6",
  "witnessSet": "a0"
}
```

{% endcode %}

## The Whole File (Deno Version)

<details>

<summary>delegate-drep.ts</summary>

{% code overflow="wrap" %}

```typescript
const DREP_ID = "drep13d6sxkyz6st9h65qqrzd8ukpywhr8swe9f6357qntgjqye0gttd";
const ADDR = "addr_test1qq7fc3ke49nkcsfglltut7apa9t3gdul4utwhxt6j2hdrw7pg4vk6erdshyhdj5xeq0vh8qdy34cpdfstvc8l9su8hgq679eew";
// See Authentication page for API key details.
const X_API_KEY = "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9";

const BODY = {
  changeAddress: ADDR,
  delegations: [
    {
      type: "drep",
      address: ADDR,
      keyHash: DREP_ID,
    },
  ],
};

const response = await fetch(
  `https://preprod.api.ada-anvil.app/v2/services/transactions/build`,
  {
    method: "POST",
    headers: { "Content-Type": "application/json", "x-api-key": X_API_KEY },
    body: JSON.stringify(BODY),
  }
);

console.log(await response.json());

export {};
```

{% endcode %}

</details>


# Delegate to a Stake Pool

Stake pool delegation with Anvil API

Stake pool delegation allows users to participate in Cardano's consensus protocol by delegating their stake to a pool that produces blocks. By integrating stake pool delegation into your application, you enable users to earn rewards while supporting the network's security and decentralization without requiring technical expertise in running validator nodes.

## API Endpoint

**URL**: `https://preprod.api.ada-anvil.app/v2/services/transactions/build`\
**Method**: POST\
**Headers**: `Content-Type: application/json`, `x-api-key: YOUR_API_KEY`

## Request Format

```json
{
  "changeAddress": "addr_test1...",
  "utxos": ["8282...", "8282..."],
  "delegations": [
    {
      "type": "pool",
      "address": "addr_test1...",
      "keyHash": "pool1z5uq..."
    }
  ]
}
```

## Parameter Details

| Parameter | Description                       | Example                  |
| --------- | --------------------------------- | ------------------------ |
| `type`    | Must be `"pool"`                  | `"pool"`                 |
| `address` | User's full address               | `"addr_test1..."`        |
| `keyHash` | Pool ID to delegate to            | `"pool1z5uq..."`         |
| `utxos`   | Array of UTXOs in CBOR hex format | `["8282...", "8282..."]` |

## Implementation Examples

**Using Fetch API:**

{% content-ref url="/pages/qfEgD3KXAoD2Ew1fpSEa" %}
[Deno & Fetch](/guides/delegations/delegate-to-a-pool/deno-and-fetch)
{% endcontent-ref %}

**Using cURL:**

{% content-ref url="/pages/HAfa7mrbIDsZxHgxDIwG" %}
[Bash & cURL](/guides/delegations/delegate-to-a-pool/bash-and-curl)
{% endcontent-ref %}

## Transaction Signing

After building the transaction, it must be signed before submission:

{% content-ref url="<https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/transaction/signing-transaction.md>" %}
<https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/transaction/signing-transaction.md>
{% endcontent-ref %}

## User Interface Examples

<figure><img src="/files/QRdFFC8RGHlcMyqIVlMI" alt="Stake Pool Delegation"><figcaption><p>Eternl's Stake Pool delegation interface provides a good reference</p></figcaption></figure>


# Bash & cURL

### Prepare Pool Delegation Transaction

**Payload**

{% code overflow="wrap" %}

```json
{
  "changeAddress": "addr_test1...",
  "delegations": [
    {
      "type": "pool",
      "address": "addr_test1...",
      "keyHash": "pool1z5uq..."
    }
  ]
}
```

{% endcode %}

**Example**

{% code overflow="wrap" %}

```sh
POOL_ID="pool1n3sjq3qvu5vvcd6aud6ndcwq7r3ghmkafcg60gznlwfrk2ucxku"
ADDR="addr_test1qzyttcj6czjltcs3tn3vls6yg90542lctrzwg5aagduqlfgupztxhczzmuakzfuwvrht542yrx7ll3fk29lcl2xl8axqh3pjdh"
# See Authentication page for API key details.
X_API_KEY="testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9"
```

{% endcode %}

{% code overflow="wrap" %}

```sh
curl -X POST \
     -H "Content-Type: application/json" \
     -H "X-Api-Key: ${X_API_KEY}" \
     -d '{
           "changeAddress": "'${ADDR}'",
           "delegations": [
             {
               "type": "pool",
               "address": "'${ADDR}'",
               "keyHash": "'${POOL_ID}'"
             }
           ]
         }' \
     https://preprod.api.ada-anvil.app/v2/services/transactions/build
```

{% endcode %}

**Output**

{% code overflow="wrap" %}

```json
{
  "hash": "a4633c716755a8d72d07c058ffabe94c9b8b0f8273a8d7babb1f607050fb3c3b",
  "complete": "84a700d90102828258203376a58b03a1a26108d79e3db28207d1e3b7db51266bb9d07691b3d724d8198201825820f4bf2eb3572ec503081a24baf506ee26f5a2ee8d5d6c707830b75516db9bb9ed020182a300581d60355efb09c4a29c7e3b63ead47a220efcbbdef1fd5369c4efff947b4b011a001dc130028201d8184a49616e76696c2d7461678258390088b5e25ac0a5f5e2115ce2cfc344415f4aabf858c4e453bd43780fa51c08966be042df3b61278e60eeba554419bdffc536517f8fa8df3f4c1a38227e99021a00032095031a053f215904d901028282008200581c1c08966be042df3b61278e60eeba554419bdffc536517f8fa8df3f4c83028200581c1c08966be042df3b61278e60eeba554419bdffc536517f8fa8df3f4c581c9c6120440ce518cc375de37536e1c0f0e28beedd4e11a7a053fb923b081a053f05390ed9010281581c355efb09c4a29c7e3b63ead47a220efcbbdef1fd5369c4efff947b4ba100d90102818258204bc7618201307ab45cdb3793107822c47230f097e9771fb358473cdb82b2e644584020ecc8e8a0b6d2f4a2bd51d388ee20abebf4982d55ce738d9b1c223bc77152fa175c15729e96073873d262017ea113ff41a8ec26a16441029f00fbf004229b07f5f6",
  "stripped": "84a700d90102828258203376a58b03a1a26108d79e3db28207d1e3b7db51266bb9d07691b3d724d8198201825820f4bf2eb3572ec503081a24baf506ee26f5a2ee8d5d6c707830b75516db9bb9ed020182a300581d60355efb09c4a29c7e3b63ead47a220efcbbdef1fd5369c4efff947b4b011a001dc130028201d8184a49616e76696c2d7461678258390088b5e25ac0a5f5e2115ce2cfc344415f4aabf858c4e453bd43780fa51c08966be042df3b61278e60eeba554419bdffc536517f8fa8df3f4c1a38227e99021a00032095031a053f215904d901028282008200581c1c08966be042df3b61278e60eeba554419bdffc536517f8fa8df3f4c83028200581c1c08966be042df3b61278e60eeba554419bdffc536517f8fa8df3f4c581c9c6120440ce518cc375de37536e1c0f0e28beedd4e11a7a053fb923b081a053f05390ed9010281581c355efb09c4a29c7e3b63ead47a220efcbbdef1fd5369c4efff947b4ba0f5f6",
  "witnessSet": "a100d90102818258204bc7618201307ab45cdb3793107822c47230f097e9771fb358473cdb82b2e644584020ecc8e8a0b6d2f4a2bd51d388ee20abebf4982d55ce738d9b1c223bc77152fa175c15729e96073873d262017ea113ff41a8ec26a16441029f00fbf004229b07"
}
```

{% endcode %}


# Deno & Fetch

Using Deno & fetch to delegate to a stake pool.

## Prepare Pool Delegation Transaction

**Payload**

{% code overflow="wrap" %}

```json
{
  "changeAddress": "addr_test1...",
  "delegations": [
    {
      "type": "pool",
      "address": "addr_test1...",
      "keyHash": "pool1z5uq..."
    }
  ]
}
```

{% endcode %}

### Example (using Deno & fetch)

*Using a preprod wallet all value needed for a delegation*

{% code overflow="wrap" %}

```typescript
const POOL_ID = "pool1n3sjq3qvu5vvcd6aud6ndcwq7r3ghmkafcg60gznlwfrk2ucxku";
const ADDR = "addr_test1qzyttcj6czjltcs3tn3vls6yg90542lctrzwg5aagduqlfgupztxhczzmuakzfuwvrht542yrx7ll3fk29lcl2xl8axqh3pjdh";
// See Authentication page for API key details.
const X_API_KEY = "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9";
```

{% endcode %}

#### API POST Request Body

*Body Structure for a delegation to a pool using the previously collected values.*

```typescript
const BODY = {
  changeAddress: ADDR,
  delegations: [
    {
      type: "pool",
      address: ADDR,
      keyHash: POOL_ID,
    },
  ],
};
```

#### Fetch Command with Deno

POST call with Fetch

{% code overflow="wrap" %}

```typescript
const response = await fetch(
  `https://preprod.api.ada-anvil.app/v2/services/transactions/build`,
  {
    method: "POST",
    headers: { "Content-Type": "application/json", "x-api-key": X_API_KEY },
    body: JSON.stringify(BODY),
  }
);

console.log(await response.json());
```

{% endcode %}

### Deno Command

```bash
deno run --allow-net delegate-pool.ts
```

### Output

{% code overflow="wrap" %}

```json
{
  "hash": "a4633c716755a8d72d07c058ffabe94c9b8b0f8273a8d7babb1f607050fb3c3b",
  "complete": "84a700d90102828258203376a58b03a1a26108d79e3db28207d1e3b7db51266bb9d07691b3d724d8198201825820f4bf2eb3572ec503081a24baf506ee26f5a2ee8d5d6c707830b75516db9bb9ed020182a300581d60355efb09c4a29c7e3b63ead47a220efcbbdef1fd5369c4efff947b4b011a001dc130028201d8184a49616e76696c2d7461678258390088b5e25ac0a5f5e2115ce2cfc344415f4aabf858c4e453bd43780fa51c08966be042df3b61278e60eeba554419bdffc536517f8fa8df3f4c1a38227e99021a00032095031a053f215904d901028282008200581c1c08966be042df3b61278e60eeba554419bdffc536517f8fa8df3f4c83028200581c1c08966be042df3b61278e60eeba554419bdffc536517f8fa8df3f4c581c9c6120440ce518cc375de37536e1c0f0e28beedd4e11a7a053fb923b081a053f05390ed9010281581c355efb09c4a29c7e3b63ead47a220efcbbdef1fd5369c4efff947b4ba100d90102818258204bc7618201307ab45cdb3793107822c47230f097e9771fb358473cdb82b2e644584020ecc8e8a0b6d2f4a2bd51d388ee20abebf4982d55ce738d9b1c223bc77152fa175c15729e96073873d262017ea113ff41a8ec26a16441029f00fbf004229b07f5f6",
  "stripped": "84a700d90102828258203376a58b03a1a26108d79e3db28207d1e3b7db51266bb9d07691b3d724d8198201825820f4bf2eb3572ec503081a24baf506ee26f5a2ee8d5d6c707830b75516db9bb9ed020182a300581d60355efb09c4a29c7e3b63ead47a220efcbbdef1fd5369c4efff947b4b011a001dc130028201d8184a49616e76696c2d7461678258390088b5e25ac0a5f5e2115ce2cfc344415f4aabf858c4e453bd43780fa51c08966be042df3b61278e60eeba554419bdffc536517f8fa8df3f4c1a38227e99021a00032095031a053f215904d901028282008200581c1c08966be042df3b61278e60eeba554419bdffc536517f8fa8df3f4c83028200581c1c08966be042df3b61278e60eeba554419bdffc536517f8fa8df3f4c581c9c6120440ce518cc375de37536e1c0f0e28beedd4e11a7a053fb923b081a053f05390ed9010281581c355efb09c4a29c7e3b63ead47a220efcbbdef1fd5369c4efff947b4ba0f5f6",
  "witnessSet": "a100d90102818258204bc7618201307ab45cdb3793107822c47230f097e9771fb358473cdb82b2e644584020ecc8e8a0b6d2f4a2bd51d388ee20abebf4982d55ce738d9b1c223bc77152fa175c15729e96073873d262017ea113ff41a8ec26a16441029f00fbf004229b07"
}
```

{% endcode %}

## The Whole File (Deno Version)

<details>

<summary>delegate-pool.ts</summary>

{% code overflow="wrap" %}

```typescript
const POOL_ID = "pool1n3sjq3qvu5vvcd6aud6ndcwq7r3ghmkafcg60gznlwfrk2ucxku";
const ADDR = "addr_test1qzj4p30l8kzrm95wmfuqh4spnvpvyjt4lyc8xj2mtl2xtrsqma6nh06158nvt3w9gf2xyl35h5csz5awufc6xtvpdhszkvcml";
// See Authentication page for API key details.
const X_API_KEY = "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9";

const BODY = {
  changeAddress: ADDR,
  delegations: [
    {
      type: "pool",
      address: ADDR,
      keyHash: POOL_ID,
    },
  ],
};

const response = await fetch(
  `https://preprod.api.ada-anvil.app/v2/services/transactions/build`,
  {
    method: "POST",
    headers: { "Content-Type": "application/json", "x-api-key": X_API_KEY },
    body: JSON.stringify(BODY),
  }
);

console.log(await response.json());

export {};
```

{% endcode %}

</details>


# Smart Contracts

Overview of Cardano smart contract capabilities within the Anvil API, including blueprint management and contract interactions.

## Introduction

The Anvil API provides a comprehensive suite of tools for working with Cardano smart contracts, offering a streamlined experience for developers to deploy, interact with, and manage smart contracts without dealing with the underlying complexity of the Cardano blockchain.

> **Prerequisites**: This guide assumes familiarity with basic Cardano concepts and smart contract development principles. If you're new to Cardano smart contracts, we recommend reviewing [Aiken Fundamentals - EUTxO](https://aiken-lang.org/fundamentals/eutxo) before proceeding.

## Where Anvil Fits in the Cardano Smart Contract Ecosystem

Smart contract development and usage follows a lifecycle from code to on-chain interaction. The diagram below illustrates where Anvil API fits in this process:

<figure><img src="/files/bLEy074Wkb6WMm072OuR" alt="Smart Contract Lifecycle"><figcaption><p>Smart Contract Lifecycle</p></figcaption></figure>

As shown in the diagram, Anvil API comes into play after you've written and compiled your smart contract code with Aiken. Our tools focus on:

* **Blueprint Management**: Storing and managing your compiled smart contract blueprints
* **Deployment**: Saving your contracts to the Cardano blockchain
* **Transaction Building**: Creating the simple or complex transactions needed to interact with contracts
* **Script Execution**: Handling the validation and execution logic upon submit

This clear separation of concerns allows you to focus on writing high-quality contract logic while Anvil handles the complexities of blockchain interaction.

## CIP-57 Blueprint Format

The Anvil API works with [CIP-57 Plutus Blueprint](https://cips.cardano.org/cip/CIP-57) format, which provides a standardized way to describe Cardano smart contracts.

### Blueprint Generation with Aiken

Aiken is the recommended smart contract language for Anvil API. It generates CIP-57 compliant blueprints automatically when you build your project:

```bash
# Generate CIP-57 blueprint file with verbose output for debugging
aiken build --verbose
```

The build process creates a `plutus.json` file in your project's `build` directory, which is a complete CIP-57 blueprint that can be directly used with the Anvil API. Using the `--verbose` flag will provide detailed compilation information and help with debugging.

Once you have your blueprint file, you can use the Anvil API to manage and interact with your smart contracts without having to deal with the low-level details of the Cardano blockchain.

## Working with Smart Contracts

Once you have your blueprint file, you can use the Anvil API to manage and interact with your smart contracts. The Anvil API provides several ways to work with Cardano smart contracts:

### Validator Types and Script Purposes

Cardano smart contracts use validators to define the conditions under which certain operations can occur. Understanding validator types is essential for implementing effective smart contracts.

{% content-ref url="/pages/C4kqFOvI4YfKlVqtozKt" %}
[Validators](/guides/smart-contract/validators)
{% endcontent-ref %}

### Blueprint Management

Learn how to store, retrieve, update, and delete your CIP-57 blueprints using the Anvil API.

{% content-ref url="/pages/fLloQsEjlrFZ3GccPWFy" %}
[Blueprint Management (CIP-57)](/guides/smart-contract/blueprint-management)
{% endcontent-ref %}

## Example Smart Contract Interactions

To help you get started with smart contracts on Cardano, we provide several example implementations:

### Hello World Smart Contract

A simple smart contract example to help you understand the basics.

{% content-ref url="/pages/EpkvVH55nFmbWpa1IkKS" %}
[Hello World Smart Contract](/guides/smart-contract/hello-world-smart-contract)
{% endcontent-ref %}

### Minting Tokens with Smart Contracts

Learn how to create and manage tokens using mint validators.

{% content-ref url="/pages/sIDc3mGeCPs5eUafjqfo" %}
[Mint with Smart Contract](/guides/smart-contract/mint-smart-contract)
{% endcontent-ref %}

### Escrow Smart Contract

A complete end-to-end example implementing an escrow system with Next.js, Weld, and Blockfrost Webhooks for real-time updates.

{% content-ref url="/pages/njTW7bu9BDbOptiklPpv" %}
[End-to-End Next.js Escrow Guide](/guides/smart-contract/escrow)
{% endcontent-ref %}


# Blueprint Management (CIP-57)

Guide to managing smart contract blueprints with the Anvil API - creating, finding, and deleting blueprints.

## Overview

The Anvil API provides powerful endpoints for managing Cardano smart contract blueprints. These endpoints allow you to create, retrieve, update, and delete CIP-57 compliant blueprints in a standardized way. This is usually the compiled `plutus.json` file from an Aiken build.

{% hint style="warning" %}
**SECURITY NOTE**

All blueprint operations require an API key, which serves as your authentication credential and determines which blueprints you can access. The system maintains blueprint isolation between users, ensuring that:

* When you create a blueprint, it's associated with your API key
* When you search for blueprints, you only see blueprints created with your API key
* When you delete blueprints, you can only remove blueprints you own

This security model ensures that your smart contract blueprints remain private to your account while still enabling collaboration through controlled sharing mechanisms.
{% endhint %}

## Blueprint Management API Endpoints

| Endpoint                   | Method | Description                                                                                                                        | Required Parameters                        | Optional Parameters                                      |
| -------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ | -------------------------------------------------------- |
| `/blueprints`              | POST   | Create or update a blueprint                                                                                                       | `blueprint`: CIP-57 compliant blueprint    | `refs`: Reference UTXOs                                  |
| `/blueprints`              | GET    | Find blueprints matching criteria                                                                                                  | None                                       | `title`, `version`, `limit`, `offset`, `sortBy`, `order` |
| `/blueprints`              | DELETE | Remove blueprints                                                                                                                  | `title`: Blueprint title                   | `version`: Blueprint version                             |
| `/blueprints/apply-params` | POST   | Apply parameters to validators - [See Documentation](/guides/smart-contract/smart-contract-utilities#apply-parameters-to-a-script) | `params`: Validator hash to params mapping | `blueprint`: CIP-57 blueprint (if not uploaded)          |

## Common Setup

The following setup is common to all blueprint operations:

```typescript
// Import your blueprint (typically from an Aiken build)
import plutusJson from "../hello-world/plutus.json" with { type: "json" };

// Setup API key and content type
const headers = {
  "x-api-key": "YOUR_API_KEY",
  "Content-Type": "application/json",
};

// Define the API endpoint
const blueprintsUrl = "https://preprod.api.ada-anvil.app/v2/services/blueprints";
```

## Creating or Updating Blueprints

Use the `POST` method to upload a new blueprint or update an existing one. The API automatically determines whether to create or update based on the blueprint's title and version.

```typescript
async function upsertBlueprint() {
  try {
    // Prepare the request body with your CIP-57 blueprint
    const input = { blueprint: plutusJson };
    
    const res = await fetch(blueprintsUrl, {
      method: "POST",
      body: JSON.stringify(input),
      headers,
    });
    
    const json = await res.json();
    
    // The response contains:
    // - jsonSchema: Generated JSON schema from the blueprint
    // - scriptAddresses: Record of validator hashes and their addresses
    // - blueprintId: ID of the created/updated blueprint
    console.log("json", JSON.stringify(json, null, 2));
  } catch (error) {
    console.log("error", error);
  }
}
```

## Finding Blueprints

Use the `GET` method to search for blueprints by title, version, or other criteria.

```typescript
async function findBlueprints() {
  try {
    const url = new URL(blueprintsUrl);
    
    // Optional search parameters
    url.searchParams.set("title", "cardano-forge/vesting-contract"); // Filter by title
    url.searchParams.set("version", "0.0.0"); // Filter by version
    // Additional optional parameters: limit, offset, sortBy, order
    
    const res = await fetch(url, {
      method: "GET",
      headers,
    });
    
    const json = await res.json();
    
    // The response contains:
    // - results: Array of matching blueprints
    // - total: Total count of matching blueprints
    console.log("json", JSON.stringify(json, null, 2));
  } catch (error) {
    console.log("error", error);
  }
}
```

## Deleting Blueprints

Use the `DELETE` method to remove blueprints from the system.

```typescript
async function deleteBlueprint() {
  try {
    const url = new URL(blueprintsUrl);
    
    // Required parameter
    url.searchParams.set("title", "cardano-forge/vesting-contract");
    
    // Optional parameter - if omitted, all versions with the specified title will be deleted
    url.searchParams.set("version", "0.0.0");
    
    const res = await fetch(url, {
      method: "DELETE",
      headers,
    });
    
    const json = await res.json();
    
    // The response contains:
    // - deleted: Array of strings identifying the deleted blueprints
    console.log("json", JSON.stringify(json, null, 2));
  } catch (error) {
    console.log("error", error);
  }
}
```


# Smart Contract Utilities

Learn how to use the Anvil API to interact with smart contract scripts. Endpoints for deriving addresses, applying parameters, and serializing/parsing Plutus data.

Smart Contract Utilities provides endpoints for interacting with Plutus scripts (also known as validators). You can use these endpoints to derive addresses, apply parameters, and handle data serialization.

| Endpoint                            | Description                                                                    |
| ----------------------------------- | ------------------------------------------------------------------------------ |
| `GET /validators/{hash}/address`    | Derives the script address from a given script hash.                           |
| `POST /blueprints/apply-params`     | Applies parameters to a script to generate a new, parameterized script hash.   |
| `POST /validators/{hash}/parse`     | Parses a CBOR hex-encoded datum or redeemer into a human-readable JSON object. |
| `POST /validators/{hash}/serialize` | Serializes a JSON object into a CBOR hex-encoded datum or redeemer string.     |

## Available Endpoints

### Get Script Address

**`GET /validators/{hash}/address`**

Derives the script address from a given script hash.

* **Path Parameters**:
  * `hash` (string): The hex-encoded hash of the Plutus script.
* **Query Parameters**:
  * `stake` (string, optional): A stake key hash to associate with the address.

<details>

<summary>TypeScript Example: Get a script address from its hash</summary>

```typescript
async function getScriptAddress(scriptHash: string) {
  const response = await fetch(`https://preprod.api.ada-anvil.app/v2/services/validators/${scriptHash}/address`, {
    method: 'GET',
    headers: {
      'Content-Type': 'application/json',
      'X-Api-Key': 'YOUR_API_KEY'
    }
  });

  const { hex, bech32 } = await response.json();

  return { hex, bech32 };
}
// Sample output:
// {
//   "hex": "61c9a6c4d47f5c8e...",
//   "bech32": "addr_test1wqsw5u8u7g5m6x0s0qqp9g3zgx9n9ahm0t4105keg3zt0swsgjmzg"
// }

```

</details>

### Apply Parameters to Script

**`POST /blueprints/apply-params`**

Applies a set of parameters to a parameterized script, returning the new script hash and compiled code. See [Hello World Example with Parameters](https://github.com/Cardano-Forge/anvil-api-examples/tree/main/smart-contracts/hello-world/aiken-hello-world-with-params) for an example of a smart contract that uses parameters.

* **Body**:
  * `params` (object): A record mapping validator hashes to arrays of parameters. Each validator hash maps to an array of parameters to apply.
    * Example: `{ "validator_hash_here": [param1, param2, ...] }`
  * `blueprint` (object, optional): Only required if the script is not yet known to Anvil. This is usually your `plutus.json` file from an Aiken build. See the [Blueprint Management](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/guides/smart-contract/blueprint-management.md) guide for more details.

<details>

<summary>TypeScript Example: Apply parameters to a script</summary>

```typescript
async function applyParameters(plutusJson?: object) {
  // Define parameters for each validator that needs them
  const params = {
    // Map validator hash to its parameters
    "333158938654b6e69ce1349aeeadcff7fdc0c285f3102b8697a1e173": [
      "48656c6c6f2c20416e76696c21",  // ByteArray: "Hello, Anvil!" in hex
      // Parameters are applied in order as defined in the blueprint
    ],
    // Multiple validators can be parameterized at once
    "another_validator_hash": [
      "30f4e824283240d2ca66f3e09b0b7adfc5d37816c072279b31f090a4",  // VerificationKeyHash (28 bytes)
      "2c256815c1a9637e624e3264e0e7a0a9a824a303a4cf8dd6c4cf96b5#0"  // UTXO reference (TxId#Index)
    ]
  };

  const response = await fetch(`https://preprod.api.ada-anvil.app/v2/services/blueprints/apply-params`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Api-Key': 'YOUR_API_KEY'
    },
    body: JSON.stringify({
      params,
      blueprint: plutusJson // Your CIP-57 blueprint (optional if already uploaded)
    })
  });

  const result = await response.json();
  const { preloadedScript, addresses } = result;
  
  return { preloadedScript, addresses };
}
// Sample output:
// {
//   "preloadedScript": { 
//     "type": "plutus", 
//     "blueprint": { /* Updated blueprint with applied params */ },
//     "validatorRefs": {}
//   },
//   "addresses": {
//     "72c6923a040389fa08c728fe41f0bb9be1722867aec4067fcc751cef": {
//       "bech32": "addr_test1wpevdy36qspcn7sgcu50us0shwd7zu3gv7hvgpnle363emcu0m8ar",
//       "hex": "7072c6923a040389fa08c728fe41f0bb9be1722867aec4067fcc751cef"
//     }
//   }
// }
```

</details>

### Parse Plutus Data

**`POST /validators/{hash}/parse`**

Parses a hex-encoded Plutus data string (e.g., datum or redeemer) into a structured JSON object based on the script's schema.

* **Path Parameters**:
  * `hash` (string): The hash of the script. The API uses this hash to fetch the validator's schema, which is required to correctly convert the data between JSON and its hex-encoded format.
* **Body**:
  * `type` (string): The type of data to parse (`"datum"` or `"redeemer"`).
  * `purpose` (string): The purpose of the script, which determines the context in which it's validated. Possible values are:
    * `"mint"`: For minting or burning assets.
    * `"spend"`: For spending transaction outputs from the script address.
    * `"withdraw"`: For withdrawing staking rewards.
    * `"publish"`: For publishing delegation certificates.
    * `"vote"`: For voting on governance proposals.
    * `"propose"`: For executing constitution guardrails when submitting governance proposals.
  * `data` (object): An object containing the hex string: `{ "hex": "..." }`.
  * `addressFormat` (string, optional): The desired address format (`"bech32"` or `"raw"`).
  * `blueprint` (object, optional): The CIP-57 blueprint if the script is not yet known to Anvil. See the [Blueprint Management](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/guides/smart-contract/blueprint-management.md) guide for more details.

<details>

<summary>TypeScript Example: Parse a datum from a transaction output</summary>

```typescript
async function parseDatum(scriptHash: string, datumHex: string) {
  const response = await fetch(`https://preprod.api.ada-anvil.app/v2/services/validators/${scriptHash}/parse`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Api-Key': 'YOUR_API_KEY'
    },
    body: JSON.stringify({
      type: "datum",
      purpose: "spend", // or "mint", etc., depending on the script
      data: {
        hex: datumHex
      }
    })
  });

  const result = await response.json();
  
  // Returns the parsed datum as a JSON object
  return result;
}

// The API parses the hex string into a human-readable JSON object based on the validator's schema.
// {
//   "owner": "30f4e824283240d2ca66f3e09b0b7adfc5d37816c072279b31f090a4"
// }

```

</details>

### Serialize Plutus Data

**`POST /validators/{hash}/serialize`**

Serializes a JSON object into a hex-encoded Plutus data string based on the script's schema. The opposite of parse.

* **Path Parameters**:
  * `hash` (string): The hash of the script. The API uses this hash to fetch the validator's schema, which is required to correctly convert the data between JSON and its hex-encoded format.
* **Body**:
  * `type` (string): The type of data to serialize (`"datum"` or `"redeemer"`).
  * `purpose` (string): The purpose of the script, which determines the context in which it's validated. Possible values are:
    * `"mint"`: For minting or burning assets.
    * `"spend"`: For spending transaction outputs from the script address.
    * `"withdraw"`: For withdrawing staking rewards.
    * `"publish"`: For publishing delegation certificates.
    * `"vote"`: For voting on governance proposals.
    * `"propose"`: For executing constitution guardrails when submitting governance proposals.
  * `data` (object): The JSON object to serialize.
  * `blueprint` (object, optional): The CIP-57 blueprint if the script is not yet known to Anvil. See the [Blueprint Management](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/guides/smart-contract/blueprint-management.md) guide for more details.

<details>

<summary>TypeScript Example: Serialize a datum object into hex</summary>

```typescript
// The is the opposite of parse. It takes a JSON object and returns a hex string.
async function serializeDatum(scriptHash: string, datumObject: unknown) {
  const response = await fetch(`https://preprod.api.ada-anvil.app/v2/services/validators/${scriptHash}/serialize`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Api-Key': 'YOUR_API_KEY'
    },
    body: JSON.stringify({
      type: "datum",
      purpose: "spend", // or "mint", etc.
      data: datumObject // A JSON object matching the datum schema defined in your blueprint.
    })
  });

  const result = await response.json();
  
  // Returns the hex-encoded datum string
  return result;
}

// The API serializes the JSON object into a hex-encoded string based on the validator's schema.
// Output: "d8799f581c30f4e824283240d2ca..."
```

</details>


# Validators


# Spend Validator

Detailed guide on spend validators including overview, use cases, and example Anvil API interactions.

## Overview

Spend validators control the conditions under which UTXOs can be spent from script addresses.

## Real-world Use Cases

* **Asset Locking**: Define conditions for when locked assets can be accessed
* **Multi-signature Schemes**: Require approval from multiple parties
* **Time-locked Funds**: Allow access only after certain time conditions
* **Conditional Payments**: Release funds when specific on-chain/off-chain conditions are met

## Required UTxO Fields and Deployed Blueprint

{% hint style="warning" %}
**PREREQUISITE: DEPLOYED SMART CONTRACT**

Before you can interact with any validator, you must have a compiled and deployed smart contract blueprint. The validator hash used in these examples comes from your deployed blueprint.

Refer to the [Blueprint Management](/guides/smart-contract/blueprint-management) guide for details on how to deploy your smart contract blueprint.
{% endhint %}

{% hint style="warning" %}
**Don't forget to lock assets at the script address in a prior transaction.** Then when spending, specify the locked UTxO using the `outputRef` field, which pinpoints the UTxO by its transaction hash and output index.

```json
{
  "txHash": "transaction_hash", // Transaction hash where funds were locked
  "index": 0 // Output index within that transaction
}
```

**Anvil API returns the UTXOs in the order they were added when you previously locked the assets.**
{% endhint %}

## Spend Validator API Interaction Example

Use the below examples to build transactions that interact with the spend validator.

If you are unfamiliar with the Anvil API transaction builder, review the [Transaction Overview](/guides/transaction) guide first.

### 1. Locking Assets at Script Address

```typescript
// Build a transaction to lock funds at a script address
const lockInput = {
  // Address where change outputs will be sent (your wallet address)
  changeAddress: "ADDR_YOUR_CHANGE_ADDRESS",
  
  // Optional message for transaction metadata (appears in block explorers)
  message: "Locking funds using the spend validator",
  
  // UTxOs to use as inputs (hex-encoded transaction outputs)
  // These provide the funds being locked at the script address
  utxos: [
    "8282...", "8282...", "8282...", "8282..."
  ],
  
  // Outputs to create in this transaction
  outputs: [
    {
      // Script address derived from validator hash
      address: scriptAddress,
      
      // Amount of lovelace (unit of ADA) to lock at script address
      lovelace: LOVELACE_AMOUNT,
      
      // Datum stores information needed for future spending
      datum: {
        // Stored directly on-chain (vs. hash-only)
        type: "inline",
        
        // The actual datum content as specified by your validator's blueprint
        value: {
          owner: "addr_test1xyz...",   // Owner's verification key hash who can unlock
        },
        
        // References validator hash and purpose for serialization
        shape: { validatorHash, purpose: "spend" }
      }
    }
  ]
};

```

### 2. Spending (Unlocking) Locked UTxO

```typescript
// Build a transaction to spend (unlock) funds from the script address
const unlockInput = {
  // Address where unlocked funds will be sent (your wallet address)
  changeAddress: "ADDR_YOUR_CHANGE_ADDRESS",
  
  // Optional message for transaction metadata (appears in block explorers)
  message: "Unlocking funds using the spend validator",
  
  // UTxOs to use for transaction fees and collateral
  // These are regular wallet UTxOs, not the script UTxO being spent
  utxos: [
    "8282...", "8282...", "8282...", "8282..."
  ],
  
  // Define how to interact with smart contract validators
  scriptInteractions: [
    {
      // Validator script hash (matches one used when locking funds)
      hash: validatorHash,
      
      // We're using the spend validator
      purpose: "spend",
      
      // Points to the specific UTXO at the script address we want to spend/unlock
      outputRef: { 
        txHash: "TRANSACTION_HASH", // Transaction hash where funds were locked
        index: 0                       // Output index within that transaction
      },
      
      // Redeemer provides arguments to the validator
      // Here we're unlocking the funds with a simple message defined in the smart contract
      redeemer: { 
        type: "json", 
        value: { 
          msg: "hex-encoded-message"  
        } 
      }
    }
  ],
  
  // Transaction must be signed by these key hashes
  // Often matches owner key from the datum to verify authorization
  requiredSigners: [signerKeyHash]
};
```

## See Examples

{% content-ref url="<https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/hello-world-smart-contract.md>" %}
<https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/hello-world-smart-contract.md>
{% endcontent-ref %}

{% content-ref url="<https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/escrow/README.md>" %}
<https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/escrow/README.md>
{% endcontent-ref %}


# Stake Validator

Detailed guide on stake validators including overview, use cases, and example Anvil API interactions for managing stake rewards.

## Overview

Stake validators (also called reward validators) control access to staking rewards associated with script-controlled stake addresses. They determine when and how accumulated rewards can be withdrawn, enabling programmatic control over staking operations.

{% hint style="info" %}
**TERMINOLOGY NOTE**

In official Cardano terminology, these are called **Stake Validators** with the script purpose **Rewarding**. However, the Anvil API uses `purpose: "withdraw"` in the transaction structure. This documentation uses the terms interchangeably.
{% endhint %}

## Real-world Use Cases

* **DAO Treasury Management**: Automated governance of reward withdrawals based on DAO votes
* **Delegated Staking Pools**: Programmatically manage stake delegation and reward distribution
* **Reward Distribution Systems**: Automate the fair distribution of staking rewards among multiple parties
* **Conditional Reward Access**: Restrict reward withdrawals based on time, market conditions, or other parameters

## Required Fields for Reward Withdrawals

{% hint style="info" %}
**For stake validators, you need two key components in your transaction:**

1. **scriptInteractions**: Specifies the validator hash, purpose ("withdraw" in Anvil API), and redeemer
2. **withdrawals**: An array specifying which stake address to withdraw rewards from

The validator hash must match the script that controls the stake address you're withdrawing from.

```json
{
  "scriptInteractions": [{ ... }],
  "withdrawals": ["stake_script_address_here"]
}
```

**Note**: The stake address must be registered and have delegated ADA to accumulate rewards before withdrawal can occur.
{% endhint %}

{% hint style="warning" %}
**PREREQUISITE: DEPLOYED SMART CONTRACT**

Before you can interact with any validator, you must have a compiled and deployed smart contract blueprint. The validator hash used in these examples comes from your deployed blueprint.

Refer to the [Blueprint Management](/guides/smart-contract/blueprint-management) guide for details on how to deploy your smart contract blueprint.
{% endhint %}

## Stake Validator API Interaction Example

Use the below examples to build transactions that interact with withdraw validators.

If you are unfamiliar with the Anvil API transaction builder, review the [Transaction Overview](/guides/transaction) guide first.

### 1. Registering a Script-Controlled Stake Address

```typescript
// Build a transaction to register a script-controlled stake address
const registerInput = {
  // Address where change outputs will be sent (your wallet address)
  changeAddress: "ADDR_YOUR_CHANGE_ADDRESS",
  
  // Optional message for transaction metadata (appears in block explorers)
  message: "Registering script-controlled stake address",
  
  // UTxOs to use as inputs (hex-encoded transaction outputs)
  utxos: [
    "8282...", "8282...", "8282...", "8282..."
  ],
  
  // Certificate for stake address registration
  certificates: [{
    type: "stakeRegistration",  // Register a stake credential
    // Stake credential controlled by the script
    credential: {
      type: "script",
      hash: withdrawValidatorHash // Hash of your withdraw validator
    }
  }],
  
  // Other possible certificate types include:
  // - "stakeDelegation": Delegate stake to a pool 
  // - "stakeDeregistration": Unregister a stake address
  // - "voteDelegation": Delegate voting power (CIP-36)
  
  // Pay the stake registration deposit (2 ADA on mainnet)
  deposits: "auto" // Automatically calculate required deposits ("auto" or specific amount in lovelace)
};
```

### 2. Withdrawing Rewards from a Script-Controlled Stake Address

```typescript
// Build a transaction to withdraw rewards from a script-controlled stake address
const withdrawInput = {
  // Address where withdrawn rewards and change will be sent
  changeAddress: "ADDR_YOUR_TREASURY_ADDRESS",
  
  // Optional message for transaction metadata
  message: "Withdrawing staking rewards using script",
  
  // UTxOs to use for transaction fees and collateral
  utxos: [
    "8282...", "8282...", "8282...", "8282..."
  ],
  
  // Define how to interact with the withdraw validator script
  scriptInteractions: [{
    // Validator script hash (matches the one used when registering)
    hash: withdrawValidatorHash,
    
    // We're using the withdraw validator
    purpose: "withdraw",
    
    // Redeemer provides arguments to the validator
    redeemer: { 
      type: "json", 
      value: { 
        action: "withdraw",
        authorizer: treasuryKeyHash,  // Key hash authorized to perform withdrawals
        beneficiary: "addr_test1..."  // Where rewards will go (if different from changeAddress)
      } 
    }
  }],
  
  // Specify the script-controlled stake address to withdraw from
  withdrawals: ["stake_script_address_here"],
  
  // Transaction must be signed by these key hashes
  // Often matches authorizer key from the redeemer to verify authorization
  requiredSigners: [treasuryKeyHash]
};
```

## Important Technical Notes

### Stake Address Lifecycle

1. **Registration**: Script-controlled stake addresses must be registered before they can receive rewards
2. **Delegation**: After registration, ADA must be delegated to a stake pool
3. **Rewards Accrual**: Rewards begin accruing after 2-3 epochs (10-15 days)
4. **Withdrawal**: Rewards can be withdrawn using the withdraw validator

### Security Considerations

* The redeemer is the only way to pass parameters to the validator
* Include proper authorization checks in your validator logic
* Consider time-locking or multi-signature requirements for high-value treasuries

### Performance Optimization

* Consider batching multiple reward withdrawals in a single transaction when possible

## See Examples


# Mint Validator

Detailed guide on mint validators including overview, use cases, token standards, and example Anvil API interactions for token minting and burning.

{% hint style="warning" %}
**Note**: This guide is currently in development and will be completed soon
{% endhint %}

## Mint Validator

### Overview

Mint validators (also called minting policies) control the creation and destruction of native tokens on Cardano. They define the rules under which tokens can be minted (created) or burned (destroyed), acting as a programmatic authority over asset supply management.

### Real-world Use Cases

* **NFT Collections**: Regulate minting of unique, non-fungible assets with provable scarcity
* **Fungible Tokens**: Create tokens with controlled supply mechanisms
* **Token Burning**: Implement redemption or destruction policies
* **Evolving NFTs**: Enable assets that can change state or properties over time
* **Multi-stage Releases**: Control phased distribution of limited assets

### Required Fields for Token Minting

{% hint style="info" %}
**For mint validators, you need two key components in your transaction:**

1. **mint**: An array defining what tokens to mint or burn
2. **scriptInteractions**: Specifies the validator hash, purpose, and redeemer

The policy ID in the mint array must match the validator hash in the scriptInteractions:

```json
{
  "mint": [{ 
    "type": "plutus",
    "policyId": "policy_id_here",
    "assetName": "TOKEN_NAME",
    "quantity": 1,
    "metadata": { ... } // Optional token metadata
  }],
  "scriptInteractions": [{
    "hash": "policy_id_here", // Must match policyId above
    "purpose": "mint",
    "redeemer": { ... }
  }]
}
```

**Note**: For native scripts (non-Plutus policies), use `type: "simple"` in the mint array and omit the scriptInteractions entry.
{% endhint %}

{% hint style="warning" %}
**PREREQUISITE: DEPLOYED SMART CONTRACT**

Before you can interact with any validator, you must have a compiled and deployed smart contract blueprint. The validator hash used in these examples comes from your deployed blueprint.

Refer to the [Blueprint Management](/guides/smart-contract/blueprint-management) guide for details on how to deploy your smart contract blueprint.
{% endhint %}

### Mint Validator API Interaction Example

Use the below examples to build transactions that interact with mint validators.

If you are unfamiliar with the Anvil API transaction builder, review the [Transaction Overview](/guides/transaction) guide first.

#### 1. Minting NFTs (CIP-25 Standard)

#### 2. Minting Fungible Tokens (CIP-68 Standard)

### See Examples

{% content-ref url="/pages/sIDc3mGeCPs5eUafjqfo" %}
[Mint with Smart Contract](/guides/smart-contract/mint-smart-contract)
{% endcontent-ref %}


# Hello World Smart Contract

2025-02-21: Missing steps for lock and unlock

## Requirements

* A Cup of Coffee
* The Aiken Hello World Example (You need to update the smart contract name to avoid getting the `already exists` message from Anvil API.)
  * The Smart Contract name should be in this format: `your_org/hello-world`
  * See the official documentation: [Aiken Hello World](https://aiken-lang.org/example--hello-world/basics)
* An Anvil API Key (you can use the one provided in this document to access `preprod`)
* `customer.json` with 100ADA, you can use the CLI tool to generate the wallet, then send funds from the faucet to it. ([Wallet CLI](/developer-tools/wallet-cli))

***

## Objectives

Interact with any Smart Contracts using Anvil API

**Objectives**

* Upload Blueprint
* Deploy Smart Contract On-Chain
* Update/Delete Blueprint
* Hello World Lock & Unlock ADA
* Get a `jsonSchema` from your blueprint to get types for the language you use.
* See below for the **code** in one file (`full.ts`)

***

## Aiken Smart Contract

<details>

<summary>Smart Contract - Update Name</summary>

**aiken.toml**

```toml
name = "your-org/your-sc-name"
version = "0.0.0"
compiler = "v1.1.10"
plutus = "v3"
license = "Apache-2.0"
description = "Aiken contracts for project 'your-org/your-sc-name'"

[repository]
user = "your-org"
project = "your-sc-name"
platform = "github"

[[dependencies]]
name = "aiken-lang/stdlib"
version = "v2.2.0"
source = "github"

[config]
```

</details>

## Helper Functions

Function to extract `validator hashes` from a `blueprint`

```typescript
function getValidators(validators: typeof blueprint.validators) {
  return [...validators.reduce((a, b) => a.set(b.hash, b) && a, new Map<string, typeof blueprint.validators[number]>()).values()];
}
```

***

## Prerequisites

You need to load your **Blueprint** like this:

```typescript
import blueprint from "./hello-world/plutus.json" with {type: "json"};
```

The **wallet used** in this guide is loaded like this:

> You also need the CSL imports and Buffer for that to work, it is only the case when you are using the wallet in the backend.

```typescript
import { Buffer } from "node:buffer";
import { FixedTransaction, PrivateKey } from "npm:@emurgo/cardano-serialization-lib-nodejs@14.1.0"; // only required due to signing in the backend.

import customer from "./customer.json" with {type: "json"}; // Optional, this guide is using this approach.
```

> You can use any wallet, like the one in your browser or with weld, but to limit the external steps and streamline the flow. This guide uses the wallet directly in the backend. You can take a look at the [signing transaction document](/guides/transaction/signing-transaction) to see how to use the browser wallet.

The **api key** used is this one:

```typescript
const X_API_KEY = "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9";
```

***

## Payload

**To upload your validator on Anvil Backend**

> Benefit of doing so, will reduce the complexity and steps to interact with your smart contract. as it will be store directly in the backend. Your API Key is link with your Blueprint and will let you update and delete it if needed.

**Deploy the Smart Contract On-Chain**

```typescript
const payload = {
  changeAddress: "addr_test...",
  message: "Smart contract deployed using anvil API",
  outputs: getValidators(blueprint.validators).map(validator => ({
    address: scriptAddresses[validator.hash],
    datum: {
      type: "script",
      hash: validator.hash
    }
  }))
}
```

* `scriptAddresses`:
  * **Key** = Validator Hash
  * **Value** = Address of the Smart Contract (*Enterprise Address*)

***

## Steps

### Step 1 - Upload Blueprint

Upload your blueprint (`plutus.json`) to Anvil Backend

#### Create Blueprint

```typescript
const blueprintRegistration = await fetch("https://preprod.api.ada-anvil.app/v2/services/blueprints",
  {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-api-key": X_API_KEY
    },
    body: JSON.stringify({
      blueprint
    })
  });

const uploadedBlueprint = await blueprintRegistration.json();
console.debug(JSON.stringify(uploadedBlueprint, null, 2))

const { scriptAddresses } = uploadedBlueprint;
```

* `scriptAddresses`: Needed for Step #2

> This API Call is an `upsert`, so if you forget the `scriptAddresses` or need to update your blueprint, you can reuse the same call.

> **Tip**: Once registered, if you need to update the name without changing the smart contract code or logic, you can safely delete the blueprint using the former name and then re-upload it.

### Step 2 - Deploy Smart Contract

> You need a valid Cardano wallet with some ADA on it (100ADA should be more than enough) [Wallet CLI](/developer-tools/wallet-cli)

Deploy the smart contract on-chain in order to get the reference UTXO

#### Payload to build the transaction:

```typescript
const contract = {
  changeAddress: customer.base_address_preprod,
  message: "Smart contract deployed using anvil API",
  outputs: getValidators(blueprint.validators).map(validator => ({
    address: scriptAddresses[validator.hash],
    datum: {
      type: "script",
      hash: validator.hash,
    }
  })),
};
```

#### Create the transaction

```typescript
const contractDeployed = await fetch(
  `https://preprod.api.ada-anvil.app/v2/services/transactions/build`,
  {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-api-key": X_API_KEY
    },
    body: JSON.stringify(contract),
  }
);

const contractToDeployTransaction = await contractDeployed.json();
console.log(JSON.stringify(contractToDeployTransaction));
```

#### Sign and Submit the transaction

> The signature process uses CSL for simplicity. For more information, see the [Signing a Transaction](/guides/transaction/signing-transaction) guide.

```typescript
// Sign the transaction using CSL.
const txToSubmitOnChain = FixedTransaction.from_bytes(
  Buffer.from(contractToDeployTransaction.complete, "hex")
);
txToSubmitOnChain.sign_and_add_vkey_signature(
  PrivateKey.from_bech32(customer.skey)
);

const urlSubmit = "https://preprod.api.ada-anvil.app/v2/services/transactions/submit";
const submitted = await fetch(urlSubmit, {
  method: "POST",
  body: JSON.stringify({
    signatures: [], // no signature required as it is part of the `txToSubmitOnChain`.
    transaction: txToSubmitOnChain.to_hex(),
  }),
  headers: {
    "Content-Type": "application/json",
    "X-Api-Key": X_API_KEY,
  },
});

const response = await submitted.json();
console.debug(response);

const { txHash } = response;
```

### Step 3 - Link Blueprint and UTXO

Update blueprint to link references to deployed validators.

```typescript
const response = await fetch(`https://preprod.api.ada-anvil.app/v2/services/blueprints`,
  {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-api-key": X_API_KEY
    },
    body: JSON.stringify({
      blueprint,
      refs: getValidators(blueprint.validators).reduce((a, b, index) => {
        a[b.hash] = { txHash, index };
        return a;
      }, {} as Record<string, { txHash: string, index: number }>),
    })
  });

const updatedBlueprint = await response.json()
console.log(updatedBlueprint);
```

### Conclusion

From here you have uploaded your blueprint and linked the reference UTXO on-chain and in Anvil Backend.

The next step is to interact with the `hello-world` Smart Contract (Lock & Unlock ADA)

***

## Full Example

<details>

<summary>full.ts</summary>

{% code overflow="wrap" %}

```typescript
import { Buffer } from "node:buffer";
import { FixedTransaction, PrivateKey } from "npm:@emurgo/cardano-serialization-lib-nodejs@14.1.0"; // only required due to signing in the backend.

import blueprint from "./plutus.json" with {type: "json"};
import customer from "./customer.json" with {type: "json"};

const X_API_KEY = "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9";
const API_ENDPOINT = "https://preprod.api.ada-anvil.app/v2/services"

const headers = {
  "x-api-key": X_API_KEY,
  "Content-Type": "application/json",
};

function getValidators(validators: typeof blueprint.validators) {
  return [...validators.reduce((a, b) => a.set(b.hash, b) && a, new Map<string, typeof blueprint.validators[number]>()).values()];
}

const blueprintRegistration = await fetch(`${API_ENDPOINT}/blueprints`,
  {
    method: "POST",
    headers,
    body: JSON.stringify({ blueprint })
  });

const uploadedBlueprint = await blueprintRegistration.json();
console.debug("uploadedBlueprint", JSON.stringify(uploadedBlueprint, null, 2))

const { scriptAddresses } = uploadedBlueprint;

const contract = {
  changeAddress: customer.base_address_preprod,
  message: "Smart contract deployed using anvil API",
  outputs: getValidators(blueprint.validators).map(validator => ({
    address: scriptAddresses[validator.hash],
    datum: {
      type: "script",
      hash: validator.hash,
    }
  })),
};

const contractDeployed = await fetch(
  `${API_ENDPOINT}/transactions/build`,
  {
    method: "POST",
    headers,
    body: JSON.stringify(contract),
  }
);

const contractToDeployTransaction = await contractDeployed.json();
console.log("contractToDeployTransaction", JSON.stringify(contractToDeployTransaction));

// Sign the transaction using CSL.
const txToSubmitOnChain = FixedTransaction.from_bytes(
  Buffer.from(contractToDeployTransaction.complete, "hex")
);
txToSubmitOnChain.sign_and_add_vkey_signature(
  PrivateKey.from_bech32(customer.skey)
);

console.log(txToSubmitOnChain.transaction_hash().to_hex());

const urlSubmit = `${API_ENDPOINT}/transactions/submit`;
const submitted = await fetch(urlSubmit, {
  method: "POST",
  body: JSON.stringify({
    signatures: [], // no signature required as it is part of the `txToSubmitOnChain`.
    transaction: txToSubmitOnChain.to_hex(),
  }),
  headers,
});

const response = await submitted.json();
console.debug("response", response);

const { txHash } = response;

const linkBlueprintAndTxHash = await fetch(`${API_ENDPOINT}/blueprints`,
  {
    method: "POST",
    headers,
    body: JSON.stringify({
      blueprint,
      refs: getValidators(blueprint.validators).reduce((a, b, index) => {
        a[b.hash] = { txHash, index };
        return a;
      }, {} as Record<string, { txHash: string, index: number }>),
    })
  });

const updatedBlueprint = await linkBlueprintAndTxHash.json()
console.log("updatedBlueprint", updatedBlueprint);
```

{% endcode %}

</details>

***

## Usage (Hello World Lock/Unlock)

### Lock Funds

#### Payload

TODO: Explain the JSON below

```typescript
const input = {
  changeAddress: customer.base_address_preprod,
  message: "Locking my fortune using anvil API",
  outputs: [
    {
      address: await getScriptAddr(hash), // script address of the first validator
      lovelace: 2_000_000, // 1 ADA = 1_000_000 Lovelace
      datum: {
        type: "inline",
        value: {
          owner: customer.key_hash // Only the Customer will be able to unlock the funds
        },
        shape: {
          validatorHash: hash,
          purpose: "spend"
        }
      }
    }
  ],
};
```

### Unlock Funds

#### Payload

TODO: Explain the JSON below

```typescript
const input = {
  changeAddress: customer.base_address_preprod,
  message: "Unlock my fortune using anvil API",
  scriptInteractions: [
    {
      hash,
      purpose: "spend",
      // This Output ref and index is the UTXO locked at the previous step.
      outputRef: {
        txHash: "7ac8f6922d51ffe2980e73c57c985b3906795440b5a565cc8806899dc88b110e", // ACTION: Replace with your txHash
        index: 0, // ACTION: Replace with you index
      },
      redeemer: { // Aka. Smart contract Parameters
        type: "json",
        value: {
          msg: Buffer.from("Bonjour Monde!!", "utf8").toString("hex") // ACTION: Replace with your String
        }
      }
    }
  ],
  requiredSigners: [customer.key_hash], // Aka. extra_signatories
};
```

### Full Example - Lock Fund

<details>

<summary>lock.ts</summary>

{% code overflow="wrap" %}

```typescript
import { Buffer } from "node:buffer";
import {
  FixedTransaction,
  PrivateKey
} from "npm:@emurgo/cardano-serialization-lib-nodejs@14.1.0";

import blueprint from "./hello-world/plutus.json" with {type: "json"}; // NOTE: You only need the hash.
import customer from "./customer.json" with {type: "json"};

const X_API_KEY = "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9";

// Save the hash in your database or backend.
// You are gonna need this hash everytime you need to interact with the smart contract.
// In this example we are loading it from the blueprint, but you do not need the blueprint.
const hash = blueprint.validators[0].hash;

async function getScriptAddr(hash: string): Promise<string> {
  const response = await fetch(`https://preprod.api.ada-anvil.app/v2/services/validators/${hash}/address`, {
    method: "GET",
    headers: {
      "x-api-key": X_API_KEY
    },
  });

  const scriptAddress = await response.json();
  console.debug("scriptAddress", scriptAddress)
  return scriptAddress.hex as string;
}

// Send 2 ADA to the Smart Contract (Lock ADA)
const input = {
  changeAddress: customer.base_address_preprod,
  message: "Locking my fortune using anvil API",
  outputs: [
    {
      address: await getScriptAddr(hash), // script address of the first validator
      lovelace: 2_000_000, // 1 ADA = 1_000_000 Lovelace
      datum: {
        type: "inline",
        value: {
          owner: customer.key_hash // Only the Customer will be able to unlock the funds
        },
        shape: {
          validatorHash: hash,
          purpose: "spend"
        }
      }
    }
  ],
};

const response = await fetch(
  `https://preprod.api.ada-anvil.app/v2/services/transactions/build`,
  {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-api-key": X_API_KEY
    },
    body: JSON.stringify(input),
  }
);

const result = await response.json()
console.log("result", result);
const txToSign = result.complete;

// Sign transaction directly in the backend
const txToSubmitOnChain = FixedTransaction.from_bytes(
  Buffer.from(txToSign, "hex")
);
// This sign the tx and add vkeys to the txToSubmitOnChain, so in submit we don't need to provide signatures
txToSubmitOnChain.sign_and_add_vkey_signature(
  PrivateKey.from_bech32(customer.skey)
);


const urlSubmit = "https://preprod.api.ada-anvil.app/v2/services/transactions/submit";
const submitted = await fetch(urlSubmit, {
  method: "POST",
  body: JSON.stringify({
    signatures: [],
    transaction: txToSubmitOnChain.to_hex(),
  }),
  headers: {
    "Content-Type": "application/json",
    "X-Api-Key": X_API_KEY,
  },
});

const submittedResponse = await submitted.json();
console.debug("submittedResponse", submittedResponse);
```

{% endcode %}

</details>

### Full Example - Unlock Fund

<details>

<summary>unlock.ts</summary>

{% code overflow="wrap" %}

```typescript
import { Buffer } from "node:buffer";
import { FixedTransaction, PrivateKey } from "npm:@emurgo/cardano-serialization-lib-nodejs@14.1.0"; // update utilities doc.

import blueprint from "./hello-world/plutus.json" with {type: "json"}; // NOTE: You only need the hash.
import customer from "./customer.json" with {type: "json"};

const X_API_KEY = "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9";

// Save the hash in your database or backend.
// You are gonna need this hash everytime you need to interact with the smart contract.
// In this example we are loading it from the blueprint, but you do not need the blueprint.
const hash = blueprint.validators[0].hash;

const input = {
  changeAddress: customer.base_address_preprod,
  message: "Unlock my fortune using anvil API",
  scriptInteractions: [
    {
      hash,
      purpose: "spend",
      // This Output ref and index is the UTXO locked at the previous step.
      outputRef: {
        txHash: "7ac8f6922d51ffe2980e73c57c985b3906795440b5a565cc8806899dc88b110e", // ACTION: Replace with your txHash
        index: 0, // ACTION: Replace with you index
      },
      redeemer: { // Aka. Smart contract Parameters
        type: "json",
        value: {
          msg: Buffer.from("Bonjour Monde!!", "utf8").toString("hex") // ACTION: Replace with your String
        }
      }
    }
  ],
  requiredSigners: [customer.key_hash], // Aka. extra_signatories
};

const response = await fetch(
  `https://preprod.api.ada-anvil.app/v2/services/transactions/build`,
  {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-api-key": X_API_KEY
    },
    body: JSON.stringify(input),
  }
);


const result = await response.json()
console.log("result", result);
const txToSign = result.complete;

// Sign transaction directly in the backend
const txToSubmitOnChain = FixedTransaction.from_bytes(
  Buffer.from(txToSign, "hex")
);
// This sign the tx and add vkeys to the txToSubmitOnChain, so in submit we don't need to provide signautres
txToSubmitOnChain.sign_and_add_vkey_signature(
  PrivateKey.from_bech32(customer.skey)
);

const urlSubmit = "https://preprod.api.ada-anvil.app/v2/services/transactions/submit";
const submitted = await fetch(urlSubmit, {
  method: "POST",
  body: JSON.stringify({
    signatures: [], // no signature required as it is part of the `txToSubmitOnChain`.
    transaction: txToSubmitOnChain.to_hex(),
  }),
  headers: {
    "Content-Type": "application/json",
    "X-Api-Key": X_API_KEY,
  },
})

const submittedResponse = await submitted.json();
console.debug("submittedResponse", submittedResponse);

```

{% endcode %}

</details>

***


# Mint with Smart Contract

Simple Smart Contract - Time-locked minting with authorized signer

{% hint style="info" %}
This guide provides a practical implementation of a mint validator. For a general explanation of script purposes and validator types, see [Mint Validator](/guides/smart-contract/validators/mint-validator).
{% endhint %}

## Objective

* Implement a simple Aiken smart contract for minting tokens
* Demonstrate the `mint` purpose validator in action
* Implement time constraints with `validity_range`
* Require specific signers with `extra_signatories`

## Where to Find the Code

The complete example is available in the [Simple Contract repository](https://github.com/Cardano-Forge/simple-contract/tree/main).

You can follow the [README.md](https://github.com/Cardano-Forge/simple-contract/blob/main/deno/README.md) for setup instructions.

## Smart Contract Overview

This example implements a minting policy with two validation conditions:

1. **Time-lock**: Minting is restricted to transactions before a predefined expiration timestamp
2. **Authorized Signer**: Only transactions signed by a specific key can mint tokens

The contract is written in Aiken and compiled to a Plutus script that produces a CIP-57 blueprint.

## Implementation Example

### Key Components for Minting Policy Transactions

When building a transaction to mint tokens with a time-locked policy, you need to include:

1. **Mint Array**: Defines what tokens to create with their metadata
2. **Script Interactions**: Provides the redeemer needed by the validator
3. **Required Signers**: Ensures the transaction is signed by the authorized key
4. **Validity Interval**: Sets the transaction time to comply with the time-lock

### Complete Implementation

The following example demonstrates the full process of building, signing, and submitting a transaction that mints tokens with our time-locked policy:

```typescript
import { Buffer } from "node:buffer";
import { FixedTransaction, PrivateKey } from "npm:@emurgo/cardano-serialization-lib-nodejs";

// Policy hash from your deployed contract blueprint
const policyHash = "eb7bddc5b588e238d2974d544a479b6bc0dc06852b38d12308ac62e5";

// Admin key hash that is authorized to mint (from the contract)
const adminKeyHash = "a2108b5b8f1fb54abdb20c23d8ef4b8303f53bd538e1cefe91335a5d";

// Step 1: Build the transaction input
const input = {
  // Address to receive change from the transaction
  changeAddress: customer.base_address_preprod,
  
  // Optional transaction message (appears in block explorers)
  message: "Minting with time-locked policy",
  
  // Define the token to mint
  mint: [
    {
      version: "cip25",
      assetName: "MyToken001",
      policyId: policyHash,
      type: "plutus", // Indicates a Plutus script-based policy
      quantity: 1,
      metadata: {
        name: "My First Token",
        description: "A token with time-lock and signer verification",
        image: "ipfs://QmYourImageHash"
      },
    },
  ],
  
  // Provide script interaction with redeemer
  scriptInteractions: [
    {
      purpose: "mint",
      hash: policyHash,
      redeemer: {
        type: "hex",
        value: "00", // Simple redeemer for this contract (empty array in CBOR)
      },
    },
  ],
  
  // Required signer enforced by the policy
  requiredSigners: [adminKeyHash],
  
  // Time validity for the time-lock
  validityInterval: {
    // Current time in seconds (must be before the contract's deadline)
    start: Math.floor(Date.now() / 1000)
  }
};

// Step 2: Build transaction with Anvil API
const response = await fetch("https://preprod.api.ada-anvil.app/v2/services/transactions/build", {
  method: "POST",
  headers: { "x-api-key": apiKey, "Content-Type": "application/json" },
  body: JSON.stringify(input),
});

const result = await response.json();
const txToSign = result.complete;
const tx = FixedTransaction.from_bytes(Buffer.from(txToSign, "hex"));

// Step 3: Sign with both required keys (multi-signature)

// Customer signature (paying for the transaction)
tx.sign_and_add_vkey_signature(PrivateKey.from_bech32(customer.skey));

// Admin signature (required by the minting policy)
tx.sign_and_add_vkey_signature(PrivateKey.from_bech32(admin.skey));

// Step 4: Submit transaction
const submitted = await fetch("https://preprod.api.ada-anvil.app/v2/services/transactions/submit", {
  method: "POST",
  headers: { "x-api-key": apiKey, "Content-Type": "application/json" },
  body: JSON.stringify({ transaction: tx.to_hex() }),
});

const submittedResponse = await submitted.json();
console.log("Transaction ID:", submittedResponse.hash);
```

## Important Technical Details

### Transaction Validation

The transaction will only succeed if the time-lock and signature conditions are met:

{% hint style="warning" %}
The transaction must be submitted before the contract's deadline timestamp. If the current time exceeds this deadline, validation will fail and the transaction will be rejected.
{% endhint %}

### Signing the Transaction

This example requires a **multi-signature transaction** where both the customer wallet and the admin wallet must sign:

{% hint style="info" %}
**Real-World Implementation**

In production applications:

* **Customer/User signatures** would typically be handled in the frontend using wallet connectors like [Weld](https://github.com/Cardano-Forge/weld). These connectors will pass the signatures to the `transactions/submit` endpoint.
* **Admin signatures** should be applied to the transaction on a secure server.
  {% endhint %}

{% hint style="info" %}
Both signatures are required for different reasons:

1. **Customer signature**: Required because they own the inputs (UTXOs) being used to pay transaction fees
2. **Admin signature**: Required by the minting policy's `extra_signatories` condition

If either signature is missing, the transaction will fail.
{% endhint %}


# End-to-End Next.js Escrow Guide

Build a Cardano escrow application with wallet connectivity, fund locking and unlocking, and transaction monitoring by interacting with a deployed smart contract.

## Introduction

This guide walks you through building a **Cardano escrow application** using Next.js and the Weld wallet connector. You'll create a web application that demonstrates the practical application of Cardano smart contracts through a real-world escrow use case.

<figure><img src="/files/FM3SDnT04LQhfWFMijSj" alt="Cardano Escrow Application Interface"><figcaption><p>Cardano Escrow Application with wallet integration and transaction monitoring</p></figcaption></figure>

If you have not already, make sure you have a deployed [Aiken's Hello World](https://aiken-lang.org/example--hello-world/basics) smart contract and have a validator hash handy.

See the below guides for more details:

{% content-ref url="/pages/KQvIgzxHoPLR3YBDtlVO" %}
[Smart Contracts](/guides/smart-contract)
{% endcontent-ref %}

{% content-ref url="/pages/fLloQsEjlrFZ3GccPWFy" %}
[Blueprint Management (CIP-57)](/guides/smart-contract/blueprint-management)
{% endcontent-ref %}

## User Journey Through Smart Contract Interaction

This application demonstrates the complete lifecycle of smart contract interaction on Cardano:

1. **Connect Wallet**: Users connect their Cardano wallet to establish their blockchain identity
2. **Lock Funds**: Users lock ADA in a smart contract, specifying themselves as the future recipient
3. **Monitor Status**: Users track the status of their locked funds in real-time
4. **Unlock Funds**: When ready, users reclaim their funds by meeting the contract's conditions

Through this process, users experience firsthand how Cardano's eUTXO model and validator scripts work together to create secure, programmable transactions.

## Real-World Applications

The principles demonstrated in this escrow application can be extended to build a variety of real-world solutions:

* **Marketplace Escrow**: Hold buyer funds until the seller confirms delivery of goods or services
* **Rental Security Deposits**: Lock tenants' deposits and release after lease terms are met
* **Milestone Payments**: Release funds to contractors upon completion of deliverables
* **Token Vesting**: Gradually unlock tokens to team members based on a schedule
* **Shared Savings**: Create group savings where funds unlock only when a goal is reached

## The Escrow Contract

### What You'll Build

With the Cardano Escrow application, users can:

* Connect any [Weld](https://github.com/Cardano-Forge/weld) supported Cardano wallet (Eternl, Lace, etc.)
* Lock ADA at a script address with owner information stored in the datum
* Monitor transaction status in real-time
* Unlock funds when ready
  * Signature verification ensures only the rightful owner can access funds
  * Use the special message from your redeemer ("Hello, World!") to unlock funds. This message is required by the smart contract in order to spend the locked funds.

### Smart Contract Overview

This application uses a Hello World smart contract to create a secure escrow by enforcing two simple conditions:

1. The transaction must include the specific message "Hello, World!" in the `redeemer` field
2. The transaction must be signed by the owner's key specified in the `datum`

When both conditions are met, the validator returns `true` and the locked funds can be spent.

<details>

<summary>Expand for technical details about the Smart Contract</summary>

**How Cardano Validators Work**

At their core, all Cardano validators function as boolean predicates - they evaluate to either `True` or `False`. A UTXO can only be spent when its validator returns `True`. If the validator returns `False` or fails with an error, the transaction is rejected.

Thanks to Cardano's smart contract model, this simple Hello World contract can be repurposed for a secure escrow application. As long as our validator returns true because the conditions are met, our transaction will be accepted by the network and the funds will be unlocked.

**Contract Implementation**

```aiken
use aiken/collection/list
use aiken/crypto.{VerificationKeyHash}
use cardano/transaction.{OutputReference, Transaction}

pub type Datum {
  owner: VerificationKeyHash,
}

pub type Redeemer {
  msg: ByteArray,
}

validator hello_aiken {
  spend(
    datum: Option<Datum>,
    redeemer: Redeemer,
    _own_ref: OutputReference,
    self: Transaction,
  ) {
    expect Some(Datum { owner }) = datum

    // Condition 1: Check if message matches
    let must_say_hello = redeemer.msg == "Hello, World!"

    // Condition 2: Check if signed by owner
    let must_be_signed = list.has(self.extra_signatories, owner)

    // Both conditions must be true (boolean AND)
    must_say_hello && must_be_signed
  }

  else(_) {
    fail
  }
}
```

**Key Technical Concepts**

Cardano's smart contracts operate through three essential components:

1. **Validator Script**: The on-chain logic that determines when funds can be spent (in our case, the deployed Hello World smart contract)
2. **Datum**: Data attached to UTXOs when funds are locked (in our case, the owner's public key hash)
3. **Redeemer**: Transaction-specific data provided when attempting to spend a UTXO (our "Hello, World!" message)

The validator hash from the deployed smart contract is stored as `ESCROW_VALIDATOR_HASH` in the application's environment variables and used to:

* Create the script address where funds are locked
* Identify the correct validator during transaction building
* Verify that funds are being spent from the correct contract

</details>

## Building the Application

### Prerequisites

Before starting, ensure you have:

* Node.js 18+ installed
* Basic familiarity with React and Next.js
* A Cardano wallet with testnet ADA (Eternl, Lace, etc.) See the [testnet faucet](https://docs.cardano.org/cardano-testnets/tools/faucet) if you need testnet ADA.
* An Anvil API key (for transaction building and submission)
* A deployed Hello World smart contract with the validator hash.

### Step-by-Step Guide

This tutorial is divided into six progressive modules, each focusing on a specific aspect of the escrow application:

1. [**Project Setup**](/guides/smart-contract/escrow/setup)
   * Initialize a Next.js project
   * Set up essential dependencies and project structure
2. [**Wallet Integration**](/guides/smart-contract/escrow/wallet-integration)
   * Connect to Cardano wallets using the Weld library
   * Implement wallet connection and state management
3. [**Fund Locking**](/guides/smart-contract/escrow/fund-locking)
   * Create the interface to lock funds in the smart contract
   * Build and submit lock transactions via Anvil API
4. [**Transaction Dashboard**](/guides/smart-contract/escrow/transaction-dashboard)
   * Develop a dashboard to monitor transaction status
   * Implement transaction history storage and retrieval
5. [**Real-time Updates**](/guides/smart-contract/escrow/realtime-updates)
   * Add real-time Cardano monitoring through webhooks
   * Update transaction status as blockchain state changes
6. [**Fund Unlocking**](/guides/smart-contract/escrow/fund-unlocking)
   * Implement the unlock functionality to complete the escrow cycle
   * Create the redeemer with the specific message and handle signing

### Best Practices

* **Security**: Never expose API keys or private credentials in client-side code
* **Testing**: Always test your application on testnet before deploying to mainnet
* **Error Handling**: Implement comprehensive error handling for all blockchain operations
* **Units**: Remember that 1 ADA = 1,000,000 Lovelace when calculating amounts


# Setup

Set up the development environment and create the basic Next.js application structure for a Cardano escrow application.

## Introduction

In this first part, we'll create a Next.js application as the foundation for our Cardano escrow application. We'll focus on setting up the initial project structure and essential dependencies needed for the wallet integration we'll implement in Part 2.

## Prerequisites

Before you begin, ensure you have:

* Node.js 18+ installed
* Basic familiarity with React and Next.js
* A code editor (like VS Code)

## Steps

### 1. Create a new Next.js project

Start by creating a new Next.js application using create-next-app:

```bash
npx create-next-app@latest cardano-smart-escrow
cd cardano-smart-escrow
```

When prompted for options, select the following:

* TypeScript: Yes
* ESLint: Yes
* Tailwind CSS: Yes
* `src/` directory: Yes
* App Router: Yes
* Use TurboPack: (Optional)
* Custom Import Alias (@/\*): No

### 2. Install Dependencies

For now, we'll only install the essential dependencies needed for our wallet integration:

```bash
npm install @ada-anvil/weld @tanstack/react-query better-sqlite3
npm install --save-dev @types/better-sqlite3
```

This installs:

* `@ada-anvil/weld`: For Cardano wallet integration. This allows you to connect to a CIP-30 compatible browser wallet (e.g., Eternl, Lace, etc.)
* `@tanstack/react-query`: For data fetching and caching
* `better-sqlite3`: For local database storage

### 3. Environment Variables

Create a `.env.example` file at the root of your project and populate it with the following:

```env

# Anvil API
ANVIL_API_ENDPOINT=https://preprod.api.ada-anvil.app/v2/services
ANVIL_API_KEY=YOUR_ANVIL_API_KEY

# Your Smart Contract Hash (See Blueprint Management Guide)
ESCROW_VALIDATOR_HASH=YOUR_ESCROW_VALIDATOR_HASH

# Database
SQLITE_DB_PATH=./data/escrow.db

# Webhook (handled in Step 5)
WEBHOOK_SECRET=YOUR_WEBHOOK_SECRET
```

Copy `.env.example` to `.env.local` and fill in your actual values. Don't worry about the actual `WEBHOOK_SECRET` value for now, we'll fill it in later. Make sure to never commit `.env.local`—add it to your `.gitignore`.

### 4. Project Structure

Below is the complete directory structure you'll build throughout this guide series. This provides a roadmap of what we'll be creating:

```
cardano-smart-escrow/
├── src/
│   ├── app/
│   │   ├── api/                    # API Routes
│   │   │   ├── escrow/             # Escrow API endpoints
│   │   │   │   ├── lock/           # Fund locking endpoint
│   │   │   │   │   └── route.ts    
│   │   │   │   ├── submit/         # Transaction submission
│   │   │   │   │   └── route.ts    
│   │   │   │   ├── transactions/   # Transaction listing
│   │   │   │   │   └── route.ts    
│   │   │   │   └── unlock/         # Fund unlocking endpoint
│   │   │   │       └── route.ts    
│   │   │   └── webhooks/           # External service handlers
│   │   │       └── blockfrost/     # Blockchain event notifications
│   │   │           └── route.ts    
│   │   ├── globals.css             # Global styles
│   │   ├── layout.tsx              # Main app layout
│   │   └── page.tsx                # Home page
│   ├── components/                 # React components
│   │   ├── LockFundsForm.tsx      # Form for locking ADA
│   │   ├── MyTransactions.tsx     # Transaction history display
│   │   ├── ReactQueryProvider.tsx  # Data fetching provider
│   │   ├── WalletConnector.tsx     # Wallet connection UI
│   │   └── WeldProvider.tsx        # Wallet provider setup
│   ├── hooks/                      # Custom React hooks
│   │   ├── useAmountSlider.ts      # ADA amount input slider
│   │   └── useTransactions.ts      # Transaction data management
│   └── lib/                        # Utility functions and types
│       ├── anvil-api.ts            # Anvil API integration
│       ├── db.ts                   # SQLite database functions
│       └── types.ts                # TypeScript type definitions
├── .env.example                    # Example configuration
└── .env.local                      # Local config (gitignored)
```

### 5. Update Global Styles

Update the global CSS file with some basic styles we'll use throughout the application `src/globals.css`:\
The CSS details are not important for the guide. This just helps with the styling of the components we'll build.

```css
@import "tailwindcss";

:root {
  --background: #ffffff;
  --foreground: #171717;
}

@theme inline {
  --color-background: var(--background);
  --color-foreground: var(--foreground);
  --font-sans: var(--font-geist-sans);
  --font-mono: var(--font-geist-mono);
}

@media (prefers-color-scheme: dark) {
:root {
    --background: #0a0a0a86;
    --foreground: #ededed;
  }
}

body {
  background: var(--background);
  color: var(--foreground);
  font-family: Arial, Helvetica, sans-serif;
}

@layer components {
  .button-primary {
    @apply
      border-2
      border-neutral-800
      rounded-2xl
      px-4
      py-2
      font-bold
      shadow-[4px_4px_0px_0px_rgba(0,0,0,1)]
      transition-all
      hover:translate-x-1
      hover:translate-y-1
      hover:shadow-none
      bg-white
      hover:bg-gray-100
      text-black;
  }

  .section-card {
    @apply
      border-2
      border-neutral-800
      rounded-2xl
      p-4
      mb-4
      shadow-[4px_4px_0px_0px_rgba(0,0,0,1)]
      bg-white;
  }
  
  /* Custom slider styles for cross-browser compatibility */
  .slider-thumb {
    @apply appearance-none;
  }
  
  /* Webkit browsers (Chrome, Safari) */
  .slider-thumb::-webkit-slider-thumb {
    @apply appearance-none w-4 h-4 bg-black rounded-full cursor-pointer;
  }
  
  /* Firefox */
  .slider-thumb::-moz-range-thumb {
    @apply appearance-none w-4 h-4 bg-black rounded-full cursor-pointer border-0;
  }
  
  /* Microsoft Edge */
  .slider-thumb::-ms-thumb {
    @apply appearance-none w-4 h-4 bg-black rounded-full cursor-pointer;
  }
}

```

### 6. Configure Root Layout

Create a basic root layout component `src/app/layout.tsx`:

```tsx
// src/app/layout.tsx
import './globals.css';
import type { Metadata } from 'next';

export const metadata: Metadata = {
  title: 'Cardano Escrow',
  description: 'Lock and unlock funds securely on the Cardano blockchain',
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}
```

### 7. Create Basic Home Page

Create a simple home page as a placeholder `src/app/page.tsx`:

```tsx
// src/app/page.tsx
export default function Page() {
  return (
    <main className="container mx-auto p-6 max-w-3xl">
      <h1 className="text-3xl font-bold mb-6 text-black">Cardano Escrow</h1>
      {/* Wallet connector will go here in Part 2 */}
      {/* Fund locking will go here in Part 3 */}
      {/* Transaction dashboard will go here in Part 4 */}
    </main>
  );
}
```

## Testing Your Setup

Let's make sure your basic setup is working correctly:

1. Start your development server:

```bash
npm run dev
```

2. Navigate to <http://localhost:3000> in your browser
3. You should see the placeholder home page with the title "Cardano Escrow"

{% hint style="success" %}
Congratulations! You've completed Part 1 of the guide. You now have a basic Next.js project set up for our Cardano escrow application.
{% endhint %}




---

[Next Page](/llms-full.txt/1)

