@metamask-previews/keyring-controller 15.0.0-preview-aedc5103 → 15.0.0-preview-cb17e99

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-XPARO3LL.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 _initVaultMutex, _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, _getMemState, getMemState_fn, _withVaultLock, withVaultLock_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, _getSerializedKeyrings, getSerializedKeyrings_fn, _restoreSerializedKeyrings, restoreSerializedKeyrings_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, _persistOrRollback, persistOrRollback_fn, _withRollback, withRollback_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.
@@ -170,6 +170,21 @@ var KeyringController = class extends BaseController {
170
170
  * @returns A promise resolving to the updated keyrings array.
171
171
  */
172
172
  __privateAdd(this, _getUpdatedKeyrings);
173
+ /**
174
+ * Serialize the current array of keyring instances,
175
+ * including unsupported keyrings by default.
176
+ *
177
+ * @param options - Method options.
178
+ * @param options.includeUnsupported - Whether to include unsupported keyrings.
179
+ * @returns The serialized keyrings.
180
+ */
181
+ __privateAdd(this, _getSerializedKeyrings);
182
+ /**
183
+ * Restore a serialized keyrings array.
184
+ *
185
+ * @param serializedKeyrings - The serialized keyrings array.
186
+ */
187
+ __privateAdd(this, _restoreSerializedKeyrings);
173
188
  /**
174
189
  * Unlock Keyrings, decrypting the vault and deserializing all
175
190
  * keyrings contained in it, using a password or an encryption key with salt.
@@ -210,7 +225,6 @@ var KeyringController = class extends BaseController {
210
225
  *
211
226
  * @param type - The type of keyring to add.
212
227
  * @param data - The data to restore a previously serialized keyring.
213
- * @param persist - Whether to persist the keyring to the vault.
214
228
  * @returns The new keyring.
215
229
  * @throws If the keyring includes duplicated accounts.
216
230
  */
@@ -218,9 +232,6 @@ var KeyringController = class extends BaseController {
218
232
  /**
219
233
  * Remove all managed keyrings, destroying all their
220
234
  * instances in memory.
221
- *
222
- * @param options - Operations options.
223
- * @param options.skipStateUpdate - Whether to skip updating the controller state.
224
235
  */
225
236
  __privateAdd(this, _clearKeyrings);
226
237
  /**
@@ -266,7 +277,42 @@ var KeyringController = class extends BaseController {
266
277
  * @fires KeyringController:unlock
267
278
  */
268
279
  __privateAdd(this, _setUnlocked);
269
- __privateAdd(this, _getMemState);
280
+ /**
281
+ * Execute the given function after acquiring the controller lock
282
+ * and save the keyrings to state after it, or rollback to their
283
+ * previous state in case of error.
284
+ *
285
+ * @param fn - The function to execute.
286
+ * @returns The result of the function.
287
+ */
288
+ __privateAdd(this, _persistOrRollback);
289
+ /**
290
+ * Execute the given function after acquiring the controller lock
291
+ * and rollback keyrings and password states in case of error.
292
+ *
293
+ * @param fn - The function to execute atomically.
294
+ * @returns The result of the function.
295
+ */
296
+ __privateAdd(this, _withRollback);
297
+ /**
298
+ * Assert that the controller mutex is locked.
299
+ *
300
+ * @throws If the controller mutex is not locked.
301
+ */
302
+ __privateAdd(this, _assertControllerMutexIsLocked);
303
+ /**
304
+ * Lock the controller mutex before executing the given function,
305
+ * and release it after the function is resolved or after an
306
+ * error is thrown.
307
+ *
308
+ * This wrapper ensures that each mutable operation that interacts with the
309
+ * controller and that changes its state is executed in a mutually exclusive way,
310
+ * preventing unsafe concurrent access that could lead to unpredictable behavior.
311
+ *
312
+ * @param fn - The function to execute while the controller mutex is locked.
313
+ * @returns The result of the function.
314
+ */
315
+ __privateAdd(this, _withControllerLock);
270
316
  /**
271
317
  * Lock the vault mutex before executing the given function,
272
318
  * and release it after the function is resolved or after an
@@ -279,7 +325,7 @@ var KeyringController = class extends BaseController {
279
325
  * @returns The result of the function.
280
326
  */
281
327
  __privateAdd(this, _withVaultLock);
282
- __privateAdd(this, _initVaultMutex, new Mutex());
328
+ __privateAdd(this, _controllerOperationMutex, new Mutex());
283
329
  __privateAdd(this, _vaultOperationMutex, new Mutex());
284
330
  __privateAdd(this, _keyringBuilders, void 0);
285
331
  __privateAdd(this, _keyrings, void 0);
@@ -303,75 +349,71 @@ var KeyringController = class extends BaseController {
303
349
  *
304
350
  * @param accountCount - Number of accounts before adding a new one, used to
305
351
  * make the method idempotent.
306
- * @returns Promise resolving to keyring current state and added account
307
- * address.
352
+ * @returns Promise resolving to the added account address.
308
353
  */
309
354
  async addNewAccount(accountCount) {
310
- const primaryKeyring = this.getKeyringsByType("HD Key Tree")[0];
311
- if (!primaryKeyring) {
312
- throw new Error("No HD keyring found");
313
- }
314
- const oldAccounts = await primaryKeyring.getAccounts();
315
- if (accountCount && oldAccounts.length !== accountCount) {
316
- if (accountCount > oldAccounts.length) {
317
- throw new Error("Account out of sequence");
355
+ return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => {
356
+ const primaryKeyring = this.getKeyringsByType("HD Key Tree")[0];
357
+ if (!primaryKeyring) {
358
+ throw new Error("No HD keyring found");
318
359
  }
319
- const existingAccount = oldAccounts[accountCount];
320
- if (!existingAccount) {
321
- throw new Error(`Can't find account at index ${accountCount}`);
360
+ const oldAccounts = await primaryKeyring.getAccounts();
361
+ if (accountCount && oldAccounts.length !== accountCount) {
362
+ if (accountCount > oldAccounts.length) {
363
+ throw new Error("Account out of sequence");
364
+ }
365
+ const existingAccount = oldAccounts[accountCount];
366
+ if (!existingAccount) {
367
+ throw new Error(`Can't find account at index ${accountCount}`);
368
+ }
369
+ return existingAccount;
322
370
  }
323
- return {
324
- keyringState: __privateMethod(this, _getMemState, getMemState_fn).call(this),
325
- addedAccountAddress: existingAccount
326
- };
327
- }
328
- const [addedAccountAddress] = await primaryKeyring.addAccounts(1);
329
- await this.verifySeedPhrase();
330
- await __privateMethod(this, _updateVault, updateVault_fn).call(this);
331
- return {
332
- keyringState: __privateMethod(this, _getMemState, getMemState_fn).call(this),
333
- addedAccountAddress
334
- };
371
+ const [addedAccountAddress] = await primaryKeyring.addAccounts(1);
372
+ await this.verifySeedPhrase();
373
+ return addedAccountAddress;
374
+ });
335
375
  }
336
376
  /**
337
377
  * Adds a new account to the specified keyring.
338
378
  *
339
379
  * @param keyring - Keyring to add the account to.
340
380
  * @param accountCount - Number of accounts before adding a new one, used to make the method idempotent.
341
- * @returns Promise resolving to keyring current state and added account
381
+ * @returns Promise resolving to the added account address
342
382
  */
343
383
  async addNewAccountForKeyring(keyring, accountCount) {
344
- const oldAccounts = await __privateMethod(this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this);
345
- if (accountCount && oldAccounts.length !== accountCount) {
346
- if (accountCount > oldAccounts.length) {
347
- throw new Error("Account out of sequence");
384
+ return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => {
385
+ const oldAccounts = await __privateMethod(this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this);
386
+ if (accountCount && oldAccounts.length !== accountCount) {
387
+ if (accountCount > oldAccounts.length) {
388
+ throw new Error("Account out of sequence");
389
+ }
390
+ const existingAccount = oldAccounts[accountCount];
391
+ assertIsStrictHexString(existingAccount);
392
+ return existingAccount;
348
393
  }
349
- const existingAccount = oldAccounts[accountCount];
350
- assertIsStrictHexString(existingAccount);
351
- return existingAccount;
352
- }
353
- await keyring.addAccounts(1);
354
- await __privateMethod(this, _updateVault, updateVault_fn).call(this);
355
- const addedAccountAddress = (await __privateMethod(this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this)).find(
356
- (selectedAddress) => !oldAccounts.includes(selectedAddress)
357
- );
358
- assertIsStrictHexString(addedAccountAddress);
359
- return addedAccountAddress;
394
+ await keyring.addAccounts(1);
395
+ const addedAccountAddress = (await __privateMethod(this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this)).find(
396
+ (selectedAddress) => !oldAccounts.includes(selectedAddress)
397
+ );
398
+ assertIsStrictHexString(addedAccountAddress);
399
+ return addedAccountAddress;
400
+ });
360
401
  }
361
402
  /**
362
403
  * Adds a new account to the default (first) HD seed phrase keyring without updating identities in preferences.
363
404
  *
364
- * @returns Promise resolving to current state when the account is added.
405
+ * @returns Promise resolving to the added account address.
365
406
  */
366
407
  async addNewAccountWithoutUpdate() {
367
- const primaryKeyring = this.getKeyringsByType("HD Key Tree")[0];
368
- if (!primaryKeyring) {
369
- throw new Error("No HD keyring found");
370
- }
371
- await primaryKeyring.addAccounts(1);
372
- await __privateMethod(this, _updateVault, updateVault_fn).call(this);
373
- await this.verifySeedPhrase();
374
- return __privateMethod(this, _getMemState, getMemState_fn).call(this);
408
+ return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => {
409
+ const primaryKeyring = this.getKeyringsByType("HD Key Tree")[0];
410
+ if (!primaryKeyring) {
411
+ throw new Error("No HD keyring found");
412
+ }
413
+ const [addedAccountAddress] = await primaryKeyring.addAccounts(1);
414
+ await this.verifySeedPhrase();
415
+ return addedAccountAddress;
416
+ });
375
417
  }
376
418
  /**
377
419
  * Effectively the same as creating a new keychain then populating it
@@ -380,14 +422,13 @@ var KeyringController = class extends BaseController {
380
422
  * @param password - Password to unlock keychain.
381
423
  * @param seed - A BIP39-compliant seed phrase as Uint8Array,
382
424
  * either as a string or an array of UTF-8 bytes that represent the string.
383
- * @returns Promise resolving to the restored keychain object.
425
+ * @returns Promise resolving when the operation ends successfully.
384
426
  */
385
427
  async createNewVaultAndRestore(password, seed) {
386
- const releaseLock = await __privateGet(this, _initVaultMutex).acquire();
387
- if (!password || !password.length) {
388
- throw new Error("Invalid password");
389
- }
390
- try {
428
+ return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => {
429
+ if (!password || !password.length) {
430
+ throw new Error("Invalid password");
431
+ }
391
432
  await __privateMethod(this, _createNewVaultWithKeyring, createNewVaultWithKeyring_fn).call(this, password, {
392
433
  type: "HD Key Tree" /* hd */,
393
434
  opts: {
@@ -395,30 +436,23 @@ var KeyringController = class extends BaseController {
395
436
  numberOfAccounts: 1
396
437
  }
397
438
  });
398
- return __privateMethod(this, _getMemState, getMemState_fn).call(this);
399
- } finally {
400
- releaseLock();
401
- }
439
+ });
402
440
  }
403
441
  /**
404
442
  * Create a new primary keychain and wipe any previous keychains.
405
443
  *
406
444
  * @param password - Password to unlock the new vault.
407
- * @returns Newly-created keychain object.
445
+ * @returns Promise resolving when the operation ends successfully.
408
446
  */
409
447
  async createNewVaultAndKeychain(password) {
410
- const releaseLock = await __privateGet(this, _initVaultMutex).acquire();
411
- try {
448
+ return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => {
412
449
  const accounts = await __privateMethod(this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this);
413
450
  if (!accounts.length) {
414
451
  await __privateMethod(this, _createNewVaultWithKeyring, createNewVaultWithKeyring_fn).call(this, password, {
415
452
  type: "HD Key Tree" /* hd */
416
453
  });
417
454
  }
418
- return __privateMethod(this, _getMemState, getMemState_fn).call(this);
419
- } finally {
420
- releaseLock();
421
- }
455
+ });
422
456
  }
423
457
  /**
424
458
  * Adds a new keyring of the given `type`.
@@ -432,7 +466,7 @@ var KeyringController = class extends BaseController {
432
466
  if (type === "QR Hardware Wallet Device" /* qr */) {
433
467
  return this.getOrAddQRKeyring();
434
468
  }
435
- return __privateMethod(this, _newKeyring, newKeyring_fn).call(this, type, opts, true);
469
+ return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => __privateMethod(this, _newKeyring, newKeyring_fn).call(this, type, opts));
436
470
  }
