Cryptographic & contract architecture
Recommendation: a hybrid of all three options — mental poker for the shuffle, state channels for the betting round, zk proofs only where a claim must be verified without revealing cards. No single option covers speed, secrecy and dispute resolution alone.
Mental poker (SRA commutative encryption)
Every seated player encrypts the 52-card deck with their own commutative key and shuffles it, in turn. Because encryption commutes, nobody can decrypt a card alone — a card is revealed only when the required subset publishes its per-card key share. This removes the dealer entirely: there is no server that ever knows the deck order.
- Deck setup: one shuffle round per player (~5 rounds of small payloads, all off-chain).
- Hole cards: only the receiving player gets the other players' key shares.
- Board cards: all shares published, deck integrity checked against the initial commitment.
- Every shuffle is committed as a hash in the channel state, so a later dispute can replay it on-chain.
Weakness handled: a player leaving mid-hand. The protocol uses threshold key shares (t-of-n Shamir over the card keys), so remaining players can complete the hand and the abandoning seat is auto-folded with its escrowed chips forfeited to the pot.
Full spec, security guarantees and the step-by-step flow to showdown: shuffle protocol.
Zero-knowledge proofs at showdown
Full zk-shuffle circuits are still too heavy for real-time tables, so zk is applied surgically: a small zk-SNARK (Groth16 on EVM/Solana, or a Stellar-side Soroban verifier) proves "my claimed 5-card hand is a permutation of my two committed hole cards plus the public board, and its rank equals R" — without revealing cards until the pot needs them. Muck still works: a losing player never reveals their cards, only the proof of a lower rank.
- Prover time target: < 300 ms in-browser (WASM), one proof per showdown participant.
- Proof is verified on-chain only if a settlement is disputed; otherwise verified peer-side.
State channels for zero-gas actions
Check / bet / call / fold are signed messages exchanged directly between seats. Each action increments a monotonic nonce over the channel state (stacks, pot, bets, deck commitment); every participant countersigns. Target action latency is under 80 ms, and gas per action is zero.
- On-chain writes per hand: 0 in the happy path, 1 settlement transaction at hand end.
- Dispute path: any signer can post the latest signed state; a challenge window (e.g. 5 min) lets others post a higher nonce.
- Timeout: unresponsive seat is force-folded after its action clock expires, proven by signed clock ticks.
Non-custodial escrow & 33/33/34 rake split
Buy-ins move from the player's wallet into a per-table escrow contract (Soroban / EVMTableEscrow / Solana PDA vault). The contract can only ever pay out according to a state signed by the table quorum — the platform holds no keys and cannot move funds.
settle(state, signatures): require quorum_signed(state) // no house key involved rake = pot * rakeBps / 10_000 // 200–300 bps net = pot - rake transfer(net, state.winner) // instant, on-chain transfer(rake * 33 / 100, referrerOf(state.winner) or treasury) distribute(rake * 33 / 100, state.activeSeats) // rakeback, pro-rata to hands played transfer(rake * 34 / 100, omniTreasury) assert paid_out == pot // exactness invariant
- Integer-safe: the 34% treasury leg absorbs rounding dust so payout always equals the pot.
- Affiliate leg falls back to the treasury when a player has no referrer.
- Rakeback accrues per seat and is claimable in one transaction, or auto-swept at cash-out.
Deployment shape
- Networks: Stellar (Soroban), EVM chains, Solana — one escrow implementation per VM, one shared channel message format.
- Transport: encrypted P2P (WebRTC) with a stateless relay fallback; the relay sees only ciphertext.
- Wallets: OmniWallet native, WalletConnect for EVM, wallet-standard adapters for Solana.
- Client: React + TanStack Start; crypto in WASM workers so the felt never blocks.