Skip to main content
Side-by-side mapping of Light Token and SPL/Solana instructions. The Light Token SDK calls compression RPC methods internally — you use the same interface functions for all token operations.

RPC

The Light Token SDK calls Photon RPC methods internally via interface functions like transferInterface and getAtaInterface. Call them directly for custom indexing, block explorers, or debugging.
API reference

Create an RPC connection

Light Token

import { createRpc, Rpc } from "@lightprotocol/stateless.js";

// Helius exposes Solana and Photon RPC endpoints through a single URL
const rpc: Rpc = createRpc(RPC_ENDPOINT, RPC_ENDPOINT);

Solana

import { Connection } from "@solana/web3.js";

const connection = new Connection(RPC_ENDPOINT);

Get balance

Fetch the parsed state of a light token account, including hot and cold balances.
Guide | Example

Light Token

import {
  getAssociatedTokenAddressInterface,
  getAtaInterface,
} from "@lightprotocol/compressed-token/unified";

const ata = getAssociatedTokenAddressInterface(mint, owner);
const account = await getAtaInterface(rpc, ata, owner, mint);

console.log(account.parsed.amount);

SPL

import { getAccount } from "@solana/spl-token";

const account = await getAccount(connection, ata);

console.log(account.amount);

Transaction history

Fetch merged and deduplicated transaction history across on-chain and compressed transactions.
Guide | Example

Light Token

const result = await rpc.getSignaturesForOwnerInterface(owner);

console.log(result.signatures); // Merged + deduplicated
console.log(result.solana);     // On-chain txs only
console.log(result.compressed); // Compressed txs only

SPL

const signatures = await connection.getSignaturesForAddress(ata);

TypeScript client

@lightprotocol/compressed-token interface functions match the @solana/spl-token API. Use rpc (from createRpc) instead of connection.

Instruction API return types

Some instruction builders return a single instruction, others return a 2D array (TransactionInstruction[][]) where each inner array is one transaction. The 2D shape handles cases where cold balances need loading in a separate preceding transaction.
Return typeAPIs
TransactionInstructioncreateAssociatedTokenAccountInterfaceInstruction, createMintToInterfaceInstruction, createLightTokenTransferInstruction, createWrapInstruction
TransactionInstruction[][]createTransferInterfaceInstructions, createLoadAtaInstructions, createUnwrapInstructions
For batch builders, loop over the array and build a transaction from each entry:
for (const ixs of instructions) {
  const tx = new Transaction().add(...ixs);
  await sendAndConfirmTransaction(rpc, tx, [payer, owner]);
}

Create mint

Create a mint account with optional token metadata. The light token program sponsors rent-exemption.
Guide | Example | Source

Light Token

import { createMintInterface } from "@lightprotocol/compressed-token";

const { mint } = await createMintInterface(
  rpc,
  payer,
  mintAuthority,
  freezeAuthority,
  decimals,
  mintKeypair
);

SPL

import { createMint } from "@solana/spl-token";

const mint = await createMint(
  connection,
  payer,
  mintAuthority,
  freezeAuthority,
  decimals
);

Create associated token account

Create an associated token account. Light-ATAs hold balances of light, SPL, or Token 2022 mints.
Guide | Example | Source

Light Token

import { createAtaInterface } from "@lightprotocol/compressed-token";

const ata = await createAtaInterface(
  rpc,
  payer,
  mint,
  owner
);

SPL

import { getOrCreateAssociatedTokenAccount } from "@solana/spl-token";

const ata = await getOrCreateAssociatedTokenAccount(
  connection,
  payer,
  mint,
  owner
);

Mint tokens

Mint tokens to a destination account. Auto-detects the token program.
Guide | Example | Source

Light Token

import { mintToInterface } from "@lightprotocol/compressed-token";

const tx = await mintToInterface(
  rpc,
  payer,
  mint,
  destination,
  mintAuthority,
  amount
);

SPL

import { mintTo } from "@solana/spl-token";

const tx = await mintTo(
  connection,
  payer,
  mint,
  destination,
  mintAuthority,
  amount
);

Transfer tokens

