DEV Community

Gopichand
Gopichand

Posted on

I Built 5 Token Extension Combinations on Solana This Week — Here's What Each One Does

If you have ever used a brokerage account, you already understand token extensions.

Your brokerage locks your account until you verify your identity. A stock you own cannot be
transferred to someone else without compliance checks on both sides. A professional license
is tied to you — the issuing body can revoke it, but you cannot sell it. An interest-bearing
savings account accrues value over time without you doing anything.

All of these are real-world rules applied to value. Token-2022 extensions let you encode
those same rules directly into a Solana token mint
— at the protocol level, not the
application layer. No backend. No database. No API that can go down.

Over the past several days I built five different token configurations as part of my
100 Days of Solana challenge.
Here is what I learned.


What Are Token Extensions?

Token-2022 is Solana's upgraded token program
(TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb).
It is backwards-compatible with the original SPL Token program, but it lets you attach
extensions to a mint at creation time.

Extensions are stored as TLV (type-length-value) blobs in the mint account's data.
Each extension adds bytes, and those bytes cost rent. More extensions = bigger account =
more SOL locked at creation. That is the core tradeoff to understand before you design
a token.


Extension 1 — Interest-Bearing Tokens

The Web2 analogy: A savings account that accrues interest over time.

spl-token create-token \
  --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb \
  --interest-rate 500
Enter fullscreen mode Exit fullscreen mode

This sets an initial rate of 500 basis points (5%). The rate can be updated later by
the rate authority:

spl-token set-interest-rate <MINT_ADDRESS> 15000
Enter fullscreen mode Exit fullscreen mode

What surprised me: The extension does NOT mint new tokens. It is purely a
calculation layer. Wallets and apps read the rate and elapsed time to display an
adjusted "UI balance." The raw on-chain balance stays the same. When you run
spl-token display, you see both:
Current rate: 15000bps
Average rate: 500bps ← historical average is preserved

Use cases: Yield-bearing stablecoins, loyalty points that grow over time,
reward tokens for long-term holders.


Extension 2 — Transfer Fees

The Web2 analogy: A payment processor that takes a cut of every transaction.

spl-token create-token \
  --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb \
  --transfer-fee 100 50000
# 100 basis points (1%) fee, max 50000 tokens per transfer
Enter fullscreen mode Exit fullscreen mode

Fees are collected at the token account level (withheld in the recipient's account),
not sent automatically. The withdrawal authority must harvest them:

spl-token withdraw-withheld-tokens <DESTINATION> --include-mints <MINT>
Enter fullscreen mode Exit fullscreen mode

What surprised me: The fee is enforced by the Token-2022 program itself on every
TransferChecked instruction. No smart contract needed. No way to bypass it from
the frontend.

Use cases: Protocol revenue, DAO treasury funding, creator royalties on every transfer.


Extension 3 — Default Account State (Frozen)

The Web2 analogy: A brokerage that freezes every new account until KYC passes.

spl-token create-token \
  --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb \
  --enable-freeze \
  --default-account-state frozen
Enter fullscreen mode Exit fullscreen mode

Every token account created for this mint starts in a frozen state. Nobody can
receive, send, or burn tokens until the freeze authority explicitly thaws them:

spl-token thaw <TOKEN_ACCOUNT_ADDRESS>
Enter fullscreen mode Exit fullscreen mode

The key insight I learned the hard way: It is not enough for the sender to be
thawed. The recipient must also be thawed. Both sides need to pass the compliance check.

When I tried to transfer from a thawed account to a still-frozen one: Program log: Error: Account is frozen (custom program error: 0x11)

Use cases: Regulated security tokens, KYC-gated stablecoins, permissioned loyalty
programs.


Extension 4 — Non-Transferable (Soulbound)

The Web2 analogy: A professional certification that belongs to you — you cannot sell it.

spl-token create-token \
  --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb \
  --enable-non-transferable \
  --decimals 0
Enter fullscreen mode Exit fullscreen mode

--decimals 0 because credentials are whole units. You either have the credential or
you don't.

Any transfer attempt fails immediately: Program log: Transfer is disabled for this mint (custom program error: 0x25)

0x25 = decimal 37 = NonTransferableError in the Token-2022 source.

Use cases: DAO membership badges, hackathon completion certificates,
verified contributor status, employee access tokens.


Extension 5 — Permanent Delegate (Revocable)

The Web2 analogy: Your employer can revoke your access badge at any time, without
asking you.

spl-token create-token \
  --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb \
  --enable-permanent-delegate \
  --enable-non-transferable \
  --enable-metadata \
  --decimals 0
Enter fullscreen mode Exit fullscreen mode

Combining --enable-non-transferable with --enable-permanent-delegate creates a
revocable soulbound token: the holder cannot move it, but the issuer can burn it
from any account without the holder's signature.

# Revoke the credential — no holder signature needed
spl-token burn <RECIPIENT_TOKEN_ACCOUNT> 1 \
  --owner ~/.config/solana/id.json \
  --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
Enter fullscreen mode Exit fullscreen mode

What surprised me: The Solana runtime logs a warning whenever a token account is
created for a mint with a permanent delegate:
Warning: Mint has a permanent delegate,
so tokens in this account may be seized at any time

Full transparency baked in at the protocol level. The holder is informed at account
creation — not buried in a ToS.


The Cost of Extensions

Each extension adds bytes to the mint account. More bytes = more rent-exempt SOL:

Configuration Data Size Rent
Default frozen only 171 bytes ~0.0021 SOL
Interest-bearing only 222 bytes ~0.0024 SOL
Transfer fees + Interest + Metadata 599 bytes ~0.0051 SOL

Metadata is the biggest cost driver because it stores variable-length strings
(name, symbol, URI) directly inside the mint account. Every character costs rent.


What Cannot Be Changed After Creation

This is the part that matters most before mainnet deployment:

  • Extensions cannot be added after mint creation. The account is allocated with exactly the space for the declared extensions. You must plan upfront.
  • The permanent delegate cannot be removed once set (only transferred to a different address if the current delegate signs).
  • Non-transferable is permanent. Once set, no authority can enable transfers.

Think of it like a database schema. Define it before you insert the first row.


Inspecting Any Token Extension Config

spl-token display <MINT_ADDRESS> \
  --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
Enter fullscreen mode Exit fullscreen mode

This single command decodes every extension in the mint account and prints it in
human-readable form. I now run this before touching any Token-2022 mint I didn't
create myself.


Where to Go Next

The best way to learn these is to run the CLI commands yourself on devnet. Each failure
message tells you exactly what rule was violated and which error code fired. That feedback
loop is faster than any tutorial.


This post is part of my #100DaysOfSolana challenge — building and documenting in public
every day. Follow along on X @GopichandAI or
GitHub.

Top comments (0)