@metamask-previews/account-tree-controller 1.2.0-preview-c20b7569 → 1.3.0-preview-a5441fae
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -3
- package/dist/AccountTreeController.cjs +28 -24
- package/dist/AccountTreeController.cjs.map +1 -1
- package/dist/AccountTreeController.d.cts.map +1 -1
- package/dist/AccountTreeController.d.mts.map +1 -1
- package/dist/AccountTreeController.mjs +28 -24
- package/dist/AccountTreeController.mjs.map +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -7,10 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- Bump `@metamask/utils` from `^11.8.0` to `^11.8.1` ([#6708](https://github.com/MetaMask/core/pull/6708))
|
|
13
|
+
|
|
14
|
+
## [1.3.0]
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- Add more internal logs ([#6730](https://github.com/MetaMask/core/pull/6730))
|
|
19
|
+
|
|
10
20
|
### Fixed
|
|
11
21
|
|
|
12
|
-
-
|
|
13
|
-
- We now wait
|
|
22
|
+
- Preverve import time for account groups ([#6727](https://github.com/MetaMask/core/pull/6727))
|
|
23
|
+
- We now wait sort accounts by their `importTime` before re-building the tree.
|
|
24
|
+
- Prevent `:account{Added,Removed}` to be used if `init` has not been called yet ([#6717](https://github.com/MetaMask/core/pull/6717))
|
|
25
|
+
- We now wait for `init` to have been called at least once. Clients will need to ensure internal accounts are fully ready before calling `init`.
|
|
14
26
|
- This should also enforce account group ordering, since all accounts will be ready to consume right away.
|
|
15
27
|
|
|
16
28
|
## [1.2.0]
|
|
@@ -329,7 +341,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
329
341
|
- Initial release ([#5847](https://github.com/MetaMask/core/pull/5847))
|
|
330
342
|
- Grouping accounts into 3 main categories: Entropy source, Snap ID, keyring types.
|
|
331
343
|
|
|
332
|
-
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/account-tree-controller@1.
|
|
344
|
+
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/account-tree-controller@1.3.0...HEAD
|
|
345
|
+
[1.3.0]: https://github.com/MetaMask/core/compare/@metamask/account-tree-controller@1.2.0...@metamask/account-tree-controller@1.3.0
|
|
333
346
|
[1.2.0]: https://github.com/MetaMask/core/compare/@metamask/account-tree-controller@1.1.0...@metamask/account-tree-controller@1.2.0
|
|
334
347
|
[1.1.0]: https://github.com/MetaMask/core/compare/@metamask/account-tree-controller@1.0.0...@metamask/account-tree-controller@1.1.0
|
|
335
348
|
[1.0.0]: https://github.com/MetaMask/core/compare/@metamask/account-tree-controller@0.18.1...@metamask/account-tree-controller@1.0.0
|
|
@@ -10,7 +10,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
10
10
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
11
11
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
12
|
};
|
|
13
|
-
var _AccountTreeController_instances, _AccountTreeController_accountIdToContext, _AccountTreeController_groupIdToWalletId, _AccountTreeController_backupAndSyncService, _AccountTreeController_rules, _AccountTreeController_trace, _AccountTreeController_backupAndSyncConfig, _AccountTreeController_initialized, _AccountTreeController_getEntropyRule, _AccountTreeController_getSnapRule, _AccountTreeController_getKeyringRule, _AccountTreeController_applyAccountWalletMetadata, _AccountTreeController_getRuleForWallet, _AccountTreeController_applyAccountGroupMetadata, _AccountTreeController_handleAccountAdded, _AccountTreeController_handleAccountRemoved, _AccountTreeController_pruneEmptyGroupAndWallet, _AccountTreeController_insert, _AccountTreeController_listAccounts, _AccountTreeController_assertAccountGroupExists, _AccountTreeController_assertAccountWalletExists, _AccountTreeController_assertAccountGroupNameIsUnique, _AccountTreeController_getDefaultSelectedAccountGroup,
|
|
13
|
+
var _AccountTreeController_instances, _AccountTreeController_accountIdToContext, _AccountTreeController_groupIdToWalletId, _AccountTreeController_backupAndSyncService, _AccountTreeController_rules, _AccountTreeController_trace, _AccountTreeController_backupAndSyncConfig, _AccountTreeController_initialized, _AccountTreeController_getEntropyRule, _AccountTreeController_getSnapRule, _AccountTreeController_getKeyringRule, _AccountTreeController_applyAccountWalletMetadata, _AccountTreeController_getRuleForWallet, _AccountTreeController_applyAccountGroupMetadata, _AccountTreeController_handleAccountAdded, _AccountTreeController_handleAccountRemoved, _AccountTreeController_pruneEmptyGroupAndWallet, _AccountTreeController_insert, _AccountTreeController_listAccounts, _AccountTreeController_assertAccountGroupExists, _AccountTreeController_assertAccountWalletExists, _AccountTreeController_assertAccountGroupNameIsUnique, _AccountTreeController_getDefaultSelectedAccountGroup, _AccountTreeController_handleMultichainAccountWalletStatusChange, _AccountTreeController_getAccountGroup, _AccountTreeController_getDefaultAccountFromAccountGroupId, _AccountTreeController_getDefaultAccountGroupId, _AccountTreeController_registerMessageHandlers, _AccountTreeController_createBackupAndSyncContext;
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.AccountTreeController = exports.getDefaultAccountTreeControllerState = exports.controllerName = void 0;
|
|
16
16
|
const account_api_1 = require("@metamask/account-api");
|
|
@@ -20,6 +20,7 @@ const utils_1 = require("@metamask/utils");
|
|
|
20
20
|
const analytics_1 = require("./backup-and-sync/analytics/index.cjs");
|
|
21
21
|
const service_1 = require("./backup-and-sync/service/index.cjs");
|
|
22
22
|
const group_1 = require("./group.cjs");
|
|
23
|
+
const logger_1 = require("./logger.cjs");
|
|
23
24
|
const entropy_1 = require("./rules/entropy.cjs");
|
|
24
25
|
const keyring_1 = require("./rules/keyring.cjs");
|
|
25
26
|
const snap_1 = require("./rules/snap.cjs");
|
|
@@ -136,9 +137,6 @@ class AccountTreeController extends base_controller_1.BaseController {
|
|
|
136
137
|
this.messagingSystem.subscribe('AccountsController:accountRemoved', (accountId) => {
|
|
137
138
|
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_handleAccountRemoved).call(this, accountId);
|
|
138
139
|
});
|
|
139
|
-
this.messagingSystem.subscribe('AccountsController:selectedAccountChange', (account) => {
|
|
140
|
-
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_handleSelectedAccountChange).call(this, account);
|
|
141
|
-
});
|
|
142
140
|
this.messagingSystem.subscribe('UserStorageController:stateChange', (userStorageControllerState) => {
|
|
143
141
|
__classPrivateFieldGet(this, _AccountTreeController_backupAndSyncService, "f").handleUserStorageStateChange(userStorageControllerState);
|
|
144
142
|
});
|
|
@@ -160,6 +158,7 @@ class AccountTreeController extends base_controller_1.BaseController {
|
|
|
160
158
|
// `reinit` to re-init everything from scratch.
|
|
161
159
|
return;
|
|
162
160
|
}
|
|
161
|
+
(0, logger_1.projectLogger)('Initializing...');
|
|
163
162
|
const wallets = {};
|
|
164
163
|
// Clear mappings for fresh rebuild.
|
|
165
164
|
__classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").clear();
|
|
@@ -167,8 +166,20 @@ class AccountTreeController extends base_controller_1.BaseController {
|
|
|
167
166
|
// Keep the current selected group to check if it's still part of the tree
|
|
168
167
|
// after rebuilding it.
|
|
169
168
|
const previousSelectedAccountGroup = this.state.accountTree.selectedAccountGroup;
|
|
169
|
+
// There's no guarantee that accounts would be sorted by their import time
|
|
170
|
+
// with `listMultichainAccounts`. We have to sort them here before constructing
|
|
171
|
+
// the tree.
|
|
172
|
+
//
|
|
173
|
+
// Because of the alignment mecanism, some accounts from the same group might not
|
|
174
|
+
// have been imported at the same time, but at least of them should have been
|
|
175
|
+
// imported at the right time, thus, inserting the group at the proper place too.
|
|
176
|
+
//
|
|
177
|
+
// Lastly, if one day we allow to have "gaps" in between groups, then this `sort`
|
|
178
|
+
// won't be enough and we would have to use group properties instead (like group
|
|
179
|
+
// index or maybe introduce a `importTime` at group level).
|
|
180
|
+
const accounts = __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_listAccounts).call(this).sort((a, b) => a.metadata.importTime - b.metadata.importTime);
|
|
170
181
|
// For now, we always re-compute all wallets, we do not re-use the existing state.
|
|
171
|
-
for (const account of
|
|
182
|
+
for (const account of accounts) {
|
|
172
183
|
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_insert).call(this, wallets, account);
|
|
173
184
|
}
|
|
174
185
|
// Once we have the account tree, we can apply persisted metadata (names + UI states).
|
|
@@ -211,8 +222,10 @@ class AccountTreeController extends base_controller_1.BaseController {
|
|
|
211
222
|
// case, no need to fire any event.
|
|
212
223
|
if (previousSelectedAccountGroup !==
|
|
213
224
|
this.state.accountTree.selectedAccountGroup) {
|
|
225
|
+
(0, logger_1.projectLogger)(`Selected (initial) group is: [${this.state.accountTree.selectedAccountGroup}]`);
|
|
214
226
|
this.messagingSystem.publish(`${exports.controllerName}:selectedAccountGroupChange`, this.state.accountTree.selectedAccountGroup, previousSelectedAccountGroup);
|
|
215
227
|
}
|
|
228
|
+
(0, logger_1.projectLogger)('Initialized!');
|
|
216
229
|
__classPrivateFieldSet(this, _AccountTreeController_initialized, true, "f");
|
|
217
230
|
}
|
|
218
231
|
/**
|
|
@@ -222,6 +235,7 @@ class AccountTreeController extends base_controller_1.BaseController {
|
|
|
222
235
|
* cleared state. Use this when you need to force a full re-init even if already initialized.
|
|
223
236
|
*/
|
|
224
237
|
reinit() {
|
|
238
|
+
(0, logger_1.projectLogger)('Re-initializing...');
|
|
225
239
|
__classPrivateFieldSet(this, _AccountTreeController_initialized, false, "f");
|
|
226
240
|
this.init();
|
|
227
241
|
}
|
|
@@ -321,6 +335,7 @@ class AccountTreeController extends base_controller_1.BaseController {
|
|
|
321
335
|
this.update((state) => {
|
|
322
336
|
state.accountTree.selectedAccountGroup = groupId;
|
|
323
337
|
});
|
|
338
|
+
(0, logger_1.projectLogger)(`Selected group is now: [${this.state.accountTree.selectedAccountGroup}]`);
|
|
324
339
|
this.messagingSystem.publish(`${exports.controllerName}:selectedAccountGroupChange`, groupId, previousSelectedAccountGroup);
|
|
325
340
|
// Update AccountsController - this will trigger selectedAccountChange event,
|
|
326
341
|
// but our handler is idempotent so it won't cause infinite loop
|
|
@@ -370,6 +385,7 @@ class AccountTreeController extends base_controller_1.BaseController {
|
|
|
370
385
|
// Validate that the name is unique
|
|
371
386
|
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_assertAccountGroupNameIsUnique).call(this, groupId, finalName);
|
|
372
387
|
}
|
|
388
|
+
(0, logger_1.projectLogger)(`[${groupId}] Set new name to: "${finalName}" (auto handle conflict: ${autoHandleConflict})`);
|
|
373
389
|
this.update((state) => {
|
|
374
390
|
/* istanbul ignore next */
|
|
375
391
|
if (!state.accountGroupsMetadata[groupId]) {
|
|
@@ -489,6 +505,7 @@ class AccountTreeController extends base_controller_1.BaseController {
|
|
|
489
505
|
* Also clears the backup and sync service state.
|
|
490
506
|
*/
|
|
491
507
|
clearState() {
|
|
508
|
+
(0, logger_1.projectLogger)('Clearing state');
|
|
492
509
|
this.update(() => {
|
|
493
510
|
return {
|
|
494
511
|
...getDefaultAccountTreeControllerState(),
|
|
@@ -556,6 +573,7 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
|
|
|
556
573
|
wallet.metadata.name =
|
|
557
574
|
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getKeyringRule).call(this).getDefaultAccountWalletName(wallet);
|
|
558
575
|
}
|
|
576
|
+
(0, logger_1.projectLogger)(`[${wallet.id}] Set default name to: "${wallet.metadata.name}"`);
|
|
559
577
|
}
|
|
560
578
|
}, _AccountTreeController_getRuleForWallet = function _AccountTreeController_getRuleForWallet(wallet) {
|
|
561
579
|
switch (wallet.type) {
|
|
@@ -635,6 +653,7 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
|
|
|
635
653
|
} while (proposedNameExists);
|
|
636
654
|
state.accountTree.wallets[walletId].groups[groupId].metadata.name =
|
|
637
655
|
proposedName;
|
|
656
|
+
(0, logger_1.projectLogger)(`[${group.id}] Set default name to: "${group.metadata.name}"`);
|
|
638
657
|
// Persist the generated name to ensure consistency
|
|
639
658
|
(_a = state.accountGroupsMetadata)[groupId] ?? (_a[groupId] = {});
|
|
640
659
|
state.accountGroupsMetadata[groupId].name = {
|
|
@@ -659,8 +678,7 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
|
|
|
659
678
|
if (!__classPrivateFieldGet(this, _AccountTreeController_initialized, "f")) {
|
|
660
679
|
return;
|
|
661
680
|
}
|
|
662
|
-
// Check if this account
|
|
663
|
-
// can proceed.
|
|
681
|
+
// Check if this account is already known by the tree to avoid double-insertion.
|
|
664
682
|
if (!__classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").has(account.id)) {
|
|
665
683
|
this.update((state) => {
|
|
666
684
|
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_insert).call(this, state.accountTree.wallets, account);
|
|
@@ -735,6 +753,7 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
|
|
|
735
753
|
const walletId = result.wallet.id;
|
|
736
754
|
let wallet = wallets[walletId];
|
|
737
755
|
if (!wallet) {
|
|
756
|
+
(0, logger_1.projectLogger)(`[${walletId}] Added as new wallet`);
|
|
738
757
|
wallets[walletId] = {
|
|
739
758
|
...result.wallet,
|
|
740
759
|
status: 'ready',
|
|
@@ -757,6 +776,7 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
|
|
|
757
776
|
const { type, id } = account;
|
|
758
777
|
const sortOrder = group_1.ACCOUNT_TYPE_TO_SORT_ORDER[type];
|
|
759
778
|
if (!group) {
|
|
779
|
+
(0, logger_1.projectLogger)(`[${walletId}] Add new group: [${groupId}]`);
|
|
760
780
|
wallet.groups[groupId] = {
|
|
761
781
|
...result.group,
|
|
762
782
|
// Type-wise, we are guaranteed to always have at least 1 account.
|
|
@@ -795,6 +815,7 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
|
|
|
795
815
|
return ((aSortOrder ?? group_1.MAX_SORT_ORDER) - (bSortOrder ?? group_1.MAX_SORT_ORDER));
|
|
796
816
|
});
|
|
797
817
|
}
|
|
818
|
+
(0, logger_1.projectLogger)(`[${groupId}] Add new account: { id: "${account.id}", type: "${account.type}", address: "${account.address}"`);
|
|
798
819
|
// Update the reverse mapping for this account.
|
|
799
820
|
__classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").set(account.id, {
|
|
800
821
|
walletId: wallet.id,
|
|
@@ -828,23 +849,6 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
|
|
|
828
849
|
}
|
|
829
850
|
// Default to the default group in case of errors.
|
|
830
851
|
return __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getDefaultAccountGroupId).call(this, wallets);
|
|
831
|
-
}, _AccountTreeController_handleSelectedAccountChange = function _AccountTreeController_handleSelectedAccountChange(account) {
|
|
832
|
-
const accountMapping = __classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").get(account.id);
|
|
833
|
-
if (!accountMapping) {
|
|
834
|
-
// Account not in tree yet, might be during initialization
|
|
835
|
-
return;
|
|
836
|
-
}
|
|
837
|
-
const { groupId } = accountMapping;
|
|
838
|
-
const previousSelectedAccountGroup = this.state.accountTree.selectedAccountGroup;
|
|
839
|
-
// Idempotent check - if the same group is already selected, do nothing
|
|
840
|
-
if (previousSelectedAccountGroup === groupId) {
|
|
841
|
-
return;
|
|
842
|
-
}
|
|
843
|
-
// Update selectedAccountGroup to match the selected account
|
|
844
|
-
this.update((state) => {
|
|
845
|
-
state.accountTree.selectedAccountGroup = groupId;
|
|
846
|
-
});
|
|
847
|
-
this.messagingSystem.publish(`${exports.controllerName}:selectedAccountGroupChange`, groupId, previousSelectedAccountGroup);
|
|
848
852
|
}, _AccountTreeController_handleMultichainAccountWalletStatusChange = function _AccountTreeController_handleMultichainAccountWalletStatusChange(walletId, walletStatus) {
|
|
849
853
|
this.update((state) => {
|
|
850
854
|
const wallet = state.accountTree.wallets[walletId];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccountTreeController.cjs","sourceRoot":"","sources":["../src/AccountTreeController.ts"],"names":[],"mappings":"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","sourcesContent":["import { AccountWalletType, select } from '@metamask/account-api';\nimport type {\n AccountGroupId,\n AccountWalletId,\n AccountSelector,\n MultichainAccountWalletId,\n AccountGroupType,\n} from '@metamask/account-api';\nimport type { MultichainAccountWalletStatus } from '@metamask/account-api';\nimport { type AccountId } from '@metamask/accounts-controller';\nimport type { StateMetadata } from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type { TraceCallback } from '@metamask/controller-utils';\nimport { isEvmAccountType } from '@metamask/keyring-api';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport { assert } from '@metamask/utils';\n\nimport type { BackupAndSyncEmitAnalyticsEventParams } from './backup-and-sync/analytics';\nimport {\n formatAnalyticsEvent,\n traceFallback,\n} from './backup-and-sync/analytics';\nimport { BackupAndSyncService } from './backup-and-sync/service';\nimport type { BackupAndSyncContext } from './backup-and-sync/types';\nimport type { AccountGroupObject, AccountTypeOrderKey } from './group';\nimport {\n ACCOUNT_TYPE_TO_SORT_ORDER,\n isAccountGroupNameUnique,\n isAccountGroupNameUniqueFromWallet,\n MAX_SORT_ORDER,\n} from './group';\nimport type { Rule } from './rule';\nimport { EntropyRule } from './rules/entropy';\nimport { KeyringRule } from './rules/keyring';\nimport { SnapRule } from './rules/snap';\nimport type {\n AccountTreeControllerConfig,\n AccountTreeControllerInternalBackupAndSyncConfig,\n AccountTreeControllerMessenger,\n AccountTreeControllerState,\n} from './types';\nimport { type AccountWalletObject, type AccountWalletObjectOf } from './wallet';\n\nexport const controllerName = 'AccountTreeController';\n\nconst accountTreeControllerMetadata: StateMetadata<AccountTreeControllerState> =\n {\n accountTree: {\n includeInStateLogs: true,\n persist: false, // We do re-recompute this state everytime.\n anonymous: false,\n usedInUi: true,\n },\n isAccountTreeSyncingInProgress: {\n includeInStateLogs: false,\n persist: false,\n anonymous: false,\n usedInUi: true,\n },\n hasAccountTreeSyncingSyncedAtLeastOnce: {\n includeInStateLogs: true,\n persist: true,\n anonymous: false,\n usedInUi: true,\n },\n accountGroupsMetadata: {\n includeInStateLogs: true,\n persist: true,\n anonymous: false,\n usedInUi: true,\n },\n accountWalletsMetadata: {\n includeInStateLogs: true,\n persist: true,\n anonymous: false,\n usedInUi: true,\n },\n };\n\n/**\n * Gets default state of the `AccountTreeController`.\n *\n * @returns The default state of the `AccountTreeController`.\n */\nexport function getDefaultAccountTreeControllerState(): AccountTreeControllerState {\n return {\n accountTree: {\n wallets: {},\n selectedAccountGroup: '',\n },\n isAccountTreeSyncingInProgress: false,\n hasAccountTreeSyncingSyncedAtLeastOnce: false,\n accountGroupsMetadata: {},\n accountWalletsMetadata: {},\n };\n}\n\n/**\n * Context for an account.\n */\nexport type AccountContext = {\n /**\n * Wallet ID associated to that account.\n */\n walletId: AccountWalletObject['id'];\n\n /**\n * Account group ID associated to that account.\n */\n groupId: AccountGroupObject['id'];\n\n /**\n * Sort order of the account.\n */\n sortOrder: (typeof ACCOUNT_TYPE_TO_SORT_ORDER)[AccountTypeOrderKey];\n};\n\nexport class AccountTreeController extends BaseController<\n typeof controllerName,\n AccountTreeControllerState,\n AccountTreeControllerMessenger\n> {\n readonly #accountIdToContext: Map<AccountId, AccountContext>;\n\n readonly #groupIdToWalletId: Map<AccountGroupId, AccountWalletId>;\n\n /**\n * Service responsible for all backup and sync operations.\n */\n readonly #backupAndSyncService: BackupAndSyncService;\n\n readonly #rules: [EntropyRule, SnapRule, KeyringRule];\n\n readonly #trace: TraceCallback;\n\n readonly #backupAndSyncConfig: AccountTreeControllerInternalBackupAndSyncConfig;\n\n #initialized: boolean;\n\n /**\n * Constructor for AccountTreeController.\n *\n * @param options - The controller options.\n * @param options.messenger - The messenger object.\n * @param options.state - Initial state to set on this controller\n * @param options.config - Optional configuration for the controller.\n */\n\n constructor({\n messenger,\n state,\n config,\n }: {\n messenger: AccountTreeControllerMessenger;\n state?: Partial<AccountTreeControllerState>;\n config?: AccountTreeControllerConfig;\n }) {\n super({\n messenger,\n name: controllerName,\n metadata: accountTreeControllerMetadata,\n state: {\n ...getDefaultAccountTreeControllerState(),\n ...state,\n },\n });\n\n // This will be set to true upon the first `init` call.\n this.#initialized = false;\n\n // Reverse map to allow fast node access from an account ID.\n this.#accountIdToContext = new Map();\n\n // Reverse map to allow fast wallet node access from a group ID.\n this.#groupIdToWalletId = new Map();\n\n // Rules to apply to construct the wallets tree.\n this.#rules = [\n // 1. We group by entropy-source\n new EntropyRule(this.messagingSystem),\n // 2. We group by Snap ID\n new SnapRule(this.messagingSystem),\n // 3. We group by wallet type (this rule cannot fail and will group all non-matching accounts)\n new KeyringRule(this.messagingSystem),\n ];\n\n // Initialize trace function\n this.#trace = config?.trace ?? traceFallback;\n\n // Initialize backup and sync config\n this.#backupAndSyncConfig = {\n emitAnalyticsEventFn: (event: BackupAndSyncEmitAnalyticsEventParams) => {\n return (\n config?.backupAndSync?.onBackupAndSyncEvent &&\n config.backupAndSync.onBackupAndSyncEvent(formatAnalyticsEvent(event))\n );\n },\n };\n\n // Initialize the backup and sync service\n this.#backupAndSyncService = new BackupAndSyncService(\n this.#createBackupAndSyncContext(),\n );\n\n this.messagingSystem.subscribe(\n 'AccountsController:accountAdded',\n (account) => {\n this.#handleAccountAdded(account);\n },\n );\n\n this.messagingSystem.subscribe(\n 'AccountsController:accountRemoved',\n (accountId) => {\n this.#handleAccountRemoved(accountId);\n },\n );\n\n this.messagingSystem.subscribe(\n 'AccountsController:selectedAccountChange',\n (account) => {\n this.#handleSelectedAccountChange(account);\n },\n );\n\n this.messagingSystem.subscribe(\n 'UserStorageController:stateChange',\n (userStorageControllerState) => {\n this.#backupAndSyncService.handleUserStorageStateChange(\n userStorageControllerState,\n );\n },\n );\n\n this.messagingSystem.subscribe(\n 'MultichainAccountService:walletStatusChange',\n (walletId, status) => {\n this.#handleMultichainAccountWalletStatusChange(walletId, status);\n },\n );\n\n this.#registerMessageHandlers();\n }\n\n /**\n * Initialize the controller's state.\n *\n * It constructs the initial state of the account tree (tree nodes, nodes\n * names, metadata, etc..) and will automatically update the controller's\n * state with it.\n */\n init() {\n if (this.#initialized) {\n // We prevent re-initilializing the state multiple times. Though, we can use\n // `reinit` to re-init everything from scratch.\n return;\n }\n\n const wallets: AccountTreeControllerState['accountTree']['wallets'] = {};\n\n // Clear mappings for fresh rebuild.\n this.#accountIdToContext.clear();\n this.#groupIdToWalletId.clear();\n\n // Keep the current selected group to check if it's still part of the tree\n // after rebuilding it.\n const previousSelectedAccountGroup =\n this.state.accountTree.selectedAccountGroup;\n\n // For now, we always re-compute all wallets, we do not re-use the existing state.\n for (const account of this.#listAccounts()) {\n this.#insert(wallets, account);\n }\n\n // Once we have the account tree, we can apply persisted metadata (names + UI states).\n let previousSelectedAccountGroupStillExists = false;\n this.update((state) => {\n state.accountTree.wallets = wallets;\n\n // Apply group metadata within the state update\n for (const wallet of Object.values(state.accountTree.wallets)) {\n this.#applyAccountWalletMetadata(state, wallet.id);\n\n // Used for default group default names (so we use human-indexing here).\n let nextNaturalNameIndex = 1;\n for (const group of Object.values(wallet.groups)) {\n this.#applyAccountGroupMetadata(\n state,\n wallet.id,\n group.id,\n // FIXME: We should not need this kind of logic if we were not inserting accounts\n // 1 by 1. Instead, we should be inserting wallets and groups directly. This would\n // allow us to naturally insert a group in the tree AND update its metadata right\n // away...\n // But here, we have to wait for the entire group to be ready before updating\n // its metadata (mainly because we're dealing with single accounts rather than entire\n // groups).\n // That is why we need this kind of extra parameter.\n nextNaturalNameIndex,\n );\n\n if (group.id === previousSelectedAccountGroup) {\n previousSelectedAccountGroupStillExists = true;\n }\n nextNaturalNameIndex += 1;\n }\n }\n\n if (\n !previousSelectedAccountGroupStillExists ||\n previousSelectedAccountGroup === ''\n ) {\n // No group is selected yet OR group no longer exists, re-sync with the\n // AccountsController.\n state.accountTree.selectedAccountGroup =\n this.#getDefaultSelectedAccountGroup(wallets);\n }\n });\n\n // We still compare the previous and new value, the previous one could have been\n // an empty string and `#getDefaultSelectedAccountGroup` could also return an\n // empty string too, thus, we would re-use the same value here again. In that\n // case, no need to fire any event.\n if (\n previousSelectedAccountGroup !==\n this.state.accountTree.selectedAccountGroup\n ) {\n this.messagingSystem.publish(\n `${controllerName}:selectedAccountGroupChange`,\n this.state.accountTree.selectedAccountGroup,\n previousSelectedAccountGroup,\n );\n }\n\n this.#initialized = true;\n }\n\n /**\n * Re-initialize the controller's state.\n *\n * This is done in one single (atomic) `update` block to avoid having a temporary\n * cleared state. Use this when you need to force a full re-init even if already initialized.\n */\n reinit() {\n this.#initialized = false;\n this.init();\n }\n\n /**\n * Rule for entropy-base wallets.\n *\n * @returns The rule for entropy-based wallets.\n */\n #getEntropyRule(): EntropyRule {\n return this.#rules[0];\n }\n\n /**\n * Rule for Snap-base wallets.\n *\n * @returns The rule for snap-based wallets.\n */\n #getSnapRule(): SnapRule {\n return this.#rules[1];\n }\n\n /**\n * Rule for keyring-base wallets.\n *\n * This rule acts as a fallback and never fails since all accounts\n * comes from a keyring anyway.\n *\n * @returns The fallback rule for every accounts that did not match\n * any other rules.\n */\n #getKeyringRule(): KeyringRule {\n return this.#rules[2];\n }\n\n /**\n * Applies wallet metadata updates (name) by checking the persistent state\n * first, and then fallbacks to default values (based on the wallet's\n * type).\n *\n * @param state Controller state to update for persistence.\n * @param walletId The wallet ID to update.\n */\n #applyAccountWalletMetadata(\n state: AccountTreeControllerState,\n walletId: AccountWalletId,\n ) {\n const wallet = state.accountTree.wallets[walletId];\n const persistedMetadata = state.accountWalletsMetadata[walletId];\n\n // Apply persisted name if available (including empty strings)\n if (persistedMetadata?.name !== undefined) {\n wallet.metadata.name = persistedMetadata.name.value;\n } else if (!wallet.metadata.name) {\n // Generate default name if none exists\n if (wallet.type === AccountWalletType.Entropy) {\n wallet.metadata.name =\n this.#getEntropyRule().getDefaultAccountWalletName(wallet);\n } else if (wallet.type === AccountWalletType.Snap) {\n wallet.metadata.name =\n this.#getSnapRule().getDefaultAccountWalletName(wallet);\n } else {\n wallet.metadata.name =\n this.#getKeyringRule().getDefaultAccountWalletName(wallet);\n }\n }\n }\n\n /**\n * Gets the appropriate rule instance for a given wallet type.\n *\n * @param wallet - The wallet object to get the rule for.\n * @returns The rule instance that handles the wallet's type.\n */\n #getRuleForWallet<WalletType extends AccountWalletType>(\n wallet: AccountWalletObjectOf<WalletType>,\n ): Rule<WalletType, AccountGroupType> {\n switch (wallet.type) {\n case AccountWalletType.Entropy:\n return this.#getEntropyRule() as unknown as Rule<\n WalletType,\n AccountGroupType\n >;\n case AccountWalletType.Snap:\n return this.#getSnapRule() as unknown as Rule<\n WalletType,\n AccountGroupType\n >;\n default:\n return this.#getKeyringRule() as unknown as Rule<\n WalletType,\n AccountGroupType\n >;\n }\n }\n\n /**\n * Applies group metadata updates (name, pinned, hidden flags) by checking\n * the persistent state first, and then fallbacks to default values (based\n * on the wallet's\n * type).\n *\n * @param state Controller state to update for persistence.\n * @param walletId The wallet ID containing the group.\n * @param groupId The account group ID to update.\n * @param nextNaturalNameIndex The next natural name index for this group (only used for default names).\n */\n #applyAccountGroupMetadata(\n state: AccountTreeControllerState,\n walletId: AccountWalletId,\n groupId: AccountGroupId,\n nextNaturalNameIndex?: number,\n ) {\n const wallet = state.accountTree.wallets[walletId];\n const group = wallet.groups[groupId];\n const persistedGroupMetadata = state.accountGroupsMetadata[group.id];\n\n // Apply persisted name if available (including empty strings)\n if (persistedGroupMetadata?.name !== undefined) {\n state.accountTree.wallets[walletId].groups[groupId].metadata.name =\n persistedGroupMetadata.name.value;\n } else if (!group.metadata.name) {\n // Get the appropriate rule for this wallet type\n const rule = this.#getRuleForWallet(wallet);\n\n // Get the prefix for groups of this wallet\n const namePrefix = rule.getDefaultAccountGroupPrefix(wallet);\n\n // Skip computed names for now - use default naming with per-wallet logic\n // TODO: Implement computed names in a future iteration\n\n // Parse the highest account index being used (similar to accounts-controller)\n let highestNameIndex = 0;\n for (const { id: otherGroupId } of Object.values(\n wallet.groups,\n ) as AccountGroupObject[]) {\n // Skip the current group being processed\n if (otherGroupId === groupId) {\n continue;\n }\n\n // We always get the name from the persisted map, since `init` will clear the\n // `state.accountTree.wallets`, thus, given empty `group.metadata.name`.\n // NOTE: If the other group has not been named yet, we just use an empty name.\n const otherGroupName =\n state.accountGroupsMetadata[otherGroupId]?.name?.value ?? '';\n\n // Parse the existing group name to extract the numeric index\n const nameMatch = otherGroupName.match(/account\\s+(\\d+)$/iu);\n if (nameMatch) {\n const nameIndex = parseInt(nameMatch[1], 10);\n if (nameIndex > highestNameIndex) {\n highestNameIndex = nameIndex;\n }\n }\n }\n\n // We just use the highest known index no matter the wallet type.\n //\n // For entropy-based wallets (bip44), if a multichain account group with group index 1\n // is inserted before another one with group index 0, then the naming will be:\n // - \"Account 1\" (group index 1)\n // - \"Account 2\" (group index 0)\n // This naming makes more sense for the end-user.\n //\n // For other type of wallets, since those wallets can create arbitrary gaps, we still\n // rely on the highest know index to avoid back-filling account with \"old names\".\n let proposedNameIndex = Math.max(\n // Use + 1 to use the next available index.\n highestNameIndex + 1,\n // In case all accounts have been renamed differently than the usual \"Account <index>\"\n // pattern, we want to use the next \"natural\" index, which is just the number of groups\n // in that wallet (e.g. [\"Account A\", \"Another Account\"], next natural index would be\n // \"Account 3\" in this case).\n nextNaturalNameIndex ?? Object.keys(wallet.groups).length,\n );\n\n // Find a unique name by checking for conflicts and incrementing if needed\n let proposedNameExists: boolean;\n let proposedName = '';\n do {\n proposedName = `${namePrefix} ${proposedNameIndex}`;\n\n // Check if this name already exists in the wallet (excluding current group)\n proposedNameExists = !isAccountGroupNameUniqueFromWallet(\n wallet,\n group.id,\n proposedName,\n );\n\n /* istanbul ignore next */\n if (proposedNameExists) {\n proposedNameIndex += 1; // Try next number\n }\n } while (proposedNameExists);\n\n state.accountTree.wallets[walletId].groups[groupId].metadata.name =\n proposedName;\n\n // Persist the generated name to ensure consistency\n state.accountGroupsMetadata[groupId] ??= {};\n state.accountGroupsMetadata[groupId].name = {\n value: proposedName,\n // The `lastUpdatedAt` field is used for backup and sync, when comparing local names\n // with backed up names. In this case, the generated name should never take precedence\n // over a user-defined name, so we set `lastUpdatedAt` to 0.\n lastUpdatedAt: 0,\n };\n }\n\n // Apply persisted UI states\n if (persistedGroupMetadata?.pinned?.value !== undefined) {\n group.metadata.pinned = persistedGroupMetadata.pinned.value;\n }\n if (persistedGroupMetadata?.hidden?.value !== undefined) {\n group.metadata.hidden = persistedGroupMetadata.hidden.value;\n }\n }\n\n /**\n * Gets the account wallet object from its ID.\n *\n * @param walletId - Account wallet ID.\n * @returns The account wallet object if found, undefined otherwise.\n */\n getAccountWalletObject(\n walletId: AccountWalletId,\n ): AccountWalletObject | undefined {\n const wallet = this.state.accountTree.wallets[walletId];\n if (!wallet) {\n return undefined;\n }\n\n return wallet;\n }\n\n /**\n * Gets all account wallet objects.\n *\n * @returns All account wallet objects.\n */\n getAccountWalletObjects(): AccountWalletObject[] {\n return Object.values(this.state.accountTree.wallets);\n }\n\n /**\n * Gets all underlying accounts from the currently selected account\n * group.\n *\n * It also support account selector, which allows to filter specific\n * accounts given some criterias (account type, address, scopes, etc...).\n *\n * @param selector - Optional account selector.\n * @returns Underlying accounts for the currently selected account (filtered\n * by the selector if provided).\n */\n getAccountsFromSelectedAccountGroup(\n selector?: AccountSelector<InternalAccount>,\n ) {\n const groupId = this.getSelectedAccountGroup();\n if (!groupId) {\n return [];\n }\n\n const group = this.getAccountGroupObject(groupId);\n // We should never reach this part, so we cannot cover it either.\n /* istanbul ignore next */\n if (!group) {\n return [];\n }\n\n const accounts: InternalAccount[] = [];\n for (const id of group.accounts) {\n const account = this.messagingSystem.call(\n 'AccountsController:getAccount',\n id,\n );\n\n // For now, we're filtering undefined account, but I believe\n // throwing would be more appropriate here.\n if (account) {\n accounts.push(account);\n }\n }\n\n return selector ? select(accounts, selector) : accounts;\n }\n\n /**\n * Gets the account group object from its ID.\n *\n * @param groupId - Account group ID.\n * @returns The account group object if found, undefined otherwise.\n */\n getAccountGroupObject(\n groupId: AccountGroupId,\n ): AccountGroupObject | undefined {\n const walletId = this.#groupIdToWalletId.get(groupId);\n if (!walletId) {\n return undefined;\n }\n\n const wallet = this.getAccountWalletObject(walletId);\n return wallet?.groups[groupId];\n }\n\n /**\n * Handles \"AccountsController:accountAdded\" event to insert\n * new accounts into the tree.\n *\n * @param account - New account.\n */\n #handleAccountAdded(account: InternalAccount) {\n // We wait for the first `init` to be called to actually build up the tree and\n // mutate it. We expect the caller to first update the `AccountsController` state\n // to force the migration of accounts, and then call `init`.\n if (!this.#initialized) {\n return;\n }\n\n // Check if this account got already added by `#initAtLeastOnce`, if not, then we\n // can proceed.\n if (!this.#accountIdToContext.has(account.id)) {\n this.update((state) => {\n this.#insert(state.accountTree.wallets, account);\n\n const context = this.#accountIdToContext.get(account.id);\n if (context) {\n const { walletId, groupId } = context;\n\n const wallet = state.accountTree.wallets[walletId];\n if (wallet) {\n this.#applyAccountWalletMetadata(state, walletId);\n this.#applyAccountGroupMetadata(state, walletId, groupId);\n }\n }\n });\n\n this.messagingSystem.publish(\n `${controllerName}:accountTreeChange`,\n this.state.accountTree,\n );\n }\n }\n\n /**\n * Handles \"AccountsController:accountRemoved\" event to remove\n * given account from the tree.\n *\n * @param accountId - Removed account ID.\n */\n #handleAccountRemoved(accountId: AccountId) {\n // We wait for the first `init` to be called to actually build up the tree and\n // mutate it. We expect the caller to first update the `AccountsController` state\n // to force the migration of accounts, and then call `init`.\n if (!this.#initialized) {\n return;\n }\n\n const context = this.#accountIdToContext.get(accountId);\n\n if (context) {\n const { walletId, groupId } = context;\n\n const previousSelectedAccountGroup =\n this.state.accountTree.selectedAccountGroup;\n let selectedAccountGroupChanged = false;\n\n this.update((state) => {\n const accounts =\n state.accountTree.wallets[walletId]?.groups[groupId]?.accounts;\n\n if (accounts) {\n const index = accounts.indexOf(accountId);\n if (index !== -1) {\n accounts.splice(index, 1);\n\n // Check if we need to update selectedAccountGroup after removal\n if (\n state.accountTree.selectedAccountGroup === groupId &&\n accounts.length === 0\n ) {\n // The currently selected group is now empty, find a new group to select\n const newSelectedAccountGroup = this.#getDefaultAccountGroupId(\n state.accountTree.wallets,\n );\n state.accountTree.selectedAccountGroup = newSelectedAccountGroup;\n selectedAccountGroupChanged =\n newSelectedAccountGroup !== previousSelectedAccountGroup;\n }\n }\n if (accounts.length === 0) {\n this.#pruneEmptyGroupAndWallet(state, walletId, groupId);\n }\n }\n });\n this.messagingSystem.publish(\n `${controllerName}:accountTreeChange`,\n this.state.accountTree,\n );\n\n // Emit selectedAccountGroupChange event if the selected group changed\n if (selectedAccountGroupChanged) {\n this.messagingSystem.publish(\n `${controllerName}:selectedAccountGroupChange`,\n this.state.accountTree.selectedAccountGroup,\n previousSelectedAccountGroup,\n );\n }\n\n // Clear reverse-mapping for that account.\n this.#accountIdToContext.delete(accountId);\n }\n }\n\n /**\n * Helper method to prune a group if it holds no accounts and additionally\n * prune the wallet if it holds no groups. This action should take place\n * after a singular account removal.\n *\n * NOTE: This method should only be used for a group that we know to be empty.\n *\n * @param state - The AccountTreeController state to prune.\n * @param walletId - The wallet ID to prune, the wallet should be the parent of the associated group that holds the removed account.\n * @param groupId - The group ID to prune, the group should be the parent of the associated account that was removed.\n * @returns The updated state.\n */\n #pruneEmptyGroupAndWallet(\n state: AccountTreeControllerState,\n walletId: AccountWalletId,\n groupId: AccountGroupId,\n ) {\n const { wallets } = state.accountTree;\n\n delete wallets[walletId].groups[groupId];\n this.#groupIdToWalletId.delete(groupId);\n\n // Clean up metadata for the pruned group\n delete state.accountGroupsMetadata[groupId];\n\n if (Object.keys(wallets[walletId].groups).length === 0) {\n delete wallets[walletId];\n }\n return state;\n }\n\n /**\n * Insert an account inside an account tree.\n *\n * We go over multiple rules to try to \"match\" the account following\n * specific criterias. If a rule \"matches\" an account, then this\n * account get added into its proper account wallet and account group.\n *\n * @param wallets - Account tree.\n * @param account - The account to be inserted.\n */\n #insert(\n wallets: AccountTreeControllerState['accountTree']['wallets'],\n account: InternalAccount,\n ) {\n const result =\n this.#getEntropyRule().match(account) ??\n this.#getSnapRule().match(account) ??\n this.#getKeyringRule().match(account); // This one cannot fail.\n\n // Update controller's state.\n const walletId = result.wallet.id;\n let wallet = wallets[walletId];\n if (!wallet) {\n wallets[walletId] = {\n ...result.wallet,\n status: 'ready',\n groups: {},\n metadata: {\n name: '', // Will get updated later.\n ...result.wallet.metadata,\n },\n // We do need to type-cast since we're not narrowing `result` with\n // the union tag `result.wallet.type`.\n } as AccountWalletObject;\n wallet = wallets[walletId];\n\n // Trigger atomic sync for new wallet (only for entropy wallets)\n if (wallet.type === AccountWalletType.Entropy) {\n this.#backupAndSyncService.enqueueSingleWalletSync(walletId);\n }\n }\n\n const groupId = result.group.id;\n let group = wallet.groups[groupId];\n const { type, id } = account;\n const sortOrder = ACCOUNT_TYPE_TO_SORT_ORDER[type];\n\n if (!group) {\n wallet.groups[groupId] = {\n ...result.group,\n // Type-wise, we are guaranteed to always have at least 1 account.\n accounts: [id],\n metadata: {\n name: '',\n ...{ pinned: false, hidden: false }, // Default UI states\n ...result.group.metadata, // Allow rules to override defaults\n },\n // We do need to type-cast since we're not narrowing `result` with\n // the union tag `result.group.type`.\n } as AccountGroupObject;\n group = wallet.groups[groupId];\n\n // Map group ID to its containing wallet ID for efficient direct access\n this.#groupIdToWalletId.set(groupId, walletId);\n\n // Trigger atomic sync for new group (only for entropy wallets)\n if (wallet.type === AccountWalletType.Entropy) {\n this.#backupAndSyncService.enqueueSingleGroupSync(groupId);\n }\n } else {\n group.accounts.push(id);\n // We need to do this at every insertion because race conditions can happen\n // during the account creation process where one provider completes before the other.\n // The discovery process in the service can also lead to some accounts being created \"out of order\".\n const { accounts } = group;\n accounts.sort(\n /* istanbul ignore next: Comparator branch execution (a===id vs b===id)\n * and return attribution vary across engines; final ordering is covered\n * by behavior tests. Ignoring the entire comparator avoids flaky line\n * coverage without reducing scenario coverage.\n */\n (a, b) => {\n const aSortOrder =\n a === id ? sortOrder : this.#accountIdToContext.get(a)?.sortOrder;\n const bSortOrder =\n b === id ? sortOrder : this.#accountIdToContext.get(b)?.sortOrder;\n return (\n (aSortOrder ?? MAX_SORT_ORDER) - (bSortOrder ?? MAX_SORT_ORDER)\n );\n },\n );\n }\n\n // Update the reverse mapping for this account.\n this.#accountIdToContext.set(account.id, {\n walletId: wallet.id,\n groupId: group.id,\n sortOrder,\n });\n }\n\n /**\n * List all internal accounts.\n *\n * @returns The list of all internal accounts.\n */\n #listAccounts(): InternalAccount[] {\n return this.messagingSystem.call(\n 'AccountsController:listMultichainAccounts',\n );\n }\n\n /**\n * Asserts that a group exists in the current account tree.\n *\n * @param groupId - The account group ID to validate.\n * @throws Error if the group does not exist.\n */\n #assertAccountGroupExists(groupId: AccountGroupId): void {\n const exists = this.#groupIdToWalletId.has(groupId);\n if (!exists) {\n throw new Error(`Account group with ID \"${groupId}\" not found in tree`);\n }\n }\n\n /**\n * Asserts that a wallet exists in the current account tree.\n *\n * @param walletId - The account wallet ID to validate.\n * @throws Error if the wallet does not exist.\n */\n #assertAccountWalletExists(walletId: AccountWalletId): void {\n const exists = Boolean(this.state.accountTree.wallets[walletId]);\n if (!exists) {\n throw new Error(`Account wallet with ID \"${walletId}\" not found in tree`);\n }\n }\n\n /**\n * Asserts that an account group name is unique within the same wallet.\n *\n * @param groupId - The account group ID to exclude from the check.\n * @param name - The name to validate for uniqueness.\n * @throws Error if the name already exists in another group within the same wallet.\n */\n #assertAccountGroupNameIsUnique(groupId: AccountGroupId, name: string): void {\n if (!isAccountGroupNameUnique(this.state, groupId, name)) {\n throw new Error('Account group name already exists');\n }\n }\n\n /**\n * Gets the currently selected account group ID.\n *\n * @returns The selected account group ID or empty string if none selected.\n */\n getSelectedAccountGroup(): AccountGroupId | '' {\n return this.state.accountTree.selectedAccountGroup;\n }\n\n /**\n * Sets the selected account group and updates the AccountsController selectedAccount accordingly.\n *\n * @param groupId - The account group ID to select.\n */\n setSelectedAccountGroup(groupId: AccountGroupId): void {\n const previousSelectedAccountGroup =\n this.state.accountTree.selectedAccountGroup;\n\n // Idempotent check - if the same group is already selected, do nothing\n if (previousSelectedAccountGroup === groupId) {\n return;\n }\n\n // Find the first account in this group to select\n const accountToSelect = this.#getDefaultAccountFromAccountGroupId(groupId);\n if (!accountToSelect) {\n throw new Error(`No accounts found in group: ${groupId}`);\n }\n\n // Update our state first\n this.update((state) => {\n state.accountTree.selectedAccountGroup = groupId;\n });\n this.messagingSystem.publish(\n `${controllerName}:selectedAccountGroupChange`,\n groupId,\n previousSelectedAccountGroup,\n );\n\n // Update AccountsController - this will trigger selectedAccountChange event,\n // but our handler is idempotent so it won't cause infinite loop\n this.messagingSystem.call(\n 'AccountsController:setSelectedAccount',\n accountToSelect,\n );\n }\n\n /**\n * Initializes the selectedAccountGroup based on the currently selected account from AccountsController.\n *\n * @param wallets - Wallets object to use for fallback logic\n * @returns The default selected account group ID or empty string if none selected.\n */\n #getDefaultSelectedAccountGroup(wallets: {\n [walletId: AccountWalletId]: AccountWalletObject;\n }): AccountGroupId | '' {\n const selectedAccount = this.messagingSystem.call(\n 'AccountsController:getSelectedMultichainAccount',\n );\n if (selectedAccount && selectedAccount.id) {\n const accountMapping = this.#accountIdToContext.get(selectedAccount.id);\n if (accountMapping) {\n const { groupId } = accountMapping;\n\n return groupId;\n }\n }\n\n // Default to the default group in case of errors.\n return this.#getDefaultAccountGroupId(wallets);\n }\n\n /**\n * Handles selected account change from AccountsController.\n * Updates selectedAccountGroup to match the selected account.\n *\n * @param account - The newly selected account.\n */\n #handleSelectedAccountChange(account: InternalAccount): void {\n const accountMapping = this.#accountIdToContext.get(account.id);\n if (!accountMapping) {\n // Account not in tree yet, might be during initialization\n return;\n }\n\n const { groupId } = accountMapping;\n const previousSelectedAccountGroup =\n this.state.accountTree.selectedAccountGroup;\n\n // Idempotent check - if the same group is already selected, do nothing\n if (previousSelectedAccountGroup === groupId) {\n return;\n }\n\n // Update selectedAccountGroup to match the selected account\n this.update((state) => {\n state.accountTree.selectedAccountGroup = groupId;\n });\n this.messagingSystem.publish(\n `${controllerName}:selectedAccountGroupChange`,\n groupId,\n previousSelectedAccountGroup,\n );\n }\n\n /**\n * Handles multichain account wallet status change from\n * the MultichainAccountService.\n *\n * @param walletId - Multichain account wallet ID.\n * @param walletStatus - New multichain account wallet status.\n */\n #handleMultichainAccountWalletStatusChange(\n walletId: MultichainAccountWalletId,\n walletStatus: MultichainAccountWalletStatus,\n ): void {\n this.update((state) => {\n const wallet = state.accountTree.wallets[walletId];\n\n if (wallet) {\n wallet.status = walletStatus;\n }\n });\n }\n\n /**\n * Gets account group object.\n *\n * @param groupId - The account group ID.\n * @returns The account group or undefined if not found.\n */\n #getAccountGroup(groupId: AccountGroupId): AccountGroupObject | undefined {\n const found = Object.values(this.state.accountTree.wallets).find(\n (wallet) => wallet.groups[groupId] !== undefined,\n );\n\n return found?.groups[groupId];\n }\n\n /**\n * Gets the default account for specified group.\n *\n * @param groupId - The account group ID.\n * @returns The first account ID in the group, or undefined if no accounts found.\n */\n #getDefaultAccountFromAccountGroupId(\n groupId: AccountGroupId,\n ): AccountId | undefined {\n const group = this.#getAccountGroup(groupId);\n\n if (group) {\n let candidate;\n for (const id of group.accounts) {\n const account = this.messagingSystem.call(\n 'AccountsController:getAccount',\n id,\n );\n\n if (!candidate) {\n candidate = id;\n }\n if (account && isEvmAccountType(account.type)) {\n // EVM accounts have a higher priority, so if we find any, we just\n // use that account!\n return account.id;\n }\n }\n\n return candidate;\n }\n\n return undefined;\n }\n\n /**\n * Gets the default group id, which is either, the first non-empty group that contains an EVM account or\n * just the first non-empty group with any accounts.\n *\n * @param wallets - The wallets object to search.\n * @returns The ID of the first non-empty group, or an empty string if no groups are found.\n */\n #getDefaultAccountGroupId(wallets: {\n [walletId: AccountWalletId]: AccountWalletObject;\n }): AccountGroupId | '' {\n let candidate: AccountGroupId | '' = '';\n\n for (const wallet of Object.values(wallets)) {\n for (const group of Object.values(wallet.groups)) {\n // We only update the candidate with the first non-empty group, but still\n // try to find a group that contains an EVM account (the `candidate` is\n // our fallback).\n if (candidate === '' && group.accounts.length > 0) {\n candidate = group.id;\n }\n\n for (const id of group.accounts) {\n const account = this.messagingSystem.call(\n 'AccountsController:getAccount',\n id,\n );\n\n if (account && isEvmAccountType(account.type)) {\n // EVM accounts have a higher priority, so if we find any, we just\n // use that group!\n return group.id;\n }\n }\n }\n }\n return candidate;\n }\n\n /**\n * Resolves name conflicts by adding a suffix to make the name unique.\n *\n * @internal\n * @param wallet - The wallet to check within.\n * @param groupId - The account group ID to exclude from the check.\n * @param name - The desired name that has a conflict.\n * @returns A unique name with suffix added if necessary.\n */\n resolveNameConflict(\n wallet: AccountWalletObject,\n groupId: AccountGroupId,\n name: string,\n ): string {\n let suffix = 2;\n let candidateName = `${name} (${suffix})`;\n\n // Keep incrementing suffix until we find a unique name\n while (\n !isAccountGroupNameUniqueFromWallet(wallet, groupId, candidateName)\n ) {\n suffix += 1;\n candidateName = `${name} (${suffix})`;\n }\n\n return candidateName;\n }\n\n /**\n * Sets a custom name for an account group.\n *\n * @param groupId - The account group ID.\n * @param name - The custom name to set.\n * @param autoHandleConflict - If true, automatically resolves name conflicts by adding a suffix. If false, throws on conflicts.\n * @throws If the account group ID is not found in the current tree.\n * @throws If the account group name already exists and autoHandleConflict is false.\n */\n setAccountGroupName(\n groupId: AccountGroupId,\n name: string,\n autoHandleConflict: boolean = false,\n ): void {\n // Validate that the group exists in the current tree\n this.#assertAccountGroupExists(groupId);\n\n const walletId = this.#groupIdToWalletId.get(groupId);\n assert(walletId, `Account group with ID \"${groupId}\" not found in tree`);\n\n const wallet = this.state.accountTree.wallets[walletId];\n let finalName = name;\n\n // Handle name conflicts based on the autoHandleConflict flag\n if (\n autoHandleConflict &&\n !isAccountGroupNameUniqueFromWallet(wallet, groupId, name)\n ) {\n finalName = this.resolveNameConflict(wallet, groupId, name);\n } else {\n // Validate that the name is unique\n this.#assertAccountGroupNameIsUnique(groupId, finalName);\n }\n\n this.update((state) => {\n /* istanbul ignore next */\n if (!state.accountGroupsMetadata[groupId]) {\n state.accountGroupsMetadata[groupId] = {};\n }\n\n // Update persistent metadata\n state.accountGroupsMetadata[groupId].name = {\n value: finalName,\n lastUpdatedAt: Date.now(),\n };\n\n // Update tree node directly using efficient mapping\n state.accountTree.wallets[walletId].groups[groupId].metadata.name =\n finalName;\n });\n\n // Trigger atomic sync for group rename (only for groups from entropy wallets)\n if (wallet.type === AccountWalletType.Entropy) {\n this.#backupAndSyncService.enqueueSingleGroupSync(groupId);\n }\n }\n\n /**\n * Sets a custom name for an account wallet.\n *\n * @param walletId - The account wallet ID.\n * @param name - The custom name to set.\n * @throws If the account wallet ID is not found in the current tree.\n */\n setAccountWalletName(walletId: AccountWalletId, name: string): void {\n // Validate that the wallet exists in the current tree\n this.#assertAccountWalletExists(walletId);\n\n this.update((state) => {\n // Update persistent metadata\n state.accountWalletsMetadata[walletId] ??= {};\n state.accountWalletsMetadata[walletId].name = {\n value: name,\n lastUpdatedAt: Date.now(),\n };\n\n // Update tree node directly\n state.accountTree.wallets[walletId].metadata.name = name;\n });\n\n // Trigger atomic sync for wallet rename (only for groups from entropy wallets)\n if (\n this.state.accountTree.wallets[walletId].type ===\n AccountWalletType.Entropy\n ) {\n this.#backupAndSyncService.enqueueSingleWalletSync(walletId);\n }\n }\n\n /**\n * Toggles the pinned state of an account group.\n *\n * @param groupId - The account group ID.\n * @param pinned - Whether the group should be pinned.\n * @throws If the account group ID is not found in the current tree.\n */\n setAccountGroupPinned(groupId: AccountGroupId, pinned: boolean): void {\n // Validate that the group exists in the current tree\n this.#assertAccountGroupExists(groupId);\n\n const walletId = this.#groupIdToWalletId.get(groupId);\n\n this.update((state) => {\n /* istanbul ignore next */\n if (!state.accountGroupsMetadata[groupId]) {\n state.accountGroupsMetadata[groupId] = {};\n }\n\n // Update persistent metadata\n state.accountGroupsMetadata[groupId].pinned = {\n value: pinned,\n lastUpdatedAt: Date.now(),\n };\n\n // Update tree node directly using efficient mapping\n if (walletId) {\n state.accountTree.wallets[walletId].groups[groupId].metadata.pinned =\n pinned;\n }\n });\n\n // Trigger atomic sync for group pinning (only for groups from entropy wallets)\n if (\n walletId &&\n this.state.accountTree.wallets[walletId].type ===\n AccountWalletType.Entropy\n ) {\n this.#backupAndSyncService.enqueueSingleGroupSync(groupId);\n }\n }\n\n /**\n * Toggles the hidden state of an account group.\n *\n * @param groupId - The account group ID.\n * @param hidden - Whether the group should be hidden.\n * @throws If the account group ID is not found in the current tree.\n */\n setAccountGroupHidden(groupId: AccountGroupId, hidden: boolean): void {\n // Validate that the group exists in the current tree\n this.#assertAccountGroupExists(groupId);\n\n const walletId = this.#groupIdToWalletId.get(groupId);\n\n this.update((state) => {\n /* istanbul ignore next */\n if (!state.accountGroupsMetadata[groupId]) {\n state.accountGroupsMetadata[groupId] = {};\n }\n\n // Update persistent metadata\n state.accountGroupsMetadata[groupId].hidden = {\n value: hidden,\n lastUpdatedAt: Date.now(),\n };\n\n // Update tree node directly using efficient mapping\n if (walletId) {\n state.accountTree.wallets[walletId].groups[groupId].metadata.hidden =\n hidden;\n }\n });\n\n // Trigger atomic sync for group hiding (only for groups from entropy wallets)\n if (\n walletId &&\n this.state.accountTree.wallets[walletId].type ===\n AccountWalletType.Entropy\n ) {\n this.#backupAndSyncService.enqueueSingleGroupSync(groupId);\n }\n }\n\n /**\n * Clears the controller state and resets to default values.\n * Also clears the backup and sync service state.\n */\n clearState(): void {\n this.update(() => {\n return {\n ...getDefaultAccountTreeControllerState(),\n };\n });\n this.#backupAndSyncService.clearState();\n\n // So we know we have to call `init` again.\n this.#initialized = false;\n }\n\n /**\n * Registers message handlers for the AccountTreeController.\n */\n #registerMessageHandlers(): void {\n this.messagingSystem.registerActionHandler(\n `${controllerName}:getSelectedAccountGroup`,\n this.getSelectedAccountGroup.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:setSelectedAccountGroup`,\n this.setSelectedAccountGroup.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:getAccountsFromSelectedAccountGroup`,\n this.getAccountsFromSelectedAccountGroup.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:setAccountWalletName`,\n this.setAccountWalletName.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:setAccountGroupName`,\n this.setAccountGroupName.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:setAccountGroupPinned`,\n this.setAccountGroupPinned.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:setAccountGroupHidden`,\n this.setAccountGroupHidden.bind(this),\n );\n }\n\n /**\n * Bi-directionally syncs the account tree with user storage.\n * This will perform a full sync, including both pulling updates\n * from user storage and pushing local changes to user storage.\n * This also performs legacy account syncing if needed.\n *\n * IMPORTANT:\n * If a full sync is already in progress, it will return the ongoing promise.\n *\n * @returns A promise that resolves when the sync is complete.\n */\n async syncWithUserStorage(): Promise<void> {\n return this.#backupAndSyncService.performFullSync();\n }\n\n /**\n * Bi-directionally syncs the account tree with user storage.\n * This will ensure at least one full sync is ran, including both pulling updates\n * from user storage and pushing local changes to user storage.\n * This also performs legacy account syncing if needed.\n *\n * IMPORTANT:\n * If the first ever full sync is already in progress, it will return the ongoing promise.\n * If the first ever full sync was previously completed, it will NOT start a new sync, and will resolve immediately.\n *\n * @returns A promise that resolves when the first ever full sync is complete.\n */\n async syncWithUserStorageAtLeastOnce(): Promise<void> {\n return this.#backupAndSyncService.performFullSyncAtLeastOnce();\n }\n\n /**\n * Creates an backup and sync context for sync operations.\n * Used by the backup and sync service.\n *\n * @returns The backup and sync context.\n */\n #createBackupAndSyncContext(): BackupAndSyncContext {\n return {\n ...this.#backupAndSyncConfig,\n controller: this,\n messenger: this.messagingSystem,\n controllerStateUpdateFn: this.update.bind(this),\n traceFn: this.#trace.bind(this),\n groupIdToWalletId: this.#groupIdToWalletId,\n };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"AccountTreeController.cjs","sourceRoot":"","sources":["../src/AccountTreeController.ts"],"names":[],"mappings":"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","sourcesContent":["import { AccountWalletType, select } from '@metamask/account-api';\nimport type {\n AccountGroupId,\n AccountWalletId,\n AccountSelector,\n MultichainAccountWalletId,\n AccountGroupType,\n} from '@metamask/account-api';\nimport type { MultichainAccountWalletStatus } from '@metamask/account-api';\nimport { type AccountId } from '@metamask/accounts-controller';\nimport type { StateMetadata } from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type { TraceCallback } from '@metamask/controller-utils';\nimport { isEvmAccountType } from '@metamask/keyring-api';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport { assert } from '@metamask/utils';\n\nimport type { BackupAndSyncEmitAnalyticsEventParams } from './backup-and-sync/analytics';\nimport {\n formatAnalyticsEvent,\n traceFallback,\n} from './backup-and-sync/analytics';\nimport { BackupAndSyncService } from './backup-and-sync/service';\nimport type { BackupAndSyncContext } from './backup-and-sync/types';\nimport type { AccountGroupObject, AccountTypeOrderKey } from './group';\nimport {\n ACCOUNT_TYPE_TO_SORT_ORDER,\n isAccountGroupNameUnique,\n isAccountGroupNameUniqueFromWallet,\n MAX_SORT_ORDER,\n} from './group';\nimport { projectLogger as log } from './logger';\nimport type { Rule } from './rule';\nimport { EntropyRule } from './rules/entropy';\nimport { KeyringRule } from './rules/keyring';\nimport { SnapRule } from './rules/snap';\nimport type {\n AccountTreeControllerConfig,\n AccountTreeControllerInternalBackupAndSyncConfig,\n AccountTreeControllerMessenger,\n AccountTreeControllerState,\n} from './types';\nimport { type AccountWalletObject, type AccountWalletObjectOf } from './wallet';\n\nexport const controllerName = 'AccountTreeController';\n\nconst accountTreeControllerMetadata: StateMetadata<AccountTreeControllerState> =\n {\n accountTree: {\n includeInStateLogs: true,\n persist: false, // We do re-recompute this state everytime.\n anonymous: false,\n usedInUi: true,\n },\n isAccountTreeSyncingInProgress: {\n includeInStateLogs: false,\n persist: false,\n anonymous: false,\n usedInUi: true,\n },\n hasAccountTreeSyncingSyncedAtLeastOnce: {\n includeInStateLogs: true,\n persist: true,\n anonymous: false,\n usedInUi: true,\n },\n accountGroupsMetadata: {\n includeInStateLogs: true,\n persist: true,\n anonymous: false,\n usedInUi: true,\n },\n accountWalletsMetadata: {\n includeInStateLogs: true,\n persist: true,\n anonymous: false,\n usedInUi: true,\n },\n };\n\n/**\n * Gets default state of the `AccountTreeController`.\n *\n * @returns The default state of the `AccountTreeController`.\n */\nexport function getDefaultAccountTreeControllerState(): AccountTreeControllerState {\n return {\n accountTree: {\n wallets: {},\n selectedAccountGroup: '',\n },\n isAccountTreeSyncingInProgress: false,\n hasAccountTreeSyncingSyncedAtLeastOnce: false,\n accountGroupsMetadata: {},\n accountWalletsMetadata: {},\n };\n}\n\n/**\n * Context for an account.\n */\nexport type AccountContext = {\n /**\n * Wallet ID associated to that account.\n */\n walletId: AccountWalletObject['id'];\n\n /**\n * Account group ID associated to that account.\n */\n groupId: AccountGroupObject['id'];\n\n /**\n * Sort order of the account.\n */\n sortOrder: (typeof ACCOUNT_TYPE_TO_SORT_ORDER)[AccountTypeOrderKey];\n};\n\nexport class AccountTreeController extends BaseController<\n typeof controllerName,\n AccountTreeControllerState,\n AccountTreeControllerMessenger\n> {\n readonly #accountIdToContext: Map<AccountId, AccountContext>;\n\n readonly #groupIdToWalletId: Map<AccountGroupId, AccountWalletId>;\n\n /**\n * Service responsible for all backup and sync operations.\n */\n readonly #backupAndSyncService: BackupAndSyncService;\n\n readonly #rules: [EntropyRule, SnapRule, KeyringRule];\n\n readonly #trace: TraceCallback;\n\n readonly #backupAndSyncConfig: AccountTreeControllerInternalBackupAndSyncConfig;\n\n #initialized: boolean;\n\n /**\n * Constructor for AccountTreeController.\n *\n * @param options - The controller options.\n * @param options.messenger - The messenger object.\n * @param options.state - Initial state to set on this controller\n * @param options.config - Optional configuration for the controller.\n */\n\n constructor({\n messenger,\n state,\n config,\n }: {\n messenger: AccountTreeControllerMessenger;\n state?: Partial<AccountTreeControllerState>;\n config?: AccountTreeControllerConfig;\n }) {\n super({\n messenger,\n name: controllerName,\n metadata: accountTreeControllerMetadata,\n state: {\n ...getDefaultAccountTreeControllerState(),\n ...state,\n },\n });\n\n // This will be set to true upon the first `init` call.\n this.#initialized = false;\n\n // Reverse map to allow fast node access from an account ID.\n this.#accountIdToContext = new Map();\n\n // Reverse map to allow fast wallet node access from a group ID.\n this.#groupIdToWalletId = new Map();\n\n // Rules to apply to construct the wallets tree.\n this.#rules = [\n // 1. We group by entropy-source\n new EntropyRule(this.messagingSystem),\n // 2. We group by Snap ID\n new SnapRule(this.messagingSystem),\n // 3. We group by wallet type (this rule cannot fail and will group all non-matching accounts)\n new KeyringRule(this.messagingSystem),\n ];\n\n // Initialize trace function\n this.#trace = config?.trace ?? traceFallback;\n\n // Initialize backup and sync config\n this.#backupAndSyncConfig = {\n emitAnalyticsEventFn: (event: BackupAndSyncEmitAnalyticsEventParams) => {\n return (\n config?.backupAndSync?.onBackupAndSyncEvent &&\n config.backupAndSync.onBackupAndSyncEvent(formatAnalyticsEvent(event))\n );\n },\n };\n\n // Initialize the backup and sync service\n this.#backupAndSyncService = new BackupAndSyncService(\n this.#createBackupAndSyncContext(),\n );\n\n this.messagingSystem.subscribe(\n 'AccountsController:accountAdded',\n (account) => {\n this.#handleAccountAdded(account);\n },\n );\n\n this.messagingSystem.subscribe(\n 'AccountsController:accountRemoved',\n (accountId) => {\n this.#handleAccountRemoved(accountId);\n },\n );\n\n this.messagingSystem.subscribe(\n 'UserStorageController:stateChange',\n (userStorageControllerState) => {\n this.#backupAndSyncService.handleUserStorageStateChange(\n userStorageControllerState,\n );\n },\n );\n\n this.messagingSystem.subscribe(\n 'MultichainAccountService:walletStatusChange',\n (walletId, status) => {\n this.#handleMultichainAccountWalletStatusChange(walletId, status);\n },\n );\n\n this.#registerMessageHandlers();\n }\n\n /**\n * Initialize the controller's state.\n *\n * It constructs the initial state of the account tree (tree nodes, nodes\n * names, metadata, etc..) and will automatically update the controller's\n * state with it.\n */\n init() {\n if (this.#initialized) {\n // We prevent re-initilializing the state multiple times. Though, we can use\n // `reinit` to re-init everything from scratch.\n return;\n }\n\n log('Initializing...');\n\n const wallets: AccountTreeControllerState['accountTree']['wallets'] = {};\n\n // Clear mappings for fresh rebuild.\n this.#accountIdToContext.clear();\n this.#groupIdToWalletId.clear();\n\n // Keep the current selected group to check if it's still part of the tree\n // after rebuilding it.\n const previousSelectedAccountGroup =\n this.state.accountTree.selectedAccountGroup;\n\n // There's no guarantee that accounts would be sorted by their import time\n // with `listMultichainAccounts`. We have to sort them here before constructing\n // the tree.\n //\n // Because of the alignment mecanism, some accounts from the same group might not\n // have been imported at the same time, but at least of them should have been\n // imported at the right time, thus, inserting the group at the proper place too.\n //\n // Lastly, if one day we allow to have \"gaps\" in between groups, then this `sort`\n // won't be enough and we would have to use group properties instead (like group\n // index or maybe introduce a `importTime` at group level).\n const accounts = this.#listAccounts().sort(\n (a, b) => a.metadata.importTime - b.metadata.importTime,\n );\n\n // For now, we always re-compute all wallets, we do not re-use the existing state.\n for (const account of accounts) {\n this.#insert(wallets, account);\n }\n\n // Once we have the account tree, we can apply persisted metadata (names + UI states).\n let previousSelectedAccountGroupStillExists = false;\n this.update((state) => {\n state.accountTree.wallets = wallets;\n\n // Apply group metadata within the state update\n for (const wallet of Object.values(state.accountTree.wallets)) {\n this.#applyAccountWalletMetadata(state, wallet.id);\n\n // Used for default group default names (so we use human-indexing here).\n let nextNaturalNameIndex = 1;\n for (const group of Object.values(wallet.groups)) {\n this.#applyAccountGroupMetadata(\n state,\n wallet.id,\n group.id,\n // FIXME: We should not need this kind of logic if we were not inserting accounts\n // 1 by 1. Instead, we should be inserting wallets and groups directly. This would\n // allow us to naturally insert a group in the tree AND update its metadata right\n // away...\n // But here, we have to wait for the entire group to be ready before updating\n // its metadata (mainly because we're dealing with single accounts rather than entire\n // groups).\n // That is why we need this kind of extra parameter.\n nextNaturalNameIndex,\n );\n\n if (group.id === previousSelectedAccountGroup) {\n previousSelectedAccountGroupStillExists = true;\n }\n nextNaturalNameIndex += 1;\n }\n }\n\n if (\n !previousSelectedAccountGroupStillExists ||\n previousSelectedAccountGroup === ''\n ) {\n // No group is selected yet OR group no longer exists, re-sync with the\n // AccountsController.\n state.accountTree.selectedAccountGroup =\n this.#getDefaultSelectedAccountGroup(wallets);\n }\n });\n\n // We still compare the previous and new value, the previous one could have been\n // an empty string and `#getDefaultSelectedAccountGroup` could also return an\n // empty string too, thus, we would re-use the same value here again. In that\n // case, no need to fire any event.\n if (\n previousSelectedAccountGroup !==\n this.state.accountTree.selectedAccountGroup\n ) {\n log(\n `Selected (initial) group is: [${this.state.accountTree.selectedAccountGroup}]`,\n );\n this.messagingSystem.publish(\n `${controllerName}:selectedAccountGroupChange`,\n this.state.accountTree.selectedAccountGroup,\n previousSelectedAccountGroup,\n );\n }\n\n log('Initialized!');\n this.#initialized = true;\n }\n\n /**\n * Re-initialize the controller's state.\n *\n * This is done in one single (atomic) `update` block to avoid having a temporary\n * cleared state. Use this when you need to force a full re-init even if already initialized.\n */\n reinit() {\n log('Re-initializing...');\n this.#initialized = false;\n this.init();\n }\n\n /**\n * Rule for entropy-base wallets.\n *\n * @returns The rule for entropy-based wallets.\n */\n #getEntropyRule(): EntropyRule {\n return this.#rules[0];\n }\n\n /**\n * Rule for Snap-base wallets.\n *\n * @returns The rule for snap-based wallets.\n */\n #getSnapRule(): SnapRule {\n return this.#rules[1];\n }\n\n /**\n * Rule for keyring-base wallets.\n *\n * This rule acts as a fallback and never fails since all accounts\n * comes from a keyring anyway.\n *\n * @returns The fallback rule for every accounts that did not match\n * any other rules.\n */\n #getKeyringRule(): KeyringRule {\n return this.#rules[2];\n }\n\n /**\n * Applies wallet metadata updates (name) by checking the persistent state\n * first, and then fallbacks to default values (based on the wallet's\n * type).\n *\n * @param state Controller state to update for persistence.\n * @param walletId The wallet ID to update.\n */\n #applyAccountWalletMetadata(\n state: AccountTreeControllerState,\n walletId: AccountWalletId,\n ) {\n const wallet = state.accountTree.wallets[walletId];\n const persistedMetadata = state.accountWalletsMetadata[walletId];\n\n // Apply persisted name if available (including empty strings)\n if (persistedMetadata?.name !== undefined) {\n wallet.metadata.name = persistedMetadata.name.value;\n } else if (!wallet.metadata.name) {\n // Generate default name if none exists\n if (wallet.type === AccountWalletType.Entropy) {\n wallet.metadata.name =\n this.#getEntropyRule().getDefaultAccountWalletName(wallet);\n } else if (wallet.type === AccountWalletType.Snap) {\n wallet.metadata.name =\n this.#getSnapRule().getDefaultAccountWalletName(wallet);\n } else {\n wallet.metadata.name =\n this.#getKeyringRule().getDefaultAccountWalletName(wallet);\n }\n log(`[${wallet.id}] Set default name to: \"${wallet.metadata.name}\"`);\n }\n }\n\n /**\n * Gets the appropriate rule instance for a given wallet type.\n *\n * @param wallet - The wallet object to get the rule for.\n * @returns The rule instance that handles the wallet's type.\n */\n #getRuleForWallet<WalletType extends AccountWalletType>(\n wallet: AccountWalletObjectOf<WalletType>,\n ): Rule<WalletType, AccountGroupType> {\n switch (wallet.type) {\n case AccountWalletType.Entropy:\n return this.#getEntropyRule() as unknown as Rule<\n WalletType,\n AccountGroupType\n >;\n case AccountWalletType.Snap:\n return this.#getSnapRule() as unknown as Rule<\n WalletType,\n AccountGroupType\n >;\n default:\n return this.#getKeyringRule() as unknown as Rule<\n WalletType,\n AccountGroupType\n >;\n }\n }\n\n /**\n * Applies group metadata updates (name, pinned, hidden flags) by checking\n * the persistent state first, and then fallbacks to default values (based\n * on the wallet's\n * type).\n *\n * @param state Controller state to update for persistence.\n * @param walletId The wallet ID containing the group.\n * @param groupId The account group ID to update.\n * @param nextNaturalNameIndex The next natural name index for this group (only used for default names).\n */\n #applyAccountGroupMetadata(\n state: AccountTreeControllerState,\n walletId: AccountWalletId,\n groupId: AccountGroupId,\n nextNaturalNameIndex?: number,\n ) {\n const wallet = state.accountTree.wallets[walletId];\n const group = wallet.groups[groupId];\n const persistedGroupMetadata = state.accountGroupsMetadata[group.id];\n\n // Apply persisted name if available (including empty strings)\n if (persistedGroupMetadata?.name !== undefined) {\n state.accountTree.wallets[walletId].groups[groupId].metadata.name =\n persistedGroupMetadata.name.value;\n } else if (!group.metadata.name) {\n // Get the appropriate rule for this wallet type\n const rule = this.#getRuleForWallet(wallet);\n\n // Get the prefix for groups of this wallet\n const namePrefix = rule.getDefaultAccountGroupPrefix(wallet);\n\n // Skip computed names for now - use default naming with per-wallet logic\n // TODO: Implement computed names in a future iteration\n\n // Parse the highest account index being used (similar to accounts-controller)\n let highestNameIndex = 0;\n for (const { id: otherGroupId } of Object.values(\n wallet.groups,\n ) as AccountGroupObject[]) {\n // Skip the current group being processed\n if (otherGroupId === groupId) {\n continue;\n }\n\n // We always get the name from the persisted map, since `init` will clear the\n // `state.accountTree.wallets`, thus, given empty `group.metadata.name`.\n // NOTE: If the other group has not been named yet, we just use an empty name.\n const otherGroupName =\n state.accountGroupsMetadata[otherGroupId]?.name?.value ?? '';\n\n // Parse the existing group name to extract the numeric index\n const nameMatch = otherGroupName.match(/account\\s+(\\d+)$/iu);\n if (nameMatch) {\n const nameIndex = parseInt(nameMatch[1], 10);\n if (nameIndex > highestNameIndex) {\n highestNameIndex = nameIndex;\n }\n }\n }\n\n // We just use the highest known index no matter the wallet type.\n //\n // For entropy-based wallets (bip44), if a multichain account group with group index 1\n // is inserted before another one with group index 0, then the naming will be:\n // - \"Account 1\" (group index 1)\n // - \"Account 2\" (group index 0)\n // This naming makes more sense for the end-user.\n //\n // For other type of wallets, since those wallets can create arbitrary gaps, we still\n // rely on the highest know index to avoid back-filling account with \"old names\".\n let proposedNameIndex = Math.max(\n // Use + 1 to use the next available index.\n highestNameIndex + 1,\n // In case all accounts have been renamed differently than the usual \"Account <index>\"\n // pattern, we want to use the next \"natural\" index, which is just the number of groups\n // in that wallet (e.g. [\"Account A\", \"Another Account\"], next natural index would be\n // \"Account 3\" in this case).\n nextNaturalNameIndex ?? Object.keys(wallet.groups).length,\n );\n\n // Find a unique name by checking for conflicts and incrementing if needed\n let proposedNameExists: boolean;\n let proposedName = '';\n do {\n proposedName = `${namePrefix} ${proposedNameIndex}`;\n\n // Check if this name already exists in the wallet (excluding current group)\n proposedNameExists = !isAccountGroupNameUniqueFromWallet(\n wallet,\n group.id,\n proposedName,\n );\n\n /* istanbul ignore next */\n if (proposedNameExists) {\n proposedNameIndex += 1; // Try next number\n }\n } while (proposedNameExists);\n\n state.accountTree.wallets[walletId].groups[groupId].metadata.name =\n proposedName;\n log(`[${group.id}] Set default name to: \"${group.metadata.name}\"`);\n\n // Persist the generated name to ensure consistency\n state.accountGroupsMetadata[groupId] ??= {};\n state.accountGroupsMetadata[groupId].name = {\n value: proposedName,\n // The `lastUpdatedAt` field is used for backup and sync, when comparing local names\n // with backed up names. In this case, the generated name should never take precedence\n // over a user-defined name, so we set `lastUpdatedAt` to 0.\n lastUpdatedAt: 0,\n };\n }\n\n // Apply persisted UI states\n if (persistedGroupMetadata?.pinned?.value !== undefined) {\n group.metadata.pinned = persistedGroupMetadata.pinned.value;\n }\n if (persistedGroupMetadata?.hidden?.value !== undefined) {\n group.metadata.hidden = persistedGroupMetadata.hidden.value;\n }\n }\n\n /**\n * Gets the account wallet object from its ID.\n *\n * @param walletId - Account wallet ID.\n * @returns The account wallet object if found, undefined otherwise.\n */\n getAccountWalletObject(\n walletId: AccountWalletId,\n ): AccountWalletObject | undefined {\n const wallet = this.state.accountTree.wallets[walletId];\n if (!wallet) {\n return undefined;\n }\n\n return wallet;\n }\n\n /**\n * Gets all account wallet objects.\n *\n * @returns All account wallet objects.\n */\n getAccountWalletObjects(): AccountWalletObject[] {\n return Object.values(this.state.accountTree.wallets);\n }\n\n /**\n * Gets all underlying accounts from the currently selected account\n * group.\n *\n * It also support account selector, which allows to filter specific\n * accounts given some criterias (account type, address, scopes, etc...).\n *\n * @param selector - Optional account selector.\n * @returns Underlying accounts for the currently selected account (filtered\n * by the selector if provided).\n */\n getAccountsFromSelectedAccountGroup(\n selector?: AccountSelector<InternalAccount>,\n ) {\n const groupId = this.getSelectedAccountGroup();\n if (!groupId) {\n return [];\n }\n\n const group = this.getAccountGroupObject(groupId);\n // We should never reach this part, so we cannot cover it either.\n /* istanbul ignore next */\n if (!group) {\n return [];\n }\n\n const accounts: InternalAccount[] = [];\n for (const id of group.accounts) {\n const account = this.messagingSystem.call(\n 'AccountsController:getAccount',\n id,\n );\n\n // For now, we're filtering undefined account, but I believe\n // throwing would be more appropriate here.\n if (account) {\n accounts.push(account);\n }\n }\n\n return selector ? select(accounts, selector) : accounts;\n }\n\n /**\n * Gets the account group object from its ID.\n *\n * @param groupId - Account group ID.\n * @returns The account group object if found, undefined otherwise.\n */\n getAccountGroupObject(\n groupId: AccountGroupId,\n ): AccountGroupObject | undefined {\n const walletId = this.#groupIdToWalletId.get(groupId);\n if (!walletId) {\n return undefined;\n }\n\n const wallet = this.getAccountWalletObject(walletId);\n return wallet?.groups[groupId];\n }\n\n /**\n * Handles \"AccountsController:accountAdded\" event to insert\n * new accounts into the tree.\n *\n * @param account - New account.\n */\n #handleAccountAdded(account: InternalAccount) {\n // We wait for the first `init` to be called to actually build up the tree and\n // mutate it. We expect the caller to first update the `AccountsController` state\n // to force the migration of accounts, and then call `init`.\n if (!this.#initialized) {\n return;\n }\n\n // Check if this account is already known by the tree to avoid double-insertion.\n if (!this.#accountIdToContext.has(account.id)) {\n this.update((state) => {\n this.#insert(state.accountTree.wallets, account);\n\n const context = this.#accountIdToContext.get(account.id);\n if (context) {\n const { walletId, groupId } = context;\n\n const wallet = state.accountTree.wallets[walletId];\n if (wallet) {\n this.#applyAccountWalletMetadata(state, walletId);\n this.#applyAccountGroupMetadata(state, walletId, groupId);\n }\n }\n });\n\n this.messagingSystem.publish(\n `${controllerName}:accountTreeChange`,\n this.state.accountTree,\n );\n }\n }\n\n /**\n * Handles \"AccountsController:accountRemoved\" event to remove\n * given account from the tree.\n *\n * @param accountId - Removed account ID.\n */\n #handleAccountRemoved(accountId: AccountId) {\n // We wait for the first `init` to be called to actually build up the tree and\n // mutate it. We expect the caller to first update the `AccountsController` state\n // to force the migration of accounts, and then call `init`.\n if (!this.#initialized) {\n return;\n }\n\n const context = this.#accountIdToContext.get(accountId);\n\n if (context) {\n const { walletId, groupId } = context;\n\n const previousSelectedAccountGroup =\n this.state.accountTree.selectedAccountGroup;\n let selectedAccountGroupChanged = false;\n\n this.update((state) => {\n const accounts =\n state.accountTree.wallets[walletId]?.groups[groupId]?.accounts;\n\n if (accounts) {\n const index = accounts.indexOf(accountId);\n if (index !== -1) {\n accounts.splice(index, 1);\n\n // Check if we need to update selectedAccountGroup after removal\n if (\n state.accountTree.selectedAccountGroup === groupId &&\n accounts.length === 0\n ) {\n // The currently selected group is now empty, find a new group to select\n const newSelectedAccountGroup = this.#getDefaultAccountGroupId(\n state.accountTree.wallets,\n );\n state.accountTree.selectedAccountGroup = newSelectedAccountGroup;\n selectedAccountGroupChanged =\n newSelectedAccountGroup !== previousSelectedAccountGroup;\n }\n }\n if (accounts.length === 0) {\n this.#pruneEmptyGroupAndWallet(state, walletId, groupId);\n }\n }\n });\n this.messagingSystem.publish(\n `${controllerName}:accountTreeChange`,\n this.state.accountTree,\n );\n\n // Emit selectedAccountGroupChange event if the selected group changed\n if (selectedAccountGroupChanged) {\n this.messagingSystem.publish(\n `${controllerName}:selectedAccountGroupChange`,\n this.state.accountTree.selectedAccountGroup,\n previousSelectedAccountGroup,\n );\n }\n\n // Clear reverse-mapping for that account.\n this.#accountIdToContext.delete(accountId);\n }\n }\n\n /**\n * Helper method to prune a group if it holds no accounts and additionally\n * prune the wallet if it holds no groups. This action should take place\n * after a singular account removal.\n *\n * NOTE: This method should only be used for a group that we know to be empty.\n *\n * @param state - The AccountTreeController state to prune.\n * @param walletId - The wallet ID to prune, the wallet should be the parent of the associated group that holds the removed account.\n * @param groupId - The group ID to prune, the group should be the parent of the associated account that was removed.\n * @returns The updated state.\n */\n #pruneEmptyGroupAndWallet(\n state: AccountTreeControllerState,\n walletId: AccountWalletId,\n groupId: AccountGroupId,\n ) {\n const { wallets } = state.accountTree;\n\n delete wallets[walletId].groups[groupId];\n this.#groupIdToWalletId.delete(groupId);\n\n // Clean up metadata for the pruned group\n delete state.accountGroupsMetadata[groupId];\n\n if (Object.keys(wallets[walletId].groups).length === 0) {\n delete wallets[walletId];\n }\n return state;\n }\n\n /**\n * Insert an account inside an account tree.\n *\n * We go over multiple rules to try to \"match\" the account following\n * specific criterias. If a rule \"matches\" an account, then this\n * account get added into its proper account wallet and account group.\n *\n * @param wallets - Account tree.\n * @param account - The account to be inserted.\n */\n #insert(\n wallets: AccountTreeControllerState['accountTree']['wallets'],\n account: InternalAccount,\n ) {\n const result =\n this.#getEntropyRule().match(account) ??\n this.#getSnapRule().match(account) ??\n this.#getKeyringRule().match(account); // This one cannot fail.\n\n // Update controller's state.\n const walletId = result.wallet.id;\n let wallet = wallets[walletId];\n if (!wallet) {\n log(`[${walletId}] Added as new wallet`);\n wallets[walletId] = {\n ...result.wallet,\n status: 'ready',\n groups: {},\n metadata: {\n name: '', // Will get updated later.\n ...result.wallet.metadata,\n },\n // We do need to type-cast since we're not narrowing `result` with\n // the union tag `result.wallet.type`.\n } as AccountWalletObject;\n wallet = wallets[walletId];\n\n // Trigger atomic sync for new wallet (only for entropy wallets)\n if (wallet.type === AccountWalletType.Entropy) {\n this.#backupAndSyncService.enqueueSingleWalletSync(walletId);\n }\n }\n\n const groupId = result.group.id;\n let group = wallet.groups[groupId];\n const { type, id } = account;\n const sortOrder = ACCOUNT_TYPE_TO_SORT_ORDER[type];\n\n if (!group) {\n log(`[${walletId}] Add new group: [${groupId}]`);\n wallet.groups[groupId] = {\n ...result.group,\n // Type-wise, we are guaranteed to always have at least 1 account.\n accounts: [id],\n metadata: {\n name: '',\n ...{ pinned: false, hidden: false }, // Default UI states\n ...result.group.metadata, // Allow rules to override defaults\n },\n // We do need to type-cast since we're not narrowing `result` with\n // the union tag `result.group.type`.\n } as AccountGroupObject;\n group = wallet.groups[groupId];\n\n // Map group ID to its containing wallet ID for efficient direct access\n this.#groupIdToWalletId.set(groupId, walletId);\n\n // Trigger atomic sync for new group (only for entropy wallets)\n if (wallet.type === AccountWalletType.Entropy) {\n this.#backupAndSyncService.enqueueSingleGroupSync(groupId);\n }\n } else {\n group.accounts.push(id);\n // We need to do this at every insertion because race conditions can happen\n // during the account creation process where one provider completes before the other.\n // The discovery process in the service can also lead to some accounts being created \"out of order\".\n const { accounts } = group;\n accounts.sort(\n /* istanbul ignore next: Comparator branch execution (a===id vs b===id)\n * and return attribution vary across engines; final ordering is covered\n * by behavior tests. Ignoring the entire comparator avoids flaky line\n * coverage without reducing scenario coverage.\n */\n (a, b) => {\n const aSortOrder =\n a === id ? sortOrder : this.#accountIdToContext.get(a)?.sortOrder;\n const bSortOrder =\n b === id ? sortOrder : this.#accountIdToContext.get(b)?.sortOrder;\n return (\n (aSortOrder ?? MAX_SORT_ORDER) - (bSortOrder ?? MAX_SORT_ORDER)\n );\n },\n );\n }\n log(\n `[${groupId}] Add new account: { id: \"${account.id}\", type: \"${account.type}\", address: \"${account.address}\"`,\n );\n\n // Update the reverse mapping for this account.\n this.#accountIdToContext.set(account.id, {\n walletId: wallet.id,\n groupId: group.id,\n sortOrder,\n });\n }\n\n /**\n * List all internal accounts.\n *\n * @returns The list of all internal accounts.\n */\n #listAccounts(): InternalAccount[] {\n return this.messagingSystem.call(\n 'AccountsController:listMultichainAccounts',\n );\n }\n\n /**\n * Asserts that a group exists in the current account tree.\n *\n * @param groupId - The account group ID to validate.\n * @throws Error if the group does not exist.\n */\n #assertAccountGroupExists(groupId: AccountGroupId): void {\n const exists = this.#groupIdToWalletId.has(groupId);\n if (!exists) {\n throw new Error(`Account group with ID \"${groupId}\" not found in tree`);\n }\n }\n\n /**\n * Asserts that a wallet exists in the current account tree.\n *\n * @param walletId - The account wallet ID to validate.\n * @throws Error if the wallet does not exist.\n */\n #assertAccountWalletExists(walletId: AccountWalletId): void {\n const exists = Boolean(this.state.accountTree.wallets[walletId]);\n if (!exists) {\n throw new Error(`Account wallet with ID \"${walletId}\" not found in tree`);\n }\n }\n\n /**\n * Asserts that an account group name is unique within the same wallet.\n *\n * @param groupId - The account group ID to exclude from the check.\n * @param name - The name to validate for uniqueness.\n * @throws Error if the name already exists in another group within the same wallet.\n */\n #assertAccountGroupNameIsUnique(groupId: AccountGroupId, name: string): void {\n if (!isAccountGroupNameUnique(this.state, groupId, name)) {\n throw new Error('Account group name already exists');\n }\n }\n\n /**\n * Gets the currently selected account group ID.\n *\n * @returns The selected account group ID or empty string if none selected.\n */\n getSelectedAccountGroup(): AccountGroupId | '' {\n return this.state.accountTree.selectedAccountGroup;\n }\n\n /**\n * Sets the selected account group and updates the AccountsController selectedAccount accordingly.\n *\n * @param groupId - The account group ID to select.\n */\n setSelectedAccountGroup(groupId: AccountGroupId): void {\n const previousSelectedAccountGroup =\n this.state.accountTree.selectedAccountGroup;\n\n // Idempotent check - if the same group is already selected, do nothing\n if (previousSelectedAccountGroup === groupId) {\n return;\n }\n\n // Find the first account in this group to select\n const accountToSelect = this.#getDefaultAccountFromAccountGroupId(groupId);\n if (!accountToSelect) {\n throw new Error(`No accounts found in group: ${groupId}`);\n }\n\n // Update our state first\n this.update((state) => {\n state.accountTree.selectedAccountGroup = groupId;\n });\n\n log(\n `Selected group is now: [${this.state.accountTree.selectedAccountGroup}]`,\n );\n\n this.messagingSystem.publish(\n `${controllerName}:selectedAccountGroupChange`,\n groupId,\n previousSelectedAccountGroup,\n );\n\n // Update AccountsController - this will trigger selectedAccountChange event,\n // but our handler is idempotent so it won't cause infinite loop\n this.messagingSystem.call(\n 'AccountsController:setSelectedAccount',\n accountToSelect,\n );\n }\n\n /**\n * Initializes the selectedAccountGroup based on the currently selected account from AccountsController.\n *\n * @param wallets - Wallets object to use for fallback logic\n * @returns The default selected account group ID or empty string if none selected.\n */\n #getDefaultSelectedAccountGroup(wallets: {\n [walletId: AccountWalletId]: AccountWalletObject;\n }): AccountGroupId | '' {\n const selectedAccount = this.messagingSystem.call(\n 'AccountsController:getSelectedMultichainAccount',\n );\n if (selectedAccount && selectedAccount.id) {\n const accountMapping = this.#accountIdToContext.get(selectedAccount.id);\n if (accountMapping) {\n const { groupId } = accountMapping;\n\n return groupId;\n }\n }\n\n // Default to the default group in case of errors.\n return this.#getDefaultAccountGroupId(wallets);\n }\n\n /**\n * Handles multichain account wallet status change from\n * the MultichainAccountService.\n *\n * @param walletId - Multichain account wallet ID.\n * @param walletStatus - New multichain account wallet status.\n */\n #handleMultichainAccountWalletStatusChange(\n walletId: MultichainAccountWalletId,\n walletStatus: MultichainAccountWalletStatus,\n ): void {\n this.update((state) => {\n const wallet = state.accountTree.wallets[walletId];\n\n if (wallet) {\n wallet.status = walletStatus;\n }\n });\n }\n\n /**\n * Gets account group object.\n *\n * @param groupId - The account group ID.\n * @returns The account group or undefined if not found.\n */\n #getAccountGroup(groupId: AccountGroupId): AccountGroupObject | undefined {\n const found = Object.values(this.state.accountTree.wallets).find(\n (wallet) => wallet.groups[groupId] !== undefined,\n );\n\n return found?.groups[groupId];\n }\n\n /**\n * Gets the default account for specified group.\n *\n * @param groupId - The account group ID.\n * @returns The first account ID in the group, or undefined if no accounts found.\n */\n #getDefaultAccountFromAccountGroupId(\n groupId: AccountGroupId,\n ): AccountId | undefined {\n const group = this.#getAccountGroup(groupId);\n\n if (group) {\n let candidate;\n for (const id of group.accounts) {\n const account = this.messagingSystem.call(\n 'AccountsController:getAccount',\n id,\n );\n\n if (!candidate) {\n candidate = id;\n }\n if (account && isEvmAccountType(account.type)) {\n // EVM accounts have a higher priority, so if we find any, we just\n // use that account!\n return account.id;\n }\n }\n\n return candidate;\n }\n\n return undefined;\n }\n\n /**\n * Gets the default group id, which is either, the first non-empty group that contains an EVM account or\n * just the first non-empty group with any accounts.\n *\n * @param wallets - The wallets object to search.\n * @returns The ID of the first non-empty group, or an empty string if no groups are found.\n */\n #getDefaultAccountGroupId(wallets: {\n [walletId: AccountWalletId]: AccountWalletObject;\n }): AccountGroupId | '' {\n let candidate: AccountGroupId | '' = '';\n\n for (const wallet of Object.values(wallets)) {\n for (const group of Object.values(wallet.groups)) {\n // We only update the candidate with the first non-empty group, but still\n // try to find a group that contains an EVM account (the `candidate` is\n // our fallback).\n if (candidate === '' && group.accounts.length > 0) {\n candidate = group.id;\n }\n\n for (const id of group.accounts) {\n const account = this.messagingSystem.call(\n 'AccountsController:getAccount',\n id,\n );\n\n if (account && isEvmAccountType(account.type)) {\n // EVM accounts have a higher priority, so if we find any, we just\n // use that group!\n return group.id;\n }\n }\n }\n }\n return candidate;\n }\n\n /**\n * Resolves name conflicts by adding a suffix to make the name unique.\n *\n * @internal\n * @param wallet - The wallet to check within.\n * @param groupId - The account group ID to exclude from the check.\n * @param name - The desired name that has a conflict.\n * @returns A unique name with suffix added if necessary.\n */\n resolveNameConflict(\n wallet: AccountWalletObject,\n groupId: AccountGroupId,\n name: string,\n ): string {\n let suffix = 2;\n let candidateName = `${name} (${suffix})`;\n\n // Keep incrementing suffix until we find a unique name\n while (\n !isAccountGroupNameUniqueFromWallet(wallet, groupId, candidateName)\n ) {\n suffix += 1;\n candidateName = `${name} (${suffix})`;\n }\n\n return candidateName;\n }\n\n /**\n * Sets a custom name for an account group.\n *\n * @param groupId - The account group ID.\n * @param name - The custom name to set.\n * @param autoHandleConflict - If true, automatically resolves name conflicts by adding a suffix. If false, throws on conflicts.\n * @throws If the account group ID is not found in the current tree.\n * @throws If the account group name already exists and autoHandleConflict is false.\n */\n setAccountGroupName(\n groupId: AccountGroupId,\n name: string,\n autoHandleConflict: boolean = false,\n ): void {\n // Validate that the group exists in the current tree\n this.#assertAccountGroupExists(groupId);\n\n const walletId = this.#groupIdToWalletId.get(groupId);\n assert(walletId, `Account group with ID \"${groupId}\" not found in tree`);\n\n const wallet = this.state.accountTree.wallets[walletId];\n let finalName = name;\n\n // Handle name conflicts based on the autoHandleConflict flag\n if (\n autoHandleConflict &&\n !isAccountGroupNameUniqueFromWallet(wallet, groupId, name)\n ) {\n finalName = this.resolveNameConflict(wallet, groupId, name);\n } else {\n // Validate that the name is unique\n this.#assertAccountGroupNameIsUnique(groupId, finalName);\n }\n\n log(\n `[${groupId}] Set new name to: \"${finalName}\" (auto handle conflict: ${autoHandleConflict})`,\n );\n\n this.update((state) => {\n /* istanbul ignore next */\n if (!state.accountGroupsMetadata[groupId]) {\n state.accountGroupsMetadata[groupId] = {};\n }\n\n // Update persistent metadata\n state.accountGroupsMetadata[groupId].name = {\n value: finalName,\n lastUpdatedAt: Date.now(),\n };\n\n // Update tree node directly using efficient mapping\n state.accountTree.wallets[walletId].groups[groupId].metadata.name =\n finalName;\n });\n\n // Trigger atomic sync for group rename (only for groups from entropy wallets)\n if (wallet.type === AccountWalletType.Entropy) {\n this.#backupAndSyncService.enqueueSingleGroupSync(groupId);\n }\n }\n\n /**\n * Sets a custom name for an account wallet.\n *\n * @param walletId - The account wallet ID.\n * @param name - The custom name to set.\n * @throws If the account wallet ID is not found in the current tree.\n */\n setAccountWalletName(walletId: AccountWalletId, name: string): void {\n // Validate that the wallet exists in the current tree\n this.#assertAccountWalletExists(walletId);\n\n this.update((state) => {\n // Update persistent metadata\n state.accountWalletsMetadata[walletId] ??= {};\n state.accountWalletsMetadata[walletId].name = {\n value: name,\n lastUpdatedAt: Date.now(),\n };\n\n // Update tree node directly\n state.accountTree.wallets[walletId].metadata.name = name;\n });\n\n // Trigger atomic sync for wallet rename (only for groups from entropy wallets)\n if (\n this.state.accountTree.wallets[walletId].type ===\n AccountWalletType.Entropy\n ) {\n this.#backupAndSyncService.enqueueSingleWalletSync(walletId);\n }\n }\n\n /**\n * Toggles the pinned state of an account group.\n *\n * @param groupId - The account group ID.\n * @param pinned - Whether the group should be pinned.\n * @throws If the account group ID is not found in the current tree.\n */\n setAccountGroupPinned(groupId: AccountGroupId, pinned: boolean): void {\n // Validate that the group exists in the current tree\n this.#assertAccountGroupExists(groupId);\n\n const walletId = this.#groupIdToWalletId.get(groupId);\n\n this.update((state) => {\n /* istanbul ignore next */\n if (!state.accountGroupsMetadata[groupId]) {\n state.accountGroupsMetadata[groupId] = {};\n }\n\n // Update persistent metadata\n state.accountGroupsMetadata[groupId].pinned = {\n value: pinned,\n lastUpdatedAt: Date.now(),\n };\n\n // Update tree node directly using efficient mapping\n if (walletId) {\n state.accountTree.wallets[walletId].groups[groupId].metadata.pinned =\n pinned;\n }\n });\n\n // Trigger atomic sync for group pinning (only for groups from entropy wallets)\n if (\n walletId &&\n this.state.accountTree.wallets[walletId].type ===\n AccountWalletType.Entropy\n ) {\n this.#backupAndSyncService.enqueueSingleGroupSync(groupId);\n }\n }\n\n /**\n * Toggles the hidden state of an account group.\n *\n * @param groupId - The account group ID.\n * @param hidden - Whether the group should be hidden.\n * @throws If the account group ID is not found in the current tree.\n */\n setAccountGroupHidden(groupId: AccountGroupId, hidden: boolean): void {\n // Validate that the group exists in the current tree\n this.#assertAccountGroupExists(groupId);\n\n const walletId = this.#groupIdToWalletId.get(groupId);\n\n this.update((state) => {\n /* istanbul ignore next */\n if (!state.accountGroupsMetadata[groupId]) {\n state.accountGroupsMetadata[groupId] = {};\n }\n\n // Update persistent metadata\n state.accountGroupsMetadata[groupId].hidden = {\n value: hidden,\n lastUpdatedAt: Date.now(),\n };\n\n // Update tree node directly using efficient mapping\n if (walletId) {\n state.accountTree.wallets[walletId].groups[groupId].metadata.hidden =\n hidden;\n }\n });\n\n // Trigger atomic sync for group hiding (only for groups from entropy wallets)\n if (\n walletId &&\n this.state.accountTree.wallets[walletId].type ===\n AccountWalletType.Entropy\n ) {\n this.#backupAndSyncService.enqueueSingleGroupSync(groupId);\n }\n }\n\n /**\n * Clears the controller state and resets to default values.\n * Also clears the backup and sync service state.\n */\n clearState(): void {\n log('Clearing state');\n\n this.update(() => {\n return {\n ...getDefaultAccountTreeControllerState(),\n };\n });\n this.#backupAndSyncService.clearState();\n\n // So we know we have to call `init` again.\n this.#initialized = false;\n }\n\n /**\n * Registers message handlers for the AccountTreeController.\n */\n #registerMessageHandlers(): void {\n this.messagingSystem.registerActionHandler(\n `${controllerName}:getSelectedAccountGroup`,\n this.getSelectedAccountGroup.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:setSelectedAccountGroup`,\n this.setSelectedAccountGroup.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:getAccountsFromSelectedAccountGroup`,\n this.getAccountsFromSelectedAccountGroup.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:setAccountWalletName`,\n this.setAccountWalletName.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:setAccountGroupName`,\n this.setAccountGroupName.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:setAccountGroupPinned`,\n this.setAccountGroupPinned.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:setAccountGroupHidden`,\n this.setAccountGroupHidden.bind(this),\n );\n }\n\n /**\n * Bi-directionally syncs the account tree with user storage.\n * This will perform a full sync, including both pulling updates\n * from user storage and pushing local changes to user storage.\n * This also performs legacy account syncing if needed.\n *\n * IMPORTANT:\n * If a full sync is already in progress, it will return the ongoing promise.\n *\n * @returns A promise that resolves when the sync is complete.\n */\n async syncWithUserStorage(): Promise<void> {\n return this.#backupAndSyncService.performFullSync();\n }\n\n /**\n * Bi-directionally syncs the account tree with user storage.\n * This will ensure at least one full sync is ran, including both pulling updates\n * from user storage and pushing local changes to user storage.\n * This also performs legacy account syncing if needed.\n *\n * IMPORTANT:\n * If the first ever full sync is already in progress, it will return the ongoing promise.\n * If the first ever full sync was previously completed, it will NOT start a new sync, and will resolve immediately.\n *\n * @returns A promise that resolves when the first ever full sync is complete.\n */\n async syncWithUserStorageAtLeastOnce(): Promise<void> {\n return this.#backupAndSyncService.performFullSyncAtLeastOnce();\n }\n\n /**\n * Creates an backup and sync context for sync operations.\n * Used by the backup and sync service.\n *\n * @returns The backup and sync context.\n */\n #createBackupAndSyncContext(): BackupAndSyncContext {\n return {\n ...this.#backupAndSyncConfig,\n controller: this,\n messenger: this.messagingSystem,\n controllerStateUpdateFn: this.update.bind(this),\n traceFn: this.#trace.bind(this),\n groupIdToWalletId: this.#groupIdToWalletId,\n };\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccountTreeController.d.cts","sourceRoot":"","sources":["../src/AccountTreeController.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EACf,eAAe,EAGhB,8BAA8B;AAI/B,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAG3D,OAAO,KAAK,EAAE,eAAe,EAAE,uCAAuC;AAUtE,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,oBAAgB;AACvE,OAAO,EACL,0BAA0B,EAI3B,oBAAgB;
|
|
1
|
+
{"version":3,"file":"AccountTreeController.d.cts","sourceRoot":"","sources":["../src/AccountTreeController.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EACf,eAAe,EAGhB,8BAA8B;AAI/B,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAG3D,OAAO,KAAK,EAAE,eAAe,EAAE,uCAAuC;AAUtE,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,oBAAgB;AACvE,OAAO,EACL,0BAA0B,EAI3B,oBAAgB;AAMjB,OAAO,KAAK,EACV,2BAA2B,EAE3B,8BAA8B,EAC9B,0BAA0B,EAC3B,oBAAgB;AACjB,OAAO,EAAE,KAAK,mBAAmB,EAA8B,qBAAiB;AAEhF,eAAO,MAAM,cAAc,0BAA0B,CAAC;AAoCtD;;;;GAIG;AACH,wBAAgB,oCAAoC,IAAI,0BAA0B,CAWjF;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B;;OAEG;IACH,QAAQ,EAAE,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAEpC;;OAEG;IACH,OAAO,EAAE,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAElC;;OAEG;IACH,SAAS,EAAE,CAAC,OAAO,0BAA0B,CAAC,CAAC,mBAAmB,CAAC,CAAC;CACrE,CAAC;AAEF,qBAAa,qBAAsB,SAAQ,cAAc,CACvD,OAAO,cAAc,EACrB,0BAA0B,EAC1B,8BAA8B,CAC/B;;IAkBC;;;;;;;OAOG;gBAES,EACV,SAAS,EACT,KAAK,EACL,MAAM,GACP,EAAE;QACD,SAAS,EAAE,8BAA8B,CAAC;QAC1C,KAAK,CAAC,EAAE,OAAO,CAAC,0BAA0B,CAAC,CAAC;QAC5C,MAAM,CAAC,EAAE,2BAA2B,CAAC;KACtC;IAiFD;;;;;;OAMG;IACH,IAAI;IA2GJ;;;;;OAKG;IACH,MAAM;IA+NN;;;;;OAKG;IACH,sBAAsB,CACpB,QAAQ,EAAE,eAAe,GACxB,mBAAmB,GAAG,SAAS;IASlC;;;;OAIG;IACH,uBAAuB,IAAI,mBAAmB,EAAE;IAIhD;;;;;;;;;;OAUG;IACH,mCAAmC,CACjC,QAAQ,CAAC,EAAE,eAAe,CAAC,eAAe,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA+B7C;;;;;OAKG;IACH,qBAAqB,CACnB,OAAO,EAAE,cAAc,GACtB,kBAAkB,GAAG,SAAS;IAiTjC;;;;OAIG;IACH,uBAAuB,IAAI,cAAc,GAAG,EAAE;IAI9C;;;;OAIG;IACH,uBAAuB,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IA0KtD;;;;;;;;OAQG;IACH,mBAAmB,CACjB,MAAM,EAAE,mBAAmB,EAC3B,OAAO,EAAE,cAAc,EACvB,IAAI,EAAE,MAAM,GACX,MAAM;IAeT;;;;;;;;OAQG;IACH,mBAAmB,CACjB,OAAO,EAAE,cAAc,EACvB,IAAI,EAAE,MAAM,EACZ,kBAAkB,GAAE,OAAe,GAClC,IAAI;IAgDP;;;;;;OAMG;IACH,oBAAoB,CAAC,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAyBnE;;;;;;OAMG;IACH,qBAAqB,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI;IAmCrE;;;;;;OAMG;IACH,qBAAqB,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI;IAmCrE;;;OAGG;IACH,UAAU,IAAI,IAAI;IAsDlB;;;;;;;;;;OAUG;IACG,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAI1C;;;;;;;;;;;OAWG;IACG,8BAA8B,IAAI,OAAO,CAAC,IAAI,CAAC;CAoBtD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccountTreeController.d.mts","sourceRoot":"","sources":["../src/AccountTreeController.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EACf,eAAe,EAGhB,8BAA8B;AAI/B,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAG3D,OAAO,KAAK,EAAE,eAAe,EAAE,uCAAuC;AAUtE,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,oBAAgB;AACvE,OAAO,EACL,0BAA0B,EAI3B,oBAAgB;
|
|
1
|
+
{"version":3,"file":"AccountTreeController.d.mts","sourceRoot":"","sources":["../src/AccountTreeController.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EACf,eAAe,EAGhB,8BAA8B;AAI/B,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAG3D,OAAO,KAAK,EAAE,eAAe,EAAE,uCAAuC;AAUtE,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,oBAAgB;AACvE,OAAO,EACL,0BAA0B,EAI3B,oBAAgB;AAMjB,OAAO,KAAK,EACV,2BAA2B,EAE3B,8BAA8B,EAC9B,0BAA0B,EAC3B,oBAAgB;AACjB,OAAO,EAAE,KAAK,mBAAmB,EAA8B,qBAAiB;AAEhF,eAAO,MAAM,cAAc,0BAA0B,CAAC;AAoCtD;;;;GAIG;AACH,wBAAgB,oCAAoC,IAAI,0BAA0B,CAWjF;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B;;OAEG;IACH,QAAQ,EAAE,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAEpC;;OAEG;IACH,OAAO,EAAE,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAElC;;OAEG;IACH,SAAS,EAAE,CAAC,OAAO,0BAA0B,CAAC,CAAC,mBAAmB,CAAC,CAAC;CACrE,CAAC;AAEF,qBAAa,qBAAsB,SAAQ,cAAc,CACvD,OAAO,cAAc,EACrB,0BAA0B,EAC1B,8BAA8B,CAC/B;;IAkBC;;;;;;;OAOG;gBAES,EACV,SAAS,EACT,KAAK,EACL,MAAM,GACP,EAAE;QACD,SAAS,EAAE,8BAA8B,CAAC;QAC1C,KAAK,CAAC,EAAE,OAAO,CAAC,0BAA0B,CAAC,CAAC;QAC5C,MAAM,CAAC,EAAE,2BAA2B,CAAC;KACtC;IAiFD;;;;;;OAMG;IACH,IAAI;IA2GJ;;;;;OAKG;IACH,MAAM;IA+NN;;;;;OAKG;IACH,sBAAsB,CACpB,QAAQ,EAAE,eAAe,GACxB,mBAAmB,GAAG,SAAS;IASlC;;;;OAIG;IACH,uBAAuB,IAAI,mBAAmB,EAAE;IAIhD;;;;;;;;;;OAUG;IACH,mCAAmC,CACjC,QAAQ,CAAC,EAAE,eAAe,CAAC,eAAe,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA+B7C;;;;;OAKG;IACH,qBAAqB,CACnB,OAAO,EAAE,cAAc,GACtB,kBAAkB,GAAG,SAAS;IAiTjC;;;;OAIG;IACH,uBAAuB,IAAI,cAAc,GAAG,EAAE;IAI9C;;;;OAIG;IACH,uBAAuB,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IA0KtD;;;;;;;;OAQG;IACH,mBAAmB,CACjB,MAAM,EAAE,mBAAmB,EAC3B,OAAO,EAAE,cAAc,EACvB,IAAI,EAAE,MAAM,GACX,MAAM;IAeT;;;;;;;;OAQG;IACH,mBAAmB,CACjB,OAAO,EAAE,cAAc,EACvB,IAAI,EAAE,MAAM,EACZ,kBAAkB,GAAE,OAAe,GAClC,IAAI;IAgDP;;;;;;OAMG;IACH,oBAAoB,CAAC,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAyBnE;;;;;;OAMG;IACH,qBAAqB,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI;IAmCrE;;;;;;OAMG;IACH,qBAAqB,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI;IAmCrE;;;OAGG;IACH,UAAU,IAAI,IAAI;IAsDlB;;;;;;;;;;OAUG;IACG,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAI1C;;;;;;;;;;;OAWG;IACG,8BAA8B,IAAI,OAAO,CAAC,IAAI,CAAC;CAoBtD"}
|
|
@@ -9,7 +9,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
9
9
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
10
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
11
|
};
|
|
12
|
-
var _AccountTreeController_instances, _AccountTreeController_accountIdToContext, _AccountTreeController_groupIdToWalletId, _AccountTreeController_backupAndSyncService, _AccountTreeController_rules, _AccountTreeController_trace, _AccountTreeController_backupAndSyncConfig, _AccountTreeController_initialized, _AccountTreeController_getEntropyRule, _AccountTreeController_getSnapRule, _AccountTreeController_getKeyringRule, _AccountTreeController_applyAccountWalletMetadata, _AccountTreeController_getRuleForWallet, _AccountTreeController_applyAccountGroupMetadata, _AccountTreeController_handleAccountAdded, _AccountTreeController_handleAccountRemoved, _AccountTreeController_pruneEmptyGroupAndWallet, _AccountTreeController_insert, _AccountTreeController_listAccounts, _AccountTreeController_assertAccountGroupExists, _AccountTreeController_assertAccountWalletExists, _AccountTreeController_assertAccountGroupNameIsUnique, _AccountTreeController_getDefaultSelectedAccountGroup,
|
|
12
|
+
var _AccountTreeController_instances, _AccountTreeController_accountIdToContext, _AccountTreeController_groupIdToWalletId, _AccountTreeController_backupAndSyncService, _AccountTreeController_rules, _AccountTreeController_trace, _AccountTreeController_backupAndSyncConfig, _AccountTreeController_initialized, _AccountTreeController_getEntropyRule, _AccountTreeController_getSnapRule, _AccountTreeController_getKeyringRule, _AccountTreeController_applyAccountWalletMetadata, _AccountTreeController_getRuleForWallet, _AccountTreeController_applyAccountGroupMetadata, _AccountTreeController_handleAccountAdded, _AccountTreeController_handleAccountRemoved, _AccountTreeController_pruneEmptyGroupAndWallet, _AccountTreeController_insert, _AccountTreeController_listAccounts, _AccountTreeController_assertAccountGroupExists, _AccountTreeController_assertAccountWalletExists, _AccountTreeController_assertAccountGroupNameIsUnique, _AccountTreeController_getDefaultSelectedAccountGroup, _AccountTreeController_handleMultichainAccountWalletStatusChange, _AccountTreeController_getAccountGroup, _AccountTreeController_getDefaultAccountFromAccountGroupId, _AccountTreeController_getDefaultAccountGroupId, _AccountTreeController_registerMessageHandlers, _AccountTreeController_createBackupAndSyncContext;
|
|
13
13
|
import { AccountWalletType, select } from "@metamask/account-api";
|
|
14
14
|
import { BaseController } from "@metamask/base-controller";
|
|
15
15
|
import { isEvmAccountType } from "@metamask/keyring-api";
|
|
@@ -17,6 +17,7 @@ import { assert } from "@metamask/utils";
|
|
|
17
17
|
import { formatAnalyticsEvent, traceFallback } from "./backup-and-sync/analytics/index.mjs";
|
|
18
18
|
import { BackupAndSyncService } from "./backup-and-sync/service/index.mjs";
|
|
19
19
|
import { ACCOUNT_TYPE_TO_SORT_ORDER, isAccountGroupNameUnique, isAccountGroupNameUniqueFromWallet, MAX_SORT_ORDER } from "./group.mjs";
|
|
20
|
+
import { projectLogger as log } from "./logger.mjs";
|
|
20
21
|
import { EntropyRule } from "./rules/entropy.mjs";
|
|
21
22
|
import { KeyringRule } from "./rules/keyring.mjs";
|
|
22
23
|
import { SnapRule } from "./rules/snap.mjs";
|
|
@@ -132,9 +133,6 @@ export class AccountTreeController extends BaseController {
|
|
|
132
133
|
this.messagingSystem.subscribe('AccountsController:accountRemoved', (accountId) => {
|
|
133
134
|
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_handleAccountRemoved).call(this, accountId);
|
|
134
135
|
});
|
|
135
|
-
this.messagingSystem.subscribe('AccountsController:selectedAccountChange', (account) => {
|
|
136
|
-
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_handleSelectedAccountChange).call(this, account);
|
|
137
|
-
});
|
|
138
136
|
this.messagingSystem.subscribe('UserStorageController:stateChange', (userStorageControllerState) => {
|
|
139
137
|
__classPrivateFieldGet(this, _AccountTreeController_backupAndSyncService, "f").handleUserStorageStateChange(userStorageControllerState);
|
|
140
138
|
});
|
|
@@ -156,6 +154,7 @@ export class AccountTreeController extends BaseController {
|
|
|
156
154
|
// `reinit` to re-init everything from scratch.
|
|
157
155
|
return;
|
|
158
156
|
}
|
|
157
|
+
log('Initializing...');
|
|
159
158
|
const wallets = {};
|
|
160
159
|
// Clear mappings for fresh rebuild.
|
|
161
160
|
__classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").clear();
|
|
@@ -163,8 +162,20 @@ export class AccountTreeController extends BaseController {
|
|
|
163
162
|
// Keep the current selected group to check if it's still part of the tree
|
|
164
163
|
// after rebuilding it.
|
|
165
164
|
const previousSelectedAccountGroup = this.state.accountTree.selectedAccountGroup;
|
|
165
|
+
// There's no guarantee that accounts would be sorted by their import time
|
|
166
|
+
// with `listMultichainAccounts`. We have to sort them here before constructing
|
|
167
|
+
// the tree.
|
|
168
|
+
//
|
|
169
|
+
// Because of the alignment mecanism, some accounts from the same group might not
|
|
170
|
+
// have been imported at the same time, but at least of them should have been
|
|
171
|
+
// imported at the right time, thus, inserting the group at the proper place too.
|
|
172
|
+
//
|
|
173
|
+
// Lastly, if one day we allow to have "gaps" in between groups, then this `sort`
|
|
174
|
+
// won't be enough and we would have to use group properties instead (like group
|
|
175
|
+
// index or maybe introduce a `importTime` at group level).
|
|
176
|
+
const accounts = __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_listAccounts).call(this).sort((a, b) => a.metadata.importTime - b.metadata.importTime);
|
|
166
177
|
// For now, we always re-compute all wallets, we do not re-use the existing state.
|
|
167
|
-
for (const account of
|
|
178
|
+
for (const account of accounts) {
|
|
168
179
|
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_insert).call(this, wallets, account);
|
|
169
180
|
}
|
|
170
181
|
// Once we have the account tree, we can apply persisted metadata (names + UI states).
|
|
@@ -207,8 +218,10 @@ export class AccountTreeController extends BaseController {
|
|
|
207
218
|
// case, no need to fire any event.
|
|
208
219
|
if (previousSelectedAccountGroup !==
|
|
209
220
|
this.state.accountTree.selectedAccountGroup) {
|
|
221
|
+
log(`Selected (initial) group is: [${this.state.accountTree.selectedAccountGroup}]`);
|
|
210
222
|
this.messagingSystem.publish(`${controllerName}:selectedAccountGroupChange`, this.state.accountTree.selectedAccountGroup, previousSelectedAccountGroup);
|
|
211
223
|
}
|
|
224
|
+
log('Initialized!');
|
|
212
225
|
__classPrivateFieldSet(this, _AccountTreeController_initialized, true, "f");
|
|
213
226
|
}
|
|
214
227
|
/**
|
|
@@ -218,6 +231,7 @@ export class AccountTreeController extends BaseController {
|
|
|
218
231
|
* cleared state. Use this when you need to force a full re-init even if already initialized.
|
|
219
232
|
*/
|
|
220
233
|
reinit() {
|
|
234
|
+
log('Re-initializing...');
|
|
221
235
|
__classPrivateFieldSet(this, _AccountTreeController_initialized, false, "f");
|
|
222
236
|
this.init();
|
|
223
237
|
}
|
|
@@ -317,6 +331,7 @@ export class AccountTreeController extends BaseController {
|
|
|
317
331
|
this.update((state) => {
|
|
318
332
|
state.accountTree.selectedAccountGroup = groupId;
|
|
319
333
|
});
|
|
334
|
+
log(`Selected group is now: [${this.state.accountTree.selectedAccountGroup}]`);
|
|
320
335
|
this.messagingSystem.publish(`${controllerName}:selectedAccountGroupChange`, groupId, previousSelectedAccountGroup);
|
|
321
336
|
// Update AccountsController - this will trigger selectedAccountChange event,
|
|
322
337
|
// but our handler is idempotent so it won't cause infinite loop
|
|
@@ -366,6 +381,7 @@ export class AccountTreeController extends BaseController {
|
|
|
366
381
|
// Validate that the name is unique
|
|
367
382
|
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_assertAccountGroupNameIsUnique).call(this, groupId, finalName);
|
|
368
383
|
}
|
|
384
|
+
log(`[${groupId}] Set new name to: "${finalName}" (auto handle conflict: ${autoHandleConflict})`);
|
|
369
385
|
this.update((state) => {
|
|
370
386
|
/* istanbul ignore next */
|
|
371
387
|
if (!state.accountGroupsMetadata[groupId]) {
|
|
@@ -485,6 +501,7 @@ export class AccountTreeController extends BaseController {
|
|
|
485
501
|
* Also clears the backup and sync service state.
|
|
486
502
|
*/
|
|
487
503
|
clearState() {
|
|
504
|
+
log('Clearing state');
|
|
488
505
|
this.update(() => {
|
|
489
506
|
return {
|
|
490
507
|
...getDefaultAccountTreeControllerState(),
|
|
@@ -551,6 +568,7 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
|
|
|
551
568
|
wallet.metadata.name =
|
|
552
569
|
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getKeyringRule).call(this).getDefaultAccountWalletName(wallet);
|
|
553
570
|
}
|
|
571
|
+
log(`[${wallet.id}] Set default name to: "${wallet.metadata.name}"`);
|
|
554
572
|
}
|
|
555
573
|
}, _AccountTreeController_getRuleForWallet = function _AccountTreeController_getRuleForWallet(wallet) {
|
|
556
574
|
switch (wallet.type) {
|
|
@@ -630,6 +648,7 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
|
|
|
630
648
|
} while (proposedNameExists);
|
|
631
649
|
state.accountTree.wallets[walletId].groups[groupId].metadata.name =
|
|
632
650
|
proposedName;
|
|
651
|
+
log(`[${group.id}] Set default name to: "${group.metadata.name}"`);
|
|
633
652
|
// Persist the generated name to ensure consistency
|
|
634
653
|
(_a = state.accountGroupsMetadata)[groupId] ?? (_a[groupId] = {});
|
|
635
654
|
state.accountGroupsMetadata[groupId].name = {
|
|
@@ -654,8 +673,7 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
|
|
|
654
673
|
if (!__classPrivateFieldGet(this, _AccountTreeController_initialized, "f")) {
|
|
655
674
|
return;
|
|
656
675
|
}
|
|
657
|
-
// Check if this account
|
|
658
|
-
// can proceed.
|
|
676
|
+
// Check if this account is already known by the tree to avoid double-insertion.
|
|
659
677
|
if (!__classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").has(account.id)) {
|
|
660
678
|
this.update((state) => {
|
|
661
679
|
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_insert).call(this, state.accountTree.wallets, account);
|
|
@@ -730,6 +748,7 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
|
|
|
730
748
|
const walletId = result.wallet.id;
|
|
731
749
|
let wallet = wallets[walletId];
|
|
732
750
|
if (!wallet) {
|
|
751
|
+
log(`[${walletId}] Added as new wallet`);
|
|
733
752
|
wallets[walletId] = {
|
|
734
753
|
...result.wallet,
|
|
735
754
|
status: 'ready',
|
|
@@ -752,6 +771,7 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
|
|
|
752
771
|
const { type, id } = account;
|
|
753
772
|
const sortOrder = ACCOUNT_TYPE_TO_SORT_ORDER[type];
|
|
754
773
|
if (!group) {
|
|
774
|
+
log(`[${walletId}] Add new group: [${groupId}]`);
|
|
755
775
|
wallet.groups[groupId] = {
|
|
756
776
|
...result.group,
|
|
757
777
|
// Type-wise, we are guaranteed to always have at least 1 account.
|
|
@@ -790,6 +810,7 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
|
|
|
790
810
|
return ((aSortOrder ?? MAX_SORT_ORDER) - (bSortOrder ?? MAX_SORT_ORDER));
|
|
791
811
|
});
|
|
792
812
|
}
|
|
813
|
+
log(`[${groupId}] Add new account: { id: "${account.id}", type: "${account.type}", address: "${account.address}"`);
|
|
793
814
|
// Update the reverse mapping for this account.
|
|
794
815
|
__classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").set(account.id, {
|
|
795
816
|
walletId: wallet.id,
|
|
@@ -823,23 +844,6 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
|
|
|
823
844
|
}
|
|
824
845
|
// Default to the default group in case of errors.
|
|
825
846
|
return __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getDefaultAccountGroupId).call(this, wallets);
|
|
826
|
-
}, _AccountTreeController_handleSelectedAccountChange = function _AccountTreeController_handleSelectedAccountChange(account) {
|
|
827
|
-
const accountMapping = __classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").get(account.id);
|
|
828
|
-
if (!accountMapping) {
|
|
829
|
-
// Account not in tree yet, might be during initialization
|
|
830
|
-
return;
|
|
831
|
-
}
|
|
832
|
-
const { groupId } = accountMapping;
|
|
833
|
-
const previousSelectedAccountGroup = this.state.accountTree.selectedAccountGroup;
|
|
834
|
-
// Idempotent check - if the same group is already selected, do nothing
|
|
835
|
-
if (previousSelectedAccountGroup === groupId) {
|
|
836
|
-
return;
|
|
837
|
-
}
|
|
838
|
-
// Update selectedAccountGroup to match the selected account
|
|
839
|
-
this.update((state) => {
|
|
840
|
-
state.accountTree.selectedAccountGroup = groupId;
|
|
841
|
-
});
|
|
842
|
-
this.messagingSystem.publish(`${controllerName}:selectedAccountGroupChange`, groupId, previousSelectedAccountGroup);
|
|
843
847
|
}, _AccountTreeController_handleMultichainAccountWalletStatusChange = function _AccountTreeController_handleMultichainAccountWalletStatusChange(walletId, walletStatus) {
|
|
844
848
|
this.update((state) => {
|
|
845
849
|
const wallet = state.accountTree.wallets[walletId];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccountTreeController.mjs","sourceRoot":"","sources":["../src/AccountTreeController.ts"],"names":[],"mappings":"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","sourcesContent":["import { AccountWalletType, select } from '@metamask/account-api';\nimport type {\n AccountGroupId,\n AccountWalletId,\n AccountSelector,\n MultichainAccountWalletId,\n AccountGroupType,\n} from '@metamask/account-api';\nimport type { MultichainAccountWalletStatus } from '@metamask/account-api';\nimport { type AccountId } from '@metamask/accounts-controller';\nimport type { StateMetadata } from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type { TraceCallback } from '@metamask/controller-utils';\nimport { isEvmAccountType } from '@metamask/keyring-api';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport { assert } from '@metamask/utils';\n\nimport type { BackupAndSyncEmitAnalyticsEventParams } from './backup-and-sync/analytics';\nimport {\n formatAnalyticsEvent,\n traceFallback,\n} from './backup-and-sync/analytics';\nimport { BackupAndSyncService } from './backup-and-sync/service';\nimport type { BackupAndSyncContext } from './backup-and-sync/types';\nimport type { AccountGroupObject, AccountTypeOrderKey } from './group';\nimport {\n ACCOUNT_TYPE_TO_SORT_ORDER,\n isAccountGroupNameUnique,\n isAccountGroupNameUniqueFromWallet,\n MAX_SORT_ORDER,\n} from './group';\nimport type { Rule } from './rule';\nimport { EntropyRule } from './rules/entropy';\nimport { KeyringRule } from './rules/keyring';\nimport { SnapRule } from './rules/snap';\nimport type {\n AccountTreeControllerConfig,\n AccountTreeControllerInternalBackupAndSyncConfig,\n AccountTreeControllerMessenger,\n AccountTreeControllerState,\n} from './types';\nimport { type AccountWalletObject, type AccountWalletObjectOf } from './wallet';\n\nexport const controllerName = 'AccountTreeController';\n\nconst accountTreeControllerMetadata: StateMetadata<AccountTreeControllerState> =\n {\n accountTree: {\n includeInStateLogs: true,\n persist: false, // We do re-recompute this state everytime.\n anonymous: false,\n usedInUi: true,\n },\n isAccountTreeSyncingInProgress: {\n includeInStateLogs: false,\n persist: false,\n anonymous: false,\n usedInUi: true,\n },\n hasAccountTreeSyncingSyncedAtLeastOnce: {\n includeInStateLogs: true,\n persist: true,\n anonymous: false,\n usedInUi: true,\n },\n accountGroupsMetadata: {\n includeInStateLogs: true,\n persist: true,\n anonymous: false,\n usedInUi: true,\n },\n accountWalletsMetadata: {\n includeInStateLogs: true,\n persist: true,\n anonymous: false,\n usedInUi: true,\n },\n };\n\n/**\n * Gets default state of the `AccountTreeController`.\n *\n * @returns The default state of the `AccountTreeController`.\n */\nexport function getDefaultAccountTreeControllerState(): AccountTreeControllerState {\n return {\n accountTree: {\n wallets: {},\n selectedAccountGroup: '',\n },\n isAccountTreeSyncingInProgress: false,\n hasAccountTreeSyncingSyncedAtLeastOnce: false,\n accountGroupsMetadata: {},\n accountWalletsMetadata: {},\n };\n}\n\n/**\n * Context for an account.\n */\nexport type AccountContext = {\n /**\n * Wallet ID associated to that account.\n */\n walletId: AccountWalletObject['id'];\n\n /**\n * Account group ID associated to that account.\n */\n groupId: AccountGroupObject['id'];\n\n /**\n * Sort order of the account.\n */\n sortOrder: (typeof ACCOUNT_TYPE_TO_SORT_ORDER)[AccountTypeOrderKey];\n};\n\nexport class AccountTreeController extends BaseController<\n typeof controllerName,\n AccountTreeControllerState,\n AccountTreeControllerMessenger\n> {\n readonly #accountIdToContext: Map<AccountId, AccountContext>;\n\n readonly #groupIdToWalletId: Map<AccountGroupId, AccountWalletId>;\n\n /**\n * Service responsible for all backup and sync operations.\n */\n readonly #backupAndSyncService: BackupAndSyncService;\n\n readonly #rules: [EntropyRule, SnapRule, KeyringRule];\n\n readonly #trace: TraceCallback;\n\n readonly #backupAndSyncConfig: AccountTreeControllerInternalBackupAndSyncConfig;\n\n #initialized: boolean;\n\n /**\n * Constructor for AccountTreeController.\n *\n * @param options - The controller options.\n * @param options.messenger - The messenger object.\n * @param options.state - Initial state to set on this controller\n * @param options.config - Optional configuration for the controller.\n */\n\n constructor({\n messenger,\n state,\n config,\n }: {\n messenger: AccountTreeControllerMessenger;\n state?: Partial<AccountTreeControllerState>;\n config?: AccountTreeControllerConfig;\n }) {\n super({\n messenger,\n name: controllerName,\n metadata: accountTreeControllerMetadata,\n state: {\n ...getDefaultAccountTreeControllerState(),\n ...state,\n },\n });\n\n // This will be set to true upon the first `init` call.\n this.#initialized = false;\n\n // Reverse map to allow fast node access from an account ID.\n this.#accountIdToContext = new Map();\n\n // Reverse map to allow fast wallet node access from a group ID.\n this.#groupIdToWalletId = new Map();\n\n // Rules to apply to construct the wallets tree.\n this.#rules = [\n // 1. We group by entropy-source\n new EntropyRule(this.messagingSystem),\n // 2. We group by Snap ID\n new SnapRule(this.messagingSystem),\n // 3. We group by wallet type (this rule cannot fail and will group all non-matching accounts)\n new KeyringRule(this.messagingSystem),\n ];\n\n // Initialize trace function\n this.#trace = config?.trace ?? traceFallback;\n\n // Initialize backup and sync config\n this.#backupAndSyncConfig = {\n emitAnalyticsEventFn: (event: BackupAndSyncEmitAnalyticsEventParams) => {\n return (\n config?.backupAndSync?.onBackupAndSyncEvent &&\n config.backupAndSync.onBackupAndSyncEvent(formatAnalyticsEvent(event))\n );\n },\n };\n\n // Initialize the backup and sync service\n this.#backupAndSyncService = new BackupAndSyncService(\n this.#createBackupAndSyncContext(),\n );\n\n this.messagingSystem.subscribe(\n 'AccountsController:accountAdded',\n (account) => {\n this.#handleAccountAdded(account);\n },\n );\n\n this.messagingSystem.subscribe(\n 'AccountsController:accountRemoved',\n (accountId) => {\n this.#handleAccountRemoved(accountId);\n },\n );\n\n this.messagingSystem.subscribe(\n 'AccountsController:selectedAccountChange',\n (account) => {\n this.#handleSelectedAccountChange(account);\n },\n );\n\n this.messagingSystem.subscribe(\n 'UserStorageController:stateChange',\n (userStorageControllerState) => {\n this.#backupAndSyncService.handleUserStorageStateChange(\n userStorageControllerState,\n );\n },\n );\n\n this.messagingSystem.subscribe(\n 'MultichainAccountService:walletStatusChange',\n (walletId, status) => {\n this.#handleMultichainAccountWalletStatusChange(walletId, status);\n },\n );\n\n this.#registerMessageHandlers();\n }\n\n /**\n * Initialize the controller's state.\n *\n * It constructs the initial state of the account tree (tree nodes, nodes\n * names, metadata, etc..) and will automatically update the controller's\n * state with it.\n */\n init() {\n if (this.#initialized) {\n // We prevent re-initilializing the state multiple times. Though, we can use\n // `reinit` to re-init everything from scratch.\n return;\n }\n\n const wallets: AccountTreeControllerState['accountTree']['wallets'] = {};\n\n // Clear mappings for fresh rebuild.\n this.#accountIdToContext.clear();\n this.#groupIdToWalletId.clear();\n\n // Keep the current selected group to check if it's still part of the tree\n // after rebuilding it.\n const previousSelectedAccountGroup =\n this.state.accountTree.selectedAccountGroup;\n\n // For now, we always re-compute all wallets, we do not re-use the existing state.\n for (const account of this.#listAccounts()) {\n this.#insert(wallets, account);\n }\n\n // Once we have the account tree, we can apply persisted metadata (names + UI states).\n let previousSelectedAccountGroupStillExists = false;\n this.update((state) => {\n state.accountTree.wallets = wallets;\n\n // Apply group metadata within the state update\n for (const wallet of Object.values(state.accountTree.wallets)) {\n this.#applyAccountWalletMetadata(state, wallet.id);\n\n // Used for default group default names (so we use human-indexing here).\n let nextNaturalNameIndex = 1;\n for (const group of Object.values(wallet.groups)) {\n this.#applyAccountGroupMetadata(\n state,\n wallet.id,\n group.id,\n // FIXME: We should not need this kind of logic if we were not inserting accounts\n // 1 by 1. Instead, we should be inserting wallets and groups directly. This would\n // allow us to naturally insert a group in the tree AND update its metadata right\n // away...\n // But here, we have to wait for the entire group to be ready before updating\n // its metadata (mainly because we're dealing with single accounts rather than entire\n // groups).\n // That is why we need this kind of extra parameter.\n nextNaturalNameIndex,\n );\n\n if (group.id === previousSelectedAccountGroup) {\n previousSelectedAccountGroupStillExists = true;\n }\n nextNaturalNameIndex += 1;\n }\n }\n\n if (\n !previousSelectedAccountGroupStillExists ||\n previousSelectedAccountGroup === ''\n ) {\n // No group is selected yet OR group no longer exists, re-sync with the\n // AccountsController.\n state.accountTree.selectedAccountGroup =\n this.#getDefaultSelectedAccountGroup(wallets);\n }\n });\n\n // We still compare the previous and new value, the previous one could have been\n // an empty string and `#getDefaultSelectedAccountGroup` could also return an\n // empty string too, thus, we would re-use the same value here again. In that\n // case, no need to fire any event.\n if (\n previousSelectedAccountGroup !==\n this.state.accountTree.selectedAccountGroup\n ) {\n this.messagingSystem.publish(\n `${controllerName}:selectedAccountGroupChange`,\n this.state.accountTree.selectedAccountGroup,\n previousSelectedAccountGroup,\n );\n }\n\n this.#initialized = true;\n }\n\n /**\n * Re-initialize the controller's state.\n *\n * This is done in one single (atomic) `update` block to avoid having a temporary\n * cleared state. Use this when you need to force a full re-init even if already initialized.\n */\n reinit() {\n this.#initialized = false;\n this.init();\n }\n\n /**\n * Rule for entropy-base wallets.\n *\n * @returns The rule for entropy-based wallets.\n */\n #getEntropyRule(): EntropyRule {\n return this.#rules[0];\n }\n\n /**\n * Rule for Snap-base wallets.\n *\n * @returns The rule for snap-based wallets.\n */\n #getSnapRule(): SnapRule {\n return this.#rules[1];\n }\n\n /**\n * Rule for keyring-base wallets.\n *\n * This rule acts as a fallback and never fails since all accounts\n * comes from a keyring anyway.\n *\n * @returns The fallback rule for every accounts that did not match\n * any other rules.\n */\n #getKeyringRule(): KeyringRule {\n return this.#rules[2];\n }\n\n /**\n * Applies wallet metadata updates (name) by checking the persistent state\n * first, and then fallbacks to default values (based on the wallet's\n * type).\n *\n * @param state Controller state to update for persistence.\n * @param walletId The wallet ID to update.\n */\n #applyAccountWalletMetadata(\n state: AccountTreeControllerState,\n walletId: AccountWalletId,\n ) {\n const wallet = state.accountTree.wallets[walletId];\n const persistedMetadata = state.accountWalletsMetadata[walletId];\n\n // Apply persisted name if available (including empty strings)\n if (persistedMetadata?.name !== undefined) {\n wallet.metadata.name = persistedMetadata.name.value;\n } else if (!wallet.metadata.name) {\n // Generate default name if none exists\n if (wallet.type === AccountWalletType.Entropy) {\n wallet.metadata.name =\n this.#getEntropyRule().getDefaultAccountWalletName(wallet);\n } else if (wallet.type === AccountWalletType.Snap) {\n wallet.metadata.name =\n this.#getSnapRule().getDefaultAccountWalletName(wallet);\n } else {\n wallet.metadata.name =\n this.#getKeyringRule().getDefaultAccountWalletName(wallet);\n }\n }\n }\n\n /**\n * Gets the appropriate rule instance for a given wallet type.\n *\n * @param wallet - The wallet object to get the rule for.\n * @returns The rule instance that handles the wallet's type.\n */\n #getRuleForWallet<WalletType extends AccountWalletType>(\n wallet: AccountWalletObjectOf<WalletType>,\n ): Rule<WalletType, AccountGroupType> {\n switch (wallet.type) {\n case AccountWalletType.Entropy:\n return this.#getEntropyRule() as unknown as Rule<\n WalletType,\n AccountGroupType\n >;\n case AccountWalletType.Snap:\n return this.#getSnapRule() as unknown as Rule<\n WalletType,\n AccountGroupType\n >;\n default:\n return this.#getKeyringRule() as unknown as Rule<\n WalletType,\n AccountGroupType\n >;\n }\n }\n\n /**\n * Applies group metadata updates (name, pinned, hidden flags) by checking\n * the persistent state first, and then fallbacks to default values (based\n * on the wallet's\n * type).\n *\n * @param state Controller state to update for persistence.\n * @param walletId The wallet ID containing the group.\n * @param groupId The account group ID to update.\n * @param nextNaturalNameIndex The next natural name index for this group (only used for default names).\n */\n #applyAccountGroupMetadata(\n state: AccountTreeControllerState,\n walletId: AccountWalletId,\n groupId: AccountGroupId,\n nextNaturalNameIndex?: number,\n ) {\n const wallet = state.accountTree.wallets[walletId];\n const group = wallet.groups[groupId];\n const persistedGroupMetadata = state.accountGroupsMetadata[group.id];\n\n // Apply persisted name if available (including empty strings)\n if (persistedGroupMetadata?.name !== undefined) {\n state.accountTree.wallets[walletId].groups[groupId].metadata.name =\n persistedGroupMetadata.name.value;\n } else if (!group.metadata.name) {\n // Get the appropriate rule for this wallet type\n const rule = this.#getRuleForWallet(wallet);\n\n // Get the prefix for groups of this wallet\n const namePrefix = rule.getDefaultAccountGroupPrefix(wallet);\n\n // Skip computed names for now - use default naming with per-wallet logic\n // TODO: Implement computed names in a future iteration\n\n // Parse the highest account index being used (similar to accounts-controller)\n let highestNameIndex = 0;\n for (const { id: otherGroupId } of Object.values(\n wallet.groups,\n ) as AccountGroupObject[]) {\n // Skip the current group being processed\n if (otherGroupId === groupId) {\n continue;\n }\n\n // We always get the name from the persisted map, since `init` will clear the\n // `state.accountTree.wallets`, thus, given empty `group.metadata.name`.\n // NOTE: If the other group has not been named yet, we just use an empty name.\n const otherGroupName =\n state.accountGroupsMetadata[otherGroupId]?.name?.value ?? '';\n\n // Parse the existing group name to extract the numeric index\n const nameMatch = otherGroupName.match(/account\\s+(\\d+)$/iu);\n if (nameMatch) {\n const nameIndex = parseInt(nameMatch[1], 10);\n if (nameIndex > highestNameIndex) {\n highestNameIndex = nameIndex;\n }\n }\n }\n\n // We just use the highest known index no matter the wallet type.\n //\n // For entropy-based wallets (bip44), if a multichain account group with group index 1\n // is inserted before another one with group index 0, then the naming will be:\n // - \"Account 1\" (group index 1)\n // - \"Account 2\" (group index 0)\n // This naming makes more sense for the end-user.\n //\n // For other type of wallets, since those wallets can create arbitrary gaps, we still\n // rely on the highest know index to avoid back-filling account with \"old names\".\n let proposedNameIndex = Math.max(\n // Use + 1 to use the next available index.\n highestNameIndex + 1,\n // In case all accounts have been renamed differently than the usual \"Account <index>\"\n // pattern, we want to use the next \"natural\" index, which is just the number of groups\n // in that wallet (e.g. [\"Account A\", \"Another Account\"], next natural index would be\n // \"Account 3\" in this case).\n nextNaturalNameIndex ?? Object.keys(wallet.groups).length,\n );\n\n // Find a unique name by checking for conflicts and incrementing if needed\n let proposedNameExists: boolean;\n let proposedName = '';\n do {\n proposedName = `${namePrefix} ${proposedNameIndex}`;\n\n // Check if this name already exists in the wallet (excluding current group)\n proposedNameExists = !isAccountGroupNameUniqueFromWallet(\n wallet,\n group.id,\n proposedName,\n );\n\n /* istanbul ignore next */\n if (proposedNameExists) {\n proposedNameIndex += 1; // Try next number\n }\n } while (proposedNameExists);\n\n state.accountTree.wallets[walletId].groups[groupId].metadata.name =\n proposedName;\n\n // Persist the generated name to ensure consistency\n state.accountGroupsMetadata[groupId] ??= {};\n state.accountGroupsMetadata[groupId].name = {\n value: proposedName,\n // The `lastUpdatedAt` field is used for backup and sync, when comparing local names\n // with backed up names. In this case, the generated name should never take precedence\n // over a user-defined name, so we set `lastUpdatedAt` to 0.\n lastUpdatedAt: 0,\n };\n }\n\n // Apply persisted UI states\n if (persistedGroupMetadata?.pinned?.value !== undefined) {\n group.metadata.pinned = persistedGroupMetadata.pinned.value;\n }\n if (persistedGroupMetadata?.hidden?.value !== undefined) {\n group.metadata.hidden = persistedGroupMetadata.hidden.value;\n }\n }\n\n /**\n * Gets the account wallet object from its ID.\n *\n * @param walletId - Account wallet ID.\n * @returns The account wallet object if found, undefined otherwise.\n */\n getAccountWalletObject(\n walletId: AccountWalletId,\n ): AccountWalletObject | undefined {\n const wallet = this.state.accountTree.wallets[walletId];\n if (!wallet) {\n return undefined;\n }\n\n return wallet;\n }\n\n /**\n * Gets all account wallet objects.\n *\n * @returns All account wallet objects.\n */\n getAccountWalletObjects(): AccountWalletObject[] {\n return Object.values(this.state.accountTree.wallets);\n }\n\n /**\n * Gets all underlying accounts from the currently selected account\n * group.\n *\n * It also support account selector, which allows to filter specific\n * accounts given some criterias (account type, address, scopes, etc...).\n *\n * @param selector - Optional account selector.\n * @returns Underlying accounts for the currently selected account (filtered\n * by the selector if provided).\n */\n getAccountsFromSelectedAccountGroup(\n selector?: AccountSelector<InternalAccount>,\n ) {\n const groupId = this.getSelectedAccountGroup();\n if (!groupId) {\n return [];\n }\n\n const group = this.getAccountGroupObject(groupId);\n // We should never reach this part, so we cannot cover it either.\n /* istanbul ignore next */\n if (!group) {\n return [];\n }\n\n const accounts: InternalAccount[] = [];\n for (const id of group.accounts) {\n const account = this.messagingSystem.call(\n 'AccountsController:getAccount',\n id,\n );\n\n // For now, we're filtering undefined account, but I believe\n // throwing would be more appropriate here.\n if (account) {\n accounts.push(account);\n }\n }\n\n return selector ? select(accounts, selector) : accounts;\n }\n\n /**\n * Gets the account group object from its ID.\n *\n * @param groupId - Account group ID.\n * @returns The account group object if found, undefined otherwise.\n */\n getAccountGroupObject(\n groupId: AccountGroupId,\n ): AccountGroupObject | undefined {\n const walletId = this.#groupIdToWalletId.get(groupId);\n if (!walletId) {\n return undefined;\n }\n\n const wallet = this.getAccountWalletObject(walletId);\n return wallet?.groups[groupId];\n }\n\n /**\n * Handles \"AccountsController:accountAdded\" event to insert\n * new accounts into the tree.\n *\n * @param account - New account.\n */\n #handleAccountAdded(account: InternalAccount) {\n // We wait for the first `init` to be called to actually build up the tree and\n // mutate it. We expect the caller to first update the `AccountsController` state\n // to force the migration of accounts, and then call `init`.\n if (!this.#initialized) {\n return;\n }\n\n // Check if this account got already added by `#initAtLeastOnce`, if not, then we\n // can proceed.\n if (!this.#accountIdToContext.has(account.id)) {\n this.update((state) => {\n this.#insert(state.accountTree.wallets, account);\n\n const context = this.#accountIdToContext.get(account.id);\n if (context) {\n const { walletId, groupId } = context;\n\n const wallet = state.accountTree.wallets[walletId];\n if (wallet) {\n this.#applyAccountWalletMetadata(state, walletId);\n this.#applyAccountGroupMetadata(state, walletId, groupId);\n }\n }\n });\n\n this.messagingSystem.publish(\n `${controllerName}:accountTreeChange`,\n this.state.accountTree,\n );\n }\n }\n\n /**\n * Handles \"AccountsController:accountRemoved\" event to remove\n * given account from the tree.\n *\n * @param accountId - Removed account ID.\n */\n #handleAccountRemoved(accountId: AccountId) {\n // We wait for the first `init` to be called to actually build up the tree and\n // mutate it. We expect the caller to first update the `AccountsController` state\n // to force the migration of accounts, and then call `init`.\n if (!this.#initialized) {\n return;\n }\n\n const context = this.#accountIdToContext.get(accountId);\n\n if (context) {\n const { walletId, groupId } = context;\n\n const previousSelectedAccountGroup =\n this.state.accountTree.selectedAccountGroup;\n let selectedAccountGroupChanged = false;\n\n this.update((state) => {\n const accounts =\n state.accountTree.wallets[walletId]?.groups[groupId]?.accounts;\n\n if (accounts) {\n const index = accounts.indexOf(accountId);\n if (index !== -1) {\n accounts.splice(index, 1);\n\n // Check if we need to update selectedAccountGroup after removal\n if (\n state.accountTree.selectedAccountGroup === groupId &&\n accounts.length === 0\n ) {\n // The currently selected group is now empty, find a new group to select\n const newSelectedAccountGroup = this.#getDefaultAccountGroupId(\n state.accountTree.wallets,\n );\n state.accountTree.selectedAccountGroup = newSelectedAccountGroup;\n selectedAccountGroupChanged =\n newSelectedAccountGroup !== previousSelectedAccountGroup;\n }\n }\n if (accounts.length === 0) {\n this.#pruneEmptyGroupAndWallet(state, walletId, groupId);\n }\n }\n });\n this.messagingSystem.publish(\n `${controllerName}:accountTreeChange`,\n this.state.accountTree,\n );\n\n // Emit selectedAccountGroupChange event if the selected group changed\n if (selectedAccountGroupChanged) {\n this.messagingSystem.publish(\n `${controllerName}:selectedAccountGroupChange`,\n this.state.accountTree.selectedAccountGroup,\n previousSelectedAccountGroup,\n );\n }\n\n // Clear reverse-mapping for that account.\n this.#accountIdToContext.delete(accountId);\n }\n }\n\n /**\n * Helper method to prune a group if it holds no accounts and additionally\n * prune the wallet if it holds no groups. This action should take place\n * after a singular account removal.\n *\n * NOTE: This method should only be used for a group that we know to be empty.\n *\n * @param state - The AccountTreeController state to prune.\n * @param walletId - The wallet ID to prune, the wallet should be the parent of the associated group that holds the removed account.\n * @param groupId - The group ID to prune, the group should be the parent of the associated account that was removed.\n * @returns The updated state.\n */\n #pruneEmptyGroupAndWallet(\n state: AccountTreeControllerState,\n walletId: AccountWalletId,\n groupId: AccountGroupId,\n ) {\n const { wallets } = state.accountTree;\n\n delete wallets[walletId].groups[groupId];\n this.#groupIdToWalletId.delete(groupId);\n\n // Clean up metadata for the pruned group\n delete state.accountGroupsMetadata[groupId];\n\n if (Object.keys(wallets[walletId].groups).length === 0) {\n delete wallets[walletId];\n }\n return state;\n }\n\n /**\n * Insert an account inside an account tree.\n *\n * We go over multiple rules to try to \"match\" the account following\n * specific criterias. If a rule \"matches\" an account, then this\n * account get added into its proper account wallet and account group.\n *\n * @param wallets - Account tree.\n * @param account - The account to be inserted.\n */\n #insert(\n wallets: AccountTreeControllerState['accountTree']['wallets'],\n account: InternalAccount,\n ) {\n const result =\n this.#getEntropyRule().match(account) ??\n this.#getSnapRule().match(account) ??\n this.#getKeyringRule().match(account); // This one cannot fail.\n\n // Update controller's state.\n const walletId = result.wallet.id;\n let wallet = wallets[walletId];\n if (!wallet) {\n wallets[walletId] = {\n ...result.wallet,\n status: 'ready',\n groups: {},\n metadata: {\n name: '', // Will get updated later.\n ...result.wallet.metadata,\n },\n // We do need to type-cast since we're not narrowing `result` with\n // the union tag `result.wallet.type`.\n } as AccountWalletObject;\n wallet = wallets[walletId];\n\n // Trigger atomic sync for new wallet (only for entropy wallets)\n if (wallet.type === AccountWalletType.Entropy) {\n this.#backupAndSyncService.enqueueSingleWalletSync(walletId);\n }\n }\n\n const groupId = result.group.id;\n let group = wallet.groups[groupId];\n const { type, id } = account;\n const sortOrder = ACCOUNT_TYPE_TO_SORT_ORDER[type];\n\n if (!group) {\n wallet.groups[groupId] = {\n ...result.group,\n // Type-wise, we are guaranteed to always have at least 1 account.\n accounts: [id],\n metadata: {\n name: '',\n ...{ pinned: false, hidden: false }, // Default UI states\n ...result.group.metadata, // Allow rules to override defaults\n },\n // We do need to type-cast since we're not narrowing `result` with\n // the union tag `result.group.type`.\n } as AccountGroupObject;\n group = wallet.groups[groupId];\n\n // Map group ID to its containing wallet ID for efficient direct access\n this.#groupIdToWalletId.set(groupId, walletId);\n\n // Trigger atomic sync for new group (only for entropy wallets)\n if (wallet.type === AccountWalletType.Entropy) {\n this.#backupAndSyncService.enqueueSingleGroupSync(groupId);\n }\n } else {\n group.accounts.push(id);\n // We need to do this at every insertion because race conditions can happen\n // during the account creation process where one provider completes before the other.\n // The discovery process in the service can also lead to some accounts being created \"out of order\".\n const { accounts } = group;\n accounts.sort(\n /* istanbul ignore next: Comparator branch execution (a===id vs b===id)\n * and return attribution vary across engines; final ordering is covered\n * by behavior tests. Ignoring the entire comparator avoids flaky line\n * coverage without reducing scenario coverage.\n */\n (a, b) => {\n const aSortOrder =\n a === id ? sortOrder : this.#accountIdToContext.get(a)?.sortOrder;\n const bSortOrder =\n b === id ? sortOrder : this.#accountIdToContext.get(b)?.sortOrder;\n return (\n (aSortOrder ?? MAX_SORT_ORDER) - (bSortOrder ?? MAX_SORT_ORDER)\n );\n },\n );\n }\n\n // Update the reverse mapping for this account.\n this.#accountIdToContext.set(account.id, {\n walletId: wallet.id,\n groupId: group.id,\n sortOrder,\n });\n }\n\n /**\n * List all internal accounts.\n *\n * @returns The list of all internal accounts.\n */\n #listAccounts(): InternalAccount[] {\n return this.messagingSystem.call(\n 'AccountsController:listMultichainAccounts',\n );\n }\n\n /**\n * Asserts that a group exists in the current account tree.\n *\n * @param groupId - The account group ID to validate.\n * @throws Error if the group does not exist.\n */\n #assertAccountGroupExists(groupId: AccountGroupId): void {\n const exists = this.#groupIdToWalletId.has(groupId);\n if (!exists) {\n throw new Error(`Account group with ID \"${groupId}\" not found in tree`);\n }\n }\n\n /**\n * Asserts that a wallet exists in the current account tree.\n *\n * @param walletId - The account wallet ID to validate.\n * @throws Error if the wallet does not exist.\n */\n #assertAccountWalletExists(walletId: AccountWalletId): void {\n const exists = Boolean(this.state.accountTree.wallets[walletId]);\n if (!exists) {\n throw new Error(`Account wallet with ID \"${walletId}\" not found in tree`);\n }\n }\n\n /**\n * Asserts that an account group name is unique within the same wallet.\n *\n * @param groupId - The account group ID to exclude from the check.\n * @param name - The name to validate for uniqueness.\n * @throws Error if the name already exists in another group within the same wallet.\n */\n #assertAccountGroupNameIsUnique(groupId: AccountGroupId, name: string): void {\n if (!isAccountGroupNameUnique(this.state, groupId, name)) {\n throw new Error('Account group name already exists');\n }\n }\n\n /**\n * Gets the currently selected account group ID.\n *\n * @returns The selected account group ID or empty string if none selected.\n */\n getSelectedAccountGroup(): AccountGroupId | '' {\n return this.state.accountTree.selectedAccountGroup;\n }\n\n /**\n * Sets the selected account group and updates the AccountsController selectedAccount accordingly.\n *\n * @param groupId - The account group ID to select.\n */\n setSelectedAccountGroup(groupId: AccountGroupId): void {\n const previousSelectedAccountGroup =\n this.state.accountTree.selectedAccountGroup;\n\n // Idempotent check - if the same group is already selected, do nothing\n if (previousSelectedAccountGroup === groupId) {\n return;\n }\n\n // Find the first account in this group to select\n const accountToSelect = this.#getDefaultAccountFromAccountGroupId(groupId);\n if (!accountToSelect) {\n throw new Error(`No accounts found in group: ${groupId}`);\n }\n\n // Update our state first\n this.update((state) => {\n state.accountTree.selectedAccountGroup = groupId;\n });\n this.messagingSystem.publish(\n `${controllerName}:selectedAccountGroupChange`,\n groupId,\n previousSelectedAccountGroup,\n );\n\n // Update AccountsController - this will trigger selectedAccountChange event,\n // but our handler is idempotent so it won't cause infinite loop\n this.messagingSystem.call(\n 'AccountsController:setSelectedAccount',\n accountToSelect,\n );\n }\n\n /**\n * Initializes the selectedAccountGroup based on the currently selected account from AccountsController.\n *\n * @param wallets - Wallets object to use for fallback logic\n * @returns The default selected account group ID or empty string if none selected.\n */\n #getDefaultSelectedAccountGroup(wallets: {\n [walletId: AccountWalletId]: AccountWalletObject;\n }): AccountGroupId | '' {\n const selectedAccount = this.messagingSystem.call(\n 'AccountsController:getSelectedMultichainAccount',\n );\n if (selectedAccount && selectedAccount.id) {\n const accountMapping = this.#accountIdToContext.get(selectedAccount.id);\n if (accountMapping) {\n const { groupId } = accountMapping;\n\n return groupId;\n }\n }\n\n // Default to the default group in case of errors.\n return this.#getDefaultAccountGroupId(wallets);\n }\n\n /**\n * Handles selected account change from AccountsController.\n * Updates selectedAccountGroup to match the selected account.\n *\n * @param account - The newly selected account.\n */\n #handleSelectedAccountChange(account: InternalAccount): void {\n const accountMapping = this.#accountIdToContext.get(account.id);\n if (!accountMapping) {\n // Account not in tree yet, might be during initialization\n return;\n }\n\n const { groupId } = accountMapping;\n const previousSelectedAccountGroup =\n this.state.accountTree.selectedAccountGroup;\n\n // Idempotent check - if the same group is already selected, do nothing\n if (previousSelectedAccountGroup === groupId) {\n return;\n }\n\n // Update selectedAccountGroup to match the selected account\n this.update((state) => {\n state.accountTree.selectedAccountGroup = groupId;\n });\n this.messagingSystem.publish(\n `${controllerName}:selectedAccountGroupChange`,\n groupId,\n previousSelectedAccountGroup,\n );\n }\n\n /**\n * Handles multichain account wallet status change from\n * the MultichainAccountService.\n *\n * @param walletId - Multichain account wallet ID.\n * @param walletStatus - New multichain account wallet status.\n */\n #handleMultichainAccountWalletStatusChange(\n walletId: MultichainAccountWalletId,\n walletStatus: MultichainAccountWalletStatus,\n ): void {\n this.update((state) => {\n const wallet = state.accountTree.wallets[walletId];\n\n if (wallet) {\n wallet.status = walletStatus;\n }\n });\n }\n\n /**\n * Gets account group object.\n *\n * @param groupId - The account group ID.\n * @returns The account group or undefined if not found.\n */\n #getAccountGroup(groupId: AccountGroupId): AccountGroupObject | undefined {\n const found = Object.values(this.state.accountTree.wallets).find(\n (wallet) => wallet.groups[groupId] !== undefined,\n );\n\n return found?.groups[groupId];\n }\n\n /**\n * Gets the default account for specified group.\n *\n * @param groupId - The account group ID.\n * @returns The first account ID in the group, or undefined if no accounts found.\n */\n #getDefaultAccountFromAccountGroupId(\n groupId: AccountGroupId,\n ): AccountId | undefined {\n const group = this.#getAccountGroup(groupId);\n\n if (group) {\n let candidate;\n for (const id of group.accounts) {\n const account = this.messagingSystem.call(\n 'AccountsController:getAccount',\n id,\n );\n\n if (!candidate) {\n candidate = id;\n }\n if (account && isEvmAccountType(account.type)) {\n // EVM accounts have a higher priority, so if we find any, we just\n // use that account!\n return account.id;\n }\n }\n\n return candidate;\n }\n\n return undefined;\n }\n\n /**\n * Gets the default group id, which is either, the first non-empty group that contains an EVM account or\n * just the first non-empty group with any accounts.\n *\n * @param wallets - The wallets object to search.\n * @returns The ID of the first non-empty group, or an empty string if no groups are found.\n */\n #getDefaultAccountGroupId(wallets: {\n [walletId: AccountWalletId]: AccountWalletObject;\n }): AccountGroupId | '' {\n let candidate: AccountGroupId | '' = '';\n\n for (const wallet of Object.values(wallets)) {\n for (const group of Object.values(wallet.groups)) {\n // We only update the candidate with the first non-empty group, but still\n // try to find a group that contains an EVM account (the `candidate` is\n // our fallback).\n if (candidate === '' && group.accounts.length > 0) {\n candidate = group.id;\n }\n\n for (const id of group.accounts) {\n const account = this.messagingSystem.call(\n 'AccountsController:getAccount',\n id,\n );\n\n if (account && isEvmAccountType(account.type)) {\n // EVM accounts have a higher priority, so if we find any, we just\n // use that group!\n return group.id;\n }\n }\n }\n }\n return candidate;\n }\n\n /**\n * Resolves name conflicts by adding a suffix to make the name unique.\n *\n * @internal\n * @param wallet - The wallet to check within.\n * @param groupId - The account group ID to exclude from the check.\n * @param name - The desired name that has a conflict.\n * @returns A unique name with suffix added if necessary.\n */\n resolveNameConflict(\n wallet: AccountWalletObject,\n groupId: AccountGroupId,\n name: string,\n ): string {\n let suffix = 2;\n let candidateName = `${name} (${suffix})`;\n\n // Keep incrementing suffix until we find a unique name\n while (\n !isAccountGroupNameUniqueFromWallet(wallet, groupId, candidateName)\n ) {\n suffix += 1;\n candidateName = `${name} (${suffix})`;\n }\n\n return candidateName;\n }\n\n /**\n * Sets a custom name for an account group.\n *\n * @param groupId - The account group ID.\n * @param name - The custom name to set.\n * @param autoHandleConflict - If true, automatically resolves name conflicts by adding a suffix. If false, throws on conflicts.\n * @throws If the account group ID is not found in the current tree.\n * @throws If the account group name already exists and autoHandleConflict is false.\n */\n setAccountGroupName(\n groupId: AccountGroupId,\n name: string,\n autoHandleConflict: boolean = false,\n ): void {\n // Validate that the group exists in the current tree\n this.#assertAccountGroupExists(groupId);\n\n const walletId = this.#groupIdToWalletId.get(groupId);\n assert(walletId, `Account group with ID \"${groupId}\" not found in tree`);\n\n const wallet = this.state.accountTree.wallets[walletId];\n let finalName = name;\n\n // Handle name conflicts based on the autoHandleConflict flag\n if (\n autoHandleConflict &&\n !isAccountGroupNameUniqueFromWallet(wallet, groupId, name)\n ) {\n finalName = this.resolveNameConflict(wallet, groupId, name);\n } else {\n // Validate that the name is unique\n this.#assertAccountGroupNameIsUnique(groupId, finalName);\n }\n\n this.update((state) => {\n /* istanbul ignore next */\n if (!state.accountGroupsMetadata[groupId]) {\n state.accountGroupsMetadata[groupId] = {};\n }\n\n // Update persistent metadata\n state.accountGroupsMetadata[groupId].name = {\n value: finalName,\n lastUpdatedAt: Date.now(),\n };\n\n // Update tree node directly using efficient mapping\n state.accountTree.wallets[walletId].groups[groupId].metadata.name =\n finalName;\n });\n\n // Trigger atomic sync for group rename (only for groups from entropy wallets)\n if (wallet.type === AccountWalletType.Entropy) {\n this.#backupAndSyncService.enqueueSingleGroupSync(groupId);\n }\n }\n\n /**\n * Sets a custom name for an account wallet.\n *\n * @param walletId - The account wallet ID.\n * @param name - The custom name to set.\n * @throws If the account wallet ID is not found in the current tree.\n */\n setAccountWalletName(walletId: AccountWalletId, name: string): void {\n // Validate that the wallet exists in the current tree\n this.#assertAccountWalletExists(walletId);\n\n this.update((state) => {\n // Update persistent metadata\n state.accountWalletsMetadata[walletId] ??= {};\n state.accountWalletsMetadata[walletId].name = {\n value: name,\n lastUpdatedAt: Date.now(),\n };\n\n // Update tree node directly\n state.accountTree.wallets[walletId].metadata.name = name;\n });\n\n // Trigger atomic sync for wallet rename (only for groups from entropy wallets)\n if (\n this.state.accountTree.wallets[walletId].type ===\n AccountWalletType.Entropy\n ) {\n this.#backupAndSyncService.enqueueSingleWalletSync(walletId);\n }\n }\n\n /**\n * Toggles the pinned state of an account group.\n *\n * @param groupId - The account group ID.\n * @param pinned - Whether the group should be pinned.\n * @throws If the account group ID is not found in the current tree.\n */\n setAccountGroupPinned(groupId: AccountGroupId, pinned: boolean): void {\n // Validate that the group exists in the current tree\n this.#assertAccountGroupExists(groupId);\n\n const walletId = this.#groupIdToWalletId.get(groupId);\n\n this.update((state) => {\n /* istanbul ignore next */\n if (!state.accountGroupsMetadata[groupId]) {\n state.accountGroupsMetadata[groupId] = {};\n }\n\n // Update persistent metadata\n state.accountGroupsMetadata[groupId].pinned = {\n value: pinned,\n lastUpdatedAt: Date.now(),\n };\n\n // Update tree node directly using efficient mapping\n if (walletId) {\n state.accountTree.wallets[walletId].groups[groupId].metadata.pinned =\n pinned;\n }\n });\n\n // Trigger atomic sync for group pinning (only for groups from entropy wallets)\n if (\n walletId &&\n this.state.accountTree.wallets[walletId].type ===\n AccountWalletType.Entropy\n ) {\n this.#backupAndSyncService.enqueueSingleGroupSync(groupId);\n }\n }\n\n /**\n * Toggles the hidden state of an account group.\n *\n * @param groupId - The account group ID.\n * @param hidden - Whether the group should be hidden.\n * @throws If the account group ID is not found in the current tree.\n */\n setAccountGroupHidden(groupId: AccountGroupId, hidden: boolean): void {\n // Validate that the group exists in the current tree\n this.#assertAccountGroupExists(groupId);\n\n const walletId = this.#groupIdToWalletId.get(groupId);\n\n this.update((state) => {\n /* istanbul ignore next */\n if (!state.accountGroupsMetadata[groupId]) {\n state.accountGroupsMetadata[groupId] = {};\n }\n\n // Update persistent metadata\n state.accountGroupsMetadata[groupId].hidden = {\n value: hidden,\n lastUpdatedAt: Date.now(),\n };\n\n // Update tree node directly using efficient mapping\n if (walletId) {\n state.accountTree.wallets[walletId].groups[groupId].metadata.hidden =\n hidden;\n }\n });\n\n // Trigger atomic sync for group hiding (only for groups from entropy wallets)\n if (\n walletId &&\n this.state.accountTree.wallets[walletId].type ===\n AccountWalletType.Entropy\n ) {\n this.#backupAndSyncService.enqueueSingleGroupSync(groupId);\n }\n }\n\n /**\n * Clears the controller state and resets to default values.\n * Also clears the backup and sync service state.\n */\n clearState(): void {\n this.update(() => {\n return {\n ...getDefaultAccountTreeControllerState(),\n };\n });\n this.#backupAndSyncService.clearState();\n\n // So we know we have to call `init` again.\n this.#initialized = false;\n }\n\n /**\n * Registers message handlers for the AccountTreeController.\n */\n #registerMessageHandlers(): void {\n this.messagingSystem.registerActionHandler(\n `${controllerName}:getSelectedAccountGroup`,\n this.getSelectedAccountGroup.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:setSelectedAccountGroup`,\n this.setSelectedAccountGroup.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:getAccountsFromSelectedAccountGroup`,\n this.getAccountsFromSelectedAccountGroup.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:setAccountWalletName`,\n this.setAccountWalletName.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:setAccountGroupName`,\n this.setAccountGroupName.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:setAccountGroupPinned`,\n this.setAccountGroupPinned.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:setAccountGroupHidden`,\n this.setAccountGroupHidden.bind(this),\n );\n }\n\n /**\n * Bi-directionally syncs the account tree with user storage.\n * This will perform a full sync, including both pulling updates\n * from user storage and pushing local changes to user storage.\n * This also performs legacy account syncing if needed.\n *\n * IMPORTANT:\n * If a full sync is already in progress, it will return the ongoing promise.\n *\n * @returns A promise that resolves when the sync is complete.\n */\n async syncWithUserStorage(): Promise<void> {\n return this.#backupAndSyncService.performFullSync();\n }\n\n /**\n * Bi-directionally syncs the account tree with user storage.\n * This will ensure at least one full sync is ran, including both pulling updates\n * from user storage and pushing local changes to user storage.\n * This also performs legacy account syncing if needed.\n *\n * IMPORTANT:\n * If the first ever full sync is already in progress, it will return the ongoing promise.\n * If the first ever full sync was previously completed, it will NOT start a new sync, and will resolve immediately.\n *\n * @returns A promise that resolves when the first ever full sync is complete.\n */\n async syncWithUserStorageAtLeastOnce(): Promise<void> {\n return this.#backupAndSyncService.performFullSyncAtLeastOnce();\n }\n\n /**\n * Creates an backup and sync context for sync operations.\n * Used by the backup and sync service.\n *\n * @returns The backup and sync context.\n */\n #createBackupAndSyncContext(): BackupAndSyncContext {\n return {\n ...this.#backupAndSyncConfig,\n controller: this,\n messenger: this.messagingSystem,\n controllerStateUpdateFn: this.update.bind(this),\n traceFn: this.#trace.bind(this),\n groupIdToWalletId: this.#groupIdToWalletId,\n };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"AccountTreeController.mjs","sourceRoot":"","sources":["../src/AccountTreeController.ts"],"names":[],"mappings":"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","sourcesContent":["import { AccountWalletType, select } from '@metamask/account-api';\nimport type {\n AccountGroupId,\n AccountWalletId,\n AccountSelector,\n MultichainAccountWalletId,\n AccountGroupType,\n} from '@metamask/account-api';\nimport type { MultichainAccountWalletStatus } from '@metamask/account-api';\nimport { type AccountId } from '@metamask/accounts-controller';\nimport type { StateMetadata } from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type { TraceCallback } from '@metamask/controller-utils';\nimport { isEvmAccountType } from '@metamask/keyring-api';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport { assert } from '@metamask/utils';\n\nimport type { BackupAndSyncEmitAnalyticsEventParams } from './backup-and-sync/analytics';\nimport {\n formatAnalyticsEvent,\n traceFallback,\n} from './backup-and-sync/analytics';\nimport { BackupAndSyncService } from './backup-and-sync/service';\nimport type { BackupAndSyncContext } from './backup-and-sync/types';\nimport type { AccountGroupObject, AccountTypeOrderKey } from './group';\nimport {\n ACCOUNT_TYPE_TO_SORT_ORDER,\n isAccountGroupNameUnique,\n isAccountGroupNameUniqueFromWallet,\n MAX_SORT_ORDER,\n} from './group';\nimport { projectLogger as log } from './logger';\nimport type { Rule } from './rule';\nimport { EntropyRule } from './rules/entropy';\nimport { KeyringRule } from './rules/keyring';\nimport { SnapRule } from './rules/snap';\nimport type {\n AccountTreeControllerConfig,\n AccountTreeControllerInternalBackupAndSyncConfig,\n AccountTreeControllerMessenger,\n AccountTreeControllerState,\n} from './types';\nimport { type AccountWalletObject, type AccountWalletObjectOf } from './wallet';\n\nexport const controllerName = 'AccountTreeController';\n\nconst accountTreeControllerMetadata: StateMetadata<AccountTreeControllerState> =\n {\n accountTree: {\n includeInStateLogs: true,\n persist: false, // We do re-recompute this state everytime.\n anonymous: false,\n usedInUi: true,\n },\n isAccountTreeSyncingInProgress: {\n includeInStateLogs: false,\n persist: false,\n anonymous: false,\n usedInUi: true,\n },\n hasAccountTreeSyncingSyncedAtLeastOnce: {\n includeInStateLogs: true,\n persist: true,\n anonymous: false,\n usedInUi: true,\n },\n accountGroupsMetadata: {\n includeInStateLogs: true,\n persist: true,\n anonymous: false,\n usedInUi: true,\n },\n accountWalletsMetadata: {\n includeInStateLogs: true,\n persist: true,\n anonymous: false,\n usedInUi: true,\n },\n };\n\n/**\n * Gets default state of the `AccountTreeController`.\n *\n * @returns The default state of the `AccountTreeController`.\n */\nexport function getDefaultAccountTreeControllerState(): AccountTreeControllerState {\n return {\n accountTree: {\n wallets: {},\n selectedAccountGroup: '',\n },\n isAccountTreeSyncingInProgress: false,\n hasAccountTreeSyncingSyncedAtLeastOnce: false,\n accountGroupsMetadata: {},\n accountWalletsMetadata: {},\n };\n}\n\n/**\n * Context for an account.\n */\nexport type AccountContext = {\n /**\n * Wallet ID associated to that account.\n */\n walletId: AccountWalletObject['id'];\n\n /**\n * Account group ID associated to that account.\n */\n groupId: AccountGroupObject['id'];\n\n /**\n * Sort order of the account.\n */\n sortOrder: (typeof ACCOUNT_TYPE_TO_SORT_ORDER)[AccountTypeOrderKey];\n};\n\nexport class AccountTreeController extends BaseController<\n typeof controllerName,\n AccountTreeControllerState,\n AccountTreeControllerMessenger\n> {\n readonly #accountIdToContext: Map<AccountId, AccountContext>;\n\n readonly #groupIdToWalletId: Map<AccountGroupId, AccountWalletId>;\n\n /**\n * Service responsible for all backup and sync operations.\n */\n readonly #backupAndSyncService: BackupAndSyncService;\n\n readonly #rules: [EntropyRule, SnapRule, KeyringRule];\n\n readonly #trace: TraceCallback;\n\n readonly #backupAndSyncConfig: AccountTreeControllerInternalBackupAndSyncConfig;\n\n #initialized: boolean;\n\n /**\n * Constructor for AccountTreeController.\n *\n * @param options - The controller options.\n * @param options.messenger - The messenger object.\n * @param options.state - Initial state to set on this controller\n * @param options.config - Optional configuration for the controller.\n */\n\n constructor({\n messenger,\n state,\n config,\n }: {\n messenger: AccountTreeControllerMessenger;\n state?: Partial<AccountTreeControllerState>;\n config?: AccountTreeControllerConfig;\n }) {\n super({\n messenger,\n name: controllerName,\n metadata: accountTreeControllerMetadata,\n state: {\n ...getDefaultAccountTreeControllerState(),\n ...state,\n },\n });\n\n // This will be set to true upon the first `init` call.\n this.#initialized = false;\n\n // Reverse map to allow fast node access from an account ID.\n this.#accountIdToContext = new Map();\n\n // Reverse map to allow fast wallet node access from a group ID.\n this.#groupIdToWalletId = new Map();\n\n // Rules to apply to construct the wallets tree.\n this.#rules = [\n // 1. We group by entropy-source\n new EntropyRule(this.messagingSystem),\n // 2. We group by Snap ID\n new SnapRule(this.messagingSystem),\n // 3. We group by wallet type (this rule cannot fail and will group all non-matching accounts)\n new KeyringRule(this.messagingSystem),\n ];\n\n // Initialize trace function\n this.#trace = config?.trace ?? traceFallback;\n\n // Initialize backup and sync config\n this.#backupAndSyncConfig = {\n emitAnalyticsEventFn: (event: BackupAndSyncEmitAnalyticsEventParams) => {\n return (\n config?.backupAndSync?.onBackupAndSyncEvent &&\n config.backupAndSync.onBackupAndSyncEvent(formatAnalyticsEvent(event))\n );\n },\n };\n\n // Initialize the backup and sync service\n this.#backupAndSyncService = new BackupAndSyncService(\n this.#createBackupAndSyncContext(),\n );\n\n this.messagingSystem.subscribe(\n 'AccountsController:accountAdded',\n (account) => {\n this.#handleAccountAdded(account);\n },\n );\n\n this.messagingSystem.subscribe(\n 'AccountsController:accountRemoved',\n (accountId) => {\n this.#handleAccountRemoved(accountId);\n },\n );\n\n this.messagingSystem.subscribe(\n 'UserStorageController:stateChange',\n (userStorageControllerState) => {\n this.#backupAndSyncService.handleUserStorageStateChange(\n userStorageControllerState,\n );\n },\n );\n\n this.messagingSystem.subscribe(\n 'MultichainAccountService:walletStatusChange',\n (walletId, status) => {\n this.#handleMultichainAccountWalletStatusChange(walletId, status);\n },\n );\n\n this.#registerMessageHandlers();\n }\n\n /**\n * Initialize the controller's state.\n *\n * It constructs the initial state of the account tree (tree nodes, nodes\n * names, metadata, etc..) and will automatically update the controller's\n * state with it.\n */\n init() {\n if (this.#initialized) {\n // We prevent re-initilializing the state multiple times. Though, we can use\n // `reinit` to re-init everything from scratch.\n return;\n }\n\n log('Initializing...');\n\n const wallets: AccountTreeControllerState['accountTree']['wallets'] = {};\n\n // Clear mappings for fresh rebuild.\n this.#accountIdToContext.clear();\n this.#groupIdToWalletId.clear();\n\n // Keep the current selected group to check if it's still part of the tree\n // after rebuilding it.\n const previousSelectedAccountGroup =\n this.state.accountTree.selectedAccountGroup;\n\n // There's no guarantee that accounts would be sorted by their import time\n // with `listMultichainAccounts`. We have to sort them here before constructing\n // the tree.\n //\n // Because of the alignment mecanism, some accounts from the same group might not\n // have been imported at the same time, but at least of them should have been\n // imported at the right time, thus, inserting the group at the proper place too.\n //\n // Lastly, if one day we allow to have \"gaps\" in between groups, then this `sort`\n // won't be enough and we would have to use group properties instead (like group\n // index or maybe introduce a `importTime` at group level).\n const accounts = this.#listAccounts().sort(\n (a, b) => a.metadata.importTime - b.metadata.importTime,\n );\n\n // For now, we always re-compute all wallets, we do not re-use the existing state.\n for (const account of accounts) {\n this.#insert(wallets, account);\n }\n\n // Once we have the account tree, we can apply persisted metadata (names + UI states).\n let previousSelectedAccountGroupStillExists = false;\n this.update((state) => {\n state.accountTree.wallets = wallets;\n\n // Apply group metadata within the state update\n for (const wallet of Object.values(state.accountTree.wallets)) {\n this.#applyAccountWalletMetadata(state, wallet.id);\n\n // Used for default group default names (so we use human-indexing here).\n let nextNaturalNameIndex = 1;\n for (const group of Object.values(wallet.groups)) {\n this.#applyAccountGroupMetadata(\n state,\n wallet.id,\n group.id,\n // FIXME: We should not need this kind of logic if we were not inserting accounts\n // 1 by 1. Instead, we should be inserting wallets and groups directly. This would\n // allow us to naturally insert a group in the tree AND update its metadata right\n // away...\n // But here, we have to wait for the entire group to be ready before updating\n // its metadata (mainly because we're dealing with single accounts rather than entire\n // groups).\n // That is why we need this kind of extra parameter.\n nextNaturalNameIndex,\n );\n\n if (group.id === previousSelectedAccountGroup) {\n previousSelectedAccountGroupStillExists = true;\n }\n nextNaturalNameIndex += 1;\n }\n }\n\n if (\n !previousSelectedAccountGroupStillExists ||\n previousSelectedAccountGroup === ''\n ) {\n // No group is selected yet OR group no longer exists, re-sync with the\n // AccountsController.\n state.accountTree.selectedAccountGroup =\n this.#getDefaultSelectedAccountGroup(wallets);\n }\n });\n\n // We still compare the previous and new value, the previous one could have been\n // an empty string and `#getDefaultSelectedAccountGroup` could also return an\n // empty string too, thus, we would re-use the same value here again. In that\n // case, no need to fire any event.\n if (\n previousSelectedAccountGroup !==\n this.state.accountTree.selectedAccountGroup\n ) {\n log(\n `Selected (initial) group is: [${this.state.accountTree.selectedAccountGroup}]`,\n );\n this.messagingSystem.publish(\n `${controllerName}:selectedAccountGroupChange`,\n this.state.accountTree.selectedAccountGroup,\n previousSelectedAccountGroup,\n );\n }\n\n log('Initialized!');\n this.#initialized = true;\n }\n\n /**\n * Re-initialize the controller's state.\n *\n * This is done in one single (atomic) `update` block to avoid having a temporary\n * cleared state. Use this when you need to force a full re-init even if already initialized.\n */\n reinit() {\n log('Re-initializing...');\n this.#initialized = false;\n this.init();\n }\n\n /**\n * Rule for entropy-base wallets.\n *\n * @returns The rule for entropy-based wallets.\n */\n #getEntropyRule(): EntropyRule {\n return this.#rules[0];\n }\n\n /**\n * Rule for Snap-base wallets.\n *\n * @returns The rule for snap-based wallets.\n */\n #getSnapRule(): SnapRule {\n return this.#rules[1];\n }\n\n /**\n * Rule for keyring-base wallets.\n *\n * This rule acts as a fallback and never fails since all accounts\n * comes from a keyring anyway.\n *\n * @returns The fallback rule for every accounts that did not match\n * any other rules.\n */\n #getKeyringRule(): KeyringRule {\n return this.#rules[2];\n }\n\n /**\n * Applies wallet metadata updates (name) by checking the persistent state\n * first, and then fallbacks to default values (based on the wallet's\n * type).\n *\n * @param state Controller state to update for persistence.\n * @param walletId The wallet ID to update.\n */\n #applyAccountWalletMetadata(\n state: AccountTreeControllerState,\n walletId: AccountWalletId,\n ) {\n const wallet = state.accountTree.wallets[walletId];\n const persistedMetadata = state.accountWalletsMetadata[walletId];\n\n // Apply persisted name if available (including empty strings)\n if (persistedMetadata?.name !== undefined) {\n wallet.metadata.name = persistedMetadata.name.value;\n } else if (!wallet.metadata.name) {\n // Generate default name if none exists\n if (wallet.type === AccountWalletType.Entropy) {\n wallet.metadata.name =\n this.#getEntropyRule().getDefaultAccountWalletName(wallet);\n } else if (wallet.type === AccountWalletType.Snap) {\n wallet.metadata.name =\n this.#getSnapRule().getDefaultAccountWalletName(wallet);\n } else {\n wallet.metadata.name =\n this.#getKeyringRule().getDefaultAccountWalletName(wallet);\n }\n log(`[${wallet.id}] Set default name to: \"${wallet.metadata.name}\"`);\n }\n }\n\n /**\n * Gets the appropriate rule instance for a given wallet type.\n *\n * @param wallet - The wallet object to get the rule for.\n * @returns The rule instance that handles the wallet's type.\n */\n #getRuleForWallet<WalletType extends AccountWalletType>(\n wallet: AccountWalletObjectOf<WalletType>,\n ): Rule<WalletType, AccountGroupType> {\n switch (wallet.type) {\n case AccountWalletType.Entropy:\n return this.#getEntropyRule() as unknown as Rule<\n WalletType,\n AccountGroupType\n >;\n case AccountWalletType.Snap:\n return this.#getSnapRule() as unknown as Rule<\n WalletType,\n AccountGroupType\n >;\n default:\n return this.#getKeyringRule() as unknown as Rule<\n WalletType,\n AccountGroupType\n >;\n }\n }\n\n /**\n * Applies group metadata updates (name, pinned, hidden flags) by checking\n * the persistent state first, and then fallbacks to default values (based\n * on the wallet's\n * type).\n *\n * @param state Controller state to update for persistence.\n * @param walletId The wallet ID containing the group.\n * @param groupId The account group ID to update.\n * @param nextNaturalNameIndex The next natural name index for this group (only used for default names).\n */\n #applyAccountGroupMetadata(\n state: AccountTreeControllerState,\n walletId: AccountWalletId,\n groupId: AccountGroupId,\n nextNaturalNameIndex?: number,\n ) {\n const wallet = state.accountTree.wallets[walletId];\n const group = wallet.groups[groupId];\n const persistedGroupMetadata = state.accountGroupsMetadata[group.id];\n\n // Apply persisted name if available (including empty strings)\n if (persistedGroupMetadata?.name !== undefined) {\n state.accountTree.wallets[walletId].groups[groupId].metadata.name =\n persistedGroupMetadata.name.value;\n } else if (!group.metadata.name) {\n // Get the appropriate rule for this wallet type\n const rule = this.#getRuleForWallet(wallet);\n\n // Get the prefix for groups of this wallet\n const namePrefix = rule.getDefaultAccountGroupPrefix(wallet);\n\n // Skip computed names for now - use default naming with per-wallet logic\n // TODO: Implement computed names in a future iteration\n\n // Parse the highest account index being used (similar to accounts-controller)\n let highestNameIndex = 0;\n for (const { id: otherGroupId } of Object.values(\n wallet.groups,\n ) as AccountGroupObject[]) {\n // Skip the current group being processed\n if (otherGroupId === groupId) {\n continue;\n }\n\n // We always get the name from the persisted map, since `init` will clear the\n // `state.accountTree.wallets`, thus, given empty `group.metadata.name`.\n // NOTE: If the other group has not been named yet, we just use an empty name.\n const otherGroupName =\n state.accountGroupsMetadata[otherGroupId]?.name?.value ?? '';\n\n // Parse the existing group name to extract the numeric index\n const nameMatch = otherGroupName.match(/account\\s+(\\d+)$/iu);\n if (nameMatch) {\n const nameIndex = parseInt(nameMatch[1], 10);\n if (nameIndex > highestNameIndex) {\n highestNameIndex = nameIndex;\n }\n }\n }\n\n // We just use the highest known index no matter the wallet type.\n //\n // For entropy-based wallets (bip44), if a multichain account group with group index 1\n // is inserted before another one with group index 0, then the naming will be:\n // - \"Account 1\" (group index 1)\n // - \"Account 2\" (group index 0)\n // This naming makes more sense for the end-user.\n //\n // For other type of wallets, since those wallets can create arbitrary gaps, we still\n // rely on the highest know index to avoid back-filling account with \"old names\".\n let proposedNameIndex = Math.max(\n // Use + 1 to use the next available index.\n highestNameIndex + 1,\n // In case all accounts have been renamed differently than the usual \"Account <index>\"\n // pattern, we want to use the next \"natural\" index, which is just the number of groups\n // in that wallet (e.g. [\"Account A\", \"Another Account\"], next natural index would be\n // \"Account 3\" in this case).\n nextNaturalNameIndex ?? Object.keys(wallet.groups).length,\n );\n\n // Find a unique name by checking for conflicts and incrementing if needed\n let proposedNameExists: boolean;\n let proposedName = '';\n do {\n proposedName = `${namePrefix} ${proposedNameIndex}`;\n\n // Check if this name already exists in the wallet (excluding current group)\n proposedNameExists = !isAccountGroupNameUniqueFromWallet(\n wallet,\n group.id,\n proposedName,\n );\n\n /* istanbul ignore next */\n if (proposedNameExists) {\n proposedNameIndex += 1; // Try next number\n }\n } while (proposedNameExists);\n\n state.accountTree.wallets[walletId].groups[groupId].metadata.name =\n proposedName;\n log(`[${group.id}] Set default name to: \"${group.metadata.name}\"`);\n\n // Persist the generated name to ensure consistency\n state.accountGroupsMetadata[groupId] ??= {};\n state.accountGroupsMetadata[groupId].name = {\n value: proposedName,\n // The `lastUpdatedAt` field is used for backup and sync, when comparing local names\n // with backed up names. In this case, the generated name should never take precedence\n // over a user-defined name, so we set `lastUpdatedAt` to 0.\n lastUpdatedAt: 0,\n };\n }\n\n // Apply persisted UI states\n if (persistedGroupMetadata?.pinned?.value !== undefined) {\n group.metadata.pinned = persistedGroupMetadata.pinned.value;\n }\n if (persistedGroupMetadata?.hidden?.value !== undefined) {\n group.metadata.hidden = persistedGroupMetadata.hidden.value;\n }\n }\n\n /**\n * Gets the account wallet object from its ID.\n *\n * @param walletId - Account wallet ID.\n * @returns The account wallet object if found, undefined otherwise.\n */\n getAccountWalletObject(\n walletId: AccountWalletId,\n ): AccountWalletObject | undefined {\n const wallet = this.state.accountTree.wallets[walletId];\n if (!wallet) {\n return undefined;\n }\n\n return wallet;\n }\n\n /**\n * Gets all account wallet objects.\n *\n * @returns All account wallet objects.\n */\n getAccountWalletObjects(): AccountWalletObject[] {\n return Object.values(this.state.accountTree.wallets);\n }\n\n /**\n * Gets all underlying accounts from the currently selected account\n * group.\n *\n * It also support account selector, which allows to filter specific\n * accounts given some criterias (account type, address, scopes, etc...).\n *\n * @param selector - Optional account selector.\n * @returns Underlying accounts for the currently selected account (filtered\n * by the selector if provided).\n */\n getAccountsFromSelectedAccountGroup(\n selector?: AccountSelector<InternalAccount>,\n ) {\n const groupId = this.getSelectedAccountGroup();\n if (!groupId) {\n return [];\n }\n\n const group = this.getAccountGroupObject(groupId);\n // We should never reach this part, so we cannot cover it either.\n /* istanbul ignore next */\n if (!group) {\n return [];\n }\n\n const accounts: InternalAccount[] = [];\n for (const id of group.accounts) {\n const account = this.messagingSystem.call(\n 'AccountsController:getAccount',\n id,\n );\n\n // For now, we're filtering undefined account, but I believe\n // throwing would be more appropriate here.\n if (account) {\n accounts.push(account);\n }\n }\n\n return selector ? select(accounts, selector) : accounts;\n }\n\n /**\n * Gets the account group object from its ID.\n *\n * @param groupId - Account group ID.\n * @returns The account group object if found, undefined otherwise.\n */\n getAccountGroupObject(\n groupId: AccountGroupId,\n ): AccountGroupObject | undefined {\n const walletId = this.#groupIdToWalletId.get(groupId);\n if (!walletId) {\n return undefined;\n }\n\n const wallet = this.getAccountWalletObject(walletId);\n return wallet?.groups[groupId];\n }\n\n /**\n * Handles \"AccountsController:accountAdded\" event to insert\n * new accounts into the tree.\n *\n * @param account - New account.\n */\n #handleAccountAdded(account: InternalAccount) {\n // We wait for the first `init` to be called to actually build up the tree and\n // mutate it. We expect the caller to first update the `AccountsController` state\n // to force the migration of accounts, and then call `init`.\n if (!this.#initialized) {\n return;\n }\n\n // Check if this account is already known by the tree to avoid double-insertion.\n if (!this.#accountIdToContext.has(account.id)) {\n this.update((state) => {\n this.#insert(state.accountTree.wallets, account);\n\n const context = this.#accountIdToContext.get(account.id);\n if (context) {\n const { walletId, groupId } = context;\n\n const wallet = state.accountTree.wallets[walletId];\n if (wallet) {\n this.#applyAccountWalletMetadata(state, walletId);\n this.#applyAccountGroupMetadata(state, walletId, groupId);\n }\n }\n });\n\n this.messagingSystem.publish(\n `${controllerName}:accountTreeChange`,\n this.state.accountTree,\n );\n }\n }\n\n /**\n * Handles \"AccountsController:accountRemoved\" event to remove\n * given account from the tree.\n *\n * @param accountId - Removed account ID.\n */\n #handleAccountRemoved(accountId: AccountId) {\n // We wait for the first `init` to be called to actually build up the tree and\n // mutate it. We expect the caller to first update the `AccountsController` state\n // to force the migration of accounts, and then call `init`.\n if (!this.#initialized) {\n return;\n }\n\n const context = this.#accountIdToContext.get(accountId);\n\n if (context) {\n const { walletId, groupId } = context;\n\n const previousSelectedAccountGroup =\n this.state.accountTree.selectedAccountGroup;\n let selectedAccountGroupChanged = false;\n\n this.update((state) => {\n const accounts =\n state.accountTree.wallets[walletId]?.groups[groupId]?.accounts;\n\n if (accounts) {\n const index = accounts.indexOf(accountId);\n if (index !== -1) {\n accounts.splice(index, 1);\n\n // Check if we need to update selectedAccountGroup after removal\n if (\n state.accountTree.selectedAccountGroup === groupId &&\n accounts.length === 0\n ) {\n // The currently selected group is now empty, find a new group to select\n const newSelectedAccountGroup = this.#getDefaultAccountGroupId(\n state.accountTree.wallets,\n );\n state.accountTree.selectedAccountGroup = newSelectedAccountGroup;\n selectedAccountGroupChanged =\n newSelectedAccountGroup !== previousSelectedAccountGroup;\n }\n }\n if (accounts.length === 0) {\n this.#pruneEmptyGroupAndWallet(state, walletId, groupId);\n }\n }\n });\n this.messagingSystem.publish(\n `${controllerName}:accountTreeChange`,\n this.state.accountTree,\n );\n\n // Emit selectedAccountGroupChange event if the selected group changed\n if (selectedAccountGroupChanged) {\n this.messagingSystem.publish(\n `${controllerName}:selectedAccountGroupChange`,\n this.state.accountTree.selectedAccountGroup,\n previousSelectedAccountGroup,\n );\n }\n\n // Clear reverse-mapping for that account.\n this.#accountIdToContext.delete(accountId);\n }\n }\n\n /**\n * Helper method to prune a group if it holds no accounts and additionally\n * prune the wallet if it holds no groups. This action should take place\n * after a singular account removal.\n *\n * NOTE: This method should only be used for a group that we know to be empty.\n *\n * @param state - The AccountTreeController state to prune.\n * @param walletId - The wallet ID to prune, the wallet should be the parent of the associated group that holds the removed account.\n * @param groupId - The group ID to prune, the group should be the parent of the associated account that was removed.\n * @returns The updated state.\n */\n #pruneEmptyGroupAndWallet(\n state: AccountTreeControllerState,\n walletId: AccountWalletId,\n groupId: AccountGroupId,\n ) {\n const { wallets } = state.accountTree;\n\n delete wallets[walletId].groups[groupId];\n this.#groupIdToWalletId.delete(groupId);\n\n // Clean up metadata for the pruned group\n delete state.accountGroupsMetadata[groupId];\n\n if (Object.keys(wallets[walletId].groups).length === 0) {\n delete wallets[walletId];\n }\n return state;\n }\n\n /**\n * Insert an account inside an account tree.\n *\n * We go over multiple rules to try to \"match\" the account following\n * specific criterias. If a rule \"matches\" an account, then this\n * account get added into its proper account wallet and account group.\n *\n * @param wallets - Account tree.\n * @param account - The account to be inserted.\n */\n #insert(\n wallets: AccountTreeControllerState['accountTree']['wallets'],\n account: InternalAccount,\n ) {\n const result =\n this.#getEntropyRule().match(account) ??\n this.#getSnapRule().match(account) ??\n this.#getKeyringRule().match(account); // This one cannot fail.\n\n // Update controller's state.\n const walletId = result.wallet.id;\n let wallet = wallets[walletId];\n if (!wallet) {\n log(`[${walletId}] Added as new wallet`);\n wallets[walletId] = {\n ...result.wallet,\n status: 'ready',\n groups: {},\n metadata: {\n name: '', // Will get updated later.\n ...result.wallet.metadata,\n },\n // We do need to type-cast since we're not narrowing `result` with\n // the union tag `result.wallet.type`.\n } as AccountWalletObject;\n wallet = wallets[walletId];\n\n // Trigger atomic sync for new wallet (only for entropy wallets)\n if (wallet.type === AccountWalletType.Entropy) {\n this.#backupAndSyncService.enqueueSingleWalletSync(walletId);\n }\n }\n\n const groupId = result.group.id;\n let group = wallet.groups[groupId];\n const { type, id } = account;\n const sortOrder = ACCOUNT_TYPE_TO_SORT_ORDER[type];\n\n if (!group) {\n log(`[${walletId}] Add new group: [${groupId}]`);\n wallet.groups[groupId] = {\n ...result.group,\n // Type-wise, we are guaranteed to always have at least 1 account.\n accounts: [id],\n metadata: {\n name: '',\n ...{ pinned: false, hidden: false }, // Default UI states\n ...result.group.metadata, // Allow rules to override defaults\n },\n // We do need to type-cast since we're not narrowing `result` with\n // the union tag `result.group.type`.\n } as AccountGroupObject;\n group = wallet.groups[groupId];\n\n // Map group ID to its containing wallet ID for efficient direct access\n this.#groupIdToWalletId.set(groupId, walletId);\n\n // Trigger atomic sync for new group (only for entropy wallets)\n if (wallet.type === AccountWalletType.Entropy) {\n this.#backupAndSyncService.enqueueSingleGroupSync(groupId);\n }\n } else {\n group.accounts.push(id);\n // We need to do this at every insertion because race conditions can happen\n // during the account creation process where one provider completes before the other.\n // The discovery process in the service can also lead to some accounts being created \"out of order\".\n const { accounts } = group;\n accounts.sort(\n /* istanbul ignore next: Comparator branch execution (a===id vs b===id)\n * and return attribution vary across engines; final ordering is covered\n * by behavior tests. Ignoring the entire comparator avoids flaky line\n * coverage without reducing scenario coverage.\n */\n (a, b) => {\n const aSortOrder =\n a === id ? sortOrder : this.#accountIdToContext.get(a)?.sortOrder;\n const bSortOrder =\n b === id ? sortOrder : this.#accountIdToContext.get(b)?.sortOrder;\n return (\n (aSortOrder ?? MAX_SORT_ORDER) - (bSortOrder ?? MAX_SORT_ORDER)\n );\n },\n );\n }\n log(\n `[${groupId}] Add new account: { id: \"${account.id}\", type: \"${account.type}\", address: \"${account.address}\"`,\n );\n\n // Update the reverse mapping for this account.\n this.#accountIdToContext.set(account.id, {\n walletId: wallet.id,\n groupId: group.id,\n sortOrder,\n });\n }\n\n /**\n * List all internal accounts.\n *\n * @returns The list of all internal accounts.\n */\n #listAccounts(): InternalAccount[] {\n return this.messagingSystem.call(\n 'AccountsController:listMultichainAccounts',\n );\n }\n\n /**\n * Asserts that a group exists in the current account tree.\n *\n * @param groupId - The account group ID to validate.\n * @throws Error if the group does not exist.\n */\n #assertAccountGroupExists(groupId: AccountGroupId): void {\n const exists = this.#groupIdToWalletId.has(groupId);\n if (!exists) {\n throw new Error(`Account group with ID \"${groupId}\" not found in tree`);\n }\n }\n\n /**\n * Asserts that a wallet exists in the current account tree.\n *\n * @param walletId - The account wallet ID to validate.\n * @throws Error if the wallet does not exist.\n */\n #assertAccountWalletExists(walletId: AccountWalletId): void {\n const exists = Boolean(this.state.accountTree.wallets[walletId]);\n if (!exists) {\n throw new Error(`Account wallet with ID \"${walletId}\" not found in tree`);\n }\n }\n\n /**\n * Asserts that an account group name is unique within the same wallet.\n *\n * @param groupId - The account group ID to exclude from the check.\n * @param name - The name to validate for uniqueness.\n * @throws Error if the name already exists in another group within the same wallet.\n */\n #assertAccountGroupNameIsUnique(groupId: AccountGroupId, name: string): void {\n if (!isAccountGroupNameUnique(this.state, groupId, name)) {\n throw new Error('Account group name already exists');\n }\n }\n\n /**\n * Gets the currently selected account group ID.\n *\n * @returns The selected account group ID or empty string if none selected.\n */\n getSelectedAccountGroup(): AccountGroupId | '' {\n return this.state.accountTree.selectedAccountGroup;\n }\n\n /**\n * Sets the selected account group and updates the AccountsController selectedAccount accordingly.\n *\n * @param groupId - The account group ID to select.\n */\n setSelectedAccountGroup(groupId: AccountGroupId): void {\n const previousSelectedAccountGroup =\n this.state.accountTree.selectedAccountGroup;\n\n // Idempotent check - if the same group is already selected, do nothing\n if (previousSelectedAccountGroup === groupId) {\n return;\n }\n\n // Find the first account in this group to select\n const accountToSelect = this.#getDefaultAccountFromAccountGroupId(groupId);\n if (!accountToSelect) {\n throw new Error(`No accounts found in group: ${groupId}`);\n }\n\n // Update our state first\n this.update((state) => {\n state.accountTree.selectedAccountGroup = groupId;\n });\n\n log(\n `Selected group is now: [${this.state.accountTree.selectedAccountGroup}]`,\n );\n\n this.messagingSystem.publish(\n `${controllerName}:selectedAccountGroupChange`,\n groupId,\n previousSelectedAccountGroup,\n );\n\n // Update AccountsController - this will trigger selectedAccountChange event,\n // but our handler is idempotent so it won't cause infinite loop\n this.messagingSystem.call(\n 'AccountsController:setSelectedAccount',\n accountToSelect,\n );\n }\n\n /**\n * Initializes the selectedAccountGroup based on the currently selected account from AccountsController.\n *\n * @param wallets - Wallets object to use for fallback logic\n * @returns The default selected account group ID or empty string if none selected.\n */\n #getDefaultSelectedAccountGroup(wallets: {\n [walletId: AccountWalletId]: AccountWalletObject;\n }): AccountGroupId | '' {\n const selectedAccount = this.messagingSystem.call(\n 'AccountsController:getSelectedMultichainAccount',\n );\n if (selectedAccount && selectedAccount.id) {\n const accountMapping = this.#accountIdToContext.get(selectedAccount.id);\n if (accountMapping) {\n const { groupId } = accountMapping;\n\n return groupId;\n }\n }\n\n // Default to the default group in case of errors.\n return this.#getDefaultAccountGroupId(wallets);\n }\n\n /**\n * Handles multichain account wallet status change from\n * the MultichainAccountService.\n *\n * @param walletId - Multichain account wallet ID.\n * @param walletStatus - New multichain account wallet status.\n */\n #handleMultichainAccountWalletStatusChange(\n walletId: MultichainAccountWalletId,\n walletStatus: MultichainAccountWalletStatus,\n ): void {\n this.update((state) => {\n const wallet = state.accountTree.wallets[walletId];\n\n if (wallet) {\n wallet.status = walletStatus;\n }\n });\n }\n\n /**\n * Gets account group object.\n *\n * @param groupId - The account group ID.\n * @returns The account group or undefined if not found.\n */\n #getAccountGroup(groupId: AccountGroupId): AccountGroupObject | undefined {\n const found = Object.values(this.state.accountTree.wallets).find(\n (wallet) => wallet.groups[groupId] !== undefined,\n );\n\n return found?.groups[groupId];\n }\n\n /**\n * Gets the default account for specified group.\n *\n * @param groupId - The account group ID.\n * @returns The first account ID in the group, or undefined if no accounts found.\n */\n #getDefaultAccountFromAccountGroupId(\n groupId: AccountGroupId,\n ): AccountId | undefined {\n const group = this.#getAccountGroup(groupId);\n\n if (group) {\n let candidate;\n for (const id of group.accounts) {\n const account = this.messagingSystem.call(\n 'AccountsController:getAccount',\n id,\n );\n\n if (!candidate) {\n candidate = id;\n }\n if (account && isEvmAccountType(account.type)) {\n // EVM accounts have a higher priority, so if we find any, we just\n // use that account!\n return account.id;\n }\n }\n\n return candidate;\n }\n\n return undefined;\n }\n\n /**\n * Gets the default group id, which is either, the first non-empty group that contains an EVM account or\n * just the first non-empty group with any accounts.\n *\n * @param wallets - The wallets object to search.\n * @returns The ID of the first non-empty group, or an empty string if no groups are found.\n */\n #getDefaultAccountGroupId(wallets: {\n [walletId: AccountWalletId]: AccountWalletObject;\n }): AccountGroupId | '' {\n let candidate: AccountGroupId | '' = '';\n\n for (const wallet of Object.values(wallets)) {\n for (const group of Object.values(wallet.groups)) {\n // We only update the candidate with the first non-empty group, but still\n // try to find a group that contains an EVM account (the `candidate` is\n // our fallback).\n if (candidate === '' && group.accounts.length > 0) {\n candidate = group.id;\n }\n\n for (const id of group.accounts) {\n const account = this.messagingSystem.call(\n 'AccountsController:getAccount',\n id,\n );\n\n if (account && isEvmAccountType(account.type)) {\n // EVM accounts have a higher priority, so if we find any, we just\n // use that group!\n return group.id;\n }\n }\n }\n }\n return candidate;\n }\n\n /**\n * Resolves name conflicts by adding a suffix to make the name unique.\n *\n * @internal\n * @param wallet - The wallet to check within.\n * @param groupId - The account group ID to exclude from the check.\n * @param name - The desired name that has a conflict.\n * @returns A unique name with suffix added if necessary.\n */\n resolveNameConflict(\n wallet: AccountWalletObject,\n groupId: AccountGroupId,\n name: string,\n ): string {\n let suffix = 2;\n let candidateName = `${name} (${suffix})`;\n\n // Keep incrementing suffix until we find a unique name\n while (\n !isAccountGroupNameUniqueFromWallet(wallet, groupId, candidateName)\n ) {\n suffix += 1;\n candidateName = `${name} (${suffix})`;\n }\n\n return candidateName;\n }\n\n /**\n * Sets a custom name for an account group.\n *\n * @param groupId - The account group ID.\n * @param name - The custom name to set.\n * @param autoHandleConflict - If true, automatically resolves name conflicts by adding a suffix. If false, throws on conflicts.\n * @throws If the account group ID is not found in the current tree.\n * @throws If the account group name already exists and autoHandleConflict is false.\n */\n setAccountGroupName(\n groupId: AccountGroupId,\n name: string,\n autoHandleConflict: boolean = false,\n ): void {\n // Validate that the group exists in the current tree\n this.#assertAccountGroupExists(groupId);\n\n const walletId = this.#groupIdToWalletId.get(groupId);\n assert(walletId, `Account group with ID \"${groupId}\" not found in tree`);\n\n const wallet = this.state.accountTree.wallets[walletId];\n let finalName = name;\n\n // Handle name conflicts based on the autoHandleConflict flag\n if (\n autoHandleConflict &&\n !isAccountGroupNameUniqueFromWallet(wallet, groupId, name)\n ) {\n finalName = this.resolveNameConflict(wallet, groupId, name);\n } else {\n // Validate that the name is unique\n this.#assertAccountGroupNameIsUnique(groupId, finalName);\n }\n\n log(\n `[${groupId}] Set new name to: \"${finalName}\" (auto handle conflict: ${autoHandleConflict})`,\n );\n\n this.update((state) => {\n /* istanbul ignore next */\n if (!state.accountGroupsMetadata[groupId]) {\n state.accountGroupsMetadata[groupId] = {};\n }\n\n // Update persistent metadata\n state.accountGroupsMetadata[groupId].name = {\n value: finalName,\n lastUpdatedAt: Date.now(),\n };\n\n // Update tree node directly using efficient mapping\n state.accountTree.wallets[walletId].groups[groupId].metadata.name =\n finalName;\n });\n\n // Trigger atomic sync for group rename (only for groups from entropy wallets)\n if (wallet.type === AccountWalletType.Entropy) {\n this.#backupAndSyncService.enqueueSingleGroupSync(groupId);\n }\n }\n\n /**\n * Sets a custom name for an account wallet.\n *\n * @param walletId - The account wallet ID.\n * @param name - The custom name to set.\n * @throws If the account wallet ID is not found in the current tree.\n */\n setAccountWalletName(walletId: AccountWalletId, name: string): void {\n // Validate that the wallet exists in the current tree\n this.#assertAccountWalletExists(walletId);\n\n this.update((state) => {\n // Update persistent metadata\n state.accountWalletsMetadata[walletId] ??= {};\n state.accountWalletsMetadata[walletId].name = {\n value: name,\n lastUpdatedAt: Date.now(),\n };\n\n // Update tree node directly\n state.accountTree.wallets[walletId].metadata.name = name;\n });\n\n // Trigger atomic sync for wallet rename (only for groups from entropy wallets)\n if (\n this.state.accountTree.wallets[walletId].type ===\n AccountWalletType.Entropy\n ) {\n this.#backupAndSyncService.enqueueSingleWalletSync(walletId);\n }\n }\n\n /**\n * Toggles the pinned state of an account group.\n *\n * @param groupId - The account group ID.\n * @param pinned - Whether the group should be pinned.\n * @throws If the account group ID is not found in the current tree.\n */\n setAccountGroupPinned(groupId: AccountGroupId, pinned: boolean): void {\n // Validate that the group exists in the current tree\n this.#assertAccountGroupExists(groupId);\n\n const walletId = this.#groupIdToWalletId.get(groupId);\n\n this.update((state) => {\n /* istanbul ignore next */\n if (!state.accountGroupsMetadata[groupId]) {\n state.accountGroupsMetadata[groupId] = {};\n }\n\n // Update persistent metadata\n state.accountGroupsMetadata[groupId].pinned = {\n value: pinned,\n lastUpdatedAt: Date.now(),\n };\n\n // Update tree node directly using efficient mapping\n if (walletId) {\n state.accountTree.wallets[walletId].groups[groupId].metadata.pinned =\n pinned;\n }\n });\n\n // Trigger atomic sync for group pinning (only for groups from entropy wallets)\n if (\n walletId &&\n this.state.accountTree.wallets[walletId].type ===\n AccountWalletType.Entropy\n ) {\n this.#backupAndSyncService.enqueueSingleGroupSync(groupId);\n }\n }\n\n /**\n * Toggles the hidden state of an account group.\n *\n * @param groupId - The account group ID.\n * @param hidden - Whether the group should be hidden.\n * @throws If the account group ID is not found in the current tree.\n */\n setAccountGroupHidden(groupId: AccountGroupId, hidden: boolean): void {\n // Validate that the group exists in the current tree\n this.#assertAccountGroupExists(groupId);\n\n const walletId = this.#groupIdToWalletId.get(groupId);\n\n this.update((state) => {\n /* istanbul ignore next */\n if (!state.accountGroupsMetadata[groupId]) {\n state.accountGroupsMetadata[groupId] = {};\n }\n\n // Update persistent metadata\n state.accountGroupsMetadata[groupId].hidden = {\n value: hidden,\n lastUpdatedAt: Date.now(),\n };\n\n // Update tree node directly using efficient mapping\n if (walletId) {\n state.accountTree.wallets[walletId].groups[groupId].metadata.hidden =\n hidden;\n }\n });\n\n // Trigger atomic sync for group hiding (only for groups from entropy wallets)\n if (\n walletId &&\n this.state.accountTree.wallets[walletId].type ===\n AccountWalletType.Entropy\n ) {\n this.#backupAndSyncService.enqueueSingleGroupSync(groupId);\n }\n }\n\n /**\n * Clears the controller state and resets to default values.\n * Also clears the backup and sync service state.\n */\n clearState(): void {\n log('Clearing state');\n\n this.update(() => {\n return {\n ...getDefaultAccountTreeControllerState(),\n };\n });\n this.#backupAndSyncService.clearState();\n\n // So we know we have to call `init` again.\n this.#initialized = false;\n }\n\n /**\n * Registers message handlers for the AccountTreeController.\n */\n #registerMessageHandlers(): void {\n this.messagingSystem.registerActionHandler(\n `${controllerName}:getSelectedAccountGroup`,\n this.getSelectedAccountGroup.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:setSelectedAccountGroup`,\n this.setSelectedAccountGroup.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:getAccountsFromSelectedAccountGroup`,\n this.getAccountsFromSelectedAccountGroup.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:setAccountWalletName`,\n this.setAccountWalletName.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:setAccountGroupName`,\n this.setAccountGroupName.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:setAccountGroupPinned`,\n this.setAccountGroupPinned.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:setAccountGroupHidden`,\n this.setAccountGroupHidden.bind(this),\n );\n }\n\n /**\n * Bi-directionally syncs the account tree with user storage.\n * This will perform a full sync, including both pulling updates\n * from user storage and pushing local changes to user storage.\n * This also performs legacy account syncing if needed.\n *\n * IMPORTANT:\n * If a full sync is already in progress, it will return the ongoing promise.\n *\n * @returns A promise that resolves when the sync is complete.\n */\n async syncWithUserStorage(): Promise<void> {\n return this.#backupAndSyncService.performFullSync();\n }\n\n /**\n * Bi-directionally syncs the account tree with user storage.\n * This will ensure at least one full sync is ran, including both pulling updates\n * from user storage and pushing local changes to user storage.\n * This also performs legacy account syncing if needed.\n *\n * IMPORTANT:\n * If the first ever full sync is already in progress, it will return the ongoing promise.\n * If the first ever full sync was previously completed, it will NOT start a new sync, and will resolve immediately.\n *\n * @returns A promise that resolves when the first ever full sync is complete.\n */\n async syncWithUserStorageAtLeastOnce(): Promise<void> {\n return this.#backupAndSyncService.performFullSyncAtLeastOnce();\n }\n\n /**\n * Creates an backup and sync context for sync operations.\n * Used by the backup and sync service.\n *\n * @returns The backup and sync context.\n */\n #createBackupAndSyncContext(): BackupAndSyncContext {\n return {\n ...this.#backupAndSyncConfig,\n controller: this,\n messenger: this.messagingSystem,\n controllerStateUpdateFn: this.update.bind(this),\n traceFn: this.#trace.bind(this),\n groupIdToWalletId: this.#groupIdToWalletId,\n };\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metamask-previews/account-tree-controller",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0-preview-a5441fae",
|
|
4
4
|
"description": "Controller to group account together based on some pre-defined rules",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"MetaMask",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"@metamask/snaps-sdk": "^9.0.0",
|
|
52
52
|
"@metamask/snaps-utils": "^11.0.0",
|
|
53
53
|
"@metamask/superstruct": "^3.1.0",
|
|
54
|
-
"@metamask/utils": "^11.8.
|
|
54
|
+
"@metamask/utils": "^11.8.1",
|
|
55
55
|
"fast-deep-equal": "^3.1.3",
|
|
56
56
|
"lodash": "^4.17.21"
|
|
57
57
|
},
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"@metamask/auto-changelog": "^3.4.4",
|
|
62
62
|
"@metamask/keyring-api": "^21.0.0",
|
|
63
63
|
"@metamask/keyring-controller": "^23.1.0",
|
|
64
|
-
"@metamask/multichain-account-service": "^1.
|
|
64
|
+
"@metamask/multichain-account-service": "^1.2.0",
|
|
65
65
|
"@metamask/profile-sync-controller": "^25.0.0",
|
|
66
66
|
"@metamask/providers": "^22.1.0",
|
|
67
67
|
"@metamask/snaps-controllers": "^14.0.1",
|