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 interface PDA 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@^0.23.0 \
@lightprotocol/compressed-token@^0.23.0
Install the CLI globally: npm install -g @lightprotocol/zk-compression-cli
Install packages in your working directory: yarn add @lightprotocol/stateless.js@^0.23.0 \
@lightprotocol/compressed-token@^0.23.0
Install the CLI globally: yarn global add @lightprotocol/zk-compression-cli
Install packages in your working directory: pnpm add @lightprotocol/stateless.js@^0.23.0 \
@lightprotocol/compressed-token@^0.23.0
Install the CLI globally: pnpm add -g @lightprotocol/zk-compression-cli
Install packages in your working directory: # 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: npm install -g @lightprotocol/zk-compression-cli
# 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 );
})();
The SPL mint must have an SPL Interface PDA for compression. The script creates it for you. For development, create a new mint with SPL interface via createMint() or add an SPL interface to an existing mint via createSplInterface() .
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