Add Compressed Token Support to Your Wallet

The following page describes how to add compressed token support to your browser extension wallet

Key benefits of compressed tokens:

  • Up to 5000x cheaper than uncompressed accounts

  • Compatible with existing programs via atomic compression and decompression between SPL <> Compressed tokens

Integration Steps

1. Install the SDK

Package ManagerCommand

npm

npm install --save \
    @lightprotocol/stateless.js \
    @lightprotocol/compressed-token \
    @solana/web3.js \
    @coral-xyz/anchor

Yarn

yarn add \
    @lightprotocol/stateless.js \
    @lightprotocol/compressed-token \
    @solana/web3.js \
    @coral-xyz/anchor

2. Create an RPC Connection

import {
  Rpc,
  createRpc,
} from "@lightprotocol/stateless.js";

const RPC_ENDPOINT = "https://devnet.helius-rpc.com?api-key=<api_key>";
const COMPRESSION_RPC_ENDPOINT = RPC_ENDPOINT;
const connection: Rpc = createRpc(RPC_ENDPOINT, COMPRESSION_RPC_ENDPOINT)
Using Localnet
# Install the development CLI
npm install @lightprotocol/zk-compression-cli
# Start a local test validator
light test-validator
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();

3. Display Compressed Token Balances

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

/// Helius exposes the Solana and compression RPC endpoints through a single URL
const RPC_ENDPOINT = 'https://devnet.helius-rpc.com?api-key=<api_key>';
const connection: Rpc = createRpc(RPC_ENDPOINT, RPC_ENDPOINT);
const publicKey = new PublicKey('CLEuMG7pzJX9xAuKCFzBP154uiG1GaNo4Fq7x6KAcAfG');

(async () => {
    // Returns balance for owner per mint
    // Can optionally apply filter: {mint, limit, cursor}
    const balances =
        await connection.getCompressedTokenBalancesByOwner(publicKey);
    console.log(balances);
})();

4. Get Compression Signature History By Owner

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

const RPC_ENDPOINT = 'https://devnet.helius-rpc.com?api-key=<api_key>';
const connection: Rpc = createRpc(RPC_ENDPOINT, RPC_ENDPOINT);
const publicKey = new PublicKey('CLEuMG7pzJX9xAuKCFzBP154uiG1GaNo4Fq7x6KAcAfG');

(async () => {
    // 1. Fetch signatures for the user
    //
    // Returns confirmed signatures for compression transactions involving the
    // specified account owner
    const signatures =
        await connection.getCompressionSignaturesForOwner(publicKey);
    console.log(signatures);

    // 2. Fetch transactions with compression info
    //
    // Returns pre- and post-compressed token balances grouped by owner
    const parsedTransaction = 
        await connection.getTransactionWithCompressionInfo(signatures[0].signature)
    console.log(parsedTransaction)
})();

Full JSON RPC API:

JSON RPC Methods

5. Sending Compressed Tokens

//To test the code snippets below, you need the following recurring keys.
import { Keypair } from "@solana/web3.js";

const PAYER = Keypair.generate();
const PUBLIC_KEY = PAYER.publicKey;
const MINT_KEYPAIR = Keypair.generate();
const RECIPIENT_PUBLIC_KEY = Keypair.generate().publicKey.toBase58();
Setup Test Mint
import { Rpc, confirmTx, createRpc } from '@lightprotocol/stateless.js';
import { createMint, mintTo } from '@lightprotocol/compressed-token';

const RPC_ENDPOINT = '<https://devnet.helius-rpc.com?api-key=><api_key>';
const COMPRESSION_ENDPOINT =
    '<https://devnet.helius-rpc.com?api-key=><api_key>';
const connection: Rpc = createRpc(RPC_ENDPOINT, COMPRESSION_ENDPOINT);

