diff --git a/packages/account-tree-controller/CHANGELOG.md b/packages/account-tree-controller/CHANGELOG.md index 3f43e00799a..3a877e56019 100644 --- a/packages/account-tree-controller/CHANGELOG.md +++ b/packages/account-tree-controller/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Always fire `:selectedAccountGroupChange` during `init` call ([#8427](https://github.com/MetaMask/core/pull/8427)) + - Many controllers were actually relying on this behavior when this used to be dynamic, so we re-introduce this behavior. - Now persist `accounTree` ([#8437](https://github.com/MetaMask/core/pull/8437)) - The tree is not persisted and can be used before `init` is called. - This allow consumers (like the assets controllers) to rely on the selected group's accounts right away. diff --git a/packages/account-tree-controller/src/AccountTreeController.test.ts b/packages/account-tree-controller/src/AccountTreeController.test.ts index 5a9b226f7e3..9a4ecd192f1 100644 --- a/packages/account-tree-controller/src/AccountTreeController.test.ts +++ b/packages/account-tree-controller/src/AccountTreeController.test.ts @@ -4586,6 +4586,48 @@ describe('AccountTreeController', () => { ); }); + it('always emits selectedAccountGroupChange on init even if the selected group did not change', () => { + const { controller, messenger, mocks } = setup({ + accounts: [MOCK_HD_ACCOUNT_1], + keyrings: [MOCK_HD_KEYRING_1], + }); + + mocks.AccountsController.getSelectedMultichainAccount.mockImplementation( + () => MOCK_HD_ACCOUNT_1, + ); + + controller.init(); + + const defaultAccountGroupId = toMultichainAccountGroupId( + toMultichainAccountWalletId(MOCK_HD_ACCOUNT_1.options.entropy.id), + MOCK_HD_ACCOUNT_1.options.entropy.groupIndex, + ); + + expect(controller.state.selectedAccountGroup).toStrictEqual( + defaultAccountGroupId, + ); + + const selectedAccountGroupChangeListener = jest.fn(); + messenger.subscribe( + 'AccountTreeController:selectedAccountGroupChange', + selectedAccountGroupChangeListener, + ); + + // Re-init with the same accounts — the selected group still exists and has not changed. + controller.reinit(); + + expect(controller.state.selectedAccountGroup).toStrictEqual( + defaultAccountGroupId, + ); + // The event must fire even though the group did not change, so that + // subscribers that missed the first init can still react accordingly. + expect(selectedAccountGroupChangeListener).toHaveBeenCalledWith( + defaultAccountGroupId, + defaultAccountGroupId, + ); + expect(selectedAccountGroupChangeListener).toHaveBeenCalledTimes(1); + }); + it('emits selectedAccountGroupChange when setSelectedAccountGroup is called', () => { // Use different keyring types to ensure different groups const { controller, messenger } = setup({ diff --git a/packages/account-tree-controller/src/AccountTreeController.ts b/packages/account-tree-controller/src/AccountTreeController.ts index 5093de0217c..676df296fa5 100644 --- a/packages/account-tree-controller/src/AccountTreeController.ts +++ b/packages/account-tree-controller/src/AccountTreeController.ts @@ -418,18 +418,15 @@ export class AccountTreeController extends BaseController< } }); - // We still compare the previous and new value, the previous one could have been - // an empty string and `#getDefaultSelectedAccountGroup` could also return an - // empty string too, thus, we would re-use the same value here again. In that - // case, no need to fire any event. - if (previousSelectedAccountGroup !== this.state.selectedAccountGroup) { - log(`Selected (initial) group is: [${this.state.selectedAccountGroup}]`); - this.messenger.publish( - `${controllerName}:selectedAccountGroupChange`, - this.state.selectedAccountGroup, - previousSelectedAccountGroup, - ); - } + // We always fire the current selected group after init, even if it did not change, to ensure that + // all subscribers are aware of it and can react accordingly (e.g. by fetching accounts from + // the selected group on startup). + log(`Selected (initial) group is: [${this.state.selectedAccountGroup}]`); + this.messenger.publish( + `${controllerName}:selectedAccountGroupChange`, + this.state.selectedAccountGroup, + previousSelectedAccountGroup, + ); log('Initialized!'); this.#initialized = true;