From de4f506246015baa16d7352e41590f3d3aa9e774 Mon Sep 17 00:00:00 2001 From: Charly Chevalier Date: Tue, 12 May 2026 11:57:26 +0200 Subject: [PATCH 1/6] feat(snap-account-service): handle :accountGroup{Created,Updated,Removed} events --- .../src/SnapAccountService.test.ts | 170 +++++++++++++++++- .../src/SnapAccountService.ts | 136 +++++++++++--- packages/snap-account-service/src/types.ts | 25 +++ 3 files changed, 300 insertions(+), 31 deletions(-) diff --git a/packages/snap-account-service/src/SnapAccountService.test.ts b/packages/snap-account-service/src/SnapAccountService.test.ts index 871206f85d..9206aa9232 100644 --- a/packages/snap-account-service/src/SnapAccountService.test.ts +++ b/packages/snap-account-service/src/SnapAccountService.test.ts @@ -105,6 +105,9 @@ function getMessenger( 'KeyringController:stateChange', 'KeyringController:unlock', 'AccountTreeController:selectedAccountGroupChange', + 'AccountTreeController:accountGroupCreated', + 'AccountTreeController:accountGroupUpdated', + 'AccountTreeController:accountGroupRemoved', ], }); return messenger; @@ -202,11 +205,57 @@ function buildSnap(id: string, hasKeyring: boolean): TruncatedSnap { /** * Builds a minimal `AccountGroupObject` for tests. * + * @param id - The group ID. * @param accounts - The list of account IDs in the group. * @returns A minimal `AccountGroupObject`. */ -function buildGroup(accounts: string[]): AccountGroupObject { - return { accounts } as unknown as AccountGroupObject; +function buildGroup( + id: AccountGroupId, + accounts: string[], +): AccountGroupObject { + return { id, accounts } as unknown as AccountGroupObject; +} + +/** + * Publishes an AccountTreeController accountGroupCreated event on the root + * messenger. + * + * @param rootMessenger - The root messenger. + * @param group - The created account group. + */ +function publishAccountGroupCreated( + rootMessenger: RootMessenger, + group: AccountGroupObject, +): void { + rootMessenger.publish('AccountTreeController:accountGroupCreated', group); +} + +/** + * Publishes an AccountTreeController accountGroupUpdated event on the root + * messenger. + * + * @param rootMessenger - The root messenger. + * @param group - The updated account group. + */ +function publishAccountGroupUpdated( + rootMessenger: RootMessenger, + group: AccountGroupObject, +): void { + rootMessenger.publish('AccountTreeController:accountGroupUpdated', group); +} + +/** + * Publishes an AccountTreeController accountGroupRemoved event on the root + * messenger. + * + * @param rootMessenger - The root messenger. + * @param groupId - The removed account group ID. + */ +function publishAccountGroupRemoved( + rootMessenger: RootMessenger, + groupId: AccountGroupId, +): void { + rootMessenger.publish('AccountTreeController:accountGroupRemoved', groupId); } /** @@ -642,7 +691,7 @@ describe('SnapAccountService', () => { const setSelectedAccounts = jest.fn().mockResolvedValue(undefined); mockLegacySnapKeyring(mocks, { setSelectedAccounts }); mocks.AccountTreeController.getAccountGroupObject.mockReturnValue( - buildGroup(MOCK_ACCOUNTS), + buildGroup(MOCK_GROUP_ID, MOCK_ACCOUNTS), ); expect(service).toBeDefined(); @@ -694,7 +743,7 @@ describe('SnapAccountService', () => { const setSelectedAccounts = jest.fn().mockRejectedValue(error); mockLegacySnapKeyring(mocks, { setSelectedAccounts }); mocks.AccountTreeController.getAccountGroupObject.mockReturnValue( - buildGroup(MOCK_ACCOUNTS), + buildGroup(MOCK_GROUP_ID, MOCK_ACCOUNTS), ); const consoleErrorSpy = jest .spyOn(console, 'error') @@ -706,7 +755,7 @@ describe('SnapAccountService', () => { expect(setSelectedAccounts).toHaveBeenCalledWith(MOCK_ACCOUNTS); expect(consoleErrorSpy).toHaveBeenCalledWith( - 'Error handling selected account group change:', + 'Error forwarding selected accounts:', error, ); @@ -729,7 +778,7 @@ describe('SnapAccountService', () => { MOCK_GROUP_ID, ); mocks.AccountTreeController.getAccountGroupObject.mockReturnValue( - buildGroup(MOCK_ACCOUNTS), + buildGroup(MOCK_GROUP_ID, MOCK_ACCOUNTS), ); expect(service).toBeDefined(); @@ -770,7 +819,7 @@ describe('SnapAccountService', () => { MOCK_GROUP_ID, ); mocks.AccountTreeController.getAccountGroupObject.mockReturnValue( - buildGroup(MOCK_ACCOUNTS), + buildGroup(MOCK_GROUP_ID, MOCK_ACCOUNTS), ); const consoleErrorSpy = jest .spyOn(console, 'error') @@ -782,11 +831,116 @@ describe('SnapAccountService', () => { expect(setSelectedAccounts).toHaveBeenCalledWith(MOCK_ACCOUNTS); expect(consoleErrorSpy).toHaveBeenCalledWith( - 'Error forwarding selected account group on unlock:', + 'Error forwarding selected accounts:', error, ); consoleErrorSpy.mockRestore(); }); }); + + describe.each([ + ['accountGroupCreated', publishAccountGroupCreated] as const, + ['accountGroupUpdated', publishAccountGroupUpdated] as const, + ])('on AccountTreeController:%s', (_eventName, publishEvent) => { + const MOCK_GROUP_ID = 'keyring:01JABC/group-1' as AccountGroupId; + const OTHER_GROUP_ID = 'keyring:01JABC/group-2' as AccountGroupId; + const MOCK_ACCOUNTS = [ + '00000000-0000-0000-0000-000000000001', + '00000000-0000-0000-0000-000000000002', + ]; + + it('forwards the accounts from the event payload when the affected group is the selected one', async () => { + const { service, rootMessenger, mocks } = setup(); + const setSelectedAccounts = jest.fn().mockResolvedValue(undefined); + mockLegacySnapKeyring(mocks, { setSelectedAccounts }); + mocks.AccountTreeController.getSelectedAccountGroup.mockReturnValue( + MOCK_GROUP_ID, + ); + expect(service).toBeDefined(); + + publishEvent(rootMessenger, buildGroup(MOCK_GROUP_ID, MOCK_ACCOUNTS)); + await flushMicrotasks(); + + expect( + mocks.AccountTreeController.getAccountGroupObject, + ).not.toHaveBeenCalled(); + expect(setSelectedAccounts).toHaveBeenCalledWith(MOCK_ACCOUNTS); + }); + + it('does nothing when the affected group is not the selected one', async () => { + const { service, rootMessenger, mocks } = setup(); + const setSelectedAccounts = jest.fn().mockResolvedValue(undefined); + mockLegacySnapKeyring(mocks, { setSelectedAccounts }); + mocks.AccountTreeController.getSelectedAccountGroup.mockReturnValue( + OTHER_GROUP_ID, + ); + expect(service).toBeDefined(); + + publishEvent(rootMessenger, buildGroup(MOCK_GROUP_ID, MOCK_ACCOUNTS)); + await flushMicrotasks(); + + expect( + mocks.AccountTreeController.getAccountGroupObject, + ).not.toHaveBeenCalled(); + expect(setSelectedAccounts).not.toHaveBeenCalled(); + }); + + it('does nothing when no account group is selected', async () => { + const { service, rootMessenger, mocks } = setup(); + const setSelectedAccounts = jest.fn().mockResolvedValue(undefined); + mockLegacySnapKeyring(mocks, { setSelectedAccounts }); + mocks.AccountTreeController.getSelectedAccountGroup.mockReturnValue(''); + expect(service).toBeDefined(); + + publishEvent(rootMessenger, buildGroup(MOCK_GROUP_ID, MOCK_ACCOUNTS)); + await flushMicrotasks(); + + expect( + mocks.AccountTreeController.getAccountGroupObject, + ).not.toHaveBeenCalled(); + expect(setSelectedAccounts).not.toHaveBeenCalled(); + }); + }); + + describe('on AccountTreeController:accountGroupRemoved', () => { + const MOCK_GROUP_ID = 'keyring:01JABC/group-1' as AccountGroupId; + const OTHER_GROUP_ID = 'keyring:01JABC/group-2' as AccountGroupId; + + it('clears the selected accounts when the removed group is the selected one', async () => { + const { service, rootMessenger, mocks } = setup(); + const setSelectedAccounts = jest.fn().mockResolvedValue(undefined); + mockLegacySnapKeyring(mocks, { setSelectedAccounts }); + mocks.AccountTreeController.getSelectedAccountGroup.mockReturnValue( + MOCK_GROUP_ID, + ); + expect(service).toBeDefined(); + + publishAccountGroupRemoved(rootMessenger, MOCK_GROUP_ID); + await flushMicrotasks(); + + expect( + mocks.AccountTreeController.getAccountGroupObject, + ).not.toHaveBeenCalled(); + expect(setSelectedAccounts).toHaveBeenCalledWith([]); + }); + + it('does nothing when the removed group is not the selected one', async () => { + const { service, rootMessenger, mocks } = setup(); + const setSelectedAccounts = jest.fn().mockResolvedValue(undefined); + mockLegacySnapKeyring(mocks, { setSelectedAccounts }); + mocks.AccountTreeController.getSelectedAccountGroup.mockReturnValue( + OTHER_GROUP_ID, + ); + expect(service).toBeDefined(); + + publishAccountGroupRemoved(rootMessenger, MOCK_GROUP_ID); + await flushMicrotasks(); + + expect( + mocks.AccountTreeController.getAccountGroupObject, + ).not.toHaveBeenCalled(); + expect(setSelectedAccounts).not.toHaveBeenCalled(); + }); + }); }); diff --git a/packages/snap-account-service/src/SnapAccountService.ts b/packages/snap-account-service/src/SnapAccountService.ts index ec2a285bec..f8ad28f0b0 100644 --- a/packages/snap-account-service/src/SnapAccountService.ts +++ b/packages/snap-account-service/src/SnapAccountService.ts @@ -11,7 +11,7 @@ import type { KeyringEntry, } from '@metamask/keyring-controller'; import { KeyringTypes } from '@metamask/keyring-controller'; -import type { BaseKeyring } from '@metamask/keyring-utils'; +import type { AccountId, BaseKeyring } from '@metamask/keyring-utils'; import type { Messenger } from '@metamask/messenger'; import type { SnapControllerGetRunnableSnapsAction, @@ -42,6 +42,10 @@ import type { AccountTreeControllerGetAccountGroupObjectAction, AccountTreeControllerGetSelectedAccountGroupAction, AccountTreeControllerSelectedAccountGroupChangeEvent, + AccountTreeControllerAccountGroupCreatedEvent, + AccountTreeControllerAccountGroupUpdatedEvent, + AccountTreeControllerAccountGroupRemovedEvent, + AccountGroupObject, } from './types'; /** @@ -100,7 +104,10 @@ type AllowedEvents = | SnapControllerSnapUninstalledEvent | KeyringControllerStateChangeEvent | KeyringControllerUnlockEvent - | AccountTreeControllerSelectedAccountGroupChangeEvent; + | AccountTreeControllerSelectedAccountGroupChangeEvent + | AccountTreeControllerAccountGroupCreatedEvent + | AccountTreeControllerAccountGroupUpdatedEvent + | AccountTreeControllerAccountGroupRemovedEvent; /** * The messenger which is restricted to actions and events accessed by @@ -181,6 +188,21 @@ export class SnapAccountService { (groupId) => this.#handleSelectedAccountGroupChange(groupId), ); + this.#messenger.subscribe( + 'AccountTreeController:accountGroupCreated', + (group) => this.#handleAccountGroupCreatedOrUpdated(group), + ); + + this.#messenger.subscribe( + 'AccountTreeController:accountGroupUpdated', + (group) => this.#handleAccountGroupCreatedOrUpdated(group), + ); + + this.#messenger.subscribe( + 'AccountTreeController:accountGroupRemoved', + (groupId) => this.#handleAccountGroupRemoved(groupId), + ); + this.#messenger.subscribe('KeyringController:unlock', () => this.#handleUnlock(), ); @@ -193,9 +215,10 @@ export class SnapAccountService { * @param groupId - The ID of the newly selected account group. */ #handleSelectedAccountGroupChange(groupId: AccountGroupId | ''): void { - this.#forwardSelectedAccountGroup(groupId).catch((error) => { - console.error('Error handling selected account group change:', error); - }); + this.#forwardSelectedAccounts( + groupId, + this.#getAccountGroup(groupId)?.accounts, + ); } /** @@ -203,15 +226,38 @@ export class SnapAccountService { * selected account group's accounts to the Snap keyring. */ #handleUnlock(): void { - const groupId = this.#messenger.call( - 'AccountTreeController:getSelectedAccountGroup', + const groupId = this.#getSelectedAccountGroupId(); + this.#forwardSelectedAccounts( + groupId, + this.#getAccountGroup(groupId)?.accounts, ); - this.#forwardSelectedAccountGroup(groupId).catch((error) => { - console.error( - 'Error forwarding selected account group on unlock:', - error, + } + + /** + * Handles created or updated account groups by forwarding the accounts of the currently + * selected account group to the Snap keyring, if the created/updated group is currently selected. + * + * @param group - The account group being created or updated. + */ + #handleAccountGroupCreatedOrUpdated(group: AccountGroupObject): void { + if (group.id === this.#getSelectedAccountGroupId()) { + this.#forwardSelectedAccounts(group.id, group.accounts); + } + } + + /** + * Handles removed account groups by forwarding the accounts of the currently + * selected account group to the Snap keyring, if the removed group is currently selected. + * + * @param groupId - The ID of the account group being removed. + */ + #handleAccountGroupRemoved(groupId: AccountGroupId): void { + if (groupId === this.#getSelectedAccountGroupId()) { + this.#forwardSelectedAccounts( + groupId, + [] /* Clearing accounts since the group is removed */, ); - }); + } } /** @@ -322,24 +368,68 @@ export class SnapAccountService { * * @param groupId - The ID of the account group whose accounts should be * forwarded. If empty, this is a no-op. + * @param accounts - The accounts to forward. If not defined, this is a no-op. */ - async #forwardSelectedAccountGroup( + #forwardSelectedAccounts( groupId: AccountGroupId | '', - ): Promise { - if (groupId) { - const group = this.#messenger.call( - 'AccountTreeController:getAccountGroupObject', - groupId, + accounts: AccountId[] | undefined, + ): void { + if (!groupId) { + log( + 'No selected account group, skipping forwarding selected accounts to Snap keyring.', ); + return; + } - if (group) { - log( - `Forwarding selected accounts (from "${groupId}") to Snap keyring: ${group.accounts.join(', ')}`, - ); + const forwardSelectedAccounts = async (): Promise => { + if (accounts) { + if (accounts.length) { + log( + `Forwarding selected accounts (from "${groupId}"): ${accounts.join(', ')}`, + ); + } else { + log(`Cleearing selected accounts (from "${groupId}")`); + } const snapKeyring = await this.getLegacySnapKeyring(); - await snapKeyring.setSelectedAccounts(group.accounts); + await snapKeyring.setSelectedAccounts(accounts); } + }; + + // There is nothing we can do if forwarding fails. This will auto-recover on the next relevant event. + forwardSelectedAccounts().catch((error) => { + console.error('Error forwarding selected accounts:', error); + }); + } + + /** + * Gets the account group object for the given group ID. + * + * @param groupId - The ID of the account group. + * @returns The account group object, or undefined if the group ID is empty or the group does not exist. + */ + #getAccountGroup( + groupId: AccountGroupId | '', + ): AccountGroupObject | undefined { + if (!groupId) { + return undefined; } + + return this.#messenger.call( + 'AccountTreeController:getAccountGroupObject', + groupId, + ); + } + + /** + * Gets the currently selected account group ID. + * + * @returns The currently selected account group ID, or an empty string if + * there is no selected account group. + */ + #getSelectedAccountGroupId(): AccountGroupId | '' { + return this.#messenger.call( + 'AccountTreeController:getSelectedAccountGroup', + ); } } diff --git a/packages/snap-account-service/src/types.ts b/packages/snap-account-service/src/types.ts index 9e175a1b7e..e9485a7d5f 100644 --- a/packages/snap-account-service/src/types.ts +++ b/packages/snap-account-service/src/types.ts @@ -25,6 +25,7 @@ import { AccountId } from '@metamask/keyring-utils'; * narrow. See the note above. */ export type AccountGroupObject = { + id: AccountGroupId; accounts: AccountId[]; }; @@ -51,3 +52,27 @@ export type AccountTreeControllerSelectedAccountGroupChangeEvent = { type: `AccountTreeController:selectedAccountGroupChange`; payload: [AccountGroupId | '', AccountGroupId | '']; }; + +/** + * Mirror of `AccountTreeControllerAccountGroupCreatedEvent`. + */ +export type AccountTreeControllerAccountGroupCreatedEvent = { + type: `AccountTreeController:accountGroupCreated`; + payload: [AccountGroupObject]; +}; + +/** + * Mirror of `AccountTreeControllerAccountGroupUpdatedEvent`. + */ +export type AccountTreeControllerAccountGroupUpdatedEvent = { + type: `AccountTreeController:accountGroupUpdated`; + payload: [AccountGroupObject]; +}; + +/** + * Mirror of `AccountTreeControllerAccountGroupRemovedEvent`. + */ +export type AccountTreeControllerAccountGroupRemovedEvent = { + type: `AccountTreeController:accountGroupRemoved`; + payload: [AccountGroupId]; +}; From 26c705af8971f81431b5122bd03b4a8f30cc2eea Mon Sep 17 00:00:00 2001 From: Charly Chevalier Date: Tue, 12 May 2026 12:17:26 +0200 Subject: [PATCH 2/6] chore: changelog --- packages/snap-account-service/CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/snap-account-service/CHANGELOG.md b/packages/snap-account-service/CHANGELOG.md index 8aae4645b9..2d89203d8f 100644 --- a/packages/snap-account-service/CHANGELOG.md +++ b/packages/snap-account-service/CHANGELOG.md @@ -28,9 +28,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The service messenger now requires the `KeyringController:withController` action. - Add `handleKeyringSnapMessage` ([#8758](https://github.com/MetaMask/core/pull/8758)) - This will be the new entry point for consumer that needs to forward keyring events to a account management Snap (instead of using the legacy Snap keyring instance directly). -- Forward selected account group accounts ([#8763](https://github.com/MetaMask/core/pull/8763)) +- Forward selected account group accounts ([#8763](https://github.com/MetaMask/core/pull/8763)), ([#8770](https://github.com/MetaMask/core/pull/8770)) - This logic used to live on the clients. - - The service messenger now requires the `KeyringController:unlock`, `AccountTreeController:selectedAccountGroupChange` events and `AccountTreeController:getAccountGroupObject` action. + - The service messenger now requires the `KeyringController:unlock`, `AccountTreeController:selectedAccountGroupChange`, `AccountTreeController:accountGroup{Created,Updated,Removed}` events. + - The service messenger now requires the `AccountTreeController:getSelectedAccountGroup` and `AccountTreeController:getAccountGroupObject` actions. ### Changed From f80989522cc544e2e0f226ebefd086acdd78cf54 Mon Sep 17 00:00:00 2001 From: Charly Chevalier Date: Tue, 12 May 2026 12:20:28 +0200 Subject: [PATCH 3/6] test: refactor --- .../src/SnapAccountService.test.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/snap-account-service/src/SnapAccountService.test.ts b/packages/snap-account-service/src/SnapAccountService.test.ts index 9206aa9232..e1569d63a0 100644 --- a/packages/snap-account-service/src/SnapAccountService.test.ts +++ b/packages/snap-account-service/src/SnapAccountService.test.ts @@ -40,6 +40,18 @@ type MockTruncatedSnap = Pick< 'id' | 'initialPermissions' | 'enabled' | 'blocked' >; +/** Mock account group type for tests. */ +type MockAccountGroup = Pick; + +/** Mock Snap keyring type for tests. */ +type MockSnapKeyring = { + type: KeyringTypes.snap; + handleKeyringSnapMessage?: jest.MockedFunction< + SnapKeyring['handleKeyringSnapMessage'] + >; + setSelectedAccounts?: jest.MockedFunction; +}; + type Mocks = { // eslint-disable-next-line @typescript-eslint/naming-convention SnapController: { @@ -213,7 +225,7 @@ function buildGroup( id: AccountGroupId, accounts: string[], ): AccountGroupObject { - return { id, accounts } as unknown as AccountGroupObject; + return { id, accounts } as MockAccountGroup as AccountGroupObject; } /** @@ -326,7 +338,7 @@ function mockLegacySnapKeyring( >; }, ): void { - const snapKeyring = { + const snapKeyring: MockSnapKeyring = { type: KeyringTypes.snap, handleKeyringSnapMessage, setSelectedAccounts, @@ -336,7 +348,7 @@ function mockLegacySnapKeyring( get keyrings() { return Object.freeze([ { - keyring: snapKeyring as unknown as KeyringEntry['keyring'], + keyring: snapKeyring as KeyringEntry['keyring'], metadata: { id: 'id-snap', name: KeyringTypes.snap }, }, ]); From af75492307d018112283589274fad5789b28f1a3 Mon Sep 17 00:00:00 2001 From: Charly Chevalier Date: Tue, 12 May 2026 16:20:21 +0200 Subject: [PATCH 4/6] chore: typo --- packages/snap-account-service/src/SnapAccountService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/snap-account-service/src/SnapAccountService.ts b/packages/snap-account-service/src/SnapAccountService.ts index f8ad28f0b0..11e5311303 100644 --- a/packages/snap-account-service/src/SnapAccountService.ts +++ b/packages/snap-account-service/src/SnapAccountService.ts @@ -388,7 +388,7 @@ export class SnapAccountService { `Forwarding selected accounts (from "${groupId}"): ${accounts.join(', ')}`, ); } else { - log(`Cleearing selected accounts (from "${groupId}")`); + log(`Clearing selected accounts (from "${groupId}")`); } const snapKeyring = await this.getLegacySnapKeyring(); From fb19ef95ff32454b1169e31a323d01cb4fde6a47 Mon Sep 17 00:00:00 2001 From: Charly Chevalier Date: Tue, 12 May 2026 20:36:22 +0200 Subject: [PATCH 5/6] chore: fix comment style --- packages/snap-account-service/src/SnapAccountService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/snap-account-service/src/SnapAccountService.ts b/packages/snap-account-service/src/SnapAccountService.ts index 11e5311303..33c97b3828 100644 --- a/packages/snap-account-service/src/SnapAccountService.ts +++ b/packages/snap-account-service/src/SnapAccountService.ts @@ -255,7 +255,7 @@ export class SnapAccountService { if (groupId === this.#getSelectedAccountGroupId()) { this.#forwardSelectedAccounts( groupId, - [] /* Clearing accounts since the group is removed */, + [], // Clearing accounts since the group is removed ); } } From 866a677aa91329cc5bf3977e80365c3512a97f25 Mon Sep 17 00:00:00 2001 From: Charly Chevalier Date: Tue, 12 May 2026 20:41:46 +0200 Subject: [PATCH 6/6] refactor: skip forwarding if no accounts --- .../src/SnapAccountService.ts | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/packages/snap-account-service/src/SnapAccountService.ts b/packages/snap-account-service/src/SnapAccountService.ts index 33c97b3828..7f73096178 100644 --- a/packages/snap-account-service/src/SnapAccountService.ts +++ b/packages/snap-account-service/src/SnapAccountService.ts @@ -381,19 +381,24 @@ export class SnapAccountService { return; } - const forwardSelectedAccounts = async (): Promise => { - if (accounts) { - if (accounts.length) { - log( - `Forwarding selected accounts (from "${groupId}"): ${accounts.join(', ')}`, - ); - } else { - log(`Clearing selected accounts (from "${groupId}")`); - } + if (!accounts) { + log( + `Account group ("${groupId}") has no accounts, skipping forwarding selected accounts to Snap keyring.`, + ); + return; + } - const snapKeyring = await this.getLegacySnapKeyring(); - await snapKeyring.setSelectedAccounts(accounts); + const forwardSelectedAccounts = async (): Promise => { + if (accounts.length) { + log( + `Forwarding selected accounts (from "${groupId}"): ${accounts.join(', ')}`, + ); + } else { + log(`Clearing selected accounts (from "${groupId}")`); } + + const snapKeyring = await this.getLegacySnapKeyring(); + await snapKeyring.setSelectedAccounts(accounts); }; // There is nothing we can do if forwarding fails. This will auto-recover on the next relevant event.