437
471
  /**
438
472
  * Method to verify a given password validity. Throws an
@@ -488,7 +522,10 @@ var KeyringController = class extends BaseController {
488
522
  * @returns A promise resolving to an array of addresses.
489
523
  */
490
524
  async getAccounts() {
491
- return __privateMethod(this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this);
525
+ return this.state.keyrings.reduce(
526
+ (accounts, keyring) => accounts.concat(keyring.accounts),
527
+ []
528
+ );
492
529
  }
493
530
  /**
494
531
  * Get encryption public key.
@@ -581,7 +618,7 @@ var KeyringController = class extends BaseController {
581
618
  * operation completes.
582
619
  */
583
620
  async persistAllKeyrings() {
584
- return __privateMethod(this, _updateVault, updateVault_fn).call(this);
621
+ return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => true);
585
622
  }
586
623
  /**
587
624
  * Imports an account with the specified import strategy.
@@ -589,88 +626,89 @@ var KeyringController = class extends BaseController {
589
626
  * @param strategy - Import strategy name.
590
627
  * @param args - Array of arguments to pass to the underlying stategy.
591
628
  * @throws Will throw when passed an unrecognized strategy.
592
- * @returns Promise resolving to keyring current state and imported account
593
- * address.
629
+ * @returns Promise resolving to the imported account address.
594
630
  */
