Skip to content
{redev}

Published 1/15/2026 · 13 min read

Tags: solana , nft , metaplex , blockchain , web3

Understanding Solana NFTs: A Complete Guide

This document explains everything you see when viewing an NFT on Solana Explorer, how each piece was created, and what can be changed after minting.

Table of Contents

  1. The Big Picture
  2. Accounts Created When Minting
  3. On-Chain vs Off-Chain Data
  4. Explorer Fields Explained
  5. What Can Be Changed
  6. How to Update NFT Data
  7. Collection Verification
  8. Cost Breakdown

The Big Picture

A Solana NFT is not a single piece of data. It’s a system of connected accounts that together represent ownership and metadata of a unique digital asset.

┌─────────────────────────────────────────────────────────────────┐
│                        SOLANA BLOCKCHAIN                         │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ┌──────────────┐    ┌──────────────┐    ┌──────────────┐       │
│  │  Mint        │    │  Metadata    │    │  Master      │       │
│  │  Account     │───▶│  Account     │───▶│  Edition     │       │
│  │              │    │  (PDA)       │    │  (PDA)       │       │
│  └──────────────┘    └──────────────┘    └──────────────┘       │
│         │                   │                                    │
│         │                   │ points to                          │
│         ▼                   ▼                                    │
│  ┌──────────────┐    ┌──────────────────────────────────┐       │
│  │  Token       │    │  OFF-CHAIN (Arweave/IPFS)        │       │
│  │  Account     │    │  ┌────────────────────────────┐  │       │
│  │  (holds 1)   │    │  │  metadata.json             │  │       │
│  └──────────────┘    │  │  - name, description       │  │       │
│         │            │  │  - image URL               │  │       │
│         │ owned by   │  │  - attributes              │  │       │
│         ▼            │  └────────────────────────────┘  │       │
│  ┌──────────────┐    │  ┌────────────────────────────┐  │       │
│  │  Your        │    │  │  image.png                 │  │       │
│  │  Wallet      │    │  │  (the actual artwork)      │  │       │
│  └──────────────┘    │  └────────────────────────────┘  │       │
│                      └──────────────────────────────────┘       │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Key Insight: The blockchain stores ownership and pointers. The actual image and rich metadata live off-chain on permanent storage (Arweave via Irys).


Accounts Created When Minting

When you run mint-nft.ts, four accounts are created:

1. Mint Account

What it is: The unique identifier for your NFT. This address IS your NFT.

Created by: createNft() function in our script

const nftMint = generateSigner(umi);
// This generates a new keypair - the public key becomes the mint address

On Explorer: This is the main address you see (e.g., 4ZsKxVKmCFZx1xcRamFkwTLrRW6gMzfkVqLUGcfobmJ5)

Contains:

FieldValueMeaning
Supply1Only 1 token exists (NFT = non-fungible)
Decimals0Cannot be divided (unlike SOL with 9 decimals)
Mint AuthoritynullNo one can mint more (frozen after creation)
Freeze AuthoritynullNo one can freeze the token

Why Mint Authority is null: For a true NFT, we set supply = 1 and then remove the mint authority so no more can ever be created. This happens automatically with createNft().


2. Token Account (Associated Token Account)

What it is: Holds the actual token. Think of it as a “wallet slot” for this specific NFT.

Created by: Automatically created by createNft() for the recipient

Address: Derived deterministically from (owner wallet + mint address)

Token Account = PDA(owner_wallet, TOKEN_PROGRAM, mint_address)

On Explorer: Shows under “Token Accounts” or “Holders”

Contains:

FieldValueMeaning
MintNFT mint addressWhich token type this holds
OwnerRecipient walletWho owns this token account
Amount1How many tokens (always 1 for NFT)

Key Concept: You don’t “own” the mint. You own a token account that holds 1 unit of that mint.


3. Metadata Account (PDA)

What it is: Stores NFT metadata on-chain. This is where the name, symbol, and URI live.

Created by: Metaplex Token Metadata Program via createNft()

Address: A Program Derived Address (PDA) - deterministically calculated:

