@metamask/accounts-controller 18.2.0 → 18.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. package/CHANGELOG.md +27 -1
  2. package/dist/AccountsController.cjs +608 -0
  3. package/dist/AccountsController.cjs.map +1 -0
  4. package/dist/{types/AccountsController.d.ts → AccountsController.d.cts} +8 -8
  5. package/dist/AccountsController.d.cts.map +1 -0
  6. package/dist/AccountsController.d.mts +215 -0
  7. package/dist/AccountsController.d.mts.map +1 -0
  8. package/dist/AccountsController.mjs +604 -9
  9. package/dist/AccountsController.mjs.map +1 -1
  10. package/dist/index.cjs +9 -0
  11. package/dist/index.cjs.map +1 -0
  12. package/dist/{types/index.d.ts → index.d.cts} +4 -4
  13. package/dist/index.d.cts.map +1 -0
  14. package/dist/index.d.mts +4 -0
  15. package/dist/index.d.mts.map +1 -0
  16. package/dist/index.mjs +2 -13
  17. package/dist/index.mjs.map +1 -1
  18. package/dist/tests/mocks.cjs +49 -0
  19. package/dist/tests/mocks.cjs.map +1 -0
  20. package/dist/{types/tests/mocks.d.ts → tests/mocks.d.cts} +3 -3
  21. package/dist/tests/mocks.d.cts.map +1 -0
  22. package/dist/tests/mocks.d.mts +17 -0
  23. package/dist/tests/mocks.d.mts.map +1 -0
  24. package/dist/tests/mocks.mjs +41 -60
  25. package/dist/tests/mocks.mjs.map +1 -1
  26. package/dist/utils.cjs +80 -0
  27. package/dist/utils.cjs.map +1 -0
  28. package/dist/{types/utils.d.ts → utils.d.cts} +3 -3
  29. package/dist/utils.d.cts.map +1 -0
  30. package/dist/utils.d.mts +28 -0
  31. package/dist/utils.d.mts.map +1 -0
  32. package/dist/utils.mjs +73 -13
  33. package/dist/utils.mjs.map +1 -1
  34. package/package.json +19 -14
  35. package/dist/AccountsController.js +0 -11
  36. package/dist/AccountsController.js.map +0 -1
  37. package/dist/chunk-2DVFC4VN.js +0 -763
  38. package/dist/chunk-2DVFC4VN.js.map +0 -1
  39. package/dist/chunk-BYPP7G2N.js +0 -56
  40. package/dist/chunk-BYPP7G2N.js.map +0 -1
  41. package/dist/chunk-RIZO66PK.mjs +0 -763
  42. package/dist/chunk-RIZO66PK.mjs.map +0 -1
  43. package/dist/chunk-UJIPPGP6.js +0 -19
  44. package/dist/chunk-UJIPPGP6.js.map +0 -1
  45. package/dist/chunk-Y2QVUNIA.mjs +0 -56
  46. package/dist/chunk-Y2QVUNIA.mjs.map +0 -1
  47. package/dist/chunk-ZNSHBDHA.mjs +0 -19
  48. package/dist/chunk-ZNSHBDHA.mjs.map +0 -1
  49. package/dist/index.js +0 -14
  50. package/dist/index.js.map +0 -1
  51. package/dist/tests/mocks.js +0 -65
  52. package/dist/tests/mocks.js.map +0 -1
  53. package/dist/tsconfig.build.tsbuildinfo +0 -1
  54. package/dist/types/AccountsController.d.ts.map +0 -1
  55. package/dist/types/index.d.ts.map +0 -1
  56. package/dist/types/tests/mocks.d.ts.map +0 -1
  57. package/dist/types/utils.d.ts.map +0 -1
  58. package/dist/utils.js +0 -14
  59. package/dist/utils.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -7,6 +7,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [18.2.1]