Transfer tokens between accounts. Handles loading cold state, creating the recipient ATA, and transferring in one call. Supports Light-to-Light, SPL-to-Light, and Light-to-SPL transfers.
Guide | Example | Source

Light Token

import { transferInterface } from "@lightprotocol/compressed-token/unified";

const tx = await transferInterface(
  rpc,
  payer,
  sourceAta,
  mint,
  recipient,
  owner,
  amount
);

SPL

import { transfer } from "@solana/spl-token";

const tx = await transfer(
  connection,
  payer,
  sourceAta,
  destinationAta,
  owner,
  amount
);
When a user receives tokens from multiple senders, each compressed account is stored separately. Use createTransferInterfaceInstructions with sliceLast to load all fragments in parallel before transferring.
Guide | Example
import { createTransferInterfaceInstructions } from "@lightprotocol/compressed-token/unified";
import { sliceLast } from "@lightprotocol/stateless.js";

const instructions = await createTransferInterfaceInstructions(
  rpc, payer.publicKey, mint, amount, owner.publicKey, recipient
);

const { rest: loadInstructions, last: transferInstructions } = sliceLast(instructions);

// Load all compressed accounts in parallel
await Promise.all(
  loadInstructions.map((ixs) => {
    const tx = new Transaction().add(...ixs);
    tx.sign(payer, owner);
    return sendAndConfirmTransaction(rpc, tx);
  })
);

// Then transfer
const transferTx = new Transaction().add(...transferInstructions);
transferTx.sign(payer, owner);
await sendAndConfirmTransaction(rpc, transferTx);
Load creates the ATA if needed and loads any compressed state into it. Light Token accounts auto-compress inactive accounts. Before any action, the SDK detects cold balances and adds instructions to load them.
Guide | Example | Source
import {
  loadAta,
  getAssociatedTokenAddressInterface,
} from "@lightprotocol/compressed-token/unified";

const ata = getAssociatedTokenAddressInterface(mint, recipient);

const sig = await loadAta(rpc, ata, recipient, mint, payer);
Wrap moves tokens from an SPL/Token 2022 account to a Light Token ATA (hot balance). Unwrap moves tokens back to SPL — use this to compose with applications that do not yet support light-token.
Guide | Example (wrap) | Example (unwrap)
import { getAssociatedTokenAddressSync } from "@solana/spl-token";
import {
  wrap,
  getAssociatedTokenAddressInterface,
} from "@lightprotocol/compressed-token/unified";

const splAta = getAssociatedTokenAddressSync(mint, owner.publicKey);
const tokenAta = getAssociatedTokenAddressInterface(mint, owner.publicKey);

await wrap(rpc, payer, splAta, tokenAta, owner, mint, amount);
import { getAssociatedTokenAddressSync } from "@solana/spl-token";
import { unwrap } from "@lightprotocol/compressed-token/unified";

const splAta = getAssociatedTokenAddressSync(mint, owner.publicKey);

await unwrap(rpc, payer, splAta, owner, mint, amount);

Approve delegate

Approve a delegate to spend tokens up to a specified amount.
Guide | Example

Light Token

import { approve } from "@lightprotocol/compressed-token";

const tx = await approve(
  rpc,
  payer,
  mint,
  amount,
  owner,
  delegate
);

SPL

import { approve } from "@solana/spl-token";

const tx = await approve(
  connection,
  payer,
  source,
  delegate,
  owner,
  amount
);

Revoke delegate

Remove all delegate permissions.
Guide | Example

Light Token

import { revoke } from "@lightprotocol/compressed-token";

const tx = await revoke(
  rpc,
  payer,
  delegatedAccounts,
  owner
);

SPL

import { revoke } from "@solana/spl-token";

const tx = await revoke(
  connection,
  payer,
  source,
  owner
);

Rust client

light_token_client uses builder structs with .instruction() or .execute(). All instructions map 1:1 to spl_token::instruction::*.

Create mint

Guide | Example | Source

Light Token

use light_token::instruction::CreateMint;

let ix = CreateMint::new(
    params,
    mint_seed.pubkey(),
    payer.pubkey(),
    address_tree.tree,
    output_queue,
)
.instruction()?;

SPL

use spl_token::instruction::initialize_mint;

