Lifecycle of a Transaction

Overview to the lifecycle of a transaction that interacts with compressed accounts.

Overview

This guide assumes you are familiar with transactions on Solana. If you aren't, we recommend to read the Solana documentation on transactions.

Transactions to interact with compressed accounts are fully compatible with Solana's Transaction and Versioned Transaction formats.

There are few nuances to build transactions with compressed accounts as compared to regular accounts:

  • Instructions must specify the list of all compressed accounts being read or written to.

  • To read or write to a compressed account, the instruction must send the current account state on-chain and prove its validity.

  • Each unique state tree that gets read or written to (via any compressed account) needs to be specified as per Solana's regular on-chain account access lists.

Reading Compressed Accounts

Reading compressed accounts follows a similar pattern to Solana accounts.

  • The main difference is that compressed account RPC methods query an indexer instead of the ledger directly.

  • The indexer, called Photon, reconstructs compressed account state from the Solana ledger by reading transaction logs.

The API exposed by the indexer closely mirrors existing RPC calls, with one-to-one mapping:

Solana RPC
Photon RPC Calls

getAccountInfo

getCompressedAccount

getBalance

getCompressedBalance

getTokenAccountsByOwner

getCompressedTokenAccountsByOwner

getProgramAccounts

getCompressedAccountsByOwner

Clients read compressed accounts similar to Solana accounts:

  1. Fetch the compressed account data from your RPC provider using its address or hash

  2. Deserialize the account's data field into the appropriate data structure.

// 1. Fetch compressed account from indexer
const compressedAccount = await rpc.getCompressedAccount(
  bn(address.toBytes())
);

// 2. Deserialize account data
const accountData = coder.types.decode(
  "AccountType",
  compressedAccount.data.data
);

Writing to Compressed Accounts

Writing to compressed accounts can be described more generally as:

(state, validityProof) -> state transition -> state'

Writing to a compressed account involves these steps:

  1. Fetch the compressed account data from your RPC provider using its address or hash

  2. Fetch a validity proof from your RPC provider using the account hash via getValidityProof() to prove the account hash exists in the state tree.

The Solana program executing the state transition Data -> Data' requires its client to pack the instructions with:

  • address: persistent identifier of the compressed account (unchanged)

  • owner program: program ID that owns this account (unchanged)

  • data: current account data

  • data': updated account data

  • validity proof: 128-byte ZK proof that verifies the current account hash exists in the state tree

Diagram illustrating compressed account state updates.  On the left, under “Read Existing Account,” four stacked boxes represent: 	1.	Address – the unique identifier of the account, 	2.	Owner Program – the Solana program controlling the account, 	3.	Data – the account’s stored state, and 	4.	Validity Proof – a zero-knowledge proof verifying that the read data is valid.  An arrow in the center points to the right, labeled “Write Updated Account,” showing the same fields—Address, Owner Program, and Data′ (updated data)—indicating that only the data field changes while the validity proof ensures correctness.  This visualization explains the read-modify-write process for compressed accounts in Solana’s ZK Compression architecture, demonstrating how off-chain state updates are validated and written on-chain.
Simplified: Read and Write compressed accounts

On-chain Execution

To write compressed state, a program invokes the Light System Program via CPI. The system program then does the following:

  1. Runs relevant checks (sum check, etc.)

  2. Nullifies the existing leaf of the compressed account that is being written to, to prevent double spending.

  3. Appends the new compressed account hash to the state tree and advances the tree's state root

  4. Emits the new compressed account state onto the Solana ledger

An RPC node then parses the transaction and compressed state and provides the read state to clients via the ZK Compression RPC API.

Diagram showing the initial data flow in before writing compressed state on-chain. A Client interacts with an Indexer (RPC) to fetch account data and generate a validity proof. The client then builds and sends a transaction to a Custom Program, which in turn invokes the Light System Program. On the right, the H(Account) hash represents the existing compressed account within the State Tree. This visualization explains how proofs and account hashes are prepared for verification prior to state updates during Solana’s on-chain execution process in. The diagram is simplified and does not include other components that are involved such as the queues, account compression program and forester nodes

Next Steps

Take the last step and understand what the trade-offs for compressed accounts are.

Considerations

Last updated

Was this helpful?