@metamask-previews/account-tree-controller 2.0.0-preview-3d9bbf60 → 2.0.0-preview-e98a6769

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.
@@ -9,9 +9,8 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
9
9
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
10
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
11
  };
12
- var _AccountTreeController_instances, _AccountTreeController_accountIdToContext, _AccountTreeController_groupIdToWalletId, _AccountTreeController_backupAndSyncService, _AccountTreeController_rules, _AccountTreeController_trace, _AccountTreeController_backupAndSyncConfig, _AccountTreeController_accountOrderCallbacks, _AccountTreeController_initialized, _AccountTreeController_getSnapRule, _AccountTreeController_getKeyringRule, _AccountTreeController_getDefaultAccountWalletName, _AccountTreeController_applyAccountWalletMetadata, _AccountTreeController_getComputedAccountGroupName, _AccountTreeController_getDefaultAccountGroupPrefix, _AccountTreeController_getDefaultAccountGroupName, _AccountTreeController_applyAccountGroupMetadata, _AccountTreeController_handleAccountAdded, _AccountTreeController_handleAccountRemoved, _AccountTreeController_pruneEmptyGroupAndWallet, _AccountTreeController_sortAccountsOfGroup, _AccountTreeController_insertAccount, _AccountTreeController_insertOrUpdateMultichainAccountWalletAndGroup, _AccountTreeController_getMultichainAccountWallets, _AccountTreeController_listAccounts, _AccountTreeController_assertAccountGroupExists, _AccountTreeController_assertAccountWalletExists, _AccountTreeController_assertAccountGroupNameIsUnique, _AccountTreeController_getDefaultSelectedAccountGroup, _AccountTreeController_handleSelectedAccountChange, _AccountTreeController_handleMultichainAccountGroupCreatedOrUpdated, _AccountTreeController_handleMultichainAccountWalletStatusChange, _AccountTreeController_getAccountGroup, _AccountTreeController_getDefaultAccountFromAccountGroupId, _AccountTreeController_getDefaultAccountGroupId, _AccountTreeController_registerMessageHandlers, _AccountTreeController_createBackupAndSyncContext;
13
- import { AccountWalletType, isBip44Account, select } from "@metamask/account-api";
14
- import { AccountGroupType } from "@metamask/account-api";
12
+ var _AccountTreeController_instances, _AccountTreeController_accountIdToContext, _AccountTreeController_groupIdToWalletId, _AccountTreeController_backupAndSyncService, _AccountTreeController_rules, _AccountTreeController_trace, _AccountTreeController_backupAndSyncConfig, _AccountTreeController_accountOrderCallbacks, _AccountTreeController_initialized, _AccountTreeController_getEntropyRule, _AccountTreeController_getSnapRule, _AccountTreeController_getKeyringRule, _AccountTreeController_applyAccountWalletMetadata, _AccountTreeController_getRuleForWallet, _AccountTreeController_getComputedAccountGroupName, _AccountTreeController_getDefaultAccountGroupName, _AccountTreeController_applyAccountGroupMetadata, _AccountTreeController_handleAccountAdded, _AccountTreeController_handleAccountRemoved, _AccountTreeController_pruneEmptyGroupAndWallet, _AccountTreeController_insert, _AccountTreeController_listAccounts, _AccountTreeController_assertAccountGroupExists, _AccountTreeController_assertAccountWalletExists, _AccountTreeController_assertAccountGroupNameIsUnique, _AccountTreeController_getDefaultSelectedAccountGroup, _AccountTreeController_handleSelectedAccountChange, _AccountTreeController_handleMultichainAccountWalletStatusChange, _AccountTreeController_getAccountGroup, _AccountTreeController_getDefaultAccountFromAccountGroupId, _AccountTreeController_getDefaultAccountGroupId, _AccountTreeController_registerMessageHandlers, _AccountTreeController_createBackupAndSyncContext;
13
+ import { AccountWalletType, select } from "@metamask/account-api";
15
14
  import { BaseController } from "@metamask/base-controller";
