Skip to main content
The compressSplTokenAccount function compresses the balance of an SPL token account, with an optional remainingAmount parameter to leave tokens in the original account. After compression, empty token accounts can now be closed to reclaim rent with closeAccount().
// Compress entire SPL token account balance
const transactionSignature = await compressSplTokenAccount(
    rpc,
    payer,
    mint, // SPL mint with token pool for compression
    owner,
    tokenAccount, // SPL token account to compress
);
Function Difference and Best Practice:
  • compressSplTokenAccount(tokenAccount, remainingAmount) compresses the entire SPL token
    account balance minus optional remaining amount only to the same owner. Use to migrate complete token
    accounts with optional partial retention.
  • compress(amount, sourceTokenAccount, toAddress) compresses specific amounts from
    source to a specified recipient. Use for transfers and precise amounts. Here is how.

Full Code Example

1

Prerequisites

Make sure you have dependencies and developer environment set up!
Dependencies
npm install @lightprotocol/stateless.js@alpha \
            @lightprotocol/compressed-token@alpha
Developer Environment
By default, all guides use Localnet.
npm install -g @lightprotocol/zk-compression-cli@alpha
# Start a local test validator
light test-validator

## ensure you have the Solana CLI accessible in your system PATH
// createRpc() defaults to local test validator endpoints
import {
  Rpc,
  createRpc,
} from "@lightprotocol/stateless.js";

const connection: Rpc = createRpc();

async function main() {
  let slot = await connection.getSlot();
  console.log(slot);

  let health = await connection.getIndexerHealth(slot);
  console.log(health);
  // "Ok"
}

main();
2

Compress SPL Token Accounts

Run this script to compress the entire SPL token account balance.
compress-full-account.ts
// 1. Setup funded payer and connect to local validator
// 2. Create SPL mint with token pool and mint SPL tokens to ATA
// 3. Call compressSplTokenAccount() to compress entire account
// 4. Verify balances and rent reclaim eligibility

import { Keypair } from '@solana/web3.js';
import { createRpc, bn } from '@lightprotocol/stateless.js';
import {
    createMint,
    compressSplTokenAccount
} from '@lightprotocol/compressed-token';
import {
    createAssociatedTokenAccount,
    mintTo,
    TOKEN_PROGRAM_ID
} from '@solana/spl-token';

async function compressFullAccount() {
    // Step 1: Setup funded payer and connect to local validator
    const rpc = createRpc(); // defaults to localhost:8899
    const payer = Keypair.generate();
    const airdropSignature = await rpc.requestAirdrop(payer.publicKey, 1000000000); // 1 SOL
    await rpc.confirmTransaction(airdropSignature);

    // Step 2: Create SPL mint with token pool and mint SPL tokens to ATA
    const { mint } = await createMint(rpc, payer, payer.publicKey, 9);
    console.log("Mint with token pool created:", mint.toBase58());

    const tokenOwner = Keypair.generate();
    const tokenAccount = await createAssociatedTokenAccount(
        rpc,
        payer,
        mint,
        tokenOwner.publicKey
    );
    console.log("SPL token account created:", tokenAccount.toBase58());

    // Mint SPL tokens to the ATA
    const splAmount = 2_000_000_000; // 2 tokens with 9 decimals
    await mintTo(rpc, payer, mint, tokenAccount, payer, splAmount);
    console.log("SPL tokens minted:", splAmount / 1_000_000_000, "tokens");

    // Check balances before compression
    const splBalanceBefore = await rpc.getTokenAccountBalance(tokenAccount);
    console.log("\nBefore Compression:");
    console.log("SPL token balance:", Number(splBalanceBefore.value.amount) / 1_000_000_000, "tokens");

    // Step 3: Compress entire SPL token account
    const compressTx = await compressSplTokenAccount(
        rpc,
        payer,
        mint, // SPL mint with token pool for compression
        tokenOwner,
        tokenAccount, // SPL token account to compress
    );

    console.log("SPL token account compressed!");
    console.log("Transaction:", compressTx);

    // Step 4: Verify balances and rent reclaim eligibility
    const splBalanceAfter = await rpc.getTokenAccountBalance(tokenAccount);
    const compressedAccountsAfter = await rpc.getCompressedTokenAccountsByOwner(
        tokenOwner.publicKey,
        { mint }
    );

    const totalCompressed = compressedAccountsAfter.items.reduce(
        (sum, account) => sum.add(account.parsed.amount),
        bn(0)
    );

    console.log("\nAfter Compression:");
    console.log("SPL token balance:", Number(splBalanceAfter.value.amount) / 1_000_000_000, "tokens");
    console.log("Compressed accounts:", compressedAccountsAfter.items.length);
    console.log("Total compressed balance:", totalCompressed.toNumber() / 1_000_000_000, "tokens");

    if (Number(splBalanceAfter.value.amount) === 0) {
        console.log("\nSPL token account is now empty and can be closed to reclaim rent!");
    }

    return {
        compressTransaction: compressTx,
        tokenAccount,
        splBalanceAfter: Number(splBalanceAfter.value.amount),
        compressedBalance: totalCompressed.toNumber()
    };
}

