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

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