let ix = initialize_mint(
    &spl_token::id(),
    &mint.pubkey(),
    &mint_authority,
    Some(&freeze_authority),
    decimals,
)?;

Create associated token account

Guide | Example | Source

Light Token

use light_token::instruction::CreateAssociatedTokenAccount;

let ix = CreateAssociatedTokenAccount::new(
    payer.pubkey(),
    owner.pubkey(),
    mint,
)
.instruction()?;

SPL

use spl_associated_token_account::instruction::create_associated_token_account;

let ix = create_associated_token_account(
    &payer.pubkey(),
    &owner.pubkey(),
    &mint,
    &spl_token::id(),
);

Create token account

Guide | Example | Source

Light Token

use light_token::instruction::CreateTokenAccount;

let ix = CreateTokenAccount::new(
    payer.pubkey(),
    account.pubkey(),
    mint,
    owner,
)
.instruction()?;

SPL

use spl_token::instruction::initialize_account;

let ix = initialize_account(
    &spl_token::id(),
    &account,
    &mint,
    &owner,
)?;

Mint tokens

Guide | Example | Source

Light Token

use light_token::instruction::MintTo;

let ix = MintTo {
    mint,
    destination,
    amount,
    authority: payer.pubkey(),
    max_top_up: None,
    fee_payer: None,
}
.instruction()?;

SPL

use spl_token::instruction::mint_to;

let ix = mint_to(
    &spl_token::id(),
    &mint,
    &destination,
    &mint_authority,
    &[],
    amount,
)?;

Transfer (interface)

Unified interface that routes across Light Token, SPL, and Token 2022 accounts.
Guide | Example | Source

Light Token

use light_token::instruction::TransferInterface;

let ix = TransferInterface {
    source,
    destination,
    amount,
    decimals,
    authority: payer.pubkey(),
    payer: payer.pubkey(),
    spl_interface: None,
    max_top_up: None,
    source_owner: LIGHT_TOKEN_PROGRAM_ID,
    destination_owner: LIGHT_TOKEN_PROGRAM_ID,
}
.instruction()?;

SPL

use spl_token::instruction::transfer;

let ix = transfer(
    &spl_token::id(),
    &source,
    &destination,
    &authority,
    &[],
    amount,
)?;

Transfer (checked)

Transfer with decimal validation.
Guide | Example | Source

Light Token

use light_token::instruction::TransferChecked;

let ix = TransferChecked {
    source,
    destination,
    mint,
    amount,
    decimals,
    authority: payer.pubkey(),
}
.instruction()?;

SPL

use spl_token::instruction::transfer_checked;

let ix = transfer_checked(
    &spl_token::id(),
    &source,
    &mint,
    &destination,
    &authority,
    &[],
    amount,
    decimals,
)?;

Burn

Permanently destroy tokens and reduce mint supply.
Guide | Example | Source

Light Token

use light_token::instruction::Burn;

let ix = Burn {
    source,
    mint,
    amount,
    authority: payer.pubkey(),
    max_top_up: None,
    fee_payer: None,
}
.instruction()?;

SPL

use spl_token::instruction::burn;

let ix = burn(
    &spl_token::id(),
    &source,
    &mint,
    &authority,
    &[],
    amount,
)?;

Freeze and thaw

Freeze prevents all transfers, burns, or closes. Only the freeze authority can freeze or thaw.
Guide | Example (freeze) | Example (thaw) | Source

Light Token

use light_token::instruction::Freeze;

let ix = Freeze {
    token_account: ata,
    mint,
    freeze_authority: payer.pubkey(),
}
.instruction()?;
use light_token::instruction::Thaw;

let ix = Thaw {
    token_account: ata,
    mint,
    freeze_authority: payer.pubkey(),
}
.instruction()?;

SPL

use spl_token::instruction::{freeze_account, thaw_account};

let ix = freeze_account(
    &spl_token::id(),
    &account,
    &mint,
    &freeze_authority,
    &[],
)?;

let ix = thaw_account(
    &spl_token::id(),
    &account,
    &mint,
    &freeze_authority,
    &[],
)?;

Approve and revoke

Approve a delegate or remove all delegate permissions.
Guide | Example (approve) | Example (revoke) | Source

Light Token

use light_token::instruction::Approve;

