> ## Documentation Index
> Fetch the complete documentation index at: https://www.zkcompression.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Compress and Decompress SPL Tokens

> Convert SPL tokens between compressed and regular format with compress() and decompress().

<CodeGroup>
  ```typescript compress() theme={null}
  // Compress SPL tokens to compressed tokens
  const compressionSignature = await compress(
      rpc,
      payer,
      mint, // SPL mint with interface PDA 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 interface PDA 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/compress-spl-token-account).
</Note>

## Get Started

<Steps>
  <Step>
    ### Compress / Decompress Tokens

    <Accordion title="Installation">
      <Tabs>
        <Tab title="npm">
          Install packages in your working directory:

          ```bash theme={null}
          npm install @lightprotocol/stateless.js@^0.23.0 \
                      @lightprotocol/compressed-token@^0.23.0
          ```

          Install the CLI globally:

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

        <Tab title="yarn">
          Install packages in your working directory:

          ```bash theme={null}
          yarn add @lightprotocol/stateless.js@^0.23.0 \
                   @lightprotocol/compressed-token@^0.23.0
          ```

          Install the CLI globally:

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

        <Tab title="pnpm">
          Install packages in your working directory:

          ```bash theme={null}
          pnpm add @lightprotocol/stateless.js@^0.23.0 \
                   @lightprotocol/compressed-token@^0.23.0
          ```

          Install the CLI globally:

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

        <Tab title="SDK 2.0 (token-interface)">
          Install packages in your working directory:

          ```bash theme={null}
          # npm
          npm install @lightprotocol/stateless.js@^0.23.0 \
                      @lightprotocol/token-interface@^0.1.2

          # yarn
          yarn add @lightprotocol/stateless.js@^0.23.0 \
                   @lightprotocol/token-interface@^0.1.2

          # pnpm
          pnpm add @lightprotocol/stateless.js@^0.23.0 \
                   @lightprotocol/token-interface@^0.1.2
          ```

          Install the CLI globally:

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

    <Tabs>
      <Tab title="Localnet">
        ```bash theme={null}
        # start local test-validator in a separate terminal
        light test-validator
        ```

        <Note>
          In the code examples, use `createRpc()` without arguments for localnet.
        </Note>
      </Tab>

      <Tab title="Devnet">
        Get an API key from [Helius](https://helius.dev) and add to `.env`:

        ```bash title=".env" theme={null}
        API_KEY=<your-helius-api-key>
        ```

        <Note>
          In the code examples, use `createRpc(RPC_URL)` with the devnet URL.
        </Note>
      </Tab>
    </Tabs>

    <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/create-mint-with-token-pool) or added to existing SPL mints via [`createTokenPool()`](/compressed-tokens/guides/add-token-pools-to-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">
        ```typescript theme={null}
        import "dotenv/config";
        import { Keypair } from "@solana/web3.js";
        import { createRpc } from "@lightprotocol/stateless.js";
        import { createMint, mintTo, decompress, compress } from "@lightprotocol/compressed-token";
        import { createAssociatedTokenAccount } from "@solana/spl-token";
        import { homedir } from "os";
        import { readFileSync } from "fs";

        // devnet:
        const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`;
        // localnet:
        // const RPC_URL = undefined;
        const payer = Keypair.fromSecretKey(
            new Uint8Array(
                JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8"))
            )
        );

        (async function () {
            // devnet:
            const rpc = createRpc(RPC_URL);
            // localnet:
            // const rpc = createRpc();

            // Setup: Get SPL tokens (needed to compress)
            const { mint } = await createMint(rpc, payer, payer.publicKey, 9);
            const splAta = await createAssociatedTokenAccount(rpc, payer, mint, payer.publicKey);
            await mintTo(rpc, payer, mint, payer.publicKey, payer, 1_000_000_000);
            await decompress(rpc, payer, mint, 1_000_000_000, payer, splAta);

            // Compress SPL tokens
            const recipient = Keypair.generate();
            const tx = await compress(rpc, payer, mint, 500_000_000, payer, splAta, recipient.publicKey);

            console.log("Mint:", mint.toBase58());
            console.log("Tx:", tx);
        })();
        ```
      </Tab>

      <Tab title="Decompress">
        ```typescript theme={null}
        import "dotenv/config";
        import { Keypair } from "@solana/web3.js";
        import { createRpc } from "@lightprotocol/stateless.js";
        import { createMint, mintTo, decompress } from "@lightprotocol/compressed-token";
        import { createAssociatedTokenAccount } from "@solana/spl-token";
        import { homedir } from "os";
        import { readFileSync } from "fs";

        // devnet:
        const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`;
        // localnet:
        // const RPC_URL = undefined;
        const payer = Keypair.fromSecretKey(
            new Uint8Array(
                JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8"))
            )
        );

        (async function () {
            // devnet:
            const rpc = createRpc(RPC_URL);
            // localnet:
            // const rpc = createRpc();

            // Setup: Create mint and mint compressed tokens
            const { mint } = await createMint(rpc, payer, payer.publicKey, 9);
            await mintTo(rpc, payer, mint, payer.publicKey, payer, 1_000_000_000);
            const splAta = await createAssociatedTokenAccount(rpc, payer, mint, payer.publicKey);

            // Decompress to SPL tokens
            const tx = await decompress(rpc, payer, mint, 500_000_000, payer, splAta);

            console.log("Mint:", mint.toBase58());
            console.log("Tx:", tx);
        })();
        ```
      </Tab>
    </Tabs>

    <Info>
      The SPL mint must have an SPL Interface PDA for compression.<br /> The script creates it for you.

      For development, create a new mint with SPL interface via [`createMint()`](https://github.com/Lightprotocol/examples-zk-compression/tree/main/compressed-token-cookbook) or add an SPL interface to an existing mint via [`createSplInterface()`](https://github.com/Lightprotocol/examples-zk-compression/tree/main/compressed-token-cookbook).
    </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="How to Compress Complete SPL Token Accounts" icon="chevron-right" color="#0066ff" href="/compressed-tokens/guides/compress-spl-token-account" horizontal />
