@prvtlabs/prvttools
v1.0.0
Published
SDK for Solana ZK Programs — Privacy-preserving VRF, Lottery, Airdrop, Dice, Voting & Payments on Solana. Zero deployment, AI-native, composable via CPI.
Maintainers
Readme
@prvtlabs/prvttools
AI-Powered Zero-Knowledge DApps on Solana — Build privacy-preserving decentralized applications without deploying a single smart contract.
What is this?
prvt.tools provides 6 pre-deployed zero-knowledge programs on Solana that developers and AI agents can call directly — no Anchor build, no deploy keys, no upgrade authority. Just build and sign transactions.
This SDK gives you TypeScript classes, utilities, and types to interact with all 6 programs.
Why use this?
- Zero Deployment — Use shared on-chain ZK programs out of the box. No Rust, no Anchor CLI.
- AI-Native — AI agents read IDL definitions and generate transaction instructions automatically.
- One Program, Unlimited Apps — Every project uses the same on-chain programs with unique PDA-isolated state.
- Privacy by Default — Sensitive data stays off-chain, only zero-knowledge proofs go on-chain.
- Composable via CPI — Any Solana program can cross-program invoke these ZK primitives.
Programs
All programs are live on Solana Devnet and verified on-chain.
| Program | Description | Program ID | Test Results |
|---------|-------------|------------|--------------|
| ZK Dice | Provably fair casino — dice, coin flip, custom range | 4zsyst6Sf6MymUCJthR65eSgWEahbJJ5nd2zRCbJ8Yh6 | 7/7 PASS |
| ZK Lottery | Provably fair lottery with committed randomness | BqB7rvCAeU8ig3AxUZsZBgfK2ZTiSHr6p67ez5z8FuhK | 7/7 PASS |
| ZK Airdrop | Merkle-proof token distribution with nullifiers | 4X1UaqFrJXjoZg8eGt866yytQB5BYZktANi2KDFi9RZ5 | 7/7 PASS |
| ZK Voting | Anonymous commit-reveal voting with Merkle eligibility | 3C5NdnjyEkwGu5mqJhh9t21b4PVCufiYgQZhqxd5sp4g | 4/6 PASS |
| ZK Payments | Private payments, escrow, invoices with range proofs | 7gN9AJYbjNtc7wkeRzU2BeUQLNfoA99DnmPyeyRMvFFg | 8/9 PASS |
| ZK VRF | Verifiable random function for tamper-proof randomness | 4QLdhkY3TT2RBxv39jCYFpetiwZxSw6dkXKFfTbkfm1B | 5/7 PASS |
Overall: 38/43 tests passing (88%)
Installation
npm install @prvtlabs/prvttoolsyarn add @prvtlabs/prvttoolspnpm add @prvtlabs/prvttoolsPeer Dependencies
npm install @coral-xyz/anchor @solana/web3.jsQuick Start
import { Connection, Keypair } from '@solana/web3.js';
import * as anchor from '@coral-xyz/anchor';
import {
ZkVrf, ZkLottery, ZkDice,
ZkAirdrop, ZkVoting, ZkPayments
} from '@prvtlabs/prvttools';
// Setup provider
const connection = new Connection('https://api.devnet.solana.com');
const wallet = new anchor.Wallet(Keypair.generate());
const provider = new anchor.AnchorProvider(connection, wallet, {});
// Initialize any program — no deployment needed
const vrf = new ZkVrf(provider);
const lottery = new ZkLottery(provider);
const dice = new ZkDice(provider);
const airdrop = new ZkAirdrop(provider);
const voting = new ZkVoting(provider);
const payments = new ZkPayments(provider);Program Guides
ZK Dice — Provably Fair Casino Games
Game types: dice6, dice100, coinFlip, custom range. Uses commit-reveal: house commits a server seed hash, player bets, house reveals seed, bet is resolved deterministically.
Instructions: initialize, commitServerSeed, placeBet, revealServerSeed, resolveBet, depositHouseFunds, withdrawHouseFunds, updateConfig
import { ZkDice, generateSalt, sha256 } from '@prvtlabs/prvttools';
const dice = new ZkDice(provider);
// House setup
const houseEdgeBps = 250; // 2.5%
const maxBet = BigInt(0.1 * 1e9);
const minBet = BigInt(0.001 * 1e9);
await dice.initialize(houseEdgeBps, maxBet, minBet, authority.publicKey);
await dice.depositHouseFunds(BigInt(1 * 1e9), depositor.publicKey);
// Commit server seed
const seedId = BigInt(Date.now());
const serverSeed = generateSalt();
const commitment = sha256(serverSeed);
await dice.commitServerSeed(seedId, commitment, house.publicKey);
// Player places bet
await dice.placeBet(
BigInt(0.01 * 1e9), // bet amount
generateSalt(), // client seed
{ dice6: {} }, // game type
4, // prediction: roll 4+ to win
seedId,
player.publicKey
);
// Reveal and resolve
await dice.revealServerSeed(seedId, serverSeed, house.publicKey);
await dice.resolveBet(seedId, player.publicKey);ZK Lottery — Provably Fair Lottery System
Create lottery rounds, users buy tickets, winner is revealed using committed randomness. Prize pool distributed to winner minus platform fee.
Instructions: initialize, createLottery, buyTickets, revealWinner, claimPrize, cancelLottery, updateConfig
import { ZkLottery } from '@prvtlabs/prvttools';
const lottery = new ZkLottery(provider);
await lottery.initialize(authority.publicKey);
// Create lottery
const lotteryId = BigInt(Date.now());
await lottery.createLottery(
lotteryId,
BigInt(0.01 * 1e9), // ticket price
100, // max tickets
BigInt(currentSlot + 1000), // end slot
creator.publicKey
);
// Buy tickets
await lottery.buyTickets(lotteryId, 5, buyer.publicKey);
// Draw winner & claim
await lottery.draw(lotteryId, vrfRoundId, authority.publicKey);
await lottery.claimPrize(lotteryId, winner.publicKey);ZK Airdrop — Privacy-Preserving Token Distribution
Distribute tokens using Merkle tree proofs. Users prove eligibility without revealing the full recipient list. Nullifiers prevent double-claiming.
Instructions: createAirdrop, claim, closeAirdrop, pauseAirdrop, resumeAirdrop, updateMerkleRoot, addFunds
import { ZkAirdrop, buildMerkleTree, computeMerkleLeaf, generateSalt } from '@prvtlabs/prvttools';
import { PublicKey } from '@solana/web3.js';
const airdrop = new ZkAirdrop(provider);
// Build Merkle tree from eligible addresses
const leaves = eligibleAddresses.map(addr => computeMerkleLeaf(addr));
const { root, proofs } = buildMerkleTree(leaves);
// Create airdrop campaign
const airdropId = BigInt(Date.now());
await airdrop.createAirdrop(
airdropId, root,
BigInt(100 * 1e9), // amount per recipient
BigInt(eligibleAddresses.length), // total recipients
tokenMint,
BigInt(currentSlot + 50000), // expiry
authority.publicKey
);
// Eligible user claims
const userIndex = eligibleAddresses.findIndex(a => a.equals(user.publicKey));
const proof = proofs.get(userIndex)!;
await airdrop.claim(airdropId, proof, BigInt(userIndex), generateSalt(), user.publicKey);ZK Voting — Anonymous Voting System
Two-phase commit-reveal voting. Phase 1: voters commit encrypted vote hash. Phase 2: voters reveal their vote. Nullifiers prevent double-voting. Merkle root gates voter eligibility.
Instructions: initialize, createProposal, commitVote, revealVote, finalizeProposal, cancelProposal, updateVoterRoot
Lifecycle: Pending → Active → Revealing → Finalized (or Cancelled)
import { ZkVoting, createVoteCommitment, generateSalt, generateNullifier,
buildMerkleTree, computeMerkleLeaf } from '@prvtlabs/prvttools';
const voting = new ZkVoting(provider);
await voting.initialize(authority.publicKey);
// Build voter eligibility tree
const leaves = eligibleVoters.map(v => computeMerkleLeaf(v));
const { root, proofs } = buildMerkleTree(leaves);
// Create proposal
const proposalId = BigInt(Date.now());
await voting.createProposal(
proposalId, "Treasury Allocation",
"How should we allocate Q1 funds?",
["Development", "Marketing", "Community", "Reserve"],
root,
BigInt(currentSlot + 100), // voting start
BigInt(currentSlot + 5000), // voting end
BigInt(currentSlot + 10000), // reveal end
creator.publicKey
);
// Commit vote (Phase 1)
const salt = generateSalt();
const commitment = createVoteCommitment(0, salt); // vote for option 0
const nullifier = generateNullifier(voter.publicKey, proposalId);
const proof = proofs.get(voterIndex)!;
await voting.commitVote(proposalId, commitment, proof, BigInt(voterIndex), nullifier, voter.publicKey);
// Reveal vote (Phase 2)
await voting.revealVote(proposalId, 0, salt, voter.publicKey);
// Finalize
await voting.finalizeProposal(proposalId, authority.publicKey);ZK Payments — Private Payment Verification & Escrow
Commit-reveal payments, range proofs, escrow with hidden amounts, and invoicing. Payment amounts stay private until revealed. Escrow supports dispute resolution.
Instructions: initialize, createPaymentCommitment, revealPayment, executePayment, createRangeAttestation, createEscrow, depositToEscrow, releaseEscrow, refundEscrow, createInvoice, payInvoice
Escrow lifecycle: Active → Funded → Released / Refunded / Disputed Invoice lifecycle: Pending → Paid / Cancelled / Overdue
import { ZkPayments, createAmountCommitment, generateSalt, sha256 } from '@prvtlabs/prvttools';
const payments = new ZkPayments(provider);
await payments.initialize(authority.publicKey);
// Private payment with hidden amount
const paymentId = BigInt(Date.now());
const amount = BigInt(0.5 * 1e9);
const salt = generateSalt();
const commitment = createAmountCommitment(amount, salt);
await payments.createPayment(
paymentId, commitment, recipient.publicKey,
BigInt(0.1 * 1e9), BigInt(1 * 1e9), // min/max range
BigInt(currentSlot + 10000),
sender.publicKey
);
await payments.revealPayment(paymentId, amount, salt, sender.publicKey);
await payments.executePayment(paymentId, sender.publicKey);
// Escrow with hidden amount
const escrowId = BigInt(Date.now());
const escrowCommitment = createAmountCommitment(BigInt(1 * 1e9), generateSalt());
const conditionHash = sha256(Buffer.from('condition_met'));
await payments.createEscrow(
escrowId, escrowCommitment, conditionHash,
arbiter.publicKey, BigInt(currentSlot + 50000),
depositor.publicKey, beneficiary.publicKey
);
await payments.depositToEscrow(escrowId, BigInt(1 * 1e9), depositor.publicKey);
await payments.releaseEscrow(escrowId, Buffer.from('condition_met'), arbiter.publicKey);
// Invoice system
const invoiceId = BigInt(Date.now());
const invoiceCommitment = createAmountCommitment(BigInt(0.1 * 1e9), generateSalt());
await payments.createInvoice(
invoiceId, invoiceCommitment, sha256(Buffer.from('Service Payment')),
BigInt(currentSlot + 10000), issuer.publicKey, payer.publicKey
);
await payments.payInvoice(invoiceId, BigInt(0.1 * 1e9), generateSalt(), payer.publicKey);ZK VRF — Verifiable Random Function
On-chain verifiable randomness. Operator commits to a seed, users request randomness, operator reveals preimage, request is fulfilled with deterministic random output.
Instructions: initialize, createRound, requestRandomness, reveal, fulfillRequest, closeRound, withdrawFees, updateConfig
import { ZkVrf, generateSalt, sha256 } from '@prvtlabs/prvttools';
const vrf = new ZkVrf(provider);
await vrf.initialize(authority.publicKey);
// Operator creates round
const roundId = BigInt(Date.now());
await vrf.createRound(roundId, authority.publicKey);
// User requests randomness
const clientSeed = generateSalt();
await vrf.requestRandomness(roundId, clientSeed, requester.publicKey);
// Operator reveals and fulfills
const serverSeed = generateSalt();
await vrf.reveal(roundId, serverSeed, oracle.publicKey);
await vrf.fulfillRequest(roundId, requester.publicKey);Utility Functions
Cryptographic Utilities
import {
sha256, // SHA256 hash
generateSalt, // Random 32-byte salt
createCommitment, // Generic commit: sha256(value + salt)
createAmountCommitment, // Amount commit for payments
createVoteCommitment, // Vote commit for voting
generateNullifier, // Nullifier to prevent double-actions
} from '@prvtlabs/prvttools';
const salt = generateSalt(); // 32-byte Buffer
const hash = sha256(Buffer.from('data')); // 32-byte hash
const commitment = createCommitment(value, salt);Merkle Tree Utilities
import {
computeMerkleLeaf, // Compute leaf from PublicKey
computeMerkleNode, // Compute parent from two children
buildMerkleTree, // Build tree, returns { root, proofs }
verifyMerkleProof, // Verify a proof against root
} from '@prvtlabs/prvttools';
const leaves = addresses.map(addr => computeMerkleLeaf(addr));
const { root, proofs } = buildMerkleTree(leaves);
const isValid = verifyMerkleProof(leaf, proof, leafIndex, root);PDA Derivation
Every program uses Program Derived Addresses for state isolation. The SDK provides helpers for all PDAs:
import {
PROGRAM_IDS,
// VRF
getVrfConfigPDA, getVrfRoundPDA, getVrfRequestPDA,
// Lottery
getLotteryConfigPDA, getLotteryPDA, getLotteryPoolPDA, getTicketPDA,
// Airdrop
getAirdropPDA, getAirdropVaultPDA, getAirdropNullifierPDA,
// Dice
getDiceConfigPDA, getHouseVaultPDA, getServerSeedPDA, getBetPDA,
// Voting
getVotingConfigPDA, getProposalPDA, getVotePDA, getVoteNullifierPDA,
// Payments
getPaymentConfigPDA, getPaymentPDA, getEscrowPDA, getEscrowVaultPDA,
getInvoicePDA, getAttestationPDA,
} from '@prvtlabs/prvttools';Types
All TypeScript types are fully exported:
import type {
// VRF
VrfConfig, VrfRound, RandomnessRequest,
// Lottery
LotteryConfig, Lottery, LotteryTicket, LotteryStatus,
// Dice
DiceConfig, ServerSeed, Bet, GameType,
// Airdrop
Airdrop, AirdropNullifier, AirdropStatus,
// Voting
VotingConfig, Proposal, Vote, ProposalStatus,
// Payments
PaymentConfig, PaymentCommitment, Escrow, Invoice,
PaymentStatus, EscrowStatus, InvoiceStatus,
} from '@prvtlabs/prvttools';AI Agent Integration
AI agents can build ZK dApps in 3 steps:
- Load IDL — Read the JSON IDL for any program (included in
idl/directory) - Derive PDAs — Use program ID + seeds to compute account addresses
- Build TX — Construct
TransactionInstructionwith discriminator + args
No deployment. No Rust. No Anchor CLI. Just transaction construction.
The SDK ships with IDL files for all 6 programs:
idl/zk_dice.jsonidl/zk_lottery.jsonidl/zk_voting.jsonidl/zk_airdrop.jsonidl/zk_payments.jsonidl/zk_vrf.json
Error Handling
All SDK methods return transaction signatures on success:
try {
const tx = await dice.placeBet(amount, clientSeed, gameType, prediction, seedId, player);
console.log('Transaction:', tx);
} catch (error) {
if (error.message.includes('insufficient funds')) {
console.error('Not enough SOL');
} else if (error.message.includes('6001')) {
console.error('Anchor program error — check error code');
} else {
console.error('Transaction failed:', error);
}
}Network Configuration
Programs are deployed on Solana Devnet. For custom RPC:
const connection = new Connection('https://your-rpc-endpoint.com', 'confirmed');
const provider = new anchor.AnchorProvider(connection, wallet, {
commitment: 'confirmed',
preflightCommitment: 'confirmed',
});Wallet Connection
Node.js / Backend
import { Connection, Keypair } from '@solana/web3.js';
import * as anchor from '@coral-xyz/anchor';
import { ZkDice } from '@prvtlabs/prvttools';
// Load keypair from file
import fs from 'fs';
const secret = JSON.parse(fs.readFileSync('./keypair.json', 'utf-8'));
const keypair = Keypair.fromSecretKey(Uint8Array.from(secret));
const connection = new Connection('https://api.devnet.solana.com', 'confirmed');
const wallet = new anchor.Wallet(keypair);
const provider = new anchor.AnchorProvider(connection, wallet, {});
const dice = new ZkDice(provider);React / Next.js (with Wallet Adapter)
npm install @solana/wallet-adapter-react @solana/wallet-adapter-wallets @solana/wallet-adapter-react-uiimport { useAnchorWallet, useConnection } from '@solana/wallet-adapter-react';
import * as anchor from '@coral-xyz/anchor';
import { ZkDice, ZkLottery } from '@prvtlabs/prvttools';
function MyComponent() {
const { connection } = useConnection();
const wallet = useAnchorWallet();
const playDice = async () => {
if (!wallet) return;
const provider = new anchor.AnchorProvider(connection, wallet, {});
const dice = new ZkDice(provider);
// Now use any SDK method — wallet signs automatically
await dice.placeBet(
BigInt(0.01 * 1e9),
generateSalt(),
{ dice6: {} },
4,
seedId,
wallet.publicKey
);
};
return <button onClick={playDice}>Roll Dice</button>;
}Wallet Provider Setup (App root)
import { WalletProvider } from '@solana/wallet-adapter-react';
import { ConnectionProvider } from '@solana/wallet-adapter-react';
import { PhantomWalletAdapter, SolflareWalletAdapter } from '@solana/wallet-adapter-wallets';
import { WalletModalProvider } from '@solana/wallet-adapter-react-ui';
const wallets = [new PhantomWalletAdapter(), new SolflareWalletAdapter()];
function App({ children }) {
return (
<ConnectionProvider endpoint="https://api.devnet.solana.com">
<WalletProvider wallets={wallets} autoConnect>
<WalletModalProvider>
{children}
</WalletModalProvider>
</WalletProvider>
</ConnectionProvider>
);
}Supported Wallets
Any Solana wallet works — Phantom, Solflare, Backpack, Ledger, Torus, etc. The SDK just needs an AnchorProvider with a connected wallet.
Use Cases
| Use Case | Programs | |----------|----------| | Gaming | ZK Dice + ZK VRF for provably fair games | | Governance | ZK Voting for anonymous DAO proposals | | Token Distribution | ZK Airdrop for private Merkle airdrops | | DeFi | ZK Payments for private escrow & invoicing | | Lotteries & Raffles | ZK Lottery + ZK VRF for fair draws | | Any dApp needing randomness | ZK VRF standalone |
Contributing
Contributions are welcome! Please submit issues and pull requests.
