Frontend leaks mainnet RPC requests to eth.merkle.io while connected to local Anvil (chain 31337) #81

Closed
opened 2026-04-27 05:44:04 +00:00 by Brouie · 8 comments
Brouie commented 2026-04-27 05:44:04 +00:00 (Migrated from gitlab.com)

Found Monday 2026-04-27 during browser walkthrough on the live YO frontend (vite dev / chain 31337 / connected to local Anvil RPC).

Summary

While browsing the TimeCurve Arena page (/timecurve/arena) connected to local Anvil chain 31337, the browser console is flooded with CORS-blocked fetch requests to https://eth.merkle.io (Ethereum mainnet public RPC).

The requests are blocked by CORS so they don't succeed — but the frontend is still issuing them, which means:

  1. Chain context leak: local-anvil-only browsing exposes user origin / wallet context to a public mainnet RPC endpoint
  2. Console noise: ~10+ CORS errors per page load, drowning out legitimate console output for QA / dev debugging
  3. Likely accidental fallback: eth.merkle.io is not anywhere in frontend/src — it must be coming through a wagmi / viem default-chains config or a multicall preflight that doesn't honor the configured chain

Repro

  1. Stack up: SKIP_ANVIL_RICH_STATE=1 bash scripts/start-local-anvil-stack.sh
  2. Frontend: cd frontend && npm run dev
  3. Browser: open http://127.0.0.1:5173/timecurve/arena (or /timecurve)
  4. Connect a wallet on chain 31337 (e.g. anvil account #0 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266)
  5. Open DevTools console → tab Console

Observed

Access to fetch at 'https://eth.merkle.io/' from origin 'http://localhost:5173' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Failed to load resource: net::ERR_FAILED

Repeats ~10+ times across page renders.

Source check

grep -rn 'eth.merkle.io\|merkle.io' frontend/src/ frontend/public/ frontend/index.html frontend/vite.config.ts returns no matches. So the URL is injected by a transitive dep (wagmi config, viem default chains, or a connector preflight).

Suggested investigation

  • Audit frontend/src/lib/wagmi*.ts (or wherever wagmi config lives) for chain-list defaults that include mainnet
  • Confirm wagmi config explicitly scopes chains to anvil (chain 31337) only when in dev mode against local
  • If wagmi multi-chain support is intentional, the local-dev build should still suppress mainnet probes (env-gated chain list)
  • Check for a viem multicall fallback or a chain-icon resolver that pings public RPCs

Severity

LOW-MEDIUM. Not a current bug breaking functionality (CORS blocks the leak), but:

  • Noise hurts dev/QA productivity
  • Browsing local-only pages should never produce mainnet RPC traffic
  • Could become a real leak if eth.merkle.io adds CORS headers in the future, or if user has a CORS-permissive proxy / browser extension

Out of scope

Not blocking the active sale / buy / WarBow / redeem flows on the local stack. Application-level UX is functional. Filing as housekeeping / hygiene item.

cc @PlasticDigits

Found Monday 2026-04-27 during browser walkthrough on the live YO frontend (vite dev / chain 31337 / connected to local Anvil RPC). ## Summary While browsing the TimeCurve Arena page (`/timecurve/arena`) connected to local Anvil chain 31337, the browser console is flooded with **CORS-blocked fetch requests to `https://eth.merkle.io`** (Ethereum mainnet public RPC). The requests are blocked by CORS so they don't succeed — but the frontend is still issuing them, which means: 1. **Chain context leak:** local-anvil-only browsing exposes user origin / wallet context to a public mainnet RPC endpoint 2. **Console noise:** ~10+ CORS errors per page load, drowning out legitimate console output for QA / dev debugging 3. **Likely accidental fallback:** `eth.merkle.io` is not anywhere in frontend/src — it must be coming through a wagmi / viem default-chains config or a multicall preflight that doesn't honor the configured chain ## Repro 1. Stack up: `SKIP_ANVIL_RICH_STATE=1 bash scripts/start-local-anvil-stack.sh` 2. Frontend: `cd frontend && npm run dev` 3. Browser: open `http://127.0.0.1:5173/timecurve/arena` (or `/timecurve`) 4. Connect a wallet on chain 31337 (e.g. anvil account #0 `0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266`) 5. Open DevTools console → tab Console ## Observed ``` Access to fetch at 'https://eth.merkle.io/' from origin 'http://localhost:5173' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Failed to load resource: net::ERR_FAILED ``` Repeats ~10+ times across page renders. ## Source check `grep -rn 'eth.merkle.io\|merkle.io' frontend/src/ frontend/public/ frontend/index.html frontend/vite.config.ts` returns no matches. So the URL is injected by a transitive dep (wagmi config, viem default chains, or a connector preflight). ## Suggested investigation - Audit `frontend/src/lib/wagmi*.ts` (or wherever wagmi config lives) for chain-list defaults that include mainnet - Confirm wagmi config explicitly scopes `chains` to anvil (chain 31337) only when in dev mode against local - If wagmi multi-chain support is intentional, the local-dev build should still suppress mainnet probes (env-gated chain list) - Check for a viem multicall fallback or a chain-icon resolver that pings public RPCs ## Severity LOW-MEDIUM. Not a current bug breaking functionality (CORS blocks the leak), but: - Noise hurts dev/QA productivity - Browsing local-only pages should never produce mainnet RPC traffic - Could become a real leak if eth.merkle.io adds CORS headers in the future, or if user has a CORS-permissive proxy / browser extension ## Out of scope Not blocking the active sale / buy / WarBow / redeem flows on the local stack. Application-level UX is functional. Filing as housekeeping / hygiene item. cc @PlasticDigits
Brouie commented 2026-04-27 05:46:30 +00:00 (Migrated from gitlab.com)

mentioned in issue #51

mentioned in issue #51
PlasticDigits commented 2026-04-27 06:44:45 +00:00 (Migrated from gitlab.com)

mentioned in commit 351a41ffff

mentioned in commit 351a41ffffb9a6379d76ff29076d714db0f93931
PlasticDigits commented 2026-04-27 06:45:12 +00:00 (Migrated from gitlab.com)

Update (pushed to main in 351a41f): @brouie can you verify on your side?

What changed

  • Single-chain wagmi: frontend/src/wagmi-config.ts now registers only configuredChain() (no bundled mainnet / sepolia). That stops viem from opening default HTTP transports to public Ethereum RPCs (e.g. eth.merkle.io) when the wallet is on Anvil 31337 — the CORS noise in the report.
  • Default env when unset: VITE_CHAIN_ID / resolveChainRpcConfig and the fallback in wagmi-config now default to Anvil 31337 (and http://127.0.0.1:8545 for RPC in chain.ts), matching scripts/start-local-anvil-stack.sh and E2E. frontend/.env.example updated.
  • Docs / skills: Invariants + wallet-connection.md, invariants test map, .cursor/skills/yieldomega-guardrails/SKILL.md item 21, play skill skills/verify-yo-local-wagmi-single-chain/SKILL.md.

Checklist to verify

  • SKIP_ANVIL_RICH_STATE=1 bash scripts/start-local-anvil-stack.sh then cd frontend && npm run dev, open /timecurve/arena on http://127.0.0.1:5173, connect Anvil acct #0 on 31337
  • DevTools Network (or Console): no requests (or no CORS failures) to eth.merkle.io
  • Filter: merkle → zero rows after load/navigation
  • Sanity: buys / reads still work against local RPC (unchanged contract wiring from .env.local)

Leaving the issue open until you confirm.

**Update (pushed to `main` in 351a41f):** @brouie can you verify on your side? ### What changed - **Single-chain wagmi:** `frontend/src/wagmi-config.ts` now registers only `configuredChain()` (no bundled `mainnet` / `sepolia`). That stops viem from opening default HTTP transports to public Ethereum RPCs (e.g. `eth.merkle.io`) when the wallet is on Anvil **31337** — the CORS noise in the report. - **Default env when unset:** `VITE_CHAIN_ID` / `resolveChainRpcConfig` and the fallback in `wagmi-config` now default to **Anvil 31337** (and `http://127.0.0.1:8545` for RPC in `chain.ts`), matching `scripts/start-local-anvil-stack.sh` and E2E. `frontend/.env.example` updated. - **Docs / skills:** Invariants + [wallet-connection.md](docs/frontend/wallet-connection.md), [invariants test map](docs/testing/invariants-and-business-logic.md#frontend-single-chain-wagmi-issue-81), [`.cursor/skills/yieldomega-guardrails/SKILL.md`](.cursor/skills/yieldomega-guardrails/SKILL.md) item 21, play skill [skills/verify-yo-local-wagmi-single-chain/SKILL.md](skills/verify-yo-local-wagmi-single-chain/SKILL.md). ### Checklist to verify - [ ] `SKIP_ANVIL_RICH_STATE=1 bash scripts/start-local-anvil-stack.sh` then `cd frontend && npm run dev`, open `/timecurve/arena` on `http://127.0.0.1:5173`, connect Anvil acct #0 on **31337** - [ ] DevTools **Network** (or **Console**): no requests (or no CORS failures) to **`eth.merkle.io`** - [ ] **Filter:** `merkle` → zero rows after load/navigation - [ ] Sanity: buys / reads still work against local RPC (unchanged contract wiring from `.env.local`) Leaving the issue **open** until you confirm.
Brouie commented 2026-04-27 06:48:43 +00:00 (Migrated from gitlab.com)

@PlasticDigits verified on 351a41f.

Verification

Browser walkthrough on the same live local-anvil stack used to repro the original bug. After git pull + Vite restart to pick up the new wagmi-config.ts:

  1. Refreshed http://127.0.0.1:5173/timecurve and /timecurve/arena
  2. Opened DevTools console
  3. Filtered for 'merkle' and 'CORS'

Result

Zero requests to eth.merkle.io. Zero CORS errors.

The console now only shows:

  • Wallet-extension noise (MetaMask + Backpack injection contention — unrelated, browser-side)
  • AudioContext autoplay warnings (unrelated, web-platform restriction)
  • One friendly info log: '[yieldomega] VITE_WALLETCONNECT_PROJECT_ID is empty - using injected wallets only (no WalletConnect / Reown). Set the env var for mobile WalletConnect.' from wagmi-config.ts:33 — that's helpful operator guidance, not an error

Sanity check

Page-level functionality unaffected:

  • TimeCurve sale state reads correctly (timer ticking, 1.004085 CL8Y per CHARM, live band rendering)
  • Pay-mode toggle works (CL8Y / ETH / USDM tabs all clickable)
  • Slider responsive
  • Buy CHARM button + cooldown rendering
  • Recent Buys / indexer mirror still populated
  • Live band, charm bounds, fee router all still showing correct values from chain 31337

Closing #81.

@PlasticDigits verified on 351a41f. ## Verification Browser walkthrough on the same live local-anvil stack used to repro the original bug. After git pull + Vite restart to pick up the new wagmi-config.ts: 1. Refreshed http://127.0.0.1:5173/timecurve and /timecurve/arena 2. Opened DevTools console 3. Filtered for 'merkle' and 'CORS' ## Result **Zero requests to eth.merkle.io. Zero CORS errors.** The console now only shows: - Wallet-extension noise (MetaMask + Backpack injection contention — unrelated, browser-side) - AudioContext autoplay warnings (unrelated, web-platform restriction) - One friendly info log: '[yieldomega] VITE_WALLETCONNECT_PROJECT_ID is empty - using injected wallets only (no WalletConnect / Reown). Set the env var for mobile WalletConnect.' from wagmi-config.ts:33 — that's helpful operator guidance, not an error ## Sanity check Page-level functionality unaffected: - TimeCurve sale state reads correctly (timer ticking, 1.004085 CL8Y per CHARM, live band rendering) - Pay-mode toggle works (CL8Y / ETH / USDM tabs all clickable) - Slider responsive - Buy CHARM button + cooldown rendering - Recent Buys / indexer mirror still populated - Live band, charm bounds, fee router all still showing correct values from chain 31337 Closing #81.
Brouie (Migrated from gitlab.com) closed this issue 2026-04-27 06:48:44 +00:00
PlasticDigits commented 2026-04-27 06:50:07 +00:00 (Migrated from gitlab.com)

mentioned in issue #82

mentioned in issue #82
PlasticDigits commented 2026-04-30 05:25:10 +00:00 (Migrated from gitlab.com)

mentioned in issue #100

mentioned in issue #100
PlasticDigits commented 2026-05-19 10:44:55 +00:00 (Migrated from gitlab.com)

mentioned in issue #219

mentioned in issue #219
PlasticDigits commented 2026-05-29 08:15:04 +00:00 (Migrated from gitlab.com)

mentioned in issue #269

mentioned in issue #269
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
code/yieldomega#81
No description provided.