feat: add @metamask/network-connection-banner-controller - #9041
Conversation
9111156 to
d1fa945
Compare
|
@metamaskbot publish-preview |
|
Preview builds have been published. Learn how to use preview builds in other projects. Expand for full list of packages and versions. |
cb4b1af to
ab5cc87
Compare
|
@metamaskbot publish-preview |
|
Preview builds have been published. Learn how to use preview builds in other projects. Expand for full list of packages and versions. |
This comment was marked as outdated.
This comment was marked as outdated.
6d2ab14 to
2fc626b
Compare
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
|
@metamaskbot publish-preview |
|
Preview builds have been published. Learn how to use preview builds in other projects. Expand for full list of packages and versions. |
replacementSelectedRpcEndpointIndex exists to keep the selected network valid when an RPC endpoint is removed, and nothing is removed here. switchToDefaultInfuraRpcEndpoint now updates the default index and then calls NetworkController:setActiveNetwork itself (skipping the call when the Infura endpoint is already selected), which also makes the network switch visible at a glance.
|
@metamaskbot publish-preview |
Some flows persist MetaMask Infura URLs with the wallet's project id already substituted (adding a popular network stores a custom typed endpoint that way), so the placeholder match alone misclassified them as custom and surfaced the banner with an Update RPC prompt for our own managed endpoint. The controller now takes infuraProjectId and getIsInfuraEndpoint matches both shapes, mirroring the extension's getIsMetaMaskInfuraEndpointUrl. URLs carrying a different project id still count as custom since they run on the user's own account.
In this order a partial failure leaves the failing default in place and the banner visible, instead of hiding the banner while the wallet is still connected to the broken endpoint.
|
@metamaskbot publish-preview |
|
Preview builds have been published. Learn how to use preview builds in other projects. Expand for full list of packages and versions. |
| * @param infuraProjectId - The wallet's Infura project id. | ||
| * @returns True if the URL is a MetaMask Infura endpoint. | ||
| */ | ||
| export function getIsInfuraEndpoint( |
There was a problem hiding this comment.
It just occurred to me, maybe this is something we can move to NetworkController. Or maybe we don't even need to check for this at all! If NetworkController can automatically correct misconfigured Infura RPC endpoints (and even consolidate duplicates) then we can go back to relying on the type property.
That's out of scope for this ticket but I'll add a new one to capture this. Then later we can come back and simplify this.
mcmire
left a comment
There was a problem hiding this comment.
One suggestion below and then this looks good to me.
…ection-banner-controller
The degraded and unavailable thresholds become constructor options with the previous values as exported defaults, following the interval option pattern used across controllers. The unavailable timeout is measured from the same failure start and the constructor throws when it does not exceed the degraded one, since the escalation timer runs for their difference.
…to 0.0.0 New packages start at 0.0.0 per the create-package template. #9041 shipped it at 0.1.0 by mistake.
…ontroller (#9436) ## Explanation The package was merged in #9041 with an initial version of `0.1.0`. New packages should start at `0.0.0` (that's what the `create-package` template sets). This bumps it back down so the first release goes out correctly. ## References Follow up to #9041 ## Checklist - [x] I've updated the test suite for new or updated code as appropriate - [x] I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate - [x] I've highlighted breaking changes using the "BREAKING" category above as appropriate - [x] I've prepared draft pull requests for cross repository changes as appropriate <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Only `package.json` version metadata changes; no runtime or API behavior is affected. > > **Overview** > Aligns `@metamask/network-connection-banner-controller` with the monorepo convention for new packages by changing **`version`** in `package.json` from **`0.1.0`** to **`0.0.0`**, matching the `create-package` template and other unreleased packages. > > This is a metadata-only fix so the first automated release can version the package correctly after it was introduced with the wrong starting version in #9041. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 08e6c20. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
…ontroller (MetaMask#9436) ## Explanation The package was merged in MetaMask#9041 with an initial version of `0.1.0`. New packages should start at `0.0.0` (that's what the `create-package` template sets). This bumps it back down so the first release goes out correctly. ## References Follow up to MetaMask#9041 ## Checklist - [x] I've updated the test suite for new or updated code as appropriate - [x] I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate - [x] I've highlighted breaking changes using the "BREAKING" category above as appropriate - [x] I've prepared draft pull requests for cross repository changes as appropriate <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Only `package.json` version metadata changes; no runtime or API behavior is affected. > > **Overview** > Aligns `@metamask/network-connection-banner-controller` with the monorepo convention for new packages by changing **`version`** in `package.json` from **`0.1.0`** to **`0.0.0`**, matching the `create-package` template and other unreleased packages. > > This is a metadata-only fix so the first automated release can version the package correctly after it was introduced with the wrong starting version in MetaMask#9041. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 08e6c20. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
…ler:stateChange (MetaMask#9893) ## Explanation `NetworkConnectionBannerController` subscribes to `ClientController:stateChanged` using an event type it defines locally. `@metamask/client-controller` only declares and exports `ClientController:stateChange`. It works at runtime because `BaseController` publishes both names. This is a contract cleanup, not a behavior fix. Now the controller uses the exported `ClientControllerStateChangeEvent`, dropping the duplicated local type and matching the three other upstream subscriptions in the file. Breaking: clients must delegate `ClientController:stateChange` instead of `ClientController:stateChanged`, otherwise the controller stops receiving UI open state and the banner never shows. Both extension and mobile need the one line swap. ## References - Introduced in MetaMask#9041 ## Checklist - [x] I've updated the test suite for new or updated code as appropriate - [x] I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate - [x] I've communicated my changes to consumers by updating changelogs for packages I've changed - [x] I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them <!-- CURSOR_SUMMARY --> --- > [!NOTE] > <sup>[Cursor Bugbot](https://cursor.com/bugbot) is generating a summary for commit ec8b9d7. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
…ler (WPC-1014) (MetaMask#31225) ## **Description** Replaces the in-app duplicate of the RPC connection banner rule + 5s/30s timer escalation with the new `@metamask/network-connection-banner-controller`, registered in mobile's Engine. **Wiring** - `package.json` declares the controller as `@metamask/network-connection-banner-controller` with a Yarn `resolutions` alias to the preview tarball (`@metamask-previews/network-connection-banner-controller@0.1.0-preview-7507a11`). When the package ships under its real name, only the resolution line gets removed — no code changes. - New init + restricted-messenger files under `app/core/Engine/`; registered in `Engine.ts`, `messengers/index.ts`, and `types.ts`. - The preview package transitively resolves newer peer controllers than mobile installs (`network-controller@^32` vs `@31`, `connectivity-controller@^0.2` vs `@0.1`). Runtime shapes match (same event names + state fields); the type-only mismatch at the messenger boundary is bridged with one `as unknown as` cast in the messenger factory — removable once mobile bumps versions. **Cleanup** - Deletes `app/reducers/networkConnectionBanner/` and `app/actions/networkConnectionBanner/`. - Selector now reads from `engine.backgroundState.NetworkConnectionBannerController` and adapts to the existing UI's `{ visible: boolean }` shape, so the banner component is untouched. - `useNetworkConnectionBanner` hook drops the inline `checkNetworkStatus`, the two `setTimeout`s, the `rpcEndpointChainAvailable` subscription, and all `showNetworkConnectionBanner`/`hideNetworkConnectionBanner` dispatches. `switchToInfura` now calls `NetworkConnectionBannerController:switchToDefaultInfuraRpc`. Analytics + navigation unchanged. Core PR: MetaMask/core#9041 ## **Changelog** CHANGELOG entry: null ## **Related issues** Fixes: [WPC-1014](https://consensyssoftware.atlassian.net/browse/WPC-1014) ## **Manual testing steps** ```gherkin Feature: Banner driven by NetworkConnectionBannerController Scenario: single Infura blip during a wide outage Given all *.infura.io endpoints are unreachable When 30s elapse Then no banner appears (single registrable domain, suppressed) Scenario: custom RPC alone fails Given a custom RPC is configured and unreachable When 5s elapse Then the degraded banner appears for that custom network And tapping "Switch to MetaMask default RPC" switches to the chain's Infura endpoint and shows the success toast Scenario: two providers fail at once Given both an Infura network and an Alchemy network are unreachable When 5s elapse Then the degraded banner appears, escalating to unavailable at 30s Scenario: device offline Given the device is offline When any RPC times out Then no banner appears (existing offline short-circuit honored) ``` ## **Screenshots/Recordings** ### **Before** <!-- n/a — UX unchanged --> ### **After** <!-- n/a — UX unchanged --> ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### Performance checks (if applicable) - [ ] I've tested on Android - [ ] I've tested with a power user scenario - [ ] I've instrumented key operations with Sentry traces for production performance metrics ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. [WPC-1014]: https://consensyssoftware.atlassian.net/browse/WPC-1014?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Touches wallet home networking UX and RPC switching via a new controller; behavior should match core#9041 but regressions in show/hide rules or Infura switch are possible until manual QA. > > **Overview** > **Moves RPC connection banner logic out of Redux/UI into `@metamask/network-connection-banner-controller`**, wired through Engine init, a restricted messenger, and `NetworkConnectionBannerController:stateChanged` syncing into `engine.backgroundState`. > > The **local Redux slice and show/hide actions are removed**; selectors now expose `status` and `FailedNetwork` from controller state. **`useNetworkConnectionBanner` no longer** polls `NetworkController`, runs 5s/30s timers, or dispatches banner actions—**`switchToInfura` calls** `NetworkConnectionBannerController:switchToDefaultInfuraRpcEndpoint` via a **route messenger** on Wallet (`WalletWithMessenger` in `MainNavigator`). Banner UI reads `status` / `network` (e.g. `switchableInfuraNetworkClientId` instead of `infuraNetworkClientId`). **Engine** seeds and updates **`ClientController:setUiOpen`** on app foreground so the controller only evaluates while the UI is open. **`url-utils` / `ip-regex` / `psl` helpers used only for the old in-hook suppression logic are dropped** with their tests. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 2233f7c. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
…ler (WPC-1014) (MetaMask#43361) ## **Description** Replaces the in-app duplicate of the RPC connection banner rule + 5s/30s timer escalation with the new `@metamask/network-connection-banner-controller`, registered in `metamask-controller` alongside ConnectivityController. Core PR: MetaMask/core#9041 ## **Changelog** CHANGELOG entry: null ## **Related issues** Fixes: [WPC-1014](https://consensyssoftware.atlassian.net/browse/WPC-1014) ## **Manual testing steps** 1. Build the extension; confirm \`Engine.context.NetworkConnectionBannerController\` is reachable from the background. 2. Open DevTools → Network panel. Block all \`*.infura.io\` hosts; wait 30s. **No banner** (single registrable domain, suppressed). 3. Block \`*.infura.io\` AND a non-Infura host. **Degraded** banner at 5s, **unavailable** at 30s. 4. Configure a custom RPC for any chain and block only that host. **Degraded** banner appears; the \"Switch to MetaMask default RPC\" button switches the chain back to its Infura endpoint and shows the success toast. 5. Disable wifi. **No banner** — the existing offline short-circuit is honored by the controller. ## **Screenshots/Recordings** ### **Before** <!-- n/a — UX unchanged --> ### **After** <!-- n/a — UX unchanged --> ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. [WPC-1014]: https://consensyssoftware.atlassian.net/browse/WPC-1014?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Changes when and how RPC failure banners appear (logic now lives in an external controller) and alters network/RPC switching paths users see during outages; UX is intended to stay the same but regression risk is in timing and suppression rules. > > **Overview** > **Moves the “Still connecting” / “Unable to connect” RPC banner out of `AppStateController` and the UI** into `@metamask/network-connection-banner-controller`, which owns the show/hide rules, 5s/30s escalation, and subscriptions to network, enablement, connectivity, client, and keyring state. > > **Background:** Registers the controller in modular init (`NetworkConnectionBannerControllerInit`, restricted messenger to `NetworkController`, `NetworkEnablementController`, `ConnectivityController`, etc.), adds it to `metamask-controller`, and drops `networkConnectionBanner` / `updateNetworkConnectionBanner` from `AppStateController`. Redux/background types now expose `networkConnectionBannerStatus` and `networkConnectionBannerNetwork` from the new controller. > > **UI:** `useNetworkConnectionBanner` reads controller selectors instead of running timers and dispatching banner updates; switching to the default Infura RPC goes through `NetworkConnectionBannerController:switchToDefaultInfuraRpcEndpoint` via a Home route messenger. The banner component uses the controller’s `FailedNetwork` shape (`switchableInfuraNetworkClientId` vs `infuraEndpointIndex`). Removed the multichain selector `selectFirstFailedNetworkForNetworkConnectionBanner`, `getDomain`/`psl`, and `updateNetworkConnectionBanner` actions. > > **Deps/policy:** Adds `@metamask/network-connection-banner-controller`, bumps `@metamask/connectivity-controller`, and updates LavaMoat policies accordingly. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit d3d88dc. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: MetaMask Bot <metamaskbot@users.noreply.github.com>
Explanation
The RPC connection banner ("Still connecting" / "Unable to connect") is currently rendered by both
metamask-extensionandmetamask-mobilebased on duplicated logic in each repo:pslso a single provider's wide outage doesn't pop the banner.{ chainId, networkName, rpcUrl, isInfuraEndpoint, infuraNetworkClientId }payload shape.This PR introduces
@metamask/network-connection-banner-controllerso there's a single source of truth. The controller:NetworkController:stateChanged,NetworkEnablementController:stateChanged, andConnectivityController:stateChangedvia the messenger.{ status: 'available' | 'degraded' | 'unavailable', network: FailedNetwork | null }that clients subscribe to.dismissBanner()andswitchToDefaultInfuraRpc({ chainId })— the latter looks up the chain's first Infura endpoint and callsNetworkController:updateNetworkwithreplacementSelectedRpcEndpointIndex.psl1.15 ships itstypesfield outside itsexportsmap so TypeScript'snode16module resolution can't find it; a small ambientsrc/psl.d.tsshim mirrors the workaround already in use in extension and mobile.ip-regexis pinned to^4.3.0(last CJS release) because the monorepo's Jest config doesn't transform ESM-only deps.References
Checklist
Note
Medium Risk
The controller can change active RPC and default endpoints via NetworkController, which affects wallet connectivity; behavior is complex but heavily tested and gated on UI/unlock lifecycle.
Overview
Adds
@metamask/network-connection-banner-controlleras the shared source of truth for the RPC connection banner, replacing duplicated extension/mobile logic.NetworkConnectionBannerControllerwatches enabled EVM networks’ default RPC health (viaNetworkController,NetworkEnablementController, andConnectivityController), applies a 5s → 30s escalation todegraded/unavailable, and exposes ephemeral UI state (networkConnectionBannerStatus,networkConnectionBannerNetwork). The banner is shown when a custom RPC is failing or every enabled network is down; isolated Infura blips among healthy peers are suppressed. Evaluation runs only while the UI is open and the wallet is unlocked (ClientController,KeyringController).Messenger actions
dismissBannerandswitchToDefaultInfuraRpcEndpointlet users hide the banner or switch the active connection to Infura and update the chain’s default RPC (active network first, thenupdateNetwork).getIsInfuraEndpointclassifies MetaMask Infura URLs for that flow.Monorepo wiring includes CODEOWNERS, README dependency graph,
teams.json, and tsconfig references. A roottypes/psl.d.tsshim supports TypeScript resolution forpsl.Reviewed by Cursor Bugbot for commit 3d3018f. Bugbot is set up for automated code reviews on this repo. Configure here.