TypeScript SDK and CLI for interacting with the Voidify Solana program.
The package includes:
- a program client for deriving Voidify PDAs and building Anchor instructions;
- deposit and withdrawal helpers;
- note generation and verification utilities;
- a CLI for users and operators;
- local/remote substream clients for deposit and relayer event indexing;
- HTTP services for relayers and substream indexing.
npm install @voidifydao/sdkThe package is ESM-only and targets modern Node.js runtimes.
The package exposes the voidify binary.
Use it without installing globally:
npx @voidifydao/sdk --helpOr install it globally with -g and run voidify directly:
npm install -g @voidifydao/sdk
voidify --helpThe recommended flow is: create one config file, fill it once, then pass it with -c for every command.
voidify config init --type default --path ./voidify-config.jsonThis tutorial writes the config into the current directory as ./voidify-config.json. If --path is omitted, the CLI uses its platform-specific default config path.
Example user config:
{
"rpcUrl": "https://mainnet.helius-rpc.com/?api-key=<YOUR_HELIUS_API_KEY>",
"programId": "<VOIDIFY_PROGRAM_ID>",
"keypair": {
"type": "file",
"path": "/absolute/path/to/solana-keypair.json"
},
"substream": {
"type": "auto",
"url": "https://substream.voidifycto.com",
"dbPath": "./substream.db"
},
"proof": {
"wasmPath": "./withdraw.wasm",
"zkeyPath": "./withdraw.zkey"
}
}programId is required for on-chain operations. proof.wasmPath and proof.zkeyPath are required for withdrawal proof generation.
Download the proof artifacts from the Voidify ceremony record release and extract them into the current directory:
curl -L -o withdraw.zip https://github.com/VoidifyCommunity/voidify-ceremony-record/releases/download/v1.0.0/withdraw.zip
unzip withdraw.zipAfter extraction, the config paths should point to:
"proof": {
"wasmPath": "./withdraw.wasm",
"zkeyPath": "./withdraw.zkey"
}You can inspect or update config values with the same -c flag:
voidify -c ./voidify-config.json config get rpcUrl
voidify -c ./voidify-config.json config set rpcUrl '"https://mainnet.helius-rpc.com/?api-key=<YOUR_HELIUS_API_KEY>"'Create your own Solana RPC endpoint before using the CLI. You can register at Helius, create an API key, and use the generated RPC URL in ./voidify-config.json:
"rpcUrl": "https://mainnet.helius-rpc.com/?api-key=<YOUR_HELIUS_API_KEY>"voidify -c ./voidify-config.json note gen 1
voidify -c ./voidify-config.json note verify <note> <commitment>
voidify -c ./voidify-config.json deposit 1
voidify -c ./voidify-config.json deposit 1 --commitment <commitment>
voidify -c ./voidify-config.json deposit list 1 --limit 50
voidify -c ./voidify-config.json withdraw <note> --recipient <recipient_pubkey>
voidify -c ./voidify-config.json withdraw <note> --relayer <relayer_name>
voidify -c ./voidify-config.json relayer list
voidify -c ./voidify-config.json relayer start
voidify -c ./voidify-config.json substreamimport { Context, makeIndexedDBStores } from "@voidifydao/sdk";
import { PublicKey } from "@solana/web3.js";
const programId = new PublicKey("<VOIDIFY_PROGRAM_ID>");
const ctx = new Context({
rpcUrl: "https://mainnet.helius-rpc.com/?api-key=<YOUR_HELIUS_API_KEY>",
programId,
wallet: walletAdapter,
substream: {
type: "remote",
url: "https://substream.voidifycto.com",
makeRepos: () => makeIndexedDBStores("voidify-substream")
},
wasmPath: "/withdraw.wasm",
zkeyPath: "/withdraw.zkey"
});import { Note } from "@voidifydao/sdk";
const note = await Note.generate("1");
console.log(note.serialize());
console.log(note.commitment);Notes are the withdrawal secret. Voidify cannot recover a lost note, and anyone with the note can withdraw the deposit.
import { Context, Note, voidify } from "@voidifydao/sdk";
import { parseUnits } from "@voidifydao/sdk";
const note = await Note.generate("1");
const signature = await voidify.deposit(
ctx,
note.commitment,
parseUnits("1", 9)
);
console.log({ signature, note: note.serialize() });import { voidify, parseUnits } from "@voidifydao/sdk";
const deposits = await voidify.listDeposits(ctx, parseUnits("1", 9), {
limit: 50
});
console.log(deposits);import { voidify } from "@voidifydao/sdk";
const signature = await voidify.withdraw(
ctx,
"<voidify-note>",
"<recipient_pubkey>"
);
console.log(signature);To choose a specific relayer by name:
const signature = await voidify.withdraw(
ctx,
"<voidify-note>",
"<recipient_pubkey>",
"<relayer_name>"
);import { voidify } from "@voidifydao/sdk";
const artifact = await voidify.prepareWithdraw(
ctx,
"<voidify-note>",
"<recipient_pubkey>"
);
const signature = await voidify.submitWithdrawToRelayer(artifact);import { voidify } from "@voidifydao/sdk";
const signature = await voidify.directWithdraw(
ctx,
"<voidify-note>",
"<recipient_pubkey>"
);Direct withdrawals are signed by the caller's wallet and may expose the withdrawal wallet on-chain. Relayer withdrawals are the privacy-preserving default.
Use VoidifyProgram when you need deterministic PDAs or access to the underlying Anchor program.
import { VoidifyProgram } from "@voidifydao/sdk";
import { Connection, PublicKey } from "@solana/web3.js";
const client = new VoidifyProgram(
new Connection("https://mainnet.helius-rpc.com/?api-key=<YOUR_HELIUS_API_KEY>"),
new PublicKey("<VOIDIFY_PROGRAM_ID>")
);
const pool = client.pool(1_000_000_000n);
const treasury = client.treasury();Available PDA helpers include:
stakeConfig()treasuryConfig()oracleConfig()pool(denomination)treasury()commitment(commitmentBytes)nullifier(nullifierHashBytes)relayerConfig(relayerPubkey)relayerEventCounter()
Create a relayer config:
voidify config init --type relayer --path ./voidify.relayer.config.jsonThe config must include:
rpcUrlprogramIdkeypairrelayerServer.portrelayerServer.hostrelayerServer.feedId
Start the service:
voidify -c ./voidify.relayer.config.json relayer startThe relayer exposes:
GET /healthPOST /api/relay/withdraw
Create a substream server config:
voidify config init --type substream --path ./voidify.substream.config.jsonStart the service:
voidify -c ./voidify.substream.config.json substreamThe CLI and SDK can use substream data in three modes:
remote: read events from a remote substream service;local: read and sync using a local SQLite database;auto: use the remote service with local caching.
Main exports:
ContextVoidifyProgramvoidifyNoteparseUnitsformatUnitstoBNSubstreamCliClientmakeIndexedDBStores
Types are exported from the package root, including deposit, relayer, event, substream, and withdrawal response types.
npm install
npm run build
npm run dev -- --helpnpm run build runs TypeScript compilation and alias rewriting with tsc-alias.
- Never share a Voidify note.
- Store keypairs outside public repositories.
- Use separate keypairs/config files for user, relayer, and substream roles.
- Prefer relayer withdrawals when preserving withdrawal privacy matters.
- Validate
programId, RPC endpoints, relayer endpoints, and proof artifacts before production use.