---
title: NFT Program
description: Understand the Thru NFT program account model, instruction surface,
  and the current ABI-guided developer view of how it works.
source_url:
  html: https://thru.org/docs/core-programs/nft-program/
  md: https://thru.org/docs/core-programs/nft-program.md
---

# NFT Program

The NFT Program manages NFT mint accounts and individual NFT accounts on Thru.

From a developer perspective, the current public view of the program is driven mainly by the flattened NFT ABI and the runtime tests that exercise minting, transfers, burns, metadata updates, and authority changes.

## Use This When

- you need an on-chain mint plus per-NFT account model
- you need to reason about NFT ownership, mint authority, or metadata updates
- you are working from the ABI and runtime behavior rather than a first-class public SDK

## Quickstart

Fetch the ABI, generate TypeScript builders, then build instruction bytes and send the transaction.

```bash
thru abi account get --include-data --out ./nft-program.abi.yaml <ABI_ACCOUNT_ADDRESS>

thru abi codegen \
  --files ./nft-program.abi.yaml \
  --language typescript \
  --output ./generated
```

```ts
import { decodeAddress } from "@thru/sdk/helpers";
import {
  NftInstruction,
  NftTransferArgs,
} from "./generated/thru/program/nft_token/types";

const instructionData = NftInstruction.builder()
  .payload()
  .select("transfer")
  .writePayload(
    NftTransferArgs.builder()
      .set_nft_account_idx(2)
      .set_dest_account_idx(3)
      .set_mint_account_idx(4)
  )
  .finish()
  .build();

const tx = await thru.transactions.build({
  feePayer: { publicKey: decodeAddress(feePayerAddress) },
  program: nftProgramAddress,
  accounts: {
    readWrite: [nftAccountAddress, destinationOwnerAddress, mintAccountAddress],
    readOnly: [],
  },
  instructionData,
});

const signedWire = await signTransaction(tx.toWireForSigning());
const signature = await thru.transactions.send(signedWire);
```

## How It Works

The program separates collection-level mint state from per-NFT state:

- the mint account stores the mint authority, total supply, and the next sequential NFT id
- each NFT account stores its mint, current owner, id, and metadata blob

Typical flows look like this:

1. initialize the NFT mint with a state proof for the new mint account
2. mint an NFT, which creates a new NFT account tied to that mint and the next id
3. transfer, burn, or update metadata on the per-NFT account
4. update the mint authority if ownership of the mint itself changes

## Account Model

| Account | What it stores | Notes |
| - | - | - |
| `NftMintAccount` | `mint_authority`, `supply`, `next_id` | Tracks collection-level state and the next id to mint. |
| `NftAccount` | `mint`, `owner`, `id`, `metadata` | Represents one NFT instance under a mint. |
| `NftMetadata` | `flags`, `external_metadata_uri` | The flattened ABI reserves 256 bytes for the external metadata URI. |
| `NftProgramAccount` | Size-discriminated envelope over mint and NFT accounts | The ABI distinguishes `mint_account` and `nft_account` by expected size. |

## Events

The current flattened NFT ABI does not define an event envelope.

That means agents should not assume a stable NFT event stream exists in the same way it does for the Token Program or Passkey Manager Program. For current integration work, the safer sources of truth are account state and transaction-level inspection.

## Instructions

| Instruction | Use it when | Notes |
| - | - | - |
| `initialize_mint` | Create a new NFT mint | Includes the mint seed and a state proof for the new mint account. |
| `mint_to` | Mint a new NFT account under the mint | The mint account controls supply and the next sequential NFT id. |
| `transfer` | Transfer ownership of an NFT account | Changes the `owner` field on the NFT account. |
| `burn` | Burn an NFT | Removes the NFT from circulation and updates mint state. |
| `update_metadata` | Change the NFT metadata payload | Use when the external metadata URI or flags need to change. |
| `set_authority` | Change the mint authority | Use when control over future minting should move to another authority. |

## What Is Not Covered Here

This page is grounded in the published flattened ABI and the runtime tests, not in a dedicated public SDK package. If you need deeper binary-layout rules, open [Specification](https://thru.org/docs/abi/specification.md) or the related ABI pages next.

## Related References

- [ABI Examples](https://thru.org/docs/abi/examples.md)
- [Specification](https://thru.org/docs/abi/specification.md)
- [Explorer MCP Overview](https://thru.org/docs/api-ref/explorer-mcp/overview.md)

## On-Chain Link

- Public explorer page: not currently documented in these docs