let ix = Approve {
    token_account: ata,
    delegate: delegate.pubkey(),
    owner: payer.pubkey(),
    amount,
}
.instruction()?;
use light_token::instruction::Revoke;

let ix = Revoke {
    token_account: ata,
    owner: payer.pubkey(),
}
.instruction()?;

SPL

use spl_token::instruction::{approve, revoke};

let ix = approve(
    &spl_token::id(),
    &source,
    &delegate,
    &owner,
    &[],
    amount,
)?;

let ix = revoke(
    &spl_token::id(),
    &source,
    &owner,
    &[],
)?;

Close token account

Close an empty token account and reclaim lamports.
Guide | Example | Source

Light Token

use light_token::instruction::{CloseAccount, LIGHT_TOKEN_PROGRAM_ID};

let ix = CloseAccount::new(
    LIGHT_TOKEN_PROGRAM_ID,
    account,
    destination,
    owner,
)
.instruction()?;

SPL

use spl_token::instruction::close_account;

let ix = close_account(
    &spl_token::id(),
    &account,
    &destination,
    &owner,
    &[],
)?;

Wrap and unwrap

Move tokens between SPL/Token 2022 and Light Token accounts.
Guide | Example (wrap) | Example (unwrap) | Source

Light Token (no SPL equivalent)

use light_token_client::actions::wrap::Wrap;

Wrap {
    rpc: &mut rpc,
    payer: &payer,
    spl_ata,
    light_ata,
    owner: &owner,
    mint,
    amount,
}
.execute()
.await?;
use light_token_client::actions::unwrap::Unwrap;

Unwrap {
    rpc: &mut rpc,
    payer: &payer,
    spl_ata,
    owner: &owner,
    mint,
    amount,
}
.execute()
.await?;

Program CPI

light_token::instruction::*Cpi structs map 1:1 to spl_token::instruction::*. Use .invoke() for external signers or .invoke_signed() for PDA signers. Add .rent_free() to sponsor rent-exemption on account creation.

CreateMintCpi

Create a rent-free mint account via CPI.
Guide | Example | Source

Light Token

use light_token::instruction::CreateMintCpi;

CreateMintCpi {
    mint_seed: mint_seed.clone(),
    authority: authority.clone(),
    payer: payer.clone(),
    address_tree: address_tree.clone(),
    output_queue: output_queue.clone(),
    compressible_config: compressible_config.clone(),
    mint: mint.clone(),
    rent_sponsor: rent_sponsor.clone(),
    system_accounts,
    cpi_context: None,
    cpi_context_account: None,
    params,
}
.invoke()?

SPL

use spl_token::instruction::initialize_mint;

let ix = initialize_mint(
    &spl_token::id(),
    &mint.pubkey(),
    &mint_authority,
    Some(&freeze_authority),
    decimals,
)?;

invoke(&ix, &[mint, rent_sysvar])?;

CreateAssociatedAccountCpi

Create a rent-free associated token account via CPI.
Guide | Example | Source

Light Token

use light_token::instruction::CreateAssociatedAccountCpi;

CreateAssociatedAccountCpi {
    payer: payer.clone(),
    owner: owner.clone(),
    mint: mint.clone(),
    ata: associated_token_account.clone(),
    bump,
}
.rent_free(
    compressible_config.clone(),
    rent_sponsor.clone(),
    system_program.clone(),
)
.invoke()?

SPL

use spl_associated_token_account::instruction::create_associated_token_account;

let ix = create_associated_token_account(
    &payer.pubkey(),
    &owner.pubkey(),
    &mint,
    &spl_token::id(),
);

invoke(&ix, &[payer, owner, mint])?;

CreateTokenAccountCpi

Create a rent-free token account via CPI.
Guide | Example | Source

Light Token

use light_token::instruction::CreateTokenAccountCpi;

CreateTokenAccountCpi {
    payer: payer.clone(),
    account: account.clone(),
    mint: mint.clone(),
    owner,
}
.rent_free(
    compressible_config.clone(),
    rent_sponsor.clone(),
    system_program.clone(),
    token_program.key,
)
.invoke()?

SPL

use spl_token::instruction::initialize_account;

