@dynamic-labs-wallet/browser 0.0.0-pr384.2 → 0.0.0-pr534.0

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.
Files changed (42) hide show
  1. package/index.cjs.js +719 -341
  2. package/index.esm.js +714 -344
  3. package/internal/core/bip340.cjs +1 -0
  4. package/internal/core/bip340.d.ts +1 -1
  5. package/internal/core/common.cjs +1 -0
  6. package/internal/core/common.d.ts +1 -0
  7. package/internal/core/ecdsa.cjs +1 -0
  8. package/internal/core/ecdsa.d.ts +1 -1
  9. package/internal/core/ed25519.cjs +1 -0
  10. package/internal/core/ed25519_exportable.cjs +1 -0
  11. package/internal/core/ed25519_exportable.d.ts +0 -1
  12. package/internal/core/index.cjs +1 -0
  13. package/internal/core/native.d.ts +2 -2
  14. package/internal/core/sr25519.cjs +1 -0
  15. package/internal/core/types.cjs +1 -0
  16. package/internal/web/generated/libmpc_executor.d.ts +30 -30
  17. package/internal/web/generated/libmpc_executor.js +2 -2
  18. package/internal/web/generated/libmpc_executor_bg.wasm +0 -0
  19. package/internal/web/index.d.ts +1 -1
  20. package/internal/web/index.js +1 -1
  21. package/package.json +4 -4
  22. package/src/client.d.ts +99 -34
  23. package/src/client.d.ts.map +1 -1
  24. package/src/constants.d.ts +2 -0
  25. package/src/constants.d.ts.map +1 -1
  26. package/src/services/encryption.d.ts +19 -0
  27. package/src/services/encryption.d.ts.map +1 -0
  28. package/src/types.d.ts +36 -1
  29. package/src/types.d.ts.map +1 -1
  30. package/src/utils.d.ts +8 -2
  31. package/src/utils.d.ts.map +1 -1
  32. package/internal/core/bip340.js +0 -1
  33. package/internal/core/common.js +0 -1
  34. package/internal/core/ecdsa.js +0 -1
  35. package/internal/core/ed25519.js +0 -1
  36. package/internal/core/ed25519_exportable.js +0 -1
  37. package/internal/core/index.js +0 -1
  38. package/internal/core/package.json +0 -17
  39. package/internal/core/sr25519.js +0 -1
  40. package/internal/core/types.js +0 -1
  41. package/internal/web/package.json +0 -17
  42. /package/internal/core/{native.js → native.cjs} +0 -0
package/index.esm.js CHANGED
@@ -1,15 +1,15 @@
1
- import { SigningAlgorithm, MPC_RELAY_PROD_API_URL, getMPCChainConfig, AuthMode, BackupLocation, parseNamespacedVersion, getClientThreshold, MPC_CONFIG, getTSSConfig, WalletOperation, getReshareConfig, FEATURE_FLAGS, ThresholdSignatureScheme, DELEGATED_SHARE_COUNT, verifiedCredentialNameToChainEnum, DynamicApiClient, getEnvironmentFromUrl, IFRAME_DOMAIN_MAP } from '@dynamic-labs-wallet/core';
1
+ import { SigningAlgorithm, MPC_RELAY_PROD_API_URL, getMPCChainConfig, AuthMode, BackupLocation, parseNamespacedVersion, FEATURE_FLAGS, ThresholdSignatureScheme, getClientThreshold, MPC_CONFIG, getTSSConfig, serializeMessageForForwardMPC, WalletOperation, getReshareConfig, verifiedCredentialNameToChainEnum, DynamicApiClient, getEnvironmentFromUrl, IFRAME_DOMAIN_MAP } from '@dynamic-labs-wallet/core';
2
2
  export * from '@dynamic-labs-wallet/core';
3
- import { BIP340, ExportableEd25519, Ecdsa, MessageHash, EcdsaKeygenResult, ExportableEd25519KeygenResult, BIP340KeygenResult } from '#internal/web';
3
+ import { BIP340, ExportableEd25519, Ecdsa, MessageHash, EcdsaSignature, EcdsaKeygenResult, ExportableEd25519KeygenResult, BIP340KeygenResult } from '#internal/web';
4
4
  export { BIP340, BIP340InitKeygenResult, BIP340KeygenResult, Ecdsa, EcdsaInitKeygenResult, EcdsaKeygenResult, EcdsaPublicKey, EcdsaSignature, Ed25519, Ed25519InitKeygenResult, Ed25519KeygenResult, MessageHash } from '#internal/web';
5
5
  import { gte } from 'semver';
6
6
  import { v4 } from 'uuid';
7
7
  import { Logger, LogLevel } from '@dynamic-labs/logger';
8
8
  export { Logger } from '@dynamic-labs/logger';
9
+ import { ProviderEnum, RoomTypeEnum } from '@dynamic-labs/sdk-api-core';
9
10
  import loadArgon2idWasm from 'argon2id';
10
11
  import { AxiosError } from 'axios';
11
12
  import createHttpError from 'http-errors';
12
- import { ProviderEnum } from '@dynamic-labs/sdk-api-core';
13
13
 
