Lifecycle of a Transaction
Overview to the lifecycle of a transaction that interacts with compressed accounts.
Overview
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:
getAccountInfo
getCompressedAccount
getBalance
getCompressedBalance
getTokenAccountsByOwner
getCompressedTokenAccountsByOwner
getProgramAccounts
getCompressedAccountsByOwner
Clients read compressed accounts similar to Solana accounts:
Fetch the compressed account data from your RPC provider using its address or hash
Here you use
getCompressedAccount(), similar togetAccountInfo().
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
);// 1. Fetch compressed account from indexer
let compressed_account = rpc
.get_compressed_account(address, None)
.await?
.value
.unwrap();
// 2. Deserialize account data
let account_data = deserialize(&compressed_account.data)?;On-chain reading within programs requires a validity proof to verify the account exists in the state tree.
Fetch the compressed account data from your RPC provider using its address or hash
Fetch a validity proof using the account hash via
getValidityProof().Clients pass the proof to the on-chain program in the instruction data.
// 1. Fetch compressed account from indexer
const compressedAccount = await rpc.getCompressedAccount(
bn(address.toBytes())
);
// 2. Get validity proof using account hash
const proof = await rpc.getValidityProof(
[bn(compressedAccount.hash)]
);
// 3. Proof is included in transaction instructions// 1. Fetch compressed account from indexer
let compressed_account = rpc
.get_compressed_account(address, None)
.await?
.value
.unwrap();
// 2. Get validity proof using account hash
let proof = rpc
.get_validity_proof(vec![compressed_account.hash], vec![], None)
.await?
.value;
// 3. Proof is included in transaction instructionsWriting 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:
Fetch the compressed account data from your RPC provider using its address or hash
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 datadata': updated account datavalidity proof: 128-byte ZK proof that verifies the current account hash exists in the state tree

On-chain Execution
To write compressed state, a program invokes the Light System Program via CPI. The system program then does the following:
Runs relevant checks (sum check, etc.)
Nullifies the existing leaf of the compressed account that is being written to, to prevent double spending.
Appends the new compressed account hash to the state tree and advances the tree's state root
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.


Next Steps
Take the last step and understand what the trade-offs for compressed accounts are.
ConsiderationsLast updated
Was this helpful?