> ## 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.

# Transfer Compressed Tokens

> Transfer compressed tokens between accounts. Transfers consume input accounts and create new output accounts with updated balances.

The `transfer()` function moves compressed tokens between accounts.

* SPL token transfers that update the existing account
* Compressed token transfers consume input accounts from the sender and create new output accounts for sender and recipient with updated balances.

<Tip>
  SPL token accounts can be compressed in the same transaction with `compress_or_decompress_amount`, if needed.
</Tip>

```typescript function-transfer-compressed-tokens.ts theme={null}
// Transfer compressed tokens
const transactionSignature = await transfer(
    rpc,
    payer,
    mint, // SPL mint with interface PDA for compression
    amount,
    payer,
    recipient, // destination address (toAddress parameter)
)
```

## Get Started

<Steps>
  <Step>
    ### Transfer Compressed 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>

    ```typescript theme={null}
    import "dotenv/config";
    import { Keypair } from "@solana/web3.js";
    import { createRpc } from "@lightprotocol/stateless.js";
    import { createMint, mintTo, transfer } from "@lightprotocol/compressed-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 tokens
        const { mint } = await createMint(rpc, payer, payer.publicKey, 9);
        const sender = Keypair.generate();
        await mintTo(rpc, payer, mint, sender.publicKey, payer, 1_000_000_000);

        // Transfer compressed tokens
        const recipient = Keypair.generate();
        const tx = await transfer(rpc, payer, mint, 500_000_000, sender, recipient.publicKey);

        console.log("Mint:", mint.toBase58());
        console.log("Recipient:", recipient.publicKey.toBase58());
        console.log("Tx:", tx);
    })();
    ```

    <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 for transfer">
    The sender doesn't have enough compressed tokens for the requested transfer amount.

    ```typescript theme={null}
    // Check current balance first
    const tokenAccounts = await rpc.getCompressedTokenAccountsByOwner(
        owner.publicKey,
        { mint }
    );

    if (tokenAccounts.items.length === 0) {
        throw new Error("No compressed token accounts found");
    }

    // Calculate total balance across all accounts
    const totalBalance = tokenAccounts.items.reduce(
        (sum, account) => sum.add(account.parsed.amount),
        new BN(0)
    );

    console.log("Available balance:", totalBalance.toString());

    // Ensure transfer amount doesn't exceed balance
    if (new BN(transferAmount).gt(totalBalance)) {
        throw new Error(`Transfer amount ${transferAmount} exceeds balance ${totalBalance.toString()}`);
    }
    ```
  </Accordion>

  <Accordion title="Account limit exceeded">
    The transfer requires more than 4 compressed accounts, which exceeds the transaction limit.

    ```typescript theme={null}
    // Error message: "Account limit exceeded: max X (4 accounts) per transaction.
    // Total balance: Y (Z accounts). Consider multiple transfers to spend full balance."

    // Split into multiple smaller transfers
    const maxTransferPerTx = 1_000_000_000; // Adjust based on your account sizes

    if (transferAmount > maxTransferPerTx) {
        console.log("Large transfer detected, splitting into multiple transactions...");

        let remainingAmount = transferAmount;
        while (remainingAmount > 0) {
            const currentTransfer = Math.min(remainingAmount, maxTransferPerTx);

            await transfer(
                rpc,
                payer,
                mint,
                currentTransfer,
                owner,
                recipient
            );

            remainingAmount -= currentTransfer;
            console.log(`Transferred ${currentTransfer}, remaining: ${remainingAmount}`);
        }
    }
    ```
  </Accordion>
</AccordionGroup>

# Advanced Configuration

<AccordionGroup>
  <Accordion title="Transfer to Multiple Recipients">
    Transfer to multiple recipients in separate transactions:

    ```typescript theme={null}
    const recipients = [
        Keypair.generate().publicKey,
        Keypair.generate().publicKey,
        Keypair.generate().publicKey,
    ];

    const amounts = [
        100_000_000, // 0.1 tokens
        200_000_000, // 0.2 tokens
        150_000_000, // 0.15 tokens
    ];

    for (let i = 0; i < recipients.length; i++) {
        const transactionSignature = await transfer(
            rpc,
            payer,
            mint,
            amounts[i],
            owner,
            recipients[i],
        );

        console.log(`Transfer ${i + 1} completed:`, transactionSignature);
    }
    ```
  </Accordion>

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

    ```typescript theme={null}
    import { approve, transferDelegated } from '@lightprotocol/compressed-token';

    // 1. Owner approves delegate
    await approve(
        rpc,
        payer,
        mint,
        amount,
        owner, // Signer
        delegate.publicKey, // PublicKey
    );

    // 2. Delegate transfers tokens
    await transferDelegated(
        rpc,
        payer,
        mint,
        transferAmount,
        delegate, // Signer - named "owner" in SDK
        recipient,
    );
    ```
  </Accordion>
</AccordionGroup>

# Next Steps

<Card title="How to Compress and Decompress SPL Tokens" icon="chevron-right" color="#0066ff" href="/compressed-tokens/guides/compress-decompress" horizontal />
