| Program ID | NFLx5WGPrTHHvdRNsidcrNcLxRruMC92E4yv7zhZBoT |
| Networks | Mainnet, Devnet |
| Source code | github.com/Lightprotocol/nullifier-program |
| Example Tx | Solana Explorer |
For the usage example source code, see here: create_nullifier.rs
- Guide
- AI Prompt
How It Works
- Derives PDA from
["nullifier", id]seeds (whereidis your unique identifier, e.g. a nonce, uuid, hash of signature, etc.) - Creates an empty rent-free PDA at that address
- If the address exists, the whole transaction fails
- Prepend or append this instruction to your transaction.
Dependencies
- Rust
- TS
Report incorrect code
Copy
Ask AI
[dependencies]
light-nullifier-program = "0.1.2"
light-client = "0.19.0"
Report incorrect code
Copy
Ask AI
npm install @lightprotocol/nullifier-program @lightprotocol/stateless.js@beta
Using the Helper
- Rust
- TS
Report incorrect code
Copy
Ask AI
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,
);
Report incorrect code
Copy
Ask AI
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,
})
);
Manually Fetching Proof
- Rust
- TS
Report incorrect code
Copy
Ask AI
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,
);
Report incorrect code
Copy
Ask AI
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,
})
);
Check If Nullifier Exists
- Rust
- TS
Report incorrect code
Copy
Ask AI
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();
Report incorrect code
Copy
Ask AI
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;
Note that this is a reference implementation. Feel free to fork the program as you see fit.
Create rent-free nullifier PDAs to prevent duplicate actions
Report incorrect code
Copy
Ask AI
---
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/guides/how-to-create-nullifier-pdas
- 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.19`
- For TypeScript: Bash `npm install @lightprotocol/nullifier-program @lightprotocol/stateless.js@beta`
- 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