595
631
  async importAccountWithStrategy(strategy, args) {
596
- let privateKey;
597
- switch (strategy) {
598
- case "privateKey":
599
- const [importedKey] = args;
600
- if (!importedKey) {
601
- throw new Error("Cannot import an empty key.");
602
- }
603
- const prefixed = add0x(importedKey);
604
- let bufferedPrivateKey;
605
- try {
606
- bufferedPrivateKey = toBuffer(prefixed);
607
- } catch {
608
- throw new Error("Cannot import invalid private key.");
609
- }
610
- if (!isValidPrivate(bufferedPrivateKey) || // ensures that the key is 64 bytes long
611
- getBinarySize(prefixed) !== 64 + "0x".length) {
612
- throw new Error("Cannot import invalid private key.");
613
- }
614
- privateKey = remove0x(prefixed);
615
- break;
616
- case "json":
617
- let wallet;
618
- const [input, password] = args;
619
- try {
620
- wallet = importers.fromEtherWallet(input, password);
621
- } catch (e) {
622
- wallet = wallet || await Wallet.fromV3(input, password, true);
623
- }
624
- privateKey = bytesToHex(wallet.getPrivateKey());
625
- break;
626
- default:
627
- throw new Error(`Unexpected import strategy: '${strategy}'`);
628
- }
629
- const newKeyring = await __privateMethod(this, _newKeyring, newKeyring_fn).call(this, "Simple Key Pair" /* simple */, [privateKey], true);
630
- const accounts = await newKeyring.getAccounts();
631
- return {
632
- keyringState: __privateMethod(this, _getMemState, getMemState_fn).call(this),
633
- importedAccountAddress: accounts[0]
634
- };
632
+ return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => {
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 __privateMethod(this, _newKeyring, newKeyring_fn).call(this, "Simple Key Pair" /* simple */, [
667
+ privateKey
668
+ ]);
669
+ const accounts = await newKeyring.getAccounts();
670
+ return accounts[0];
671
+ });
635
672
  }
636
673
  /**
637
674
  * Removes an account from keyring state.
638
675
  *
639
676
  * @param address - Address of the account to remove.
640
677
  * @fires KeyringController:accountRemoved
641
- * @returns Promise resolving current state when this account removal completes.
678
+ * @returns Promise resolving when the account is removed.
642
679
  */
643
680
  async removeAccount(address) {
644
- const keyring = await this.getKeyringForAccount(
645
- address
646
- );
647
- if (!keyring.removeAccount) {
648
- throw new Error("`KeyringController - The keyring for the current address does not support the method removeAccount" /* UnsupportedRemoveAccount */);
649
- }
650
- await keyring.removeAccount(address);
651
- const accounts = await keyring.getAccounts();
652
- if (accounts.length === 0) {
653
- await __privateMethod(this, _removeEmptyKeyrings, removeEmptyKeyrings_fn).call(this);
654
- }
655
- await __privateMethod(this, _updateVault, updateVault_fn).call(this);
681
+ await __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => {
682
+ const keyring = await this.getKeyringForAccount(
683
+ address
684
+ );
685
+ if (!keyring.removeAccount) {
686
+ throw new Error("`KeyringController - The keyring for the current address does not support the method removeAccount" /* UnsupportedRemoveAccount */);
687
+ }
688
+ await keyring.removeAccount(address);
689
+ const accounts = await keyring.getAccounts();
690
+ if (accounts.length === 0) {
691
+ await __privateMethod(this, _removeEmptyKeyrings, removeEmptyKeyrings_fn).call(this);
692
+ }
693
+ });
656
694
  this.messagingSystem.publish(`${name}:accountRemoved`, address);
657
- return __privateMethod(this, _getMemState, getMemState_fn).call(this);
658
695
  }
659
696
  /**
660
697
  * Deallocates all secrets and locks the wallet.
661
698
  *
662
- * @returns Promise resolving to current state.
699
+ * @returns Promise resolving when the operation completes.
663
700
  */
664
701
  async setLocked() {
665
- __privateMethod(this, _unsubscribeFromQRKeyringsEvents, unsubscribeFromQRKeyringsEvents_fn).call(this);
666
- __privateSet(this, _password, void 0);
667
- this.update((state) => {
668
- state.isUnlocked = false;
669
- state.keyrings = [];
702
+ return __privateMethod(this, _withRollback, withRollback_fn).call(this, async () => {
703
+ __privateMethod(this, _unsubscribeFromQRKeyringsEvents, unsubscribeFromQRKeyringsEvents_fn).call(this);
704
+ __privateSet(this, _password, void 0);
705
+ await __privateMethod(this, _clearKeyrings, clearKeyrings_fn).call(this);
706
+ this.update((state) => {
707
+ state.isUnlocked = false;
708
+ state.keyrings = [];
709
+ });
710
+ this.messagingSystem.publish(`${name}:lock`);
670
711
  });
671
- await __privateMethod(this, _clearKeyrings, clearKeyrings_fn).call(this);
672
- this.messagingSystem.publish(`${name}:lock`);
673
- return __privateMethod(this, _getMemState, getMemState_fn).call(this);
674
712
  }
675
713
  /**
676
714
  * Signs message by calling down into a specific keyring.
@@ -824,32 +862,26 @@ var KeyringController = class extends BaseController {
824
862
  *
825
863
  * @param encryptionKey - Key to unlock the keychain.
826
864
  * @param encryptionSalt - Salt to unlock the keychain.
827
- * @returns Promise resolving to the current state.
865
+ * @returns Promise resolving when the operation completes.
828
866
  */
829
867
  async submitEncryptionKey(encryptionKey, encryptionSalt) {
830
- __privateSet(this, _keyrings, await __privateMethod(this, _unlockKeyrings, unlockKeyrings_fn).call(this, void 0, encryptionKey, encryptionSalt));
831
- __privateMethod(this, _setUnlocked, setUnlocked_fn).call(this);
832
- const qrKeyring = this.getQRKeyring();
833
- if (qrKeyring) {
834
- __privateMethod(this, _subscribeToQRKeyringEvents, subscribeToQRKeyringEvents_fn).call(this, qrKeyring);
835
- }
836
- return __privateMethod(this, _getMemState, getMemState_fn).call(this);
868
+ return __privateMethod(this, _withRollback, withRollback_fn).call(this, async () => {
869
+ __privateSet(this, _keyrings, await __privateMethod(this, _unlockKeyrings, unlockKeyrings_fn).call(this, void 0, encryptionKey, encryptionSalt));
870
+ __privateMethod(this, _setUnlocked, setUnlocked_fn).call(this);
871
+ });
837
872
  }
838
873
  /**
839
874
  * Attempts to decrypt the current vault and load its keyrings,
840
875
  * using the given password.
841
876
  *
842
877
  * @param password - Password to unlock the keychain.
843
- * @returns Promise resolving to the current state.
878
+ * @returns Promise resolving when the operation completes.
844
879
  */
845
880
  async submitPassword(password) {
846
- __privateSet(this, _keyrings, await __privateMethod(this, _unlockKeyrings, unlockKeyrings_fn).call(this, password));
847
- __privateMethod(this, _setUnlocked, setUnlocked_fn).call(this);
848
- const qrKeyring = this.getQRKeyring();
849
- if (qrKeyring) {
850
- __privateMethod(this, _subscribeToQRKeyringEvents, subscribeToQRKeyringEvents_fn).call(this, qrKeyring);
851
- }
852
- return __privateMethod(this, _getMemState, getMemState_fn).call(this);
881
+ return __privateMethod(this, _withRollback, withRollback_fn).call(this, async () => {
882
+ __privateSet(this, _keyrings, await __privateMethod(this, _unlockKeyrings, unlockKeyrings_fn).call(this, password));
883
+ __privateMethod(this, _setUnlocked, setUnlocked_fn).call(this);
884
+ });
853
885
  }
854
886
  /**
855
887
  * Verifies the that the seed phrase restores the current keychain's accounts.
@@ -899,14 +931,15 @@ var KeyringController = class extends BaseController {
899
931
  * @returns The added keyring
900
932
  */
901
933
  async getOrAddQRKeyring() {
902
- return this.getQRKeyring() || await __privateMethod(this, _addQRKeyring, addQRKeyring_fn).call(this);
934
+ return this.getQRKeyring() || await __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => __privateMethod(this, _addQRKeyring, addQRKeyring_fn).call(this));
903
935
  }
904
936
  // TODO: Replace `any` with type
905
937
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
906
938
  async restoreQRKeyring(serialized) {
907
- const keyring = this.getQRKeyring() || await __privateMethod(this, _addQRKeyring, addQRKeyring_fn).call(this);
908
- keyring.deserialize(serialized);
909
- await __privateMethod(this, _updateVault, updateVault_fn).call(this);
939
+ return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => {
940
+ const keyring = this.getQRKeyring() || await __privateMethod(this, _addQRKeyring, addQRKeyring_fn).call(this);
941
+ keyring.deserialize(serialized);
942
+ });
910
943
  }
