> ## Documentation Index
> Fetch the complete documentation index at: https://luminouslabs-cc5545c6-swen-add-code-runner.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Compress and Decompress SPL Tokens

> Guide to compress and decompress SPL tokens, troubleshooting and advanced configurations.

The `compress()` and `decompress()` functions convert SPL tokens between compressed and regular format.

<CodeGroup>
  ```typescript compress() theme={null}
  // Compress SPL tokens to compressed tokens
  const compressionSignature = await compress(
      rpc,
      payer,
      mint, // SPL mint with token pool for compression
      amount,
      payer, // owner of SPL tokens
      tokenAccount.address, // source SPL token account (sourceTokenAccount parameter)
      recipient, // recipient owner address (toAddress parameter)
  );
  ```

  ```typescript decompress() theme={null}
  // Decompress compressed tokens to SPL tokens
  const transactionSignature = await decompress(
      rpc,
      payer,
      mint, // SPL mint with token pool for compression
      amount,
      payer, // owner of compressed tokens
      tokenAccount.address, // destination token account (toAddress parameter)
  );
  ```
</CodeGroup>

<Note>
  **Function Difference and Best Practice:**

  * `compress(amount, sourceTokenAccount, toAddress)` compresses specific amounts from source to a specified recipient. Use for transfers and precise amounts.
  * `compressSplTokenAccount(tokenAccount, remainingAmount)` compresses the entire SPL token account balance minus optional remaining amount only to the same owner. Use to migrate complete token accounts with optional partial retention. [Here is how](/compressed-tokens/guides/how-to-compress-complete-spl-token-accounts).
</Note>

# Full Code Example

