Skip to main content
The anchor-spl crate provides CPI clients and helper modules for interacting with Solana’s core SPL programs from your Anchor programs. This page covers the available modules and how to use them effectively.

Installation

Add anchor-spl to your program’s dependencies:
cargo add anchor-spl
Update your Cargo.toml to include the IDL build feature:
Cargo.toml
[features]
idl-build = [
    "anchor-lang/idl-build",
    "anchor-spl/idl-build",
]

[dependencies]
anchor-lang = "0.32.1"
anchor-spl = "0.32.1"

Available Modules

The anchor-spl crate provides the following modules for common SPL program interactions:

Token Programs

ModuleDescription
tokenOriginal Token Program instructions and account types
token_2022Token Extensions Program base instructions
token_2022_extensionsToken Extensions Program extension-specific instructions
token_interfaceAccount types compatible with both Token Program and Token Extensions
associated_tokenAssociated Token Account Program instructions

Other SPL Programs

ModuleDescription
metadataMetaplex Token Metadata Program integration
memoSPL Memo Program integration
stakeSolana Stake Program integration
governanceSPL Governance Program integration

Token Program Integration

The most common use case is interacting with Solana’s Token Programs. The anchor-spl crate provides both program-specific modules (token, token_2022) and a unified interface (token_interface). The token_interface module provides account types that work with both the original Token Program and Token Extensions Program. This is the recommended approach for maximum compatibility:
use anchor_spl::token_interface::{
    self, Mint, TokenAccount, TokenInterface, MintTo
};

#[derive(Accounts)]
pub struct MintTokens<'info> {
    #[account(mut)]
    pub mint: InterfaceAccount<'info, Mint>,
    #[account(mut)]
    pub token_account: InterfaceAccount<'info, TokenAccount>,
    pub authority: Signer<'info>,
    pub token_program: Interface<'info, TokenInterface>,
}

pub fn mint_tokens(ctx: Context<MintTokens>, amount: u64) -> Result<()> {
    token_interface::mint_to(
        CpiContext::new(
            ctx.accounts.token_program.to_account_info(),
            MintTo {
                mint: ctx.accounts.mint.to_account_info(),
                to: ctx.accounts.token_account.to_account_info(),
                authority: ctx.accounts.authority.to_account_info(),
            },
        ),
        amount,
    )?;
    Ok(())
}

Program-Specific Modules

For scenarios requiring specific token program features:
use anchor_spl::token_2022::{
    self, Token2022, transfer_checked, TransferChecked
};

#[derive(Accounts)]
pub struct TransferWithExtensions<'info> {
    #[account(mut)]
    pub from: Account<'info, TokenAccount>,
    #[account(mut)]
    pub to: Account<'info, TokenAccount>,
    pub mint: Account<'info, Mint>,
    pub authority: Signer<'info>,
    pub token_program: Program<'info, Token2022>,
}

Associated Token Accounts

The associated_token module provides helper functions for working with Associated Token Accounts (ATAs):
use anchor_spl::{
    associated_token::AssociatedToken,
    token_interface::{Mint, TokenAccount, TokenInterface},
};

#[derive(Accounts)]
pub struct CreateATA<'info> {
    #[account(mut)]
    pub payer: Signer<'info>,
    #[account(
        init,
        payer = payer,
        associated_token::mint = mint,
        associated_token::authority = payer,
    )]
    pub token_account: InterfaceAccount<'info, TokenAccount>,
    pub mint: InterfaceAccount<'info, Mint>,
    pub token_program: Interface<'info, TokenInterface>,
    pub associated_token_program: Program<'info, AssociatedToken>,
    pub system_program: Program<'info, System>,
}

Token Extensions

Token 2022 introduces extensions that add functionality to mints and token accounts. Use the token_2022_extensions module:
use anchor_spl::token_2022_extensions::{
    transfer_fee, TransferFee, TransferFeeConfig,
};

#[derive(Accounts)]
pub struct InitializeTransferFee<'info> {
    #[account(mut)]
    pub mint: Account<'info, Mint>,
    pub authority: Signer<'info>,
    pub token_program: Program<'info, Token2022>,
}

pub fn initialize_transfer_fee(
    ctx: Context<InitializeTransferFee>,
    transfer_fee_basis_points: u16,
    maximum_fee: u64,
) -> Result<()> {
    transfer_fee::initialize_transfer_fee_config(
        CpiContext::new(
            ctx.accounts.token_program.to_account_info(),
            TransferFeeConfig {
                mint: ctx.accounts.mint.to_account_info(),
            },
        ),
        Some(&ctx.accounts.authority.key()),
        Some(&ctx.accounts.authority.key()),
        transfer_fee_basis_points,
        maximum_fee,
    )?;
    Ok(())
}

Metadata Integration

For NFTs and fungible tokens with metadata, use the metadata module:
use anchor_spl::metadata::{
    Metadata,
    mpl_token_metadata::types::DataV2,
};