911
944
  async resetQRKeyringState() {
912
945
  (await this.getOrAddQRKeyring()).resetStore();
@@ -933,34 +966,37 @@ var KeyringController = class extends BaseController {
933
966
  (await this.getOrAddQRKeyring()).cancelSync();
934
967
  }
935
968
  async connectQRHardware(page) {
936
- try {
937
- const keyring = this.getQRKeyring() || await __privateMethod(this, _addQRKeyring, addQRKeyring_fn).call(this);
938
- let accounts;
939
- switch (page) {
940
- case -1:
941
- accounts = await keyring.getPreviousPage();
942
- break;
943
- case 1:
944
- accounts = await keyring.getNextPage();
945
- break;
946
- default:
947
- accounts = await keyring.getFirstPage();
969
+ return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => {
970
+ try {
971
+ const keyring = this.getQRKeyring() || await __privateMethod(this, _addQRKeyring, addQRKeyring_fn).call(this);
972
+ let accounts;
973
+ switch (page) {
974
+ case -1:
975
+ accounts = await keyring.getPreviousPage();
976
+ break;
977
+ case 1:
978
+ accounts = await keyring.getNextPage();
979
+ break;
980
+ default:
981
+ accounts = await keyring.getFirstPage();
982
+ }
983
+ return accounts.map((account) => {
984
+ return {
985
+ ...account,
986
+ balance: "0x0"
987
+ };
988
+ });
989
+ } catch (e) {
990
+ throw new Error(`Unspecified error when connect QR Hardware, ${e}`);
948
991
  }
949
- return accounts.map((account) => {
950
- return {
951
- ...account,
952
- balance: "0x0"
953
- };
954
- });
955
- } catch (e) {
956
- throw new Error(`Unspecified error when connect QR Hardware, ${e}`);
957
- }
992
+ });
958
993
  }
959
994
  async unlockQRHardwareWalletAccount(index) {
960
- const keyring = this.getQRKeyring() || await __privateMethod(this, _addQRKeyring, addQRKeyring_fn).call(this);
961
- keyring.setAccountToUnlock(index);
962
- await keyring.addAccounts(1);
963
- await __privateMethod(this, _updateVault, updateVault_fn).call(this);
995
+ return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => {
996
+ const keyring = this.getQRKeyring() || await __privateMethod(this, _addQRKeyring, addQRKeyring_fn).call(this);
997
+ keyring.setAccountToUnlock(index);
998
+ await keyring.addAccounts(1);
999
+ });
964
1000
  }
