- consumes the existing account hash and
- produces a new account hash with updated data.
- The existing account hash is nullified to prevent double spending.
Find full code examples at the end for Anchor and native Rust.
Implementation Guide
This guide will cover the components of a Solana program that updates compressed accounts.Here is the complete flow:


1
Program Setup
Dependencies, Constants, Compressed Account
Dependencies, Constants, Compressed Account
DependenciesAdd dependencies to your program.
- The
light-sdkprovides macros, wrappers and CPI interface to create and interact with compressed accounts. - Add the serialization library (
borshfor native Rust, or useAnchorSerialize).
CPISigner is the configuration struct for CPI’s to the Light System Program.- CPIs to the Light System program must be signed with a PDA derived by your program with the seed
b"authority" derive_light_cpi_signer!derives the CPI signer PDA for you at compile time.
- the standard traits (
Clone,Debug,Default), borshorAnchorSerializeto serialize account data, andLightDiscriminatorto implements a unique type ID (8 bytes) to distinguish account types. The default compressed account layout enforces a discriminator in its own field, not the first 8 bytes of the data field[^1].
The traits listed above are required for
LightAccount. LightAccount wraps MyCompressedAccount in Step 3 to set the discriminator and create the compressed account’s data.2
Instruction Data
Define the instruction data with the following parameters:
- Anchor
- Native Rust
Anchor handles instruction deserialization automatically. Pass the parameters directly to the instruction function:
- Validity Proof
- Define
proofto include the proof that the account exists in the state tree. - Clients fetch a validity proof with
getValidityProof()from an RPC provider that supports ZK Compression (Helius, Triton, …).
- Specify input state and output state tree (stores updated account hash)
- Define
account_meta: CompressedAccountMetato reference the existing account and specify the state tree to store the updated account hash:tree_info: PackedStateTreeInfo: References the existing account hash in the state tree.address: The account’s derived address.output_state_tree_index: References the state tree account that will store the updated account hash.
Clients fetch the current account with
getCompressedAccount() and populate CompressedAccountMeta with the account’s metadata.- Current account data
- Define fields to include the current account data passed by the client.
- This depends on your program logic. This example includes
current_message(orcurrent_accountin Anchor) andnew_messagefields.new_messagecontains the new data that will replace the message field of the compressed account after the update.
3
Update Compressed Account
Load the compressed account and update it with Pass these parameters to
LightAccount::new_mut().new_mut():- hashes the current account data as input state and
- lets your program define the output state.
- Anchor
- Native Rust
new_mut():&program_id: The program’s ID that owns the compressed account.&account_meta: TheCompressedAccountMetafrom instruction data (Step 2) that identifies the existing account and specifies the output state tree.- Include the curent account data.
- Anchor: Pass
current_accountdirectly - Native: Construct
MyCompressedAccountwith data frominstruction_data
- Anchor: Pass
- A
LightAccountwrapper similar to Anchor’sAccount. new_mut()lets the program modify the output state. This example setsmessagetonew_message.
new_mut() only hashes the input state. The Light System Program verifies that input hash exists in a state tree and creates the output hash in Step 4.4
Light System Program CPI
Invoke the Light System Program to update the compressed account.Set up
Build the CPI instruction:
The Light System Program
- validates the account exists in state tree,
- nullifies the existing account hash in the state tree, and
- appends the updated account hash to the state tree.
- Anchor
- Native Rust
CpiAccounts::new():CpiAccounts::new() parses accounts for the CPI call to Light System Program.Pass these parameters:ctx.accounts.signer.as_ref(): the transaction signerctx.remaining_accounts: Slice with[system_accounts, ...packed_tree_accounts]. The client builds this withPackedAccountsand passes it to the instruction.&LIGHT_CPI_SIGNER: Your program’s CPI signer PDA defined in Constants.
System Accounts List
System Accounts List
| Name | Description | |
|---|---|---|
| 1 | Verifies validity proofs, compressed account ownership checks, CPIs the account compression program to update tree accounts | |
| 2 | CPI Signer | - PDA to sign CPI calls from your program to Light System Program - Verified by Light System Program during CPI - Derived from your program ID |
| 3 | Registered Program PDA | - Access control to the Account Compression Program |
| 4 | - Logs compressed account state to Solana ledger (only used in v1) - Indexers parse transaction logs to reconstruct compressed account state | |
| 5 | Signs CPI calls from Light System Program to Account Compression Program | |
| 6 | - Writes to state and address tree accounts - Client and the account compression program do not interact directly. | |
| 7 | Invoking Program | Your program’s ID, used by Light System Program to: - Derive the CPI Signer PDA - Verify the CPI Signer matches your program ID - Set the owner of created compressed accounts |
| 8 | Solana System Program to transfer lamports |
new_cpi()initializes the CPI instruction with theproofto prove that the account exists in the specified state tree - in the Instruction Data (Step 2).with_light_accountadds theLightAccountwith the modified compressed account data - defined in Step 3invoke(light_cpi_accounts)calls the Light System Program withCpiAccounts.
Full Code Example
The example programs below implement all steps from this guide. Make sure you have your developer environment set up first, or simply run:- Anchor
- Native Rust
Find the source code here.