Overview
The Light Protocol system program (SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7) is the core program for compressed account operations. It handles account creation, updates, and deletion using Merkle trees and zero-knowledge proofs.
Program ID: SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7Documentation: System Program
Instruction Types
Invoke
Direct compressed account operations
InvokeCpi
Cross-program invocation context
CPI Context
Account for storing CPI state
Discriminators
| Instruction | Discriminator (8 bytes) | Description |
|---|---|---|
| InitializeCpiContextAccount | [163, 223, 82, 38, 39, 61, 233, 233] | Initialize CPI context account |
| Invoke | [163, 223, 82, 38, 39, 61, 233, 233] | Execute compressed account ops |
| InvokeCpi | [230, 224, 109, 130, 81, 205, 191, 65] | CPI from another program |
| InvokeCpiWithReadOnly | Custom | CPI with readonly accounts |
| InvokeCpiWithAccountInfo | Custom | CPI with account info mode |
Invoke
Discriminator:[163, 223, 82, 38, 39, 61, 233, 233]
Direct invocation for compressed account operations.
Instruction Data
Compressed accounts to spend (with Merkle proofs and context)
New compressed accounts to create
Parameters for creating new addresses in address Merkle tree
Lamports to compress (move into Merkle tree) or decompress (move to Solana account)
True for compress operation, false for decompress
ZK proof for validity of operation (Groth16 proof, 128 bytes)
Operations
Create Compressed Accounts
Create Compressed Accounts
Create new compressed accounts in Merkle tree.Steps:
- Derive addresses for output accounts (if needed)
- Verify address non-inclusion proofs
- Hash output accounts
- Insert hashes into state tree output queue
- Emit event with account data
- Authority must sign
- Addresses must not exist (proven by ZK proof)
- Sum check: input lamports = output lamports + fees
Update Compressed Accounts
Update Compressed Accounts
Spend existing compressed accounts and create new ones.Steps:
- Verify inclusion proofs for input accounts
- Compute and insert nullifiers (prevent double-spend)
- Create output accounts
- Verify sum check
- Valid inclusion proofs for inputs
- Authority signature
- Nullifiers not already used
- Sum check passes
Delete Compressed Accounts
Delete Compressed Accounts
Spend compressed accounts without creating outputs.Steps:
- Verify inclusion proofs
- Insert nullifiers
- Decompress lamports to Solana account
Compress Lamports
Compress Lamports
Move lamports from Solana account into compressed account.Steps:
- Transfer lamports from fee payer
- Create compressed account with lamports
- Insert into Merkle tree
Decompress Lamports
Decompress Lamports
Move lamports from compressed account to Solana account.Steps:
- Verify inclusion proof for compressed account
- Insert nullifier (spend account)
- Transfer lamports to destination Solana account
Example
InvokeCpi
Discriminator:[230, 224, 109, 130, 81, 205, 191, 65]
Cross-program invocation for compressed account operations.
Instruction Data
Same data structure as Invoke instruction
CPI context for tracking cross-program state:
invoking_program: Program making the CPIcpi_context_account_index: Index of CPI context accountexecute: Whether to execute (true) or just validate (false)
CPI Context Account
Accounts called via CPI must use a CPI context account to store intermediate state:CPI Flow
Initialize CPI Context
Calling program initializes CPI context account with
InitializeCpiContextAccountExample
InvokeCpiWithReadOnly
Variant of InvokeCpi that supports readonly input accounts. Use case: Read compressed account data without spending itInitializeCpiContextAccount
Initialize CPI context account for cross-program calls. Accounts:- Fee payer (mut, signer)
- CPI context account (mut)
- System program
Accounts
Input Accounts
Authority for input compressed accounts (must sign)
Pays transaction fees and provides lamports for compression
PDA proving program is registered to use Merkle trees
Noop program for emitting events (SPL Noop)
Account compression program managing Merkle trees
State and address Merkle tree accounts (dynamic count)
Nullifier queue accounts for spent account tracking
Output queue accounts for new compressed accounts
Error Codes
Common error codes (11000+ range):| Code | Error | Description |
|---|---|---|
| 11001 | InvalidAuthority | Authority check failed |
| 11002 | InvalidProof | ZK proof verification failed |
| 11003 | SumCheckFailed | Input/output lamports don’t match |
| 11004 | InvalidMerkleTree | Wrong Merkle tree account |
| 11005 | InvalidNullifierQueue | Wrong nullifier queue |
| 11006 | DuplicateNullifier | Nullifier already exists |
| 11007 | InvalidAddress | Address derivation failed |
| 11008 | AddressAlreadyExists | Address already in tree |
| 11009 | InvalidCpiContext | CPI context validation failed |
| 11010 | InvalidProgramOwner | Program not registered |
Best Practices
Always Provide Valid Proofs
Always Provide Valid Proofs
Inclusion and non-inclusion proofs must be fresh and correspond to current Merkle roots. Get proofs from Photon indexer.
Verify Sum Checks
Verify Sum Checks
Client-side verification: ensure total input lamports = total output lamports + fees. On-chain verification will reject mismatches.
Use CPI Context Properly
Use CPI Context Properly
When calling via CPI, always initialize CPI context account first, then use two-phase (setup + execute) pattern.
Handle Nullifiers Correctly
Handle Nullifiers Correctly
Each compressed account can only be spent once. Track nullifiers to prevent double-spend attempts.
Register Your Program
Register Your Program
Programs that use compressed accounts must be registered with the account compression program.
Resources
Program Source
View on GitHub
SDK
TypeScript SDK (stateless.js)
Rust SDK
Rust program SDK
Examples
Program examples