@metamask-previews/account-tree-controller 0.6.0-preview-9fd73632 → 0.6.0-preview-848266f
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 +7 -0
- package/dist/AccountTreeController.cjs +108 -6
- package/dist/AccountTreeController.cjs.map +1 -1
- package/dist/AccountTreeController.d.cts +25 -4
- package/dist/AccountTreeController.d.cts.map +1 -1
- package/dist/AccountTreeController.d.mts +25 -4
- package/dist/AccountTreeController.d.mts.map +1 -1
- package/dist/AccountTreeController.mjs +108 -6
- 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 +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,13 @@ 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
|
+
|
|
10
17
|
## [0.6.0]
|
|
11
18
|
|
|
12
19
|
### 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, _AccountTreeController_handleAccountAdded, _AccountTreeController_handleAccountRemoved, _AccountTreeController_insert, _AccountTreeController_listAccounts;
|
|
13
|
+
var _AccountTreeController_instances, _AccountTreeController_reverse, _AccountTreeController_rules, _AccountTreeController_wallets, _AccountTreeController_getDefaultSelectedAccountGroup, _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
15
|
exports.AccountTreeController = exports.getDefaultAccountTreeControllerState = void 0;
|
|
16
16
|
const base_controller_1 = require("@metamask/base-controller");
|
|
@@ -31,6 +31,7 @@ function getDefaultAccountTreeControllerState() {
|
|
|
31
31
|
return {
|
|
32
32
|
accountTree: {
|
|
33
33
|
wallets: {},
|
|
34
|
+
selectedAccountGroup: '',
|
|
34
35
|
},
|
|
35
36
|
};
|
|
36
37
|
}
|
|
@@ -75,6 +76,10 @@ class AccountTreeController extends base_controller_1.BaseController {
|
|
|
75
76
|
this.messagingSystem.subscribe('AccountsController:accountRemoved', (accountId) => {
|
|
76
77
|
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_handleAccountRemoved).call(this, accountId);
|
|
77
78
|
});
|
|
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);
|
|
78
83
|
}
|
|
79
84
|
init() {
|
|
80
85
|
const wallets = {};
|
|
@@ -84,6 +89,11 @@ class AccountTreeController extends base_controller_1.BaseController {
|
|
|
84
89
|
}
|
|
85
90
|
this.update((state) => {
|
|
86
91
|
state.accountTree.wallets = wallets;
|
|
92
|
+
if (state.accountTree.selectedAccountGroup === '') {
|
|
93
|
+
// No group is selected yet, re-sync with the AccountsController.
|
|
94
|
+
state.accountTree.selectedAccountGroup =
|
|
95
|
+
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getDefaultSelectedAccountGroup).call(this, wallets);
|
|
96
|
+
}
|
|
87
97
|
});
|
|
88
98
|
}
|
|
89
99
|
getWallet(id) {
|
|
@@ -92,9 +102,52 @@ class AccountTreeController extends base_controller_1.BaseController {
|
|
|
92
102
|
getWallets() {
|
|
93
103
|
return Array.from(__classPrivateFieldGet(this, _AccountTreeController_wallets, "f").values());
|
|
94
104
|
}
|
|
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.accountTree.selectedAccountGroup;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Sets the selected account group and updates the AccountsController selectedAccount accordingly.
|
|
115
|
+
*
|
|
116
|
+
* @param groupId - The account group ID to select.
|
|
117
|
+
*/
|
|
118
|
+
setSelectedAccountGroup(groupId) {
|
|
119
|
+
const currentSelectedGroup = this.state.accountTree.selectedAccountGroup;
|
|
120
|
+
// Idempotent check - if the same group is already selected, do nothing
|
|
121
|
+
if (currentSelectedGroup === groupId) {
|
|
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, groupId);
|
|
126
|
+
if (!accountToSelect) {
|
|
127
|
+
throw new Error(`No accounts found in group: ${groupId}`);
|
|
128
|
+
}
|
|
129
|
+
// Update our state first
|
|
130
|
+
this.update((state) => {
|
|
131
|
+
state.accountTree.selectedAccountGroup = groupId;
|
|
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
|
+
}
|
|
95
137
|
}
|
|
96
138
|
exports.AccountTreeController = AccountTreeController;
|
|
97
|
-
_AccountTreeController_reverse = new WeakMap(), _AccountTreeController_rules = new WeakMap(), _AccountTreeController_wallets = new WeakMap(), _AccountTreeController_instances = new WeakSet(),
|
|
139
|
+
_AccountTreeController_reverse = new WeakMap(), _AccountTreeController_rules = new WeakMap(), _AccountTreeController_wallets = new WeakMap(), _AccountTreeController_instances = new WeakSet(), _AccountTreeController_getDefaultSelectedAccountGroup = function _AccountTreeController_getDefaultSelectedAccountGroup(wallets) {
|
|
140
|
+
const selectedAccount = this.messagingSystem.call('AccountsController:getSelectedAccount');
|
|
141
|
+
if (selectedAccount && selectedAccount.id) {
|
|
142
|
+
const accountMapping = __classPrivateFieldGet(this, _AccountTreeController_reverse, "f").get(selectedAccount.id);
|
|
143
|
+
if (accountMapping) {
|
|
144
|
+
const { groupId } = accountMapping;
|
|
145
|
+
return groupId;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// Default to the first non-empty group in case of errors.
|
|
149
|
+
return __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_findFirstNonEmptyGroup).call(this, wallets);
|
|
150
|
+
}, _AccountTreeController_handleAccountAdded = function _AccountTreeController_handleAccountAdded(account) {
|
|
98
151
|
this.update((state) => {
|
|
99
152
|
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_insert).call(this, state.accountTree.wallets, account);
|
|
100
153
|
});
|
|
@@ -102,11 +155,22 @@ _AccountTreeController_reverse = new WeakMap(), _AccountTreeController_rules = n
|
|
|
102
155
|
const found = __classPrivateFieldGet(this, _AccountTreeController_reverse, "f").get(accountId);
|
|
103
156
|
if (found) {
|
|
104
157
|
const { walletId, groupId } = found;
|
|
158
|
+
// Clean up the reverse mapping to prevent memory leaks
|
|
159
|
+
__classPrivateFieldGet(this, _AccountTreeController_reverse, "f").delete(accountId);
|
|
105
160
|
this.update((state) => {
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
161
|
+
const accounts = state.accountTree.wallets[walletId]?.groups[groupId]?.accounts;
|
|
162
|
+
if (accounts) {
|
|
163
|
+
const index = accounts.indexOf(accountId);
|
|
164
|
+
if (index !== -1) {
|
|
165
|
+
accounts.splice(index, 1);
|
|
166
|
+
// Check if we need to update selectedAccountGroup after removal
|
|
167
|
+
if (state.accountTree.selectedAccountGroup === groupId &&
|
|
168
|
+
accounts.length === 0) {
|
|
169
|
+
// The currently selected group is now empty, find a new group to select
|
|
170
|
+
state.accountTree.selectedAccountGroup =
|
|
171
|
+
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_findFirstNonEmptyGroup).call(this, state.accountTree.wallets);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
110
174
|
}
|
|
111
175
|
});
|
|
112
176
|
}
|
|
@@ -146,5 +210,43 @@ _AccountTreeController_reverse = new WeakMap(), _AccountTreeController_rules = n
|
|
|
146
210
|
}
|
|
147
211
|
}, _AccountTreeController_listAccounts = function _AccountTreeController_listAccounts() {
|
|
148
212
|
return this.messagingSystem.call('AccountsController:listMultichainAccounts');
|
|
213
|
+
}, _AccountTreeController_handleSelectedAccountChange = function _AccountTreeController_handleSelectedAccountChange(account) {
|
|
214
|
+
const accountMapping = __classPrivateFieldGet(this, _AccountTreeController_reverse, "f").get(account.id);
|
|
215
|
+
if (!accountMapping) {
|
|
216
|
+
// Account not in tree yet, might be during initialization
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const { groupId } = accountMapping;
|
|
220
|
+
const currentSelectedGroup = this.state.accountTree.selectedAccountGroup;
|
|
221
|
+
// Idempotent check - if the same group is already selected, do nothing
|
|
222
|
+
if (currentSelectedGroup === groupId) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
// Update selectedAccountGroup to match the selected account
|
|
226
|
+
this.update((state) => {
|
|
227
|
+
state.accountTree.selectedAccountGroup = groupId;
|
|
228
|
+
});
|
|
229
|
+
}, _AccountTreeController_getFirstAccountInGroup = function _AccountTreeController_getFirstAccountInGroup(groupId) {
|
|
230
|
+
for (const wallet of Object.values(this.state.accountTree.wallets)) {
|
|
231
|
+
if (wallet.groups[groupId]) {
|
|
232
|
+
const group = wallet.groups[groupId];
|
|
233
|
+
if (group && group.accounts.length > 0) {
|
|
234
|
+
return group.accounts[0];
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return undefined;
|
|
239
|
+
}, _AccountTreeController_findFirstNonEmptyGroup = function _AccountTreeController_findFirstNonEmptyGroup(wallets) {
|
|
240
|
+
for (const wallet of Object.values(wallets)) {
|
|
241
|
+
for (const group of Object.values(wallet.groups)) {
|
|
242
|
+
if (group.accounts.length > 0) {
|
|
243
|
+
return group.id;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return '';
|
|
248
|
+
}, _AccountTreeController_registerMessageHandlers = function _AccountTreeController_registerMessageHandlers() {
|
|
249
|
+
this.messagingSystem.registerActionHandler(`${controllerName}:getSelectedAccountGroup`, this.getSelectedAccountGroup.bind(this));
|
|
250
|
+
this.messagingSystem.registerActionHandler(`${controllerName}:setSelectedAccountGroup`, this.setSelectedAccountGroup.bind(this));
|
|
149
251
|
};
|
|
150
252
|
//# 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":";;;;;;;;;;;;;;;AAYA,+DAKmC;AAOnC,6CAIiB;AAEjB,MAAM,cAAc,GAAG,uBAAuB,CAAC;AA0F/C,MAAM,6BAA6B,GACjC;IACE,WAAW,EAAE;QACX,OAAO,EAAE,KAAK;QACd,SAAS,EAAE,KAAK;KACjB;CACF,CAAC;AAEJ;;;;GAIG;AACH,SAAgB,oCAAoC;IAClD,OAAO;QACL,WAAW,EAAE;YACX,OAAO,EAAE,EAAE;YACX,oBAAoB,EAAE,EAAE;SACzB;KACF,CAAC;AACJ,CAAC;AAPD,oFAOC;AAED,MAAa,qBAAsB,SAAQ,gCAI1C;IAOC;;;;;;OAMG;IACH,YAAY,EACV,SAAS,EACT,KAAK,GAIN;QACC,KAAK,CAAC;YACJ,SAAS;YACT,IAAI,EAAE,cAAc;YACpB,QAAQ,EAAE,6BAA6B;YACvC,KAAK,EAAE;gBACL,GAAG,oCAAoC,EAAE;gBACzC,GAAG,KAAK;aACT;SACF,CAAC,CAAC;;QA5BI,iDAAgD;QAEhD,+CAAqB;QAErB,iDAAkD;QAyBzD,uBAAA,IAAI,kCAAY,IAAI,GAAG,EAAE,MAAA,CAAC;QAE1B,4DAA4D;QAC5D,uBAAA,IAAI,kCAAY,IAAI,GAAG,EAAE,MAAA,CAAC;QAE1B,gDAAgD;QAChD,uBAAA,IAAI,gCAAU;YACZ,gCAAgC;YAChC,IAAI,+BAAuB,CAAC,IAAI,CAAC,eAAe,CAAC;YACjD,yBAAyB;YACzB,IAAI,sBAAc,CAAC,IAAI,CAAC,eAAe,CAAC;YACxC,8FAA8F;YAC9F,IAAI,yBAAiB,CAAC,IAAI,CAAC,eAAe,CAAC;SAC5C,MAAA,CAAC;QAEF,IAAI,CAAC,eAAe,CAAC,SAAS,CAC5B,iCAAiC,EACjC,CAAC,OAAO,EAAE,EAAE;YACV,uBAAA,IAAI,mFAAoB,MAAxB,IAAI,EAAqB,OAAO,CAAC,CAAC;QACpC,CAAC,CACF,CAAC;QAEF,IAAI,CAAC,eAAe,CAAC,SAAS,CAC5B,mCAAmC,EACnC,CAAC,SAAS,EAAE,EAAE;YACZ,uBAAA,IAAI,qFAAsB,MAA1B,IAAI,EAAuB,SAAS,CAAC,CAAC;QACxC,CAAC,CACF,CAAC;QAEF,IAAI,CAAC,eAAe,CAAC,SAAS,CAC5B,0CAA0C,EAC1C,CAAC,OAAO,EAAE,EAAE;YACV,uBAAA,IAAI,4FAA6B,MAAjC,IAAI,EAA8B,OAAO,CAAC,CAAC;QAC7C,CAAC,CACF,CAAC;QAEF,uBAAA,IAAI,wFAAyB,MAA7B,IAAI,CAA2B,CAAC;IAClC,CAAC;IAED,IAAI;QACF,MAAM,OAAO,GAAyD,EAAE,CAAC;QAEzE,kFAAkF;QAClF,KAAK,MAAM,OAAO,IAAI,uBAAA,IAAI,6EAAc,MAAlB,IAAI,CAAgB,EAAE;YAC1C,uBAAA,IAAI,uEAAQ,MAAZ,IAAI,EAAS,OAAO,EAAE,OAAO,CAAC,CAAC;SAChC;QAED,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACpB,KAAK,CAAC,WAAW,CAAC,OAAO,GAAG,OAAO,CAAC;YAEpC,IAAI,KAAK,CAAC,WAAW,CAAC,oBAAoB,KAAK,EAAE,EAAE;gBACjD,iEAAiE;gBACjE,KAAK,CAAC,WAAW,CAAC,oBAAoB;oBACpC,uBAAA,IAAI,+FAAgC,MAApC,IAAI,EAAiC,OAAO,CAAC,CAAC;aACjD;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IA2BD,SAAS,CAAC,EAAmB;QAC3B,OAAO,uBAAA,IAAI,sCAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC/B,CAAC;IAED,UAAU;QACR,OAAO,KAAK,CAAC,IAAI,CAAC,uBAAA,IAAI,sCAAS,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5C,CAAC;IA4FD;;;;OAIG;IACH,uBAAuB;QACrB,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,oBAAoB,CAAC;IACrD,CAAC;IAED;;;;OAIG;IACH,uBAAuB,CAAC,OAAuB;QAC7C,MAAM,oBAAoB,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,oBAAoB,CAAC;QAEzE,uEAAuE;QACvE,IAAI,oBAAoB,KAAK,OAAO,EAAE;YACpC,OAAO;SACR;QAED,iDAAiD;QACjD,MAAM,eAAe,GAAG,uBAAA,IAAI,uFAAwB,MAA5B,IAAI,EAAyB,OAAO,CAAC,CAAC;QAC9D,IAAI,CAAC,eAAe,EAAE;YACpB,MAAM,IAAI,KAAK,CAAC,+BAA+B,OAAO,EAAE,CAAC,CAAC;SAC3D;QAED,yBAAyB;QACzB,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACpB,KAAK,CAAC,WAAW,CAAC,oBAAoB,GAAG,OAAO,CAAC;QACnD,CAAC,CAAC,CAAC;QAEH,6EAA6E;QAC7E,gEAAgE;QAChE,IAAI,CAAC,eAAe,CAAC,IAAI,CACvB,uCAAuC,EACvC,eAAe,CAChB,CAAC;IACJ,CAAC;CAgFF;AA9UD,sDA8UC;uTA5OiC,OAE/B;IACC,MAAM,eAAe,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAC/C,uCAAuC,CACxC,CAAC;IACF,IAAI,eAAe,IAAI,eAAe,CAAC,EAAE,EAAE;QACzC,MAAM,cAAc,GAAG,uBAAA,IAAI,sCAAS,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;QAC7D,IAAI,cAAc,EAAE;YAClB,MAAM,EAAE,OAAO,EAAE,GAAG,cAAc,CAAC;YAEnC,OAAO,OAAO,CAAC;SAChB;KACF;IAED,0DAA0D;IAC1D,OAAO,uBAAA,IAAI,uFAAwB,MAA5B,IAAI,EAAyB,OAAO,CAAC,CAAC;AAC/C,CAAC,iGAUmB,OAAwB;IAC1C,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;QACpB,uBAAA,IAAI,uEAAQ,MAAZ,IAAI,EAAS,KAAK,CAAC,WAAW,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;AACL,CAAC,qGAEqB,SAAoB;IACxC,MAAM,KAAK,GAAG,uBAAA,IAAI,sCAAS,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAE3C,IAAI,KAAK,EAAE;QACT,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;QAEpC,uDAAuD;QACvD,uBAAA,IAAI,sCAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAEhC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACpB,MAAM,QAAQ,GACZ,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC;YAEjE,IAAI,QAAQ,EAAE;gBACZ,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;gBAC1C,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE;oBAChB,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;oBAE1B,gEAAgE;oBAChE,IACE,KAAK,CAAC,WAAW,CAAC,oBAAoB,KAAK,OAAO;wBAClD,QAAQ,CAAC,MAAM,KAAK,CAAC,EACrB;wBACA,wEAAwE;wBACxE,KAAK,CAAC,WAAW,CAAC,oBAAoB;4BACpC,uBAAA,IAAI,uFAAwB,MAA5B,IAAI,EAAyB,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;qBAC3D;iBACF;aACF;QACH,CAAC,CAAC,CAAC;KACJ;AACH,CAAC,yEAGC,OAA6D,EAC7D,OAAwB;IAExB,KAAK,MAAM,IAAI,IAAI,uBAAA,IAAI,oCAAO,EAAE;QAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAElC,IAAI,CAAC,KAAK,EAAE;YACV,iDAAiD;YACjD,SAAS;SACV;QAED,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;QAEhC,2CAA2C;QAC3C,uBAAA,IAAI,sCAAS,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QAErC,6BAA6B;QAC7B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE;YACvB,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG;gBACnB,EAAE,EAAE,MAAM,CAAC,EAAE;gBACb,MAAM,EAAE;oBACN,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE;wBACV,EAAE,EAAE,KAAK,CAAC,EAAE;wBACZ,QAAQ,EAAE,EAAE;wBACZ,QAAQ,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,cAAc,EAAE,EAAE;qBAC3C;iBACF;gBACD,QAAQ,EAAE;oBACR,IAAI,EAAE,MAAM,CAAC,cAAc,EAAE;iBAC9B;aACF,CAAC;SACH;QACD,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAE9D,+CAA+C;QAC/C,uBAAA,IAAI,sCAAS,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE;YAC5B,QAAQ,EAAE,MAAM,CAAC,EAAE;YACnB,OAAO,EAAE,KAAK,CAAC,EAAE;SAClB,CAAC,CAAC;QAEH,OAAO;KACR;AACH,CAAC;IAGC,OAAO,IAAI,CAAC,eAAe,CAAC,IAAI,CAC9B,2CAA2C,CAC5C,CAAC;AACJ,CAAC,mHAiD4B,OAAwB;IACnD,MAAM,cAAc,GAAG,uBAAA,IAAI,sCAAS,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACrD,IAAI,CAAC,cAAc,EAAE;QACnB,0DAA0D;QAC1D,OAAO;KACR;IAED,MAAM,EAAE,OAAO,EAAE,GAAG,cAAc,CAAC;IACnC,MAAM,oBAAoB,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,oBAAoB,CAAC;IAEzE,uEAAuE;IACvE,IAAI,oBAAoB,KAAK,OAAO,EAAE;QACpC,OAAO;KACR;IAED,4DAA4D;IAC5D,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;QACpB,KAAK,CAAC,WAAW,CAAC,oBAAoB,GAAG,OAAO,CAAC;IACnD,CAAC,CAAC,CAAC;AACL,CAAC,yGAQuB,OAAuB;IAC7C,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE;QAClE,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE;YAC1B,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACrC,IAAI,KAAK,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE;gBACtC,OAAO,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;aAC1B;SACF;KACF;IACD,OAAO,SAAS,CAAC;AACnB,CAAC,yGAQuB,OAEvB;IACC,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE;QAC3C,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE;YAChD,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE;gBAC7B,OAAO,KAAK,CAAC,EAAE,CAAC;aACjB;SACF;KACF;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;IAMC,IAAI,CAAC,eAAe,CAAC,qBAAqB,CACxC,GAAG,cAAc,0BAA0B,EAC3C,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,CACxC,CAAC;IAEF,IAAI,CAAC,eAAe,CAAC,qBAAqB,CACxC,GAAG,cAAc,0BAA0B,EAC3C,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,CACxC,CAAC;AACJ,CAAC","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 selectedAccountGroup: AccountGroupId | '';\n };\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 };\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\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.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.#reverse.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 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 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 }\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.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.#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.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,5 +1,5 @@
|
|
|
1
1
|
import type { AccountGroupId, AccountWalletId } from "@metamask/account-api";
|
|
2
|
-
import type { AccountId, AccountsControllerAccountAddedEvent, AccountsControllerAccountRemovedEvent, AccountsControllerGetAccountAction, AccountsControllerListMultichainAccountsAction } from "@metamask/accounts-controller";
|
|
2
|
+
import type { AccountId, AccountsControllerAccountAddedEvent, AccountsControllerAccountRemovedEvent, AccountsControllerGetAccountAction, AccountsControllerGetSelectedAccountAction, AccountsControllerListMultichainAccountsAction, AccountsControllerSelectedAccountChangeEvent, AccountsControllerSetSelectedAccountAction } 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";
|
|
@@ -27,13 +27,22 @@ export type AccountTreeControllerState = {
|
|
|
27
27
|
wallets: {
|
|
28
28
|
[walletId: AccountWalletId]: AccountWalletObject;
|
|
29
29
|
};
|
|
30
|
+
selectedAccountGroup: AccountGroupId | '';
|
|
30
31
|
};
|
|
31
32
|
};
|
|
32
33
|
export type AccountTreeControllerGetStateAction = ControllerGetStateAction<typeof controllerName, AccountTreeControllerState>;
|
|
33
|
-
export type
|
|
34
|
-
|
|
34
|
+
export type AccountTreeControllerSetSelectedAccountGroupAction = {
|
|
35
|
+
type: `${typeof controllerName}:setSelectedAccountGroup`;
|
|
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;
|
|
35
44
|
export type AccountTreeControllerStateChangeEvent = ControllerStateChangeEvent<typeof controllerName, AccountTreeControllerState>;
|
|
36
|
-
export type AllowedEvents = AccountsControllerAccountAddedEvent | AccountsControllerAccountRemovedEvent;
|
|
45
|
+
export type AllowedEvents = AccountsControllerAccountAddedEvent | AccountsControllerAccountRemovedEvent | AccountsControllerSelectedAccountChangeEvent;
|
|
37
46
|
export type AccountTreeControllerEvents = AccountTreeControllerStateChangeEvent;
|
|
38
47
|
export type AccountTreeControllerMessenger = RestrictedMessenger<typeof controllerName, AccountTreeControllerActions | AllowedActions, AccountTreeControllerEvents | AllowedEvents, AllowedActions['type'], AllowedEvents['type']>;
|
|
39
48
|
/**
|
|
@@ -58,6 +67,18 @@ export declare class AccountTreeController extends BaseController<typeof control
|
|
|
58
67
|
init(): void;
|
|
59
68
|
getWallet(id: AccountWalletId): AccountTreeWallet | undefined;
|
|
60
69
|
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 groupId - The account group ID to select.
|
|
80
|
+
*/
|
|
81
|
+
setSelectedAccountGroup(groupId: AccountGroupId): void;
|
|
61
82
|
}
|
|
62
83
|
export {};
|
|
63
84
|
//# 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,8CAA8C,
|
|
1
|
+
{"version":3,"file":"AccountTreeController.d.cts","sourceRoot":"","sources":["../src/AccountTreeController.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AccountGroupId, AccountWalletId } from "@metamask/account-api";
|
|
2
|
-
import type { AccountId, AccountsControllerAccountAddedEvent, AccountsControllerAccountRemovedEvent, AccountsControllerGetAccountAction, AccountsControllerListMultichainAccountsAction } from "@metamask/accounts-controller";
|
|
2
|
+
import type { AccountId, AccountsControllerAccountAddedEvent, AccountsControllerAccountRemovedEvent, AccountsControllerGetAccountAction, AccountsControllerGetSelectedAccountAction, AccountsControllerListMultichainAccountsAction, AccountsControllerSelectedAccountChangeEvent, AccountsControllerSetSelectedAccountAction } 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";
|
|
@@ -27,13 +27,22 @@ export type AccountTreeControllerState = {
|
|
|
27
27
|
wallets: {
|
|
28
28
|
[walletId: AccountWalletId]: AccountWalletObject;
|
|
29
29
|
};
|
|
30
|
+
selectedAccountGroup: AccountGroupId | '';
|
|
30
31
|
};
|
|
31
32
|
};
|
|
32
33
|
export type AccountTreeControllerGetStateAction = ControllerGetStateAction<typeof controllerName, AccountTreeControllerState>;
|
|
33
|
-
export type
|
|
34
|
-
|
|
34
|
+
export type AccountTreeControllerSetSelectedAccountGroupAction = {
|
|
35
|
+
type: `${typeof controllerName}:setSelectedAccountGroup`;
|
|
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;
|
|
35
44
|
export type AccountTreeControllerStateChangeEvent = ControllerStateChangeEvent<typeof controllerName, AccountTreeControllerState>;
|
|
36
|
-
export type AllowedEvents = AccountsControllerAccountAddedEvent | AccountsControllerAccountRemovedEvent;
|
|
45
|
+
export type AllowedEvents = AccountsControllerAccountAddedEvent | AccountsControllerAccountRemovedEvent | AccountsControllerSelectedAccountChangeEvent;
|
|
37
46
|
export type AccountTreeControllerEvents = AccountTreeControllerStateChangeEvent;
|
|
38
47
|
export type AccountTreeControllerMessenger = RestrictedMessenger<typeof controllerName, AccountTreeControllerActions | AllowedActions, AccountTreeControllerEvents | AllowedEvents, AllowedActions['type'], AllowedEvents['type']>;
|
|
39
48
|
/**
|
|
@@ -58,6 +67,18 @@ export declare class AccountTreeController extends BaseController<typeof control
|
|
|
58
67
|
init(): void;
|
|
59
68
|
getWallet(id: AccountWalletId): AccountTreeWallet | undefined;
|
|
60
69
|
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 groupId - The account group ID to select.
|
|
80
|
+
*/
|
|
81
|
+
setSelectedAccountGroup(groupId: AccountGroupId): void;
|
|
61
82
|
}
|
|
62
83
|
export {};
|
|
63
84
|
//# 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,8CAA8C,
|
|
1
|
+
{"version":3,"file":"AccountTreeController.d.mts","sourceRoot":"","sources":["../src/AccountTreeController.ts"],"names":[],"mappings":"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"}
|
|
@@ -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, _AccountTreeController_handleAccountAdded, _AccountTreeController_handleAccountRemoved, _AccountTreeController_insert, _AccountTreeController_listAccounts;
|
|
12
|
+
var _AccountTreeController_instances, _AccountTreeController_reverse, _AccountTreeController_rules, _AccountTreeController_wallets, _AccountTreeController_getDefaultSelectedAccountGroup, _AccountTreeController_handleAccountAdded, _AccountTreeController_handleAccountRemoved, _AccountTreeController_insert, _AccountTreeController_listAccounts, _AccountTreeController_handleSelectedAccountChange, _AccountTreeController_getFirstAccountInGroup, _AccountTreeController_findFirstNonEmptyGroup, _AccountTreeController_registerMessageHandlers;
|
|
13
13
|
import { BaseController } from "@metamask/base-controller";
|
|
14
14
|
import { EntropySourceWalletRule, SnapWalletRule, KeyringWalletRule } from "./rules/index.mjs";
|
|
15
15
|
const controllerName = 'AccountTreeController';
|
|
@@ -28,6 +28,7 @@ export function getDefaultAccountTreeControllerState() {
|
|
|
28
28
|
return {
|
|
29
29
|
accountTree: {
|
|
30
30
|
wallets: {},
|
|
31
|
+
selectedAccountGroup: '',
|
|
31
32
|
},
|
|
32
33
|
};
|
|
33
34
|
}
|
|
@@ -71,6 +72,10 @@ export class AccountTreeController extends BaseController {
|
|
|
71
72
|
this.messagingSystem.subscribe('AccountsController:accountRemoved', (accountId) => {
|
|
72
73
|
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_handleAccountRemoved).call(this, accountId);
|
|
73
74
|
});
|
|
75
|
+
this.messagingSystem.subscribe('AccountsController:selectedAccountChange', (account) => {
|
|
76
|
+
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_handleSelectedAccountChange).call(this, account);
|
|
77
|
+
});
|
|
78
|
+
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_registerMessageHandlers).call(this);
|
|
74
79
|
}
|
|
75
80
|
init() {
|
|
76
81
|
const wallets = {};
|
|
@@ -80,6 +85,11 @@ export class AccountTreeController extends BaseController {
|
|
|
80
85
|
}
|
|
81
86
|
this.update((state) => {
|
|
82
87
|
state.accountTree.wallets = wallets;
|
|
88
|
+
if (state.accountTree.selectedAccountGroup === '') {
|
|
89
|
+
// No group is selected yet, re-sync with the AccountsController.
|
|
90
|
+
state.accountTree.selectedAccountGroup =
|
|
91
|
+
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getDefaultSelectedAccountGroup).call(this, wallets);
|
|
92
|
+
}
|
|
83
93
|
});
|
|
84
94
|
}
|
|
85
95
|
getWallet(id) {
|
|
@@ -88,8 +98,51 @@ export class AccountTreeController extends BaseController {
|
|
|
88
98
|
getWallets() {
|
|
89
99
|
return Array.from(__classPrivateFieldGet(this, _AccountTreeController_wallets, "f").values());
|
|
90
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Gets the currently selected account group ID.
|
|
103
|
+
*
|
|
104
|
+
* @returns The selected account group ID or empty string if none selected.
|
|
105
|
+
*/
|
|
106
|
+
getSelectedAccountGroup() {
|
|
107
|
+
return this.state.accountTree.selectedAccountGroup;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Sets the selected account group and updates the AccountsController selectedAccount accordingly.
|
|
111
|
+
*
|
|
112
|
+
* @param groupId - The account group ID to select.
|
|
113
|
+
*/
|
|
114
|
+
setSelectedAccountGroup(groupId) {
|
|
115
|
+
const currentSelectedGroup = this.state.accountTree.selectedAccountGroup;
|
|
116
|
+
// Idempotent check - if the same group is already selected, do nothing
|
|
117
|
+
if (currentSelectedGroup === groupId) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
// Find the first account in this group to select
|
|
121
|
+
const accountToSelect = __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getFirstAccountInGroup).call(this, groupId);
|
|
122
|
+
if (!accountToSelect) {
|
|
123
|
+
throw new Error(`No accounts found in group: ${groupId}`);
|
|
124
|
+
}
|
|
125
|
+
// Update our state first
|
|
126
|
+
this.update((state) => {
|
|
127
|
+
state.accountTree.selectedAccountGroup = groupId;
|
|
128
|
+
});
|
|
129
|
+
// Update AccountsController - this will trigger selectedAccountChange event,
|
|
130
|
+
// but our handler is idempotent so it won't cause infinite loop
|
|
131
|
+
this.messagingSystem.call('AccountsController:setSelectedAccount', accountToSelect);
|
|
132
|
+
}
|
|
91
133
|
}
|
|
92
|
-
_AccountTreeController_reverse = new WeakMap(), _AccountTreeController_rules = new WeakMap(), _AccountTreeController_wallets = new WeakMap(), _AccountTreeController_instances = new WeakSet(),
|
|
134
|
+
_AccountTreeController_reverse = new WeakMap(), _AccountTreeController_rules = new WeakMap(), _AccountTreeController_wallets = new WeakMap(), _AccountTreeController_instances = new WeakSet(), _AccountTreeController_getDefaultSelectedAccountGroup = function _AccountTreeController_getDefaultSelectedAccountGroup(wallets) {
|
|
135
|
+
const selectedAccount = this.messagingSystem.call('AccountsController:getSelectedAccount');
|
|
136
|
+
if (selectedAccount && selectedAccount.id) {
|
|
137
|
+
const accountMapping = __classPrivateFieldGet(this, _AccountTreeController_reverse, "f").get(selectedAccount.id);
|
|
138
|
+
if (accountMapping) {
|
|
139
|
+
const { groupId } = accountMapping;
|
|
140
|
+
return groupId;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// Default to the first non-empty group in case of errors.
|
|
144
|
+
return __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_findFirstNonEmptyGroup).call(this, wallets);
|
|
145
|
+
}, _AccountTreeController_handleAccountAdded = function _AccountTreeController_handleAccountAdded(account) {
|
|
93
146
|
this.update((state) => {
|
|
94
147
|
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_insert).call(this, state.accountTree.wallets, account);
|
|
95
148
|
});
|
|
@@ -97,11 +150,22 @@ _AccountTreeController_reverse = new WeakMap(), _AccountTreeController_rules = n
|
|
|
97
150
|
const found = __classPrivateFieldGet(this, _AccountTreeController_reverse, "f").get(accountId);
|
|
98
151
|
if (found) {
|
|
99
152
|
const { walletId, groupId } = found;
|
|
153
|
+
// Clean up the reverse mapping to prevent memory leaks
|
|
154
|
+
__classPrivateFieldGet(this, _AccountTreeController_reverse, "f").delete(accountId);
|
|
100
155
|
this.update((state) => {
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
156
|
+
const accounts = state.accountTree.wallets[walletId]?.groups[groupId]?.accounts;
|
|
157
|
+
if (accounts) {
|
|
158
|
+
const index = accounts.indexOf(accountId);
|
|
159
|
+
if (index !== -1) {
|
|
160
|
+
accounts.splice(index, 1);
|
|
161
|
+
// Check if we need to update selectedAccountGroup after removal
|
|
162
|
+
if (state.accountTree.selectedAccountGroup === groupId &&
|
|
163
|
+
accounts.length === 0) {
|
|
164
|
+
// The currently selected group is now empty, find a new group to select
|
|
165
|
+
state.accountTree.selectedAccountGroup =
|
|
166
|
+
__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_findFirstNonEmptyGroup).call(this, state.accountTree.wallets);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
105
169
|
}
|
|
106
170
|
});
|
|
107
171
|
}
|
|
@@ -141,5 +205,43 @@ _AccountTreeController_reverse = new WeakMap(), _AccountTreeController_rules = n
|
|
|
141
205
|
}
|
|
142
206
|
}, _AccountTreeController_listAccounts = function _AccountTreeController_listAccounts() {
|
|
143
207
|
return this.messagingSystem.call('AccountsController:listMultichainAccounts');
|
|
208
|
+
}, _AccountTreeController_handleSelectedAccountChange = function _AccountTreeController_handleSelectedAccountChange(account) {
|
|
209
|
+
const accountMapping = __classPrivateFieldGet(this, _AccountTreeController_reverse, "f").get(account.id);
|
|
210
|
+
if (!accountMapping) {
|
|
211
|
+
// Account not in tree yet, might be during initialization
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
const { groupId } = accountMapping;
|
|
215
|
+
const currentSelectedGroup = this.state.accountTree.selectedAccountGroup;
|
|
216
|
+
// Idempotent check - if the same group is already selected, do nothing
|
|
217
|
+
if (currentSelectedGroup === groupId) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
// Update selectedAccountGroup to match the selected account
|
|
221
|
+
this.update((state) => {
|
|
222
|
+
state.accountTree.selectedAccountGroup = groupId;
|
|
223
|
+
});
|
|
224
|
+
}, _AccountTreeController_getFirstAccountInGroup = function _AccountTreeController_getFirstAccountInGroup(groupId) {
|
|
225
|
+
for (const wallet of Object.values(this.state.accountTree.wallets)) {
|
|
226
|
+
if (wallet.groups[groupId]) {
|
|
227
|
+
const group = wallet.groups[groupId];
|
|
228
|
+
if (group && group.accounts.length > 0) {
|
|
229
|
+
return group.accounts[0];
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return undefined;
|
|
234
|
+
}, _AccountTreeController_findFirstNonEmptyGroup = function _AccountTreeController_findFirstNonEmptyGroup(wallets) {
|
|
235
|
+
for (const wallet of Object.values(wallets)) {
|
|
236
|
+
for (const group of Object.values(wallet.groups)) {
|
|
237
|
+
if (group.accounts.length > 0) {
|
|
238
|
+
return group.id;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return '';
|
|
243
|
+
}, _AccountTreeController_registerMessageHandlers = function _AccountTreeController_registerMessageHandlers() {
|
|
244
|
+
this.messagingSystem.registerActionHandler(`${controllerName}:getSelectedAccountGroup`, this.getSelectedAccountGroup.bind(this));
|
|
245
|
+
this.messagingSystem.registerActionHandler(`${controllerName}:setSelectedAccountGroup`, this.setSelectedAccountGroup.bind(this));
|
|
144
246
|
};
|
|
145
247
|
//# 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 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.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 selectedAccountGroup: AccountGroupId | '';\n };\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 };\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\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.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.#reverse.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 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 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 }\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.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.#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.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"]}
|
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":";;;AAcA,qEAGiC;AAF/B,8HAAA,qBAAqB,OAAA;AACrB,6IAAA,oCAAoC,OAAA","sourcesContent":["export type {\n AccountTreeControllerState,\n AccountTreeControllerGetStateAction,\n AccountTreeControllerActions,\n AccountTreeControllerSetSelectedAccountGroupAction,\n AccountTreeControllerGetSelectedAccountGroupAction,\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, AccountTreeControllerStateChangeEvent, AccountTreeControllerEvents, AccountTreeControllerMessenger, AccountWalletObject, AccountWalletMetadata, AccountGroupObject, AccountGroupMetadata, } from "./AccountTreeController.cjs";
|
|
1
|
+
export type { AccountTreeControllerState, AccountTreeControllerGetStateAction, AccountTreeControllerActions, AccountTreeControllerSetSelectedAccountGroupAction, AccountTreeControllerGetSelectedAccountGroupAction, 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,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"}
|
|
1
|
+
{"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,0BAA0B,EAC1B,mCAAmC,EACnC,4BAA4B,EAC5B,kDAAkD,EAClD,kDAAkD,EAClD,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, AccountTreeControllerStateChangeEvent, AccountTreeControllerEvents, AccountTreeControllerMessenger, AccountWalletObject, AccountWalletMetadata, AccountGroupObject, AccountGroupMetadata, } from "./AccountTreeController.mjs";
|
|
1
|
+
export type { AccountTreeControllerState, AccountTreeControllerGetStateAction, AccountTreeControllerActions, AccountTreeControllerSetSelectedAccountGroupAction, AccountTreeControllerGetSelectedAccountGroupAction, 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,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"}
|
|
1
|
+
{"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,0BAA0B,EAC1B,mCAAmC,EACnC,4BAA4B,EAC5B,kDAAkD,EAClD,kDAAkD,EAClD,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":"AAcA,OAAO,EACL,qBAAqB,EACrB,oCAAoC,EACrC,oCAAgC","sourcesContent":["export type {\n AccountTreeControllerState,\n AccountTreeControllerGetStateAction,\n AccountTreeControllerActions,\n AccountTreeControllerSetSelectedAccountGroupAction,\n AccountTreeControllerGetSelectedAccountGroupAction,\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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metamask-previews/account-tree-controller",
|
|
3
|
-
"version": "0.6.0-preview-
|
|
3
|
+
"version": "0.6.0-preview-848266f",
|
|
4
4
|
"description": "Controller to group account together based on some pre-defined rules",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"MetaMask",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"lodash": "^4.17.21"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
-
"@metamask/account-api": "^0.
|
|
56
|
+
"@metamask/account-api": "^0.3.0",
|
|
57
57
|
"@metamask/accounts-controller": "^32.0.0",
|
|
58
58
|
"@metamask/auto-changelog": "^3.4.4",
|
|
59
59
|
"@metamask/keyring-api": "^19.0.0",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"webextension-polyfill": "^0.12.0"
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {
|
|
73
|
-
"@metamask/account-api": "^0.
|
|
73
|
+
"@metamask/account-api": "^0.3.0",
|
|
74
74
|
"@metamask/accounts-controller": "^32.0.0",
|
|
75
75
|
"@metamask/keyring-controller": "^22.0.0",
|
|
76
76
|
"@metamask/providers": "^22.0.0",
|