# RAIN Risk Markets dApp — Build Summary

Static frontend (no build step): `index.html` + `style.css` + `app.js` + `abis.js`.
Ethers v6.13.2 from cdnjs. Visual style matches the simulator (dark, lime accent, same CSS variables).

## What was built
- **Wallet**: MetaMask/injected connect; Arbitrum One (42161) check with `wallet_switchEthereumChain` + `wallet_addEthereumChain` fallback; reload on account/chain change.
- **CONFIG** at top of `app.js` with placeholder addresses (`…DEAD01–04 // DEPLOY_ME`); placeholder detection shows a "contracts not yet deployed" banner and disables write buttons. USDC is set to real Arbitrum-native USDC.
- **Stats strip**: TVL, `pricePerShare()`, `hwmPricePerShare()`, `underWater()` (Recovery Gate), `globalNetExposure()`.
- **Vault panel**: shares/assets/USDC balance, TVL cap, withdrawal delay; approve+`deposit`; two-step exit via `requestWithdrawal(shares)` then `redeem` after unlock (claim button enabled only when unlocked); pending withdrawal display.
- **Trade panel**: markets enumerated via `nextMarketId()` + `getMarket(i)` (id, EV, sigma, max multiplier, outcome count); client-side max-stake estimate mirroring Rules 1–3 math from RiskEngine (incl. gross-up for spread); approve+`placePosition(marketId, stake, brand, brandMarkupPpm)`.
- **Positions + feed**: user positions discovered via `PositionPlaced` log filter (last ~900k blocks) + `positions(id)`; live listeners on `PositionPlaced`, `PositionSettled` (win/loss + payout toast), `PositionVoided`, `PnLApplied`, `WithdrawalRequested`; VOID+REFUND button appears when `placedAt + fulfillmentTimeout` has passed (`voidTimedOut`).
- **Errors**: rejection/allowance/gas/wrong-network handled with toasts; mobile-friendly grid.
- All ABIs in `abis.js` extracted verbatim from the .sol sources (no invented names).

## v1.1.0 (2026-07-27)
- **Read-only bootstrap**: markets + vault stats load via public RPC (`JsonRpcProvider`, arb1.arbitrum.io/rpc) before any wallet connects; `R()` helper picks signer-bound vs read-only contracts.
- **Connect chooser modal**: Browser Wallet vs WalletConnect QR. WalletConnect via `@walletconnect/ethereum-provider@2.17.0` (esm.sh, no build step), reads `CONFIG.walletConnectProjectId`; empty id → "activation pending" toast. Silent WC session restore on load.
- **Header logo**: official RAIN logo (`rain-logo-v2.png`, 36px) next to title.
- **Footer**: `RAIN dApp v1.1.0 · 2026-07-27`.
- Real Arbitrum One mainnet addresses in CONFIG.

## v1.2.0 (2026-07-27) — QR wallet connect
- **Coinbase Wallet QR connect** added as a third chooser option (🔵 Coinbase Wallet). Uses `@coinbase/wallet-sdk@4.3.0` via esm.sh (no build step, **no API key/signup required**). `eth_requestAccounts` opens Coinbase's own QR/popup UI; works with the Coinbase Wallet mobile app + Smart Wallet. Wired via `connectCoinbase()` → `finishConnect(cb, false, "Coinbase")`; net badge shows `· CB`; disconnect handled.
- **WalletConnect projectId still empty.** Reown Dashboard signup was retried twice via Browserbase (correct email form: Email + Password + form's own Sign Up button, cookie banner accepted, Turnstile wait). Both attempts: the SPA never transitioned past the sign-up page and no verification email ever arrived at moshe@agent.joni.ai — Reown appears to silently block automated signups (likely Turnstile/bot scoring). If the user creates a project manually at dashboard.reown.com, paste the Project ID into `CONFIG.walletConnectProjectId` and the existing WC QR flow works as-is.
- Footer bumped to `v1.2.0`.

## Deploy rule ⚠️
**Bump the version on EVERY deploy** — update `APP_VERSION` in `app.js` and the footer date, so the live site (`https://rainsandbox.xyz/app/`) is verifiable via `curl | grep APP_VERSION` despite CDN caching (~1 min lag). Deploy via agentbase skill: `update-code.cjs --files-json` (files under `app/` prefix in repo, workflow `b086e87d-01e0-4c20-9b0b-3e73df8a0cd1`) then `redeploy.cjs`, poll `service-status.cjs` until `live`.

## Verified
`node --check` on app.js and abis.js; HTML structure sanity check — all pass. Nothing deployed, no keys touched.

## Assumptions
- Frontend places positions with `brand = address(0)` and `brandMarkupPpm = 0` (protocol frontend); change in CONFIG for brand deployments.
- Markets have no on-chain names; displayed as "Market #id — max X× · σ".
- Max-stake hint is a client-side estimate replicating contract math (same integer ops); the contract remains the source of truth.
- Fee-split totals: FeeSplitter exposes ppm params and per-brand accrual but no cumulative totals, so the stats strip shows exposure/HWM/gate instead; `Split` events could be aggregated later if desired.
- Position log scan window is ~900k blocks; extend or index off-chain for full history.
- Logo uses a simplified drop mark (the simulator's inline SVG wordmark can be copied in verbatim if preferred).
