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

> For some use cases, such as sending payments, you might want to prevent your on chain instruction from being executed more than once.

The nullifier program utility solves this for you.

1. It derives PDA from `["nullifier", id]` seeds (where `id` is your unique identifier, e.g. a nonce, uuid, hash of signature, etc.)
2. Creates an empty rent-free PDA at that address
3. If the address exists, the whole transaction fails
4. Prepend or append this instruction to your transaction.

We also deployed a reference implementation to public networks so you can get started quickly:

|                        |                                                                                                                                            |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| **Cost per nullifier** | \~15,000 lamports (\~0.000015 SOL)                                                                                                         |
| **Program ID**         | `NFLx5WGPrTHHvdRNsidcrNcLxRruMC92E4yv7zhZBoT`                                                                                              |
| **Networks**           | Mainnet, Devnet                                                                                                                            |
| **Source code**        | [github.com/Lightprotocol/nullifier-program](https://github.com/Lightprotocol/nullifier-program/)                                          |
| **Example Tx**         | [Solana Explorer](https://explorer.solana.com/tx/38fA6kbKRcYb5XSez9ffQzfCcMbMHcaJGseCogShNXC5SemQsEo88ZMSPCLP9xv9PG8qSJnhWvWFqSYJnfBMLrpB) |

<Callout type="info">
  Example source code: [TypeScript](https://github.com/Lightprotocol/nullifier-program/tree/main/examples/action-create-nullifier.ts) | [Rust](https://github.com/Lightprotocol/nullifier-program/tree/main/examples/rust)
</Callout>

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

    <Tabs>
      <Tab title="Rust">
        ```toml theme={null}
        [dependencies]
        light-nullifier-program = "0.1.2"
        light-client = "0.23.0"
        ```
      </Tab>

      <Tab title="TS">
        ```bash theme={null}
        npm install @lightprotocol/nullifier-program @lightprotocol/stateless.js@^0.23.0
        ```
      </Tab>
    </Tabs>

    ## Using the Helper

    <Tabs>
      <Tab title="Rust">
        ```rust theme={null}
        use light_nullifier_program::sdk::{create_nullifier_ix, PROGRAM_ID};
        use light_client::{LightClient, LightClientConfig};
        use solana_sdk::{system_instruction, transaction::Transaction};

        let mut rpc = LightClient::new(
            LightClientConfig::new("https://mainnet.helius-rpc.com/?api-key=...")
        ).await?;

        // Create a unique 32-byte ID
        let id: [u8; 32] = /* hash of payment inputs or random */;

        // Build nullifier instruction
        let nullifier_ix = create_nullifier_ix(&mut rpc, payer.pubkey(), id).await?;

        // Combine with your transaction
        let transfer_ix = system_instruction::transfer(&payer.pubkey(), &recipient, 1_000_000);
        let tx = Transaction::new_signed_with_payer(
            &[nullifier_ix, transfer_ix],
            Some(&payer.pubkey()),
            &[&payer],
            recent_blockhash,
        );
        ```
      </Tab>

      <Tab title="TS">
        ```typescript theme={null}
        import { createNullifierIx } from "@lightprotocol/nullifier-program";
        import { createRpc } from "@lightprotocol/stateless.js";
        import { Transaction, SystemProgram, ComputeBudgetProgram } from "@solana/web3.js";

        const rpc = createRpc("https://mainnet.helius-rpc.com/?api-key=...");

        // Create a unique 32-byte ID (e.g., hash of payment inputs)
        const id = new Uint8Array(32);
        crypto.getRandomValues(id);

        // Build nullifier instruction
        const nullifierIx = await createNullifierIx(rpc, payer.publicKey, id);

        // Combine with your transaction
        const tx = new Transaction().add(
          nullifierIx,
          // your main instruction
          SystemProgram.transfer({
            fromPubkey: payer.publicKey,
            toPubkey: recipient,
            lamports: 1_000_000,
          })
        );
        ```
      </Tab>
    </Tabs>

    ## Manually Fetching Proof

    <Tabs>
      <Tab title="Rust">
        ```rust theme={null}
        use light_nullifier_program::sdk::{fetch_proof, build_instruction};

        // Step 1: Fetch proof
        let proof_result = fetch_proof(&mut rpc, &id).await?;

        // Step 2: Build instruction
        let nullifier_ix = build_instruction(payer.pubkey(), id, proof_result);

        // Add to transaction
        let tx = Transaction::new_signed_with_payer(
            &[nullifier_ix, transfer_ix],
            Some(&payer.pubkey()),
            &[&payer],
            recent_blockhash,
        );
        ```
      </Tab>

      <Tab title="TS">
        ```typescript theme={null}
        import { fetchProof, buildInstruction } from "@lightprotocol/nullifier-program";

        // Step 1: Fetch proof
        const proofResult = await fetchProof(rpc, id);

        // Step 2: Build instruction
        const nullifierIx = buildInstruction(payer.publicKey, id, proofResult);

        // Combine with your transaction
        const tx = new Transaction().add(
          nullifierIx,
          // your main instruction
          SystemProgram.transfer({
            fromPubkey: payer.publicKey,
            toPubkey: recipient,
            lamports: 1_000_000,
          })
        );
        ```
      </Tab>
    </Tabs>

    ## Check If Nullifier Exists

    <Tabs>
      <Tab title="Rust">
        ```rust theme={null}
        use light_nullifier_program::sdk::derive_nullifier_address;

        let address = derive_nullifier_address(&id);
        let account = rpc.get_compressed_account(None, Some(address)).await?;
        let exists = account.value.is_some();
        ```
      </Tab>

      <Tab title="TS">
        ```typescript theme={null}
        import { deriveNullifierAddress } from "@lightprotocol/nullifier-program";
        import { bn } from "@lightprotocol/stateless.js";

        const address = deriveNullifierAddress(id);
        const account = await rpc.getCompressedAccount(bn(address.toBytes()));
        const exists = account !== null;
        ```
      </Tab>
    </Tabs>

    ***

    Note that this is a reference implementation. Feel free to fork the program as you see fit.

    <Callout type="info">
      Questions or need support? [Telegram](https://t.me/swen_light) | [email](mailto:support@lightprotocol.com) | [Discord](https://discord.com/invite/7cJ8BhAXhu)
    </Callout>
  </Tab>

  <Tab title="AI Prompt">
    <Prompt description="Create rent-free nullifier PDAs to prevent duplicate actions" actions={["copy", "cursor"]}>
      {`---
            description: Create rent-free nullifier PDAs to prevent duplicate actions
            allowed-tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, AskUserQuestion, Task, TaskCreate, TaskGet, TaskList, TaskUpdate, TaskOutput, mcp__deepwiki, mcp__zkcompression
            ---

            ## Create rent-free nullifier PDAs to prevent duplicate actions

            Context:
            - Guide: https://zkcompression.com/pda/compressed-pdas/nullifier-pda
            - Skills and resources index: https://zkcompression.com/skill.md
            - Dedicated skill: https://github.com/Lightprotocol/skills/tree/main/skills/zk-nullifier
            - Rust crates: light-nullifier-program, light-client
            - TS packages: @lightprotocol/nullifier-program, @lightprotocol/stateless.js
            - Example: https://github.com/Lightprotocol/examples-light-token/blob/main/rust-client/actions/create_nullifier.rs
            - Program source: https://github.com/Lightprotocol/nullifier-program/

            Key APIs:
            - Rust: create_nullifier_ix(), fetch_proof(), build_instruction(), derive_nullifier_address()
            - TS: createNullifierIx(), fetchProof(), buildInstruction(), deriveNullifierAddress()

            ### 1. Index project
            - Grep \`nullifier|create_nullifier|createNullifierIx|deriveNullifierAddress|NFLx5WGPrTHHvdRNsidcrNcLxRruMC92E4yv7zhZBoT\` across src/
            - Glob \`**/*.rs\` and \`**/*.ts\` for project structure
            - Identify: existing transaction building, duplicate prevention logic, payment flow
            - Check Cargo.toml or package.json for existing light-* dependencies
            - Task subagent (Grep/Read/WebFetch) if project has multiple packages to scan in parallel

            ### 2. Read references
            - WebFetch the guide above — review both Rust and TS 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: Rust or TypeScript?
            - AskUserQuestion: what is the goal? (prevent duplicate payments, idempotent instruction execution, other use case)
            - AskUserQuestion: do you need the helper (create_nullifier_ix) or manual proof fetching (fetch_proof + build_instruction)?
            - 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 pattern: create unique 32-byte ID → build nullifier instruction → prepend to transaction
            - If checking existence is needed, add derive_nullifier_address + get_compressed_account check
            - If anything is unclear or ambiguous, loop back to step 3 (AskUserQuestion)
            - Present the plan to the user for approval before proceeding

            ### 5. Implement
            - For Rust: Bash \`cargo add light-nullifier-program@0.1 light-client@0.23\`
            - For TypeScript: Bash \`npm install @lightprotocol/nullifier-program @lightprotocol/stateless.js@^0.23.0\`
            - Follow the guide and the approved plan
            - Write/Edit to create or modify files
            - TaskUpdate to mark each step done

            ### 6. Verify
            - Rust: Bash \`cargo check\` + \`cargo test\` if tests exist
            - TypeScript: Bash \`tsc --noEmit\` + run existing test suite if present
            - 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: Create rent-free nullifier PDAs to prevent duplicate actions
    allowed-tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, AskUserQuestion, Task, TaskCreate, TaskGet, TaskList, TaskUpdate, TaskOutput, mcp__deepwiki, mcp__zkcompression
    ---

    ## Create rent-free nullifier PDAs to prevent duplicate actions

    Context:
    - Guide: https://zkcompression.com/pda/compressed-pdas/nullifier-pda
    - Skills and resources index: https://zkcompression.com/skill.md
    - Dedicated skill: https://github.com/Lightprotocol/skills/tree/main/skills/zk-nullifier
    - Rust crates: light-nullifier-program, light-client
    - TS packages: @lightprotocol/nullifier-program, @lightprotocol/stateless.js
    - Example: https://github.com/Lightprotocol/examples-light-token/blob/main/rust-client/actions/create_nullifier.rs
    - Program source: https://github.com/Lightprotocol/nullifier-program/

    Key APIs:
    - Rust: create_nullifier_ix(), fetch_proof(), build_instruction(), derive_nullifier_address()
    - TS: createNullifierIx(), fetchProof(), buildInstruction(), deriveNullifierAddress()

    ### 1. Index project
    - Grep `nullifier|create_nullifier|createNullifierIx|deriveNullifierAddress|NFLx5WGPrTHHvdRNsidcrNcLxRruMC92E4yv7zhZBoT` across src/
    - Glob `**/*.rs` and `**/*.ts` for project structure
    - Identify: existing transaction building, duplicate prevention logic, payment flow
    - Check Cargo.toml or package.json for existing light-* dependencies
    - Task subagent (Grep/Read/WebFetch) if project has multiple packages to scan in parallel

    ### 2. Read references
    - WebFetch the guide above — review both Rust and TS 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: Rust or TypeScript?
    - AskUserQuestion: what is the goal? (prevent duplicate payments, idempotent instruction execution, other use case)
    - AskUserQuestion: do you need the helper (create_nullifier_ix) or manual proof fetching (fetch_proof + build_instruction)?
    - 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 pattern: create unique 32-byte ID → build nullifier instruction → prepend to transaction
    - If checking existence is needed, add derive_nullifier_address + get_compressed_account check
    - If anything is unclear or ambiguous, loop back to step 3 (AskUserQuestion)
    - Present the plan to the user for approval before proceeding

    ### 5. Implement
    - For Rust: Bash `cargo add light-nullifier-program@0.1 light-client@0.23`
    - For TypeScript: Bash `npm install @lightprotocol/nullifier-program @lightprotocol/stateless.js@^0.23.0`
    - Follow the guide and the approved plan
    - Write/Edit to create or modify files
    - TaskUpdate to mark each step done

    ### 6. Verify
    - Rust: Bash `cargo check` + `cargo test` if tests exist
    - TypeScript: Bash `tsc --noEmit` + run existing test suite if present
    - 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>
