> ## Documentation Index
> Fetch the complete documentation index at: https://www.zkcompression.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Lifecycle of a Transaction

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

# Overview

<Info>
  This guide assumes you are familiar with transactions on Solana. If you aren't, we recommend to read the [Solana documentation on transactions](https://solana.com/docs/core/transactions).
</Info>

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](https://solana.com/docs/core/transactions#array-of-account-addresses).

# 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      |

<Tabs>
  <Tab title="Clients">
    Clients read compressed accounts similar to Solana accounts:

    1. Fetch the compressed account data from your RPC provider using its address or hash
       * Here you use [`getCompressedAccount()`](/api-reference/json-rpc-methods/getcompressedaccount), similar to `getAccountInfo()`.
    2. Deserialize the account's data field into the appropriate data structure.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      // 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
      );
      ```

      ```rust Rust theme={null}
      // 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)?;
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Programs">
    On-chain reading within programs requires a validity proof to verify the account exists in the state tree.

    1. Fetch the compressed account data from your RPC provider using its address or hash
    2. Fetch a validity proof using the account hash via [`getValidityProof()`](/api-reference/json-rpc-methods/getvalidityproof).
    3. Clients pass the proof to the on-chain program in the instruction data.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      // 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
      ```

      ```rust Rust theme={null}
      // 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 instructions
      ```
    </CodeGroup>

    <Info>
      See this [read-only program example](https://github.com/Lightprotocol/program-examples/tree/main/read-only) implementation.
    </Info>
  </Tab>
</Tabs>

# Writing to Compressed Accounts

Writing to compressed accounts can be described more generally as:

```bash theme={null}
(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()`](/api-reference/json-rpc-methods/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

<Info>
  See this [guide to update a compressed account](/pda/compressed-pdas/guides/how-to-update-compressed-accounts).
</Info>

<Frame caption="Simplified: Read and Write compressed accounts">
  <img src="https://mintcdn.com/luminouslabs-cc5545c6/71xq4qzgNsL3Pf0n/images/Light-Protocol-v2%20-Batched-Merkle-trees-(4).png?fit=max&auto=format&n=71xq4qzgNsL3Pf0n&q=85&s=c1745f81c092d3468de23911a558bde7" width="5926" height="2592" data-path="images/Light-Protocol-v2 -Batched-Merkle-trees-(4).png" />
</Frame>

# 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](https://github.com/Lightprotocol/light-protocol/blob/v.1.0.0/programs/system/src/invoke/verify_state_proof.rs#L204-L210), etc.)
2. [Verifies the validity proof](https://github.com/Lightprotocol/light-protocol/blob/v.1.0.0/programs/system/src/invoke/processor.rs#L209-L214)
3. [Nullifies](https://github.com/Lightprotocol/light-protocol/blob/v.1.0.0/programs/system/src/invoke/processor.rs#L209-L214) the existing leaf of the compressed account that is being written to, to prevent double spending.
4. [Appends](https://github.com/Lightprotocol/light-protocol/blob/v.1.0.0/programs/system/src/invoke/processor.rs#L245-L254) the new compressed account hash to the state tree and advances the tree's state root
5. [Emits](https://github.com/Lightprotocol/light-protocol/blob/v.1.0.0/programs/system/src/invoke/processor.rs#L272-L279) 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.

<Tabs>
  <Tab title="Before Tx Execution">
    <Frame>
      <img src="https://mintcdn.com/luminouslabs-cc5545c6/_CD81U5EBeZEhMtt/images/image-(2).png?fit=max&auto=format&n=_CD81U5EBeZEhMtt&q=85&s=f90e21853c228ff74f94d380d5d0d84a" width="4096" height="2218" data-path="images/image-(2).png" />
    </Frame>
  </Tab>

  <Tab title="After Tx Execution">
    <Frame>
      <img src="https://mintcdn.com/luminouslabs-cc5545c6/_CD81U5EBeZEhMtt/images/image-(3).png?fit=max&auto=format&n=_CD81U5EBeZEhMtt&q=85&s=1e37164f8b8d4ea71bc162e7d13219a6" width="4096" height="2218" data-path="images/image-(3).png" />
    </Frame>
  </Tab>
</Tabs>

# Next Steps

<Card title="Understand the trade-offs for compressed accounts." icon="chevron-right" color="#0066ff" href="/learn/core-concepts/considerations" horizontal />