16
15
  import { isEvmAccountType } from "@metamask/keyring-api";
17
16
  import { assert } from "@metamask/utils";
@@ -19,7 +18,7 @@ import { formatAnalyticsEvent, traceFallback } from "./backup-and-sync/analytics
19
18
  import { BackupAndSyncService } from "./backup-and-sync/service/index.mjs";
20
19
  import { ACCOUNT_TYPE_TO_SORT_ORDER, isAccountGroupNameUnique, isAccountGroupNameUniqueFromWallet, MAX_SORT_ORDER } from "./group.mjs";
21
20
  import { projectLogger as log } from "./logger.mjs";
22
- import { getEntropyDefaultAccountGroupPrefix, getEntropyDefaultAccountWalletName } from "./rules/entropy.mjs";
21
+ import { EntropyRule } from "./rules/entropy.mjs";
23
22
  import { KeyringRule } from "./rules/keyring.mjs";
24
23
  import { SnapRule } from "./rules/snap.mjs";
25
24
  export const controllerName = 'AccountTreeController';
@@ -115,7 +114,7 @@ export class AccountTreeController extends BaseController {
115
114
  // Rules to apply to construct the wallets tree.
116
115
  __classPrivateFieldSet(this, _AccountTreeController_rules, [
117
116
  // 1. We group by entropy-source
118
- // NOTE: This is now done by consuming the `MultichainAccountService` wallets and groups.
117
+ new EntropyRule(this.messenger),
119
118
  // 2. We group by Snap ID
120
119
  new SnapRule(this.messenger),
121
120
  // 3. We group by wallet type (this rule cannot fail and will group all non-matching accounts)
@@ -146,12 +145,6 @@ export class AccountTreeController extends BaseController {
146
145
  this.messenger.subscribe('UserStorageController:stateChange', (userStorageControllerState) => {
147
146
  __classPrivateFieldGet(this, _AccountTreeController_backupAndSyncService, "f").handleUserStorageStateChange(userStorageControllerState);
148
147
  });
149
- this.messenger.subscribe('MultichainAccountService:multichainAccountGroupCreated', (group) => {
150
- __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_handleMultichainAccountGroupCreatedOrUpdated).call(this, group);
151
- });
152
- this.messenger.subscribe('MultichainAccountService:multichainAccountGroupUpdated', (group) => {
153
- __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_handleMultichainAccountGroupCreatedOrUpdated).call(this, group);
154
- });
155
148
  this.messenger.subscribe('MultichainAccountService:walletStatusChange', (walletId, status) => {
156
149
  __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_handleMultichainAccountWalletStatusChange).call(this, walletId, status);
157
150
  });
@@ -171,7 +164,6 @@ export class AccountTreeController extends BaseController {
171
164
  return;
172
165
  }
173
166
  log('Initializing...');
174
- // For now, we always re-compute all wallets, we do not re-use the existing state.
175
167
  const wallets = {};
176
168
  // Clear mappings for fresh rebuild.
177
169
  __classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").clear();
@@ -179,12 +171,6 @@ export class AccountTreeController extends BaseController {
179
171
  // Keep the current selected group to check if it's still part of the tree
180
172
  // after rebuilding it.
181
173
  const previousSelectedAccountGroup = this.state.accountTree.selectedAccountGroup;
182
- // Insert all multichain account wallet/groups.
183
- for (const wallet of __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getMultichainAccountWallets).call(this)) {
184
- for (const group of wallet.getMultichainAccountGroups()) {
185
- __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_insertOrUpdateMultichainAccountWalletAndGroup).call(this, wallets, wallet, group);
186
- }
187
- }
188
174
  // There's no guarantee that accounts would be sorted by their import time
189
175
  // with `listMultichainAccounts`. We have to sort them here before constructing
190
176
  // the tree.
@@ -197,14 +183,9 @@ export class AccountTreeController extends BaseController {
197
183
  // won't be enough and we would have to use group properties instead (like group
198
184
  // index or maybe introduce a `importTime` at group level).
199
185
  const accounts = __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_listAccounts).call(this).sort((a, b) => a.metadata.importTime - b.metadata.importTime);
200
- // Insert all other kind of accounts (private keys, HW, other Snap accounts, etc.).
186
+ // For now, we always re-compute all wallets, we do not re-use the existing state.
201
187
  for (const account of accounts) {
202
- // BIP-44 accounts are owned byt the `MultichainAccountService`, so we have to skip them
203
- // here, since we've already added wallets/groups for each of them.
204
- if (isBip44Account(account)) {
205
- continue;
206
- }
207
- __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_insertAccount).call(this, wallets, account);
188
+ __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_insert).call(this, wallets, account);
208
189
  }
209
190
  // Once we have the account tree, we can apply persisted metadata (names + UI states).
210
191
  let previousSelectedAccountGroupStillExists = false;
@@ -575,19 +556,12 @@ export class AccountTreeController extends BaseController {
575
556
  return __classPrivateFieldGet(this, _AccountTreeController_backupAndSyncService, "f").performFullSyncAtLeastOnce();
576
557
  }
577
558
  }
578
- _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeController_groupIdToWalletId = new WeakMap(), _AccountTreeController_backupAndSyncService = new WeakMap(), _AccountTreeController_rules = new WeakMap(), _AccountTreeController_trace = new WeakMap(), _AccountTreeController_backupAndSyncConfig = new WeakMap(), _AccountTreeController_accountOrderCallbacks = new WeakMap(), _AccountTreeController_initialized = new WeakMap(), _AccountTreeController_instances = new WeakSet(), _AccountTreeController_getSnapRule = function _AccountTreeController_getSnapRule() {
559
+ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeController_groupIdToWalletId = new WeakMap(), _AccountTreeController_backupAndSyncService = new WeakMap(), _AccountTreeController_rules = new WeakMap(), _AccountTreeController_trace = new WeakMap(), _AccountTreeController_backupAndSyncConfig = new WeakMap(), _AccountTreeController_accountOrderCallbacks = new WeakMap(), _AccountTreeController_initialized = new WeakMap(), _AccountTreeController_instances = new WeakSet(), _AccountTreeController_getEntropyRule = function _AccountTreeController_getEntropyRule() {
579
560
  return __classPrivateFieldGet(this, _AccountTreeController_rules, "f")[0];
580
- }, _AccountTreeController_getKeyringRule = function _AccountTreeController_getKeyringRule() {
561
+ }, _AccountTreeController_getSnapRule = function _AccountTreeController_getSnapRule() {
581
562
  return __classPrivateFieldGet(this, _AccountTreeController_rules, "f")[1];
582
- }, _AccountTreeController_getDefaultAccountWalletName = function _AccountTreeController_getDefaultAccountWalletName(wallet) {
583
- switch (wallet.type) {
584
- case AccountWalletType.Entropy:
585
- return getEntropyDefaultAccountWalletName(this.messenger, wallet);
586
- case AccountWalletType.Snap:
587
- return __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getSnapRule).call(this).getDefaultAccountWalletName(wallet);
588
- default:
589
- return __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getKeyringRule).call(this).getDefaultAccountWalletName(wallet);
590
- }
563
+ }, _AccountTreeController_getKeyringRule = function _AccountTreeController_getKeyringRule() {
564
+ return __classPrivateFieldGet(this, _AccountTreeController_rules, "f")[2];
591
565
  }, _AccountTreeController_applyAccountWalletMetadata = function _AccountTreeController_applyAccountWalletMetadata(state, walletId) {
592
566
  const wallet = state.accountTree.wallets[walletId];
593
567
  const persistedMetadata = state.accountWalletsMetadata[walletId];
@@ -597,9 +571,29 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
597
571
  }
598
572
  else if (!wallet.metadata.name) {
599
573
  // Generate default name if none exists
600
- wallet.metadata.name = __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getDefaultAccountWalletName).call(this, wallet);
574
+ if (wallet.type === AccountWalletType.Entropy) {
575
+ wallet.metadata.name =
576
+ __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getEntropyRule).call(this).getDefaultAccountWalletName(wallet);
577
+ }
578
+ else if (wallet.type === AccountWalletType.Snap) {
579
+ wallet.metadata.name =
580
+ __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getSnapRule).call(this).getDefaultAccountWalletName(wallet);
581
+ }
582
+ else {
583
+ wallet.metadata.name =
584
+ __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getKeyringRule).call(this).getDefaultAccountWalletName(wallet);
585
+ }
601
586
  log(`[${wallet.id}] Set default name to: "${wallet.metadata.name}"`);
602
587
  }
588
+ }, _AccountTreeController_getRuleForWallet = function _AccountTreeController_getRuleForWallet(wallet) {
589
+ switch (wallet.type) {
590
+ case AccountWalletType.Entropy:
591
+ return __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getEntropyRule).call(this);
592
+ case AccountWalletType.Snap:
593
+ return __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getSnapRule).call(this);
594
+ default:
595
+ return __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getKeyringRule).call(this);
596
+ }
603
597
  }, _AccountTreeController_getComputedAccountGroupName = function _AccountTreeController_getComputedAccountGroupName(wallet, group) {
604
598
  let proposedName = ''; // Empty means there's no computed name for this group.
605
599
  for (const id of group.accounts) {
@@ -624,18 +618,11 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
624
618
  proposedName = this.resolveNameConflict(wallet, group.id, proposedName);
625
619
  }
626
620
  return proposedName;
627
- }, _AccountTreeController_getDefaultAccountGroupPrefix = function _AccountTreeController_getDefaultAccountGroupPrefix(wallet) {
628
- switch (wallet.type) {
629
- case AccountWalletType.Entropy:
630
- return getEntropyDefaultAccountGroupPrefix();
631
- case AccountWalletType.Snap:
632
- return __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getSnapRule).call(this).getDefaultAccountGroupPrefix(wallet);
633
- default:
634
- return __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getKeyringRule).call(this).getDefaultAccountGroupPrefix(wallet);
635
- }
636
621
  }, _AccountTreeController_getDefaultAccountGroupName = function _AccountTreeController_getDefaultAccountGroupName(state, wallet, group, nextNaturalNameIndex) {
622
+ // Get the appropriate rule for this wallet type
623
+ const rule = __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getRuleForWallet).call(this, wallet);
637
624
  // Get the prefix for groups of this wallet
638
- const namePrefix = __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getDefaultAccountGroupPrefix).call(this, wallet);
625
+ const namePrefix = rule.getDefaultAccountGroupPrefix(wallet);
639
626
  // Parse the highest account index being used (similar to accounts-controller)
640
627
  let highestNameIndex = 0;
