Documentation Index Fetch the complete documentation index at: https://luminouslabs-cc5545c6-indexing-tokens.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
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.
SPL token accounts can be compressed in the same transaction with compress_or_decompress_amount, if needed.
function-transfer-compressed-tokens.ts
// Transfer compressed tokens
const transactionSignature = await transfer (
rpc ,
payer ,
mint , // SPL mint with token pool for compression
amount ,
payer ,
recipient , // destination address (toAddress parameter)
)
Get Started
Transfer Compressed Tokens
Install packages in your working directory: npm install @lightprotocol/stateless.js@beta \
@lightprotocol/compressed-token@beta
Install the CLI globally: npm install -g @lightprotocol/zk-compression-cli@beta
Install packages in your working directory: yarn add @lightprotocol/stateless.js@beta \
@lightprotocol/compressed-token@beta
Install the CLI globally: yarn global add @lightprotocol/zk-compression-cli@beta
Install packages in your working directory: pnpm add @lightprotocol/stateless.js@beta \
@lightprotocol/compressed-token@beta
Install the CLI globally: pnpm add -g @lightprotocol/zk-compression-cli@beta
# start local test-validator in a separate terminal
light test-validator
In the code examples, use createRpc() without arguments for localnet.
Get an API key from Helius and add to .env: API_KEY =< your-helius-api-key >
In the code examples, use createRpc(RPC_URL) with the devnet URL.
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 );
})();
Make sure the SPL mint has a token pool for compression. The script creates this token pool for you. For development, you can create a new mint with token pool via createMint() or add a token pool to an existing mint via createTokenPool() .
Troubleshooting
Insufficient balance for transfer
The sender doesn’t have enough compressed tokens for the requested transfer amount. // 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 () } ` );
}
The transfer requires more than 4 compressed accounts, which exceeds the transaction limit. // 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 } ` );
}
}
Advanced Configuration
Transfer to Multiple Recipients
Transfer to multiple recipients in separate transactions: 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 );
}
Transfer with Delegate Authority
Transfer tokens using delegate authority: 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 ,
);
Next Steps
How to Compress and Decompress SPL Tokens