All articles
solanatoken-2022splstablecoins

Token-2022 extensions: a field guide for builders

Every Token-2022 extension explained — transfer hooks, confidential transfers, interest-bearing, permanent delegate, and the rest. When to use which.

Share
devrels.xyz/a/6short link

The original SPL Token program was deliberately minimal: mint, burn, transfer, freeze. Any behavior beyond that — KYC checks, fees on transfer, interest accrual, confidential balances — meant writing your own program and forking the wallet ecosystem to recognize it. Token-2022 (program ID TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb) is the new token program that ships those behaviors as composable extensions attached to the mint or token account itself.

Token-2022 extensions: a field guide for builders

Every major stablecoin issuer that launched on Solana in 2024-2026 picked Token-2022. So did Ondo (USDY), Paxos (USDG, PYUSD), Agora (AUSD), and a long list of NFT and RWA projects. If you're designing any token primitive today, you start with Token-2022 and opt out only if you have a specific reason.

How extensions work

Extensions are bytes appended to a mint or token account, each marked by a discriminator. The program reads the discriminators to know which extensions to enforce. There's no fork, no proxy contract — extensions are part of the runtime semantics.

Two scopes: mint extensions apply to the whole token (every account behaves the same) and token account extensions apply to individual holders.

The mint extensions

  • TransferHook — every transfer calls into a program you choose. The hook can deny, log, or update state. The basis for KYC, allowlists, royalty enforcement, and on-chain compliance.
  • TransferFee — fixed bps fee withheld on every transfer, claimable by a withdraw authority. Used by tokens that want a built-in tax (royalty, burn, treasury).
  • ConfidentialTransfer — balances and amounts hidden using ElGamal encryption + ZK proofs. The wallet can prove its balance is sufficient without revealing it.
  • InterestBearing — the displayed balance compounds at a configurable rate. The base "principal" is what's actually stored. Used by yieldcoins (USDY-style).
  • PermanentDelegate — a single address that can transfer or burn any balance, ever. The compliance escape hatch for regulated stables.
  • NonTransferable — soul-bound tokens. Mintable, burnable, never transferable. Useful for credentials, badges, KYC tier markers.
  • MetadataPointer + TokenMetadata — metadata stored on the mint itself. No Metaplex hop, no metadata PDA. The new default for NFT and fungible metadata.
  • GroupPointer + MemberPointer — collection membership at the protocol level. The native way to group a set of mints (think NFT collections without Metaplex).
  • MintCloseAuthority — the mint can be closed (rent reclaimed) once supply hits zero. Useful for ephemeral or escrow-style tokens.
  • DefaultAccountState — newly created token accounts start as Frozen by default. Combined with a freeze authority, gates token holding behind your unfreeze logic.
  • ConfidentialMintBurn — extends ConfidentialTransfer to mints and burns. Required if your token wants end-to-end confidentiality including issuance.

The token account extensions

  • ImmutableOwner — the owner of this token account cannot be changed. Always enabled on Associated Token Accounts. Set it on direct accounts to protect against ownership-change attacks.
  • CpiGuard — blocks operations on this account when invoked via CPI. The user's opt-in protection against malicious programs taking actions on their behalf.
  • MemoTransfer — incoming transfers must include a memo. Useful for exchanges or apps that need a reference ID with every deposit.

Creating a mint with extensions

typescript
import { Connection, Keypair, sendAndConfirmTransaction, SystemProgram, Transaction } from "@solana/web3.js"
import {
  createInitializeMintInstruction,
  createInitializeTransferFeeConfigInstruction,
  createInitializeMetadataPointerInstruction,
  getMintLen,
  ExtensionType,
  TOKEN_2022_PROGRAM_ID,
} from "@solana/spl-token"

const extensions = [ExtensionType.TransferFeeConfig, ExtensionType.MetadataPointer]
const mintLen = getMintLen(extensions)
const lamports = await connection.getMinimumBalanceForRentExemption(mintLen)

const tx = new Transaction().add(
  SystemProgram.createAccount({
    fromPubkey: payer.publicKey,
    newAccountPubkey: mint.publicKey,
    space: mintLen,
    lamports,
    programId: TOKEN_2022_PROGRAM_ID,
  }),
  createInitializeTransferFeeConfigInstruction(
    mint.publicKey,
    feeAuthority,
    withdrawAuthority,
    /* fee basis points */ 100,        // 1%
    /* max fee */ BigInt(1_000_000_000),
    TOKEN_2022_PROGRAM_ID,
  ),
  createInitializeMetadataPointerInstruction(
    mint.publicKey,
    payer.publicKey,
    /* metadata address — point at the mint itself */ mint.publicKey,
    TOKEN_2022_PROGRAM_ID,
  ),
  createInitializeMintInstruction(mint.publicKey, 9, mintAuthority, null, TOKEN_2022_PROGRAM_ID),
)

await sendAndConfirmTransaction(connection, tx, [payer, mint])

Important: extensions are immutable once the mint is initialized. Pick your set carefully, because you can't add or remove an extension later.

Use-when / avoid-when

Stablecoin / RWA issuer: TransferHook + PermanentDelegate + DefaultAccountState=Frozen + ConfidentialTransfer (optional). Avoid InterestBearing unless you're shipping a yieldcoin.

NFT collection: MetadataPointer + GroupPointer + TransferHook (royalty enforcement). Avoid TransferFee — Token-2022 fees compound oddly with NFT marketplace flows.

Yieldcoin: InterestBearing + TransferHook + ConfidentialTransfer (optional). Avoid TransferFee — your fee accounting will fight the interest accrual.

Loyalty / credential: NonTransferable + MetadataPointer. Avoid TransferHook — there are no transfers to hook.

Plain SPL-equivalent: no extensions. Token-2022 still works — you just get the new program ID and forward compatibility.

What you give up

Token-2022 mints use a different program ID, which means every wallet, SDK, and indexer has to know about both programs. Most major wallets and SDKs support Token-2022 now, but if you're integrating against a long tail of older tools (an obscure CEX, a legacy explorer, an unmaintained DEX aggregator), the surface area is wider. Check the consumers of your token before you ship.

Also: extensions cost rent. A mint with five extensions costs meaningfully more than a plain SPL mint. Negligible for most projects, worth knowing about if you're minting millions of accounts.

References

If you're designing a new token in 2026, the default answer is Token-2022, and the only real question is which extensions belong on the mint.

Keep reading

Get new articles in your inbox

Technical deep-dives on Solana tooling, infrastructure, and ecosystem. No noise.

Token-2022 extensions: a field guide for builders | devrels.xyz