(async() => {
    /// Airdrop lamports to pay fees.
    await confirmTx(
        connection,
        await connection.requestAirdrop(PAYER.publicKey, 1e9),
    );

    /// Create compressed-token mint
    const { mint, transactionSignature } = await createMint(
        connection,
        PAYER,
        PAYER.publicKey,
        9,
        PAYER,
    );

    console.log(`create-mint success! txId: ${transactionSignature}`);

    /// Mint compressed tokens
    const mintToTxId = await mintTo(
        connection,
        PAYER,
        mint,
        PAYER.publicKey,
        PAYER,
        1e9,
    );

    console.log(`mint-to success! txId: ${mintToTxId}`);
})();
import { Rpc, createRpc, bn } from '@lightprotocol/stateless.js';
import { CompressedTokenProgram, selectMinCompressedTokenAccountsForTransfer } from '@lightprotocol/compressed-token';

const RPC_ENDPOINT = 'https://devnet.helius-rpc.com?api-key=<api_key>';
const connection: Rpc = createRpc(RPC_ENDPOINT, RPC_ENDPOINT);
const publicKey = PUBLIC_KEY;
const recipient = RECIPIENT_PUBLIC_KEY;
const mint = MINT_KEYPAIR.publicKey;
const amount = bn(1e8);

(async () => {
    // 1. Fetch latest token account state
    const compressedTokenAccounts =
        await connection.getCompressedTokenAccountsByOwner(publicKey, {
            mint,
        });

    // 2. Select accounts to transfer from based on the transfer amount
    const [inputAccounts] = selectMinCompressedTokenAccountsForTransfer(
        compressedTokenAccounts.items,
        amount,
    );

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

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

    console.log(ix);
    // 5. Sign and send...
})();

Advanced Integration

Decompress and Compress SPL Tokens
import { Rpc, createRpc, bn } from '@lightprotocol/stateless.js';
import { CompressedTokenProgram, selectMinCompressedTokenAccountsForTransfer } from '@lightprotocol/compressed-token';
import { createAssociatedTokenAccount } from '@solana/spl-token';

const RPC_ENDPOINT = 'https://devnet.helius-rpc.com?api-key=<api_key>';
const connection: Rpc = createRpc(RPC_ENDPOINT, RPC_ENDPOINT);
const publicKey = PUBLIC_KEY;
const mint = MINT_KEYPAIR.publicKey;
const amount = bn(1e8);

(async () => {
    // 0. Create an associated token account for the user if it doesn't exist
    const ata = await createAssociatedTokenAccount(
        connection,
        PAYER,
        mint,
        publicKey,
    );

    // 1. Fetch the latest compressed token account state
    const compressedTokenAccounts =
        await connection.getCompressedTokenAccountsByOwner(publicKey, {
            mint,
        });

    // 2. Select accounts to transfer from based on the transfer amount
    const [inputAccounts] = selectMinCompressedTokenAccountsForTransfer(
        compressedTokenAccounts,
        amount,
    );

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

    // 4. Create the decompress instruction
    const decompressIx = await CompressedTokenProgram.decompress({
        payer: publicKey,
        inputCompressedTokenAccounts: inputAccounts,
        toAddress: ata,
        amount,
        recentInputStateRootIndices: proof.rootIndices,
        recentValidityProof: proof.compressedProof,
    });

    // 5. Create the compress instruction
    const compressIx = await CompressedTokenProgram.compress({
        payer: publicKey,
        owner: publicKey,
        source: ata,
        toAddress: publicKey,
        amount,
        mint,
    });

    // 6. Sign and send the transaction with sequential decompression and compression
})();

Best Practices

  • Clear UI Indicators — Provide clear visual distinctions between compressed and uncompressed SPL tokens

  • Transaction History — Provide detailed transaction histories for compressed tokens, including compression-specific details like state root updates

  • Decompression and Compression — Provide a clear path for users to convert between compressed and uncompressed tokens when needed

Support

For additional support or questions, please refer to our documentation or contact Swen or Mert on Telegram or via email

Last updated