Documentation Index Fetch the complete documentation index at: https://mintlify.com/Lightprotocol/light-protocol/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Compressed accounts store state in Merkle trees instead of traditional Solana accounts, eliminating rent costs while maintaining full composability and security through zero-knowledge proofs.
Compressed Account Structure
A compressed account contains:
interface CompressedAccountWithMerkleContext {
hash : BN254 ; // Account hash (unique identifier)
treeInfo : TreeInfo ; // Merkle tree location
leafIndex : number ; // Position in tree
owner : PublicKey ; // Account owner
lamports : BN ; // SOL balance
address : Uint8Array | null ; // Optional: account address
data : CompressedAccountData | null ; // Optional: account data
}
Account Data
Compressed accounts can store arbitrary data:
interface CompressedAccountData {
discriminator : number []; // 8-byte program discriminator
data : Buffer ; // Serialized account data
dataHash : number []; // Hash of the data (32 bytes)
}
Querying Compressed Accounts
By Owner
Fetch all compressed accounts owned by a public key:
import { createRpc } from '@lightprotocol/stateless.js' ;
import { PublicKey } from '@solana/web3.js' ;
const rpc = createRpc ();
const owner = new PublicKey ( '...' );
// Get all accounts
const response = await rpc . getCompressedAccountsByOwner ( owner );
console . log ( 'Found accounts:' , response . items . length );
response . items . forEach ( account => {
console . log ( 'Account:' , {
hash: account . hash . toString (),
lamports: account . lamports . toString (),
leafIndex: account . leafIndex ,
});
});
Handle large result sets with cursor-based pagination:
import { bn } from '@lightprotocol/stateless.js' ;
let cursor : string | undefined ;
const allAccounts = [];
do {
const response = await rpc . getCompressedAccountsByOwner ( owner , {
cursor ,
limit: bn ( 1000 ),
});
allAccounts . push ( ... response . items );
cursor = response . cursor ?? undefined ;
} while ( cursor );
console . log ( 'Total accounts:' , allAccounts . length );
By Hash
Fetch a specific account by its hash:
import { bn } from '@lightprotocol/stateless.js' ;
const accountHash = bn ( '1234567890...' );
const account = await rpc . getCompressedAccount (
undefined , // address
accountHash // hash
);
if ( account ) {
console . log ( 'Found account:' , {
owner: account . owner . toBase58 (),
lamports: account . lamports . toString (),
hasData: account . data !== null ,
});
}
By Address
Query by account address (if the account has one):
const address = new Uint8Array ([ /* 32 bytes */ ]);
const account = await rpc . getCompressedAccount (
address , // address
undefined // hash
);
Multiple Accounts
Batch fetch multiple accounts:
const hashes = [
bn ( 'hash1...' ),
bn ( 'hash2...' ),
bn ( 'hash3...' ),
];
const accounts = await rpc . getMultipleCompressedAccounts ( hashes );
accounts . forEach (( account , i ) => {
console . log ( `Account ${ i } :` , account . lamports . toString ());
});
Creating Compressed Accounts
Compress SOL
Create a compressed account by compressing regular SOL:
import { compress } from '@lightprotocol/stateless.js' ;
import { Keypair } from '@solana/web3.js' ;
const payer = Keypair . generate ();
const recipient = Keypair . generate (). publicKey ;
// Compress 0.01 SOL into a compressed account
const signature = await compress (
rpc ,
payer , // Fee payer
10_000_000 , // 0.01 SOL in lamports
recipient // Owner of compressed account
);
// Query the new account
const accounts = await rpc . getCompressedAccountsByOwner ( recipient );
console . log ( 'New account lamports:' , accounts . items [ 0 ]. lamports . toString ());
With Custom State Tree
Specify which state tree to use:
import { selectStateTreeInfo } from '@lightprotocol/stateless.js' ;
// Get available trees
const treeInfos = await rpc . getStateTreeInfos ();
const selectedTree = selectStateTreeInfo ( treeInfos );
// Compress to specific tree
await compress (
rpc ,
payer ,
10_000_000 ,
recipient ,
selectedTree // Optional: specify output tree
);
Create Account with Address (V1 Only)
This method is only available in V1 and is deprecated. Use program-specific account creation methods instead.
import { createAccount , LightSystemProgram } from '@lightprotocol/stateless.js' ;
const address = new Uint8Array ([ /* 32 bytes */ ]);
await createAccount (
rpc ,
payer ,
[ address ], // Array of addresses to create
LightSystemProgram . programId , // Owner program
undefined , // No data
stateTree // Output state tree
);
Transferring Compressed Accounts
Basic Transfer
Transfer compressed SOL from one owner to another:
import { transfer } from '@lightprotocol/stateless.js' ;
const owner = Keypair . generate ();
const recipient = Keypair . generate (). publicKey ;
// Transfer 0.001 SOL
const signature = await transfer (
rpc ,
payer , // Fee payer
1_000_000 , // Amount in lamports
owner , // Current owner (signer)
recipient // Destination
);
How Transfers Work
Input Selection : SDK automatically selects compressed accounts to cover the amount
Proof Generation : Requests validity proof from the RPC
Account Creation : Creates output accounts (recipient + change)
Nullification : Marks input accounts as spent
// The SDK does this automatically:
const accounts = await rpc . getCompressedAccountsByOwner ( owner . publicKey );
// Select minimum accounts needed
const [ inputAccounts ] = selectMinCompressedSolAccountsForTransfer (
accounts . items ,
amount
);
// Get validity proof
const proof = await rpc . getValidityProof (
inputAccounts . map ( acc => bn ( acc . hash ))
);
// Build instruction
const ix = await LightSystemProgram . transfer ({
payer: payer . publicKey ,
inputCompressedAccounts: inputAccounts ,
toAddress: recipient ,
lamports: amount ,
recentInputStateRootIndices: proof . rootIndices ,
recentValidityProof: proof . compressedProof ,
});
Decompressing Accounts
Back to Regular SOL
Convert compressed SOL back to regular Solana accounts:
import { decompress } from '@lightprotocol/stateless.js' ;
const owner = Keypair . generate ();
const recipient = Keypair . generate (). publicKey ;
// Decompress 0.001 SOL to recipient's regular account
const signature = await decompress (
rpc ,
payer , // Fee payer
1_000_000 , // Amount to decompress
recipient // Destination (regular Solana account)
);
// Check regular balance
const balance = await rpc . getBalance ( recipient );
console . log ( 'Decompressed balance:' , balance );
Querying Balances
Total Balance by Owner
Get total compressed SOL balance:
const balance = await rpc . getCompressedBalanceByOwner ( owner . publicKey );
console . log ( 'Total compressed SOL:' , balance . toString (), 'lamports' );
Single Account Balance
Get balance of a specific compressed account:
const balance = await rpc . getCompressedBalance (
undefined , // address
accountHash // hash
);
console . log ( 'Account balance:' , balance . toString (), 'lamports' );
Merkle Proofs
Get Account Proof
Fetch the Merkle proof for an account:
const proof = await rpc . getCompressedAccountProof ( accountHash );
console . log ( 'Proof:' , {
hash: proof . hash . toString (),
leafIndex: proof . leafIndex ,
root: proof . root . toString (),
merkleProof: proof . merkleProof . map ( p => p . toString ()),
});
Batch Proof Requests
Get proofs for multiple accounts:
const hashes = [
bn ( 'hash1...' ),
bn ( 'hash2...' ),
bn ( 'hash3...' ),
];
const proofs = await rpc . getMultipleCompressedAccountProofs ( hashes );
proofs . forEach (( proof , i ) => {
console . log ( `Proof ${ i } root:` , proof . root . toString ());
});
Account Filtering
With Filters
Filter accounts by data pattern:
const response = await rpc . getCompressedAccountsByOwner ( owner , {
filters: [
{
memcmp: {
offset: 0 ,
bytes: 'base58string...' ,
},
},
],
});
With Data Slice
Limit the data returned:
const response = await rpc . getCompressedAccountsByOwner ( owner , {
dataSlice: {
offset: 0 ,
length: 32 , // Only return first 32 bytes
},
});
Transaction Signatures
Get Signatures for Account
Find all transactions involving a compressed account:
const signatures = await rpc . getCompressionSignaturesForAccount (
accountHash
);
signatures . forEach ( sig => {
console . log ( 'Transaction:' , {
signature: sig . signature ,
slot: sig . slot ,
blockTime: new Date ( sig . blockTime * 1000 ),
});
});
Get Signatures by Owner
Find all compression transactions for an owner:
const signatures = await rpc . getCompressionSignaturesForOwner (
owner . publicKey ,
{
cursor: undefined ,
limit: bn ( 100 ),
}
);
console . log ( 'Found' , signatures . items . length , 'transactions' );
Best Practices
Account Selection
When selecting accounts for transfers:
import { selectMinCompressedSolAccountsForTransfer } from '@lightprotocol/stateless.js' ;
const accounts = await rpc . getCompressedAccountsByOwner ( owner . publicKey );
// Select minimum accounts to cover the amount
const [ selectedAccounts , totalAmount ] =
selectMinCompressedSolAccountsForTransfer (
accounts . items ,
requiredAmount
);
if ( totalAmount . lt ( requiredAmount )) {
throw new Error ( 'Insufficient balance' );
}
Error Handling
Handle common errors:
try {
const account = await rpc . getCompressedAccount ( undefined , hash );
if ( ! account ) {
console . log ( 'Account not found or already spent' );
}
} catch ( error ) {
if ( error . message . includes ( 'failed to get info' )) {
console . error ( 'RPC error:' , error );
} else {
throw error ;
}
}
Transaction Confirmation
Wait for transaction confirmation:
import type { ConfirmOptions } from '@solana/web3.js' ;
const confirmOptions : ConfirmOptions = {
commitment: 'confirmed' ,
skipPreflight: false ,
};
const signature = await transfer (
rpc ,
payer ,
amount ,
owner ,
recipient ,
confirmOptions
);
// Wait for finalization
await rpc . confirmTransaction ( signature , 'finalized' );
Next Steps
RPC Methods Complete RPC method reference
Compressed Tokens Work with compressed SPL tokens