@metamask-previews/account-tree-controller 0.6.0-preview-bd86fa14 → 0.7.0-preview-3f5f144a
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 +21 -1
- package/dist/AccountTreeController.cjs +195 -41
- package/dist/AccountTreeController.cjs.map +1 -1
- package/dist/AccountTreeController.d.cts +31 -40
- package/dist/AccountTreeController.d.cts.map +1 -1
- package/dist/AccountTreeController.d.mts +31 -40
- package/dist/AccountTreeController.d.mts.map +1 -1
- package/dist/AccountTreeController.mjs +193 -39
- package/dist/AccountTreeController.mjs.map +1 -1
- package/dist/AccountTreeGroup.cjs +44 -35
- package/dist/AccountTreeGroup.cjs.map +1 -1
- package/dist/AccountTreeGroup.d.cts +15 -22
- package/dist/AccountTreeGroup.d.cts.map +1 -1
- package/dist/AccountTreeGroup.d.mts +15 -22
- package/dist/AccountTreeGroup.d.mts.map +1 -1
- package/dist/AccountTreeGroup.mjs +42 -33
- package/dist/AccountTreeGroup.mjs.map +1 -1
- package/dist/AccountTreeRule.cjs +37 -0
- package/dist/AccountTreeRule.cjs.map +1 -0
- package/dist/AccountTreeRule.d.cts +53 -0
- package/dist/AccountTreeRule.d.cts.map +1 -0
- package/dist/AccountTreeRule.d.mts +53 -0
- package/dist/AccountTreeRule.d.mts.map +1 -0
- package/dist/AccountTreeRule.mjs +33 -0
- package/dist/AccountTreeRule.mjs.map +1 -0
- package/dist/AccountTreeWallet.cjs +53 -26
- package/dist/AccountTreeWallet.cjs.map +1 -1
- package/dist/AccountTreeWallet.d.cts +26 -21
- package/dist/AccountTreeWallet.d.cts.map +1 -1
- package/dist/AccountTreeWallet.d.mts +26 -21
- package/dist/AccountTreeWallet.d.mts.map +1 -1
- package/dist/AccountTreeWallet.mjs +52 -25
- package/dist/AccountTreeWallet.mjs.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/rules/entropy.cjs +68 -0
- package/dist/rules/entropy.cjs.map +1 -0
- package/dist/rules/entropy.d.cts +13 -0
- package/dist/rules/entropy.d.cts.map +1 -0
- package/dist/rules/entropy.d.mts +13 -0
- package/dist/rules/entropy.d.mts.map +1 -0
- package/dist/rules/entropy.mjs +64 -0
- package/dist/rules/entropy.mjs.map +1 -0
- package/dist/rules/keyring.cjs +86 -0
- package/dist/rules/keyring.cjs.map +1 -0
- package/dist/rules/keyring.d.cts +21 -0
- package/dist/rules/keyring.d.cts.map +1 -0
- package/dist/rules/keyring.d.mts +21 -0
- package/dist/rules/keyring.d.mts.map +1 -0
- package/dist/rules/keyring.mjs +81 -0
- package/dist/rules/keyring.mjs.map +1 -0
- package/dist/rules/snap.cjs +58 -0
- package/dist/rules/snap.cjs.map +1 -0
- package/dist/rules/snap.d.cts +22 -0
- package/dist/rules/snap.d.cts.map +1 -0
- package/dist/rules/snap.d.mts +22 -0
- package/dist/rules/snap.d.mts.map +1 -0
- package/dist/rules/snap.mjs +54 -0
- package/dist/rules/snap.mjs.map +1 -0
- package/dist/types.cjs +3 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.d.cts +83 -0
- package/dist/types.d.cts.map +1 -0
- package/dist/types.d.mts +83 -0
- package/dist/types.d.mts.map +1 -0
- package/dist/types.mjs +2 -0
- package/dist/types.mjs.map +1 -0
- package/package.json +4 -4
- package/dist/rules/EntropySourceWalletRule.cjs +0 -100
- package/dist/rules/EntropySourceWalletRule.cjs.map +0 -1
- package/dist/rules/EntropySourceWalletRule.d.cts +0 -19
- package/dist/rules/EntropySourceWalletRule.d.cts.map +0 -1
- package/dist/rules/EntropySourceWalletRule.d.mts +0 -19
- package/dist/rules/EntropySourceWalletRule.d.mts.map +0 -1
- package/dist/rules/EntropySourceWalletRule.mjs +0 -95
- package/dist/rules/EntropySourceWalletRule.mjs.map +0 -1
- package/dist/rules/KeyringWalletRule.cjs +0 -99
- package/dist/rules/KeyringWalletRule.cjs.map +0 -1
- package/dist/rules/KeyringWalletRule.d.cts +0 -18
- package/dist/rules/KeyringWalletRule.d.cts.map +0 -1
- package/dist/rules/KeyringWalletRule.d.mts +0 -18
- package/dist/rules/KeyringWalletRule.d.mts.map +0 -1
- package/dist/rules/KeyringWalletRule.mjs +0 -94
- package/dist/rules/KeyringWalletRule.mjs.map +0 -1
- package/dist/rules/SnapWalletRule.cjs +0 -70
- package/dist/rules/SnapWalletRule.cjs.map +0 -1
- package/dist/rules/SnapWalletRule.d.cts +0 -10
- package/dist/rules/SnapWalletRule.d.cts.map +0 -1
- package/dist/rules/SnapWalletRule.d.mts +0 -10
- package/dist/rules/SnapWalletRule.d.mts.map +0 -1
- package/dist/rules/SnapWalletRule.mjs +0 -66
- package/dist/rules/SnapWalletRule.mjs.map +0 -1
- package/dist/rules/WalletRule.cjs +0 -13
- package/dist/rules/WalletRule.cjs.map +0 -1
- package/dist/rules/WalletRule.d.cts +0 -37
- package/dist/rules/WalletRule.d.cts.map +0 -1
- package/dist/rules/WalletRule.d.mts +0 -37
- package/dist/rules/WalletRule.d.mts.map +0 -1
- package/dist/rules/WalletRule.mjs +0 -9
- package/dist/rules/WalletRule.mjs.map +0 -1
- package/dist/rules/index.cjs +0 -21
- package/dist/rules/index.cjs.map +0 -1
- package/dist/rules/index.d.cts +0 -5
- package/dist/rules/index.d.cts.map +0 -1
- package/dist/rules/index.d.mts +0 -5
- package/dist/rules/index.d.mts.map +0 -1
- package/dist/rules/index.mjs +0 -5
- package/dist/rules/index.mjs.map +0 -1
- package/dist/rules/utils.cjs +0 -15
- package/dist/rules/utils.cjs.map +0 -1
- package/dist/rules/utils.d.cts +0 -11
- package/dist/rules/utils.d.cts.map +0 -1
- package/dist/rules/utils.d.mts +0 -11
- package/dist/rules/utils.d.mts.map +0 -1
- package/dist/rules/utils.mjs +0 -11
- package/dist/rules/utils.mjs.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.7.0]
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Add BIP-44/multichain accounts support ([#6185](https://github.com/MetaMask/core/pull/6185))
|
|
15
|
+
- Those are being attached to the `entropy` wallet category.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- **BREAKING:** Bump peer dependency `@metamask/account-api` from `^0.2.0` to `^0.3.0` ([#6165](https://github.com/MetaMask/core/pull/6165))
|
|
20
|
+
- Add `selectedAccountGroup` state and bidirectional synchronization with `AccountsController` ([#6186](https://github.com/MetaMask/core/pull/6186))
|
|
21
|
+
- New `getSelectedAccountGroup()` and `setSelectedAccountGroup()` methods.
|
|
22
|
+
- Automatic synchronization when selected account changes in AccountsController.
|
|
23
|
+
- New action types `AccountTreeControllerGetSelectedAccountGroupAction` and `AccountTreeControllerSetSelectedAccountGroupAction`.
|
|
24
|
+
- Now use one account group per account for `snap` and `keyring` wallet categories ([#6185](https://github.com/MetaMask/core/pull/6185))
|
|
25
|
+
- We used to group all accounts under the `'default'` group, but we now compute the group ID using the address of each accounts.
|
|
26
|
+
- Compute account group name based on their underlying account. ([#6185](https://github.com/MetaMask/core/pull/6185))
|
|
27
|
+
- This replaces the previous `'Default'` name for groups.
|
|
28
|
+
|
|
10
29
|
## [0.6.0]
|
|
11
30
|
|
|
12
31
|
### Changed
|
|
@@ -63,7 +82,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
63
82
|
- Initial release ([#5847](https://github.com/MetaMask/core/pull/5847))
|
|
64
83
|
- Grouping accounts into 3 main categories: Entropy source, Snap ID, keyring types.
|
|
65
84
|
|
|
66
|
-
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/account-tree-controller@0.
|
|
85
|
+
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/account-tree-controller@0.7.0...HEAD
|
|
86
|
+
[0.7.0]: https://github.com/MetaMask/core/compare/@metamask/account-tree-controller@0.6.0...@metamask/account-tree-controller@0.7.0
|
|
67
87
|
[0.6.0]: https://github.com/MetaMask/core/compare/@metamask/account-tree-controller@0.5.0...@metamask/account-tree-controller@0.6.0
|
|
68
88
|
[0.5.0]: https://github.com/MetaMask/core/compare/@metamask/account-tree-controller@0.4.0...@metamask/account-tree-controller@0.5.0
|
|
69
89
|
[0.4.0]: https://github.com/MetaMask/core/compare/@metamask/account-tree-controller@0.3.0...@metamask/account-tree-controller@0.4.0
|
|
@@ -10,12 +10,16 @@ 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,
|
|
13
|
+
var _AccountTreeController_instances, _AccountTreeController_accountIdToContext, _AccountTreeController_rules, _AccountTreeController_categoryToRule, _AccountTreeController_getDefaultSelectedAccountGroup, _AccountTreeController_renameAccountWalletIfNeeded, _AccountTreeController_renameAccountGroupIfNeeded, _AccountTreeController_handleAccountAdded, _AccountTreeController_handleAccountRemoved, _AccountTreeController_insert, _AccountTreeController_listAccounts, _AccountTreeController_handleSelectedAccountChange, _AccountTreeController_getFirstAccountInGroup, _AccountTreeController_findFirstNonEmptyGroup, _AccountTreeController_registerMessageHandlers;
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.AccountTreeController = exports.getDefaultAccountTreeControllerState = void 0;
|
|
15
|
+
exports.AccountTreeController = exports.getDefaultAccountTreeControllerState = exports.controllerName = void 0;
|
|
16
|
+
const account_api_1 = require("@metamask/account-api");
|
|
16
17
|
const base_controller_1 = require("@metamask/base-controller");
|
|
17
|
-
const
|
|
18
|
-
const
|
|
18
|
+
const AccountTreeWallet_1 = require("./AccountTreeWallet.cjs");
|
|
19
|
+
const entropy_1 = require("./rules/entropy.cjs");
|
|
20
|
+
const keyring_1 = require("./rules/keyring.cjs");
|
|
21
|
+
const snap_1 = require("./rules/snap.cjs");
|
|
22
|
+
exports.controllerName = 'AccountTreeController';
|
|
19
23
|
const accountTreeControllerMetadata = {
|
|
20
24
|
accountTree: {
|
|
21
25
|
persist: false,
|
|
@@ -31,6 +35,7 @@ function getDefaultAccountTreeControllerState() {
|
|
|
31
35
|
return {
|
|
32
36
|
accountTree: {
|
|
33
37
|
wallets: {},
|
|
38
|
+
selectedAccountGroup: '',
|
|
34
39
|
},
|
|
35
40
|
};
|
|
36
41
|
}
|
|
@@ -46,7 +51,7 @@ class AccountTreeController extends base_controller_1.BaseController {
|
|
|
46
51
|
constructor({ messenger, state, }) {
|
|
47
52
|
super({
|
|
48
53
|
messenger,
|
|
49
|
-
name: controllerName,
|
|
54
|
+
name: exports.controllerName,
|
|
50
55
|
metadata: accountTreeControllerMetadata,
|
|
51
56
|
state: {
|
|
52
57
|
...getDefaultAccountTreeControllerState(),
|
|
@@ -54,20 +59,24 @@ class AccountTreeController extends base_controller_1.BaseController {
|
|
|
54
59
|
},
|
|
55
60
|
});
|
|
56
61
|
_AccountTreeController_instances.add(this);
|
|
57
|
-
|
|
62
|
+
_AccountTreeController_accountIdToContext.set(this, void 0);
|
|
58
63
|
_AccountTreeController_rules.set(this, void 0);
|
|
59
|
-
|
|
60
|
-
__classPrivateFieldSet(this, _AccountTreeController_wallets, new Map(), "f");
|
|
64
|
+
_AccountTreeController_categoryToRule.set(this, void 0);
|
|
61
65
|
// Reverse map to allow fast node access from an account ID.
|
|
62
|
-
__classPrivateFieldSet(this,
|
|
66
|
+
__classPrivateFieldSet(this, _AccountTreeController_accountIdToContext, new Map(), "f");
|
|
63
67
|
// Rules to apply to construct the wallets tree.
|
|
68
|
+
__classPrivateFieldSet(this, _AccountTreeController_categoryToRule, {
|
|
69
|
+
[account_api_1.AccountWalletCategory.Entropy]: new entropy_1.EntropyRule(this.messagingSystem),
|
|
70
|
+
[account_api_1.AccountWalletCategory.Snap]: new snap_1.SnapRule(this.messagingSystem),
|
|
71
|
+
[account_api_1.AccountWalletCategory.Keyring]: new keyring_1.KeyringRule(this.messagingSystem),
|
|
72
|
+
}, "f");
|
|
64
73
|
__classPrivateFieldSet(this, _AccountTreeController_rules, [
|
|
65
74
|
// 1. We group by entropy-source
|
|
66
|
-
|
|
75
|
+
__classPrivateFieldGet(this, _AccountTreeController_categoryToRule, "f")[account_api_1.AccountWalletCategory.Entropy],
|
|
67
76
|
// 2. We group by Snap ID
|
|
68
|
-
|
|
77
|
+
__classPrivateFieldGet(this, _AccountTreeController_categoryToRule, "f")[account_api_1.AccountWalletCategory.Snap],
|
|
69
78
|
// 3. We group by wallet type (this rule cannot fail and will group all non-matching accounts)
|
|
70
|
-
|
|
79
|
+
__classPrivateFieldGet(this, _AccountTreeController_categoryToRule, "f")[account_api_1.AccountWalletCategory.Keyring],
|
|
71
80
|
], "f");
|
|
72
81
|
this.messagingSystem.subscribe('AccountsController:accountAdded', (account) => {
|
|
73
82
|
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_handleAccountAdded).call(this, account);
|
|
@@ -75,6 +84,10 @@ class AccountTreeController extends base_controller_1.BaseController {
|
|
|
75
84
|
this.messagingSystem.subscribe('AccountsController:accountRemoved', (accountId) => {
|
|
76
85
|
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_handleAccountRemoved).call(this, accountId);
|
|
77
86
|
});
|
|
87
|
+
this.messagingSystem.subscribe('AccountsController:selectedAccountChange', (account) => {
|
|
88
|
+
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_handleSelectedAccountChange).call(this, account);
|
|
89
|
+
});
|
|
90
|
+
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_registerMessageHandlers).call(this);
|
|
78
91
|
}
|
|
79
92
|
init() {
|
|
80
93
|
const wallets = {};
|
|
@@ -82,63 +95,166 @@ class AccountTreeController extends base_controller_1.BaseController {
|
|
|
82
95
|
for (const account of __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_listAccounts).call(this)) {
|
|
83
96
|
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_insert).call(this, wallets, account);
|
|
84
97
|
}
|
|
98
|
+
// Once we have the account tree, we can compute the name.
|
|
99
|
+
for (const wallet of Object.values(wallets)) {
|
|
100
|
+
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_renameAccountWalletIfNeeded).call(this, wallet);
|
|
101
|
+
for (const group of Object.values(wallet.groups)) {
|
|
102
|
+
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_renameAccountGroupIfNeeded).call(this, wallet, group);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
85
105
|
this.update((state) => {
|
|
86
106
|
state.accountTree.wallets = wallets;
|
|
107
|
+
if (state.accountTree.selectedAccountGroup === '') {
|
|
108
|
+
// No group is selected yet, re-sync with the AccountsController.
|
|
109
|
+
state.accountTree.selectedAccountGroup =
|
|
110
|
+
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getDefaultSelectedAccountGroup).call(this, wallets);
|
|
111
|
+
}
|
|
87
112
|
});
|
|
88
113
|
}
|
|
89
|
-
|
|
90
|
-
|
|
114
|
+
getAccountWallet(walletId) {
|
|
115
|
+
const wallet = this.state.accountTree.wallets[walletId];
|
|
116
|
+
if (!wallet) {
|
|
117
|
+
return undefined;
|
|
118
|
+
}
|
|
119
|
+
return new AccountTreeWallet_1.AccountTreeWallet({ messenger: this.messagingSystem, wallet });
|
|
91
120
|
}
|
|
92
|
-
|
|
93
|
-
return
|
|
121
|
+
getAccountWallets() {
|
|
122
|
+
return Object.values(this.state.accountTree.wallets).map((wallet) => {
|
|
123
|
+
return new AccountTreeWallet_1.AccountTreeWallet({ messenger: this.messagingSystem, wallet });
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Gets the currently selected account group ID.
|
|
128
|
+
*
|
|
129
|
+
* @returns The selected account group ID or empty string if none selected.
|
|
130
|
+
*/
|
|
131
|
+
getSelectedAccountGroup() {
|
|
132
|
+
return this.state.accountTree.selectedAccountGroup;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Sets the selected account group and updates the AccountsController selectedAccount accordingly.
|
|
136
|
+
*
|
|
137
|
+
* @param groupId - The account group ID to select.
|
|
138
|
+
*/
|
|
139
|
+
setSelectedAccountGroup(groupId) {
|
|
140
|
+
const currentSelectedGroup = this.state.accountTree.selectedAccountGroup;
|
|
141
|
+
// Idempotent check - if the same group is already selected, do nothing
|
|
142
|
+
if (currentSelectedGroup === groupId) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
// Find the first account in this group to select
|
|
146
|
+
const accountToSelect = __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getFirstAccountInGroup).call(this, groupId);
|
|
147
|
+
if (!accountToSelect) {
|
|
148
|
+
throw new Error(`No accounts found in group: ${groupId}`);
|
|
149
|
+
}
|
|
150
|
+
// Update our state first
|
|
151
|
+
this.update((state) => {
|
|
152
|
+
state.accountTree.selectedAccountGroup = groupId;
|
|
153
|
+
});
|
|
154
|
+
// Update AccountsController - this will trigger selectedAccountChange event,
|
|
155
|
+
// but our handler is idempotent so it won't cause infinite loop
|
|
156
|
+
this.messagingSystem.call('AccountsController:setSelectedAccount', accountToSelect);
|
|
94
157
|
}
|
|
95
158
|
}
|
|
96
159
|
exports.AccountTreeController = AccountTreeController;
|
|
97
|
-
|
|
160
|
+
_AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeController_rules = new WeakMap(), _AccountTreeController_categoryToRule = new WeakMap(), _AccountTreeController_instances = new WeakSet(), _AccountTreeController_getDefaultSelectedAccountGroup = function _AccountTreeController_getDefaultSelectedAccountGroup(wallets) {
|
|
161
|
+
const selectedAccount = this.messagingSystem.call('AccountsController:getSelectedAccount');
|
|
162
|
+
if (selectedAccount && selectedAccount.id) {
|
|
163
|
+
const accountMapping = __classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").get(selectedAccount.id);
|
|
164
|
+
if (accountMapping) {
|
|
165
|
+
const { groupId } = accountMapping;
|
|
166
|
+
return groupId;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// Default to the first non-empty group in case of errors.
|
|
170
|
+
return __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_findFirstNonEmptyGroup).call(this, wallets);
|
|
171
|
+
}, _AccountTreeController_renameAccountWalletIfNeeded = function _AccountTreeController_renameAccountWalletIfNeeded(wallet) {
|
|
172
|
+
if (wallet.metadata.name) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const rule = __classPrivateFieldGet(this, _AccountTreeController_categoryToRule, "f")[wallet.metadata.type];
|
|
176
|
+
wallet.metadata.name = rule.getDefaultAccountWalletName(wallet);
|
|
177
|
+
}, _AccountTreeController_renameAccountGroupIfNeeded = function _AccountTreeController_renameAccountGroupIfNeeded(wallet, group) {
|
|
178
|
+
if (group.metadata.name) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const rule = __classPrivateFieldGet(this, _AccountTreeController_categoryToRule, "f")[wallet.metadata.type];
|
|
182
|
+
group.metadata.name = rule.getDefaultAccountGroupName(group);
|
|
183
|
+
}, _AccountTreeController_handleAccountAdded = function _AccountTreeController_handleAccountAdded(account) {
|
|
98
184
|
this.update((state) => {
|
|
99
185
|
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_insert).call(this, state.accountTree.wallets, account);
|
|
186
|
+
const context = __classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").get(account.id);
|
|
187
|
+
if (context) {
|
|
188
|
+
const { walletId, groupId } = context;
|
|
189
|
+
const wallet = state.accountTree.wallets[walletId];
|
|
190
|
+
if (wallet) {
|
|
191
|
+
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_renameAccountWalletIfNeeded).call(this, wallet);
|
|
192
|
+
const group = wallet.groups[groupId];
|
|
193
|
+
if (group) {
|
|
194
|
+
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_renameAccountGroupIfNeeded).call(this, wallet, group);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
100
198
|
});
|
|
101
199
|
}, _AccountTreeController_handleAccountRemoved = function _AccountTreeController_handleAccountRemoved(accountId) {
|
|
102
|
-
const
|
|
103
|
-
if (
|
|
104
|
-
const { walletId, groupId } =
|
|
200
|
+
const context = __classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").get(accountId);
|
|
201
|
+
if (context) {
|
|
202
|
+
const { walletId, groupId } = context;
|
|
105
203
|
this.update((state) => {
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
204
|
+
const accounts = state.accountTree.wallets[walletId]?.groups[groupId]?.accounts;
|
|
205
|
+
if (accounts) {
|
|
206
|
+
const index = accounts.indexOf(accountId);
|
|
207
|
+
if (index !== -1) {
|
|
208
|
+
accounts.splice(index, 1);
|
|
209
|
+
// Check if we need to update selectedAccountGroup after removal
|
|
210
|
+
if (state.accountTree.selectedAccountGroup === groupId &&
|
|
211
|
+
accounts.length === 0) {
|
|
212
|
+
// The currently selected group is now empty, find a new group to select
|
|
213
|
+
state.accountTree.selectedAccountGroup =
|
|
214
|
+
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_findFirstNonEmptyGroup).call(this, state.accountTree.wallets);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
110
217
|
}
|
|
111
218
|
});
|
|
219
|
+
// Clear reverse-mapping for that account.
|
|
220
|
+
__classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").delete(accountId);
|
|
112
221
|
}
|
|
113
222
|
}, _AccountTreeController_insert = function _AccountTreeController_insert(wallets, account) {
|
|
114
223
|
for (const rule of __classPrivateFieldGet(this, _AccountTreeController_rules, "f")) {
|
|
115
|
-
const
|
|
116
|
-
if (!
|
|
224
|
+
const result = rule.match(account);
|
|
225
|
+
if (!result) {
|
|
117
226
|
// No match for that rule, we go to the next one.
|
|
118
227
|
continue;
|
|
119
228
|
}
|
|
120
|
-
const { wallet, group } = match;
|
|
121
|
-
// Update in-memory wallet/group instances.
|
|
122
|
-
__classPrivateFieldGet(this, _AccountTreeController_wallets, "f").set(wallet.id, wallet);
|
|
123
229
|
// Update controller's state.
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
230
|
+
const walletId = result.wallet.id;
|
|
231
|
+
let wallet = wallets[walletId];
|
|
232
|
+
if (!wallet) {
|
|
233
|
+
wallets[walletId] = {
|
|
234
|
+
id: walletId,
|
|
235
|
+
groups: {},
|
|
236
|
+
metadata: {
|
|
237
|
+
name: '',
|
|
238
|
+
...result.wallet.metadata,
|
|
133
239
|
},
|
|
240
|
+
};
|
|
241
|
+
wallet = wallets[walletId];
|
|
242
|
+
}
|
|
243
|
+
const groupId = result.group.id;
|
|
244
|
+
let group = wallet.groups[groupId];
|
|
245
|
+
if (!group) {
|
|
246
|
+
wallet.groups[groupId] = {
|
|
247
|
+
id: groupId,
|
|
248
|
+
accounts: [],
|
|
134
249
|
metadata: {
|
|
135
|
-
name:
|
|
250
|
+
name: '', // Will get updated later.
|
|
136
251
|
},
|
|
137
252
|
};
|
|
253
|
+
group = wallet.groups[groupId];
|
|
138
254
|
}
|
|
139
|
-
|
|
255
|
+
group.accounts.push(account.id);
|
|
140
256
|
// Update the reverse mapping for this account.
|
|
141
|
-
__classPrivateFieldGet(this,
|
|
257
|
+
__classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").set(account.id, {
|
|
142
258
|
walletId: wallet.id,
|
|
143
259
|
groupId: group.id,
|
|
144
260
|
});
|
|
@@ -146,5 +262,43 @@ _AccountTreeController_reverse = new WeakMap(), _AccountTreeController_rules = n
|
|
|
146
262
|
}
|
|
147
263
|
}, _AccountTreeController_listAccounts = function _AccountTreeController_listAccounts() {
|
|
148
264
|
return this.messagingSystem.call('AccountsController:listMultichainAccounts');
|
|
265
|
+
}, _AccountTreeController_handleSelectedAccountChange = function _AccountTreeController_handleSelectedAccountChange(account) {
|
|
266
|
+
const accountMapping = __classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").get(account.id);
|
|
267
|
+
if (!accountMapping) {
|
|
268
|
+
// Account not in tree yet, might be during initialization
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
const { groupId } = accountMapping;
|
|
272
|
+
const currentSelectedGroup = this.state.accountTree.selectedAccountGroup;
|
|
273
|
+
// Idempotent check - if the same group is already selected, do nothing
|
|
274
|
+
if (currentSelectedGroup === groupId) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
// Update selectedAccountGroup to match the selected account
|
|
278
|
+
this.update((state) => {
|
|
279
|
+
state.accountTree.selectedAccountGroup = groupId;
|
|
280
|
+
});
|
|
281
|
+
}, _AccountTreeController_getFirstAccountInGroup = function _AccountTreeController_getFirstAccountInGroup(groupId) {
|
|
282
|
+
for (const wallet of Object.values(this.state.accountTree.wallets)) {
|
|
283
|
+
if (wallet.groups[groupId]) {
|
|
284
|
+
const group = wallet.groups[groupId];
|
|
285
|
+
if (group && group.accounts.length > 0) {
|
|
286
|
+
return group.accounts[0];
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return undefined;
|
|
291
|
+
}, _AccountTreeController_findFirstNonEmptyGroup = function _AccountTreeController_findFirstNonEmptyGroup(wallets) {
|
|
292
|
+
for (const wallet of Object.values(wallets)) {
|
|
293
|
+
for (const group of Object.values(wallet.groups)) {
|
|
294
|
+
if (group.accounts.length > 0) {
|
|
295
|
+
return group.id;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return '';
|
|
300
|
+
}, _AccountTreeController_registerMessageHandlers = function _AccountTreeController_registerMessageHandlers() {
|
|
301
|
+
this.messagingSystem.registerActionHandler(`${exports.controllerName}:getSelectedAccountGroup`, this.getSelectedAccountGroup.bind(this));
|
|
302
|
+
this.messagingSystem.registerActionHandler(`${exports.controllerName}:setSelectedAccountGroup`, this.setSelectedAccountGroup.bind(this));
|
|
149
303
|
};
|
|
150
304
|
//# sourceMappingURL=AccountTreeController.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccountTreeController.cjs","sourceRoot":"","sources":["../src/AccountTreeController.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AccountGroupId, AccountWalletId } from '@metamask/account-api';\nimport type {\n AccountId,\n AccountsControllerAccountAddedEvent,\n AccountsControllerAccountRemovedEvent,\n AccountsControllerGetAccountAction,\n AccountsControllerListMultichainAccountsAction,\n} from '@metamask/accounts-controller';\nimport type { StateMetadata } from '@metamask/base-controller';\nimport {\n type ControllerGetStateAction,\n type ControllerStateChangeEvent,\n type RestrictedMessenger,\n BaseController,\n} from '@metamask/base-controller';\nimport type { KeyringControllerGetStateAction } from '@metamask/keyring-controller';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport type { GetSnap as SnapControllerGetSnap } from '@metamask/snaps-controllers';\n\nimport type { AccountTreeWallet } from './AccountTreeWallet';\nimport type { WalletRule } from './rules';\nimport {\n EntropySourceWalletRule,\n SnapWalletRule,\n KeyringWalletRule,\n} from './rules';\n\nconst controllerName = 'AccountTreeController';\n\ntype AccountReverseMapping = {\n walletId: AccountWalletId;\n groupId: AccountGroupId;\n};\n\n// Do not export this one, we just use it to have a common type interface between group and wallet metadata.\ntype Metadata = {\n name: string;\n};\n\nexport type AccountWalletMetadata = Metadata;\n\nexport type AccountGroupMetadata = Metadata;\n\nexport type AccountGroupObject = {\n id: AccountGroupId;\n // Blockchain Accounts:\n accounts: AccountId[];\n metadata: AccountGroupMetadata;\n};\n\nexport type AccountWalletObject = {\n id: AccountWalletId;\n // Account groups OR Multichain accounts (once available).\n groups: {\n [groupId: AccountGroupId]: AccountGroupObject;\n };\n metadata: AccountWalletMetadata;\n};\n\nexport type AccountTreeControllerState = {\n accountTree: {\n wallets: {\n // Wallets:\n [walletId: AccountWalletId]: AccountWalletObject;\n };\n };\n};\n\nexport type AccountTreeControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n AccountTreeControllerState\n>;\n\nexport type AllowedActions =\n | AccountsControllerGetAccountAction\n | AccountsControllerListMultichainAccountsAction\n | KeyringControllerGetStateAction\n | SnapControllerGetSnap;\n\nexport type AccountTreeControllerActions = never;\n\nexport type AccountTreeControllerStateChangeEvent = ControllerStateChangeEvent<\n typeof controllerName,\n AccountTreeControllerState\n>;\n\nexport type AllowedEvents =\n | AccountsControllerAccountAddedEvent\n | AccountsControllerAccountRemovedEvent;\n\nexport type AccountTreeControllerEvents = AccountTreeControllerStateChangeEvent;\n\nexport type AccountTreeControllerMessenger = RestrictedMessenger<\n typeof controllerName,\n AccountTreeControllerActions | AllowedActions,\n AccountTreeControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\nconst accountTreeControllerMetadata: StateMetadata<AccountTreeControllerState> =\n {\n accountTree: {\n persist: false, // We do re-recompute this state everytime.\n anonymous: false,\n },\n };\n\n/**\n * Gets default state of the `AccountTreeController`.\n *\n * @returns The default state of the `AccountTreeController`.\n */\nexport function getDefaultAccountTreeControllerState(): AccountTreeControllerState {\n return {\n accountTree: {\n wallets: {},\n },\n };\n}\n\nexport class AccountTreeController extends BaseController<\n typeof controllerName,\n AccountTreeControllerState,\n AccountTreeControllerMessenger\n> {\n readonly #reverse: Map<AccountId, AccountReverseMapping>;\n\n readonly #rules: WalletRule[];\n\n readonly #wallets: Map<AccountWalletId, AccountTreeWallet>;\n\n /**\n * Constructor for AccountTreeController.\n *\n * @param options - The controller options.\n * @param options.messenger - The messenger object.\n * @param options.state - Initial state to set on this controller\n */\n constructor({\n messenger,\n state,\n }: {\n messenger: AccountTreeControllerMessenger;\n state?: Partial<AccountTreeControllerState>;\n }) {\n super({\n messenger,\n name: controllerName,\n metadata: accountTreeControllerMetadata,\n state: {\n ...getDefaultAccountTreeControllerState(),\n ...state,\n },\n });\n this.#wallets = new Map();\n\n // Reverse map to allow fast node access from an account ID.\n this.#reverse = new Map();\n\n // Rules to apply to construct the wallets tree.\n this.#rules = [\n // 1. We group by entropy-source\n new EntropySourceWalletRule(this.messagingSystem),\n // 2. We group by Snap ID\n new SnapWalletRule(this.messagingSystem),\n // 3. We group by wallet type (this rule cannot fail and will group all non-matching accounts)\n new KeyringWalletRule(this.messagingSystem),\n ];\n\n this.messagingSystem.subscribe(\n 'AccountsController:accountAdded',\n (account) => {\n this.#handleAccountAdded(account);\n },\n );\n\n this.messagingSystem.subscribe(\n 'AccountsController:accountRemoved',\n (accountId) => {\n this.#handleAccountRemoved(accountId);\n },\n );\n }\n\n init() {\n const wallets = {};\n\n // For now, we always re-compute all wallets, we do not re-use the existing state.\n for (const account of this.#listAccounts()) {\n this.#insert(wallets, account);\n }\n\n this.update((state) => {\n state.accountTree.wallets = wallets;\n });\n }\n\n getWallet(id: AccountWalletId): AccountTreeWallet | undefined {\n return this.#wallets.get(id);\n }\n\n getWallets(): AccountTreeWallet[] {\n return Array.from(this.#wallets.values());\n }\n\n #handleAccountAdded(account: InternalAccount) {\n this.update((state) => {\n this.#insert(state.accountTree.wallets, account);\n });\n }\n\n #handleAccountRemoved(accountId: AccountId) {\n const found = this.#reverse.get(accountId);\n\n if (found) {\n const { walletId, groupId } = found;\n this.update((state) => {\n const { accounts } =\n state.accountTree.wallets[walletId].groups[groupId];\n\n const index = accounts.indexOf(accountId);\n if (index !== -1) {\n accounts.splice(index, 1);\n }\n });\n }\n }\n\n #insert(\n wallets: { [walletId: AccountWalletId]: AccountWalletObject },\n account: InternalAccount,\n ) {\n for (const rule of this.#rules) {\n const match = rule.match(account);\n\n if (!match) {\n // No match for that rule, we go to the next one.\n continue;\n }\n\n const { wallet, group } = match;\n\n // Update in-memory wallet/group instances.\n this.#wallets.set(wallet.id, wallet);\n\n // Update controller's state.\n if (!wallets[wallet.id]) {\n wallets[wallet.id] = {\n id: wallet.id,\n groups: {\n [group.id]: {\n id: group.id,\n accounts: [],\n metadata: { name: group.getDefaultName() },\n },\n },\n metadata: {\n name: wallet.getDefaultName(),\n },\n };\n }\n wallets[wallet.id].groups[group.id].accounts.push(account.id);\n\n // Update the reverse mapping for this account.\n this.#reverse.set(account.id, {\n walletId: wallet.id,\n groupId: group.id,\n });\n\n return;\n }\n }\n\n #listAccounts(): InternalAccount[] {\n return this.messagingSystem.call(\n 'AccountsController:listMultichainAccounts',\n );\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"AccountTreeController.cjs","sourceRoot":"","sources":["../src/AccountTreeController.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AccountGroupId, AccountWalletId } from '@metamask/account-api';\nimport { AccountWalletCategory } from '@metamask/account-api';\nimport type { AccountId } from '@metamask/accounts-controller';\nimport type { StateMetadata } from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\n\nimport type { AccountTreeRule } from './AccountTreeRule';\nimport { AccountTreeWallet } from './AccountTreeWallet';\nimport { EntropyRule } from './rules/entropy';\nimport { KeyringRule } from './rules/keyring';\nimport { SnapRule } from './rules/snap';\nimport type {\n AccountGroupObject,\n AccountTreeControllerMessenger,\n AccountTreeControllerState,\n AccountWalletObject,\n} from './types';\n\nexport const controllerName = 'AccountTreeController';\n\nconst accountTreeControllerMetadata: StateMetadata<AccountTreeControllerState> =\n {\n accountTree: {\n persist: false, // We do re-recompute this state everytime.\n anonymous: false,\n },\n };\n\n/**\n * Gets default state of the `AccountTreeController`.\n *\n * @returns The default state of the `AccountTreeController`.\n */\nexport function getDefaultAccountTreeControllerState(): AccountTreeControllerState {\n return {\n accountTree: {\n wallets: {},\n selectedAccountGroup: '',\n },\n };\n}\n\n/**\n * Context for an account.\n */\nexport type AccountContext = {\n /**\n * Wallet ID associated to that account.\n */\n walletId: AccountWalletId;\n\n /**\n * Account group ID associated to that account.\n */\n groupId: AccountGroupId;\n};\n\nexport class AccountTreeController extends BaseController<\n typeof controllerName,\n AccountTreeControllerState,\n AccountTreeControllerMessenger\n> {\n readonly #accountIdToContext: Map<AccountId, AccountContext>;\n\n readonly #rules: AccountTreeRule[];\n\n readonly #categoryToRule: Record<AccountWalletCategory, AccountTreeRule>;\n\n /**\n * Constructor for AccountTreeController.\n *\n * @param options - The controller options.\n * @param options.messenger - The messenger object.\n * @param options.state - Initial state to set on this controller\n */\n constructor({\n messenger,\n state,\n }: {\n messenger: AccountTreeControllerMessenger;\n state?: Partial<AccountTreeControllerState>;\n }) {\n super({\n messenger,\n name: controllerName,\n metadata: accountTreeControllerMetadata,\n state: {\n ...getDefaultAccountTreeControllerState(),\n ...state,\n },\n });\n\n // Reverse map to allow fast node access from an account ID.\n this.#accountIdToContext = new Map();\n\n // Rules to apply to construct the wallets tree.\n this.#categoryToRule = {\n [AccountWalletCategory.Entropy]: new EntropyRule(this.messagingSystem),\n [AccountWalletCategory.Snap]: new SnapRule(this.messagingSystem),\n [AccountWalletCategory.Keyring]: new KeyringRule(this.messagingSystem),\n } as const;\n this.#rules = [\n // 1. We group by entropy-source\n this.#categoryToRule[AccountWalletCategory.Entropy],\n // 2. We group by Snap ID\n this.#categoryToRule[AccountWalletCategory.Snap],\n // 3. We group by wallet type (this rule cannot fail and will group all non-matching accounts)\n this.#categoryToRule[AccountWalletCategory.Keyring],\n ];\n\n this.messagingSystem.subscribe(\n 'AccountsController:accountAdded',\n (account) => {\n this.#handleAccountAdded(account);\n },\n );\n\n this.messagingSystem.subscribe(\n 'AccountsController:accountRemoved',\n (accountId) => {\n this.#handleAccountRemoved(accountId);\n },\n );\n\n this.messagingSystem.subscribe(\n 'AccountsController:selectedAccountChange',\n (account) => {\n this.#handleSelectedAccountChange(account);\n },\n );\n\n this.#registerMessageHandlers();\n }\n\n init() {\n const wallets: AccountTreeControllerState['accountTree']['wallets'] = {};\n\n // For now, we always re-compute all wallets, we do not re-use the existing state.\n for (const account of this.#listAccounts()) {\n this.#insert(wallets, account);\n }\n\n // Once we have the account tree, we can compute the name.\n for (const wallet of Object.values(wallets)) {\n this.#renameAccountWalletIfNeeded(wallet);\n\n for (const group of Object.values(wallet.groups)) {\n this.#renameAccountGroupIfNeeded(wallet, group);\n }\n }\n\n this.update((state) => {\n state.accountTree.wallets = wallets;\n\n if (state.accountTree.selectedAccountGroup === '') {\n // No group is selected yet, re-sync with the AccountsController.\n state.accountTree.selectedAccountGroup =\n this.#getDefaultSelectedAccountGroup(wallets);\n }\n });\n }\n\n /**\n * Initializes the selectedAccountGroup based on the currently selected account from AccountsController.\n *\n * @param wallets - Wallets object to use for fallback logic\n * @returns The default selected account group ID or empty string if none selected.\n */\n #getDefaultSelectedAccountGroup(wallets: {\n [walletId: AccountWalletId]: AccountWalletObject;\n }): AccountGroupId | '' {\n const selectedAccount = this.messagingSystem.call(\n 'AccountsController:getSelectedAccount',\n );\n if (selectedAccount && selectedAccount.id) {\n const accountMapping = this.#accountIdToContext.get(selectedAccount.id);\n if (accountMapping) {\n const { groupId } = accountMapping;\n\n return groupId;\n }\n }\n\n // Default to the first non-empty group in case of errors.\n return this.#findFirstNonEmptyGroup(wallets);\n }\n\n #renameAccountWalletIfNeeded(wallet: AccountWalletObject) {\n if (wallet.metadata.name) {\n return;\n }\n\n const rule = this.#categoryToRule[wallet.metadata.type];\n wallet.metadata.name = rule.getDefaultAccountWalletName(wallet);\n }\n\n #renameAccountGroupIfNeeded(\n wallet: AccountWalletObject,\n group: AccountGroupObject,\n ) {\n if (group.metadata.name) {\n return;\n }\n\n const rule = this.#categoryToRule[wallet.metadata.type];\n group.metadata.name = rule.getDefaultAccountGroupName(group);\n }\n\n getAccountWallet(walletId: AccountWalletId): AccountTreeWallet | undefined {\n const wallet = this.state.accountTree.wallets[walletId];\n if (!wallet) {\n return undefined;\n }\n\n return new AccountTreeWallet({ messenger: this.messagingSystem, wallet });\n }\n\n getAccountWallets(): AccountTreeWallet[] {\n return Object.values(this.state.accountTree.wallets).map((wallet) => {\n return new AccountTreeWallet({ messenger: this.messagingSystem, wallet });\n });\n }\n\n #handleAccountAdded(account: InternalAccount) {\n this.update((state) => {\n this.#insert(state.accountTree.wallets, account);\n\n const context = this.#accountIdToContext.get(account.id);\n if (context) {\n const { walletId, groupId } = context;\n\n const wallet = state.accountTree.wallets[walletId];\n if (wallet) {\n this.#renameAccountWalletIfNeeded(wallet);\n\n const group = wallet.groups[groupId];\n if (group) {\n this.#renameAccountGroupIfNeeded(wallet, group);\n }\n }\n }\n });\n }\n\n #handleAccountRemoved(accountId: AccountId) {\n const context = this.#accountIdToContext.get(accountId);\n\n if (context) {\n const { walletId, groupId } = context;\n\n this.update((state) => {\n const accounts =\n state.accountTree.wallets[walletId]?.groups[groupId]?.accounts;\n\n if (accounts) {\n const index = accounts.indexOf(accountId);\n if (index !== -1) {\n accounts.splice(index, 1);\n\n // Check if we need to update selectedAccountGroup after removal\n if (\n state.accountTree.selectedAccountGroup === groupId &&\n accounts.length === 0\n ) {\n // The currently selected group is now empty, find a new group to select\n state.accountTree.selectedAccountGroup =\n this.#findFirstNonEmptyGroup(state.accountTree.wallets);\n }\n }\n }\n });\n\n // Clear reverse-mapping for that account.\n this.#accountIdToContext.delete(accountId);\n }\n }\n\n #insert(\n wallets: AccountTreeControllerState['accountTree']['wallets'],\n account: InternalAccount,\n ) {\n for (const rule of this.#rules) {\n const result = rule.match(account);\n\n if (!result) {\n // No match for that rule, we go to the next one.\n continue;\n }\n\n // Update controller's state.\n const walletId = result.wallet.id;\n let wallet = wallets[walletId];\n if (!wallet) {\n wallets[walletId] = {\n id: walletId,\n groups: {},\n metadata: {\n name: '', // Will get updated later.\n ...result.wallet.metadata,\n },\n };\n wallet = wallets[walletId];\n }\n\n const groupId = result.group.id;\n let group = wallet.groups[groupId];\n if (!group) {\n wallet.groups[groupId] = {\n id: groupId,\n accounts: [],\n metadata: {\n name: '', // Will get updated later.\n },\n };\n group = wallet.groups[groupId];\n }\n\n group.accounts.push(account.id);\n\n // Update the reverse mapping for this account.\n this.#accountIdToContext.set(account.id, {\n walletId: wallet.id,\n groupId: group.id,\n });\n\n return;\n }\n }\n\n #listAccounts(): InternalAccount[] {\n return this.messagingSystem.call(\n 'AccountsController:listMultichainAccounts',\n );\n }\n\n /**\n * Gets the currently selected account group ID.\n *\n * @returns The selected account group ID or empty string if none selected.\n */\n getSelectedAccountGroup(): AccountGroupId | '' {\n return this.state.accountTree.selectedAccountGroup;\n }\n\n /**\n * Sets the selected account group and updates the AccountsController selectedAccount accordingly.\n *\n * @param groupId - The account group ID to select.\n */\n setSelectedAccountGroup(groupId: AccountGroupId): void {\n const currentSelectedGroup = this.state.accountTree.selectedAccountGroup;\n\n // Idempotent check - if the same group is already selected, do nothing\n if (currentSelectedGroup === groupId) {\n return;\n }\n\n // Find the first account in this group to select\n const accountToSelect = this.#getFirstAccountInGroup(groupId);\n if (!accountToSelect) {\n throw new Error(`No accounts found in group: ${groupId}`);\n }\n\n // Update our state first\n this.update((state) => {\n state.accountTree.selectedAccountGroup = groupId;\n });\n\n // Update AccountsController - this will trigger selectedAccountChange event,\n // but our handler is idempotent so it won't cause infinite loop\n this.messagingSystem.call(\n 'AccountsController:setSelectedAccount',\n accountToSelect,\n );\n }\n\n /**\n * Handles selected account change from AccountsController.\n * Updates selectedAccountGroup to match the selected account.\n *\n * @param account - The newly selected account.\n */\n #handleSelectedAccountChange(account: InternalAccount): void {\n const accountMapping = this.#accountIdToContext.get(account.id);\n if (!accountMapping) {\n // Account not in tree yet, might be during initialization\n return;\n }\n\n const { groupId } = accountMapping;\n const currentSelectedGroup = this.state.accountTree.selectedAccountGroup;\n\n // Idempotent check - if the same group is already selected, do nothing\n if (currentSelectedGroup === groupId) {\n return;\n }\n\n // Update selectedAccountGroup to match the selected account\n this.update((state) => {\n state.accountTree.selectedAccountGroup = groupId;\n });\n }\n\n /**\n * Gets the first account ID in the specified group.\n *\n * @param groupId - The account group ID.\n * @returns The first account ID in the group, or undefined if no accounts found.\n */\n #getFirstAccountInGroup(groupId: AccountGroupId): AccountId | undefined {\n for (const wallet of Object.values(this.state.accountTree.wallets)) {\n if (wallet.groups[groupId]) {\n const group = wallet.groups[groupId];\n if (group && group.accounts.length > 0) {\n return group.accounts[0];\n }\n }\n }\n return undefined;\n }\n\n /**\n * Finds the first non-empty group in the given wallets object.\n *\n * @param wallets - The wallets object to search.\n * @returns The ID of the first non-empty group, or an empty string if no groups are found.\n */\n #findFirstNonEmptyGroup(wallets: {\n [walletId: AccountWalletId]: AccountWalletObject;\n }): AccountGroupId | '' {\n for (const wallet of Object.values(wallets)) {\n for (const group of Object.values(wallet.groups)) {\n if (group.accounts.length > 0) {\n return group.id;\n }\n }\n }\n return '';\n }\n\n /**\n * Registers message handlers for the AccountTreeController.\n */\n #registerMessageHandlers(): void {\n this.messagingSystem.registerActionHandler(\n `${controllerName}:getSelectedAccountGroup`,\n this.getSelectedAccountGroup.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:setSelectedAccountGroup`,\n this.setSelectedAccountGroup.bind(this),\n );\n }\n}\n"]}
|
|
@@ -1,47 +1,27 @@
|
|
|
1
1
|
import type { AccountGroupId, AccountWalletId } from "@metamask/account-api";
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import type {
|
|
5
|
-
|
|
6
|
-
import type { AccountTreeWallet } from "./AccountTreeWallet.cjs";
|
|
7
|
-
declare const controllerName = "AccountTreeController";
|
|
8
|
-
type Metadata = {
|
|
9
|
-
name: string;
|
|
10
|
-
};
|
|
11
|
-
export type AccountWalletMetadata = Metadata;
|
|
12
|
-
export type AccountGroupMetadata = Metadata;
|
|
13
|
-
export type AccountGroupObject = {
|
|
14
|
-
id: AccountGroupId;
|
|
15
|
-
accounts: AccountId[];
|
|
16
|
-
metadata: AccountGroupMetadata;
|
|
17
|
-
};
|
|
18
|
-
export type AccountWalletObject = {
|
|
19
|
-
id: AccountWalletId;
|
|
20
|
-
groups: {
|
|
21
|
-
[groupId: AccountGroupId]: AccountGroupObject;
|
|
22
|
-
};
|
|
23
|
-
metadata: AccountWalletMetadata;
|
|
24
|
-
};
|
|
25
|
-
export type AccountTreeControllerState = {
|
|
26
|
-
accountTree: {
|
|
27
|
-
wallets: {
|
|
28
|
-
[walletId: AccountWalletId]: AccountWalletObject;
|
|
29
|
-
};
|
|
30
|
-
};
|
|
31
|
-
};
|
|
32
|
-
export type AccountTreeControllerGetStateAction = ControllerGetStateAction<typeof controllerName, AccountTreeControllerState>;
|
|
33
|
-
export type AllowedActions = AccountsControllerGetAccountAction | AccountsControllerListMultichainAccountsAction | KeyringControllerGetStateAction | SnapControllerGetSnap;
|
|
34
|
-
export type AccountTreeControllerActions = never;
|
|
35
|
-
export type AccountTreeControllerStateChangeEvent = ControllerStateChangeEvent<typeof controllerName, AccountTreeControllerState>;
|
|
36
|
-
export type AllowedEvents = AccountsControllerAccountAddedEvent | AccountsControllerAccountRemovedEvent;
|
|
37
|
-
export type AccountTreeControllerEvents = AccountTreeControllerStateChangeEvent;
|
|
38
|
-
export type AccountTreeControllerMessenger = RestrictedMessenger<typeof controllerName, AccountTreeControllerActions | AllowedActions, AccountTreeControllerEvents | AllowedEvents, AllowedActions['type'], AllowedEvents['type']>;
|
|
2
|
+
import { BaseController } from "@metamask/base-controller";
|
|
3
|
+
import { AccountTreeWallet } from "./AccountTreeWallet.cjs";
|
|
4
|
+
import type { AccountTreeControllerMessenger, AccountTreeControllerState } from "./types.cjs";
|
|
5
|
+
export declare const controllerName = "AccountTreeController";
|
|
39
6
|
/**
|
|
40
7
|
* Gets default state of the `AccountTreeController`.
|
|
41
8
|
*
|
|
42
9
|
* @returns The default state of the `AccountTreeController`.
|
|
43
10
|
*/
|
|
44
11
|
export declare function getDefaultAccountTreeControllerState(): AccountTreeControllerState;
|
|
12
|
+
/**
|
|
13
|
+
* Context for an account.
|
|
14
|
+
*/
|
|
15
|
+
export type AccountContext = {
|
|
16
|
+
/**
|
|
17
|
+
* Wallet ID associated to that account.
|
|
18
|
+
*/
|
|
19
|
+
walletId: AccountWalletId;
|
|
20
|
+
/**
|
|
21
|
+
* Account group ID associated to that account.
|
|
22
|
+
*/
|
|
23
|
+
groupId: AccountGroupId;
|
|
24
|
+
};
|
|
45
25
|
export declare class AccountTreeController extends BaseController<typeof controllerName, AccountTreeControllerState, AccountTreeControllerMessenger> {
|
|
46
26
|
#private;
|
|
47
27
|
/**
|
|
@@ -56,8 +36,19 @@ export declare class AccountTreeController extends BaseController<typeof control
|
|
|
56
36
|
state?: Partial<AccountTreeControllerState>;
|
|
57
37
|
});
|
|
58
38
|
init(): void;
|
|
59
|
-
|
|
60
|
-
|
|
39
|
+
getAccountWallet(walletId: AccountWalletId): AccountTreeWallet | undefined;
|
|
40
|
+
getAccountWallets(): AccountTreeWallet[];
|
|
41
|
+
/**
|
|
42
|
+
* Gets the currently selected account group ID.
|
|
43
|
+
*
|
|
44
|
+
* @returns The selected account group ID or empty string if none selected.
|
|
45
|
+
*/
|
|
46
|
+
getSelectedAccountGroup(): AccountGroupId | '';
|
|
47
|
+
/**
|
|
48
|
+
* Sets the selected account group and updates the AccountsController selectedAccount accordingly.
|
|
49
|
+
*
|
|
50
|
+
* @param groupId - The account group ID to select.
|
|
51
|
+
*/
|
|
52
|
+
setSelectedAccountGroup(groupId: AccountGroupId): void;
|
|
61
53
|
}
|
|
62
|
-
export {};
|
|
63
54
|
//# sourceMappingURL=AccountTreeController.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccountTreeController.d.cts","sourceRoot":"","sources":["../src/AccountTreeController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,8BAA8B;
|
|
1
|
+
{"version":3,"file":"AccountTreeController.d.cts","sourceRoot":"","sources":["../src/AccountTreeController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,8BAA8B;AAI7E,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAI3D,OAAO,EAAE,iBAAiB,EAAE,gCAA4B;AAIxD,OAAO,KAAK,EAEV,8BAA8B,EAC9B,0BAA0B,EAE3B,oBAAgB;AAEjB,eAAO,MAAM,cAAc,0BAA0B,CAAC;AAUtD;;;;GAIG;AACH,wBAAgB,oCAAoC,IAAI,0BAA0B,CAOjF;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B;;OAEG;IACH,QAAQ,EAAE,eAAe,CAAC;IAE1B;;OAEG;IACH,OAAO,EAAE,cAAc,CAAC;CACzB,CAAC;AAEF,qBAAa,qBAAsB,SAAQ,cAAc,CACvD,OAAO,cAAc,EACrB,0BAA0B,EAC1B,8BAA8B,CAC/B;;IAOC;;;;;;OAMG;gBACS,EACV,SAAS,EACT,KAAK,GACN,EAAE;QACD,SAAS,EAAE,8BAA8B,CAAC;QAC1C,KAAK,CAAC,EAAE,OAAO,CAAC,0BAA0B,CAAC,CAAC;KAC7C;IAqDD,IAAI;IA0EJ,gBAAgB,CAAC,QAAQ,EAAE,eAAe,GAAG,iBAAiB,GAAG,SAAS;IAS1E,iBAAiB,IAAI,iBAAiB,EAAE;IAsHxC;;;;OAIG;IACH,uBAAuB,IAAI,cAAc,GAAG,EAAE;IAI9C;;;;OAIG;IACH,uBAAuB,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;CAyGvD"}
|
|
@@ -1,47 +1,27 @@
|
|
|
1
1
|
import type { AccountGroupId, AccountWalletId } from "@metamask/account-api";
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import type {
|
|
5
|
-
|
|
6
|
-
import type { AccountTreeWallet } from "./AccountTreeWallet.mjs";
|
|
7
|
-
declare const controllerName = "AccountTreeController";
|
|
8
|
-
type Metadata = {
|
|
9
|
-
name: string;
|
|
10
|
-
};
|
|
11
|
-
export type AccountWalletMetadata = Metadata;
|
|
12
|
-
export type AccountGroupMetadata = Metadata;
|
|
13
|
-
export type AccountGroupObject = {
|
|
14
|
-
id: AccountGroupId;
|
|
15
|
-
accounts: AccountId[];
|
|
16
|
-
metadata: AccountGroupMetadata;
|
|
17
|
-
};
|
|
18
|
-
export type AccountWalletObject = {
|
|
19
|
-
id: AccountWalletId;
|
|
20
|
-
groups: {
|
|
21
|
-
[groupId: AccountGroupId]: AccountGroupObject;
|
|
22
|
-
};
|
|
23
|
-
metadata: AccountWalletMetadata;
|
|
24
|
-
};
|
|
25
|
-
export type AccountTreeControllerState = {
|
|
26
|
-
accountTree: {
|
|
27
|
-
wallets: {
|
|
28
|
-
[walletId: AccountWalletId]: AccountWalletObject;
|
|
29
|
-
};
|
|
30
|
-
};
|
|
31
|
-
};
|
|
32
|
-
export type AccountTreeControllerGetStateAction = ControllerGetStateAction<typeof controllerName, AccountTreeControllerState>;
|
|
33
|
-
export type AllowedActions = AccountsControllerGetAccountAction | AccountsControllerListMultichainAccountsAction | KeyringControllerGetStateAction | SnapControllerGetSnap;
|
|
34
|
-
export type AccountTreeControllerActions = never;
|
|
35
|
-
export type AccountTreeControllerStateChangeEvent = ControllerStateChangeEvent<typeof controllerName, AccountTreeControllerState>;
|
|
36
|
-
export type AllowedEvents = AccountsControllerAccountAddedEvent | AccountsControllerAccountRemovedEvent;
|
|
37
|
-
export type AccountTreeControllerEvents = AccountTreeControllerStateChangeEvent;
|
|
38
|
-
export type AccountTreeControllerMessenger = RestrictedMessenger<typeof controllerName, AccountTreeControllerActions | AllowedActions, AccountTreeControllerEvents | AllowedEvents, AllowedActions['type'], AllowedEvents['type']>;
|
|
2
|
+
import { BaseController } from "@metamask/base-controller";
|
|
3
|
+
import { AccountTreeWallet } from "./AccountTreeWallet.mjs";
|
|
4
|
+
import type { AccountTreeControllerMessenger, AccountTreeControllerState } from "./types.mjs";
|
|
5
|
+
export declare const controllerName = "AccountTreeController";
|
|
39
6
|
/**
|
|
40
7
|
* Gets default state of the `AccountTreeController`.
|
|
41
8
|
*
|
|
42
9
|
* @returns The default state of the `AccountTreeController`.
|
|
43
10
|
*/
|
|
44
11
|
export declare function getDefaultAccountTreeControllerState(): AccountTreeControllerState;
|
|
12
|
+
/**
|
|
13
|
+
* Context for an account.
|
|
14
|
+
*/
|
|
15
|
+
export type AccountContext = {
|
|
16
|
+
/**
|
|
17
|
+
* Wallet ID associated to that account.
|
|
18
|
+
*/
|
|
19
|
+
walletId: AccountWalletId;
|
|
20
|
+
/**
|
|
21
|
+
* Account group ID associated to that account.
|
|
22
|
+
*/
|
|
23
|
+
groupId: AccountGroupId;
|
|
24
|
+
};
|
|
45
25
|
export declare class AccountTreeController extends BaseController<typeof controllerName, AccountTreeControllerState, AccountTreeControllerMessenger> {
|
|
46
26
|
#private;
|
|
47
27
|
/**
|
|
@@ -56,8 +36,19 @@ export declare class AccountTreeController extends BaseController<typeof control
|
|
|
56
36
|
state?: Partial<AccountTreeControllerState>;
|
|
57
37
|
});
|
|
58
38
|
init(): void;
|
|
59
|
-
|
|
60
|
-
|
|
39
|
+
getAccountWallet(walletId: AccountWalletId): AccountTreeWallet | undefined;
|
|
40
|
+
getAccountWallets(): AccountTreeWallet[];
|
|
41
|
+
/**
|
|
42
|
+
* Gets the currently selected account group ID.
|
|
43
|
+
*
|
|
44
|
+
* @returns The selected account group ID or empty string if none selected.
|
|
45
|
+
*/
|
|
46
|
+
getSelectedAccountGroup(): AccountGroupId | '';
|
|
47
|
+
/**
|
|
48
|
+
* Sets the selected account group and updates the AccountsController selectedAccount accordingly.
|
|
49
|
+
*
|
|
50
|
+
* @param groupId - The account group ID to select.
|
|
51
|
+
*/
|
|
52
|
+
setSelectedAccountGroup(groupId: AccountGroupId): void;
|
|
61
53
|
}
|
|
62
|
-
export {};
|
|
63
54
|
//# sourceMappingURL=AccountTreeController.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccountTreeController.d.mts","sourceRoot":"","sources":["../src/AccountTreeController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,8BAA8B;
|
|
1
|
+
{"version":3,"file":"AccountTreeController.d.mts","sourceRoot":"","sources":["../src/AccountTreeController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,8BAA8B;AAI7E,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAI3D,OAAO,EAAE,iBAAiB,EAAE,gCAA4B;AAIxD,OAAO,KAAK,EAEV,8BAA8B,EAC9B,0BAA0B,EAE3B,oBAAgB;AAEjB,eAAO,MAAM,cAAc,0BAA0B,CAAC;AAUtD;;;;GAIG;AACH,wBAAgB,oCAAoC,IAAI,0BAA0B,CAOjF;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B;;OAEG;IACH,QAAQ,EAAE,eAAe,CAAC;IAE1B;;OAEG;IACH,OAAO,EAAE,cAAc,CAAC;CACzB,CAAC;AAEF,qBAAa,qBAAsB,SAAQ,cAAc,CACvD,OAAO,cAAc,EACrB,0BAA0B,EAC1B,8BAA8B,CAC/B;;IAOC;;;;;;OAMG;gBACS,EACV,SAAS,EACT,KAAK,GACN,EAAE;QACD,SAAS,EAAE,8BAA8B,CAAC;QAC1C,KAAK,CAAC,EAAE,OAAO,CAAC,0BAA0B,CAAC,CAAC;KAC7C;IAqDD,IAAI;IA0EJ,gBAAgB,CAAC,QAAQ,EAAE,eAAe,GAAG,iBAAiB,GAAG,SAAS;IAS1E,iBAAiB,IAAI,iBAAiB,EAAE;IAsHxC;;;;OAIG;IACH,uBAAuB,IAAI,cAAc,GAAG,EAAE;IAI9C;;;;OAIG;IACH,uBAAuB,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;CAyGvD"}
|