SDKs
One surface, two backends. @privora/sdk (TypeScript) proves in the browser with snarkjs; privora-sdk (Rust) proves in-process with ark-circom for agents, CLIs and keepers. Same calls, same guarantees, differ only in the prover.
The SDK is generic by design: it gives you the confidential primitives (deposit, place, cancel, withdraw, positions, market data) and you bring your app's logic. The witness is built and proven on your machine, the SDK never ships your secret anywhere.
TypeScript#
Install
pnpm add @privora/sdk @solana/web3.js
The trade loop
Create a client against a market and a relayer, point it at the proving artifacts (the circuit .wasm + .zkey), then run the loop. Every method proves locally and submits through the relayer, you never sign a trade. maintBps and fundingIndex come from px.marketParams(), the proof binds the order to the market's current funding index.
import { PrivoraClient } from "@privora/sdk";
const px = await PrivoraClient.create({
market, // resolved from the program + quote mint
relayer, // the fee-payer endpoint
artifacts, // per-circuit { wasm, zkey } — deposit, place, cancel, settleOffset, withdraw, …
indexerUrl, // the chain-derived read path
});
// 1) shield collateral (the one linkable, wallet-signed step — the deposit token transfer)
const dep = await px.deposit({ wallet, depositorToken, amount: 1_000_000n });
// 2) place a confidential resting order, proven in-browser, relayer-submitted
const { maintBps, fundingIndex } = await px.marketParams();
const ord = await px.placeOrder({
note: dep.note, leafIndex: dep.leafIndex,
side: 0, // 0 = long / bid, 1 = short / ask
size: 1000n, price: 6460n,
escrowCollateral: 1_000_000n, // your margin; leverage = size·price / escrow
maintBps, fundingIndex,
});
// 3) watch your fill, then CLOSE by netting against an equal opposite leg you also hold
// (isolated exit, Model B): the two fold into ONE balance note worth escrow + realized PnL − fee
const close = await px.settleOffset({ long, short, pnl /* short.notional − long.notional */, fee });
// — or pull a still-unfilled quote and reclaim its escrow —
await px.cancel({ escrowCollateral: 1_000_000n, size: 1000n, price: 6460n, freshSecret: ord.freshSecret });
// 4) off-ramp: spend a balance note back to tokens (the one linkable exit; recipient bound in the proof)
await px.withdraw({ note: close.note, leafIndex: close.leafIndex, recipientToken });The full confidential surface
Beyond the core loop, PrivoraClient exposes everything a real venue needs, every method proves locally and relays, none reveals your position:
// Set-and-forget liquidation — arm a position once; the committee + keeper enforce it even if you're offline.
await px.armLiquidation({ orderCommit, liqLevel, liqSalt, side, price, bucketSize, feed, apiBase });
// Iceberg — show only a small peak on the lit book; the true size stays hidden in the order commitment.
const ice = await px.placeIceberg({ note, leafIndex, totalSize: 50_000n, displaySize: 5_000n,
price: 6460n, escrowCollateral, side: 0, maintBps, fundingIndex });
// Cross-margin (Mode 2) — one shielded collateral pool backing several same-side legs, liquidated on
// AGGREGATE health. Isolated stays the default; cross-margin is opt-in per pool.
const pool = await px.portfolioOpen({ note, leafIndex, netSize: 10_000n, price: 6460n,
escrowCollateral, side: 0, maintBps, fundingIndex, ownerSecret });
await px.portfolioAdd({ note, leafIndex, old: pool, ownerSecret, /* … */ addSize, price, escrowAdd });
const closed = await px.portfolioClose({ state, ownerSecret, liqLevel, liqSalt, mark, maintBps });
// Reads (no private query) — your live fill state + the market params.
const pos = await px.position(ord.orderCommit); // filled / remaining
const portfolio = await px.readPortfolio(ownerCommit);stopTriggered(side, mark, { stopLoss, takeProfit }) check; your app (or agent) watches the public mark and fires a normal close when it trips. There is no on-chain stop order to farm, the trigger price never leaves your device. The app wires this for you (see Trade on the app).payout − fee); a flat per-submission network fee covers the relayer's gas. Pass the net fee into settleOffset when fees are enabled on the market.Market data
Read the lit book, trades, candles and the mark from the indexer, the same reproducible, chain-derived read path the app uses. No private query exposes your interest.
import { MarketData } from "@privora/sdk";
const md = new MarketData(indexerUrl, market.key);
const book = await md.book(); // { bids, asks } aggregated depth
const trades = await md.trades(100); // recent prints
const candles = await md.candles(60, 500); // OHLCV, 60s
const marks = await md.marks(500); // the funding-adjusted mark seriesRust#
The privora crate mirrors the TypeScript surface, but proves with ark-circom (much faster), built for agents, CLIs, market makers and keepers. PrivoraClient is the one-crate client against the deployed venue: it proves locally, submits the unlinkable legs through the relayer, and reads the lit book from the indexer, the agent holds no SOL and never signs a trade.
use privora::{PrivoraClient, PrivoraConfig};
let mut px = PrivoraClient::new(cfg, agent_keypair); // cfg mirrors the keeper's /config
px.fund(1_000)?; // box faucet: test-USDC + a little SOL
let note = px.deposit(1_000_000)?; // shield collateral (the one signed leg)
// a confidential resting order, proven in-process, relayer-submitted
let ord = px.place(¬e, 1_000, 6_460, 1_000_000, 0)?; // size, price, escrow, side
px.cancel(&ord.order_commit, ord.escrow, ord.size, ord.price, ord.fresh_secret)?;
let pos = px.position(&ord.order_commit)?; // live fill state
let book = px.book()?; // lit book, no private queryThe low-level building blocks (privora::sdk witnesses, privora::prover, privora::client instruction builders) are re-exported for keepers and bespoke flows.
Proving artifacts#
- Each circuit ships a
.wasm(witness calculator) and a.zkey(proving key). The browser uses them with snarkjs; the Rust path reads the same ceremony.zkeyso its proofs verify against the exact same on-chain verifying key. - The web app serves them under
/circuits/*; for the Rust path, point--zkeysat the ceremony directory.