Working With Token-2022 Extensions
TL;DR
- - Token-2022 adds extensions to SPL tokens: transfer fees, metadata, confidential transfers, and more
- - Each extension is opt-in and configured at mint creation time
- - Extensions cannot be added after the mint is created
Token-2022 (also called Token Extensions) is an upgraded version of the SPL Token Program that adds configurable extensions to token mints and accounts. Extensions enable features like transfer fees, confidential transfers, on-chain metadata, and permanent delegates — all enforced at the protocol level.
Available extensions
Mint extensions
| Extension | Description |
|---|---|
| Transfer Fee | Automatically charge a fee on every transfer |
| Confidential Transfers | Encrypt token balances and transfer amounts |
| Mint Close Authority | Allow closing the mint account to reclaim rent |
| Interest-Bearing | Display balances with accrued interest (UI only) |
| Non-Transferable | Soulbound tokens that cannot be transferred |
| Permanent Delegate | A delegate that always has authority over any token account |
| Transfer Hook | Call a custom program on every transfer |
| Metadata | Store token name, symbol, and URI on-chain |
| Metadata Pointer | Point to an external metadata account |
| Group / Member | Token group relationships |
| Confidential Transfer Fee | Transfer fee with encrypted amounts |
Account extensions
| Extension | Description |
|---|---|
| Immutable Owner | Token account owner cannot be changed |
| Default Account State | New token accounts start frozen |
| CPI Guard | Restrict certain operations via CPI |
| Memo Required | Require a memo on incoming transfers |
Creating a Token-2022 mint with extensions
Extensions must be specified at mint creation time. They cannot be added later.
import {
createInitializeMintInstruction,
createInitializeTransferFeeConfigInstruction,
ExtensionType,
getMintLen,
TOKEN_2022_PROGRAM_ID,
} from "@solana/spl-token";
// Calculate space needed for mint + extensions
const mintLen = getMintLen([ExtensionType.TransferFeeConfig]);
const lamports = await connection.getMinimumBalanceForRentExemption(mintLen);
// Create account with enough space
const createAccountIx = SystemProgram.createAccount({
fromPubkey: payer.publicKey,
newAccountPubkey: mint.publicKey,
space: mintLen,
lamports,
programId: TOKEN_2022_PROGRAM_ID,
});
// Initialize the transfer fee extension BEFORE initializing the mint
const initTransferFeeIx = createInitializeTransferFeeConfigInstruction(
mint.publicKey,
feeAuthority, // who can update the fee
withdrawAuthority, // who can withdraw collected fees
100, // fee in basis points (1%)
BigInt(1_000_000), // maximum fee per transfer
TOKEN_2022_PROGRAM_ID,
);
// Initialize the mint itself
const initMintIx = createInitializeMintInstruction(
mint.publicKey,
decimals,
mintAuthority,
freezeAuthority,
TOKEN_2022_PROGRAM_ID,
);
Order matters: extension initialization instructions must come before initializeMint.
Transfer fees
The Transfer Fee extension automatically deducts a percentage of every transfer and holds it in the recipient’s token account. The fee is calculated as:
fee = min(amount * feeBasisPoints / 10000, maximumFee)
Collecting fees
Withheld fees accumulate in token accounts. The withdraw authority can harvest them:
import { harvestWithheldTokensToMint } from "@solana/spl-token";
await harvestWithheldTokensToMint(
connection,
payer,
mint.publicKey,
[tokenAccountAddress1, tokenAccountAddress2],
undefined,
TOKEN_2022_PROGRAM_ID,
);
This moves withheld fees from individual token accounts to the mint’s fee vault, where the withdraw authority can then withdraw them.
Metadata extension
Store token metadata directly in the mint account — no separate Metaplex metadata account needed:
import {
createInitializeMetadataPointerInstruction,
createInitializeInstruction,
createUpdateFieldInstruction,
} from "@solana/spl-token-metadata";
// Point metadata to the mint itself
const metadataPointerIx = createInitializeMetadataPointerInstruction(
mint.publicKey,
authority,
mint.publicKey, // metadata stored on the mint account
TOKEN_2022_PROGRAM_ID,
);
// Initialize metadata
const initMetadataIx = createInitializeInstruction({
programId: TOKEN_2022_PROGRAM_ID,
mint: mint.publicKey,
metadata: mint.publicKey,
name: "My Token",
symbol: "MTK",
uri: "https://example.com/metadata.json",
mintAuthority: authority,
updateAuthority: authority,
});
Transfer hooks
The Transfer Hook extension calls a custom program on every token transfer. The hook program can enforce custom logic (KYC checks, royalty enforcement, custom fee distribution).
The hook program must implement the spl_transfer_hook_interface::execute interface. The Token-2022 program calls it via CPI after every successful transfer.
Compatibility considerations
Token-2022 vs. original Token Program
| Aspect | Token Program | Token-2022 |
|---|---|---|
| Program ID | TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA | TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb |
| Extensions | None | Full extension support |
| DeFi compatibility | Universal | Growing (check each protocol) |
| Wallet support | Universal | Most major wallets |
Not all DeFi protocols support Token-2022 tokens yet. Check compatibility before creating a Token-2022 mint for use in DeFi.
Associated Token Accounts
ATAs for Token-2022 use the same derivation as the original Token Program, but with Token-2022’s program ID. Use getAssociatedTokenAddressSync with TOKEN_2022_PROGRAM_ID.
FAQ
Can I add extensions to an existing SPL Token mint? No. Extensions must be configured at mint creation. To add extensions, you would need to create a new Token-2022 mint and migrate tokens.
Do Token-2022 tokens work with Jupiter? Jupiter supports many Token-2022 tokens. Check Jupiter’s documentation for the latest compatibility information, as support varies by extension type.
How much extra rent does a Token-2022 mint cost?
It depends on which extensions are enabled. A basic Token-2022 mint costs similar rent to the original Token Program. Adding extensions increases the account size and therefore the rent-exempt minimum. Use getMintLen([...extensions]) to calculate.
Can I use both Token Program and Token-2022 in the same transaction? Yes. They are separate programs with separate accounts. A single transaction can include instructions for both programs.
Updated: