@metamask-previews/account-tree-controller 0.16.1-preview-34d02af7 → 0.17.0-preview-d243c525
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 +13 -5
- package/dist/AccountTreeController.cjs +63 -72
- 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 +63 -72
- package/dist/AccountTreeController.mjs.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -13,16 +13,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
13
13
|
|
|
14
14
|
### Changed
|
|
15
15
|
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
- This could rarely lead to inconsistencies until the next single updates or next full sync.
|
|
16
|
+
- Computed names (inherited from previous existing accounts) is disabled temporarily ([#6601](https://github.com/MetaMask/core/pull/6601))
|
|
17
|
+
- They do interfere with the naming mechanism, so we disable them temporarily in favor of the new per-wallet sequential naming.
|
|
19
18
|
|
|
20
19
|
### Fixed
|
|
21
20
|
|
|
22
|
-
- Fix
|
|
21
|
+
- Fix multi-wallet account group naming inconsistencies and duplicates ([#6601](https://github.com/MetaMask/core/pull/6601))
|
|
23
22
|
- Implement proper per-wallet sequential numbering with highest account index parsing.
|
|
24
23
|
- Add name persistence during group initialization to ensure consistency across app restarts.
|
|
25
24
|
|
|
25
|
+
## [0.17.0]
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- Single group sync events will not get enqueued anymore if a full sync is in progress ([#6651](https://github.com/MetaMask/core/pull/6651))
|
|
30
|
+
- This prevents too many unnecessary storage fetches (which would prevent being rate limited).
|
|
31
|
+
- This could rarely lead to inconsistencies until the next single updates or next full sync.
|
|
32
|
+
|
|
26
33
|
## [0.16.1]
|
|
27
34
|
|
|
28
35
|
### Added
|
|
@@ -267,7 +274,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
267
274
|
- Initial release ([#5847](https://github.com/MetaMask/core/pull/5847))
|
|
268
275
|
- Grouping accounts into 3 main categories: Entropy source, Snap ID, keyring types.
|
|
269
276
|
|
|
270
|
-
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/account-tree-controller@0.
|
|
277
|
+
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/account-tree-controller@0.17.0...HEAD
|
|
278
|
+
[0.17.0]: https://github.com/MetaMask/core/compare/@metamask/account-tree-controller@0.16.1...@metamask/account-tree-controller@0.17.0
|
|
271
279
|
[0.16.1]: https://github.com/MetaMask/core/compare/@metamask/account-tree-controller@0.16.0...@metamask/account-tree-controller@0.16.1
|
|
272
280
|
[0.16.0]: https://github.com/MetaMask/core/compare/@metamask/account-tree-controller@0.15.1...@metamask/account-tree-controller@0.16.0
|
|
273
281
|
[0.15.1]: https://github.com/MetaMask/core/compare/@metamask/account-tree-controller@0.15.0...@metamask/account-tree-controller@0.15.1
|
|
@@ -176,9 +176,9 @@ class AccountTreeController extends base_controller_1.BaseController {
|
|
|
176
176
|
this.update((state) => {
|
|
177
177
|
state.accountTree.wallets = wallets;
|
|
178
178
|
// Apply group metadata within the state update
|
|
179
|
-
for (const wallet of Object.values(wallets)) {
|
|
179
|
+
for (const wallet of Object.values(state.accountTree.wallets)) {
|
|
180
180
|
for (const group of Object.values(wallet.groups)) {
|
|
181
|
-
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_applyAccountGroupMetadata).call(this, state, wallet, group);
|
|
181
|
+
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_applyAccountGroupMetadata).call(this, state, wallet.id, group.id);
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
184
|
if (!previousSelectedAccountGroupStillExists ||
|
|
@@ -344,9 +344,8 @@ class AccountTreeController extends base_controller_1.BaseController {
|
|
|
344
344
|
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_assertAccountGroupNameIsUnique).call(this, groupId, finalName);
|
|
345
345
|
}
|
|
346
346
|
this.update((state) => {
|
|
347
|
-
var _a;
|
|
348
347
|
// Update persistent metadata
|
|
349
|
-
|
|
348
|
+
state.accountGroupsMetadata[groupId] = {};
|
|
350
349
|
state.accountGroupsMetadata[groupId].name = {
|
|
351
350
|
value: finalName,
|
|
352
351
|
lastUpdatedAt: Date.now(),
|
|
@@ -399,9 +398,8 @@ class AccountTreeController extends base_controller_1.BaseController {
|
|
|
399
398
|
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_assertAccountGroupExists).call(this, groupId);
|
|
400
399
|
const walletId = __classPrivateFieldGet(this, _AccountTreeController_groupIdToWalletId, "f").get(groupId);
|
|
401
400
|
this.update((state) => {
|
|
402
|
-
var _a;
|
|
403
401
|
// Update persistent metadata
|
|
404
|
-
|
|
402
|
+
state.accountGroupsMetadata[groupId] = {};
|
|
405
403
|
state.accountGroupsMetadata[groupId].pinned = {
|
|
406
404
|
value: pinned,
|
|
407
405
|
lastUpdatedAt: Date.now(),
|
|
@@ -431,9 +429,8 @@ class AccountTreeController extends base_controller_1.BaseController {
|
|
|
431
429
|
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_assertAccountGroupExists).call(this, groupId);
|
|
432
430
|
const walletId = __classPrivateFieldGet(this, _AccountTreeController_groupIdToWalletId, "f").get(groupId);
|
|
433
431
|
this.update((state) => {
|
|
434
|
-
var _a;
|
|
435
432
|
// Update persistent metadata
|
|
436
|
-
|
|
433
|
+
state.accountGroupsMetadata[groupId] = {};
|
|
437
434
|
state.accountGroupsMetadata[groupId].hidden = {
|
|
438
435
|
value: hidden,
|
|
439
436
|
lastUpdatedAt: Date.now(),
|
|
@@ -530,77 +527,74 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
|
|
|
530
527
|
default:
|
|
531
528
|
return __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getKeyringRule).call(this);
|
|
532
529
|
}
|
|
533
|
-
}, _AccountTreeController_applyAccountGroupMetadata = function _AccountTreeController_applyAccountGroupMetadata(state,
|
|
530
|
+
}, _AccountTreeController_applyAccountGroupMetadata = function _AccountTreeController_applyAccountGroupMetadata(state, walletId, groupId) {
|
|
534
531
|
var _a, _b;
|
|
532
|
+
const wallet = state.accountTree.wallets[walletId];
|
|
533
|
+
const group = wallet.groups[groupId];
|
|
535
534
|
const persistedGroupMetadata = state.accountGroupsMetadata[group.id];
|
|
536
535
|
// Apply persisted name if available (including empty strings)
|
|
537
536
|
if (persistedGroupMetadata?.name !== undefined) {
|
|
538
|
-
|
|
537
|
+
state.accountTree.wallets[walletId].groups[groupId].metadata.name =
|
|
538
|
+
persistedGroupMetadata.name.value;
|
|
539
539
|
}
|
|
540
540
|
else if (!group.metadata.name) {
|
|
541
541
|
// Get the appropriate rule for this wallet type
|
|
542
542
|
const rule = __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getRuleForWallet).call(this, wallet);
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
//
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
//
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
// Parse the highest account index being used (similar to accounts-controller)
|
|
555
|
-
let highestAccountNameIndex = 0;
|
|
556
|
-
for (const existingGroup of Object.values(wallet.groups)) {
|
|
557
|
-
// Skip the current group being processed
|
|
558
|
-
if (existingGroup.id === group.id) {
|
|
559
|
-
continue;
|
|
560
|
-
}
|
|
561
|
-
// Parse the existing group name to extract the numeric index
|
|
562
|
-
// TODO: This regex only matches "Account N" pattern. Hardware wallets (Trezor, Ledger, etc.)
|
|
563
|
-
// use different patterns like "Trezor N", "Ledger N" per keyringTypeToName().
|
|
564
|
-
// We'll enhance this to handle all keyring types in a future iteration.
|
|
565
|
-
const nameMatch = existingGroup.metadata.name.match(/Account (\d+)$/u);
|
|
566
|
-
if (nameMatch) {
|
|
567
|
-
const nameIndex = parseInt(nameMatch[1], 10);
|
|
568
|
-
if (nameIndex > highestAccountNameIndex) {
|
|
569
|
-
highestAccountNameIndex = nameIndex;
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
// For entropy-based multichain groups, start with the actual groupIndex
|
|
574
|
-
if (group.type === account_api_1.AccountGroupType.MultichainAccount &&
|
|
575
|
-
group.metadata.entropy) {
|
|
576
|
-
proposedNameIndex = group.metadata.entropy.groupIndex;
|
|
577
|
-
}
|
|
578
|
-
else {
|
|
579
|
-
// For other wallet types, start with the number of existing groups
|
|
580
|
-
// This gives us the next logical sequential number
|
|
581
|
-
proposedNameIndex = Object.keys(wallet.groups).length;
|
|
543
|
+
// Skip computed names for now - use default naming with per-wallet logic
|
|
544
|
+
// TODO: Implement computed names in a future iteration
|
|
545
|
+
// Generate default name and ensure it's unique within the wallet
|
|
546
|
+
let proposedName = '';
|
|
547
|
+
let proposedNameIndex;
|
|
548
|
+
// Parse the highest account index being used (similar to accounts-controller)
|
|
549
|
+
let highestAccountNameIndex = 0;
|
|
550
|
+
for (const existingGroup of Object.values(wallet.groups)) {
|
|
551
|
+
// Skip the current group being processed
|
|
552
|
+
if (existingGroup.id === group.id) {
|
|
553
|
+
continue;
|
|
582
554
|
}
|
|
583
|
-
//
|
|
584
|
-
|
|
585
|
-
//
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
if (nameExists) {
|
|
593
|
-
proposedNameIndex += 1; // Try next number
|
|
555
|
+
// Parse the existing group name to extract the numeric index
|
|
556
|
+
// TODO: This regex only matches "Account N" pattern. Hardware wallets (Trezor, Ledger, etc.)
|
|
557
|
+
// use different patterns like "Trezor N", "Ledger N" per keyringTypeToName().
|
|
558
|
+
// We'll enhance this to handle all keyring types in a future iteration.
|
|
559
|
+
const nameMatch = existingGroup.metadata.name.match(/Account (\d+)$/u);
|
|
560
|
+
if (nameMatch) {
|
|
561
|
+
const nameIndex = parseInt(nameMatch[1], 10);
|
|
562
|
+
if (nameIndex > highestAccountNameIndex) {
|
|
563
|
+
highestAccountNameIndex = nameIndex;
|
|
594
564
|
}
|
|
595
|
-
}
|
|
596
|
-
group.metadata.name = proposedName;
|
|
597
|
-
// Persist the generated name to ensure consistency
|
|
598
|
-
(_a = state.accountGroupsMetadata)[_b = group.id] ?? (_a[_b] = {});
|
|
599
|
-
state.accountGroupsMetadata[group.id].name = {
|
|
600
|
-
value: proposedName,
|
|
601
|
-
lastUpdatedAt: Date.now(),
|
|
602
|
-
};
|
|
565
|
+
}
|
|
603
566
|
}
|
|
567
|
+
// For entropy-based multichain groups, start with the actual groupIndex
|
|
568
|
+
if (group.type === account_api_1.AccountGroupType.MultichainAccount &&
|
|
569
|
+
group.metadata.entropy) {
|
|
570
|
+
proposedNameIndex = group.metadata.entropy.groupIndex;
|
|
571
|
+
}
|
|
572
|
+
else {
|
|
573
|
+
// For other wallet types, start with the number of existing groups
|
|
574
|
+
// This gives us the next logical sequential number
|
|
575
|
+
proposedNameIndex = Object.keys(wallet.groups).length;
|
|
576
|
+
}
|
|
577
|
+
// Use the higher of the two: highest parsed index or computed index
|
|
578
|
+
proposedNameIndex = Math.max(highestAccountNameIndex, proposedNameIndex);
|
|
579
|
+
// Find a unique name by checking for conflicts and incrementing if needed
|
|
580
|
+
let nameExists;
|
|
581
|
+
do {
|
|
582
|
+
proposedName = rule.getDefaultAccountGroupName(proposedNameIndex);
|
|
583
|
+
// Check if this name already exists in the wallet (excluding current group)
|
|
584
|
+
nameExists = !(0, group_1.isAccountGroupNameUniqueFromWallet)(wallet, group.id, proposedName);
|
|
585
|
+
/* istanbul ignore next */
|
|
586
|
+
if (nameExists) {
|
|
587
|
+
proposedNameIndex += 1; // Try next number
|
|
588
|
+
}
|
|
589
|
+
} while (nameExists);
|
|
590
|
+
state.accountTree.wallets[walletId].groups[groupId].metadata.name =
|
|
591
|
+
proposedName;
|
|
592
|
+
// Persist the generated name to ensure consistency
|
|
593
|
+
(_a = state.accountGroupsMetadata)[_b = group.id] ?? (_a[_b] = {});
|
|
594
|
+
state.accountGroupsMetadata[group.id].name = {
|
|
595
|
+
value: proposedName,
|
|
596
|
+
lastUpdatedAt: Date.now(),
|
|
597
|
+
};
|
|
604
598
|
}
|
|
605
599
|
// Apply persisted UI states
|
|
606
600
|
if (persistedGroupMetadata?.pinned?.value !== undefined) {
|
|
@@ -618,10 +612,7 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
|
|
|
618
612
|
const wallet = state.accountTree.wallets[walletId];
|
|
619
613
|
if (wallet) {
|
|
620
614
|
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_applyAccountWalletMetadata).call(this, wallet);
|
|
621
|
-
|
|
622
|
-
if (group) {
|
|
623
|
-
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_applyAccountGroupMetadata).call(this, state, wallet, group);
|
|
624
|
-
}
|
|
615
|
+
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_applyAccountGroupMetadata).call(this, state, walletId, groupId);
|
|
625
616
|
}
|
|
626
617
|
}
|
|
627
618
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccountTreeController.cjs","sourceRoot":"","sources":["../src/AccountTreeController.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n AccountWalletType,\n AccountGroupType,\n select,\n} from '@metamask/account-api';\nimport type {\n AccountGroupId,\n AccountWalletId,\n AccountSelector,\n MultichainAccountWalletId,\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, AccountGroupObjectOf } from './group';\nimport {\n isAccountGroupNameUnique,\n isAccountGroupNameUniqueFromWallet,\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\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 /**\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 // 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 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 for (const wallet of Object.values(wallets)) {\n this.#applyAccountWalletMetadata(wallet);\n\n for (const group of Object.values(wallet.groups)) {\n if (group.id === previousSelectedAccountGroup) {\n previousSelectedAccountGroupStillExists = true;\n }\n }\n }\n\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(wallets)) {\n for (const group of Object.values(wallet.groups)) {\n this.#applyAccountGroupMetadata(state, wallet, group);\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\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 wallet Account wallet object to update.\n */\n #applyAccountWalletMetadata(wallet: AccountWalletObject) {\n const persistedMetadata = this.state.accountWalletsMetadata[wallet.id];\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 wallet Account wallet object of the account group to update.\n * @param group Account group object to update.\n */\n #applyAccountGroupMetadata(\n state: AccountTreeControllerState,\n wallet: AccountWalletObject,\n group: AccountGroupObject,\n ) {\n const persistedGroupMetadata = state.accountGroupsMetadata[group.id];\n\n // Apply persisted name if available (including empty strings)\n if (persistedGroupMetadata?.name !== undefined) {\n group.metadata.name = 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 const typedWallet = wallet as AccountWalletObjectOf<typeof wallet.type>;\n const typedGroup = typedWallet.groups[group.id] as AccountGroupObjectOf<\n typeof group.type\n >;\n\n // Use computed name first, then fallback to default naming if empty\n const computedName = rule.getComputedAccountGroupName(typedGroup);\n\n if (computedName) {\n group.metadata.name = computedName;\n } else {\n // Generate default name and ensure it's unique within the wallet\n let proposedName = '';\n let proposedNameIndex: number;\n\n // Parse the highest account index being used (similar to accounts-controller)\n let highestAccountNameIndex = 0;\n for (const existingGroup of Object.values(wallet.groups)) {\n // Skip the current group being processed\n if (existingGroup.id === group.id) {\n continue;\n }\n // Parse the existing group name to extract the numeric index\n // TODO: This regex only matches \"Account N\" pattern. Hardware wallets (Trezor, Ledger, etc.)\n // use different patterns like \"Trezor N\", \"Ledger N\" per keyringTypeToName().\n // We'll enhance this to handle all keyring types in a future iteration.\n const nameMatch =\n existingGroup.metadata.name.match(/Account (\\d+)$/u);\n if (nameMatch) {\n const nameIndex = parseInt(nameMatch[1], 10);\n if (nameIndex > highestAccountNameIndex) {\n highestAccountNameIndex = nameIndex;\n }\n }\n }\n\n // For entropy-based multichain groups, start with the actual groupIndex\n if (\n group.type === AccountGroupType.MultichainAccount &&\n group.metadata.entropy\n ) {\n proposedNameIndex = group.metadata.entropy.groupIndex;\n } else {\n // For other wallet types, start with the number of existing groups\n // This gives us the next logical sequential number\n proposedNameIndex = Object.keys(wallet.groups).length;\n }\n\n // Use the higher of the two: highest parsed index or computed index\n proposedNameIndex = Math.max(\n highestAccountNameIndex,\n proposedNameIndex,\n );\n\n // Find a unique name by checking for conflicts and incrementing if needed\n let nameExists: boolean;\n do {\n proposedName = rule.getDefaultAccountGroupName(proposedNameIndex);\n\n // Check if this name already exists in the wallet (excluding current group)\n nameExists = !isAccountGroupNameUniqueFromWallet(\n wallet,\n group.id,\n proposedName,\n );\n\n /* istanbul ignore next */\n if (nameExists) {\n proposedNameIndex += 1; // Try next number\n }\n } while (nameExists);\n\n group.metadata.name = proposedName;\n\n // Persist the generated name to ensure consistency\n state.accountGroupsMetadata[group.id] ??= {};\n state.accountGroupsMetadata[group.id].name = {\n value: proposedName,\n lastUpdatedAt: Date.now(),\n };\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 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(wallet);\n\n const group = wallet.groups[groupId];\n if (group) {\n this.#applyAccountGroupMetadata(state, wallet, group);\n }\n }\n }\n });\n this.messagingSystem.publish(\n `${controllerName}:accountTreeChange`,\n this.state.accountTree,\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 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 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 if (!group) {\n wallet.groups[groupId] = {\n ...result.group,\n // Type-wise, we are guaranteed to always have at least 1 account.\n accounts: [account.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(account.id);\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 });\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 // Update persistent metadata\n state.accountGroupsMetadata[groupId] ??= {};\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 // Update persistent metadata\n state.accountGroupsMetadata[groupId] ??= {};\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 // Update persistent metadata\n state.accountGroupsMetadata[groupId] ??= {};\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\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 {\n AccountWalletType,\n AccountGroupType,\n select,\n} from '@metamask/account-api';\nimport type {\n AccountGroupId,\n AccountWalletId,\n AccountSelector,\n MultichainAccountWalletId,\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 } from './group';\nimport {\n isAccountGroupNameUnique,\n isAccountGroupNameUniqueFromWallet,\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\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 /**\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 // 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 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 for (const wallet of Object.values(wallets)) {\n this.#applyAccountWalletMetadata(wallet);\n\n for (const group of Object.values(wallet.groups)) {\n if (group.id === previousSelectedAccountGroup) {\n previousSelectedAccountGroupStillExists = true;\n }\n }\n }\n\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 for (const group of Object.values(wallet.groups)) {\n this.#applyAccountGroupMetadata(state, wallet.id, group.id);\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\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 wallet Account wallet object to update.\n */\n #applyAccountWalletMetadata(wallet: AccountWalletObject) {\n const persistedMetadata = this.state.accountWalletsMetadata[wallet.id];\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 */\n #applyAccountGroupMetadata(\n state: AccountTreeControllerState,\n walletId: AccountWalletId,\n groupId: AccountGroupId,\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 // Skip computed names for now - use default naming with per-wallet logic\n // TODO: Implement computed names in a future iteration\n\n // Generate default name and ensure it's unique within the wallet\n let proposedName = '';\n let proposedNameIndex: number;\n\n // Parse the highest account index being used (similar to accounts-controller)\n let highestAccountNameIndex = 0;\n for (const existingGroup of Object.values(\n wallet.groups,\n ) as AccountGroupObject[]) {\n // Skip the current group being processed\n if (existingGroup.id === group.id) {\n continue;\n }\n // Parse the existing group name to extract the numeric index\n // TODO: This regex only matches \"Account N\" pattern. Hardware wallets (Trezor, Ledger, etc.)\n // use different patterns like \"Trezor N\", \"Ledger N\" per keyringTypeToName().\n // We'll enhance this to handle all keyring types in a future iteration.\n const nameMatch = existingGroup.metadata.name.match(/Account (\\d+)$/u);\n if (nameMatch) {\n const nameIndex = parseInt(nameMatch[1], 10);\n if (nameIndex > highestAccountNameIndex) {\n highestAccountNameIndex = nameIndex;\n }\n }\n }\n\n // For entropy-based multichain groups, start with the actual groupIndex\n if (\n group.type === AccountGroupType.MultichainAccount &&\n group.metadata.entropy\n ) {\n proposedNameIndex = group.metadata.entropy.groupIndex;\n } else {\n // For other wallet types, start with the number of existing groups\n // This gives us the next logical sequential number\n proposedNameIndex = Object.keys(wallet.groups).length;\n }\n\n // Use the higher of the two: highest parsed index or computed index\n proposedNameIndex = Math.max(highestAccountNameIndex, proposedNameIndex);\n\n // Find a unique name by checking for conflicts and incrementing if needed\n let nameExists: boolean;\n do {\n proposedName = rule.getDefaultAccountGroupName(proposedNameIndex);\n\n // Check if this name already exists in the wallet (excluding current group)\n nameExists = !isAccountGroupNameUniqueFromWallet(\n wallet,\n group.id,\n proposedName,\n );\n\n /* istanbul ignore next */\n if (nameExists) {\n proposedNameIndex += 1; // Try next number\n }\n } while (nameExists);\n\n state.accountTree.wallets[walletId].groups[groupId].metadata.name =\n proposedName;\n\n // Persist the generated name to ensure consistency\n state.accountGroupsMetadata[group.id] ??= {};\n state.accountGroupsMetadata[group.id].name = {\n value: proposedName,\n lastUpdatedAt: Date.now(),\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 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(wallet);\n this.#applyAccountGroupMetadata(state, walletId, groupId);\n }\n }\n });\n this.messagingSystem.publish(\n `${controllerName}:accountTreeChange`,\n this.state.accountTree,\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 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 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 if (!group) {\n wallet.groups[groupId] = {\n ...result.group,\n // Type-wise, we are guaranteed to always have at least 1 account.\n accounts: [account.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(account.id);\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 });\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 // Update persistent metadata\n state.accountGroupsMetadata[groupId] = {};\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 // Update persistent metadata\n state.accountGroupsMetadata[groupId] = {};\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 // Update persistent metadata\n state.accountGroupsMetadata[groupId] = {};\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\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":"AAKA,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EACf,eAAe,EAEhB,8BAA8B;AAI/B,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAG3D,OAAO,KAAK,EAAE,eAAe,EAAE,uCAAuC;AAUtE,OAAO,KAAK,EAAE,kBAAkB,
|
|
1
|
+
{"version":3,"file":"AccountTreeController.d.cts","sourceRoot":"","sources":["../src/AccountTreeController.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EACf,eAAe,EAEhB,8BAA8B;AAI/B,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAG3D,OAAO,KAAK,EAAE,eAAe,EAAE,uCAAuC;AAUtE,OAAO,KAAK,EAAE,kBAAkB,EAAE,oBAAgB;AASlD,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;CACnC,CAAC;AAEF,qBAAa,qBAAsB,SAAQ,cAAc,CACvD,OAAO,cAAc,EACrB,0BAA0B,EAC1B,8BAA8B,CAC/B;;IAgBC;;;;;;;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;IAqFD;;;;;;OAMG;IACH,IAAI;IAsQJ;;;;;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;IA+PjC;;;;OAIG;IACH,uBAAuB,IAAI,cAAc,GAAG,EAAE;IAI9C;;;;OAIG;IACH,uBAAuB,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IAsMtD;;;;;;;;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;IAwCP;;;;;;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;IA+BrE;;;;;;OAMG;IACH,qBAAqB,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI;IA+BrE;;;OAGG;IACH,UAAU,IAAI,IAAI;IAiDlB;;;;;;;;;;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":"AAKA,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EACf,eAAe,EAEhB,8BAA8B;AAI/B,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAG3D,OAAO,KAAK,EAAE,eAAe,EAAE,uCAAuC;AAUtE,OAAO,KAAK,EAAE,kBAAkB,
|
|
1
|
+
{"version":3,"file":"AccountTreeController.d.mts","sourceRoot":"","sources":["../src/AccountTreeController.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EACf,eAAe,EAEhB,8BAA8B;AAI/B,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAG3D,OAAO,KAAK,EAAE,eAAe,EAAE,uCAAuC;AAUtE,OAAO,KAAK,EAAE,kBAAkB,EAAE,oBAAgB;AASlD,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;CACnC,CAAC;AAEF,qBAAa,qBAAsB,SAAQ,cAAc,CACvD,OAAO,cAAc,EACrB,0BAA0B,EAC1B,8BAA8B,CAC/B;;IAgBC;;;;;;;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;IAqFD;;;;;;OAMG;IACH,IAAI;IAsQJ;;;;;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;IA+PjC;;;;OAIG;IACH,uBAAuB,IAAI,cAAc,GAAG,EAAE;IAI9C;;;;OAIG;IACH,uBAAuB,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IAsMtD;;;;;;;;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;IAwCP;;;;;;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;IA+BrE;;;;;;OAMG;IACH,qBAAqB,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI;IA+BrE;;;OAGG;IACH,UAAU,IAAI,IAAI;IAiDlB;;;;;;;;;;OAUG;IACG,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAI1C;;;;;;;;;;;OAWG;IACG,8BAA8B,IAAI,OAAO,CAAC,IAAI,CAAC;CAoBtD"}
|
|
@@ -172,9 +172,9 @@ export class AccountTreeController extends BaseController {
|
|
|
172
172
|
this.update((state) => {
|
|
173
173
|
state.accountTree.wallets = wallets;
|
|
174
174
|
// Apply group metadata within the state update
|
|
175
|
-
for (const wallet of Object.values(wallets)) {
|
|
175
|
+
for (const wallet of Object.values(state.accountTree.wallets)) {
|
|
176
176
|
for (const group of Object.values(wallet.groups)) {
|
|
177
|
-
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_applyAccountGroupMetadata).call(this, state, wallet, group);
|
|
177
|
+
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_applyAccountGroupMetadata).call(this, state, wallet.id, group.id);
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
180
|
if (!previousSelectedAccountGroupStillExists ||
|
|
@@ -340,9 +340,8 @@ export class AccountTreeController extends BaseController {
|
|
|
340
340
|
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_assertAccountGroupNameIsUnique).call(this, groupId, finalName);
|
|
341
341
|
}
|
|
342
342
|
this.update((state) => {
|
|
343
|
-
var _a;
|
|
344
343
|
// Update persistent metadata
|
|
345
|
-
|
|
344
|
+
state.accountGroupsMetadata[groupId] = {};
|
|
346
345
|
state.accountGroupsMetadata[groupId].name = {
|
|
347
346
|
value: finalName,
|
|
348
347
|
lastUpdatedAt: Date.now(),
|
|
@@ -395,9 +394,8 @@ export class AccountTreeController extends BaseController {
|
|
|
395
394
|
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_assertAccountGroupExists).call(this, groupId);
|
|
396
395
|
const walletId = __classPrivateFieldGet(this, _AccountTreeController_groupIdToWalletId, "f").get(groupId);
|
|
397
396
|
this.update((state) => {
|
|
398
|
-
var _a;
|
|
399
397
|
// Update persistent metadata
|
|
400
|
-
|
|
398
|
+
state.accountGroupsMetadata[groupId] = {};
|
|
401
399
|
state.accountGroupsMetadata[groupId].pinned = {
|
|
402
400
|
value: pinned,
|
|
403
401
|
lastUpdatedAt: Date.now(),
|
|
@@ -427,9 +425,8 @@ export class AccountTreeController extends BaseController {
|
|
|
427
425
|
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_assertAccountGroupExists).call(this, groupId);
|
|
428
426
|
const walletId = __classPrivateFieldGet(this, _AccountTreeController_groupIdToWalletId, "f").get(groupId);
|
|
429
427
|
this.update((state) => {
|
|
430
|
-
var _a;
|
|
431
428
|
// Update persistent metadata
|
|
432
|
-
|
|
429
|
+
state.accountGroupsMetadata[groupId] = {};
|
|
433
430
|
state.accountGroupsMetadata[groupId].hidden = {
|
|
434
431
|
value: hidden,
|
|
435
432
|
lastUpdatedAt: Date.now(),
|
|
@@ -525,77 +522,74 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
|
|
|
525
522
|
default:
|
|
526
523
|
return __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getKeyringRule).call(this);
|
|
527
524
|
}
|
|
528
|
-
}, _AccountTreeController_applyAccountGroupMetadata = function _AccountTreeController_applyAccountGroupMetadata(state,
|
|
525
|
+
}, _AccountTreeController_applyAccountGroupMetadata = function _AccountTreeController_applyAccountGroupMetadata(state, walletId, groupId) {
|
|
529
526
|
var _a, _b;
|
|
527
|
+
const wallet = state.accountTree.wallets[walletId];
|
|
528
|
+
const group = wallet.groups[groupId];
|
|
530
529
|
const persistedGroupMetadata = state.accountGroupsMetadata[group.id];
|
|
531
530
|
// Apply persisted name if available (including empty strings)
|
|
532
531
|
if (persistedGroupMetadata?.name !== undefined) {
|
|
533
|
-
|
|
532
|
+
state.accountTree.wallets[walletId].groups[groupId].metadata.name =
|
|
533
|
+
persistedGroupMetadata.name.value;
|
|
534
534
|
}
|
|
535
535
|
else if (!group.metadata.name) {
|
|
536
536
|
// Get the appropriate rule for this wallet type
|
|
537
537
|
const rule = __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getRuleForWallet).call(this, wallet);
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
//
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
//
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
// Parse the highest account index being used (similar to accounts-controller)
|
|
550
|
-
let highestAccountNameIndex = 0;
|
|
551
|
-
for (const existingGroup of Object.values(wallet.groups)) {
|
|
552
|
-
// Skip the current group being processed
|
|
553
|
-
if (existingGroup.id === group.id) {
|
|
554
|
-
continue;
|
|
555
|
-
}
|
|
556
|
-
// Parse the existing group name to extract the numeric index
|
|
557
|
-
// TODO: This regex only matches "Account N" pattern. Hardware wallets (Trezor, Ledger, etc.)
|
|
558
|
-
// use different patterns like "Trezor N", "Ledger N" per keyringTypeToName().
|
|
559
|
-
// We'll enhance this to handle all keyring types in a future iteration.
|
|
560
|
-
const nameMatch = existingGroup.metadata.name.match(/Account (\d+)$/u);
|
|
561
|
-
if (nameMatch) {
|
|
562
|
-
const nameIndex = parseInt(nameMatch[1], 10);
|
|
563
|
-
if (nameIndex > highestAccountNameIndex) {
|
|
564
|
-
highestAccountNameIndex = nameIndex;
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
// For entropy-based multichain groups, start with the actual groupIndex
|
|
569
|
-
if (group.type === AccountGroupType.MultichainAccount &&
|
|
570
|
-
group.metadata.entropy) {
|
|
571
|
-
proposedNameIndex = group.metadata.entropy.groupIndex;
|
|
572
|
-
}
|
|
573
|
-
else {
|
|
574
|
-
// For other wallet types, start with the number of existing groups
|
|
575
|
-
// This gives us the next logical sequential number
|
|
576
|
-
proposedNameIndex = Object.keys(wallet.groups).length;
|
|
538
|
+
// Skip computed names for now - use default naming with per-wallet logic
|
|
539
|
+
// TODO: Implement computed names in a future iteration
|
|
540
|
+
// Generate default name and ensure it's unique within the wallet
|
|
541
|
+
let proposedName = '';
|
|
542
|
+
let proposedNameIndex;
|
|
543
|
+
// Parse the highest account index being used (similar to accounts-controller)
|
|
544
|
+
let highestAccountNameIndex = 0;
|
|
545
|
+
for (const existingGroup of Object.values(wallet.groups)) {
|
|
546
|
+
// Skip the current group being processed
|
|
547
|
+
if (existingGroup.id === group.id) {
|
|
548
|
+
continue;
|
|
577
549
|
}
|
|
578
|
-
//
|
|
579
|
-
|
|
580
|
-
//
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
if (nameExists) {
|
|
588
|
-
proposedNameIndex += 1; // Try next number
|
|
550
|
+
// Parse the existing group name to extract the numeric index
|
|
551
|
+
// TODO: This regex only matches "Account N" pattern. Hardware wallets (Trezor, Ledger, etc.)
|
|
552
|
+
// use different patterns like "Trezor N", "Ledger N" per keyringTypeToName().
|
|
553
|
+
// We'll enhance this to handle all keyring types in a future iteration.
|
|
554
|
+
const nameMatch = existingGroup.metadata.name.match(/Account (\d+)$/u);
|
|
555
|
+
if (nameMatch) {
|
|
556
|
+
const nameIndex = parseInt(nameMatch[1], 10);
|
|
557
|
+
if (nameIndex > highestAccountNameIndex) {
|
|
558
|
+
highestAccountNameIndex = nameIndex;
|
|
589
559
|
}
|
|
590
|
-
}
|
|
591
|
-
group.metadata.name = proposedName;
|
|
592
|
-
// Persist the generated name to ensure consistency
|
|
593
|
-
(_a = state.accountGroupsMetadata)[_b = group.id] ?? (_a[_b] = {});
|
|
594
|
-
state.accountGroupsMetadata[group.id].name = {
|
|
595
|
-
value: proposedName,
|
|
596
|
-
lastUpdatedAt: Date.now(),
|
|
597
|
-
};
|
|
560
|
+
}
|
|
598
561
|
}
|
|
562
|
+
// For entropy-based multichain groups, start with the actual groupIndex
|
|
563
|
+
if (group.type === AccountGroupType.MultichainAccount &&
|
|
564
|
+
group.metadata.entropy) {
|
|
565
|
+
proposedNameIndex = group.metadata.entropy.groupIndex;
|
|
566
|
+
}
|
|
567
|
+
else {
|
|
568
|
+
// For other wallet types, start with the number of existing groups
|
|
569
|
+
// This gives us the next logical sequential number
|
|
570
|
+
proposedNameIndex = Object.keys(wallet.groups).length;
|
|
571
|
+
}
|
|
572
|
+
// Use the higher of the two: highest parsed index or computed index
|
|
573
|
+
proposedNameIndex = Math.max(highestAccountNameIndex, proposedNameIndex);
|
|
574
|
+
// Find a unique name by checking for conflicts and incrementing if needed
|
|
575
|
+
let nameExists;
|
|
576
|
+
do {
|
|
577
|
+
proposedName = rule.getDefaultAccountGroupName(proposedNameIndex);
|
|
578
|
+
// Check if this name already exists in the wallet (excluding current group)
|
|
579
|
+
nameExists = !isAccountGroupNameUniqueFromWallet(wallet, group.id, proposedName);
|
|
580
|
+
/* istanbul ignore next */
|
|
581
|
+
if (nameExists) {
|
|
582
|
+
proposedNameIndex += 1; // Try next number
|
|
583
|
+
}
|
|
584
|
+
} while (nameExists);
|
|
585
|
+
state.accountTree.wallets[walletId].groups[groupId].metadata.name =
|
|
586
|
+
proposedName;
|
|
587
|
+
// Persist the generated name to ensure consistency
|
|
588
|
+
(_a = state.accountGroupsMetadata)[_b = group.id] ?? (_a[_b] = {});
|
|
589
|
+
state.accountGroupsMetadata[group.id].name = {
|
|
590
|
+
value: proposedName,
|
|
591
|
+
lastUpdatedAt: Date.now(),
|
|
592
|
+
};
|
|
599
593
|
}
|
|
600
594
|
// Apply persisted UI states
|
|
601
595
|
if (persistedGroupMetadata?.pinned?.value !== undefined) {
|
|
@@ -613,10 +607,7 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
|
|
|
613
607
|
const wallet = state.accountTree.wallets[walletId];
|
|
614
608
|
if (wallet) {
|
|
615
609
|
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_applyAccountWalletMetadata).call(this, wallet);
|
|
616
|
-
|
|
617
|
-
if (group) {
|
|
618
|
-
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_applyAccountGroupMetadata).call(this, state, wallet, group);
|
|
619
|
-
}
|
|
610
|
+
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_applyAccountGroupMetadata).call(this, state, walletId, groupId);
|
|
620
611
|
}
|
|
621
612
|
}
|
|
622
613
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccountTreeController.mjs","sourceRoot":"","sources":["../src/AccountTreeController.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n AccountWalletType,\n AccountGroupType,\n select,\n} from '@metamask/account-api';\nimport type {\n AccountGroupId,\n AccountWalletId,\n AccountSelector,\n MultichainAccountWalletId,\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, AccountGroupObjectOf } from './group';\nimport {\n isAccountGroupNameUnique,\n isAccountGroupNameUniqueFromWallet,\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\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 /**\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 // 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 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 for (const wallet of Object.values(wallets)) {\n this.#applyAccountWalletMetadata(wallet);\n\n for (const group of Object.values(wallet.groups)) {\n if (group.id === previousSelectedAccountGroup) {\n previousSelectedAccountGroupStillExists = true;\n }\n }\n }\n\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(wallets)) {\n for (const group of Object.values(wallet.groups)) {\n this.#applyAccountGroupMetadata(state, wallet, group);\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\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 wallet Account wallet object to update.\n */\n #applyAccountWalletMetadata(wallet: AccountWalletObject) {\n const persistedMetadata = this.state.accountWalletsMetadata[wallet.id];\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 wallet Account wallet object of the account group to update.\n * @param group Account group object to update.\n */\n #applyAccountGroupMetadata(\n state: AccountTreeControllerState,\n wallet: AccountWalletObject,\n group: AccountGroupObject,\n ) {\n const persistedGroupMetadata = state.accountGroupsMetadata[group.id];\n\n // Apply persisted name if available (including empty strings)\n if (persistedGroupMetadata?.name !== undefined) {\n group.metadata.name = 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 const typedWallet = wallet as AccountWalletObjectOf<typeof wallet.type>;\n const typedGroup = typedWallet.groups[group.id] as AccountGroupObjectOf<\n typeof group.type\n >;\n\n // Use computed name first, then fallback to default naming if empty\n const computedName = rule.getComputedAccountGroupName(typedGroup);\n\n if (computedName) {\n group.metadata.name = computedName;\n } else {\n // Generate default name and ensure it's unique within the wallet\n let proposedName = '';\n let proposedNameIndex: number;\n\n // Parse the highest account index being used (similar to accounts-controller)\n let highestAccountNameIndex = 0;\n for (const existingGroup of Object.values(wallet.groups)) {\n // Skip the current group being processed\n if (existingGroup.id === group.id) {\n continue;\n }\n // Parse the existing group name to extract the numeric index\n // TODO: This regex only matches \"Account N\" pattern. Hardware wallets (Trezor, Ledger, etc.)\n // use different patterns like \"Trezor N\", \"Ledger N\" per keyringTypeToName().\n // We'll enhance this to handle all keyring types in a future iteration.\n const nameMatch =\n existingGroup.metadata.name.match(/Account (\\d+)$/u);\n if (nameMatch) {\n const nameIndex = parseInt(nameMatch[1], 10);\n if (nameIndex > highestAccountNameIndex) {\n highestAccountNameIndex = nameIndex;\n }\n }\n }\n\n // For entropy-based multichain groups, start with the actual groupIndex\n if (\n group.type === AccountGroupType.MultichainAccount &&\n group.metadata.entropy\n ) {\n proposedNameIndex = group.metadata.entropy.groupIndex;\n } else {\n // For other wallet types, start with the number of existing groups\n // This gives us the next logical sequential number\n proposedNameIndex = Object.keys(wallet.groups).length;\n }\n\n // Use the higher of the two: highest parsed index or computed index\n proposedNameIndex = Math.max(\n highestAccountNameIndex,\n proposedNameIndex,\n );\n\n // Find a unique name by checking for conflicts and incrementing if needed\n let nameExists: boolean;\n do {\n proposedName = rule.getDefaultAccountGroupName(proposedNameIndex);\n\n // Check if this name already exists in the wallet (excluding current group)\n nameExists = !isAccountGroupNameUniqueFromWallet(\n wallet,\n group.id,\n proposedName,\n );\n\n /* istanbul ignore next */\n if (nameExists) {\n proposedNameIndex += 1; // Try next number\n }\n } while (nameExists);\n\n group.metadata.name = proposedName;\n\n // Persist the generated name to ensure consistency\n state.accountGroupsMetadata[group.id] ??= {};\n state.accountGroupsMetadata[group.id].name = {\n value: proposedName,\n lastUpdatedAt: Date.now(),\n };\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 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(wallet);\n\n const group = wallet.groups[groupId];\n if (group) {\n this.#applyAccountGroupMetadata(state, wallet, group);\n }\n }\n }\n });\n this.messagingSystem.publish(\n `${controllerName}:accountTreeChange`,\n this.state.accountTree,\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 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 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 if (!group) {\n wallet.groups[groupId] = {\n ...result.group,\n // Type-wise, we are guaranteed to always have at least 1 account.\n accounts: [account.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(account.id);\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 });\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 // Update persistent metadata\n state.accountGroupsMetadata[groupId] ??= {};\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 // Update persistent metadata\n state.accountGroupsMetadata[groupId] ??= {};\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 // Update persistent metadata\n state.accountGroupsMetadata[groupId] ??= {};\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\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":";;;;;;;;;;;;AAAA,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,MAAM,EACP,8BAA8B;AAU/B,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAE3D,OAAO,EAAE,gBAAgB,EAAE,8BAA8B;AAEzD,OAAO,EAAE,MAAM,EAAE,wBAAwB;AAGzC,OAAO,EACL,oBAAoB,EACpB,aAAa,EACd,8CAAoC;AACrC,OAAO,EAAE,oBAAoB,EAAE,4CAAkC;AAGjE,OAAO,EACL,wBAAwB,EACxB,kCAAkC,EACnC,oBAAgB;AAEjB,OAAO,EAAE,WAAW,EAAE,4BAAwB;AAC9C,OAAO,EAAE,WAAW,EAAE,4BAAwB;AAC9C,OAAO,EAAE,QAAQ,EAAE,yBAAqB;AASxC,MAAM,CAAC,MAAM,cAAc,GAAG,uBAAuB,CAAC;AAEtD,MAAM,6BAA6B,GACjC;IACE,WAAW,EAAE;QACX,kBAAkB,EAAE,IAAI;QACxB,OAAO,EAAE,KAAK;QACd,SAAS,EAAE,KAAK;QAChB,QAAQ,EAAE,IAAI;KACf;IACD,8BAA8B,EAAE;QAC9B,kBAAkB,EAAE,KAAK;QACzB,OAAO,EAAE,KAAK;QACd,SAAS,EAAE,KAAK;QAChB,QAAQ,EAAE,IAAI;KACf;IACD,sCAAsC,EAAE;QACtC,kBAAkB,EAAE,IAAI;QACxB,OAAO,EAAE,IAAI;QACb,SAAS,EAAE,KAAK;QAChB,QAAQ,EAAE,IAAI;KACf;IACD,qBAAqB,EAAE;QACrB,kBAAkB,EAAE,IAAI;QACxB,OAAO,EAAE,IAAI;QACb,SAAS,EAAE,KAAK;QAChB,QAAQ,EAAE,IAAI;KACf;IACD,sBAAsB,EAAE;QACtB,kBAAkB,EAAE,IAAI;QACxB,OAAO,EAAE,IAAI;QACb,SAAS,EAAE,KAAK;QAChB,QAAQ,EAAE,IAAI;KACf;CACF,CAAC;AAEJ;;;;GAIG;AACH,MAAM,UAAU,oCAAoC;IAClD,OAAO;QACL,WAAW,EAAE;YACX,OAAO,EAAE,EAAE;YACX,oBAAoB,EAAE,EAAE;SACzB;QACD,8BAA8B,EAAE,KAAK;QACrC,sCAAsC,EAAE,KAAK;QAC7C,qBAAqB,EAAE,EAAE;QACzB,sBAAsB,EAAE,EAAE;KAC3B,CAAC;AACJ,CAAC;AAiBD,MAAM,OAAO,qBAAsB,SAAQ,cAI1C;IAgBC;;;;;;;OAOG;IAEH,YAAY,EACV,SAAS,EACT,KAAK,EACL,MAAM,GAKP;QACC,KAAK,CAAC;YACJ,SAAS;YACT,IAAI,EAAE,cAAc;YACpB,QAAQ,EAAE,6BAA6B;YACvC,KAAK,EAAE;gBACL,GAAG,oCAAoC,EAAE;gBACzC,GAAG,KAAK;aACT;SACF,CAAC,CAAC;;QAzCI,4DAAoD;QAEpD,2DAAyD;QAElE;;WAEG;QACM,8DAA4C;QAE5C,+CAA6C;QAE7C,+CAAsB;QAEtB,6DAAuE;QA8B9E,4DAA4D;QAC5D,uBAAA,IAAI,6CAAuB,IAAI,GAAG,EAAE,MAAA,CAAC;QAErC,gEAAgE;QAChE,uBAAA,IAAI,4CAAsB,IAAI,GAAG,EAAE,MAAA,CAAC;QAEpC,gDAAgD;QAChD,uBAAA,IAAI,gCAAU;YACZ,gCAAgC;YAChC,IAAI,WAAW,CAAC,IAAI,CAAC,eAAe,CAAC;YACrC,yBAAyB;YACzB,IAAI,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC;YAClC,8FAA8F;YAC9F,IAAI,WAAW,CAAC,IAAI,CAAC,eAAe,CAAC;SACtC,MAAA,CAAC;QAEF,4BAA4B;QAC5B,uBAAA,IAAI,gCAAU,MAAM,EAAE,KAAK,IAAI,aAAa,MAAA,CAAC;QAE7C,oCAAoC;QACpC,uBAAA,IAAI,8CAAwB;YAC1B,oBAAoB,EAAE,CAAC,KAA4C,EAAE,EAAE;gBACrE,OAAO,CACL,MAAM,EAAE,aAAa,EAAE,oBAAoB;oBAC3C,MAAM,CAAC,aAAa,CAAC,oBAAoB,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC,CACvE,CAAC;YACJ,CAAC;SACF,MAAA,CAAC;QAEF,yCAAyC;QACzC,uBAAA,IAAI,+CAAyB,IAAI,oBAAoB,CACnD,uBAAA,IAAI,2FAA4B,MAAhC,IAAI,CAA8B,CACnC,MAAA,CAAC;QAEF,IAAI,CAAC,eAAe,CAAC,SAAS,CAC5B,iCAAiC,EACjC,CAAC,OAAO,EAAE,EAAE;YACV,uBAAA,IAAI,mFAAoB,MAAxB,IAAI,EAAqB,OAAO,CAAC,CAAC;QACpC,CAAC,CACF,CAAC;QAEF,IAAI,CAAC,eAAe,CAAC,SAAS,CAC5B,mCAAmC,EACnC,CAAC,SAAS,EAAE,EAAE;YACZ,uBAAA,IAAI,qFAAsB,MAA1B,IAAI,EAAuB,SAAS,CAAC,CAAC;QACxC,CAAC,CACF,CAAC;QAEF,IAAI,CAAC,eAAe,CAAC,SAAS,CAC5B,0CAA0C,EAC1C,CAAC,OAAO,EAAE,EAAE;YACV,uBAAA,IAAI,4FAA6B,MAAjC,IAAI,EAA8B,OAAO,CAAC,CAAC;QAC7C,CAAC,CACF,CAAC;QAEF,IAAI,CAAC,eAAe,CAAC,SAAS,CAC5B,mCAAmC,EACnC,CAAC,0BAA0B,EAAE,EAAE;YAC7B,uBAAA,IAAI,mDAAsB,CAAC,4BAA4B,CACrD,0BAA0B,CAC3B,CAAC;QACJ,CAAC,CACF,CAAC;QAEF,IAAI,CAAC,eAAe,CAAC,SAAS,CAC5B,6CAA6C,EAC7C,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE;YACnB,uBAAA,IAAI,0GAA2C,MAA/C,IAAI,EAA4C,QAAQ,EAAE,MAAM,CAAC,CAAC;QACpE,CAAC,CACF,CAAC;QAEF,uBAAA,IAAI,wFAAyB,MAA7B,IAAI,CAA2B,CAAC;IAClC,CAAC;IAED;;;;;;OAMG;IACH,IAAI;QACF,MAAM,OAAO,GAAyD,EAAE,CAAC;QAEzE,oCAAoC;QACpC,uBAAA,IAAI,iDAAoB,CAAC,KAAK,EAAE,CAAC;QACjC,uBAAA,IAAI,gDAAmB,CAAC,KAAK,EAAE,CAAC;QAEhC,0EAA0E;QAC1E,uBAAuB;QACvB,MAAM,4BAA4B,GAChC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,oBAAoB,CAAC;QAE9C,kFAAkF;QAClF,KAAK,MAAM,OAAO,IAAI,uBAAA,IAAI,6EAAc,MAAlB,IAAI,CAAgB,EAAE;YAC1C,uBAAA,IAAI,uEAAQ,MAAZ,IAAI,EAAS,OAAO,EAAE,OAAO,CAAC,CAAC;SAChC;QAED,sFAAsF;QACtF,IAAI,uCAAuC,GAAG,KAAK,CAAC;QACpD,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE;YAC3C,uBAAA,IAAI,2FAA4B,MAAhC,IAAI,EAA6B,MAAM,CAAC,CAAC;YAEzC,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE;gBAChD,IAAI,KAAK,CAAC,EAAE,KAAK,4BAA4B,EAAE;oBAC7C,uCAAuC,GAAG,IAAI,CAAC;iBAChD;aACF;SACF;QAED,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACpB,KAAK,CAAC,WAAW,CAAC,OAAO,GAAG,OAAO,CAAC;YAEpC,+CAA+C;YAC/C,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE;gBAC7D,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE;oBAChD,uBAAA,IAAI,0FAA2B,MAA/B,IAAI,EAA4B,KAAK,EAAE,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;iBAC7D;aACF;YAED,IACE,CAAC,uCAAuC;gBACxC,4BAA4B,KAAK,EAAE,EACnC;gBACA,uEAAuE;gBACvE,sBAAsB;gBACtB,KAAK,CAAC,WAAW,CAAC,oBAAoB;oBACpC,uBAAA,IAAI,+FAAgC,MAApC,IAAI,EAAiC,OAAO,CAAC,CAAC;aACjD;QACH,CAAC,CAAC,CAAC;QAEH,gFAAgF;QAChF,6EAA6E;QAC7E,6EAA6E;QAC7E,mCAAmC;QACnC,IACE,4BAA4B;YAC5B,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,oBAAoB,EAC3C;YACA,IAAI,CAAC,eAAe,CAAC,OAAO,CAC1B,GAAG,cAAc,6BAA6B,EAC9C,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,oBAAoB,EAC3C,4BAA4B,CAC7B,CAAC;SACH;IACH,CAAC;IAsMD;;;;;OAKG;IACH,sBAAsB,CACpB,QAAyB;QAEzB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACxD,IAAI,CAAC,MAAM,EAAE;YACX,OAAO,SAAS,CAAC;SAClB;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;;;OAIG;IACH,uBAAuB;QACrB,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IACvD,CAAC;IAED;;;;;;;;;;OAUG;IACH,mCAAmC,CACjC,QAA2C;QAE3C,MAAM,OAAO,GAAG,IAAI,CAAC,uBAAuB,EAAE,CAAC;QAC/C,IAAI,CAAC,OAAO,EAAE;YACZ,OAAO,EAAE,CAAC;SACX;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,qBAAqB,CAAC,OAAO,CAAC,CAAC;QAClD,iEAAiE;QACjE,0BAA0B;QAC1B,IAAI,CAAC,KAAK,EAAE;YACV,OAAO,EAAE,CAAC;SACX;QAED,MAAM,QAAQ,GAAsB,EAAE,CAAC;QACvC,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,QAAQ,EAAE;YAC/B,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CACvC,+BAA+B,EAC/B,EAAE,CACH,CAAC;YAEF,4DAA4D;YAC5D,2CAA2C;YAC3C,IAAI,OAAO,EAAE;gBACX,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;aACxB;SACF;QAED,OAAO,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IAC1D,CAAC;IAED;;;;;OAKG;IACH,qBAAqB,CACnB,OAAuB;QAEvB,MAAM,QAAQ,GAAG,uBAAA,IAAI,gDAAmB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACtD,IAAI,CAAC,QAAQ,EAAE;YACb,OAAO,SAAS,CAAC;SAClB;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,sBAAsB,CAAC,QAAQ,CAAC,CAAC;QACrD,OAAO,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IACjC,CAAC;IAuPD;;;;OAIG;IACH,uBAAuB;QACrB,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,oBAAoB,CAAC;IACrD,CAAC;IAED;;;;OAIG;IACH,uBAAuB,CAAC,OAAuB;QAC7C,MAAM,4BAA4B,GAChC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,oBAAoB,CAAC;QAE9C,uEAAuE;QACvE,IAAI,4BAA4B,KAAK,OAAO,EAAE;YAC5C,OAAO;SACR;QAED,iDAAiD;QACjD,MAAM,eAAe,GAAG,uBAAA,IAAI,oGAAqC,MAAzC,IAAI,EAAsC,OAAO,CAAC,CAAC;QAC3E,IAAI,CAAC,eAAe,EAAE;YACpB,MAAM,IAAI,KAAK,CAAC,+BAA+B,OAAO,EAAE,CAAC,CAAC;SAC3D;QAED,yBAAyB;QACzB,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACpB,KAAK,CAAC,WAAW,CAAC,oBAAoB,GAAG,OAAO,CAAC;QACnD,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,eAAe,CAAC,OAAO,CAC1B,GAAG,cAAc,6BAA6B,EAC9C,OAAO,EACP,4BAA4B,CAC7B,CAAC;QAEF,6EAA6E;QAC7E,gEAAgE;QAChE,IAAI,CAAC,eAAe,CAAC,IAAI,CACvB,uCAAuC,EACvC,eAAe,CAChB,CAAC;IACJ,CAAC;IAuKD;;;;;;;;OAQG;IACH,mBAAmB,CACjB,MAA2B,EAC3B,OAAuB,EACvB,IAAY;QAEZ,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,IAAI,aAAa,GAAG,GAAG,IAAI,KAAK,MAAM,GAAG,CAAC;QAE1C,uDAAuD;QACvD,OACE,CAAC,kCAAkC,CAAC,MAAM,EAAE,OAAO,EAAE,aAAa,CAAC,EACnE;YACA,MAAM,IAAI,CAAC,CAAC;YACZ,aAAa,GAAG,GAAG,IAAI,KAAK,MAAM,GAAG,CAAC;SACvC;QAED,OAAO,aAAa,CAAC;IACvB,CAAC;IAED;;;;;;;;OAQG;IACH,mBAAmB,CACjB,OAAuB,EACvB,IAAY,EACZ,qBAA8B,KAAK;QAEnC,qDAAqD;QACrD,uBAAA,IAAI,yFAA0B,MAA9B,IAAI,EAA2B,OAAO,CAAC,CAAC;QAExC,MAAM,QAAQ,GAAG,uBAAA,IAAI,gDAAmB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACtD,MAAM,CAAC,QAAQ,EAAE,0BAA0B,OAAO,qBAAqB,CAAC,CAAC;QAEzE,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACxD,IAAI,SAAS,GAAG,IAAI,CAAC;QAErB,6DAA6D;QAC7D,IACE,kBAAkB;YAClB,CAAC,kCAAkC,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,EAC1D;YACA,SAAS,GAAG,IAAI,CAAC,mBAAmB,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;SAC7D;aAAM;YACL,mCAAmC;YACnC,uBAAA,IAAI,+FAAgC,MAApC,IAAI,EAAiC,OAAO,EAAE,SAAS,CAAC,CAAC;SAC1D;QAED,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACpB,6BAA6B;YAC7B,KAAK,CAAC,qBAAqB,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;YAC1C,KAAK,CAAC,qBAAqB,CAAC,OAAO,CAAC,CAAC,IAAI,GAAG;gBAC1C,KAAK,EAAE,SAAS;gBAChB,aAAa,EAAE,IAAI,CAAC,GAAG,EAAE;aAC1B,CAAC;YAEF,oDAAoD;YACpD,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,IAAI;gBAC/D,SAAS,CAAC;QACd,CAAC,CAAC,CAAC;QAEH,8EAA8E;QAC9E,IAAI,MAAM,CAAC,IAAI,KAAK,iBAAiB,CAAC,OAAO,EAAE;YAC7C,uBAAA,IAAI,mDAAsB,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC;SAC5D;IACH,CAAC;IAED;;;;;;OAMG;IACH,oBAAoB,CAAC,QAAyB,EAAE,IAAY;QAC1D,sDAAsD;QACtD,uBAAA,IAAI,0FAA2B,MAA/B,IAAI,EAA4B,QAAQ,CAAC,CAAC;QAE1C,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;;YACpB,6BAA6B;YAC7B,MAAA,KAAK,CAAC,sBAAsB,EAAC,QAAQ,SAAR,QAAQ,IAAM,EAAE,EAAC;YAC9C,KAAK,CAAC,sBAAsB,CAAC,QAAQ,CAAC,CAAC,IAAI,GAAG;gBAC5C,KAAK,EAAE,IAAI;gBACX,aAAa,EAAE,IAAI,CAAC,GAAG,EAAE;aAC1B,CAAC;YAEF,4BAA4B;YAC5B,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,IAAI,GAAG,IAAI,CAAC;QAC3D,CAAC,CAAC,CAAC;QAEH,+EAA+E;QAC/E,IACE,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,IAAI;YAC7C,iBAAiB,CAAC,OAAO,EACzB;YACA,uBAAA,IAAI,mDAAsB,CAAC,uBAAuB,CAAC,QAAQ,CAAC,CAAC;SAC9D;IACH,CAAC;IAED;;;;;;OAMG;IACH,qBAAqB,CAAC,OAAuB,EAAE,MAAe;QAC5D,qDAAqD;QACrD,uBAAA,IAAI,yFAA0B,MAA9B,IAAI,EAA2B,OAAO,CAAC,CAAC;QAExC,MAAM,QAAQ,GAAG,uBAAA,IAAI,gDAAmB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAEtD,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACpB,6BAA6B;YAC7B,KAAK,CAAC,qBAAqB,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;YAC1C,KAAK,CAAC,qBAAqB,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG;gBAC5C,KAAK,EAAE,MAAM;gBACb,aAAa,EAAE,IAAI,CAAC,GAAG,EAAE;aAC1B,CAAC;YAEF,oDAAoD;YACpD,IAAI,QAAQ,EAAE;gBACZ,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,MAAM;oBACjE,MAAM,CAAC;aACV;QACH,CAAC,CAAC,CAAC;QAEH,+EAA+E;QAC/E,IACE,QAAQ;YACR,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,IAAI;gBAC3C,iBAAiB,CAAC,OAAO,EAC3B;YACA,uBAAA,IAAI,mDAAsB,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC;SAC5D;IACH,CAAC;IAED;;;;;;OAMG;IACH,qBAAqB,CAAC,OAAuB,EAAE,MAAe;QAC5D,qDAAqD;QACrD,uBAAA,IAAI,yFAA0B,MAA9B,IAAI,EAA2B,OAAO,CAAC,CAAC;QAExC,MAAM,QAAQ,GAAG,uBAAA,IAAI,gDAAmB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAEtD,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACpB,6BAA6B;YAC7B,KAAK,CAAC,qBAAqB,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;YAC1C,KAAK,CAAC,qBAAqB,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG;gBAC5C,KAAK,EAAE,MAAM;gBACb,aAAa,EAAE,IAAI,CAAC,GAAG,EAAE;aAC1B,CAAC;YAEF,oDAAoD;YACpD,IAAI,QAAQ,EAAE;gBACZ,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,MAAM;oBACjE,MAAM,CAAC;aACV;QACH,CAAC,CAAC,CAAC;QAEH,8EAA8E;QAC9E,IACE,QAAQ;YACR,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,IAAI;gBAC3C,iBAAiB,CAAC,OAAO,EAC3B;YACA,uBAAA,IAAI,mDAAsB,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC;SAC5D;IACH,CAAC;IAED;;;OAGG;IACH,UAAU;QACR,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE;YACf,OAAO;gBACL,GAAG,oCAAoC,EAAE;aAC1C,CAAC;QACJ,CAAC,CAAC,CAAC;QACH,uBAAA,IAAI,mDAAsB,CAAC,UAAU,EAAE,CAAC;IAC1C,CAAC;IA0CD;;;;;;;;;;OAUG;IACH,KAAK,CAAC,mBAAmB;QACvB,OAAO,uBAAA,IAAI,mDAAsB,CAAC,eAAe,EAAE,CAAC;IACtD,CAAC;IAED;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,8BAA8B;QAClC,OAAO,uBAAA,IAAI,mDAAsB,CAAC,0BAA0B,EAAE,CAAC;IACjE,CAAC;CAkBF;;IA//BG,OAAO,uBAAA,IAAI,oCAAO,CAAC,CAAC,CAAC,CAAC;AACxB,CAAC;IAQC,OAAO,uBAAA,IAAI,oCAAO,CAAC,CAAC,CAAC,CAAC;AACxB,CAAC;IAYC,OAAO,uBAAA,IAAI,oCAAO,CAAC,CAAC,CAAC,CAAC;AACxB,CAAC,iHAS2B,MAA2B;IACrD,MAAM,iBAAiB,GAAG,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAEvE,8DAA8D;IAC9D,IAAI,iBAAiB,EAAE,IAAI,KAAK,SAAS,EAAE;QACzC,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAG,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC;KACrD;SAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE;QAChC,uCAAuC;QACvC,IAAI,MAAM,CAAC,IAAI,KAAK,iBAAiB,CAAC,OAAO,EAAE;YAC7C,MAAM,CAAC,QAAQ,CAAC,IAAI;gBAClB,uBAAA,IAAI,+EAAgB,MAApB,IAAI,CAAkB,CAAC,2BAA2B,CAAC,MAAM,CAAC,CAAC;SAC9D;aAAM,IAAI,MAAM,CAAC,IAAI,KAAK,iBAAiB,CAAC,IAAI,EAAE;YACjD,MAAM,CAAC,QAAQ,CAAC,IAAI;gBAClB,uBAAA,IAAI,4EAAa,MAAjB,IAAI,CAAe,CAAC,2BAA2B,CAAC,MAAM,CAAC,CAAC;SAC3D;aAAM;YACL,MAAM,CAAC,QAAQ,CAAC,IAAI;gBAClB,uBAAA,IAAI,+EAAgB,MAApB,IAAI,CAAkB,CAAC,2BAA2B,CAAC,MAAM,CAAC,CAAC;SAC9D;KACF;AACH,CAAC,6FASC,MAAyC;IAEzC,QAAQ,MAAM,CAAC,IAAI,EAAE;QACnB,KAAK,iBAAiB,CAAC,OAAO;YAC5B,OAAO,uBAAA,IAAI,+EAAgB,MAApB,IAAI,CAGV,CAAC;QACJ,KAAK,iBAAiB,CAAC,IAAI;YACzB,OAAO,uBAAA,IAAI,4EAAa,MAAjB,IAAI,CAGV,CAAC;QACJ;YACE,OAAO,uBAAA,IAAI,+EAAgB,MAApB,IAAI,CAGV,CAAC;KACL;AACH,CAAC,+GAaC,KAAiC,EACjC,QAAyB,EACzB,OAAuB;;IAEvB,MAAM,MAAM,GAAG,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACrC,MAAM,sBAAsB,GAAG,KAAK,CAAC,qBAAqB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAErE,8DAA8D;IAC9D,IAAI,sBAAsB,EAAE,IAAI,KAAK,SAAS,EAAE;QAC9C,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,IAAI;YAC/D,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC;KACrC;SAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE;QAC/B,gDAAgD;QAChD,MAAM,IAAI,GAAG,uBAAA,IAAI,iFAAkB,MAAtB,IAAI,EAAmB,MAAM,CAAC,CAAC;QAE5C,yEAAyE;QACzE,uDAAuD;QAEvD,iEAAiE;QACjE,IAAI,YAAY,GAAG,EAAE,CAAC;QACtB,IAAI,iBAAyB,CAAC;QAE9B,8EAA8E;QAC9E,IAAI,uBAAuB,GAAG,CAAC,CAAC;QAChC,KAAK,MAAM,aAAa,IAAI,MAAM,CAAC,MAAM,CACvC,MAAM,CAAC,MAAM,CACU,EAAE;YACzB,yCAAyC;YACzC,IAAI,aAAa,CAAC,EAAE,KAAK,KAAK,CAAC,EAAE,EAAE;gBACjC,SAAS;aACV;YACD,6DAA6D;YAC7D,6FAA6F;YAC7F,8EAA8E;YAC9E,wEAAwE;YACxE,MAAM,SAAS,GAAG,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;YACvE,IAAI,SAAS,EAAE;gBACb,MAAM,SAAS,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC7C,IAAI,SAAS,GAAG,uBAAuB,EAAE;oBACvC,uBAAuB,GAAG,SAAS,CAAC;iBACrC;aACF;SACF;QAED,wEAAwE;QACxE,IACE,KAAK,CAAC,IAAI,KAAK,gBAAgB,CAAC,iBAAiB;YACjD,KAAK,CAAC,QAAQ,CAAC,OAAO,EACtB;YACA,iBAAiB,GAAG,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,CAAC;SACvD;aAAM;YACL,mEAAmE;YACnE,mDAAmD;YACnD,iBAAiB,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC;SACvD;QAED,oEAAoE;QACpE,iBAAiB,GAAG,IAAI,CAAC,GAAG,CAAC,uBAAuB,EAAE,iBAAiB,CAAC,CAAC;QAEzE,0EAA0E;QAC1E,IAAI,UAAmB,CAAC;QACxB,GAAG;YACD,YAAY,GAAG,IAAI,CAAC,0BAA0B,CAAC,iBAAiB,CAAC,CAAC;YAElE,4EAA4E;YAC5E,UAAU,GAAG,CAAC,kCAAkC,CAC9C,MAAM,EACN,KAAK,CAAC,EAAE,EACR,YAAY,CACb,CAAC;YAEF,0BAA0B;YAC1B,IAAI,UAAU,EAAE;gBACd,iBAAiB,IAAI,CAAC,CAAC,CAAC,kBAAkB;aAC3C;SACF,QAAQ,UAAU,EAAE;QAErB,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,IAAI;YAC/D,YAAY,CAAC;QAEf,mDAAmD;QACnD,MAAA,KAAK,CAAC,qBAAqB,OAAC,KAAK,CAAC,EAAE,eAAM,EAAE,EAAC;QAC7C,KAAK,CAAC,qBAAqB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,GAAG;YAC3C,KAAK,EAAE,YAAY;YACnB,aAAa,EAAE,IAAI,CAAC,GAAG,EAAE;SAC1B,CAAC;KACH;IAED,4BAA4B;IAC5B,IAAI,sBAAsB,EAAE,MAAM,EAAE,KAAK,KAAK,SAAS,EAAE;QACvD,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,sBAAsB,CAAC,MAAM,CAAC,KAAK,CAAC;KAC7D;IACD,IAAI,sBAAsB,EAAE,MAAM,EAAE,KAAK,KAAK,SAAS,EAAE;QACvD,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,sBAAsB,CAAC,MAAM,CAAC,KAAK,CAAC;KAC7D;AACH,CAAC,iGA+FmB,OAAwB;IAC1C,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;QACpB,uBAAA,IAAI,uEAAQ,MAAZ,IAAI,EAAS,KAAK,CAAC,WAAW,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAEjD,MAAM,OAAO,GAAG,uBAAA,IAAI,iDAAoB,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACzD,IAAI,OAAO,EAAE;YACX,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;YAEtC,MAAM,MAAM,GAAG,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YACnD,IAAI,MAAM,EAAE;gBACV,uBAAA,IAAI,2FAA4B,MAAhC,IAAI,EAA6B,MAAM,CAAC,CAAC;gBACzC,uBAAA,IAAI,0FAA2B,MAA/B,IAAI,EAA4B,KAAK,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;aAC3D;SACF;IACH,CAAC,CAAC,CAAC;IACH,IAAI,CAAC,eAAe,CAAC,OAAO,CAC1B,GAAG,cAAc,oBAAoB,EACrC,IAAI,CAAC,KAAK,CAAC,WAAW,CACvB,CAAC;AACJ,CAAC,qGAQqB,SAAoB;IACxC,MAAM,OAAO,GAAG,uBAAA,IAAI,iDAAoB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAExD,IAAI,OAAO,EAAE;QACX,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;QAEtC,MAAM,4BAA4B,GAChC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,oBAAoB,CAAC;QAC9C,IAAI,2BAA2B,GAAG,KAAK,CAAC;QAExC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACpB,MAAM,QAAQ,GACZ,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC;YAEjE,IAAI,QAAQ,EAAE;gBACZ,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;gBAC1C,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE;oBAChB,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;oBAE1B,gEAAgE;oBAChE,IACE,KAAK,CAAC,WAAW,CAAC,oBAAoB,KAAK,OAAO;wBAClD,QAAQ,CAAC,MAAM,KAAK,CAAC,EACrB;wBACA,wEAAwE;wBACxE,MAAM,uBAAuB,GAAG,uBAAA,IAAI,yFAA0B,MAA9B,IAAI,EAClC,KAAK,CAAC,WAAW,CAAC,OAAO,CAC1B,CAAC;wBACF,KAAK,CAAC,WAAW,CAAC,oBAAoB,GAAG,uBAAuB,CAAC;wBACjE,2BAA2B;4BACzB,uBAAuB,KAAK,4BAA4B,CAAC;qBAC5D;iBACF;gBACD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE;oBACzB,uBAAA,IAAI,yFAA0B,MAA9B,IAAI,EAA2B,KAAK,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;iBAC1D;aACF;QACH,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,eAAe,CAAC,OAAO,CAC1B,GAAG,cAAc,oBAAoB,EACrC,IAAI,CAAC,KAAK,CAAC,WAAW,CACvB,CAAC;QAEF,sEAAsE;QACtE,IAAI,2BAA2B,EAAE;YAC/B,IAAI,CAAC,eAAe,CAAC,OAAO,CAC1B,GAAG,cAAc,6BAA6B,EAC9C,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,oBAAoB,EAC3C,4BAA4B,CAC7B,CAAC;SACH;QAED,0CAA0C;QAC1C,uBAAA,IAAI,iDAAoB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;KAC5C;AACH,CAAC,6GAeC,KAAiC,EACjC,QAAyB,EACzB,OAAuB;IAEvB,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC,WAAW,CAAC;IAEtC,OAAO,OAAO,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACzC,uBAAA,IAAI,gDAAmB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAExC,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE;QACtD,OAAO,OAAO,CAAC,QAAQ,CAAC,CAAC;KAC1B;IACD,OAAO,KAAK,CAAC;AACf,CAAC,yEAaC,OAA6D,EAC7D,OAAwB;IAExB,MAAM,MAAM,GACV,uBAAA,IAAI,+EAAgB,MAApB,IAAI,CAAkB,CAAC,KAAK,CAAC,OAAO,CAAC;QACrC,uBAAA,IAAI,4EAAa,MAAjB,IAAI,CAAe,CAAC,KAAK,CAAC,OAAO,CAAC;QAClC,uBAAA,IAAI,+EAAgB,MAApB,IAAI,CAAkB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,wBAAwB;IAEjE,6BAA6B;IAC7B,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;IAClC,IAAI,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC/B,IAAI,CAAC,MAAM,EAAE;QACX,OAAO,CAAC,QAAQ,CAAC,GAAG;YAClB,GAAG,MAAM,CAAC,MAAM;YAChB,MAAM,EAAE,OAAO;YACf,MAAM,EAAE,EAAE;YACV,QAAQ,EAAE;gBACR,IAAI,EAAE,EAAE;gBACR,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ;aAC1B;YACD,kEAAkE;YAClE,sCAAsC;SAChB,CAAC;QACzB,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;QAE3B,gEAAgE;QAChE,IAAI,MAAM,CAAC,IAAI,KAAK,iBAAiB,CAAC,OAAO,EAAE;YAC7C,uBAAA,IAAI,mDAAsB,CAAC,uBAAuB,CAAC,QAAQ,CAAC,CAAC;SAC9D;KACF;IAED,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;IAChC,IAAI,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACnC,IAAI,CAAC,KAAK,EAAE;QACV,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG;YACvB,GAAG,MAAM,CAAC,KAAK;YACf,kEAAkE;YAClE,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;YACtB,QAAQ,EAAE;gBACR,IAAI,EAAE,EAAE;gBACR,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE;gBACnC,GAAG,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,mCAAmC;aAC9D;YACD,kEAAkE;YAClE,qCAAqC;SAChB,CAAC;QACxB,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAE/B,uEAAuE;QACvE,uBAAA,IAAI,gDAAmB,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAE/C,+DAA+D;QAC/D,IAAI,MAAM,CAAC,IAAI,KAAK,iBAAiB,CAAC,OAAO,EAAE;YAC7C,uBAAA,IAAI,mDAAsB,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC;SAC5D;KACF;SAAM;QACL,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;KACjC;IAED,+CAA+C;IAC/C,uBAAA,IAAI,iDAAoB,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE;QACvC,QAAQ,EAAE,MAAM,CAAC,EAAE;QACnB,OAAO,EAAE,KAAK,CAAC,EAAE;KAClB,CAAC,CAAC;AACL,CAAC;IAQC,OAAO,IAAI,CAAC,eAAe,CAAC,IAAI,CAC9B,2CAA2C,CAC5C,CAAC;AACJ,CAAC,6GAQyB,OAAuB;IAC/C,MAAM,MAAM,GAAG,uBAAA,IAAI,gDAAmB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACpD,IAAI,CAAC,MAAM,EAAE;QACX,MAAM,IAAI,KAAK,CAAC,0BAA0B,OAAO,qBAAqB,CAAC,CAAC;KACzE;AACH,CAAC,+GAQ0B,QAAyB;IAClD,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;IACjE,IAAI,CAAC,MAAM,EAAE;QACX,MAAM,IAAI,KAAK,CAAC,2BAA2B,QAAQ,qBAAqB,CAAC,CAAC;KAC3E;AACH,CAAC,yHAS+B,OAAuB,EAAE,IAAY;IACnE,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE;QACxD,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;KACtD;AACH,CAAC,yHAuD+B,OAE/B;IACC,MAAM,eAAe,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAC/C,iDAAiD,CAClD,CAAC;IACF,IAAI,eAAe,IAAI,eAAe,CAAC,EAAE,EAAE;QACzC,MAAM,cAAc,GAAG,uBAAA,IAAI,iDAAoB,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;QACxE,IAAI,cAAc,EAAE;YAClB,MAAM,EAAE,OAAO,EAAE,GAAG,cAAc,CAAC;YAEnC,OAAO,OAAO,CAAC;SAChB;KACF;IAED,kDAAkD;IAClD,OAAO,uBAAA,IAAI,yFAA0B,MAA9B,IAAI,EAA2B,OAAO,CAAC,CAAC;AACjD,CAAC,mHAQ4B,OAAwB;IACnD,MAAM,cAAc,GAAG,uBAAA,IAAI,iDAAoB,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAChE,IAAI,CAAC,cAAc,EAAE;QACnB,0DAA0D;QAC1D,OAAO;KACR;IAED,MAAM,EAAE,OAAO,EAAE,GAAG,cAAc,CAAC;IACnC,MAAM,4BAA4B,GAChC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,oBAAoB,CAAC;IAE9C,uEAAuE;IACvE,IAAI,4BAA4B,KAAK,OAAO,EAAE;QAC5C,OAAO;KACR;IAED,4DAA4D;IAC5D,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;QACpB,KAAK,CAAC,WAAW,CAAC,oBAAoB,GAAG,OAAO,CAAC;IACnD,CAAC,CAAC,CAAC;IACH,IAAI,CAAC,eAAe,CAAC,OAAO,CAC1B,GAAG,cAAc,6BAA6B,EAC9C,OAAO,EACP,4BAA4B,CAC7B,CAAC;AACJ,CAAC,+IAUC,QAAmC,EACnC,YAA2C;IAE3C,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;QACpB,MAAM,MAAM,GAAG,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAEnD,IAAI,MAAM,EAAE;YACV,MAAM,CAAC,MAAM,GAAG,YAAY,CAAC;SAC9B;IACH,CAAC,CAAC,CAAC;AACL,CAAC,2FAQgB,OAAuB;IACtC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,IAAI,CAC9D,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,SAAS,CACjD,CAAC;IAEF,OAAO,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;AAChC,CAAC,mIASC,OAAuB;IAEvB,MAAM,KAAK,GAAG,uBAAA,IAAI,gFAAiB,MAArB,IAAI,EAAkB,OAAO,CAAC,CAAC;IAE7C,IAAI,KAAK,EAAE;QACT,IAAI,SAAS,CAAC;QACd,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,QAAQ,EAAE;YAC/B,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CACvC,+BAA+B,EAC/B,EAAE,CACH,CAAC;YAEF,IAAI,CAAC,SAAS,EAAE;gBACd,SAAS,GAAG,EAAE,CAAC;aAChB;YACD,IAAI,OAAO,IAAI,gBAAgB,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;gBAC7C,kEAAkE;gBAClE,oBAAoB;gBACpB,OAAO,OAAO,CAAC,EAAE,CAAC;aACnB;SACF;QAED,OAAO,SAAS,CAAC;KAClB;IAED,OAAO,SAAS,CAAC;AACnB,CAAC,6GASyB,OAEzB;IACC,IAAI,SAAS,GAAwB,EAAE,CAAC;IAExC,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE;QAC3C,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE;YAChD,yEAAyE;YACzE,uEAAuE;YACvE,iBAAiB;YACjB,IAAI,SAAS,KAAK,EAAE,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE;gBACjD,SAAS,GAAG,KAAK,CAAC,EAAE,CAAC;aACtB;YAED,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,QAAQ,EAAE;gBAC/B,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CACvC,+BAA+B,EAC/B,EAAE,CACH,CAAC;gBAEF,IAAI,OAAO,IAAI,gBAAgB,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;oBAC7C,kEAAkE;oBAClE,kBAAkB;oBAClB,OAAO,KAAK,CAAC,EAAE,CAAC;iBACjB;aACF;SACF;KACF;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;IAgNC,IAAI,CAAC,eAAe,CAAC,qBAAqB,CACxC,GAAG,cAAc,0BAA0B,EAC3C,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,CACxC,CAAC;IAEF,IAAI,CAAC,eAAe,CAAC,qBAAqB,CACxC,GAAG,cAAc,0BAA0B,EAC3C,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,CACxC,CAAC;IAEF,IAAI,CAAC,eAAe,CAAC,qBAAqB,CACxC,GAAG,cAAc,sCAAsC,EACvD,IAAI,CAAC,mCAAmC,CAAC,IAAI,CAAC,IAAI,CAAC,CACpD,CAAC;IAEF,IAAI,CAAC,eAAe,CAAC,qBAAqB,CACxC,GAAG,cAAc,uBAAuB,EACxC,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,CACrC,CAAC;IAEF,IAAI,CAAC,eAAe,CAAC,qBAAqB,CACxC,GAAG,cAAc,sBAAsB,EACvC,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,CACpC,CAAC;IAEF,IAAI,CAAC,eAAe,CAAC,qBAAqB,CACxC,GAAG,cAAc,wBAAwB,EACzC,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,CACtC,CAAC;IAEF,IAAI,CAAC,eAAe,CAAC,qBAAqB,CACxC,GAAG,cAAc,wBAAwB,EACzC,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,CACtC,CAAC;AACJ,CAAC;IAwCC,OAAO;QACL,GAAG,uBAAA,IAAI,kDAAqB;QAC5B,UAAU,EAAE,IAAI;QAChB,SAAS,EAAE,IAAI,CAAC,eAAe;QAC/B,uBAAuB,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;QAC/C,OAAO,EAAE,uBAAA,IAAI,oCAAO,CAAC,IAAI,CAAC,IAAI,CAAC;QAC/B,iBAAiB,EAAE,uBAAA,IAAI,gDAAmB;KAC3C,CAAC;AACJ,CAAC","sourcesContent":["import {\n AccountWalletType,\n AccountGroupType,\n select,\n} from '@metamask/account-api';\nimport type {\n AccountGroupId,\n AccountWalletId,\n AccountSelector,\n MultichainAccountWalletId,\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 } from './group';\nimport {\n isAccountGroupNameUnique,\n isAccountGroupNameUniqueFromWallet,\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\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 /**\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 // 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 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 for (const wallet of Object.values(wallets)) {\n this.#applyAccountWalletMetadata(wallet);\n\n for (const group of Object.values(wallet.groups)) {\n if (group.id === previousSelectedAccountGroup) {\n previousSelectedAccountGroupStillExists = true;\n }\n }\n }\n\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 for (const group of Object.values(wallet.groups)) {\n this.#applyAccountGroupMetadata(state, wallet.id, group.id);\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\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 wallet Account wallet object to update.\n */\n #applyAccountWalletMetadata(wallet: AccountWalletObject) {\n const persistedMetadata = this.state.accountWalletsMetadata[wallet.id];\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 */\n #applyAccountGroupMetadata(\n state: AccountTreeControllerState,\n walletId: AccountWalletId,\n groupId: AccountGroupId,\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 // Skip computed names for now - use default naming with per-wallet logic\n // TODO: Implement computed names in a future iteration\n\n // Generate default name and ensure it's unique within the wallet\n let proposedName = '';\n let proposedNameIndex: number;\n\n // Parse the highest account index being used (similar to accounts-controller)\n let highestAccountNameIndex = 0;\n for (const existingGroup of Object.values(\n wallet.groups,\n ) as AccountGroupObject[]) {\n // Skip the current group being processed\n if (existingGroup.id === group.id) {\n continue;\n }\n // Parse the existing group name to extract the numeric index\n // TODO: This regex only matches \"Account N\" pattern. Hardware wallets (Trezor, Ledger, etc.)\n // use different patterns like \"Trezor N\", \"Ledger N\" per keyringTypeToName().\n // We'll enhance this to handle all keyring types in a future iteration.\n const nameMatch = existingGroup.metadata.name.match(/Account (\\d+)$/u);\n if (nameMatch) {\n const nameIndex = parseInt(nameMatch[1], 10);\n if (nameIndex > highestAccountNameIndex) {\n highestAccountNameIndex = nameIndex;\n }\n }\n }\n\n // For entropy-based multichain groups, start with the actual groupIndex\n if (\n group.type === AccountGroupType.MultichainAccount &&\n group.metadata.entropy\n ) {\n proposedNameIndex = group.metadata.entropy.groupIndex;\n } else {\n // For other wallet types, start with the number of existing groups\n // This gives us the next logical sequential number\n proposedNameIndex = Object.keys(wallet.groups).length;\n }\n\n // Use the higher of the two: highest parsed index or computed index\n proposedNameIndex = Math.max(highestAccountNameIndex, proposedNameIndex);\n\n // Find a unique name by checking for conflicts and incrementing if needed\n let nameExists: boolean;\n do {\n proposedName = rule.getDefaultAccountGroupName(proposedNameIndex);\n\n // Check if this name already exists in the wallet (excluding current group)\n nameExists = !isAccountGroupNameUniqueFromWallet(\n wallet,\n group.id,\n proposedName,\n );\n\n /* istanbul ignore next */\n if (nameExists) {\n proposedNameIndex += 1; // Try next number\n }\n } while (nameExists);\n\n state.accountTree.wallets[walletId].groups[groupId].metadata.name =\n proposedName;\n\n // Persist the generated name to ensure consistency\n state.accountGroupsMetadata[group.id] ??= {};\n state.accountGroupsMetadata[group.id].name = {\n value: proposedName,\n lastUpdatedAt: Date.now(),\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 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(wallet);\n this.#applyAccountGroupMetadata(state, walletId, groupId);\n }\n }\n });\n this.messagingSystem.publish(\n `${controllerName}:accountTreeChange`,\n this.state.accountTree,\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 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 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 if (!group) {\n wallet.groups[groupId] = {\n ...result.group,\n // Type-wise, we are guaranteed to always have at least 1 account.\n accounts: [account.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(account.id);\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 });\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 // Update persistent metadata\n state.accountGroupsMetadata[groupId] = {};\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 // Update persistent metadata\n state.accountGroupsMetadata[groupId] = {};\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 // Update persistent metadata\n state.accountGroupsMetadata[groupId] = {};\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\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": "0.
|
|
3
|
+
"version": "0.17.0-preview-d243c525",
|
|
4
4
|
"description": "Controller to group account together based on some pre-defined rules",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"MetaMask",
|
|
@@ -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": "^0.
|
|
64
|
+
"@metamask/multichain-account-service": "^0.10.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",
|