965
1001
  async getAccountKeyringType(account) {
966
1002
  const keyring = await this.getKeyringForAccount(
@@ -969,21 +1005,22 @@ var KeyringController = class extends BaseController {
969
1005
  return keyring.type;
970
1006
  }
971
1007
  async forgetQRDevice() {
972
- const keyring = this.getQRKeyring();
973
- if (!keyring) {
974
- return { removedAccounts: [], remainingAccounts: [] };
975
- }
976
- const allAccounts = await __privateMethod(this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this);
977
- keyring.forgetDevice();
978
- const remainingAccounts = await __privateMethod(this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this);
979
- const removedAccounts = allAccounts.filter(
980
- (address) => !remainingAccounts.includes(address)
981
- );
982
- await __privateMethod(this, _updateVault, updateVault_fn).call(this);
983
- return { removedAccounts, remainingAccounts };
1008
+ return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => {
1009
+ const keyring = this.getQRKeyring();
1010
+ if (!keyring) {
1011
+ return { removedAccounts: [], remainingAccounts: [] };
1012
+ }
1013
+ const allAccounts = await __privateMethod(this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this);
1014
+ keyring.forgetDevice();
1015
+ const remainingAccounts = await __privateMethod(this, _getAccountsFromKeyrings, getAccountsFromKeyrings_fn).call(this);
1016
+ const removedAccounts = allAccounts.filter(
1017
+ (address) => !remainingAccounts.includes(address)
1018
+ );
1019
+ return { removedAccounts, remainingAccounts };
1020
+ });
984
1021
  }
985
1022
  };
986
- _initVaultMutex = new WeakMap();
1023
+ _controllerOperationMutex = new WeakMap();
987
1024
  _vaultOperationMutex = new WeakMap();
988
1025
  _keyringBuilders = new WeakMap();
989
1026
  _keyrings = new WeakMap();
@@ -1051,15 +1088,10 @@ getKeyringBuilderForType_fn = function(type) {
1051
1088
  };
1052
1089
  _addQRKeyring = new WeakSet();
