@metamask-previews/network-connection-banner-controller 0.1.0-preview-3c0e8a808 → 0.1.0-preview-33855f889
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.
- package/README.md +15 -7
- package/dist/NetworkConnectionBannerController-method-action-types.cjs.map +1 -1
- package/dist/NetworkConnectionBannerController-method-action-types.d.cts +25 -13
- package/dist/NetworkConnectionBannerController-method-action-types.d.cts.map +1 -1
- package/dist/NetworkConnectionBannerController-method-action-types.d.mts +25 -13
- package/dist/NetworkConnectionBannerController-method-action-types.d.mts.map +1 -1
- package/dist/NetworkConnectionBannerController-method-action-types.mjs.map +1 -1
- package/dist/NetworkConnectionBannerController.cjs +82 -41
- package/dist/NetworkConnectionBannerController.cjs.map +1 -1
- package/dist/NetworkConnectionBannerController.d.cts +28 -16
- package/dist/NetworkConnectionBannerController.d.cts.map +1 -1
- package/dist/NetworkConnectionBannerController.d.mts +28 -16
- package/dist/NetworkConnectionBannerController.d.mts.map +1 -1
- package/dist/NetworkConnectionBannerController.mjs +82 -41
- package/dist/NetworkConnectionBannerController.mjs.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,18 +5,26 @@ connection banner based on RPC endpoint health. It encapsulates the shared
|
|
|
5
5
|
rule, the 5s/30s timer state machine, and the eTLD+1 grouping previously
|
|
6
6
|
duplicated across MetaMask clients.
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## Lifecycle
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
The controller stays dormant after construction so the 5s / 30s escalation
|
|
11
|
+
timers do not run before a user is actually looking at the wallet (e.g. while
|
|
12
|
+
the app is still on the lock screen). The UI that renders the banner is
|
|
13
|
+
responsible for driving the lifecycle:
|
|
14
14
|
|
|
15
15
|
```typescript
|
|
16
|
-
|
|
16
|
+
// When the wallet UI that shows the banner becomes active
|
|
17
|
+
// (e.g. the home surface mounts after unlock):
|
|
18
|
+
networkConnectionBannerController.start();
|
|
19
|
+
|
|
20
|
+
// When it goes away:
|
|
21
|
+
networkConnectionBannerController.stop();
|
|
17
22
|
```
|
|
18
23
|
|
|
19
|
-
|
|
24
|
+
Both methods are idempotent. `start` runs the initial evaluation immediately
|
|
25
|
+
and enables reactions to upstream state changes. `stop` cancels any pending
|
|
26
|
+
timers and resets the banner state to `available`. Upstream state changes are
|
|
27
|
+
ignored while stopped.
|
|
20
28
|
|
|
21
29
|
## Installation
|
|
22
30
|
|
|
@@ -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 *
|
|
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"]}
|
|
@@ -4,14 +4,27 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { NetworkConnectionBannerController } from "./NetworkConnectionBannerController.cjs";
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
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.
|
|
8
9
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
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.
|
|
11
15
|
*/
|
|
12
|
-
export type
|
|
13
|
-
type: `NetworkConnectionBannerController:
|
|
14
|
-
handler: NetworkConnectionBannerController['
|
|
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'];
|
|
15
28
|
};
|
|
16
29
|
/**
|
|
17
30
|
* Clears the banner state such that the banner will be hidden.
|
|
@@ -21,20 +34,19 @@ export type NetworkConnectionBannerControllerDismissBannerAction = {
|
|
|
21
34
|
handler: NetworkConnectionBannerController['dismissBanner'];
|
|
22
35
|
};
|
|
23
36
|
/**
|
|
24
|
-
* Switches the chain's default RPC endpoint to its
|
|
37
|
+
* Switches the chain's default RPC endpoint to its Infura endpoint,
|
|
25
38
|
* causing the banner to clear once the network becomes available again.
|
|
26
39
|
*
|
|
27
|
-
* @param
|
|
28
|
-
* @param args.chainId - The chain whose default RPC should be switched.
|
|
40
|
+
* @param chainId - The chain whose default RPC endpoint should be switched.
|
|
29
41
|
* @throws If the chain configuration cannot be found, or if it has no
|
|
30
42
|
* Infura endpoint to switch to, or if the default is already Infura.
|
|
31
43
|
*/
|
|
32
|
-
export type
|
|
33
|
-
type: `NetworkConnectionBannerController:
|
|
34
|
-
handler: NetworkConnectionBannerController['
|
|
44
|
+
export type NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcEndpointAction = {
|
|
45
|
+
type: `NetworkConnectionBannerController:switchToDefaultInfuraRpcEndpoint`;
|
|
46
|
+
handler: NetworkConnectionBannerController['switchToDefaultInfuraRpcEndpoint'];
|
|
35
47
|
};
|
|
36
48
|
/**
|
|
37
49
|
* Union of all NetworkConnectionBannerController action types.
|
|
38
50
|
*/
|
|
39
|
-
export type NetworkConnectionBannerControllerMethodActions =
|
|
51
|
+
export type NetworkConnectionBannerControllerMethodActions = NetworkConnectionBannerControllerStartAction | NetworkConnectionBannerControllerStopAction | NetworkConnectionBannerControllerDismissBannerAction | NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcEndpointAction;
|
|
40
52
|
//# 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
|
|
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"}
|
|
@@ -4,14 +4,27 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { NetworkConnectionBannerController } from "./NetworkConnectionBannerController.mjs";
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
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.
|
|
8
9
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
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.
|
|
11
15
|
*/
|
|
12
|
-
export type
|
|
13
|
-
type: `NetworkConnectionBannerController:
|
|
14
|
-
handler: NetworkConnectionBannerController['
|
|
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'];
|
|
15
28
|
};
|
|
16
29
|
/**
|
|
17
30
|
* Clears the banner state such that the banner will be hidden.
|
|
@@ -21,20 +34,19 @@ export type NetworkConnectionBannerControllerDismissBannerAction = {
|
|
|
21
34
|
handler: NetworkConnectionBannerController['dismissBanner'];
|
|
22
35
|
};
|
|
23
36
|
/**
|
|
24
|
-
* Switches the chain's default RPC endpoint to its
|
|
37
|
+
* Switches the chain's default RPC endpoint to its Infura endpoint,
|
|
25
38
|
* causing the banner to clear once the network becomes available again.
|
|
26
39
|
*
|
|
27
|
-
* @param
|
|
28
|
-
* @param args.chainId - The chain whose default RPC should be switched.
|
|
40
|
+
* @param chainId - The chain whose default RPC endpoint should be switched.
|
|
29
41
|
* @throws If the chain configuration cannot be found, or if it has no
|
|
30
42
|
* Infura endpoint to switch to, or if the default is already Infura.
|
|
31
43
|
*/
|
|
32
|
-
export type
|
|
33
|
-
type: `NetworkConnectionBannerController:
|
|
34
|
-
handler: NetworkConnectionBannerController['
|
|
44
|
+
export type NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcEndpointAction = {
|
|
45
|
+
type: `NetworkConnectionBannerController:switchToDefaultInfuraRpcEndpoint`;
|
|
46
|
+
handler: NetworkConnectionBannerController['switchToDefaultInfuraRpcEndpoint'];
|
|
35
47
|
};
|
|
36
48
|
/**
|
|
37
49
|
* Union of all NetworkConnectionBannerController action types.
|
|
38
50
|
*/
|
|
39
|
-
export type NetworkConnectionBannerControllerMethodActions =
|
|
51
|
+
export type NetworkConnectionBannerControllerMethodActions = NetworkConnectionBannerControllerStartAction | NetworkConnectionBannerControllerStopAction | NetworkConnectionBannerControllerDismissBannerAction | NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcEndpointAction;
|
|
40
52
|
//# 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
|
|
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 +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 *
|
|
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"]}
|
|
@@ -10,7 +10,7 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
|
|
|
10
10
|
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
11
|
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
12
12
|
};
|
|
13
|
-
var _NetworkConnectionBannerController_instances, _NetworkConnectionBannerController_degradedTimer, _NetworkConnectionBannerController_unavailableTimer, _NetworkConnectionBannerController_pendingNetworkClientId,
|
|
13
|
+
var _NetworkConnectionBannerController_instances, _NetworkConnectionBannerController_degradedTimer, _NetworkConnectionBannerController_unavailableTimer, _NetworkConnectionBannerController_pendingNetworkClientId, _NetworkConnectionBannerController_isStarted, _NetworkConnectionBannerController_refreshState, _NetworkConnectionBannerController_resetBanner, _NetworkConnectionBannerController_clearTimers, _NetworkConnectionBannerController_findFailedNetwork, _NetworkConnectionBannerController_getEnabledEvmChainIds, _NetworkConnectionBannerController_collectNetworksWithMetadata, _NetworkConnectionBannerController_buildFailedNetwork, _NetworkConnectionBannerController_pickBannerNetwork;
|
|
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");
|
|
@@ -20,6 +20,12 @@ const network_enablement_controller_1 = require("@metamask/network-enablement-co
|
|
|
20
20
|
const utils_1 = require("@metamask/utils");
|
|
21
21
|
const reselect_1 = require("reselect");
|
|
22
22
|
const url_utils_1 = require("./url-utils.cjs");
|
|
23
|
+
/**
|
|
24
|
+
* The name of the {@link NetworkConnectionBannerController}, used to namespace
|
|
25
|
+
* the controller's actions and events and to namespace the controller's state
|
|
26
|
+
* data when composed with other controllers.
|
|
27
|
+
*/
|
|
28
|
+
const CONTROLLER_NAME = 'NetworkConnectionBannerController';
|
|
23
29
|
/**
|
|
24
30
|
* Selects `networksMetadata` from the `NetworkController` state.
|
|
25
31
|
*
|
|
@@ -49,11 +55,21 @@ const selectNetworkControllerFields = (0, reselect_1.createSelector)([selectNetw
|
|
|
49
55
|
networkConfigurationsByChainId,
|
|
50
56
|
}));
|
|
51
57
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
58
|
+
* Selects the `NetworkEnablementController` state field that influences the
|
|
59
|
+
* banner rule.
|
|
60
|
+
*
|
|
61
|
+
* @param state - The `NetworkEnablementController` state.
|
|
62
|
+
* @returns The relevant enablement fields.
|
|
63
|
+
*/
|
|
64
|
+
const selectNetworkEnablementControllerFields = (0, reselect_1.createSelector)([network_enablement_controller_1.selectEnabledNetworkMap], (enabledNetworkMap) => ({ enabledNetworkMap }));
|
|
65
|
+
/**
|
|
66
|
+
* Selects the `ConnectivityController` state field that influences the
|
|
67
|
+
* banner rule.
|
|
68
|
+
*
|
|
69
|
+
* @param state - The `ConnectivityController` state.
|
|
70
|
+
* @returns The relevant connectivity fields.
|
|
55
71
|
*/
|
|
56
|
-
const
|
|
72
|
+
const selectConnectivityControllerFields = (0, reselect_1.createSelector)([connectivity_controller_1.connectivityControllerSelectors.selectConnectivityStatus], (connectivityStatus) => ({ connectivityStatus }));
|
|
57
73
|
const networkConnectionBannerControllerMetadata = {
|
|
58
74
|
status: {
|
|
59
75
|
persist: false,
|
|
@@ -91,9 +107,10 @@ const DEGRADED_BANNER_TIMEOUT = 5000;
|
|
|
91
107
|
*/
|
|
92
108
|
const UNAVAILABLE_BANNER_TIMEOUT = 30000;
|
|
93
109
|
const MESSENGER_EXPOSED_METHODS = [
|
|
94
|
-
'
|
|
110
|
+
'start',
|
|
111
|
+
'stop',
|
|
95
112
|
'dismissBanner',
|
|
96
|
-
'
|
|
113
|
+
'switchToDefaultInfuraRpcEndpoint',
|
|
97
114
|
];
|
|
98
115
|
/**
|
|
99
116
|
* NetworkConnectionBannerController decides whether the network connection
|
|
@@ -116,7 +133,7 @@ const MESSENGER_EXPOSED_METHODS = [
|
|
|
116
133
|
* can act on.
|
|
117
134
|
*
|
|
118
135
|
* Clients only need to render the banner from the controller's state and wire
|
|
119
|
-
* click handlers to {@link dismissBanner} or {@link
|
|
136
|
+
* click handlers to {@link dismissBanner} or {@link switchToDefaultInfuraRpcEndpoint}.
|
|
120
137
|
*/
|
|
121
138
|
class NetworkConnectionBannerController extends base_controller_1.BaseController {
|
|
122
139
|
/**
|
|
@@ -129,38 +146,51 @@ class NetworkConnectionBannerController extends base_controller_1.BaseController
|
|
|
129
146
|
super({
|
|
130
147
|
messenger,
|
|
131
148
|
metadata: networkConnectionBannerControllerMetadata,
|
|
132
|
-
name:
|
|
149
|
+
name: CONTROLLER_NAME,
|
|
133
150
|
state: getDefaultNetworkConnectionBannerControllerState(),
|
|
134
151
|
});
|
|
135
152
|
_NetworkConnectionBannerController_instances.add(this);
|
|
136
153
|
_NetworkConnectionBannerController_degradedTimer.set(this, void 0);
|
|
137
154
|
_NetworkConnectionBannerController_unavailableTimer.set(this, void 0);
|
|
138
155
|
_NetworkConnectionBannerController_pendingNetworkClientId.set(this, void 0);
|
|
139
|
-
|
|
140
|
-
// Scoped selectors per controller guideline so unrelated upstream
|
|
141
|
-
// `stateChange` events (e.g. a `NetworkController` selected client id
|
|
142
|
-
// update) do not trigger a re-evaluation.
|
|
156
|
+
_NetworkConnectionBannerController_isStarted.set(this, false);
|
|
143
157
|
// Upstream controllers still expose :stateChange; switch to :stateChanged
|
|
144
158
|
// once those packages migrate their event types.
|
|
145
159
|
/* eslint-disable no-restricted-syntax -- awaiting upstream :stateChanged migration */
|
|
146
|
-
this.messenger.subscribe('NetworkController:stateChange', () => __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m",
|
|
147
|
-
this.messenger.subscribe('NetworkEnablementController:stateChange', () => __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m",
|
|
148
|
-
this.messenger.subscribe('ConnectivityController:stateChange', () => __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m",
|
|
160
|
+
this.messenger.subscribe('NetworkController:stateChange', (networkControllerState) => __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_refreshState).call(this, { networkControllerState }), selectNetworkControllerFields);
|
|
161
|
+
this.messenger.subscribe('NetworkEnablementController:stateChange', (networkEnablementControllerState) => __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_refreshState).call(this, { networkEnablementControllerState }), selectNetworkEnablementControllerFields);
|
|
162
|
+
this.messenger.subscribe('ConnectivityController:stateChange', (connectivityControllerState) => __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_refreshState).call(this, { connectivityControllerState }), selectConnectivityControllerFields);
|
|
149
163
|
/* eslint-enable no-restricted-syntax */
|
|
150
164
|
this.messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
|
|
151
165
|
}
|
|
152
166
|
/**
|
|
153
|
-
*
|
|
167
|
+
* Look for a failed network, if any, and populate the initial state of the
|
|
168
|
+
* banner. Reacts to upstream state changes from this point on.
|
|
154
169
|
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
170
|
+
* Call this when the wallet UI that consumes the banner becomes active
|
|
171
|
+
* (typically when the wallet is unlocked and the home surface mounts) so
|
|
172
|
+
* timers do not run while the user is not looking at the wallet. Should
|
|
173
|
+
* be called after `NetworkController`, `NetworkEnablementController`, and
|
|
174
|
+
* `ConnectivityController` have been initialized. Idempotent.
|
|
157
175
|
*/
|
|
158
|
-
|
|
159
|
-
if (__classPrivateFieldGet(this,
|
|
176
|
+
start() {
|
|
177
|
+
if (__classPrivateFieldGet(this, _NetworkConnectionBannerController_isStarted, "f")) {
|
|
160
178
|
return;
|
|
161
179
|
}
|
|
180
|
+
__classPrivateFieldSet(this, _NetworkConnectionBannerController_isStarted, true, "f");
|
|
162
181
|
__classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_refreshState).call(this);
|
|
163
|
-
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Stops evaluating network connection state. Clears any pending banner
|
|
185
|
+
* timers and resets state to `available`. Call this when the UI
|
|
186
|
+
* consuming the banner is no longer active. Idempotent.
|
|
187
|
+
*/
|
|
188
|
+
stop() {
|
|
189
|
+
if (!__classPrivateFieldGet(this, _NetworkConnectionBannerController_isStarted, "f")) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
__classPrivateFieldSet(this, _NetworkConnectionBannerController_isStarted, false, "f");
|
|
193
|
+
__classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_resetBanner).call(this);
|
|
164
194
|
}
|
|
165
195
|
/**
|
|
166
196
|
* Clears the banner state such that the banner will be hidden.
|
|
@@ -169,15 +199,14 @@ class NetworkConnectionBannerController extends base_controller_1.BaseController
|
|
|
169
199
|
__classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_resetBanner).call(this);
|
|
170
200
|
}
|
|
171
201
|
/**
|
|
172
|
-
* Switches the chain's default RPC endpoint to its
|
|
202
|
+
* Switches the chain's default RPC endpoint to its Infura endpoint,
|
|
173
203
|
* causing the banner to clear once the network becomes available again.
|
|
174
204
|
*
|
|
175
|
-
* @param
|
|
176
|
-
* @param args.chainId - The chain whose default RPC should be switched.
|
|
205
|
+
* @param chainId - The chain whose default RPC endpoint should be switched.
|
|
177
206
|
* @throws If the chain configuration cannot be found, or if it has no
|
|
178
207
|
* Infura endpoint to switch to, or if the default is already Infura.
|
|
179
208
|
*/
|
|
180
|
-
async
|
|
209
|
+
async switchToDefaultInfuraRpcEndpoint(chainId) {
|
|
181
210
|
const networkConfiguration = this.messenger.call('NetworkController:getNetworkConfigurationByChainId', chainId);
|
|
182
211
|
if (!networkConfiguration) {
|
|
183
212
|
throw new Error(`No network configuration found for chain ID "${chainId}".`);
|
|
@@ -197,17 +226,21 @@ class NetworkConnectionBannerController extends base_controller_1.BaseController
|
|
|
197
226
|
}
|
|
198
227
|
}
|
|
199
228
|
exports.NetworkConnectionBannerController = NetworkConnectionBannerController;
|
|
200
|
-
_NetworkConnectionBannerController_degradedTimer = new WeakMap(), _NetworkConnectionBannerController_unavailableTimer = new WeakMap(), _NetworkConnectionBannerController_pendingNetworkClientId = new WeakMap(),
|
|
201
|
-
if (__classPrivateFieldGet(this,
|
|
202
|
-
|
|
229
|
+
_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, } = {}) {
|
|
230
|
+
if (!__classPrivateFieldGet(this, _NetworkConnectionBannerController_isStarted, "f")) {
|
|
231
|
+
return;
|
|
203
232
|
}
|
|
204
|
-
}
|
|
205
|
-
|
|
233
|
+
const { connectivityStatus } = connectivityControllerState ??
|
|
234
|
+
this.messenger.call('ConnectivityController:getState');
|
|
206
235
|
if (connectivityStatus === connectivity_controller_1.CONNECTIVITY_STATUSES.Offline) {
|
|
207
236
|
__classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_resetBanner).call(this);
|
|
208
237
|
return;
|
|
209
238
|
}
|
|
210
|
-
const
|
|
239
|
+
const networkState = networkControllerState ??
|
|
240
|
+
this.messenger.call('NetworkController:getState');
|
|
241
|
+
const enablementState = networkEnablementControllerState ??
|
|
242
|
+
this.messenger.call('NetworkEnablementController:getState');
|
|
243
|
+
const failedNetwork = __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_findFailedNetwork).call(this, networkState, enablementState);
|
|
211
244
|
if (!failedNetwork) {
|
|
212
245
|
__classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_resetBanner).call(this);
|
|
213
246
|
return;
|
|
@@ -227,6 +260,11 @@ _NetworkConnectionBannerController_degradedTimer = new WeakMap(), _NetworkConnec
|
|
|
227
260
|
state.status = 'available';
|
|
228
261
|
state.network = null;
|
|
229
262
|
});
|
|
263
|
+
// A synchronous listener on our `stateChanged` event above may have
|
|
264
|
+
// called `stop()` re-entrantly. Bail before scheduling anything.
|
|
265
|
+
if (!__classPrivateFieldGet(this, _NetworkConnectionBannerController_isStarted, "f")) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
230
268
|
// Capture the failing network at schedule time. We trust the messenger
|
|
231
269
|
// contract: if the failure resolves or the target changes during the
|
|
232
270
|
// wait, our upstream subscriptions will have cancelled or replaced this
|
|
@@ -239,6 +277,11 @@ _NetworkConnectionBannerController_degradedTimer = new WeakMap(), _NetworkConnec
|
|
|
239
277
|
state.status = 'degraded';
|
|
240
278
|
state.network = failedNetwork;
|
|
241
279
|
});
|
|
280
|
+
// A synchronous listener on our `stateChanged` event above may have
|
|
281
|
+
// called `stop()` re-entrantly. Bail before scheduling the escalation.
|
|
282
|
+
if (!__classPrivateFieldGet(this, _NetworkConnectionBannerController_isStarted, "f")) {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
242
285
|
__classPrivateFieldSet(this, _NetworkConnectionBannerController_unavailableTimer, setTimeout(() => {
|
|
243
286
|
__classPrivateFieldSet(this, _NetworkConnectionBannerController_unavailableTimer, undefined, "f");
|
|
244
287
|
this.update((state) => {
|
|
@@ -265,20 +308,18 @@ _NetworkConnectionBannerController_degradedTimer = new WeakMap(), _NetworkConnec
|
|
|
265
308
|
clearTimeout(__classPrivateFieldGet(this, _NetworkConnectionBannerController_unavailableTimer, "f"));
|
|
266
309
|
__classPrivateFieldSet(this, _NetworkConnectionBannerController_unavailableTimer, undefined, "f");
|
|
267
310
|
}
|
|
268
|
-
}, _NetworkConnectionBannerController_findFailedNetwork = function _NetworkConnectionBannerController_findFailedNetwork() {
|
|
269
|
-
const networksWithMetadata = __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_collectNetworksWithMetadata).call(this);
|
|
311
|
+
}, _NetworkConnectionBannerController_findFailedNetwork = function _NetworkConnectionBannerController_findFailedNetwork(networkState, enablementState) {
|
|
312
|
+
const networksWithMetadata = __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_collectNetworksWithMetadata).call(this, networkState, enablementState);
|
|
270
313
|
const failedNetworks = networksWithMetadata
|
|
271
314
|
.filter(({ metadata }) => metadata.status !== network_controller_1.NetworkStatus.Available)
|
|
272
315
|
.map((network) => __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_buildFailedNetwork).call(this, network));
|
|
273
316
|
return __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_pickBannerNetwork).call(this, failedNetworks, networksWithMetadata.length);
|
|
274
|
-
}, _NetworkConnectionBannerController_getEnabledEvmChainIds = function _NetworkConnectionBannerController_getEnabledEvmChainIds() {
|
|
275
|
-
const { enabledNetworkMap } = this.messenger.call('NetworkEnablementController:getState');
|
|
317
|
+
}, _NetworkConnectionBannerController_getEnabledEvmChainIds = function _NetworkConnectionBannerController_getEnabledEvmChainIds(enabledNetworkMap) {
|
|
276
318
|
return Object.entries(enabledNetworkMap[utils_1.KnownCaipNamespace.Eip155] ?? {})
|
|
277
319
|
.filter(([, enabled]) => enabled)
|
|
278
320
|
.map(([chainId]) => chainId);
|
|
279
|
-
}, _NetworkConnectionBannerController_collectNetworksWithMetadata = function _NetworkConnectionBannerController_collectNetworksWithMetadata() {
|
|
280
|
-
|
|
281
|
-
return __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_getEnabledEvmChainIds).call(this).flatMap((chainId) => {
|
|
321
|
+
}, _NetworkConnectionBannerController_collectNetworksWithMetadata = function _NetworkConnectionBannerController_collectNetworksWithMetadata({ networkConfigurationsByChainId, networksMetadata, }, { enabledNetworkMap, }) {
|
|
322
|
+
return __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_getEnabledEvmChainIds).call(this, enabledNetworkMap).flatMap((chainId) => {
|
|
282
323
|
const networkConfiguration = networkConfigurationsByChainId[chainId];
|
|
283
324
|
if (!networkConfiguration) {
|
|
284
325
|
return [];
|
|
@@ -309,9 +350,9 @@ _NetworkConnectionBannerController_degradedTimer = new WeakMap(), _NetworkConnec
|
|
|
309
350
|
// chain that we could offer to switch to.
|
|
310
351
|
let switchableInfuraNetworkClientId = null;
|
|
311
352
|
if (!isInfuraEndpoint) {
|
|
312
|
-
const
|
|
353
|
+
const infuraEndpoint = rpcEndpoints.find((endpoint, index) => index !== defaultRpcEndpointIndex &&
|
|
313
354
|
getIsInfuraEndpoint(endpoint.url));
|
|
314
|
-
switchableInfuraNetworkClientId =
|
|
355
|
+
switchableInfuraNetworkClientId = infuraEndpoint?.networkClientId ?? null;
|
|
315
356
|
}
|
|
316
357
|
return {
|
|
317
358
|
chainId,
|
|
@@ -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 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 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 { getDomain } from './url-utils';\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 * 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 controllerName = 'NetworkConnectionBannerController';\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 * Details of a failing network the banner describes.\n */\nexport type FailedNetwork = {\n chainId: Hex;\n networkClientId: string;\n name: string;\n rpcUrl: string;\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 * The registrable domain (eTLD+1) of `rpcUrl`, used to group endpoints by\n * provider. `null` when the URL is invalid.\n */\n domain: string | null;\n};\n\n/**\n * State for the {@link NetworkConnectionBannerController}.\n */\nexport type NetworkConnectionBannerControllerState = {\n status: NetworkConnectionBannerStatus;\n network: FailedNetwork | null;\n};\n\nconst networkConnectionBannerControllerMetadata = {\n status: {\n persist: false,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: true,\n },\n network: {\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 status: 'available',\n network: 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 'init',\n 'dismissBanner',\n 'switchToDefaultInfuraRpc',\n] as const;\n\n/**\n * Retrieves the state of the {@link NetworkConnectionBannerController}.\n */\nexport type NetworkConnectionBannerControllerGetStateAction =\n ControllerGetStateAction<\n typeof controllerName,\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 controllerName,\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 controllerName,\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, the 5s / 30s timer escalation, and the\n * eTLD+1 grouping that was previously duplicated across MetaMask clients.\n *\n * The banner shows when:\n *\n * - The first 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 * - Failed RPCs span more than one registrable domain (likely client-side).\n * - Every enabled EVM network with known connectivity status is failing\n * (escape hatch for single-network setups so they still get a signal).\n *\n * A wide single-provider outage (e.g. every `*.infura.io` network goes down at\n * once) collapses to one domain and is suppressed, except in the all-down\n * single-network case. When a custom failure is present, it's surfaced first\n * so the \"Switch to MetaMask default RPC\" CTA targets the network the user\n * 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 switchToDefaultInfuraRpc}.\n */\nexport class NetworkConnectionBannerController extends BaseController<\n typeof controllerName,\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 #initialized = 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: controllerName,\n state: getDefaultNetworkConnectionBannerControllerState(),\n });\n\n // Scoped selectors per controller guideline so unrelated upstream\n // `stateChange` events (e.g. a `NetworkController` selected client id\n // update) do not trigger a re-evaluation.\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 () => this.#onUpstreamChange(),\n selectNetworkControllerFields,\n );\n this.messenger.subscribe(\n 'NetworkEnablementController:stateChange',\n () => this.#onUpstreamChange(),\n selectEnabledNetworkMap,\n );\n this.messenger.subscribe(\n 'ConnectivityController:stateChange',\n () => this.#onUpstreamChange(),\n connectivityControllerSelectors.selectConnectivityStatus,\n );\n /* eslint-enable no-restricted-syntax */\n\n this.messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n /**\n * Starts evaluating network connection state.\n *\n * This method should be called after the upstream network, network\n * enablement, and connectivity controllers have been initialized.\n */\n init(): void {\n if (this.#initialized) {\n return;\n }\n\n this.#refreshState();\n this.#initialized = true;\n }\n\n #onUpstreamChange(): void {\n if (this.#initialized) {\n this.#refreshState();\n }\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 first Infura endpoint,\n * causing the banner to clear once the network becomes available again.\n *\n * @param args - The arguments to this action.\n * @param args.chainId - The chain whose default RPC 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 switchToDefaultInfuraRpc({ chainId }: { 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(): void {\n const { connectivityStatus } = this.messenger.call(\n 'ConnectivityController:getState',\n );\n if (connectivityStatus === CONNECTIVITY_STATUSES.Offline) {\n this.#resetBanner();\n return;\n }\n\n const failedNetwork = this.#findFailedNetwork();\n\n if (!failedNetwork) {\n this.#resetBanner();\n return;\n }\n\n if (\n this.state.status !== 'available' &&\n this.state.network?.networkClientId === failedNetwork.networkClientId\n ) {\n this.update((state) => {\n state.network = failedNetwork;\n });\n return;\n }\n\n if (this.#pendingNetworkClientId === failedNetwork.networkClientId) {\n return;\n }\n\n this.#clearTimers();\n this.update((state) => {\n state.status = 'available';\n state.network = null;\n });\n\n // Capture the failing network at schedule time. We trust the messenger\n // contract: if the failure resolves or the target changes during the\n // wait, our upstream subscriptions will have cancelled or replaced this\n // timer via `#clearTimers` 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.status = 'degraded';\n state.network = failedNetwork;\n });\n this.#unavailableTimer = setTimeout(() => {\n this.#unavailableTimer = undefined;\n this.update((state) => {\n state.status = 'unavailable';\n state.network = 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 (this.state.status !== 'available' || this.state.network !== null) {\n this.update((state) => {\n state.status = 'available';\n state.network = 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(): FailedNetwork | null {\n const networksWithMetadata = this.#collectNetworksWithMetadata();\n const failedNetworks = networksWithMetadata\n .filter(({ metadata }) => metadata.status !== NetworkStatus.Available)\n .map((network) => this.#buildFailedNetwork(network));\n return this.#pickBannerNetwork(failedNetworks, networksWithMetadata.length);\n }\n\n #getEnabledEvmChainIds(): Hex[] {\n const { enabledNetworkMap } = this.messenger.call(\n 'NetworkEnablementController:getState',\n );\n return Object.entries(enabledNetworkMap[KnownCaipNamespace.Eip155] ?? {})\n .filter(([, enabled]) => enabled)\n .map(([chainId]) => chainId as Hex);\n }\n\n #collectNetworksWithMetadata(): NetworkWithMetadata[] {\n const { networkConfigurationsByChainId, networksMetadata } =\n this.messenger.call('NetworkController:getState');\n\n return this.#getEnabledEvmChainIds().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 const metadata = networksMetadata[defaultRpcEndpoint.networkClientId];\n if (!metadata) {\n return [];\n }\n return [\n {\n chainId,\n name,\n rpcEndpoints,\n defaultRpcEndpointIndex,\n defaultRpcEndpoint,\n metadata,\n },\n ];\n });\n }\n\n #buildFailedNetwork({\n chainId,\n name,\n rpcEndpoints,\n defaultRpcEndpointIndex,\n defaultRpcEndpoint,\n }: NetworkWithMetadata): 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 otherInfura = rpcEndpoints.find(\n (endpoint, index) =>\n index !== defaultRpcEndpointIndex &&\n getIsInfuraEndpoint(endpoint.url),\n );\n switchableInfuraNetworkClientId = otherInfura?.networkClientId ?? null;\n }\n\n return {\n chainId,\n networkClientId: defaultRpcEndpoint.networkClientId,\n name,\n rpcUrl: defaultRpcEndpoint.url,\n isInfuraEndpoint,\n switchableInfuraNetworkClientId,\n domain: getDomain(defaultRpcEndpoint.url),\n };\n }\n\n #pickBannerNetwork(\n failedNetworks: FailedNetwork[],\n totalNetworksWithMetadata: 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 distinctDomains = new Set(\n failedNetworks\n .map((entry) => entry.domain)\n .filter((domain): domain is string => domain !== null),\n ).size;\n const areAllKnownNetworksFailed =\n failedNetworks.length === totalNetworksWithMetadata;\n\n if (\n !firstCustomFailed &&\n distinctDomains <= 1 &&\n !areAllKnownNetworksFailed\n ) {\n return null;\n }\n\n return firstCustomFailed ?? failedNetworks[0];\n }\n}\n\ntype NetworkWithMetadata = {\n chainId: Hex;\n name: string;\n rpcEndpoints: NetworkConfiguration['rpcEndpoints'];\n defaultRpcEndpointIndex: number;\n defaultRpcEndpoint: NetworkConfiguration['rpcEndpoints'][number];\n metadata: NetworkMetadata;\n};\n\n/**\n * Whether an RPC URL is a MetaMask Infura endpoint. Matches the\n * `{infuraProjectId}` placeholder form that lives on stored network\n * configurations before the wallet substitutes the real project id at\n * request time. URLs that already carry a substituted key are treated as\n * custom (we do not have the project id in this package).\n *\n * @param url - The RPC URL to check.\n * @returns True if the URL is the placeholder form of a MetaMask Infura\n * endpoint.\n */\nfunction getIsInfuraEndpoint(url: string): boolean {\n return /^https:\\/\\/[^./]+\\.infura\\.io\\/v3\\/\\{infuraProjectId\\}$/u.test(url);\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 {\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 { getDomain } 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 * A network from `NetworkController` state that has a default RPC endpoint\n * with a known metadata status. Used as the input to the failed-network\n * detection pipeline.\n */\ntype NetworkWithMetadata = {\n chainId: Hex;\n name: string;\n rpcEndpoints: NetworkConfiguration['rpcEndpoints'];\n defaultRpcEndpointIndex: number;\n defaultRpcEndpoint: NetworkConfiguration['rpcEndpoints'][number];\n metadata: NetworkMetadata;\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 * The registrable domain (eTLD+1) of `rpcUrl`, used to group endpoints by\n * provider. `null` when the URL is invalid.\n */\n domain: string | null;\n};\n\n/**\n * State for the {@link NetworkConnectionBannerController}.\n */\nexport type NetworkConnectionBannerControllerState = {\n status: NetworkConnectionBannerStatus;\n network: FailedNetwork | null;\n};\n\nconst networkConnectionBannerControllerMetadata = {\n status: {\n persist: false,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: true,\n },\n network: {\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 status: 'available',\n network: 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, the 5s / 30s timer escalation, and the\n * eTLD+1 grouping that was previously duplicated across MetaMask clients.\n *\n * The banner shows when:\n *\n * - The first 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 * - Failed RPCs span more than one registrable domain (likely client-side).\n * - Every enabled EVM network with known connectivity status is failing\n * (escape hatch for single-network setups so they still get a signal).\n *\n * A wide single-provider outage (e.g. every `*.infura.io` network goes down at\n * once) collapses to one domain and is suppressed, except in the all-down\n * single-network case. When a custom failure is present, it's surfaced first\n * so the \"Switch to MetaMask default RPC\" CTA targets the network the user\n * 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({ networkControllerState }),\n selectNetworkControllerFields,\n );\n this.messenger.subscribe(\n 'NetworkEnablementController:stateChange',\n (networkEnablementControllerState) =>\n this.#refreshState({ networkEnablementControllerState }),\n selectNetworkEnablementControllerFields,\n );\n this.messenger.subscribe(\n 'ConnectivityController:stateChange',\n (connectivityControllerState) =>\n this.#refreshState({ connectivityControllerState }),\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 }\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 const { connectivityStatus } =\n connectivityControllerState ??\n this.messenger.call('ConnectivityController:getState');\n if (connectivityStatus === CONNECTIVITY_STATUSES.Offline) {\n this.#resetBanner();\n return;\n }\n\n const networkState =\n networkControllerState ??\n this.messenger.call('NetworkController:getState');\n const enablementState =\n networkEnablementControllerState ??\n this.messenger.call('NetworkEnablementController:getState');\n\n const failedNetwork = this.#findFailedNetwork(\n networkState,\n enablementState,\n );\n if (!failedNetwork) {\n this.#resetBanner();\n return;\n }\n\n if (\n this.state.status !== 'available' &&\n this.state.network?.networkClientId === failedNetwork.networkClientId\n ) {\n this.update((state) => {\n state.network = failedNetwork;\n });\n return;\n }\n\n if (this.#pendingNetworkClientId === failedNetwork.networkClientId) {\n return;\n }\n\n this.#clearTimers();\n this.update((state) => {\n state.status = 'available';\n state.network = null;\n });\n\n // A synchronous listener on our `stateChanged` event above may have\n // called `stop()` re-entrantly. Bail before scheduling anything.\n if (!this.#isStarted) {\n return;\n }\n\n // Capture the failing network at schedule time. We trust the messenger\n // contract: if the failure resolves or the target changes during the\n // wait, our upstream subscriptions will have cancelled or replaced this\n // timer via `#clearTimers` 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.status = 'degraded';\n state.network = failedNetwork;\n });\n // A synchronous listener on our `stateChanged` event above may have\n // called `stop()` re-entrantly. Bail before scheduling the escalation.\n if (!this.#isStarted) {\n return;\n }\n this.#unavailableTimer = setTimeout(() => {\n this.#unavailableTimer = undefined;\n this.update((state) => {\n state.status = 'unavailable';\n state.network = 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 (this.state.status !== 'available' || this.state.network !== null) {\n this.update((state) => {\n state.status = 'available';\n state.network = 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 networksWithMetadata = this.#collectNetworksWithMetadata(\n networkState,\n enablementState,\n );\n const failedNetworks = networksWithMetadata\n .filter(({ metadata }) => metadata.status !== NetworkStatus.Available)\n .map((network) => this.#buildFailedNetwork(network));\n return this.#pickBannerNetwork(failedNetworks, networksWithMetadata.length);\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 #collectNetworksWithMetadata(\n {\n networkConfigurationsByChainId,\n networksMetadata,\n }: Pick<\n NetworkState,\n 'networkConfigurationsByChainId' | 'networksMetadata'\n >,\n {\n enabledNetworkMap,\n }: Pick<NetworkEnablementControllerState, 'enabledNetworkMap'>,\n ): NetworkWithMetadata[] {\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 const metadata = networksMetadata[defaultRpcEndpoint.networkClientId];\n if (!metadata) {\n return [];\n }\n return [\n {\n chainId,\n name,\n rpcEndpoints,\n defaultRpcEndpointIndex,\n defaultRpcEndpoint,\n metadata,\n },\n ];\n });\n }\n\n #buildFailedNetwork({\n chainId,\n name,\n rpcEndpoints,\n defaultRpcEndpointIndex,\n defaultRpcEndpoint,\n }: NetworkWithMetadata): 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 domain: getDomain(defaultRpcEndpoint.url),\n };\n }\n\n #pickBannerNetwork(\n failedNetworks: FailedNetwork[],\n totalNetworksWithMetadata: 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 distinctDomains = new Set(\n failedNetworks\n .map((entry) => entry.domain)\n .filter((domain): domain is string => domain !== null),\n ).size;\n const areAllKnownNetworksFailed =\n failedNetworks.length === totalNetworksWithMetadata;\n\n if (\n !firstCustomFailed &&\n distinctDomains <= 1 &&\n !areAllKnownNetworksFailed\n ) {\n return null;\n }\n\n return firstCustomFailed ?? failedNetworks[0];\n }\n}\n\n/**\n * Whether an RPC URL is a MetaMask Infura endpoint. Matches the\n * `{infuraProjectId}` placeholder form that lives on stored network\n * configurations before the wallet substitutes the real project id at\n * request time. URLs that already carry a substituted key are treated as\n * custom (we do not have the project id in this package).\n *\n * @param url - The RPC URL to check.\n * @returns True if the URL is the placeholder form of a MetaMask Infura\n * endpoint.\n */\nfunction getIsInfuraEndpoint(url: string): boolean {\n return /^https:\\/\\/[^./]+\\.infura\\.io\\/v3\\/\\{infuraProjectId\\}$/u.test(url);\n}\n"]}
|