11
+
12
+ ### Changed
13
+
14
+ - Bump `@metamask/eth-snap-keyring` from `^4.3.1` to `^4.3.3` ([#4689](https://github.com/MetaMask/core/pull/4689))
15
+ - Bump `@metamask/snaps-sdk` from `^6.1.1` to `^6.5.0` ([#4689](https://github.com/MetaMask/core/pull/4689))
16
+ - Bump `@metamask/snaps-utils` from `^7.8.1` to `^8.1.1` ([#4689](https://github.com/MetaMask/core/pull/4689))
17
+ - Bump peer dependency `@metamask/snaps-controllers` from `^9.3.0` to `^9.7.0` ([#4689](https://github.com/MetaMask/core/pull/4689))
18
+
19
+ ### Fixed
20
+
21
+ - Produce and export ESM-compatible TypeScript type declaration files in addition to CommonJS-compatible declaration files ([#4648](https://github.com/MetaMask/core/pull/4648))
22
+ - Previously, this package shipped with only one variant of type declaration
23
+ files, and these files were only CommonJS-compatible, and the `exports`
24
+ field in `package.json` linked to these files. This is an anti-pattern and
25
+ was rightfully flagged by the
26
+ ["Are the Types Wrong?"](https://arethetypeswrong.github.io/) tool as
27
+ ["masquerading as CJS"](https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/FalseCJS.md).
28
+ All of the ATTW checks now pass.
29
+ - Remove chunk files ([#4648](https://github.com/MetaMask/core/pull/4648)).
30
+ - Previously, the build tool we used to generate JavaScript files extracted
31
+ common code to "chunk" files. While this was intended to make this package
32
+ more tree-shakeable, it also made debugging more difficult for our
33
+ development teams. These chunk files are no longer present.
34
+
10
35
  ## [18.2.0]
11
36
 
12
37
  ### Added
@@ -295,7 +320,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
295
320
 
296
321
  - Initial release ([#1637](https://github.com/MetaMask/core/pull/1637))
297
322
 
298
- [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/accounts-controller@18.2.0...HEAD
323
+ [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/accounts-controller@18.2.1...HEAD
324
+ [18.2.1]: https://github.com/MetaMask/core/compare/@metamask/accounts-controller@18.2.0...@metamask/accounts-controller@18.2.1
299
325
  [18.2.0]: https://github.com/MetaMask/core/compare/@metamask/accounts-controller@18.1.1...@metamask/accounts-controller@18.2.0
300
326
  [18.1.1]: https://github.com/MetaMask/core/compare/@metamask/accounts-controller@18.1.0...@metamask/accounts-controller@18.1.1
301
327
  [18.1.0]: https://github.com/MetaMask/core/compare/@metamask/accounts-controller@18.0.0...@metamask/accounts-controller@18.1.0
@@ -0,0 +1,608 @@
1
+ "use strict";
2
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
4
+ 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");
5
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
6
+ };
7
+ var _AccountsController_instances, _AccountsController_generateInternalAccountForNonSnapAccount, _AccountsController_listSnapAccounts, _AccountsController_listNormalAccounts, _AccountsController_handleOnKeyringStateChange, _AccountsController_handleOnSnapStateChange, _AccountsController_getAccountsByKeyringType, _AccountsController_getLastSelectedAccount, _AccountsController_isAccountCompatibleWithChain, _AccountsController_getLastSelectedIndex, _AccountsController_handleNewAccountAdded, _AccountsController_publishAccountChangeEvent, _AccountsController_handleAccountRemoved, _AccountsController_populateExistingMetadata, _AccountsController_registerMessageHandlers;
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.AccountsController = exports.EMPTY_ACCOUNT = void 0;
10
+ const base_controller_1 = require("@metamask/base-controller");
11
+ const eth_snap_keyring_1 = require("@metamask/eth-snap-keyring");
12
+ const keyring_api_1 = require("@metamask/keyring-api");
13
+ const keyring_controller_1 = require("@metamask/keyring-controller");
14
+ const utils_1 = require("@metamask/utils");
15
+ const utils_2 = require("./utils.cjs");
16
+ const controllerName = 'AccountsController';
17
+ const accountsControllerMetadata = {
18
+ internalAccounts: {
19
+ persist: true,
20
+ anonymous: false,
21
+ },
22
+ };
23
+ const defaultState = {
24
+ internalAccounts: {
25
+ accounts: {},
26
+ selectedAccount: '',
27
+ },
28
+ };
29
+ exports.EMPTY_ACCOUNT = {
30
+ id: '',
31
+ address: '',
32
+ options: {},
33
+ methods: [],
34
+ type: keyring_api_1.EthAccountType.Eoa,
35
+ metadata: {
36
+ name: '',
37
+ keyring: {
38
+ type: '',
39
+ },
40
+ importTime: 0,
41
+ },
42
+ };
43
+ /**
44
+ * Controller that manages internal accounts.
45
+ * The accounts controller is responsible for creating and managing internal accounts.
46
+ * It also provides convenience methods for accessing and updating the internal accounts.
47
+ * The accounts controller also listens for keyring state changes and updates the internal accounts accordingly.
48
+ * The accounts controller also listens for snap state changes and updates the internal accounts accordingly.
49
+ *
50
+ */
51
+ class AccountsController extends base_controller_1.BaseController {
52
+ /**
53
+ * Constructor for AccountsController.
54
+ *
55
+ * @param options - The controller options.
56
+ * @param options.messenger - The messenger object.
57
+ * @param options.state - Initial state to set on this controller
58
+ */
59
+ constructor({ messenger, state, }) {
60
+ super({
61
+ messenger,
62
+ name: controllerName,
63
+ metadata: accountsControllerMetadata,
64
+ state: {
65
+ ...defaultState,
66
+ ...state,
67
+ },
68
+ });
69
+ _AccountsController_instances.add(this);
70
+ this.messagingSystem.subscribe('SnapController:stateChange', (snapStateState) => __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_handleOnSnapStateChange).call(this, snapStateState));
71
+ this.messagingSystem.subscribe('KeyringController:stateChange', (keyringState) => __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_handleOnKeyringStateChange).call(this, keyringState));
72
+ __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_registerMessageHandlers).call(this);
73
+ }
74
+ /**
75
+ * Returns the internal account object for the given account ID, if it exists.
76
+ *
77
+ * @param accountId - The ID of the account to retrieve.
78
+ * @returns The internal account object, or undefined if the account does not exist.
79
+ */
80
+ getAccount(accountId) {
81
+ return this.state.internalAccounts.accounts[accountId];
82
+ }
83
+ /**
84
+ * Returns an array of all evm internal accounts.
85
+ *
86
+ * @returns An array of InternalAccount objects.
87
+ */
88
+ listAccounts() {
89
+ const accounts = Object.values(this.state.internalAccounts.accounts);
90
+ return accounts.filter((account) => (0, keyring_api_1.isEvmAccountType)(account.type));
91
+ }
92
+ /**
93
+ * Returns an array of all internal accounts.
94
+ *
95
+ * @param chainId - The chain ID.
96
+ * @returns An array of InternalAccount objects.
97
+ */
98
+ listMultichainAccounts(chainId) {
99
+ const accounts = Object.values(this.state.internalAccounts.accounts);
100
+ if (!chainId) {
101
+ return accounts;
102
+ }
103
+ if (!(0, utils_1.isCaipChainId)(chainId)) {
104
+ throw new Error(`Invalid CAIP-2 chain ID: ${String(chainId)}`);
105
+ }
106
+ return accounts.filter((account) => __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_isAccountCompatibleWithChain).call(this, account, chainId));
107
+ }
108
+ /**
109
+ * Returns the internal account object for the given account ID.
110
+ *
111
+ * @param accountId - The ID of the account to retrieve.
112
+ * @returns The internal account object.
113
+ * @throws An error if the account ID is not found.
114
+ */
115
+ getAccountExpect(accountId) {
116
+ const account = this.getAccount(accountId);
117
+ if (account === undefined) {
118
+ throw new Error(`Account Id "${accountId}" not found`);
119
+ }
120
+ return account;
121
+ }
122
+ /**
123
+ * Returns the last selected EVM account.
124
+ *
125
+ * @returns The selected internal account.
126
+ */
127
+ getSelectedAccount() {
128
+ // Edge case where the extension is setup but the srp is not yet created
129
+ // certain ui elements will query the selected address before any accounts are created.
130
+ if (this.state.internalAccounts.selectedAccount === '') {
131
+ return exports.EMPTY_ACCOUNT;
132
+ }
133
+ const selectedAccount = this.getAccountExpect(this.state.internalAccounts.selectedAccount);
134
+ if ((0, keyring_api_1.isEvmAccountType)(selectedAccount.type)) {
135
+ return selectedAccount;
136
+ }
137
+ const accounts = this.listAccounts();
138
+ if (!accounts.length) {
139
+ // ! Should never reach this.
140
+ throw new Error('No EVM accounts');
141
+ }
142
+ // This will never be undefined because we have already checked if accounts.length is > 0
143
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
144
+ return __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getLastSelectedAccount).call(this, accounts);
145
+ }
146
+ /**
147
+ * __WARNING The return value may be undefined if there isn't an account for that chain id.__
148
+ *
149
+ * Retrieves the last selected account by chain ID.
150
+ *
151
+ * @param chainId - The chain ID to filter the accounts.
152
+ * @returns The last selected account compatible with the specified chain ID or undefined.
153
+ */
154
+ getSelectedMultichainAccount(chainId) {
155
+ // Edge case where the extension is setup but the srp is not yet created
156
+ // certain ui elements will query the selected address before any accounts are created.
157
+ if (this.state.internalAccounts.selectedAccount === '') {
158
+ return exports.EMPTY_ACCOUNT;
159
+ }
160
+ if (!chainId) {
161
+ return this.getAccountExpect(this.state.internalAccounts.selectedAccount);
162
+ }
163
+ if (!(0, utils_1.isCaipChainId)(chainId)) {
164
+ throw new Error(`Invalid CAIP-2 chain ID: ${chainId}`);
165
+ }
166
+ const accounts = Object.values(this.state.internalAccounts.accounts).filter((account) => __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_isAccountCompatibleWithChain).call(this, account, chainId));
167
+ return __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getLastSelectedAccount).call(this, accounts);
168
+ }
169
+ /**
170
+ * Returns the account with the specified address.
171
+ * ! This method will only return the first account that matches the address
172
+ * @param address - The address of the account to retrieve.
173
+ * @returns The account with the specified address, or undefined if not found.
174
+ */
175
+ getAccountByAddress(address) {
176
+ return this.listMultichainAccounts().find((account) => account.address.toLowerCase() === address.toLowerCase());
177
+ }
178
+ /**
179
+ * Sets the selected account by its ID.
180
+ *
181
+ * @param accountId - The ID of the account to be selected.
182
+ */
183
+ setSelectedAccount(accountId) {
184
+ const account = this.getAccountExpect(accountId);
185
+ this.update((currentState) => {
186
+ currentState.internalAccounts.accounts[account.id].metadata.lastSelected =
187
+ Date.now();
188
+ currentState.internalAccounts.selectedAccount = account.id;
189
+ });
190
+ __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_publishAccountChangeEvent).call(this, account);
191
+ }
192
+ /**
193
+ * Sets the name of the account with the given ID.
194
+ *
195
+ * @param accountId - The ID of the account to set the name for.
196
+ * @param accountName - The new name for the account.
197
+ * @throws An error if an account with the same name already exists.
198
+ */
199
+ setAccountName(accountId, accountName) {
200
+ // This will check for name uniqueness and fire the `accountRenamed` event
201
+ // if the account has been renamed.
202
+ this.updateAccountMetadata(accountId, {
203
+ name: accountName,
204
+ nameLastUpdatedAt: Date.now(),
205
+ });
206
+ }
207
+ /**
208
+ * Updates the metadata of the account with the given ID.
209
+ *
210
+ * @param accountId - The ID of the account for which the metadata will be updated.
211
+ * @param metadata - The new metadata for the account.
212
+ */
213
+ updateAccountMetadata(accountId, metadata) {
214
+ const account = this.getAccountExpect(accountId);
215
+ if (metadata.name &&
216
+ this.listMultichainAccounts().find((internalAccount) => internalAccount.metadata.name === metadata.name &&
217
+ internalAccount.id !== accountId)) {
218
+ throw new Error('Account name already exists');
219
+ }
220
+ this.update((currentState) => {
221
+ const internalAccount = {
222
+ ...account,
223
+ metadata: { ...account.metadata, ...metadata },
224
+ };
225
+ // Do not remove this comment - This error is flaky: Comment out or restore the `ts-expect-error` directive below as needed.
226
+ // See: https://github.com/MetaMask/utils/issues/168
227
+ // // @ts-expect-error Known issue - `Json` causes recursive error in immer `Draft`/`WritableDraft` types
228
+ currentState.internalAccounts.accounts[accountId] = internalAccount;
229
+ if (metadata.name) {
230
+ this.messagingSystem.publish('AccountsController:accountRenamed', internalAccount);
231
+ }
232
+ });
233
+ }
234
+ /**
235
+ * Updates the internal accounts list by retrieving normal and snap accounts,
236
+ * removing duplicates, and updating the metadata of each account.
237
+ *
238
+ * @returns A Promise that resolves when the accounts have been updated.
239
+ */
240
+ async updateAccounts() {
241
+ const snapAccounts = await __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_listSnapAccounts).call(this);
242
+ const normalAccounts = await __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_listNormalAccounts).call(this);
243
+ // keyring type map.
244
+ const keyringTypes = new Map();
245
+ const previousAccounts = this.state.internalAccounts.accounts;
246
+ const accounts = [
247
+ ...normalAccounts,
248
+ ...snapAccounts,
249
+ ].reduce((internalAccountMap, internalAccount) => {
250
+ const keyringTypeName = (0, utils_2.keyringTypeToName)(internalAccount.metadata.keyring.type);
251
+ const keyringAccountIndex = keyringTypes.get(keyringTypeName) ?? 0;
252
+ if (keyringAccountIndex) {
253
+ keyringTypes.set(keyringTypeName, keyringAccountIndex + 1);
254
+ }
255
+ else {
256
+ keyringTypes.set(keyringTypeName, 1);
257
+ }
258
+ const existingAccount = previousAccounts[internalAccount.id];
259
+ internalAccountMap[internalAccount.id] = {
260
+ ...internalAccount,
261
+ metadata: {
262
+ ...internalAccount.metadata,
263
+ name: __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, existingAccount?.id, 'name') ??
264
+ `${keyringTypeName} ${keyringAccountIndex + 1}`,
265
+ importTime: __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, existingAccount?.id, 'importTime') ??
266
+ Date.now(),
267
+ lastSelected: __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, existingAccount?.id, 'lastSelected') ?? 0,
268
+ },
269
+ };
270
+ return internalAccountMap;
271
+ }, {});
272
+ this.update((currentState) => {
273
+ currentState.internalAccounts.accounts = accounts;
274
+ if (!currentState.internalAccounts.accounts[currentState.internalAccounts.selectedAccount]) {
275
+ const lastSelectedAccount = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getLastSelectedAccount).call(this, Object.values(accounts));
276
+ if (lastSelectedAccount) {
277
+ currentState.internalAccounts.selectedAccount =
278
+ lastSelectedAccount.id;
279
+ currentState.internalAccounts.accounts[lastSelectedAccount.id].metadata.lastSelected = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getLastSelectedIndex).call(this);
280
+ __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_publishAccountChangeEvent).call(this, lastSelectedAccount);
281
+ }
282
+ else {
283
+ // It will be undefined if there are no accounts
284
+ currentState.internalAccounts.selectedAccount = '';
285
+ }
286
+ }
287
+ });
288
+ }
289
+ /**
290
+ * Loads the backup state of the accounts controller.
291
+ *
292
+ * @param backup - The backup state to load.
293
+ */
294
+ loadBackup(backup) {
295
+ if (backup.internalAccounts) {
296
+ this.update((currentState) => {
297
+ currentState.internalAccounts = backup.internalAccounts;
298
+ });
299
+ }
300
+ }
301
+ /**
302
+ * Returns the next account number for a given keyring type.
303
+ * @param keyringType - The type of keyring.
304
+ * @param accounts - Existing accounts to check for the next available account number.
305
+ * @returns An object containing the account prefix and index to use.
306
+ */
307
+ getNextAvailableAccountName(keyringType = keyring_controller_1.KeyringTypes.hd, accounts) {
308
+ const keyringName = (0, utils_2.keyringTypeToName)(keyringType);
309
+ const keyringAccounts = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getAccountsByKeyringType).call(this, keyringType, accounts);
310
+ const lastDefaultIndexUsedForKeyringType = keyringAccounts.reduce((maxInternalAccountIndex, internalAccount) => {
311
+ // We **DO NOT USE** `\d+` here to only consider valid "human"
312
+ // number (rounded decimal number)
313
+ const match = new RegExp(`${keyringName} ([0-9]+)$`, 'u').exec(internalAccount.metadata.name);
314
+ if (match) {
315
+ // Quoting `RegExp.exec` documentation:
316
+ // > The returned array has the matched text as the first item, and then one item for
317
+ // > each capturing group of the matched text.
318
+ // So use `match[1]` to get the captured value
319
+ const internalAccountIndex = parseInt(match[1], 10);
320
+ return Math.max(maxInternalAccountIndex, internalAccountIndex);
321
+ }
322
+ return maxInternalAccountIndex;
323
+ }, 0);
324
+ const index = Math.max(keyringAccounts.length + 1,
325
+ // ESLint is confused; this is a number.
326
+ // eslint-disable-next-line @typescript-eslint/restrict-plus-operands
327
+ lastDefaultIndexUsedForKeyringType + 1);
328
+ return `${keyringName} ${index}`;
329
+ }
330
+ }
331
+ exports.AccountsController = AccountsController;
332
+ _AccountsController_instances = new WeakSet(), _AccountsController_generateInternalAccountForNonSnapAccount = function _AccountsController_generateInternalAccountForNonSnapAccount(address, type) {
333
+ return {
334
+ id: (0, utils_2.getUUIDFromAddressOfNormalAccount)(address),
335
+ address,
336
+ options: {},
337
+ methods: [
338
+ keyring_api_1.EthMethod.PersonalSign,
339
+ keyring_api_1.EthMethod.Sign,
340
+ keyring_api_1.EthMethod.SignTransaction,
341
+ keyring_api_1.EthMethod.SignTypedDataV1,
342
+ keyring_api_1.EthMethod.SignTypedDataV3,
343
+ keyring_api_1.EthMethod.SignTypedDataV4,
344
+ ],
345
+ type: keyring_api_1.EthAccountType.Eoa,
346
+ metadata: {
347
+ name: '',
348
+ importTime: Date.now(),
349
+ keyring: {
350
+ type,
351
+ },
352
+ },
353
+ };
354
+ }, _AccountsController_listSnapAccounts =
355
+ /**
356
+ * Returns a list of internal accounts created using the SnapKeyring.
357
+ *
358
+ * @returns A promise that resolves to an array of InternalAccount objects.
359
+ */
360
+ async function _AccountsController_listSnapAccounts() {
361
+ const [snapKeyring] = this.messagingSystem.call('KeyringController:getKeyringsByType', eth_snap_keyring_1.SnapKeyring.type);
362
+ // snap keyring is not available until the first account is created in the keyring controller
363
+ if (!snapKeyring) {
364
+ return [];
365
+ }
366
+ const snapAccounts = snapKeyring.listAccounts();
367
+ return snapAccounts;
368
+ }, _AccountsController_listNormalAccounts =
369
+ /**
370
+ * Returns a list of normal accounts.
371
+ * Note: listNormalAccounts is a temporary method until the keyrings all implement the InternalAccount interface.
372
+ * Once all keyrings implement the InternalAccount interface, this method can be removed and getAccounts can be used instead.
373
+ *
374
+ * @returns A Promise that resolves to an array of InternalAccount objects.
375
+ */
376
+ async function _AccountsController_listNormalAccounts() {
377
+ const addresses = await this.messagingSystem.call('KeyringController:getAccounts');
378
+ const internalAccounts = [];
379
+ for (const address of addresses) {
380
+ const keyring = await this.messagingSystem.call('KeyringController:getKeyringForAccount', address);
381
+ const keyringType = keyring.type;
382
+ if (!(0, utils_2.isNormalKeyringType)(keyringType)) {
383
+ // We only consider "normal accounts" here, so keep looping
384
+ continue;
385
+ }
386
+ const id = (0, utils_2.getUUIDFromAddressOfNormalAccount)(address);
387
+ const nameLastUpdatedAt = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, id, 'nameLastUpdatedAt');
388
+ internalAccounts.push({
389
+ id,
390
+ address,
391
+ options: {},
392
+ methods: [
393
+ keyring_api_1.EthMethod.PersonalSign,
394
+ keyring_api_1.EthMethod.Sign,
395
+ keyring_api_1.EthMethod.SignTransaction,
396
+ keyring_api_1.EthMethod.SignTypedDataV1,
397
+ keyring_api_1.EthMethod.SignTypedDataV3,
398
+ keyring_api_1.EthMethod.SignTypedDataV4,
399
+ ],
400
+ type: keyring_api_1.EthAccountType.Eoa,
401
+ metadata: {
402
+ name: __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, id, 'name') ?? '',
403
+ ...(nameLastUpdatedAt && { nameLastUpdatedAt }),
404
+ importTime: __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, id, 'importTime') ?? Date.now(),
405
+ lastSelected: __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, id, 'lastSelected') ?? 0,
406
+ keyring: {
407
+ type: keyring.type,
408
+ },
409
+ },
410
+ });
411
+ }
412
+ return internalAccounts;
413
+ }, _AccountsController_handleOnKeyringStateChange = function _AccountsController_handleOnKeyringStateChange(keyringState) {
414
+ // check if there are any new accounts added
415
+ // TODO: change when accountAdded event is added to the keyring controller
416
+ // We check for keyrings length to be greater than 0 because the extension client may try execute
417
+ // submit password twice and clear the keyring state.
418
+ // https://github.com/MetaMask/KeyringController/blob/2d73a4deed8d013913f6ef0c9f5c0bb7c614f7d3/src/KeyringController.ts#L910
419
+ if (keyringState.isUnlocked && keyringState.keyrings.length > 0) {
420
+ const updatedNormalKeyringAddresses = [];
421
+ const updatedSnapKeyringAddresses = [];
422
+ for (const keyring of keyringState.keyrings) {
423
+ if (keyring.type === keyring_controller_1.KeyringTypes.snap) {
424
+ updatedSnapKeyringAddresses.push(...keyring.accounts.map((address) => {
425
+ return {
426
+ address,
427
+ type: keyring.type,
428
+ };
429
+ }));
430
+ }
431
+ else {
432
+ updatedNormalKeyringAddresses.push(...keyring.accounts.map((address) => {
433
+ return {
434
+ address,
435
+ type: keyring.type,
436
+ };
437
+ }));
438
+ }
439
+ }
440
+ const { previousNormalInternalAccounts, previousSnapInternalAccounts } = this.listMultichainAccounts().reduce((accumulator, account) => {
441
+ if (account.metadata.keyring.type === keyring_controller_1.KeyringTypes.snap) {
442
+ accumulator.previousSnapInternalAccounts.push(account);
443
+ }
444
+ else {
445
+ accumulator.previousNormalInternalAccounts.push(account);
446
+ }
447
+ return accumulator;
448
+ }, {
449
+ previousNormalInternalAccounts: [],
450
+ previousSnapInternalAccounts: [],
451
+ });
452
+ const addedAccounts = [];
453
+ const deletedAccounts = [];
454
+ // snap account ids are random uuid while normal accounts
455
+ // are determininistic based on the address
456
+ // ^NOTE: This will be removed when normal accounts also implement internal accounts
457
+ // finding all the normal accounts that were added
458
+ for (const account of updatedNormalKeyringAddresses) {
459
+ if (!this.state.internalAccounts.accounts[(0, utils_2.getUUIDFromAddressOfNormalAccount)(account.address)]) {
460
+ addedAccounts.push(account);
461
+ }
462
+ }
463
+ // finding all the snap accounts that were added
464
+ for (const account of updatedSnapKeyringAddresses) {
465
+ if (!previousSnapInternalAccounts.find((internalAccount) => internalAccount.address.toLowerCase() ===
466
+ account.address.toLowerCase())) {
467
+ addedAccounts.push(account);
468
+ }
469
+ }
470
+ // finding all the normal accounts that were deleted
471
+ for (const account of previousNormalInternalAccounts) {
472
+ if (!updatedNormalKeyringAddresses.find(({ address }) => address.toLowerCase() === account.address.toLowerCase())) {
473
+ deletedAccounts.push(account);
474
+ }
475
+ }
476
+ // finding all the snap accounts that were deleted
477
+ for (const account of previousSnapInternalAccounts) {
478
+ if (!updatedSnapKeyringAddresses.find(({ address }) => address.toLowerCase() === account.address.toLowerCase())) {
479
+ deletedAccounts.push(account);
480
+ }
481
+ }
482
+ this.update((currentState) => {
483
+ if (deletedAccounts.length > 0) {
484
+ for (const account of deletedAccounts) {
485
+ currentState.internalAccounts.accounts = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_handleAccountRemoved).call(this, currentState.internalAccounts.accounts, account.id);
486
+ }
487
+ }
488
+ if (addedAccounts.length > 0) {
489
+ for (const account of addedAccounts) {
490
+ currentState.internalAccounts.accounts =
491
+ __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_handleNewAccountAdded).call(this, currentState.internalAccounts.accounts, account);
492
+ }
493
+ }
494
+ // We don't use list accounts because it is not the updated state yet.
495
+ const existingAccounts = Object.values(currentState.internalAccounts.accounts);
496
+ // handle if the selected account was deleted
497
+ if (!currentState.internalAccounts.accounts[this.state.internalAccounts.selectedAccount]) {
498
+ const lastSelectedAccount = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getLastSelectedAccount).call(this, existingAccounts);
499
+ if (lastSelectedAccount) {
500
+ currentState.internalAccounts.selectedAccount =
501
+ lastSelectedAccount.id;
502
+ currentState.internalAccounts.accounts[lastSelectedAccount.id].metadata.lastSelected = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getLastSelectedIndex).call(this);
503
+ __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_publishAccountChangeEvent).call(this, lastSelectedAccount);
504
+ }
505
+ else {
506
+ // It will be undefined if there are no accounts
507
+ currentState.internalAccounts.selectedAccount = '';
508
+ }
509
+ }
510
+ });
511
+ }
512
+ }, _AccountsController_handleOnSnapStateChange = function _AccountsController_handleOnSnapStateChange(snapState) {
513
+ // only check if snaps changed in status
514
+ const { snaps } = snapState;
515
+ const accounts = this.listMultichainAccounts().filter((account) => account.metadata.snap);
516
+ this.update((currentState) => {
517
+ accounts.forEach((account) => {
518
+ const currentAccount = currentState.internalAccounts.accounts[account.id];
519
+ if (currentAccount.metadata.snap) {
520
+ const snapId = currentAccount.metadata.snap.id;
521
+ const storedSnap = snaps[snapId];
522
+ if (storedSnap) {
523
+ currentAccount.metadata.snap.enabled =
524
+ storedSnap.enabled && !storedSnap.blocked;
525
+ }
526
+ }
527
+ });
528
+ });
529
+ }, _AccountsController_getAccountsByKeyringType = function _AccountsController_getAccountsByKeyringType(keyringType, accounts) {
530
+ return (accounts ?? this.listMultichainAccounts()).filter((internalAccount) => {
531
+ // We do consider `hd` and `simple` keyrings to be of same type. So we check those 2 types
532
+ // to group those accounts together!
533
+ if (keyringType === keyring_controller_1.KeyringTypes.hd ||
534
+ keyringType === keyring_controller_1.KeyringTypes.simple) {
535
+ return (internalAccount.metadata.keyring.type === keyring_controller_1.KeyringTypes.hd ||
536
+ internalAccount.metadata.keyring.type === keyring_controller_1.KeyringTypes.simple);
537
+ }
538
+ return internalAccount.metadata.keyring.type === keyringType;
539
+ });
540
+ }, _AccountsController_getLastSelectedAccount = function _AccountsController_getLastSelectedAccount(accounts) {
541
+ const [accountToSelect] = accounts.sort((accountA, accountB) => {
542
+ // sort by lastSelected descending
543
+ return ((accountB.metadata.lastSelected ?? 0) -
544
+ (accountA.metadata.lastSelected ?? 0));
545
+ });
546
+ return accountToSelect;
547
+ }, _AccountsController_isAccountCompatibleWithChain = function _AccountsController_isAccountCompatibleWithChain(account, chainId) {
548
+ // TODO: Change this logic to not use account's type
549
+ // Because we currently only use type, we can only use namespace for now.
550
+ return account.type.startsWith((0, utils_1.parseCaipChainId)(chainId).namespace);
551
+ }, _AccountsController_getLastSelectedIndex = function _AccountsController_getLastSelectedIndex() {
552
+ // NOTE: For now we use the current date, since we know this value
553
+ // will always be higher than any already selected account index.
554
+ return Date.now();
555
+ }, _AccountsController_handleNewAccountAdded = function _AccountsController_handleNewAccountAdded(accountsState, account) {
556
+ let newAccount;
557
+ if (account.type !== keyring_controller_1.KeyringTypes.snap) {
558
+ newAccount = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_generateInternalAccountForNonSnapAccount).call(this, account.address, account.type);
559
+ }
560
+ else {
561
+ const [snapKeyring] = this.messagingSystem.call('KeyringController:getKeyringsByType', eth_snap_keyring_1.SnapKeyring.type);
562
+ newAccount = snapKeyring.getAccountByAddress(account.address);
563
+ // The snap deleted the account before the keyring controller could add it
564
+ if (!newAccount) {
565
+ return accountsState;
566
+ }
567
+ }
568
+ const isFirstAccount = Object.keys(accountsState).length === 0;
569
+ // Get next account name available for this given keyring
570
+ const accountName = this.getNextAvailableAccountName(newAccount.metadata.keyring.type, Object.values(accountsState));
571
+ const newAccountWithUpdatedMetadata = {
572
+ ...newAccount,
573
+ metadata: {
574
+ ...newAccount.metadata,
575
+ name: accountName,
576
+ importTime: Date.now(),
577
+ lastSelected: isFirstAccount ? __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getLastSelectedIndex).call(this) : 0,
578
+ },
579
+ };
580
+ accountsState[newAccount.id] = newAccountWithUpdatedMetadata;
581
+ this.messagingSystem.publish('AccountsController:accountAdded', newAccountWithUpdatedMetadata);
582
+ return accountsState;
583
+ }, _AccountsController_publishAccountChangeEvent = function _AccountsController_publishAccountChangeEvent(account) {
584
+ if ((0, keyring_api_1.isEvmAccountType)(account.type)) {
585
+ this.messagingSystem.publish('AccountsController:selectedEvmAccountChange', account);
586
+ }
587
+ this.messagingSystem.publish('AccountsController:selectedAccountChange', account);
588
+ }, _AccountsController_handleAccountRemoved = function _AccountsController_handleAccountRemoved(accountsState, accountId) {
589
+ delete accountsState[accountId];
590
+ this.messagingSystem.publish('AccountsController:accountRemoved', accountId);
591
+ return accountsState;
592
+ }, _AccountsController_populateExistingMetadata = function _AccountsController_populateExistingMetadata(accountId, metadataKey, account) {
593
+ const internalAccount = account ?? this.getAccount(accountId);
594
+ return internalAccount ? internalAccount.metadata[metadataKey] : undefined;
595
+ }, _AccountsController_registerMessageHandlers = function _AccountsController_registerMessageHandlers() {
596
+ this.messagingSystem.registerActionHandler(`${controllerName}:setSelectedAccount`, this.setSelectedAccount.bind(this));
597
+ this.messagingSystem.registerActionHandler(`${controllerName}:listAccounts`, this.listAccounts.bind(this));
598
+ this.messagingSystem.registerActionHandler(`${controllerName}:listMultichainAccounts`, this.listMultichainAccounts.bind(this));
599
+ this.messagingSystem.registerActionHandler(`${controllerName}:setAccountName`, this.setAccountName.bind(this));
600
+ this.messagingSystem.registerActionHandler(`${controllerName}:updateAccounts`, this.updateAccounts.bind(this));
601
+ this.messagingSystem.registerActionHandler(`${controllerName}:getSelectedAccount`, this.getSelectedAccount.bind(this));
602
+ this.messagingSystem.registerActionHandler(`${controllerName}:getSelectedMultichainAccount`, this.getSelectedMultichainAccount.bind(this));
603
+ this.messagingSystem.registerActionHandler(`${controllerName}:getAccountByAddress`, this.getAccountByAddress.bind(this));
604
+ this.messagingSystem.registerActionHandler(`${controllerName}:getNextAvailableAccountName`, this.getNextAvailableAccountName.bind(this));
605
+ this.messagingSystem.registerActionHandler(`AccountsController:getAccount`, this.getAccount.bind(this));
606
+ this.messagingSystem.registerActionHandler(`AccountsController:updateAccountMetadata`, this.updateAccountMetadata.bind(this));
607
+ };
608
+ //# sourceMappingURL=AccountsController.cjs.map