Skip to content
Merged
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
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -897,6 +897,7 @@
/packages/wallet/src/initialization/instances/keyring-controller/ @MetaMask/accounts-engineers @MetaMask/core-platform
/packages/wallet/src/initialization/instances/logging-controller/ @MetaMask/confirmations
/packages/wallet/src/initialization/instances/network-controller/ @MetaMask/core-platform @MetaMask/metamask-assets
/packages/wallet/src/initialization/instances/network-enablement-controller/ @MetaMask/metamask-assets
Comment thread
Prithpal-Sooriya marked this conversation as resolved.
Resolved
Comment thread
cursor[bot] marked this conversation as resolved.
Resolved
/packages/wallet/src/initialization/instances/passkey-controller/ @MetaMask/web3auth
/packages/wallet/src/initialization/instances/permission-controller/ @MetaMask/core-platform
/packages/wallet/src/initialization/instances/remote-feature-flag-controller/ @MetaMask/core-platform @MetaMask/extension-platform @MetaMask/mobile-platform
Expand Down
1 change: 1 addition & 0 deletions codeowners.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ const config = {
},
'network-enablement-controller': {
teams: ['@MetaMask/metamask-assets'],
initializationPath: 'network-enablement-controller',
},
'node-utils': {
teams: ['@MetaMask/core-platform'],
Expand Down
1 change: 1 addition & 0 deletions packages/network-enablement-controller/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

- Import `@metamask/slip44` with a JSON import attribute so Node.js ESM consumers do not throw `ERR_IMPORT_ATTRIBUTE_MISSING` when importing `NetworkEnablementController` ([#10997](https://github.com/MetaMask/core/pull/10997))
- Preserve existing network enablement when an auto-enabled network from the config registry is added ([#10763](https://github.com/MetaMask/core/pull/10763))

## [7.0.2]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { fetchWithErrorHandling } from '@metamask/controller-utils';
// `@metamask/slip44` publishes a JSON file as its entry point. Node.js ESM
// rejects that import unless it carries a JSON import attribute, which crashes
// the wallet daemon on Node 22 and 24.
// @ts-expect-error: No type definitions for '@metamask/slip44'
import slip44 from '@metamask/slip44';
import slip44 from '@metamask/slip44' with { type: 'json' };
Comment thread
cursor[bot] marked this conversation as resolved.
Resolved
Comment thread
cursor[bot] marked this conversation as resolved.
Resolved
Comment thread
cursor[bot] marked this conversation as resolved.
Resolved

const CHAINID_NETWORK_URL = 'https://chainid.network/chains.json';

Expand Down
7 changes: 7 additions & 0 deletions packages/wallet/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- **BREAKING:** Add `NetworkEnablementController` to the default initialization configurations ([#10997](https://github.com/MetaMask/core/pull/10997))
- Delegates `MultichainNetworkController:getState`. `Wallet` does not construct `MultichainNetworkController`. Clients must register that action on the wallet messenger before calling `wallet.init()`. `NetworkEnablementController.init()` reads it during `Wallet.init()`, and `enableAllPopularNetworks` and `listPopularMultichainNetworks` read it later.
- Also delegates `ConfigRegistryController:getState`, `NetworkController:getState`, `NetworkController:networkAdded`, `NetworkController:networkRemoved`, `NetworkController:stateChange`, and `TransactionController:transactionSubmitted`. `Wallet` constructs those controllers during default initialization.

### Changed

- Add `@metamask/network-enablement-controller` as a dependency ([#10997](https://github.com/MetaMask/core/pull/10997))
- Bump `@metamask/claims-controller` from `^1.0.4` to `^1.1.0` ([#11092](https://github.com/MetaMask/core/pull/11092))
- Bump `@metamask/shield-controller` from `^7.0.5` to `^7.1.0` ([#11092](https://github.com/MetaMask/core/pull/11092))
- Bump `@metamask/subscription-controller` from `^15.0.0` to `^15.1.0` ([#11092](https://github.com/MetaMask/core/pull/11092))
Expand Down
1 change: 1 addition & 0 deletions packages/wallet/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"@metamask/logging-controller": "^10.0.0",
"@metamask/messenger": "^3.0.0",
"@metamask/network-controller": "^37.1.1",
"@metamask/network-enablement-controller": "^7.0.2",
"@metamask/passkey-controller": "^4.1.0",
"@metamask/permission-controller": "^14.0.0",
"@metamask/remote-feature-flag-controller": "^7.0.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/wallet/src/Wallet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ describe('Wallet', () => {

const results = await wallet.init();

expect(results).toHaveLength(7);
expect(results).toHaveLength(8);
});

it('disallows modifying the messenger', async () => {
Expand Down
1 change: 1 addition & 0 deletions packages/wallet/src/initialization/instances/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export { geolocationController } from './geolocation-controller/geolocation-cont
export { keyringController } from './keyring-controller/keyring-controller.js';
export { loggingController } from './logging-controller/logging-controller.js';
export { networkController } from './network-controller/network-controller.js';
export { networkEnablementController } from './network-enablement-controller/network-enablement-controller.js';
export { passkeyController } from './passkey-controller/passkey-controller.js';
export { permissionController } from './permission-controller/permission-controller.js';
export { subjectMetadataController } from './permission-controller/subject-metadata-controller.js';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import { Messenger } from '@metamask/messenger';
import { NetworkEnablementController } from '@metamask/network-enablement-controller';
import type { NetworkEnablementControllerState } from '@metamask/network-enablement-controller';
import { describe, expect, it, vi } from 'vitest';

import { defaultConfigurations } from '../../defaults.js';
import type {
DefaultActions,
DefaultEvents,
RootMessenger,
} from '../../defaults.js';
import { networkEnablementController } from './network-enablement-controller.js';

type ActionHandler = (...args: unknown[]) => unknown;

type AnyMessenger = Messenger<string>;

const PERSISTED_STATE: NetworkEnablementControllerState = {
enabledNetworkMap: {
eip155: {
'0x1': false,
},
},
nativeAssetIdentifiers: {
'eip155:1': 'eip155:1/slip44:60',
},
};

/**
* Creates a root messenger for use in tests.
*
* @returns A root messenger.
*/
function getRootMessenger(): RootMessenger<DefaultActions, DefaultEvents> {
return new Messenger({ namespace: 'Root' });
}

describe('networkEnablementController', () => {
it('is registered as a default initialization configuration', () => {
expect(Object.values(defaultConfigurations)).toContain(
networkEnablementController,
);
});

it('initializes a NetworkEnablementController with default state', () => {
const messenger =
networkEnablementController.getMessenger(getRootMessenger());

const instance = networkEnablementController.init({
state: undefined,
messenger,
options: {},
});

expect(instance).toBeInstanceOf(NetworkEnablementController);
expect(instance.state.enabledNetworkMap.eip155?.['0x1']).toBe(true);
expect(instance.state.nativeAssetIdentifiers).toStrictEqual({});
});

it('forwards provided state over the defaults', () => {
const messenger =
networkEnablementController.getMessenger(getRootMessenger());

const instance = networkEnablementController.init({
state: PERSISTED_STATE,
messenger,
options: {},
});

expect(instance.state.enabledNetworkMap.eip155?.['0x1']).toBe(false);
expect(instance.state.nativeAssetIdentifiers).toStrictEqual(
PERSISTED_STATE.nativeAssetIdentifiers,
);
});

it('exposes its state through the root messenger', () => {
const rootMessenger = getRootMessenger();
const messenger = networkEnablementController.getMessenger(rootMessenger);

networkEnablementController.init({
state: undefined,
messenger,
options: {},
});

expect(
rootMessenger.call('NetworkEnablementController:getState')
.enabledNetworkMap.eip155?.['0x1'],
).toBe(true);
});

it.each([
'ConfigRegistryController:getState',
'MultichainNetworkController:getState',
'NetworkController:getState',
])('delegates the %s action', (actionType) => {
const rootMessenger = getRootMessenger();
const handler = vi.fn().mockReturnValue('result');
registerActionHandler(rootMessenger, actionType, handler);
const messenger = networkEnablementController.getMessenger(rootMessenger);

const result = (
messenger as unknown as { call(type: string): unknown }
).call(actionType);

expect(handler).toHaveBeenCalledTimes(1);
expect(result).toBe('result');
});

it.each([
'NetworkController:networkAdded',
'NetworkController:networkRemoved',
'NetworkController:stateChange',
'TransactionController:transactionSubmitted',
])('delegates the %s event', (eventType) => {
const rootMessenger = getRootMessenger();
const messenger = networkEnablementController.getMessenger(rootMessenger);
const listener = vi.fn();
(
messenger as unknown as {
subscribe(type: string, handler: ActionHandler): void;
}
).subscribe(eventType, listener);

publishEvent(rootMessenger, eventType, 'payload');

expect(listener).toHaveBeenCalledWith('payload');
});
});

function registerActionHandler(
parent: RootMessenger<DefaultActions, DefaultEvents>,
actionType: string,
handler: ActionHandler,
): void {
const messenger = new Messenger({
namespace: actionType.split(':')[0],
parent: parent as unknown as AnyMessenger,
});

(
messenger as unknown as {
registerActionHandler(type: string, handler: ActionHandler): void;
}
).registerActionHandler(actionType, handler);
}

function publishEvent(
parent: RootMessenger<DefaultActions, DefaultEvents>,
eventType: string,
...payload: unknown[]
): void {
const messenger = new Messenger({
namespace: eventType.split(':')[0],
parent: parent as unknown as AnyMessenger,
});

(
messenger as unknown as {
publish(type: string, ...payload: unknown[]): void;
}
).publish(eventType, ...payload);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Messenger } from '@metamask/messenger';
import { NetworkEnablementController } from '@metamask/network-enablement-controller';
import type { NetworkEnablementControllerMessenger } from '@metamask/network-enablement-controller';

import type { InitializationConfiguration } from '../../types.js';

export const networkEnablementController: InitializationConfiguration<
Comment thread
Prithpal-Sooriya marked this conversation as resolved.
Resolved
Comment thread
cursor[bot] marked this conversation as resolved.
Resolved
NetworkEnablementController,
NetworkEnablementControllerMessenger
> = {
name: 'NetworkEnablementController',
reference: NetworkEnablementController,
init: ({ state, messenger }) =>
new NetworkEnablementController({
state,
messenger,
}),
getMessenger: (parent) => {
const messenger: NetworkEnablementControllerMessenger = new Messenger({
namespace: 'NetworkEnablementController',
parent,
});

parent.delegate({
messenger,
actions: [
'ConfigRegistryController:getState',
'MultichainNetworkController:getState',
'NetworkController:getState',
],
events: [
'NetworkController:networkAdded',
'NetworkController:networkRemoved',
'NetworkController:stateChange',
'TransactionController:transactionSubmitted',
],
Comment thread
cursor[bot] marked this conversation as resolved.
Resolved
});

return messenger;
},
};
3 changes: 3 additions & 0 deletions packages/wallet/tsconfig.build.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@
{
"path": "../network-controller/tsconfig.build.json"
},
{
"path": "../network-enablement-controller/tsconfig.build.json"
},
{
"path": "../passkey-controller/tsconfig.build.json"
},
Expand Down
3 changes: 3 additions & 0 deletions packages/wallet/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@
{
"path": "../network-controller"
},
{
"path": "../network-enablement-controller"
},
{
"path": "../passkey-controller"
},
Expand Down
3 changes: 3 additions & 0 deletions packages/wallet/tsconfig.lint.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@
{
"path": "../network-controller/tsconfig.lint.json"
},
{
"path": "../network-enablement-controller/tsconfig.lint.json"
},
{
"path": "../passkey-controller/tsconfig.lint.json"
},
Expand Down
1 change: 1 addition & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9163,6 +9163,7 @@ __metadata:
"@metamask/logging-controller": "npm:^10.0.0"
"@metamask/messenger": "npm:^3.0.0"
"@metamask/network-controller": "npm:^37.1.1"
"@metamask/network-enablement-controller": "npm:^7.0.2"
"@metamask/passkey-controller": "npm:^4.1.0"
"@metamask/permission-controller": "npm:^14.0.0"
"@metamask/remote-feature-flag-controller": "npm:^7.0.0"
Expand Down
Loading