let ix = initialize_account(
    &spl_token::id(),
    &account,
    &mint,
    &owner,
)?;

invoke(&ix, &[account, mint, owner])?;

MintToCpi

Mint tokens to a destination account via CPI.
Guide | Example | Source

Light Token

use light_token::instruction::MintToCpi;

MintToCpi {
    mint: mint.clone(),
    destination: destination.clone(),
    authority: authority.clone(),
    amount,
    fee_payer: None,
    max_top_up: None,
}
.invoke()?

SPL

use spl_token::instruction::mint_to;

let ix = mint_to(
    &spl_token::id(),
    &mint,
    &destination,
    &mint_authority,
    &[],
    amount,
)?;

invoke(&ix, &[mint, destination, authority])?;

TransferCheckedCpi

Transfer with decimal validation via CPI.
Guide | Example | Source

Light Token

use light_token::instruction::TransferCheckedCpi;

TransferCheckedCpi {
    source: source.clone(),
    destination: destination.clone(),
    mint: mint.clone(),
    authority: authority.clone(),
    amount,
    decimals,
}
.invoke()?

SPL

use spl_token::instruction::transfer_checked;

let ix = transfer_checked(
    &spl_token::id(),
    &source,
    &mint,
    &destination,
    &authority,
    &[],
    amount,
    decimals,
)?;

invoke(&ix, &[source, mint, destination, authority])?;

BurnCpi

Burn tokens via CPI.
Guide | Example | Source

Light Token

use light_token::instruction::BurnCpi;

BurnCpi {
    source: source.clone(),
    mint: mint.clone(),
    authority: authority.clone(),
    amount,
}
.invoke()?

SPL

use spl_token::instruction::burn;

let ix = burn(
    &spl_token::id(),
    &source,
    &mint,
    &authority,
    &[],
    amount,
)?;

invoke(&ix, &[source, mint, authority])?;

FreezeCpi and ThawCpi

Freeze or thaw a token account via CPI.
Guide | Example (freeze) | Example (thaw) | Source

Light Token

use light_token::instruction::FreezeCpi;

FreezeCpi {
    token_account: token_account.clone(),
    mint: mint.clone(),
    freeze_authority: freeze_authority.clone(),
}
.invoke()?
use light_token::instruction::ThawCpi;

ThawCpi {
    token_account: token_account.clone(),
    mint: mint.clone(),
    freeze_authority: freeze_authority.clone(),
}
.invoke()?

SPL

use spl_token::instruction::{freeze_account, thaw_account};

let ix = freeze_account(
    &spl_token::id(), &account, &mint, &freeze_authority, &[],
)?;
invoke(&ix, &[account, mint, freeze_authority])?;

let ix = thaw_account(
    &spl_token::id(), &account, &mint, &freeze_authority, &[],
)?;
invoke(&ix, &[account, mint, freeze_authority])?;

ApproveCpi and RevokeCpi

Approve a delegate or revoke permissions via CPI.
Guide | Example (approve) | Example (revoke) | Source

Light Token

use light_token::instruction::ApproveCpi;

ApproveCpi {
    token_account: token_account.clone(),
    delegate: delegate.clone(),
    owner: owner.clone(),
    amount,
}
.invoke()?
use light_token::instruction::RevokeCpi;

RevokeCpi {
    token_account: token_account.clone(),
    owner: owner.clone(),
}
.invoke()?

SPL

use spl_token::instruction::{approve, revoke};

let ix = approve(
    &spl_token::id(), &source, &delegate, &owner, &[], amount,
)?;
invoke(&ix, &[source, delegate, owner])?;

let ix = revoke(
    &spl_token::id(), &source, &owner, &[],
)?;
invoke(&ix, &[source, owner])?;

CloseAccountCpi

Close a token account and reclaim lamports via CPI.
Guide | Example | Source

Light Token

use light_token::instruction::CloseAccountCpi;

CloseAccountCpi {
    account: account.clone(),
    destination: destination.clone(),
    authority: authority.clone(),
}
.invoke()?

SPL

use spl_token::instruction::close_account;

let ix = close_account(
    &spl_token::id(), &account, &destination, &owner, &[],
)?;
invoke(&ix, &[account, destination, owner])?;

