Skip to main content
// 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
);
After compression, empty token accounts can now be closed to reclaim rent with closeAccount().
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.

Get Started

1

Compress SPL Token Accounts

Install packages in your working directory:
npm install @lightprotocol/stateless.js@alpha \
            @lightprotocol/compressed-token@alpha
Install the CLI globally:
npm install -g @lightprotocol/zk-compression-cli@alpha
# start local test-validator in a separate terminal
light test-validator
In the code examples, use createRpc() without arguments for localnet.
import "dotenv/config";
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";
import { homedir } from "os";
import { readFileSync } from "fs";

// devnet:
const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`;
// localnet:
// const RPC_URL = undefined;
const payer = Keypair.fromSecretKey(
    new Uint8Array(
        JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8"))
    )
);

(async function () {
    // devnet:
    const rpc = createRpc(RPC_URL);
    // localnet:
    // const rpc = createRpc();

    // Setup: Create mint and SPL token account with tokens
    const { mint } = await createMint(rpc, payer, payer.publicKey, 9);
    const owner = Keypair.generate();
    const tokenAccount = await createAssociatedTokenAccount(
        rpc,
        payer,
        mint,
        owner.publicKey,
        undefined,
        TOKEN_PROGRAM_ID
    );
    await mintTo(rpc, payer, mint, tokenAccount, payer, bn(1_000_000_000).toNumber());

    // Compress entire SPL token account balance
    const tx = await compressSplTokenAccount(
        rpc,
        payer,
        mint,
        owner,
        tokenAccount
    );

    console.log("Mint:", mint.toBase58());
    console.log("Tx:", tx);
})();
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

How to Create a Mint Account with Token Pool