diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 59fb52d8e9..727c6e6f6b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -137,6 +137,7 @@ ## Initialization /packages/wallet/src/initialization/instances/accounts-controller/ @MetaMask/accounts-engineers +/packages/wallet/src/initialization/instances/address-book-controller/ @MetaMask/confirmations /packages/wallet/src/initialization/instances/approval-controller/ @MetaMask/confirmations /packages/wallet/src/initialization/instances/connectivity-controller/ @MetaMask/core-platform /packages/wallet/src/initialization/instances/keyring-controller/ @MetaMask/accounts-engineers @MetaMask/core-platform diff --git a/README.md b/README.md index 31241932b0..65828bdd0c 100644 --- a/README.md +++ b/README.md @@ -612,6 +612,7 @@ linkStyle default opacity:0.5 user_operation_controller --> transaction_controller; user_operation_controller --> eth_block_tracker; wallet --> accounts_controller; + wallet --> address_book_controller; wallet --> approval_controller; wallet --> base_controller; wallet --> connectivity_controller; diff --git a/packages/wallet/CHANGELOG.md b/packages/wallet/CHANGELOG.md index 9afc460900..7b5d82544d 100644 --- a/packages/wallet/CHANGELOG.md +++ b/packages/wallet/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- **BREAKING:** Wire `AddressBookController` into the default wallet initialization ([#9291](https://github.com/MetaMask/core/pull/9291)) + ## [5.0.0] ### Added diff --git a/packages/wallet/package.json b/packages/wallet/package.json index b2935ac95e..41cfadbdd8 100644 --- a/packages/wallet/package.json +++ b/packages/wallet/package.json @@ -54,6 +54,7 @@ }, "dependencies": { "@metamask/accounts-controller": "^39.0.3", + "@metamask/address-book-controller": "^7.1.2", "@metamask/approval-controller": "^9.0.2", "@metamask/base-controller": "^9.1.0", "@metamask/browser-passworder": "^6.0.0", diff --git a/packages/wallet/src/Wallet.test.ts b/packages/wallet/src/Wallet.test.ts index 2cc683da6b..2c39a1544e 100644 --- a/packages/wallet/src/Wallet.test.ts +++ b/packages/wallet/src/Wallet.test.ts @@ -1,3 +1,4 @@ +import { getDefaultAddressBookControllerState } from '@metamask/address-book-controller'; import { CONNECTIVITY_STATUSES } from '@metamask/connectivity-controller'; import { Messenger } from '@metamask/messenger'; import { InMemoryStorageAdapter } from '@metamask/storage-service'; @@ -262,6 +263,62 @@ describe('Wallet', () => { }); }); + describe('AddressBookController', () => { + const ADDRESS = '0x1234567890123456789012345678901234567890'; + + it('is wired and exposes its state on the wallet messenger', async () => { + const wallet = await setupWallet(); + const { messenger } = wallet; + + expect(messenger.call('AddressBookController:getState')).toStrictEqual( + getDefaultAddressBookControllerState(), + ); + }); + + it('applies initial state passed through the Wallet constructor', () => { + const entry = { + address: ADDRESS, + name: 'Alice', + chainId: '0x1' as const, + memo: '', + isEns: false, + }; + + const wallet = new Wallet({ + state: { + AddressBookController: { + addressBook: { '0x1': { [ADDRESS]: entry } }, + }, + }, + instanceOptions: { + connectivityController: { + connectivityAdapter: new AlwaysOnlineAdapter(), + }, + networkController: { + infuraProjectId: 'fake-infura-project-id', + }, + storageService: { + storage: new InMemoryStorageAdapter(), + }, + remoteFeatureFlagController: REMOTE_FEATURE_FLAG_OPTIONS, + }, + }); + + expect( + wallet.state.AddressBookController.addressBook['0x1'][ADDRESS], + ).toStrictEqual(entry); + }); + + it('routes its method actions through the wallet messenger', async () => { + const wallet = await setupWallet(); + const { messenger } = wallet; + + messenger.call('AddressBookController:set', ADDRESS, 'Alice'); + + expect(messenger.call('AddressBookController:list')).toHaveLength(1); + }); + }); + describe('ConnectivityController', () => { it('reports online connectivity status', () => { const wallet = new Wallet({ diff --git a/packages/wallet/src/initialization/instances/address-book-controller/address-book-controller.test.ts b/packages/wallet/src/initialization/instances/address-book-controller/address-book-controller.test.ts new file mode 100644 index 0000000000..552cafe954 --- /dev/null +++ b/packages/wallet/src/initialization/instances/address-book-controller/address-book-controller.test.ts @@ -0,0 +1,93 @@ +import { + AddressBookController, + getDefaultAddressBookControllerState, +} from '@metamask/address-book-controller'; +import { Messenger } from '@metamask/messenger'; + +import { defaultConfigurations } from '../../defaults'; +import type { + DefaultActions, + DefaultEvents, + RootMessenger, +} from '../../defaults'; +import { addressBookController } from './address-book-controller'; + +const ADDRESS = '0x1234567890123456789012345678901234567890'; + +/** + * Creates a root messenger for use in tests. + * + * @returns A root messenger. + */ +function getRootMessenger(): RootMessenger { + return new Messenger({ namespace: 'Root' }); +} + +describe('addressBookController', () => { + it('is registered as a default initialization configuration', () => { + expect(Object.values(defaultConfigurations)).toContain( + addressBookController, + ); + }); + + it('initializes an AddressBookController with default state', () => { + const messenger = addressBookController.getMessenger(getRootMessenger()); + + const instance = addressBookController.init({ + state: undefined, + messenger, + options: {}, + }); + + expect(instance).toBeInstanceOf(AddressBookController); + expect(instance.state).toStrictEqual( + getDefaultAddressBookControllerState(), + ); + }); + + it('merges provided state over the defaults', () => { + const messenger = addressBookController.getMessenger(getRootMessenger()); + + const entry = { + address: ADDRESS, + name: 'Alice', + chainId: '0x1' as const, + memo: '', + isEns: false, + }; + + const instance = addressBookController.init({ + state: { addressBook: { '0x1': { [ADDRESS]: entry } } }, + messenger, + options: {}, + }); + + expect(instance.state.addressBook['0x1'][ADDRESS]).toStrictEqual(entry); + }); + + it('exposes its state through the root messenger', () => { + const rootMessenger = getRootMessenger(); + const messenger = addressBookController.getMessenger(rootMessenger); + + addressBookController.init({ state: undefined, messenger, options: {} }); + + expect(rootMessenger.call('AddressBookController:getState')).toStrictEqual( + getDefaultAddressBookControllerState(), + ); + }); + + it('registers its method actions on the root messenger', () => { + const rootMessenger = getRootMessenger(); + const messenger = addressBookController.getMessenger(rootMessenger); + + const instance = addressBookController.init({ + state: undefined, + messenger, + options: {}, + }); + + rootMessenger.call('AddressBookController:set', ADDRESS, 'Alice'); + + expect(instance.list()).toHaveLength(1); + }); +}); diff --git a/packages/wallet/src/initialization/instances/address-book-controller/address-book-controller.ts b/packages/wallet/src/initialization/instances/address-book-controller/address-book-controller.ts new file mode 100644 index 0000000000..eda7ee9343 --- /dev/null +++ b/packages/wallet/src/initialization/instances/address-book-controller/address-book-controller.ts @@ -0,0 +1,24 @@ +import { + AddressBookController, + AddressBookControllerMessenger, +} from '@metamask/address-book-controller'; +import { Messenger } from '@metamask/messenger'; + +import type { InitializationConfiguration } from '../../types'; + +export const addressBookController: InitializationConfiguration< + AddressBookController, + AddressBookControllerMessenger +> = { + name: 'AddressBookController', + init: ({ state, messenger }) => + new AddressBookController({ + state, + messenger, + }), + getMessenger: (parent) => + new Messenger({ + namespace: 'AddressBookController', + parent, + }), +}; diff --git a/packages/wallet/src/initialization/instances/index.ts b/packages/wallet/src/initialization/instances/index.ts index 03fab5e5d5..ad8d554b67 100644 --- a/packages/wallet/src/initialization/instances/index.ts +++ b/packages/wallet/src/initialization/instances/index.ts @@ -1,4 +1,5 @@ export { accountsController } from './accounts-controller/accounts-controller'; +export { addressBookController } from './address-book-controller/address-book-controller'; export { approvalController } from './approval-controller/approval-controller'; export { connectivityController } from './connectivity-controller/connectivity-controller'; export { keyringController } from './keyring-controller/keyring-controller'; diff --git a/packages/wallet/tsconfig.build.json b/packages/wallet/tsconfig.build.json index fcf397e033..bfa1aa578b 100644 --- a/packages/wallet/tsconfig.build.json +++ b/packages/wallet/tsconfig.build.json @@ -7,6 +7,7 @@ }, "references": [ { "path": "../accounts-controller/tsconfig.build.json" }, + { "path": "../address-book-controller/tsconfig.build.json" }, { "path": "../approval-controller/tsconfig.build.json" }, { "path": "../base-controller/tsconfig.build.json" }, { "path": "../connectivity-controller/tsconfig.build.json" }, diff --git a/packages/wallet/tsconfig.json b/packages/wallet/tsconfig.json index a206b1baba..9d52f9fb40 100644 --- a/packages/wallet/tsconfig.json +++ b/packages/wallet/tsconfig.json @@ -5,6 +5,7 @@ }, "references": [ { "path": "../accounts-controller/tsconfig.json" }, + { "path": "../address-book-controller/tsconfig.json" }, { "path": "../approval-controller/tsconfig.json" }, { "path": "../base-controller/tsconfig.json" }, { "path": "../connectivity-controller/tsconfig.json" }, diff --git a/yarn.lock b/yarn.lock index 780cd82fdd..65c0bc754d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8935,6 +8935,7 @@ __metadata: resolution: "@metamask/wallet@workspace:packages/wallet" dependencies: "@metamask/accounts-controller": "npm:^39.0.3" + "@metamask/address-book-controller": "npm:^7.1.2" "@metamask/approval-controller": "npm:^9.0.2" "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/base-controller": "npm:^9.1.0"