Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -296,16 +296,6 @@ export type AppStateControllerSetProductTourAction = {
handler: AppStateController['setProductTour'];
};

/**
* Updates the network connection banner state
*
* @param networkConnectionBanner - The new banner state
*/
export type AppStateControllerUpdateNetworkConnectionBannerAction = {
type: `AppStateController:updateNetworkConnectionBanner`;
handler: AppStateController['updateNetworkConnectionBanner'];
};

/**
* Sets a unique ID for the current extension popup
*
Expand Down Expand Up @@ -587,7 +577,6 @@ export type AppStateControllerMethodActions =
| AppStateControllerSetMusdConversionEducationSeenAction
| AppStateControllerAddMusdConversionDismissedCtaKeyAction
| AppStateControllerSetProductTourAction
| AppStateControllerUpdateNetworkConnectionBannerAction
| AppStateControllerSetCurrentExtensionPopupIdAction
| AppStateControllerSetTrezorModelAction
| AppStateControllerUpdateNftDropDownStateAction
Expand Down
3 changes: 0 additions & 3 deletions app/scripts/controllers/app-state-controller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1080,9 +1080,6 @@ describe('AppStateController', () => {
"lastVisitedRoute": null,
"musdConversionDismissedCtaKeys": [],
"musdConversionEducationSeen": false,
"networkConnectionBanner": {
"status": "unknown",
},
"newPrivacyPolicyToastClickedOrClosed": null,
"newPrivacyPolicyToastShownDate": null,
"nftsDropdownState": {},
Expand Down
26 changes: 0 additions & 26 deletions app/scripts/controllers/app-state-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ import { SecurityAlertResponse } from '../lib/ppom/types';
import {
AccountOverviewTabKey,
CarouselSlide,
NetworkConnectionBanner,
StorageWriteErrorType,
} from '../../../shared/constants/app-state';
import type {
Expand Down Expand Up @@ -114,7 +113,6 @@ export type AppStateControllerState = {
lastUpdatedAt: number | null;
lastUpdatedFromVersion: string | null;
lastViewedUserSurvey: number | null;
networkConnectionBanner: NetworkConnectionBanner;
newPrivacyPolicyToastClickedOrClosed: boolean | null;
newPrivacyPolicyToastShownDate: number | null;
pna25Acknowledged: boolean;
Expand Down Expand Up @@ -260,7 +258,6 @@ type AppStateControllerInitState = Partial<
| 'signatureSecurityAlertResponses'
| 'addressSecurityAlertResponses'
| 'currentExtensionPopupId'
| 'networkConnectionBanner'
>
>;

Expand Down Expand Up @@ -334,9 +331,6 @@ function getInitialStateOverrides() {
currentExtensionPopupId: 0,
nftsDropdownState: {},
signatureSecurityAlertResponses: {},
networkConnectionBanner: {
status: 'unknown' as const,
},
};
}

Expand Down Expand Up @@ -439,12 +433,6 @@ const controllerMetadata: StateMetadata<AppStateControllerState> = {
includeInDebugSnapshot: true,
usedInUi: true,
},
networkConnectionBanner: {
includeInStateLogs: false,
persist: false,
includeInDebugSnapshot: false,
usedInUi: true,
},
newPrivacyPolicyToastClickedOrClosed: {
includeInStateLogs: true,
persist: true,
Expand Down Expand Up @@ -749,7 +737,6 @@ const MESSENGER_EXPOSED_METHODS = [
'setTermsOfUseLastAgreed',
'setTrezorModel',
'setUpdateModalLastDismissedAt',
'updateNetworkConnectionBanner',
'updateNftDropDownState',
'updateSlides',
'updateThrottledOriginState',
Expand Down Expand Up @@ -1349,19 +1336,6 @@ export class AppStateController extends BaseController<
});
}

/**
* Updates the network connection banner state
*
* @param networkConnectionBanner - The new banner state
*/
updateNetworkConnectionBanner(
networkConnectionBanner: AppStateControllerState['networkConnectionBanner'],
): void {
this.update((state) => {
state.networkConnectionBanner = networkConnectionBanner;
});
}