641
628
  for (const { id: otherGroupId } of Object.values(wallet.groups)) {
@@ -761,15 +748,10 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
761
748
  if (!__classPrivateFieldGet(this, _AccountTreeController_initialized, "f")) {
762
749
  return;
763
750
  }
764
- if (isBip44Account(account)) {
765
- // We're skipping BIP-44 accounts since we rely on the `MultichainAccountService` to do
766
- // the grouping of wallets/groups directly.
767
- return;
768
- }
769
751
  // Check if this account is already known by the tree to avoid double-insertion.
770
752
  if (!__classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").has(account.id)) {
771
753
  this.update((state) => {
772
- __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_insertAccount).call(this, state.accountTree.wallets, account);
754
+ __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_insert).call(this, state.accountTree.wallets, account);
773
755
  const context = __classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").get(account.id);
774
756
  if (context) {
775
757
  const { walletId, groupId } = context;
@@ -792,40 +774,33 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
792
774
  const context = __classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").get(accountId);
793
775
  if (context) {
794
776
  const { walletId, groupId } = context;
795
- // If it's in the context, then `group` should be defined.
796
- const group = this.state.accountTree.wallets[walletId]?.groups[groupId];
797
- assert(group, 'Account group associated with a context cannot be undefined');
798
- // We're only considering non-BIP-44 accounts for single account events.
799
- if (group.type !== AccountGroupType.MultichainAccount) {
800
- const index = group.accounts.indexOf(accountId);
801
- if (index !== -1) {
802
- let selectedAccountGroupChanged = false;
803
- const previousSelectedAccountGroup = this.state.accountTree.selectedAccountGroup;
804
- this.update((state) => {
805
- const accounts = state.accountTree.wallets[walletId]?.groups[groupId].accounts;
806
- // Effectively remove account from the group and state.
777
+ const previousSelectedAccountGroup = this.state.accountTree.selectedAccountGroup;
778
+ let selectedAccountGroupChanged = false;
779
+ this.update((state) => {
780
+ const accounts = state.accountTree.wallets[walletId]?.groups[groupId]?.accounts;
781
+ if (accounts) {
782
+ const index = accounts.indexOf(accountId);
783
+ if (index !== -1) {
807
784
  accounts.splice(index, 1);
808
- // If there's no more account, we have to prune the group and maybe select
809
- // a new account group too.
810
- if (accounts.length === 0) {
811
- // Check if we need to update selectedAccountGroup after removal
812
- if (state.accountTree.selectedAccountGroup === groupId) {
813
- // The currently selected group is now empty, find a new group to select
814
- const newSelectedAccountGroup = __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getDefaultAccountGroupId).call(this, state.accountTree.wallets);
815
- state.accountTree.selectedAccountGroup =
816
- newSelectedAccountGroup;
817
- selectedAccountGroupChanged =
818
- newSelectedAccountGroup !== previousSelectedAccountGroup;
819
- }
820
- __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_pruneEmptyGroupAndWallet).call(this, state, walletId, groupId);
785
+ // Check if we need to update selectedAccountGroup after removal
786
+ if (state.accountTree.selectedAccountGroup === groupId &&
787
+ accounts.length === 0) {
788
+ // The currently selected group is now empty, find a new group to select
789
+ const newSelectedAccountGroup = __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getDefaultAccountGroupId).call(this, state.accountTree.wallets);
790
+ state.accountTree.selectedAccountGroup = newSelectedAccountGroup;
791
+ selectedAccountGroupChanged =
792
+ newSelectedAccountGroup !== previousSelectedAccountGroup;
821
793
  }
822
- });
823
- this.messenger.publish(`${controllerName}:accountTreeChange`, this.state.accountTree);
824
- // Emit selectedAccountGroupChange event if the selected group changed
825
- if (selectedAccountGroupChanged) {
826
- this.messenger.publish(`${controllerName}:selectedAccountGroupChange`, this.state.accountTree.selectedAccountGroup, previousSelectedAccountGroup);
794
+ }
795
+ if (accounts.length === 0) {
796
+ __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_pruneEmptyGroupAndWallet).call(this, state, walletId, groupId);
827
797
  }
828
798
  }
799
+ });
800
+ this.messenger.publish(`${controllerName}:accountTreeChange`, this.state.accountTree);
801
+ // Emit selectedAccountGroupChange event if the selected group changed
802
+ if (selectedAccountGroupChanged) {
803
+ this.messenger.publish(`${controllerName}:selectedAccountGroupChange`, this.state.accountTree.selectedAccountGroup, previousSelectedAccountGroup);
829
804
  }
830
805
  // Clear reverse-mapping for that account.
831
806
  __classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").delete(accountId);
@@ -842,23 +817,9 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
842
817
  delete state.accountWalletsMetadata[walletId];
843
818
  }
844
819
  return state;
845
- }, _AccountTreeController_sortAccountsOfGroup = function _AccountTreeController_sortAccountsOfGroup(group) {
846
- group.accounts.sort(
847
- /* istanbul ignore next: Comparator branch execution (a===id vs b===id)
848
- * and return attribution vary across engines; final ordering is covered
849
- * by behavior tests. Ignoring the entire comparator avoids flaky line
850
- * coverage without reducing scenario coverage.
851
- */
852
- (a, b) => {
853
- const aSortOrder = __classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").get(a)?.sortOrder;
854
- const bSortOrder = __classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").get(b)?.sortOrder;
855
- return (aSortOrder ?? MAX_SORT_ORDER) - (bSortOrder ?? MAX_SORT_ORDER);
856
- });
857
- }, _AccountTreeController_insertAccount = function _AccountTreeController_insertAccount(wallets, account) {
858
- // Those are owned by the `MultichainAccountService`, so they should be already handled
859
- // by `#insertOrUpdateMultichainAccountWalletAndGroup` method.
860
- assert(!isBip44Account(account), 'BIP-44 accounts cannot be inserted explicitly');
861
- const result = __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getSnapRule).call(this).match(account) ??
820
+ }, _AccountTreeController_insert = function _AccountTreeController_insert(wallets, account) {
821
+ const result = __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getEntropyRule).call(this).match(account) ??
822
+ __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getSnapRule).call(this).match(account) ??
862
823
  __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getKeyringRule).call(this).match(account); // This one cannot fail.