1053
1090
  addQRKeyring_fn = async function() {
1054
- const qrKeyring = await __privateMethod(this, _newKeyring, newKeyring_fn).call(this, "QR Hardware Wallet Device" /* qr */, {
1091
+ __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this);
1092
+ return await __privateMethod(this, _newKeyring, newKeyring_fn).call(this, "QR Hardware Wallet Device" /* qr */, {
1055
1093
  accounts: []
1056
1094
  });
1057
- const accounts = await qrKeyring.getAccounts();
1058
- await __privateMethod(this, _checkForDuplicate, checkForDuplicate_fn).call(this, "QR Hardware Wallet Device" /* qr */, accounts);
1059
- __privateGet(this, _keyrings).push(qrKeyring);
1060
- await __privateMethod(this, _updateVault, updateVault_fn).call(this);
1061
- __privateMethod(this, _subscribeToQRKeyringEvents, subscribeToQRKeyringEvents_fn).call(this, qrKeyring);
1062
- return qrKeyring;
1063
1095
  };
1064
1096
  _subscribeToQRKeyringEvents = new WeakSet();
1065
1097
  subscribeToQRKeyringEvents_fn = function(qrKeyring) {
@@ -1081,6 +1113,7 @@ unsubscribeFromQRKeyringsEvents_fn = function() {
1081
1113
  };
1082
1114
  _createNewVaultWithKeyring = new WeakSet();
1083
1115
  createNewVaultWithKeyring_fn = async function(password, keyring) {
1116
+ __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this);
1084
1117
  if (typeof password !== "string") {
1085
1118
  throw new TypeError("KeyringController - Password must be of type string." /* WrongPasswordType */);
1086
1119
  }
@@ -1088,12 +1121,36 @@ createNewVaultWithKeyring_fn = async function(password, keyring) {
1088
1121
  await __privateMethod(this, _clearKeyrings, clearKeyrings_fn).call(this);
1089
1122
  await __privateMethod(this, _createKeyringWithFirstAccount, createKeyringWithFirstAccount_fn).call(this, keyring.type, keyring.opts);
1090
1123
  __privateMethod(this, _setUnlocked, setUnlocked_fn).call(this);
1091
- return __privateMethod(this, _getMemState, getMemState_fn).call(this);
1092
1124
  };
1093
1125
  _getUpdatedKeyrings = new WeakSet();
1094
1126
  getUpdatedKeyrings_fn = async function() {
1095
1127
  return Promise.all(__privateGet(this, _keyrings).map(displayForKeyring));
1096
1128
  };
1129
+ _getSerializedKeyrings = new WeakSet();
1130
+ getSerializedKeyrings_fn = async function({ includeUnsupported } = {
1131
+ includeUnsupported: true
1132
+ }) {
1133
+ const serializedKeyrings = await Promise.all(
1134
+ __privateGet(this, _keyrings).map(async (keyring) => {
1135
+ const [type, data] = await Promise.all([
1136
+ keyring.type,
1137
+ keyring.serialize()
1138
+ ]);
1139
+ return { type, data };
1140
+ })
1141
+ );
1142
+ if (includeUnsupported) {
1143
+ serializedKeyrings.push(...__privateGet(this, _unsupportedKeyrings));
1144
+ }
1145
+ return serializedKeyrings;
1146
+ };
1147
+ _restoreSerializedKeyrings = new WeakSet();
1148
+ restoreSerializedKeyrings_fn = async function(serializedKeyrings) {
1149
+ await __privateMethod(this, _clearKeyrings, clearKeyrings_fn).call(this);
1150
+ for (const serializedKeyring of serializedKeyrings) {
1151
+ await __privateMethod(this, _restoreKeyring, restoreKeyring_fn).call(this, serializedKeyring);
1152
+ }
1153
+ };
1097
1154
  _unlockKeyrings = new WeakSet();
1098
1155
  unlockKeyrings_fn = async function(password, encryptionKey, encryptionSalt) {
1099
1156
  return __privateMethod(this, _withVaultLock, withVaultLock_fn).call(this, async ({ releaseLock }) => {
@@ -1101,7 +1158,6 @@ unlockKeyrings_fn = async function(password, encryptionKey, encryptionSalt) {
1101
1158
  if (!encryptedVault) {
1102
1159
  throw new Error("KeyringController - Cannot unlock without a previous vault." /* VaultError */);
1103
1160
  }
1104
- await __privateMethod(this, _clearKeyrings, clearKeyrings_fn).call(this, { skipStateUpdate: true });
1105
1161
  let vault;
1106
1162
  const updatedState = {};
1107
1163
  if (__privateGet(this, _cacheEncryptionKey)) {
@@ -1141,7 +1197,7 @@ unlockKeyrings_fn = async function(password, encryptionKey, encryptionSalt) {
1141
1197
  if (!isSerializedKeyringsArray(vault)) {
1142
1198
  throw new Error("KeyringController - The decrypted vault has an unexpected shape." /* VaultDataError */);
1143
1199
  }
1144
- await Promise.all(vault.map(__privateMethod(this, _restoreKeyring, restoreKeyring_fn).bind(this)));
1200
+ await __privateMethod(this, _restoreSerializedKeyrings, restoreSerializedKeyrings_fn).call(this, vault);
1145
1201
  const updatedKeyrings = await __privateMethod(this, _getUpdatedKeyrings, getUpdatedKeyrings_fn).call(this);
1146
1202
  this.update((state) => {
1147
1203
  state.keyrings = updatedKeyrings;
@@ -1164,16 +1220,7 @@ updateVault_fn = function() {
1164
1220
  if (!__privateGet(this, _password) && !encryptionKey) {
1165
1221
  throw new Error("KeyringController - Cannot persist vault without password and encryption key" /* MissingCredentials */);
1166
1222
  }
1167
- const serializedKeyrings = await Promise.all(
1168
- __privateGet(this, _keyrings).map(async (keyring) => {
1169
- const [type, data] = await Promise.all([
1170
- keyring.type,
1171
- keyring.serialize()
1172
- ]);
1173
- return { type, data };
1174
- })
1175
- );
1176
- serializedKeyrings.push(...__privateGet(this, _unsupportedKeyrings));
1223
+ const serializedKeyrings = await __privateMethod(this, _getSerializedKeyrings, getSerializedKeyrings_fn).call(this);
1177
1224
  if (!serializedKeyrings.some((keyring) => keyring.type === "HD Key Tree" /* hd */)) {
1178
1225
  throw new Error("KeyringController - No HD Keyring found" /* NoHdKeyring */);
1179
1226
  }
@@ -1233,14 +1280,16 @@ getAccountsFromKeyrings_fn = async function() {
1233
1280
  };
1234
1281
  _createKeyringWithFirstAccount = new WeakSet();
1235
1282
  createKeyringWithFirstAccount_fn = async function(type, opts) {
1236
- const keyring = await __privateMethod(this, _newKeyring, newKeyring_fn).call(this, type, opts, true);
1283
+ __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this);
1284
+ const keyring = await __privateMethod(this, _newKeyring, newKeyring_fn).call(this, type, opts);
1237
1285
  const [firstAccount] = await keyring.getAccounts();
1238
1286
  if (!firstAccount) {
1239
1287
  throw new Error("KeyringController - First Account not found." /* NoFirstAccount */);
1240
1288
  }
1241
1289
  };
1242
1290
  _newKeyring = new WeakSet();
1243
- newKeyring_fn = async function(type, data, persist = false) {
1291
+ newKeyring_fn = async function(type, data) {
1292
+ __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this);
1244
1293
  const keyringBuilder = __privateMethod(this, _getKeyringBuilderForType, getKeyringBuilderForType_fn).call(this, type);
1245
1294
  if (!keyringBuilder) {
1246
1295
  throw new Error(
@@ -1262,32 +1311,26 @@ newKeyring_fn = async function(type, data, persist = false) {
1262
1311
  await keyring.addAccounts(1);
1263
1312
  }
1264
1313
  await __privateMethod(this, _checkForDuplicate, checkForDuplicate_fn).call(this, type, await keyring.getAccounts());
1265
- if (persist) {
1266
- __privateGet(this, _keyrings).push(keyring);
1267
- await __privateMethod(this, _updateVault, updateVault_fn).call(this);
1314
+ if (type === "QR Hardware Wallet Device" /* qr */) {
1315
+ __privateMethod(this, _subscribeToQRKeyringEvents, subscribeToQRKeyringEvents_fn).call(this, keyring);
1268
1316
  }
1317
+ __privateGet(this, _keyrings).push(keyring);
1269
1318
  return keyring;
1270
1319
  };
1271
1320
  _clearKeyrings = new WeakSet();
1272
- clearKeyrings_fn = async function(options = { skipStateUpdate: false }) {
1321
+ clearKeyrings_fn = async function() {
1322
+ __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this);
1273
1323
  for (const keyring of __privateGet(this, _keyrings)) {
1274
1324
  await __privateMethod(this, _destroyKeyring, destroyKeyring_fn).call(this, keyring);
1275
1325
  }
1276
1326
  __privateSet(this, _keyrings, []);
1277
- if (!options.skipStateUpdate) {
1278
- this.update((state) => {
1279
- state.keyrings = [];
1280
- });
1281
- }
1282
1327
  };
1283
1328
  _restoreKeyring = new WeakSet();
1284
1329
  restoreKeyring_fn = async function(serialized) {
1330
+ __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this);
1285
1331
  try {
1286
1332
  const { type, data } = serialized;
1287
- const keyring = await __privateMethod(this, _newKeyring, newKeyring_fn).call(this, type, data);
1288
- await keyring.getAccounts();
1289
- __privateGet(this, _keyrings).push(keyring);
1290
- return keyring;
1333
+ return await __privateMethod(this, _newKeyring, newKeyring_fn).call(this, type, data);
1291
1334
  } catch (_) {
1292
1335
  __privateGet(this, _unsupportedKeyrings).push(serialized);
1293
1336
  return void 0;
@@ -1299,6 +1342,7 @@ destroyKeyring_fn = async function(keyring) {
1299
1342
  };
1300
1343
  _removeEmptyKeyrings = new WeakSet();
1301
1344
  removeEmptyKeyrings_fn = async function() {
1345
+ __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this);
1302
1346
  const validKeyrings = [];
1303
1347
  await Promise.all(
1304
1348
  __privateGet(this, _keyrings).map(async (keyring) => {
@@ -1334,27 +1378,57 @@ checkForDuplicate_fn = async function(type, newAccountArray) {
1334
1378
  };
1335
1379
  _setUnlocked = new WeakSet();
1336
1380
  setUnlocked_fn = function() {
1381
+ __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this);
1337
1382
  this.update((state) => {
1338
1383
  state.isUnlocked = true;
1339
1384
  });
1340
1385
  this.messagingSystem.publish(`${name}:unlock`);
1341
1386
  };
1342
- _getMemState = new WeakSet();
1343
- getMemState_fn = function() {
1344
- return {
1345
- isUnlocked: this.state.isUnlocked,
1346
- keyrings: this.state.keyrings
1347
- };
1387
+ _persistOrRollback = new WeakSet();
1388
+ persistOrRollback_fn = async function(fn) {
1389
+ return __privateMethod(this, _withRollback, withRollback_fn).call(this, async ({ releaseLock }) => {
1390
+ const callbackResult = await fn({ releaseLock });
1391
+ await __privateMethod(this, _updateVault, updateVault_fn).call(this);
1392
+ return callbackResult;
1393
+ });
1394
+ };
1395
+ _withRollback = new WeakSet();
1396
+ withRollback_fn = async function(fn) {
1397
+ return __privateMethod(this, _withControllerLock, withControllerLock_fn).call(this, async ({ releaseLock }) => {
1398
+ const currentSerializedKeyrings = await __privateMethod(this, _getSerializedKeyrings, getSerializedKeyrings_fn).call(this);
1399
+ const currentPassword = __privateGet(this, _password);
1400
+ try {
1401
+ return await fn({ releaseLock });
1402
+ } catch (e) {
1403
+ await __privateMethod(this, _restoreSerializedKeyrings, restoreSerializedKeyrings_fn).call(this, currentSerializedKeyrings);
1404
+ __privateSet(this, _password, currentPassword);
1405
+ throw e;
1406
+ }
1407
+ });
1408
+ };
1409
+ _assertControllerMutexIsLocked = new WeakSet();
1410
+ assertControllerMutexIsLocked_fn = function() {
1411
+ if (!__privateGet(this, _controllerOperationMutex).isLocked()) {
1412
+ throw new Error("KeyringController - attempt to update vault during a non mutually exclusive operation" /* ControllerLockRequired */);
1413
+ }
1414
+ };
1415
+ _withControllerLock = new WeakSet();
1416
+ withControllerLock_fn = async function(fn) {
1417
+ return withLock(__privateGet(this, _controllerOperationMutex), fn);
1348
1418
  };
1349
1419
  _withVaultLock = new WeakSet();
1350
1420
  withVaultLock_fn = async function(fn) {
1351
- const releaseLock = await __privateGet(this, _vaultOperationMutex).acquire();
1421
+ __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this);
1422
+ return withLock(__privateGet(this, _vaultOperationMutex), fn);
1423
+ };
1424
+ async function withLock(mutex, fn) {
1425
+ const releaseLock = await mutex.acquire();
1352
1426
  try {
1353
1427
  return await fn({ releaseLock });
1354
1428
  } finally {
1355
1429
  releaseLock();
1356
1430
  }
1357
- };
1431
+ }
1358
1432
  var KeyringController_default = KeyringController;
1359
1433
 
1360
1434
  export {
@@ -1367,4 +1441,4 @@ export {
1367
1441
  KeyringController,
1368
1442
  KeyringController_default
1369
1443
  };
1370
- //# sourceMappingURL=chunk-5QTEIEPG.mjs.map
1444
+ //# sourceMappingURL=chunk-5VBIIMC6.mjs.map