@metamask-previews/keyring-controller 14.0.1-preview-f04be62f → 15.0.0-preview-fb52b9eb

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.
@@ -3,7 +3,7 @@ import {
3
3
  __privateGet,
4
4
  __privateMethod,
5
5
  __privateSet
6
- } from "./chunk-NAAWD7HX.mjs";
6
+ } from "./chunk-HT7WOORD.mjs";
7
7
 
8
8
  // src/KeyringController.ts
9
9
  import { isValidPrivate, toBuffer, getBinarySize } from "@ethereumjs/util";
@@ -86,7 +86,7 @@ async function displayForKeyring(keyring) {
86
86
  accounts: accounts.map(normalize)
87
87
  };
88
88
  }
89
- var _keyringBuilders, _keyrings, _unsupportedKeyrings, _password, _encryptor, _cacheEncryptionKey, _qrKeyringStateListener, _registerMessageHandlers, registerMessageHandlers_fn, _getKeyringBuilderForType, getKeyringBuilderForType_fn, _addQRKeyring, addQRKeyring_fn, _subscribeToQRKeyringEvents, subscribeToQRKeyringEvents_fn, _unsubscribeFromQRKeyringsEvents, unsubscribeFromQRKeyringsEvents_fn, _createNewVaultWithKeyring, createNewVaultWithKeyring_fn, _updateKeyringsInState, updateKeyringsInState_fn, _unlockKeyrings, unlockKeyrings_fn, _createKeyringWithFirstAccount, createKeyringWithFirstAccount_fn, _newKeyring, newKeyring_fn, _clearKeyrings, clearKeyrings_fn, _restoreKeyring, restoreKeyring_fn, _destroyKeyring, destroyKeyring_fn, _removeEmptyKeyrings, removeEmptyKeyrings_fn, _checkForDuplicate, checkForDuplicate_fn, _setUnlocked, setUnlocked_fn, _getMemState, getMemState_fn;
89
+ var _controllerOperationMutex, _vaultOperationMutex, _keyringBuilders, _keyrings, _unsupportedKeyrings, _password, _encryptor, _cacheEncryptionKey, _qrKeyringStateListener, _registerMessageHandlers, registerMessageHandlers_fn, _getKeyringBuilderForType, getKeyringBuilderForType_fn, _addQRKeyring, addQRKeyring_fn, _subscribeToQRKeyringEvents, subscribeToQRKeyringEvents_fn, _unsubscribeFromQRKeyringsEvents, unsubscribeFromQRKeyringsEvents_fn, _createNewVaultWithKeyring, createNewVaultWithKeyring_fn, _getUpdatedKeyrings, getUpdatedKeyrings_fn, _unlockKeyrings, unlockKeyrings_fn, _updateVault, updateVault_fn, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn, _createKeyringWithFirstAccount, createKeyringWithFirstAccount_fn, _newKeyring, newKeyring_fn, _clearKeyrings, clearKeyrings_fn, _restoreKeyring, restoreKeyring_fn, _destroyKeyring, destroyKeyring_fn, _removeEmptyKeyrings, removeEmptyKeyrings_fn, _checkForDuplicate, checkForDuplicate_fn, _setUnlocked, setUnlocked_fn, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn, _withControllerLock, withControllerLock_fn, _withVaultLock, withVaultLock_fn;
90
90
  var KeyringController = class extends BaseController {
91
91
  /**
92
92
  * Creates a KeyringController instance.
@@ -164,9 +164,12 @@ var KeyringController = class extends BaseController {
164
164
  */
165
165
  __privateAdd(this, _createNewVaultWithKeyring);
166
166
  /**
167
- * Update the controller state with its current keyrings.
167
+ * Get the updated array of each keyring's type and
168
+ * accounts list.
169
+ *
170
+ * @returns A promise resolving to the updated keyrings array.
168
171
  */
169
- __privateAdd(this, _updateKeyringsInState);
172
+ __privateAdd(this, _getUpdatedKeyrings);
170
173
  /**
171
174
  * Unlock Keyrings, decrypting the vault and deserializing all
172
175
  * keyrings contained in it, using a password or an encryption key with salt.
@@ -177,6 +180,19 @@ var KeyringController = class extends BaseController {
177
180
  * @returns A promise resolving to the deserialized keyrings array.
178
181
  */
179
182
  __privateAdd(this, _unlockKeyrings);
183
+ /**
184
+ * Update the vault with the current keyrings.
185
+ *
186
+ * @returns A promise resolving to `true` if the operation is successful.
187
+ */
188
+ __privateAdd(this, _updateVault);
189
+ /**
190
+ * Retrieves all the accounts from keyrings instances
191
+ * that are currently in memory.
192
+ *
193
+ * @returns A promise resolving to an array of accounts.
194
+ */
195
+ __privateAdd(this, _getAccountsFromKeyrings);
180
196
  /**
181
197
  * Create a new keyring, ensuring that the first account is
182
198
  * also created.
@@ -191,14 +207,20 @@ var KeyringController = class extends BaseController {
191
207
  * using the given `opts`. The keyring is built using the keyring builder
192
208
  * registered for the given `type`.
193
209
  *
210
+ *
194
211
  * @param type - The type of keyring to add.
195
212
  * @param data - The data to restore a previously serialized keyring.
213
+ * @param persist - Whether to persist the keyring to the vault.
196
214
  * @returns The new keyring.
215
+ * @throws If the keyring includes duplicated accounts.
197
216
  */
198
217
  __privateAdd(this, _newKeyring);
199
218
  /**
200
219
  * Remove all managed keyrings, destroying all their
201
220
  * instances in memory.
221
+ *
222
+ * @param options - Operations options.
223
+ * @param options.skipStateUpdate - Whether to skip updating the controller state.
202
224
  */
203
225
  __privateAdd(this, _clearKeyrings);
204
226
  /**
@@ -244,8 +266,39 @@ var KeyringController = class extends BaseController {
244
266
  * @fires KeyringController:unlock
245
267
  */
246
268
  __privateAdd(this, _setUnlocked);
247
- __privateAdd(this, _getMemState);
248
- this.mutex = new Mutex();
269
+ /**
270
+ * Assert that the controller mutex is locked.
271
+ *
272
+ * @throws If the controller mutex is not locked.
273
+ */
274
+ __privateAdd(this, _assertControllerMutexIsLocked);
275
+ /**
276
+ * Lock the controller mutex before executing the given function,
277
+ * and release it after the function is resolved or after an
278
+ * error is thrown.
279
+ *
280
+ * This wrapper ensures that each mutable operation that interacts with the
281
+ * controller and that changes its state is executed in a mutually exclusive way,
282
+ * preventing unsafe concurrent access that could lead to unpredictable behavior.
283
+ *
284
+ * @param fn - The function to execute while the controller mutex is locked.
285
+ * @returns The result of the function.
286
+ */
287
+ __privateAdd(this, _withControllerLock);
288
+ /**
289
+ * Lock the vault mutex before executing the given function,
290
+ * and release it after the function is resolved or after an
291
+ * error is thrown.
292
+ *
293
+ * This ensures that each operation that interacts with the vault
294
+ * is executed in a mutually exclusive way.
295
+ *
296
+ * @param fn - The function to execute while the vault mutex is locked.
297
+ * @returns The result of the function.
298
+ */
299
+ __privateAdd(this, _withVaultLock);
300
+ __privateAdd(this, _controllerOperationMutex, new Mutex());
301
+ __privateAdd(this, _vaultOperationMutex, new Mutex());
249
302
  __privateAdd(this, _keyringBuilders, void 0);
250
303
  __privateAdd(this, _keyrings, void 0);
251
304
  __privateAdd(this, _unsupportedKeyrings, void 0);
@@ -268,73 +321,74 @@ var KeyringController = class extends BaseController {
268
321
  *
269
322
  * @param accountCount - Number of accounts before adding a new one, used to
270
323
  * make the method idempotent.
271
- * @returns Promise resolving to keyring current state and added account
272
- * address.
324
+ * @returns Promise resolving to the added account address.
273
325
  */
274
326
  async addNewAccount(accountCount) {
275
- const primaryKeyring = this.getKeyringsByType("HD Key Tree")[0];
276
- if (!primaryKeyring) {
277
- throw new Error("No HD keyring found");
278
- }
279
- const oldAccounts = await this.getAccounts();
280
- if (accountCount && oldAccounts.length !== accountCount) {
281
- if (accountCount > oldAccounts.length) {
282
- throw new Error("Account out of sequence");
327
+ return __privateMethod(this, _withControllerLock, withControllerLock_fn).call(this, async () => {
328
+ const primaryKeyring = this.getKeyringsByType("HD Key Tree")[0];
329
+ if (!primaryKeyring) {
330
+ throw new Error("No HD keyring found");
283
331
  }
284
- const primaryKeyringAccounts = await primaryKeyring.getAccounts();
285
- return {
286
- keyringState: __privateMethod(this, _getMemState, getMemState_fn).call(this),
287
- addedAccountAddress: primaryKeyringAccounts[accountCount]
288
- };
289
- }
290
- const addedAccountAddress = await this.addNewAccountForKeyring(
291
- primaryKeyring
292
- );
293
- await this.verifySeedPhrase();
294
- return {
295
- keyringState: __privateMethod(this, _getMemState, getMemState_fn).call(this),
296
- addedAccountAddress
297
- };
332
+ const oldAccounts = await primaryKeyring.getAccounts();
333
+ if (accountCount && oldAccounts.length !== accountCount) {
334
+ if (accountCount > oldAccounts.length) {
335
+ throw new Error("Account out of sequence");
336
+ }
337
+ const existingAccount = oldAccounts[accountCount];
338
+ if (!existingAccount) {
339
+ throw new Error(`Can't find account at index ${accountCount}`);
340
+ }
341
+ return existingAccount;
342
+ }
343
+ const [addedAccountAddress] = await primaryKeyring.addAccounts(1);
344
+ await this.verifySeedPhrase();
345
+ await __privateMethod(this, _updateVault, updateVault_fn).call(this);
346
+ return addedAccountAddress;
347
+ });
298
348
  }
299
349
  /**
300
350
  * Adds a new account to the specified keyring.
301
351
  *
302
352
  * @param keyring - Keyring to add the account to.
303
353
  * @param accountCount - Number of accounts before adding a new one, used to make the method idempotent.
304
- * @returns Promise resolving to keyring current state and added account
354
+ * @returns Promise resolving to the added account address
305
355
  */
306
356
  async addNewAccountForKeyring(keyring, accountCount) {
307
- const oldAccounts = await this.getAccounts();
308
- if (accountCount && oldAccounts.length !== accountCount) {
309
- if (accountCount > oldAccounts.length) {
310
- throw new Error("Account out of sequence");
357
+ return __privateMethod(this, _withControllerLock, withControllerLock_fn).call(this, async () => {
358
+ const oldAccounts = await __privateMethod(this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this);
359
+ if (accountCount && oldAccounts.length !== accountCount) {
360
+ if (accountCount > oldAccounts.length) {
361
+ throw new Error("Account out of sequence");
362
+ }
363
+ const existingAccount = oldAccounts[accountCount];
364
+ assertIsStrictHexString(existingAccount);
365
+ return existingAccount;
311
366
  }
312
- const existingAccount = oldAccounts[accountCount];
313
- assertIsStrictHexString(existingAccount);
314
- return existingAccount;
315
- }
316
- await keyring.addAccounts(1);
317
- await this.persistAllKeyrings();
318
- const addedAccountAddress = (await this.getAccounts()).find(
319
- (selectedAddress) => !oldAccounts.includes(selectedAddress)
320
- );
321
- assertIsStrictHexString(addedAccountAddress);
322
- return addedAccountAddress;
367
+ await keyring.addAccounts(1);
368
+ await __privateMethod(this, _updateVault, updateVault_fn).call(this);
369
+ const addedAccountAddress = (await __privateMethod(this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this)).find(
370
+ (selectedAddress) => !oldAccounts.includes(selectedAddress)
371
+ );
372
+ assertIsStrictHexString(addedAccountAddress);
373
+ return addedAccountAddress;
374
+ });
323
375
  }
324
376
  /**
325
377
  * Adds a new account to the default (first) HD seed phrase keyring without updating identities in preferences.
326
378
  *
327
- * @returns Promise resolving to current state when the account is added.
379
+ * @returns Promise resolving to the added account address.
328
380
  */
329
381
  async addNewAccountWithoutUpdate() {
330
- const primaryKeyring = this.getKeyringsByType("HD Key Tree")[0];
331
- if (!primaryKeyring) {
332
- throw new Error("No HD keyring found");
333
- }
334
- await primaryKeyring.addAccounts(1);
335
- await this.persistAllKeyrings();
336
- await this.verifySeedPhrase();
337
- return __privateMethod(this, _getMemState, getMemState_fn).call(this);
382
+ return __privateMethod(this, _withControllerLock, withControllerLock_fn).call(this, async () => {
383
+ const primaryKeyring = this.getKeyringsByType("HD Key Tree")[0];
384
+ if (!primaryKeyring) {
385
+ throw new Error("No HD keyring found");
386
+ }
387
+ const [addedAccountAddress] = await primaryKeyring.addAccounts(1);
388
+ await __privateMethod(this, _updateVault, updateVault_fn).call(this);
389
+ await this.verifySeedPhrase();
390
+ return addedAccountAddress;
391
+ });
338
392
  }
339
393
  /**
340
394
  * Effectively the same as creating a new keychain then populating it
@@ -343,14 +397,13 @@ var KeyringController = class extends BaseController {
343
397
  * @param password - Password to unlock keychain.
344
398
  * @param seed - A BIP39-compliant seed phrase as Uint8Array,
345
399
  * either as a string or an array of UTF-8 bytes that represent the string.
346
- * @returns Promise resolving to the restored keychain object.
400
+ * @returns Promise resolving when the operation ends successfully.
347
401
  */
348
402
  async createNewVaultAndRestore(password, seed) {
349
- const releaseLock = await this.mutex.acquire();
350
- if (!password || !password.length) {
351
- throw new Error("Invalid password");
352
- }
353
- try {
403
+ return __privateMethod(this, _withControllerLock, withControllerLock_fn).call(this, async () => {
404
+ if (!password || !password.length) {
405
+ throw new Error("Invalid password");
406
+ }
354
407
  await __privateMethod(this, _createNewVaultWithKeyring, createNewVaultWithKeyring_fn).call(this, password, {
355
408
  type: "HD Key Tree" /* hd */,
356
409
  opts: {
@@ -358,30 +411,23 @@ var KeyringController = class extends BaseController {
358
411
  numberOfAccounts: 1
359
412
  }
360
413
  });
361
- return __privateMethod(this, _getMemState, getMemState_fn).call(this);
362
- } finally {
363
- releaseLock();
364
- }
414
+ });
365
415
  }
366
416
  /**
367
417
  * Create a new primary keychain and wipe any previous keychains.
368
418
  *
369
419
  * @param password - Password to unlock the new vault.
370
- * @returns Newly-created keychain object.
420
+ * @returns Promise resolving when the operation ends successfully.
371
421
  */
372
422
  async createNewVaultAndKeychain(password) {
373
- const releaseLock = await this.mutex.acquire();
374
- try {
375
- const accounts = await this.getAccounts();
423
+ return __privateMethod(this, _withControllerLock, withControllerLock_fn).call(this, async () => {
424
+ const accounts = await __privateMethod(this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this);
376
425
  if (!accounts.length) {
377
426
  await __privateMethod(this, _createNewVaultWithKeyring, createNewVaultWithKeyring_fn).call(this, password, {
378
427
  type: "HD Key Tree" /* hd */
379
428
  });
380
429
  }
381
- return __privateMethod(this, _getMemState, getMemState_fn).call(this);
382
- } finally {
383
- releaseLock();
384
- }
430
+ });
385
431
  }
386
432
  /**
387
433
  * Adds a new keyring of the given `type`.
@@ -395,21 +441,7 @@ var KeyringController = class extends BaseController {
395
441
  if (type === "QR Hardware Wallet Device" /* qr */) {
396
442
  return this.getOrAddQRKeyring();
397
443
  }
398
- const keyring = await __privateMethod(this, _newKeyring, newKeyring_fn).call(this, type, opts);
399
- if (type === "HD Key Tree" /* hd */ && (!isObject(opts) || !opts.mnemonic)) {
400
- if (!keyring.generateRandomMnemonic) {
401
- throw new Error(
402
- "KeyringController - The current keyring does not support the method generateRandomMnemonic." /* UnsupportedGenerateRandomMnemonic */
403
- );
404
- }
405
- keyring.generateRandomMnemonic();
406
- await keyring.addAccounts(1);
407
- }
408
- const accounts = await keyring.getAccounts();
409
- await __privateMethod(this, _checkForDuplicate, checkForDuplicate_fn).call(this, type, accounts);
410
- __privateGet(this, _keyrings).push(keyring);
411
- await this.persistAllKeyrings();
412
- return keyring;
444
+ return __privateMethod(this, _withControllerLock, withControllerLock_fn).call(this, async () => __privateMethod(this, _newKeyring, newKeyring_fn).call(this, type, opts, true));
413
445
  }
414
446
  /**
415
447
  * Method to verify a given password validity. Throws an
@@ -460,19 +492,15 @@ var KeyringController = class extends BaseController {
460
492
  return await keyring.exportAccount(normalize(address));
461
493
  }
462
494
  /**
463
- * Returns the public addresses of all accounts for the current keyring.
495
+ * Returns the public addresses of all accounts from every keyring.
464
496
  *
465
497
  * @returns A promise resolving to an array of addresses.
466
498
  */
467
499
  async getAccounts() {
468
- const keyrings = __privateGet(this, _keyrings);
469
- const keyringArrays = await Promise.all(
470
- keyrings.map(async (keyring) => keyring.getAccounts())
500
+ return this.state.keyrings.reduce(
501
+ (accounts, keyring) => accounts.concat(keyring.accounts),
502
+ []
471
503
  );
472
- const addresses = keyringArrays.reduce((res, arr) => {
473
- return res.concat(arr);
474
- }, []);
475
- return addresses.map(normalize);
476
504
  }
477
505
  /**
478
506
  * Get encryption public key.
@@ -565,60 +593,7 @@ var KeyringController = class extends BaseController {
565
593
  * operation completes.
566
594
  */
567
595
  async persistAllKeyrings() {
568
- const { encryptionKey, encryptionSalt } = this.state;
569
- if (!__privateGet(this, _password) && !encryptionKey) {
570
- throw new Error("KeyringController - Cannot persist vault without password and encryption key" /* MissingCredentials */);
571
- }
572
- const serializedKeyrings = await Promise.all(
573
- __privateGet(this, _keyrings).map(async (keyring) => {
574
- const [type, data] = await Promise.all([
575
- keyring.type,
576
- keyring.serialize()
577
- ]);
578
- return { type, data };
579
- })
580
- );
581
- serializedKeyrings.push(...__privateGet(this, _unsupportedKeyrings));
582
- let vault;
583
- let newEncryptionKey;
584
- if (__privateGet(this, _cacheEncryptionKey)) {
585
- assertIsExportableKeyEncryptor(__privateGet(this, _encryptor));
586
- if (encryptionKey) {
587
- const key = await __privateGet(this, _encryptor).importKey(encryptionKey);
588
- const vaultJSON = await __privateGet(this, _encryptor).encryptWithKey(
589
- key,
590
- serializedKeyrings
591
- );
592
- vaultJSON.salt = encryptionSalt;
593
- vault = JSON.stringify(vaultJSON);
594
- } else if (__privateGet(this, _password)) {
595
- const { vault: newVault, exportedKeyString } = await __privateGet(this, _encryptor).encryptWithDetail(
596
- __privateGet(this, _password),
597
- serializedKeyrings
598
- );
599
- vault = newVault;
600
- newEncryptionKey = exportedKeyString;
601
- }
602
- } else {
603
- if (typeof __privateGet(this, _password) !== "string") {
604
- throw new TypeError("KeyringController - Password must be of type string." /* WrongPasswordType */);
605
- }
606
- vault = await __privateGet(this, _encryptor).encrypt(__privateGet(this, _password), serializedKeyrings);
607
- }
608
- if (!vault) {
609
- throw new Error("KeyringController - Cannot persist vault without vault information" /* MissingVaultData */);
610
- }
611
- this.update((state) => {
612
- state.vault = vault;
613
- });
614
- await __privateMethod(this, _updateKeyringsInState, updateKeyringsInState_fn).call(this);
615
- if (newEncryptionKey) {
616
- this.update((state) => {
617
- state.encryptionKey = newEncryptionKey;
618
- state.encryptionSalt = JSON.parse(vault).salt;
619
- });
620
- }
621
- return true;
596
+ return __privateMethod(this, _withControllerLock, withControllerLock_fn).call(this, async () => __privateMethod(this, _updateVault, updateVault_fn).call(this));
622
597
  }
623
598
  /**
624
599
  * Imports an account with the specified import strategy.
@@ -626,90 +601,88 @@ var KeyringController = class extends BaseController {
626
601
  * @param strategy - Import strategy name.
627
602
  * @param args - Array of arguments to pass to the underlying stategy.
628
603
  * @throws Will throw when passed an unrecognized strategy.
629
- * @returns Promise resolving to keyring current state and imported account
630
- * address.
604
+ * @returns Promise resolving to the imported account address.
631
605
  */
632
606
  async importAccountWithStrategy(strategy, args) {
633
- let privateKey;
634
- switch (strategy) {
635
- case "privateKey":
636
- const [importedKey] = args;
637
- if (!importedKey) {
638
- throw new Error("Cannot import an empty key.");
639
- }
640
- const prefixed = add0x(importedKey);
641
- let bufferedPrivateKey;
642
- try {
643
- bufferedPrivateKey = toBuffer(prefixed);
644
- } catch {
645
- throw new Error("Cannot import invalid private key.");
646
- }
647
- if (!isValidPrivate(bufferedPrivateKey) || // ensures that the key is 64 bytes long
648
- getBinarySize(prefixed) !== 64 + "0x".length) {
649
- throw new Error("Cannot import invalid private key.");
650
- }
651
- privateKey = remove0x(prefixed);
652
- break;
653
- case "json":
654
- let wallet;
655
- const [input, password] = args;
656
- try {
657
- wallet = importers.fromEtherWallet(input, password);
658
- } catch (e) {
659
- wallet = wallet || await Wallet.fromV3(input, password, true);
660
- }
661
- privateKey = bytesToHex(wallet.getPrivateKey());
662
- break;
663
- default:
664
- throw new Error(`Unexpected import strategy: '${strategy}'`);
665
- }
666
- const newKeyring = await this.addNewKeyring("Simple Key Pair" /* simple */, [
667
- privateKey
668
- ]);
669
- const accounts = await newKeyring.getAccounts();
670
- return {
671
- keyringState: __privateMethod(this, _getMemState, getMemState_fn).call(this),
672
- importedAccountAddress: accounts[0]
673
- };
607
+ return __privateMethod(this, _withControllerLock, withControllerLock_fn).call(this, async () => {
608
+ let privateKey;
609
+ switch (strategy) {
610
+ case "privateKey":
611
+ const [importedKey] = args;
612
+ if (!importedKey) {
613
+ throw new Error("Cannot import an empty key.");
614
+ }
615
+ const prefixed = add0x(importedKey);
616
+ let bufferedPrivateKey;
617
+ try {
618
+ bufferedPrivateKey = toBuffer(prefixed);
619
+ } catch {
620
+ throw new Error("Cannot import invalid private key.");
621
+ }
622
+ if (!isValidPrivate(bufferedPrivateKey) || // ensures that the key is 64 bytes long
623
+ getBinarySize(prefixed) !== 64 + "0x".length) {
624
+ throw new Error("Cannot import invalid private key.");
625
+ }
626
+ privateKey = remove0x(prefixed);
627
+ break;
628
+ case "json":
629
+ let wallet;
630
+ const [input, password] = args;
631
+ try {
632
+ wallet = importers.fromEtherWallet(input, password);
633
+ } catch (e) {
634
+ wallet = wallet || await Wallet.fromV3(input, password, true);
635
+ }
636
+ privateKey = bytesToHex(wallet.getPrivateKey());
637
+ break;
638
+ default:
639
+ throw new Error(`Unexpected import strategy: '${strategy}'`);
640
+ }
641
+ const newKeyring = await __privateMethod(this, _newKeyring, newKeyring_fn).call(this, "Simple Key Pair" /* simple */, [privateKey], true);
642
+ const accounts = await newKeyring.getAccounts();
643
+ return accounts[0];
644
+ });
674
645
  }
675
646
  /**
676
647
  * Removes an account from keyring state.
677
648
  *
678
649
  * @param address - Address of the account to remove.
679
650
  * @fires KeyringController:accountRemoved
680
- * @returns Promise resolving current state when this account removal completes.
651
+ * @returns Promise resolving when the account is removed.
681
652
  */
682
653
  async removeAccount(address) {
683
- const keyring = await this.getKeyringForAccount(
684
- address
685
- );
686
- if (!keyring.removeAccount) {
687
- throw new Error("`KeyringController - The keyring for the current address does not support the method removeAccount" /* UnsupportedRemoveAccount */);
688
- }
689
- await keyring.removeAccount(address);
690
- const accounts = await keyring.getAccounts();
691
- if (accounts.length === 0) {
692
- await __privateMethod(this, _removeEmptyKeyrings, removeEmptyKeyrings_fn).call(this);
693
- }
694
- await this.persistAllKeyrings();
654
+ await __privateMethod(this, _withControllerLock, withControllerLock_fn).call(this, async () => {
655
+ const keyring = await this.getKeyringForAccount(
656
+ address
657
+ );
658
+ if (!keyring.removeAccount) {
659
+ throw new Error("`KeyringController - The keyring for the current address does not support the method removeAccount" /* UnsupportedRemoveAccount */);
660
+ }
661
+ await keyring.removeAccount(address);
662
+ const accounts = await keyring.getAccounts();
663
+ if (accounts.length === 0) {
664
+ await __privateMethod(this, _removeEmptyKeyrings, removeEmptyKeyrings_fn).call(this);
665
+ }
666
+ await __privateMethod(this, _updateVault, updateVault_fn).call(this);
667
+ });
695
668
  this.messagingSystem.publish(`${name}:accountRemoved`, address);
696
- return __privateMethod(this, _getMemState, getMemState_fn).call(this);
697
669
  }
698
670
  /**
699
671
  * Deallocates all secrets and locks the wallet.
700
672
  *
701
- * @returns Promise resolving to current state.
673
+ * @returns Promise resolving when the operation completes.
702
674
  */
703
675
  async setLocked() {
704
- __privateMethod(this, _unsubscribeFromQRKeyringsEvents, unsubscribeFromQRKeyringsEvents_fn).call(this);
705
- __privateSet(this, _password, void 0);
706
- this.update((state) => {
707
- state.isUnlocked = false;
708
- state.keyrings = [];
676
+ return __privateMethod(this, _withControllerLock, withControllerLock_fn).call(this, async () => {
677
+ __privateMethod(this, _unsubscribeFromQRKeyringsEvents, unsubscribeFromQRKeyringsEvents_fn).call(this);
678
+ __privateSet(this, _password, void 0);
679
+ this.update((state) => {
680
+ state.isUnlocked = false;
681
+ state.keyrings = [];
682
+ });
683
+ await __privateMethod(this, _clearKeyrings, clearKeyrings_fn).call(this);
684
+ this.messagingSystem.publish(`${name}:lock`);
709
685
  });
710
- await __privateMethod(this, _clearKeyrings, clearKeyrings_fn).call(this);
711
- this.messagingSystem.publish(`${name}:lock`);
712
- return __privateMethod(this, _getMemState, getMemState_fn).call(this);
713
686
  }
714
687
  /**
715
688
  * Signs message by calling down into a specific keyring.
@@ -803,9 +776,10 @@ var KeyringController = class extends BaseController {
803
776
  *
804
777
  * @param from - Address of the sender.
805
778
  * @param transactions - Base transactions to include in the UserOperation.
779
+ * @param executionContext - The execution context to use for the UserOperation.
806
780
  * @returns A pseudo-UserOperation that can be used to construct a real.
807
781
  */
808
- async prepareUserOperation(from, transactions) {
782
+ async prepareUserOperation(from, transactions, executionContext) {
809
783
  const address = normalize(from);
810
784
  const keyring = await this.getKeyringForAccount(
811
785
  address
@@ -813,7 +787,11 @@ var KeyringController = class extends BaseController {
813
787
  if (!keyring.prepareUserOperation) {
814
788
  throw new Error("KeyringController - The keyring for the current address does not support the method prepareUserOperation." /* UnsupportedPrepareUserOperation */);
815
789
  }
816
- return await keyring.prepareUserOperation(address, transactions);
790
+ return await keyring.prepareUserOperation(
791
+ address,
792
+ transactions,
793
+ executionContext
794
+ );
817
795
  }
818
796
  /**
819
797
  * Patches properties of a UserOperation. Currently, only the
@@ -821,9 +799,10 @@ var KeyringController = class extends BaseController {
821
799
  *
822
800
  * @param from - Address of the sender.
823
801
  * @param userOp - UserOperation to patch.
802
+ * @param executionContext - The execution context to use for the UserOperation.
824
803
  * @returns A patch to apply to the UserOperation.
825
804
  */
826
- async patchUserOperation(from, userOp) {
805
+ async patchUserOperation(from, userOp, executionContext) {
827
806
  const address = normalize(from);
828
807
  const keyring = await this.getKeyringForAccount(
829
808
  address
@@ -831,16 +810,17 @@ var KeyringController = class extends BaseController {
831
810
  if (!keyring.patchUserOperation) {
832
811
  throw new Error("KeyringController - The keyring for the current address does not support the method patchUserOperation." /* UnsupportedPatchUserOperation */);
833
812
  }
834
- return await keyring.patchUserOperation(address, userOp);
813
+ return await keyring.patchUserOperation(address, userOp, executionContext);
835
814
  }
836
815
  /**
837
816
  * Signs an UserOperation.
838
817
  *
839
818
  * @param from - Address of the sender.
840
819
  * @param userOp - UserOperation to sign.
820
+ * @param executionContext - The execution context to use for the UserOperation.
841
821
  * @returns The signature of the UserOperation.
842
822
  */
843
- async signUserOperation(from, userOp) {
823
+ async signUserOperation(from, userOp, executionContext) {
844
824
  const address = normalize(from);
845
825
  const keyring = await this.getKeyringForAccount(
846
826
  address
@@ -848,7 +828,7 @@ var KeyringController = class extends BaseController {
848
828
  if (!keyring.signUserOperation) {
849
829
  throw new Error("KeyringController - The keyring for the current address does not support the method signUserOperation." /* UnsupportedSignUserOperation */);
850
830
  }
851
- return await keyring.signUserOperation(address, userOp);
831
+ return await keyring.signUserOperation(address, userOp, executionContext);
852
832
  }
853
833
  /**
854
834
  * Attempts to decrypt the current vault and load its keyrings,
@@ -856,32 +836,34 @@ var KeyringController = class extends BaseController {
856
836
  *
857
837
  * @param encryptionKey - Key to unlock the keychain.
858
838
  * @param encryptionSalt - Salt to unlock the keychain.
859
- * @returns Promise resolving to the current state.
839
+ * @returns Promise resolving when the operation completes.
860
840
  */
861
841
  async submitEncryptionKey(encryptionKey, encryptionSalt) {
862
- __privateSet(this, _keyrings, await __privateMethod(this, _unlockKeyrings, unlockKeyrings_fn).call(this, void 0, encryptionKey, encryptionSalt));
863
- __privateMethod(this, _setUnlocked, setUnlocked_fn).call(this);
864
- const qrKeyring = this.getQRKeyring();
865
- if (qrKeyring) {
866
- __privateMethod(this, _subscribeToQRKeyringEvents, subscribeToQRKeyringEvents_fn).call(this, qrKeyring);
867
- }
868
- return __privateMethod(this, _getMemState, getMemState_fn).call(this);
842
+ return __privateMethod(this, _withControllerLock, withControllerLock_fn).call(this, async () => {
843
+ __privateSet(this, _keyrings, await __privateMethod(this, _unlockKeyrings, unlockKeyrings_fn).call(this, void 0, encryptionKey, encryptionSalt));
844
+ __privateMethod(this, _setUnlocked, setUnlocked_fn).call(this);
845
+ const qrKeyring = this.getQRKeyring();
846
+ if (qrKeyring) {
847
+ __privateMethod(this, _subscribeToQRKeyringEvents, subscribeToQRKeyringEvents_fn).call(this, qrKeyring);
848
+ }
849
+ });
869
850
  }
870
851
  /**
871
852
  * Attempts to decrypt the current vault and load its keyrings,
872
853
  * using the given password.
873
854
  *
874
855
  * @param password - Password to unlock the keychain.
875
- * @returns Promise resolving to the current state.
856
+ * @returns Promise resolving when the operation completes.
876
857
  */
877
858
  async submitPassword(password) {
878
- __privateSet(this, _keyrings, await __privateMethod(this, _unlockKeyrings, unlockKeyrings_fn).call(this, password));
879
- __privateMethod(this, _setUnlocked, setUnlocked_fn).call(this);
880
- const qrKeyring = this.getQRKeyring();
881
- if (qrKeyring) {
882
- __privateMethod(this, _subscribeToQRKeyringEvents, subscribeToQRKeyringEvents_fn).call(this, qrKeyring);
883
- }
884
- return __privateMethod(this, _getMemState, getMemState_fn).call(this);
859
+ return __privateMethod(this, _withControllerLock, withControllerLock_fn).call(this, async () => {
860
+ __privateSet(this, _keyrings, await __privateMethod(this, _unlockKeyrings, unlockKeyrings_fn).call(this, password));
861
+ __privateMethod(this, _setUnlocked, setUnlocked_fn).call(this);
862
+ const qrKeyring = this.getQRKeyring();
863
+ if (qrKeyring) {
864
+ __privateMethod(this, _subscribeToQRKeyringEvents, subscribeToQRKeyringEvents_fn).call(this, qrKeyring);
865
+ }
866
+ });
885
867
  }
886
868
  /**
887
869
  * Verifies the that the seed phrase restores the current keychain's accounts.
@@ -931,13 +913,16 @@ var KeyringController = class extends BaseController {
931
913
  * @returns The added keyring
932
914
  */
933
915
  async getOrAddQRKeyring() {
934
- return this.getQRKeyring() || await __privateMethod(this, _addQRKeyring, addQRKeyring_fn).call(this);
916
+ return this.getQRKeyring() || await __privateMethod(this, _withControllerLock, withControllerLock_fn).call(this, async () => __privateMethod(this, _addQRKeyring, addQRKeyring_fn).call(this));
935
917
  }
936
918
  // TODO: Replace `any` with type
937
919
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
938
920
  async restoreQRKeyring(serialized) {
939
- (await this.getOrAddQRKeyring()).deserialize(serialized);
940
- await this.persistAllKeyrings();
921
+ return __privateMethod(this, _withControllerLock, withControllerLock_fn).call(this, async () => {
922
+ const keyring = this.getQRKeyring() || await __privateMethod(this, _addQRKeyring, addQRKeyring_fn).call(this);
923
+ keyring.deserialize(serialized);
924
+ await __privateMethod(this, _updateVault, updateVault_fn).call(this);
925
+ });
941
926
  }
942
927
  async resetQRKeyringState() {
943
928
  (await this.getOrAddQRKeyring()).resetStore();
@@ -964,34 +949,38 @@ var KeyringController = class extends BaseController {
964
949
  (await this.getOrAddQRKeyring()).cancelSync();
965
950
  }
966
951
  async connectQRHardware(page) {
967
- try {
968
- const keyring = await this.getOrAddQRKeyring();
969
- let accounts;
970
- switch (page) {
971
- case -1:
972
- accounts = await keyring.getPreviousPage();
973
- break;
974
- case 1:
975
- accounts = await keyring.getNextPage();
976
- break;
977
- default:
978
- accounts = await keyring.getFirstPage();
952
+ return __privateMethod(this, _withControllerLock, withControllerLock_fn).call(this, async () => {
953
+ try {
954
+ const keyring = this.getQRKeyring() || await __privateMethod(this, _addQRKeyring, addQRKeyring_fn).call(this);
955
+ let accounts;
956
+ switch (page) {
957
+ case -1:
958
+ accounts = await keyring.getPreviousPage();
959
+ break;
960
+ case 1:
961
+ accounts = await keyring.getNextPage();
962
+ break;
963
+ default:
964
+ accounts = await keyring.getFirstPage();
965
+ }
966
+ return accounts.map((account) => {
967
+ return {
968
+ ...account,
969
+ balance: "0x0"
970
+ };
971
+ });
972
+ } catch (e) {
973
+ throw new Error(`Unspecified error when connect QR Hardware, ${e}`);
979
974
  }
980
- return accounts.map((account) => {
981
- return {
982
- ...account,
983
- balance: "0x0"
984
- };
985
- });
986
- } catch (e) {
987
- throw new Error(`Unspecified error when connect QR Hardware, ${e}`);
988
- }
975
+ });
989
976
  }
990
977
  async unlockQRHardwareWalletAccount(index) {
991
- const keyring = await this.getOrAddQRKeyring();
992
- keyring.setAccountToUnlock(index);
993
- await this.addNewAccountForKeyring(keyring);
994
- await this.persistAllKeyrings();
978
+ return __privateMethod(this, _withControllerLock, withControllerLock_fn).call(this, async () => {
979
+ const keyring = this.getQRKeyring() || await __privateMethod(this, _addQRKeyring, addQRKeyring_fn).call(this);
980
+ keyring.setAccountToUnlock(index);
981
+ await keyring.addAccounts(1);
982
+ await __privateMethod(this, _updateVault, updateVault_fn).call(this);
983
+ });
995
984
  }
996
985
  async getAccountKeyringType(account) {
997
986
  const keyring = await this.getKeyringForAccount(
@@ -1000,17 +989,24 @@ var KeyringController = class extends BaseController {
1000
989
  return keyring.type;
1001
990
  }
1002
991
  async forgetQRDevice() {
1003
- const keyring = await this.getOrAddQRKeyring();
1004
- const allAccounts = await this.getAccounts();
1005
- keyring.forgetDevice();
1006
- const remainingAccounts = await this.getAccounts();
1007
- const removedAccounts = allAccounts.filter(
1008
- (address) => !remainingAccounts.includes(address)
1009
- );
1010
- await this.persistAllKeyrings();
1011
- return { removedAccounts, remainingAccounts };
992
+ return __privateMethod(this, _withControllerLock, withControllerLock_fn).call(this, async () => {
993
+ const keyring = this.getQRKeyring();
994
+ if (!keyring) {
995
+ return { removedAccounts: [], remainingAccounts: [] };
996
+ }
997
+ const allAccounts = await __privateMethod(this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this);
998
+ keyring.forgetDevice();
999
+ const remainingAccounts = await __privateMethod(this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this);
1000
+ const removedAccounts = allAccounts.filter(
1001
+ (address) => !remainingAccounts.includes(address)
1002
+ );
1003
+ await __privateMethod(this, _updateVault, updateVault_fn).call(this);
1004
+ return { removedAccounts, remainingAccounts };
1005
+ });
1012
1006
  }
1013
1007
  };
1008
+ _controllerOperationMutex = new WeakMap();
1009
+ _vaultOperationMutex = new WeakMap();
1014
1010
  _keyringBuilders = new WeakMap();
1015
1011
  _keyrings = new WeakMap();
1016
1012
  _unsupportedKeyrings = new WeakMap();
@@ -1077,13 +1073,14 @@ getKeyringBuilderForType_fn = function(type) {
1077
1073
  };
1078
1074
  _addQRKeyring = new WeakSet();
1079
1075
  addQRKeyring_fn = async function() {
1076
+ __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this);
1080
1077
  const qrKeyring = await __privateMethod(this, _newKeyring, newKeyring_fn).call(this, "QR Hardware Wallet Device" /* qr */, {
1081
1078
  accounts: []
1082
1079
  });
1083
1080
  const accounts = await qrKeyring.getAccounts();
1084
1081
  await __privateMethod(this, _checkForDuplicate, checkForDuplicate_fn).call(this, "QR Hardware Wallet Device" /* qr */, accounts);
1085
1082
  __privateGet(this, _keyrings).push(qrKeyring);
1086
- await this.persistAllKeyrings();
1083
+ await __privateMethod(this, _updateVault, updateVault_fn).call(this);
1087
1084
  __privateMethod(this, _subscribeToQRKeyringEvents, subscribeToQRKeyringEvents_fn).call(this, qrKeyring);
1088
1085
  return qrKeyring;
1089
1086
  };
@@ -1107,6 +1104,7 @@ unsubscribeFromQRKeyringsEvents_fn = function() {
1107
1104
  };
1108
1105
  _createNewVaultWithKeyring = new WeakSet();
1109
1106
  createNewVaultWithKeyring_fn = async function(password, keyring) {
1107
+ __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this);
1110
1108
  if (typeof password !== "string") {
1111
1109
  throw new TypeError("KeyringController - Password must be of type string." /* WrongPasswordType */);
1112
1110
  }
@@ -1114,78 +1112,160 @@ createNewVaultWithKeyring_fn = async function(password, keyring) {
1114
1112
  await __privateMethod(this, _clearKeyrings, clearKeyrings_fn).call(this);
1115
1113
  await __privateMethod(this, _createKeyringWithFirstAccount, createKeyringWithFirstAccount_fn).call(this, keyring.type, keyring.opts);
1116
1114
  __privateMethod(this, _setUnlocked, setUnlocked_fn).call(this);
1117
- return __privateMethod(this, _getMemState, getMemState_fn).call(this);
1118
1115
  };
1119
- _updateKeyringsInState = new WeakSet();
1120
- updateKeyringsInState_fn = async function() {
1121
- const keyrings = await Promise.all(__privateGet(this, _keyrings).map(displayForKeyring));
1122
- this.update((state) => {
1123
- state.keyrings = keyrings;
1124
- });
1116
+ _getUpdatedKeyrings = new WeakSet();
1117
+ getUpdatedKeyrings_fn = async function() {
1118
+ return Promise.all(__privateGet(this, _keyrings).map(displayForKeyring));
1125
1119
  };
1126
1120
  _unlockKeyrings = new WeakSet();
1127
1121
  unlockKeyrings_fn = async function(password, encryptionKey, encryptionSalt) {
1128
- const encryptedVault = this.state.vault;
1129
- if (!encryptedVault) {
1130
- throw new Error("KeyringController - Cannot unlock without a previous vault." /* VaultError */);
1131
- }
1132
- await __privateMethod(this, _clearKeyrings, clearKeyrings_fn).call(this);
1133
- let vault;
1134
- if (__privateGet(this, _cacheEncryptionKey)) {
1135
- assertIsExportableKeyEncryptor(__privateGet(this, _encryptor));
1136
- if (password) {
1137
- const result = await __privateGet(this, _encryptor).decryptWithDetail(
1138
- password,
1139
- encryptedVault
1140
- );
1141
- vault = result.vault;
1142
- __privateSet(this, _password, password);
1143
- this.update((state) => {
1144
- state.encryptionKey = result.exportedKeyString;
1145
- state.encryptionSalt = result.salt;
1146
- });
1122
+ return __privateMethod(this, _withVaultLock, withVaultLock_fn).call(this, async ({ releaseLock }) => {
1123
+ const encryptedVault = this.state.vault;
1124
+ if (!encryptedVault) {
1125
+ throw new Error("KeyringController - Cannot unlock without a previous vault." /* VaultError */);
1126
+ }
1127
+ await __privateMethod(this, _clearKeyrings, clearKeyrings_fn).call(this, { skipStateUpdate: true });
1128
+ let vault;
1129
+ const updatedState = {};
1130
+ if (__privateGet(this, _cacheEncryptionKey)) {
1131
+ assertIsExportableKeyEncryptor(__privateGet(this, _encryptor));
1132
+ if (password) {
1133
+ const result = await __privateGet(this, _encryptor).decryptWithDetail(
1134
+ password,
1135
+ encryptedVault
1136
+ );
1137
+ vault = result.vault;
1138
+ __privateSet(this, _password, password);
1139
+ updatedState.encryptionKey = result.exportedKeyString;
1140
+ updatedState.encryptionSalt = result.salt;
1141
+ } else {
1142
+ const parsedEncryptedVault = JSON.parse(encryptedVault);
1143
+ if (encryptionSalt !== parsedEncryptedVault.salt) {
1144
+ throw new Error("KeyringController - Encryption key and salt provided are expired" /* ExpiredCredentials */);
1145
+ }
1146
+ if (typeof encryptionKey !== "string") {
1147
+ throw new TypeError("KeyringController - Password must be of type string." /* WrongPasswordType */);
1148
+ }
1149
+ const key = await __privateGet(this, _encryptor).importKey(encryptionKey);
1150
+ vault = await __privateGet(this, _encryptor).decryptWithKey(
1151
+ key,
1152
+ parsedEncryptedVault
1153
+ );
1154
+ updatedState.encryptionKey = encryptionKey;
1155
+ updatedState.encryptionSalt = encryptionSalt;
1156
+ }
1147
1157
  } else {
1148
- const parsedEncryptedVault = JSON.parse(encryptedVault);
1149
- if (encryptionSalt !== parsedEncryptedVault.salt) {
1150
- throw new Error("KeyringController - Encryption key and salt provided are expired" /* ExpiredCredentials */);
1158
+ if (typeof password !== "string") {
1159
+ throw new TypeError("KeyringController - Password must be of type string." /* WrongPasswordType */);
1151
1160
  }
1152
- if (typeof encryptionKey !== "string") {
1161
+ vault = await __privateGet(this, _encryptor).decrypt(password, encryptedVault);
1162
+ __privateSet(this, _password, password);
1163
+ }
1164
+ if (!isSerializedKeyringsArray(vault)) {
1165
+ throw new Error("KeyringController - The decrypted vault has an unexpected shape." /* VaultDataError */);
1166
+ }
1167
+ await Promise.all(vault.map(__privateMethod(this, _restoreKeyring, restoreKeyring_fn).bind(this)));
1168
+ const updatedKeyrings = await __privateMethod(this, _getUpdatedKeyrings, getUpdatedKeyrings_fn).call(this);
1169
+ this.update((state) => {
1170
+ state.keyrings = updatedKeyrings;
1171
+ if (updatedState.encryptionKey || updatedState.encryptionSalt) {
1172
+ state.encryptionKey = updatedState.encryptionKey;
1173
+ state.encryptionSalt = updatedState.encryptionSalt;
1174
+ }
1175
+ });
1176
+ if (__privateGet(this, _password) && (!__privateGet(this, _cacheEncryptionKey) || !encryptionKey) && __privateGet(this, _encryptor).isVaultUpdated && !__privateGet(this, _encryptor).isVaultUpdated(encryptedVault)) {
1177
+ releaseLock();
1178
+ await __privateMethod(this, _updateVault, updateVault_fn).call(this);
1179
+ }
1180
+ return __privateGet(this, _keyrings);
1181
+ });
1182
+ };
1183
+ _updateVault = new WeakSet();
1184
+ updateVault_fn = function() {
1185
+ return __privateMethod(this, _withVaultLock, withVaultLock_fn).call(this, async () => {
1186
+ const { encryptionKey, encryptionSalt } = this.state;
1187
+ if (!__privateGet(this, _password) && !encryptionKey) {
1188
+ throw new Error("KeyringController - Cannot persist vault without password and encryption key" /* MissingCredentials */);
1189
+ }
1190
+ const serializedKeyrings = await Promise.all(
1191
+ __privateGet(this, _keyrings).map(async (keyring) => {
1192
+ const [type, data] = await Promise.all([
1193
+ keyring.type,
1194
+ keyring.serialize()
1195
+ ]);
1196
+ return { type, data };
1197
+ })
1198
+ );
1199
+ serializedKeyrings.push(...__privateGet(this, _unsupportedKeyrings));
1200
+ if (!serializedKeyrings.some((keyring) => keyring.type === "HD Key Tree" /* hd */)) {
1201
+ throw new Error("KeyringController - No HD Keyring found" /* NoHdKeyring */);
1202
+ }
1203
+ const updatedState = {};
1204
+ if (__privateGet(this, _cacheEncryptionKey)) {
1205
+ assertIsExportableKeyEncryptor(__privateGet(this, _encryptor));
1206
+ if (encryptionKey) {
1207
+ const key = await __privateGet(this, _encryptor).importKey(encryptionKey);
1208
+ const vaultJSON = await __privateGet(this, _encryptor).encryptWithKey(
1209
+ key,
1210
+ serializedKeyrings
1211
+ );
1212
+ vaultJSON.salt = encryptionSalt;
1213
+ updatedState.vault = JSON.stringify(vaultJSON);
1214
+ } else if (__privateGet(this, _password)) {
1215
+ const { vault: newVault, exportedKeyString } = await __privateGet(this, _encryptor).encryptWithDetail(
1216
+ __privateGet(this, _password),
1217
+ serializedKeyrings
1218
+ );
1219
+ updatedState.vault = newVault;
1220
+ updatedState.encryptionKey = exportedKeyString;
1221
+ }
1222
+ } else {
1223
+ if (typeof __privateGet(this, _password) !== "string") {
1153
1224
  throw new TypeError("KeyringController - Password must be of type string." /* WrongPasswordType */);
1154
1225
  }
1155
- const key = await __privateGet(this, _encryptor).importKey(encryptionKey);
1156
- vault = await __privateGet(this, _encryptor).decryptWithKey(key, parsedEncryptedVault);
1157
- this.update((state) => {
1158
- state.encryptionKey = encryptionKey;
1159
- state.encryptionSalt = encryptionSalt;
1160
- });
1226
+ updatedState.vault = await __privateGet(this, _encryptor).encrypt(
1227
+ __privateGet(this, _password),
1228
+ serializedKeyrings
1229
+ );
1161
1230
  }
1162
- } else {
1163
- if (typeof password !== "string") {
1164
- throw new TypeError("KeyringController - Password must be of type string." /* WrongPasswordType */);
1231
+ if (!updatedState.vault) {
1232
+ throw new Error("KeyringController - Cannot persist vault without vault information" /* MissingVaultData */);
1165
1233
  }
1166
- vault = await __privateGet(this, _encryptor).decrypt(password, encryptedVault);
1167
- __privateSet(this, _password, password);
1168
- }
1169
- if (!isSerializedKeyringsArray(vault)) {
1170
- throw new Error("KeyringController - The decrypted vault has an unexpected shape." /* VaultDataError */);
1171
- }
1172
- await Promise.all(vault.map(__privateMethod(this, _restoreKeyring, restoreKeyring_fn).bind(this)));
1173
- await __privateMethod(this, _updateKeyringsInState, updateKeyringsInState_fn).call(this);
1174
- if (__privateGet(this, _password) && (!__privateGet(this, _cacheEncryptionKey) || !encryptionKey) && __privateGet(this, _encryptor).isVaultUpdated && !__privateGet(this, _encryptor).isVaultUpdated(encryptedVault)) {
1175
- await this.persistAllKeyrings();
1176
- }
1177
- return __privateGet(this, _keyrings);
1234
+ const updatedKeyrings = await __privateMethod(this, _getUpdatedKeyrings, getUpdatedKeyrings_fn).call(this);
1235
+ this.update((state) => {
1236
+ state.vault = updatedState.vault;
1237
+ state.keyrings = updatedKeyrings;
1238
+ if (updatedState.encryptionKey) {
1239
+ state.encryptionKey = updatedState.encryptionKey;
1240
+ state.encryptionSalt = JSON.parse(updatedState.vault).salt;
1241
+ }
1242
+ });
1243
+ return true;
1244
+ });
1245
+ };
1246
+ _getAccountsFromKeyrings = new WeakSet();
1247
+ getAccountsFromKeyrings_fn = async function() {
1248
+ const keyrings = __privateGet(this, _keyrings);
1249
+ const keyringArrays = await Promise.all(
1250
+ keyrings.map(async (keyring) => keyring.getAccounts())
1251
+ );
1252
+ const addresses = keyringArrays.reduce((res, arr) => {
1253
+ return res.concat(arr);
1254
+ }, []);
1255
+ return addresses.map(normalize);
1178
1256
  };
1179
1257
  _createKeyringWithFirstAccount = new WeakSet();
1180
1258
  createKeyringWithFirstAccount_fn = async function(type, opts) {
1181
- const keyring = await this.addNewKeyring(type, opts);
1259
+ __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this);
1260
+ const keyring = await __privateMethod(this, _newKeyring, newKeyring_fn).call(this, type, opts, true);
1182
1261
  const [firstAccount] = await keyring.getAccounts();
1183
1262
  if (!firstAccount) {
1184
1263
  throw new Error("KeyringController - First Account not found." /* NoFirstAccount */);
1185
1264
  }
1186
1265
  };
1187
1266
  _newKeyring = new WeakSet();
1188
- newKeyring_fn = async function(type, data) {
1267
+ newKeyring_fn = async function(type, data, persist = false) {
1268
+ __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this);
1189
1269
  const keyringBuilder = __privateMethod(this, _getKeyringBuilderForType, getKeyringBuilderForType_fn).call(this, type);
1190
1270
  if (!keyringBuilder) {
1191
1271
  throw new Error(
@@ -1197,20 +1277,38 @@ newKeyring_fn = async function(type, data) {
1197
1277
  if (keyring.init) {
1198
1278
  await keyring.init();
1199
1279
  }
1280
+ if (type === "HD Key Tree" /* hd */ && (!isObject(data) || !data.mnemonic)) {
1281
+ if (!keyring.generateRandomMnemonic) {
1282
+ throw new Error(
1283
+ "KeyringController - The current keyring does not support the method generateRandomMnemonic." /* UnsupportedGenerateRandomMnemonic */
1284
+ );
1285
+ }
1286
+ keyring.generateRandomMnemonic();
1287
+ await keyring.addAccounts(1);
1288
+ }
1289
+ await __privateMethod(this, _checkForDuplicate, checkForDuplicate_fn).call(this, type, await keyring.getAccounts());
1290
+ if (persist) {
1291
+ __privateGet(this, _keyrings).push(keyring);
1292
+ await __privateMethod(this, _updateVault, updateVault_fn).call(this);
1293
+ }
1200
1294
  return keyring;
1201
1295
  };
1202
1296
  _clearKeyrings = new WeakSet();
1203
- clearKeyrings_fn = async function() {
1297
+ clearKeyrings_fn = async function(options = { skipStateUpdate: false }) {
1298
+ __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this);
1204
1299
  for (const keyring of __privateGet(this, _keyrings)) {
1205
1300
  await __privateMethod(this, _destroyKeyring, destroyKeyring_fn).call(this, keyring);
1206
1301
  }
1207
1302
  __privateSet(this, _keyrings, []);
1208
- this.update((state) => {
1209
- state.keyrings = [];
1210
- });
1303
+ if (!options.skipStateUpdate) {
1304
+ this.update((state) => {
1305
+ state.keyrings = [];
1306
+ });
1307
+ }
1211
1308
  };
1212
1309
  _restoreKeyring = new WeakSet();
1213
1310
  restoreKeyring_fn = async function(serialized) {
1311
+ __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this);
1214
1312
  try {
1215
1313
  const { type, data } = serialized;
1216
1314
  const keyring = await __privateMethod(this, _newKeyring, newKeyring_fn).call(this, type, data);
@@ -1228,6 +1326,7 @@ destroyKeyring_fn = async function(keyring) {
1228
1326
  };
1229
1327
  _removeEmptyKeyrings = new WeakSet();
1230
1328
  removeEmptyKeyrings_fn = async function() {
1329
+ __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this);
1231
1330
  const validKeyrings = [];
1232
1331
  await Promise.all(
1233
1332
  __privateGet(this, _keyrings).map(async (keyring) => {
@@ -1243,7 +1342,7 @@ removeEmptyKeyrings_fn = async function() {
1243
1342
  };
1244
1343
  _checkForDuplicate = new WeakSet();
1245
1344
  checkForDuplicate_fn = async function(type, newAccountArray) {
1246
- const accounts = await this.getAccounts();
1345
+ const accounts = await __privateMethod(this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this);
1247
1346
  switch (type) {
1248
1347
  case "Simple Key Pair" /* simple */: {
1249
1348
  const isIncluded = Boolean(
@@ -1263,18 +1362,35 @@ checkForDuplicate_fn = async function(type, newAccountArray) {
1263
1362
  };
1264
1363
  _setUnlocked = new WeakSet();
1265
1364
  setUnlocked_fn = function() {
1365
+ __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this);
1266
1366
  this.update((state) => {
1267
1367
  state.isUnlocked = true;
1268
1368
  });
1269
1369
  this.messagingSystem.publish(`${name}:unlock`);
1270
1370
  };
1271
- _getMemState = new WeakSet();
1272
- getMemState_fn = function() {
1273
- return {
1274
- isUnlocked: this.state.isUnlocked,
1275
- keyrings: this.state.keyrings
1276
- };
1371
+ _assertControllerMutexIsLocked = new WeakSet();
1372
+ assertControllerMutexIsLocked_fn = function() {
1373
+ if (!__privateGet(this, _controllerOperationMutex).isLocked()) {
1374
+ throw new Error("KeyringController - attempt to update vault during a non mutually exclusive operation" /* ControllerLockRequired */);
1375
+ }
1376
+ };
1377
+ _withControllerLock = new WeakSet();
1378
+ withControllerLock_fn = async function(fn) {
1379
+ return withLock(__privateGet(this, _controllerOperationMutex), fn);
1380
+ };
1381
+ _withVaultLock = new WeakSet();
1382
+ withVaultLock_fn = async function(fn) {
1383
+ __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this);
1384
+ return withLock(__privateGet(this, _vaultOperationMutex), fn);
1277
1385
  };
1386
+ async function withLock(mutex, fn) {
1387
+ const releaseLock = await mutex.acquire();
1388
+ try {
1389
+ return await fn({ releaseLock });
1390
+ } finally {
1391
+ releaseLock();
1392
+ }
1393
+ }
1278
1394
  var KeyringController_default = KeyringController;
1279
1395
 
1280
1396
  export {
@@ -1287,4 +1403,4 @@ export {
1287
1403
  KeyringController,
1288
1404
  KeyringController_default
1289
1405
  };
1290
- //# sourceMappingURL=chunk-6HZWCYLD.mjs.map
1406
+ //# sourceMappingURL=chunk-RVCG63UG.mjs.map