863
824
  // Update controller's state.
864
825
  const walletId = result.wallet.id;
@@ -877,119 +838,62 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
877
838
  // the union tag `result.wallet.type`.
878
839
  };
879
840
  wallet = wallets[walletId];
841
+ // Trigger atomic sync for new wallet (only for entropy wallets)
842
+ if (wallet.type === AccountWalletType.Entropy) {
843
+ __classPrivateFieldGet(this, _AccountTreeController_backupAndSyncService, "f").enqueueSingleWalletSync(walletId);
844
+ }
880
845
  }
881
846
  const groupId = result.group.id;
882
- assert(result.group.type === AccountGroupType.SingleAccount, `Account insertion should always result in a "${AccountGroupType.SingleAccount}" group type`);
883
- assert(!wallet.groups[groupId], 'Single account insertion cannot re-use existing group');
884
- log(`[${walletId}] Add new group: [${groupId}]`);
885
- const group = {
886
- ...result.group,
887
- // Type-wise, we are guaranteed to always have at least 1 account.
888
- accounts: [account.id],
889
- metadata: {
890
- name: '',
891
- ...{ pinned: false, hidden: false },
892
- ...result.group.metadata, // Allow rules to override defaults
893
- },
894
- // We do need to type-cast since we're not narrowing `result` with
895
- // the union tag `result.group.type`.
896
- };
897
- wallet.groups[groupId] = group;
898
- // Map group ID to its containing wallet ID for efficient direct access
899
- __classPrivateFieldGet(this, _AccountTreeController_groupIdToWalletId, "f").set(groupId, walletId);
900
- log(`[${groupId}] Add new account: { id: "${account.id}", type: "${account.type}", address: "${account.address}"`);
901
- // Update the reverse mapping for this account.
902
- __classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").set(account.id, {
903
- walletId: wallet.id,
904
- groupId: group.id,
905
- sortOrder: ACCOUNT_TYPE_TO_SORT_ORDER[account.type],
906
- });
907
- // We need to do this at every insertion because race conditions can happen
908
- // during the account creation process where one provider completes before the other.
909
- // The discovery process in the service can also lead to some accounts being created "out of order".
910
- __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_sortAccountsOfGroup).call(this, group);
911
- }, _AccountTreeController_insertOrUpdateMultichainAccountWalletAndGroup = function _AccountTreeController_insertOrUpdateMultichainAccountWalletAndGroup(wallets, multichainAccountWallet, multichainAccountGroup) {
912
- const walletId = multichainAccountWallet.id;
913
- const groupId = multichainAccountGroup.id;
914
- let wallet = null;
915
- let group = null;
916
- // Check type, mainly to infer proper wallet object type.
917
- const walletObject = wallets[walletId];
918
- if (walletObject && walletObject.type === AccountWalletType.Entropy) {
919
- wallet = walletObject;
920
- group = wallet.groups[groupId];
921
- }
922
- // We always re-use the account list from the group (if accounts get
923
- // added/removed/re-ordered within the group).
924
- const accounts = multichainAccountGroup.getAccounts();
925
- const accountIds = accounts
926
- // For now, we need this type-cast because `getAccounts` do not have the same
927
- // type-constraint (uses string[] instead of [string; ...string])
928
- .map((account) => account.id);
929
- // Create the group object first, to inject it in the wallet in case this wallet is
930
- // not part of the tree yet.
847
+ let group = wallet.groups[groupId];
848
+ const { type, id } = account;
849
+ const sortOrder = ACCOUNT_TYPE_TO_SORT_ORDER[type];
931
850
  if (!group) {
932
- group = {
933
- id: multichainAccountGroup.id,
934
- type: multichainAccountGroup.type,
935
- accounts: accountIds,
851
+ log(`[${walletId}] Add new group: [${groupId}]`);
852
+ wallet.groups[groupId] = {
853
+ ...result.group,
854
+ // Type-wise, we are guaranteed to always have at least 1 account.
855
+ accounts: [id],
936
856
  metadata: {
937
- entropy: {
938
- groupIndex: multichainAccountGroup.groupIndex,
939
- },
940
857
  name: '',
941
- pinned: false,
942
- hidden: false,
943
- },
944
- };
945
- }
946
- else {
947
- // We clear existing contexts for previous accounts of that group because:
948
- // - Accounts might have been removed
949
- // - Accounts context mapping will get updated below
950
- group.accounts.forEach((accountId) => __classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").delete(accountId));
951
- group.accounts = accountIds;
952
- }
953
- if (!wallet) {
954
- wallet = {
955
- id: multichainAccountWallet.id,
956
- type: multichainAccountWallet.type,
957
- status: multichainAccountWallet.status,
958
- groups: {
959
- [group.id]: group,
960
- },
961
- metadata: {
962
- entropy: {
963
- id: multichainAccountWallet.entropySource,
964
- },
965
- name: '', // Will get updated later.
858
+ ...{ pinned: false, hidden: false },
859
+ ...result.group.metadata, // Allow rules to override defaults
966
860
  },
861
+ // We do need to type-cast since we're not narrowing `result` with
862
+ // the union tag `result.group.type`.
967
863
  };
968
- wallets[walletId] = wallet;
969
- // Trigger atomic sync for new wallet.
970
- __classPrivateFieldGet(this, _AccountTreeController_backupAndSyncService, "f").enqueueSingleWalletSync(walletId);
864
+ group = wallet.groups[groupId];
865
+ // Map group ID to its containing wallet ID for efficient direct access
866
+ __classPrivateFieldGet(this, _AccountTreeController_groupIdToWalletId, "f").set(groupId, walletId);
867
+ // Trigger atomic sync for new group (only for entropy wallets)
868
+ if (wallet.type === AccountWalletType.Entropy) {
869
+ __classPrivateFieldGet(this, _AccountTreeController_backupAndSyncService, "f").enqueueSingleGroupSync(groupId);
870
+ }
971
871
  }
972
872
  else {
973
- wallet.groups[group.id] = group;
974
- // Trigger atomic sync for new group.
975
- __classPrivateFieldGet(this, _AccountTreeController_backupAndSyncService, "f").enqueueSingleGroupSync(groupId);
976
- }
977
- // Map group ID to its containing wallet ID for efficient direct access
978
- __classPrivateFieldGet(this, _AccountTreeController_groupIdToWalletId, "f").set(groupId, walletId);
979
- // Update the reverse mapping for all accounts account.
980
- for (const account of accounts) {
981
- __classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").set(account.id, {
982
- walletId: wallet.id,
983
- groupId: group.id,
984
- sortOrder: ACCOUNT_TYPE_TO_SORT_ORDER[account.type],
873
+ group.accounts.push(id);
874
+ // We need to do this at every insertion because race conditions can happen
875
+ // during the account creation process where one provider completes before the other.
876
+ // The discovery process in the service can also lead to some accounts being created "out of order".
877
+ const { accounts } = group;
878
+ accounts.sort(
879
+ /* istanbul ignore next: Comparator branch execution (a===id vs b===id)
880
+ * and return attribution vary across engines; final ordering is covered
881
+ * by behavior tests. Ignoring the entire comparator avoids flaky line
882
+ * coverage without reducing scenario coverage.
883
+ */
884
+ (a, b) => {
885
+ const aSortOrder = a === id ? sortOrder : __classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").get(a)?.sortOrder;
886
+ const bSortOrder = b === id ? sortOrder : __classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").get(b)?.sortOrder;
887
+ return ((aSortOrder ?? MAX_SORT_ORDER) - (bSortOrder ?? MAX_SORT_ORDER));
985
888
  });
986
889
  }
987
- // We need to do this at every insertion because race conditions can happen
988
- // during the account creation process where one provider completes before the other.
989
- // The discovery process in the service can also lead to some accounts being created "out of order".
990
- __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_sortAccountsOfGroup).call(this, group);
991
- }, _AccountTreeController_getMultichainAccountWallets = function _AccountTreeController_getMultichainAccountWallets() {
992
- return this.messenger.call('MultichainAccountService:getMultichainAccountWallets');
890
+ log(`[${groupId}] Add new account: { id: "${account.id}", type: "${account.type}", address: "${account.address}"`);
891
+ // Update the reverse mapping for this account.
892
+ __classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").set(account.id, {
893
+ walletId: wallet.id,
894
+ groupId: group.id,
895
+ sortOrder,
896
+ });
993
897
  }, _AccountTreeController_listAccounts = function _AccountTreeController_listAccounts() {
994
898
  return this.messenger.call('AccountsController:listMultichainAccounts');
995
899
  }, _AccountTreeController_assertAccountGroupExists = function _AccountTreeController_assertAccountGroupExists(groupId) {
@@ -1034,25 +938,6 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
1034
938
  state.accountTree.selectedAccountGroup = groupId;
1035
939
  });
