LiteSVM Docs
Testing with SPL Tokens

Token Operations

Working with token operations - minting, transfers, and account management

Create a Token Mint

A mint is the foundation of any SPL token. It defines the token's properties and controls who can mint new tokens.

use litesvm::LiteSVM;
use litesvm_token::{
    spl_token::native_mint::DECIMALS,
    CreateMint,
};
use solana_sdk::{
    native_token::LAMPORTS_PER_SOL,
    signature::{Keypair, Signer},
};

#[test]
fn test_create_mint() {
    let mut svm = LiteSVM::new();

    // Create payer account and fund it
    let payer = Keypair::new();
    svm.airdrop(&payer.pubkey(), 10 * LAMPORTS_PER_SOL).unwrap();

    // Create a new SPL token mint with the payer as the mint authority
    let mint = CreateMint::new(&mut svm, &payer)
        .authority(&payer.pubkey())
        .decimals(DECIMALS)
        .send()
        .unwrap();
}

Create a Token Account

A token account holds the balance of a specific spl-token for a specific user. To understand the difference between this and an ATA, read this section.

use litesvm::LiteSVM;
use litesvm_token::{
    spl_token::native_mint::DECIMALS,
    CreateMint, CreateAccount,
};
use solana_sdk::{
    native_token::LAMPORTS_PER_SOL,
    signature::{Keypair, Signer},
};

#[test]
fn test_create_mint() {
    let mut svm = LiteSVM::new();

    // Create payer account and fund it
    let payer = Keypair::new();
    svm.airdrop(&payer.pubkey(), 10 * LAMPORTS_PER_SOL).unwrap();

    // Create a new SPL token mint with the payer as the mint authority
    let mint = CreateMint::new(&mut svm, &payer)
        .authority(&payer.pubkey())
        .decimals(DECIMALS)
        .send()
        .unwrap();

    // Create a token account for the payer
    // You must have created a Mint Account to be able to create a Token Account
    let token_account = CreateAccount::new(&mut svm, &payer, &mint)
        .owner(&payer.pubkey())
        .send()
        .unwrap();
}

Create an Associated Token Account

Associated Token Accounts (ATAs) are deterministic addresses for holding tokens for a specific user. To understand the difference between this and an ATA, read this section.

use litesvm::LiteSVM;
use litesvm_token::{
    spl_token::native_mint::DECIMALS,
    CreateMint, CreateAssociatedTokenAccount,
};
use solana_sdk::{
    native_token::LAMPORTS_PER_SOL,
    signature::{Keypair, Signer},
};

#[test]
fn test_create_mint() {
    let mut svm = LiteSVM::new();

    // Create payer account and fund it
    let payer = Keypair::new();
    svm.airdrop(&payer.pubkey(), 10 * LAMPORTS_PER_SOL).unwrap();

    // Create a new SPL token mint with the payer as the mint authority
    let mint = CreateMint::new(&mut svm, &payer)
        .authority(&payer.pubkey())
        .decimals(DECIMALS)
        .send()
        .unwrap();

    // Create an ATA for the payer
    // You must have created a Mint Account to be able to create a Token Account
    let associated_token_account = CreateAssociatedTokenAccount::new(&mut svm, &payer, &mint)
        .owner(&payer.pubkey())
        .send()
        .unwrap();
}

Mint Tokens into a Token Account

Once you have a mint and token accounts, you can mint tokens to those accounts.

use litesvm::LiteSVM;
use litesvm_token::{
    get_spl_account,
    spl_token::{native_mint::DECIMALS, state::Account as TokenAccount},
    CreateAssociatedTokenAccount, CreateMint, MintTo,
};
use solana_sdk::{
    native_token::LAMPORTS_PER_SOL,
    signature::{Keypair, Signer},
};

