Skip to content

feat(network): mainnet RPC + settings switcher (Tier 3.1)#3

Closed
epicexcelsior wants to merge 3 commits into
v3from
epic/mainnet-switcher
Closed

feat(network): mainnet RPC + settings switcher (Tier 3.1)#3
epicexcelsior wants to merge 3 commits into
v3from
epic/mainnet-switcher

Conversation

@epicexcelsior

@epicexcelsior epicexcelsior commented May 15, 2026

Copy link
Copy Markdown
Owner

Summary

  • Adds Settings → Network row that opens a bottom-sheet for switching between mainnet beta / devnet / custom RPC. Preference persists to AsyncStorage and takes priority over the build-time EXPO_PUBLIC_SOLANA_RPC env default.
  • Mainnet selection is gated behind a one-time confirmation alert ("real funds, not audited") with the acknowledgement persisted in PrefKeys.MAINNET_ACK.
  • Recovery-key modal surfaces an extra mainnet reminder when the runtime cluster is mainnet beta (T-WALLET-* threat-model family).
  • Explorer URLs now honour the runtime cluster — mainnet drops the ?cluster= query (the explorer default), devnet/testnet/custom map correctly, fixing the previous always-?cluster=devnet hardcode.

Files

  • src/infrastructure/network/preference.ts (new): pref types, layered URL resolution (pref → env → public devnet), cached load + change emitter, mainnet/devnet/testnet defaults, URL validator.
  • src/infrastructure/network/connection.ts: adds getEffectiveRpcUrl() / getActiveSolanaConnection(); singleton rebuilt on every pref change via subscriber. Legacy solanaConnection export kept for back-compat.
  • context/NetworkModeContext.tsx: exposes pref / cluster / rpcUrl / setPref; rebuilds DirectRpcAdapter when pref.url changes so live consumers (balance / send) pick up the new endpoint without restart.
  • components/settings/NetworkSwitcherSheet.tsx (new): radio selection + validated custom URL input, mainnet confirmation gate, active-URL preview.
  • components/settings/ExportWalletModal.tsx: mainnet-only banner above the recovery-key warning.
  • screens/SettingsScreen.tsx: new "solana cluster · {current}" row in the network section.
  • src/services/explorer.ts: cluster-aware URL builder.
  • src/storage/index.ts: NETWORK_PREF + MAINNET_ACK key constants.

Singleton rebuild — full / restart-required / not-attempted

Full. The solanaConnection export is reassigned in-place on every pref change (subscribed module-side), and useNetworkMode()'s DirectRpcAdapter re-mounts on pref.url change, so the live UI paths (balance refresh, send transaction blockhash / submit) follow the new endpoint without restart. A restart hint remains in the sheet UI as belt-and-suspenders for any caller that captured the connection at module-load and never re-reads it.

Coordination with anonmesh#52 (NetworkBanner)

Banner was NOT extended in this PR — it doesn't exist on this branch yet. PR anonmesh#52 ships the env-only banner; a follow-up after both merge will extend it to read the runtime pref via useNetworkMode().cluster. Merge order: anonmesh#52 first, then this PR.

Safety rails respected

Test plan

  • Open Settings → tap "solana cluster · devnet" row → sheet opens with devnet radio selected
  • Switch to mainnet → confirmation alert fires ("Real funds...") → tap Continue → row updates to "mainnet beta"
  • Switch back to mainnet a second time → alert does NOT re-fire (acknowledgement persisted)
  • Pick custom → paste valid https URL → save button enables → save → row reflects custom
  • Pick custom → paste "not a url" → save button disabled, inline error shown
  • After mainnet switch → open Settings → Reveal recovery key → mainnet reminder banner appears above the standard recovery warning
  • On mainnet, complete a send → transaction explorer link opens explorer.solana.com (no ?cluster=devnet)
  • Force-quit + relaunch → preference persists across app launch

Generated with Claude Code.

epicexcelsior and others added 3 commits May 15, 2026 02:21
Adds a Settings → Network row that opens a bottom-sheet for picking
mainnet beta / devnet / custom RPC. Choice persists to AsyncStorage and
takes priority over the build-time EXPO_PUBLIC_SOLANA_RPC default.

- src/infrastructure/network/preference.ts: pref types, layered URL
  resolution (pref → env → public devnet), cached load + change emitter
- src/infrastructure/network/connection.ts: getEffectiveRpcUrl() +
  getActiveSolanaConnection(); singleton rebuilt on every pref change,
  legacy solanaConnection export kept for back-compat
- context/NetworkModeContext.tsx: exposes pref / cluster / rpcUrl /
  setPref; rebuilds DirectRpcAdapter when pref.url changes so live
  consumers (balance / send) pick up the new endpoint without restart
- components/settings/NetworkSwitcherSheet.tsx: radio selection +
  validated custom URL input; mainnet selection gated behind a one-time
  confirmation alert ("real funds, not audited"), acknowledgement
  persisted in PrefKeys.MAINNET_ACK
- screens/SettingsScreen.tsx: new "solana cluster · {current}" row above
  cellular fallback in the existing network section
- src/storage/index.ts: NETWORK_PREF + MAINNET_ACK key constants

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Explorer URLs were hardcoded to ?cluster=devnet, so a transaction sent
on mainnet would open as if it were a devnet tx and report "not found".
Now reads the active cluster via getEffectiveCluster() — mainnet drops
the query (explorer.solana.com default), devnet/testnet/custom map to
their documented values. Custom RPCs fall back to devnet for display
since the explorer can't resolve a private endpoint on its own.

Function name preserved for back-compat with existing callsites
(TxDetailModal, SuccessCard, FailureCard, sendTransaction); also adds
a clearer buildExplorerTxUrl alias for new callers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Surfaces an additional warning banner inside ExportWalletModal when the
runtime cluster is mainnet beta. Sits above the existing recovery-key
warning so a user who graduated onto mainnet via the network switcher
gets a clear "these keys move real funds" prompt before they see the
secret. T-WALLET-* threat-model family.

Non-mainnet (devnet/testnet/custom) flow is unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@epicexcelsior

Copy link
Copy Markdown
Owner Author

Re-routing to anonmesh/mobile_app — wrong repo target by mistake.

@epicexcelsior epicexcelsior deleted the epic/mainnet-switcher branch May 16, 2026 05:08
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.

1 participant