@metamask-previews/network-connection-banner-controller 0.1.0-preview-68794c0ff → 0.1.0-preview-c4108f3eb

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/README.md +9 -16
  2. package/dist/NetworkConnectionBannerController-method-action-types.cjs.map +1 -1
  3. package/dist/NetworkConnectionBannerController-method-action-types.d.cts +4 -26
  4. package/dist/NetworkConnectionBannerController-method-action-types.d.cts.map +1 -1
  5. package/dist/NetworkConnectionBannerController-method-action-types.d.mts +4 -26
  6. package/dist/NetworkConnectionBannerController-method-action-types.d.mts.map +1 -1
  7. package/dist/NetworkConnectionBannerController-method-action-types.mjs.map +1 -1
  8. package/dist/NetworkConnectionBannerController.cjs +72 -49
  9. package/dist/NetworkConnectionBannerController.cjs.map +1 -1
  10. package/dist/NetworkConnectionBannerController.d.cts +24 -23
  11. package/dist/NetworkConnectionBannerController.d.cts.map +1 -1
  12. package/dist/NetworkConnectionBannerController.d.mts +24 -23
  13. package/dist/NetworkConnectionBannerController.d.mts.map +1 -1
  14. package/dist/NetworkConnectionBannerController.mjs +72 -49
  15. package/dist/NetworkConnectionBannerController.mjs.map +1 -1
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +1 -1
  18. package/dist/index.d.cts.map +1 -1
  19. package/dist/index.d.mts +1 -1
  20. package/dist/index.d.mts.map +1 -1
  21. package/dist/index.mjs.map +1 -1
  22. package/dist/url-utils.cjs +16 -4
  23. package/dist/url-utils.cjs.map +1 -1
  24. package/dist/url-utils.d.cts +6 -3
  25. package/dist/url-utils.d.cts.map +1 -1
  26. package/dist/url-utils.d.mts +6 -3
  27. package/dist/url-utils.d.mts.map +1 -1
  28. package/dist/url-utils.mjs +16 -4
  29. package/dist/url-utils.mjs.map +1 -1
  30. package/package.json +3 -1
package/README.md CHANGED
@@ -8,22 +8,15 @@ the 5s/30s timer state machine.
8
8
 
9
9
  The controller stays dormant after construction so the 5s / 30s escalation
10
10
  timers do not run before a user is actually looking at the wallet (e.g. while
11
- the app is still on the lock screen). The UI that renders the banner is
12
- responsible for driving the lifecycle:
13
-
14
- ```typescript
15
- // When the wallet UI that shows the banner becomes active
16
- // (e.g. the home surface mounts after unlock):
17
- networkConnectionBannerController.start();
18
-
19
- // When it goes away:
20
- networkConnectionBannerController.stop();
21
- ```
22
-
23
- Both methods are idempotent. `start` runs the initial evaluation immediately
24
- and enables reactions to upstream state changes. `stop` cancels any pending
25
- timers and resets the banner state to `available`. Upstream state changes are
26
- ignored while stopped.
11
+ the app is still on the lock screen). It manages its own lifecycle by
12
+ subscribing to `ClientController:stateChanged` and
13
+ `KeyringController:unlock` / `KeyringController:lock`: evaluation runs only
14
+ while the client UI is open on an unlocked wallet. When either condition
15
+ stops holding, pending timers are cancelled and the banner state resets to
16
+ `available`; upstream state changes are ignored until both hold again.
17
+
18
+ Clients need no lifecycle wiring beyond keeping `ClientController`'s
19
+ `isUiOpen` up to date (via `ClientController:setUiOpen`).
27
20
 
28
21
  ## Installation
29
22
 
@@ -1 +1 @@
1
- {"version":3,"file":"NetworkConnectionBannerController-method-action-types.cjs","sourceRoot":"","sources":["../src/NetworkConnectionBannerController-method-action-types.ts"],"names":[],"mappings":";AAAA;;;GAGG","sourcesContent":["/**\n * This file is auto generated.\n * Do not edit manually.\n */\n\nimport type { NetworkConnectionBannerController } from './NetworkConnectionBannerController';\n\n/**\n * Look for a failed network, if any, and populate the initial state of the\n * banner. Reacts to upstream state changes from this point on.\n *\n * Call this when the wallet UI that consumes the banner becomes active\n * (typically when the wallet is unlocked and the home surface mounts) so\n * timers do not run while the user is not looking at the wallet. Should\n * be called after `NetworkController`, `NetworkEnablementController`, and\n * `ConnectivityController` have been initialized. Idempotent.\n */\nexport type NetworkConnectionBannerControllerStartAction = {\n type: `NetworkConnectionBannerController:start`;\n handler: NetworkConnectionBannerController['start'];\n};\n\n/**\n * Stops evaluating network connection state. Clears any pending banner\n * timers and resets state to `available`. Call this when the UI\n * consuming the banner is no longer active. Idempotent.\n */\nexport type NetworkConnectionBannerControllerStopAction = {\n type: `NetworkConnectionBannerController:stop`;\n handler: NetworkConnectionBannerController['stop'];\n};\n\n/**\n * Clears the banner state such that the banner will be hidden.\n */\nexport type NetworkConnectionBannerControllerDismissBannerAction = {\n type: `NetworkConnectionBannerController:dismissBanner`;\n handler: NetworkConnectionBannerController['dismissBanner'];\n};\n\n/**\n * Switches the chain's default RPC endpoint to its Infura endpoint,\n * causing the banner to clear once the network becomes available again.\n *\n * @param chainId - The chain whose default RPC endpoint should be switched.\n * @throws If the chain configuration cannot be found, or if it has no\n * Infura endpoint to switch to, or if the default is already Infura.\n */\nexport type NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcEndpointAction =\n {\n type: `NetworkConnectionBannerController:switchToDefaultInfuraRpcEndpoint`;\n handler: NetworkConnectionBannerController['switchToDefaultInfuraRpcEndpoint'];\n };\n\n/**\n * Union of all NetworkConnectionBannerController action types.\n */\nexport type NetworkConnectionBannerControllerMethodActions =\n | NetworkConnectionBannerControllerStartAction\n | NetworkConnectionBannerControllerStopAction\n | NetworkConnectionBannerControllerDismissBannerAction\n | NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcEndpointAction;\n"]}
1
+ {"version":3,"file":"NetworkConnectionBannerController-method-action-types.cjs","sourceRoot":"","sources":["../src/NetworkConnectionBannerController-method-action-types.ts"],"names":[],"mappings":";AAAA;;;GAGG","sourcesContent":["/**\n * This file is auto generated.\n * Do not edit manually.\n */\n\nimport type { NetworkConnectionBannerController } from './NetworkConnectionBannerController';\n\n/**\n * Clears the banner state such that the banner will be hidden.\n */\nexport type NetworkConnectionBannerControllerDismissBannerAction = {\n type: `NetworkConnectionBannerController:dismissBanner`;\n handler: NetworkConnectionBannerController['dismissBanner'];\n};\n\n/**\n * Switches the chain's default RPC endpoint to its Infura endpoint and\n * makes it the active network, causing the banner to clear once the\n * network becomes available again.\n *\n * @param chainId - The chain whose default RPC endpoint should be switched.\n * @throws If the chain configuration cannot be found, or if it has no\n * Infura endpoint to switch to, or if the default is already Infura.\n */\nexport type NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcEndpointAction =\n {\n type: `NetworkConnectionBannerController:switchToDefaultInfuraRpcEndpoint`;\n handler: NetworkConnectionBannerController['switchToDefaultInfuraRpcEndpoint'];\n };\n\n/**\n * Union of all NetworkConnectionBannerController action types.\n */\nexport type NetworkConnectionBannerControllerMethodActions =\n | NetworkConnectionBannerControllerDismissBannerAction\n | NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcEndpointAction;\n"]}
@@ -3,29 +3,6 @@
3
3
  * Do not edit manually.
4
4
  */
5
5
  import type { NetworkConnectionBannerController } from "./NetworkConnectionBannerController.cjs";
6
- /**
7
- * Look for a failed network, if any, and populate the initial state of the
8
- * banner. Reacts to upstream state changes from this point on.
9
- *
10
- * Call this when the wallet UI that consumes the banner becomes active
11
- * (typically when the wallet is unlocked and the home surface mounts) so
12
- * timers do not run while the user is not looking at the wallet. Should
13
- * be called after `NetworkController`, `NetworkEnablementController`, and
14
- * `ConnectivityController` have been initialized. Idempotent.
15
- */
16
- export type NetworkConnectionBannerControllerStartAction = {
17
- type: `NetworkConnectionBannerController:start`;
18
- handler: NetworkConnectionBannerController['start'];
19
- };
20
- /**
21
- * Stops evaluating network connection state. Clears any pending banner
22
- * timers and resets state to `available`. Call this when the UI
23
- * consuming the banner is no longer active. Idempotent.
24
- */
25
- export type NetworkConnectionBannerControllerStopAction = {
26
- type: `NetworkConnectionBannerController:stop`;
27
- handler: NetworkConnectionBannerController['stop'];
28
- };
29
6
  /**
30
7
  * Clears the banner state such that the banner will be hidden.
31
8
  */
@@ -34,8 +11,9 @@ export type NetworkConnectionBannerControllerDismissBannerAction = {
34
11
  handler: NetworkConnectionBannerController['dismissBanner'];
35
12
  };
36
13
  /**
37
- * Switches the chain's default RPC endpoint to its Infura endpoint,
38
- * causing the banner to clear once the network becomes available again.
14
+ * Switches the chain's default RPC endpoint to its Infura endpoint and
15
+ * makes it the active network, causing the banner to clear once the
16
+ * network becomes available again.
39
17
  *
40
18
  * @param chainId - The chain whose default RPC endpoint should be switched.
41
19
  * @throws If the chain configuration cannot be found, or if it has no
@@ -48,5 +26,5 @@ export type NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcEndpointAct
48
26
  /**
49
27
  * Union of all NetworkConnectionBannerController action types.
50
28
  */