compressFullAccount().catch(console.error);
Make sure the SPL mint has a token pool for compression.
The script creates this token pool for you.
For development, you can create a new mint with token pool via createMint() or add a token pool to an existing mint via createTokenPool().

Troubleshooting

The token account doesn’t have enough tokens for the operation.
// Check token account balance before compression
const balance = await rpc.getTokenAccountBalance(tokenAccount);

if (Number(balance.value.amount) === 0) {
    console.log("Token account is empty");
    return;
}

console.log("Available balance:", Number(balance.value.amount));

// Proceed with compression
const compressTx = await compressSplTokenAccount(
    rpc,
    payer,
    mint,
    owner,
    tokenAccount,
);
The remainingAmount parameter exceeds the current account balance.
const balance = await rpc.getTokenAccountBalance(tokenAccount);
const availableAmount = Number(balance.value.amount);
const remainingAmount = bn(500_000_000); // 0.5 tokens

if (remainingAmount.gt(bn(availableAmount))) {
    console.log(`Cannot leave ${remainingAmount.toString()} tokens`);
    console.log(`Available balance: ${availableAmount}`);
    throw new Error("Remaining amount exceeds balance");
}

// Use valid remaining amount
const compressTx = await compressSplTokenAccount(
    rpc,
    payer,
    mint,
    owner,
    tokenAccount,
    remainingAmount, // must be <= balance
);

Advanced Configuration

Compress most tokens while leaving some in SPL format:
import { bn } from '@lightprotocol/stateless.js';

// Leave 100 tokens (0.1 with 9 decimals) in SPL account
const remainingAmount = bn(100_000_000);

const compressTx = await compressSplTokenAccount(
    rpc,
    payer,
    mint,
    owner,
    tokenAccount,
    remainingAmount, // amount to keep in SPL format
);

// Account will retain remainingAmount tokens
Compress several token accounts for the same mint:
const tokenAccounts = [
    { account: new PublicKey("ACCOUNT_1"), owner: owner1 },
    { account: new PublicKey("ACCOUNT_2"), owner: owner2 },
    { account: new PublicKey("ACCOUNT_3"), owner: owner3 },
];

// Compress each account
for (const { account, owner } of tokenAccounts) {
    console.log(`Compressing account: ${account.toBase58()}`);

    try {
        const compressTx = await compressSplTokenAccount(
            rpc,
            payer,
            mint,
            owner,
            account,
        );
        console.log(`Compressed: ${compressTx}`);
    } catch (error) {
        console.log(`Failed: ${error.message}`);
    }
}

Next Steps

Learn how to create and register SPL mints with token pools for compression.