Nullifier Program
Creates a rent-free PDA derived from id. If the id has been used before, the PDA already exists, causing the instruction to fail.
Prerequisites
- Rust 1.79+
- Solana CLI 2.2+
- Anchor CLI 0.31.1
- Node.js 18+
Build
Test (Rust)
Test (TypeScript)
Requires local validator with Light Protocol.
- lightprotocol/zk-compression-cli 0.28.0-beta.5
Rust SDK
Works with LightClient (production) or LightProgramTest (testing).
use ;
use ;
let mut rpc = new.await?;
let ix = create_nullifier_ix.await?;
Or step-by-step:
use ;
let proof_result = fetch_proof.await?;
let ix = build_instruction;
TypeScript SDK
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;
How it works
- 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)
The nullifier program code is unaudited, use at your own risk.