Skip to main content

On-chain

ShieldedPool — the entry point for every user action. Holds the commitment tree, the nullifier set, and the proof verifiers. Never learns an amount. It also custodies all collateral directly, in one shared pool across every market — a deposit does not name a market, so there is nothing to hold per-market. Solvency is a running global check (collateral paid out can never exceed collateral deposited), not a per-market invariant. ElGamalAccumulator — holds the running encrypted total per market, per side. It stores ciphertext points and has no ability to decrypt anything. Adding a stake is point addition on BabyJubJub. Vault — holds the outcome and the market’s immutable clock (bettingCloseTime, resolutionStartTime). It does not custody collateral for the shielded flow — see ShieldedPool, above. Vault.split/merge/redeem still exist as plain, non-ZK functions, but the shielded flow never calls them. EncryptedParimutuelPool — settlement. Accepts the decrypted final totals along with a Chaum-Pedersen proof that the decryption is honest with respect to the published committee key. This is the only path a payout ever reads. PythResolver — resolves a market from signed price data at a timestamp committed before betting opened. See Resolution.

Off-chain

Sequencer — batches commitments into the tree and serves Merkle paths.
Inserting one commitment costs more gas than a user action can afford, so actions only queue a commitment and the sequencer grafts 64 at a time. Batching is a correctness requirement of the gas budget, not an optimisation.
The sequencer is trusted for liveness and ordering only. It cannot steal, cannot insert a leaf nobody queued (the contract checks every submitted leaf against the on-chain queue), and cannot choose padding (fillers are derived on-chain). What it can do is stall. Relaying. The sequencer can also submit betEncrypted, redeemPrivate, and withdraw on your behalf — proof-gated, not sender-gated, so nothing about their validity depends on trusting it. deposit is never relayed this way: it moves real collateral via transferFrom(msg.sender), so it always needs your own wallet regardless of relaying. For the other three, relaying means your wallet address never appears on that specific transaction — the sequencer knows it was you, but the chain does not.
That is trust relocated, not removed. Relaying adds a liveness dependency, the same way batching does, and it is optional — configured per deployment, off unless the operator funds it. Optionally, right after a relayed withdrawal lands, the same relayer can send the recipient a small amount of native gas in a follow-up transaction, so a freshly-used address has enough to transact again.
Publisher — the only process holding the committee key, and therefore the only thing that can turn the accumulated ciphertext into a number. It decrypts the total, computes a coarse ratio, and publishes only that.
Mid-market odds are indicative. Verifying them on-chain would require the plaintext totals, which is exactly what the encryption exists to prevent — so no payout ever reads them. Settlement goes through a cryptographic proof instead. The odds inform; the money is proved.

The flow

1

Deposit

Collateral enters ShieldedPool’s shared pool, a commitment is queued, and the sequencer grafts it into the tree.
2

Bet

A proof shows you own an unspent note without revealing which. The encrypted stake is added to the accumulator. A position note is queued.
3

Resolve

The oracle resolver reads a signed price at the committed timestamp and sets the outcome.
4

Settle

The committee publishes final totals with a proof that the decryption is honest.
5

Redeem

A winning position becomes a settled note. No collateral moves.
6

Withdraw

A settled note becomes public collateral, in a fixed denomination, at a time of your choosing.

Uniform gas envelope

Monad charges the declared gas limit, and that limit is a public field on the transaction. A snug per-action limit would reveal which private action you took even though the proof stays sealed. So every user action is submitted with one identical declared limit. Privacy has a measurable price, and this is where it is paid. See Performance.