// How Metaplex finds the metadata account
const metadataPda = findMetadataPda(umi, { mint: nftMint.publicKey });
// PDA = seeds("metadata", TOKEN_METADATA_PROGRAM, mint_address)

On Explorer: Click “Metadata” tab on the NFT page

Contains:

FieldOur ValueMeaning
Name”Founder #1”Display name (max 32 chars on-chain)
Symbol”TREE”Collection symbol (max 10 chars)
URIhttps://arweave.net/...Points to off-chain JSON
Seller Fee Basis Points0Royalty percentage (0 = 0%)
Creators[{ address, verified, share }]Who created this + royalty split
Collection{ key, verified }Which collection this belongs to
Is MutabletrueCan metadata be updated?
Update AuthorityYour walletWho can update metadata
Primary Sale HappenedfalseHas this been sold?

4. Master Edition Account (PDA)

What it is: Proves this is a “master” NFT (not a print/copy). Controls edition printing.

Created by: Automatically by createNft()

Address: Another PDA derived from the mint:

Master Edition PDA = seeds("metadata", TOKEN_METADATA_PROGRAM, mint, "edition")

Contains:

FieldValueMeaning
Supply0Number of prints made from this master
Max SupplyNone/0Maximum prints allowed (None = unlimited potential)

Why it exists: Metaplex supports “editions” where you can print copies of a master NFT. For our utility NFTs, we don’t use this feature, but the account is still required.


On-Chain vs Off-Chain Data

On-Chain (Stored on Solana)

Stored directly in the Metadata Account:

- name (truncated to 32 chars)
- symbol (truncated to 10 chars)
- uri (pointer to off-chain JSON)
- seller_fee_basis_points
- creators array
- collection reference
- is_mutable flag
- update_authority

Cost: ~0.00561 SOL rent-exempt minimum for metadata account

Off-Chain (Stored on Arweave via Irys)

The URI points to a JSON file:

{
  "name": "Founder #1",
  "symbol": "TREE",
  "description": "TreehouseHQ Owner NFT. Grants ownership of one TreehouseHQ workspace. Founder Edition.",
  "image": "https://arweave.net/abc123...",
  "external_url": "https://treehousehq.io",
  "attributes": [
    { "trait_type": "Edition", "value": "Founder" },
    { "trait_type": "Number", "value": "1" },
    { "trait_type": "Type", "value": "Owner" }
  ],
  "properties": {
    "files": [{ "uri": "https://arweave.net/abc123...", "type": "image/png" }],
    "category": "image"
  }
}

Why off-chain?

  1. Cost: Storing a 500KB image on-chain would cost ~3.5 SOL in rent
  2. Flexibility: JSON can contain unlimited data
  3. Permanence: Arweave stores data forever (pay once)

Created by: Our script’s upload step:

// Upload image first
const [imageUri] = await umi.uploader.upload([imageFile]);

// Then upload JSON that references the image
const metadataUri = await umi.uploader.uploadJson({
  name: nftName,
  image: imageUri,
  // ... other fields
});

Explorer Fields Explained

When you view 4ZsKxVKmCFZx1xcRamFkwTLrRW6gMzfkVqLUGcfobmJ5 on Solana Explorer:

Overview Tab

FieldExampleSourceExplanation
Address4ZsKxVKm…Mint AccountThe NFT’s unique identifier
Owner ProgramToken Metadata Program-Metaplex program that manages this
Balance0 SOLMint AccountMint accounts don’t hold SOL
TokenTREEMetadata PDASymbol from metadata
Decimals0Mint AccountNFTs have 0 decimals
Supply1Mint AccountOnly 1 exists

Metadata Tab

FieldExampleMutable?Explanation
NameFounder #1Yes*Display name
SymbolTREEYes*Token symbol
URIhttps://arweave.net/Yes*Off-chain metadata URL
Seller Fee0%Yes*Royalty on secondary sales
Update AuthoritySQavJPZ…YesWho can modify metadata
Is MutabletrueOne-wayCan be set to false (irreversible)
Primary SalefalseOne-waySet true after first sale

