Skip to main content
Leading Solana Wallets like Phantom and Backpack already support compressed tokens.

Integration Steps

Display Compressed Token Balances

import { Rpc, createRpc } from '@lightprotocol/stateless.js';
import { PublicKey } from '@solana/web3.js';

const connection: Rpc = createRpc();
const publicKey = new PublicKey('FWwR2s4TwpWN3nkCzVfhuPrpePG8kNzBXAxEbNsaDFNu');

(async () => {
    const balances = await connection.getCompressedTokenBalancesByOwnerV2(publicKey);

    if (balances.value.items.length === 0) {
        console.log("No compressed token balances found");
        return;
    }

    for (const item of balances.value.items) {
        const balanceValue = typeof item.balance === 'string'
            ? parseInt(item.balance, 16)
            : item.balance;

        const mintInfo = await connection.getAccountInfo(new PublicKey(item.mint));
        const decimals = mintInfo.data[44];
        const formattedBalance = balanceValue / Math.pow(10, decimals);

        console.log(`Mint: ${item.mint}`);
        console.log(`Balance: ${formattedBalance} tokens\n`);
    }
})();

Get Transaction History

import { Rpc, createRpc } from '@lightprotocol/stateless.js';
import { PublicKey } from '@solana/web3.js';

const connection: Rpc = createRpc();
const publicKey = new PublicKey('FWwR2s4TwpWN3nkCzVfhuPrpePG8kNzBXAxEbNsaDFNu');

(async () => {
    const signatures = await connection.getCompressionSignaturesForTokenOwner(publicKey);

    if (signatures.items.length > 0) {
        console.log(`Signatures:`);
        signatures.items.forEach((sig, index) => {
            console.log(`${index + 1}. ${sig.signature}`);
            console.log(`   Slot: ${sig.slot}`);
            console.log(`   Time: ${new Date(sig.blockTime * 1000).toISOString()}`);
        });
    } else {
        console.log("No transactions found");
    }
})();
import { Rpc, createRpc } from '@lightprotocol/stateless.js';
import { PublicKey } from '@solana/web3.js';

const RPC_ENDPOINT = process.env.RPC_ENDPOINT || 'https://devnet.helius-rpc.com?api-key=<your-api-key>';

const connection: Rpc = createRpc(RPC_ENDPOINT, RPC_ENDPOINT);
const publicKey = new PublicKey('FWwR2s4TwpWN3nkCzVfhuPrpePG8kNzBXAxEbNsaDFNu');

(async () => {
    try {
        const signatures = await connection.getCompressionSignaturesForTokenOwner(publicKey);

        if (signatures.items.length === 0) {
            console.log("No transactions found");
            return;
        }

        console.log(`Latest Transaction:`);
        console.log(`Signature: ${signatures.items[0].signature}`);
        console.log(`Slot: ${signatures.items[0].slot}`);

        const txInfo = await connection.getTransactionWithCompressionInfo(signatures.items[0].signature);

        if (!txInfo) {
            console.log('Transaction not found or has no compression info');
            return;
        }

        if (txInfo.compressionInfo) {
            console.log(`\nClosed Accounts: ${txInfo.compressionInfo.closedAccounts.length}`);
            console.log(`Opened Accounts: ${txInfo.compressionInfo.openedAccounts.length}`);
        }
    } catch (error) {
        console.error('Error fetching transaction history:', error);
    }
})();

Send Compressed Tokens

import {
  Rpc,
  createRpc,
  bn,
  dedupeSigner,
  sendAndConfirmTx,
  buildAndSignTx,
} from "@lightprotocol/stateless.js";
import {
  CompressedTokenProgram,
  selectMinCompressedTokenAccountsForTransfer,
} from "@lightprotocol/compressed-token";
import { ComputeBudgetProgram, Keypair, PublicKey } from "@solana/web3.js";

const connection: Rpc = createRpc();
const mint = new PublicKey("MINT_ADDRESS");
const payer = PAYER_KEYPAIR;
const owner = payer;
const recipient = Keypair.generate();
const amount = bn(1e8);

(async () => {
  const compressedTokenAccounts =
    await connection.getCompressedTokenAccountsByOwner(owner.publicKey, { mint });

  if (compressedTokenAccounts.items.length === 0) {
    console.log("No compressed token accounts found");
    return;
  }

  const [inputAccounts] = selectMinCompressedTokenAccountsForTransfer(
    compressedTokenAccounts.items,
    amount
  );

  const proof = await connection.getValidityProof(
    inputAccounts.map((account) => account.compressedAccount.hash)
  );

  const ix = await CompressedTokenProgram.transfer({
    payer: payer.publicKey,
    inputCompressedTokenAccounts: inputAccounts,
    toAddress: recipient.publicKey,
    amount,
    recentInputStateRootIndices: proof.rootIndices,
    recentValidityProof: proof.compressedProof,
  });

  const { blockhash } = await connection.getLatestBlockhash();
  const additionalSigners = dedupeSigner(payer, [owner]);
  const signedTx = buildAndSignTx(
    [ComputeBudgetProgram.setComputeUnitLimit({ units: 300_000 }), ix],
    payer,
    blockhash,
    additionalSigners
  );

  const transferTxId = await sendAndConfirmTx(connection, signedTx);
  console.log(`Transaction: ${transferTxId}`);
})();

Advanced Integrations

Use these integrations to let users convert between regular and compressed format as needed.
This example converts compressed tokens to regular SPL format using CompressedTokenProgram.decompress().
import {
  bn,
  buildAndSignTx,
  sendAndConfirmTx,
  dedupeSigner,
  Rpc,
  createRpc,
} from "@lightprotocol/stateless.js";
import { ComputeBudgetProgram } from "@solana/web3.js";
import {
  CompressedTokenProgram,
  getTokenPoolInfos,
  selectMinCompressedTokenAccountsForTransfer,
  selectTokenPoolInfosForDecompression,
} from "@lightprotocol/compressed-token";

