One bare token, one feature-rich, one compliance-gated — here is what I learned.
Last week I knew how to mint a basic SPL token. This week I learned that Solana's Token Extensions Program lets you build tokens with protocol-level features — transfer fees, interest accrual, metadata, frozen accounts, soulbound restrictions, and revocable credentials — without writing a single line of on-chain code.
I spent three days building three different tokens, each with a different combination of extensions. Here is what I built, what each combination is good for, and what surprised me.
Combination 1: Interest-Bearing Token (Savings Accounts)
Extensions used: Interest-bearing (500 basis points = 5%)
The simplest extension: continuous compounding interest stored as a rate and timestamp. No new tokens are minted. Instead, wallets and applications calculate an adjusted display balance based on elapsed time.
# Create a token with 5% annual interest
spl-token create-token \
--program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb \
--interest-rate 500
# Create an account and mint tokens
spl-token create-account [MINT_ADDRESS] \
--program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
spl-token mint [MINT_ADDRESS] 1000 \
--program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
# Inspect the interest-bearing configuration
spl-token display [MINT_ADDRESS] \
--program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
The spl-token display output shows a Current rate: 500bps and a Rate authority. The raw balance stays 1000, but the UI amount ticks upward over time using A = P * e^(rt).
Good for: Yield-bearing tokens, savings accounts, staking receipts. Any situation where the balance should grow over time without a background job minting every second.
Combination 2: Multi-Extension Token (Transfer Fees + Interest + Metadata)
Extensions used: Transfer fees (100bps, max 5 tokens), Interest-bearing (5bps), Metadata, Metadata Pointer
This is the Swiss Army knife. One token that charges fees on transfers, displays interest-adjusted balances, and carries its own name and symbol on-chain.
# Create the mint with all three extensions
spl-token --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb \
create-token \
--decimals 2 \
--transfer-fee-basis-points 100 \
--transfer-fee-maximum-fee 500 \
--interest-rate 5 \
--enable-metadata
# Initialize metadata
spl-token initialize-metadata [MINT_ADDRESS] \
"ArcCoin" "ARC" \
"https://raw.githubusercontent.com/solana-developers/opos-asset/main/assets/CompressedCoil/metadata.json" \
--program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
# Mint and transfer to see the fee
spl-token create-account [MINT_ADDRESS]
spl-token mint [MINT_ADDRESS] 1000
spl-token transfer [MINT_ADDRESS] 100 [RECIPIENT] --expected-fee 1
When I transferred 100 tokens, the recipient received 99. The 1-token fee was withheld in a special balance that the mint authority can harvest later.
Good for: Revenue-generating tokens, ecosystem currencies, branded tokens that need on-chain identity. The extension model means all features work independently without conflicting.
Combination 3: Compliance-Gated Token (Default Frozen)
Extensions used: Default Account State (Frozen), Freeze authority
Every token account starts frozen. No one can send, receive, or burn until the freeze authority explicitly thaws them. This is protocol-level access control — no frontend bug can bypass it.
# Create a mint where all accounts start frozen
spl-token create-token \
--program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb \
--enable-freeze \
--default-account-state frozen
# Try to mint — this FAILS because the default account is frozen
spl-token mint [MINT_ADDRESS] 100
# Error: Account is frozen
# Thaw the account, then mint succeeds
spl-token thaw [YOUR_TOKEN_ACCOUNT]
spl-token mint [MINT_ADDRESS] 100 # succeeds
# Try to send to a frozen account — FAILS
spl-token transfer [MINT_ADDRESS] 50 [FROZEN_ACCOUNT]
# Error: destination account is frozen
Both the sender and the recipient must be thawed. This creates a fully permissioned system.
Good for: Regulated assets, stablecoins with compliance requirements, security tokens, loyalty programs where accounts need approval before activation.
What surprised me
Extensions cost real SOL. Each extension adds bytes to the mint account. My interest-bearing mint was 222 bytes (0.002436 SOL rent). The multi-extension mint was 599 bytes (0.005060 SOL). The simple frozen mint was 171 bytes (0.002081 SOL). The difference between the simplest and the most complex was 2.4x in rent cost. When designing a token, every extension is a deliberate tradeoff between features and cost.
You cannot add extensions after minting. This was the hardest mental shift from Web2. In Stripe, you can add a new product feature anytime. On Solana, the account size is fixed at creation. If you forgot to enable metadata, you cannot add it later — you have to create a new mint and migrate.
Where to go deeper
The official documentation covers every extension with implementation details:
Try combining extensions that fit your use case. The most powerful pattern I found was frozen-by-default + transfer fees + metadata — a token that enforces compliance, generates revenue, and identifies itself, all without a single line of smart contract code.
Top comments (0)