Skip to content

Add offline dev mode (?mock): run & verify a mod with no chain, phone, or deploy#12

Open
sacha-l wants to merge 1 commit into
paritytech:mainfrom
sacha-l:feat/offline-dev-mode
Open

Add offline dev mode (?mock): run & verify a mod with no chain, phone, or deploy#12
sacha-l wants to merge 1 commit into
paritytech:mainfrom
sacha-l:feat/offline-dev-mode

Conversation

@sacha-l

@sacha-l sacha-l commented Jun 10, 2026

Copy link
Copy Markdown

What this is

An offline dev mode for the template: open http://localhost:3000/?mock and the whole app runs in a plain browser — register, Solo play, profile, and leaderboard — backed by an in-memory leaderboard and a local Bulletin blob store. No Polkadot host, no chain, no phone, no funding, no deploy.

Why it's needed

Today the template ships only the live, phone-signed, funded, on-chain path. A freshly-modded app cannot be run or verified without an irreversible deploy that binds contract ownership to your signer. That has two costs:

  • You can only discover problems by deploying. The deploy is the first time anything actually runs end-to-end, so a broken mod (or a name/ownership collision) only surfaces after you've signed and spent.
  • No feedback loop for iteration (or for coding agents). There's no way to confirm "my mod works" before the irreversible step, so people edit blind → deploy → fail → repeat.

A dev mode closes that loop: build and verify a mod in seconds, then deploy once you know it works.

What it fixes

  • Lets a developer (or an automated tool) verify register / play / leaderboard / profile end-to-end with zero chain access, before any deploy.
  • Removes the "deploy is the only way to test" trap entirely for the Solo + leaderboard flow.

How it works

  • ?mock stubs the product account (gated by import.meta.env.DEV).
  • createDevLeaderboard() implements the exact live contract surfaceregister / updateResult / isRegistered / getPlayerCid / getPlayerPoints / getPlayerCount / getPlayerAt — with the same { success, value } query shape and origin-mutating txs, so the pages need zero changes.
  • uploadToBulletin / fetchFromGateway are stubbed via a local blob store keyed by the (pure-computed) CID, so Solo's "upload history → store CID → read it back" round-trips fully offline.
  • getContract() / ensureMapping() / uploadToBulletin() / fetchFromGateway() short-circuit under _devMode. State persists in localStorage; two opponents are seeded so the board is populated.

Production safety

_devMode can only be set under import.meta.env.DEV, so esbuild constant-folds it to false and tree-shakes the entire dev path out of production builds — verified: no dev strings (rps:dev:*) in dist/. No production behavior change.

Verification

In a plain browser (no host): load ?mock → play a best-of-3 → Save to Chain (uploadToBulletinregisterupdateResult(cid, delta)) → Home → Leaderboard renders all players sorted — 0 console errors throughout. tsc -b passes; prod build succeeds and strips the dev code.

Multiplayer still requires the host (it uses the Statement Store); Solo + leaderboard + profile work fully offline.

Relation to #11

Complements #11 (collision-free naming). Together they make the standard flow: npm run name:new → verify at ?mock → deploy once. Independent — either can merge alone.

…deploy

A freshly-modded app can currently only be exercised against a live,
phone-signed, funded on-chain deploy — so there is no way to confirm a mod works
before the irreversible deploy step, and the only way to discover a problem is to
deploy. This adds a fully offline dev mode.

- `http://localhost:3000/?mock` stubs the product account and backs the
  leaderboard with an in-memory contract matching the live surface (register /
  updateResult / isRegistered / getPlayerCid / getPlayerPoints / getPlayerCount /
  getPlayerAt), same `{ success, value }` query shape and origin-mutating txs, so
  the pages need zero changes.
- Bulletin upload/read are stubbed via a local blob store keyed by the
  (pure-computed) CID, so Solo's "upload history then store CID" round-trips
  offline. State persists in localStorage; two opponents are seeded so the board
  is populated.
- getContract()/ensureMapping()/uploadToBulletin()/fetchFromGateway() short-circuit
  under `_devMode`, set only under `import.meta.env.DEV` — the whole path is
  tree-shaken from production builds (verified: no dev strings in dist).
- README documents `?mock` as the way to build and verify a mod before deploying.

Verified in a plain browser (no host): register -> play best-of-3 -> save ->
leaderboard renders all players sorted, 0 console errors. Multiplayer still needs
the host (Statement Store); Solo + leaderboard + profile work fully offline.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@cla-bot-2021

cla-bot-2021 Bot commented Jun 10, 2026

Copy link
Copy Markdown

User @claude, please sign the CLA here.

@sacha-l

sacha-l commented Jun 10, 2026

Copy link
Copy Markdown
Author

Part of the modding-DX umbrella: #13 (this PR is item 3 — offline dev mode).

@illegalcall

Copy link
Copy Markdown
Collaborator

Two issues to fix before merge:

  1. Profile and player history are broken in mock mode. The pages don't call the fetchFromGateway() you stubbed; they fetch the gateway directly via fetch(IPFS_GATEWAY + cid) (MyProfile.tsx:43, PlayerHistory.tsx:27, SoloGame.tsx:71, MultiplayerGame.tsx:235). After a save, the profile still shows "No games yet", the history page shows "No game history found", and the console has Failed to fetch errors. Route those four call sites through fetchFromGateway(cid) (this also gives the live path the multi-gateway fallback for free).

  2. Because of the same bypass in SoloGame.tsx:71, a second save silently discards the previous game: the history-merge fetch fails, the catch treats it as a first-time player, and the new save overwrites the blob. After two games + two saves, the stored history contains one game. This is the one I'd consider blocking: "Saved!" masking data loss undermines trust in the dev mode.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants