Token-2022 Basics
9 examples to get you started with Token-2022 (Token Extensions) - 7 basic and 2 intermediate.
Search across all documentation pages
9 examples to get you started with Token-2022 (Token Extensions) - 7 basic and 2 intermediate.
solana config set --url devnet
spl-token --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb --versioncargo add anchor-lang@0.32.1 anchor-spl@0.32.1Token-2022 program ID: TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb.
Same CLI flow as classic SPL with explicit program ID.
spl-token create-token --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb --decimals 6Related: Transfer Fees - fee extension
ATA derivation includes token program ID.
spl-token create-account <MINT> --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEbRelated: Associated Token Accounts (ATAs)
Display shows enabled extensions on mint/account.
spl-token display <MINT> --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEbRelated: Metadata & Metadata Pointer
Baseline Token-2022 without extensions mirrors classic behavior.
spl-token mint <MINT> 1 --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
spl-token transfer <MINT> 0.5 <RECIPIENT> --fund-recipient --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb1 mints one token@solana-program/token-2022 package provides instruction buildersRelated: Migrating from SPL Token
Clients branch on token program before building instructions.
import { createSolanaRpc, address } from "@solana/kit";
const TOKEN_2022 = address("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb");
const rpc = createSolanaRpc("https://api.devnet.solana.com");
const { value } = await rpc.getAccountInfo(address("MINT_PUBKEY")).send();
const isToken2022 = value?.owner === TOKEN_2022;
console.log("Token-2022 mint:", isToken2022);owner field is the token program IDRelated: Reading NFTs (DAS API)
Common revenue extension configured at mint init.
# There is no --enable-transfer-fees flag; the fee is configured by these two flags
spl-token create-token --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb \
--decimals 6 \
--transfer-fee-basis-points 100 \
--transfer-fee-maximum-fee 1--transfer-fee-maximum-fee is a UI amount: 1 caps the fee at one tokenRelated: Transfer Fees
Single code path for Token and Token-2022 mints.
use anchor_lang::prelude::*; // InterfaceAccount, Interface
use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface};
#[derive(Accounts)]
pub struct Deposit<'info> {
pub mint: InterfaceAccount<'info, Mint>,
#[account(mut, token::mint = mint)]
pub user_token: InterfaceAccount<'info, TokenAccount>,
pub token_program: Interface<'info, TokenInterface>,
}InterfaceAccount deserializes both program layoutsanchor_spl::token::{Mint, TokenAccount} - those are classic-Token-only and their owner check rejects Token-2022 accountstoken_program account matching mint ownerRelated: SPL Token in Programs
Custom program invoked on every transfer.
// Transfer hook program ID stored in mint extension
// Token-2022 CPIs to hook with extra accounts from remaining_accountsRelated: Transfer Hooks
| Need | Extension |
|---|---|
| Revenue on transfers | Transfer fee |
| Compliance gating | Default account state + non-transferable |
| On-mint metadata | Metadata pointer |
| Privacy | Confidential transfer |
Related: Token-2022 Best Practices
Stack versions: This page was written for Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, anchor-lang 0.32.1, Rust 1.91.1, @solana/kit 7.0.0, Surfpool 0.12.0, and LiteSVM 0.6.x.
Reviewed by Chris St. John·Last updated Jul 19, 2026