DEV Community

Cover image for Solana NFTs Without Metaplex: What I Learned Building with Token Extensions published: true
Gopichand
Gopichand

Posted on

Solana NFTs Without Metaplex: What I Learned Building with Token Extensions published: true

Before this week I thought you needed Metaplex to build a real Solana NFT. It turns out you can mint a full NFT, stamp metadata directly onto it, group it inside a collection, audit every byte, and mutate it live — using only the Token Extensions program and the SPL token CLI. No third-party framework required.

This post covers what I built during Days 44–47 of #100DaysOfSolana, what surprised me coming from a Web2 background, and what I would build next.


The Mental Model: What a Solana NFT Actually Is

Before writing a single command, I had to unlearn something. In Web2, an NFT feels like a record in a database that points to a JPEG. On Solana, it is simpler and more precise than that.

A Solana NFT is just a mint account with three properties set just right:

Property Value
Supply 1 (exactly one token exists)
Decimals 0 (cannot be split)
Mint authority (not set) — disabled forever

That is it. The "NFT-ness" is not a special program. It is a configuration of the same SPL Token primitives that power every fungible token on the network.

What makes a modern Solana NFT different from a bare 1-of-1 token is the Token Extensions program (Token-2022). Instead of storing metadata in a separate account managed by Metaplex, Token Extensions lets you stamp the name, symbol, URI, and custom fields directly onto the mint account itself. Everything lives in one place. Any wallet, any marketplace, any RPC node can read it without trusting a third-party indexer.


What I Built: Four Days, Four Commands

Day 44 — First NFT with on-chain metadata

I minted a 1-of-1 token using the Token-2022 program, initialized a Metadata Pointer extension on it, and attached a name, symbol, and URI pointing to a JSON file on GitHub Gist.

# Create the mint with metadata extension enabled
spl-token create-token \
  --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb \
  --enable-metadata \
  --decimals 0 \
  ./nftTnVuyNU1kwTgv7edG6BPmHCtp2NMrawbw94kwZTF.json

# Stamp name, symbol, and URI directly onto the mint
spl-token initialize-metadata \
  nftTnVuyNU1kwTgv7edG6BPmHCtp2NMrawbw94kwZTF \
  "First Light" \
  "LIGHT" \
  "https://gist.githubusercontent.com/gopichandchalla16/..."
Enter fullscreen mode Exit fullscreen mode

Mint address: nftTnVuyNU1kwTgv7edG6BPmHCtp2NMrawbw94kwZTF
View on Solana Explorer (devnet)


Day 45 — NFT collection using Group and Member extensions

I created a separate collection mint using the Group extension, then minted two member NFTs and registered each one under the collection using the Member extension.

# Register a member NFT into the collection
spl-token group-member-initialize \
  <MEMBER_MINT> \
  <COLLECTION_MINT> \
  --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
Enter fullscreen mode Exit fullscreen mode

The collection mint ended up with Size: 2, Max Size: 3. Each member NFT carries a Group field that points byte-for-byte to the collection mint — the on-chain equivalent of a foreign key.


Day 46 — Auditing every byte on devnet

Instead of trusting that everything worked, I read both mints back from devnet the same way a senior engineer reviews their own work before shipping.

spl-token display nftTnVuyNU1kwTgv7edG6BPmHCtp2NMrawbw94kwZTF
spl-token display AC3peC3tdZUnLY44zGYC7YAuEaPknkMcRqsmAyvXJtMx
Enter fullscreen mode Exit fullscreen mode

The key check was this line inside Token Group Member on each member NFT:
Group: AC3peC3tdZUnLY44zGYC7YAuEaPknkMcRqsmAyvXJtMx ✅

That address matches the collection mint exactly. Phantom and every marketplace reads this field to verify collection membership — without trusting any off-chain index. That is what "verifiable provenance" actually means. Not marketing. Just two byte arrays comparing equal.


Day 47 — Mutating metadata live

This was the day I stopped treating the NFT like a fragile artifact and started treating it like a live row of data.

# Rename it
spl-token update-metadata nftTnVuy... name "Field Notes"

# Add a custom field — the schema is completely open
spl-token update-metadata nftTnVuy... rarity legendary

# Remove it
spl-token update-metadata nftTnVuy... rarity --remove

# Swap the URI to point at a new metadata JSON
spl-token update-metadata nftTnVuy... uri https://gist.githubusercontent.com/janvinsha/...
Enter fullscreen mode Exit fullscreen mode

Every command was one transaction. Every change appeared in Explorer within seconds.


The Surprising Part

Three things hit differently than I expected coming from Web2.

1. The on-chain layer and the off-chain layer move at different speeds.

When I renamed the NFT to "Field Notes", Explorer showed the new name in seconds. But the image — which lives at the URI I pointed at, hosted on GitHub Gist — stayed cached in wallets for much longer. The on-chain pointer updates instantly. The thing it points at does not. This is why serious NFT projects use Arweave or IPFS for permanent image hosting instead of a mutable HTTP server.

2. There is no separate NFT program.

I kept waiting to discover the "real" NFT layer. There is none. The same Token-2022 program that handles transfer fees, interest-bearing tokens, and compliance controls is also the program that handles NFT metadata and collections. Extensions compose on the same primitives.

3. The metadata schema is a blank canvas.

The additional_metadata array accepts any key-value pair you want. I stamped rarity: legendary onto a live mint in one CLI call and watched it appear on Explorer. No migrations. No schema definitions. No contract upgrades. Just one transaction.


What I Would Build Next

  • Permanent image hosting — move the image URI from GitHub Gist to Arweave so the pointer and the asset are both permanent
  • Dynamic NFT — use the mutable metadata to track something that changes over time, like XP or level in a game (Solana's official Dynamic Metadata NFT guide covers this pattern)
  • Metaplex comparisonMetaplex Core is the dominant alternative NFT standard on Solana, with a different account structure and royalty model. I want to build the same collection in both standards and write a direct comparison.

Resources I Actually Used


All the code and terminal output from every day is in my public repo:
👉 github.com/gopichandchalla16/100-days-of-solana

This post is part of #100DaysOfSolana. Follow along or jump in any day at mlh.link/solana-100.

Top comments (0)