Creates a rent-free PDA derived from id. If the id has been used before, the PDA already exists, causing the instruction to fail.
- Rust 1.79+
- Solana CLI 2.2+
- Anchor CLI 0.31.1
- Node.js 18+
anchor build
npm run buildcargo test-sbf -p light-nullifier-programRequires local validator.
npm install -g @lightprotocol/zk-compression-cli@0.28.0-beta.5light test-validator
npm testBoth examples load your Solana keypair at ~/.config/solana/id.json.
Devnet(default):
Set up the .env file with a Helius API key (get one here):
cp .env.example .env
# edit .env and add your API_KEYLocalnet: For localnet, install the CLI, start the test-validator with the program, and swap the RPC comments in the example files:
npm install -g @lightprotocol/zk-compression-cli@0.28.0-beta.5light test-validator --sbf-program NFLx5WGPrTHHvdRNsidcrNcLxRruMC92E4yv7zhZBoT target/deploy/light_nullifier_program.soFind and run example here: rust/src/main.rs.
cd examples/rust && cargo runFind and run example here: action-create-nullifier.ts.
npm installnpm run ts:create-nullifierWorks with LightClient (production) or LightProgramTest (testing).
use light_nullifier_program::sdk::{create_nullifier_ix, PROGRAM_ID};
use light_client::{LightClient, LightClientConfig};
let rpc_url = "https://devnet.helius-rpc.com/?api-key=...".to_string();
let config = LightClientConfig::new(rpc_url, None, None);
let mut rpc = LightClient::new(config).await?;
let ix = create_nullifier_ix(&mut rpc, payer.pubkey(), id).await?;Or step-by-step:
use light_nullifier_program::sdk::{fetch_proof, build_instruction};
let proof_result = fetch_proof(&mut rpc, &id).await?;
let ix = build_instruction(payer.pubkey(), id, proof_result);Works with any Rpc from @lightprotocol/stateless.js.
import {
createNullifierIx,
PROGRAM_ID,
} from "@lightprotocol/nullifier-program";
import { createRpc } from "@lightprotocol/stateless.js";
const rpc = createRpc("https://devnet.helius-rpc.com/?api-key=...");
const ix = await createNullifierIx(rpc, payer.publicKey, id);Or step-by-step:
import { fetchProof, buildInstruction } from "@lightprotocol/nullifier-program";
const proofResult = await fetchProof(rpc, id);
const ix = buildInstruction(payer.publicKey, id, proofResult);Check if nullifier exists:
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;- Derive a compressed account address from
["nullifier", id]seeds. E.g. hash payment inputs. - Creates the empty rentfree PDA account, "spending the nullifier"
- If the address already exists, the instruction fails
- prepend or append this instruction to your regular transaction (eg. payment)
Find more documentation here: https://www.zkcompression.com/compressed-pdas/guides/how-to-create-nullifier-pdas.
The nullifier program code is unaudited, use at your own risk.