*Only if is_mutable = true

Collection Section

FieldExampleExplanation
Collection Address8YK7GZH…Which collection this belongs to
VerifiedYes (checkmark)Collection authority confirmed membership

Creators Section

FieldExampleExplanation
AddressSQavJPZ…Creator wallet
VerifiedYesCreator signed to confirm
Share100%Royalty share (if royalties enabled)

Holders Tab

Shows the Token Account:

FieldExampleExplanation
OwnerSQavJPZ…Wallet that owns the NFT
Token Account7xYz…ATA holding the token
Amount1Always 1 for NFTs

What Can Be Changed

Always Changeable (if mutable)

FieldHow to ChangeNotes
NameUpdate metadataMax 32 chars on-chain
SymbolUpdate metadataMax 10 chars
URIUpdate metadataPoint to new JSON
ImageUpload new, update URIOld image stays on Arweave
AttributesUpload new JSON, update URI
DescriptionUpload new JSON, update URI
Seller FeeUpdate metadata0-10000 basis points
CreatorsUpdate metadataComplex rules apply
Update AuthorityTransfer authorityCan delegate to another wallet

One-Way Changes (cannot undo)

FieldChangeEffect
Is Mutabletrue → falseFreezes all metadata forever
Primary Salefalse → trueIndicates first sale happened
Collection Verifiedfalse → trueOnly collection authority can do this

Never Changeable

FieldWhy
Mint AddressIt’s the account’s public key
Mint AuthoritySet to null on creation
SupplyFrozen at 1
DecimalsSet at creation, immutable
Master EditionCreated once

How to Update NFT Data

Update On-Chain Metadata

Create a new script scripts/update-metadata.ts:

import { createUmi } from '@metaplex-foundation/umi-bundle-defaults';
import {
  mplTokenMetadata,
  updateV1,
  findMetadataPda,
} from '@metaplex-foundation/mpl-token-metadata';
import { keypairIdentity, publicKey } from '@metaplex-foundation/umi';

async function updateNftMetadata() {
  const umi = createUmi('https://api.devnet.solana.com')
    .use(mplTokenMetadata());

  // Load your keypair (must be update authority)
  const keypair = umi.eddsa.createKeypairFromSecretKey(secretKey);
  umi.use(keypairIdentity(keypair));

  const mintAddress = publicKey('4ZsKxVKmCFZx1xcRamFkwTLrRW6gMzfkVqLUGcfobmJ5');

  await updateV1(umi, {
    mint: mintAddress,
    data: {
      name: 'New Name Here',
      symbol: 'TREE',
      uri: 'https://arweave.net/new-metadata-uri',
      sellerFeeBasisPoints: 0,
      creators: null, // keep existing
    },
  }).sendAndConfirm(umi);

  console.log('Metadata updated!');
}

Update Off-Chain Data (Image/Attributes)

You cannot modify files on Arweave. Instead:

  1. Upload new files to get new URIs
  2. Update on-chain URI to point to new JSON
// 1. Upload new image
const newImageBuffer = fs.readFileSync('assets/new-image.png');
const [newImageUri] = await umi.uploader.upload([{
  buffer: newImageBuffer,
  fileName: 'new-image.png',
  contentType: 'image/png',
}]);

// 2. Upload new metadata JSON
const newMetadataUri = await umi.uploader.uploadJson({
  name: 'Updated NFT Name',
  image: newImageUri,
  attributes: [
    { trait_type: 'Edition', value: 'Founder' },
    { trait_type: 'Upgraded', value: 'true' },
  ],
});

// 3. Update on-chain URI
await updateV1(umi, {
  mint: mintAddress,
  data: {
    name: 'Updated NFT Name',
    uri: newMetadataUri,
    // ... other fields
  },
}).sendAndConfirm(umi);

Transfer Update Authority

To let someone else update the NFT:

import { updateV1 } from '@metaplex-foundation/mpl-token-metadata';

await updateV1(umi, {
  mint: mintAddress,
  newUpdateAuthority: publicKey('NEW_AUTHORITY_WALLET'),
}).sendAndConfirm(umi);

