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

# Use Token-2022 with Compression

> Complete guide to mint, compress and transfer tokens with Token-2022 Metadata with ZK Compression.

# What you will do

With this guide you will mint, compress, and transfer tokens with Token-2022 Metadata.

# Overview Token 2022 Extensions

Token 2022 Extensions are optional features that can be added to Token 2022 mints on Solana to enable additional functionality.

ZK Compression supports compressing the following mint-extensions:

* MetadataPointer
* TokenMetadata
* InterestBearingConfig
* GroupPointer
* GroupMemberPointer
* TokenGroup
* TokenGroupMember

All other extensions are not yet supported.

<Info>
  If you require support for other mint-extensions, [let us know](https://t.me/swen_light)!
</Info>

# Get started

<Steps>
  <Step title="Prerequisites">
    <Info>
      You need the following SDK versions:

      * `@lightprotocol/stateless.js` ≥ 0.21.0
      * `@lightprotocol/compressed-token` ≥ 0.21.0
      * `@solana/web3.js` ≥ 1.95.3
    </Info>

    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@^0.23.0 \
                      @lightprotocol/compressed-token@^0.23.0
          ```
        </Tab>

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

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

        <Tab title="SDK 2.0 (token-interface)">
          ```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
          ```
        </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
              ```
            </Tab>

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

            <Tab title="pnpm">
              ```bash theme={null}
              pnpm add -g @lightprotocol/zk-compression-cli
              ```
            </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";

          const RPC_ENDPOINT = "https://devnet.helius-rpc.com?api-key=<your_api_key>";
          const connection = createRpc(RPC_ENDPOINT);

          async function main() {
            let slot = await connection.getSlot();
            console.log(slot);

            let health = await connection.getIndexerHealth(slot);
            console.log(health);
            // "Ok"
          }

          main();
          ```
        </Tab>
      </Tabs>
    </Accordion>
  </Step>

  <Step title="Mint, compress, and transfer tokens with Token-2022 Metadata">
    Run `compress-t22.ts`.

    ```typescript compress-t22.ts expandable theme={null}
    // Token-2022 with ZK Compression - Local
    // 1. Load wallet and connect to local validator
    // 2. Create Token-2022 mint with metadata extension and register for compression via createTokenPool()
    // 3. Mint SPL tokens to ATA, compress via compress(), and transfer compressed tokens via transfer()
    // 4. Verify balances via getTokenAccountBalance and getCompressedTokenAccountsByOwner

    import { confirmTx, createRpc } from "@lightprotocol/stateless.js";
    import {
      compress,
      createTokenPool,
      transfer,
    } from "@lightprotocol/compressed-token";
    import {
      getOrCreateAssociatedTokenAccount,
      mintTo as mintToSpl,
      TOKEN_2022_PROGRAM_ID,
      createInitializeMetadataPointerInstruction,
      createInitializeMintInstruction,
      ExtensionType,
      getMintLen,
      LENGTH_SIZE,
      TYPE_SIZE,
    } from "@solana/spl-token";
    import {
      Keypair,
      sendAndConfirmTransaction,
      SystemProgram,
      Transaction,
    } from "@solana/web3.js";
    import {
      createInitializeInstruction,
      pack,
      TokenMetadata,
    } from "@solana/spl-token-metadata";

    // Step 1: Setup local connection and load wallet
    import * as fs from 'fs';
    import * as os from 'os';

    const walletPath = `${os.homedir()}/.config/solana/id.json`;
    const secretKey = JSON.parse(fs.readFileSync(walletPath, 'utf8'));
    const payer = Keypair.fromSecretKey(Buffer.from(secretKey));
    const connection = createRpc(); // defaults to localhost:8899

    (async () => {

      // Generate mint keypair and define Token-2022 metadata
      const mint = Keypair.generate();
      const decimals = 9;

      const metadata: TokenMetadata = {
        mint: mint.publicKey,
        name: "Local Test Token",
        symbol: "LTT",
        uri: "https://example.com/token-metadata.json",
        additionalMetadata: [["environment", "localnet"], ["test", "true"]],
      };

      // Calculate space requirements for Token-2022 mint with MetadataPointer extension
      const mintLen = getMintLen([ExtensionType.MetadataPointer]);
      const metadataLen = TYPE_SIZE + LENGTH_SIZE + pack(metadata).length;

      // Check wallet balance
      const balance = await connection.getBalance(payer.publicKey);

      // Step 2: Create Token-2022 mint with metadata extension
      const mintLamports = await connection.getMinimumBalanceForRentExemption(
        mintLen + metadataLen
      );

      const mintTransaction = new Transaction().add(
        // Create account for Token-2022 mint
        SystemProgram.createAccount({
          fromPubkey: payer.publicKey,
          newAccountPubkey: mint.publicKey,
          space: mintLen,
          lamports: mintLamports,
          programId: TOKEN_2022_PROGRAM_ID,
        }),
        // Initialize MetadataPointer extension
        createInitializeMetadataPointerInstruction(
          mint.publicKey,
          payer.publicKey,
          mint.publicKey,
          TOKEN_2022_PROGRAM_ID
        ),
        // Initialize Token-2022 mint account
        createInitializeMintInstruction(
          mint.publicKey,
          decimals,
          payer.publicKey,
          null,
          TOKEN_2022_PROGRAM_ID
        ),
        // Initialize token metadata
        createInitializeInstruction({
          programId: TOKEN_2022_PROGRAM_ID,
          mint: mint.publicKey,
          metadata: mint.publicKey,
          name: metadata.name,
          symbol: metadata.symbol,
          uri: metadata.uri,
          mintAuthority: payer.publicKey,
          updateAuthority: payer.publicKey,
        })
      );

      // Send Token-2022 mint creation transaction
      const mintCreationTxId = await sendAndConfirmTransaction(
        connection,
        mintTransaction,
        [payer, mint]
      );
      console.log(`Token-2022 mint created with address: ${mint.publicKey.toString()}`);

      // Step 3: Call createTokenPool() to initialize omnibus account
      // and register Token-2022 mint with Compressed Token Program
      // Create PDA that holds SPL tokens for compressed tokens
      const registerTxId = await createTokenPool(
        connection,
        payer,
        mint.publicKey, // Token-2022 mint to register with compressed token program
        undefined,
        TOKEN_2022_PROGRAM_ID
      );

      // Step 4: Create associated token account and mint tokens
      const ata = await getOrCreateAssociatedTokenAccount(
        connection,
        payer,
        mint.publicKey, // Token-2022 mint with interface PDA for compression
        payer.publicKey,
        undefined,
        undefined,
        undefined,
        TOKEN_2022_PROGRAM_ID
      );

      // Mint Token-2022 tokens to ATA
      const mintAmount = 400_000_000; // 0.4 tokens
      const mintToTxId = await mintToSpl(
        connection,
        payer,
        mint.publicKey, // Token-2022 mint with interface PDA for compression
        ata.address, // destination token account
        payer.publicKey, // mint authority
        mintAmount, // amount to mint
        undefined, // multiSigners (not used for single authority)
        undefined,
        TOKEN_2022_PROGRAM_ID
      );
      console.log(`Minted ${mintAmount / 1e9} Token-2022 tokens with metadata extension`);
      console.log(`Transaction: ${mintToTxId}`);

      // Step 5: Call compress() to convert Token-2022 tokens to compressed format
      // Transfer Token-2022 tokens to omnibus pool and mint compressed tokens
      const compressAmount = 300_000_000; // 0.3 tokens
      const compressTxId = await compress(
        connection,
        payer,
        mint.publicKey, // Token-2022 mint with interface PDA for compression
        compressAmount, // amount to compress
        payer, // owner of SPL tokens
        ata.address, // Source ATA for compression
        payer.publicKey // recipient for compressed tokens
      );
      console.log(`\nCompressed ${compressAmount / 1e9} Token-2022 tokens`);
      console.log(`Transaction: ${compressTxId}`);

      // Step 6: Transfer compressed Token-2022 tokens
      const transferRecipient = Keypair.generate(); // Keypair recipient
      const transferAmount = 100_000_000; // 0.1 tokens
      const transferTxId = await transfer(
        connection,
        payer,
        mint.publicKey, // Token-2022 mint with interface PDA for compression
        transferAmount,
        payer,
        transferRecipient.publicKey
      );
      console.log(`\nTransferred ${transferAmount / 1e9} Compressed Token-2022 tokens`);
      console.log(`Transaction: ${transferTxId}`);
      console.log(`Recipient: ${transferRecipient.publicKey.toString()}`);

      // Step 7: Verify balances via getTokenAccountBalance and getCompressedTokenAccountsByOwner
      const senderCompressedAccounts = await connection.getCompressedTokenAccountsByOwner(payer.publicKey, { mint: mint.publicKey });
      const senderBalance = senderCompressedAccounts.items.reduce((sum, account) => sum + Number(account.parsed.amount), 0);

      const recipientCompressedAccounts = await connection.getCompressedTokenAccountsByOwner(transferRecipient.publicKey, { mint: mint.publicKey });
      const recipientBalance = recipientCompressedAccounts.items.reduce((sum, account) => sum + Number(account.parsed.amount), 0);

      const splBalance = await connection.getTokenAccountBalance(ata.address);

      console.log(`\nSummary Token-2022 balances:`);
      console.log(`Sender balance: ${senderBalance / 1e9} compressed tokens / ${splBalance.value.uiAmount} SPL tokens`);
      console.log(`Recipient balance: ${recipientBalance / 1e9} compressed tokens`);

    })().catch(console.error);
    ```
  </Step>
</Steps>

# Next Steps

<Card title="Create an Airdrop with Compressed Tokens." icon="chevron-right" color="#0066ff" href="/compressed-tokens/token-distribution" horizontal />