1036
940
  this.messenger.publish(`${controllerName}:selectedAccountGroupChange`, groupId, previousSelectedAccountGroup);
1037
- }, _AccountTreeController_handleMultichainAccountGroupCreatedOrUpdated = function _AccountTreeController_handleMultichainAccountGroupCreatedOrUpdated(multichainAccountGroup) {
1038
- // We wait for the first `init` to be called to actually build up the tree and
1039
- // mutate it. We expect the caller to first update the `AccountsController` state
1040
- // to force the migration of accounts, and then call `init`.
1041
- if (!__classPrivateFieldGet(this, _AccountTreeController_initialized, "f")) {
1042
- return;
1043
- }
1044
- this.update((state) => {
1045
- __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_insertOrUpdateMultichainAccountWalletAndGroup).call(this, state.accountTree.wallets, multichainAccountGroup.wallet, multichainAccountGroup);
1046
- const wallet = state.accountTree.wallets[multichainAccountGroup.wallet.id];
1047
- if (wallet) {
1048
- __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_applyAccountWalletMetadata).call(this, state, wallet.id);
1049
- const group = wallet.groups[multichainAccountGroup.id];
1050
- if (group) {
1051
- __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_applyAccountGroupMetadata).call(this, state, wallet.id, group.id);
1052
- }
1053
- }
1054
- });
1055
- this.messenger.publish(`${controllerName}:accountTreeChange`, this.state.accountTree);
1056
941
  }, _AccountTreeController_handleMultichainAccountWalletStatusChange = function _AccountTreeController_handleMultichainAccountWalletStatusChange(walletId, walletStatus) {
1057
942
  this.update((state) => {
1058
943
  const wallet = state.accountTree.wallets[walletId];