51
- export type NetworkConnectionBannerControllerMethodActions = NetworkConnectionBannerControllerStartAction | NetworkConnectionBannerControllerStopAction | NetworkConnectionBannerControllerDismissBannerAction | NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcEndpointAction;
29
+ export type NetworkConnectionBannerControllerMethodActions = NetworkConnectionBannerControllerDismissBannerAction | NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcEndpointAction;
52
30
  //# sourceMappingURL=NetworkConnectionBannerController-method-action-types.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"NetworkConnectionBannerController-method-action-types.d.cts","sourceRoot":"","sources":["../src/NetworkConnectionBannerController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,iCAAiC,EAAE,gDAA4C;AAE7F;;;;;;;;;GASG;AACH,MAAM,MAAM,4CAA4C,GAAG;IACzD,IAAI,EAAE,yCAAyC,CAAC;IAChD,OAAO,EAAE,iCAAiC,CAAC,OAAO,CAAC,CAAC;CACrD,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,2CAA2C,GAAG;IACxD,IAAI,EAAE,wCAAwC,CAAC;IAC/C,OAAO,EAAE,iCAAiC,CAAC,MAAM,CAAC,CAAC;CACpD,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,oDAAoD,GAAG;IACjE,IAAI,EAAE,iDAAiD,CAAC;IACxD,OAAO,EAAE,iCAAiC,CAAC,eAAe,CAAC,CAAC;CAC7D,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,uEAAuE,GACjF;IACE,IAAI,EAAE,oEAAoE,CAAC;IAC3E,OAAO,EAAE,iCAAiC,CAAC,kCAAkC,CAAC,CAAC;CAChF,CAAC;AAEJ;;GAEG;AACH,MAAM,MAAM,8CAA8C,GACtD,4CAA4C,GAC5C,2CAA2C,GAC3C,oDAAoD,GACpD,uEAAuE,CAAC"}
1
+ {"version":3,"file":"NetworkConnectionBannerController-method-action-types.d.cts","sourceRoot":"","sources":["../src/NetworkConnectionBannerController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,iCAAiC,EAAE,gDAA4C;AAE7F;;GAEG;AACH,MAAM,MAAM,oDAAoD,GAAG;IACjE,IAAI,EAAE,iDAAiD,CAAC;IACxD,OAAO,EAAE,iCAAiC,CAAC,eAAe,CAAC,CAAC;CAC7D,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,MAAM,uEAAuE,GACjF;IACE,IAAI,EAAE,oEAAoE,CAAC;IAC3E,OAAO,EAAE,iCAAiC,CAAC,kCAAkC,CAAC,CAAC;CAChF,CAAC;AAEJ;;GAEG;AACH,MAAM,MAAM,8CAA8C,GACtD,oDAAoD,GACpD,uEAAuE,CAAC"}
@@ -3,29 +3,6 @@
3
3
  * Do not edit manually.
4
4
  */
5
5
  import type { NetworkConnectionBannerController } from "./NetworkConnectionBannerController.mjs";
6
- /**
7
- * Look for a failed network, if any, and populate the initial state of the
8
- * banner. Reacts to upstream state changes from this point on.
9
- *
10
- * Call this when the wallet UI that consumes the banner becomes active
11
- * (typically when the wallet is unlocked and the home surface mounts) so
12
- * timers do not run while the user is not looking at the wallet. Should
13
- * be called after `NetworkController`, `NetworkEnablementController`, and
14
- * `ConnectivityController` have been initialized. Idempotent.
15
- */
16
- export type NetworkConnectionBannerControllerStartAction = {
17
- type: `NetworkConnectionBannerController:start`;
18
- handler: NetworkConnectionBannerController['start'];
19
- };
20
- /**
21
- * Stops evaluating network connection state. Clears any pending banner
22
- * timers and resets state to `available`. Call this when the UI
23
- * consuming the banner is no longer active. Idempotent.
24
- */
25
- export type NetworkConnectionBannerControllerStopAction = {
26
- type: `NetworkConnectionBannerController:stop`;
27
- handler: NetworkConnectionBannerController['stop'];
28
- };
29
6
  /**
30
7
  * Clears the banner state such that the banner will be hidden.
31
8
  */
@@ -34,8 +11,9 @@ export type NetworkConnectionBannerControllerDismissBannerAction = {
34
11
  handler: NetworkConnectionBannerController['dismissBanner'];
35
12
  };
36
13
  /**
37
- * Switches the chain's default RPC endpoint to its Infura endpoint,
38
- * causing the banner to clear once the network becomes available again.
14
+ * Switches the chain's default RPC endpoint to its Infura endpoint and
15
+ * makes it the active network, causing the banner to clear once the
16
+ * network becomes available again.
39
17
  *
40
18
  * @param chainId - The chain whose default RPC endpoint should be switched.
41
19
  * @throws If the chain configuration cannot be found, or if it has no
@@ -48,5 +26,5 @@ export type NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcEndpointAct
48
26
  /**
49
27
  * Union of all NetworkConnectionBannerController action types.
50
28
  */
51
- export type NetworkConnectionBannerControllerMethodActions = NetworkConnectionBannerControllerStartAction | NetworkConnectionBannerControllerStopAction | NetworkConnectionBannerControllerDismissBannerAction | NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcEndpointAction;
29
+ export type NetworkConnectionBannerControllerMethodActions = NetworkConnectionBannerControllerDismissBannerAction | NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcEndpointAction;
52
30
  //# sourceMappingURL=NetworkConnectionBannerController-method-action-types.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"NetworkConnectionBannerController-method-action-types.d.mts","sourceRoot":"","sources":["../src/NetworkConnectionBannerController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,iCAAiC,EAAE,gDAA4C;AAE7F;;;;;;;;;GASG;AACH,MAAM,MAAM,4CAA4C,GAAG;IACzD,IAAI,EAAE,yCAAyC,CAAC;IAChD,OAAO,EAAE,iCAAiC,CAAC,OAAO,CAAC,CAAC;CACrD,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,2CAA2C,GAAG;IACxD,IAAI,EAAE,wCAAwC,CAAC;IAC/C,OAAO,EAAE,iCAAiC,CAAC,MAAM,CAAC,CAAC;CACpD,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,oDAAoD,GAAG;IACjE,IAAI,EAAE,iDAAiD,CAAC;IACxD,OAAO,EAAE,iCAAiC,CAAC,eAAe,CAAC,CAAC;CAC7D,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,uEAAuE,GACjF;IACE,IAAI,EAAE,oEAAoE,CAAC;IAC3E,OAAO,EAAE,iCAAiC,CAAC,kCAAkC,CAAC,CAAC;CAChF,CAAC;AAEJ;;GAEG;AACH,MAAM,MAAM,8CAA8C,GACtD,4CAA4C,GAC5C,2CAA2C,GAC3C,oDAAoD,GACpD,uEAAuE,CAAC"}
1
+ {"version":3,"file":"NetworkConnectionBannerController-method-action-types.d.mts","sourceRoot":"","sources":["../src/NetworkConnectionBannerController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,iCAAiC,EAAE,gDAA4C;AAE7F;;GAEG;AACH,MAAM,MAAM,oDAAoD,GAAG;IACjE,IAAI,EAAE,iDAAiD,CAAC;IACxD,OAAO,EAAE,iCAAiC,CAAC,eAAe,CAAC,CAAC;CAC7D,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,MAAM,uEAAuE,GACjF;IACE,IAAI,EAAE,oEAAoE,CAAC;IAC3E,OAAO,EAAE,iCAAiC,CAAC,kCAAkC,CAAC,CAAC;CAChF,CAAC;AAEJ;;GAEG;AACH,MAAM,MAAM,8CAA8C,GACtD,oDAAoD,GACpD,uEAAuE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"NetworkConnectionBannerController-method-action-types.mjs","sourceRoot":"","sources":["../src/NetworkConnectionBannerController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/**\n * This file is auto generated.\n * Do not edit manually.\n */\n\nimport type { NetworkConnectionBannerController } from './NetworkConnectionBannerController';\n\n/**\n * Look for a failed network, if any, and populate the initial state of the\n * banner. Reacts to upstream state changes from this point on.\n *\n * Call this when the wallet UI that consumes the banner becomes active\n * (typically when the wallet is unlocked and the home surface mounts) so\n * timers do not run while the user is not looking at the wallet. Should\n * be called after `NetworkController`, `NetworkEnablementController`, and\n * `ConnectivityController` have been initialized. Idempotent.\n */\nexport type NetworkConnectionBannerControllerStartAction = {\n type: `NetworkConnectionBannerController:start`;\n handler: NetworkConnectionBannerController['start'];\n};\n\n/**\n * Stops evaluating network connection state. Clears any pending banner\n * timers and resets state to `available`. Call this when the UI\n * consuming the banner is no longer active. Idempotent.\n */\nexport type NetworkConnectionBannerControllerStopAction = {\n type: `NetworkConnectionBannerController:stop`;\n handler: NetworkConnectionBannerController['stop'];\n};\n\n/**\n * Clears the banner state such that the banner will be hidden.\n */\nexport type NetworkConnectionBannerControllerDismissBannerAction = {\n type: `NetworkConnectionBannerController:dismissBanner`;\n handler: NetworkConnectionBannerController['dismissBanner'];\n};\n\n/**\n * Switches the chain's default RPC endpoint to its Infura endpoint,\n * causing the banner to clear once the network becomes available again.\n *\n * @param chainId - The chain whose default RPC endpoint should be switched.\n * @throws If the chain configuration cannot be found, or if it has no\n * Infura endpoint to switch to, or if the default is already Infura.\n */\nexport type NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcEndpointAction =\n {\n type: `NetworkConnectionBannerController:switchToDefaultInfuraRpcEndpoint`;\n handler: NetworkConnectionBannerController['switchToDefaultInfuraRpcEndpoint'];\n };\n\n/**\n * Union of all NetworkConnectionBannerController action types.\n */\nexport type NetworkConnectionBannerControllerMethodActions =\n | NetworkConnectionBannerControllerStartAction\n | NetworkConnectionBannerControllerStopAction\n | NetworkConnectionBannerControllerDismissBannerAction\n | NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcEndpointAction;\n"]}
1
+ {"version":3,"file":"NetworkConnectionBannerController-method-action-types.mjs","sourceRoot":"","sources":["../src/NetworkConnectionBannerController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/**\n * This file is auto generated.\n * Do not edit manually.\n */\n\nimport type { NetworkConnectionBannerController } from './NetworkConnectionBannerController';\n\n/**\n * Clears the banner state such that the banner will be hidden.\n */\nexport type NetworkConnectionBannerControllerDismissBannerAction = {\n type: `NetworkConnectionBannerController:dismissBanner`;\n handler: NetworkConnectionBannerController['dismissBanner'];\n};\n\n/**\n * Switches the chain's default RPC endpoint to its Infura endpoint and\n * makes it the active network, causing the banner to clear once the\n * network becomes available again.\n *\n * @param chainId - The chain whose default RPC endpoint should be switched.\n * @throws If the chain configuration cannot be found, or if it has no\n * Infura endpoint to switch to, or if the default is already Infura.\n */\nexport type NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcEndpointAction =\n {\n type: `NetworkConnectionBannerController:switchToDefaultInfuraRpcEndpoint`;\n handler: NetworkConnectionBannerController['switchToDefaultInfuraRpcEndpoint'];\n };\n\n/**\n * Union of all NetworkConnectionBannerController action types.\n */\nexport type NetworkConnectionBannerControllerMethodActions =\n | NetworkConnectionBannerControllerDismissBannerAction\n | NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcEndpointAction;\n"]}
@@ -1,19 +1,20 @@
1
1
  "use strict";
2
- var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
3
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
4
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
5
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
6
- };
7
2
  var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
8
3
  if (kind === "m") throw new TypeError("Private method is not writable");
9
4
  if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
10
5
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
11
6
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
12
7
  };
13
- var _NetworkConnectionBannerController_instances, _NetworkConnectionBannerController_degradedTimer, _NetworkConnectionBannerController_unavailableTimer, _NetworkConnectionBannerController_pendingNetworkClientId, _NetworkConnectionBannerController_isStarted, _NetworkConnectionBannerController_refreshState, _NetworkConnectionBannerController_resetBanner, _NetworkConnectionBannerController_clearTimers, _NetworkConnectionBannerController_findFailedNetwork, _NetworkConnectionBannerController_getEnabledEvmChainIds, _NetworkConnectionBannerController_collectEnabledNetworks, _NetworkConnectionBannerController_buildFailedNetwork, _NetworkConnectionBannerController_pickFailedNetworkToDisplay;
8
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
10
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
+ };
13
+ var _NetworkConnectionBannerController_instances, _NetworkConnectionBannerController_degradedTimer, _NetworkConnectionBannerController_unavailableTimer, _NetworkConnectionBannerController_pendingNetworkClientId, _NetworkConnectionBannerController_isStarted, _NetworkConnectionBannerController_isUiOpen, _NetworkConnectionBannerController_isUnlocked, _NetworkConnectionBannerController_infuraProjectId, _NetworkConnectionBannerController_updateLifecycle, _NetworkConnectionBannerController_start, _NetworkConnectionBannerController_stop, _NetworkConnectionBannerController_refreshState, _NetworkConnectionBannerController_resetBanner, _NetworkConnectionBannerController_clearTimers, _NetworkConnectionBannerController_findFailedNetwork, _NetworkConnectionBannerController_getEnabledEvmChainIds, _NetworkConnectionBannerController_collectEnabledNetworks, _NetworkConnectionBannerController_buildFailedNetwork, _NetworkConnectionBannerController_pickFailedNetworkToDisplay;
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.NetworkConnectionBannerController = exports.getDefaultNetworkConnectionBannerControllerState = void 0;
16
16
  const base_controller_1 = require("@metamask/base-controller");
