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

# Create Compressed Accounts

> Guide to create compressed accounts in Solana programs with full code examples.

Compressed accounts and addresses are created via CPI to the Light System Program.

* Compressed and regular Solana accounts share the same functionality and are fully composable.
* A compressed account has two identifiers: the account hash and its address (optional). In comparison, regular Solana accounts are identified by their address.
* The account hash is not persistent and changes with every write to the account.
* For Solana PDA like behavior your compressed account needs an address as persistent identifier.

<Note>
  Find [full code examples at the end](/compressed-pdas/guides/how-to-create-compressed-accounts#full-code-example) for Anchor and native Rust.
</Note>

# Implementation Guide

This guide will cover the components of a Solana program that creates compressed accounts.\
Here is the complete flow:

<div className="hidden dark:block">
  <Frame>
    <img src="https://mintcdn.com/luminouslabs-cc5545c6/_CD81U5EBeZEhMtt/images/program-create-1.png?fit=max&auto=format&n=_CD81U5EBeZEhMtt&q=85&s=90e5b68888ef5b93dc137a47942587a6" alt="" width="1146" height="639" data-path="images/program-create-1.png" />
  </Frame>
</div>

<div className="block dark:hidden">
  <Frame>
    <img src="https://mintcdn.com/luminouslabs-cc5545c6/_CD81U5EBeZEhMtt/images/program-create.png?fit=max&auto=format&n=_CD81U5EBeZEhMtt&q=85&s=ab9c4cdc75a95d20cc191b40b3e7d941" alt="" width="1146" height="639" data-path="images/program-create.png" />
  </Frame>
</div>

<Tabs>
  <Tab title="Guide">
    <Steps>
      <Step>
        ### Dependencies

        Add dependencies to your program.

        <CodeGroup>
          ```toml Anchor theme={null}
          [dependencies]
          light-sdk = "0.23.0"
          anchor_lang = "0.31.1"
          ```

          ```toml Native Rust theme={null}
          [dependencies]
          light-sdk = "0.23.0"
          borsh = "0.10.4"
          solana-program = "2.2"
          ```
        </CodeGroup>

        * The `light-sdk` provides macros, wrappers and CPI interface to create and interact with compressed accounts.
        * Add the serialization library (`borsh` for native Rust, or use `AnchorSerialize`).
      </Step>

      <Step>
        ### Constants

        Set program address and derive the CPI authority PDA to call the Light System program.

        <CodeGroup>
          ```rust Anchor theme={null}
          declare_id!("rent4o4eAiMbxpkAM1HeXzks9YeGuz18SEgXEizVvPq");

          pub const LIGHT_CPI_SIGNER: CpiSigner =
              derive_light_cpi_signer!("rent4o4eAiMbxpkAM1HeXzks9YeGuz18SEgXEizVvPq");
          ```

          ```rust Native Rust theme={null}
          pub const ID: Pubkey = pubkey!("rent4o4eAiMbxpkAM1HeXzks9YeGuz18SEgXEizVvPq");
          pub const LIGHT_CPI_SIGNER: CpiSigner =
              derive_light_cpi_signer!("rent4o4eAiMbxpkAM1HeXzks9YeGuz18SEgXEizVvPq");
          ```
        </CodeGroup>

        **`CPISigner`** is the configuration struct for CPIs to the Light System Program.

        * CPI 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.
      </Step>

      <Step>
        ### Compressed Account

        <CodeGroup>
          ```rust Anchor theme={null}
          #[event] // declared as event so that it is part of the idl.
          #[derive(
              Clone,
              Debug,
              Default,
              LightDiscriminator
          )]
          pub struct MyCompressedAccount {
              pub owner: Pubkey,
              pub message: String,
          }
          ```

          ```rust Native Rust theme={null}
          #[derive(
              Clone,
              Debug,
              Default,
              BorshSerialize,
              BorshDeserialize,
              LightDiscriminator
          )]
          pub struct MyCompressedAccount {
              pub owner: Pubkey,
              pub message: String,
          }
          ```
        </CodeGroup>

        Define your compressed account struct and derive

        * the standard traits (`Clone`, `Debug`, `Default`),
        * `borsh` or `AnchorSerialize` to serialize account data, and
        * `LightDiscriminator` to implements a unique type ID (8 bytes) to distinguish account types. The default compressed account layout enforces a discriminator in its *own field*, <Tooltip tip="The Anchor framework reserves the first 8 bytes of a *regular account's data field* for the discriminator." cta="Anchor" href="https://www.anchor-lang.com/">not the first 8 bytes of the data field</Tooltip>.

        <Info>
          The traits listed above are required for `LightAccount`. `LightAccount` wraps `my-compressed-account` in Step 7 to set the discriminator and create the compressed account's data.
        </Info>
      </Step>

      <Step>
        ### Instruction Data

        Define the instruction data with the following parameters:

        <Tabs>
          <Tab title="Anchor">
            Anchor handles instruction deserialization automatically. Pass the parameters directly to the instruction function:

            ```rust theme={null}
            pub fn create_account<'info>(
                ctx: Context<'_, '_, '_, 'info, GenericAnchorAccounts<'info>>,
                proof: ValidityProof,
                address_tree_info: PackedAddressTreeInfo,
                output_state_tree_index: u8,
                message: String,
            ) -> Result<()>
            ```
          </Tab>

          <Tab title="Native Rust">
            Define an instruction data struct that will be deserialized from the instruction data:

            ```rust Native Rust theme={null}
            pub struct CreateInstructionData {
                pub proof: ValidityProof,
                pub address_tree_info: PackedAddressTreeInfo,
                pub output_state_tree_index: u8,
                pub message: String,
            }
            ```
          </Tab>
        </Tabs>

        1. **Validity Proof**

        * Define `proof` to include the proof that the address does not exist yet in the specified address tree.
        * Clients fetch a validity proof with `getValidityProof()` from an RPC provider that supports ZK Compression (Helius, Triton, ...).

        2. **Specify Merkle trees to store address and account hash**

        * Define `address_tree_info: PackedAddressTreeInfo` to reference the address tree account used to derive the address in the next step.
        * Define `output_state_tree_index` to reference the state tree account that stores the compressed account hash.

        <Info>
          Clients pack accounts into the accounts array to reduce transaction size. Packed structs like `PackedAddressTreeInfo` contain account indices (u8) instead of 32 byte pubkeys. The indices point to the account in the accounts array to retrieve the public key and other metadata.
        </Info>

        3. **Initial account data**

        * Define fields for your program logic. Clients pass the initial values.
        * This example includes the `message` field to define the initial state of the account.
      </Step>

      <Step>
        ### Derive Address

        Derive the address as a persistent unique identifier for the compressed account.

        <CodeGroup>
          ```rust Anchor theme={null}
          let (address, address_seed) = derive_address(
              &[b"message", ctx.accounts.signer.key().as_ref()],
              &address_tree_info
                  .get_tree_pubkey(&light_cpi_accounts)
              &crate::ID,
          );
          ```

          ```rust Native Rust theme={null}
          let (address, address_seed) = derive_address(
              &[b"message", signer.key.as_ref()],
              &instruction_data
                  .address_tree_info
                  .get_tree_pubkey(&light_cpi_accounts)
              &ID,
          );
          ```
        </CodeGroup>

        **Pass these parameters to `derive_address()`:**

        * `&custom_seeds`: Predefined inputs, such as strings, numbers or other account addresses. This example uses `b"message"` and the signer's pubkey.
        * `&address_tree_pubkey`: The pubkey of the address tree where the address will be created.
          * Retrieved by calling `get_tree_pubkey()` on `address_tree_info`, which unpacks the index from the accounts array.
          * This parameter ensures an address is unique to an address tree. Different trees produce different addresses from identical seeds.
        * `&program_id`: Your program's ID.

        **The SDK returns:**

        * `address`: The derived address for the compressed account.
        * `address_seed`: Pass this to the Light System Program CPI in *Step 8* to create the address.
      </Step>

      <Step>
        ### Address Tree Check (optional)

        Ensure global uniqueness of an address by verifying that the address tree pubkey matches the program's tree constant.

        <Info>
          Every address is unique, but the same seeds can be used to create different addresses in different address trees. To enforce that a compressed PDA can only be created once with the same seed, you must check the address tree pubkey.
        </Info>

        ```rust theme={null}
        let address_tree = light_cpi_accounts.tree_pubkeys().unwrap()
            [address_tree_info.address_merkle_tree_pubkey_index as usize];

        if address_tree != light_sdk::constants::ADDRESS_TREE_V2 {
            return Err(ProgramError::InvalidAccountData.into());
        }
        ```
      </Step>

      <Step>
        ### Initialize Compressed Account

        Initialize the compressed account struct with `LightAccount::new_init()`.

        <Note>
          `new_init()` creates a `LightAccount` instance similar to anchor `Account` and lets your program define the initial account data.
        </Note>

        <CodeGroup>
          ```rust Anchor theme={null}
          let mut my_compressed_account = LightAccount::<MyCompressedAccount>::new_init(
              &crate::ID,
              Some(address),
              output_state_tree_index,
          );

          my_compressed_account.owner = ctx.accounts.signer.key();
          my_compressed_account.message = message.clone();
          ```

          ```rust Native Rust theme={null}
          let mut my_compressed_account = LightAccount::<MyCompressedAccount>::new_init(
              &ID,
              Some(address),
              instruction_data.output_state_tree_index,
          );
          my_compressed_account.owner = *signer.key;
          my_compressed_account.message = instruction_data.message;
          ```
        </CodeGroup>

        **Pass these parameters to `new_init()`:**

        * `&owner`: The program's ID that owns the compressed account.
        * `Some(address)`: The derived address from *Step 5*. Pass `None` for accounts without addresses.
        * `output_state_tree_index`: References the state tree account that will store the updated account hash, defined in instruction data (*Step 4*)

        **The SDK creates:**

        * A `LightAccount` wrapper similar to Anchor's `Account.`
        * `new_init()` lets the program set the initial data. This example sets:
          * `owner` to the signer's pubkey
          * `message` to an arbitrary string
      </Step>

      <Step>
        ### Light System Program CPI

        Invoke the Light System Program to create the compressed account and its address.

        <Note>
          The Light System Program

          * verifies the validity proof against the address tree's Merkle root,
          * inserts the address into the address tree, and
          * appends the new account hash to the state tree.
        </Note>

        <Tabs>
          <Tab title="Anchor">
            ```rust theme={null}
            let light_cpi_accounts = CpiAccounts::new(
                ctx.accounts.signer.as_ref(),
                ctx.remaining_accounts,
                crate::LIGHT_CPI_SIGNER,
            );

            let new_address_params = address_tree_info
                .into_new_address_params_assigned_packed(address_seed, Some(0));

            LightSystemProgramCpi::new_cpi(LIGHT_CPI_SIGNER, proof)
                .with_light_account(my_compressed_account)?
                .with_new_addresses(&[new_address_params])
                .invoke(light_cpi_accounts)?;
            ```

            **Set up `CpiAccounts::new()`:**

            `CpiAccounts::new()` parses accounts for the CPI call to Light System Program.

            **Pass these parameters:**

            * `ctx.accounts.signer.as_ref()`: the transaction signer
            * `ctx.remaining_accounts`: Slice with `[system_accounts, ...packed_tree_accounts]`. The client builds this with `PackedAccounts` and passes it to the instruction.
            * `&LIGHT_CPI_SIGNER`: Your program's CPI signer PDA defined in Constants.
          </Tab>

          <Tab title="Native Rust">
            ```rust theme={null}
            let signer = accounts.first();

            let light_cpi_accounts = CpiAccounts::new(
                signer,
                &accounts[1..],
                LIGHT_CPI_SIGNER
            );

            let new_address_params = instruction_data
                .address_tree_info
                .into_new_address_params_assigned_packed(address_seed, Some(0));

            LightSystemProgramCpi::new_cpi(LIGHT_CPI_SIGNER, instruction_data.proof)
                .with_light_account(my_compressed_account)?
                .with_new_addresses(&[new_address_params])
                .invoke(light_cpi_accounts)?;
            ```

            **Set up `CpiAccounts::new()`:**

            `CpiAccounts::new()` parses accounts for the CPI call to Light System Program.

            **Pass these parameters:**

            * `signer`: account that signs and pays for the transaction
            * `&accounts[1..]`: Slice with `[system_accounts, ...packed_tree_accounts]`. The client builds this with `PackedAccounts`.
            * `&LIGHT_CPI_SIGNER`: Your program's CPI signer PDA defined in Constants.
          </Tab>
        </Tabs>

        <Accordion title="System Accounts List">
          <table>
            <colgroup>
              <col style={{width: '5%'}} />

              <col style={{width: '30%', textAlign: 'left'}} />

              <col style={{width: '65%'}} />
            </colgroup>

            <thead>
              <tr>
                <th style={{textAlign: 'left'}} />

                <th style={{textAlign: 'left'}} />

                <th style={{textAlign: 'left'}} />
              </tr>
            </thead>

            <tbody>
              <tr>
                <td>1</td>
                <td style={{textAlign: 'left'}}><strong><Tooltip tip="SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7" cta="Program ID" href="https://solscan.io/account/SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7">Light System Program</Tooltip></strong></td>
                <td>Verifies validity proofs, compressed account ownership checks, and CPIs the Account Compression Program to update tree accounts.</td>
              </tr>

              <tr>
                <td>2</td>
                <td style={{textAlign: 'left'}}><strong>CPI Signer</strong></td>

                <td>
                  * PDA to sign CPI calls from your program to the Light System Program.<br />
                  * Verified by the Light System Program during CPI.<br />
                  * Derived from your program ID.
                </td>
              </tr>

              <tr>
                <td>3</td>
                <td style={{textAlign: 'left'}}><strong>Registered Program PDA</strong></td>
                <td>Provides access control to the Account Compression Program.</td>
              </tr>

              <tr>
                <td>4</td>
                <td style={{textAlign: 'left'}}><strong><Tooltip tip="PDA derived from Light System Program ID with seed b 'cpi_authority'.HZH7qSLcpAeDqCopVU4e5XkhT9j3JFsQiq8CmruY3aru" cta="Program ID" href="https://solscan.io/account/HZH7qSLcpAeDqCopVU4e5XkhT9j3JFsQiq8CmruY3aru">Account Compression Authority</Tooltip></strong></td>
                <td>Signs CPI calls from the Light System Program to the Account Compression Program.</td>
              </tr>

              <tr>
                <td>5</td>
                <td style={{textAlign: 'left'}}><strong><Tooltip tip="compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq" cta="Program ID" href="https://solscan.io/account/compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq">Account Compression Program</Tooltip></strong></td>

                <td>
                  * Writes to state and address tree accounts.<br />
                  * Clients and the Account Compression Program do not interact directly — handled internally.
                </td>
              </tr>

              <tr>
                <td>6</td>
                <td style={{textAlign: 'left'}}><strong><Tooltip tip="11111111111111111111111111111111" cta="Program ID" href="https://solscan.io/account/11111111111111111111111111111111">System Program</Tooltip></strong></td>
                <td>Solana System Program used to transfer lamports.</td>
              </tr>
            </tbody>
          </table>
        </Accordion>

        **Build the CPI instruction**:

        `new_cpi()` initializes the CPI instruction with the `proof` to prove that an address does not exist yet in the specified address tree *- defined in the Instruction Data (Step 4).*

        * `with_light_account` adds the `LightAccount` with the initial compressed account data to the CPI instruction *- defined in Step 7*.
        * `with_new_addresses` adds the `address_seed` and metadata to the CPI instruction data - returned by `derive_address` *in Step 5*.
        * `invoke(light_cpi_accounts)` calls the Light System Program with `CpiAccounts.`
      </Step>
    </Steps>
  </Tab>

  <Tab title="AI Prompt">
    <Prompt description="Build a program that creates compressed accounts with addresses" actions={["copy", "cursor"]}>
      {`---
            description: Build a program that creates compressed accounts with addresses
            allowed-tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, AskUserQuestion, Task, TaskCreate, TaskGet, TaskList, TaskUpdate, TaskOutput, mcp__deepwiki, mcp__zkcompression
            ---

            ## Build a program that creates compressed accounts with addresses

            Context:
            - Guide: https://zkcompression.com/compressed-pdas/guides/how-to-create-compressed-accounts
            - Skills and resources index: https://zkcompression.com/skill.md
            - Crate: light-sdk (LightAccount, CpiAccounts, LightSystemProgramCpi, derive_light_cpi_signer!)
            - Anchor example: https://github.com/Lightprotocol/program-examples/tree/main/basic-operations/anchor/create
            - Native Rust example: https://github.com/Lightprotocol/program-examples/tree/main/basic-operations/native/programs/create

            Key SDK API: LightAccount::new_init(), derive_address()

            ### 1. Index project
            - Grep \`declare_id|#\[program\]|entrypoint!|Pubkey|AccountInfo|seeds|init|payer|space\` across src/
            - Glob \`**/*.rs\` and \`**/Cargo.toml\` for project structure
            - Identify: program ID, existing instructions, account structs, framework (Anchor or native)
            - Read Cargo.toml — note existing dependencies and Solana SDK version
            - Task subagent (Grep/Read/WebFetch) if project has multiple crates to scan in parallel

            ### 2. Read references
            - WebFetch the guide above — review both Anchor and Native Rust code samples
            - WebFetch skill.md — check for a dedicated skill and resources matching this task
            - TaskCreate one todo per phase below to track progress

            ### 3. Clarify intention
            - AskUserQuestion: what is the goal? (new program from scratch, add account creation to existing program, migrate from regular accounts)
            - AskUserQuestion: Anchor or Native Rust framework?
            - AskUserQuestion: does the program already have compressed account instructions, or is this the first one?
            - Summarize findings and wait for user confirmation before implementing

            ### 4. Create plan
            - Based on steps 1–3, draft an implementation plan: which files to modify, what code to add, dependency changes
            - Follow the guide's step order: Dependencies → Constants → Account Struct → Instruction Data → Derive Address → Address Tree Check → Initialize Account → Light System Program CPI
            - If anything is unclear or ambiguous, loop back to step 3 (AskUserQuestion)
            - Present the plan to the user for approval before proceeding

            ### 5. Implement
            - Add deps: Bash \`cargo add light-sdk@0.23\` (add \`anchor_lang@0.31\` for Anchor or \`solana-program@2.2\` + \`borsh@0.10\` for Native Rust)
            - Follow the guide and the approved plan
            - Write/Edit to create or modify files
            - TaskUpdate to mark each step done

            ### 6. Verify
            - Bash \`cargo build-sbf\` or \`anchor build\`
            - Bash \`cargo test-sbf\` or \`anchor test\` if tests exist
            - TaskUpdate to mark complete

            ### Tools
            - mcp__zkcompression__SearchLightProtocol("<query>") for API details
            - mcp__deepwiki__ask_question("Lightprotocol/light-protocol", "<q>") for architecture
            - Task subagent with Grep/Read/WebFetch for parallel lookups
            - TaskList to check remaining work`}
    </Prompt>

    ```text theme={null}
    ---
    description: Build a program that creates compressed accounts with addresses
    allowed-tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, AskUserQuestion, Task, TaskCreate, TaskGet, TaskList, TaskUpdate, TaskOutput, mcp__deepwiki, mcp__zkcompression
    ---

    ## Build a program that creates compressed accounts with addresses

    Context:
    - Guide: https://zkcompression.com/compressed-pdas/guides/how-to-create-compressed-accounts
    - Skills and resources index: https://zkcompression.com/skill.md
    - Crate: light-sdk (LightAccount, CpiAccounts, LightSystemProgramCpi, derive_light_cpi_signer!)
    - Anchor example: https://github.com/Lightprotocol/program-examples/tree/main/basic-operations/anchor/create
    - Native Rust example: https://github.com/Lightprotocol/program-examples/tree/main/basic-operations/native/programs/create

    Key SDK API: LightAccount::new_init(), derive_address()

    ### 1. Index project
    - Grep `declare_id|#\[program\]|entrypoint!|Pubkey|AccountInfo|seeds|init|payer|space` across src/
    - Glob `**/*.rs` and `**/Cargo.toml` for project structure
    - Identify: program ID, existing instructions, account structs, framework (Anchor or native)
    - Read Cargo.toml — note existing dependencies and Solana SDK version
    - Task subagent (Grep/Read/WebFetch) if project has multiple crates to scan in parallel

    ### 2. Read references
    - WebFetch the guide above — review both Anchor and Native Rust code samples
    - WebFetch skill.md — check for a dedicated skill and resources matching this task
    - TaskCreate one todo per phase below to track progress

    ### 3. Clarify intention
    - AskUserQuestion: what is the goal? (new program from scratch, add account creation to existing program, migrate from regular accounts)
    - AskUserQuestion: Anchor or Native Rust framework?
    - AskUserQuestion: does the program already have compressed account instructions, or is this the first one?
    - Summarize findings and wait for user confirmation before implementing

    ### 4. Create plan
    - Based on steps 1–3, draft an implementation plan: which files to modify, what code to add, dependency changes
    - Follow the guide's step order: Dependencies → Constants → Account Struct → Instruction Data → Derive Address → Address Tree Check → Initialize Account → Light System Program CPI
    - If anything is unclear or ambiguous, loop back to step 3 (AskUserQuestion)
    - Present the plan to the user for approval before proceeding

    ### 5. Implement
    - Add deps: Bash `cargo add light-sdk@0.23` (add `anchor_lang@0.31` for Anchor or `solana-program@2.2` + `borsh@0.10` for Native Rust)
    - Follow the guide and the approved plan
    - Write/Edit to create or modify files
    - TaskUpdate to mark each step done

    ### 6. Verify
    - Bash `cargo build-sbf` or `anchor build`
    - Bash `cargo test-sbf` or `anchor test` if tests exist
    - TaskUpdate to mark complete

    ### Tools
    - mcp__zkcompression__SearchLightProtocol("<query>") for API details
    - mcp__deepwiki__ask_question("Lightprotocol/light-protocol", "<q>") for architecture
    - Task subagent with Grep/Read/WebFetch for parallel lookups
    - TaskList to check remaining work
    ```
  </Tab>
</Tabs>

# Full Code Example

The example programs below implement all steps from this guide.

<Accordion title="Setup">
  **Install Solana CLI:**

  ```bash theme={null}
  sh -c "$(curl -sSfL https://release.solana.com/v2.2.15/install)"
  ```

  **Install Anchor CLI:**

  ```bash theme={null}
  cargo install --git https://github.com/coral-xyz/anchor avm --force
  avm install latest
  avm use latest
  ```

  **Install the Light CLI:**

  <Tabs>
    <Tab title="npm">
      ```bash theme={null}
      npm install -g @lightprotocol/zk-compression-cli
      ```
    </Tab>

    <Tab title="yarn">
      ```bash theme={null}
      yarn global add @lightprotocol/zk-compression-cli
      ```
    </Tab>

    <Tab title="pnpm">
      ```bash theme={null}
      pnpm add -g @lightprotocol/zk-compression-cli
      ```
    </Tab>
  </Tabs>

  **Verify installation:**

  ```bash theme={null}
  light --version
  ```
</Accordion>

<Warning>
  For help with debugging, see the [Error Cheatsheet](/resources/error-cheatsheet).
</Warning>

<Tabs>
  <Tab title="Anchor">
    <Info>
      Find the source code [here](https://github.com/Lightprotocol/program-examples/tree/main/basic-operations/anchor/create).
    </Info>

    ```rust theme={null}
    #![allow(unexpected_cfgs)]
    #![allow(deprecated)]

    use anchor_lang::{prelude::*, AnchorDeserialize, AnchorSerialize};
    use light_sdk::{
        account::LightAccount,
        address::v2::derive_address,
        cpi::{v2::CpiAccounts, CpiSigner},
        derive_light_cpi_signer,
        instruction::{PackedAddressTreeInfo, ValidityProof},
        LightDiscriminator, PackedAddressTreeInfoExt,
    };
    use light_sdk::constants::ADDRESS_TREE_V2;

    declare_id!("Hps5oaKdYWqjVZJnAxUE1uwbozwEgZZGCRA57p2wdqcS");

    pub const LIGHT_CPI_SIGNER: CpiSigner =
        derive_light_cpi_signer!("Hps5oaKdYWqjVZJnAxUE1uwbozwEgZZGCRA57p2wdqcS");

    #[program]
    pub mod create {

        use super::*;
        use light_sdk::cpi::{
            v2::LightSystemProgramCpi, InvokeLightSystemProgram, LightCpiInstruction,
        };

        /// Creates a new compressed account
        pub fn create_account<'info>(
            ctx: Context<'_, '_, '_, 'info, GenericAnchorAccounts<'info>>,
            proof: ValidityProof,
            address_tree_info: PackedAddressTreeInfo,
            output_state_tree_index: u8,
            message: String,
        ) -> Result<()> {
            let light_cpi_accounts = CpiAccounts::new(
                ctx.accounts.signer.as_ref(),
                ctx.remaining_accounts,
                crate::LIGHT_CPI_SIGNER,
            );

            let address_tree_pubkey = address_tree_info
                .get_tree_pubkey(&light_cpi_accounts)
                .map_err(|_| ErrorCode::AccountNotEnoughKeys)?;

            if address_tree_pubkey.to_bytes() != ADDRESS_TREE_V2 {
                msg!("Invalid address tree");
                return Err(ProgramError::InvalidAccountData.into());
            }

            let (address, address_seed) = derive_address(
                &[b"message", ctx.accounts.signer.key().as_ref()],
                &address_tree_pubkey,
                &crate::ID,
            );

            let mut my_compressed_account = LightAccount::<MyCompressedAccount>::new_init(
                &crate::ID,
                Some(address),
                output_state_tree_index,
            );

            my_compressed_account.owner = ctx.accounts.signer.key();
            my_compressed_account.message = message.clone();

            msg!(
                "Created compressed account with message: {}",
                my_compressed_account.message
            );

            LightSystemProgramCpi::new_cpi(LIGHT_CPI_SIGNER, proof)
                .with_light_account(my_compressed_account)?
                .with_new_addresses(&[address_tree_info.into_new_address_params_assigned_packed(address_seed, Some(0))])
                .invoke(light_cpi_accounts)?;

            Ok(())
        }
    }

    #[derive(Accounts)]
    pub struct GenericAnchorAccounts<'info> {
        #[account(mut)]
        pub signer: Signer<'info>,
    }

    // declared as event so that it is part of the idl.
    #[event]
    #[derive(Clone, Debug, Default, LightDiscriminator)]
    pub struct MyCompressedAccount {
        pub owner: Pubkey,
        pub message: String,
    }
    ```
  </Tab>

  <Tab title="Native Rust">
    <Info>
      Find the source code [here](https://github.com/Lightprotocol/program-examples/tree/main/basic-operations/native/programs/create).
    </Info>

    ```rust theme={null}
    #![allow(unexpected_cfgs)]

    #[cfg(any(test, feature = "test-helpers"))]
    pub mod test_helpers;

    use borsh::{BorshDeserialize, BorshSerialize};
    use light_macros::pubkey;
    use light_sdk::{
        account::sha::LightAccount,
        address::v2::derive_address,
        cpi::{
            v2::{CpiAccounts, LightSystemProgramCpi},
            CpiSigner, InvokeLightSystemProgram, LightCpiInstruction,
        },
        derive_light_cpi_signer,
        error::LightSdkError,
        instruction::{PackedAddressTreeInfo, ValidityProof},
        LightDiscriminator,
    };
    use light_sdk::constants::ADDRESS_TREE_V2;
    use light_sdk::PackedAddressTreeInfoExt;
    use solana_program::{
        account_info::AccountInfo, entrypoint, program_error::ProgramError, pubkey::Pubkey,
    };

    pub const ID: Pubkey = pubkey!("rent4o4eAiMbxpkAM1HeXzks9YeGuz18SEgXEizVvPq");
    pub const LIGHT_CPI_SIGNER: CpiSigner =
        derive_light_cpi_signer!("rent4o4eAiMbxpkAM1HeXzks9YeGuz18SEgXEizVvPq");

    entrypoint!(process_instruction);

    #[repr(u8)]
    #[derive(Debug)]
    pub enum InstructionType {
        Create = 0,
    }

    impl TryFrom<u8> for InstructionType {
        type Error = LightSdkError;

        fn try_from(value: u8) -> Result<Self, Self::Error> {
            match value {
                0 => Ok(InstructionType::Create),
                _ => panic!("Invalid instruction discriminator."),
            }
        }
    }

    #[derive(Debug, Default, Clone, BorshSerialize, BorshDeserialize, LightDiscriminator)]
    pub struct MyCompressedAccount {
        pub owner: Pubkey,
        pub message: String,
    }

    #[derive(BorshSerialize, BorshDeserialize)]
    pub struct CreateInstructionData {
        pub proof: ValidityProof,
        pub address_tree_info: PackedAddressTreeInfo,
        pub output_state_tree_index: u8,
        pub message: String,
    }

    pub fn process_instruction(
        program_id: &Pubkey,
        accounts: &[AccountInfo],
        instruction_data: &[u8],
    ) -> Result<(), ProgramError> {
        if program_id != &ID {
            return Err(ProgramError::IncorrectProgramId);
        }
        if instruction_data.is_empty() {
            return Err(ProgramError::InvalidInstructionData);
        }

        let discriminator = InstructionType::try_from(instruction_data[0])
            .map_err(|_| ProgramError::InvalidInstructionData)?;

        match discriminator {
            InstructionType::Create => {
                let instruction_data = CreateInstructionData::try_from_slice(&instruction_data[1..])
                    .map_err(|_| ProgramError::InvalidInstructionData)?;
                create(accounts, instruction_data)
            }
        }
    }

    pub fn create(
        accounts: &[AccountInfo],
        instruction_data: CreateInstructionData,
    ) -> Result<(), ProgramError> {
        let signer = accounts.first().ok_or(ProgramError::NotEnoughAccountKeys)?;

        let light_cpi_accounts = CpiAccounts::new(signer, &accounts[1..], LIGHT_CPI_SIGNER);

        let address_tree_pubkey = instruction_data
            .address_tree_info
            .get_tree_pubkey(&light_cpi_accounts)
            .map_err(|_| ProgramError::NotEnoughAccountKeys)?;

        if address_tree_pubkey.to_bytes() != ADDRESS_TREE_V2 {
            solana_program::msg!("Invalid address tree");
            return Err(ProgramError::InvalidAccountData);
        }

        let (address, address_seed) = derive_address(
            &[b"message", signer.key.as_ref()],
            &address_tree_pubkey,
            &ID,
        );

        let new_address_params = instruction_data
            .address_tree_info
            .into_new_address_params_assigned_packed(address_seed, Some(0));

        let mut my_compressed_account = LightAccount::<MyCompressedAccount>::new_init(
            &ID,
            Some(address),
            instruction_data.output_state_tree_index,
        );
        my_compressed_account.owner = *signer.key;
        my_compressed_account.message = instruction_data.message;

        LightSystemProgramCpi::new_cpi(LIGHT_CPI_SIGNER, instruction_data.proof)
            .with_light_account(my_compressed_account)?
            .with_new_addresses(&[new_address_params])
            .invoke(light_cpi_accounts)?;

        Ok(())
    }
    ```
  </Tab>
</Tabs>

# Next Steps

<CardGroup>
  <Card title="Build a client for your program" icon="chevron-right" color="#0066ff" href="/compressed-pdas/guides/client-guide" horizontal />

  <Card title="Learn how to update compressed accounts" icon="chevron-right" color="#0066ff" href="/compressed-pdas/guides/how-to-update-compressed-accounts" horizontal />
</CardGroup>
