@dynamic-labs-wallet/browser 0.0.47 → 0.0.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.cjs.js CHANGED
@@ -3,6 +3,7 @@
3
3
  var core = require('@dynamic-labs-wallet/core');
4
4
  var web = require('./internal/web');
5
5
  var logger$1 = require('@dynamic-labs/logger');
6
+ var messageTransport = require('@dynamic-labs/message-transport');
6
7
 
7
8
  function _extends() {
8
9
  _extends = Object.assign || function assign(target) {
@@ -184,7 +185,6 @@ const encryptData = async ({ data, password })=>{
184
185
  cipher: bytesToBase64(new Uint8Array(encryptedData))
185
186
  };
186
187
  } catch (error) {
187
- console.error('Error encrypting data:', error);
188
188
  throw new Error('Error encrypting data');
189
189
  }
190
190
  };
@@ -208,7 +208,6 @@ const decryptData = async ({ data, password })=>{
208
208
  }, key, cipherBytes);
209
209
  return new TextDecoder().decode(decryptedData);
210
210
  } catch (error) {
211
- console.error('Decryption error details:', error);
212
211
  throw new Error('Decryption failed');
213
212
  }
214
213
  };
@@ -305,7 +304,6 @@ const downloadFileFromGoogleDrive = async ({ accessToken, name })=>{
305
304
  // The client will handle validation of the structure
306
305
  return JSON.parse(fileRawData);
307
306
  } catch (error) {
308
- console.error('Error parsing backup file:', error);
309
307
  return null;
310
308
  }
311
309
  };
@@ -379,17 +377,91 @@ const localStorageWriteTest = {
379
377
  }
380
378
  });
381
379
 
382
- var WalletOperation = /*#__PURE__*/ function(WalletOperation) {
383
- WalletOperation["REACH_THRESHOLD"] = "REACH_THRESHOLD";
384
- WalletOperation["REACH_ALL_PARTIES"] = "REACH_ALL_PARTIES";
385
- WalletOperation["SIGN_MESSAGE"] = "SIGN_MESSAGE";
386
- WalletOperation["SIGN_TRANSACTION"] = "SIGN_TRANSACTION";
387
- WalletOperation["REFRESH"] = "REFRESH";
388
- WalletOperation["RESHARE"] = "RESHARE";
389
- WalletOperation["EXPORT_PRIVATE_KEY"] = "EXPORT_PRIVATE_KEY";
390
- WalletOperation["NO_OPERATION"] = "NO_OPERATION";
391
- return WalletOperation;
392
- }({});
380
+ /**
381
+ * StorageRequestChannelAdapter.getItem() sends a message within host domain
382
+ * the bridge on host will capture this request and forwards it to the iframe via contentWindow.postMessage()
383
+ */ class StorageRequestChannelAdapter {
384
+ async getItem(key) {
385
+ const item = await this.requestChannel.request('getItem', {
386
+ source: 'localStorage',
387
+ key
388
+ });
389
+ return item ? JSON.parse(item) : null;
390
+ }
391
+ async setItem(key, value) {
392
+ const stringifiedValue = typeof value === 'object' ? JSON.stringify(value) : value;
393
+ return this.requestChannel.request('setItem', {
394
+ source: 'localStorage',
395
+ key,
396
+ data: stringifiedValue
397
+ });
398
+ }
399
+ async removeItem(key) {
400
+ return this.requestChannel.request('deleteItem', {
401
+ source: 'localStorage',
402
+ key
403
+ });
404
+ }
405
+ constructor(messageTransport$1){
406
+ this.requestChannel = messageTransport.createRequestChannel(messageTransport$1);
407
+ }
408
+ }
409
+
410
+ const setupMessageTransportBridge = (messageTransport$1, iframe, iframeOrigin)=>{
411
+ if (!(iframe == null ? void 0 : iframe.contentWindow)) {
412
+ throw new Error('Iframe or contentWindow not available');
413
+ }
414
+ const logger = new logger$1.Logger('debug');
415
+ messageTransport$1.on((message)=>{
416
+ // Forward the message to webview via postMessage
417
+ if (message.origin === 'host') {
418
+ var _iframe_contentWindow;
419
+ logger.debug(`[host bridge] host --> bridge --> iframe`, message);
420
+ iframe == null ? void 0 : (_iframe_contentWindow = iframe.contentWindow) == null ? void 0 : _iframe_contentWindow.postMessage(message, iframeOrigin);
421
+ }
422
+ });
423
+ const handleIncomingMessage = (message)=>{
424
+ const { data } = message;
425
+ if (!data) return;
426
+ if ((data == null ? void 0 : data.origin) !== 'webview') {
427
+ logger.debug(`skipped message: ${JSON.stringify(data)}: origin is not host`);
428
+ return;
429
+ }
430
+ if (typeof data !== 'object') {
431
+ logger.debug(`skipped message: ${JSON.stringify(data)}: data is not an object`);
432
+ return;
433
+ }
434
+ try {
435
+ const message = messageTransport.parseMessageTransportData(data);
436
+ logger.debug(`[host bridge] iframe --> bridge --> host`, message);
437
+ messageTransport$1.emit(message);
438
+ } catch (error) {
439
+ if (!(error instanceof SyntaxError)) {
440
+ logger.error('Error handling incoming message:', error);
441
+ }
442
+ }
443
+ };
444
+ /**
445
+ * Handle incoming message from android client
446
+ */ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
447
+ // @ts-ignore
448
+ document.addEventListener('message', handleIncomingMessage);
449
+ /**
450
+ * Handle incoming message from iOS client
451
+ */ window.addEventListener('message', handleIncomingMessage);
452
+ };
453
+
454
+ class IframeDisplayChannelAdapter {
455
+ async displayClientShares(accountAddress) {
456
+ await this.requestChannel.request('displayClientShares', accountAddress);
457
+ }
458
+ async displayPrivateKey(accountAddress, privateKey) {
459
+ await this.requestChannel.request('displayPrivateKey', accountAddress, privateKey);
460
+ }
461
+ constructor(messageTransport$1){
462
+ this.requestChannel = messageTransport.createRequestChannel(messageTransport$1);
463
+ }
464
+ }
393
465
 
