Everything an external security auditor needs, in one place: contracts in scope with verified source, system architecture, the invariants to try to break, the threat model, test & coverage summary, known incidents, and accepted risks. Prepared honestly β including the gaps.
| CONTRACT | SLOC | STATUS | SOURCIFY |
|---|---|---|---|
| ChannelManager.sol (V3) β simulator-parity limits, incl. reclaimStale | 635 | ACTIVE | exact_match β |
| ChannelManager.sol (V2) | 635 | RETIRED | exact_match β |
| ChannelManager.sol (V1, commit e7da239) | 601 | RETIRED / DRAINED | exact_match β |
| HouseBond.sol | 116 | ACTIVE | exact_match β |
| BlackjackRulesVerifier.sol | 330 | ACTIVE | exact_match β |
| RouletteRulesVerifier.sol β European roulette channel rules (13 bet types, exact casino payouts, solvency check) | 296 | ACTIVE | exact_match β |
| GameMuxVerifier.sol β action-range dispatcher (0β31 blackjack, 32β63 roulette); manager's active verifier | 45 | ACTIVE | exact_match β |
| IRulesVerifier.sol (interface) | 51 | IN SCOPE | β |
| MockEntropyLocal.sol (test-only) | 27 | OUT OF SCOPE | β |
External dependencies: OpenZeppelin (SafeERC20, ReentrancyGuard, Ownable), Pyth Entropy (provider callback), RUSD ERC-20 (6 decimals, standard).
Off-chain components (threat-model context, not in on-chain scope): services/house-node/ (TS house node: WAL, checkpointing, grace timers, reconciliation, recover_close) and app/channels-sdk.js (browser SDK: session keys, hash chains, EIP-712 signing, snapshot/restore persistence).
Player deposits RUSD; the operator allocates house float. Hands are played off-chain via dual-signed EIP-712 ChannelStates (domain "RAIN ChannelManager" v1). Randomness = mutual commit-reveal hash chains (N=4096) + a Pyth Entropy session seed mixed per card: card_k = keccak(pRev_k, hRev_k, sessionSeed, channelId, k) % 52, house-reveals-first ordering.
On-chain surface: open (entropy seed request) Β· checkpoint (nonce floor) Β· cooperative close Β· forced close + challenge window + finalize Β· reveal verification (submitReveals/demandReveal) Β· fraud proof (proveBadTransition) Β· stale-channel recovery (reclaimStale, V2 only, 30-day) Β· fee waterfall at close (1.25% of lost gross β burn 4000 / rebate 1700 / standby 500 / residual 6300 ppm).
Key parameters: 180s player grace β forfeit of open hand's gross Β· house fault β hand void + comp max(2Γ gross, 5 RUSD) from HouseBond + equal penalty burned Β· tiered challenge windows (β€10,000 RUSD β 10 min; above β 24 h) Β· exposure caps (Ξ£ allocations β€ 2% vault float, single β€ 0.25%, 8ΓmaxUnitStake β€ allocation) Β· HouseBond 7-day withdrawal delay, pending withdrawals remain slashable Β· funds always pay out to playerOwner wallet, never session keys.
| ChannelManager V3 (active) | 0xe13DC056111906aA4d3659880EBDC67138c76DA7 |
| ChannelManager V2 (retired) | 0xacbfCf3523DDA59419a181Afb2e17A80BE453fEb |
| ChannelManager V1 (retired) | 0x82698e4C14DC5D144bdA2a028aDe60d886681AFb |
| HouseBond (2Γ100k RUSD bonds) | 0x37E41E1d1EE2F96cE0203af06d639448547ade84 |
| BlackjackRulesVerifier | 0xFdcCE2DE2F8A28cBBdBaB01131C3bab138Eb9293 |
| RouletteRulesVerifier | 0x5239969ff32e5733F319d3e80eD64D19adF4962f |
| GameMuxVerifier (manager.verifier) | 0x14155583112F87777B95AB0EA76786bf079c6A83 |
| Blackjack V3 (Pyth fallback) | 0x2E7747E9c4c18511A4F9e79C6bD38061e1443F7A |
| RUSD token (demo, 6 dec) | 0x260c7019E760763988843Bc5b873d749b5937469 |
| Pyth Entropy | 0x7698E925FfC29655576D0b361D75Af579e20AdAc |
House node: wss://api.rainsandbox.xyz Β· health: api.rainsandbox.xyz/health Β· Arbiscan API verification pending (no API key); Sourcify metadata renders on Arbiscan/Blockscout regardless.
playerBal + houseBal + feeAccrued + grossAtRisk == deposit + allocation. No path may mint or destroy escrowed RUSD.playerOwner (never session keys); house payouts only to operator; fee waterfall split is exact and applied exactly once._entropyCallback; voidOpen full-refund only in SeedPending after 1h; exposure caps enforced at open.nonReentrant; RUSD is standard but assume-hostile analysis welcome.proveBadTransition accepts only a dual-signed prev and an accused-signed illegal next; a legal transition must never be punishable; card derivation must be unbiased given one honest party.finalizeClose, reclaimStale, submitReveals) β must not worsen an honest party's outcomeforge coverage is unobtainable β via_ir + coverage instrumentation hits "stack too deep" even with --ir-minimum. Line-coverage numbers unavailable.v4.0.0 kept session material in memory only β page refresh abandoned open channels. 4 channels (2,400 RUSD) recovered via operator cooperativeClose from WAL states. Fix: localStorage snapshot/restore, auto-resume, recover_close, 30-min reconciliation sweep.
Opened mid-maintenance; WAL wiped by redeploys, no dual-signed state on either side. V1 has no recovery path by design (re-audited: all settle paths need dual signatures). Player's 500 RUSD deposit refunded from treasury; 2,000 RUSD house allocation written off. This failure mode is exactly why V2's reclaimStale exists.
Bar: a redeploy mid-session must never destroy the only copy of a signed state. Three layers now: per-hand on-chain checkpoints (~130k gas, default ON), checkpoint-on-disconnect, SIGTERM flush β plus client snapshots and reclaimStale as the durable floor.
/tmp β mitigated in v4.1.0 by per-hand checkpoints, disconnect checkpoints, SIGTERM flush, client snapshots + recover_close. On-chain checkpoint + reclaimStale is the durable floor.proveBadReveal intentionally subsumed by submitReveals chain verification.Contact for audit engagement: open a channel via the dApp or the operator address above. Suggested firms & budget rationale are in spec Β§7.3.