<Steps>
  <Step>
    ### Prerequisites

    Make sure you have dependencies and developer environment set up!

    <Accordion title="Prerequisites & Setup">
      **Dependencies**

      <Tabs>
        <Tab title="npm">
          ```bash theme={null}
          npm install @lightprotocol/stateless.js@alpha \
                      @lightprotocol/compressed-token@alpha
          ```
        </Tab>

        <Tab title="yarn">
          ```bash theme={null}
          yarn add @lightprotocol/stateless.js@alpha \
                   @lightprotocol/compressed-token@alpha
          ```
        </Tab>

        <Tab title="pnpm">
          ```bash theme={null}
          pnpm add @lightprotocol/stateless.js@alpha \
                   @lightprotocol/compressed-token@alpha
          ```
        </Tab>
      </Tabs>

      **Developer Environment**

      <Tabs>
        <Tab title="Localnet">
          By default, all guides use Localnet.

          <Tabs>
            <Tab title="npm">
              ```bash theme={null}
              npm install -g @lightprotocol/zk-compression-cli@alpha
              ```
            </Tab>

            <Tab title="yarn">
              ```bash theme={null}
              yarn global add @lightprotocol/zk-compression-cli@alpha
              ```
            </Tab>

            <Tab title="pnpm">
              ```bash theme={null}
              pnpm add -g @lightprotocol/zk-compression-cli@alpha
              ```
            </Tab>
          </Tabs>

          ```bash theme={null}
          # Start a local test validator
          light test-validator

          ## ensure you have the Solana CLI accessible in your system PATH
          ```

          ```typescript theme={null}
          // 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();
          ```
        </Tab>

        <Tab title="Devnet">
          Replace `<your-api-key>` with your actual API key. [Get your API key here](https://www.helius.dev/zk-compression), if you don't have one yet.

          ```typescript theme={null}
          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);
          ```
        </Tab>
      </Tabs>
    </Accordion>
  </Step>

  <Step>
    ### Compress / Decompress Tokens

    Run this script to compress / decompress tokens!

    <Info>
      Before we can compress or decompresss, we need:

      * An SPL mint with a token pool for compression. This token pool can be created for new SPL mints via [`createMint()`](/compressed-tokens/guides/how-to-create-and-register-a-mint-account-for-compression) or added to existing SPL mints via [`createTokenPool()`](/compressed-tokens/guides/how-to-create-compressed-token-pools-for-mint-accounts).
      * For `compress()` SPL tokens in an Associated Token Account, or
      * For `decompress()` compressed token accounts with sufficient balance.
    </Info>

    <Tabs>
      <Tab title="Compress Tokens">
        ```typescript compress-tokens.ts highlight={44-54} expandable theme={null}
        // 1. Setup funded payer and connect to local validator
        // 2. Create SPL mint with token pool and mint SPL tokens to ATA
        // 3. Call compress() to convert SPL tokens to compressed format
        // 4. Verify balances via getTokenAccountBalance and getCompressedTokenAccountsByOwner

        import { Keypair } from '@solana/web3.js';
        import { createRpc } from '@lightprotocol/stateless.js';
        import {
            createMint,
            compress
        } from '@lightprotocol/compressed-token';
        import {
            getOrCreateAssociatedTokenAccount,
            mintTo as splMintTo,
            TOKEN_PROGRAM_ID
        } from '@solana/spl-token';
        import BN from 'bn.js';

        async function compressTokens() {
            // Step 1: Setup funded payer and connect to local validator
            const rpc = createRpc(); // defaults to localhost:8899
            const payer = Keypair.generate();
            const airdropSignature = await rpc.requestAirdrop(payer.publicKey, 1000000000); // 1 SOL
            await rpc.confirmTransaction(airdropSignature);

            // Step 2: Create SPL mint with token pool and mint SPL tokens to ATA
            const { mint } = await createMint(rpc, payer, payer.publicKey, 9);
            console.log("Mint with token pool created:", mint.toBase58());

            const tokenOwner = Keypair.generate();
            const tokenAccount = await getOrCreateAssociatedTokenAccount(
                rpc, payer, mint, tokenOwner.publicKey, false, TOKEN_PROGRAM_ID
            );

            // Mint SPL tokens to the ATA
            const splAmount = 1_000_000_000; // 1 token with 9 decimals
            await splMintTo(rpc, payer, mint, tokenAccount.address, payer, splAmount, [], undefined, TOKEN_PROGRAM_ID);
            console.log("SPL tokens minted:", splAmount / 1_000_000_000, "tokens");

            console.log("Compress Tokens");

            const compressAmount = 400_000_000; // 0.4 tokens

            // Step 3: Call compress() to convert to compressed format
            // Lock SPL tokens to pool account and mint compressed tokens
            const compressTx = await compress(
                rpc,
                payer,
                mint, // SPL mint with token pool for compression
                compressAmount, // amount to compress
                tokenOwner, // owner of SPL tokens
                tokenAccount.address, // source token account
                tokenOwner.publicKey, // recipient for compressed tokens
            );

            console.log("Compressed amount:", compressAmount / 1_000_000_000, "tokens");
            console.log("Transaction:", compressTx);

            // Step 4: Verify balances via getTokenAccountBalance and getCompressedTokenAccountsByOwner
            const finalTokenBalance = await rpc.getTokenAccountBalance(tokenAccount.address);
            const finalCompressedAccounts = await rpc.getCompressedTokenAccountsByOwner(
                tokenOwner.publicKey,
                { mint }
            );

            // Calculate total compressed balance
            const finalCompressedBalance = finalCompressedAccounts.items.reduce(
                (sum, account) => sum.add(account.parsed.amount),
                new BN(0)
            );

            console.log("\nFinal balances:");
            console.log("Regular SPL tokens:", finalTokenBalance.value.uiAmount);
            console.log("Compressed tokens:", finalCompressedBalance.toNumber() / 1_000_000_000);

            return {
                compressTransaction: compressTx,
                finalCompressedBalance,
                finalSplBalance: finalTokenBalance.value.amount
            };
        }

        compressTokens().catch(console.error);
        ```
      </Tab>

      <Tab title="Compress and Decompress">
        Compress SPL tokens and decompress in one script.

        ```typescript compress-and-decompress-tokens.ts highlight={46-55, 75-83} expandable theme={null}
        // 1. Setup funded payer and connect to local validator
        // 2. Create SPL mint with token pool and mint SPL tokens to ATA
        // 3. Compress SPL tokens to compressed format
        // 4. Decompress compressed tokens back to SPL format
        // 5. Verify final balances

        import { Keypair } from '@solana/web3.js';
        import { createRpc } from '@lightprotocol/stateless.js';
        import {
            createMint,
            compress,
            decompress
        } from '@lightprotocol/compressed-token';
        import {
            getOrCreateAssociatedTokenAccount,
            mintTo as splMintTo,
            TOKEN_PROGRAM_ID
        } from '@solana/spl-token';
        import BN from 'bn.js';

        async function compressAndDecompressTokens() {
            // Step 1: Setup funded payer and connect to local validator
            const rpc = createRpc(); // defaults to localhost:8899
            const payer = Keypair.generate();
            const airdropSignature = await rpc.requestAirdrop(payer.publicKey, 1000000000); // 1 SOL
            await rpc.confirmTransaction(airdropSignature);

            // Step 2: Create SPL mint with token pool and mint SPL tokens to ATA
            const { mint } = await createMint(rpc, payer, payer.publicKey, 9);
            console.log("Mint with token pool created:", mint.toBase58());

            const tokenOwner = Keypair.generate();
            const tokenAccount = await getOrCreateAssociatedTokenAccount(
                rpc, payer, mint, tokenOwner.publicKey, false, TOKEN_PROGRAM_ID
            );

            // Mint SPL tokens to the ATA
            const splAmount = 1_000_000_000; // 1 token with 9 decimals
            await splMintTo(rpc, payer, mint, tokenAccount.address, payer, splAmount, [], undefined, TOKEN_PROGRAM_ID);
            console.log("SPL tokens minted:", splAmount / 1_000_000_000, "tokens");

            console.log("\n=== Compress Tokens ===");

            const compressAmount = 600_000_000; // 0.6 tokens

            // Step 3: Compress SPL tokens
            const compressTx = await compress(
                rpc,
                payer,
                mint,
                compressAmount,
                tokenOwner,
                tokenAccount.address,
                tokenOwner.publicKey,
            );

            console.log("Compressed amount:", compressAmount / 1_000_000_000, "tokens");
            console.log("Compress transaction:", compressTx);

            // Verify compressed balance
            const compressedAccounts = await rpc.getCompressedTokenAccountsByOwner(
                tokenOwner.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");

            console.log("\n=== Decompress Tokens ===");

            const decompressAmount = 300_000_000; // 0.3 tokens

            // Step 4: Decompress compressed tokens back to SPL format
            const decompressTx = await decompress(
                rpc,
                payer,
                mint,
                decompressAmount,
                tokenOwner,
                tokenAccount.address,
            );

            console.log("Decompressed amount:", decompressAmount / 1_000_000_000, "tokens");
            console.log("Decompress transaction:", decompressTx);

            // Step 5: Verify final balances
            const finalTokenBalance = await rpc.getTokenAccountBalance(tokenAccount.address);
            const finalCompressedAccounts = await rpc.getCompressedTokenAccountsByOwner(
                tokenOwner.publicKey,
                { mint }
            );
            const finalCompressedBalance = finalCompressedAccounts.items.reduce(
                (sum, account) => sum.add(account.parsed.amount),
                new BN(0)
            );

            console.log("\n=== Final Balances ===");
            console.log("Regular SPL tokens:", finalTokenBalance.value.uiAmount);
            console.log("Compressed tokens:", finalCompressedBalance.toNumber() / 1_000_000_000);

            return {
                compressTransaction: compressTx,
                decompressTransaction: decompressTx,
                finalCompressedBalance,
                finalSplBalance: finalTokenBalance.value.amount
            };
        }

        compressAndDecompressTokens().catch(console.error);

        ```
      </Tab>
    </Tabs>

    <Info>
      Make sure the SPL mint has a token pool for compression.<br /> The script creates this token pool for you.

      For development, you can create a new mint with token pool via [`createMint()`](/compressed-tokens/guides/how-to-create-and-register-a-mint-account-for-compression) or add a token pool to an existing mint via [`createTokenPool()`](/compressed-tokens/guides/how-to-create-compressed-token-pools-for-mint-accounts).
    </Info>
  </Step>
</Steps>

# Troubleshooting

<AccordionGroup>
  <Accordion title="Insufficient balance between decompress and compress">
    Check your balances before operations:

    ```typescript theme={null}
    // For decompression - check compressed balance
    const compressedAccounts = await rpc.getCompressedTokenAccountsByOwner(
        owner.publicKey,
        { mint }
    );
    const compressedBalance = compressedAccounts.items.reduce(
        (sum, account) => sum.add(account.parsed.amount),
        new BN(0)
    );

    // For compression - check SPL token balance
    const tokenBalance = await rpc.getTokenAccountBalance(tokenAccount);
    const splBalance = new BN(tokenBalance.value.amount);

    console.log("Can decompress up to:", compressedBalance.toString());
    console.log("Can compress up to:", splBalance.toString());
    ```
  </Accordion>

  <Accordion title="Invalid owner">
    Ensure the signer owns the tokens being decompressed/compressed:

    ```typescript theme={null}
    // The owner parameter must be the actual owner
    const decompressTx = await decompress(
        rpc,
        payer, // can be different (pays fees)
        mint,
        amount,
        actualOwner, // must own compressed tokens
        destinationAta,
    );

    const compressTx = await compress(
        rpc,
        payer, // can be different (pays fees)
        mint,
        amount,
        actualOwner, // must own SPL tokens
        sourceAta,
        recipient,
    );
    ```
  </Accordion>
</AccordionGroup>

# Advanced Configuration

<AccordionGroup>
  <Accordion title="Compress to Different Owner">
    Compress tokens directly to someone else:

    ```typescript theme={null}
    const recipientWallet = new PublicKey("RECIPIENT_WALLET_ADDRESS");

    // Compress your SPL tokens to recipient
    const compressTx = await compress(
        rpc,
        payer,
        mint,
        amount,
        tokenOwner, // current owner signs
        tokenAccount, // your token account
        recipientWallet, // recipient gets compressed tokens
    );
    ```
  </Accordion>

  <Accordion title="Batch Operations">
    Compress multiple token accounts:

    ```typescript theme={null}
    // Compress to multiple recipients at once
    const recipients = [recipient1.publicKey, recipient2.publicKey, recipient3.publicKey];
    const amounts = [1_000_000_000, 2_000_000_000, 500_000_000]; // Different amounts

    const batchCompressTx = await compress(
        rpc,
        payer,
        mint,
        amounts, // Array of amounts
        owner,
        tokenAccount,
        recipients, // Array of recipients
    );

    console.log("Batch compression completed:", batchCompressTx);
    ```
  </Accordion>

  <Accordion title="Decompress with Delegate Authority">
    Decompress tokens using delegate authority:

    ```typescript theme={null}
    import { decompressDelegated } from '@lightprotocol/compressed-token';
    import { getAssociatedTokenAddress, TOKEN_PROGRAM_ID } from '@solana/spl-token';

    // Get ATA for decompressed tokens
    const ataAddress = await getAssociatedTokenAddress(
        mint,
        recipient,
        false,
        TOKEN_PROGRAM_ID
    );

    // Delegate decompresses tokens
    await decompressDelegated(
        rpc,
        payer,
        mint,
        amount,
        delegate, // Signer - owner of compressed tokens
        ataAddress, // Uncompressed token account (ATA)
    );
    ```
  </Accordion>
</AccordionGroup>

# Next Steps

<Card title="Learn how to compress complete token accounts to reclaim rent afterwards." icon="chevron-right" color="#0066ff" href="/compressed-tokens/guides/how-to-compress-complete-spl-token-accounts" horizontal />