#[test]
fn test_mint_tokens() {
    let mut svm = LiteSVM::new();

    // Create payer account and fund it
    let payer = Keypair::new();
    svm.airdrop(&payer.pubkey(), 10 * LAMPORTS_PER_SOL).unwrap();

    // Create two users
    let alice = Keypair::new();

    // Create a new SPL token mint with alice as the mint authority
    let mint = CreateMint::new(&mut svm, &payer)
        .authority(&alice.pubkey())
        .decimals(DECIMALS)
        .send()
        .unwrap();

    // Create associated token accounts (ATAs) for alice and bob
    let alice_token_account = CreateAssociatedTokenAccount::new(&mut svm, &payer, &mint)
        .owner(&alice.pubkey())
        .send()
        .unwrap();

    // Mint 1000 tokens to Alice's account
    MintTo::new(&mut svm, &payer, &mint, &alice_token_account, 1000)
        .owner(&alice)
        .send()
        .unwrap();

    // Verify balance
    let alice_account: TokenAccount = get_spl_account(&svm, &alice_token_account).unwrap();
    let alice_balance = alice_account.amount;
    assert_eq!(alice_balance, 1000);
}

Make a Token Transfer

Transfer tokens between accounts using the Transfer instruction.

use litesvm::LiteSVM;
use litesvm_token::{
    get_spl_account,
    spl_token::{native_mint::DECIMALS, state::Account as TokenAccount},
    CreateAssociatedTokenAccount, CreateMint, MintTo, Transfer,
};
use solana_sdk::{
    native_token::LAMPORTS_PER_SOL,
    signature::{Keypair, Signer},
};

#[test]
fn test_token_transfer() {
        let mut svm = LiteSVM::new();

    // Create payer account and fund it
    let payer = Keypair::new();
    svm.airdrop(&payer.pubkey(), 10 * LAMPORTS_PER_SOL).unwrap();

    // Create two users
    let alice = Keypair::new();
    let bob = Keypair::new();

    // Create a new SPL token mint with alice as the mint authority
    let mint = CreateMint::new(&mut svm, &payer)
        .authority(&alice.pubkey())
        .decimals(DECIMALS)
        .send()
        .unwrap();

    // Create associated token accounts (ATAs) for alice and bob
    let alice_token_account = CreateAssociatedTokenAccount::new(&mut svm, &payer, &mint)
        .owner(&alice.pubkey())
        .send()
        .unwrap();

    let bob_token_account = CreateAssociatedTokenAccount::new(&mut svm, &payer, &mint)
        .owner(&bob.pubkey())
        .send()
        .unwrap();

    // Mint 1000 tokens to Alice's account
    MintTo::new(&mut svm, &payer, &mint, &alice_token_account, 1000)
        .owner(&alice)
        .send()
        .unwrap();

    // Transfer 400 tokens from Alice to Bob
    Transfer::new(&mut svm, &payer, &mint, &bob_token_account, 400)
        .source(&alice_token_account)
        .owner(&alice)
        .send()
        .unwrap();

    // Verify balances
    let alice_account: TokenAccount = get_spl_account(&svm, &alice_token_account).unwrap();
    let bob_account: TokenAccount = get_spl_account(&svm, &bob_token_account).unwrap();

    let alice_balance = alice_account.amount;
    let bob_balance = bob_account.amount;

    assert_eq!(alice_balance, 600);
    assert_eq!(bob_balance, 400);

    println!("SPL token transfer successful!");
}

Working with Decimals

Most tokens use decimals to represent fractional amounts. Here's how to handle them correctly:

// For a token with 6 decimals (like USDC)
let decimals = 6;
let one_token = 10_u64.pow(decimals as u32);
let amount_tokens = 100; // Want to send 100 tokens
let amount_raw = amount_tokens * one_token; // 100,000,000 raw units

// For a token with 9 decimals (like SOL)
let decimals = 9;
let one_sol = 10_u64.pow(decimals as u32);
let amount_sol = 1.5; // Want to send 1.5 SOL
let amount_lamports = (amount_sol * one_sol as f64) as u64; // 1,500,000,000 lamports