/**
* Sets a unique ID for the current extension popup
*
Expand Down
4 changes: 3 additions & 1 deletion app/scripts/messenger-client-init/controller-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ import {
import { ClaimsController, ClaimsService } from '@metamask/claims-controller';
import { ClientController } from '@metamask/client-controller';
import { ConnectivityController } from '@metamask/connectivity-controller';
import { NetworkConnectionBannerController } from '@metamask/network-connection-banner-controller';
import {
ProfileMetricsController,
ProfileMetricsService,
Expand Down Expand Up @@ -233,7 +234,8 @@ export type MessengerClient =
| StaticAssetsController
| ProfileMetricsController
| ProfileMetricsService
| ConnectivityController;
| ConnectivityController
| NetworkConnectionBannerController;

/**
* Flat state object for all messenger clients supporting or required by modular initialization.
Expand Down
5 changes: 5 additions & 0 deletions app/scripts/messenger-client-init/messengers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ import {
getSubscriptionControllerMessenger,
} from './subscription';
import { getConnectivityControllerMessenger } from './connectivity';
import { getNetworkConnectionBannerControllerMessenger } from './network-connection-banner';
import { getGatorPermissionsControllerMessenger } from './gator-permissions/gator-permissions-controller-messenger';
import { getMetaMetricsControllerMessenger } from './metametrics-controller-messenger';
import { getUserStorageControllerInitMessenger } from './identity/user-storage-controller-messenger';
Expand Down Expand Up @@ -402,6 +403,10 @@ export const MESSENGER_FACTORIES = {
getMessenger: getConnectivityControllerMessenger,
getInitMessenger: noop,
},
NetworkConnectionBannerController: {
getMessenger: getNetworkConnectionBannerControllerMessenger,
getInitMessenger: noop,
},
ClaimsController: {
getMessenger: getClaimsControllerMessenger,
getInitMessenger: getClaimsControllerInitMessenger,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { getNetworkConnectionBannerControllerMessenger } from './network-connection-banner-controller-messenger';
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {
Messenger,
type MessengerActions,
type MessengerEvents,
} from '@metamask/messenger';
import type { NetworkConnectionBannerControllerMessenger } from '@metamask/network-connection-banner-controller';
import { RootMessenger } from '../../../lib/messenger';

/**
* Get a messenger for the NetworkConnectionBannerController.
*
* @param messenger - The root messenger.
* @returns The messenger for NetworkConnectionBannerController.
*/
export function getNetworkConnectionBannerControllerMessenger(
messenger: RootMessenger<
MessengerActions<NetworkConnectionBannerControllerMessenger>,
MessengerEvents<NetworkConnectionBannerControllerMessenger>
>,
): NetworkConnectionBannerControllerMessenger {
const controllerMessenger: NetworkConnectionBannerControllerMessenger =
new Messenger({
namespace: 'NetworkConnectionBannerController',
parent: messenger,
});
return controllerMessenger;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { NetworkConnectionBannerControllerInit } from './network-connection-banner-controller-init';
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {
NetworkConnectionBannerController,
type NetworkConnectionBannerControllerMessenger,
} from '@metamask/network-connection-banner-controller';

import { MessengerClientInitFunction } from '../types';

/**
* Initialize the NetworkConnectionBannerController.
*
* Encapsulates the show/hide rule and 5s/30s timer state machine for the
* "Still connecting" / "Unable to connect" banner. Subscribes to
* NetworkController, NetworkEnablementController, and ConnectivityController
* state via the messenger.
*
* @param request - The controller init request.
* @param request.controllerMessenger - The messenger for the controller.
* @returns The controller init result.
*/
export const NetworkConnectionBannerControllerInit: MessengerClientInitFunction<
NetworkConnectionBannerController,
NetworkConnectionBannerControllerMessenger
> = (request) => {
const { controllerMessenger } = request;

const messengerClient = new NetworkConnectionBannerController({
messenger: controllerMessenger,
});

return {
messengerClient,
persistedStateKey: null,
};
};
6 changes: 2 additions & 4 deletions app/scripts/metamask-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,7 @@ import {
SubscriptionServiceInit,
} from './messenger-client-init/subscription';
import { ConnectivityControllerInit } from './messenger-client-init/connectivity';
import { NetworkConnectionBannerControllerInit } from './messenger-client-init/network-connection-banner';
import { AccountTrackerControllerInit } from './messenger-client-init/account-tracker-controller-init';
import { OnboardingControllerInit } from './messenger-client-init/onboarding-controller-init';
import { RemoteFeatureFlagControllerInit } from './messenger-client-init/remote-feature-flag-controller-init';
Expand Down Expand Up @@ -703,6 +704,7 @@ export default class MetamaskController extends EventEmitter {
SubscriptionController: SubscriptionControllerInit,
SubscriptionService: SubscriptionServiceInit,
ConnectivityController: ConnectivityControllerInit,
NetworkConnectionBannerController: NetworkConnectionBannerControllerInit,
NetworkOrderController: NetworkOrderControllerInit,
ShieldController: ShieldControllerInit,
ClaimsController: ClaimsControllerInit,
Expand Down Expand Up @@ -3326,10 +3328,6 @@ export default class MetamaskController extends EventEmitter {
appStateController.addMusdConversionDismissedCtaKey.bind(
appStateController,
),
updateNetworkConnectionBanner:
appStateController.updateNetworkConnectionBanner.bind(
appStateController,
),
setShowShieldEntryModalOnce:
appStateController.setShowShieldEntryModalOnce.bind(appStateController),
setPendingShieldCohort:
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@
"yarn-binary:hydrate": "corepack hydrate .yarn/yarn-corepack.tgz --activate"
},
"resolutions": {
"@metamask/network-connection-banner-controller": "npm:@metamask-previews/network-connection-banner-controller@0.1.0-preview-7507a11",
"@metamask/bridge-controller": "73.2.0",
"@metamask/messenger@npm:^0.3.0": "^1.2.0",
"@metamask/network-controller": "32.0.0",
Expand Down Expand Up @@ -397,6 +398,7 @@
"@metamask/multichain-network-controller": "^3.1.0",
"@metamask/multichain-transactions-controller": "^7.1.0",
"@metamask/name-controller": "^9.1.0",
"@metamask/network-connection-banner-controller": "^0.1.0",
"@metamask/network-controller": "^32.0.0",
"@metamask/network-enablement-controller": "^5.3.0",
"@metamask/notification-services-controller": "^23.1.0",
Expand Down Expand Up @@ -503,7 +505,6 @@
"nanoid": "^3.3.8",
"pify": "^5.0.0",
"prop-types": "^15.6.1",
"psl": "^1.15.0",
"punycode": "^2.1.1",
"qrcode-generator": "^2.0.4",
"qrcode.react": "^4.2.0",
Expand Down
42 changes: 1 addition & 41 deletions shared/lib/url-utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getDomain, isLocalhostOrIPAddress } from './url-utils';
import { isLocalhostOrIPAddress } from './url-utils';

describe('isLocalhostOrIPAddress', () => {
it('returns true for "localhost" (any case)', () => {
Expand All @@ -23,43 +23,3 @@ describe('isLocalhostOrIPAddress', () => {
expect(isLocalhostOrIPAddress('my-custom-rpc')).toBe(false);
});
});

describe('getDomain', () => {
it('returns the last two labels for a multi-label hostname', () => {
expect(getDomain('https://mainnet.infura.io/v3/abc')).toBe('infura.io');
});

it('groups subdomain-heavy hostnames under the same registrable domain', () => {
expect(getDomain('https://linea-mainnet.infura.io/v3/abc')).toBe(
'infura.io',
);
expect(getDomain('https://polygon-mainnet.g.alchemy.com/v2/abc')).toBe(
'alchemy.com',
);
});

it('returns the hostname as-is when it has exactly two labels', () => {
expect(getDomain('https://alchemy.com/')).toBe('alchemy.com');
});

it('handles multi-part public suffixes like .co.uk', () => {
expect(getDomain('https://api.example.co.uk/v1')).toBe('example.co.uk');
expect(getDomain('https://example.co.uk/')).toBe('example.co.uk');
});

it('returns single-label hosts (e.g., localhost) verbatim', () => {
expect(getDomain('http://localhost:8545')).toBe('localhost');
});

it('returns IPv4 addresses verbatim', () => {
expect(getDomain('http://127.0.0.1:8545')).toBe('127.0.0.1');
});

it('returns IPv6 addresses verbatim, including brackets', () => {
expect(getDomain('http://[::1]:8545')).toBe('[::1]');
});

it('returns null for an invalid URL', () => {
expect(getDomain('not a url')).toBeNull();
});
});
29 changes: 0 additions & 29 deletions shared/lib/url-utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import urlLib from 'url';
import ipRegex from 'ip-regex';
import psl from 'psl';

export function addUrlProtocolPrefix(urlString: string) {
let trimmed = urlString.trim();
Expand Down Expand Up @@ -80,31 +79,3 @@ export function isLocalhostOrIPAddress(hostname: string): boolean {

return ipRegex({ exact: true }).test(hostnameWithoutBrackets);
}

/**
* Registrable domain (eTLD+1) for a URL, computed via the Public Suffix List
* so multi-part suffixes like ".co.uk" resolve correctly. Used to group RPC
* endpoints by provider so a single provider's wide outage (e.g. *.infura.io)
* is treated as one failure rather than many.
*
* Localhost, IP literals, and single-label hosts are returned verbatim rather
* than reduced to a domain (psl returns null or garbage for those, and callers
* grouping by domain still need to distinguish them).
*
* @param urlString - The URL to extract a domain from.
* @returns The domain, or null if the URL is invalid.
*/
export function getDomain(urlString: string): string | null {
const url = getValidUrl(urlString);
if (url === null) {
return null;
}

const { hostname } = url;

if (!hostname.includes('.') || isLocalhostOrIPAddress(hostname)) {
return hostname;
}

return psl.get(hostname) ?? hostname;
}
1 change: 0 additions & 1 deletion shared/types/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,6 @@ export type ControllerStatePropertiesEnumerated = {
pendingShieldCohort: AppStateControllerState['pendingShieldCohort'];
pendingShieldCohortTxType: AppStateControllerState['pendingShieldCohortTxType'];
throttledOrigins: AppStateControllerState['throttledOrigins'];
networkConnectionBanner: AppStateControllerState['networkConnectionBanner'];
isWalletResetInProgress: AppStateControllerState['isWalletResetInProgress'];
sidePanelGasPollTokens: AppStateControllerState['sidePanelGasPollTokens'];
passkeyAutoUnlockSuppressed: AppStateControllerState['passkeyAutoUnlockSuppressed'];
Expand Down
17 changes: 0 additions & 17 deletions types/psl.d.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ import { NETWORKS_ROUTE } from '../../../helpers/constants/routes';
import { NetworkConnectionBanner } from './network-connection-banner';

jest.mock('../../../store/actions', () => ({
updateNetworkConnectionBanner: jest.fn(() => ({
type: 'UPDATE_NETWORK_CONNECTION_BANNER',
})),
setEditedNetwork: jest.fn(() => ({
type: 'SET_EDITED_NETWORK',
})),
Expand Down
Loading
Loading