@metamask-previews/account-tree-controller 1.3.0-preview-a5441fae → 1.3.0-preview-683bbcb0
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 +4 -0
- package/dist/AccountTreeController.cjs +122 -67
- package/dist/AccountTreeController.cjs.map +1 -1
- package/dist/AccountTreeController.d.cts.map +1 -1
- package/dist/AccountTreeController.d.mts.map +1 -1
- package/dist/AccountTreeController.mjs +122 -67
- package/dist/AccountTreeController.mjs.map +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
9
9
|
|
|
10
10
|
### Changed
|
|
11
11
|
|
|
12
|
+
- Re-introduce computed names for account groups ([#6758](https://github.com/MetaMask/core/pull/6758))
|
|
13
|
+
- Those names are computed using the old internal account names, allowing to automatically migrate them.
|
|
14
|
+
- We only consider EVM account names.
|
|
15
|
+
- This automatically handles conflicting names, similarly to backup & sync (adding a suffix ` (n)` in case of conflicts.
|
|
12
16
|
- Bump `@metamask/utils` from `^11.8.0` to `^11.8.1` ([#6708](https://github.com/MetaMask/core/pull/6708))
|
|
13
17
|
|
|
14
18
|
## [1.3.0]
|
|
@@ -10,7 +10,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
10
10
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
11
11
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
12
|
};
|
|
13
|
-
var _AccountTreeController_instances, _AccountTreeController_accountIdToContext, _AccountTreeController_groupIdToWalletId, _AccountTreeController_backupAndSyncService, _AccountTreeController_rules, _AccountTreeController_trace, _AccountTreeController_backupAndSyncConfig, _AccountTreeController_initialized, _AccountTreeController_getEntropyRule, _AccountTreeController_getSnapRule, _AccountTreeController_getKeyringRule, _AccountTreeController_applyAccountWalletMetadata, _AccountTreeController_getRuleForWallet, _AccountTreeController_applyAccountGroupMetadata, _AccountTreeController_handleAccountAdded, _AccountTreeController_handleAccountRemoved, _AccountTreeController_pruneEmptyGroupAndWallet, _AccountTreeController_insert, _AccountTreeController_listAccounts, _AccountTreeController_assertAccountGroupExists, _AccountTreeController_assertAccountWalletExists, _AccountTreeController_assertAccountGroupNameIsUnique, _AccountTreeController_getDefaultSelectedAccountGroup, _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_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");
|
|
@@ -137,6 +137,9 @@ class AccountTreeController extends base_controller_1.BaseController {
|
|
|
137
137
|
this.messagingSystem.subscribe('AccountsController:accountRemoved', (accountId) => {
|
|
138
138
|
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_handleAccountRemoved).call(this, accountId);
|
|
139
139
|
});
|
|
140
|
+
this.messagingSystem.subscribe('AccountsController:selectedAccountChange', (account) => {
|
|
141
|
+
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_handleSelectedAccountChange).call(this, account);
|
|
142
|
+
});
|
|
140
143
|
this.messagingSystem.subscribe('UserStorageController:stateChange', (userStorageControllerState) => {
|
|
141
144
|
__classPrivateFieldGet(this, _AccountTreeController_backupAndSyncService, "f").handleUserStorageStateChange(userStorageControllerState);
|
|
142
145
|
});
|
|
@@ -192,16 +195,22 @@ class AccountTreeController extends base_controller_1.BaseController {
|
|
|
192
195
|
// Used for default group default names (so we use human-indexing here).
|
|
193
196
|
let nextNaturalNameIndex = 1;
|
|
194
197
|
for (const group of Object.values(wallet.groups)) {
|
|
195
|
-
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_applyAccountGroupMetadata).call(this, state, wallet.id, group.id,
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
198
|
+
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_applyAccountGroupMetadata).call(this, state, wallet.id, group.id, {
|
|
199
|
+
// We allow computed name when initializing the tree.
|
|
200
|
+
// This will automatically handle account name migration for the very first init of the
|
|
201
|
+
// tree. Once groups are created, their name will be persisted, thus, taking precedence
|
|
202
|
+
// over the computed names (even if we re-init).
|
|
203
|
+
allowComputedName: true,
|
|
204
|
+
// FIXME: We should not need this kind of logic if we were not inserting accounts
|
|
205
|
+
// 1 by 1. Instead, we should be inserting wallets and groups directly. This would
|
|
206
|
+
// allow us to naturally insert a group in the tree AND update its metadata right
|
|
207
|
+
// away...
|
|
208
|
+
// But here, we have to wait for the entire group to be ready before updating
|
|
209
|
+
// its metadata (mainly because we're dealing with single accounts rather than entire
|
|
210
|
+
// groups).
|
|
211
|
+
// That is why we need this kind of extra parameter.
|
|
212
|
+
nextNaturalNameIndex,
|
|
213
|
+
});
|
|
205
214
|
if (group.id === previousSelectedAccountGroup) {
|
|
206
215
|
previousSelectedAccountGroupStillExists = true;
|
|
207
216
|
}
|
|
@@ -584,7 +593,82 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
|
|
|
584
593
|
default:
|
|
585
594
|
return __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getKeyringRule).call(this);
|
|
586
595
|
}
|
|
587
|
-
},
|
|
596
|
+
}, _AccountTreeController_getComputedAccountGroupName = function _AccountTreeController_getComputedAccountGroupName(wallet, group) {
|
|
597
|
+
let proposedName = ''; // Empty means there's no computed name for this group.
|
|
598
|
+
for (const id of group.accounts) {
|
|
599
|
+
const account = this.messagingSystem.call('AccountsController:getAccount', id);
|
|
600
|
+
if (!account) {
|
|
601
|
+
continue;
|
|
602
|
+
}
|
|
603
|
+
// We only consider EVM account types for computed names.
|
|
604
|
+
if ((0, keyring_api_1.isEvmAccountType)(account.type) && account.metadata.name.length) {
|
|
605
|
+
proposedName = account.metadata.name;
|
|
606
|
+
break;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
// If this name already exists for whatever reason, we rename it to resolve this conflict.
|
|
610
|
+
if (proposedName.length &&
|
|
611
|
+
!(0, group_1.isAccountGroupNameUniqueFromWallet)(wallet, group.id, proposedName)) {
|
|
612
|
+
proposedName = this.resolveNameConflict(wallet, group.id, proposedName);
|
|
613
|
+
}
|
|
614
|
+
return proposedName;
|
|
615
|
+
}, _AccountTreeController_getDefaultAccountGroupName = function _AccountTreeController_getDefaultAccountGroupName(state, wallet, group, nextNaturalNameIndex) {
|
|
616
|
+
// Get the appropriate rule for this wallet type
|
|
617
|
+
const rule = __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getRuleForWallet).call(this, wallet);
|
|
618
|
+
// Get the prefix for groups of this wallet
|
|
619
|
+
const namePrefix = rule.getDefaultAccountGroupPrefix(wallet);
|
|
620
|
+
// Parse the highest account index being used (similar to accounts-controller)
|
|
621
|
+
let highestNameIndex = 0;
|
|
622
|
+
for (const { id: otherGroupId } of Object.values(wallet.groups)) {
|
|
623
|
+
// Skip the current group being processed
|
|
624
|
+
if (otherGroupId === group.id) {
|
|
625
|
+
continue;
|
|
626
|
+
}
|
|
627
|
+
// We always get the name from the persisted map, since `init` will clear the
|
|
628
|
+
// `state.accountTree.wallets`, thus, given empty `group.metadata.name`.
|
|
629
|
+
// NOTE: If the other group has not been named yet, we just use an empty name.
|
|
630
|
+
const otherGroupName = state.accountGroupsMetadata[otherGroupId]?.name?.value ?? '';
|
|
631
|
+
// Parse the existing group name to extract the numeric index
|
|
632
|
+
const nameMatch = otherGroupName.match(/account\s+(\d+)$/iu);
|
|
633
|
+
if (nameMatch) {
|
|
634
|
+
const nameIndex = parseInt(nameMatch[1], 10);
|
|
635
|
+
if (nameIndex > highestNameIndex) {
|
|
636
|
+
highestNameIndex = nameIndex;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
// We just use the highest known index no matter the wallet type.
|
|
641
|
+
//
|
|
642
|
+
// For entropy-based wallets (bip44), if a multichain account group with group index 1
|
|
643
|
+
// is inserted before another one with group index 0, then the naming will be:
|
|
644
|
+
// - "Account 1" (group index 1)
|
|
645
|
+
// - "Account 2" (group index 0)
|
|
646
|
+
// This naming makes more sense for the end-user.
|
|
647
|
+
//
|
|
648
|
+
// For other type of wallets, since those wallets can create arbitrary gaps, we still
|
|
649
|
+
// rely on the highest know index to avoid back-filling account with "old names".
|
|
650
|
+
let proposedNameIndex = Math.max(
|
|
651
|
+
// Use + 1 to use the next available index.
|
|
652
|
+
highestNameIndex + 1,
|
|
653
|
+
// In case all accounts have been renamed differently than the usual "Account <index>"
|
|
654
|
+
// pattern, we want to use the next "natural" index, which is just the number of groups
|
|
655
|
+
// in that wallet (e.g. ["Account A", "Another Account"], next natural index would be
|
|
656
|
+
// "Account 3" in this case).
|
|
657
|
+
nextNaturalNameIndex ?? Object.keys(wallet.groups).length);
|
|
658
|
+
// Find a unique name by checking for conflicts and incrementing if needed
|
|
659
|
+
let proposedNameExists;
|
|
660
|
+
let proposedName = '';
|
|
661
|
+
do {
|
|
662
|
+
proposedName = `${namePrefix} ${proposedNameIndex}`;
|
|
663
|
+
// Check if this name already exists in the wallet (excluding current group)
|
|
664
|
+
proposedNameExists = !(0, group_1.isAccountGroupNameUniqueFromWallet)(wallet, group.id, proposedName);
|
|
665
|
+
/* istanbul ignore next */
|
|
666
|
+
if (proposedNameExists) {
|
|
667
|
+
proposedNameIndex += 1; // Try next number
|
|
668
|
+
}
|
|
669
|
+
} while (proposedNameExists);
|
|
670
|
+
return proposedName;
|
|
671
|
+
}, _AccountTreeController_applyAccountGroupMetadata = function _AccountTreeController_applyAccountGroupMetadata(state, walletId, groupId, { allowComputedName, nextNaturalNameIndex, } = {}) {
|
|
588
672
|
var _a;
|
|
589
673
|
const wallet = state.accountTree.wallets[walletId];
|
|
590
674
|
const group = wallet.groups[groupId];
|
|
@@ -595,62 +679,16 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
|
|
|
595
679
|
persistedGroupMetadata.name.value;
|
|
596
680
|
}
|
|
597
681
|
else if (!group.metadata.name) {
|
|
598
|
-
// Get the appropriate rule for this wallet type
|
|
599
|
-
const rule = __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getRuleForWallet).call(this, wallet);
|
|
600
|
-
// Get the prefix for groups of this wallet
|
|
601
|
-
const namePrefix = rule.getDefaultAccountGroupPrefix(wallet);
|
|
602
|
-
// Skip computed names for now - use default naming with per-wallet logic
|
|
603
|
-
// TODO: Implement computed names in a future iteration
|
|
604
|
-
// Parse the highest account index being used (similar to accounts-controller)
|
|
605
|
-
let highestNameIndex = 0;
|
|
606
|
-
for (const { id: otherGroupId } of Object.values(wallet.groups)) {
|
|
607
|
-
// Skip the current group being processed
|
|
608
|
-
if (otherGroupId === groupId) {
|
|
609
|
-
continue;
|
|
610
|
-
}
|
|
611
|
-
// We always get the name from the persisted map, since `init` will clear the
|
|
612
|
-
// `state.accountTree.wallets`, thus, given empty `group.metadata.name`.
|
|
613
|
-
// NOTE: If the other group has not been named yet, we just use an empty name.
|
|
614
|
-
const otherGroupName = state.accountGroupsMetadata[otherGroupId]?.name?.value ?? '';
|
|
615
|
-
// Parse the existing group name to extract the numeric index
|
|
616
|
-
const nameMatch = otherGroupName.match(/account\s+(\d+)$/iu);
|
|
617
|
-
if (nameMatch) {
|
|
618
|
-
const nameIndex = parseInt(nameMatch[1], 10);
|
|
619
|
-
if (nameIndex > highestNameIndex) {
|
|
620
|
-
highestNameIndex = nameIndex;
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
// We just use the highest known index no matter the wallet type.
|
|
625
|
-
//
|
|
626
|
-
// For entropy-based wallets (bip44), if a multichain account group with group index 1
|
|
627
|
-
// is inserted before another one with group index 0, then the naming will be:
|
|
628
|
-
// - "Account 1" (group index 1)
|
|
629
|
-
// - "Account 2" (group index 0)
|
|
630
|
-
// This naming makes more sense for the end-user.
|
|
631
|
-
//
|
|
632
|
-
// For other type of wallets, since those wallets can create arbitrary gaps, we still
|
|
633
|
-
// rely on the highest know index to avoid back-filling account with "old names".
|
|
634
|
-
let proposedNameIndex = Math.max(
|
|
635
|
-
// Use + 1 to use the next available index.
|
|
636
|
-
highestNameIndex + 1,
|
|
637
|
-
// In case all accounts have been renamed differently than the usual "Account <index>"
|
|
638
|
-
// pattern, we want to use the next "natural" index, which is just the number of groups
|
|
639
|
-
// in that wallet (e.g. ["Account A", "Another Account"], next natural index would be
|
|
640
|
-
// "Account 3" in this case).
|
|
641
|
-
nextNaturalNameIndex ?? Object.keys(wallet.groups).length);
|
|
642
|
-
// Find a unique name by checking for conflicts and incrementing if needed
|
|
643
|
-
let proposedNameExists;
|
|
644
682
|
let proposedName = '';
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
}
|
|
683
|
+
// Computed names are usually only used for existing/old accounts. So this option
|
|
684
|
+
// should be used only when we first initialize the tree.
|
|
685
|
+
if (allowComputedName) {
|
|
686
|
+
proposedName = __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getComputedAccountGroupName).call(this, wallet, group);
|
|
687
|
+
}
|
|
688
|
+
// If we still don't have a valid name candidate, we fallback to a default name.
|
|
689
|
+
if (!proposedName.length) {
|
|
690
|
+
proposedName = __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getDefaultAccountGroupName).call(this, state, wallet, group, nextNaturalNameIndex);
|
|
691
|
+
}
|
|
654
692
|
state.accountTree.wallets[walletId].groups[groupId].metadata.name =
|
|
655
693
|
proposedName;
|
|
656
694
|
(0, logger_1.projectLogger)(`[${group.id}] Set default name to: "${group.metadata.name}"`);
|
|
@@ -849,6 +887,23 @@ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeControlle
|
|
|
849
887
|
}
|
|
850
888
|
// Default to the default group in case of errors.
|
|
851
889
|
return __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getDefaultAccountGroupId).call(this, wallets);
|
|
890
|
+
}, _AccountTreeController_handleSelectedAccountChange = function _AccountTreeController_handleSelectedAccountChange(account) {
|
|
891
|
+
const accountMapping = __classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").get(account.id);
|
|
892
|
+
if (!accountMapping) {
|
|
893
|
+
// Account not in tree yet, might be during initialization
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
const { groupId } = accountMapping;
|
|
897
|
+
const previousSelectedAccountGroup = this.state.accountTree.selectedAccountGroup;
|
|
898
|
+
// Idempotent check - if the same group is already selected, do nothing
|
|
899
|
+
if (previousSelectedAccountGroup === groupId) {
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
902
|
+
// Update selectedAccountGroup to match the selected account
|
|
903
|
+
this.update((state) => {
|
|
904
|
+
state.accountTree.selectedAccountGroup = groupId;
|
|
905
|
+
});
|
|
906
|
+
this.messagingSystem.publish(`${exports.controllerName}:selectedAccountGroupChange`, groupId, previousSelectedAccountGroup);
|
|
852
907
|
}, _AccountTreeController_handleMultichainAccountWalletStatusChange = function _AccountTreeController_handleMultichainAccountWalletStatusChange(walletId, walletStatus) {
|
|
853
908
|
this.update((state) => {
|
|
854
909
|
const wallet = state.accountTree.wallets[walletId];
|