> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/solana-foundation/anchor/llms.txt
> Use this file to discover all available pages before exploring further.

# Token Basics

> Learn basic SPL token operations in Anchor including creating accounts, minting, and transferring tokens.

## Overview

This guide covers the fundamental token operations you'll need in Anchor programs:

* Creating token accounts
* Minting new tokens
* Transferring tokens between accounts
* Using Program Derived Addresses (PDAs) as authorities

All examples use the `token_interface` module, which works with both the Token Program and Token-2022 Program.

## Creating Token Accounts

Before tokens can be held, you must create a token account. Anchor provides two approaches:

### Associated Token Accounts (Recommended)

Associated Token Accounts (ATAs) are the standard way to create token accounts for users:

```rust theme={null}
use anchor_lang::prelude::*;
use anchor_spl::associated_token::AssociatedToken;
use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface};

#[derive(Accounts)]
pub struct CreateTokenAccount<'info> {
    #[account(mut)]
    pub payer: Signer<'info>,
    #[account(
        init,
        payer = payer,
        associated_token::mint = mint,
        associated_token::authority = payer,
        associated_token::token_program = token_program,
    )]
    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>,
}
```

**Key points:**

* Uses `associated_token` constraints
* Address is deterministically derived from owner + mint
* Requires the `AssociatedToken` program
* Use `init_if_needed` to handle existing accounts (requires `init-if-needed` feature)

### Custom Token Accounts with PDAs

For program-controlled accounts, use custom PDAs:

```rust theme={null}
#[derive(Accounts)]
pub struct CreateVault<'info> {
    #[account(mut)]
    pub payer: Signer<'info>,
    #[account(
        init,
        payer = payer,
        token::mint = mint,
        token::authority = vault, // PDA is its own authority
        token::token_program = token_program,
        seeds = [b"vault", mint.key().as_ref()],
        bump
    )]
    pub vault: InterfaceAccount<'info, TokenAccount>,
    pub mint: InterfaceAccount<'info, Mint>,
    pub token_program: Interface<'info, TokenInterface>,
    pub system_program: Program<'info, System>,
}
```

**Key points:**

* Uses `token` constraints instead of `associated_token`
* Requires `seeds` and `bump` for PDA derivation
* Can set the PDA as its own authority for program-controlled transfers

## Minting Tokens

Minting creates new token supply. Only the mint authority can mint tokens.

### Basic Minting

```rust theme={null}
use anchor_spl::token_interface::{self, Mint, MintTo, TokenAccount, TokenInterface};

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

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

**Important:** The amount should account for decimals. If the mint has 6 decimals, minting 1,000,000 base units equals 1 token.

### Minting with PDA Authority

When the mint authority is a PDA, your program must sign with the PDA's seeds:

```rust theme={null}
pub fn mint_tokens(
    ctx: Context<MintTokens>,
    amount: u64
) -> Result<()> {
    // Get the seeds for the PDA
    let seeds = &[
        b"mint".as_ref(),
        &[ctx.bumps.mint]
    ];
    let signer_seeds = &[&seeds[..]];
    
    let cpi_accounts = MintTo {
        mint: ctx.accounts.mint.to_account_info(),
        to: ctx.accounts.token_account.to_account_info(),
        authority: ctx.accounts.mint.to_account_info(), // PDA signs
    };
    
    let cpi_ctx = CpiContext::new(
        ctx.accounts.token_program.to_account_info(),
        cpi_accounts
    ).with_signer(signer_seeds); // Add signer seeds
    
    token_interface::mint_to(cpi_ctx, amount)?;
    Ok(())
}

#[derive(Accounts)]
pub struct MintTokens<'info> {
    #[account(mut)]
    pub payer: Signer<'info>,
    #[account(
        mut,
        seeds = [b"mint"],
        bump,
        // Mint authority is the PDA itself
    )]
    pub mint: InterfaceAccount<'info, Mint>,
    #[account(mut)]
    pub token_account: InterfaceAccount<'info, TokenAccount>,
    pub token_program: Interface<'info, TokenInterface>,
}
```

**Key points:**

* Use `ctx.bumps.<account_name>` to access the bump seed
* Call `.with_signer()` on the CPI context with the seeds
* The PDA must be set as the mint authority when creating the mint

## Transferring Tokens

Transfers move tokens from one token account to another. Use `transfer_checked` for safety.

### Basic Transfer

```rust theme={null}
use anchor_spl::token_interface::{self, TransferChecked, Mint, TokenAccount, TokenInterface};

pub fn transfer_tokens(
    ctx: Context<TransferTokens>,
    amount: u64
) -> Result<()> {
    let cpi_accounts = TransferChecked {
        from: ctx.accounts.from.to_account_info(),
        mint: ctx.accounts.mint.to_account_info(),
        to: ctx.accounts.to.to_account_info(),
        authority: ctx.accounts.authority.to_account_info(),
    };
    
    let cpi_ctx = CpiContext::new(
        ctx.accounts.token_program.to_account_info(),
        cpi_accounts
    );
    
    let decimals = ctx.accounts.mint.decimals;
    token_interface::transfer_checked(cpi_ctx, amount, decimals)?;
    Ok(())
}

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

**Key points:**

* `transfer_checked` verifies the mint and decimals for safety
* The authority must own the source token account
* Both token accounts must be for the same mint

### Transfer from Program-Controlled Account

When transferring from a PDA-owned account, use PDA signing:

