Skip to content

fix(web-wallet): de-flake E2E cold-start connect (#177 item 2b) - #181

Open
raul-oliveira wants to merge 1 commit into
raul-oliveira/feat/web-wallet-e2e-followups-pr1from
raul-oliveira/feat/web-wallet-e2e-followups-pr2
Open

fix(web-wallet): de-flake E2E cold-start connect (#177 item 2b)#181
raul-oliveira wants to merge 1 commit into
raul-oliveira/feat/web-wallet-e2e-followups-pr1from
raul-oliveira/feat/web-wallet-e2e-followups-pr2

Conversation

@raul-oliveira

Copy link
Copy Markdown
Contributor

Motivation

The dominant flake in the real-MetaMask E2E suite is the cold-start of MetaMask's MV3 service
worker plus the Snap's first heavy op
. On its first RPC the Snap starts a read-only Hathor wallet
(a wallet-service round-trip). The dApp's own connect path races that with a NETWORK_CHECK
(htr_getConnectedNetwork, capped by SNAP_TIMEOUT_MS); if the check fires while the cold-start is
still in flight, connect throws "Snap is not responding" and the journey never reaches the
connected home (empirically ~2 pass / 3 fail on a slow headless box — issue #177 item 2).

helpers/journeys.ts already warmed the Snap out-of-band (warmSnap) before the timed connect, but
the warm-up was best-effort: the provider/worker waits used fixed try-counts and the final
read-only invoke (htr_getConnectedNetwork) was fire-and-forget (.catch(() => undefined)). So
warmSnap could return while the read-only wallet was still starting — leaving the very race the
dApp's timed check then loses.

This PR (decision D1 in the #177 design) converts warmSnap into a success-gated readiness
probe
: it does not return until every stage has actually succeeded, each bounded by a real
deadline (not a fixed try-count) — (a) provider injected, (b) wallet_getSnaps resolves (MV3 worker
awake), (c) wallet_requestSnaps resolves (Snap installed, retried on a cold-worker reject), (d) a
real htr_getConnectedNetwork invoke resolves (read-only wallet finished starting). Stage (d) — the
exact call the dApp's timed check would otherwise race — is now awaited instead of fire-and-forget.
New budgets live in the centralized TIMEOUTS.probe (from PR 1), so E2E_TIMEOUT_SCALE still scales
them.

This is PR 2 of the #177 follow-up sequence and closes item 2b. It stacks on PR 1
(#178, base branch raul-oliveira/feat/web-wallet-e2e-followups-pr1), which relocated the shared
timeouts module; GitHub auto-retargets this PR to master once PR 1 merges. Item 2a shipped in PR 1;
the nightly CI (item 2c) is PR 3. No production runtime code changes — only the E2E harness.

Acceptance Criteria

  • warmSnap is success-gated: it returns only after stages (a)–(d) each resolve within their
    deadline; the previously fire-and-forget htr_getConnectedNetwork (stage d) is now awaited.
  • New probe budgets are centralized in TIMEOUTS.probe (wrapped in ms(), plain numbers so they
    survive page.evaluate), scaling with E2E_TIMEOUT_SCALE; SNAP_ID, connect, switchNetwork,
    and the provision* helpers are unchanged.
  • yarn workspace @hathor/web-wallet lint is clean.
  • A normal run (yarn e2e --project=onboarding, the webServer's SNAP_TIMEOUT_MS=60000 cap) passes:
    9/9 with the probe applied.
  • Measured de-flake (onboarding journey, --retries=0, cold every run, first-attempt pass rate).
    To make the cold-start race observable, the dApp was driven with a production-like SNAP_TIMEOUT_MS
    cap far below the webServer's 60000, servers booted manually and reused by Playwright. Each run uses
    a fresh MetaMask seed → fresh xpub → the wallet-service is cold per run, so warming is per-run and
    the comparison is clean:
    • SNAP_TIMEOUT_MS=5000 — baseline (pristine) 8/8 vs after (probe) 6/6. No regression at a
      viable cap.
    • SNAP_TIMEOUT_MS=6000 baseline 8/8; SNAP_TIMEOUT_MS=8000 baseline 8/8 — this box is fast
      enough that the best-effort background warm-up already completes before connect, so the flake does
      not reproduce at these caps.
    • SNAP_TIMEOUT_MS=4000 — baseline flakes 4/8, but this cap sits below the Snap's warm
      read-only-wallet floor
      : getAndStartReadOnlyHathorWallet (packages/snap src/utils/wallet.ts)
      constructs a fresh wallet and calls startReadOnly() on every RPC, so even a fully warmed
      wallet-service still costs a fixed round-trip that exceeds 4000ms. Neither best-effort nor the
      probe can win there (probe 0/5), so 4000 is not a valid comparison point.
    • Honest note: this box is too fast to exhibit a clean before/after separation — where the
      baseline flakes (4000) it is below the warm floor, and where the probe could help (≥5000) the box
      already passes 8/8. The probe is still correct: it removes the theoretical race by
      success-gating stage (d) (fire-and-forget → awaited), which strictly dominates on a genuinely slow
      box (the issue's headless 2-pass/3-fail environment) where the cold-start greatly exceeds the warm
      floor. Traced failures confirmed the exact mechanism: "Network check timeout" → "Snap is not
      responding"
      at useWalletConnection.ts (the htr_getConnectedNetwork NETWORK_CHECK).

Checklist

  • If you are requesting a merge into master, confirm this code is production-ready and can be included in future releases as soon as it gets merged
  • Make sure either the unit tests and/or the QA tests are capable of testing the new features
  • Make sure you do not include new dependencies in the project unless strictly necessary and do not include dev-dependencies as production ones. More dependencies increase the possibility of one of them being hijacked and affecting us.

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 492d2242-3284-4e15-83df-5f22cb4d5d9e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@raul-oliveira raul-oliveira self-assigned this Jul 29, 2026
@raul-oliveira raul-oliveira moved this from Todo to In Progress (Done) in Hathor Network Jul 29, 2026
@raul-oliveira raul-oliveira moved this from In Progress (Done) to In Progress (WIP) in Hathor Network Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In Progress (WIP)

Development

Successfully merging this pull request may close these issues.

1 participant