// 1. Setup RPC connection and fetch compressed token accounts with getCompressedTokenAccountsByOwner()
// 2. Select accounts and token pool infos using selectMinCompressedTokenAccountsForTransfer() and selectTokenPoolInfosForDecompression()
// 3. Create decompress instruction with CompressedTokenProgram.decompress() and submit transaction

// Step 1: Setup RPC connection and define decompression parameters
const connection: Rpc = createRpc("https://mainnet.helius-rpc.com?api-key=<api_key>";);
const payer = PAYER_KEYPAIR;
const owner = PAYER_KEYPAIR;
const mint = MINT_ADDRESS;
const amount = 1e5; // 100K tokens to decompress

(async () => {
  // 1. Fetch compressed token accounts
  const compressedTokenAccounts =
    await connection.getCompressedTokenAccountsByOwner(owner.publicKey, {
      mint,
    });

  // 2. Select
  const [inputAccounts] = selectMinCompressedTokenAccountsForTransfer(
    compressedTokenAccounts.items,
    bn(amount)
  );

  // 3. Fetch validity proof
  const proof = await connection.getValidityProof(
    inputAccounts.map((account) => account.compressedAccount.hash)
  );

  // 4. Fetch & Select tokenPoolInfos
  const tokenPoolInfos = await getTokenPoolInfos(connection, mint);
  const selectedTokenPoolInfos = selectTokenPoolInfosForDecompression(
    tokenPoolInfos,
    amount
  );

  // 5. Build instruction
  const ix = await CompressedTokenProgram.decompress({
    payer: payer.publicKey,
    inputCompressedTokenAccounts: inputAccounts,
    toAddress: owner.publicKey,
    amount,
    tokenPoolInfos: selectedTokenPoolInfos,
    recentInputStateRootIndices: proof.rootIndices,
    recentValidityProof: proof.compressedProof,
  });


  // 6. Sign, send, and confirm.
  // Example with keypair:
  const { blockhash } = await connection.getLatestBlockhash();
  const additionalSigners = dedupeSigner(payer, [owner]);
  const signedTx = buildAndSignTx(
    [ComputeBudgetProgram.setComputeUnitLimit({ units: 300_000 }), ix],
    payer,
    blockhash,
    additionalSigners
  );

  return await sendAndConfirmTx(connection, signedTx);
})();
This example converts regular SPL tokens to compressed format using CompressedTokenProgram.compress().
// 1. Setup RPC connection and get user ATA with getOrCreateAssociatedTokenAccount()
// 2. Fetch state tree and token pool infos using getStateTreeInfos() and getTokenPoolInfos()
// 3. Create compress instruction with CompressedTokenProgram.compress() and submit transaction


import {
  buildAndSignTx,
  sendAndConfirmTx,
  Rpc,
  createRpc,
  selectStateTreeInfo,
} from "@lightprotocol/stateless.js";
import { ComputeBudgetProgram } from "@solana/web3.js";
import {
  CompressedTokenProgram,
  getTokenPoolInfos,
  selectTokenPoolInfo,
} from "@lightprotocol/compressed-token";
import { getOrCreateAssociatedTokenAccount } from "@solana/spl-token";

// Step 1: Setup RPC connection and define compression parameters
const connection: Rpc = createRpc(
  "https://mainnet.helius-rpc.com?api-key=<api_key>"
);
  const payer = <PAYER_KEYPAIR>;
  const mint = <MINT_ADDRESS>;
const amount = 1e5; // 100K tokens to compress

(async () => {
  // Step 2: Get or create associated token account for SPL tokens
  const sourceTokenAccount = await getOrCreateAssociatedTokenAccount(
    connection,
    payer, // fee payer
    mint, // token mint address
    payer.publicKey // token account owner
  );

  // Step 3: Fetch and select state tree info for compression
  const treeInfos = await connection.getStateTreeInfos();
  const treeInfo = selectStateTreeInfo(treeInfos);

  // Step 4: Fetch and select token pool info for compression
  const tokenPoolInfos = await getTokenPoolInfos(connection, mint);
  const tokenPoolInfo = selectTokenPoolInfo(tokenPoolInfos);

  // Step 5: Create compress instruction - transfer SPL tokens to pool and create compressed accounts
  const compressInstruction = await CompressedTokenProgram.compress({
    payer: payer.publicKey, // fee payer
    owner: payer.publicKey, // owner of source SPL tokens
    source: sourceTokenAccount.address, // source ATA address
    toAddress: payer.publicKey, // recipient of compressed tokens (self)
    amount, // amount to compress
    mint, // token mint address
    outputStateTreeInfo: treeInfo, // state tree for compressed accounts
    tokenPoolInfo, // token pool for compression
  });

  // Step 6: Build, sign, and submit compression transaction
  const { blockhash } = await connection.getLatestBlockhash();
  const tx = buildAndSignTx(
    [
      ComputeBudgetProgram.setComputeUnitLimit({ units: 300_000 }),
      compressInstruction,
    ],
    payer, // transaction signer
    blockhash,
    [payer] // additional signers
  );
  await sendAndConfirmTx(connection, tx);
})();

Common Errors

If getCompressedTokenBalancesByOwnerV2 returns empty:
  • Ensure the wallet has compressed tokens (not regular SPL tokens)
  • Verify you’re on the correct network (devnet/mainnet)

Next Steps

Create an Airdrop with Compressed Tokens.