Make NFT Immutable (Permanent)

Warning: This cannot be undone!

await updateV1(umi, {
  mint: mintAddress,
  isMutable: false,
}).sendAndConfirm(umi);

After this, no one can ever change the metadata again.


Collection Verification

Why Verification Matters

Anyone can create an NFT and claim it belongs to a collection. Verification proves the collection authority approved this NFT’s membership.

Unverified: NFT says "I'm in TreehouseHQ collection" (anyone can claim)
Verified:   Collection authority signed "Yes, this NFT is in my collection"

How We Verify

In our mint script:

// 1. Create NFT with collection reference (unverified)
await createNft(umi, {
  // ...
  collection: {
    key: publicKey(collectionAddress),
    verified: false,  // Initially unverified
  },
}).sendAndConfirm(umi);

// 2. Collection authority verifies it
await verifyCollectionV1(umi, {
  metadata: findMetadataPda(umi, { mint: nftMint.publicKey }),
  collectionMint: publicKey(collectionAddress),
  authority: umi.identity,  // Must be collection update authority
}).sendAndConfirm(umi);

Unverify (Remove from Collection)

import { unverifyCollectionV1 } from '@metaplex-foundation/mpl-token-metadata';

await unverifyCollectionV1(umi, {
  metadata: findMetadataPda(umi, { mint: nftMint.publicKey }),
  collectionMint: publicKey(collectionAddress),
  authority: umi.identity,
}).sendAndConfirm(umi);

Cost Breakdown

One-Time Costs (rent-exempt deposits)

These are not “spent” - they’re held as rent deposits and could theoretically be recovered if accounts are closed:

AccountSize (bytes)Rent (SOL)
Mint Account82~0.00144
Metadata Account679~0.00561
Master Edition282~0.00277
Token Account165~0.00203
Total per NFT~0.0118 SOL

Transaction Fees

OperationFee (SOL)
Create NFT~0.000005
Verify Collection~0.000005
Update Metadata~0.000005

Storage Costs (Arweave via Irys)

ItemTypical SizeCost
Image (500KB)500,000 bytes~0.002 SOL
Metadata JSON~1KB~0.00001 SOL

Total Cost per NFT

Rent deposits:     ~0.012 SOL
Transaction fees:  ~0.00001 SOL
Storage (image):   ~0.002 SOL
Storage (JSON):    ~0.00001 SOL
─────────────────────────────
Total:             ~0.014 SOL (~$2-3 at typical prices)

Glossary

TermDefinition
MintThe token type definition. For NFTs, supply = 1.
PDAProgram Derived Address. Deterministically calculated, no private key.
ATAAssociated Token Account. Standard token account for a wallet.
RentSOL deposit required to keep an account alive on Solana.
Rent-ExemptAccount has enough SOL to live forever (minimum balance).
Basis Points1/100th of a percent. 100 bp = 1%, 10000 bp = 100%.
URIUniform Resource Identifier. Link to off-chain data.
ArweavePermanent decentralized storage network.
IrysService that pays Arweave fees using SOL (formerly Bundlr).
Update AuthorityWallet that can modify NFT metadata.
Collection AuthorityWallet that can verify/unverify collection membership.

Your NFT Summary

Based on our devnet test:

Mint Address:        4ZsKxVKmCFZx1xcRamFkwTLrRW6gMzfkVqLUGcfobmJ5
Owner:               SQavJPZYN5s5iW72Q5e652oZC9nmSwcwZgeBms15j61
Collection:          8YK7GZHykNvHKeGC7dnqC6LPVrtMDGy3Jam9h6Fhj78V
Name:                Founder #1
Symbol:              TREE
Is Mutable:          true (can update artwork later)
Royalties:           0%
Collection Verified: Yes
Network:             Devnet

What you can change:

  • Name, symbol, description
  • Image (upload new, update URI)
  • Attributes
  • Update authority (transfer to another wallet)

What you cannot change:

  • Mint address
  • That it’s an NFT (supply = 1)
  • History of ownership (stored in transactions)

Further Reading

Related Articles