Private prediction markets, step by step.
PolyShield uses zero-knowledge proofs to hide which depositor placed which bet. The vault is a single Polymarket account shared by everyone in the pool — so on-chain, every bet looks the same. Here is the full flow.
Your wallet only ever signs deposit() (public by design). Every bet, settlement, and withdrawal is generated as a proof in your browser and submitted by the relay, never your wallet — so nothing on-chain links you to a bet. Inside the vault, all depositors share one Polymarket identity.
Deposit USDC into the shared vault
You transfer USDC to the Vault and your browser generates a spending note (secret, balance, nonce, owner_address). A mandatory deposit-binding ZK proof ties the committed balance to the exact amount you transferred — so no one can commit more than they paid. The secret is derived from a wallet signature; there is nothing to back up. Your deposit amount is public; the note contents are not.
C = Poseidon4(secret, balance, nonce, owner_address)
Choose a position & prove it
Browse live Polymarket markets and pick a side. Your browser generates a BET_AUTH proof showing your note has enough balance, the nullifier belongs to your note, and the new note after spending is well-formed. The Vault injects the fee, so the spend is exactly bet_amount + fee. No secret ever leaves your device.
nullifier = Poseidon2(secret, nonce)
The relay submits the authorization
Your ZK proof goes to the Proof Relay — not your wallet. The relay calls Vault.authorizeBet() from its own EOA and pays the gas, so your wallet address never appears in any bet-related transaction. This is what keeps the deposit unlinkable from the bet.
Vault.authorizeBet(proof, publicInputs) // tx.from = relay
The vault EOA places the order
The Signing Layer sees the BetAuthorized event and places the order on Polymarket's CLOB from the vault's single shared EOA — a fill-and-kill (FAK) order for market bets, or a resting GTC/GTD order for limit bets. Every depositor's bets come from that one address, so no CLOB observer can tell which depositor authorized which bet. Collateral is funded just-in-time.
POST /order { tokenId, price, size, type: "FAK" | "GTC" | "GTD" }Settle winnings into a fresh note
When the market resolves, the Vault derives the payout on-chain from the real Gnosis CTF. You generate a SETTLEMENT_CREDIT proof locally and the payout is added to a fresh private note — the Vault injects the payout, so you cannot inflate the credit. One click; no payout witness required.
new_commitment = Poseidon4(secret, balance + credit, nonce+1, owner)
Withdraw to your own address
The WITHDRAWAL proof proves you know a note secret and commits to a recipient via its Poseidon hash. You can only withdraw to the wallet that made the original deposit — enforced inside the circuit via owner_address and re-checked by the Vault. The relay submits it, so no identifying data appears on-chain.
recipient_hash = Poseidon2(recipient_address, 0)
What an observer with full on-chain visibility can and cannot learn. The contracts protect funds on every path; only the client protects privacy.
| Threat | Mitigated? | How |
|---|---|---|
| Observer sees which EOA placed the Polymarket order | ✓ YES | All orders from the vault's single shared EOA; the depositor never appears |
| On-chain observer links a nullifier to a depositor address | ✓ YES | Nullifier = Poseidon2(secret, nonce); not derivable without the secret |
| Relay or signing layer learns who authorized which bet | ✓ YES | They see only the ZK proof; public inputs contain no depositor ID |
| Backend index de-anonymizes or forges notes | ✓ YES | Serves only public data; client matches by its own nullifier — worst case is incomplete recovery |
| Forged deposit balance, double-spend, or inflated credit | ✓ YES | Blocked on-chain by the circuits + Vault-injected values, regardless of who sends the tx |
| Calling a spend function from your OWN wallet | ⚠ NO | Self-deanonymizes — the frontend never does this; it is a client discipline (threat T19) |
| That a wallet used PolyShield at all | ⚠ NO | Vault.deposit() is a public ERC-20 transfer — only post-deposit activity is private |
| Owner can upgrade the contracts instantly (UUPS) | ⚠ NO | Largest trust assumption — the owner key must be a multisig/HSM in production |
Common questions about private trading.
What is PolyShield?
PolyShield is a non-custodial, zero-knowledge privacy vault for Polymarket. Many users deposit USDC into one shared vault that holds a single Polymarket account. Every bet is placed from that one shared account, so on-chain observers cannot tell which depositor authorized which trade. It is live on Polygon mainnet in an open beta.
What does PolyShield hide, and what stays public?
Hidden: which depositor authorized which bet, and your running position — this is enforced by cryptography, not a promise. Public: that some wallet deposited into the vault and how much, because a deposit is an ordinary on-chain USDC transfer. PolyShield hides your trading activity, not the fact that you deposited.
Is PolyShield a mixer?
No. A mixer breaks the link between a sender and an arbitrary recipient. PolyShield is withdraw-to-self only: your funds can only return to the same wallet that deposited them, enforced inside the zero-knowledge circuit and re-checked on-chain. It hides which bets are yours, not where your money goes.
Is PolyShield non-custodial? Can it take my funds?
PolyShield is non-custodial and cannot move your balance to anyone but you — withdraw-to-self is enforced by the circuit and the contract. The main trust assumption is the contract upgrade key, which can replace contract logic; in production that key is held by a multisig or HSM. PolyShield is experimental beta software handling real funds, so only deposit what you can afford to risk.
Do I need KYC to use PolyShield?
No. PolyShield is a permissionless smart-contract protocol — you connect a self-custodial EVM wallet on Polygon and deposit USDC. There is no account, no email, and no identity verification.
How do I get my money out? Can I withdraw to any wallet?
You can only withdraw to the same wallet that made the deposit. This is enforced cryptographically: the recipient is bound inside the withdrawal proof and independently re-checked by the vault. There is no way to redirect a withdrawal to a third-party address.
Is there a deposit limit?
Yes. During the beta there is a $50,000 USDC maximum cumulative deposit per address, enforced on-chain. Minimum bet and withdrawal amounts are $1.
How long does it take to place a private bet?
Each action generates a zero-knowledge proof locally in your browser using WebAssembly, which typically takes 30 seconds to about 2 minutes depending on your device. Keep the tab open while it runs — proving is CPU-bound and happens entirely on your device; no secret is ever sent to a server.
What are the fees?
PolyShield charges a 0.3% fee on each bet plus a small flat relay reimbursement (about $0.15 in USDC), and a flat $1 fee (in USDC) on each withdrawal. Fees accumulate in the vault. The relay reimbursement covers Polygon network costs — it is taken in USDC from your note, so PolyShield never asks your wallet to pay gas for a bet directly; the relay submits it.
Does the operator or relay see my bets?
No. The relay and signing layer only ever see zero-knowledge proofs and public inputs, which contain no depositor identity. The one opt-in exception is auto-settlement, where you may hand the operator an encrypted blob so it can settle a single bet for you — that links you to that one bet at the operator level and nothing more.
What happens if I lose my device or clear my browser?
Your notes are recoverable from your wallet alone. Note secrets are derived deterministically from wallet signatures, so signing once on a new device reconstructs every note. The only unrecoverable loss is losing the depositing wallet itself — there is no admin override or server-side recovery.
What network and token does PolyShield use?
PolyShield runs on Polygon mainnet, the same chain as Polymarket, and accepts and pays out in USDC only. All conversion between USDC and Polymarket collateral is handled internally by the vault.