Files
gh-tenequm-claude-plugins-s…/skills/solana-development/references/tokens-2022.md
2025-11-30 09:01:25 +08:00

5.2 KiB

SPL Token-2022 (Token Extensions Program)

Token Extensions Program (Token-2022) guide covering extension types, setup for both Anchor and Native Rust, and practical examples including transfer hooks. Includes extension configuration, space calculation, and initialization patterns.

For related topics, see:

Table of Contents

  1. What are Token Extensions?
  2. Available Extensions
  3. Using Token-2022 in Anchor
  4. Using Token-2022 in Native Rust
  5. Transfer Hook Extension Example

What are Token Extensions?

The Token Extensions Program (Token-2022) provides additional features through extensions. Extensions are optional functionality that can be added to a token mint or token account.

Key Points:

  • Extensions must be enabled during account creation
  • Cannot add extensions after creation
  • Some extensions are incompatible with each other
  • Extensions add state to the tlv_data field

Available Extensions

pub enum ExtensionType {
    TransferFeeConfig,           // Transfer fees
    TransferFeeAmount,           // Withheld fees
    MintCloseAuthority,          // Close mint accounts
    ConfidentialTransferMint,    // Confidential transfers
    DefaultAccountState,         // Default state for new accounts
    ImmutableOwner,              // Cannot change owner
    MemoTransfer,                // Require memos
    NonTransferable,             // Cannot transfer tokens
    InterestBearingConfig,       // Tokens accrue interest
    PermanentDelegate,           // Permanent delegate authority
    TransferHook,                // Custom transfer logic
    MetadataPointer,             // Point to metadata
    TokenMetadata,               // On-chain metadata
    GroupPointer,                // Token groups
    TokenGroup,                  // Group config
    GroupMemberPointer,          // Group membership
    TokenGroupMember,            // Member config
    // ... and more
}

Using Token-2022 in Anchor

use anchor_spl::token_2022::{self, Token2022};
use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface};

#[derive(Accounts)]
pub struct CreateToken2022Mint<'info> {
    #[account(
        init,
        payer = payer,
        mint::decimals = 9,
        mint::authority = mint_authority,
        mint::token_program = token_program,
    )]
    pub mint: InterfaceAccount<'info, Mint>,

    /// CHECK: Mint authority
    pub mint_authority: UncheckedAccount<'info>,

    #[account(mut)]
    pub payer: Signer<'info>,

    pub token_program: Program<'info, Token2022>,
    pub system_program: Program<'info, System>,
}

Note: The anchor-spl crate includes the token_2022_extensions module for working with extensions, but not all extension instructions are fully implemented yet. You may need to manually implement CPI calls for some extensions.


Using Token-2022 in Native Rust

use spl_token_2022::{
    extension::ExtensionType,
    instruction::initialize_mint2,
};

pub fn create_token_2022_mint(
    payer: &AccountInfo,
    mint: &AccountInfo,
    mint_authority: &Pubkey,
    decimals: u8,
    extensions: &[ExtensionType],
) -> ProgramResult {
    // Calculate space needed for extensions
    let mut space = 82; // Base mint size
    for extension in extensions {
        space += extension.get_account_len();
    }

    // Create account with proper size
    // ... (similar to regular mint creation)

    // Initialize extensions
    // Each extension has its own initialization instruction

    // Finally initialize mint
    invoke(
        &initialize_mint2(
            &spl_token_2022::ID,
            mint.key,
            mint_authority,
            None,
            decimals,
        )?,
        &[mint.clone()],
    )?;

    Ok(())
}

Transfer Hook Extension Example (Anchor)

use anchor_lang::prelude::*;
use anchor_spl::token_interface::{TokenAccount, TokenInterface};

#[program]
pub mod transfer_hook {
    use super::*;

    #[interface(spl_transfer_hook_interface::execute)]
    pub fn execute_transfer_hook(
        ctx: Context<TransferHook>,
        amount: u64,
    ) -> Result<()> {
        msg!("Transfer hook called! Amount: {}", amount);
        // Custom transfer logic here
        Ok(())
    }
}

#[derive(Accounts)]
pub struct TransferHook<'info> {
    pub source: InterfaceAccount<'info, TokenAccount>,
    pub destination: InterfaceAccount<'info, TokenAccount>,
    /// CHECK: authority
    pub authority: UncheckedAccount<'info>,
}

Next Steps