17
+ const client_controller_1 = require("@metamask/client-controller");
17
18
  const connectivity_controller_1 = require("@metamask/connectivity-controller");
18
19
  const network_controller_1 = require("@metamask/network-controller");
19
20
  const network_enablement_controller_1 = require("@metamask/network-enablement-controller");
@@ -107,8 +108,6 @@ const DEGRADED_BANNER_TIMEOUT = 5000;
107
108
  */
108
109
  const UNAVAILABLE_BANNER_TIMEOUT = 30000;
109
110
  const MESSENGER_EXPOSED_METHODS = [
110
- 'start',
111
- 'stop',
112
111
  'dismissBanner',
113
112
  'switchToDefaultInfuraRpcEndpoint',
114
113
  ];
@@ -129,6 +128,9 @@ const MESSENGER_EXPOSED_METHODS = [
129
128
  * failure is present, it's surfaced first so the "Switch to MetaMask default
130
129
  * RPC" CTA targets the network the user can act on.
131
130
  *
131
+ * The controller manages its own lifecycle: it evaluates RPC health (and
132
+ * runs the escalation timers) only while the client UI is open
133
+ * (`ClientController`) and the wallet is unlocked (`KeyringController`).
132
134
  * Clients only need to render the banner from the controller's state and wire
133
135
  * click handlers to {@link dismissBanner} or {@link switchToDefaultInfuraRpcEndpoint}.
134
136
  */
@@ -138,8 +140,9 @@ class NetworkConnectionBannerController extends base_controller_1.BaseController
138
140
  *
139
141
  * @param args - The arguments to this controller.
140
142
  * @param args.messenger - The messenger suited for this controller.
143
+ * @param args.infuraProjectId - The wallet's Infura project id.
141
144
  */
142
- constructor({ messenger }) {
145
+ constructor({ messenger, infuraProjectId, }) {
143
146
  super({
144
147
  messenger,
145
148
  metadata: networkConnectionBannerControllerMetadata,
@@ -151,6 +154,12 @@ class NetworkConnectionBannerController extends base_controller_1.BaseController
151
154
  _NetworkConnectionBannerController_unavailableTimer.set(this, void 0);
152
155
  _NetworkConnectionBannerController_pendingNetworkClientId.set(this, void 0);
153
156
  _NetworkConnectionBannerController_isStarted.set(this, false);
157
+ /** Whether the client UI is open. Combined with {@link #isUnlocked}. */
158
+ _NetworkConnectionBannerController_isUiOpen.set(this, false);
159
+ /** Whether the keyring is unlocked. Combined with {@link #isUiOpen}. */
160
+ _NetworkConnectionBannerController_isUnlocked.set(this, false);
161
+ _NetworkConnectionBannerController_infuraProjectId.set(this, void 0);
162
+ __classPrivateFieldSet(this, _NetworkConnectionBannerController_infuraProjectId, infuraProjectId, "f");
154
163
  // Upstream controllers still expose :stateChange; switch to :stateChanged
155
164
  // once those packages migrate their event types.
156
165
  /* eslint-disable no-restricted-syntax -- awaiting upstream :stateChanged migration */
@@ -170,40 +179,21 @@ class NetworkConnectionBannerController extends base_controller_1.BaseController
170
179
  connectivityControllerState,
171
180
  }), selectConnectivityControllerFields);
172
181
  /* eslint-enable no-restricted-syntax */
173
- this.messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
174
- }
175
- /**
176
- * Look for a failed network, if any, and populate the initial state of the
177
- * banner. Reacts to upstream state changes from this point on.
178
- *
179
- * Call this when the wallet UI that consumes the banner becomes active
180
- * (typically when the wallet is unlocked and the home surface mounts) so
181
- * timers do not run while the user is not looking at the wallet. Should
182
- * be called after `NetworkController`, `NetworkEnablementController`, and
183
- * `ConnectivityController` have been initialized. Idempotent.
184
- */
185
- start() {
186
- if (__classPrivateFieldGet(this, _NetworkConnectionBannerController_isStarted, "f")) {
187
- return;
188
- }
189
- __classPrivateFieldSet(this, _NetworkConnectionBannerController_isStarted, true, "f");
190
- __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_refreshState).call(this, {
191
- networkControllerState: this.messenger.call('NetworkController:getState'),
192
- networkEnablementControllerState: this.messenger.call('NetworkEnablementController:getState'),
193
- connectivityControllerState: this.messenger.call('ConnectivityController:getState'),
182
+ // Lifecycle: evaluate RPC health (and run the banner escalation timers)
183
+ // only while the client UI is open on an unlocked wallet.
184
+ this.messenger.subscribe('ClientController:stateChanged', (isUiOpen) => {
185
+ __classPrivateFieldSet(this, _NetworkConnectionBannerController_isUiOpen, isUiOpen, "f");
186
+ __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_updateLifecycle).call(this);
187
+ }, client_controller_1.clientControllerSelectors.selectIsUiOpen);
188
+ this.messenger.subscribe('KeyringController:unlock', () => {
189
+ __classPrivateFieldSet(this, _NetworkConnectionBannerController_isUnlocked, true, "f");
190
+ __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_updateLifecycle).call(this);
194
191
  });
195
- }
196
- /**
197
- * Stops evaluating network connection state. Clears any pending banner
198
- * timers and resets state to `available`. Call this when the UI
199
- * consuming the banner is no longer active. Idempotent.
200
- */
201
- stop() {
202
- if (!__classPrivateFieldGet(this, _NetworkConnectionBannerController_isStarted, "f")) {
203
- return;
204
- }
205
- __classPrivateFieldSet(this, _NetworkConnectionBannerController_isStarted, false, "f");
206
- __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_resetBanner).call(this);
192
+ this.messenger.subscribe('KeyringController:lock', () => {
193
+ __classPrivateFieldSet(this, _NetworkConnectionBannerController_isUnlocked, false, "f");
194
+ __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_updateLifecycle).call(this);
195
+ });
196
+ this.messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
207
197
  }
