Skip to main content
The mergeTokenAccounts() function consolidates multiple compressed accounts of the same mint into a single compressed account. The function
  1. consumes multiple compressed token accounts (up to 8 accounts), and
  2. creates a single output compressed token account with combined balance for the owner.
State trees where compressed account’s are stored, are append only. mergeTokenAccounts() reduces account fragmentation to simplify balance calculations from getCompressedTokenAccountsByOwner.
// Combines multiple compressed token accounts into single compressed account
const transactionSignature = await mergeTokenAccounts(
    rpc,
    payer,
    mint, // SPL mint with token pool for compression
    owner,
);

Get Started

1

Merge Compressed 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, mintTo, mergeTokenAccounts } from "@lightprotocol/compressed-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 mint multiple times to create multiple accounts
    const { mint } = await createMint(rpc, payer, payer.publicKey, 9);
    const owner = Keypair.generate();
    await mintTo(rpc, payer, mint, owner.publicKey, payer, bn(100_000_000));
    await mintTo(rpc, payer, mint, owner.publicKey, payer, bn(200_000_000));
    await mintTo(rpc, payer, mint, owner.publicKey, payer, bn(300_000_000));

    // Merge all accounts for owner
    const tx = await mergeTokenAccounts(rpc, payer, mint, owner);

    console.log("Mint:", mint.toBase58());
    console.log("Tx:", tx);
})();
Before we merge compressed accounts, we need
  • Multiple compressed token accounts of the same mint owned by the same wallet, and
  • an SPL mint with a token pool for compression. This token pool can be created for new SPL mints via createMint() or added to existing SPL mints via createTokenPool().

Troubleshooting

The owner has no compressed token accounts for the specified mint:
// Check if accounts exist before merging
const accounts = await rpc.getCompressedTokenAccountsByOwner(
    owner.publicKey,
    { mint }
);

if (accounts.items.length === 0) {
    console.log("No compressed token accounts found for this mint");
    console.log("Mint address:", mint.toBase58());
    console.log("Owner address:", owner.publicKey.toBase58());
    return;
}

console.log(`Found ${accounts.items.length} accounts to merge`);

Advanced Configuration

// Get account count
const accounts = await rpc.getCompressedTokenAccountsByOwner(
    owner.publicKey,
    { mint }
);

// Only merge if more than 2 accounts
if (accounts.items.length > 2) {
    console.log(`Merging ${accounts.items.length} accounts...`);

    const mergeTx = await mergeTokenAccounts(
        rpc,
        payer,
        mint,
        tokenOwner,
    );

    console.log("Merge completed:", mergeTx);
} else {
    console.log("Merge not needed - optimal account structure");
}
const mints = [
    new PublicKey("MINT_1_ADDRESS"),
    new PublicKey("MINT_2_ADDRESS"),
];

// Merge accounts for each mint
for (const mint of mints) {
    console.log(`Merging accounts for mint: ${mint.toBase58()}`);

    const mergeTx = await mergeTokenAccounts(
        rpc,
        payer,
        mint,
        tokenOwner,
    );

    console.log(`Merge completed: ${mergeTx}`);
}

Next Steps

How to Approve and Revoke Delegate Authority