#[derive(Accounts)]
pub struct CreateMetadata<'info> {
    #[account(mut)]
    pub metadata: UncheckedAccount<'info>,
    pub mint: Account<'info, Mint>,
    pub mint_authority: Signer<'info>,
    #[account(mut)]
    pub payer: Signer<'info>,
    pub update_authority: SystemAccount<'info>,
    pub system_program: Program<'info, System>,
    pub rent: Sysvar<'info, Rent>,
    pub metadata_program: Program<'info, Metadata>,
}
The metadata module requires enabling the metadata feature in your Cargo.toml:
anchor-spl = { version = "0.32.1", features = ["metadata"] }

Memo Program

Add on-chain memos to your transactions:
use anchor_spl::memo::{Memo, BuildMemo};

#[derive(Accounts)]
pub struct AddMemo<'info> {
    pub signer: Signer<'info>,
    pub memo_program: Program<'info, Memo>,
}

pub fn add_memo(ctx: Context<AddMemo>, memo: String) -> Result<()> {
    let cpi_program = ctx.accounts.memo_program.to_account_info();
    let cpi_accounts = BuildMemo {
        memo: memo.as_bytes(),
    };
    anchor_spl::memo::build_memo(
        CpiContext::new(cpi_program, cpi_accounts),
        memo.as_bytes(),
    )?;
    Ok(())
}

Feature Flags

The anchor-spl crate uses feature flags to control which modules are included:
[dependencies.anchor-spl]
version = "0.32.1"
features = [
    "token",                    # Token Program
    "token_2022",              # Token Extensions Program
    "token_2022_extensions",   # Token Extensions
    "associated_token",        # Associated Token Accounts
    "metadata",                # Metaplex Metadata
    "memo",                    # Memo Program
    "stake",                   # Stake Program
    "governance",              # Governance Program
]
By default, token, token_2022, token_2022_extensions, associated_token, and mint features are enabled.

Common Patterns

Creating and Minting Tokens

use anchor_lang::prelude::*;
use anchor_spl::{
    associated_token::AssociatedToken,
    token_interface::{self, Mint, MintTo, TokenAccount, TokenInterface},
};

declare_id!("YourProgramIDHere");

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

    pub fn create_and_mint(
        ctx: Context<CreateAndMint>,
        amount: u64,
    ) -> Result<()> {
        // Mint tokens to the associated token account
        token_interface::mint_to(
            CpiContext::new(
                ctx.accounts.token_program.to_account_info(),
                MintTo {
                    mint: ctx.accounts.mint.to_account_info(),
                    to: ctx.accounts.token_account.to_account_info(),
                    authority: ctx.accounts.mint_authority.to_account_info(),
                },
            ),
            amount,
        )?;
        Ok(())
    }
}

#[derive(Accounts)]
pub struct CreateAndMint<'info> {
    #[account(mut)]
    pub mint_authority: Signer<'info>,
    #[account(
        mut,
        mint::authority = mint_authority,
        mint::decimals = 9,
    )]
    pub mint: InterfaceAccount<'info, Mint>,
    #[account(
        init_if_needed,
        payer = mint_authority,
        associated_token::mint = mint,
        associated_token::authority = mint_authority,
    )]
    pub token_account: InterfaceAccount<'info, TokenAccount>,
    pub token_program: Interface<'info, TokenInterface>,
    pub associated_token_program: Program<'info, AssociatedToken>,
    pub system_program: Program<'info, System>,
}

Transfer with Authority

use anchor_spl::token_interface::{self, Transfer};

pub fn transfer_tokens(
    ctx: Context<TransferTokens>,
    amount: u64,
) -> Result<()> {
    token_interface::transfer(
        CpiContext::new(
            ctx.accounts.token_program.to_account_info(),
            Transfer {
                from: ctx.accounts.from.to_account_info(),
                to: ctx.accounts.to.to_account_info(),
                authority: ctx.accounts.authority.to_account_info(),
            },
        ),
        amount,
    )?;
    Ok(())
}

#[derive(Accounts)]
pub struct TransferTokens<'info> {
    #[account(mut)]
    pub from: InterfaceAccount<'info, TokenAccount>,
    #[account(mut)]
    pub to: InterfaceAccount<'info, TokenAccount>,
    pub authority: Signer<'info>,
    pub token_program: Interface<'info, TokenInterface>,
}

Best Practices

Always use InterfaceAccount<'info, T> and Interface<'info, TokenInterface> when you want your program to work with both Token Program and Token Extensions Program:
// Good - Works with both programs
pub mint: InterfaceAccount<'info, Mint>,
pub token_program: Interface<'info, TokenInterface>,

// Limited - Only works with original Token Program
pub mint: Account<'info, Mint>,
pub token_program: Program<'info, Token>,
Always validate that token accounts belong to the expected token program:
require_eq!(
    ctx.accounts.token_account.owner,
    ctx.accounts.token_program.key(),
    ErrorCode::InvalidTokenProgram
);
Ensure token accounts belong to the correct mint:
#[account(
    mut,
    token::mint = mint,
    token::authority = authority,
)]
pub token_account: InterfaceAccount<'info, TokenAccount>,

Resources

Build docs developers (and LLMs) love