394
466
  class DynamicWalletClient {
395
467
  async initialize() {
@@ -403,11 +475,196 @@ class DynamicWalletClient {
403
475
  return result;
404
476
  }
405
477
  /**
478
+ * Initializes the iframe display for a specific container.
479
+ *
480
+ * @param {HTMLElement} container - The container to which the iframe will be attached.
481
+ * @returns {Promise<{
482
+ * iframe: HTMLIFrameElement;
483
+ * iframeDisplay: IframeDisplayChannelAdapter;
484
+ * cleanup: () => void;
485
+ * }>} A promise that resolves when the iframe is loaded.
486
+ */ async initializeIframeDisplayForContainer({ container }) {
487
+ try {
488
+ const iframe = await this.loadIframeForContainer(container);
489
+ const transport = messageTransport.applyDefaultMessageOrigin({
490
+ defaultOrigin: 'host',
491
+ messageTransport: messageTransport.createMessageTransport()
492
+ });
493
+ setupMessageTransportBridge(transport, iframe, this.iframeDomain);
494
+ const iframeDisplay = new IframeDisplayChannelAdapter(transport);
495
+ return {
496
+ iframe,
497
+ iframeDisplay,
498
+ cleanup: ()=>{
499
+ container.removeChild(iframe);
500
+ }
501
+ };
502
+ } catch (error) {
503
+ this.logger.error('Error initializing iframe:', error);
504
+ throw error;
505
+ }
506
+ }
507
+ /**
508
+ * this is called on class construction time
509
+ * @returns {Promise<void>} that resolves when the iframe is loaded and the message transport and iframe storage are initialized
510
+ */ initializeIframeCommunication() {
511
+ if (!this.iframeLoadPromise) {
512
+ this.iframeLoadPromise = this.doInitializeIframeCommunication();
513
+ }
514
+ return this.iframeLoadPromise;
515
+ }
516
+ /**
517
+ * initialize the iframe communication by awaiting the iframe load promise
518
+ * and initializing the message transport and iframe storage after iframe is successfully loaded
519
+ */ async doInitializeIframeCommunication() {
520
+ try {
521
+ await this.loadIframe();
522
+ this.initializeMessageTransport();
523
+ this.initializeIframeStorage();
524
+ } catch (error) {
525
+ this.logger.error('Error initializing iframe:', error);
526
+ throw error;
527
+ }
528
+ }
529
+ /**
530
+ * create a promise to load an iframe
531
+ * @returns {Promise<void>} that resolves when the iframe is loaded
532
+ */ loadIframe() {
533
+ return new Promise((resolve, reject)=>{
534
+ const iframe = document.createElement('iframe');
535
+ const iframeTimeoutId = setTimeout(()=>{
536
+ reject(new Error('Iframe load timeout'));
537
+ }, 10000);
538
+ iframe.style.display = 'none';
539
+ iframe.setAttribute('title', 'Dynamic Wallet Iframe');
540
+ iframe.style.position = 'fixed';
541
+ iframe.style.top = '0';
542
+ iframe.style.left = '0';
543
+ iframe.style.width = '0';
544
+ iframe.style.height = '0';
545
+ iframe.style.border = 'none';
546
+ iframe.style.pointerEvents = 'none';
547
+ const params = new URLSearchParams({
548
+ instanceId: this.instanceId,
549
+ hostOrigin: window.location.origin
550
+ });
551
+ iframe.src = `${this.iframeDomain}/waas/${this.environmentId}?${params.toString()}`;
552
+ this.logger.debug('Creating iframe with src:', iframe.src);
553
+ document.body.appendChild(iframe);
554
+ iframe.onload = ()=>{
555
+ clearTimeout(iframeTimeoutId);
556
+ this.logger.debug('Iframe loaded successfully');
557
+ this.iframe = iframe;
558
+ resolve();
559
+ };
560
+ iframe.onerror = (error)=>{
561
+ clearTimeout(iframeTimeoutId);
562
+ this.logger.error('Iframe failed to load:', error);
563
+ reject(new Error('Failed to load iframe'));
564
+ };
565
+ });
566
+ }
567
+ /**
568
+ * Load an iframe for a specific container
569
+ * @param {HTMLElement} container - The container to which the iframe will be attached
570
+ * @returns {Promise<HTMLIFrameElement>} that resolves when the iframe is loaded
571
+ */ loadIframeForContainer(container) {
572
+ return new Promise((resolve, reject)=>{
573
+ const iframe = document.createElement('iframe');
574
+ const iframeTimeoutId = setTimeout(()=>{
575
+ reject(new Error('Iframe load timeout'));
576
+ }, 10000);
577
+ iframe.style.display = 'block';
578
+ iframe.style.width = '100%';
579
+ iframe.style.height = '100%';
580
+ iframe.setAttribute('title', 'Dynamic Wallet Storage');
581
+ iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin');
582
+ const params = new URLSearchParams({
583
+ instanceId: this.instanceId,
584
+ hostOrigin: window.location.origin
585
+ });
586
+ iframe.src = `${this.iframeDomain}/waas/${this.environmentId}?${params.toString()}`;
587
+ this.logger.debug('Creating iframe with src:', iframe.src);
588
+ // Add iframe to the provided container
589
+ container.appendChild(iframe);
590
+ iframe.onload = ()=>{
591
+ clearTimeout(iframeTimeoutId);
592
+ this.logger.debug('Iframe loaded successfully');
593
+ this.iframe = iframe;
594
+ resolve(iframe);
595
+ };
596
+ iframe.onerror = (error)=>{
597
+ clearTimeout(iframeTimeoutId);
598
+ this.logger.error('Iframe failed to load:', error);
599
+ reject(new Error('Failed to load iframe'));
600
+ };
601
+ });
602
+ }
603
+ /**
604
+ * initialize the message transport after iframe is successfully loaded
605
+ */ initializeMessageTransport() {
606
+ const transport = messageTransport.applyDefaultMessageOrigin({
607
+ defaultOrigin: 'host',
608
+ messageTransport: messageTransport.createMessageTransport()
609
+ });
610
+ this.messageTransport = transport;
611
+ if (!this.iframe) {
612
+ throw new Error('Iframe not available');
613
+ }
614
+ setupMessageTransportBridge(this.messageTransport, this.iframe, this.iframeDomain);
615
+ }
616
+ /**
617
+ * initialize the iframe storage after iframe is successfully loaded
618
+ */ initializeIframeStorage() {
619
+ if (!this.messageTransport) {
620
+ throw new Error('Message transport not initialized');
621
+ }
622
+ this.iframeStorage = new StorageRequestChannelAdapter(this.messageTransport);
623
+ }
624
+ /**
625
+ * Gets the initialized iframe instance. This method ensures the iframe is properly loaded and configured.
626
+ * The first call will initialize and await the iframe loading process.
627
+ * Subsequent calls will return the same iframe instance immediately.
628
+ *
629
+ * @throws {Error} If iframe initialization fails
630
+ * @throws {Error} If message transport initialization fails
631
+ * @throws {Error} If iframe storage initialization fails
632
+ * @returns {Promise<HTMLIFrameElement>} The initialized iframe element
633
+ */ async getIframe() {
634
+ await this.initializeIframeCommunication();
635
+ if (!this.iframe) {
636
+ throw new Error('Failed to initialize iframe');
637
+ }
638
+ if (!this.messageTransport) {
639
+ throw new Error('Failed to initialize message transport');
640
+ }
641
+ if (!this.iframeStorage) {
642
+ throw new Error('Failed to initialize iframe storage');
643
+ }
644
+ return this.iframe;
645
+ }
646
+ /**
647
+ * Gets the initialized iframe storage instance. This method ensures the iframe storage is properly configured.
648
+ * The first call will initialize and await the iframe communication process.
649
+ * Subsequent calls will return the same storage instance immediately.
650
+ *
651
+ * @throws {Error} If iframe storage initialization fails
652
+ * @returns {Promise<SupportedStorage>} The initialized iframe storage instance
653
+ */ async getIframeStorage() {
654
+ await this.initializeIframeCommunication();
655
+ if (!this.iframeStorage) {
656
+ throw new Error('Failed to initialize iframe storage');
657
+ }
658
+ return this.iframeStorage;
659
+ }
660
+ /**
406
661
  * Client initialization logic
407
662
  */ async _initialize() {
408
663
  try {
409
- const initializePromises = [];
410
- initializePromises.push(this.restoreWallets());
664
+ const initializePromises = [
665
+ this.restoreWallets(),
666
+ this.useIframeStorage && this.initializeIframeCommunication()
667
+ ];
411
668
  await Promise.all(initializePromises);
412
669
  return {
413
670
  error: null
@@ -514,6 +771,52 @@ class DynamicWalletClient {
514
771
  throw new Error('Error creating wallet account');
515
772
  }
516
773
  }
774
+ async importRawPrivateKey({ chainName, privateKey, thresholdSignatureScheme, onError, onCeremonyComplete }) {
775
+ const mpcSigner = getMPCSigner({
776
+ chainName,
777
+ baseRelayUrl: this.baseMPCRelayApiUrl
778
+ });
779
+ const clientKeygenInitResults = await this.clientInitializeKeyGen({
780
+ chainName,
781
+ thresholdSignatureScheme
782
+ });
783
+ const clientKeygenIds = clientKeygenInitResults.map((result)=>result.keygenId);
784
+ const { roomId, serverKeygenIds } = await this.apiClient.importPrivateKey({
785
+ chainName,
786
+ clientKeygenIds,
787
+ thresholdSignatureScheme,
788
+ onError,
789
+ onCeremonyComplete
790
+ });
791
+ const { threshold } = core.getTSSConfig(thresholdSignatureScheme);
792
+ const clientKeygenResults = await Promise.all(clientKeygenInitResults.map(async (currentInit, index)=>{
793
+ const otherClientKeygenIds = clientKeygenInitResults.filter((init)=>init.keygenId !== currentInit.keygenId).map((init)=>init.keygenId);
794
+ if (index === 0) {
795
+ const otherKeyGenIds = [
796
+ ...serverKeygenIds,
797
+ ...otherClientKeygenIds
798
+ ];
799
+ const importerKeygenResult = await mpcSigner.importPrivateKeyImporter(roomId, threshold, privateKey, currentInit, otherKeyGenIds);
800
+ return importerKeygenResult;
801
+ } else {
802
+ const recipientKeygenResult = await mpcSigner.importPrivateKeyRecipient(roomId, threshold, currentInit, [
803
+ ...serverKeygenIds,
804
+ ...otherClientKeygenIds
805
+ ]);
806
+ return recipientKeygenResult;
807
+ }
808
+ }));
809
+ const [clientKeygenResult] = clientKeygenResults;
810
+ const rawPublicKey = await this.derivePublicKey({
811
+ chainName,
812
+ keyShare: clientKeygenResult,
813
+ derivationPath: undefined
814
+ });
815
+ return {
816
+ rawPublicKey,
817
+ clientKeyShares: clientKeygenResults
818
+ };
819
+ }
517
820
  async serverSign({ walletId, message }) {
518
821
  // Create the room and sign the message
519
822
  if (typeof message !== 'string') {
@@ -562,12 +865,12 @@ class DynamicWalletClient {
562
865
  await this.verifyPassword({
563
866
  accountAddress,
564
867
  password,
565
- walletOperation: WalletOperation.SIGN_MESSAGE
868
+ walletOperation: core.WalletOperation.SIGN_MESSAGE
566
869
  });
567
870
  const wallet = await this.getWallet({
568
871
  accountAddress,
569
872
  password,
570
- walletOperation: WalletOperation.SIGN_MESSAGE
873
+ walletOperation: core.WalletOperation.SIGN_MESSAGE
571
874
  });
572
875
  // Perform the server sign
573
876
  const data = await this.serverSign({
@@ -576,11 +879,14 @@ class DynamicWalletClient {
576
879
  });
577
880
  const derivationPath = wallet.derivationPath && wallet.derivationPath != '' ? new Uint32Array(Object.values(JSON.parse(wallet.derivationPath))) : undefined;
578
881
  // Perform the client sign and return the signature
882
+ const clientKeyShares = await this.getClientKeySharesFromLocalStorage({
883
+ accountAddress
884
+ });
579
885
  const signature = await this.clientSign({
580
886
  chainName,
581
887
  message,
582
888
  roomId: data.roomId,
583
- keyShare: wallet.clientKeyShares[0],
889
+ keyShare: clientKeyShares[0],
584
890
  derivationPath
585
891
  });
586
892
  return signature;
@@ -589,11 +895,11 @@ class DynamicWalletClient {
589
895
  await this.verifyPassword({
590
896
  accountAddress,
591
897
  password,
592
- walletOperation: WalletOperation.REFRESH
898
+ walletOperation: core.WalletOperation.REFRESH
593
899
  });
594
900
  const wallet = await this.getWallet({
595
901
  accountAddress,
596
- walletOperation: WalletOperation.REFRESH,
902
+ walletOperation: core.WalletOperation.NO_OPERATION,
597
903
  password
598
904
  });
599
905
  const mpcSigner = getMPCSigner({
@@ -605,12 +911,18 @@ class DynamicWalletClient {
605
911
  walletId: wallet.walletId
606
912
  });
607
913
  const roomId = data.roomId;
608
- const refreshResults = await Promise.all(wallet.clientKeyShares.map((clientKeyShare)=>mpcSigner.refresh(roomId, clientKeyShare)));
914
+ const clientKeyShares = await this.getClientKeySharesFromLocalStorage({
915
+ accountAddress
916
+ });
917
+ const refreshResults = await Promise.all(clientKeyShares.map((clientKeyShare)=>mpcSigner.refresh(roomId, clientKeyShare)));
609
918
  this.walletMap[accountAddress] = _extends({}, this.walletMap[accountAddress], {
610
- clientKeyShares: refreshResults,
611
919
  clientKeySharesBackupInfo: getClientKeyShareBackupInfo()
612
920
  });
613
- await this.storage.setItem(this.storageKey, JSON.stringify(this.walletMap));
921
+ await this.setClientKeySharesToLocalStorage({
922
+ accountAddress,
923
+ clientKeyShares: refreshResults,
924
+ overwriteOrMerge: 'overwrite'
925
+ });
614
926
  await this.storeEncryptedBackupByWallet({
615
927
  accountAddress,
616
928
  password: password != null ? password : this.environmentId
@@ -638,7 +950,7 @@ class DynamicWalletClient {
638
950
  * existingClientKeyShares: ClientKeyShare[]
639
951
  * }>} Object containing new and existing client keygen results, IDs and shares
640
952
  * @todo Support higher to lower reshare strategies
641
- */ async reshareStrategy({ chainName, wallet, oldThresholdSignatureScheme, newThresholdSignatureScheme }) {
953
+ */ async reshareStrategy({ chainName, wallet, accountAddress, oldThresholdSignatureScheme, newThresholdSignatureScheme }) {
642
954
  const mpcSigner = getMPCSigner({
643
955
  chainName,
644
956
  baseRelayUrl: this.baseMPCRelayApiUrl
@@ -654,7 +966,9 @@ class DynamicWalletClient {
654
966
  }, ()=>mpcSigner.initKeygen()));
655
967
  const newClientKeygenIds = newClientInitKeygenResults.map((result)=>result.keygenId);
656
968
  // Get existing client shares
657
- const existingClientKeyShares = wallet.clientKeyShares.slice(0, existingClientShareCount);
969
+ const existingClientKeyShares = (await this.getClientKeySharesFromLocalStorage({
970
+ accountAddress
971
+ })).slice(0, existingClientShareCount);
658
972
  const existingClientKeygenIds = await Promise.all(existingClientKeyShares.map(async (keyShare)=>await this.getExportId({
659
973
  chainName,
660
974
  clientKeyShare: keyShare
@@ -670,7 +984,7 @@ class DynamicWalletClient {
670
984
  await this.verifyPassword({
671
985
  accountAddress,
672
986
  password,
673
- walletOperation: WalletOperation.RESHARE
987
+ walletOperation: core.WalletOperation.RESHARE
674
988
  });
675
989
  const { existingClientShareCount } = core.getReshareConfig({
676
990
  oldThresholdSignatureScheme,
@@ -678,12 +992,13 @@ class DynamicWalletClient {
678
992
  });
679
993
  const wallet = await this.getWallet({
680
994
  accountAddress,
681
- walletOperation: WalletOperation.RESHARE,
995
+ walletOperation: core.WalletOperation.NO_OPERATION,
682
996
  shareCount: existingClientShareCount,
683
997
  password
684
998
  });
685
999
  const { newClientInitKeygenResults, newClientKeygenIds, existingClientKeygenIds, existingClientKeyShares } = await this.reshareStrategy({
686
1000
  chainName,
1001
+ accountAddress,
687
1002
  wallet,
688
1003
  oldThresholdSignatureScheme,
689
1004
  newThresholdSignatureScheme
@@ -716,35 +1031,39 @@ class DynamicWalletClient {
716
1031
  ...newClientInitKeygenResults.map((keygenResult)=>mpcSigner.reshareNewParty(roomId, oldMpcConfig.threshold, newMpcConfig.threshold, keygenResult, allPartyKeygenIds)),
717
1032
  ...existingClientKeyShares.map((keyShare)=>mpcSigner.reshareRemainingParty(roomId, newMpcConfig.threshold, keyShare, allPartyKeygenIds))
718
1033
  ]);
719
- this.walletMap[accountAddress] = _extends({}, this.walletMap[accountAddress], {
720
- clientKeyShares: reshareResults
1034
+ await this.setClientKeySharesToLocalStorage({
1035
+ accountAddress,
1036
+ clientKeyShares: reshareResults,
1037
+ overwriteOrMerge: 'overwrite'
721
1038
  });
722
- await this.storage.setItem(this.storageKey, JSON.stringify(this.walletMap));
723
1039
  await this.storeEncryptedBackupByWallet({
724
1040
  accountAddress,
725
1041
  password
726
1042
  });
727
1043
  return reshareResults;
728
1044
  }
729
- async exportKey({ accountAddress, chainName, password = undefined }) {
1045
+ async exportKey({ accountAddress, displayContainer, chainName, password = undefined }) {
730
1046
  const wallet = await this.getWallet({
731
1047
  accountAddress,
732
1048
  password,
733
- walletOperation: WalletOperation.EXPORT_PRIVATE_KEY
1049
+ walletOperation: core.WalletOperation.EXPORT_PRIVATE_KEY
734
1050
  });
735
1051
  const mpcSigner = getMPCSigner({
736
1052
  chainName,
737
1053
  baseRelayUrl: this.baseMPCRelayApiUrl
738
1054
  });
1055
+ const clientKeyShares = await this.getClientKeySharesFromLocalStorage({
1056
+ accountAddress
1057
+ });
739
1058
  const exportId = await this.getExportId({
740
1059
  chainName,
741
- clientKeyShare: wallet.clientKeyShares[0]
1060
+ clientKeyShare: clientKeyShares[0]
742
1061
  });
743
1062
  const data = await this.apiClient.exportKey({
744
1063
  walletId: wallet.walletId,
745
1064
  exportId
746
1065
  });
747
- const keyExportRaw = await mpcSigner.exportFullPrivateKey(data.roomId, wallet.clientKeyShares[0], exportId);
1066
+ const keyExportRaw = await mpcSigner.exportFullPrivateKey(data.roomId, clientKeyShares[0], exportId);
748
1067
  if (!keyExportRaw) {
749
1068
  throw new Error('Error exporting private key');
750
1069
  }
@@ -787,11 +1106,17 @@ class DynamicWalletClient {
787
1106
  } else if (mpcSigner instanceof web.BIP340) {
788
1107
  derivedPrivateKey = await mpcSigner.derivePrivateKeyFromXpriv(keyExportRaw, walletDerivationPath);
789
1108
  }
1109
+ const rawPublicKey = await this.derivePublicKey({
1110
+ chainName,
1111
+ keyShare: walletKeyShares[0],
1112
+ derivationPath: walletDerivationPath
1113
+ });
790
1114
  return {
791
- derivedPrivateKey
1115
+ derivedPrivateKey,
1116
+ rawPublicKey
792
1117
  };
793
1118
  } catch (error) {
794
- console.error('Error in offlineExportKey:', error);
1119
+ this.logger.error('Error in offlineExportKey:', error);
795
1120
  throw error;
796
1121
  }
797
1122
  }
@@ -806,6 +1131,47 @@ class DynamicWalletClient {
806
1131
  return serializedEncryptedKeyShare;
807
1132
  }
808
1133
  /**
1134
+ * temporary helper function to store encrypted backup by wallet based on `useIframeStorage` flag
1135
+ * TODO: revise this to only use iframe storage when iframe is deployed
1136
+ */ async getClientKeySharesFromLocalStorage({ accountAddress }) {
1137
+ var _this_iframeStorage;
1138
+ if (!this.useIframeStorage) {
1139
+ return this.walletMap[accountAddress].clientKeyShares;
1140
+ }
1141
+ await this.initializeIframeCommunication();
1142
+ const walletObject = await ((_this_iframeStorage = this.iframeStorage) == null ? void 0 : _this_iframeStorage.getItem(accountAddress));
1143
+ if (!walletObject) {
1144
+ this.logger.debug(`No item found in iframe local storage for accountAddress: ${accountAddress}`);
1145
+ return [];
1146
+ }
1147
+ try {
1148
+ return (walletObject == null ? void 0 : walletObject.clientKeyShares) || [];
1149
+ } catch (error) {
1150
+ this.logger.error(`Error parsing clientKeyShares: ${error} for accountAddress: ${accountAddress}`);
1151
+ return [];
1152
+ }
1153
+ }
1154
+ /**
1155
+ * temporary helper function to store encrypted backup by wallet based on `useIframeStorage` flag
1156
+ * TODO: revise this to only use iframe storage when iframe is deployed
1157
+ */ async setClientKeySharesToLocalStorage({ accountAddress, clientKeyShares, overwriteOrMerge = 'merge' }) {
1158
+ var _this_iframeStorage;
1159
+ if (!this.useIframeStorage) {
1160
+ var _this_walletMap_accountAddress;
1161
+ this.walletMap[accountAddress] = _extends({}, this.walletMap[accountAddress] || {}, {
1162
+ clientKeyShares: overwriteOrMerge === 'overwrite' ? clientKeyShares : mergeUniqueKeyShares(((_this_walletMap_accountAddress = this.walletMap[accountAddress]) == null ? void 0 : _this_walletMap_accountAddress.clientKeyShares) || [], clientKeyShares)
1163
+ });
1164
+ await this.storage.setItem(this.storageKey, JSON.stringify(this.walletMap));
1165
+ return;
1166
+ }
1167
+ await this.initializeIframeCommunication();
1168
+ await ((_this_iframeStorage = this.iframeStorage) == null ? void 0 : _this_iframeStorage.setItem(accountAddress, {
1169
+ clientKeyShares: overwriteOrMerge === 'overwrite' ? clientKeyShares : mergeUniqueKeyShares(await this.getClientKeySharesFromLocalStorage({
1170
+ accountAddress
1171
+ }), clientKeyShares)
1172
+ }));
1173
+ }
1174
+ /**
809
1175
  * Encrypts and stores wallet key shares as backups.
810
1176
  *
811
1177
  * This method encrypts all client key shares for a specific wallet and stores them
@@ -823,7 +1189,9 @@ class DynamicWalletClient {
823
1189
  * @param {string} params.accountAddress - The account address of the wallet to backup
824
1190
  * @param {string} [params.password] - Optional password for encrypting the key shares
825
1191
  */ async storeEncryptedBackupByWallet({ accountAddress, clientKeyShares = undefined, password = undefined }) {
826
- const keySharesToBackup = clientKeyShares != null ? clientKeyShares : this.walletMap[accountAddress].clientKeyShares;
1192
+ const keySharesToBackup = clientKeyShares != null ? clientKeyShares : await this.getClientKeySharesFromLocalStorage({
1193
+ accountAddress
1194
+ });
827
1195
  const encryptedKeyShares = await Promise.all(keySharesToBackup.map((keyShare)=>this.encryptKeyShare({
828
1196
  keyShare,
829
1197
  password
@@ -884,7 +1252,7 @@ class DynamicWalletClient {
884
1252
  await this.getWallet({
885
1253
  accountAddress,
886
1254
  password: existingPassword,
887
- walletOperation: WalletOperation.REACH_ALL_PARTIES
1255
+ walletOperation: core.WalletOperation.REACH_ALL_PARTIES
888
1256
  });
889
1257
  await this.storeEncryptedBackupByWallet({
890
1258
  accountAddress,
@@ -914,7 +1282,7 @@ class DynamicWalletClient {
914
1282
  */ recoverStrategy({ clientKeyShareBackupInfo, thresholdSignatureScheme, walletOperation, shareCount = undefined }) {
915
1283
  const { backups } = clientKeyShareBackupInfo;
916
1284
  const { clientThreshold } = core.MPC_CONFIG[thresholdSignatureScheme];
917
- let requiredShareCount = walletOperation === WalletOperation.REFRESH || walletOperation === WalletOperation.REACH_ALL_PARTIES || walletOperation === WalletOperation.RESHARE ? clientThreshold : 1;
1285
+ let requiredShareCount = walletOperation === core.WalletOperation.REFRESH || walletOperation === core.WalletOperation.REACH_ALL_PARTIES || walletOperation === core.WalletOperation.RESHARE ? clientThreshold : 1;
918
1286
  // Override requiredShareCount if shareCount is provided
919
1287
  if (shareCount !== undefined) {
920
1288
  requiredShareCount = shareCount;
@@ -947,8 +1315,10 @@ class DynamicWalletClient {
947
1315
  password: password != null ? password : this.environmentId
948
1316
  })));
949
1317
  if (storeRecoveredShares) {
950
- this.walletMap[accountAddress] = _extends({}, this.walletMap[accountAddress], {
951
- clientKeyShares: mergeUniqueKeyShares(this.walletMap[accountAddress].clientKeyShares || [], decryptedKeyShares)
1318
+ await this.setClientKeySharesToLocalStorage({
1319
+ accountAddress,
1320
+ clientKeyShares: decryptedKeyShares,
1321
+ overwriteOrMerge: 'merge'
952
1322
  });
953
1323
  await this.storage.setItem(this.storageKey, JSON.stringify(this.walletMap));
954
1324
  }
@@ -964,10 +1334,12 @@ class DynamicWalletClient {
964
1334
  async backupKeySharesToGoogleDrive({ accountAddress, fileName, oauthAccountId, password }) {
965
1335
  await this.getWallet({
966
1336
  accountAddress,
967
- walletOperation: WalletOperation.REACH_ALL_PARTIES,
1337
+ walletOperation: core.WalletOperation.REACH_ALL_PARTIES,
968
1338
  password
969
1339
  });
970
- const clientKeyShares = this.walletMap[accountAddress].clientKeyShares;
1340
+ const clientKeyShares = await this.getClientKeySharesFromLocalStorage({
1341
+ accountAddress
1342
+ });
971
1343
  if (clientKeyShares.length === 0) {
972
1344
  throw new Error('No key shares found');
973
1345
  }
@@ -1022,7 +1394,7 @@ class DynamicWalletClient {
1022
1394
  await this.storage.setItem(this.storageKey, JSON.stringify(this.walletMap));
1023
1395
  return ids;
1024
1396
  }
1025
- async restoreBackupFromGoogleDrive({ accountAddress, oauthAccountId, name, password }) {
1397
+ async restoreBackupFromGoogleDrive({ accountAddress, oauthAccountId, name, displayContainer, password }) {
1026
1398
  await this.getWallet({
1027
1399
  accountAddress
1028
1400
  });
@@ -1050,71 +1422,27 @@ class DynamicWalletClient {
1050
1422
  keyShare,
1051
1423
  password
1052
1424
  })));
1053
- this.walletMap[accountAddress] = _extends({}, this.walletMap[accountAddress], {
1054
- clientKeyShares: mergeUniqueKeyShares(this.walletMap[accountAddress].clientKeyShares || [], decryptedKeyShares)
1425
+ await this.setClientKeySharesToLocalStorage({
1426
+ accountAddress,
1427
+ clientKeyShares: decryptedKeyShares,
1428
+ overwriteOrMerge: 'merge'
1055
1429
  });
1430
+ if (this.useIframeStorage) {
1431
+ const { iframeDisplay } = await this.initializeIframeDisplayForContainer({
1432
+ container: displayContainer
1433
+ });
1434
+ iframeDisplay.displayClientShares(accountAddress);
1435
+ }
1056
1436
  return decryptedKeyShares;
1057
1437
  }
1058
- async importRawPrivateKey({ chainName, privateKey, thresholdSignatureScheme, onError, onCeremonyComplete }) {
1059
- const mpcSigner = getMPCSigner({
1060
- chainName,
1061
- baseRelayUrl: this.baseMPCRelayApiUrl
1062
- });
1063
- const clientKeygenInitResults = await this.clientInitializeKeyGen({
1064
- chainName,
1065
- thresholdSignatureScheme
1066
- });
1067
- const clientKeygenIds = clientKeygenInitResults.map((result)=>result.keygenId);
1068
- const { roomId, serverKeygenIds } = await this.apiClient.importPrivateKey({
1069
- chainName,
1070
- clientKeygenIds,
1071
- thresholdSignatureScheme,
1072
- onError,
1073
- onCeremonyComplete
1074
- });
1075
- const { threshold } = core.getTSSConfig(thresholdSignatureScheme);
1076
- const clientKeygenResults = await Promise.all(clientKeygenInitResults.map(async (currentInit, index)=>{
1077
- const otherClientKeygenIds = clientKeygenInitResults.filter((init)=>init.keygenId !== currentInit.keygenId).map((init)=>init.keygenId);
1078
- if (index === 0) {
1079
- const otherKeyGenIds = [
1080
- ...serverKeygenIds,
1081
- ...otherClientKeygenIds
1082
- ];
1083
- const importerKeygenResult = await mpcSigner.importPrivateKeyImporter(roomId, threshold, privateKey, currentInit, otherKeyGenIds);
1084
- return importerKeygenResult;
1085
- } else {
1086
- const recipientKeygenResult = await mpcSigner.importPrivateKeyRecipient(roomId, threshold, currentInit, [
1087
- ...serverKeygenIds,
1088
- ...otherClientKeygenIds
1089
- ]);
1090
- return recipientKeygenResult;
1091
- }
1092
- }));
1093
- const [clientKeygenResult] = clientKeygenResults;
1094
- const rawPublicKey = await this.derivePublicKey({
1095
- chainName,
1096
- keyShare: clientKeygenResult,
1097
- derivationPath: undefined
1098
- });
1099
- return {
1100
- rawPublicKey,
1101
- clientKeyShares: clientKeygenResults
1102
- };
1103
- }
1104
1438
  async exportClientKeyshares({ accountAddress, password }) {
1105
1439
  await this.verifyPassword({
1106
1440
  accountAddress,
1107
1441
  password,
1108
- walletOperation: WalletOperation.REACH_ALL_PARTIES
1109
- });
1110
- await this.getWallet({
1111
- accountAddress,
1112
- walletOperation: WalletOperation.REACH_ALL_PARTIES,
1113
- password
1442
+ walletOperation: core.WalletOperation.REACH_ALL_PARTIES
1114
1443
  });
1115
- const clientKeyShares = await this.getClientKeyShares({
1116
- accountAddress,
1117
- password
1444
+ const clientKeyShares = await this.getClientKeySharesFromLocalStorage({
1445
+ accountAddress
1118
1446
  });
1119
1447
  if (!accountAddress) {
1120
1448
  throw new Error('Must provide an account address');
@@ -1136,19 +1464,21 @@ class DynamicWalletClient {
1136
1464
  a.click();
1137
1465
  }
1138
1466
  async getClientKeyShares({ accountAddress, password }) {
1139
- const wallet = await this.getWallet({
1467
+ await this.getWallet({
1140
1468
  accountAddress,
1141
1469
  password,
1142
- walletOperation: WalletOperation.REACH_THRESHOLD
1470
+ walletOperation: core.WalletOperation.REACH_THRESHOLD
1471
+ });
1472
+ return this.getClientKeySharesFromLocalStorage({
1473
+ accountAddress
1143
1474
  });
1144
- return wallet.clientKeyShares;
1145
1475
  }
1146
1476
  /**
1147
1477
  * Helper function to check if the required wallet fields are present and valid
1148
1478
  * @param accountAddress - The account address of the wallet to check
1149
1479
  * @param walletOperation - The wallet operation that determines required fields
1150
1480
  * @returns boolean indicating if wallet needs to be re-fetched and restored from server
1151
- */ async checkWalletFields({ accountAddress, walletOperation = WalletOperation.REACH_THRESHOLD, shareCount }) {
1481
+ */ async checkWalletFields({ accountAddress, walletOperation = core.WalletOperation.REACH_THRESHOLD, shareCount }) {
1152
1482
  let keyshareCheck = false;
1153
1483
  let walletCheck = false;
1154
1484
  let thresholdSignatureSchemeCheck = false;
@@ -1168,7 +1498,6 @@ class DynamicWalletClient {
1168
1498
  }
1169
1499
  // check if wallet already exists with sufficient keyshares
1170
1500
  if (existingWallet) {
1171
- var _existingWallet_clientKeyShares;
1172
1501
  const { shares } = this.recoverStrategy({
1173
1502
  clientKeyShareBackupInfo: existingWallet.clientKeySharesBackupInfo || {
1174
1503
  backups: getClientKeyShareBackupInfo()
@@ -1178,7 +1507,10 @@ class DynamicWalletClient {
1178
1507
  shareCount
1179
1508
  });
1180
1509
  const { dynamic: requiredDynamicKeyShareIds = [] } = shares;
1181
- if (requiredDynamicKeyShareIds.length <= (((_existingWallet_clientKeyShares = existingWallet.clientKeyShares) == null ? void 0 : _existingWallet_clientKeyShares.length) || 0)) {
1510
+ const clientKeyShares = await this.getClientKeySharesFromLocalStorage({
1511
+ accountAddress
1512
+ });
1513
+ if (requiredDynamicKeyShareIds.length <= ((clientKeyShares == null ? void 0 : clientKeyShares.length) || 0)) {
1182
1514
  keyshareCheck = true;
1183
1515
  }
1184
1516
  }
@@ -1188,7 +1520,7 @@ class DynamicWalletClient {
1188
1520
  * verifyPassword attempts to recover and decrypt a single client key share using the provided password.
1189
1521
  * If successful, the key share is encrypted with the new password. This method solely performs the recovery
1190
1522
  * and decryption without storing the restored key shares. If unsuccessful, it throws an error.
1191
- */ async verifyPassword({ accountAddress, password = undefined, walletOperation = WalletOperation.NO_OPERATION }) {
1523
+ */ async verifyPassword({ accountAddress, password = undefined, walletOperation = core.WalletOperation.NO_OPERATION }) {
1192
1524
  await this.getWallet({
1193
1525
  accountAddress,
1194
1526
  password,
@@ -1232,7 +1564,7 @@ class DynamicWalletClient {
1232
1564
  }
1233
1565
  /**
1234
1566
  * check if the operation requires a password
1235
- */ async requiresPasswordForOperation({ accountAddress, walletOperation = WalletOperation.REACH_THRESHOLD }) {
1567
+ */ async requiresPasswordForOperation({ accountAddress, walletOperation = core.WalletOperation.REACH_THRESHOLD }) {
1236
1568
  const isEncrypted = await this.isPasswordEncrypted({
1237
1569
  accountAddress
1238
1570
  });
@@ -1246,12 +1578,14 @@ class DynamicWalletClient {
1246
1578
  }
1247
1579
  /**
1248
1580
  * check if the operation requires restoring backup shares
1249
- */ async requiresRestoreBackupSharesForOperation({ accountAddress, walletOperation = WalletOperation.REACH_THRESHOLD }) {
1581
+ */ async requiresRestoreBackupSharesForOperation({ accountAddress, walletOperation = core.WalletOperation.REACH_THRESHOLD }) {
1250
1582
  const clientKeySharesBackupInfo = await this.getWalletClientKeyShareBackupInfo({
1251
1583
  accountAddress
1252
1584
  });
1253
- const clientKeyShares = this.walletMap[accountAddress].clientKeyShares || [];
1254
- if (walletOperation === WalletOperation.REACH_ALL_PARTIES || walletOperation === WalletOperation.REFRESH || walletOperation === WalletOperation.RESHARE) {
1585
+ const clientKeyShares = await this.getClientKeySharesFromLocalStorage({
1586
+ accountAddress
1587
+ });
1588
+ if (walletOperation === core.WalletOperation.REACH_ALL_PARTIES || walletOperation === core.WalletOperation.REFRESH || walletOperation === core.WalletOperation.RESHARE) {
1255
1589
  return true;
1256
1590
  }
1257
1591
  const { requiredShareCount } = this.recoverStrategy({
@@ -1277,7 +1611,7 @@ class DynamicWalletClient {
1277
1611
  walletProperties: wallet == null ? void 0 : wallet.walletProperties
1278
1612
  });
1279
1613
  }
1280
- async getWallet({ accountAddress, walletOperation = WalletOperation.NO_OPERATION, shareCount = undefined, password = undefined }) {
1614
+ async getWallet({ accountAddress, walletOperation = core.WalletOperation.NO_OPERATION, shareCount = undefined, password = undefined }) {
1281
1615
  var _user_verifiedCredentials;
1282
1616
  const existingWalletCheck = await this.checkWalletFields({
1283
1617
  accountAddress,
@@ -1303,7 +1637,7 @@ class DynamicWalletClient {
1303
1637
  walletProperties
1304
1638
  })
1305
1639
  });
1306
- if (walletOperation !== WalletOperation.NO_OPERATION && await this.requiresRestoreBackupSharesForOperation({
1640
+ if (walletOperation !== core.WalletOperation.NO_OPERATION && await this.requiresRestoreBackupSharesForOperation({
1307
1641
  accountAddress,
1308
1642
  walletOperation
1309
1643
  })) {
@@ -1313,6 +1647,12 @@ class DynamicWalletClient {
1313
1647
  walletOperation: walletOperation,
1314
1648
  shareCount
1315
1649
  });
1650
+ if (this.useIframeStorage) {
1651
+ var _this_iframeStorage;
1652
+ (_this_iframeStorage = this.iframeStorage) == null ? void 0 : _this_iframeStorage.setItem(`${accountAddress}`, JSON.stringify({
1653
+ clientKeyShares: decryptedKeyShares
1654
+ }));
1655
+ }
1316
1656
  this.logger.debug('Recovered backup', decryptedKeyShares);
1317
1657
  }
1318
1658
  const walletCount = Object.keys(this.walletMap).length;
@@ -1345,13 +1685,13 @@ class DynamicWalletClient {
1345
1685
  };
1346
1686
  });
1347
1687
  this.walletMap = wallets.reduce((acc, wallet)=>{
1348
- var _acc_accountAddress;
1688
+ var _acc_wallet_accountAddress, _acc_accountAddress;
1349
1689
  const accountAddress = wallet.accountAddress;
1350
1690
  acc[wallet.accountAddress] = {
1351
1691
  walletId: wallet.walletId,
1352
1692
  chainName: wallet.chainName,
1353
1693
  accountAddress: wallet.accountAddress,
1354
- clientKeyShares: wallet.clientKeyShares || [],
1694
+ clientKeyShares: ((_acc_wallet_accountAddress = acc[wallet.accountAddress]) == null ? void 0 : _acc_wallet_accountAddress.clientKeyShares) || [],
1355
1695
  clientKeySharesBackupInfo: wallet.clientKeySharesBackupInfo,
1356
1696
  derivationPath: ((_acc_accountAddress = acc[accountAddress]) == null ? void 0 : _acc_accountAddress.derivationPath) || undefined,
1357
1697
  thresholdSignatureScheme: wallet.thresholdSignatureScheme
@@ -1365,7 +1705,14 @@ class DynamicWalletClient {
1365
1705
  this.logger = logger;
1366
1706
  this.walletMap = {} // todo: store in session storage
1367
1707
  ;
1708
+ this.iframeStorage = null;
1709
+ this.iframeDisplay = null;
1368
1710
  this.memoryStorage = null;
1711
+ this.messageTransport = null;
1712
+ this.iframe = null;
1713
+ this.useIframeStorage = false // TODO: remove this when iframe is deployed
1714
+ ;
1715
+ this.iframeLoadPromise = null;
1369
1716
  this.environmentId = environmentId;
1370
1717
  this.storageKey = `${STORAGE_KEY}-${storageKey != null ? storageKey : environmentId}`;
1371
1718
  this.baseMPCRelayApiUrl = baseMPCRelayApiUrl;
@@ -1383,6 +1730,9 @@ class DynamicWalletClient {
1383
1730
  this.memoryStorage = {};
1384
1731
  this.storage = memoryLocalStorageAdapter(this.memoryStorage);
1385
1732
  }
1733
+ this.iframeDomain = core.IFRAME_DOMAIN;
1734
+ // Generate unique instanceId when client is created
1735
+ this.instanceId = crypto.randomUUID();
1386
1736
  // initialize the client
1387
1737
  this.initialize();
1388
1738
  }
@@ -1437,7 +1787,6 @@ Object.defineProperty(exports, "MessageHash", {
1437
1787
  get: function () { return web.MessageHash; }
1438
1788
  });
1439
1789
  exports.DynamicWalletClient = DynamicWalletClient;
1440
- exports.WalletOperation = WalletOperation;
1441
1790
  exports.base64ToBytes = base64ToBytes;
1442
1791
  exports.bytesToBase64 = bytesToBase64;
1443
1792
  exports.ensureBase64Padding = ensureBase64Padding;