208
198
  /**
209
199
  * Clears the banner state such that the banner will be hidden.
@@ -212,8 +202,9 @@ class NetworkConnectionBannerController extends base_controller_1.BaseController
212
202
  __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_resetBanner).call(this);
213
203
  }
214
204
  /**
215
- * Switches the chain's default RPC endpoint to its Infura endpoint,
216
- * causing the banner to clear once the network becomes available again.
205
+ * Switches the chain's default RPC endpoint to its Infura endpoint and
206
+ * makes it the active network, causing the banner to clear once the
207
+ * network becomes available again.
217
208
  *
218
209
  * @param chainId - The chain whose default RPC endpoint should be switched.
219
210
  * @throws If the chain configuration cannot be found, or if it has no
@@ -224,7 +215,7 @@ class NetworkConnectionBannerController extends base_controller_1.BaseController
224
215
  if (!networkConfiguration) {
225
216
  throw new Error(`No network configuration found for chain ID "${chainId}".`);
226
217
  }
227
- const infuraEndpointIndex = networkConfiguration.rpcEndpoints.findIndex((endpoint) => (0, url_utils_1.getIsInfuraEndpoint)(endpoint.url));
218
+ const infuraEndpointIndex = networkConfiguration.rpcEndpoints.findIndex((endpoint) => (0, url_utils_1.getIsInfuraEndpoint)(endpoint.url, __classPrivateFieldGet(this, _NetworkConnectionBannerController_infuraProjectId, "f")));
228
219
  if (infuraEndpointIndex === -1) {
229
220
  throw new Error(`No Infura endpoint available for chain ID "${chainId}".`);
230
221
  }
@@ -232,14 +223,46 @@ class NetworkConnectionBannerController extends base_controller_1.BaseController
232
223
  // The default is already Infura; nothing to do.
233
224
  return;
234
225
  }
226
+ // Move the active connection onto the Infura endpoint first, then make
227
+ // it the default. In this order a partial failure leaves the failing
228
+ // default in place and the banner visible, rather than hiding the banner
229
+ // while the wallet is still connected to the broken endpoint.
230
+ const infuraNetworkClientId = networkConfiguration.rpcEndpoints[infuraEndpointIndex].networkClientId;
231
+ const { selectedNetworkClientId } = this.messenger.call('NetworkController:getState');
232
+ if (infuraNetworkClientId !== selectedNetworkClientId) {
233
+ await this.messenger.call('NetworkController:setActiveNetwork', infuraNetworkClientId);
234
+ }
235
235
  await this.messenger.call('NetworkController:updateNetwork', chainId, {
236
236
  ...networkConfiguration,
237
237
  defaultRpcEndpointIndex: infuraEndpointIndex,
238
- }, { replacementSelectedRpcEndpointIndex: infuraEndpointIndex });
238
+ });
239
239
  }
240
240
  }
241
241
  exports.NetworkConnectionBannerController = NetworkConnectionBannerController;
242
- _NetworkConnectionBannerController_degradedTimer = new WeakMap(), _NetworkConnectionBannerController_unavailableTimer = new WeakMap(), _NetworkConnectionBannerController_pendingNetworkClientId = new WeakMap(), _NetworkConnectionBannerController_isStarted = new WeakMap(), _NetworkConnectionBannerController_instances = new WeakSet(), _NetworkConnectionBannerController_refreshState = function _NetworkConnectionBannerController_refreshState({ networkControllerState, networkEnablementControllerState, connectivityControllerState, }) {
242
+ _NetworkConnectionBannerController_degradedTimer = new WeakMap(), _NetworkConnectionBannerController_unavailableTimer = new WeakMap(), _NetworkConnectionBannerController_pendingNetworkClientId = new WeakMap(), _NetworkConnectionBannerController_isStarted = new WeakMap(), _NetworkConnectionBannerController_isUiOpen = new WeakMap(), _NetworkConnectionBannerController_isUnlocked = new WeakMap(), _NetworkConnectionBannerController_infuraProjectId = new WeakMap(), _NetworkConnectionBannerController_instances = new WeakSet(), _NetworkConnectionBannerController_updateLifecycle = function _NetworkConnectionBannerController_updateLifecycle() {
243
+ if (__classPrivateFieldGet(this, _NetworkConnectionBannerController_isUiOpen, "f") && __classPrivateFieldGet(this, _NetworkConnectionBannerController_isUnlocked, "f")) {
244
+ __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_start).call(this);
245
+ }
246
+ else {
247
+ __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_stop).call(this);
248
+ }
249
+ }, _NetworkConnectionBannerController_start = function _NetworkConnectionBannerController_start() {
250
+ if (__classPrivateFieldGet(this, _NetworkConnectionBannerController_isStarted, "f")) {
251
+ return;
252
+ }
253
+ __classPrivateFieldSet(this, _NetworkConnectionBannerController_isStarted, true, "f");
254
+ __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_refreshState).call(this, {
255
+ networkControllerState: this.messenger.call('NetworkController:getState'),
256
+ networkEnablementControllerState: this.messenger.call('NetworkEnablementController:getState'),
257
+ connectivityControllerState: this.messenger.call('ConnectivityController:getState'),
258
+ });
259
+ }, _NetworkConnectionBannerController_stop = function _NetworkConnectionBannerController_stop() {
260
+ if (!__classPrivateFieldGet(this, _NetworkConnectionBannerController_isStarted, "f")) {
261
+ return;
262
+ }
263
+ __classPrivateFieldSet(this, _NetworkConnectionBannerController_isStarted, false, "f");
264
+ __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_resetBanner).call(this);
265
+ }, _NetworkConnectionBannerController_refreshState = function _NetworkConnectionBannerController_refreshState({ networkControllerState, networkEnablementControllerState, connectivityControllerState, }) {
243
266
  if (!__classPrivateFieldGet(this, _NetworkConnectionBannerController_isStarted, "f")) {
244
267
  return;
245
268
  }
@@ -357,13 +380,13 @@ _NetworkConnectionBannerController_degradedTimer = new WeakMap(), _NetworkConnec
357
380
  ];
358
381
  });
359
382
  }, _NetworkConnectionBannerController_buildFailedNetwork = function _NetworkConnectionBannerController_buildFailedNetwork({ chainId, name, rpcEndpoints, defaultRpcEndpointIndex, defaultRpcEndpoint, }) {
360
- const isInfuraEndpoint = (0, url_utils_1.getIsInfuraEndpoint)(defaultRpcEndpoint.url);
383
+ const isInfuraEndpoint = (0, url_utils_1.getIsInfuraEndpoint)(defaultRpcEndpoint.url, __classPrivateFieldGet(this, _NetworkConnectionBannerController_infuraProjectId, "f"));
361
384
  // For custom endpoints (non-Infura), find an Infura endpoint on this
362
385
  // chain that we could offer to switch to.
363
386
  let switchableInfuraNetworkClientId = null;
364
387
  if (!isInfuraEndpoint) {
365
388
  const infuraEndpoint = rpcEndpoints.find((endpoint, index) => index !== defaultRpcEndpointIndex &&
366
- (0, url_utils_1.getIsInfuraEndpoint)(endpoint.url));
389
+ (0, url_utils_1.getIsInfuraEndpoint)(endpoint.url, __classPrivateFieldGet(this, _NetworkConnectionBannerController_infuraProjectId, "f")));
367
390
  switchableInfuraNetworkClientId = infuraEndpoint?.networkClientId ?? null;
368
391
  }
369
392
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"NetworkConnectionBannerController.cjs","sourceRoot":"","sources":["../src/NetworkConnectionBannerController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangedEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport {\n CONNECTIVITY_STATUSES,\n connectivityControllerSelectors,\n} from '@metamask/connectivity-controller';\nimport type {\n ConnectivityControllerGetStateAction,\n ConnectivityControllerState,\n ConnectivityControllerStateChangeEvent,\n} from '@metamask/connectivity-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport type {\n NetworkConfiguration,\n NetworkControllerGetNetworkConfigurationByChainIdAction,\n NetworkControllerGetStateAction,\n NetworkControllerUpdateNetworkAction,\n NetworkControllerStateChangeEvent,\n NetworkMetadata,\n NetworkState,\n} from '@metamask/network-controller';\nimport { NetworkStatus } from '@metamask/network-controller';\nimport type {\n NetworkEnablementControllerGetStateAction,\n NetworkEnablementControllerState,\n NetworkEnablementControllerStateChangeEvent,\n} from '@metamask/network-enablement-controller';\nimport { selectEnabledNetworkMap } from '@metamask/network-enablement-controller';\nimport type { Hex } from '@metamask/utils';\nimport { KnownCaipNamespace } from '@metamask/utils';\nimport { createSelector } from 'reselect';\n\nimport type { NetworkConnectionBannerControllerMethodActions } from './NetworkConnectionBannerController-method-action-types';\nimport { getIsInfuraEndpoint } from './url-utils';\n\n/**\n * The name of the {@link NetworkConnectionBannerController}, used to namespace\n * the controller's actions and events and to namespace the controller's state\n * data when composed with other controllers.\n */\nconst CONTROLLER_NAME = 'NetworkConnectionBannerController';\n\n/**\n * Selects `networksMetadata` from the `NetworkController` state.\n *\n * @param state - The `NetworkController` state.\n * @returns The networks metadata map keyed by network client id.\n */\nconst selectNetworksMetadata = (\n state: NetworkState,\n): NetworkState['networksMetadata'] => state.networksMetadata;\n\n/**\n * Selects `networkConfigurationsByChainId` from the `NetworkController`\n * state.\n *\n * @param state - The `NetworkController` state.\n * @returns The network configurations keyed by chain id.\n */\nconst selectNetworkConfigurationsByChainId = (\n state: NetworkState,\n): NetworkState['networkConfigurationsByChainId'] =>\n state.networkConfigurationsByChainId;\n\n/**\n * Selects the `NetworkController` state fields that influence the banner\n * rule. Composed with `createSelector` so the return object stays reference\n * stable while unrelated `NetworkController` state (e.g.\n * `selectedNetworkClientId`) changes.\n *\n * @param state - The `NetworkController` state.\n * @returns The relevant network fields.\n */\nconst selectNetworkControllerFields = createSelector(\n [selectNetworksMetadata, selectNetworkConfigurationsByChainId],\n (networksMetadata, networkConfigurationsByChainId) => ({\n networksMetadata,\n networkConfigurationsByChainId,\n }),\n);\n\n/**\n * Selects the `NetworkEnablementController` state field that influences the\n * banner rule.\n *\n * @param state - The `NetworkEnablementController` state.\n * @returns The relevant enablement fields.\n */\nconst selectNetworkEnablementControllerFields = createSelector(\n [selectEnabledNetworkMap],\n (enabledNetworkMap) => ({ enabledNetworkMap }),\n);\n\n/**\n * Selects the `ConnectivityController` state field that influences the\n * banner rule.\n *\n * @param state - The `ConnectivityController` state.\n * @returns The relevant connectivity fields.\n */\nconst selectConnectivityControllerFields = createSelector(\n [connectivityControllerSelectors.selectConnectivityStatus],\n (connectivityStatus) => ({ connectivityStatus }),\n);\n\n/**\n * Status the banner can be in. `available` means no banner is shown; the\n * `degraded` and `unavailable` values mirror the two-tier escalation that the\n * UI renders.\n */\nexport type NetworkConnectionBannerStatus =\n | 'available'\n | 'degraded'\n | 'unavailable';\n\n/**\n * An enabled network from `NetworkController` state with a default RPC\n * endpoint. Used as the input to the failed-network detection pipeline.\n * `metadata` is missing until the network's connectivity has been looked up.\n */\ntype EnabledNetwork = {\n chainId: Hex;\n name: string;\n rpcEndpoints: NetworkConfiguration['rpcEndpoints'];\n defaultRpcEndpointIndex: number;\n defaultRpcEndpoint: NetworkConfiguration['rpcEndpoints'][number];\n metadata: NetworkMetadata | undefined;\n};\n\n/**\n * Details of a failing network the banner describes.\n */\nexport type FailedNetwork = {\n /** The chain id of the failing network. */\n chainId: Hex;\n /** The `networkClientId` of the failing default RPC endpoint. */\n networkClientId: string;\n /** The display name for the failing network. */\n name: string;\n /** The URL of the failing default RPC endpoint. */\n rpcUrl: string;\n /** Whether the failing endpoint is a MetaMask Infura endpoint. */\n isInfuraEndpoint: boolean;\n /**\n * The networkClientId of an Infura endpoint on the same chain that the user\n * can switch to. `null` when the failing endpoint is already Infura or when\n * no Infura alternative exists.\n */\n switchableInfuraNetworkClientId: string | null;\n};\n\n/**\n * State for the {@link NetworkConnectionBannerController}.\n *\n * The keys carry the controller-domain prefix (like\n * `ConnectivityController`'s `connectivityStatus`) because some clients merge\n * all controller states into one flat object, where generic names like\n * `status` collide across controllers.\n */\nexport type NetworkConnectionBannerControllerState = {\n networkConnectionBannerStatus: NetworkConnectionBannerStatus;\n networkConnectionBannerNetwork: FailedNetwork | null;\n};\n\nconst networkConnectionBannerControllerMetadata = {\n networkConnectionBannerStatus: {\n persist: false,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: true,\n },\n networkConnectionBannerNetwork: {\n persist: false,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: true,\n },\n} satisfies StateMetadata<NetworkConnectionBannerControllerState>;\n\n/**\n * Constructs the default {@link NetworkConnectionBannerController} state.\n *\n * @returns The default state.\n */\nexport function getDefaultNetworkConnectionBannerControllerState(): NetworkConnectionBannerControllerState {\n return {\n networkConnectionBannerStatus: 'available',\n networkConnectionBannerNetwork: null,\n };\n}\n\n/**\n * How long (in milliseconds) a failing network must remain in a \"failed\"\n * status (\"degraded\" or \"unavailable\") before the degraded banner appears.\n */\nconst DEGRADED_BANNER_TIMEOUT = 5_000;\n\n/**\n * How long (in milliseconds) a failing network must remain in a \"failed\"\n * status before the banner escalates to \"unavailable\".\n */\nconst UNAVAILABLE_BANNER_TIMEOUT = 30_000;\n\nconst MESSENGER_EXPOSED_METHODS = [\n 'start',\n 'stop',\n 'dismissBanner',\n 'switchToDefaultInfuraRpcEndpoint',\n] as const;\n\n/**\n * Retrieves the state of the {@link NetworkConnectionBannerController}.\n */\nexport type NetworkConnectionBannerControllerGetStateAction =\n ControllerGetStateAction<\n typeof CONTROLLER_NAME,\n NetworkConnectionBannerControllerState\n >;\n\n/**\n * Actions that {@link NetworkConnectionBannerControllerMessenger} exposes to\n * other consumers.\n */\nexport type NetworkConnectionBannerControllerActions =\n | NetworkConnectionBannerControllerGetStateAction\n | NetworkConnectionBannerControllerMethodActions;\n\n/**\n * Actions from other messengers that\n * {@link NetworkConnectionBannerControllerMessenger} calls.\n */\ntype AllowedActions =\n | NetworkControllerGetStateAction\n | NetworkControllerGetNetworkConfigurationByChainIdAction\n | NetworkControllerUpdateNetworkAction\n | NetworkEnablementControllerGetStateAction\n | ConnectivityControllerGetStateAction;\n\n/**\n * Published when the state of {@link NetworkConnectionBannerController}\n * changes.\n */\nexport type NetworkConnectionBannerControllerStateChangedEvent =\n ControllerStateChangedEvent<\n typeof CONTROLLER_NAME,\n NetworkConnectionBannerControllerState\n >;\n\n/**\n * Events that {@link NetworkConnectionBannerControllerMessenger} exposes to\n * other consumers.\n */\nexport type NetworkConnectionBannerControllerEvents =\n NetworkConnectionBannerControllerStateChangedEvent;\n\n/**\n * Events from other messengers that\n * {@link NetworkConnectionBannerControllerMessenger} subscribes to.\n */\ntype AllowedEvents =\n | NetworkControllerStateChangeEvent\n | NetworkEnablementControllerStateChangeEvent\n | ConnectivityControllerStateChangeEvent;\n\n/**\n * The messenger restricted to actions and events accessed by\n * {@link NetworkConnectionBannerController}.\n */\nexport type NetworkConnectionBannerControllerMessenger = Messenger<\n typeof CONTROLLER_NAME,\n NetworkConnectionBannerControllerActions | AllowedActions,\n NetworkConnectionBannerControllerEvents | AllowedEvents\n>;\n\n/**\n * Options for constructing the {@link NetworkConnectionBannerController}.\n */\nexport type NetworkConnectionBannerControllerOptions = {\n /**\n * The messenger for inter-controller communication.\n */\n messenger: NetworkConnectionBannerControllerMessenger;\n};\n\n/**\n * NetworkConnectionBannerController decides whether the network connection\n * banner should be shown for the user, and which failing network it should\n * describe. It encapsulates the rule and the 5s / 30s timer escalation.\n *\n * The banner shows when:\n *\n * - A failing network's default RPC is a custom (non-Infura) endpoint —\n * users always want to be informed about errors with RPCs they've chosen.\n * - Every enabled EVM network is reporting a failed connectivity status\n * (escape hatch so Infura-only setups still get a signal).\n *\n * A partial Infura outage alongside healthy peers is suppressed since it\n * usually resolves on its own and the user cannot act on it. When a custom\n * failure is present, it's surfaced first so the \"Switch to MetaMask default\n * RPC\" CTA targets the network the user can act on.\n *\n * Clients only need to render the banner from the controller's state and wire\n * click handlers to {@link dismissBanner} or {@link switchToDefaultInfuraRpcEndpoint}.\n */\nexport class NetworkConnectionBannerController extends BaseController<\n typeof CONTROLLER_NAME,\n NetworkConnectionBannerControllerState,\n NetworkConnectionBannerControllerMessenger\n> {\n #degradedTimer: ReturnType<typeof setTimeout> | undefined;\n\n #unavailableTimer: ReturnType<typeof setTimeout> | undefined;\n\n #pendingNetworkClientId: string | undefined;\n\n #isStarted = false;\n\n /**\n * Constructs a new {@link NetworkConnectionBannerController}.\n *\n * @param args - The arguments to this controller.\n * @param args.messenger - The messenger suited for this controller.\n */\n constructor({ messenger }: NetworkConnectionBannerControllerOptions) {\n super({\n messenger,\n metadata: networkConnectionBannerControllerMetadata,\n name: CONTROLLER_NAME,\n state: getDefaultNetworkConnectionBannerControllerState(),\n });\n\n // Upstream controllers still expose :stateChange; switch to :stateChanged\n // once those packages migrate their event types.\n /* eslint-disable no-restricted-syntax -- awaiting upstream :stateChanged migration */\n this.messenger.subscribe(\n 'NetworkController:stateChange',\n (networkControllerState) =>\n this.#refreshState({\n networkControllerState,\n networkEnablementControllerState: this.messenger.call(\n 'NetworkEnablementController:getState',\n ),\n connectivityControllerState: this.messenger.call(\n 'ConnectivityController:getState',\n ),\n }),\n selectNetworkControllerFields,\n );\n this.messenger.subscribe(\n 'NetworkEnablementController:stateChange',\n (networkEnablementControllerState) =>\n this.#refreshState({\n networkControllerState: this.messenger.call(\n 'NetworkController:getState',\n ),\n networkEnablementControllerState,\n connectivityControllerState: this.messenger.call(\n 'ConnectivityController:getState',\n ),\n }),\n selectNetworkEnablementControllerFields,\n );\n this.messenger.subscribe(\n 'ConnectivityController:stateChange',\n (connectivityControllerState) =>\n this.#refreshState({\n networkControllerState: this.messenger.call(\n 'NetworkController:getState',\n ),\n networkEnablementControllerState: this.messenger.call(\n 'NetworkEnablementController:getState',\n ),\n connectivityControllerState,\n }),\n selectConnectivityControllerFields,\n );\n /* eslint-enable no-restricted-syntax */\n\n this.messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n /**\n * Look for a failed network, if any, and populate the initial state of the\n * banner. Reacts to upstream state changes from this point on.\n *\n * Call this when the wallet UI that consumes the banner becomes active\n * (typically when the wallet is unlocked and the home surface mounts) so\n * timers do not run while the user is not looking at the wallet. Should\n * be called after `NetworkController`, `NetworkEnablementController`, and\n * `ConnectivityController` have been initialized. Idempotent.\n */\n start(): void {\n if (this.#isStarted) {\n return;\n }\n\n this.#isStarted = true;\n this.#refreshState({\n networkControllerState: this.messenger.call('NetworkController:getState'),\n networkEnablementControllerState: this.messenger.call(\n 'NetworkEnablementController:getState',\n ),\n connectivityControllerState: this.messenger.call(\n 'ConnectivityController:getState',\n ),\n });\n }\n\n /**\n * Stops evaluating network connection state. Clears any pending banner\n * timers and resets state to `available`. Call this when the UI\n * consuming the banner is no longer active. Idempotent.\n */\n stop(): void {\n if (!this.#isStarted) {\n return;\n }\n\n this.#isStarted = false;\n this.#resetBanner();\n }\n\n /**\n * Clears the banner state such that the banner will be hidden.\n */\n dismissBanner(): void {\n this.#resetBanner();\n }\n\n /**\n * Switches the chain's default RPC endpoint to its Infura endpoint,\n * causing the banner to clear once the network becomes available again.\n *\n * @param chainId - The chain whose default RPC endpoint should be switched.\n * @throws If the chain configuration cannot be found, or if it has no\n * Infura endpoint to switch to, or if the default is already Infura.\n */\n async switchToDefaultInfuraRpcEndpoint(chainId: Hex): Promise<void> {\n const networkConfiguration = this.messenger.call(\n 'NetworkController:getNetworkConfigurationByChainId',\n chainId,\n );\n if (!networkConfiguration) {\n throw new Error(\n `No network configuration found for chain ID \"${chainId}\".`,\n );\n }\n\n const infuraEndpointIndex = networkConfiguration.rpcEndpoints.findIndex(\n (endpoint) => getIsInfuraEndpoint(endpoint.url),\n );\n if (infuraEndpointIndex === -1) {\n throw new Error(\n `No Infura endpoint available for chain ID \"${chainId}\".`,\n );\n }\n if (infuraEndpointIndex === networkConfiguration.defaultRpcEndpointIndex) {\n // The default is already Infura; nothing to do.\n return;\n }\n\n await this.messenger.call(\n 'NetworkController:updateNetwork',\n chainId,\n {\n ...networkConfiguration,\n defaultRpcEndpointIndex: infuraEndpointIndex,\n },\n { replacementSelectedRpcEndpointIndex: infuraEndpointIndex },\n );\n }\n\n #refreshState({\n networkControllerState,\n networkEnablementControllerState,\n connectivityControllerState,\n }: {\n networkControllerState: Pick<\n NetworkState,\n 'networkConfigurationsByChainId' | 'networksMetadata'\n >;\n networkEnablementControllerState: Pick<\n NetworkEnablementControllerState,\n 'enabledNetworkMap'\n >;\n connectivityControllerState: Pick<\n ConnectivityControllerState,\n 'connectivityStatus'\n >;\n }): void {\n if (!this.#isStarted) {\n return;\n }\n\n if (\n connectivityControllerState.connectivityStatus ===\n CONNECTIVITY_STATUSES.Offline\n ) {\n this.#resetBanner();\n return;\n }\n\n const failedNetwork = this.#findFailedNetwork(\n networkControllerState,\n networkEnablementControllerState,\n );\n if (!failedNetwork) {\n this.#resetBanner();\n return;\n }\n\n if (\n this.state.networkConnectionBannerStatus !== 'available' &&\n this.state.networkConnectionBannerNetwork?.networkClientId ===\n failedNetwork.networkClientId\n ) {\n this.update((state) => {\n // Even if the network client ID has not changed, save the current\n // version of the failed network to state in case the user has updated\n // its RPC URL.\n state.networkConnectionBannerNetwork = failedNetwork;\n });\n return;\n }\n\n // A degraded timer is already counting down for this same network.\n // Repeated `stateChange` events must not clear and restart it, or a\n // steady stream of updates could postpone the banner indefinitely.\n if (this.#pendingNetworkClientId === failedNetwork.networkClientId) {\n return;\n }\n\n this.#clearTimers();\n this.update((state) => {\n state.networkConnectionBannerStatus = 'available';\n state.networkConnectionBannerNetwork = null;\n });\n\n // If `stop` is called before scheduling timers, bail early.\n if (!this.#isStarted) {\n return;\n }\n\n // Remember which network the pending timer is for (see the guard above)\n // and capture the failing network at schedule time. If the failure\n // resolves or a different network becomes the banner target while we\n // wait, the subscription handlers re-enter this method and cancel or\n // replace the timer before it fires.\n this.#pendingNetworkClientId = failedNetwork.networkClientId;\n this.#degradedTimer = setTimeout(() => {\n this.#degradedTimer = undefined;\n this.#pendingNetworkClientId = undefined;\n this.update((state) => {\n state.networkConnectionBannerStatus = 'degraded';\n state.networkConnectionBannerNetwork = failedNetwork;\n });\n // If `stop` is called before scheduling timers, bail early.\n if (!this.#isStarted) {\n return;\n }\n this.#unavailableTimer = setTimeout(() => {\n this.#unavailableTimer = undefined;\n this.update((state) => {\n state.networkConnectionBannerStatus = 'unavailable';\n state.networkConnectionBannerNetwork = failedNetwork;\n });\n }, UNAVAILABLE_BANNER_TIMEOUT - DEGRADED_BANNER_TIMEOUT);\n }, DEGRADED_BANNER_TIMEOUT);\n }\n\n /**\n * Clears timers and resets banner state to {@link NetworkConnectionBannerStatus|`available`}\n * if it isn't there already.\n */\n #resetBanner(): void {\n this.#clearTimers();\n this.#pendingNetworkClientId = undefined;\n if (\n this.state.networkConnectionBannerStatus !== 'available' ||\n this.state.networkConnectionBannerNetwork !== null\n ) {\n this.update((state) => {\n state.networkConnectionBannerStatus = 'available';\n state.networkConnectionBannerNetwork = null;\n });\n }\n }\n\n #clearTimers(): void {\n if (this.#degradedTimer !== undefined) {\n clearTimeout(this.#degradedTimer);\n this.#degradedTimer = undefined;\n }\n if (this.#unavailableTimer !== undefined) {\n clearTimeout(this.#unavailableTimer);\n this.#unavailableTimer = undefined;\n }\n }\n\n #findFailedNetwork(\n networkState: Pick<\n NetworkState,\n 'networkConfigurationsByChainId' | 'networksMetadata'\n >,\n enablementState: Pick<\n NetworkEnablementControllerState,\n 'enabledNetworkMap'\n >,\n ): FailedNetwork | null {\n const enabledNetworks = this.#collectEnabledNetworks(\n networkState,\n enablementState,\n );\n // Networks whose connectivity has not been looked up yet are not failed.\n const failedNetworks = enabledNetworks\n .filter(\n ({ metadata }) =>\n metadata !== undefined && metadata.status !== NetworkStatus.Available,\n )\n .map((network) => this.#buildFailedNetwork(network));\n return this.#pickFailedNetworkToDisplay(\n failedNetworks,\n enabledNetworks.length,\n );\n }\n\n #getEnabledEvmChainIds(\n enabledNetworkMap: NetworkEnablementControllerState['enabledNetworkMap'],\n ): Hex[] {\n return Object.entries(enabledNetworkMap[KnownCaipNamespace.Eip155] ?? {})\n .filter(([, enabled]) => enabled)\n .map(([chainId]) => chainId as Hex);\n }\n\n #collectEnabledNetworks(\n {\n networkConfigurationsByChainId,\n networksMetadata,\n }: Pick<\n NetworkState,\n 'networkConfigurationsByChainId' | 'networksMetadata'\n >,\n {\n enabledNetworkMap,\n }: Pick<NetworkEnablementControllerState, 'enabledNetworkMap'>,\n ): EnabledNetwork[] {\n return this.#getEnabledEvmChainIds(enabledNetworkMap).flatMap((chainId) => {\n const networkConfiguration = networkConfigurationsByChainId[chainId];\n if (!networkConfiguration) {\n return [];\n }\n const { rpcEndpoints, defaultRpcEndpointIndex, name } =\n networkConfiguration;\n const defaultRpcEndpoint = rpcEndpoints[defaultRpcEndpointIndex];\n if (!defaultRpcEndpoint) {\n return [];\n }\n return [\n {\n chainId,\n name,\n rpcEndpoints,\n defaultRpcEndpointIndex,\n defaultRpcEndpoint,\n metadata: networksMetadata[defaultRpcEndpoint.networkClientId],\n },\n ];\n });\n }\n\n #buildFailedNetwork({\n chainId,\n name,\n rpcEndpoints,\n defaultRpcEndpointIndex,\n defaultRpcEndpoint,\n }: EnabledNetwork): FailedNetwork {\n const isInfuraEndpoint = getIsInfuraEndpoint(defaultRpcEndpoint.url);\n\n // For custom endpoints (non-Infura), find an Infura endpoint on this\n // chain that we could offer to switch to.\n let switchableInfuraNetworkClientId: string | null = null;\n if (!isInfuraEndpoint) {\n const infuraEndpoint = rpcEndpoints.find(\n (endpoint, index) =>\n index !== defaultRpcEndpointIndex &&\n getIsInfuraEndpoint(endpoint.url),\n );\n switchableInfuraNetworkClientId = infuraEndpoint?.networkClientId ?? null;\n }\n\n return {\n chainId,\n networkClientId: defaultRpcEndpoint.networkClientId,\n name,\n rpcUrl: defaultRpcEndpoint.url,\n isInfuraEndpoint,\n switchableInfuraNetworkClientId,\n };\n }\n\n #pickFailedNetworkToDisplay(\n failedNetworks: FailedNetwork[],\n totalEnabledNetworks: number,\n ): FailedNetwork | null {\n if (failedNetworks.length === 0) {\n return null;\n }\n\n const firstCustomFailed = failedNetworks.find(\n (entry) => !entry.isInfuraEndpoint,\n );\n const areAllEnabledNetworksFailed =\n failedNetworks.length === totalEnabledNetworks;\n\n if (!firstCustomFailed && !areAllEnabledNetworksFailed) {\n return null;\n }\n\n return firstCustomFailed ?? failedNetworks[0];\n }\n}\n"]}
1
+ {"version":3,"file":"NetworkConnectionBannerController.cjs","sourceRoot":"","sources":["../src/NetworkConnectionBannerController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangedEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport { clientControllerSelectors } from '@metamask/client-controller';\nimport type { ClientControllerState } from '@metamask/client-controller';\nimport {\n CONNECTIVITY_STATUSES,\n connectivityControllerSelectors,\n} from '@metamask/connectivity-controller';\nimport type {\n ConnectivityControllerGetStateAction,\n ConnectivityControllerState,\n ConnectivityControllerStateChangeEvent,\n} from '@metamask/connectivity-controller';\nimport type {\n KeyringControllerLockEvent,\n KeyringControllerUnlockEvent,\n} from '@metamask/keyring-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport type {\n NetworkConfiguration,\n NetworkControllerGetNetworkConfigurationByChainIdAction,\n NetworkControllerGetStateAction,\n NetworkControllerUpdateNetworkAction,\n NetworkControllerSetActiveNetworkAction,\n NetworkControllerStateChangeEvent,\n NetworkMetadata,\n NetworkState,\n} from '@metamask/network-controller';\nimport { NetworkStatus } from '@metamask/network-controller';\nimport type {\n NetworkEnablementControllerGetStateAction,\n NetworkEnablementControllerState,\n NetworkEnablementControllerStateChangeEvent,\n} from '@metamask/network-enablement-controller';\nimport { selectEnabledNetworkMap } from '@metamask/network-enablement-controller';\nimport type { Hex } from '@metamask/utils';\nimport { KnownCaipNamespace } from '@metamask/utils';\nimport { createSelector } from 'reselect';\n\nimport type { NetworkConnectionBannerControllerMethodActions } from './NetworkConnectionBannerController-method-action-types';\nimport { getIsInfuraEndpoint } from './url-utils';\n\n/**\n * The name of the {@link NetworkConnectionBannerController}, used to namespace\n * the controller's actions and events and to namespace the controller's state\n * data when composed with other controllers.\n */\nconst CONTROLLER_NAME = 'NetworkConnectionBannerController';\n\n/**\n * Selects `networksMetadata` from the `NetworkController` state.\n *\n * @param state - The `NetworkController` state.\n * @returns The networks metadata map keyed by network client id.\n */\nconst selectNetworksMetadata = (\n state: NetworkState,\n): NetworkState['networksMetadata'] => state.networksMetadata;\n\n/**\n * Selects `networkConfigurationsByChainId` from the `NetworkController`\n * state.\n *\n * @param state - The `NetworkController` state.\n * @returns The network configurations keyed by chain id.\n */\nconst selectNetworkConfigurationsByChainId = (\n state: NetworkState,\n): NetworkState['networkConfigurationsByChainId'] =>\n state.networkConfigurationsByChainId;\n\n/**\n * Selects the `NetworkController` state fields that influence the banner\n * rule. Composed with `createSelector` so the return object stays reference\n * stable while unrelated `NetworkController` state (e.g.\n * `selectedNetworkClientId`) changes.\n *\n * @param state - The `NetworkController` state.\n * @returns The relevant network fields.\n */\nconst selectNetworkControllerFields = createSelector(\n [selectNetworksMetadata, selectNetworkConfigurationsByChainId],\n (networksMetadata, networkConfigurationsByChainId) => ({\n networksMetadata,\n networkConfigurationsByChainId,\n }),\n);\n\n/**\n * Selects the `NetworkEnablementController` state field that influences the\n * banner rule.\n *\n * @param state - The `NetworkEnablementController` state.\n * @returns The relevant enablement fields.\n */\nconst selectNetworkEnablementControllerFields = createSelector(\n [selectEnabledNetworkMap],\n (enabledNetworkMap) => ({ enabledNetworkMap }),\n);\n\n/**\n * Selects the `ConnectivityController` state field that influences the\n * banner rule.\n *\n * @param state - The `ConnectivityController` state.\n * @returns The relevant connectivity fields.\n */\nconst selectConnectivityControllerFields = createSelector(\n [connectivityControllerSelectors.selectConnectivityStatus],\n (connectivityStatus) => ({ connectivityStatus }),\n);\n\n/**\n * Status the banner can be in. `available` means no banner is shown; the\n * `degraded` and `unavailable` values mirror the two-tier escalation that the\n * UI renders.\n */\nexport type NetworkConnectionBannerStatus =\n | 'available'\n | 'degraded'\n | 'unavailable';\n\n/**\n * An enabled network from `NetworkController` state with a default RPC\n * endpoint. Used as the input to the failed-network detection pipeline.\n * `metadata` is missing until the network's connectivity has been looked up.\n */\ntype EnabledNetwork = {\n chainId: Hex;\n name: string;\n rpcEndpoints: NetworkConfiguration['rpcEndpoints'];\n defaultRpcEndpointIndex: number;\n defaultRpcEndpoint: NetworkConfiguration['rpcEndpoints'][number];\n metadata: NetworkMetadata | undefined;\n};\n\n/**\n * Details of a failing network the banner describes.\n */\nexport type FailedNetwork = {\n /** The chain id of the failing network. */\n chainId: Hex;\n /** The `networkClientId` of the failing default RPC endpoint. */\n networkClientId: string;\n /** The display name for the failing network. */\n name: string;\n /** The URL of the failing default RPC endpoint. */\n rpcUrl: string;\n /** Whether the failing endpoint is a MetaMask Infura endpoint. */\n isInfuraEndpoint: boolean;\n /**\n * The networkClientId of an Infura endpoint on the same chain that the user\n * can switch to. `null` when the failing endpoint is already Infura or when\n * no Infura alternative exists.\n */\n switchableInfuraNetworkClientId: string | null;\n};\n\n/**\n * State for the {@link NetworkConnectionBannerController}.\n *\n * The keys carry the controller-domain prefix (like\n * `ConnectivityController`'s `connectivityStatus`) because some clients merge\n * all controller states into one flat object, where generic names like\n * `status` collide across controllers.\n */\nexport type NetworkConnectionBannerControllerState = {\n networkConnectionBannerStatus: NetworkConnectionBannerStatus;\n networkConnectionBannerNetwork: FailedNetwork | null;\n};\n\nconst networkConnectionBannerControllerMetadata = {\n networkConnectionBannerStatus: {\n persist: false,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: true,\n },\n networkConnectionBannerNetwork: {\n persist: false,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: true,\n },\n} satisfies StateMetadata<NetworkConnectionBannerControllerState>;\n\n/**\n * Constructs the default {@link NetworkConnectionBannerController} state.\n *\n * @returns The default state.\n */\nexport function getDefaultNetworkConnectionBannerControllerState(): NetworkConnectionBannerControllerState {\n return {\n networkConnectionBannerStatus: 'available',\n networkConnectionBannerNetwork: null,\n };\n}\n\n/**\n * How long (in milliseconds) a failing network must remain in a \"failed\"\n * status (\"degraded\" or \"unavailable\") before the degraded banner appears.\n */\nconst DEGRADED_BANNER_TIMEOUT = 5_000;\n\n/**\n * How long (in milliseconds) a failing network must remain in a \"failed\"\n * status before the banner escalates to \"unavailable\".\n */\nconst UNAVAILABLE_BANNER_TIMEOUT = 30_000;\n\nconst MESSENGER_EXPOSED_METHODS = [\n 'dismissBanner',\n 'switchToDefaultInfuraRpcEndpoint',\n] as const;\n\n/**\n * Retrieves the state of the {@link NetworkConnectionBannerController}.\n */\nexport type NetworkConnectionBannerControllerGetStateAction =\n ControllerGetStateAction<\n typeof CONTROLLER_NAME,\n NetworkConnectionBannerControllerState\n >;\n\n/**\n * Actions that {@link NetworkConnectionBannerControllerMessenger} exposes to\n * other consumers.\n */\nexport type NetworkConnectionBannerControllerActions =\n | NetworkConnectionBannerControllerGetStateAction\n | NetworkConnectionBannerControllerMethodActions;\n\n/**\n * Actions from other messengers that\n * {@link NetworkConnectionBannerControllerMessenger} calls.\n */\ntype AllowedActions =\n | NetworkControllerGetStateAction\n | NetworkControllerGetNetworkConfigurationByChainIdAction\n | NetworkControllerUpdateNetworkAction\n | NetworkControllerSetActiveNetworkAction\n | NetworkEnablementControllerGetStateAction\n | ConnectivityControllerGetStateAction;\n\n/**\n * Published when the state of {@link NetworkConnectionBannerController}\n * changes.\n */\nexport type NetworkConnectionBannerControllerStateChangedEvent =\n ControllerStateChangedEvent<\n typeof CONTROLLER_NAME,\n NetworkConnectionBannerControllerState\n >;\n\n/**\n * Events that {@link NetworkConnectionBannerControllerMessenger} exposes to\n * other consumers.\n */\nexport type NetworkConnectionBannerControllerEvents =\n NetworkConnectionBannerControllerStateChangedEvent;\n\n/**\n * Published when the state of `ClientController` changes. Defined here\n * because the `client-controller` package still exports the legacy\n * `:stateChange` event type.\n */\ntype ClientControllerStateChangedEvent = ControllerStateChangedEvent<\n 'ClientController',\n ClientControllerState\n>;\n\n/**\n * Events from other messengers that\n * {@link NetworkConnectionBannerControllerMessenger} subscribes to.\n */\ntype AllowedEvents =\n | NetworkControllerStateChangeEvent\n | NetworkEnablementControllerStateChangeEvent\n | ConnectivityControllerStateChangeEvent\n | ClientControllerStateChangedEvent\n | KeyringControllerUnlockEvent\n | KeyringControllerLockEvent;\n\n/**\n * The messenger restricted to actions and events accessed by\n * {@link NetworkConnectionBannerController}.\n */\nexport type NetworkConnectionBannerControllerMessenger = Messenger<\n typeof CONTROLLER_NAME,\n NetworkConnectionBannerControllerActions | AllowedActions,\n NetworkConnectionBannerControllerEvents | AllowedEvents\n>;\n\n/**\n * Options for constructing the {@link NetworkConnectionBannerController}.\n */\nexport type NetworkConnectionBannerControllerOptions = {\n /**\n * The messenger for inter-controller communication.\n */\n messenger: NetworkConnectionBannerControllerMessenger;\n\n /**\n * The wallet's Infura project id, used to recognize MetaMask Infura\n * endpoints whose URL was persisted with the id already substituted.\n */\n infuraProjectId: string;\n};\n\n/**\n * NetworkConnectionBannerController decides whether the network connection\n * banner should be shown for the user, and which failing network it should\n * describe. It encapsulates the rule and the 5s / 30s timer escalation.\n *\n * The banner shows when:\n *\n * - A failing network's default RPC is a custom (non-Infura) endpoint —\n * users always want to be informed about errors with RPCs they've chosen.\n * - Every enabled EVM network is reporting a failed connectivity status\n * (escape hatch so Infura-only setups still get a signal).\n *\n * A partial Infura outage alongside healthy peers is suppressed since it\n * usually resolves on its own and the user cannot act on it. When a custom\n * failure is present, it's surfaced first so the \"Switch to MetaMask default\n * RPC\" CTA targets the network the user can act on.\n *\n * The controller manages its own lifecycle: it evaluates RPC health (and\n * runs the escalation timers) only while the client UI is open\n * (`ClientController`) and the wallet is unlocked (`KeyringController`).\n * Clients only need to render the banner from the controller's state and wire\n * click handlers to {@link dismissBanner} or {@link switchToDefaultInfuraRpcEndpoint}.\n */\nexport class NetworkConnectionBannerController extends BaseController<\n typeof CONTROLLER_NAME,\n NetworkConnectionBannerControllerState,\n NetworkConnectionBannerControllerMessenger\n> {\n #degradedTimer: ReturnType<typeof setTimeout> | undefined;\n\n #unavailableTimer: ReturnType<typeof setTimeout> | undefined;\n\n #pendingNetworkClientId: string | undefined;\n\n #isStarted = false;\n\n /** Whether the client UI is open. Combined with {@link #isUnlocked}. */\n #isUiOpen = false;\n\n /** Whether the keyring is unlocked. Combined with {@link #isUiOpen}. */\n #isUnlocked = false;\n\n readonly #infuraProjectId: string;\n\n /**\n * Constructs a new {@link NetworkConnectionBannerController}.\n *\n * @param args - The arguments to this controller.\n * @param args.messenger - The messenger suited for this controller.\n * @param args.infuraProjectId - The wallet's Infura project id.\n */\n constructor({\n messenger,\n infuraProjectId,\n }: NetworkConnectionBannerControllerOptions) {\n super({\n messenger,\n metadata: networkConnectionBannerControllerMetadata,\n name: CONTROLLER_NAME,\n state: getDefaultNetworkConnectionBannerControllerState(),\n });\n\n this.#infuraProjectId = infuraProjectId;\n\n // Upstream controllers still expose :stateChange; switch to :stateChanged\n // once those packages migrate their event types.\n /* eslint-disable no-restricted-syntax -- awaiting upstream :stateChanged migration */\n this.messenger.subscribe(\n 'NetworkController:stateChange',\n (networkControllerState) =>\n this.#refreshState({\n networkControllerState,\n networkEnablementControllerState: this.messenger.call(\n 'NetworkEnablementController:getState',\n ),\n connectivityControllerState: this.messenger.call(\n 'ConnectivityController:getState',\n ),\n }),\n selectNetworkControllerFields,\n );\n this.messenger.subscribe(\n 'NetworkEnablementController:stateChange',\n (networkEnablementControllerState) =>\n this.#refreshState({\n networkControllerState: this.messenger.call(\n 'NetworkController:getState',\n ),\n networkEnablementControllerState,\n connectivityControllerState: this.messenger.call(\n 'ConnectivityController:getState',\n ),\n }),\n selectNetworkEnablementControllerFields,\n );\n this.messenger.subscribe(\n 'ConnectivityController:stateChange',\n (connectivityControllerState) =>\n this.#refreshState({\n networkControllerState: this.messenger.call(\n 'NetworkController:getState',\n ),\n networkEnablementControllerState: this.messenger.call(\n 'NetworkEnablementController:getState',\n ),\n connectivityControllerState,\n }),\n selectConnectivityControllerFields,\n );\n /* eslint-enable no-restricted-syntax */\n\n // Lifecycle: evaluate RPC health (and run the banner escalation timers)\n // only while the client UI is open on an unlocked wallet.\n this.messenger.subscribe(\n 'ClientController:stateChanged',\n (isUiOpen) => {\n this.#isUiOpen = isUiOpen;\n this.#updateLifecycle();\n },\n clientControllerSelectors.selectIsUiOpen,\n );\n this.messenger.subscribe('KeyringController:unlock', () => {\n this.#isUnlocked = true;\n this.#updateLifecycle();\n });\n this.messenger.subscribe('KeyringController:lock', () => {\n this.#isUnlocked = false;\n this.#updateLifecycle();\n });\n\n this.messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n #updateLifecycle(): void {\n if (this.#isUiOpen && this.#isUnlocked) {\n this.#start();\n } else {\n this.#stop();\n }\n }\n\n /**\n * Look for a failed network, if any, and populate the initial state of the\n * banner. Reacts to upstream state changes from this point on. Idempotent.\n */\n #start(): void {\n if (this.#isStarted) {\n return;\n }\n\n this.#isStarted = true;\n this.#refreshState({\n networkControllerState: this.messenger.call('NetworkController:getState'),\n networkEnablementControllerState: this.messenger.call(\n 'NetworkEnablementController:getState',\n ),\n connectivityControllerState: this.messenger.call(\n 'ConnectivityController:getState',\n ),\n });\n }\n\n /**\n * Stops evaluating network connection state. Clears any pending banner\n * timers and resets state to `available`. Idempotent.\n */\n #stop(): void {\n if (!this.#isStarted) {\n return;\n }\n\n this.#isStarted = false;\n this.#resetBanner();\n }\n\n /**\n * Clears the banner state such that the banner will be hidden.\n */\n dismissBanner(): void {\n this.#resetBanner();\n }\n\n /**\n * Switches the chain's default RPC endpoint to its Infura endpoint and\n * makes it the active network, causing the banner to clear once the\n * network becomes available again.\n *\n * @param chainId - The chain whose default RPC endpoint should be switched.\n * @throws If the chain configuration cannot be found, or if it has no\n * Infura endpoint to switch to, or if the default is already Infura.\n */\n async switchToDefaultInfuraRpcEndpoint(chainId: Hex): Promise<void> {\n const networkConfiguration = this.messenger.call(\n 'NetworkController:getNetworkConfigurationByChainId',\n chainId,\n );\n if (!networkConfiguration) {\n throw new Error(\n `No network configuration found for chain ID \"${chainId}\".`,\n );\n }\n\n const infuraEndpointIndex = networkConfiguration.rpcEndpoints.findIndex(\n (endpoint) => getIsInfuraEndpoint(endpoint.url, this.#infuraProjectId),\n );\n if (infuraEndpointIndex === -1) {\n throw new Error(\n `No Infura endpoint available for chain ID \"${chainId}\".`,\n );\n }\n if (infuraEndpointIndex === networkConfiguration.defaultRpcEndpointIndex) {\n // The default is already Infura; nothing to do.\n return;\n }\n\n // Move the active connection onto the Infura endpoint first, then make\n // it the default. In this order a partial failure leaves the failing\n // default in place and the banner visible, rather than hiding the banner\n // while the wallet is still connected to the broken endpoint.\n const infuraNetworkClientId =\n networkConfiguration.rpcEndpoints[infuraEndpointIndex].networkClientId;\n const { selectedNetworkClientId } = this.messenger.call(\n 'NetworkController:getState',\n );\n if (infuraNetworkClientId !== selectedNetworkClientId) {\n await this.messenger.call(\n 'NetworkController:setActiveNetwork',\n infuraNetworkClientId,\n );\n }\n\n await this.messenger.call('NetworkController:updateNetwork', chainId, {\n ...networkConfiguration,\n defaultRpcEndpointIndex: infuraEndpointIndex,\n });\n }\n\n #refreshState({\n networkControllerState,\n networkEnablementControllerState,\n connectivityControllerState,\n }: {\n networkControllerState: Pick<\n NetworkState,\n 'networkConfigurationsByChainId' | 'networksMetadata'\n >;\n networkEnablementControllerState: Pick<\n NetworkEnablementControllerState,\n 'enabledNetworkMap'\n >;\n connectivityControllerState: Pick<\n ConnectivityControllerState,\n 'connectivityStatus'\n >;\n }): void {\n if (!this.#isStarted) {\n return;\n }\n\n if (\n connectivityControllerState.connectivityStatus ===\n CONNECTIVITY_STATUSES.Offline\n ) {\n this.#resetBanner();\n return;\n }\n\n const failedNetwork = this.#findFailedNetwork(\n networkControllerState,\n networkEnablementControllerState,\n );\n if (!failedNetwork) {\n this.#resetBanner();\n return;\n }\n\n if (\n this.state.networkConnectionBannerStatus !== 'available' &&\n this.state.networkConnectionBannerNetwork?.networkClientId ===\n failedNetwork.networkClientId\n ) {\n this.update((state) => {\n // Even if the network client ID has not changed, save the current\n // version of the failed network to state in case the user has updated\n // its RPC URL.\n state.networkConnectionBannerNetwork = failedNetwork;\n });\n return;\n }\n\n // A degraded timer is already counting down for this same network.\n // Repeated `stateChange` events must not clear and restart it, or a\n // steady stream of updates could postpone the banner indefinitely.\n if (this.#pendingNetworkClientId === failedNetwork.networkClientId) {\n return;\n }\n\n this.#clearTimers();\n this.update((state) => {\n state.networkConnectionBannerStatus = 'available';\n state.networkConnectionBannerNetwork = null;\n });\n\n // If `stop` is called before scheduling timers, bail early.\n if (!this.#isStarted) {\n return;\n }\n\n // Remember which network the pending timer is for (see the guard above)\n // and capture the failing network at schedule time. If the failure\n // resolves or a different network becomes the banner target while we\n // wait, the subscription handlers re-enter this method and cancel or\n // replace the timer before it fires.\n this.#pendingNetworkClientId = failedNetwork.networkClientId;\n this.#degradedTimer = setTimeout(() => {\n this.#degradedTimer = undefined;\n this.#pendingNetworkClientId = undefined;\n this.update((state) => {\n state.networkConnectionBannerStatus = 'degraded';\n state.networkConnectionBannerNetwork = failedNetwork;\n });\n // If `stop` is called before scheduling timers, bail early.\n if (!this.#isStarted) {\n return;\n }\n this.#unavailableTimer = setTimeout(() => {\n this.#unavailableTimer = undefined;\n this.update((state) => {\n state.networkConnectionBannerStatus = 'unavailable';\n state.networkConnectionBannerNetwork = failedNetwork;\n });\n }, UNAVAILABLE_BANNER_TIMEOUT - DEGRADED_BANNER_TIMEOUT);\n }, DEGRADED_BANNER_TIMEOUT);\n }\n\n /**\n * Clears timers and resets banner state to {@link NetworkConnectionBannerStatus|`available`}\n * if it isn't there already.\n */\n #resetBanner(): void {\n this.#clearTimers();\n this.#pendingNetworkClientId = undefined;\n if (\n this.state.networkConnectionBannerStatus !== 'available' ||\n this.state.networkConnectionBannerNetwork !== null\n ) {\n this.update((state) => {\n state.networkConnectionBannerStatus = 'available';\n state.networkConnectionBannerNetwork = null;\n });\n }\n }\n\n #clearTimers(): void {\n if (this.#degradedTimer !== undefined) {\n clearTimeout(this.#degradedTimer);\n this.#degradedTimer = undefined;\n }\n if (this.#unavailableTimer !== undefined) {\n clearTimeout(this.#unavailableTimer);\n this.#unavailableTimer = undefined;\n }\n }\n\n #findFailedNetwork(\n networkState: Pick<\n NetworkState,\n 'networkConfigurationsByChainId' | 'networksMetadata'\n >,\n enablementState: Pick<\n NetworkEnablementControllerState,\n 'enabledNetworkMap'\n >,\n ): FailedNetwork | null {\n const enabledNetworks = this.#collectEnabledNetworks(\n networkState,\n enablementState,\n );\n // Networks whose connectivity has not been looked up yet are not failed.\n const failedNetworks = enabledNetworks\n .filter(\n ({ metadata }) =>\n metadata !== undefined && metadata.status !== NetworkStatus.Available,\n )\n .map((network) => this.#buildFailedNetwork(network));\n return this.#pickFailedNetworkToDisplay(\n failedNetworks,\n enabledNetworks.length,\n );\n }\n\n #getEnabledEvmChainIds(\n enabledNetworkMap: NetworkEnablementControllerState['enabledNetworkMap'],\n ): Hex[] {\n return Object.entries(enabledNetworkMap[KnownCaipNamespace.Eip155] ?? {})\n .filter(([, enabled]) => enabled)\n .map(([chainId]) => chainId as Hex);\n }\n\n #collectEnabledNetworks(\n {\n networkConfigurationsByChainId,\n networksMetadata,\n }: Pick<\n NetworkState,\n 'networkConfigurationsByChainId' | 'networksMetadata'\n >,\n {\n enabledNetworkMap,\n }: Pick<NetworkEnablementControllerState, 'enabledNetworkMap'>,\n ): EnabledNetwork[] {\n return this.#getEnabledEvmChainIds(enabledNetworkMap).flatMap((chainId) => {\n const networkConfiguration = networkConfigurationsByChainId[chainId];\n if (!networkConfiguration) {\n return [];\n }\n const { rpcEndpoints, defaultRpcEndpointIndex, name } =\n networkConfiguration;\n const defaultRpcEndpoint = rpcEndpoints[defaultRpcEndpointIndex];\n if (!defaultRpcEndpoint) {\n return [];\n }\n return [\n {\n chainId,\n name,\n rpcEndpoints,\n defaultRpcEndpointIndex,\n defaultRpcEndpoint,\n metadata: networksMetadata[defaultRpcEndpoint.networkClientId],\n },\n ];\n });\n }\n\n #buildFailedNetwork({\n chainId,\n name,\n rpcEndpoints,\n defaultRpcEndpointIndex,\n defaultRpcEndpoint,\n }: EnabledNetwork): FailedNetwork {\n const isInfuraEndpoint = getIsInfuraEndpoint(\n defaultRpcEndpoint.url,\n this.#infuraProjectId,\n );\n\n // For custom endpoints (non-Infura), find an Infura endpoint on this\n // chain that we could offer to switch to.\n let switchableInfuraNetworkClientId: string | null = null;\n if (!isInfuraEndpoint) {\n const infuraEndpoint = rpcEndpoints.find(\n (endpoint, index) =>\n index !== defaultRpcEndpointIndex &&\n getIsInfuraEndpoint(endpoint.url, this.#infuraProjectId),\n );\n switchableInfuraNetworkClientId = infuraEndpoint?.networkClientId ?? null;\n }\n\n return {\n chainId,\n networkClientId: defaultRpcEndpoint.networkClientId,\n name,\n rpcUrl: defaultRpcEndpoint.url,\n isInfuraEndpoint,\n switchableInfuraNetworkClientId,\n };\n }\n\n #pickFailedNetworkToDisplay(\n failedNetworks: FailedNetwork[],\n totalEnabledNetworks: number,\n ): FailedNetwork | null {\n if (failedNetworks.length === 0) {\n return null;\n }\n\n const firstCustomFailed = failedNetworks.find(\n (entry) => !entry.isInfuraEndpoint,\n );\n const areAllEnabledNetworksFailed =\n failedNetworks.length === totalEnabledNetworks;\n\n if (!firstCustomFailed && !areAllEnabledNetworksFailed) {\n return null;\n }\n\n return firstCustomFailed ?? failedNetworks[0];\n }\n}\n"]}