```rust theme={null}
pub fn transfer_from_vault(
    ctx: Context<TransferFromVault>,
    amount: u64
) -> Result<()> {
    // PDA seeds for the vault
    let seeds = &[
        b"vault".as_ref(),
        ctx.accounts.mint.key().as_ref(),
        &[ctx.bumps.vault]
    ];
    let signer_seeds = &[&seeds[..]];
    
    let cpi_accounts = TransferChecked {
        from: ctx.accounts.vault.to_account_info(),
        mint: ctx.accounts.mint.to_account_info(),
        to: ctx.accounts.destination.to_account_info(),
        authority: ctx.accounts.vault.to_account_info(), // PDA signs
    };
    
    let cpi_ctx = CpiContext::new(
        ctx.accounts.token_program.to_account_info(),
        cpi_accounts
    ).with_signer(signer_seeds);
    
    let decimals = ctx.accounts.mint.decimals;
    token_interface::transfer_checked(cpi_ctx, amount, decimals)?;
    Ok(())
}

#[derive(Accounts)]
pub struct TransferFromVault<'info> {
    pub mint: InterfaceAccount<'info, Mint>,
    #[account(
        mut,
        seeds = [b"vault", mint.key().as_ref()],
        bump,
    )]
    pub vault: InterfaceAccount<'info, TokenAccount>,
    #[account(mut)]
    pub destination: InterfaceAccount<'info, TokenAccount>,
    pub token_program: Interface<'info, TokenInterface>,
}
```

## Complete Example

Here's a complete example showing mint creation, token account creation, minting, and transferring:

```rust title="lib.rs" theme={null}
use anchor_lang::prelude::*;
use anchor_spl::{
    associated_token::AssociatedToken,
    token_interface::{self, Mint, MintTo, TokenAccount, TokenInterface, TransferChecked},
};

declare_id!("YourProgramIDHere");

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

    pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
        msg!("Mint created: {}", ctx.accounts.mint.key());
        Ok(())
    }

    pub fn mint_tokens(ctx: Context<MintTokens>, amount: u64) -> Result<()> {
        let seeds = &[b"mint".as_ref(), &[ctx.bumps.mint]];
        let signer_seeds = &[&seeds[..]];

        let cpi_accounts = MintTo {
            mint: ctx.accounts.mint.to_account_info(),
            to: ctx.accounts.token_account.to_account_info(),
            authority: ctx.accounts.mint.to_account_info(),
        };

        let cpi_ctx = CpiContext::new(
            ctx.accounts.token_program.to_account_info(),
            cpi_accounts,
        )
        .with_signer(signer_seeds);

        token_interface::mint_to(cpi_ctx, amount)?;
        Ok(())
    }

    pub fn transfer_tokens(ctx: Context<TransferTokens>, amount: u64) -> Result<()> {
        let cpi_accounts = TransferChecked {
            from: ctx.accounts.from.to_account_info(),
            mint: ctx.accounts.mint.to_account_info(),
            to: ctx.accounts.to.to_account_info(),
            authority: ctx.accounts.authority.to_account_info(),
        };

        let cpi_ctx = CpiContext::new(
            ctx.accounts.token_program.to_account_info(),
            cpi_accounts,
        );

        token_interface::transfer_checked(cpi_ctx, amount, ctx.accounts.mint.decimals)?;
        Ok(())
    }
}

#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(mut)]
    pub payer: Signer<'info>,
    #[account(
        init,
        payer = payer,
        mint::decimals = 6,
        mint::authority = mint,
        seeds = [b"mint"],
        bump
    )]
    pub mint: InterfaceAccount<'info, Mint>,
    pub token_program: Interface<'info, TokenInterface>,
    pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct MintTokens<'info> {
    #[account(mut)]
    pub payer: Signer<'info>,
    #[account(
        mut,
        seeds = [b"mint"],
        bump
    )]
    pub mint: InterfaceAccount<'info, Mint>,
    #[account(
        init_if_needed,
        payer = payer,
        associated_token::mint = mint,
        associated_token::authority = payer,
        associated_token::token_program = token_program,
    )]
    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>,
}

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

## Common Patterns

### Checking Token Balances

```rust theme={null}
let balance = ctx.accounts.token_account.amount;
require!(balance >= amount, ErrorCode::InsufficientBalance);
```

### Validating Mint and Owner

```rust theme={null}
#[account(
    constraint = token_account.mint == mint.key() @ ErrorCode::InvalidMint,
    constraint = token_account.owner == authority.key() @ ErrorCode::InvalidOwner,
)]
pub token_account: InterfaceAccount<'info, TokenAccount>,
```

### Burning Tokens

```rust theme={null}
use anchor_spl::token_interface::{self, Burn};

let cpi_accounts = Burn {
    mint: ctx.accounts.mint.to_account_info(),
    from: ctx.accounts.token_account.to_account_info(),
    authority: ctx.accounts.authority.to_account_info(),
};

let cpi_ctx = CpiContext::new(
    ctx.accounts.token_program.to_account_info(),
    cpi_accounts
);

token_interface::burn(cpi_ctx, amount)?;
```

## Best Practices

1. **Use `transfer_checked` over `transfer`** - It validates mint and decimals
2. **Use `init_if_needed` for ATAs** - Handles existing accounts gracefully (requires feature flag)
3. **Store bump seeds** - Use `ctx.bumps.<account>` instead of recomputing
4. **Use `InterfaceAccount` and `Interface`** - Works with both token programs
5. **Validate token account relationships** - Check mint and owner match expectations
6. **Handle decimals correctly** - Remember amounts are in base units

## Next Steps

* [Token Extensions](/tokens/token-extensions) - Learn about Token-2022 extensions like metadata, transfer hooks, and more
* [Token Overview](/tokens/overview) - Review token concepts and terminology
