light-nullifier-program 0.1.2

Nullifier program and SDK for Light Protocol ZK compression on Solana
Documentation

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

anchor build
npm run build

Test (Rust)

cargo test-sbf -p light-nullifier-program

Test (TypeScript)

Requires local validator with Light Protocol.

  • lightprotocol/zk-compression-cli 0.28.0-beta.5
light test-validator
npm test

Rust SDK

Works with LightClient (production) or LightProgramTest (testing).

use light_nullifier_program::sdk::{create_nullifier_ix, PROGRAM_ID};
use light_client::{LightClient, LightClientConfig};

let mut rpc = LightClient::new(LightClientConfig::new("https://devnet.helius-rpc.com/?api-key=...")).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);

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

  1. Derive a compressed account address from ["nullifier", id] seeds. E.g. hash payment inputs.
  2. Creates the empty rentfree PDA account, "spending the nullifier"
  3. If the address already exists, the instruction fails
  4. prepend or append this instruction to your regular transaction (eg. payment)

The nullifier program code is unaudited, use at your own risk.