14
14
  function _extends() {
15
15
  _extends = Object.assign || function assign(target) {
@@ -464,10 +464,13 @@ const SIGNED_SESSION_ID_MIN_VERSION_BY_NAMESPACE = {
464
464
  WalletKit: '4.25.4',
465
465
  ClientSDK: '0.1.0-alpha.0'
466
466
  };
467
+ const ROOM_EXPIRATION_TIME = 1000 * 60 * 10; // 10 minutes
468
+ const ROOM_CACHE_COUNT = 5;
467
469
 
468
470
  const isBrowser = ()=>typeof window !== 'undefined';
469
- const getClientKeyShareExportFileName = ({ thresholdSignatureScheme, accountAddress })=>{
470
- return `${CLIENT_KEYSHARE_EXPORT_FILENAME_PREFIX}-${thresholdSignatureScheme}-${accountAddress}.json`;
471
+ const getClientKeyShareExportFileName = ({ thresholdSignatureScheme, accountAddress, isGoogleDrive = false })=>{
472
+ const suffix = isGoogleDrive ? '-google-drive' : '';
473
+ return `${CLIENT_KEYSHARE_EXPORT_FILENAME_PREFIX}-${thresholdSignatureScheme}-${accountAddress}${suffix}.json`;
471
474
  };
472
475
  const getClientKeyShareBackupInfo = (params)=>{
473
476
  var _params_walletProperties, _params_walletProperties_keyShares_;
@@ -476,7 +479,8 @@ const getClientKeyShareBackupInfo = (params)=>{
476
479
  [BackupLocation.GOOGLE_DRIVE]: [],
477
480
  [BackupLocation.ICLOUD]: [],
478
481
  [BackupLocation.USER]: [],
479
- [BackupLocation.EXTERNAL]: []
482
+ [BackupLocation.EXTERNAL]: [],
483
+ [BackupLocation.DELEGATED]: []
480
484
  };
481
485
  if (!(params == null ? void 0 : (_params_walletProperties = params.walletProperties) == null ? void 0 : _params_walletProperties.keyShares)) {
482
486
  return {
@@ -606,6 +610,19 @@ const createBackupData = ({ encryptedKeyShares, accountAddress, thresholdSignatu
606
610
  }
607
611
  };
608
612
  };
613
+ const downloadStringAsFile = ({ filename, content, mimeType = 'application/json' })=>{
614
+ const blob = new Blob([
615
+ content
616
+ ], {
617
+ type: mimeType
618
+ });
619
+ const url = URL.createObjectURL(blob);
620
+ const a = document.createElement('a');
621
+ a.href = url;
622
+ a.download = filename;
623
+ a.click();
624
+ URL.revokeObjectURL(url);
625
+ };
609
626
 
610
627
  /**
611
628
  * Uploads a backup to Google Drive App
@@ -652,6 +669,84 @@ const createBackupData = ({ encryptedKeyShares, accountAddress, thresholdSignatu
652
669
  }
653
670
  };
654
671
 
672
+ const ALG_LABEL_RSA = 'HYBRID-RSA-AES-256';
673
+ /**
674
+ * Convert base64 to base64url encoding
675
+ */ const toBase64Url = (buffer)=>{
676
+ const base64 = Buffer.from(buffer).toString('base64');
677
+ return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
678
+ };
679
+ /**
680
+ * Convert ArrayBuffer to base64url
681
+ */ const arrayBufferToBase64Url = (buffer)=>{
682
+ return toBase64Url(buffer);
683
+ };
684
+ /**
685
+ * Import RSA public key from PEM format
686
+ */ const importRSAPublicKey = async (publicKeyPem)=>{
687
+ // Remove PEM headers and decode base64
688
+ const pemHeader = '-----BEGIN PUBLIC KEY-----';
689
+ const pemFooter = '-----END PUBLIC KEY-----';
690
+ const pemContents = publicKeyPem.replace(pemHeader, '').replace(pemFooter, '').replace(/\s/g, '');
691
+ const binaryDer = Buffer.from(pemContents, 'base64').toString('binary');
692
+ const keyData = new Uint8Array(binaryDer.length);
693
+ for(let i = 0; i < binaryDer.length; i++){
694
+ keyData[i] = binaryDer.charCodeAt(i);
695
+ }
696
+ return await crypto.subtle.importKey('spki', keyData, {
697
+ name: 'RSA-OAEP',
698
+ hash: 'SHA-256'
699
+ }, false, [
700
+ 'encrypt'
701
+ ]);
702
+ };
703
+ // encodedEnvelopeBytes intentionally omitted; alg/ct/ek/iv/tag/kid is sufficient
704
+ /**
705
+ * Encrypts data using HYBRID-RSA-AES-256 encryption scheme with Web Crypto API.
706
+ * 1. Generate random AES-256 key
707
+ * 2. Encrypt AES key with RSA public key
708
+ * 3. Encrypt data with AES-256-GCM
709
+ */ const encryptDelegatedKeyShare = async (data, publicKeyPem, keyId)=>{
710
+ try {
711
+ // Step 1: Generate a random AES-256 key and 16-byte IV
712
+ const aesKey = await crypto.subtle.generateKey({
713
+ name: 'AES-GCM',
714
+ length: 256
715
+ }, true, [
716
+ 'encrypt'
717
+ ]);
718
+ const iv = crypto.getRandomValues(new Uint8Array(16)); // 128-bit IV for GCM
719
+ // Step 2: Encrypt the data with AES-256-GCM
720
+ const plaintext = new TextEncoder().encode(data);
721
+ const encryptedData = await crypto.subtle.encrypt({
722
+ name: 'AES-GCM',
723
+ iv: iv
724
+ }, aesKey, plaintext);
725
+ // Extract the auth tag from the encrypted data (last 16 bytes)
726
+ const encryptedDataArray = new Uint8Array(encryptedData);
727
+ const authTag = encryptedDataArray.slice(-16);
728
+ const ciphertext = encryptedDataArray.slice(0, -16);
729
+ // Step 3: Encrypt the AES key with RSA public key
730
+ const rsaPublicKey = await importRSAPublicKey(publicKeyPem);
731
+ // Export the AES key to encrypt it
732
+ const aesKeyData = await crypto.subtle.exportKey('raw', aesKey);
733
+ const encryptedAesKey = await crypto.subtle.encrypt({
734
+ name: 'RSA-OAEP'
735
+ }, rsaPublicKey, aesKeyData);
736
+ return _extends({
737
+ alg: ALG_LABEL_RSA,
738
+ iv: arrayBufferToBase64Url(iv.buffer),
739
+ ct: arrayBufferToBase64Url(ciphertext.buffer),
740
+ tag: arrayBufferToBase64Url(authTag.buffer),
741
+ ek: arrayBufferToBase64Url(encryptedAesKey)
742
+ }, keyId ? {
743
+ kid: keyId
744
+ } : {});
745
+ } catch (error) {
746
+ throw new Error(`Encryption failed: ${error instanceof Error ? error.message : String(error)}`);
747
+ }
748
+ };
749
+
655
750
  const localStorageWriteTest = {
656
751
  tested: false,
657
752
  writable: false
@@ -721,7 +816,61 @@ const localStorageWriteTest = {
721
816
  }
722
817
  });
723
818
 
819
+ const createDelegationWithGoogleDriveDistribution = ({ existingShares, delegatedShare })=>({
820
+ dynamicBackendShares: existingShares,
821
+ googleDriveShares: existingShares,
822
+ delegatedShare
823
+ });
824
+ const createDelegationOnlyDistribution = ({ existingShares, delegatedShare })=>({
825
+ dynamicBackendShares: existingShares,
826
+ googleDriveShares: [],
827
+ delegatedShare
828
+ });
829
+ const createGoogleDriveOnlyDistribution = ({ allShares })=>({
830
+ dynamicBackendShares: allShares.slice(0, -1),
831
+ googleDriveShares: allShares.slice(-1)
832
+ });
833
+ const createDynamicOnlyDistribution = ({ allShares })=>({
834
+ dynamicBackendShares: allShares,
835
+ googleDriveShares: []
836
+ });
837
+ const hasGoogleDriveBackup = (backupInfo)=>{
838
+ var _backupInfo_backups_BackupLocation_GOOGLE_DRIVE, _backupInfo_backups;
839
+ var _backupInfo_backups_BackupLocation_GOOGLE_DRIVE_length;
840
+ return ((_backupInfo_backups_BackupLocation_GOOGLE_DRIVE_length = backupInfo == null ? void 0 : (_backupInfo_backups = backupInfo.backups) == null ? void 0 : (_backupInfo_backups_BackupLocation_GOOGLE_DRIVE = _backupInfo_backups[BackupLocation.GOOGLE_DRIVE]) == null ? void 0 : _backupInfo_backups_BackupLocation_GOOGLE_DRIVE.length) != null ? _backupInfo_backups_BackupLocation_GOOGLE_DRIVE_length : 0) > 0;
841
+ };
842
+ const hasDelegatedBackup = (backupInfo)=>{
843
+ var _backupInfo_backups_BackupLocation_DELEGATED, _backupInfo_backups;
844
+ var _backupInfo_backups_BackupLocation_DELEGATED_length;
845
+ return ((_backupInfo_backups_BackupLocation_DELEGATED_length = backupInfo == null ? void 0 : (_backupInfo_backups = backupInfo.backups) == null ? void 0 : (_backupInfo_backups_BackupLocation_DELEGATED = _backupInfo_backups[BackupLocation.DELEGATED]) == null ? void 0 : _backupInfo_backups_BackupLocation_DELEGATED.length) != null ? _backupInfo_backups_BackupLocation_DELEGATED_length : 0) > 0;
846
+ };
847
+ /**
848
+ * Distribution for adding Google Drive backup to an existing delegation.
849
+ * Client's shares go to both Dynamic and Google Drive.
850
+ * delegatedShare is undefined - we don't re-publish, but preserve the location.
851
+ */ const createAddGoogleDriveToExistingDelegationDistribution = ({ clientShares })=>({
852
+ dynamicBackendShares: clientShares,
853
+ googleDriveShares: clientShares
854
+ });
855
+
724
856
  class DynamicWalletClient {
857
+ async initializeForwardMPCClient() {
858
+ try {
859
+ await this.apiClient.forwardMPCClient.connect();
860
+ this.logger.info('Connected to ForwardMPC enclave websocket. Instance: ' + this.instanceId);
861
+ try {
862
+ await this.apiClient.forwardMPCClient.handshake();
863
+ this.logger.debug('Handshaked with ForwardMPC enclave websocket. Instance: ' + this.instanceId);
864
+ } catch (error) {
865
+ this.logger.error('Error handshaking with ForwardMPC enclave websocket. Instance: ' + this.instanceId, error);
866
+ }
867
+ } catch (error) {
868
+ this.logger.error('Error connecting to ForwardMPC enclave websocket. Instance: ' + this.instanceId, {
869
+ error,
870
+ environmentId: this.environmentId
871
+ });
872
+ }
873
+ }
725
874
  getAuthMode() {
726
875
  return this.authMode;
727
876
  }
@@ -793,30 +942,41 @@ class DynamicWalletClient {
793
942
  throw error;
794
943
  }
795
944
  }
796
- async initialize() {
945
+ async initialize(traceContext) {
797
946
  if (this.initializePromise) {
798
947
  return await this.initializePromise;
799
948
  }
800
949
  this.logger.debug('[DynamicWaasWalletClient] Initializing Dynamic Waas Wallet SDK');
801
- this.initializePromise = this._initialize();
950
+ this.initializePromise = this._initialize(traceContext);
802
951
  const result = await this.initializePromise;
803
952
  this.logger.debug('[DynamicWaasWalletClient] Dynamic Waas Wallet SDK initialized');
804
953
  return result;
805
954
  }
806
955
  /**
807
956
  * Client initialization logic
808
- */ async _initialize() {
957
+ */ async _initialize(traceContext) {
809
958
  try {
810
959
  const initializePromises = [
811
960
  this.restoreWallets()
812
961
  ];
962
+ if (this.featureFlags[FEATURE_FLAGS.ENABLE_ROOM_CACHE_FLAG] === true) {
963
+ // Restore rooms first, then create rooms if needed (in series, but not blocking other initialization)
964
+ initializePromises.push(this.restoreRooms().then(()=>// assume two of two threshold signature scheme
965
+ this.createRooms({
966
+ roomType: RoomTypeEnum.Threshold,
967
+ thresholdSignatureScheme: ThresholdSignatureScheme.TWO_OF_TWO,
968
+ roomCount: ROOM_CACHE_COUNT
969
+ })));
970
+ }
813
971
  await Promise.all(initializePromises);
814
972
  return {
815
- error: null
973
+ error: null,
974
+ traceContext
816
975
  };
817
976
  } catch (error) {
818
977
  return {
819
- error
978
+ error,
979
+ traceContext
820
980
  };
821
981
  }
822
982
  }
@@ -895,7 +1055,7 @@ class DynamicWalletClient {
895
1055
  clientKeygenResults
896
1056
  };
897
1057
  }
898
- async keyGen({ chainName, thresholdSignatureScheme, onError, onCeremonyComplete }) {
1058
+ async keyGen({ chainName, thresholdSignatureScheme, onError, onCeremonyComplete, traceContext }) {
899
1059
  const dynamicRequestId = v4();
900
1060
  try {
901
1061
  const clientKeygenInitResults = await this.clientInitializeKeyGen({
@@ -903,11 +1063,12 @@ class DynamicWalletClient {
903
1063
  thresholdSignatureScheme
904
1064
  });
905
1065
  const clientKeygenIds = clientKeygenInitResults.map((result)=>result.keygenId);
906
- this.logger.debug('[DynamicWaasWalletClient] Initialized client key generation', {
1066
+ this.instrument('[DynamicWaasWalletClient] Initialized client key generation', _extends({
907
1067
  clientKeygenIds: clientKeygenIds.join(', '),
908
1068
  chainName,
909
- thresholdSignatureScheme
910
- });
1069
+ thresholdSignatureScheme,
1070
+ key: 'keyGen'
1071
+ }, this.getTraceContext(traceContext)));
911
1072
  const { roomId, serverKeygenIds } = await this.serverInitializeKeyGen({
912
1073
  chainName,
913
1074
  clientKeygenIds,
@@ -915,12 +1076,14 @@ class DynamicWalletClient {
915
1076
  thresholdSignatureScheme,
916
1077
  onCeremonyComplete
917
1078
  });
918
- this.logger.debug('[DynamicWaasWalletClient] Server key generation initialized', {
1079
+ this.instrument('[DynamicWaasWalletClient] Server key generation initialized', _extends({
919
1080
  roomId,
920
1081
  clientKeygenIds: clientKeygenIds.join(', '),
921
1082
  serverKeygenIds: serverKeygenIds.join(', '),
922
- chainName
923
- });
1083
+ chainName,
1084
+ key: 'keyGen',
1085
+ operation: 'serverKeyGen'
1086
+ }, this.getTraceContext(traceContext)));
924
1087
  const { rawPublicKey, clientKeygenResults: clientKeyShares } = await this.clientKeyGen({
925
1088
  chainName,
926
1089
  roomId,
@@ -928,15 +1091,17 @@ class DynamicWalletClient {
928
1091
  clientKeygenInitResults,
929
1092
  thresholdSignatureScheme
930
1093
  });
931
- this.logger.debug('[DynamicWaasWalletClient] Client key generation completed', {
1094
+ this.instrument('[DynamicWaasWalletClient] Client key generation completed', _extends({
932
1095
  roomId,
933
1096
  serverKeygenIds: serverKeygenIds.join(', '),
934
1097
  clientKeygenIds: clientKeygenIds.join(', '),
935
1098
  chainName,
936
1099
  thresholdSignatureScheme,
937
1100
  rawPublicKey,
938
- clientKeySharesCount: clientKeyShares.length
939
- });
1101
+ clientKeySharesCount: clientKeyShares.length,
1102
+ key: 'keyGen',
1103
+ operation: 'clientKeyGen'
1104
+ }, this.getTraceContext(traceContext)));
940
1105
  return {
941
1106
  rawPublicKey,
942
1107
  clientKeyShares
@@ -945,16 +1110,16 @@ class DynamicWalletClient {
945
1110
  logError({
946
1111
  message: 'Error in keyGen',
947
1112
  error: error,
948
- context: {
1113
+ context: _extends({
949
1114
  chainName,
950
1115
  thresholdSignatureScheme,
951
1116
  dynamicRequestId
952
- }
1117
+ }, this.getTraceContext(traceContext))
953
1118
  });
954
1119
  throw error;
955
1120
  }
956
1121
  }
957
- async importRawPrivateKey({ chainName, privateKey, thresholdSignatureScheme, onError, onCeremonyComplete }) {
1122
+ async importRawPrivateKey({ chainName, privateKey, thresholdSignatureScheme, onError, onCeremonyComplete, traceContext }) {
958
1123
  const dynamicRequestId = v4();
959
1124
  try {
960
1125
  const mpcSigner = getMPCSigner({
@@ -966,11 +1131,13 @@ class DynamicWalletClient {
966
1131
  thresholdSignatureScheme
967
1132
  });
968
1133
  const clientKeygenIds = clientKeygenInitResults.map((result)=>result.keygenId);
969
- this.logger.debug('[DynamicWaasWalletClient] Client key generation initialized', {
1134
+ this.instrument('[DynamicWaasWalletClient] Client key generation initialized', _extends({
970
1135
  clientKeygenIds: clientKeygenIds.join(', '),
971
1136
  chainName,
972
- thresholdSignatureScheme
973
- });
1137
+ thresholdSignatureScheme,
1138
+ key: 'importRawPrivateKey',
1139
+ operation: 'clientKeyGen'
1140
+ }, this.getTraceContext(traceContext)));
974
1141
  const { roomId, serverKeygenIds } = await this.apiClient.importPrivateKey({
975
1142
  chainName,
976
1143
  clientKeygenIds,
@@ -979,12 +1146,14 @@ class DynamicWalletClient {
979
1146
  onError,
980
1147
  onCeremonyComplete
981
1148
  });
982
- this.logger.debug('[DynamicWaasWalletClient] Server key generation initialized', {
1149
+ this.instrument('[DynamicWaasWalletClient] Server key generation initialized', _extends({
983
1150
  roomId,
984
1151
  clientKeygenIds: clientKeygenIds.join(', '),
985
1152
  serverKeygenIds: serverKeygenIds.join(', '),
986
- chainName
987
- });
1153
+ chainName,
1154
+ key: 'importRawPrivateKey',
1155
+ operation: 'serverImportPrivateKey'
1156
+ }, this.getTraceContext(traceContext)));
988
1157
  const { threshold } = getTSSConfig(thresholdSignatureScheme);
989
1158
  const clientKeygenResults = await Promise.all(clientKeygenInitResults.map(async (currentInit, index)=>{
990
1159
  const otherClientKeygenIds = clientKeygenInitResults.filter((init)=>init.keygenId !== currentInit.keygenId).map((init)=>init.keygenId);
@@ -1009,14 +1178,16 @@ class DynamicWalletClient {
1009
1178
  keyShare: clientKeygenResult,
1010
1179
  derivationPath: undefined
1011
1180
  });
1012
- this.logger.debug('[DynamicWaasWalletClient] Completed import of raw private key', {
1181
+ this.instrument('[DynamicWaasWalletClient] Completed import of raw private key', _extends({
1013
1182
  rawPublicKey,
1014
1183
  chainName,
1015
1184
  thresholdSignatureScheme,
1016
1185
  roomId,
1017
1186
  serverKeygenIds: serverKeygenIds.join(', '),
1018
- clientKeygenIds: clientKeygenIds.join(', ')
1019
- });
1187
+ clientKeygenIds: clientKeygenIds.join(', '),
1188
+ key: 'importRawPrivateKey',
1189
+ operation: 'importRawPrivateKey'
1190
+ }, this.getTraceContext(traceContext)));
1020
1191
  return {
1021
1192
  rawPublicKey,
1022
1193
  clientKeyShares: clientKeygenResults
@@ -1025,16 +1196,16 @@ class DynamicWalletClient {
1025
1196
  logError({
1026
1197
  message: 'Error in importRawPrivateKey',
1027
1198
  error: error,
1028
- context: {
1199
+ context: _extends({
1029
1200
  chainName,
1030
1201
  thresholdSignatureScheme,
1031
1202
  dynamicRequestId
1032
- }
1203
+ }, this.getTraceContext(traceContext))
1033
1204
  });
1034
1205
  throw error;
1035
1206
  }
1036
1207
  }
1037
- async serverSign({ walletId, message, isFormatted, mfaToken, context, onError, dynamicRequestId }) {
1208
+ async serverSign({ walletId, message, isFormatted, mfaToken, roomId, context, onError, dynamicRequestId, traceContext }) {
1038
1209
  // Create the room and sign the message
1039
1210
  if (typeof message !== 'string') {
1040
1211
  message = `0x${Buffer.from(message).toString('hex')}`;
@@ -1045,44 +1216,101 @@ class DynamicWalletClient {
1045
1216
  isFormatted,
1046
1217
  dynamicRequestId,
1047
1218
  mfaToken,
1219
+ roomId,
1048
1220
  context: context ? JSON.parse(JSON.stringify(context, (_key, value)=>typeof value === 'bigint' ? value.toString() : value)) : undefined,
1049
- onError
1221
+ onError,
1222
+ forwardMPCClientEnabled: this.forwardMPCEnabled,
1223
+ traceContext
1050
1224
  });
1051
1225
  return data;
1052
1226
  }
1053
- async clientSign({ chainName, message, roomId, keyShare, derivationPath, isFormatted, dynamicRequestId }) {
1227
+ async forwardMPCClientSign({ chainName, message, roomId, keyShare, derivationPath, formattedMessage, dynamicRequestId, isFormatted, traceContext }) {
1228
+ try {
1229
+ if (!this.apiClient.forwardMPCClient.connected) {
1230
+ await this.initializeForwardMPCClient();
1231
+ }
1232
+ const messageForForwardMPC = serializeMessageForForwardMPC({
1233
+ message,
1234
+ isFormatted,
1235
+ chainName
1236
+ });
1237
+ this.logger.info('Forward MPC enabled, signing message with forward MPC', this.getTraceContext(traceContext));
1238
+ const signature = await this.apiClient.forwardMPCClient.signMessage({
1239
+ keyshare: keyShare,
1240
+ message: chainName === 'SVM' ? formattedMessage : messageForForwardMPC,
1241
+ relayDomain: this.baseMPCRelayApiUrl || '',
1242
+ signingAlgo: chainName === 'EVM' ? 'ECDSA' : 'ED25519',
1243
+ hashAlgo: chainName === 'EVM' && !isFormatted ? 'keccak256' : undefined,
1244
+ derivationPath: derivationPath,
1245
+ roomUuid: roomId
1246
+ });
1247
+ const signatureBytes = signature.data.signature;
1248
+ if (!(signatureBytes instanceof Uint8Array)) {
1249
+ throw new TypeError(`Invalid signature format: expected Uint8Array, got ${typeof signatureBytes}`);
1250
+ }
1251
+ // Convert to EcdsaSignature
1252
+ if (chainName === 'EVM') {
1253
+ const ecdsaSignature = EcdsaSignature.fromBuffer(signatureBytes);
1254
+ return ecdsaSignature;
1255
+ } else {
1256
+ return signatureBytes;
1257
+ }
1258
+ } catch (error) {
1259
+ this.logger.error('Error signing message with forward MPC client', {
1260
+ error,
1261
+ environmentId: this.environmentId,
1262
+ userId: this.userId,
1263
+ dynamicRequestId
1264
+ });
1265
+ throw error;
1266
+ }
1267
+ }
1268
+ async clientSign({ chainName, message, roomId, keyShare, derivationPath, isFormatted, dynamicRequestId, traceContext }) {
1054
1269
  try {
1055
1270
  const mpcSigner = getMPCSigner({
1056
1271
  chainName,
1057
1272
  baseRelayUrl: this.baseMPCRelayApiUrl
1058
1273
  });
1059
1274
  const formattedMessage = isFormatted ? new MessageHash(message) : formatMessage(chainName, message);
1060
- this.logger.debug('[DynamicWaasWalletClient] Starting client sign', {
1275
+ this.logger.debug('[DynamicWaasWalletClient] Starting client sign', _extends({
1061
1276
  chainName,
1062
1277
  message,
1063
1278
  roomId,
1064
1279
  derivationPath,
1065
1280
  isFormatted
1066
- });
1281
+ }, this.getTraceContext(traceContext)));
1282
+ if (this.forwardMPCEnabled) {
1283
+ return this.forwardMPCClientSign({
1284
+ chainName,
1285
+ message,
1286
+ roomId,
1287
+ keyShare,
1288
+ derivationPath,
1289
+ formattedMessage,
1290
+ dynamicRequestId,
1291
+ isFormatted,
1292
+ traceContext
1293
+ });
1294
+ }
1067
1295
  const signature = await mpcSigner.sign(roomId, keyShare, formattedMessage, derivationPath);
1068
1296
  return signature;
1069
1297
  } catch (error) {
1070
1298
  logError({
1071
1299
  message: 'Error in clientSign',
1072
1300
  error: error,
1073
- context: {
1301
+ context: _extends({
1074
1302
  chainName,
1075
1303
  roomId,
1076
1304
  derivationPath,
1077
1305
  isFormatted,
1078
1306
  dynamicRequestId
1079
- }
1307
+ }, this.getTraceContext(traceContext))
1080
1308
  });
1081
1309
  throw error;
1082
1310
  }
1083
1311
  }
1084
1312
  //todo: need to modify with imported flag
1085
- async sign({ accountAddress, message, chainName, password = undefined, isFormatted = false, signedSessionId, mfaToken, context, onError }) {
1313
+ async sign({ accountAddress, message, chainName, password = undefined, isFormatted = false, signedSessionId, mfaToken, context, onError, traceContext }) {
1086
1314
  const dynamicRequestId = v4();
1087
1315
  try {
1088
1316
  await this.verifyPassword({
@@ -1097,23 +1325,44 @@ class DynamicWalletClient {
1097
1325
  walletOperation: WalletOperation.SIGN_MESSAGE,
1098
1326
  signedSessionId
1099
1327
  });
1100
- // Perform the server sign
1101
- const data = await this.serverSign({
1328
+ let roomId;
1329
+ if (this.featureFlags[FEATURE_FLAGS.ENABLE_ROOM_CACHE_FLAG] === true) {
1330
+ const room = await this.getRoom(RoomTypeEnum.Threshold, wallet.thresholdSignatureScheme);
1331
+ roomId = room == null ? void 0 : room.roomId;
1332
+ if (roomId) {
1333
+ this.instrument('[DynamicWaasWalletClient] Using cached room', _extends({
1334
+ key: 'sign',
1335
+ operation: 'cachedRoomFound',
1336
+ accountAddress,
1337
+ chainName,
1338
+ walletId: wallet.walletId,
1339
+ roomId
1340
+ }, this.getTraceContext(traceContext)));
1341
+ }
1342
+ }
1343
+ // Perform the server sign - always call, but only await if roomId is not provided
1344
+ const serverSignPromise = this.serverSign({
1102
1345
  walletId: wallet.walletId,
1103
1346
  message,
1104
1347
  isFormatted,
1105
1348
  mfaToken,
1349
+ roomId,
1106
1350
  context,
1107
1351
  onError,
1108
- dynamicRequestId
1352
+ dynamicRequestId,
1353
+ traceContext
1109
1354
  });
1110
- this.logger.debug('[DynamicWaasWalletClient] Server sign completed', {
1355
+ // Only await if roomId is not provided
1356
+ roomId = roomId != null ? roomId : (await serverSignPromise).roomId;
1357
+ this.instrument('[DynamicWaasWalletClient] Server sign completed', _extends({
1111
1358
  message,
1112
1359
  accountAddress,
1113
1360
  walletId: wallet.walletId,
1114
- roomId: data.roomId,
1115
- dynamicRequestId
1116
- });
1361
+ roomId,
1362
+ dynamicRequestId,
1363
+ key: 'sign',
1364
+ operation: 'serverSign'
1365
+ }, this.getTraceContext(traceContext)));
1117
1366
  const derivationPath = wallet.derivationPath && wallet.derivationPath != '' ? new Uint32Array(Object.values(JSON.parse(wallet.derivationPath))) : undefined;
1118
1367
  // Perform the client sign and return the signature
1119
1368
  const clientKeyShares = await this.getClientKeySharesFromLocalStorage({
@@ -1122,35 +1371,39 @@ class DynamicWalletClient {
1122
1371
  const signature = await this.clientSign({
1123
1372
  chainName,
1124
1373
  message,
1125
- roomId: data.roomId,
1374
+ roomId,
1126
1375
  keyShare: clientKeyShares[0],
1127
1376
  derivationPath,
1128
1377
  isFormatted,
1129
- dynamicRequestId
1378
+ dynamicRequestId,
1379
+ traceContext
1130
1380
  });
1131
- this.logger.debug('[DynamicWaasWalletClient] Client sign completed', {
1381
+ this.instrument('[DynamicWaasWalletClient] Client sign completed', _extends({
1382
+ accountAddress,
1132
1383
  chainName,
1133
1384
  message,
1134
- roomId: data.roomId,
1385
+ roomId,
1135
1386
  derivationPath,
1136
- isFormatted
1137
- });
1387
+ isFormatted,
1388
+ key: 'sign',
1389
+ operation: 'clientSign'
1390
+ }, this.getTraceContext(traceContext)));
1138
1391
  return signature;
1139
1392
  } catch (error) {
1140
1393
  logError({
1141
1394
  message: 'Error in sign',
1142
1395
  error: error,
1143
- context: {
1396
+ context: _extends({
1144
1397
  accountAddress,
1145
1398
  chainName,
1146
1399
  isFormatted: isFormatted ? 'true' : 'false',
1147
1400
  dynamicRequestId
1148
- }
1401
+ }, this.getTraceContext(traceContext))
1149
1402
  });
1150
1403
  throw error;
1151
1404
  }
1152
1405
  }
1153
- async refreshWalletAccountShares({ accountAddress, chainName, password = undefined, signedSessionId, mfaToken }) {
1406
+ async refreshWalletAccountShares({ accountAddress, chainName, password = undefined, signedSessionId, mfaToken, traceContext }) {
1154
1407
  const dynamicRequestId = v4();
1155
1408
  try {
1156
1409
  await this.verifyPassword({
@@ -1194,11 +1447,11 @@ class DynamicWalletClient {
1194
1447
  logError({
1195
1448
  message: 'Error in refreshWalletAccountShares',
1196
1449
  error: error,
1197
- context: {
1450
+ context: _extends({
1198
1451
  accountAddress,
1199
1452
  chainName,
1200
1453
  dynamicRequestId
1201
- }
1454
+ }, this.getTraceContext(traceContext))
1202
1455
  });
1203
1456
  throw error;
1204
1457
  }
@@ -1254,7 +1507,7 @@ class DynamicWalletClient {
1254
1507
  existingClientKeyShares
1255
1508
  };
1256
1509
  }
1257
- async reshare({ chainName, accountAddress, oldThresholdSignatureScheme, newThresholdSignatureScheme, password = undefined, signedSessionId, backupToGoogleDrive = false, delegateToProjectEnvironment = false, mfaToken }) {
1510
+ async reshare({ chainName, accountAddress, oldThresholdSignatureScheme, newThresholdSignatureScheme, password = undefined, signedSessionId, backupToGoogleDrive = false, delegateToProjectEnvironment = false, mfaToken, revokeDelegation = false }) {
1258
1511
  const dynamicRequestId = v4();
1259
1512
  try {
1260
1513
  await this.verifyPassword({
@@ -1293,7 +1546,8 @@ class DynamicWalletClient {
1293
1546
  newThresholdSignatureScheme,
1294
1547
  dynamicRequestId,
1295
1548
  delegateToProjectEnvironment,
1296
- mfaToken
1549
+ mfaToken,
1550
+ revokeDelegation
1297
1551
  });
1298
1552
  const { roomId, serverKeygenIds, newServerKeygenIds = [] } = data;
1299
1553
  // Get the MPC config for the threshold signature scheme
@@ -1308,35 +1562,68 @@ class DynamicWalletClient {
1308
1562
  chainName,
1309
1563
  baseRelayUrl: this.baseMPCRelayApiUrl
1310
1564
  });
1311
- const reshareResults = await Promise.all([
1312
- ...newClientInitKeygenResults.map((keygenResult)=>mpcSigner.reshareNewParty(roomId, oldMpcConfig.threshold, newMpcConfig.threshold, keygenResult, allPartyKeygenIds)),
1313
- ...existingClientKeyShares.map((keyShare)=>mpcSigner.reshareRemainingParty(roomId, newMpcConfig.threshold, keyShare, allPartyKeygenIds))
1565
+ const existingResharePromises = existingClientKeyShares.map((keyShare)=>mpcSigner.reshareRemainingParty(roomId, newMpcConfig.threshold, keyShare, allPartyKeygenIds));
1566
+ const newResharePromises = newClientInitKeygenResults.map((keygenResult)=>mpcSigner.reshareNewParty(roomId, oldMpcConfig.threshold, newMpcConfig.threshold, keygenResult, allPartyKeygenIds));
1567
+ // Run both share parties in parallel by group
1568
+ const [existingReshareResults, newReshareResults] = await Promise.all([
1569
+ Promise.all(existingResharePromises),
1570
+ Promise.all(newResharePromises)
1314
1571
  ]);
1572
+ const clientKeysharesToLocalStorage = delegateToProjectEnvironment ? [
1573
+ ...existingReshareResults
1574
+ ] : [
1575
+ ...existingReshareResults,
1576
+ ...newReshareResults
1577
+ ];
1315
1578
  this.walletMap[accountAddress] = _extends({}, this.walletMap[accountAddress], {
1316
1579
  thresholdSignatureScheme: newThresholdSignatureScheme
1317
1580
  });
1581
+ // store client key shares to localStorage
1318
1582
  await this.setClientKeySharesToLocalStorage({
1319
1583
  accountAddress,
1320
- clientKeyShares: reshareResults,
1584
+ clientKeyShares: clientKeysharesToLocalStorage,
1321
1585
  overwriteOrMerge: 'overwrite'
1322
1586
  });
1323
- if (delegateToProjectEnvironment) {
1324
- await this.storeEncryptedBackupByDelegatedWallet({
1325
- accountAddress,
1326
- password,
1327
- signedSessionId
1587
+ const allClientShares = [
1588
+ ...existingReshareResults,
1589
+ ...newReshareResults
1590
+ ];
1591
+ let distribution;
1592
+ if (delegateToProjectEnvironment && backupToGoogleDrive) {
1593
+ // Delegation + Google Drive: Client's existing share backs up to both Dynamic and Google Drive.
1594
+ // The new share goes to the webhook for delegation.
1595
+ distribution = createDelegationWithGoogleDriveDistribution({
1596
+ existingShares: existingReshareResults,
1597
+ delegatedShare: newReshareResults[0]
1598
+ });
1599
+ } else if (delegateToProjectEnvironment) {
1600
+ // Delegation only: Client's existing share backs up to Dynamic.
1601
+ // The new share goes to the webhook for delegation. No Google Drive backup.
1602
+ distribution = createDelegationOnlyDistribution({
1603
+ existingShares: existingReshareResults,
1604
+ delegatedShare: newReshareResults[0]
1605
+ });
1606
+ } else if (backupToGoogleDrive) {
1607
+ // Google Drive only: Split shares between Dynamic (N-1) and Google Drive (1).
1608
+ // The last share (new share) goes to Google Drive.
1609
+ distribution = createGoogleDriveOnlyDistribution({
1610
+ allShares: allClientShares
1328
1611
  });
1329
1612
  } else {
1330
- await this.storeEncryptedBackupByWallet({
1331
- accountAddress,
1332
- password,
1333
- signedSessionId,
1334
- backupToGoogleDrive
1613
+ // No delegation, no Google Drive: All shares go to Dynamic backend only.
1614
+ distribution = createDynamicOnlyDistribution({
1615
+ allShares: allClientShares
1335
1616
  });
1336
1617
  }
1618
+ await this.backupSharesWithDistribution({
1619
+ accountAddress,
1620
+ password,
1621
+ signedSessionId,
1622
+ distribution
1623
+ });
1337
1624
  } catch (error) {
1338
1625
  logError({
1339
- message: 'Error in reshare',
1626
+ message: 'Error in reshare, resetting wallet to previous state',
1340
1627
  error: error,
1341
1628
  context: {
1342
1629
  accountAddress,
@@ -1347,52 +1634,25 @@ class DynamicWalletClient {
1347
1634
  dynamicRequestId
1348
1635
  }
1349
1636
  });
1350
- throw error;
1351
- }
1352
- }
1353
- async sendKeySharesToDelegatedAccess({ accountAddress, chainName, delegatedKeyShares, environmentId, userId, walletId }) {
1354
- try {
1355
- if (!this.delegatedAccessEndpoint) {
1356
- throw new Error('Cannot send key shares to delegated access because delegated access endpoint is not set');
1357
- }
1358
- const delegatedAccessEndpoint = this.delegatedAccessEndpoint;
1359
- const response = await fetch(delegatedAccessEndpoint, {
1360
- method: 'POST',
1361
- body: JSON.stringify({
1362
- chainName,
1363
- accountAddress,
1364
- delegatedKeyShares,
1365
- environmentId,
1366
- userId,
1367
- walletId
1368
- })
1637
+ // reset user wallet when reshare fails, this would allow the client to recover wallets from an active state
1638
+ this.walletMap[accountAddress] = _extends({}, this.walletMap[accountAddress], {
1639
+ thresholdSignatureScheme: oldThresholdSignatureScheme
1369
1640
  });
1370
- const data = await response.json();
1371
- return data;
1372
- } catch (error) {
1373
- logError({
1374
- message: 'Error in sendKeySharesToDelegatedAccess',
1375
- error: error,
1376
- context: {
1377
- accountAddress,
1378
- chainName
1379
- }
1641
+ await this.setClientKeySharesToLocalStorage({
1642
+ accountAddress,
1643
+ clientKeyShares: [],
1644
+ overwriteOrMerge: 'overwrite'
1380
1645
  });
1381
1646
  throw error;
1382
1647
  }
1383
1648
  }
1384
- async delegateKeyShares({ accountAddress, password = undefined, signedSessionId, mfaToken }) {
1649
+ async performDelegationOperation({ accountAddress, password, signedSessionId, mfaToken, newThresholdSignatureScheme, revokeDelegation = false, operationName }) {
1385
1650
  try {
1651
+ var _this_walletMap_accountAddress;
1386
1652
  const delegateToProjectEnvironment = this.featureFlags && this.featureFlags[FEATURE_FLAGS.ENABLE_DELEGATED_KEY_SHARES_FLAG] === true;
1387
1653
  if (!delegateToProjectEnvironment) {
1388
1654
  throw new Error('Delegation is not allowed for this project environment');
1389
1655
  }
1390
- const environmentSettings = await this.apiClient.getEnvironmentSettings();
1391
- const delegatedAccessEndpoint = environmentSettings.sdk.waas.delegatedAccessEndpoint;
1392
- if (!delegatedAccessEndpoint) {
1393
- throw new Error('Cannot delegate key shares because verified access endpoint is not set in the environment settings');
1394
- }
1395
- this.delegatedAccessEndpoint = delegatedAccessEndpoint;
1396
1656
  const wallet = await this.getWallet({
1397
1657
  accountAddress,
1398
1658
  walletOperation: WalletOperation.REACH_ALL_PARTIES,
@@ -1403,28 +1663,21 @@ class DynamicWalletClient {
1403
1663
  throw new Error('Delegation is not allowed for SUI');
1404
1664
  }
1405
1665
  const currentThresholdSignatureScheme = this.walletMap[accountAddress].thresholdSignatureScheme;
1406
- if (currentThresholdSignatureScheme === ThresholdSignatureScheme.TWO_OF_TWO) {
1407
- // Reshare to 2-of-3, which will automatically handle the backup distribution
1408
- await this.reshare({
1409
- chainName: this.walletMap[accountAddress].chainName,
1410
- accountAddress,
1411
- oldThresholdSignatureScheme: currentThresholdSignatureScheme,
1412
- newThresholdSignatureScheme: ThresholdSignatureScheme.TWO_OF_THREE,
1413
- password,
1414
- signedSessionId,
1415
- backupToGoogleDrive: false,
1416
- delegateToProjectEnvironment: true,
1417
- mfaToken
1418
- });
1419
- const backupInfo = this.walletMap[accountAddress].clientKeySharesBackupInfo;
1420
- const delegatedKeyShares = backupInfo.backups[BackupLocation.EXTERNAL] || [];
1421
- return delegatedKeyShares;
1422
- } else {
1423
- throw new Error('Delegation is not allowed for this threshold signature scheme');
1424
- }
1666
+ await this.reshare({
1667
+ chainName: this.walletMap[accountAddress].chainName,
1668
+ accountAddress,
1669
+ oldThresholdSignatureScheme: currentThresholdSignatureScheme,
1670
+ newThresholdSignatureScheme,
1671
+ password,
1672
+ signedSessionId,
1673
+ backupToGoogleDrive: hasGoogleDriveBackup((_this_walletMap_accountAddress = this.walletMap[accountAddress]) == null ? void 0 : _this_walletMap_accountAddress.clientKeySharesBackupInfo),
1674
+ delegateToProjectEnvironment: true,
1675
+ mfaToken,
1676
+ revokeDelegation
1677
+ });
1425
1678
  } catch (error) {
1426
1679
  logError({
1427
- message: 'Error in delegateKeyShares',
1680
+ message: `Error in ${operationName}`,
1428
1681
  error: error,
1429
1682
  context: {
1430
1683
  accountAddress
@@ -1433,7 +1686,31 @@ class DynamicWalletClient {
1433
1686
  throw error;
1434
1687
  }
1435
1688
  }
1436
- async exportKey({ accountAddress, chainName, password = undefined, signedSessionId, mfaToken }) {
1689
+ async delegateKeyShares({ accountAddress, password = undefined, signedSessionId, mfaToken }) {
1690
+ await this.performDelegationOperation({
1691
+ accountAddress,
1692
+ password,
1693
+ signedSessionId,
1694
+ mfaToken,
1695
+ newThresholdSignatureScheme: ThresholdSignatureScheme.TWO_OF_THREE,
1696
+ operationName: 'delegateKeyShares'
1697
+ });
1698
+ const backupInfo = this.walletMap[accountAddress].clientKeySharesBackupInfo;
1699
+ const delegatedKeyShares = backupInfo.backups[BackupLocation.DELEGATED] || [];
1700
+ return delegatedKeyShares;
1701
+ }
1702
+ async revokeDelegation({ accountAddress, password = undefined, signedSessionId, mfaToken }) {
1703
+ await this.performDelegationOperation({
1704
+ accountAddress,
1705
+ password,
1706
+ signedSessionId,
1707
+ mfaToken,
1708
+ newThresholdSignatureScheme: ThresholdSignatureScheme.TWO_OF_TWO,
1709
+ revokeDelegation: true,
1710
+ operationName: 'revokeDelegation'
1711
+ });
1712
+ }
1713
+ async exportKey({ accountAddress, chainName, password = undefined, signedSessionId, mfaToken, traceContext }) {
1437
1714
  const dynamicRequestId = v4();
1438
1715
  try {
1439
1716
  const wallet = await this.getWallet({
@@ -1459,24 +1736,24 @@ class DynamicWalletClient {
1459
1736
  dynamicRequestId,
1460
1737
  mfaToken
1461
1738
  });
1462
- this.logger.debug('[DynamicWaasWalletClient] Starting export of private key', {
1739
+ this.logger.debug('[DynamicWaasWalletClient] Starting export of private key', _extends({
1463
1740
  accountAddress,
1464
1741
  chainName,
1465
1742
  walletId: wallet.walletId,
1466
1743
  exportId,
1467
1744
  roomId: data.roomId
1468
- });
1745
+ }, this.getTraceContext(traceContext)));
1469
1746
  const keyExportRaw = await mpcSigner.exportFullPrivateKey(data.roomId, clientKeyShares[0], exportId);
1470
1747
  if (!keyExportRaw) {
1471
1748
  throw new Error('Error exporting private key');
1472
1749
  }
1473
- this.logger.debug('[DynamicWaasWalletClient] Completed export of private key', {
1750
+ this.logger.debug('[DynamicWaasWalletClient] Completed export of private key', _extends({
1474
1751
  accountAddress,
1475
1752
  chainName,
1476
1753
  walletId: wallet.walletId,
1477
1754
  exportId,
1478
1755
  roomId: data.roomId
1479
- });
1756
+ }, this.getTraceContext(traceContext)));
1480
1757
  const derivationPath = wallet.derivationPath && wallet.derivationPath != '' ? new Uint32Array(Object.values(JSON.parse(wallet.derivationPath))) : undefined;
1481
1758
  let derivedPrivateKey;
1482
1759
  if (mpcSigner instanceof Ecdsa) {
@@ -1493,11 +1770,11 @@ class DynamicWalletClient {
1493
1770
  logError({
1494
1771
  message: 'Error in exportKey',
1495
1772
  error: error,
1496
- context: {
1773
+ context: _extends({
1497
1774
  accountAddress,
1498
1775
  chainName,
1499
1776
  dynamicRequestId
1500
- }
1777
+ }, this.getTraceContext(traceContext))
1501
1778
  });
1502
1779
  throw error;
1503
1780
  }
@@ -1597,43 +1874,14 @@ class DynamicWalletClient {
1597
1874
  });
1598
1875
  await ((_this_storage = this.storage) == null ? void 0 : _this_storage.setItem(accountAddress, stringifiedClientKeyShares));
1599
1876
  }
1600
- /**
1601
- * Central backup orchestrator that encrypts and stores wallet key shares.
1602
- *
1603
- * This method serves as the main backup coordinator, handling the distribution of encrypted
1604
- * key shares between Dynamic's backend and Google Drive based on the wallet's threshold scheme.
1605
- * It is used by multiple operations including reshare, refresh, and manual backup requests.
1606
- *
1607
- * **Backup Distribution Strategy:**
1608
- * - **Single share wallets**: All shares stored on Dynamic's backend only
1609
- * - **Multi-share wallets (2+)**: When backing up to Google Drive, N-1 shares on Dynamic's backend, 1 share on Google Drive
1610
- * - **Multi-share wallets (2+)**: When not backing up to Google Drive, all shares on Dynamic's backend
1611
- *
1612
- * **Process Flow:**
1613
- * 1. Encrypts all client key shares with the provided password (or environment ID if no password)
1614
- * 2. For multi-share wallets (2+): conditionally distributes N-1 to backend, 1 to Google Drive
1615
- * 3. For other configurations: stores all shares on Dynamic's backend
1616
- * 4. Updates backup metadata and synchronizes wallet state
1617
- * 5. Persists the updated wallet map to local storage
1618
- *
1619
- * @param params - The backup operation parameters
1620
- * @param params.accountAddress - The account address of the wallet to backup
1621
- * @param params.clientKeyShares - Optional specific key shares to backup (uses localStorage if not provided)
1622
- * @param params.password - Optional password for encryption (uses environment ID if not provided)
1623
- * @param params.signedSessionId - Optional signed session ID for authentication
1624
- * @param params.backupToGoogleDrive - Whether to backup to Google Drive (defaults to false)
1625
- * @returns Promise with backup metadata including share locations and IDs
1626
- */ async storeEncryptedBackupByWallet({ accountAddress, clientKeyShares = undefined, password = undefined, signedSessionId, backupToGoogleDrive = false }) {
1877
+ async backupSharesWithDistribution({ accountAddress, password, signedSessionId, distribution, preserveDelegatedLocation = false }) {
1627
1878
  const dynamicRequestId = v4();
1628
1879
  try {
1629
- var _this_walletMap_accountAddress, _this_walletMap_accountAddress_clientKeySharesBackupInfo_backups_BackupLocation_GOOGLE_DRIVE, _this_walletMap_accountAddress_clientKeySharesBackupInfo_backups, _this_walletMap_accountAddress_clientKeySharesBackupInfo, _this_walletMap_accountAddress1;
1630
- const keySharesToBackup = clientKeyShares != null ? clientKeyShares : await this.getClientKeySharesFromLocalStorage({
1631
- accountAddress
1632
- });
1880
+ var _this_walletMap_accountAddress;
1633
1881
  if (!((_this_walletMap_accountAddress = this.walletMap[accountAddress]) == null ? void 0 : _this_walletMap_accountAddress.walletId)) {
1634
1882
  const error = new Error(`WalletId not found for accountAddress ${accountAddress}`);
1635
1883
  logError({
1636
- message: 'Error in storeEncryptedBackupByWallet, wallet or walletId not found from the wallet map',
1884
+ message: 'Error in backupSharesWithDistribution, wallet or walletId not found from the wallet map',
1637
1885
  error,
1638
1886
  context: {
1639
1887
  accountAddress,
@@ -1642,55 +1890,73 @@ class DynamicWalletClient {
1642
1890
  });
1643
1891
  throw error;
1644
1892
  }
1645
- // TODO(zfaizal2): throw error if signedSessionId is not provided after service deploy
1646
- let dynamicClientKeyShares = [];
1647
- let googleDriveKeyShares = [];
1648
- const encryptedKeyShares = await Promise.all(keySharesToBackup.map((keyShare)=>this.encryptKeyShare({
1649
- keyShare,
1650
- password
1651
- })));
1652
- const hasExistingGoogleDriveBackup = ((_this_walletMap_accountAddress1 = this.walletMap[accountAddress]) == null ? void 0 : (_this_walletMap_accountAddress_clientKeySharesBackupInfo = _this_walletMap_accountAddress1.clientKeySharesBackupInfo) == null ? void 0 : (_this_walletMap_accountAddress_clientKeySharesBackupInfo_backups = _this_walletMap_accountAddress_clientKeySharesBackupInfo.backups) == null ? void 0 : (_this_walletMap_accountAddress_clientKeySharesBackupInfo_backups_BackupLocation_GOOGLE_DRIVE = _this_walletMap_accountAddress_clientKeySharesBackupInfo_backups[BackupLocation.GOOGLE_DRIVE]) == null ? void 0 : _this_walletMap_accountAddress_clientKeySharesBackupInfo_backups_BackupLocation_GOOGLE_DRIVE.length) > 0;
1653
- // Backup to Google Drive if:
1654
- // 1. Explicitly requested via flag, OR
1655
- // 2. User already has Google Drive backups
1656
- const shouldBackupToGoogleDrive = backupToGoogleDrive || hasExistingGoogleDriveBackup;
1657
- if (shouldBackupToGoogleDrive && keySharesToBackup.length >= 2) {
1658
- // For 2 shares: 1 to backend, 1 to Google Drive
1659
- // For 3+ shares: N-1 to backend, 1 to Google Drive
1660
- const googleDriveShareCount = 1;
1661
- dynamicClientKeyShares = encryptedKeyShares.slice(0, -googleDriveShareCount);
1662
- googleDriveKeyShares = encryptedKeyShares.slice(-googleDriveShareCount);
1663
- } else {
1664
- dynamicClientKeyShares = encryptedKeyShares;
1665
- }
1666
- const data = await this.apiClient.storeEncryptedBackupByWallet({
1667
- walletId: this.walletMap[accountAddress].walletId,
1668
- encryptedKeyShares: dynamicClientKeyShares,
1669
- passwordEncrypted: Boolean(password) && password !== this.environmentId,
1670
- encryptionVersion: ENCRYPTION_VERSION_CURRENT,
1671
- signedSessionId,
1672
- authMode: this.authMode,
1673
- requiresSignedSessionId: this.requiresSignedSessionId(),
1674
- dynamicRequestId
1675
- });
1676
- if (data.keyShareIds.length === 0) {
1677
- throw new Error('No key shares were backed up');
1678
- }
1679
- const locations = [
1680
- {
1893
+ const locations = [];
1894
+ if (distribution.dynamicBackendShares.length > 0) {
1895
+ const encryptedDynamicShares = await Promise.all(distribution.dynamicBackendShares.map((keyShare)=>this.encryptKeyShare({
1896
+ keyShare,
1897
+ password
1898
+ })));
1899
+ const data = await this.apiClient.storeEncryptedBackupByWallet({
1900
+ walletId: this.walletMap[accountAddress].walletId,
1901
+ encryptedKeyShares: encryptedDynamicShares,
1902
+ passwordEncrypted: Boolean(password) && password !== this.environmentId,
1903
+ encryptionVersion: ENCRYPTION_VERSION_CURRENT,
1904
+ signedSessionId,
1905
+ authMode: this.authMode,
1906
+ requiresSignedSessionId: this.requiresSignedSessionId(),
1907
+ dynamicRequestId
1908
+ });
1909
+ if (data.keyShareIds.length === 0) {
1910
+ throw new Error('No key shares were backed up to Dynamic backend');
1911
+ }
1912
+ locations.push({
1681
1913
  location: BackupLocation.DYNAMIC,
1682
1914
  externalKeyShareId: data.keyShareIds[0]
1683
- }
1684
- ];
1685
- if (googleDriveKeyShares.length > 0) {
1915
+ });
1916
+ }
1917
+ if (distribution.googleDriveShares.length > 0) {
1918
+ const encryptedGoogleDriveShares = await Promise.all(distribution.googleDriveShares.map((keyShare)=>this.encryptKeyShare({
1919
+ keyShare,
1920
+ password
1921
+ })));
1686
1922
  await this.uploadKeySharesToGoogleDrive({
1687
1923
  accountAddress,
1688
- encryptedKeyShares: googleDriveKeyShares
1924
+ encryptedKeyShares: encryptedGoogleDriveShares
1689
1925
  });
1690
1926
  locations.push({
1691
1927
  location: BackupLocation.GOOGLE_DRIVE
1692
1928
  });
1693
1929
  }
1930
+ if (distribution.delegatedShare) {
1931
+ var _publicKey_key, _publicKey_key1, _publicKey_key2;
1932
+ const publicKey = await this.apiClient.getDelegatedEncryptionKey({
1933
+ environmentId: this.environmentId
1934
+ });
1935
+ if (!(publicKey == null ? void 0 : (_publicKey_key = publicKey.key) == null ? void 0 : _publicKey_key.publicKeyPemB64)) {
1936
+ throw new Error('Public key not found');
1937
+ }
1938
+ var _publicKey_key_keyId;
1939
+ const encryptedDelegatedKeyShareEnvelope = await encryptDelegatedKeyShare(JSON.stringify(distribution.delegatedShare), publicKey == null ? void 0 : (_publicKey_key1 = publicKey.key) == null ? void 0 : _publicKey_key1.publicKeyPemB64, (_publicKey_key_keyId = publicKey == null ? void 0 : (_publicKey_key2 = publicKey.key) == null ? void 0 : _publicKey_key2.keyId) != null ? _publicKey_key_keyId : publicKey == null ? void 0 : publicKey.keyId);
1940
+ const { status } = await this.apiClient.publishDelegatedKeyShare({
1941
+ walletId: this.walletMap[accountAddress].walletId,
1942
+ encryptedKeyShare: encryptedDelegatedKeyShareEnvelope,
1943
+ signedSessionId,
1944
+ requiresSignedSessionId: this.requiresSignedSessionId(),
1945
+ dynamicRequestId
1946
+ });
1947
+ if (status !== 200) {
1948
+ throw new Error('Failed to publish delegated key share');
1949
+ }
1950
+ locations.push({
1951
+ location: BackupLocation.DELEGATED
1952
+ });
1953
+ }
1954
+ // Preserve existing delegated location without re-publishing
1955
+ if (preserveDelegatedLocation && !distribution.delegatedShare) {
1956
+ locations.push({
1957
+ location: BackupLocation.DELEGATED
1958
+ });
1959
+ }
1694
1960
  const backupData = await this.apiClient.markKeySharesAsBackedUp({
1695
1961
  walletId: this.walletMap[accountAddress].walletId,
1696
1962
  locations,
@@ -1712,10 +1978,10 @@ class DynamicWalletClient {
1712
1978
  clientKeySharesBackupInfo: updatedBackupInfo
1713
1979
  });
1714
1980
  await this.storage.setItem(this.storageKey, JSON.stringify(this.walletMap));
1715
- return data;
1981
+ return backupData;
1716
1982
  } catch (error) {
1717
1983
  logError({
1718
- message: 'Error in storeEncryptedBackupByWallet',
1984
+ message: 'Error in backupSharesWithDistribution',
1719
1985
  error: error,
1720
1986
  context: {
1721
1987
  accountAddress,
@@ -1725,109 +1991,87 @@ class DynamicWalletClient {
1725
1991
  throw error;
1726
1992
  }
1727
1993
  }
1728
- async storeEncryptedBackupByDelegatedWallet({ accountAddress, clientKeyShares = undefined, password = undefined, signedSessionId }) {
1729
- const dynamicRequestId = v4();
1730
- try {
1731
- var _this_walletMap_accountAddress, _this_walletMap_accountAddress_clientKeySharesBackupInfo_backups_BackupLocation_EXTERNAL, _this_walletMap_accountAddress_clientKeySharesBackupInfo_backups, _this_walletMap_accountAddress_clientKeySharesBackupInfo, _this_walletMap_accountAddress1;
1732
- const keySharesToBackup = clientKeyShares != null ? clientKeyShares : await this.getClientKeySharesFromLocalStorage({
1733
- accountAddress
1734
- });
1735
- if (!((_this_walletMap_accountAddress = this.walletMap[accountAddress]) == null ? void 0 : _this_walletMap_accountAddress.walletId)) {
1736
- const error = new Error(`WalletId not found for accountAddress ${accountAddress}`);
1737
- logError({
1738
- message: 'Error in storeEncryptedBackupByWallet, wallet or walletId not found from the wallet map',
1739
- error,
1740
- context: {
1741
- accountAddress,
1742
- walletMap: this.walletMap
1743
- }
1744
- });
1745
- throw error;
1746
- }
1747
- let dynamicClientKeyShares = [];
1748
- let delegatedKeyShares = [];
1749
- const encryptedKeyShares = await Promise.all(keySharesToBackup.map((keyShare)=>this.encryptKeyShare({
1750
- keyShare,
1751
- password
1752
- })));
1753
- const hasExistingDelegatedBackup = ((_this_walletMap_accountAddress1 = this.walletMap[accountAddress]) == null ? void 0 : (_this_walletMap_accountAddress_clientKeySharesBackupInfo = _this_walletMap_accountAddress1.clientKeySharesBackupInfo) == null ? void 0 : (_this_walletMap_accountAddress_clientKeySharesBackupInfo_backups = _this_walletMap_accountAddress_clientKeySharesBackupInfo.backups) == null ? void 0 : (_this_walletMap_accountAddress_clientKeySharesBackupInfo_backups_BackupLocation_EXTERNAL = _this_walletMap_accountAddress_clientKeySharesBackupInfo_backups[BackupLocation.EXTERNAL]) == null ? void 0 : _this_walletMap_accountAddress_clientKeySharesBackupInfo_backups_BackupLocation_EXTERNAL.length) > 0;
1754
- const shouldBackupToDelegated = hasExistingDelegatedBackup || keySharesToBackup.length >= 2;
1755
- if (shouldBackupToDelegated) {
1756
- // For 2 shares: 1 to backend, 1 to delegated
1757
- // For 3+ shares: N-1 to backend, 1 to delegated
1758
- dynamicClientKeyShares = encryptedKeyShares.slice(0, -DELEGATED_SHARE_COUNT);
1759
- delegatedKeyShares = encryptedKeyShares.slice(-DELEGATED_SHARE_COUNT);
1760
- } else {
1761
- dynamicClientKeyShares = encryptedKeyShares;
1762
- }
1763
- const data = await this.apiClient.storeEncryptedBackupByWallet({
1764
- walletId: this.walletMap[accountAddress].walletId,
1765
- encryptedKeyShares: dynamicClientKeyShares,
1766
- passwordEncrypted: Boolean(password) && password !== this.environmentId,
1767
- encryptionVersion: ENCRYPTION_VERSION_CURRENT,
1768
- signedSessionId,
1769
- authMode: this.authMode,
1770
- requiresSignedSessionId: this.requiresSignedSessionId(),
1771
- dynamicRequestId
1772
- });
1773
- await this.apiClient.markKeySharesAsBackedUp({
1774
- walletId: this.walletMap[accountAddress].walletId,
1775
- dynamicRequestId,
1776
- locations: [
1777
- {
1778
- location: BackupLocation.DYNAMIC
1779
- }
1780
- ]
1781
- });
1782
- if (delegatedKeyShares.length > 0) {
1783
- const wallet = this.walletMap[accountAddress];
1784
- var _this_userId;
1785
- const delegatedKeyShareIds = await this.sendKeySharesToDelegatedAccess({
1786
- accountAddress,
1787
- chainName: wallet.chainName,
1788
- environmentId: this.environmentId,
1789
- walletId: wallet.walletId,
1790
- userId: (_this_userId = this.userId) != null ? _this_userId : '',
1791
- delegatedKeyShares: delegatedKeyShares
1792
- });
1793
- data.keyShares.push({
1794
- backupLocation: BackupLocation.EXTERNAL,
1795
- id: delegatedKeyShareIds
1796
- });
1797
- //todo: combine with user share service backup once other branch is merged
1798
- await this.apiClient.markKeySharesAsBackedUp({
1799
- walletId: this.walletMap[accountAddress].walletId,
1800
- locations: [
1801
- {
1802
- location: BackupLocation.EXTERNAL
1803
- }
1804
- ],
1805
- dynamicRequestId
1806
- });
1807
- }
1808
- const updatedBackupInfo = getClientKeyShareBackupInfo({
1809
- walletProperties: {
1810
- derivationPath: this.walletMap[accountAddress].derivationPath,
1811
- keyShares: data.keyShares,
1812
- thresholdSignatureScheme: this.walletMap[accountAddress].thresholdSignatureScheme
1813
- }
1814
- });
1815
- this.walletMap[accountAddress] = _extends({}, this.walletMap[accountAddress], {
1816
- clientKeySharesBackupInfo: updatedBackupInfo
1994
+ /**
1995
+ * Central backup orchestrator that encrypts and stores wallet key shares.
1996
+ *
1997
+ * This method serves as the main backup coordinator, handling the distribution of encrypted
1998
+ * key shares between Dynamic's backend and Google Drive based on the wallet's threshold scheme.
1999
+ * It is used by multiple operations including reshare, refresh, and manual backup requests.
2000
+ *
2001
+ * **Backup Distribution Strategy:**
2002
+ * - **Single share wallets**: All shares stored on Dynamic's backend only
2003
+ * - **Multi-share wallets (2+)**: When backing up to Google Drive, N-1 shares on Dynamic's backend, 1 share on Google Drive
2004
+ * - **Multi-share wallets (2+)**: When not backing up to Google Drive, all shares on Dynamic's backend
2005
+ *
2006
+ * **Process Flow:**
2007
+ * 1. Encrypts all client key shares with the provided password (or environment ID if no password)
2008
+ * 2. For multi-share wallets (2+): conditionally distributes N-1 to backend, 1 to Google Drive
2009
+ * 3. For other configurations: stores all shares on Dynamic's backend
2010
+ * 4. Updates backup metadata and synchronizes wallet state
2011
+ * 5. Persists the updated wallet map to local storage
2012
+ *
2013
+ * **Delegated Key Shares:**
2014
+ * - When delegatedKeyshare is provided, the method will not store the delegated key share but it will mark the delegated share as backed up on Dynamic's backend
2015
+ * - and encrypt the delegated key share to publish it to the webhook
2016
+ *
2017
+ * @param params - The backup operation parameters
2018
+ * @param params.accountAddress - The account address of the wallet to backup
2019
+ * @param params.clientKeyShares - Optional specific key shares to backup (uses localStorage if not provided)
2020
+ * @param params.password - Optional password for encryption (uses environment ID if not provided)
2021
+ * @param params.signedSessionId - Optional signed session ID for authentication
2022
+ * @param params.backupToGoogleDrive - Whether to backup to Google Drive (defaults to false)
2023
+ * @returns Promise with backup metadata including share locations and IDs
2024
+ */ async storeEncryptedBackupByWallet({ accountAddress, clientKeyShares = undefined, password = undefined, signedSessionId, backupToGoogleDrive = false, delegatedKeyshare = undefined }) {
2025
+ var _this_walletMap_accountAddress, _this_walletMap_accountAddress1;
2026
+ const keySharesToBackup = clientKeyShares != null ? clientKeyShares : await this.getClientKeySharesFromLocalStorage({
2027
+ accountAddress
2028
+ });
2029
+ const shouldBackupToGoogleDrive = backupToGoogleDrive || hasGoogleDriveBackup((_this_walletMap_accountAddress = this.walletMap[accountAddress]) == null ? void 0 : _this_walletMap_accountAddress.clientKeySharesBackupInfo);
2030
+ const hasExistingDelegation = hasDelegatedBackup((_this_walletMap_accountAddress1 = this.walletMap[accountAddress]) == null ? void 0 : _this_walletMap_accountAddress1.clientKeySharesBackupInfo);
2031
+ let distribution;
2032
+ let preserveDelegatedLocation = false;
2033
+ if (delegatedKeyshare && shouldBackupToGoogleDrive) {
2034
+ // NEW delegation + Google Drive: Client's shares back up to both Dynamic and Google Drive.
2035
+ // The delegated share goes to the webhook.
2036
+ distribution = createDelegationWithGoogleDriveDistribution({
2037
+ existingShares: keySharesToBackup,
2038
+ delegatedShare: delegatedKeyshare
2039
+ });
2040
+ } else if (delegatedKeyshare) {
2041
+ // NEW delegation only: Client's shares back up to Dynamic.
2042
+ // The delegated share goes to the webhook. No Google Drive backup.
2043
+ distribution = createDelegationOnlyDistribution({
2044
+ existingShares: keySharesToBackup,
2045
+ delegatedShare: delegatedKeyshare
2046
+ });
2047
+ } else if (hasExistingDelegation && shouldBackupToGoogleDrive) {
2048
+ // ADD Google Drive to EXISTING delegation: Client's share backs up to both Dynamic and GD.
2049
+ // Don't re-publish delegated share, just preserve the location.
2050
+ distribution = createAddGoogleDriveToExistingDelegationDistribution({
2051
+ clientShares: keySharesToBackup
2052
+ });
2053
+ preserveDelegatedLocation = true;
2054
+ } else if (shouldBackupToGoogleDrive && keySharesToBackup.length >= 2) {
2055
+ // Google Drive only (no delegation): Split shares between Dynamic (N-1) and Google Drive (1).
2056
+ distribution = createGoogleDriveOnlyDistribution({
2057
+ allShares: keySharesToBackup
1817
2058
  });
1818
- await this.storage.setItem(this.storageKey, JSON.stringify(this.walletMap));
1819
- return data;
1820
- } catch (error) {
1821
- logError({
1822
- message: 'Error in storeEncryptedBackupByDelegatedWallet',
1823
- error: error,
1824
- context: {
1825
- accountAddress,
1826
- dynamicRequestId
1827
- }
2059
+ } else {
2060
+ // No delegation, no Google Drive: All shares go to Dynamic backend only.
2061
+ distribution = createDynamicOnlyDistribution({
2062
+ allShares: keySharesToBackup
1828
2063
  });
1829
- throw error;
1830
2064
  }
2065
+ const backupData = await this.backupSharesWithDistribution({
2066
+ accountAddress,
2067
+ password,
2068
+ signedSessionId,
2069
+ distribution,
2070
+ preserveDelegatedLocation
2071
+ });
2072
+ return _extends({}, backupData, {
2073
+ keyShareIds: backupData.locationsWithKeyShares.map((ks)=>ks.keyShareId)
2074
+ });
1831
2075
  }
1832
2076
  async storeEncryptedBackupByWalletWithRetry({ accountAddress, clientKeyShares, password, signedSessionId }) {
1833
2077
  await retryPromise(()=>this.storeEncryptedBackupByWallet({
@@ -2018,14 +2262,18 @@ class DynamicWalletClient {
2018
2262
  return (_ks_externalKeyShareId = ks.externalKeyShareId) != null ? _ks_externalKeyShareId : '';
2019
2263
  });
2020
2264
  } else {
2021
- // Already 2-of-3, only call backup
2022
- const data = await this.storeEncryptedBackupByWallet({
2265
+ await this.storeEncryptedBackupByWallet({
2023
2266
  accountAddress,
2024
2267
  password,
2025
2268
  signedSessionId,
2026
2269
  backupToGoogleDrive: true
2027
2270
  });
2028
- return data.keyShareIds;
2271
+ const backupInfo = this.walletMap[accountAddress].clientKeySharesBackupInfo;
2272
+ const googleDriveShares = backupInfo.backups[BackupLocation.GOOGLE_DRIVE] || [];
2273
+ return googleDriveShares.map((ks)=>{
2274
+ var _ks_externalKeyShareId;
2275
+ return (_ks_externalKeyShareId = ks.externalKeyShareId) != null ? _ks_externalKeyShareId : '';
2276
+ });
2029
2277
  }
2030
2278
  } catch (error) {
2031
2279
  logError({
@@ -2061,7 +2309,8 @@ class DynamicWalletClient {
2061
2309
  const thresholdSignatureScheme = this.walletMap[accountAddress].thresholdSignatureScheme;
2062
2310
  const fileName = getClientKeyShareExportFileName({
2063
2311
  thresholdSignatureScheme,
2064
- accountAddress
2312
+ accountAddress,
2313
+ isGoogleDrive: true
2065
2314
  });
2066
2315
  const backupData = createBackupData({
2067
2316
  encryptedKeyShares,
@@ -2086,7 +2335,7 @@ class DynamicWalletClient {
2086
2335
  throw error;
2087
2336
  }
2088
2337
  }
2089
- async restoreBackupFromGoogleDrive({ accountAddress, password, signedSessionId }) {
2338
+ async exportClientKeysharesFromGoogleDrive({ accountAddress, password, signedSessionId }) {
2090
2339
  try {
2091
2340
  await this.getWallet({
2092
2341
  accountAddress,
@@ -2097,15 +2346,16 @@ class DynamicWalletClient {
2097
2346
  oauthAccountId
2098
2347
  });
2099
2348
  const thresholdSignatureScheme = this.walletMap[accountAddress].thresholdSignatureScheme;
2100
- const fileName = getClientKeyShareExportFileName({
2349
+ const backupFileName = getClientKeyShareExportFileName({
2101
2350
  thresholdSignatureScheme,
2102
- accountAddress
2351
+ accountAddress,
2352
+ isGoogleDrive: true
2103
2353
  });
2104
2354
  let backupData = null;
2105
2355
  try {
2106
2356
  backupData = await retryPromise(()=>downloadFileFromGoogleDrive({
2107
2357
  accessToken,
2108
- fileName
2358
+ fileName: backupFileName
2109
2359
  }));
2110
2360
  } catch (error) {
2111
2361
  logError({
@@ -2113,7 +2363,7 @@ class DynamicWalletClient {
2113
2363
  error: error,
2114
2364
  context: {
2115
2365
  accountAddress,
2116
- fileName
2366
+ fileName: backupFileName
2117
2367
  }
2118
2368
  });
2119
2369
  throw error;
@@ -2125,7 +2375,7 @@ class DynamicWalletClient {
2125
2375
  error: new Error('No backup file found'),
2126
2376
  context: {
2127
2377
  accountAddress,
2128
- fileName
2378
+ fileName: backupFileName
2129
2379
  }
2130
2380
  });
2131
2381
  throw error;
@@ -2138,7 +2388,7 @@ class DynamicWalletClient {
2138
2388
  error,
2139
2389
  context: {
2140
2390
  accountAddress,
2141
- fileName
2391
+ fileName: backupFileName
2142
2392
  }
2143
2393
  });
2144
2394
  throw error;
@@ -2148,15 +2398,20 @@ class DynamicWalletClient {
2148
2398
  keyShare,
2149
2399
  password
2150
2400
  })));
2151
- await this.setClientKeySharesToLocalStorage({
2401
+ const text = JSON.stringify(decryptedKeyShares, null, 2);
2402
+ const exportFileName = getClientKeyShareExportFileName({
2403
+ thresholdSignatureScheme,
2152
2404
  accountAddress,
2153
- clientKeyShares: decryptedKeyShares,
2154
- overwriteOrMerge: 'merge'
2405
+ isGoogleDrive: true
2406
+ });
2407
+ downloadStringAsFile({
2408
+ filename: exportFileName,
2409
+ content: text,
2410
+ mimeType: 'application/json'
2155
2411
  });
2156
- return decryptedKeyShares;
2157
2412
  } catch (error) {
2158
2413
  logError({
2159
- message: 'Error in restoreBackupFromGoogleDrive',
2414
+ message: 'Error in exportClientKeysharesFromGoogleDrive',
2160
2415
  error: error,
2161
2416
  context: {
2162
2417
  accountAddress
@@ -2183,16 +2438,11 @@ class DynamicWalletClient {
2183
2438
  keyShares: clientKeyShares,
2184
2439
  derivationPath
2185
2440
  });
2186
- const blob = new Blob([
2187
- text
2188
- ], {
2189
- type: 'text/plain'
2441
+ downloadStringAsFile({
2442
+ filename: `${CLIENT_KEYSHARE_EXPORT_FILENAME_PREFIX}-${accountAddress}.txt`,
2443
+ content: text,
2444
+ mimeType: 'text/plain'
2190
2445
  });
2191
- const url = URL.createObjectURL(blob);
2192
- const a = document.createElement('a');
2193
- a.href = url;
2194
- a.download = `${CLIENT_KEYSHARE_EXPORT_FILENAME_PREFIX}-${accountAddress}.txt`;
2195
- a.click();
2196
2446
  }
2197
2447
  async getClientKeyShares({ accountAddress, password, signedSessionId }) {
2198
2448
  await this.getWallet({
@@ -2492,29 +2742,142 @@ class DynamicWalletClient {
2492
2742
  }
2493
2743
  this.apiClient.syncAuthToken(authToken);
2494
2744
  }
2745
+ async createRooms({ roomType, thresholdSignatureScheme, roomCount = 5 }) {
2746
+ const numberOfParties = this.getNumberOfParties(roomType, thresholdSignatureScheme);
2747
+ try {
2748
+ var _rooms_roomIds;
2749
+ if (DynamicWalletClient.roomsInitializing[numberOfParties]) {
2750
+ return;
2751
+ }
2752
+ DynamicWalletClient.roomsInitializing[numberOfParties] = true;
2753
+ // Check if rooms for this number of parties already exist (from previous restore or creation)
2754
+ const existingRooms = DynamicWalletClient.rooms[numberOfParties];
2755
+ if (existingRooms && existingRooms.length > 0) {
2756
+ DynamicWalletClient.roomsInitializing[numberOfParties] = false;
2757
+ return;
2758
+ }
2759
+ const rooms = await this.apiClient.createRoomsWithoutWalletId({
2760
+ roomType,
2761
+ roomCount,
2762
+ thresholdSignatureScheme
2763
+ });
2764
+ const newRooms = (rooms == null ? void 0 : (_rooms_roomIds = rooms.roomIds) == null ? void 0 : _rooms_roomIds.map((roomId)=>({
2765
+ roomId,
2766
+ createdAt: Date.now(),
2767
+ expiresAt: Date.now() + ROOM_EXPIRATION_TIME
2768
+ }))) || [];
2769
+ const updatedRooms = [
2770
+ ...newRooms,
2771
+ ...existingRooms || []
2772
+ ];
2773
+ await this.setRooms(numberOfParties, updatedRooms);
2774
+ DynamicWalletClient.roomsInitializing[numberOfParties] = false;
2775
+ } catch (error) {
2776
+ DynamicWalletClient.roomsInitializing[numberOfParties] = false;
2777
+ this.logger.error('Error creating rooms', error);
2778
+ throw error;
2779
+ }
2780
+ }
2781
+ async restoreRooms() {
2782
+ const roomData = await this.storage.getItem(`${this.storageKey}-rooms`);
2783
+ if (!roomData) {
2784
+ return {};
2785
+ }
2786
+ const parsedRoomData = JSON.parse(roomData);
2787
+ const now = Date.now();
2788
+ // Filter out expired rooms for all party counts
2789
+ const restoredRooms = {};
2790
+ for (const [partiesStr, rooms] of Object.entries(parsedRoomData)){
2791
+ const numberOfParties = Number(partiesStr);
2792
+ const validRooms = rooms.filter((room)=>room.expiresAt > now);
2793
+ if (validRooms.length > 0) {
2794
+ restoredRooms[numberOfParties] = validRooms;
2795
+ }
2796
+ }
2797
+ // Restore all rooms to the static property
2798
+ DynamicWalletClient.rooms = restoredRooms;
2799
+ return restoredRooms;
2800
+ }
2801
+ async getRooms(roomType, thresholdSignatureScheme) {
2802
+ if (roomType && thresholdSignatureScheme) {
2803
+ const numberOfParties = this.getNumberOfParties(roomType, thresholdSignatureScheme);
2804
+ return DynamicWalletClient.rooms[numberOfParties] || [];
2805
+ }
2806
+ return DynamicWalletClient.rooms;
2807
+ }
2808
+ async setRooms(numberOfParties, rooms) {
2809
+ DynamicWalletClient.rooms[numberOfParties] = rooms;
2810
+ await this.storage.setItem(`${this.storageKey}-rooms`, JSON.stringify(DynamicWalletClient.rooms));
2811
+ }
2812
+ getNumberOfParties(roomType, thresholdSignatureScheme) {
2813
+ const MPC_SCHEME_CONFIG = MPC_CONFIG[thresholdSignatureScheme];
2814
+ return roomType === RoomTypeEnum.Threshold ? MPC_SCHEME_CONFIG.threshold : MPC_SCHEME_CONFIG.numberOfParties;
2815
+ }
2816
+ async getRoom(roomType, thresholdSignatureScheme) {
2817
+ const numberOfParties = this.getNumberOfParties(roomType, thresholdSignatureScheme);
2818
+ const now = Date.now();
2819
+ const schemeRooms = DynamicWalletClient.rooms[numberOfParties] || [];
2820
+ // Filter out expired rooms
2821
+ const filteredRooms = schemeRooms.filter((room)=>room.expiresAt > now);
2822
+ await this.setRooms(numberOfParties, filteredRooms);
2823
+ const room = filteredRooms.shift();
2824
+ if (room) {
2825
+ await this.setRooms(numberOfParties, filteredRooms);
2826
+ }
2827
+ // If less than or only 1 room left after removing one, trigger async creation for more rooms
2828
+ // Check the length after removing the room
2829
+ const remainingRooms = DynamicWalletClient.rooms[numberOfParties] || [];
2830
+ if (remainingRooms.length <= 1) {
2831
+ this.createRooms({
2832
+ roomType,
2833
+ thresholdSignatureScheme,
2834
+ roomCount: ROOM_CACHE_COUNT
2835
+ });
2836
+ }
2837
+ return room;
2838
+ }
2839
+ /**
2840
+ * Helper method to instrument with automatic properties inclusion
2841
+ */ instrument(message, context) {
2842
+ const defaultContext = {
2843
+ environmentId: context.environmentId || this.environmentId
2844
+ };
2845
+ this.logger.debug(message, _extends({}, defaultContext, context));
2846
+ this.logger.instrument(message, _extends({}, defaultContext, context));
2847
+ }
2848
+ getTraceContext(traceContext) {
2849
+ const now = Date.now();
2850
+ return _extends({
2851
+ now,
2852
+ time: (traceContext == null ? void 0 : traceContext.startTime) ? now - traceContext.startTime : 0
2853
+ }, traceContext);
2854
+ }
2495
2855
  constructor({ environmentId, baseApiUrl, baseMPCRelayApiUrl, storageKey, debug, featureFlags, authMode = AuthMode.HEADER, authToken = undefined, // Represents the version of the client SDK used by developer
2496
- sdkVersion }){
2856
+ sdkVersion, forwardMPCClient, baseClientKeysharesRelayApiUrl }){
2497
2857
  this.userId = undefined;
2498
2858
  this.sessionId = undefined;
2499
- this.delegatedAccessEndpoint = undefined;
2500
2859
  this.initializePromise = null;
2501
2860
  this.logger = logger;
2502
2861
  this.walletMap = {} // todo: store in session storage
2503
2862
  ;
2504
2863
  this.memoryStorage = null;
2505
2864
  this.iframe = null;
2865
+ this.forwardMPCEnabled = false;
2506
2866
  this.featureFlags = {};
2507
2867
  this.environmentId = environmentId;
2508
2868
  this.storageKey = `${STORAGE_KEY}-${storageKey != null ? storageKey : environmentId}`;
2509
2869
  this.baseMPCRelayApiUrl = baseMPCRelayApiUrl;
2510
2870
  this.authMode = authMode;
2511
2871
  this.sdkVersion = sdkVersion;
2872
+ this.baseClientKeysharesRelayApiUrl = baseClientKeysharesRelayApiUrl;
2512
2873
  this.apiClient = new DynamicApiClient({
2513
2874
  environmentId,
2514
2875
  authToken,
2515
2876
  baseApiUrl,
2516
2877
  authMode,
2517
- sdkVersion
2878
+ sdkVersion,
2879
+ forwardMPCClient,
2880
+ baseClientKeysharesRelayApiUrl
2518
2881
  });
2519
2882
  this.debug = Boolean(debug);
2520
2883
  this.logger.setLogLevel(this.debug ? LogLevel.DEBUG : DEFAULT_LOG_LEVEL);
@@ -2534,8 +2897,15 @@ class DynamicWalletClient {
2534
2897
  if (authMode === AuthMode.HEADER && authToken) {
2535
2898
  this.initLoggerContext(authToken);
2536
2899
  }
2900
+ this.forwardMPCEnabled = this.featureFlags && this.featureFlags[FEATURE_FLAGS.ENABLE_FORWARD_MPC_CLIENT_FLAG] === true;
2901
+ // if forwardMPCEnabled is true and forwardMPCClient is not provided, initialize the forwardMPCClient
2902
+ if (this.forwardMPCEnabled && !forwardMPCClient) {
2903
+ this.initializeForwardMPCClient();
2904
+ }
2537
2905
  }
2538
2906
  }
2907
+ DynamicWalletClient.rooms = {};
2908
+ DynamicWalletClient.roomsInitializing = {};
2539
2909
 
2540
2910
  const ERROR_KEYGEN_FAILED = '[DynamicWaasWalletClient]: Error with keygen';
2541
2911
  const ERROR_CREATE_WALLET_ACCOUNT = '[DynamicWaasWalletClient]: Error creating wallet account';
@@ -2547,4 +2917,4 @@ const ERROR_VERIFY_TRANSACTION_SIGNATURE = '[DynamicWaasWalletClient]: Error ver
2547
2917
  const ERROR_EXPORT_PRIVATE_KEY = '[DynamicWaasWalletClient]: Error exporting private key';
2548
2918
  const ERROR_IMPORT_PRIVATE_KEY = '[DynamicWaasWalletClient]: Error importing private key';
2549
2919
 
2550
- export { DynamicWalletClient, ERROR_ACCOUNT_ADDRESS_REQUIRED, ERROR_CREATE_WALLET_ACCOUNT, ERROR_EXPORT_PRIVATE_KEY, ERROR_IMPORT_PRIVATE_KEY, ERROR_KEYGEN_FAILED, ERROR_SIGN_MESSAGE, ERROR_SIGN_TYPED_DATA, ERROR_VERIFY_MESSAGE_SIGNATURE, ERROR_VERIFY_TRANSACTION_SIGNATURE, createBackupData, formatEvmMessage, formatMessage, getClientKeyShareBackupInfo, getClientKeyShareExportFileName, getGoogleOAuthAccountId, getMPCSignatureScheme, getMPCSigner, isBrowser, isHexString, mergeUniqueKeyShares, retryPromise, timeoutPromise };
2920
+ export { DynamicWalletClient, ERROR_ACCOUNT_ADDRESS_REQUIRED, ERROR_CREATE_WALLET_ACCOUNT, ERROR_EXPORT_PRIVATE_KEY, ERROR_IMPORT_PRIVATE_KEY, ERROR_KEYGEN_FAILED, ERROR_SIGN_MESSAGE, ERROR_SIGN_TYPED_DATA, ERROR_VERIFY_MESSAGE_SIGNATURE, ERROR_VERIFY_TRANSACTION_SIGNATURE, createAddGoogleDriveToExistingDelegationDistribution, createBackupData, createDelegationOnlyDistribution, createDelegationWithGoogleDriveDistribution, createDynamicOnlyDistribution, createGoogleDriveOnlyDistribution, downloadStringAsFile, formatEvmMessage, formatMessage, getClientKeyShareBackupInfo, getClientKeyShareExportFileName, getGoogleOAuthAccountId, getMPCSignatureScheme, getMPCSigner, hasDelegatedBackup, hasGoogleDriveBackup, isBrowser, isHexString, mergeUniqueKeyShares, retryPromise, timeoutPromise };