diff --git a/eslint-suppressions.json b/eslint-suppressions.json index 47eeb2b5bab..2c57c22256e 100644 --- a/eslint-suppressions.json +++ b/eslint-suppressions.json @@ -1415,7 +1415,7 @@ }, "packages/profile-sync-controller/src/controllers/user-storage/UserStorageController.ts": { "@typescript-eslint/explicit-function-return-type": { - "count": 13 + "count": 12 }, "id-length": { "count": 1 diff --git a/packages/account-tree-controller/CHANGELOG.md b/packages/account-tree-controller/CHANGELOG.md index 91b01bf7ac3..807407934a6 100644 --- a/packages/account-tree-controller/CHANGELOG.md +++ b/packages/account-tree-controller/CHANGELOG.md @@ -7,9 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -### Changed +### Added + +- Expose missing public `AccountTreeController` methods through its messenger ([#7976](https://github.com/MetaMask/core/pull/7976/)) + - The following actions are now available: + - `AccountTreeController:getAccountWalletObject` + - `AccountTreeController:getAccountWalletObjects` + - `AccountTreeController:getAccountGroupObject` + - `AccountTreeController:clearState` + - `AccountTreeController:syncWithUserStorage` + - `AccountTreeController:syncWithUserStorageAtLeastOnce` + - Corresponding action types (e.g. `AccountTreeControllerGetAccountWalletObjectAction`) are available as well. + +### Removed -- Bump `@metamask/accounts-controller` from `^36.0.0` to `^36.0.1` ([#7996](https://github.com/MetaMask/core/pull/7996)) +- **BREAKING:** Remove `resolveNameConflict` from `AccountTreeController` ([#7976](https://github.com/MetaMask/core/pull/7976)) + - This method was only used internally. ## [4.1.1] diff --git a/packages/account-tree-controller/package.json b/packages/account-tree-controller/package.json index 0d68a892882..d5e86f62d1b 100644 --- a/packages/account-tree-controller/package.json +++ b/packages/account-tree-controller/package.json @@ -40,6 +40,7 @@ "build:docs": "typedoc", "changelog:update": "../../scripts/update-changelog.sh @metamask/account-tree-controller", "changelog:validate": "../../scripts/validate-changelog.sh @metamask/account-tree-controller", + "generate-method-action-types": "tsx ../../scripts/generate-method-action-types.ts", "publish:preview": "yarn npm publish --tag preview", "since-latest-release": "../../scripts/since-latest-release.sh", "test": "NODE_OPTIONS=--experimental-vm-modules jest --reporters=jest-silent-reporter", @@ -72,6 +73,7 @@ "deepmerge": "^4.2.2", "jest": "^29.7.0", "ts-jest": "^29.2.5", + "tsx": "^4.20.5", "typedoc": "^0.25.13", "typedoc-plugin-missing-exports": "^2.0.0", "typescript": "~5.3.3", diff --git a/packages/account-tree-controller/src/AccountTreeController-method-action-types.ts b/packages/account-tree-controller/src/AccountTreeController-method-action-types.ts new file mode 100644 index 00000000000..365dbe342c9 --- /dev/null +++ b/packages/account-tree-controller/src/AccountTreeController-method-action-types.ts @@ -0,0 +1,196 @@ +/** + * This file is auto generated by `scripts/generate-method-action-types.ts`. + * Do not edit manually. + */ + +import type { AccountTreeController } from './AccountTreeController'; + +/** + * Gets the account wallet object from its ID. + * + * @param walletId - Account wallet ID. + * @returns The account wallet object if found, undefined otherwise. + */ +export type AccountTreeControllerGetAccountWalletObjectAction = { + type: `AccountTreeController:getAccountWalletObject`; + handler: AccountTreeController['getAccountWalletObject']; +}; + +/** + * Gets all account wallet objects. + * + * @returns All account wallet objects. + */ +export type AccountTreeControllerGetAccountWalletObjectsAction = { + type: `AccountTreeController:getAccountWalletObjects`; + handler: AccountTreeController['getAccountWalletObjects']; +}; + +/** + * Gets all underlying accounts from the currently selected account + * group. + * + * It also support account selector, which allows to filter specific + * accounts given some criterias (account type, address, scopes, etc...). + * + * @param selector - Optional account selector. + * @returns Underlying accounts for the currently selected account (filtered + * by the selector if provided). + */ +export type AccountTreeControllerGetAccountsFromSelectedAccountGroupAction = { + type: `AccountTreeController:getAccountsFromSelectedAccountGroup`; + handler: AccountTreeController['getAccountsFromSelectedAccountGroup']; +}; + +/** + * Gets the account group object from its ID. + * + * @param groupId - Account group ID. + * @returns The account group object if found, undefined otherwise. + */ +export type AccountTreeControllerGetAccountGroupObjectAction = { + type: `AccountTreeController:getAccountGroupObject`; + handler: AccountTreeController['getAccountGroupObject']; +}; + +/** + * Gets the account's context which contains its wallet ID, group ID, and sort order. + * + * @param accountId - Account ID. + * @returns The account context if found, undefined otherwise. + */ +export type AccountTreeControllerGetAccountContextAction = { + type: `AccountTreeController:getAccountContext`; + handler: AccountTreeController['getAccountContext']; +}; + +/** + * Gets the currently selected account group ID. + * + * @returns The selected account group ID or empty string if none selected. + */ +export type AccountTreeControllerGetSelectedAccountGroupAction = { + type: `AccountTreeController:getSelectedAccountGroup`; + handler: AccountTreeController['getSelectedAccountGroup']; +}; + +/** + * Sets the selected account group and updates the AccountsController selectedAccount accordingly. + * + * @param groupId - The account group ID to select. + */ +export type AccountTreeControllerSetSelectedAccountGroupAction = { + type: `AccountTreeController:setSelectedAccountGroup`; + handler: AccountTreeController['setSelectedAccountGroup']; +}; + +/** + * Sets a custom name for an account group. + * + * @param groupId - The account group ID. + * @param name - The custom name to set. + * @param autoHandleConflict - If true, automatically resolves name conflicts by adding a suffix. If false, throws on conflicts. + * @throws If the account group ID is not found in the current tree. + * @throws If the account group name already exists and autoHandleConflict is false. + */ +export type AccountTreeControllerSetAccountGroupNameAction = { + type: `AccountTreeController:setAccountGroupName`; + handler: AccountTreeController['setAccountGroupName']; +}; + +/** + * Sets a custom name for an account wallet. + * + * @param walletId - The account wallet ID. + * @param name - The custom name to set. + * @throws If the account wallet ID is not found in the current tree. + */ +export type AccountTreeControllerSetAccountWalletNameAction = { + type: `AccountTreeController:setAccountWalletName`; + handler: AccountTreeController['setAccountWalletName']; +}; + +/** + * Toggles the pinned state of an account group. + * + * @param groupId - The account group ID. + * @param pinned - Whether the group should be pinned. + * @throws If the account group ID is not found in the current tree. + */ +export type AccountTreeControllerSetAccountGroupPinnedAction = { + type: `AccountTreeController:setAccountGroupPinned`; + handler: AccountTreeController['setAccountGroupPinned']; +}; + +/** + * Toggles the hidden state of an account group. + * + * @param groupId - The account group ID. + * @param hidden - Whether the group should be hidden. + * @throws If the account group ID is not found in the current tree. + */ +export type AccountTreeControllerSetAccountGroupHiddenAction = { + type: `AccountTreeController:setAccountGroupHidden`; + handler: AccountTreeController['setAccountGroupHidden']; +}; + +/** + * Clears the controller state and resets to default values. + * Also clears the backup and sync service state. + */ +export type AccountTreeControllerClearStateAction = { + type: `AccountTreeController:clearState`; + handler: AccountTreeController['clearState']; +}; + +/** + * Bi-directionally syncs the account tree with user storage. + * This will perform a full sync, including both pulling updates + * from user storage and pushing local changes to user storage. + * This also performs legacy account syncing if needed. + * + * IMPORTANT: + * If a full sync is already in progress, it will return the ongoing promise. + * + * @returns A promise that resolves when the sync is complete. + */ +export type AccountTreeControllerSyncWithUserStorageAction = { + type: `AccountTreeController:syncWithUserStorage`; + handler: AccountTreeController['syncWithUserStorage']; +}; + +/** + * Bi-directionally syncs the account tree with user storage. + * This will ensure at least one full sync is ran, including both pulling updates + * from user storage and pushing local changes to user storage. + * This also performs legacy account syncing if needed. + * + * IMPORTANT: + * If the first ever full sync is already in progress, it will return the ongoing promise. + * If the first ever full sync was previously completed, it will NOT start a new sync, and will resolve immediately. + * + * @returns A promise that resolves when the first ever full sync is complete. + */ +export type AccountTreeControllerSyncWithUserStorageAtLeastOnceAction = { + type: `AccountTreeController:syncWithUserStorageAtLeastOnce`; + handler: AccountTreeController['syncWithUserStorageAtLeastOnce']; +}; + +/** + * Union of all AccountTreeController action types. + */ +export type AccountTreeControllerMethodActions = + | AccountTreeControllerGetAccountWalletObjectAction + | AccountTreeControllerGetAccountWalletObjectsAction + | AccountTreeControllerGetAccountsFromSelectedAccountGroupAction + | AccountTreeControllerGetAccountGroupObjectAction + | AccountTreeControllerGetAccountContextAction + | AccountTreeControllerGetSelectedAccountGroupAction + | AccountTreeControllerSetSelectedAccountGroupAction + | AccountTreeControllerSetAccountGroupNameAction + | AccountTreeControllerSetAccountWalletNameAction + | AccountTreeControllerSetAccountGroupPinnedAction + | AccountTreeControllerSetAccountGroupHiddenAction + | AccountTreeControllerClearStateAction + | AccountTreeControllerSyncWithUserStorageAction + | AccountTreeControllerSyncWithUserStorageAtLeastOnceAction; diff --git a/packages/account-tree-controller/src/AccountTreeController.test.ts b/packages/account-tree-controller/src/AccountTreeController.test.ts index 624e60b2af4..cf890836556 100644 --- a/packages/account-tree-controller/src/AccountTreeController.test.ts +++ b/packages/account-tree-controller/src/AccountTreeController.test.ts @@ -4575,21 +4575,18 @@ describe('AccountTreeController', () => { }); // Test suffix resolution directly using the public method - const wallet = controller.state.accountTree.wallets[walletId]; - const resolvedName = controller.resolveNameConflict( - wallet, - groupId, - 'Suffix Test', - ); - expect(resolvedName).toBe('Suffix Test (3)'); + controller.setAccountGroupName(groupId, 'Suffix Test', true); - // Test with no conflicts: should return "Unique Name (2)" - const uniqueName = controller.resolveNameConflict( - wallet, - groupId, - 'Unique Name', - ); - expect(uniqueName).toBe('Unique Name (2)'); + const collidingGroupObject = controller.getAccountGroupObject(groupId); + + expect(collidingGroupObject?.metadata.name).toBe('Suffix Test (3)'); + + // Test with no conflicts: should return "Unique Name" + controller.setAccountGroupName(groupId, 'Unique Name', true); + + const uniqueGroupObject = controller.getAccountGroupObject(groupId); + + expect(uniqueGroupObject?.metadata.name).toBe('Unique Name'); }); it('throws error when group ID not found in tree', () => { diff --git a/packages/account-tree-controller/src/AccountTreeController.ts b/packages/account-tree-controller/src/AccountTreeController.ts index 9f8d0aeacdc..20d993a1129 100644 --- a/packages/account-tree-controller/src/AccountTreeController.ts +++ b/packages/account-tree-controller/src/AccountTreeController.ts @@ -44,6 +44,23 @@ import type { AccountWalletObject, AccountWalletObjectOf } from './wallet'; export const controllerName = 'AccountTreeController'; +const MESSENGER_EXPOSED_METHODS = [ + 'getSelectedAccountGroup', + 'setSelectedAccountGroup', + 'getAccountsFromSelectedAccountGroup', + 'getAccountContext', + 'setAccountWalletName', + 'setAccountGroupName', + 'setAccountGroupPinned', + 'setAccountGroupHidden', + 'getAccountWalletObject', + 'getAccountWalletObjects', + 'getAccountGroupObject', + 'clearState', + 'syncWithUserStorage', + 'syncWithUserStorageAtLeastOnce', +] as const; + const accountTreeControllerMetadata: StateMetadata = { accountTree: { @@ -246,7 +263,10 @@ export class AccountTreeController extends BaseController< }, ); - this.#registerMessageHandlers(); + this.messenger.registerMethodActionHandlers( + this, + MESSENGER_EXPOSED_METHODS, + ); } /** @@ -507,7 +527,7 @@ export class AccountTreeController extends BaseController< proposedName.length && !isAccountGroupNameUniqueFromWallet(wallet, group.id, proposedName) ) { - proposedName = this.resolveNameConflict(wallet, group.id, proposedName); + proposedName = this.#resolveNameConflict(wallet, group.id, proposedName); } return proposedName; @@ -1324,7 +1344,7 @@ export class AccountTreeController extends BaseController< * @param name - The desired name that has a conflict. * @returns A unique name with suffix added if necessary. */ - resolveNameConflict( + #resolveNameConflict( wallet: AccountWalletObject, groupId: AccountGroupId, name: string, @@ -1371,7 +1391,7 @@ export class AccountTreeController extends BaseController< autoHandleConflict && !isAccountGroupNameUniqueFromWallet(wallet, groupId, name) ) { - finalName = this.resolveNameConflict(wallet, groupId, name); + finalName = this.#resolveNameConflict(wallet, groupId, name); } else { // Validate that the name is unique this.#assertAccountGroupNameIsUnique(groupId, finalName); @@ -1538,51 +1558,6 @@ export class AccountTreeController extends BaseController< this.#initialized = false; } - /** - * Registers message handlers for the AccountTreeController. - */ - #registerMessageHandlers(): void { - this.messenger.registerActionHandler( - `${controllerName}:getSelectedAccountGroup`, - this.getSelectedAccountGroup.bind(this), - ); - - this.messenger.registerActionHandler( - `${controllerName}:setSelectedAccountGroup`, - this.setSelectedAccountGroup.bind(this), - ); - - this.messenger.registerActionHandler( - `${controllerName}:getAccountsFromSelectedAccountGroup`, - this.getAccountsFromSelectedAccountGroup.bind(this), - ); - - this.messenger.registerActionHandler( - `${controllerName}:getAccountContext`, - this.getAccountContext.bind(this), - ); - - this.messenger.registerActionHandler( - `${controllerName}:setAccountWalletName`, - this.setAccountWalletName.bind(this), - ); - - this.messenger.registerActionHandler( - `${controllerName}:setAccountGroupName`, - this.setAccountGroupName.bind(this), - ); - - this.messenger.registerActionHandler( - `${controllerName}:setAccountGroupPinned`, - this.setAccountGroupPinned.bind(this), - ); - - this.messenger.registerActionHandler( - `${controllerName}:setAccountGroupHidden`, - this.setAccountGroupHidden.bind(this), - ); - } - /** * Bi-directionally syncs the account tree with user storage. * This will perform a full sync, including both pulling updates diff --git a/packages/account-tree-controller/src/index.ts b/packages/account-tree-controller/src/index.ts index d25b4896a36..96df965c0a6 100644 --- a/packages/account-tree-controller/src/index.ts +++ b/packages/account-tree-controller/src/index.ts @@ -11,14 +11,6 @@ export type { AccountTreeControllerState, AccountTreeControllerGetStateAction, AccountTreeControllerActions, - AccountTreeControllerSetSelectedAccountGroupAction, - AccountTreeControllerGetSelectedAccountGroupAction, - AccountTreeControllerGetAccountsFromSelectedAccountGroupAction, - AccountTreeControllerGetAccountContextAction, - AccountTreeControllerSetAccountWalletNameAction, - AccountTreeControllerSetAccountGroupNameAction, - AccountTreeControllerSetAccountGroupPinnedAction, - AccountTreeControllerSetAccountGroupHiddenAction, AccountTreeControllerStateChangeEvent, AccountTreeControllerAccountTreeChangeEvent, AccountTreeControllerSelectedAccountGroupChangeEvent, @@ -26,6 +18,23 @@ export type { AccountTreeControllerMessenger, } from './types'; +export type { + AccountTreeControllerGetAccountWalletObjectAction, + AccountTreeControllerGetAccountWalletObjectsAction, + AccountTreeControllerGetAccountsFromSelectedAccountGroupAction, + AccountTreeControllerGetAccountGroupObjectAction, + AccountTreeControllerGetAccountContextAction, + AccountTreeControllerGetSelectedAccountGroupAction, + AccountTreeControllerSetSelectedAccountGroupAction, + AccountTreeControllerSetAccountGroupNameAction, + AccountTreeControllerSetAccountWalletNameAction, + AccountTreeControllerSetAccountGroupPinnedAction, + AccountTreeControllerSetAccountGroupHiddenAction, + AccountTreeControllerClearStateAction, + AccountTreeControllerSyncWithUserStorageAction, + AccountTreeControllerSyncWithUserStorageAtLeastOnceAction, +} from './AccountTreeController-method-action-types'; + export type { AccountContext } from './AccountTreeController'; export { diff --git a/packages/account-tree-controller/src/types.ts b/packages/account-tree-controller/src/types.ts index 415c5b4f55f..2ca9126d698 100644 --- a/packages/account-tree-controller/src/types.ts +++ b/packages/account-tree-controller/src/types.ts @@ -23,10 +23,8 @@ import type { } from '@metamask/profile-sync-controller'; import type { GetSnap as SnapControllerGetSnap } from '@metamask/snaps-controllers'; -import type { - AccountTreeController, - controllerName, -} from './AccountTreeController'; +import type { controllerName } from './AccountTreeController'; +import type { AccountTreeControllerMethodActions } from './AccountTreeController-method-action-types'; import type { BackupAndSyncAnalyticsEventPayload, BackupAndSyncEmitAnalyticsEventParams, @@ -79,46 +77,6 @@ export type AccountTreeControllerGetStateAction = ControllerGetStateAction< AccountTreeControllerState >; -export type AccountTreeControllerSetSelectedAccountGroupAction = { - type: `${typeof controllerName}:setSelectedAccountGroup`; - handler: AccountTreeController['setSelectedAccountGroup']; -}; - -export type AccountTreeControllerGetSelectedAccountGroupAction = { - type: `${typeof controllerName}:getSelectedAccountGroup`; - handler: AccountTreeController['getSelectedAccountGroup']; -}; - -export type AccountTreeControllerGetAccountsFromSelectedAccountGroupAction = { - type: `${typeof controllerName}:getAccountsFromSelectedAccountGroup`; - handler: AccountTreeController['getAccountsFromSelectedAccountGroup']; -}; - -export type AccountTreeControllerGetAccountContextAction = { - type: `${typeof controllerName}:getAccountContext`; - handler: AccountTreeController['getAccountContext']; -}; - -export type AccountTreeControllerSetAccountWalletNameAction = { - type: `${typeof controllerName}:setAccountWalletName`; - handler: AccountTreeController['setAccountWalletName']; -}; - -export type AccountTreeControllerSetAccountGroupNameAction = { - type: `${typeof controllerName}:setAccountGroupName`; - handler: AccountTreeController['setAccountGroupName']; -}; - -export type AccountTreeControllerSetAccountGroupHiddenAction = { - type: `${typeof controllerName}:setAccountGroupHidden`; - handler: AccountTreeController['setAccountGroupHidden']; -}; - -export type AccountTreeControllerSetAccountGroupPinnedAction = { - type: `${typeof controllerName}:setAccountGroupPinned`; - handler: AccountTreeController['setAccountGroupPinned']; -}; - export type AllowedActions = | AccountsControllerGetAccountAction | AccountsControllerGetSelectedMultichainAccountAction @@ -127,23 +85,16 @@ export type AllowedActions = | KeyringControllerGetStateAction | SnapControllerGetSnap | UserStorageController.UserStorageControllerGetStateAction - | UserStorageController.UserStorageControllerPerformGetStorage - | UserStorageController.UserStorageControllerPerformGetStorageAllFeatureEntries - | UserStorageController.UserStorageControllerPerformSetStorage - | UserStorageController.UserStorageControllerPerformBatchSetStorage - | AuthenticationController.AuthenticationControllerGetSessionProfile + | UserStorageController.UserStorageControllerPerformGetStorageAction + | UserStorageController.UserStorageControllerPerformGetStorageAllFeatureEntriesAction + | UserStorageController.UserStorageControllerPerformSetStorageAction + | UserStorageController.UserStorageControllerPerformBatchSetStorageAction + | AuthenticationController.AuthenticationControllerGetSessionProfileAction | MultichainAccountServiceCreateMultichainAccountGroupAction; export type AccountTreeControllerActions = | AccountTreeControllerGetStateAction - | AccountTreeControllerSetSelectedAccountGroupAction - | AccountTreeControllerGetSelectedAccountGroupAction - | AccountTreeControllerGetAccountsFromSelectedAccountGroupAction - | AccountTreeControllerGetAccountContextAction - | AccountTreeControllerSetAccountWalletNameAction - | AccountTreeControllerSetAccountGroupNameAction - | AccountTreeControllerSetAccountGroupPinnedAction - | AccountTreeControllerSetAccountGroupHiddenAction; + | AccountTreeControllerMethodActions; export type AccountTreeControllerStateChangeEvent = ControllerStateChangeEvent< typeof controllerName, diff --git a/packages/accounts-controller/CHANGELOG.md b/packages/accounts-controller/CHANGELOG.md index 31c212349d9..d746fbfec31 100644 --- a/packages/accounts-controller/CHANGELOG.md +++ b/packages/accounts-controller/CHANGELOG.md @@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Expose missing public `AccountsController` methods through its messenger ([#7976](https://github.com/MetaMask/core/pull/7976/)) + - The following actions are now available: + - `AccountController:loadBackupAction` + - Corresponding action types (e.g. `AccountControllerLoadBackupAction`) are available as well. + +### Removed + +- **BREAKING:** Remove `getAccountExpect` from `AccountsController` ([#7976](https://github.com/MetaMask/core/pull/7976)) + - This method was only used internally. + ## [36.0.1] ### Changed diff --git a/packages/accounts-controller/package.json b/packages/accounts-controller/package.json index e81bf0dbc14..8ae42b8b765 100644 --- a/packages/accounts-controller/package.json +++ b/packages/accounts-controller/package.json @@ -40,6 +40,7 @@ "build:docs": "typedoc", "changelog:update": "../../scripts/update-changelog.sh @metamask/accounts-controller", "changelog:validate": "../../scripts/validate-changelog.sh @metamask/accounts-controller", + "generate-method-action-types": "tsx ../../scripts/generate-method-action-types.ts", "publish:preview": "yarn npm publish --tag preview", "since-latest-release": "../../scripts/since-latest-release.sh", "test": "NODE_OPTIONS=--experimental-vm-modules jest --reporters=jest-silent-reporter", @@ -77,6 +78,7 @@ "@types/readable-stream": "^2.3.0", "jest": "^29.7.0", "ts-jest": "^29.2.5", + "tsx": "^4.20.5", "typedoc": "^0.25.13", "typedoc-plugin-missing-exports": "^2.0.0", "typescript": "~5.3.3", diff --git a/packages/accounts-controller/src/AccountsController-method-action-types.ts b/packages/accounts-controller/src/AccountsController-method-action-types.ts new file mode 100644 index 00000000000..816b42d40c1 --- /dev/null +++ b/packages/accounts-controller/src/AccountsController-method-action-types.ts @@ -0,0 +1,168 @@ +/** + * This file is auto generated by `scripts/generate-method-action-types.ts`. + * Do not edit manually. + */ + +import type { AccountsController } from './AccountsController'; + +/** + * Returns the internal account object for the given account ID, if it exists. + * + * @param accountId - The ID of the account to retrieve. + * @returns The internal account object, or undefined if the account does not exist. + */ +export type AccountsControllerGetAccountAction = { + type: `AccountsController:getAccount`; + handler: AccountsController['getAccount']; +}; + +/** + * Returns the internal account objects for the given account IDs, if they exist. + * + * @param accountIds - The IDs of the accounts to retrieve. + * @returns The internal account objects, or undefined if the account(s) do not exist. + */ +export type AccountsControllerGetAccountsAction = { + type: `AccountsController:getAccounts`; + handler: AccountsController['getAccounts']; +}; + +/** + * Returns an array of all evm internal accounts. + * + * @returns An array of InternalAccount objects. + */ +export type AccountsControllerListAccountsAction = { + type: `AccountsController:listAccounts`; + handler: AccountsController['listAccounts']; +}; + +/** + * Returns an array of all internal accounts. + * + * @param chainId - The chain ID. + * @returns An array of InternalAccount objects. + */ +export type AccountsControllerListMultichainAccountsAction = { + type: `AccountsController:listMultichainAccounts`; + handler: AccountsController['listMultichainAccounts']; +}; + +/** + * Returns the last selected EVM account. + * + * @returns The selected internal account. + */ +export type AccountsControllerGetSelectedAccountAction = { + type: `AccountsController:getSelectedAccount`; + handler: AccountsController['getSelectedAccount']; +}; + +/** + * __WARNING The return value may be undefined if there isn't an account for that chain id.__ + * + * Retrieves the last selected account by chain ID. + * + * @param chainId - The chain ID to filter the accounts. + * @returns The last selected account compatible with the specified chain ID or undefined. + */ +export type AccountsControllerGetSelectedMultichainAccountAction = { + type: `AccountsController:getSelectedMultichainAccount`; + handler: AccountsController['getSelectedMultichainAccount']; +}; + +/** + * Returns the account with the specified address. + * ! This method will only return the first account that matches the address + * + * @param address - The address of the account to retrieve. + * @returns The account with the specified address, or undefined if not found. + */ +export type AccountsControllerGetAccountByAddressAction = { + type: `AccountsController:getAccountByAddress`; + handler: AccountsController['getAccountByAddress']; +}; + +/** + * Sets the selected account by its ID. + * + * @param accountId - The ID of the account to be selected. + */ +export type AccountsControllerSetSelectedAccountAction = { + type: `AccountsController:setSelectedAccount`; + handler: AccountsController['setSelectedAccount']; +}; + +/** + * Sets the name of the account with the given ID. + * + * @param accountId - The ID of the account to set the name for. + * @param accountName - The new name for the account. + * @throws An error if an account with the same name already exists. + */ +export type AccountsControllerSetAccountNameAction = { + type: `AccountsController:setAccountName`; + handler: AccountsController['setAccountName']; +}; + +/** + * Sets the name of the account with the given ID and select it. + * + * @param accountId - The ID of the account to set the name for and select. + * @param accountName - The new name for the account. + * @throws An error if an account with the same name already exists. + */ +export type AccountsControllerSetAccountNameAndSelectAccountAction = { + type: `AccountsController:setAccountNameAndSelectAccount`; + handler: AccountsController['setAccountNameAndSelectAccount']; +}; + +/** + * Updates the metadata of the account with the given ID. + * + * @param accountId - The ID of the account for which the metadata will be updated. + * @param metadata - The new metadata for the account. + */ +export type AccountsControllerUpdateAccountMetadataAction = { + type: `AccountsController:updateAccountMetadata`; + handler: AccountsController['updateAccountMetadata']; +}; + +/** + * Updates the internal accounts list by retrieving normal and snap accounts, + * removing duplicates, and updating the metadata of each account. + * + * @returns A Promise that resolves when the accounts have been updated. + */ +export type AccountsControllerUpdateAccountsAction = { + type: `AccountsController:updateAccounts`; + handler: AccountsController['updateAccounts']; +}; + +/** + * Loads the backup state of the accounts controller. + * + * @param backup - The backup state to load. + */ +export type AccountsControllerLoadBackupAction = { + type: `AccountsController:loadBackup`; + handler: AccountsController['loadBackup']; +}; + +/** + * Union of all AccountsController action types. + */ +export type AccountsControllerMethodActions = + | AccountsControllerGetAccountAction + | AccountsControllerGetAccountsAction + | AccountsControllerListAccountsAction + | AccountsControllerListMultichainAccountsAction + | AccountsControllerGetSelectedAccountAction + | AccountsControllerGetSelectedMultichainAccountAction + | AccountsControllerGetAccountByAddressAction + | AccountsControllerSetSelectedAccountAction + | AccountsControllerSetAccountNameAction + | AccountsControllerSetAccountNameAndSelectAccountAction + | AccountsControllerUpdateAccountMetadataAction + | AccountsControllerUpdateAccountsAction + | AccountsControllerLoadBackupAction; diff --git a/packages/accounts-controller/src/AccountsController.test.ts b/packages/accounts-controller/src/AccountsController.test.ts index 30efe029834..9ccfe9636b2 100644 --- a/packages/accounts-controller/src/AccountsController.test.ts +++ b/packages/accounts-controller/src/AccountsController.test.ts @@ -386,11 +386,9 @@ describe('AccountsController', () => { messenger.publish('SnapController:stateChange', mockSnapChangeState, []); - const updatedAccount = accountsController.getAccountExpect( - mockSnapAccount.id, - ); + const updatedAccount = accountsController.getAccount(mockSnapAccount.id); - expect(updatedAccount.metadata.snap?.enabled).toBe(true); + expect(updatedAccount?.metadata.snap?.enabled).toBe(true); }); it('disables an account if the Snap is disabled', async () => { @@ -430,11 +428,9 @@ describe('AccountsController', () => { messenger.publish('SnapController:stateChange', mockSnapChangeState, []); - const updatedAccount = accountsController.getAccountExpect( - mockSnapAccount.id, - ); + const updatedAccount = accountsController.getAccount(mockSnapAccount.id); - expect(updatedAccount.metadata.snap?.enabled).toBe(false); + expect(updatedAccount?.metadata.snap?.enabled).toBe(false); }); it('disables an account if the Snap is blocked', async () => { @@ -474,11 +470,9 @@ describe('AccountsController', () => { messenger.publish('SnapController:stateChange', mockSnapChangeState, []); - const updatedAccount = accountsController.getAccountExpect( - mockSnapAccount.id, - ); + const updatedAccount = accountsController.getAccount(mockSnapAccount.id); - expect(updatedAccount.metadata.snap?.enabled).toBe(false); + expect(updatedAccount?.metadata.snap?.enabled).toBe(false); }); it('does not trigger any unnecessary updates', async () => { @@ -521,16 +515,14 @@ describe('AccountsController', () => { // First update will update the account's metadata, thus triggering a `AccountsController:stateChange`. messenger.publish('SnapController:stateChange', mockSnapChangeState, []); - const updatedAccount = accountsController.getAccountExpect( - mockSnapAccount.id, - ); - expect(updatedAccount.metadata.snap?.enabled).toBe(true); + const updatedAccount = accountsController.getAccount(mockSnapAccount.id); + expect(updatedAccount?.metadata.snap?.enabled).toBe(true); expect(mockStateChange).toHaveBeenCalled(); // Second update is the same, thus the account does not need any update, and SHOULD NOT trigger a `AccountsController:stateChange`. mockStateChange.mockReset(); messenger.publish('SnapController:stateChange', mockSnapChangeState, []); - expect(updatedAccount.metadata.snap?.enabled).toBe(true); + expect(updatedAccount?.metadata.snap?.enabled).toBe(true); expect(mockStateChange).not.toHaveBeenCalled(); }); @@ -565,15 +557,13 @@ describe('AccountsController', () => { }); // Initial state - const account = accountsController.getAccountExpect(mockSnapAccount.id); - expect(account.metadata.snap?.enabled).toBe(true); + const account = accountsController.getAccount(mockSnapAccount.id); + expect(account?.metadata.snap?.enabled).toBe(true); // The Snap 'mock-snap' won't be found, so we will automatically consider it disabled. messenger.publish('SnapController:stateChange', mockSnapChangeState, []); - const updatedAccount = accountsController.getAccountExpect( - mockSnapAccount.id, - ); - expect(updatedAccount.metadata.snap?.enabled).toBe(false); + const updatedAccount = accountsController.getAccount(mockSnapAccount.id); + expect(updatedAccount?.metadata.snap?.enabled).toBe(false); }); }); @@ -2903,6 +2893,24 @@ describe('AccountsController', () => { EMPTY_ACCOUNT, ); }); + + it('throws an error if the selected account ID does not exist in the accounts list', () => { + const { accountsController } = setupAccountsController({ + initialState: { + internalAccounts: { + accounts: { + [mockOlderEvmAccount.id]: mockOlderEvmAccount, + [mockNewerEvmAccount.id]: mockNewerEvmAccount, + }, + selectedAccount: 'non-existent-account-id', + }, + }, + }); + + expect(() => accountsController.getSelectedAccount()).toThrow( + 'Account Id "non-existent-account-id" not found', + ); + }); }); describe('getSelectedMultichainAccount', () => { @@ -2990,6 +2998,25 @@ describe('AccountsController', () => { EMPTY_ACCOUNT, ); }); + + it('throws an error if the selected account ID does not exist in the accounts list', () => { + const { accountsController } = setupAccountsController({ + initialState: { + internalAccounts: { + accounts: { + [mockOlderEvmAccount.id]: mockOlderEvmAccount, + [mockNewerEvmAccount.id]: mockNewerEvmAccount, + [mockBtcAccount.id]: mockBtcAccount, + }, + selectedAccount: 'non-existent-account-id', + }, + }, + }); + + expect(() => accountsController.getSelectedMultichainAccount()).toThrow( + 'Account Id "non-existent-account-id" not found', + ); + }); }); describe('listAccounts', () => { @@ -3136,40 +3163,28 @@ describe('AccountsController', () => { }); }); - describe('getAccountExpect', () => { - it('return an account by ID', () => { + describe('setSelectedAccount', () => { + it('set the selected account', () => { const { accountsController } = setupAccountsController({ initialState: { internalAccounts: { - accounts: { [mockAccount.id]: mockAccount }, + accounts: { + [mockAccount.id]: mockAccount, + [mockAccount2.id]: mockAccount2, + }, selectedAccount: mockAccount.id, }, }, }); - const result = accountsController.getAccountExpect(mockAccount.id); - expect(result).toStrictEqual(setExpectedLastSelectedAsAny(mockAccount)); - }); - - it('throw an error for an unknown account ID', () => { - const accountId = 'unknown id'; - const { accountsController } = setupAccountsController({ - initialState: { - internalAccounts: { - accounts: { [mockAccount.id]: mockAccount }, - selectedAccount: mockAccount.id, - }, - }, - }); + accountsController.setSelectedAccount(mockAccount2.id); - expect(() => accountsController.getAccountExpect(accountId)).toThrow( - `Account Id "${accountId}" not found`, - ); + expect( + accountsController.state.internalAccounts.selectedAccount, + ).toStrictEqual(mockAccount2.id); }); - }); - describe('setSelectedAccount', () => { - it('set the selected account', () => { + it('throws an error if the account ID does not exist in the accounts list', () => { const { accountsController } = setupAccountsController({ initialState: { internalAccounts: { @@ -3177,16 +3192,14 @@ describe('AccountsController', () => { [mockAccount.id]: mockAccount, [mockAccount2.id]: mockAccount2, }, - selectedAccount: mockAccount.id, + selectedAccount: 'non-existent-account-id', }, }, }); - accountsController.setSelectedAccount(mockAccount2.id); - - expect( - accountsController.state.internalAccounts.selectedAccount, - ).toStrictEqual(mockAccount2.id); + expect(() => + accountsController.setSelectedAccount('non-existent-account-id'), + ).toThrow('Account Id "non-existent-account-id" not found'); }); it('does not emit setSelectedEvmAccountChange if the account is non-EVM', () => { @@ -3256,9 +3269,9 @@ describe('AccountsController', () => { newAccountName, ); - expect( - accountsController.getAccountExpect(mockAccount.id).metadata.name, - ).toBe(newAccountName); + expect(accountsController.getAccount(mockAccount.id)?.metadata.name).toBe( + newAccountName, + ); expect(accountsController.state.internalAccounts.selectedAccount).toBe( mockAccount.id, ); @@ -3283,7 +3296,7 @@ describe('AccountsController', () => { ); expect( - accountsController.getAccountExpect(mockAccount2.id).metadata.name, + accountsController.getAccount(mockAccount2.id)?.metadata.name, ).toBe(newAccountName); expect(accountsController.state.internalAccounts.selectedAccount).toBe( mockAccount2.id, @@ -3303,7 +3316,7 @@ describe('AccountsController', () => { ); expect( - accountsController.getAccountExpect(mockAccount.id).metadata + accountsController.getAccount(mockAccount.id)?.metadata .nameLastUpdatedAt, ).toBe(expectedTimestamp); }); @@ -3321,7 +3334,7 @@ describe('AccountsController', () => { expect(messengerSpy).toHaveBeenCalledWith( 'AccountsController:accountRenamed', - accountsController.getAccountExpect(mockAccount.id), + accountsController.getAccount(mockAccount.id), ); }); }); @@ -3338,9 +3351,9 @@ describe('AccountsController', () => { }); accountsController.setAccountName(mockAccount.id, 'new name'); - expect( - accountsController.getAccountExpect(mockAccount.id).metadata.name, - ).toBe('new name'); + expect(accountsController.getAccount(mockAccount.id)?.metadata.name).toBe( + 'new name', + ); }); it('sets the nameLastUpdatedAt timestamp when setting the name of an existing account', () => { @@ -3360,7 +3373,7 @@ describe('AccountsController', () => { accountsController.setAccountName(mockAccount.id, 'new name'); expect( - accountsController.getAccountExpect(mockAccount.id).metadata + accountsController.getAccount(mockAccount.id)?.metadata .nameLastUpdatedAt, ).toBe(expectedTimestamp); }); @@ -3382,7 +3395,7 @@ describe('AccountsController', () => { expect(messengerSpy).toHaveBeenCalledWith( 'AccountsController:accountRenamed', - accountsController.getAccountExpect(mockAccount.id), + accountsController.getAccount(mockAccount.id), ); }); @@ -3428,8 +3441,7 @@ describe('AccountsController', () => { }); expect( - accountsController.getAccountExpect(mockAccount.id).metadata - .lastSelected, + accountsController.getAccount(mockAccount.id)?.metadata.lastSelected, ).toBe(1); }); }); diff --git a/packages/accounts-controller/src/AccountsController.ts b/packages/accounts-controller/src/AccountsController.ts index 58aace7d3fd..832c600c1aa 100644 --- a/packages/accounts-controller/src/AccountsController.ts +++ b/packages/accounts-controller/src/AccountsController.ts @@ -38,6 +38,7 @@ import type { CaipChainId } from '@metamask/utils'; import type { WritableDraft } from 'immer/dist/internal.js'; import { cloneDeep } from 'lodash'; +import { AccountsControllerMethodActions } from './AccountsController-method-action-types'; import type { MultichainNetworkControllerNetworkDidChangeEvent } from './types'; import type { AccountsControllerStrictState } from './typing'; import type { HdSnapKeyringAccount } from './utils'; @@ -67,65 +68,21 @@ export type AccountsControllerGetStateAction = ControllerGetStateAction< AccountsControllerState >; -export type AccountsControllerSetSelectedAccountAction = { - type: `${typeof controllerName}:setSelectedAccount`; - handler: AccountsController['setSelectedAccount']; -}; - -export type AccountsControllerSetAccountNameAction = { - type: `${typeof controllerName}:setAccountName`; - handler: AccountsController['setAccountName']; -}; - -export type AccountsControllerSetAccountNameAndSelectAccountAction = { - type: `${typeof controllerName}:setAccountNameAndSelectAccount`; - handler: AccountsController['setAccountNameAndSelectAccount']; -}; - -export type AccountsControllerListAccountsAction = { - type: `${typeof controllerName}:listAccounts`; - handler: AccountsController['listAccounts']; -}; - -export type AccountsControllerListMultichainAccountsAction = { - type: `${typeof controllerName}:listMultichainAccounts`; - handler: AccountsController['listMultichainAccounts']; -}; - -export type AccountsControllerUpdateAccountsAction = { - type: `${typeof controllerName}:updateAccounts`; - handler: AccountsController['updateAccounts']; -}; - -export type AccountsControllerGetSelectedAccountAction = { - type: `${typeof controllerName}:getSelectedAccount`; - handler: AccountsController['getSelectedAccount']; -}; - -export type AccountsControllerGetSelectedMultichainAccountAction = { - type: `${typeof controllerName}:getSelectedMultichainAccount`; - handler: AccountsController['getSelectedMultichainAccount']; -}; - -export type AccountsControllerGetAccountByAddressAction = { - type: `${typeof controllerName}:getAccountByAddress`; - handler: AccountsController['getAccountByAddress']; -}; - -export type AccountsControllerGetAccountAction = { - type: `${typeof controllerName}:getAccount`; - handler: AccountsController['getAccount']; -}; - -export type AccountsControllerGetAccountsAction = { - type: `${typeof controllerName}:getAccounts`; - handler: AccountsController['getAccounts']; -}; - -export type AccountsControllerUpdateAccountMetadataAction = { - type: `${typeof controllerName}:updateAccountMetadata`; - handler: AccountsController['updateAccountMetadata']; -}; +const MESSENGER_EXPOSED_METHODS = [ + 'setSelectedAccount', + 'setAccountName', + 'setAccountNameAndSelectAccount', + 'listAccounts', + 'listMultichainAccounts', + 'updateAccounts', + 'getSelectedAccount', + 'getSelectedMultichainAccount', + 'getAccountByAddress', + 'getAccount', + 'getAccounts', + 'updateAccountMetadata', + 'loadBackup', +] as const; export type AllowedActions = | KeyringControllerGetKeyringsByTypeAction @@ -133,18 +90,7 @@ export type AllowedActions = export type AccountsControllerActions = | AccountsControllerGetStateAction - | AccountsControllerSetSelectedAccountAction - | AccountsControllerListAccountsAction - | AccountsControllerListMultichainAccountsAction - | AccountsControllerSetAccountNameAction - | AccountsControllerSetAccountNameAndSelectAccountAction - | AccountsControllerUpdateAccountsAction - | AccountsControllerGetAccountByAddressAction - | AccountsControllerGetSelectedAccountAction - | AccountsControllerGetAccountAction - | AccountsControllerGetAccountsAction - | AccountsControllerGetSelectedMultichainAccountAction - | AccountsControllerUpdateAccountMetadataAction; + | AccountsControllerMethodActions; export type AccountsControllerChangeEvent = ControllerStateChangeEvent< typeof controllerName, @@ -298,8 +244,12 @@ export class AccountsController extends BaseController< }, }); + this.messenger.registerMethodActionHandlers( + this, + MESSENGER_EXPOSED_METHODS, + ); + this.#subscribeToMessageEvents(); - this.#registerMessageHandlers(); } /** @@ -360,7 +310,7 @@ export class AccountsController extends BaseController< * @returns The internal account object. * @throws An error if the account ID is not found. */ - getAccountExpect(accountId: string): InternalAccount { + #getAccountExpect(accountId: string): InternalAccount { const account = this.getAccount(accountId); if (account === undefined) { throw new Error(`Account Id "${accountId}" not found`); @@ -384,7 +334,7 @@ export class AccountsController extends BaseController< return EMPTY_ACCOUNT; } - const account = this.getAccountExpect(selectedAccount); + const account = this.#getAccountExpect(selectedAccount); if (isEvmAccountType(account.type)) { return account; } @@ -422,7 +372,7 @@ export class AccountsController extends BaseController< } if (!chainId) { - return this.getAccountExpect(selectedAccount); + return this.#getAccountExpect(selectedAccount); } const accounts = this.listMultichainAccounts(chainId); @@ -448,7 +398,7 @@ export class AccountsController extends BaseController< * @param accountId - The ID of the account to be selected. */ setSelectedAccount(accountId: string): void { - const account = this.getAccountExpect(accountId); + const account = this.#getAccountExpect(accountId); if (this.state.internalAccounts.selectedAccount === account.id) { return; @@ -486,7 +436,7 @@ export class AccountsController extends BaseController< * @throws An error if an account with the same name already exists. */ setAccountNameAndSelectAccount(accountId: string, accountName: string): void { - const account = this.getAccountExpect(accountId); + const account = this.#getAccountExpect(accountId); this.#assertAccountCanBeRenamed(account, accountName); @@ -536,7 +486,7 @@ export class AccountsController extends BaseController< accountId: string, metadata: Partial, ): void { - const account = this.getAccountExpect(accountId); + const account = this.#getAccountExpect(accountId); if (metadata.name) { this.#assertAccountCanBeRenamed(account, metadata.name); @@ -1178,69 +1128,4 @@ export class AccountsController extends BaseController< (id) => this.#handleOnMultichainNetworkDidChange(id), ); } - - /** - * Registers message handlers for the AccountsController. - */ - #registerMessageHandlers(): void { - this.messenger.registerActionHandler( - `${controllerName}:setSelectedAccount`, - this.setSelectedAccount.bind(this), - ); - - this.messenger.registerActionHandler( - `${controllerName}:listAccounts`, - this.listAccounts.bind(this), - ); - - this.messenger.registerActionHandler( - `${controllerName}:listMultichainAccounts`, - this.listMultichainAccounts.bind(this), - ); - - this.messenger.registerActionHandler( - `${controllerName}:setAccountName`, - this.setAccountName.bind(this), - ); - - this.messenger.registerActionHandler( - `${controllerName}:setAccountNameAndSelectAccount`, - this.setAccountNameAndSelectAccount.bind(this), - ); - - this.messenger.registerActionHandler( - `${controllerName}:updateAccounts`, - this.updateAccounts.bind(this), - ); - - this.messenger.registerActionHandler( - `${controllerName}:getSelectedAccount`, - this.getSelectedAccount.bind(this), - ); - - this.messenger.registerActionHandler( - `${controllerName}:getSelectedMultichainAccount`, - this.getSelectedMultichainAccount.bind(this), - ); - - this.messenger.registerActionHandler( - `${controllerName}:getAccountByAddress`, - this.getAccountByAddress.bind(this), - ); - - this.messenger.registerActionHandler( - `AccountsController:getAccount`, - this.getAccount.bind(this), - ); - - this.messenger.registerActionHandler( - `AccountsController:getAccounts`, - this.getAccounts.bind(this), - ); - - this.messenger.registerActionHandler( - `AccountsController:updateAccountMetadata`, - this.updateAccountMetadata.bind(this), - ); - } } diff --git a/packages/accounts-controller/src/index.ts b/packages/accounts-controller/src/index.ts index c3af66e31d5..cbf415bd443 100644 --- a/packages/accounts-controller/src/index.ts +++ b/packages/accounts-controller/src/index.ts @@ -2,18 +2,6 @@ export type { AccountId, AccountsControllerState, AccountsControllerGetStateAction, - AccountsControllerSetSelectedAccountAction, - AccountsControllerSetAccountNameAction, - AccountsControllerSetAccountNameAndSelectAccountAction, - AccountsControllerListAccountsAction, - AccountsControllerListMultichainAccountsAction, - AccountsControllerUpdateAccountsAction, - AccountsControllerGetSelectedAccountAction, - AccountsControllerGetSelectedMultichainAccountAction, - AccountsControllerGetAccountByAddressAction, - AccountsControllerGetAccountAction, - AccountsControllerGetAccountsAction, - AccountsControllerUpdateAccountMetadataAction, AllowedActions, AccountsControllerActions, AccountsControllerChangeEvent, @@ -29,6 +17,21 @@ export type { AccountsControllerEvents, AccountsControllerMessenger, } from './AccountsController'; +export type { + AccountsControllerGetAccountAction, + AccountsControllerGetAccountsAction, + AccountsControllerListAccountsAction, + AccountsControllerListMultichainAccountsAction, + AccountsControllerGetSelectedAccountAction, + AccountsControllerGetSelectedMultichainAccountAction, + AccountsControllerGetAccountByAddressAction, + AccountsControllerSetSelectedAccountAction, + AccountsControllerSetAccountNameAction, + AccountsControllerSetAccountNameAndSelectAccountAction, + AccountsControllerUpdateAccountMetadataAction, + AccountsControllerUpdateAccountsAction, + AccountsControllerLoadBackupAction, +} from './AccountsController-method-action-types'; export { EMPTY_ACCOUNT, AccountsController } from './AccountsController'; export { keyringTypeToName, diff --git a/packages/assets-controllers/src/TokenBalancesController.ts b/packages/assets-controllers/src/TokenBalancesController.ts index 2ac7fcc38b1..2c8f0e91a43 100644 --- a/packages/assets-controllers/src/TokenBalancesController.ts +++ b/packages/assets-controllers/src/TokenBalancesController.ts @@ -151,7 +151,7 @@ export type AllowedActions = | AccountTrackerUpdateNativeBalancesAction | AccountTrackerUpdateStakedBalancesAction | KeyringControllerGetStateAction - | AuthenticationController.AuthenticationControllerGetBearerToken; + | AuthenticationController.AuthenticationControllerGetBearerTokenAction; export type AllowedEvents = | TokensControllerStateChangeEvent diff --git a/packages/assets-controllers/src/TokenDetectionController.ts b/packages/assets-controllers/src/TokenDetectionController.ts index 464ed175d23..d3c2f312806 100644 --- a/packages/assets-controllers/src/TokenDetectionController.ts +++ b/packages/assets-controllers/src/TokenDetectionController.ts @@ -152,7 +152,7 @@ export type AllowedActions = | TokensControllerAddDetectedTokensAction | TokensControllerAddTokensAction | NetworkControllerFindNetworkClientIdByChainIdAction - | AuthenticationController.AuthenticationControllerGetBearerToken; + | AuthenticationController.AuthenticationControllerGetBearerTokenAction; export type TokenDetectionControllerStateChangeEvent = ControllerStateChangeEvent; diff --git a/packages/bridge-controller/src/types.ts b/packages/bridge-controller/src/types.ts index 8a215683fb1..41217d8578c 100644 --- a/packages/bridge-controller/src/types.ts +++ b/packages/bridge-controller/src/types.ts @@ -13,7 +13,7 @@ import type { NetworkControllerFindNetworkClientIdByChainIdAction, NetworkControllerGetNetworkClientByIdAction, } from '@metamask/network-controller'; -import type { AuthenticationControllerGetBearerToken } from '@metamask/profile-sync-controller/auth'; +import type { AuthenticationControllerGetBearerTokenAction } from '@metamask/profile-sync-controller/auth'; import type { RemoteFeatureFlagControllerGetStateAction } from '@metamask/remote-feature-flag-controller'; import type { HandleSnapRequest } from '@metamask/snaps-controllers'; import type { Infer } from '@metamask/superstruct'; @@ -393,7 +393,7 @@ export type BridgeControllerEvents = BridgeControllerStateChangeEvent; export type AllowedActions = | AccountsControllerGetAccountByAddressAction - | AuthenticationControllerGetBearerToken + | AuthenticationControllerGetBearerTokenAction | GetCurrencyRateState | TokenRatesControllerGetStateAction | MultichainAssetsRatesControllerGetStateAction diff --git a/packages/bridge-status-controller/src/types.ts b/packages/bridge-status-controller/src/types.ts index 66f3cb6464c..12d4688e4c5 100644 --- a/packages/bridge-status-controller/src/types.ts +++ b/packages/bridge-status-controller/src/types.ts @@ -20,7 +20,7 @@ import type { NetworkControllerGetNetworkClientByIdAction, NetworkControllerGetStateAction, } from '@metamask/network-controller'; -import type { AuthenticationControllerGetBearerToken } from '@metamask/profile-sync-controller/auth'; +import type { AuthenticationControllerGetBearerTokenAction } from '@metamask/profile-sync-controller/auth'; import type { RemoteFeatureFlagControllerGetStateAction } from '@metamask/remote-feature-flag-controller'; import type { HandleSnapRequest } from '@metamask/snaps-controllers'; import type { Infer } from '@metamask/superstruct'; @@ -304,7 +304,7 @@ type AllowedActions = | GetGasFeeState | AccountsControllerGetAccountByAddressAction | RemoteFeatureFlagControllerGetStateAction - | AuthenticationControllerGetBearerToken; + | AuthenticationControllerGetBearerTokenAction; /** * The external events available to the BridgeStatusController. diff --git a/packages/claims-controller/src/ClaimsService.ts b/packages/claims-controller/src/ClaimsService.ts index 1e54c596c44..8e09a1a50a0 100644 --- a/packages/claims-controller/src/ClaimsService.ts +++ b/packages/claims-controller/src/ClaimsService.ts @@ -55,7 +55,7 @@ export type ClaimsServiceActions = | ClaimsServiceGenerateMessageForClaimSignatureAction; export type AllowedActions = - AuthenticationController.AuthenticationControllerGetBearerToken; + AuthenticationController.AuthenticationControllerGetBearerTokenAction; export type ClaimsServiceEvents = never; diff --git a/packages/core-backend/src/BackendWebSocketService.ts b/packages/core-backend/src/BackendWebSocketService.ts index 59e2d4efb8d..d8ca9139d5d 100644 --- a/packages/core-backend/src/BackendWebSocketService.ts +++ b/packages/core-backend/src/BackendWebSocketService.ts @@ -237,7 +237,7 @@ export type BackendWebSocketServiceActions = BackendWebSocketServiceMethodActions; type AllowedActions = - AuthenticationController.AuthenticationControllerGetBearerToken; + AuthenticationController.AuthenticationControllerGetBearerTokenAction; // Event types for WebSocket connection state changes export type BackendWebSocketServiceConnectionStateChangedEvent = { diff --git a/packages/multichain-account-service/package.json b/packages/multichain-account-service/package.json index 386f59a5656..c95bea8f732 100644 --- a/packages/multichain-account-service/package.json +++ b/packages/multichain-account-service/package.json @@ -40,6 +40,7 @@ "build:docs": "typedoc", "changelog:update": "../../scripts/update-changelog.sh @metamask/multichain-account-service", "changelog:validate": "../../scripts/validate-changelog.sh @metamask/multichain-account-service", + "generate-method-action-types": "tsx ../../scripts/generate-method-action-types.ts", "publish:preview": "yarn npm publish --tag preview", "since-latest-release": "../../scripts/since-latest-release.sh", "test": "NODE_OPTIONS=--experimental-vm-modules jest --reporters=jest-silent-reporter", @@ -78,6 +79,7 @@ "deepmerge": "^4.2.2", "jest": "^29.7.0", "ts-jest": "^29.2.5", + "tsx": "^4.20.5", "typedoc": "^0.25.13", "typedoc-plugin-missing-exports": "^2.0.0", "typescript": "~5.3.3", diff --git a/packages/multichain-account-service/src/MultichainAccountService-method-action-types.ts b/packages/multichain-account-service/src/MultichainAccountService-method-action-types.ts new file mode 100644 index 00000000000..caf82281de1 --- /dev/null +++ b/packages/multichain-account-service/src/MultichainAccountService-method-action-types.ts @@ -0,0 +1,191 @@ +/** + * This file is auto generated by `scripts/generate-method-action-types.ts`. + * Do not edit manually. + */ + +import type { MultichainAccountService } from './MultichainAccountService'; + +/** + * Re-synchronize MetaMask accounts and the providers accounts if needed. + * + * NOTE: This is mostly required if one of the providers (keyrings or Snaps) + * have different sets of accounts. This method would ensure that both are + * in-sync and use the same accounts (and same IDs). + * + * READ THIS CAREFULLY (State inconsistency bugs/de-sync) + * We've seen some problems were keyring accounts on some Snaps were not synchronized + * with the accounts on MM side. This causes problems where we cannot interact with + * those accounts because the Snap does know about them. + * To "workaround" this de-sync problem for now, we make sure that both parties are + * in-sync when the service boots up. + * ---------------------------------------------------------------------------------- + */ +export type MultichainAccountServiceResyncAccountsAction = { + type: `MultichainAccountService:resyncAccounts`; + handler: MultichainAccountService['resyncAccounts']; +}; + +export type MultichainAccountServiceEnsureCanUseSnapPlatformAction = { + type: `MultichainAccountService:ensureCanUseSnapPlatform`; + handler: MultichainAccountService['ensureCanUseSnapPlatform']; +}; + +/** + * Gets a reference to the multichain account wallet matching this entropy source. + * + * @param options - Options. + * @param options.entropySource - The entropy source of the multichain account. + * @throws If none multichain account match this entropy. + * @returns A reference to the multichain account wallet. + */ +export type MultichainAccountServiceGetMultichainAccountWalletAction = { + type: `MultichainAccountService:getMultichainAccountWallet`; + handler: MultichainAccountService['getMultichainAccountWallet']; +}; + +/** + * Gets an array of all multichain account wallets. + * + * @returns An array of all multichain account wallets. + */ +export type MultichainAccountServiceGetMultichainAccountWalletsAction = { + type: `MultichainAccountService:getMultichainAccountWallets`; + handler: MultichainAccountService['getMultichainAccountWallets']; +}; + +/** + * Creates a new multichain account wallet by either importing an existing mnemonic, + * creating a new vault and keychain, or restoring a vault and keyring. + * + * NOTE: This method should only be called in client code where a mutex lock is acquired. + * `discoverAccounts` should be called after this method to discover and create accounts. + * + * @param params - The parameters to use to create the new wallet. + * @param params.mnemonic - The mnemonic to use to create the new wallet. + * @param params.password - The password to encrypt the vault with. + * @param params.type - The flow type to use to create the new wallet. + * @throws If the mnemonic has already been imported. + * @returns The new multichain account wallet. + */ +export type MultichainAccountServiceCreateMultichainAccountWalletAction = { + type: `MultichainAccountService:createMultichainAccountWallet`; + handler: MultichainAccountService['createMultichainAccountWallet']; +}; + +/** + * Removes a multichain account wallet. + * + * NOTE: This method should only be called in client code as a revert mechanism. + * At the point that this code is called, discovery shouldn't have been triggered. + * This is meant to be used in the scenario where a seed phrase backup is not successful. + * + * @param entropySource - The entropy source of the multichain account wallet. + * @param accountAddress - The address of the account to remove. + * @returns The removed multichain account wallet. + */ +export type MultichainAccountServiceRemoveMultichainAccountWalletAction = { + type: `MultichainAccountService:removeMultichainAccountWallet`; + handler: MultichainAccountService['removeMultichainAccountWallet']; +}; + +/** + * Gets a reference to the multichain account group matching this entropy source + * and a group index. + * + * @param options - Options. + * @param options.entropySource - The entropy source of the multichain account. + * @param options.groupIndex - The group index of the multichain account. + * @throws If none multichain account match this entropy source and group index. + * @returns A reference to the multichain account. + */ +export type MultichainAccountServiceGetMultichainAccountGroupAction = { + type: `MultichainAccountService:getMultichainAccountGroup`; + handler: MultichainAccountService['getMultichainAccountGroup']; +}; + +/** + * Gets all multichain account groups for a given entropy source. + * + * @param options - Options. + * @param options.entropySource - The entropy source to query. + * @throws If no multichain accounts match this entropy source. + * @returns A list of all multichain accounts. + */ +export type MultichainAccountServiceGetMultichainAccountGroupsAction = { + type: `MultichainAccountService:getMultichainAccountGroups`; + handler: MultichainAccountService['getMultichainAccountGroups']; +}; + +/** + * Creates the next multichain account group. + * + * @param options - Options. + * @param options.entropySource - The wallet's entropy source. + * @returns The next multichain account group. + */ +export type MultichainAccountServiceCreateNextMultichainAccountGroupAction = { + type: `MultichainAccountService:createNextMultichainAccountGroup`; + handler: MultichainAccountService['createNextMultichainAccountGroup']; +}; + +/** + * Creates a multichain account group. + * + * @param options - Options. + * @param options.groupIndex - The group index to use. + * @param options.entropySource - The wallet's entropy source. + * @returns The multichain account group for this group index. + */ +export type MultichainAccountServiceCreateMultichainAccountGroupAction = { + type: `MultichainAccountService:createMultichainAccountGroup`; + handler: MultichainAccountService['createMultichainAccountGroup']; +}; + +/** + * Set basic functionality state and trigger alignment if enabled. + * When basic functionality is disabled, snap-based providers are disabled. + * When enabled, all snap providers are enabled and wallet alignment is triggered. + * EVM providers are never disabled as they're required for basic wallet functionality. + * + * @param enabled - Whether basic functionality is enabled. + */ +export type MultichainAccountServiceSetBasicFunctionalityAction = { + type: `MultichainAccountService:setBasicFunctionality`; + handler: MultichainAccountService['setBasicFunctionality']; +}; + +/** + * Align all multichain account wallets. + */ +export type MultichainAccountServiceAlignWalletsAction = { + type: `MultichainAccountService:alignWallets`; + handler: MultichainAccountService['alignWallets']; +}; + +/** + * Align a specific multichain account wallet. + * + * @param entropySource - The entropy source of the multichain account wallet. + */ +export type MultichainAccountServiceAlignWalletAction = { + type: `MultichainAccountService:alignWallet`; + handler: MultichainAccountService['alignWallet']; +}; + +/** + * Union of all MultichainAccountService action types. + */ +export type MultichainAccountServiceMethodActions = + | MultichainAccountServiceResyncAccountsAction + | MultichainAccountServiceEnsureCanUseSnapPlatformAction + | MultichainAccountServiceGetMultichainAccountWalletAction + | MultichainAccountServiceGetMultichainAccountWalletsAction + | MultichainAccountServiceCreateMultichainAccountWalletAction + | MultichainAccountServiceRemoveMultichainAccountWalletAction + | MultichainAccountServiceGetMultichainAccountGroupAction + | MultichainAccountServiceGetMultichainAccountGroupsAction + | MultichainAccountServiceCreateNextMultichainAccountGroupAction + | MultichainAccountServiceCreateMultichainAccountGroupAction + | MultichainAccountServiceSetBasicFunctionalityAction + | MultichainAccountServiceAlignWalletsAction + | MultichainAccountServiceAlignWalletAction; diff --git a/packages/multichain-account-service/src/MultichainAccountService.test.ts b/packages/multichain-account-service/src/MultichainAccountService.test.ts index c452c3c1df4..474c2675bfb 100644 --- a/packages/multichain-account-service/src/MultichainAccountService.test.ts +++ b/packages/multichain-account-service/src/MultichainAccountService.test.ts @@ -847,13 +847,14 @@ describe('MultichainAccountService', () => { }); it('resync accounts with MultichainAccountService:resyncAccounts', async () => { - const { messenger, service } = await setup({ + const { messenger, mocks } = await setup({ accounts: [MOCK_HD_ACCOUNT_1], }); - const resyncAccountsSpy = jest.spyOn(service, 'resyncAccounts'); await messenger.call('MultichainAccountService:resyncAccounts'); - expect(resyncAccountsSpy).toHaveBeenCalled(); + + expect(mocks.EvmAccountProvider.resyncAccounts).toHaveBeenCalled(); + expect(mocks.SolAccountProvider.resyncAccounts).toHaveBeenCalled(); }); it('removes a multichain account wallet with MultichainAccountService:removeMultichainAccountWallet', async () => { @@ -873,18 +874,16 @@ describe('MultichainAccountService', () => { }); it('checks for Snap platform readiness with MultichainAccountService:ensureCanUseSnapPlatform', async () => { - const { messenger, service } = await setup({ + const { rootMessenger, spies } = await setup({ accounts: [], }); - await service.ensureCanUseSnapPlatform(); - - const ensureCanUseSnapPlatformSpy = jest.spyOn( - service, - 'ensureCanUseSnapPlatform', + await rootMessenger.call( + 'MultichainAccountService:ensureCanUseSnapPlatform', ); - await messenger.call('MultichainAccountService:ensureCanUseSnapPlatform'); - expect(ensureCanUseSnapPlatformSpy).toHaveBeenCalled(); + expect( + spies.SnapPlatformWatcher.ensureCanUseSnapPlatform, + ).toHaveBeenCalled(); }); }); diff --git a/packages/multichain-account-service/src/MultichainAccountService.ts b/packages/multichain-account-service/src/MultichainAccountService.ts index bd8c39e7b7d..792c1f9b451 100644 --- a/packages/multichain-account-service/src/MultichainAccountService.ts +++ b/packages/multichain-account-service/src/MultichainAccountService.ts @@ -90,6 +90,22 @@ export type CreateWalletParams = password: string; }; +const MESSENGER_EXPOSED_METHODS = [ + 'getMultichainAccountGroup', + 'getMultichainAccountGroups', + 'getMultichainAccountWallet', + 'getMultichainAccountWallets', + 'createNextMultichainAccountGroup', + 'createMultichainAccountGroup', + 'setBasicFunctionality', + 'alignWallets', + 'alignWallet', + 'createMultichainAccountWallet', + 'resyncAccounts', + 'removeMultichainAccountWallet', + 'ensureCanUseSnapPlatform', +] as const; + /** * Service to expose multichain accounts capabilities. */ @@ -154,57 +170,9 @@ export class MultichainAccountService { this.#watcher = new SnapPlatformWatcher(messenger); - this.#messenger.registerActionHandler( - 'MultichainAccountService:getMultichainAccountGroup', - (...args) => this.getMultichainAccountGroup(...args), - ); - this.#messenger.registerActionHandler( - 'MultichainAccountService:getMultichainAccountGroups', - (...args) => this.getMultichainAccountGroups(...args), - ); - this.#messenger.registerActionHandler( - 'MultichainAccountService:getMultichainAccountWallet', - (...args) => this.getMultichainAccountWallet(...args), - ); - this.#messenger.registerActionHandler( - 'MultichainAccountService:getMultichainAccountWallets', - (...args) => this.getMultichainAccountWallets(...args), - ); - this.#messenger.registerActionHandler( - 'MultichainAccountService:createNextMultichainAccountGroup', - (...args) => this.createNextMultichainAccountGroup(...args), - ); - this.#messenger.registerActionHandler( - 'MultichainAccountService:createMultichainAccountGroup', - (...args) => this.createMultichainAccountGroup(...args), - ); - this.#messenger.registerActionHandler( - 'MultichainAccountService:setBasicFunctionality', - (...args) => this.setBasicFunctionality(...args), - ); - this.#messenger.registerActionHandler( - 'MultichainAccountService:alignWallets', - (...args) => this.alignWallets(...args), - ); - this.#messenger.registerActionHandler( - 'MultichainAccountService:alignWallet', - (...args) => this.alignWallet(...args), - ); - this.#messenger.registerActionHandler( - 'MultichainAccountService:createMultichainAccountWallet', - (...args) => this.createMultichainAccountWallet(...args), - ); - this.#messenger.registerActionHandler( - 'MultichainAccountService:resyncAccounts', - (...args) => this.resyncAccounts(...args), - ); - this.#messenger.registerActionHandler( - 'MultichainAccountService:removeMultichainAccountWallet', - (...args) => this.removeMultichainAccountWallet(...args), - ); - this.#messenger.registerActionHandler( - 'MultichainAccountService:ensureCanUseSnapPlatform', - (...args) => this.ensureCanUseSnapPlatform(...args), + this.#messenger.registerMethodActionHandlers( + this, + MESSENGER_EXPOSED_METHODS, ); } diff --git a/packages/multichain-account-service/src/index.ts b/packages/multichain-account-service/src/index.ts index 8a322a0894f..59f24f42b0f 100644 --- a/packages/multichain-account-service/src/index.ts +++ b/packages/multichain-account-service/src/index.ts @@ -2,17 +2,25 @@ export type { MultichainAccountServiceActions, MultichainAccountServiceEvents, MultichainAccountServiceMessenger, - MultichainAccountServiceGetMultichainAccountGroupAction, + MultichainAccountServiceMultichainAccountGroupCreatedEvent, + MultichainAccountServiceMultichainAccountGroupUpdatedEvent, + MultichainAccountServiceWalletStatusChangeEvent, +} from './types'; +export type { + MultichainAccountServiceResyncAccountsAction, + MultichainAccountServiceEnsureCanUseSnapPlatformAction, MultichainAccountServiceGetMultichainAccountWalletAction, MultichainAccountServiceGetMultichainAccountWalletsAction, + MultichainAccountServiceCreateMultichainAccountWalletAction, + MultichainAccountServiceRemoveMultichainAccountWalletAction, + MultichainAccountServiceGetMultichainAccountGroupAction, MultichainAccountServiceGetMultichainAccountGroupsAction, - MultichainAccountServiceCreateMultichainAccountGroupAction, MultichainAccountServiceCreateNextMultichainAccountGroupAction, + MultichainAccountServiceCreateMultichainAccountGroupAction, MultichainAccountServiceSetBasicFunctionalityAction, - MultichainAccountServiceMultichainAccountGroupCreatedEvent, - MultichainAccountServiceMultichainAccountGroupUpdatedEvent, - MultichainAccountServiceWalletStatusChangeEvent, -} from './types'; + MultichainAccountServiceAlignWalletsAction, + MultichainAccountServiceAlignWalletAction, +} from './MultichainAccountService-method-action-types'; export { AccountProviderWrapper, BaseBip44AccountProvider, diff --git a/packages/multichain-account-service/src/types.ts b/packages/multichain-account-service/src/types.ts index 74943136558..074ccb328b2 100644 --- a/packages/multichain-account-service/src/types.ts +++ b/packages/multichain-account-service/src/types.ts @@ -35,94 +35,14 @@ import type { SnapStateChange as SnapControllerStateChangeEvent, } from '@metamask/snaps-controllers'; -import type { - MultichainAccountService, - serviceName, -} from './MultichainAccountService'; - -export type MultichainAccountServiceGetMultichainAccountGroupAction = { - type: `${typeof serviceName}:getMultichainAccountGroup`; - handler: MultichainAccountService['getMultichainAccountGroup']; -}; - -export type MultichainAccountServiceGetMultichainAccountGroupsAction = { - type: `${typeof serviceName}:getMultichainAccountGroups`; - handler: MultichainAccountService['getMultichainAccountGroups']; -}; - -export type MultichainAccountServiceGetMultichainAccountWalletAction = { - type: `${typeof serviceName}:getMultichainAccountWallet`; - handler: MultichainAccountService['getMultichainAccountWallet']; -}; - -export type MultichainAccountServiceGetMultichainAccountWalletsAction = { - type: `${typeof serviceName}:getMultichainAccountWallets`; - handler: MultichainAccountService['getMultichainAccountWallets']; -}; - -export type MultichainAccountServiceCreateNextMultichainAccountGroupAction = { - type: `${typeof serviceName}:createNextMultichainAccountGroup`; - handler: MultichainAccountService['createNextMultichainAccountGroup']; -}; - -export type MultichainAccountServiceCreateMultichainAccountGroupAction = { - type: `${typeof serviceName}:createMultichainAccountGroup`; - handler: MultichainAccountService['createMultichainAccountGroup']; -}; - -export type MultichainAccountServiceSetBasicFunctionalityAction = { - type: `${typeof serviceName}:setBasicFunctionality`; - handler: MultichainAccountService['setBasicFunctionality']; -}; - -export type MultichainAccountServiceAlignWalletAction = { - type: `${typeof serviceName}:alignWallet`; - handler: MultichainAccountService['alignWallet']; -}; - -export type MultichainAccountServiceAlignWalletsAction = { - type: `${typeof serviceName}:alignWallets`; - handler: MultichainAccountService['alignWallets']; -}; - -export type MultichainAccountServiceCreateMultichainAccountWalletAction = { - type: `${typeof serviceName}:createMultichainAccountWallet`; - handler: MultichainAccountService['createMultichainAccountWallet']; -}; - -export type MultichainAccountServiceResyncAccountsAction = { - type: `${typeof serviceName}:resyncAccounts`; - handler: MultichainAccountService['resyncAccounts']; -}; - -export type MultichainAccountServiceRemoveMultichainAccountWalletAction = { - type: `${typeof serviceName}:removeMultichainAccountWallet`; - handler: MultichainAccountService['removeMultichainAccountWallet']; -}; - -export type MultichainAccountServiceEnsureCanUseSnapPlatformAction = { - type: `${typeof serviceName}:ensureCanUseSnapPlatform`; - handler: MultichainAccountService['ensureCanUseSnapPlatform']; -}; - +import type { serviceName } from './MultichainAccountService'; +import type { MultichainAccountServiceMethodActions } from './MultichainAccountService-method-action-types'; /** * All actions that {@link MultichainAccountService} registers so that other * modules can call them. */ export type MultichainAccountServiceActions = - | MultichainAccountServiceGetMultichainAccountGroupAction - | MultichainAccountServiceGetMultichainAccountGroupsAction - | MultichainAccountServiceGetMultichainAccountWalletAction - | MultichainAccountServiceGetMultichainAccountWalletsAction - | MultichainAccountServiceCreateNextMultichainAccountGroupAction - | MultichainAccountServiceCreateMultichainAccountGroupAction - | MultichainAccountServiceSetBasicFunctionalityAction - | MultichainAccountServiceAlignWalletAction - | MultichainAccountServiceAlignWalletsAction - | MultichainAccountServiceCreateMultichainAccountWalletAction - | MultichainAccountServiceResyncAccountsAction - | MultichainAccountServiceRemoveMultichainAccountWalletAction - | MultichainAccountServiceEnsureCanUseSnapPlatformAction; + MultichainAccountServiceMethodActions; export type MultichainAccountServiceMultichainAccountGroupCreatedEvent = { type: `${typeof serviceName}:multichainAccountGroupCreated`; diff --git a/packages/notification-services-controller/src/NotificationServicesController/NotificationServicesController.test.ts b/packages/notification-services-controller/src/NotificationServicesController/NotificationServicesController.test.ts index e8a9252ee87..c09a7f953af 100644 --- a/packages/notification-services-controller/src/NotificationServicesController/NotificationServicesController.test.ts +++ b/packages/notification-services-controller/src/NotificationServicesController/NotificationServicesController.test.ts @@ -1591,7 +1591,7 @@ function mockNotificationMessenger(): { }); const mockGetBearerToken = - typedMockAction().mockResolvedValue( + typedMockAction().mockResolvedValue( AuthenticationController.Mocks.MOCK_OATH_TOKEN_RESPONSE.access_token, ); diff --git a/packages/notification-services-controller/src/NotificationServicesController/NotificationServicesController.ts b/packages/notification-services-controller/src/NotificationServicesController/NotificationServicesController.ts index 1d75602191d..957ff38a450 100644 --- a/packages/notification-services-controller/src/NotificationServicesController/NotificationServicesController.ts +++ b/packages/notification-services-controller/src/NotificationServicesController/NotificationServicesController.ts @@ -228,9 +228,9 @@ type AllowedActions = // Keyring Controller Requests | KeyringControllerGetStateAction // Auth Controller Requests - | AuthenticationController.AuthenticationControllerGetBearerToken - | AuthenticationController.AuthenticationControllerIsSignedIn - | AuthenticationController.AuthenticationControllerPerformSignIn + | AuthenticationController.AuthenticationControllerGetBearerTokenAction + | AuthenticationController.AuthenticationControllerIsSignedInAction + | AuthenticationController.AuthenticationControllerPerformSignInAction // Push Notifications Controller Requests | NotificationServicesPushControllerEnablePushNotificationsAction | NotificationServicesPushControllerDisablePushNotificationsAction diff --git a/packages/notification-services-controller/src/NotificationServicesPushController/NotificationServicesPushController.test.ts b/packages/notification-services-controller/src/NotificationServicesPushController/NotificationServicesPushController.test.ts index 394aec2973f..f0d726f2630 100644 --- a/packages/notification-services-controller/src/NotificationServicesPushController/NotificationServicesPushController.test.ts +++ b/packages/notification-services-controller/src/NotificationServicesPushController/NotificationServicesPushController.test.ts @@ -414,14 +414,14 @@ function mockAuthBearerTokenCall( messenger: NotificationServicesPushControllerMessenger, ): jest.Mock< ReturnType< - AuthenticationController.AuthenticationControllerGetBearerToken['handler'] + AuthenticationController.AuthenticationControllerGetBearerTokenAction['handler'] >, Parameters< - AuthenticationController.AuthenticationControllerGetBearerToken['handler'] + AuthenticationController.AuthenticationControllerGetBearerTokenAction['handler'] > > { type Fn = - AuthenticationController.AuthenticationControllerGetBearerToken['handler']; + AuthenticationController.AuthenticationControllerGetBearerTokenAction['handler']; const mockAuthGetBearerToken = jest .fn, Parameters>() .mockResolvedValue(MOCK_JWT); diff --git a/packages/notification-services-controller/src/NotificationServicesPushController/NotificationServicesPushController.ts b/packages/notification-services-controller/src/NotificationServicesPushController/NotificationServicesPushController.ts index 285f3800058..164a46283ac 100644 --- a/packages/notification-services-controller/src/NotificationServicesPushController/NotificationServicesPushController.ts +++ b/packages/notification-services-controller/src/NotificationServicesPushController/NotificationServicesPushController.ts @@ -57,7 +57,7 @@ export type Actions = | NotificationServicesPushControllerSubscribeToNotificationsAction; type AllowedActions = - AuthenticationController.AuthenticationControllerGetBearerToken; + AuthenticationController.AuthenticationControllerGetBearerTokenAction; export type NotificationServicesPushControllerStateChangeEvent = ControllerStateChangeEvent< diff --git a/packages/profile-metrics-controller/src/ProfileMetricsService.ts b/packages/profile-metrics-controller/src/ProfileMetricsService.ts index de680f73982..af72ee6b341 100644 --- a/packages/profile-metrics-controller/src/ProfileMetricsService.ts +++ b/packages/profile-metrics-controller/src/ProfileMetricsService.ts @@ -48,7 +48,7 @@ export type ProfileMetricsServiceActions = ProfileMetricsServiceMethodActions; * Actions from other messengers that {@link ProfileMetricsService} calls. */ type AllowedActions = - AuthenticationController.AuthenticationControllerGetBearerToken; + AuthenticationController.AuthenticationControllerGetBearerTokenAction; /** * Events that {@link ProfileMetricsService} exposes to other consumers. diff --git a/packages/profile-sync-controller/CHANGELOG.md b/packages/profile-sync-controller/CHANGELOG.md index 113b8877da1..f7d0979a7a0 100644 --- a/packages/profile-sync-controller/CHANGELOG.md +++ b/packages/profile-sync-controller/CHANGELOG.md @@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Expose missing public `UserStorageController` methods through its messenger ([#7976](https://github.com/MetaMask/core/pull/7976/)) + - The following actions are now available: + - `UserStorageController:performDeleteStorageAllFeatureEntries` + - `UserStorageController:listEntropySources` + - `UserStorageController:setIsBackupAndSyncFeatureEnabled` + - `UserStorageController:setIsContactSyncingInProgress` + - `UserStorageController:syncContactsWithUserStorage` + - Corresponding action types (e.g. `UserStorageControllerPerformDeleteStorageAllFeatureEntriesAction`) are available as well. + +### Changed + +- **BREAKING:** Standardize names of `AuthenticationController` and `UserStorageController` messenger action types ([#7976](https://github.com/MetaMask/core/pull/7976/)) + - All existing types for messenger actions have been renamed so they end in `Action` (e.g. `AuthenticationControllerPerformSignIn` -> `AuthenticationControllerPerformSignInAction`). You will need to update imports appropriately. + - This change only affects the types. The action type strings themselves have not changed, so you do not need to update the list of actions you pass when initializing `AuthenticationController` and `UserStorageController` messengers. + ## [27.1.0] ### Changed diff --git a/packages/profile-sync-controller/package.json b/packages/profile-sync-controller/package.json index c77b258cfcb..9ad39132365 100644 --- a/packages/profile-sync-controller/package.json +++ b/packages/profile-sync-controller/package.json @@ -93,6 +93,9 @@ "build:docs": "typedoc", "changelog:update": "../../scripts/update-changelog.sh @metamask/profile-sync-controller", "changelog:validate": "../../scripts/validate-changelog.sh @metamask/profile-sync-controller", + "generate-method-action-types": "yarn generate-method-action-types:authentication \"$@\" && yarn generate-method-action-types:user-storage \"$@\"", + "generate-method-action-types:authentication": "tsx ../../scripts/generate-method-action-types.ts src/controllers/authentication", + "generate-method-action-types:user-storage": "tsx ../../scripts/generate-method-action-types.ts src/controllers/user-storage", "publish:preview": "yarn npm publish --tag preview", "since-latest-release": "../../scripts/since-latest-release.sh", "test": "NODE_OPTIONS=--experimental-vm-modules jest --reporters=jest-silent-reporter", @@ -130,6 +133,7 @@ "jest-environment-jsdom": "^29.7.0", "nock": "^13.3.1", "ts-jest": "^29.2.5", + "tsx": "^4.20.5", "typedoc": "^0.25.13", "typedoc-plugin-missing-exports": "^2.0.0", "typescript": "~5.3.3", diff --git a/packages/profile-sync-controller/src/controllers/authentication/AuthenticationController-method-action-types.ts b/packages/profile-sync-controller/src/controllers/authentication/AuthenticationController-method-action-types.ts new file mode 100644 index 00000000000..9b3db73e8c5 --- /dev/null +++ b/packages/profile-sync-controller/src/controllers/authentication/AuthenticationController-method-action-types.ts @@ -0,0 +1,61 @@ +/** + * This file is auto generated by `scripts/generate-method-action-types.ts`. + * Do not edit manually. + */ + +import type { AuthenticationController } from './AuthenticationController'; + +export type AuthenticationControllerPerformSignInAction = { + type: `AuthenticationController:performSignIn`; + handler: AuthenticationController['performSignIn']; +}; + +export type AuthenticationControllerPerformSignOutAction = { + type: `AuthenticationController:performSignOut`; + handler: AuthenticationController['performSignOut']; +}; + +/** + * Will return a bearer token. + * Logs a user in if a user is not logged in. + * + * @returns profile for the session. + */ +export type AuthenticationControllerGetBearerTokenAction = { + type: `AuthenticationController:getBearerToken`; + handler: AuthenticationController['getBearerToken']; +}; + +/** + * Will return a session profile. + * Logs a user in if a user is not logged in. + * + * @param entropySourceId - The entropy source ID used to derive the key, + * when multiple sources are available (Multi-SRP). + * @returns profile for the session. + */ +export type AuthenticationControllerGetSessionProfileAction = { + type: `AuthenticationController:getSessionProfile`; + handler: AuthenticationController['getSessionProfile']; +}; + +export type AuthenticationControllerGetUserProfileLineageAction = { + type: `AuthenticationController:getUserProfileLineage`; + handler: AuthenticationController['getUserProfileLineage']; +}; + +export type AuthenticationControllerIsSignedInAction = { + type: `AuthenticationController:isSignedIn`; + handler: AuthenticationController['isSignedIn']; +}; + +/** + * Union of all AuthenticationController action types. + */ +export type AuthenticationControllerMethodActions = + | AuthenticationControllerPerformSignInAction + | AuthenticationControllerPerformSignOutAction + | AuthenticationControllerGetBearerTokenAction + | AuthenticationControllerGetSessionProfileAction + | AuthenticationControllerGetUserProfileLineageAction + | AuthenticationControllerIsSignedInAction; diff --git a/packages/profile-sync-controller/src/controllers/authentication/AuthenticationController.test.ts b/packages/profile-sync-controller/src/controllers/authentication/AuthenticationController.test.ts index 09fed6861d3..224bbb9c273 100644 --- a/packages/profile-sync-controller/src/controllers/authentication/AuthenticationController.test.ts +++ b/packages/profile-sync-controller/src/controllers/authentication/AuthenticationController.test.ts @@ -6,7 +6,7 @@ import type { MockAnyNamespace, } from '@metamask/messenger'; -import AuthenticationController from './AuthenticationController'; +import { AuthenticationController } from './AuthenticationController'; import type { AuthenticationControllerMessenger, AuthenticationControllerState, diff --git a/packages/profile-sync-controller/src/controllers/authentication/AuthenticationController.ts b/packages/profile-sync-controller/src/controllers/authentication/AuthenticationController.ts index 37aefcc5d47..238b4a84bd2 100644 --- a/packages/profile-sync-controller/src/controllers/authentication/AuthenticationController.ts +++ b/packages/profile-sync-controller/src/controllers/authentication/AuthenticationController.ts @@ -18,6 +18,7 @@ import { createSnapAllPublicKeysRequest, createSnapSignMessageRequest, } from './auth-snap-requests'; +import { AuthenticationControllerMethodActions } from './AuthenticationController-method-action-types'; import type { LoginResponse, SRPInterface, @@ -83,38 +84,23 @@ type ControllerConfig = { env: Env; }; -// Messenger Actions -type CreateActionsObj = { - [K in Controller]: { - type: `${typeof controllerName}:${K}`; - handler: AuthenticationController[K]; - }; -}; -type ActionsObj = CreateActionsObj< - | 'performSignIn' - | 'performSignOut' - | 'getBearerToken' - | 'getSessionProfile' - | 'getUserProfileLineage' - | 'isSignedIn' ->; +const MESSENGER_EXPOSED_METHODS = [ + 'performSignIn', + 'performSignOut', + 'getBearerToken', + 'getSessionProfile', + 'getUserProfileLineage', + 'isSignedIn', +] as const; + export type Actions = - | ActionsObj[keyof ActionsObj] - | AuthenticationControllerGetStateAction; + | AuthenticationControllerGetStateAction + | AuthenticationControllerMethodActions; + export type AuthenticationControllerGetStateAction = ControllerGetStateAction< typeof controllerName, AuthenticationControllerState >; -export type AuthenticationControllerPerformSignIn = ActionsObj['performSignIn']; -export type AuthenticationControllerPerformSignOut = - ActionsObj['performSignOut']; -export type AuthenticationControllerGetBearerToken = - ActionsObj['getBearerToken']; -export type AuthenticationControllerGetSessionProfile = - ActionsObj['getSessionProfile']; -export type AuthenticationControllerGetUserProfileLineage = - ActionsObj['getUserProfileLineage']; -export type AuthenticationControllerIsSignedIn = ActionsObj['isSignedIn']; export type AuthenticationControllerStateChangeEvent = ControllerStateChangeEvent< @@ -140,7 +126,7 @@ export type AuthenticationControllerMessenger = Messenger< * Controller that enables authentication for restricted endpoints. * Used for Backup & Sync, Notifications, and other services. */ -export default class AuthenticationController extends BaseController< +export class AuthenticationController extends BaseController< typeof controllerName, AuthenticationControllerState, AuthenticationControllerMessenger @@ -223,42 +209,10 @@ export default class AuthenticationController extends BaseController< ); this.#keyringController.setupLockedStateSubscriptions(); - this.#registerMessageHandlers(); - } - - /** - * Constructor helper for registering this controller's messaging system - * actions. - */ - #registerMessageHandlers(): void { - this.messenger.registerActionHandler( - 'AuthenticationController:getBearerToken', - this.getBearerToken.bind(this), - ); - - this.messenger.registerActionHandler( - 'AuthenticationController:getSessionProfile', - this.getSessionProfile.bind(this), - ); - - this.messenger.registerActionHandler( - 'AuthenticationController:isSignedIn', - this.isSignedIn.bind(this), - ); - - this.messenger.registerActionHandler( - 'AuthenticationController:performSignIn', - this.performSignIn.bind(this), - ); - - this.messenger.registerActionHandler( - 'AuthenticationController:performSignOut', - this.performSignOut.bind(this), - ); - this.messenger.registerActionHandler( - 'AuthenticationController:getUserProfileLineage', - this.getUserProfileLineage.bind(this), + this.messenger.registerMethodActionHandlers( + this, + MESSENGER_EXPOSED_METHODS, ); } diff --git a/packages/profile-sync-controller/src/controllers/authentication/index.ts b/packages/profile-sync-controller/src/controllers/authentication/index.ts index c3d62950a41..3d9926709cc 100644 --- a/packages/profile-sync-controller/src/controllers/authentication/index.ts +++ b/packages/profile-sync-controller/src/controllers/authentication/index.ts @@ -1,7 +1,15 @@ -import Controller from './AuthenticationController'; +import { AuthenticationController } from './AuthenticationController'; -const AuthenticationController = Controller; -export { Controller }; +export { AuthenticationController as Controller }; export default AuthenticationController; export * from './AuthenticationController'; export * as Mocks from './mocks'; + +export type { + AuthenticationControllerPerformSignInAction, + AuthenticationControllerPerformSignOutAction, + AuthenticationControllerGetBearerTokenAction, + AuthenticationControllerGetSessionProfileAction, + AuthenticationControllerGetUserProfileLineageAction, + AuthenticationControllerIsSignedInAction, +} from './AuthenticationController-method-action-types'; diff --git a/packages/profile-sync-controller/src/controllers/user-storage/UserStorageController-method-action-types.ts b/packages/profile-sync-controller/src/controllers/user-storage/UserStorageController-method-action-types.ts new file mode 100644 index 00000000000..46977986548 --- /dev/null +++ b/packages/profile-sync-controller/src/controllers/user-storage/UserStorageController-method-action-types.ts @@ -0,0 +1,162 @@ +/** + * This file is auto generated by `scripts/generate-method-action-types.ts`. + * Do not edit manually. + */ + +import type { UserStorageController } from './UserStorageController'; + +/** + * Allows retrieval of stored data. Data stored is string formatted. + * Developers can extend the entry path and entry name through the `schema.ts` file. + * + * @param path - string in the form of `${feature}.${key}` that matches schema + * @param entropySourceId - The entropy source ID used to generate the encryption key. + * @returns the decrypted string contents found from user storage (or null if not found) + */ +export type UserStorageControllerPerformGetStorageAction = { + type: `UserStorageController:performGetStorage`; + handler: UserStorageController['performGetStorage']; +}; + +/** + * Allows retrieval of all stored data for a specific feature. Data stored is formatted as an array of strings. + * Developers can extend the entry path through the `schema.ts` file. + * + * @param path - string in the form of `${feature}` that matches schema + * @param entropySourceId - The entropy source ID used to generate the encryption key. + * @returns the array of decrypted string contents found from user storage (or null if not found) + */ +export type UserStorageControllerPerformGetStorageAllFeatureEntriesAction = { + type: `UserStorageController:performGetStorageAllFeatureEntries`; + handler: UserStorageController['performGetStorageAllFeatureEntries']; +}; + +/** + * Allows storage of user data. Data stored must be string formatted. + * Developers can extend the entry path and entry name through the `schema.ts` file. + * + * @param path - string in the form of `${feature}.${key}` that matches schema + * @param value - The string data you want to store. + * @param entropySourceId - The entropy source ID used to generate the encryption key. + * @returns nothing. NOTE that an error is thrown if fails to store data. + */ +export type UserStorageControllerPerformSetStorageAction = { + type: `UserStorageController:performSetStorage`; + handler: UserStorageController['performSetStorage']; +}; + +/** + * Allows storage of multiple user data entries for one specific feature. Data stored must be string formatted. + * Developers can extend the entry path through the `schema.ts` file. + * + * @param path - string in the form of `${feature}` that matches schema + * @param values - data to store, in the form of an array of `[entryKey, entryValue]` pairs + * @param entropySourceId - The entropy source ID used to generate the encryption key. + * @returns nothing. NOTE that an error is thrown if fails to store data. + */ +export type UserStorageControllerPerformBatchSetStorageAction = { + type: `UserStorageController:performBatchSetStorage`; + handler: UserStorageController['performBatchSetStorage']; +}; + +/** + * Allows deletion of user data. Developers can extend the entry path and entry name through the `schema.ts` file. + * + * @param path - string in the form of `${feature}.${key}` that matches schema + * @param entropySourceId - The entropy source ID used to generate the encryption key. + * @returns nothing. NOTE that an error is thrown if fails to delete data. + */ +export type UserStorageControllerPerformDeleteStorageAction = { + type: `UserStorageController:performDeleteStorage`; + handler: UserStorageController['performDeleteStorage']; +}; + +/** + * Allows deletion of all user data entries for a specific feature. + * Developers can extend the entry path through the `schema.ts` file. + * + * @param path - string in the form of `${feature}` that matches schema + * @param entropySourceId - The entropy source ID used to generate the encryption key. + * @returns nothing. NOTE that an error is thrown if fails to delete data. + */ +export type UserStorageControllerPerformDeleteStorageAllFeatureEntriesAction = { + type: `UserStorageController:performDeleteStorageAllFeatureEntries`; + handler: UserStorageController['performDeleteStorageAllFeatureEntries']; +}; + +/** + * Allows delete of multiple user data entries for one specific feature. Data deleted must be string formatted. + * Developers can extend the entry path through the `schema.ts` file. + * + * @param path - string in the form of `${feature}` that matches schema + * @param values - data to store, in the form of an array of entryKey[] + * @param entropySourceId - The entropy source ID used to generate the encryption key. + * @returns nothing. NOTE that an error is thrown if fails to store data. + */ +export type UserStorageControllerPerformBatchDeleteStorageAction = { + type: `UserStorageController:performBatchDeleteStorage`; + handler: UserStorageController['performBatchDeleteStorage']; +}; + +/** + * Retrieves the storage key, for internal use only! + * + * @returns the storage key + */ +export type UserStorageControllerGetStorageKeyAction = { + type: `UserStorageController:getStorageKey`; + handler: UserStorageController['getStorageKey']; +}; + +/** + * Lists all the available HD keyring metadata IDs. + * These IDs can be used in a multi-SRP context to segregate data specific to different SRPs. + * + * @returns A promise that resolves to an array of HD keyring metadata IDs. + */ +export type UserStorageControllerListEntropySourcesAction = { + type: `UserStorageController:listEntropySources`; + handler: UserStorageController['listEntropySources']; +}; + +export type UserStorageControllerSetIsBackupAndSyncFeatureEnabledAction = { + type: `UserStorageController:setIsBackupAndSyncFeatureEnabled`; + handler: UserStorageController['setIsBackupAndSyncFeatureEnabled']; +}; + +/** + * Sets the isContactSyncingInProgress flag to prevent infinite loops during contact synchronization + * + * @param isContactSyncingInProgress - Whether contact syncing is in progress + */ +export type UserStorageControllerSetIsContactSyncingInProgressAction = { + type: `UserStorageController:setIsContactSyncingInProgress`; + handler: UserStorageController['setIsContactSyncingInProgress']; +}; + +/** + * Syncs the address book list with the user storage address book list. + * This method is used to make sure that the address book list is up-to-date with the user storage address book list and vice-versa. + * It will add new contacts to the address book list, update/merge conflicting contacts and re-upload the results in some cases to the user storage. + */ +export type UserStorageControllerSyncContactsWithUserStorageAction = { + type: `UserStorageController:syncContactsWithUserStorage`; + handler: UserStorageController['syncContactsWithUserStorage']; +}; + +/** + * Union of all UserStorageController action types. + */ +export type UserStorageControllerMethodActions = + | UserStorageControllerPerformGetStorageAction + | UserStorageControllerPerformGetStorageAllFeatureEntriesAction + | UserStorageControllerPerformSetStorageAction + | UserStorageControllerPerformBatchSetStorageAction + | UserStorageControllerPerformDeleteStorageAction + | UserStorageControllerPerformDeleteStorageAllFeatureEntriesAction + | UserStorageControllerPerformBatchDeleteStorageAction + | UserStorageControllerGetStorageKeyAction + | UserStorageControllerListEntropySourcesAction + | UserStorageControllerSetIsBackupAndSyncFeatureEnabledAction + | UserStorageControllerSetIsContactSyncingInProgressAction + | UserStorageControllerSyncContactsWithUserStorageAction; diff --git a/packages/profile-sync-controller/src/controllers/user-storage/UserStorageController.test.ts b/packages/profile-sync-controller/src/controllers/user-storage/UserStorageController.test.ts index 01ad34ea1af..8451be8ed21 100644 --- a/packages/profile-sync-controller/src/controllers/user-storage/UserStorageController.test.ts +++ b/packages/profile-sync-controller/src/controllers/user-storage/UserStorageController.test.ts @@ -13,7 +13,7 @@ import { } from './__fixtures__/mockServices'; import { BACKUPANDSYNC_FEATURES } from './constants'; import { MOCK_STORAGE_DATA, MOCK_STORAGE_KEY } from './mocks/mockStorage'; -import UserStorageController, { defaultState } from './UserStorageController'; +import { UserStorageController, defaultState } from './UserStorageController'; import { USER_STORAGE_FEATURE_NAMES } from '../../shared/storage-schema'; describe('UserStorageController', () => { diff --git a/packages/profile-sync-controller/src/controllers/user-storage/UserStorageController.ts b/packages/profile-sync-controller/src/controllers/user-storage/UserStorageController.ts index 563101108e0..7911f8312ab 100644 --- a/packages/profile-sync-controller/src/controllers/user-storage/UserStorageController.ts +++ b/packages/profile-sync-controller/src/controllers/user-storage/UserStorageController.ts @@ -29,6 +29,7 @@ import type { HandleSnapRequest } from '@metamask/snaps-controllers'; import { BACKUPANDSYNC_FEATURES } from './constants'; import { syncContactsWithUserStorage } from './contact-syncing/controller-integration'; import { setupContactSyncingSubscriptions } from './contact-syncing/setup-subscriptions'; +import type { UserStorageControllerMethodActions } from './UserStorageController-method-action-types'; import type { UserStorageGenericFeatureKey, UserStorageGenericPathWithFeatureAndKey, @@ -39,11 +40,11 @@ import type { NativeScrypt } from '../../shared/types/encryption'; import { EventQueue } from '../../shared/utils/event-queue'; import { createSnapSignMessageRequest } from '../authentication/auth-snap-requests'; import type { - AuthenticationControllerGetBearerToken, - AuthenticationControllerGetSessionProfile, - AuthenticationControllerIsSignedIn, - AuthenticationControllerPerformSignIn, -} from '../authentication/AuthenticationController'; + AuthenticationControllerGetBearerTokenAction, + AuthenticationControllerGetSessionProfileAction, + AuthenticationControllerIsSignedInAction, + AuthenticationControllerPerformSignInAction, +} from '../authentication/AuthenticationController-method-action-types'; const controllerName = 'UserStorageController'; @@ -139,42 +140,28 @@ type ControllerConfig = { }; }; -// Messenger Actions -type CreateActionsObj = { - [K in Controller]: { - type: `${typeof controllerName}:${K}`; - handler: UserStorageController[K]; - }; -}; -type ActionsObj = CreateActionsObj< - | 'performGetStorage' - | 'performGetStorageAllFeatureEntries' - | 'performSetStorage' - | 'performBatchSetStorage' - | 'performDeleteStorage' - | 'performBatchDeleteStorage' - | 'getStorageKey' ->; +const MESSENGER_EXPOSED_METHODS = [ + 'performGetStorage', + 'performGetStorageAllFeatureEntries', + 'performSetStorage', + 'performBatchSetStorage', + 'performDeleteStorage', + 'performBatchDeleteStorage', + 'getStorageKey', + 'performDeleteStorageAllFeatureEntries', + 'listEntropySources', + 'setIsBackupAndSyncFeatureEnabled', + 'setIsContactSyncingInProgress', + 'syncContactsWithUserStorage', +] as const; + export type UserStorageControllerGetStateAction = ControllerGetStateAction< typeof controllerName, UserStorageControllerState >; export type Actions = - | ActionsObj[keyof ActionsObj] - | UserStorageControllerGetStateAction; -export type UserStorageControllerPerformGetStorage = - ActionsObj['performGetStorage']; -export type UserStorageControllerPerformGetStorageAllFeatureEntries = - ActionsObj['performGetStorageAllFeatureEntries']; -export type UserStorageControllerPerformSetStorage = - ActionsObj['performSetStorage']; -export type UserStorageControllerPerformBatchSetStorage = - ActionsObj['performBatchSetStorage']; -export type UserStorageControllerPerformDeleteStorage = - ActionsObj['performDeleteStorage']; -export type UserStorageControllerPerformBatchDeleteStorage = - ActionsObj['performBatchDeleteStorage']; -export type UserStorageControllerGetStorageKey = ActionsObj['getStorageKey']; + | UserStorageControllerGetStateAction + | UserStorageControllerMethodActions; export type AllowedActions = // Keyring Requests @@ -182,10 +169,10 @@ export type AllowedActions = // Snap Requests | HandleSnapRequest // Auth Requests - | AuthenticationControllerGetBearerToken - | AuthenticationControllerGetSessionProfile - | AuthenticationControllerPerformSignIn - | AuthenticationControllerIsSignedIn + | AuthenticationControllerGetBearerTokenAction + | AuthenticationControllerGetSessionProfileAction + | AuthenticationControllerPerformSignInAction + | AuthenticationControllerIsSignedInAction // Contact Syncing | AddressBookControllerListAction | AddressBookControllerSetAction @@ -201,7 +188,6 @@ export type UserStorageControllerStateChangeEvent = ControllerStateChangeEvent< export type Events = UserStorageControllerStateChangeEvent; export type AllowedEvents = - | UserStorageControllerStateChangeEvent | KeyringControllerLockEvent | KeyringControllerUnlockEvent // Address Book Events @@ -223,7 +209,7 @@ export type UserStorageControllerMessenger = Messenger< * - data stored on UserStorage is FULLY encrypted, with the only keys stored/managed on the client. * - No one can access this data unless they are have the SRP and are able to run the signing snap. */ -export default class UserStorageController extends BaseController< +export class UserStorageController extends BaseController< typeof controllerName, UserStorageControllerState, UserStorageControllerMessenger @@ -346,8 +332,12 @@ export default class UserStorageController extends BaseController< }, ); + this.messenger.registerMethodActionHandlers( + this, + MESSENGER_EXPOSED_METHODS, + ); + this.#keyringController.setupLockedStateSubscriptions(); - this.#registerMessageHandlers(); this.#nativeScryptCrypto = nativeScryptCrypto; // Contact Syncing @@ -358,47 +348,6 @@ export default class UserStorageController extends BaseController< }); } - /** - * Constructor helper for registering this controller's messaging system - * actions. - */ - #registerMessageHandlers(): void { - this.messenger.registerActionHandler( - 'UserStorageController:performGetStorage', - this.performGetStorage.bind(this), - ); - - this.messenger.registerActionHandler( - 'UserStorageController:performGetStorageAllFeatureEntries', - this.performGetStorageAllFeatureEntries.bind(this), - ); - - this.messenger.registerActionHandler( - 'UserStorageController:performSetStorage', - this.performSetStorage.bind(this), - ); - - this.messenger.registerActionHandler( - 'UserStorageController:performBatchSetStorage', - this.performBatchSetStorage.bind(this), - ); - - this.messenger.registerActionHandler( - 'UserStorageController:performDeleteStorage', - this.performDeleteStorage.bind(this), - ); - - this.messenger.registerActionHandler( - 'UserStorageController:performBatchDeleteStorage', - this.performBatchDeleteStorage.bind(this), - ); - - this.messenger.registerActionHandler( - 'UserStorageController:getStorageKey', - this.getStorageKey.bind(this), - ); - } - /** * Allows retrieval of stored data. Data stored is string formatted. * Developers can extend the entry path and entry name through the `schema.ts` file. @@ -554,7 +503,7 @@ export default class UserStorageController extends BaseController< * * @returns A promise that resolves to an array of HD keyring metadata IDs. */ - async listEntropySources() { + async listEntropySources(): Promise { if (!this.#isUnlocked) { throw new Error( 'listEntropySources - unable to list entropy sources, wallet is locked', diff --git a/packages/profile-sync-controller/src/controllers/user-storage/contact-syncing/types.ts b/packages/profile-sync-controller/src/controllers/user-storage/contact-syncing/types.ts index 4e60940d0f0..496db060f2a 100644 --- a/packages/profile-sync-controller/src/controllers/user-storage/contact-syncing/types.ts +++ b/packages/profile-sync-controller/src/controllers/user-storage/contact-syncing/types.ts @@ -6,7 +6,7 @@ import type { USER_STORAGE_VERSION, } from './constants'; import type { UserStorageControllerMessenger } from '../UserStorageController'; -import type UserStorageController from '../UserStorageController'; +import type { UserStorageController } from '../UserStorageController'; export type UserStorageContactEntry = { /** diff --git a/packages/profile-sync-controller/src/controllers/user-storage/index.ts b/packages/profile-sync-controller/src/controllers/user-storage/index.ts index 732a6aad660..c6d746f502b 100644 --- a/packages/profile-sync-controller/src/controllers/user-storage/index.ts +++ b/packages/profile-sync-controller/src/controllers/user-storage/index.ts @@ -1,10 +1,23 @@ -import Controller from './UserStorageController'; +import { UserStorageController } from './UserStorageController'; -const UserStorageController = Controller; -export { Controller }; +export { UserStorageController as Controller }; export default UserStorageController; export * from './UserStorageController'; export * as Mocks from './mocks'; export * from './constants'; export * from '../../shared/encryption'; export * from '../../shared/storage-schema'; +export type { + UserStorageControllerPerformGetStorageAction, + UserStorageControllerPerformGetStorageAllFeatureEntriesAction, + UserStorageControllerPerformSetStorageAction, + UserStorageControllerPerformBatchSetStorageAction, + UserStorageControllerPerformDeleteStorageAction, + UserStorageControllerPerformDeleteStorageAllFeatureEntriesAction, + UserStorageControllerPerformBatchDeleteStorageAction, + UserStorageControllerGetStorageKeyAction, + UserStorageControllerListEntropySourcesAction, + UserStorageControllerSetIsBackupAndSyncFeatureEnabledAction, + UserStorageControllerSetIsContactSyncingInProgressAction, + UserStorageControllerSyncContactsWithUserStorageAction, +} from './UserStorageController-method-action-types'; diff --git a/packages/subscription-controller/src/SubscriptionController.ts b/packages/subscription-controller/src/SubscriptionController.ts index 8ff4ba2693b..e640a5005dc 100644 --- a/packages/subscription-controller/src/SubscriptionController.ts +++ b/packages/subscription-controller/src/SubscriptionController.ts @@ -154,8 +154,8 @@ export type SubscriptionControllerActions = | SubscriptionControllerClearLastSelectedPaymentMethodAction; export type AllowedActions = - | AuthenticationController.AuthenticationControllerGetBearerToken - | AuthenticationController.AuthenticationControllerPerformSignOut; + | AuthenticationController.AuthenticationControllerGetBearerTokenAction + | AuthenticationController.AuthenticationControllerPerformSignOutAction; // Events export type SubscriptionControllerStateChangeEvent = ControllerStateChangeEvent< diff --git a/yarn.lock b/yarn.lock index 09a4cc741aa..7aae069dd17 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2301,6 +2301,7 @@ __metadata: jest: "npm:^29.7.0" lodash: "npm:^4.17.21" ts-jest: "npm:^29.2.5" + tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" typescript: "npm:~5.3.3" @@ -2341,6 +2342,7 @@ __metadata: jest: "npm:^29.7.0" lodash: "npm:^4.17.21" ts-jest: "npm:^29.2.5" + tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" typescript: "npm:~5.3.3" @@ -4040,6 +4042,7 @@ __metadata: jest: "npm:^29.7.0" lodash: "npm:^4.17.21" ts-jest: "npm:^29.2.5" + tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" typescript: "npm:~5.3.3" @@ -4520,6 +4523,7 @@ __metadata: nock: "npm:^13.3.1" siwe: "npm:^2.3.2" ts-jest: "npm:^29.2.5" + tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" typescript: "npm:~5.3.3"