@metamask-previews/account-tree-controller 0.6.0-preview-f476a4a9 → 0.6.0-preview-9fd73632
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 +0 -7
- package/dist/AccountTreeController.cjs +3 -106
- package/dist/AccountTreeController.cjs.map +1 -1
- package/dist/AccountTreeController.d.cts +4 -25
- package/dist/AccountTreeController.d.cts.map +1 -1
- package/dist/AccountTreeController.d.mts +4 -25
- package/dist/AccountTreeController.d.mts.map +1 -1
- package/dist/AccountTreeController.mjs +3 -106
- package/dist/AccountTreeController.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/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,13 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
-
### Added
|
|
11
|
-
|
|
12
|
-
- Add `selectedAccountGroup` state and bidirectional synchronization with AccountsController ([#6186](https://github.com/MetaMask/core/pull/6186))
|
|
13
|
-
- New `getSelectedAccountGroup()` and `setSelectedAccountGroup()` methods
|
|
14
|
-
- Automatic synchronization when selected account changes in AccountsController
|
|
15
|
-
- New action types `AccountTreeControllerGetSelectedAccountGroupAction` and `AccountTreeControllerSetSelectedAccountGroupAction`
|
|
16
|
-
|
|
17
10
|
## [0.6.0]
|
|
18
11
|
|
|
19
12
|
### Changed
|
|
@@ -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_reverse, _AccountTreeController_rules, _AccountTreeController_wallets,
|
|
13
|
+
var _AccountTreeController_instances, _AccountTreeController_reverse, _AccountTreeController_rules, _AccountTreeController_wallets, _AccountTreeController_handleAccountAdded, _AccountTreeController_handleAccountRemoved, _AccountTreeController_insert, _AccountTreeController_listAccounts;
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.AccountTreeController = exports.getDefaultAccountTreeControllerState = void 0;
|
|
16
16
|
const base_controller_1 = require("@metamask/base-controller");
|
|
@@ -21,10 +21,6 @@ const accountTreeControllerMetadata = {
|
|
|
21
21
|
persist: false,
|
|
22
22
|
anonymous: false,
|
|
23
23
|
},
|
|
24
|
-
selectedAccountGroup: {
|
|
25
|
-
persist: true,
|
|
26
|
-
anonymous: false,
|
|
27
|
-
},
|
|
28
24
|
};
|
|
29
25
|
/**
|
|
30
26
|
* Gets default state of the `AccountTreeController`.
|
|
@@ -36,7 +32,6 @@ function getDefaultAccountTreeControllerState() {
|
|
|
36
32
|
accountTree: {
|
|
37
33
|
wallets: {},
|
|
38
34
|
},
|
|
39
|
-
selectedAccountGroup: '',
|
|
40
35
|
};
|
|
41
36
|
}
|
|
42
37
|
exports.getDefaultAccountTreeControllerState = getDefaultAccountTreeControllerState;
|
|
@@ -80,10 +75,6 @@ class AccountTreeController extends base_controller_1.BaseController {
|
|
|
80
75
|
this.messagingSystem.subscribe('AccountsController:accountRemoved', (accountId) => {
|
|
81
76
|
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_handleAccountRemoved).call(this, accountId);
|
|
82
77
|
});
|
|
83
|
-
this.messagingSystem.subscribe('AccountsController:selectedAccountChange', (account) => {
|
|
84
|
-
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_handleSelectedAccountChange).call(this, account);
|
|
85
|
-
});
|
|
86
|
-
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_registerMessageHandlers).call(this);
|
|
87
78
|
}
|
|
88
79
|
init() {
|
|
89
80
|
const wallets = {};
|
|
@@ -93,11 +84,6 @@ class AccountTreeController extends base_controller_1.BaseController {
|
|
|
93
84
|
}
|
|
94
85
|
this.update((state) => {
|
|
95
86
|
state.accountTree.wallets = wallets;
|
|
96
|
-
if (state.selectedAccountGroup === '') {
|
|
97
|
-
// No group is selected yet, re-sync with the AccountsController.
|
|
98
|
-
state.selectedAccountGroup =
|
|
99
|
-
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getDefaultSelectedAccountGroup).call(this, wallets);
|
|
100
|
-
}
|
|
101
87
|
});
|
|
102
88
|
}
|
|
103
89
|
getWallet(id) {
|
|
@@ -106,52 +92,9 @@ class AccountTreeController extends base_controller_1.BaseController {
|
|
|
106
92
|
getWallets() {
|
|
107
93
|
return Array.from(__classPrivateFieldGet(this, _AccountTreeController_wallets, "f").values());
|
|
108
94
|
}
|
|
109
|
-
/**
|
|
110
|
-
* Gets the currently selected account group ID.
|
|
111
|
-
*
|
|
112
|
-
* @returns The selected account group ID or empty string if none selected.
|
|
113
|
-
*/
|
|
114
|
-
getSelectedAccountGroup() {
|
|
115
|
-
return this.state.selectedAccountGroup;
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Sets the selected account group and updates the AccountsController selectedAccount accordingly.
|
|
119
|
-
*
|
|
120
|
-
* @param accountGroupId - The account group ID to select.
|
|
121
|
-
*/
|
|
122
|
-
setSelectedAccountGroup(accountGroupId) {
|
|
123
|
-
const currentSelectedGroup = this.state.selectedAccountGroup;
|
|
124
|
-
// Idempotent check - if the same group is already selected, do nothing
|
|
125
|
-
if (currentSelectedGroup === accountGroupId) {
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
// Find the first account in this group to select
|
|
129
|
-
const accountToSelect = __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getFirstAccountInGroup).call(this, accountGroupId);
|
|
130
|
-
if (!accountToSelect) {
|
|
131
|
-
throw new Error(`No accounts found in group: ${accountGroupId}`);
|
|
132
|
-
}
|
|
133
|
-
// Update our state first
|
|
134
|
-
this.update((state) => {
|
|
135
|
-
state.selectedAccountGroup = accountGroupId;
|
|
136
|
-
});
|
|
137
|
-
// Update AccountsController - this will trigger selectedAccountChange event,
|
|
138
|
-
// but our handler is idempotent so it won't cause infinite loop
|
|
139
|
-
this.messagingSystem.call('AccountsController:setSelectedAccount', accountToSelect);
|
|
140
|
-
}
|
|
141
95
|
}
|
|
142
96
|
exports.AccountTreeController = AccountTreeController;
|
|
143
|
-
_AccountTreeController_reverse = new WeakMap(), _AccountTreeController_rules = new WeakMap(), _AccountTreeController_wallets = new WeakMap(), _AccountTreeController_instances = new WeakSet(),
|
|
144
|
-
const selectedAccount = this.messagingSystem.call('AccountsController:getSelectedAccount');
|
|
145
|
-
if (selectedAccount && selectedAccount.id) {
|
|
146
|
-
const accountMapping = __classPrivateFieldGet(this, _AccountTreeController_reverse, "f").get(selectedAccount.id);
|
|
147
|
-
if (accountMapping) {
|
|
148
|
-
const { groupId } = accountMapping;
|
|
149
|
-
return groupId;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
// Default to the first wallet, first group in case of errors.
|
|
153
|
-
return Object.values(Object.values(wallets)[0]?.groups ?? {})[0]?.id ?? '';
|
|
154
|
-
}, _AccountTreeController_handleAccountAdded = function _AccountTreeController_handleAccountAdded(account) {
|
|
97
|
+
_AccountTreeController_reverse = new WeakMap(), _AccountTreeController_rules = new WeakMap(), _AccountTreeController_wallets = new WeakMap(), _AccountTreeController_instances = new WeakSet(), _AccountTreeController_handleAccountAdded = function _AccountTreeController_handleAccountAdded(account) {
|
|
155
98
|
this.update((state) => {
|
|
156
99
|
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_insert).call(this, state.accountTree.wallets, account);
|
|
157
100
|
});
|
|
@@ -159,20 +102,12 @@ _AccountTreeController_reverse = new WeakMap(), _AccountTreeController_rules = n
|
|
|
159
102
|
const found = __classPrivateFieldGet(this, _AccountTreeController_reverse, "f").get(accountId);
|
|
160
103
|
if (found) {
|
|
161
104
|
const { walletId, groupId } = found;
|
|
162
|
-
// Clean up the reverse mapping to prevent memory leaks
|
|
163
|
-
__classPrivateFieldGet(this, _AccountTreeController_reverse, "f").delete(accountId);
|
|
164
105
|
this.update((state) => {
|
|
165
|
-
const
|
|
166
|
-
const { accounts } = group;
|
|
106
|
+
const { accounts } = state.accountTree.wallets[walletId].groups[groupId];
|
|
167
107
|
const index = accounts.indexOf(accountId);
|
|
168
108
|
if (index !== -1) {
|
|
169
109
|
accounts.splice(index, 1);
|
|
170
110
|
}
|
|
171
|
-
// Check if we need to update selectedAccountGroup
|
|
172
|
-
if (state.selectedAccountGroup === groupId && accounts.length === 0) {
|
|
173
|
-
// The currently selected group is now empty, find a new group to select
|
|
174
|
-
state.selectedAccountGroup = __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_findFirstNonEmptyGroup).call(this, state.accountTree.wallets);
|
|
175
|
-
}
|
|
176
111
|
});
|
|
177
112
|
}
|
|
178
113
|
}, _AccountTreeController_insert = function _AccountTreeController_insert(wallets, account) {
|
|
@@ -211,43 +146,5 @@ _AccountTreeController_reverse = new WeakMap(), _AccountTreeController_rules = n
|
|
|
211
146
|
}
|
|
212
147
|
}, _AccountTreeController_listAccounts = function _AccountTreeController_listAccounts() {
|
|
213
148
|
return this.messagingSystem.call('AccountsController:listMultichainAccounts');
|
|
214
|
-
}, _AccountTreeController_handleSelectedAccountChange = function _AccountTreeController_handleSelectedAccountChange(account) {
|
|
215
|
-
const accountMapping = __classPrivateFieldGet(this, _AccountTreeController_reverse, "f").get(account.id);
|
|
216
|
-
if (!accountMapping) {
|
|
217
|
-
// Account not in tree yet, might be during initialization
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
const { groupId } = accountMapping;
|
|
221
|
-
const currentSelectedGroup = this.state.selectedAccountGroup;
|
|
222
|
-
// Idempotent check - if the same group is already selected, do nothing
|
|
223
|
-
if (currentSelectedGroup === groupId) {
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
226
|
-
// Update selectedAccountGroup to match the selected account
|
|
227
|
-
this.update((state) => {
|
|
228
|
-
state.selectedAccountGroup = groupId;
|
|
229
|
-
});
|
|
230
|
-
}, _AccountTreeController_getFirstAccountInGroup = function _AccountTreeController_getFirstAccountInGroup(accountGroupId) {
|
|
231
|
-
for (const wallet of Object.values(this.state.accountTree.wallets)) {
|
|
232
|
-
if (Object.hasOwnProperty.call(wallet.groups, accountGroupId)) {
|
|
233
|
-
const group = wallet.groups[accountGroupId];
|
|
234
|
-
if (group && group.accounts.length > 0) {
|
|
235
|
-
return group.accounts[0];
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
return undefined;
|
|
240
|
-
}, _AccountTreeController_findFirstNonEmptyGroup = function _AccountTreeController_findFirstNonEmptyGroup(wallets) {
|
|
241
|
-
for (const wallet of Object.values(wallets)) {
|
|
242
|
-
for (const group of Object.values(wallet.groups)) {
|
|
243
|
-
if (group.accounts.length > 0) {
|
|
244
|
-
return group.id;
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
return '';
|
|
249
|
-
}, _AccountTreeController_registerMessageHandlers = function _AccountTreeController_registerMessageHandlers() {
|
|
250
|
-
this.messagingSystem.registerActionHandler(`${controllerName}:getSelectedAccountGroup`, this.getSelectedAccountGroup.bind(this));
|
|
251
|
-
this.messagingSystem.registerActionHandler(`${controllerName}:setSelectedAccountGroup`, this.setSelectedAccountGroup.bind(this));
|
|
252
149
|
};
|
|
253
150
|
//# 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 AccountsControllerGetSelectedAccountAction,\n AccountsControllerListMultichainAccountsAction,\n AccountsControllerSelectedAccountChangeEvent,\n AccountsControllerSetSelectedAccountAction,\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 selectedAccountGroup: AccountGroupId | '';\n};\n\nexport type AccountTreeControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n AccountTreeControllerState\n>;\n\nexport type AccountTreeControllerSetSelectedAccountGroupAction = {\n type: `${typeof controllerName}:setSelectedAccountGroup`;\n handler: AccountTreeController['setSelectedAccountGroup'];\n};\n\nexport type AccountTreeControllerGetSelectedAccountGroupAction = {\n type: `${typeof controllerName}:getSelectedAccountGroup`;\n handler: AccountTreeController['getSelectedAccountGroup'];\n};\n\nexport type AllowedActions =\n | AccountsControllerGetAccountAction\n | AccountsControllerGetSelectedAccountAction\n | AccountsControllerListMultichainAccountsAction\n | AccountsControllerSetSelectedAccountAction\n | KeyringControllerGetStateAction\n | SnapControllerGetSnap;\n\nexport type AccountTreeControllerActions =\n | AccountTreeControllerGetStateAction\n | AccountTreeControllerSetSelectedAccountGroupAction\n | AccountTreeControllerGetSelectedAccountGroupAction;\n\nexport type AccountTreeControllerStateChangeEvent = ControllerStateChangeEvent<\n typeof controllerName,\n AccountTreeControllerState\n>;\n\nexport type AllowedEvents =\n | AccountsControllerAccountAddedEvent\n | AccountsControllerAccountRemovedEvent\n | AccountsControllerSelectedAccountChangeEvent;\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 selectedAccountGroup: {\n persist: true,\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 selectedAccountGroup: '',\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 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: { [walletId: AccountWalletId]: AccountWalletObject } = {};\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 if (state.selectedAccountGroup === '') {\n // No group is selected yet, re-sync with the AccountsController.\n state.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.#reverse.get(selectedAccount.id);\n if (accountMapping) {\n const { groupId } = accountMapping;\n\n return groupId;\n }\n }\n\n // Default to the first wallet, first group in case of errors.\n return Object.values(Object.values(wallets)[0]?.groups ?? {})[0]?.id ?? '';\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\n // Clean up the reverse mapping to prevent memory leaks\n this.#reverse.delete(accountId);\n\n this.update((state) => {\n const group = state.accountTree.wallets[walletId].groups[groupId];\n const { accounts } = group;\n\n const index = accounts.indexOf(accountId);\n if (index !== -1) {\n accounts.splice(index, 1);\n }\n\n // Check if we need to update selectedAccountGroup\n if (state.selectedAccountGroup === groupId && accounts.length === 0) {\n // The currently selected group is now empty, find a new group to select\n state.selectedAccountGroup = this.#findFirstNonEmptyGroup(\n state.accountTree.wallets,\n );\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 /**\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.selectedAccountGroup;\n }\n\n /**\n * Sets the selected account group and updates the AccountsController selectedAccount accordingly.\n *\n * @param accountGroupId - The account group ID to select.\n */\n setSelectedAccountGroup(accountGroupId: AccountGroupId): void {\n const currentSelectedGroup = this.state.selectedAccountGroup;\n\n // Idempotent check - if the same group is already selected, do nothing\n if (currentSelectedGroup === accountGroupId) {\n return;\n }\n\n // Find the first account in this group to select\n const accountToSelect = this.#getFirstAccountInGroup(accountGroupId);\n if (!accountToSelect) {\n throw new Error(`No accounts found in group: ${accountGroupId}`);\n }\n\n // Update our state first\n this.update((state) => {\n state.selectedAccountGroup = accountGroupId;\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.#reverse.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.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.selectedAccountGroup = groupId;\n });\n }\n\n /**\n * Gets the first account ID in the specified group.\n *\n * @param accountGroupId - The account group ID.\n * @returns The first account ID in the group, or undefined if no accounts found.\n */\n #getFirstAccountInGroup(\n accountGroupId: AccountGroupId,\n ): AccountId | undefined {\n for (const wallet of Object.values(\n this.state.accountTree.wallets,\n ) as AccountWalletObject[]) {\n if (Object.hasOwnProperty.call(wallet.groups, accountGroupId)) {\n const group = (wallet.groups as Record<string, AccountGroupObject>)[\n accountGroupId\n ];\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
|
+
{"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,5 +1,5 @@
|
|
|
1
1
|
import type { AccountGroupId, AccountWalletId } from "@metamask/account-api";
|
|
2
|
-
import type { AccountId, AccountsControllerAccountAddedEvent, AccountsControllerAccountRemovedEvent, AccountsControllerGetAccountAction,
|
|
2
|
+
import type { AccountId, AccountsControllerAccountAddedEvent, AccountsControllerAccountRemovedEvent, AccountsControllerGetAccountAction, AccountsControllerListMultichainAccountsAction } from "@metamask/accounts-controller";
|
|
3
3
|
import { type ControllerGetStateAction, type ControllerStateChangeEvent, type RestrictedMessenger, BaseController } from "@metamask/base-controller";
|
|
4
4
|
import type { KeyringControllerGetStateAction } from "@metamask/keyring-controller";
|
|
5
5
|
import type { GetSnap as SnapControllerGetSnap } from "@metamask/snaps-controllers";
|
|
@@ -28,21 +28,12 @@ export type AccountTreeControllerState = {
|
|
|
28
28
|
[walletId: AccountWalletId]: AccountWalletObject;
|
|
29
29
|
};
|
|
30
30
|
};
|
|
31
|
-
selectedAccountGroup: AccountGroupId | '';
|
|
32
31
|
};
|
|
33
32
|
export type AccountTreeControllerGetStateAction = ControllerGetStateAction<typeof controllerName, AccountTreeControllerState>;
|
|
34
|
-
export type
|
|
35
|
-
|
|
36
|
-
handler: AccountTreeController['setSelectedAccountGroup'];
|
|
37
|
-
};
|
|
38
|
-
export type AccountTreeControllerGetSelectedAccountGroupAction = {
|
|
39
|
-
type: `${typeof controllerName}:getSelectedAccountGroup`;
|
|
40
|
-
handler: AccountTreeController['getSelectedAccountGroup'];
|
|
41
|
-
};
|
|
42
|
-
export type AllowedActions = AccountsControllerGetAccountAction | AccountsControllerGetSelectedAccountAction | AccountsControllerListMultichainAccountsAction | AccountsControllerSetSelectedAccountAction | KeyringControllerGetStateAction | SnapControllerGetSnap;
|
|
43
|
-
export type AccountTreeControllerActions = AccountTreeControllerGetStateAction | AccountTreeControllerSetSelectedAccountGroupAction | AccountTreeControllerGetSelectedAccountGroupAction;
|
|
33
|
+
export type AllowedActions = AccountsControllerGetAccountAction | AccountsControllerListMultichainAccountsAction | KeyringControllerGetStateAction | SnapControllerGetSnap;
|
|
34
|
+
export type AccountTreeControllerActions = never;
|
|
44
35
|
export type AccountTreeControllerStateChangeEvent = ControllerStateChangeEvent<typeof controllerName, AccountTreeControllerState>;
|
|
45
|
-
export type AllowedEvents = AccountsControllerAccountAddedEvent | AccountsControllerAccountRemovedEvent
|
|
36
|
+
export type AllowedEvents = AccountsControllerAccountAddedEvent | AccountsControllerAccountRemovedEvent;
|
|
46
37
|
export type AccountTreeControllerEvents = AccountTreeControllerStateChangeEvent;
|
|
47
38
|
export type AccountTreeControllerMessenger = RestrictedMessenger<typeof controllerName, AccountTreeControllerActions | AllowedActions, AccountTreeControllerEvents | AllowedEvents, AllowedActions['type'], AllowedEvents['type']>;
|
|
48
39
|
/**
|
|
@@ -67,18 +58,6 @@ export declare class AccountTreeController extends BaseController<typeof control
|
|
|
67
58
|
init(): void;
|
|
68
59
|
getWallet(id: AccountWalletId): AccountTreeWallet | undefined;
|
|
69
60
|
getWallets(): AccountTreeWallet[];
|
|
70
|
-
/**
|
|
71
|
-
* Gets the currently selected account group ID.
|
|
72
|
-
*
|
|
73
|
-
* @returns The selected account group ID or empty string if none selected.
|
|
74
|
-
*/
|
|
75
|
-
getSelectedAccountGroup(): AccountGroupId | '';
|
|
76
|
-
/**
|
|
77
|
-
* Sets the selected account group and updates the AccountsController selectedAccount accordingly.
|
|
78
|
-
*
|
|
79
|
-
* @param accountGroupId - The account group ID to select.
|
|
80
|
-
*/
|
|
81
|
-
setSelectedAccountGroup(accountGroupId: AccountGroupId): void;
|
|
82
61
|
}
|
|
83
62
|
export {};
|
|
84
63
|
//# 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;AAC7E,OAAO,KAAK,EACV,SAAS,EACT,mCAAmC,EACnC,qCAAqC,EACrC,kCAAkC,EAClC,
|
|
1
|
+
{"version":3,"file":"AccountTreeController.d.cts","sourceRoot":"","sources":["../src/AccountTreeController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,8BAA8B;AAC7E,OAAO,KAAK,EACV,SAAS,EACT,mCAAmC,EACnC,qCAAqC,EACrC,kCAAkC,EAClC,8CAA8C,EAC/C,sCAAsC;AAEvC,OAAO,EACL,KAAK,wBAAwB,EAC7B,KAAK,0BAA0B,EAC/B,KAAK,mBAAmB,EACxB,cAAc,EACf,kCAAkC;AACnC,OAAO,KAAK,EAAE,+BAA+B,EAAE,qCAAqC;AAEpF,OAAO,KAAK,EAAE,OAAO,IAAI,qBAAqB,EAAE,oCAAoC;AAEpF,OAAO,KAAK,EAAE,iBAAiB,EAAE,gCAA4B;AAQ7D,QAAA,MAAM,cAAc,0BAA0B,CAAC;AAQ/C,KAAK,QAAQ,GAAG;IACd,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC;AAE7C,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;AAE5C,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,EAAE,cAAc,CAAC;IAEnB,QAAQ,EAAE,SAAS,EAAE,CAAC;IACtB,QAAQ,EAAE,oBAAoB,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,eAAe,CAAC;IAEpB,MAAM,EAAE;QACN,CAAC,OAAO,EAAE,cAAc,GAAG,kBAAkB,CAAC;KAC/C,CAAC;IACF,QAAQ,EAAE,qBAAqB,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,WAAW,EAAE;QACX,OAAO,EAAE;YAEP,CAAC,QAAQ,EAAE,eAAe,GAAG,mBAAmB,CAAC;SAClD,CAAC;KACH,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,mCAAmC,GAAG,wBAAwB,CACxE,OAAO,cAAc,EACrB,0BAA0B,CAC3B,CAAC;AAEF,MAAM,MAAM,cAAc,GACtB,kCAAkC,GAClC,8CAA8C,GAC9C,+BAA+B,GAC/B,qBAAqB,CAAC;AAE1B,MAAM,MAAM,4BAA4B,GAAG,KAAK,CAAC;AAEjD,MAAM,MAAM,qCAAqC,GAAG,0BAA0B,CAC5E,OAAO,cAAc,EACrB,0BAA0B,CAC3B,CAAC;AAEF,MAAM,MAAM,aAAa,GACrB,mCAAmC,GACnC,qCAAqC,CAAC;AAE1C,MAAM,MAAM,2BAA2B,GAAG,qCAAqC,CAAC;AAEhF,MAAM,MAAM,8BAA8B,GAAG,mBAAmB,CAC9D,OAAO,cAAc,EACrB,4BAA4B,GAAG,cAAc,EAC7C,2BAA2B,GAAG,aAAa,EAC3C,cAAc,CAAC,MAAM,CAAC,EACtB,aAAa,CAAC,MAAM,CAAC,CACtB,CAAC;AAUF;;;;GAIG;AACH,wBAAgB,oCAAoC,IAAI,0BAA0B,CAMjF;AAED,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;IAwCD,IAAI;IAaJ,SAAS,CAAC,EAAE,EAAE,eAAe,GAAG,iBAAiB,GAAG,SAAS;IAI7D,UAAU,IAAI,iBAAiB,EAAE;CA6ElC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AccountGroupId, AccountWalletId } from "@metamask/account-api";
|
|
2
|
-
import type { AccountId, AccountsControllerAccountAddedEvent, AccountsControllerAccountRemovedEvent, AccountsControllerGetAccountAction,
|
|
2
|
+
import type { AccountId, AccountsControllerAccountAddedEvent, AccountsControllerAccountRemovedEvent, AccountsControllerGetAccountAction, AccountsControllerListMultichainAccountsAction } from "@metamask/accounts-controller";
|
|
3
3
|
import { type ControllerGetStateAction, type ControllerStateChangeEvent, type RestrictedMessenger, BaseController } from "@metamask/base-controller";
|
|
4
4
|
import type { KeyringControllerGetStateAction } from "@metamask/keyring-controller";
|
|
5
5
|
import type { GetSnap as SnapControllerGetSnap } from "@metamask/snaps-controllers";
|
|
@@ -28,21 +28,12 @@ export type AccountTreeControllerState = {
|
|
|
28
28
|
[walletId: AccountWalletId]: AccountWalletObject;
|
|
29
29
|
};
|
|
30
30
|
};
|
|
31
|
-
selectedAccountGroup: AccountGroupId | '';
|
|
32
31
|
};
|
|
33
32
|
export type AccountTreeControllerGetStateAction = ControllerGetStateAction<typeof controllerName, AccountTreeControllerState>;
|
|
34
|
-
export type
|
|
35
|
-
|
|
36
|
-
handler: AccountTreeController['setSelectedAccountGroup'];
|
|
37
|
-
};
|
|
38
|
-
export type AccountTreeControllerGetSelectedAccountGroupAction = {
|
|
39
|
-
type: `${typeof controllerName}:getSelectedAccountGroup`;
|
|
40
|
-
handler: AccountTreeController['getSelectedAccountGroup'];
|
|
41
|
-
};
|
|
42
|
-
export type AllowedActions = AccountsControllerGetAccountAction | AccountsControllerGetSelectedAccountAction | AccountsControllerListMultichainAccountsAction | AccountsControllerSetSelectedAccountAction | KeyringControllerGetStateAction | SnapControllerGetSnap;
|
|
43
|
-
export type AccountTreeControllerActions = AccountTreeControllerGetStateAction | AccountTreeControllerSetSelectedAccountGroupAction | AccountTreeControllerGetSelectedAccountGroupAction;
|
|
33
|
+
export type AllowedActions = AccountsControllerGetAccountAction | AccountsControllerListMultichainAccountsAction | KeyringControllerGetStateAction | SnapControllerGetSnap;
|
|
34
|
+
export type AccountTreeControllerActions = never;
|
|
44
35
|
export type AccountTreeControllerStateChangeEvent = ControllerStateChangeEvent<typeof controllerName, AccountTreeControllerState>;
|
|
45
|
-
export type AllowedEvents = AccountsControllerAccountAddedEvent | AccountsControllerAccountRemovedEvent
|
|
36
|
+
export type AllowedEvents = AccountsControllerAccountAddedEvent | AccountsControllerAccountRemovedEvent;
|
|
46
37
|
export type AccountTreeControllerEvents = AccountTreeControllerStateChangeEvent;
|
|
47
38
|
export type AccountTreeControllerMessenger = RestrictedMessenger<typeof controllerName, AccountTreeControllerActions | AllowedActions, AccountTreeControllerEvents | AllowedEvents, AllowedActions['type'], AllowedEvents['type']>;
|
|
48
39
|
/**
|
|
@@ -67,18 +58,6 @@ export declare class AccountTreeController extends BaseController<typeof control
|
|
|
67
58
|
init(): void;
|
|
68
59
|
getWallet(id: AccountWalletId): AccountTreeWallet | undefined;
|
|
69
60
|
getWallets(): AccountTreeWallet[];
|
|
70
|
-
/**
|
|
71
|
-
* Gets the currently selected account group ID.
|
|
72
|
-
*
|
|
73
|
-
* @returns The selected account group ID or empty string if none selected.
|
|
74
|
-
*/
|
|
75
|
-
getSelectedAccountGroup(): AccountGroupId | '';
|
|
76
|
-
/**
|
|
77
|
-
* Sets the selected account group and updates the AccountsController selectedAccount accordingly.
|
|
78
|
-
*
|
|
79
|
-
* @param accountGroupId - The account group ID to select.
|
|
80
|
-
*/
|
|
81
|
-
setSelectedAccountGroup(accountGroupId: AccountGroupId): void;
|
|
82
61
|
}
|
|
83
62
|
export {};
|
|
84
63
|
//# 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;AAC7E,OAAO,KAAK,EACV,SAAS,EACT,mCAAmC,EACnC,qCAAqC,EACrC,kCAAkC,EAClC,
|
|
1
|
+
{"version":3,"file":"AccountTreeController.d.mts","sourceRoot":"","sources":["../src/AccountTreeController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,8BAA8B;AAC7E,OAAO,KAAK,EACV,SAAS,EACT,mCAAmC,EACnC,qCAAqC,EACrC,kCAAkC,EAClC,8CAA8C,EAC/C,sCAAsC;AAEvC,OAAO,EACL,KAAK,wBAAwB,EAC7B,KAAK,0BAA0B,EAC/B,KAAK,mBAAmB,EACxB,cAAc,EACf,kCAAkC;AACnC,OAAO,KAAK,EAAE,+BAA+B,EAAE,qCAAqC;AAEpF,OAAO,KAAK,EAAE,OAAO,IAAI,qBAAqB,EAAE,oCAAoC;AAEpF,OAAO,KAAK,EAAE,iBAAiB,EAAE,gCAA4B;AAQ7D,QAAA,MAAM,cAAc,0BAA0B,CAAC;AAQ/C,KAAK,QAAQ,GAAG;IACd,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC;AAE7C,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;AAE5C,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,EAAE,cAAc,CAAC;IAEnB,QAAQ,EAAE,SAAS,EAAE,CAAC;IACtB,QAAQ,EAAE,oBAAoB,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,eAAe,CAAC;IAEpB,MAAM,EAAE;QACN,CAAC,OAAO,EAAE,cAAc,GAAG,kBAAkB,CAAC;KAC/C,CAAC;IACF,QAAQ,EAAE,qBAAqB,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,WAAW,EAAE;QACX,OAAO,EAAE;YAEP,CAAC,QAAQ,EAAE,eAAe,GAAG,mBAAmB,CAAC;SAClD,CAAC;KACH,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,mCAAmC,GAAG,wBAAwB,CACxE,OAAO,cAAc,EACrB,0BAA0B,CAC3B,CAAC;AAEF,MAAM,MAAM,cAAc,GACtB,kCAAkC,GAClC,8CAA8C,GAC9C,+BAA+B,GAC/B,qBAAqB,CAAC;AAE1B,MAAM,MAAM,4BAA4B,GAAG,KAAK,CAAC;AAEjD,MAAM,MAAM,qCAAqC,GAAG,0BAA0B,CAC5E,OAAO,cAAc,EACrB,0BAA0B,CAC3B,CAAC;AAEF,MAAM,MAAM,aAAa,GACrB,mCAAmC,GACnC,qCAAqC,CAAC;AAE1C,MAAM,MAAM,2BAA2B,GAAG,qCAAqC,CAAC;AAEhF,MAAM,MAAM,8BAA8B,GAAG,mBAAmB,CAC9D,OAAO,cAAc,EACrB,4BAA4B,GAAG,cAAc,EAC7C,2BAA2B,GAAG,aAAa,EAC3C,cAAc,CAAC,MAAM,CAAC,EACtB,aAAa,CAAC,MAAM,CAAC,CACtB,CAAC;AAUF;;;;GAIG;AACH,wBAAgB,oCAAoC,IAAI,0BAA0B,CAMjF;AAED,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;IAwCD,IAAI;IAaJ,SAAS,CAAC,EAAE,EAAE,eAAe,GAAG,iBAAiB,GAAG,SAAS;IAI7D,UAAU,IAAI,iBAAiB,EAAE;CA6ElC"}
|
|
@@ -9,7 +9,7 @@ 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_reverse, _AccountTreeController_rules, _AccountTreeController_wallets,
|
|
12
|
+
var _AccountTreeController_instances, _AccountTreeController_reverse, _AccountTreeController_rules, _AccountTreeController_wallets, _AccountTreeController_handleAccountAdded, _AccountTreeController_handleAccountRemoved, _AccountTreeController_insert, _AccountTreeController_listAccounts;
|
|
13
13
|
import { BaseController } from "@metamask/base-controller";
|
|
14
14
|
import { EntropySourceWalletRule, SnapWalletRule, KeyringWalletRule } from "./rules/index.mjs";
|
|
15
15
|
const controllerName = 'AccountTreeController';
|
|
@@ -18,10 +18,6 @@ const accountTreeControllerMetadata = {
|
|
|
18
18
|
persist: false,
|
|
19
19
|
anonymous: false,
|
|
20
20
|
},
|
|
21
|
-
selectedAccountGroup: {
|
|
22
|
-
persist: true,
|
|
23
|
-
anonymous: false,
|
|
24
|
-
},
|
|
25
21
|
};
|
|
26
22
|
/**
|
|
27
23
|
* Gets default state of the `AccountTreeController`.
|
|
@@ -33,7 +29,6 @@ export function getDefaultAccountTreeControllerState() {
|
|
|
33
29
|
accountTree: {
|
|
34
30
|
wallets: {},
|
|
35
31
|
},
|
|
36
|
-
selectedAccountGroup: '',
|
|
37
32
|
};
|
|
38
33
|
}
|
|
39
34
|
export class AccountTreeController extends BaseController {
|
|
@@ -76,10 +71,6 @@ export class AccountTreeController extends BaseController {
|
|
|
76
71
|
this.messagingSystem.subscribe('AccountsController:accountRemoved', (accountId) => {
|
|
77
72
|
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_handleAccountRemoved).call(this, accountId);
|
|
78
73
|
});
|
|
79
|
-
this.messagingSystem.subscribe('AccountsController:selectedAccountChange', (account) => {
|
|
80
|
-
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_handleSelectedAccountChange).call(this, account);
|
|
81
|
-
});
|
|
82
|
-
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_registerMessageHandlers).call(this);
|
|
83
74
|
}
|
|
84
75
|
init() {
|
|
85
76
|
const wallets = {};
|
|
@@ -89,11 +80,6 @@ export class AccountTreeController extends BaseController {
|
|
|
89
80
|
}
|
|
90
81
|
this.update((state) => {
|
|
91
82
|
state.accountTree.wallets = wallets;
|
|
92
|
-
if (state.selectedAccountGroup === '') {
|
|
93
|
-
// No group is selected yet, re-sync with the AccountsController.
|
|
94
|
-
state.selectedAccountGroup =
|
|
95
|
-
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getDefaultSelectedAccountGroup).call(this, wallets);
|
|
96
|
-
}
|
|
97
83
|
});
|
|
98
84
|
}
|
|
99
85
|
getWallet(id) {
|
|
@@ -102,51 +88,8 @@ export class AccountTreeController extends BaseController {
|
|
|
102
88
|
getWallets() {
|
|
103
89
|
return Array.from(__classPrivateFieldGet(this, _AccountTreeController_wallets, "f").values());
|
|
104
90
|
}
|
|
105
|
-
/**
|
|
106
|
-
* Gets the currently selected account group ID.
|
|
107
|
-
*
|
|
108
|
-
* @returns The selected account group ID or empty string if none selected.
|
|
109
|
-
*/
|
|
110
|
-
getSelectedAccountGroup() {
|
|
111
|
-
return this.state.selectedAccountGroup;
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Sets the selected account group and updates the AccountsController selectedAccount accordingly.
|
|
115
|
-
*
|
|
116
|
-
* @param accountGroupId - The account group ID to select.
|
|
117
|
-
*/
|
|
118
|
-
setSelectedAccountGroup(accountGroupId) {
|
|
119
|
-
const currentSelectedGroup = this.state.selectedAccountGroup;
|
|
120
|
-
// Idempotent check - if the same group is already selected, do nothing
|
|
121
|
-
if (currentSelectedGroup === accountGroupId) {
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
// Find the first account in this group to select
|
|
125
|
-
const accountToSelect = __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getFirstAccountInGroup).call(this, accountGroupId);
|
|
126
|
-
if (!accountToSelect) {
|
|
127
|
-
throw new Error(`No accounts found in group: ${accountGroupId}`);
|
|
128
|
-
}
|
|
129
|
-
// Update our state first
|
|
130
|
-
this.update((state) => {
|
|
131
|
-
state.selectedAccountGroup = accountGroupId;
|
|
132
|
-
});
|
|
133
|
-
// Update AccountsController - this will trigger selectedAccountChange event,
|
|
134
|
-
// but our handler is idempotent so it won't cause infinite loop
|
|
135
|
-
this.messagingSystem.call('AccountsController:setSelectedAccount', accountToSelect);
|
|
136
|
-
}
|
|
137
91
|
}
|
|
138
|
-
_AccountTreeController_reverse = new WeakMap(), _AccountTreeController_rules = new WeakMap(), _AccountTreeController_wallets = new WeakMap(), _AccountTreeController_instances = new WeakSet(),
|
|
139
|
-
const selectedAccount = this.messagingSystem.call('AccountsController:getSelectedAccount');
|
|
140
|
-
if (selectedAccount && selectedAccount.id) {
|
|
141
|
-
const accountMapping = __classPrivateFieldGet(this, _AccountTreeController_reverse, "f").get(selectedAccount.id);
|
|
142
|
-
if (accountMapping) {
|
|
143
|
-
const { groupId } = accountMapping;
|
|
144
|
-
return groupId;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
// Default to the first wallet, first group in case of errors.
|
|
148
|
-
return Object.values(Object.values(wallets)[0]?.groups ?? {})[0]?.id ?? '';
|
|
149
|
-
}, _AccountTreeController_handleAccountAdded = function _AccountTreeController_handleAccountAdded(account) {
|
|
92
|
+
_AccountTreeController_reverse = new WeakMap(), _AccountTreeController_rules = new WeakMap(), _AccountTreeController_wallets = new WeakMap(), _AccountTreeController_instances = new WeakSet(), _AccountTreeController_handleAccountAdded = function _AccountTreeController_handleAccountAdded(account) {
|
|
150
93
|
this.update((state) => {
|
|
151
94
|
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_insert).call(this, state.accountTree.wallets, account);
|
|
152
95
|
});
|
|
@@ -154,20 +97,12 @@ _AccountTreeController_reverse = new WeakMap(), _AccountTreeController_rules = n
|
|
|
154
97
|
const found = __classPrivateFieldGet(this, _AccountTreeController_reverse, "f").get(accountId);
|
|
155
98
|
if (found) {
|
|
156
99
|
const { walletId, groupId } = found;
|
|
157
|
-
// Clean up the reverse mapping to prevent memory leaks
|
|
158
|
-
__classPrivateFieldGet(this, _AccountTreeController_reverse, "f").delete(accountId);
|
|
159
100
|
this.update((state) => {
|
|
160
|
-
const
|
|
161
|
-
const { accounts } = group;
|
|
101
|
+
const { accounts } = state.accountTree.wallets[walletId].groups[groupId];
|
|
162
102
|
const index = accounts.indexOf(accountId);
|
|
163
103
|
if (index !== -1) {
|
|
164
104
|
accounts.splice(index, 1);
|
|
165
105
|
}
|
|
166
|
-
// Check if we need to update selectedAccountGroup
|
|
167
|
-
if (state.selectedAccountGroup === groupId && accounts.length === 0) {
|
|
168
|
-
// The currently selected group is now empty, find a new group to select
|
|
169
|
-
state.selectedAccountGroup = __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_findFirstNonEmptyGroup).call(this, state.accountTree.wallets);
|
|
170
|
-
}
|
|
171
106
|
});
|
|
172
107
|
}
|
|
173
108
|
}, _AccountTreeController_insert = function _AccountTreeController_insert(wallets, account) {
|
|
@@ -206,43 +141,5 @@ _AccountTreeController_reverse = new WeakMap(), _AccountTreeController_rules = n
|
|
|
206
141
|
}
|
|
207
142
|
}, _AccountTreeController_listAccounts = function _AccountTreeController_listAccounts() {
|
|
208
143
|
return this.messagingSystem.call('AccountsController:listMultichainAccounts');
|
|
209
|
-
}, _AccountTreeController_handleSelectedAccountChange = function _AccountTreeController_handleSelectedAccountChange(account) {
|
|
210
|
-
const accountMapping = __classPrivateFieldGet(this, _AccountTreeController_reverse, "f").get(account.id);
|
|
211
|
-
if (!accountMapping) {
|
|
212
|
-
// Account not in tree yet, might be during initialization
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
const { groupId } = accountMapping;
|
|
216
|
-
const currentSelectedGroup = this.state.selectedAccountGroup;
|
|
217
|
-
// Idempotent check - if the same group is already selected, do nothing
|
|
218
|
-
if (currentSelectedGroup === groupId) {
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
221
|
-
// Update selectedAccountGroup to match the selected account
|
|
222
|
-
this.update((state) => {
|
|
223
|
-
state.selectedAccountGroup = groupId;
|
|
224
|
-
});
|
|
225
|
-
}, _AccountTreeController_getFirstAccountInGroup = function _AccountTreeController_getFirstAccountInGroup(accountGroupId) {
|
|
226
|
-
for (const wallet of Object.values(this.state.accountTree.wallets)) {
|
|
227
|
-
if (Object.hasOwnProperty.call(wallet.groups, accountGroupId)) {
|
|
228
|
-
const group = wallet.groups[accountGroupId];
|
|
229
|
-
if (group && group.accounts.length > 0) {
|
|
230
|
-
return group.accounts[0];
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
return undefined;
|
|
235
|
-
}, _AccountTreeController_findFirstNonEmptyGroup = function _AccountTreeController_findFirstNonEmptyGroup(wallets) {
|
|
236
|
-
for (const wallet of Object.values(wallets)) {
|
|
237
|
-
for (const group of Object.values(wallet.groups)) {
|
|
238
|
-
if (group.accounts.length > 0) {
|
|
239
|
-
return group.id;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
return '';
|
|
244
|
-
}, _AccountTreeController_registerMessageHandlers = function _AccountTreeController_registerMessageHandlers() {
|
|
245
|
-
this.messagingSystem.registerActionHandler(`${controllerName}:getSelectedAccountGroup`, this.getSelectedAccountGroup.bind(this));
|
|
246
|
-
this.messagingSystem.registerActionHandler(`${controllerName}:setSelectedAccountGroup`, this.setSelectedAccountGroup.bind(this));
|
|
247
144
|
};
|
|
248
145
|
//# sourceMappingURL=AccountTreeController.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccountTreeController.mjs","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 AccountsControllerGetSelectedAccountAction,\n AccountsControllerListMultichainAccountsAction,\n AccountsControllerSelectedAccountChangeEvent,\n AccountsControllerSetSelectedAccountAction,\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 selectedAccountGroup: AccountGroupId | '';\n};\n\nexport type AccountTreeControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n AccountTreeControllerState\n>;\n\nexport type AccountTreeControllerSetSelectedAccountGroupAction = {\n type: `${typeof controllerName}:setSelectedAccountGroup`;\n handler: AccountTreeController['setSelectedAccountGroup'];\n};\n\nexport type AccountTreeControllerGetSelectedAccountGroupAction = {\n type: `${typeof controllerName}:getSelectedAccountGroup`;\n handler: AccountTreeController['getSelectedAccountGroup'];\n};\n\nexport type AllowedActions =\n | AccountsControllerGetAccountAction\n | AccountsControllerGetSelectedAccountAction\n | AccountsControllerListMultichainAccountsAction\n | AccountsControllerSetSelectedAccountAction\n | KeyringControllerGetStateAction\n | SnapControllerGetSnap;\n\nexport type AccountTreeControllerActions =\n | AccountTreeControllerGetStateAction\n | AccountTreeControllerSetSelectedAccountGroupAction\n | AccountTreeControllerGetSelectedAccountGroupAction;\n\nexport type AccountTreeControllerStateChangeEvent = ControllerStateChangeEvent<\n typeof controllerName,\n AccountTreeControllerState\n>;\n\nexport type AllowedEvents =\n | AccountsControllerAccountAddedEvent\n | AccountsControllerAccountRemovedEvent\n | AccountsControllerSelectedAccountChangeEvent;\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 selectedAccountGroup: {\n persist: true,\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 selectedAccountGroup: '',\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 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: { [walletId: AccountWalletId]: AccountWalletObject } = {};\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 if (state.selectedAccountGroup === '') {\n // No group is selected yet, re-sync with the AccountsController.\n state.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.#reverse.get(selectedAccount.id);\n if (accountMapping) {\n const { groupId } = accountMapping;\n\n return groupId;\n }\n }\n\n // Default to the first wallet, first group in case of errors.\n return Object.values(Object.values(wallets)[0]?.groups ?? {})[0]?.id ?? '';\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\n // Clean up the reverse mapping to prevent memory leaks\n this.#reverse.delete(accountId);\n\n this.update((state) => {\n const group = state.accountTree.wallets[walletId].groups[groupId];\n const { accounts } = group;\n\n const index = accounts.indexOf(accountId);\n if (index !== -1) {\n accounts.splice(index, 1);\n }\n\n // Check if we need to update selectedAccountGroup\n if (state.selectedAccountGroup === groupId && accounts.length === 0) {\n // The currently selected group is now empty, find a new group to select\n state.selectedAccountGroup = this.#findFirstNonEmptyGroup(\n state.accountTree.wallets,\n );\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 /**\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.selectedAccountGroup;\n }\n\n /**\n * Sets the selected account group and updates the AccountsController selectedAccount accordingly.\n *\n * @param accountGroupId - The account group ID to select.\n */\n setSelectedAccountGroup(accountGroupId: AccountGroupId): void {\n const currentSelectedGroup = this.state.selectedAccountGroup;\n\n // Idempotent check - if the same group is already selected, do nothing\n if (currentSelectedGroup === accountGroupId) {\n return;\n }\n\n // Find the first account in this group to select\n const accountToSelect = this.#getFirstAccountInGroup(accountGroupId);\n if (!accountToSelect) {\n throw new Error(`No accounts found in group: ${accountGroupId}`);\n }\n\n // Update our state first\n this.update((state) => {\n state.selectedAccountGroup = accountGroupId;\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.#reverse.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.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.selectedAccountGroup = groupId;\n });\n }\n\n /**\n * Gets the first account ID in the specified group.\n *\n * @param accountGroupId - The account group ID.\n * @returns The first account ID in the group, or undefined if no accounts found.\n */\n #getFirstAccountInGroup(\n accountGroupId: AccountGroupId,\n ): AccountId | undefined {\n for (const wallet of Object.values(\n this.state.accountTree.wallets,\n ) as AccountWalletObject[]) {\n if (Object.hasOwnProperty.call(wallet.groups, accountGroupId)) {\n const group = (wallet.groups as Record<string, AccountGroupObject>)[\n accountGroupId\n ];\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
|
+
{"version":3,"file":"AccountTreeController.mjs","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"]}
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"index.cjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAYA,qEAGiC;AAF/B,8HAAA,qBAAqB,OAAA;AACrB,6IAAA,oCAAoC,OAAA","sourcesContent":["export type {\n AccountTreeControllerState,\n AccountTreeControllerGetStateAction,\n AccountTreeControllerActions,\n AccountTreeControllerStateChangeEvent,\n AccountTreeControllerEvents,\n AccountTreeControllerMessenger,\n AccountWalletObject,\n AccountWalletMetadata,\n AccountGroupObject,\n AccountGroupMetadata,\n} from './AccountTreeController';\nexport {\n AccountTreeController,\n getDefaultAccountTreeControllerState,\n} from './AccountTreeController';\nexport type { AccountTreeWallet } from './AccountTreeWallet';\nexport type { AccountTreeGroup } from './AccountTreeGroup';\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { AccountTreeControllerState, AccountTreeControllerGetStateAction, AccountTreeControllerActions,
|
|
1
|
+
export type { AccountTreeControllerState, AccountTreeControllerGetStateAction, AccountTreeControllerActions, AccountTreeControllerStateChangeEvent, AccountTreeControllerEvents, AccountTreeControllerMessenger, AccountWalletObject, AccountWalletMetadata, AccountGroupObject, AccountGroupMetadata, } from "./AccountTreeController.cjs";
|
|
2
2
|
export { AccountTreeController, getDefaultAccountTreeControllerState, } from "./AccountTreeController.cjs";
|
|
3
3
|
export type { AccountTreeWallet } from "./AccountTreeWallet.cjs";
|
|
4
4
|
export type { AccountTreeGroup } from "./AccountTreeGroup.cjs";
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,0BAA0B,EAC1B,mCAAmC,EACnC,4BAA4B,EAC5B,
|
|
1
|
+
{"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,0BAA0B,EAC1B,mCAAmC,EACnC,4BAA4B,EAC5B,qCAAqC,EACrC,2BAA2B,EAC3B,8BAA8B,EAC9B,mBAAmB,EACnB,qBAAqB,EACrB,kBAAkB,EAClB,oBAAoB,GACrB,oCAAgC;AACjC,OAAO,EACL,qBAAqB,EACrB,oCAAoC,GACrC,oCAAgC;AACjC,YAAY,EAAE,iBAAiB,EAAE,gCAA4B;AAC7D,YAAY,EAAE,gBAAgB,EAAE,+BAA2B"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { AccountTreeControllerState, AccountTreeControllerGetStateAction, AccountTreeControllerActions,
|
|
1
|
+
export type { AccountTreeControllerState, AccountTreeControllerGetStateAction, AccountTreeControllerActions, AccountTreeControllerStateChangeEvent, AccountTreeControllerEvents, AccountTreeControllerMessenger, AccountWalletObject, AccountWalletMetadata, AccountGroupObject, AccountGroupMetadata, } from "./AccountTreeController.mjs";
|
|
2
2
|
export { AccountTreeController, getDefaultAccountTreeControllerState, } from "./AccountTreeController.mjs";
|
|
3
3
|
export type { AccountTreeWallet } from "./AccountTreeWallet.mjs";
|
|
4
4
|
export type { AccountTreeGroup } from "./AccountTreeGroup.mjs";
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,0BAA0B,EAC1B,mCAAmC,EACnC,4BAA4B,EAC5B,
|
|
1
|
+
{"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,0BAA0B,EAC1B,mCAAmC,EACnC,4BAA4B,EAC5B,qCAAqC,EACrC,2BAA2B,EAC3B,8BAA8B,EAC9B,mBAAmB,EACnB,qBAAqB,EACrB,kBAAkB,EAClB,oBAAoB,GACrB,oCAAgC;AACjC,OAAO,EACL,qBAAqB,EACrB,oCAAoC,GACrC,oCAAgC;AACjC,YAAY,EAAE,iBAAiB,EAAE,gCAA4B;AAC7D,YAAY,EAAE,gBAAgB,EAAE,+BAA2B"}
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.mjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAYA,OAAO,EACL,qBAAqB,EACrB,oCAAoC,EACrC,oCAAgC","sourcesContent":["export type {\n AccountTreeControllerState,\n AccountTreeControllerGetStateAction,\n AccountTreeControllerActions,\n AccountTreeControllerStateChangeEvent,\n AccountTreeControllerEvents,\n AccountTreeControllerMessenger,\n AccountWalletObject,\n AccountWalletMetadata,\n AccountGroupObject,\n AccountGroupMetadata,\n} from './AccountTreeController';\nexport {\n AccountTreeController,\n getDefaultAccountTreeControllerState,\n} from './AccountTreeController';\nexport type { AccountTreeWallet } from './AccountTreeWallet';\nexport type { AccountTreeGroup } from './AccountTreeGroup';\n"]}
|
package/package.json
CHANGED