Skip to main content
The SDK provides instruction-level APIs that return instructions without sending transactions. Combine these instructions to build custom transactions with multiple instructions. This guide demonstrates creating a token pool and compressing existing SPL tokens in a single transaction.

Full Code Example

Prerequisites

Make sure you have dependencies and developer environment set up!
Dependencies
npm install --save-dev typescript tsx @types/node && \
npm install --save \
    @lightprotocol/stateless.js \
    @lightprotocol/compressed-token \
    @solana/web3.js \
    @solana/spl-token
Alternatives:
yarn add --dev typescript tsx @types/node && \
yarn add \
    @lightprotocol/stateless.js \
    @lightprotocol/compressed-token \
    @solana/web3.js \
    @solana/spl-token
pnpm add --save-dev typescript tsx @types/node && \
pnpm add \
    @lightprotocol/stateless.js \
    @lightprotocol/compressed-token \
    @solana/web3.js \
    @solana/spl-token
Developer EnvironmentBy default, this guide uses Localnet.
# Install the development CLI
npm install @lightprotocol/zk-compression-cli
# Start a local test validator
light test-validator

## ensure you have the Solana CLI accessible in your system PATH
// createRpc() defaults to local test validator endpoints
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();
Alternative: Using DevnetFollow these steps to create an RPC Connection. Replace <your_api_key> with your API key before running.
Get your API Key here, if you don’t have one yet.
import { createRpc } from "@lightprotocol/stateless.js";

// Helius exposes Solana and Photon RPC endpoints through a single URL
const RPC_ENDPOINT = "https://devnet.helius-rpc.com?api-key=<your_api_key>";
const connection = createRpc(RPC_ENDPOINT, RPC_ENDPOINT, RPC_ENDPOINT);

console.log("Connection created!");
console.log("RPC Endpoint:", RPC_ENDPOINT);

Create Token Pool + Compress

create-pool-and-compress.ts
// 1. Setup: Create regular SPL token and mint to ATA
// 2. Build instructions for create token pool and compress
// 3. Combine instructions in one transaction
// 4. Verify compressed balance

import {
    Keypair,
    ComputeBudgetProgram,
} from '@solana/web3.js';
import {
    createRpc,
    buildAndSignTx,
    sendAndConfirmTx,
    selectStateTreeInfo,
} from '@lightprotocol/stateless.js';
import { CompressedTokenProgram } from '@lightprotocol/compressed-token';
import {
    createMint,
    getOrCreateAssociatedTokenAccount,
    mintTo,
    TOKEN_PROGRAM_ID,
} from '@solana/spl-token';
import BN from 'bn.js';

async function createPoolAndCompress() {
    // Step 1: Setup - Create regular SPL token and mint to ATA
    const rpc = createRpc();
    const payer = Keypair.generate();
    const airdropSignature = await rpc.requestAirdrop(payer.publicKey, 1000000000);
    await rpc.confirmTransaction(airdropSignature);

    // Create regular SPL token mint
    const mint = await createMint(
        rpc,
        payer,
        payer.publicKey,
        null,
        9,
        undefined,
        undefined,
        TOKEN_PROGRAM_ID
    );

    // Create ATA and mint tokens to it
    const ata = await getOrCreateAssociatedTokenAccount(
        rpc,
        payer,
        mint,
        payer.publicKey,
        undefined,
        undefined,
        undefined,
        TOKEN_PROGRAM_ID
    );

    await mintTo(
        rpc,
        payer,
        mint,
        ata.address,
        payer,
        1_000_000_000, // 1 token with 9 decimals
        undefined,
        undefined,
        TOKEN_PROGRAM_ID
    );

    console.log("Regular SPL token created:", mint.toBase58());
    console.log("ATA balance:", 1, "token");

    // Step 2: Build instructions for create token pool and compress
    const outputStateTreeInfo = selectStateTreeInfo(await rpc.getStateTreeInfos());

    // Derive token pool PDA
    const tokenPoolPda = CompressedTokenProgram.deriveTokenPoolPda(mint);

    // Create token pool instruction
    const createTokenPoolIx = await CompressedTokenProgram.createTokenPool({
        feePayer: payer.publicKey,
        mint,
        tokenProgram: TOKEN_PROGRAM_ID,
    });

    // Manually construct TokenPoolInfo for first-time compression
    const tokenPoolInfo = {
        mint: mint,
        tokenPoolPda: tokenPoolPda,
        tokenProgram: TOKEN_PROGRAM_ID,
        isInitialized: true, // Set to true even though pool will be created in this tx
        balance: new BN(0),
        poolIndex: 0,
        bump: 0, // Placeholder value
    };

    // Create compress instruction
    const compressIx = await CompressedTokenProgram.compress({
        outputStateTreeInfo,
        tokenPoolInfo,
        payer: payer.publicKey,
        owner: payer.publicKey,
        source: ata.address,
        toAddress: payer.publicKey,
        amount: new BN(1_000_000_000),
        mint,
    });

    // Step 3: Combine instructions in one transaction
    const { blockhash } = await rpc.getLatestBlockhash();

    const allInstructions = [
        ComputeBudgetProgram.setComputeUnitLimit({ units: 1_000_000 }),
        createTokenPoolIx,
        compressIx,
    ];

    const tx = buildAndSignTx(
        allInstructions,
        payer,
        blockhash,
        []
    );

    const txId = await sendAndConfirmTx(rpc, tx);

    console.log("Token pool created and tokens compressed");
    console.log("Transaction:", txId);

    // Step 4: Verify compressed balance
    const compressedAccounts = await rpc.getCompressedTokenAccountsByOwner(
        payer.publicKey,
        { mint }
    );

    const compressedBalance = compressedAccounts.items.reduce(
        (sum, account) => sum.add(account.parsed.amount),
        new BN(0)
    );

    console.log("Compressed balance:", compressedBalance.toNumber() / 1_000_000_000, "tokens");

    return {
        transactionSignature: txId,
        mint,
        compressedBalance: compressedBalance.toNumber(),
    };
}

createPoolAndCompress().catch(console.error);

Next Steps

Learn how to transfer compressed tokens.