Add Compressed Token Support to Your Wallet
The following page describes how to add compressed token support to your wallet application.
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
npm
npm install --save \
@lightprotocol/stateless.js \
@lightprotocol/compressed-token \
@solana/web3.js
Yarn
yarn add \
@lightprotocol/stateless.js \
@lightprotocol/compressed-token \
@solana/web3.js
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 connection: Rpc = createRpc(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';
const RPC_ENDPOINT = 'https://devnet.helius-rpc.com?api-key=<api_key>';
const connection: Rpc = createRpc(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.getCompressedTokenBalancesByOwnerV2(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);
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. Send Compressed Tokens
Setup Test Mint
import { Keypair } from "@solana/web3.js";
import { Rpc, confirmTx, createRpc } from '@lightprotocol/stateless.js';
import { createMint, mintTo } from '@lightprotocol/compressed-token';
// Set these values...
const RPC_ENDPOINT = '<https://devnet.helius-rpc.com?api-key=><api_key>';
const connection: Rpc = createRpc(RPC_ENDPOINT);
const PAYER = Keypair.generate();
const PUBLIC_KEY = PAYER.publicKey;
const MINT_KEYPAIR = Keypair.generate();
const RECIPIENT_PUBLIC_KEY = Keypair.generate().publicKey.toBase58();
/// Airdrop tokens to PAYER beforehand.
(async() => {
/// Create and register 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,
dedupeSigner,
sendAndConfirmTx,
buildAndSignTx,
} from "@lightprotocol/stateless.js";
import {
CompressedTokenProgram,
selectMinCompressedTokenAccountsForTransfer,
} from "@lightprotocol/compressed-token";
import { ComputeBudgetProgram, Keypair } from "@solana/web3.js";
// 0. Set these values
const RPC_ENDPOINT = "https://mainnet.helius-rpc.com?api-key=<api_key>";
const mint = <MINT_ADDRESS>;
const payer = <PAYER_KEYPAIR>;
const owner = payer;
const recipient = Keypair.generate();
const amount = bn(1e8);
const connection: Rpc = createRpc(RPC_ENDPOINT);
(async () => {
// 1. Fetch latest token account state
const compressedTokenAccounts =
await connection.getCompressedTokenAccountsByOwner(owner.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) => account.compressedAccount.hash)
);
// 4. Create transfer instruction
const ix = await CompressedTokenProgram.transfer({
payer: payer.publicKey,
inputCompressedTokenAccounts: inputAccounts,
toAddress: recipient.publicKey,
amount,
recentInputStateRootIndices: proof.rootIndices,
recentValidityProof: proof.compressedProof,
});
// 8. Sign, send, and confirm...
const { blockhash } = await connection.getLatestBlockhash();
const additionalSigners = dedupeSigner(payer, [owner]);
const signedTx = buildAndSignTx(
[ComputeBudgetProgram.setComputeUnitLimit({ units: 300_000 }), ix],
payer,
blockhash,
additionalSigners
);
return await sendAndConfirmTx(connection, signedTx);
})();
Advanced Integration
Decompress SPL Tokens
import {
bn,
buildAndSignTx,
sendAndConfirmTx,
dedupeSigner,
Rpc,
createRpc,
} from "@lightprotocol/stateless.js";
import { ComputeBudgetProgram } from "@solana/web3.js";
import {
CompressedTokenProgram,
getTokenPoolInfos,
selectMinCompressedTokenAccountsForTransfer,
selectTokenPoolInfosForDecompression,
} from "@lightprotocol/compressed-token";
// 0. Set these values.
const connection: Rpc = createRpc("https://mainnet.helius-rpc.com?api-key=<api_key>";);
const payer = PAYER_KEYPAIR;
const owner = PAYER_KEYPAIR;
const mint = MINT_ADDRESS;
const amount = 1e5;
(async () => {
// 1. Fetch compressed token accounts
const compressedTokenAccounts =
await connection.getCompressedTokenAccountsByOwner(owner.publicKey, {
mint,
});
// 2. Select
const [inputAccounts] = selectMinCompressedTokenAccountsForTransfer(
compressedTokenAccounts.items,
bn(amount)
);
// 3. Fetch validity proof
const proof = await connection.getValidityProof(
inputAccounts.map((account) => account.compressedAccount.hash)
);
// 4. Fetch & Select tokenPoolInfos
const tokenPoolInfos = await getTokenPoolInfos(connection, mint);
const selectedTokenPoolInfos = selectTokenPoolInfosForDecompression(
tokenPoolInfos,
amount
);
// 5. Build instruction
const ix = await CompressedTokenProgram.decompress({
payer: payer.publicKey,
inputCompressedTokenAccounts: inputAccounts,
toAddress: owner.publicKey,
amount,
tokenPoolInfos: selectedTokenPoolInfos,
recentInputStateRootIndices: proof.rootIndices,
recentValidityProof: proof.compressedProof,
});
// 6. Sign, send, and confirm.
// Example with keypair:
const { blockhash } = await connection.getLatestBlockhash();
const additionalSigners = dedupeSigner(payer, [owner]);
const signedTx = buildAndSignTx(
[ComputeBudgetProgram.setComputeUnitLimit({ units: 300_000 }), ix],
payer,
blockhash,
additionalSigners
);
return await sendAndConfirmTx(connection, signedTx);
})();
Compress SPL Tokens
import {
buildAndSignTx,
sendAndConfirmTx,
Rpc,
createRpc,
selectStateTreeInfo,
} from "@lightprotocol/stateless.js";
import { ComputeBudgetProgram } from "@solana/web3.js";
import {
CompressedTokenProgram,
getTokenPoolInfos,
selectTokenPoolInfo,
} from "@lightprotocol/compressed-token";
import { getOrCreateAssociatedTokenAccount } from "@solana/spl-token";
// 0. Set these values.
const connection: Rpc = createRpc(
"https://mainnet.helius-rpc.com?api-key=<api_key>"
);
const payer = <PAYER_KEYPAIR>;
const mint = <MINT_ADDRESS>;
const amount = 1e5;
(async () => {
// 1. Get user ATA
const sourceTokenAccount = await getOrCreateAssociatedTokenAccount(
connection,
payer,
mint,
payer.publicKey
);
// 2. Fetch & Select treeInfos
const treeInfos = await connection.getStateTreeInfos();
const treeInfo = selectStateTreeInfo(treeInfos);
// 3. Fetch & Select tokenPoolInfo
const tokenPoolInfos = await getTokenPoolInfos(connection, mint);
const tokenPoolInfo = selectTokenPoolInfo(tokenPoolInfos);
// 4. Build compress instruction
const compressInstruction = await CompressedTokenProgram.compress({
payer: payer.publicKey,
owner: payer.publicKey,
source: sourceTokenAccount.address,
toAddress: payer.publicKey, // to self.
amount,
mint,
outputStateTreeInfo: treeInfo,
tokenPoolInfo,
});
// 5. Sign and send tx
// Example with Keypair:
const { blockhash } = await connection.getLatestBlockhash();
const tx = buildAndSignTx(
[
ComputeBudgetProgram.setComputeUnitLimit({ units: 300_000 }),
compressInstruction,
],
payer,
blockhash,
[payer]
);
await sendAndConfirmTx(connection, tx);
})();
Best Practices
Clear UI Indicators — Provide clear visual distinctions between compressed and uncompressed SPL tokens
Transaction History — Provide detailed transaction histories for compressed tokens
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
Was this helpful?