Threat model
Funds are 2-of-3, order privacy is 3-of-3. We state this split openly.
| Observer | Learns | Doesn't learn |
|---|---|---|
| Chain observer | The escrow address exists and makes batched shielded transactions | Who bet, which side, how much, who won |
| Any single relayer | “A 5-unit deposit arrived” (shared viewing key) | Orders, sides, prices, positions, which deposit belongs to which payout |
| 2 colluding relayers | Same as one for orders; can move escrow funds (FROST 2-of-3) | Orders (the MPC needs all 3 to collude) |
| All 3 colluding | Everything | — |
| Counterparty | That a fill happened at the midpoint | Who you are, your size beyond the fill |
| Network observer | That someone talks to a zPredict relayer | Contents (TLS); your IP if you use Tor |
- No transparent address in any product path: deposits, payouts, refunds, change.
- Deposits only in fixed denominations; never reveal order size.
- Payouts batched per epoch to fresh diversified, shielded-only addresses.
- Wallet-default fee (ZIP 317) and expiry (ZIP 315); padded actions.
- Memos carry no order data and no plaintext tags.
- The browser shares orders; each share goes straight to its own relayer on its own host.
- Frontend is static and self-hosted: no CDN, no third-party fonts, no analytics. Relayers log no IPs.
- Viewing keys never leave the relayers; never shown in the UI or logs.
- Judge mode credits a demo balance directly; no testnet deposit happens.
- The escrow address shown in Deposit is a placeholder until the FROST DKG runs.
- With the local mock relayers, one process combines the shares in the clear instead of running MPC.