HONEST STATUS: This is a demo deployment on Arbitrum One using play-money RUSD (faucet token). Contracts are pre-audit — Sourcify-verified, 163/163 forge + 38/38 integration tests green, but no external audit yet. Current max bet is 3,125 RUSD/hand, bond-limited (design target at $300M TVL: $520k–600k). One V1 incident is fully disclosed in the audit package. Credibility is the product — read the gaps before the claims.

🛡 RAIN CHANNELS — AUDIT PACKAGE

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.

⚠ PRE-AUDIT — NOT YET EXTERNALLY AUDITED SOURCIFY EXACT-MATCH VERIFIED FORGE 163/163 · INTEGRATION 38/38 DEMO STAKES (RUSD PLAY-MONEY)

1CONTRACTS IN SCOPE

Solidity 0.8.24 · Foundry · via_ir=true · ~1,470 raw lines (~1,100 SLoC) · protocol/src/channels/
CONTRACTSLOCSTATUSSOURCIFY
ChannelManager.sol (V3) — simulator-parity limits, incl. reclaimStale635ACTIVEexact_match ✅
ChannelManager.sol (V2)635RETIREDexact_match ✅
ChannelManager.sol (V1, commit e7da239)601RETIRED / DRAINEDexact_match ✅
HouseBond.sol116ACTIVEexact_match ✅
BlackjackRulesVerifier.sol330ACTIVEexact_match ✅
RouletteRulesVerifier.sol — European roulette rules (13 bet types, exact payouts, solvency check)296ACTIVEexact_match ✅
GameMuxVerifier.sol — action-range dispatcher (0–31 blackjack, 32–63 roulette)45ACTIVEexact_match ✅
IRulesVerifier.sol (interface)51IN SCOPE
MockEntropyLocal.sol (test-only)27OUT 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).

2SYSTEM SUMMARY

FunFair-style state channels for casino games

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 (% 37 for roulette), 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, 30-day) · fee waterfall at close (1.25% of lost gross → burn 4000 / rebate 1700 / standby 500 / residual 6300 ppm).

Key parameters: 180 s 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 (≤100,000 RUSD → 10 min; above → 24 h) · exposure caps (Σ allocations ≤ 10% vault float, single ≤ 1.5%, 8×maxUnitStake ≤ allocation) · HouseBond 7-day withdrawal delay, pending withdrawals remain slashable · funds always pay to playerOwner, never session keys.

3DEPLOYED ADDRESSES — ARBITRUM ONE (42161)

All active contracts verified on Sourcify (exact_match); links open Arbiscan

House node: wss://api.rainsandbox.xyz · health: api.rainsandbox.xyz/health · V3 constants: EXPOSURE_SINGLE_PPM 15,000 · EXPOSURE_TOTAL_PPM 100,000 · TIER_THRESHOLD 100k RUSD.

4KEY INVARIANTS — WHAT TO TRY TO BREAK

Breaking any one is a valid finding
I1 · CONSERVATIONFor any settling state: playerBal + houseBal + feeAccrued + grossAtRisk == deposit + allocation. No path may mint or destroy escrowed RUSD.
I2 · NONCE MONOTONICITYA checkpointed nonce is a hard floor — no close may settle on a lower nonce; a challenge must strictly increase the closing nonce.
I3 · DUAL SIGNATURE INTEGRITYEvery settlement-bearing state requires valid player-session AND house-session signatures bound to the channel's registered keys, chainId, and verifying contract. No cross-channel or cross-chain replay (fuzz-tested).
I4 · FUNDS DESTINATIONSPlayer payouts only to playerOwner (never session keys); house payouts only to operator; the fee waterfall split is exact and applied exactly once.
I5 · RECLAIMSTALE CONSERVATISMOnly after ≥30 days of zero on-chain activity; settles at the last on-chain checkpoint (or opening balances); never better for the caller than the last anchored state; permissionless; cannot grief an active channel.
I6 · FORFEIT / FAULT PATHSPlayer-forfeit only after grace, and only the open hand's gross; house-fault pays void + comp max(2× gross, 5 RUSD) from HouseBond + equal penalty burned — never from other channels' escrow.
I7 · HOUSEBOND7-day withdrawal delay; pending withdrawals remain slashable; slash pays compensation before burn when underfunded; only the manager may slash; setManager only owner.
I8 · ENTROPYOnly the Pyth Entropy contract may invoke _entropyCallback; voidOpen full-refund only in SeedPending after 1 h; exposure caps enforced at open.
I9 · REENTRANCYAll fund-moving functions are nonReentrant; RUSD is standard but assume-hostile analysis welcome.
I10 · RULES VERIFIERSproveBadTransition accepts only a dual-signed prev and an accused-signed illegal next; a legal transition must never be punishable; card/number derivation must be unbiased given one honest party.
I11 · ROULETTE / MUX (v5.0.0)(a) spin outcome keccak(...)%37 unbiased given one honest party; (b) _bet solvency guarantees _spin can never underflow houseBal; (c) mux dispatch never lets one game's payload validate under the other's rules; (d) node-enforced worst-case payout per spin ≤ 8×maxUnitStake.

5THREAT MODEL

😈 MALICIOUS PLAYER

  • Stale-state forceClose → challenge window + checkpoint floor
  • Mid-hand stall → 180 s grace → forfeit of open gross
  • Fake reveals → on-chain hash-chain verification
  • Session-key theft → irrelevant: funds only pay to playerOwner

🏠 MALICIOUS OPERATOR / HOUSE

  • Stale-state close → player challenge
  • Withholding reveals → demandReveal escalation
  • Refusing to countersign → player forceClose from last dual-signed state
  • Node death → reclaimStale after 30 d guarantees escrow release

👥 THIRD PARTIES

  • Griefing via permissionless functions (finalizeClose, reclaimStale, submitReveals) → must not worsen an honest party's outcome

🌐 INFRASTRUCTURE

  • Arbitrum sequencer outage vs challenge windows → tiered 10 min / 24 h — accepted risk at demo stakes
  • Pyth Entropy liveness → voidOpen full refund after 1 h

6TEST & COVERAGE SUMMARY

Test-to-code ratio ≈ 1.9:1 by lines
FORGE (UNIT + FUZZ)
163/163
INTEGRATION (ANVIL E2E)
38/38
MAINNET E2E + DISPUTE DRILL
✅ EXECUTED
FORGE COVERAGE %
N/A (tooling)

7INCIDENT LOG

Full disclosure — what broke, what it cost, how it was fixed. Details in DEPLOYMENT.md.
✅ Abandoned-escrow recovery (v4.0.1)

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.

⚠ Stuck V1 channel 0xdd64aa43… — 2,500 RUSD locked permanently

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 reclaimStale exists from V2 on.

✅ House-node durability (v4.1.0)

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.

✅ Zombie-session reconnect loop (v4.2.1)

Idle-tab channels closed by reconciliation left clients in an endless "Reconnecting…" loop. Fix: typed unknown_channel errors, on-chain state check, self-healing reopen. Funds were never at risk — UX bug, disclosed anyway.

8ACCEPTED RISKS & KNOWN LIMITATIONS

Auditors: please do not re-report these — known and accepted for v1 / demo stakes

9AUDIT-READINESS CHECKLIST

10AUDIT DOCUMENTS — DOWNLOAD THE FULL PACKAGE

Contact for audit engagement: via the dApp at rainsandbox.xyz or the operator address in the deployment log. Suggested firms & budget rationale in spec §7.3.