Anchor macros

#[light_account(...)] replaces #[account(...)] for rent-free account initialization. Add #[light_program] above #[program] to enable compression.

Create mint

Guide | Example | Source

Light Token

#[light_account(init,
    mint::signer = mint_signer,
    mint::authority = fee_payer,
    mint::decimals = 9,
    mint::seeds = &[MINT_SIGNER_SEED, self.authority.to_account_info().key.as_ref()],
    mint::bump = params.mint_signer_bump
)]
pub mint: UncheckedAccount<'info>,

Anchor

#[account(
    init,
    payer = fee_payer,
    mint::decimals = 9,
    mint::authority = fee_payer,
)]
pub mint: InterfaceAccount<'info, Mint>,

Create mint with metadata

Metadata fields are declared inline instead of requiring a separate CPI.
Guide | Example | Source

Light Token

#[light_account(init,
    mint::signer = mint_signer,
    mint::authority = fee_payer,
    mint::decimals = 9,
    mint::seeds = &[MINT_SIGNER_SEED, self.authority.to_account_info().key.as_ref()],
    mint::bump = params.mint_signer_bump,
    mint::name = params.name.clone(),
    mint::symbol = params.symbol.clone(),
    mint::uri = params.uri.clone(),
    mint::update_authority = authority
)]
pub mint: UncheckedAccount<'info>,

Anchor + Token 2022

#[account(
    init,
    payer = fee_payer,
    mint::decimals = 9,
    mint::authority = fee_payer,
    extensions::metadata_pointer::authority = fee_payer,
    extensions::metadata_pointer::metadata_address = mint_account,
)]
pub mint_account: InterfaceAccount<'info, Mint>,

// Metadata requires a separate CPI:
token_metadata_initialize(
    cpi_ctx,
    params.name,
    params.symbol,
    params.uri,
)?;

Create associated token account

Guide | Example | Source

Light Token

#[light_account(
    init,
    associated_token::authority = ata_owner,
    associated_token::mint = ata_mint,
    associated_token::bump = params.ata_bump
)]
pub ata: UncheckedAccount<'info>,

Anchor

#[account(
    init,
    payer = fee_payer,
    associated_token::mint = mint,
    associated_token::authority = owner,
)]
pub ata: Account<'info, TokenAccount>,

Create token account (vault)

Guide | Example | Source

Light Token

#[account(
    mut,
    seeds = [VAULT_SEED, mint.key().as_ref()],
    bump,
)]
#[light_account(init,
    token::authority = [VAULT_SEED, self.mint.key()],
    token::mint = mint,
    token::owner = vault_authority,
    token::bump = params.vault_bump
)]
pub vault: UncheckedAccount<'info>,

Anchor

#[account(
    init,
    payer = fee_payer,
    token::mint = mint,
    token::authority = authority,
)]
pub vault: Account<'info, TokenAccount>,

Light-PDA init

Add #[light_program] above #[program], derive LightAccount on state structs with a compression_info field, and derive LightAccounts on the accounts struct.
Guide | Example | Source

Light Token

use light_account::{
    CompressionInfo, LightAccount, LightAccounts,
    CreateAccountsProof, derive_light_cpi_signer,
    light_program, CpiSigner, LightDiscriminator,
};

#[light_program]
#[program]
pub mod my_program {
    // instruction logic unchanged
}

#[derive(LightAccount, LightDiscriminator)]
pub struct MyState {
    pub compression_info: CompressionInfo,
    pub authority: Pubkey,
    pub data: u64,
}

#[derive(LightAccounts)]
pub struct Initialize<'info> {
    #[light_account(init)]
    pub state: UncheckedAccount<'info>,
    #[account(mut)]
    pub pda_rent_sponsor: AccountInfo<'info>,
    // ...
}

Anchor

#[program]
pub mod my_program {
    // instruction logic unchanged
}

#[account]
pub struct MyState {
    pub authority: Pubkey,
    pub data: u64,
}

#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(
        init,
        payer = payer,
        space = 8 + MyState::INIT_SPACE,
    )]
    pub state: Account<'info, MyState>,
    #[account(mut)]
    pub payer: Signer<'info>,
    pub system_program: Program<'info, System>,
}