Transfer Fees, Metadata, and Soulbound Tokens: A Tour of Solana Token Extensions

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • MyrinNew
    Senior Member
    • Feb 2024
    • 5168

    #1

    Transfer Fees, Metadata, and Soulbound Tokens: A Tour of Solana Token Extensions

    I spent the last five days building tokens on Solana — and it completely

    changed how I think about what a "token" actually is.


    Coming from Web2 and EVM development, I assumed a token was a number in a

    database with some transfer logic wrapped around it. On Solana, it's much

    closer to a configurable protocol object. Here's everything I learned across

    Days 29–33 of my #100DaysOfSolana challenge.



    The Starting Point: Two Token Programs

    Solana has two token programs:
    • SPL Token (original) — simple, battle-tested, does the basics
    • Token Extensions Program (Token-2022) — everything the original does,
      plus built-in extensions for fees, metadata, soulbound tokens, and more


    I used Token-2022 for almost everything this week because it lets you attach

    behavior directly to the mint — no separate smart contract needed.



    Day 29 — Creating My First SPL Token

    The first thing that surprised me: a token on Solana is two separate accounts.
    • Mint account — holds the token's configuration (supply, decimals,
      mint authority)
    • Token account — holds a specific wallet's balance of that token



    spl-token create-token --decimals 9
    spl-token create-account
    spl-token mint 1000






    In Web2, this would be one database table with a balance column. On Solana,

    the separation means the mint config is completely independent of who holds

    what — any wallet can hold any token as long as they have a token account for it.



    Day 30 — Token-2022 with On-Chain Metadata

    The original SPL token has no name or symbol stored on-chain. You need an

    external metadata program (Metaplex) to give it an identity. Token-2022

    changes this with the metadata extension — name, symbol, and URI live

    directly inside the mint account.






    spl-token create-token \
    --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb \
    --enable-metadata \
    --decimals 9

    spl-token initialize-metadata \
    "GopichandToken" "GOPI" \
    "https://your-metadata-uri.json"







    Run spl-token display and you'll see the name and symbol

    sitting right there in the mint account output. No external call. No

    separate metadata program. The token is the metadata.



    Day 31 — Transfer Fees at the Protocol Level

    This is where it got interesting. I attached a 1% transfer fee to a

    token mint — meaning every transfer automatically withholds 1% in the

    recipient's token account, uncollectable by the recipient, waiting for the

    mint authority to withdraw.






    spl-token create-token \
    --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb \
    --transfer-fee-basis-points 100 \
    --transfer-fee-maximum-fee 5000 \
    --decimals 9







    After transferring 100 tokens, the recipient got 99. The 1 withheld token

    sat locked in their account until I ran:






    spl-token withdraw-withheld-tokens \
    \








    In Web2, building a platform fee on transfers requires intercepting every

    transaction in your backend. On Solana, it's a flag on the mint. The

    program enforces it — not your server.



    Day 32 — The Full Lifecycle in One Sitting

    Day 32 was a consolidation day. No new concepts — just proving I could

    build the complete token lifecycle from scratch without checking notes:

    1. Create Token-2022 mint with metadata + 2% transfer fee
    2. Initialize metadata (name: ReinforceCoin, symbol: RFC)
    3. Create token account and mint 1000 RFC
    4. Transfer 100 to a second wallet → recipient gets 98, 2 withheld
    5. Withdraw withheld fees → balance becomes 902
      Mint: 6YnDTE8cETtvKyesVM9frSeWCfpQUx757qcCQyHaBe9T
      Final balance: 902 RFC ✅


    The "aha" moment: I realized the pattern is always the same regardless of

    which extensions you use. Create → configure → mint → transfer →

    collect.
    The specifics change. The lifecycle doesn't.



    Day 33 — Soulbound Tokens

    The most conceptually interesting day. I created a non-transferable

    token
    — a token that is permanently locked to the wallet it's minted into.






    spl-token create-token \
    --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb \
    --enable-non-transferable







    I minted 10 tokens, then tried to transfer 5 to a second wallet. The

    program rejected it instantly:

    Program log: Transfer is disabled for this mint

    custom program error: 0x25


    That error code 0x25 (decimal 37) is NonTransferable — a first-class

    error in the Token Extensions Program. This isn't application logic that

    a clever script could bypass. The program itself rejects the instruction.


    What's interesting is that burning still works. The owner can destroy

    tokens they hold. They just can't send them. After burning 3, my balance

    dropped from 10 to 7 — exactly as expected.


    Real-world uses for non-transferable tokens:
    • Course completion certificates
    • KYC / identity verification
    • Hackathon participation badges
    • Employee credentials
    • On-chain reputation scores


    In Web2, preventing credential trading means writing application rules that

    can be worked around. On Solana, the restriction is part of the asset itself.





    What Surprised Me

    The separation of mint and token accounts took longer to internalize than

    I expected.
    I kept confusing "the mint address" with "my token account."

    They're different accounts with different purposes. The mint is the factory.

    The token account is the warehouse.


    The error messages are actually readable. Transfer is disabled for

    this mint is exactly what it sounds like. Coming from debugging opaque

    EVM reverts, this felt refreshingly honest.


    Extensions compose. You can combine metadata + transfer fees in a

    single mint creation command. Day 32 showed me that Token-2022 isn't a

    collection of separate features — it's a composable system.





    What's Next

    I'm on Day 33 of 100. Next up: deeper into Token-2022 extensions

    (interest-bearing tokens, confidential transfers), then moving into

    Anchor and on-chain programs.


    I'm building toward AI agents that can own wallets and execute Solana

    transactions autonomously — and understanding the token layer deeply is

    a prerequisite for that.


    If you're following along, my full build log is at:

    👉 github.com/gopichandchalla16/100-days-of-solana





    Day 34 of #100DaysOfSolana — writing in public, building in public.




    More...
Working...