Solana's token system is nothing like what I expected coming from Web2 frontend dev. Here's everything I learned in 5 days from minting my first token to locking one so it can never move again.
Context: where I started
I came in with a Computer Science background, decent TypeScript skills, and a strong understanding of crypto from years of community building and education in Nigeria. But writing blockchain logic? That was new territory.
My goal was simple: understand how tokens actually work on Solana at the code level, not just the concept level. I started on Day 29 with a blank terminal and one mission mint something.
The walkthrough
Day 29–30 First mint with the Token Extensions Program
The first decision was which program to use. Solana has two: the original SPL Token Program and the newer Token Extensions Program (also called Token-2022). I chose Token Extensions from the start because it supports advanced features transfer fees, metadata, and non-transferable tokens without needing a separate program.
import { createMint } from "@solana/spl-token";
const mint = await createMint(
connection,
payer, // fee payer keypair
payer.publicKey, // mint authority
null, // freeze authority
9, // decimals
undefined,
undefined,
TOKEN_2022_PROGRAM_ID // this is what makes it Token Extensions
);
The TOKEN_2022_PROGRAM_ID at the end is everything. Without it, you're on the original program and lose access to extensions entirely.
Day 31 Transfer fees and why they exist
Transfer fees let a token creator take a percentage of every transfer. The implementation is more nuanced than you'd expect fees are withheld on destination accounts, not auto-transferred. You have to harvest them manually.
await createInitializeTransferFeeConfigInstruction(
mint,
transferFeeConfigAuthority,
withdrawWithheldAuthority,
100, // 1% (basis points — so 100 = 1%)
BigInt(1000) // max fee cap in token units
);
What surprised me: The fee is withheld on the recipient's account, not deducted before transfer. The sender sends the full amount, the recipient gets slightly less, and the creator must run a separate harvest instruction to collect. Very different from how payment fees work in Web2.
Day 32 Metadata directly on the token
With the original Token Program, metadata lives on a separate Metaplex account. Token Extensions lets you attach it directly to the mint name, symbol, URI, and custom key-value fields.
await tokenMetadataInitialize(
connection,
payer,
mint,
updateAuthority,
mint, // mint authority signs
"MyToken", // name
"MTK", // symbol
"https://...", // URI pointing to JSON metadata
TOKEN_2022_PROGRAM_ID
);
You can call tokenMetadataUpdateField later to add arbitrary key-value pairs useful for attaching on-chain properties to a token after the fact.
Day 33 Non-transferable (soulbound) tokens
This was my favourite session. A non-transferable token can be minted but never moved. Once it's in a wallet, it stays there. The extension enforces this at the program level not through off-chain rules that can be bypassed.
const extensions = [ExtensionType.NonTransferable];
const mintLen = getMintLen(extensions);
const createAccountIx = SystemProgram.createAccount({
fromPubkey: payer.publicKey,
newAccountPubkey: mintKeypair.publicKey,
space: mintLen,
lamports: await connection.getMinimumBalanceForRentExemption(mintLen),
programId: TOKEN_2022_PROGRAM_ID,
});
const initNonTransferableIx =
createInitializeNonTransferableMintInstruction(
mintKeypair.publicKey,
TOKEN_2022_PROGRAM_ID
);
Use cases: event credentials, certificates, loyalty status, proof of participation anything where you want on-chain proof of something that shouldn't be sold or transferred.
What surprised me
Extension initialization order matters. Extensions must be initialized in a specific sequence during mint creation. Getting it wrong produces cryptic transaction errors. The docs mention it, but it doesn't really land until you see the failure.
Rent-exemption is a real upfront cost. Every account on Solana must hold enough SOL to be rent-exempt. The more extensions you add, the larger the account, and the more lamports you need. You can't stack five extensions and expect the same fee as one account for this in any production setup.
What's next
- Interest-bearing tokens: tokens that accrue value over time, entirely on-chain
- Permanent delegate extension: assigning an authority that can always move tokens regardless of owner
- Building a frontend UI to interact with these programs
- Exploring real-world use cases for communities across Africa: event credentials, loyalty tokens, proof-of-participation badges
If you're also learning Solana or building in the African Web3 space, drop a comment or follow along. Building in public, one day at a time.
Top comments (0)