Add Compressed Token Support to Your Wallet
The following page describes how to add compressed token support to your browser extension wallet
Key benefits of compressed tokens:
Up to 5000x cheaper than uncompressed accounts
Compatible with existing programs via atomic compression and decompression between SPL <> Compressed tokens
Integration Steps
1. Install the SDK
Package Manager | Command |
---|---|
npm |
|
Yarn |
|
2. Create an RPC Connection
import {
Rpc,
createRpc,
} from "@lightprotocol/stateless.js";
const RPC_ENDPOINT = "https://devnet.helius-rpc.com?api-key=<api_key>";
const COMPRESSION_RPC_ENDPOINT = RPC_ENDPOINT;
const connection: Rpc = createRpc(RPC_ENDPOINT, COMPRESSION_RPC_ENDPOINT)
Using Localnet
# Install the development CLI
npm install @lightprotocol/zk-compression-cli
# Start a local test validator
light test-validator
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();
3. Display Compressed Token Balances
import { Rpc, createRpc } from '@lightprotocol/stateless.js';
import { PublicKey } from '@solana/web3.js';
/// Helius exposes the Solana and compression RPC endpoints through a single URL
const RPC_ENDPOINT = 'https://devnet.helius-rpc.com?api-key=<api_key>';
const connection: Rpc = createRpc(RPC_ENDPOINT, RPC_ENDPOINT);
const publicKey = new PublicKey('CLEuMG7pzJX9xAuKCFzBP154uiG1GaNo4Fq7x6KAcAfG');
(async () => {
// Returns balance for owner per mint
// Can optionally apply filter: {mint, limit, cursor}
const balances =
await connection.getCompressedTokenBalancesByOwner(publicKey);
console.log(balances);
})();
4. Get Compression Signature History By Owner
import { Rpc, createRpc } from '@lightprotocol/stateless.js';
import { PublicKey } from '@solana/web3.js';
const RPC_ENDPOINT = 'https://devnet.helius-rpc.com?api-key=<api_key>';
const connection: Rpc = createRpc(RPC_ENDPOINT, RPC_ENDPOINT);
const publicKey = new PublicKey('CLEuMG7pzJX9xAuKCFzBP154uiG1GaNo4Fq7x6KAcAfG');
(async () => {
// 1. Fetch signatures for the user
//
// Returns confirmed signatures for compression transactions involving the
// specified account owner
const signatures =
await connection.getCompressionSignaturesForOwner(publicKey);
console.log(signatures);
// 2. Fetch transactions with compression info
//
// Returns pre- and post-compressed token balances grouped by owner
const parsedTransaction =
await connection.getTransactionWithCompressionInfo(signatures[0].signature)
console.log(parsedTransaction)
})();
Full JSON RPC API:
JSON RPC Methods5. Sending Compressed Tokens
//To test the code snippets below, you need the following recurring keys.
import { Keypair } from "@solana/web3.js";
const PAYER = Keypair.generate();
const PUBLIC_KEY = PAYER.publicKey;
const MINT_KEYPAIR = Keypair.generate();
const RECIPIENT_PUBLIC_KEY = Keypair.generate().publicKey.toBase58();
Setup Test Mint
import { Rpc, confirmTx, createRpc } from '@lightprotocol/stateless.js';
import { createMint, mintTo } from '@lightprotocol/compressed-token';
const RPC_ENDPOINT = '<https://devnet.helius-rpc.com?api-key=><api_key>';
const COMPRESSION_ENDPOINT =
'<https://devnet.helius-rpc.com?api-key=><api_key>';
const connection: Rpc = createRpc(RPC_ENDPOINT, COMPRESSION_ENDPOINT);
(async() => {
/// Airdrop lamports to pay fees.
await confirmTx(
connection,
await connection.requestAirdrop(PAYER.publicKey, 1e9),
);
/// Create compressed-token mint
const { mint, transactionSignature } = await createMint(
connection,
PAYER,
PAYER.publicKey,
9,
PAYER,
);
console.log(`create-mint success! txId: ${transactionSignature}`);
/// Mint compressed tokens
const mintToTxId = await mintTo(
connection,
PAYER,
mint,
PAYER.publicKey,
PAYER,
1e9,
);
console.log(`mint-to success! txId: ${mintToTxId}`);
})();
import { Rpc, createRpc, bn } from '@lightprotocol/stateless.js';
import { CompressedTokenProgram, selectMinCompressedTokenAccountsForTransfer } from '@lightprotocol/compressed-token';
const RPC_ENDPOINT = 'https://devnet.helius-rpc.com?api-key=<api_key>';
const connection: Rpc = createRpc(RPC_ENDPOINT, RPC_ENDPOINT);
const publicKey = PUBLIC_KEY;
const recipient = RECIPIENT_PUBLIC_KEY;
const mint = MINT_KEYPAIR.publicKey;
const amount = bn(1e8);
(async () => {
// 1. Fetch latest token account state
const compressedTokenAccounts =
await connection.getCompressedTokenAccountsByOwner(publicKey, {
mint,
});
// 2. Select accounts to transfer from based on the transfer amount
const [inputAccounts] = selectMinCompressedTokenAccountsForTransfer(
compressedTokenAccounts.items,
amount,
);
// 3. Fetch recent validity proof
const proof = await connection.getValidityProof(
inputAccounts.map(account => bn(account.compressedAccount.hash)),
);
// 4. Create transfer instruction
const ix = await CompressedTokenProgram.transfer({
payer: publicKey,
inputCompressedTokenAccounts: inputAccounts,
toAddress: recipient,
amount,
recentInputStateRootIndices: proof.rootIndices,
recentValidityProof: proof.compressedProof,
});
console.log(ix);
// 5. Sign and send...
})();
Advanced Integration
Decompress and Compress SPL Tokens
import { Rpc, createRpc, bn } from '@lightprotocol/stateless.js';
import { CompressedTokenProgram, selectMinCompressedTokenAccountsForTransfer } from '@lightprotocol/compressed-token';
import { createAssociatedTokenAccount } from '@solana/spl-token';
const RPC_ENDPOINT = 'https://devnet.helius-rpc.com?api-key=<api_key>';
const connection: Rpc = createRpc(RPC_ENDPOINT, RPC_ENDPOINT);
const publicKey = PUBLIC_KEY;
const mint = MINT_KEYPAIR.publicKey;
const amount = bn(1e8);
(async () => {
// 0. Create an associated token account for the user if it doesn't exist
const ata = await createAssociatedTokenAccount(
connection,
PAYER,
mint,
publicKey,
);
// 1. Fetch the latest compressed token account state
const compressedTokenAccounts =
await connection.getCompressedTokenAccountsByOwner(publicKey, {
mint,
});
// 2. Select accounts to transfer from based on the transfer amount
const [inputAccounts] = selectMinCompressedTokenAccountsForTransfer(
compressedTokenAccounts,
amount,
);
// 3. Fetch recent validity proof
const proof = await connection.getValidityProof(
inputAccounts.map(account => bn(account.compressedAccount.hash)),
);
// 4. Create the decompress instruction
const decompressIx = await CompressedTokenProgram.decompress({
payer: publicKey,
inputCompressedTokenAccounts: inputAccounts,
toAddress: ata,
amount,
recentInputStateRootIndices: proof.rootIndices,
recentValidityProof: proof.compressedProof,
});
// 5. Create the compress instruction
const compressIx = await CompressedTokenProgram.compress({
payer: publicKey,
owner: publicKey,
source: ata,
toAddress: publicKey,
amount,
mint,
});
// 6. Sign and send the transaction with sequential decompression and compression
})();
Best Practices
Clear UI Indicators — Provide clear visual distinctions between compressed and uncompressed SPL tokens
Transaction History — Provide detailed transaction histories for compressed tokens, including compression-specific details like state root updates
Decompression and Compression — Provide a clear path for users to convert between compressed and uncompressed tokens when needed
Support
For additional support or questions, please refer to our documentation or contact Swen or Mert on Telegram or via email
Last updated