@metamask-previews/network-connection-banner-controller 0.1.0-preview-7507a11
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/CHANGELOG.md +14 -0
- package/LICENSE +20 -0
- package/README.md +18 -0
- package/dist/NetworkConnectionBannerController-method-action-types.cjs +7 -0
- package/dist/NetworkConnectionBannerController-method-action-types.cjs.map +1 -0
- package/dist/NetworkConnectionBannerController-method-action-types.d.cts +32 -0
- package/dist/NetworkConnectionBannerController-method-action-types.d.cts.map +1 -0
- package/dist/NetworkConnectionBannerController-method-action-types.d.mts +32 -0
- package/dist/NetworkConnectionBannerController-method-action-types.d.mts.map +1 -0
- package/dist/NetworkConnectionBannerController-method-action-types.mjs +6 -0
- package/dist/NetworkConnectionBannerController-method-action-types.mjs.map +1 -0
- package/dist/NetworkConnectionBannerController.cjs +288 -0
- package/dist/NetworkConnectionBannerController.cjs.map +1 -0
- package/dist/NetworkConnectionBannerController.d.cts +146 -0
- package/dist/NetworkConnectionBannerController.d.cts.map +1 -0
- package/dist/NetworkConnectionBannerController.d.mts +146 -0
- package/dist/NetworkConnectionBannerController.d.mts.map +1 -0
- package/dist/NetworkConnectionBannerController.mjs +283 -0
- package/dist/NetworkConnectionBannerController.mjs.map +1 -0
- package/dist/index.cjs +10 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +5 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +5 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +3 -0
- package/dist/index.mjs.map +1 -0
- package/dist/url-utils.cjs +55 -0
- package/dist/url-utils.cjs.map +1 -0
- package/dist/url-utils.d.cts +24 -0
- package/dist/url-utils.d.cts.map +1 -0
- package/dist/url-utils.d.mts +24 -0
- package/dist/url-utils.d.mts.map +1 -0
- package/dist/url-utils.mjs +54 -0
- package/dist/url-utils.mjs.map +1 -0
- package/package.json +80 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Initial release ([#9041](https://github.com/MetaMask/core/pull/9041))
|
|
13
|
+
|
|
14
|
+
[Unreleased]: https://github.com/MetaMask/core/
|
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 MetaMask
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
package/README.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# `@metamask/network-connection-banner-controller`
|
|
2
|
+
|
|
3
|
+
NetworkConnectionBannerController decides when and how to surface the network
|
|
4
|
+
connection banner based on RPC endpoint health. It encapsulates the shared
|
|
5
|
+
rule, the 5s/30s timer state machine, and the eTLD+1 grouping previously
|
|
6
|
+
duplicated across MetaMask clients.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
`yarn add @metamask/network-connection-banner-controller`
|
|
11
|
+
|
|
12
|
+
or
|
|
13
|
+
|
|
14
|
+
`npm install @metamask/network-connection-banner-controller`
|
|
15
|
+
|
|
16
|
+
## Contributing
|
|
17
|
+
|
|
18
|
+
This package is part of a monorepo. Instructions for contributing can be found in the [monorepo README](https://github.com/MetaMask/core#readme).
|
|
@@ -0,0 +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 * Clears the banner state regardless of the current rule outcome. The next\n * subscription-driven evaluation will re-show the banner if the conditions\n * still hold.\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 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 */\nexport type NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcAction = {\n type: `NetworkConnectionBannerController:switchToDefaultInfuraRpc`;\n handler: NetworkConnectionBannerController['switchToDefaultInfuraRpc'];\n};\n\n/**\n * Union of all NetworkConnectionBannerController action types.\n */\nexport type NetworkConnectionBannerControllerMethodActions =\n | NetworkConnectionBannerControllerDismissBannerAction\n | NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcAction;\n"]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is auto generated.
|
|
3
|
+
* Do not edit manually.
|
|
4
|
+
*/
|
|
5
|
+
import type { NetworkConnectionBannerController } from "./NetworkConnectionBannerController.cjs";
|
|
6
|
+
/**
|
|
7
|
+
* Clears the banner state regardless of the current rule outcome. The next
|
|
8
|
+
* subscription-driven evaluation will re-show the banner if the conditions
|
|
9
|
+
* still hold.
|
|
10
|
+
*/
|
|
11
|
+
export type NetworkConnectionBannerControllerDismissBannerAction = {
|
|
12
|
+
type: `NetworkConnectionBannerController:dismissBanner`;
|
|
13
|
+
handler: NetworkConnectionBannerController['dismissBanner'];
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Switches the chain's default RPC endpoint to its first Infura endpoint,
|
|
17
|
+
* causing the banner to clear once the network becomes available again.
|
|
18
|
+
*
|
|
19
|
+
* @param args - The arguments to this action.
|
|
20
|
+
* @param args.chainId - The chain whose default RPC should be switched.
|
|
21
|
+
* @throws If the chain configuration cannot be found, or if it has no
|
|
22
|
+
* Infura endpoint to switch to, or if the default is already Infura.
|
|
23
|
+
*/
|
|
24
|
+
export type NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcAction = {
|
|
25
|
+
type: `NetworkConnectionBannerController:switchToDefaultInfuraRpc`;
|
|
26
|
+
handler: NetworkConnectionBannerController['switchToDefaultInfuraRpc'];
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Union of all NetworkConnectionBannerController action types.
|
|
30
|
+
*/
|
|
31
|
+
export type NetworkConnectionBannerControllerMethodActions = NetworkConnectionBannerControllerDismissBannerAction | NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcAction;
|
|
32
|
+
//# sourceMappingURL=NetworkConnectionBannerController-method-action-types.d.cts.map
|
|
@@ -0,0 +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;;;;GAIG;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,+DAA+D,GAAG;IAC5E,IAAI,EAAE,4DAA4D,CAAC;IACnE,OAAO,EAAE,iCAAiC,CAAC,0BAA0B,CAAC,CAAC;CACxE,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,8CAA8C,GACtD,oDAAoD,GACpD,+DAA+D,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is auto generated.
|
|
3
|
+
* Do not edit manually.
|
|
4
|
+
*/
|
|
5
|
+
import type { NetworkConnectionBannerController } from "./NetworkConnectionBannerController.mjs";
|
|
6
|
+
/**
|
|
7
|
+
* Clears the banner state regardless of the current rule outcome. The next
|
|
8
|
+
* subscription-driven evaluation will re-show the banner if the conditions
|
|
9
|
+
* still hold.
|
|
10
|
+
*/
|
|
11
|
+
export type NetworkConnectionBannerControllerDismissBannerAction = {
|
|
12
|
+
type: `NetworkConnectionBannerController:dismissBanner`;
|
|
13
|
+
handler: NetworkConnectionBannerController['dismissBanner'];
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Switches the chain's default RPC endpoint to its first Infura endpoint,
|
|
17
|
+
* causing the banner to clear once the network becomes available again.
|
|
18
|
+
*
|
|
19
|
+
* @param args - The arguments to this action.
|
|
20
|
+
* @param args.chainId - The chain whose default RPC should be switched.
|
|
21
|
+
* @throws If the chain configuration cannot be found, or if it has no
|
|
22
|
+
* Infura endpoint to switch to, or if the default is already Infura.
|
|
23
|
+
*/
|
|
24
|
+
export type NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcAction = {
|
|
25
|
+
type: `NetworkConnectionBannerController:switchToDefaultInfuraRpc`;
|
|
26
|
+
handler: NetworkConnectionBannerController['switchToDefaultInfuraRpc'];
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Union of all NetworkConnectionBannerController action types.
|
|
30
|
+
*/
|
|
31
|
+
export type NetworkConnectionBannerControllerMethodActions = NetworkConnectionBannerControllerDismissBannerAction | NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcAction;
|
|
32
|
+
//# sourceMappingURL=NetworkConnectionBannerController-method-action-types.d.mts.map
|
|
@@ -0,0 +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;;;;GAIG;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,+DAA+D,GAAG;IAC5E,IAAI,EAAE,4DAA4D,CAAC;IACnE,OAAO,EAAE,iCAAiC,CAAC,0BAA0B,CAAC,CAAC;CACxE,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,8CAA8C,GACtD,oDAAoD,GACpD,+DAA+D,CAAC"}
|
|
@@ -0,0 +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 * Clears the banner state regardless of the current rule outcome. The next\n * subscription-driven evaluation will re-show the banner if the conditions\n * still hold.\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 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 */\nexport type NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcAction = {\n type: `NetworkConnectionBannerController:switchToDefaultInfuraRpc`;\n handler: NetworkConnectionBannerController['switchToDefaultInfuraRpc'];\n};\n\n/**\n * Union of all NetworkConnectionBannerController action types.\n */\nexport type NetworkConnectionBannerControllerMethodActions =\n | NetworkConnectionBannerControllerDismissBannerAction\n | NetworkConnectionBannerControllerSwitchToDefaultInfuraRpcAction;\n"]}
|
|
@@ -0,0 +1,288 @@
|
|
|
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
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
8
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
9
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
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
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
12
|
+
};
|
|
13
|
+
var _NetworkConnectionBannerController_instances, _NetworkConnectionBannerController_degradedTimer, _NetworkConnectionBannerController_unavailableTimer, _NetworkConnectionBannerController_evaluate, _NetworkConnectionBannerController_resetBanner, _NetworkConnectionBannerController_clearTimers, _NetworkConnectionBannerController_findFailedNetworkForBanner;
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.NetworkConnectionBannerController = exports.getDefaultNetworkConnectionBannerControllerState = exports.controllerName = void 0;
|
|
16
|
+
const base_controller_1 = require("@metamask/base-controller");
|
|
17
|
+
const connectivity_controller_1 = require("@metamask/connectivity-controller");
|
|
18
|
+
const network_controller_1 = require("@metamask/network-controller");
|
|
19
|
+
const utils_1 = require("@metamask/utils");
|
|
20
|
+
const url_utils_1 = require("./url-utils.cjs");
|
|
21
|
+
/**
|
|
22
|
+
* The name of the {@link NetworkConnectionBannerController}, used to namespace
|
|
23
|
+
* the controller's actions and events and to namespace the controller's state
|
|
24
|
+
* data when composed with other controllers.
|
|
25
|
+
*/
|
|
26
|
+
exports.controllerName = 'NetworkConnectionBannerController';
|
|
27
|
+
const networkConnectionBannerControllerMetadata = {
|
|
28
|
+
status: {
|
|
29
|
+
persist: false,
|
|
30
|
+
includeInDebugSnapshot: true,
|
|
31
|
+
includeInStateLogs: true,
|
|
32
|
+
usedInUi: true,
|
|
33
|
+
},
|
|
34
|
+
network: {
|
|
35
|
+
persist: false,
|
|
36
|
+
includeInDebugSnapshot: true,
|
|
37
|
+
includeInStateLogs: true,
|
|
38
|
+
usedInUi: true,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Constructs the default {@link NetworkConnectionBannerController} state.
|
|
43
|
+
*
|
|
44
|
+
* @returns The default state.
|
|
45
|
+
*/
|
|
46
|
+
function getDefaultNetworkConnectionBannerControllerState() {
|
|
47
|
+
return {
|
|
48
|
+
status: 'available',
|
|
49
|
+
network: null,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
exports.getDefaultNetworkConnectionBannerControllerState = getDefaultNetworkConnectionBannerControllerState;
|
|
53
|
+
const DEGRADED_BANNER_TIMEOUT_MS = 5000;
|
|
54
|
+
const UNAVAILABLE_BANNER_TIMEOUT_MS = 30000;
|
|
55
|
+
const MESSENGER_EXPOSED_METHODS = [
|
|
56
|
+
'dismissBanner',
|
|
57
|
+
'switchToDefaultInfuraRpc',
|
|
58
|
+
];
|
|
59
|
+
/**
|
|
60
|
+
* NetworkConnectionBannerController decides whether the network connection
|
|
61
|
+
* banner should be shown for the user, and which failing network it should
|
|
62
|
+
* describe. It encapsulates the rule, the 5s / 30s timer escalation, and the
|
|
63
|
+
* eTLD+1 grouping that was previously duplicated across MetaMask clients.
|
|
64
|
+
*
|
|
65
|
+
* The banner shows when:
|
|
66
|
+
*
|
|
67
|
+
* - The first failing network's default RPC is a custom (non-Infura) endpoint
|
|
68
|
+
* — users always want to be informed about errors with RPCs they've chosen.
|
|
69
|
+
* - Failed RPCs span more than one registrable domain (likely client-side).
|
|
70
|
+
* - Every enabled EVM network is failing (escape hatch for single-network
|
|
71
|
+
* setups so they still get a signal).
|
|
72
|
+
*
|
|
73
|
+
* A wide single-provider outage (e.g. every `*.infura.io` network goes down at
|
|
74
|
+
* once) collapses to one domain and is suppressed, except in the all-down
|
|
75
|
+
* single-network case. When a custom failure is present, it's surfaced first
|
|
76
|
+
* so the "Switch to MetaMask default RPC" CTA targets the network the user
|
|
77
|
+
* can act on.
|
|
78
|
+
*
|
|
79
|
+
* Clients only need to render the banner from the controller's state and wire
|
|
80
|
+
* click handlers to {@link dismissBanner} or {@link switchToDefaultInfuraRpc}.
|
|
81
|
+
*/
|
|
82
|
+
class NetworkConnectionBannerController extends base_controller_1.BaseController {
|
|
83
|
+
/**
|
|
84
|
+
* Constructs a new {@link NetworkConnectionBannerController}.
|
|
85
|
+
*
|
|
86
|
+
* @param args - The arguments to this controller.
|
|
87
|
+
* @param args.messenger - The messenger suited for this controller.
|
|
88
|
+
*/
|
|
89
|
+
constructor({ messenger }) {
|
|
90
|
+
super({
|
|
91
|
+
messenger,
|
|
92
|
+
metadata: networkConnectionBannerControllerMetadata,
|
|
93
|
+
name: exports.controllerName,
|
|
94
|
+
state: getDefaultNetworkConnectionBannerControllerState(),
|
|
95
|
+
});
|
|
96
|
+
_NetworkConnectionBannerController_instances.add(this);
|
|
97
|
+
_NetworkConnectionBannerController_degradedTimer.set(this, void 0);
|
|
98
|
+
_NetworkConnectionBannerController_unavailableTimer.set(this, void 0);
|
|
99
|
+
const onStateChange = () => __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_evaluate).call(this);
|
|
100
|
+
this.messenger.subscribe('NetworkController:stateChanged', onStateChange);
|
|
101
|
+
this.messenger.subscribe('NetworkEnablementController:stateChanged', onStateChange);
|
|
102
|
+
this.messenger.subscribe('ConnectivityController:stateChanged', onStateChange);
|
|
103
|
+
this.messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Clears the banner state regardless of the current rule outcome. The next
|
|
107
|
+
* subscription-driven evaluation will re-show the banner if the conditions
|
|
108
|
+
* still hold.
|
|
109
|
+
*/
|
|
110
|
+
dismissBanner() {
|
|
111
|
+
__classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_resetBanner).call(this);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Switches the chain's default RPC endpoint to its first Infura endpoint,
|
|
115
|
+
* causing the banner to clear once the network becomes available again.
|
|
116
|
+
*
|
|
117
|
+
* @param args - The arguments to this action.
|
|
118
|
+
* @param args.chainId - The chain whose default RPC should be switched.
|
|
119
|
+
* @throws If the chain configuration cannot be found, or if it has no
|
|
120
|
+
* Infura endpoint to switch to, or if the default is already Infura.
|
|
121
|
+
*/
|
|
122
|
+
async switchToDefaultInfuraRpc({ chainId }) {
|
|
123
|
+
const networkConfiguration = this.messenger.call('NetworkController:getNetworkConfigurationByChainId', chainId);
|
|
124
|
+
if (!networkConfiguration) {
|
|
125
|
+
throw new Error(`No network configuration found for chain ID "${chainId}".`);
|
|
126
|
+
}
|
|
127
|
+
const infuraEndpointIndex = networkConfiguration.rpcEndpoints.findIndex((endpoint) => isInfuraEndpoint(endpoint.url));
|
|
128
|
+
if (infuraEndpointIndex === -1) {
|
|
129
|
+
throw new Error(`No Infura endpoint available for chain ID "${chainId}".`);
|
|
130
|
+
}
|
|
131
|
+
if (infuraEndpointIndex === networkConfiguration.defaultRpcEndpointIndex) {
|
|
132
|
+
// The default is already Infura; nothing to do.
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
await this.messenger.call('NetworkController:updateNetwork', chainId, {
|
|
136
|
+
...networkConfiguration,
|
|
137
|
+
defaultRpcEndpointIndex: infuraEndpointIndex,
|
|
138
|
+
}, { replacementSelectedRpcEndpointIndex: infuraEndpointIndex });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
exports.NetworkConnectionBannerController = NetworkConnectionBannerController;
|
|
142
|
+
_NetworkConnectionBannerController_degradedTimer = new WeakMap(), _NetworkConnectionBannerController_unavailableTimer = new WeakMap(), _NetworkConnectionBannerController_instances = new WeakSet(), _NetworkConnectionBannerController_evaluate = function _NetworkConnectionBannerController_evaluate() {
|
|
143
|
+
const { connectivityStatus } = this.messenger.call('ConnectivityController:getState');
|
|
144
|
+
if (connectivityStatus === connectivity_controller_1.CONNECTIVITY_STATUSES.Offline) {
|
|
145
|
+
__classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_resetBanner).call(this);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const failed = __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_findFailedNetworkForBanner).call(this);
|
|
149
|
+
if (!failed) {
|
|
150
|
+
__classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_resetBanner).call(this);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
// If the banner is currently showing for a different network or status,
|
|
154
|
+
// restart the escalation timeline for the new one. Otherwise let the
|
|
155
|
+
// existing timers continue.
|
|
156
|
+
if (this.state.status !== 'available' &&
|
|
157
|
+
this.state.network?.networkClientId === failed.networkClientId) {
|
|
158
|
+
this.update((draft) => {
|
|
159
|
+
draft.network = failed;
|
|
160
|
+
});
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
__classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_clearTimers).call(this);
|
|
164
|
+
this.update((draft) => {
|
|
165
|
+
draft.status = 'available';
|
|
166
|
+
draft.network = null;
|
|
167
|
+
});
|
|
168
|
+
__classPrivateFieldSet(this, _NetworkConnectionBannerController_degradedTimer, setTimeout(() => {
|
|
169
|
+
__classPrivateFieldSet(this, _NetworkConnectionBannerController_degradedTimer, undefined, "f");
|
|
170
|
+
const stillFailed = __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_findFailedNetworkForBanner).call(this);
|
|
171
|
+
if (!stillFailed) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
this.update((draft) => {
|
|
175
|
+
draft.status = 'degraded';
|
|
176
|
+
draft.network = stillFailed;
|
|
177
|
+
});
|
|
178
|
+
__classPrivateFieldSet(this, _NetworkConnectionBannerController_unavailableTimer, setTimeout(() => {
|
|
179
|
+
__classPrivateFieldSet(this, _NetworkConnectionBannerController_unavailableTimer, undefined, "f");
|
|
180
|
+
const stillFailedAtEscalation = __classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_findFailedNetworkForBanner).call(this);
|
|
181
|
+
if (!stillFailedAtEscalation) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
this.update((draft) => {
|
|
185
|
+
draft.status = 'unavailable';
|
|
186
|
+
draft.network = stillFailedAtEscalation;
|
|
187
|
+
});
|
|
188
|
+
}, UNAVAILABLE_BANNER_TIMEOUT_MS - DEGRADED_BANNER_TIMEOUT_MS), "f");
|
|
189
|
+
}, DEGRADED_BANNER_TIMEOUT_MS), "f");
|
|
190
|
+
}, _NetworkConnectionBannerController_resetBanner = function _NetworkConnectionBannerController_resetBanner() {
|
|
191
|
+
__classPrivateFieldGet(this, _NetworkConnectionBannerController_instances, "m", _NetworkConnectionBannerController_clearTimers).call(this);
|
|
192
|
+
if (this.state.status !== 'available' || this.state.network !== null) {
|
|
193
|
+
this.update((draft) => {
|
|
194
|
+
draft.status = 'available';
|
|
195
|
+
draft.network = null;
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}, _NetworkConnectionBannerController_clearTimers = function _NetworkConnectionBannerController_clearTimers() {
|
|
199
|
+
if (__classPrivateFieldGet(this, _NetworkConnectionBannerController_degradedTimer, "f") !== undefined) {
|
|
200
|
+
clearTimeout(__classPrivateFieldGet(this, _NetworkConnectionBannerController_degradedTimer, "f"));
|
|
201
|
+
__classPrivateFieldSet(this, _NetworkConnectionBannerController_degradedTimer, undefined, "f");
|
|
202
|
+
}
|
|
203
|
+
if (__classPrivateFieldGet(this, _NetworkConnectionBannerController_unavailableTimer, "f") !== undefined) {
|
|
204
|
+
clearTimeout(__classPrivateFieldGet(this, _NetworkConnectionBannerController_unavailableTimer, "f"));
|
|
205
|
+
__classPrivateFieldSet(this, _NetworkConnectionBannerController_unavailableTimer, undefined, "f");
|
|
206
|
+
}
|
|
207
|
+
}, _NetworkConnectionBannerController_findFailedNetworkForBanner = function _NetworkConnectionBannerController_findFailedNetworkForBanner() {
|
|
208
|
+
const { enabledNetworkMap } = this.messenger.call('NetworkEnablementController:getState');
|
|
209
|
+
const enabledEvmChainIds = Object.entries(enabledNetworkMap[utils_1.KnownCaipNamespace.Eip155] ?? {})
|
|
210
|
+
.filter(([, enabled]) => enabled)
|
|
211
|
+
.map(([chainId]) => chainId);
|
|
212
|
+
const { networkConfigurationsByChainId, networksMetadata } = this.messenger.call('NetworkController:getState');
|
|
213
|
+
const failedNetworks = [];
|
|
214
|
+
let totalEnabled = 0;
|
|
215
|
+
for (const chainId of enabledEvmChainIds) {
|
|
216
|
+
const networkConfiguration = networkConfigurationsByChainId[chainId];
|
|
217
|
+
if (!networkConfiguration) {
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
const { rpcEndpoints, defaultRpcEndpointIndex, name } = networkConfiguration;
|
|
221
|
+
const defaultRpcEndpoint = rpcEndpoints[defaultRpcEndpointIndex];
|
|
222
|
+
if (!defaultRpcEndpoint) {
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
totalEnabled += 1;
|
|
226
|
+
const metadata = networksMetadata[defaultRpcEndpoint.networkClientId];
|
|
227
|
+
if (metadata === undefined ||
|
|
228
|
+
metadata.status === network_controller_1.NetworkStatus.Available) {
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
const endpointIsInfura = isInfuraEndpoint(defaultRpcEndpoint.url);
|
|
232
|
+
// For custom endpoints (non-Infura), find an Infura endpoint on this
|
|
233
|
+
// chain that we could offer to switch to.
|
|
234
|
+
let infuraNetworkClientId = null;
|
|
235
|
+
if (!endpointIsInfura) {
|
|
236
|
+
const otherInfura = rpcEndpoints.find((endpoint, index) => index !== defaultRpcEndpointIndex && isInfuraEndpoint(endpoint.url));
|
|
237
|
+
infuraNetworkClientId = otherInfura?.networkClientId ?? null;
|
|
238
|
+
}
|
|
239
|
+
failedNetworks.push({
|
|
240
|
+
chainId,
|
|
241
|
+
networkClientId: defaultRpcEndpoint.networkClientId,
|
|
242
|
+
networkName: name,
|
|
243
|
+
rpcUrl: defaultRpcEndpoint.url,
|
|
244
|
+
isInfuraEndpoint: endpointIsInfura,
|
|
245
|
+
infuraNetworkClientId,
|
|
246
|
+
domain: (0, url_utils_1.getDomain)(defaultRpcEndpoint.url),
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
if (failedNetworks.length === 0) {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
const firstCustomFailed = failedNetworks.find((entry) => !entry.isInfuraEndpoint);
|
|
253
|
+
const distinctDomains = new Set(failedNetworks
|
|
254
|
+
.map((entry) => entry.domain)
|
|
255
|
+
.filter((domain) => domain !== null)).size;
|
|
256
|
+
const areAllEnabledNetworksFailed = failedNetworks.length === totalEnabled;
|
|
257
|
+
if (!firstCustomFailed &&
|
|
258
|
+
distinctDomains <= 1 &&
|
|
259
|
+
!areAllEnabledNetworksFailed) {
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
const selected = firstCustomFailed ?? failedNetworks[0];
|
|
263
|
+
return {
|
|
264
|
+
chainId: selected.chainId,
|
|
265
|
+
networkClientId: selected.networkClientId,
|
|
266
|
+
networkName: selected.networkName,
|
|
267
|
+
rpcUrl: selected.rpcUrl,
|
|
268
|
+
isInfuraEndpoint: selected.isInfuraEndpoint,
|
|
269
|
+
infuraNetworkClientId: selected.infuraNetworkClientId,
|
|
270
|
+
};
|
|
271
|
+
};
|
|
272
|
+
/**
|
|
273
|
+
* Checks if an RPC URL is hosted on the Infura service. Detection is by
|
|
274
|
+
* hostname suffix rather than the exact MetaMask-key URL pattern, so any
|
|
275
|
+
* `*.infura.io` URL counts. That's the right shape for grouping by provider.
|
|
276
|
+
*
|
|
277
|
+
* @param url - The RPC URL to check.
|
|
278
|
+
* @returns True if the URL's host is on `infura.io`.
|
|
279
|
+
*/
|
|
280
|
+
function isInfuraEndpoint(url) {
|
|
281
|
+
try {
|
|
282
|
+
return new URL(url).hostname.toLowerCase().endsWith('.infura.io');
|
|
283
|
+
}
|
|
284
|
+
catch {
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
//# sourceMappingURL=NetworkConnectionBannerController.cjs.map
|
|
@@ -0,0 +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 { CONNECTIVITY_STATUSES } from '@metamask/connectivity-controller';\nimport type {\n ConnectivityControllerGetStateAction,\n ConnectivityControllerState,\n} from '@metamask/connectivity-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport type {\n NetworkControllerGetNetworkConfigurationByChainIdAction,\n NetworkControllerGetStateAction,\n NetworkControllerUpdateNetworkAction,\n NetworkState,\n} from '@metamask/network-controller';\nimport { NetworkStatus } from '@metamask/network-controller';\nimport type {\n NetworkEnablementControllerGetStateAction,\n NetworkEnablementControllerState,\n} from '@metamask/network-enablement-controller';\nimport type { Hex } from '@metamask/utils';\nimport { KnownCaipNamespace } from '@metamask/utils';\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 */\nexport const 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 the failing network the banner should describe. Populated when\n * {@link NetworkConnectionBannerControllerState.status} is `degraded` or\n * `unavailable`, `null` otherwise.\n *\n * `infuraNetworkClientId` is the networkClientId of an Infura endpoint on the\n * same chain that the user can switch to. `null` when the failing endpoint is\n * already Infura, or when no Infura alternative exists.\n */\nexport type NetworkConnectionBannerFailedNetwork = {\n chainId: Hex;\n networkClientId: string;\n networkName: string;\n rpcUrl: string;\n isInfuraEndpoint: boolean;\n infuraNetworkClientId: string | null;\n};\n\n/**\n * State for the {@link NetworkConnectionBannerController}.\n */\nexport type NetworkConnectionBannerControllerState = {\n status: NetworkConnectionBannerStatus;\n network: NetworkConnectionBannerFailedNetwork | 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\nconst DEGRADED_BANNER_TIMEOUT_MS = 5_000;\nconst UNAVAILABLE_BANNER_TIMEOUT_MS = 30_000;\n\nconst MESSENGER_EXPOSED_METHODS = [\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 | ControllerStateChangedEvent<'NetworkController', NetworkState>\n | ControllerStateChangedEvent<\n 'NetworkEnablementController',\n NetworkEnablementControllerState\n >\n | ControllerStateChangedEvent<\n 'ConnectivityController',\n ConnectivityControllerState\n >;\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 is failing (escape hatch for single-network\n * 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 /**\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 const onStateChange = (): void => this.#evaluate();\n this.messenger.subscribe('NetworkController:stateChanged', onStateChange);\n this.messenger.subscribe(\n 'NetworkEnablementController:stateChanged',\n onStateChange,\n );\n this.messenger.subscribe(\n 'ConnectivityController:stateChanged',\n onStateChange,\n );\n\n this.messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n /**\n * Clears the banner state regardless of the current rule outcome. The next\n * subscription-driven evaluation will re-show the banner if the conditions\n * still hold.\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) => isInfuraEndpoint(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 #evaluate(): 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 failed = this.#findFailedNetworkForBanner();\n\n if (!failed) {\n this.#resetBanner();\n return;\n }\n\n // If the banner is currently showing for a different network or status,\n // restart the escalation timeline for the new one. Otherwise let the\n // existing timers continue.\n if (\n this.state.status !== 'available' &&\n this.state.network?.networkClientId === failed.networkClientId\n ) {\n this.update((draft) => {\n draft.network = failed;\n });\n return;\n }\n\n this.#clearTimers();\n this.update((draft) => {\n draft.status = 'available';\n draft.network = null;\n });\n\n this.#degradedTimer = setTimeout(() => {\n this.#degradedTimer = undefined;\n const stillFailed = this.#findFailedNetworkForBanner();\n if (!stillFailed) {\n return;\n }\n this.update((draft) => {\n draft.status = 'degraded';\n draft.network = stillFailed;\n });\n this.#unavailableTimer = setTimeout(() => {\n this.#unavailableTimer = undefined;\n const stillFailedAtEscalation = this.#findFailedNetworkForBanner();\n if (!stillFailedAtEscalation) {\n return;\n }\n this.update((draft) => {\n draft.status = 'unavailable';\n draft.network = stillFailedAtEscalation;\n });\n }, UNAVAILABLE_BANNER_TIMEOUT_MS - DEGRADED_BANNER_TIMEOUT_MS);\n }, DEGRADED_BANNER_TIMEOUT_MS);\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 if (this.state.status !== 'available' || this.state.network !== null) {\n this.update((draft) => {\n draft.status = 'available';\n draft.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 #findFailedNetworkForBanner(): NetworkConnectionBannerFailedNetwork | null {\n const { enabledNetworkMap } = this.messenger.call(\n 'NetworkEnablementController:getState',\n );\n const enabledEvmChainIds = Object.entries(\n enabledNetworkMap[KnownCaipNamespace.Eip155] ?? {},\n )\n .filter(([, enabled]) => enabled)\n .map(([chainId]) => chainId as Hex);\n const { networkConfigurationsByChainId, networksMetadata } =\n this.messenger.call('NetworkController:getState');\n\n type EnrichedFailedNetwork = NetworkConnectionBannerFailedNetwork & {\n domain: string | null;\n };\n const failedNetworks: EnrichedFailedNetwork[] = [];\n let totalEnabled = 0;\n\n for (const chainId of enabledEvmChainIds) {\n const networkConfiguration = networkConfigurationsByChainId[chainId];\n if (!networkConfiguration) {\n continue;\n }\n\n const { rpcEndpoints, defaultRpcEndpointIndex, name } =\n networkConfiguration;\n const defaultRpcEndpoint = rpcEndpoints[defaultRpcEndpointIndex];\n if (!defaultRpcEndpoint) {\n continue;\n }\n\n totalEnabled += 1;\n\n const metadata = networksMetadata[defaultRpcEndpoint.networkClientId];\n if (\n metadata === undefined ||\n metadata.status === NetworkStatus.Available\n ) {\n continue;\n }\n\n const endpointIsInfura = isInfuraEndpoint(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 infuraNetworkClientId: string | null = null;\n if (!endpointIsInfura) {\n const otherInfura = rpcEndpoints.find(\n (endpoint, index) =>\n index !== defaultRpcEndpointIndex && isInfuraEndpoint(endpoint.url),\n );\n infuraNetworkClientId = otherInfura?.networkClientId ?? null;\n }\n\n failedNetworks.push({\n chainId,\n networkClientId: defaultRpcEndpoint.networkClientId,\n networkName: name,\n rpcUrl: defaultRpcEndpoint.url,\n isInfuraEndpoint: endpointIsInfura,\n infuraNetworkClientId,\n domain: getDomain(defaultRpcEndpoint.url),\n });\n }\n\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 areAllEnabledNetworksFailed = failedNetworks.length === totalEnabled;\n\n if (\n !firstCustomFailed &&\n distinctDomains <= 1 &&\n !areAllEnabledNetworksFailed\n ) {\n return null;\n }\n\n const selected = firstCustomFailed ?? failedNetworks[0];\n return {\n chainId: selected.chainId,\n networkClientId: selected.networkClientId,\n networkName: selected.networkName,\n rpcUrl: selected.rpcUrl,\n isInfuraEndpoint: selected.isInfuraEndpoint,\n infuraNetworkClientId: selected.infuraNetworkClientId,\n };\n }\n}\n\n/**\n * Checks if an RPC URL is hosted on the Infura service. Detection is by\n * hostname suffix rather than the exact MetaMask-key URL pattern, so any\n * `*.infura.io` URL counts. That's the right shape for grouping by provider.\n *\n * @param url - The RPC URL to check.\n * @returns True if the URL's host is on `infura.io`.\n */\nfunction isInfuraEndpoint(url: string): boolean {\n try {\n return new URL(url).hostname.toLowerCase().endsWith('.infura.io');\n } catch {\n return false;\n }\n}\n"]}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import type { ControllerGetStateAction, ControllerStateChangedEvent } from "@metamask/base-controller";
|
|
2
|
+
import { BaseController } from "@metamask/base-controller";
|
|
3
|
+
import type { ConnectivityControllerGetStateAction, ConnectivityControllerState } from "@metamask/connectivity-controller";
|
|
4
|
+
import type { Messenger } from "@metamask/messenger";
|
|
5
|
+
import type { NetworkControllerGetNetworkConfigurationByChainIdAction, NetworkControllerGetStateAction, NetworkControllerUpdateNetworkAction, NetworkState } from "@metamask/network-controller";
|
|
6
|
+
import type { NetworkEnablementControllerGetStateAction, NetworkEnablementControllerState } from "@metamask/network-enablement-controller";
|
|
7
|
+
import type { Hex } from "@metamask/utils";
|
|
8
|
+
import type { NetworkConnectionBannerControllerMethodActions } from "./NetworkConnectionBannerController-method-action-types.cjs";
|
|
9
|
+
/**
|
|
10
|
+
* The name of the {@link NetworkConnectionBannerController}, used to namespace
|
|
11
|
+
* the controller's actions and events and to namespace the controller's state
|
|
12
|
+
* data when composed with other controllers.
|
|
13
|
+
*/
|
|
14
|
+
export declare const controllerName = "NetworkConnectionBannerController";
|
|
15
|
+
/**
|
|
16
|
+
* Status the banner can be in. `available` means no banner is shown; the
|
|
17
|
+
* `degraded` and `unavailable` values mirror the two-tier escalation that the
|
|
18
|
+
* UI renders.
|
|
19
|
+
*/
|
|
20
|
+
export type NetworkConnectionBannerStatus = 'available' | 'degraded' | 'unavailable';
|
|
21
|
+
/**
|
|
22
|
+
* Details of the failing network the banner should describe. Populated when
|
|
23
|
+
* {@link NetworkConnectionBannerControllerState.status} is `degraded` or
|
|
24
|
+
* `unavailable`, `null` otherwise.
|
|
25
|
+
*
|
|
26
|
+
* `infuraNetworkClientId` is the networkClientId of an Infura endpoint on the
|
|
27
|
+
* same chain that the user can switch to. `null` when the failing endpoint is
|
|
28
|
+
* already Infura, or when no Infura alternative exists.
|
|
29
|
+
*/
|
|
30
|
+
export type NetworkConnectionBannerFailedNetwork = {
|
|
31
|
+
chainId: Hex;
|
|
32
|
+
networkClientId: string;
|
|
33
|
+
networkName: string;
|
|
34
|
+
rpcUrl: string;
|
|
35
|
+
isInfuraEndpoint: boolean;
|
|
36
|
+
infuraNetworkClientId: string | null;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* State for the {@link NetworkConnectionBannerController}.
|
|
40
|
+
*/
|
|
41
|
+
export type NetworkConnectionBannerControllerState = {
|
|
42
|
+
status: NetworkConnectionBannerStatus;
|
|
43
|
+
network: NetworkConnectionBannerFailedNetwork | null;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Constructs the default {@link NetworkConnectionBannerController} state.
|
|
47
|
+
*
|
|
48
|
+
* @returns The default state.
|
|
49
|
+
*/
|
|
50
|
+
export declare function getDefaultNetworkConnectionBannerControllerState(): NetworkConnectionBannerControllerState;
|
|
51
|
+
/**
|
|
52
|
+
* Retrieves the state of the {@link NetworkConnectionBannerController}.
|
|
53
|
+
*/
|
|
54
|
+
export type NetworkConnectionBannerControllerGetStateAction = ControllerGetStateAction<typeof controllerName, NetworkConnectionBannerControllerState>;
|
|
55
|
+
/**
|
|
56
|
+
* Actions that {@link NetworkConnectionBannerControllerMessenger} exposes to
|
|
57
|
+
* other consumers.
|
|
58
|
+
*/
|
|
59
|
+
export type NetworkConnectionBannerControllerActions = NetworkConnectionBannerControllerGetStateAction | NetworkConnectionBannerControllerMethodActions;
|
|
60
|
+
/**
|
|
61
|
+
* Actions from other messengers that
|
|
62
|
+
* {@link NetworkConnectionBannerControllerMessenger} calls.
|
|
63
|
+
*/
|
|
64
|
+
type AllowedActions = NetworkControllerGetStateAction | NetworkControllerGetNetworkConfigurationByChainIdAction | NetworkControllerUpdateNetworkAction | NetworkEnablementControllerGetStateAction | ConnectivityControllerGetStateAction;
|
|
65
|
+
/**
|
|
66
|
+
* Published when the state of {@link NetworkConnectionBannerController}
|
|
67
|
+
* changes.
|
|
68
|
+
*/
|
|
69
|
+
export type NetworkConnectionBannerControllerStateChangedEvent = ControllerStateChangedEvent<typeof controllerName, NetworkConnectionBannerControllerState>;
|
|
70
|
+
/**
|
|
71
|
+
* Events that {@link NetworkConnectionBannerControllerMessenger} exposes to
|
|
72
|
+
* other consumers.
|
|
73
|
+
*/
|
|
74
|
+
export type NetworkConnectionBannerControllerEvents = NetworkConnectionBannerControllerStateChangedEvent;
|
|
75
|
+
/**
|
|
76
|
+
* Events from other messengers that
|
|
77
|
+
* {@link NetworkConnectionBannerControllerMessenger} subscribes to.
|
|
78
|
+
*/
|
|
79
|
+
type AllowedEvents = ControllerStateChangedEvent<'NetworkController', NetworkState> | ControllerStateChangedEvent<'NetworkEnablementController', NetworkEnablementControllerState> | ControllerStateChangedEvent<'ConnectivityController', ConnectivityControllerState>;
|
|
80
|
+
/**
|
|
81
|
+
* The messenger restricted to actions and events accessed by
|
|
82
|
+
* {@link NetworkConnectionBannerController}.
|
|
83
|
+
*/
|
|
84
|
+
export type NetworkConnectionBannerControllerMessenger = Messenger<typeof controllerName, NetworkConnectionBannerControllerActions | AllowedActions, NetworkConnectionBannerControllerEvents | AllowedEvents>;
|
|
85
|
+
/**
|
|
86
|
+
* Options for constructing the {@link NetworkConnectionBannerController}.
|
|
87
|
+
*/
|
|
88
|
+
export type NetworkConnectionBannerControllerOptions = {
|
|
89
|
+
/**
|
|
90
|
+
* The messenger for inter-controller communication.
|
|
91
|
+
*/
|
|
92
|
+
messenger: NetworkConnectionBannerControllerMessenger;
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* NetworkConnectionBannerController decides whether the network connection
|
|
96
|
+
* banner should be shown for the user, and which failing network it should
|
|
97
|
+
* describe. It encapsulates the rule, the 5s / 30s timer escalation, and the
|
|
98
|
+
* eTLD+1 grouping that was previously duplicated across MetaMask clients.
|
|
99
|
+
*
|
|
100
|
+
* The banner shows when:
|
|
101
|
+
*
|
|
102
|
+
* - The first failing network's default RPC is a custom (non-Infura) endpoint
|
|
103
|
+
* — users always want to be informed about errors with RPCs they've chosen.
|
|
104
|
+
* - Failed RPCs span more than one registrable domain (likely client-side).
|
|
105
|
+
* - Every enabled EVM network is failing (escape hatch for single-network
|
|
106
|
+
* setups so they still get a signal).
|
|
107
|
+
*
|
|
108
|
+
* A wide single-provider outage (e.g. every `*.infura.io` network goes down at
|
|
109
|
+
* once) collapses to one domain and is suppressed, except in the all-down
|
|
110
|
+
* single-network case. When a custom failure is present, it's surfaced first
|
|
111
|
+
* so the "Switch to MetaMask default RPC" CTA targets the network the user
|
|
112
|
+
* can act on.
|
|
113
|
+
*
|
|
114
|
+
* Clients only need to render the banner from the controller's state and wire
|
|
115
|
+
* click handlers to {@link dismissBanner} or {@link switchToDefaultInfuraRpc}.
|
|
116
|
+
*/
|
|
117
|
+
export declare class NetworkConnectionBannerController extends BaseController<typeof controllerName, NetworkConnectionBannerControllerState, NetworkConnectionBannerControllerMessenger> {
|
|
118
|
+
#private;
|
|
119
|
+
/**
|
|
120
|
+
* Constructs a new {@link NetworkConnectionBannerController}.
|
|
121
|
+
*
|
|
122
|
+
* @param args - The arguments to this controller.
|
|
123
|
+
* @param args.messenger - The messenger suited for this controller.
|
|
124
|
+
*/
|
|
125
|
+
constructor({ messenger }: NetworkConnectionBannerControllerOptions);
|
|
126
|
+
/**
|
|
127
|
+
* Clears the banner state regardless of the current rule outcome. The next
|
|
128
|
+
* subscription-driven evaluation will re-show the banner if the conditions
|
|
129
|
+
* still hold.
|
|
130
|
+
*/
|
|
131
|
+
dismissBanner(): void;
|
|
132
|
+
/**
|
|
133
|
+
* Switches the chain's default RPC endpoint to its first Infura endpoint,
|
|
134
|
+
* causing the banner to clear once the network becomes available again.
|
|
135
|
+
*
|
|
136
|
+
* @param args - The arguments to this action.
|
|
137
|
+
* @param args.chainId - The chain whose default RPC should be switched.
|
|
138
|
+
* @throws If the chain configuration cannot be found, or if it has no
|
|
139
|
+
* Infura endpoint to switch to, or if the default is already Infura.
|
|
140
|
+
*/
|
|
141
|
+
switchToDefaultInfuraRpc({ chainId }: {
|
|
142
|
+
chainId: Hex;
|
|
143
|
+
}): Promise<void>;
|
|
144
|
+
}
|
|
145
|
+
export {};
|
|
146
|
+
//# sourceMappingURL=NetworkConnectionBannerController.d.cts.map
|