Add Wallet Support for Compressed Tokens
Complete guide to add Compressed Token Support to Your Wallet Application.
What you will do
With this guide you will add Compressed Token Support to Your Wallet Application.
Leading Solana Wallets like Phantom and Backpack already support compressed tokens.
Integration Steps
Display Compressed Token Balances
This example fetches and displays all compressed token balances for a wallet address.
import { Rpc, createRpc } from '@lightprotocol/stateless.js';
import { PublicKey } from '@solana/web3.js';
// 1. Setup RPC connection to local test validator
// 2. Call getCompressedTokenBalancesByOwnerV2() to fetch compressed token balances per mint
// 3. Display results with balance amounts and mint addresses
const connection: Rpc = createRpc(); // defaults to localhost:8899
const publicKey = new PublicKey('CLEuMG7pzJX9xAuKCFzBP154uiG1GaNo4Fq7x6KAcAfG'); // public
(async () => {
// Fetch compressed token balances for wallet address
// Returns balance for owner per mint - can optionally apply filter: {mint, limit, cursor}
const balances = await connection.getCompressedTokenBalancesByOwnerV2(publicKey);
console.log(balances);
})();
Get Transaction History
This example retrieves compression transaction signatures and detailed transaction information for wallet transaction history display.
import { Rpc, createRpc } from '@lightprotocol/stateless.js';
import { PublicKey } from '@solana/web3.js';
// 1. Setup RPC connection and fetch compression transaction signatures using getCompressionSignaturesForOwner()
// 2. Retrieve detailed transaction data with getTransactionWithCompressionInfo() including pre/post balances
// 3. Display transaction history with signature list and balance changes
const connection: Rpc = createRpc(); // defaults to localhost:8899
const publicKey = new PublicKey('CLEuMG7pzJX9xAuKCFzBP154uiG1GaNo4Fq7x6KAcAfG');
(async () => {
// Fetch compression transaction signatures for wallet address
// Returns confirmed signatures for compression transactions involving the specified account owner
const signatures = await connection.getCompressionSignaturesForOwner(publicKey);
console.log(signatures);
// Check if any signatures exist before trying to access them
if (signatures.items.length > 0) {
// Retrieve detailed transaction information with compression data
// Returns pre- and post-compressed token balances grouped by owner
const parsedTransaction = await connection.getTransactionWithCompressionInfo(signatures.items[0].signature);
console.log(parsedTransaction);
} else {
console.log("No compression transactions found for this address");
}
})();
Send Compressed Tokens
First, set up a test mint to and mint 10 compressed tokens to your filesystem wallet.
Make sure you add your Mint address to send-tokens.ts
.
// Compressed Token Transfer - Local
// 1. Load wallet and fetch compressed token accounts with getCompressedTokenAccountsByOwner()
// 2. Select accounts for transfer using selectMinCompressedTokenAccountsForTransfer()
// and get validity proof with getValidityProof()
// 3. Create transfer instruction with CompressedTokenProgram.transfer()
// and submit transaction with sendAndConfirmTx()
// 4. Verify balances via getCompressedTokenAccountsByOwner()
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";
import * as fs from 'fs';
import * as os from 'os';
// Step 1: Setup RPC connection and define transfer parameters
const connection: Rpc = createRpc(); // defaults to localhost:8899
const mint = new PublicKey("MINT ADDRESS"); // Replace with mint address
// Load wallet from filesystem
const walletPath = `${os.homedir()}/.config/solana/id.json`;
const secretKey = JSON.parse(fs.readFileSync(walletPath, 'utf8'));
const payer = Keypair.fromSecretKey(Buffer.from(secretKey));
const owner = payer;
const recipient = Keypair.generate();
const amount = bn(1e8);
(async () => {
// Step 2: Fetch compressed account hashes from state trees
const compressedTokenAccounts =
await connection.getCompressedTokenAccountsByOwner(owner.publicKey, {
mint, // SPL mint with token pool for compression
});
if (compressedTokenAccounts.items.length === 0) {
console.log("No compressed token accounts found for this mint");
return;
}
// Show initial sender balance
const initialBalance = compressedTokenAccounts.items.reduce((sum, account) => sum + Number(account.parsed.amount), 0);
console.log(`Sender balance: ${initialBalance / 1e8} compressed tokens`);
// Step 3: Select minimum compressed accounts for transfer amount
const [inputAccounts] = selectMinCompressedTokenAccountsForTransfer(
compressedTokenAccounts.items,
amount
);
// Get validity proof for Merkle tree verification
const proof = await connection.getValidityProof(
inputAccounts.map((account) => account.compressedAccount.hash)
);
// Step 4: Create transfer instruction that consumes input accounts and creates new output accounts
const ix = await CompressedTokenProgram.transfer({
payer: payer.publicKey,
inputCompressedTokenAccounts: inputAccounts, // accounts to consume
toAddress: recipient.publicKey,
amount,
recentInputStateRootIndices: proof.rootIndices,
recentValidityProof: proof.compressedProof,
});
// Step 5: Build, sign, and submit transaction
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(`\nTransferred ${amount.toNumber() / 1e8} compressed tokens`);
console.log(`Transaction: ${transferTxId}`);
console.log(`Recipient: ${recipient.publicKey.toString()}`);
// Step 6: Verify via getCompressedTokenAccountsByOwner
const senderCompressedAccounts = await connection.getCompressedTokenAccountsByOwner(payer.publicKey, { mint });
const senderBalance = senderCompressedAccounts.items.reduce((sum, account) => sum + Number(account.parsed.amount), 0);
const recipientCompressedAccounts = await connection.getCompressedTokenAccountsByOwner(recipient.publicKey, { mint });
const recipientBalance = recipientCompressedAccounts.items.reduce((sum, account) => sum + Number(account.parsed.amount), 0);
console.log(`\nSummary compressed token balances:`);
console.log(`Sender balance: ${senderBalance / 1e8} compressed tokens`);
console.log(`Recipient balance: ${recipientBalance / 1e8} compressed token`);
return transferTxId;
})();
Advanced Integrations
Use these integrations to let users convert between regular and compressed format as needed.
Common Errors
Best Practices
Clear UI Indicators — Provide clear visual distinctions between compressed and uncompressed SPL tokens
Transaction History — Provide detailed transaction histories for compressed tokens
Decompression and Compression — Provide a clear path for users to convert between compressed and uncompressed tokens when needed
Next Steps
Explore more guides in our cookbook section.
CookbookLast updated