@metamask-previews/account-tree-controller 2.0.0-preview-d74d2f9f → 2.0.0-preview-3d9bbf60
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 +6 -0
- package/dist/AccountTreeController.cjs +219 -104
- package/dist/AccountTreeController.cjs.map +1 -1
- package/dist/AccountTreeController.d.cts +0 -12
- package/dist/AccountTreeController.d.cts.map +1 -1
- package/dist/AccountTreeController.d.mts +0 -12
- package/dist/AccountTreeController.d.mts.map +1 -1
- package/dist/AccountTreeController.mjs +221 -106
- package/dist/AccountTreeController.mjs.map +1 -1
- package/dist/rules/entropy.cjs +53 -69
- package/dist/rules/entropy.cjs.map +1 -1
- package/dist/rules/entropy.d.cts +24 -12
- package/dist/rules/entropy.d.cts.map +1 -1
- package/dist/rules/entropy.d.mts +24 -12
- package/dist/rules/entropy.d.mts.map +1 -1
- package/dist/rules/entropy.mjs +49 -67
- package/dist/rules/entropy.mjs.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +3 -3
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +3 -3
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -9,8 +9,9 @@ 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,
|
|
13
|
-
import { AccountWalletType, select } 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_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";
|
|
14
15
|
import { BaseController } from "@metamask/base-controller";
|
|
15
16
|
import { isEvmAccountType } from "@metamask/keyring-api";
|
|
16
17
|
import { assert } from "@metamask/utils";
|
|
@@ -18,7 +19,7 @@ import { formatAnalyticsEvent, traceFallback } from "./backup-and-sync/analytics
|
|
|
18
19
|
import { BackupAndSyncService } from "./backup-and-sync/service/index.mjs";
|
|
19
20
|
import { ACCOUNT_TYPE_TO_SORT_ORDER, isAccountGroupNameUnique, isAccountGroupNameUniqueFromWallet, MAX_SORT_ORDER } from "./group.mjs";
|
|
20
21
|
import { projectLogger as log } from "./logger.mjs";
|
|
21
|
-
import {
|
|
22
|
+
import { getEntropyDefaultAccountGroupPrefix, getEntropyDefaultAccountWalletName } from "./rules/entropy.mjs";
|
|
22
23
|
import { KeyringRule } from "./rules/keyring.mjs";
|
|
23
24
|
import { SnapRule } from "./rules/snap.mjs";
|
|
24
25
|
export const controllerName = 'AccountTreeController';
|
|
@@ -114,7 +115,7 @@ export class AccountTreeController extends BaseController {
|
|
|
114
115
|
// Rules to apply to construct the wallets tree.
|
|
115
116
|
__classPrivateFieldSet(this, _AccountTreeController_rules, [
|
|
116
117
|
// 1. We group by entropy-source
|
|
117
|
-
|
|
118
|
+
// NOTE: This is now done by consuming the `MultichainAccountService` wallets and groups.
|
|
118
119
|
// 2. We group by Snap ID
|
|
119
120
|
new SnapRule(this.messenger),
|
|
120
121
|
// 3. We group by wallet type (this rule cannot fail and will group all non-matching accounts)
|
|
@@ -145,6 +146,12 @@ export class AccountTreeController extends BaseController {
|
|
|
145
146
|
this.messenger.subscribe('UserStorageController:stateChange', (userStorageControllerState) => {
|
|
146
147
|
__classPrivateFieldGet(this, _AccountTreeController_backupAndSyncService, "f").handleUserStorageStateChange(userStorageControllerState);
|
|
147
148
|
});
|
|
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
|
+
});
|
|
148
155
|
this.messenger.subscribe('MultichainAccountService:walletStatusChange', (walletId, status) => {
|
|
149
156
|
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_handleMultichainAccountWalletStatusChange).call(this, walletId, status);
|
|
150
157
|
});
|
|
@@ -164,6 +171,7 @@ export class AccountTreeController extends BaseController {
|
|
|
164
171
|
return;
|
|
165
172
|
}
|
|
166
173
|
log('Initializing...');
|
|
174
|
+
// For now, we always re-compute all wallets, we do not re-use the existing state.
|
|
167
175
|
const wallets = {};
|
|
168
176
|
// Clear mappings for fresh rebuild.
|
|
169
177
|
__classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").clear();
|
|
@@ -171,6 +179,12 @@ export class AccountTreeController extends BaseController {
|
|
|
171
179
|
// Keep the current selected group to check if it's still part of the tree
|
|
172
180
|
// after rebuilding it.
|
|
173
181
|
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
|
+
}
|
|
174
188
|
// There's no guarantee that accounts would be sorted by their import time
|
|
175
189
|
// with `listMultichainAccounts`. We have to sort them here before constructing
|
|
176
190
|
// the tree.
|
|
@@ -183,9 +197,14 @@ export class AccountTreeController extends BaseController {
|
|
|
183
197
|
// won't be enough and we would have to use group properties instead (like group
|
|
184
198
|
// index or maybe introduce a `importTime` at group level).
|
|
185
199
|
const accounts = __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_listAccounts).call(this).sort((a, b) => a.metadata.importTime - b.metadata.importTime);
|
|
186
|
-
//
|
|
200
|
+
// Insert all other kind of accounts (private keys, HW, other Snap accounts, etc.).
|
|
187
201
|
for (const account of accounts) {
|
|
188
|
-
|
|
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);
|
|
189
208
|
}
|
|
190
209
|
// Once we have the account tree, we can apply persisted metadata (names + UI states).
|
|
191
210
|
let previousSelectedAccountGroupStillExists = false;
|
|
@@ -556,12 +575,19 @@ export class AccountTreeController extends BaseController {
|
|
|
556
575
|
return __classPrivateFieldGet(this, _AccountTreeController_backupAndSyncService, "f").performFullSyncAtLeastOnce();
|
|
557
576
|
}
|
|
558
577
|
}
|
|
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(),
|
|
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() {
|
|
560
579
|
return __classPrivateFieldGet(this, _AccountTreeController_rules, "f")[0];
|
|
561
|
-
}, _AccountTreeController_getSnapRule = function _AccountTreeController_getSnapRule() {
|
|
562
|
-
return __classPrivateFieldGet(this, _AccountTreeController_rules, "f")[1];
|
|
563
580
|
}, _AccountTreeController_getKeyringRule = function _AccountTreeController_getKeyringRule() {
|
|
564
|
-
return __classPrivateFieldGet(this, _AccountTreeController_rules, "f")[
|
|
581
|
+
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
|
+
}
|
|
565
591
|
}, _AccountTreeController_applyAccountWalletMetadata = function _AccountTreeController_applyAccountWalletMetadata(state, walletId) {
|
|
566
592
|
const wallet = state.accountTree.wallets[walletId];
|
|
567
593
|
const persistedMetadata = state.accountWalletsMetadata[walletId];
|
|
@@ -571,29 +597,9 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
|
|
|
571
597
|
}
|
|
572
598
|
else if (!wallet.metadata.name) {
|
|
573
599
|
// Generate default name if none exists
|
|
574
|
-
|
|
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
|
-
}
|
|
600
|
+
wallet.metadata.name = __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getDefaultAccountWalletName).call(this, wallet);
|
|
586
601
|
log(`[${wallet.id}] Set default name to: "${wallet.metadata.name}"`);
|
|
587
602
|
}
|
|
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
|
-
}
|
|
597
603
|
}, _AccountTreeController_getComputedAccountGroupName = function _AccountTreeController_getComputedAccountGroupName(wallet, group) {
|
|
598
604
|
let proposedName = ''; // Empty means there's no computed name for this group.
|
|
599
605
|
for (const id of group.accounts) {
|
|
@@ -618,11 +624,18 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
|
|
|
618
624
|
proposedName = this.resolveNameConflict(wallet, group.id, proposedName);
|
|
619
625
|
}
|
|
620
626
|
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
|
+
}
|
|
621
636
|
}, _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);
|
|
624
637
|
// Get the prefix for groups of this wallet
|
|
625
|
-
const namePrefix =
|
|
638
|
+
const namePrefix = __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getDefaultAccountGroupPrefix).call(this, wallet);
|
|
626
639
|
// Parse the highest account index being used (similar to accounts-controller)
|
|
627
640
|
let highestNameIndex = 0;
|
|
628
641
|
for (const { id: otherGroupId } of Object.values(wallet.groups)) {
|
|
@@ -748,10 +761,15 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
|
|
|
748
761
|
if (!__classPrivateFieldGet(this, _AccountTreeController_initialized, "f")) {
|
|
749
762
|
return;
|
|
750
763
|
}
|
|
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
|
+
}
|
|
751
769
|
// Check if this account is already known by the tree to avoid double-insertion.
|
|
752
770
|
if (!__classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").has(account.id)) {
|
|
753
771
|
this.update((state) => {
|
|
754
|
-
__classPrivateFieldGet(this, _AccountTreeController_instances, "m",
|
|
772
|
+
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_insertAccount).call(this, state.accountTree.wallets, account);
|
|
755
773
|
const context = __classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").get(account.id);
|
|
756
774
|
if (context) {
|
|
757
775
|
const { walletId, groupId } = context;
|
|
@@ -774,33 +792,40 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
|
|
|
774
792
|
const context = __classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").get(accountId);
|
|
775
793
|
if (context) {
|
|
776
794
|
const { walletId, groupId } = context;
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
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.
|
|
784
807
|
accounts.splice(index, 1);
|
|
785
|
-
//
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
//
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
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);
|
|
793
821
|
}
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
|
|
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);
|
|
797
827
|
}
|
|
798
828
|
}
|
|
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);
|
|
804
829
|
}
|
|
805
830
|
// Clear reverse-mapping for that account.
|
|
806
831
|
__classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").delete(accountId);
|
|
@@ -817,9 +842,23 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
|
|
|
817
842
|
delete state.accountWalletsMetadata[walletId];
|
|
818
843
|
}
|
|
819
844
|
return state;
|
|
820
|
-
},
|
|
821
|
-
|
|
822
|
-
|
|
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) ??
|
|
823
862
|
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getKeyringRule).call(this).match(account); // This one cannot fail.
|
|
824
863
|
// Update controller's state.
|
|
825
864
|
const walletId = result.wallet.id;
|
|
@@ -838,62 +877,119 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
|
|
|
838
877
|
// the union tag `result.wallet.type`.
|
|
839
878
|
};
|
|
840
879
|
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
|
-
}
|
|
845
880
|
}
|
|
846
881
|
const groupId = result.group.id;
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
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.
|
|
850
931
|
if (!group) {
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
accounts: [id],
|
|
932
|
+
group = {
|
|
933
|
+
id: multichainAccountGroup.id,
|
|
934
|
+
type: multichainAccountGroup.type,
|
|
935
|
+
accounts: accountIds,
|
|
856
936
|
metadata: {
|
|
937
|
+
entropy: {
|
|
938
|
+
groupIndex: multichainAccountGroup.groupIndex,
|
|
939
|
+
},
|
|
857
940
|
name: '',
|
|
858
|
-
|
|
859
|
-
|
|
941
|
+
pinned: false,
|
|
942
|
+
hidden: false,
|
|
860
943
|
},
|
|
861
|
-
// We do need to type-cast since we're not narrowing `result` with
|
|
862
|
-
// the union tag `result.group.type`.
|
|
863
944
|
};
|
|
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
|
-
}
|
|
871
945
|
}
|
|
872
946
|
else {
|
|
873
|
-
group
|
|
874
|
-
//
|
|
875
|
-
//
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
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.
|
|
966
|
+
},
|
|
967
|
+
};
|
|
968
|
+
wallets[walletId] = wallet;
|
|
969
|
+
// Trigger atomic sync for new wallet.
|
|
970
|
+
__classPrivateFieldGet(this, _AccountTreeController_backupAndSyncService, "f").enqueueSingleWalletSync(walletId);
|
|
971
|
+
}
|
|
972
|
+
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],
|
|
888
985
|
});
|
|
889
986
|
}
|
|
890
|
-
|
|
891
|
-
//
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
});
|
|
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');
|
|
897
993
|
}, _AccountTreeController_listAccounts = function _AccountTreeController_listAccounts() {
|
|
898
994
|
return this.messenger.call('AccountsController:listMultichainAccounts');
|
|
899
995
|
}, _AccountTreeController_assertAccountGroupExists = function _AccountTreeController_assertAccountGroupExists(groupId) {
|
|
@@ -938,6 +1034,25 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
|
|
|
938
1034
|
state.accountTree.selectedAccountGroup = groupId;
|
|
939
1035
|
});
|
|
940
1036
|
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);
|
|
941
1056
|
}, _AccountTreeController_handleMultichainAccountWalletStatusChange = function _AccountTreeController_handleMultichainAccountWalletStatusChange(walletId, walletStatus) {
|
|
942
1057
|
this.update((state) => {
|
|
943
1058
|
const wallet = state.accountTree.wallets[walletId];
|