@dynamic-labs-wallet/browser 0.0.0-pr384.2 → 0.0.0-pr526.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.
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, ThresholdSignatureScheme, getClientThreshold, MPC_CONFIG, getTSSConfig, serializeMessageForForwardMPC, WalletOperation, getReshareConfig, FEATURE_FLAGS, 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,12 @@ 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_CACHE_COUNT = 5;
467
468
 
468
469
  const isBrowser = ()=>typeof window !== 'undefined';
469
- const getClientKeyShareExportFileName = ({ thresholdSignatureScheme, accountAddress })=>{
470
- return `${CLIENT_KEYSHARE_EXPORT_FILENAME_PREFIX}-${thresholdSignatureScheme}-${accountAddress}.json`;
470
+ const getClientKeyShareExportFileName = ({ thresholdSignatureScheme, accountAddress, isGoogleDrive = false })=>{
471
+ const suffix = isGoogleDrive ? '-google-drive' : '';
472
+ return `${CLIENT_KEYSHARE_EXPORT_FILENAME_PREFIX}-${thresholdSignatureScheme}-${accountAddress}${suffix}.json`;
471
473
  };
472
474
  const getClientKeyShareBackupInfo = (params)=>{
473
475
  var _params_walletProperties, _params_walletProperties_keyShares_;
@@ -476,7 +478,8 @@ const getClientKeyShareBackupInfo = (params)=>{
476
478
  [BackupLocation.GOOGLE_DRIVE]: [],
477
479
  [BackupLocation.ICLOUD]: [],
478
480
  [BackupLocation.USER]: [],
479
- [BackupLocation.EXTERNAL]: []
481
+ [BackupLocation.EXTERNAL]: [],
482
+ [BackupLocation.DELEGATED]: []
480
483
  };
481
484
  if (!(params == null ? void 0 : (_params_walletProperties = params.walletProperties) == null ? void 0 : _params_walletProperties.keyShares)) {
482
485
  return {
@@ -606,6 +609,19 @@ const createBackupData = ({ encryptedKeyShares, accountAddress, thresholdSignatu
606
609
  }
607
610
  };
608
611
  };
612
+ const downloadStringAsFile = ({ filename, content, mimeType = 'application/json' })=>{
613
+ const blob = new Blob([
614
+ content
615
+ ], {
616
+ type: mimeType
617
+ });
618
+ const url = URL.createObjectURL(blob);
619
+ const a = document.createElement('a');
620
+ a.href = url;
621
+ a.download = filename;
622
+ a.click();
623
+ URL.revokeObjectURL(url);
624
+ };
609
625
 
610
626
  /**
611
627
  * Uploads a backup to Google Drive App
@@ -652,6 +668,84 @@ const createBackupData = ({ encryptedKeyShares, accountAddress, thresholdSignatu
652
668
  }
653
669
  };
654
670
 
671
+ const ALG_LABEL_RSA = 'HYBRID-RSA-AES-256';
672
+ /**
673
+ * Convert base64 to base64url encoding
674
+ */ const toBase64Url = (buffer)=>{
675
+ const base64 = Buffer.from(buffer).toString('base64');
676
+ return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
677
+ };
678
+ /**
679
+ * Convert ArrayBuffer to base64url
680
+ */ const arrayBufferToBase64Url = (buffer)=>{
681
+ return toBase64Url(buffer);
682
+ };
683
+ /**
684
+ * Import RSA public key from PEM format
685
+ */ const importRSAPublicKey = async (publicKeyPem)=>{
686
+ // Remove PEM headers and decode base64
687
+ const pemHeader = '-----BEGIN PUBLIC KEY-----';
688
+ const pemFooter = '-----END PUBLIC KEY-----';
689
+ const pemContents = publicKeyPem.replace(pemHeader, '').replace(pemFooter, '').replace(/\s/g, '');
690
+ const binaryDer = Buffer.from(pemContents, 'base64').toString('binary');
691
+ const keyData = new Uint8Array(binaryDer.length);
692
+ for(let i = 0; i < binaryDer.length; i++){
693
+ keyData[i] = binaryDer.charCodeAt(i);
694
+ }
695
+ return await crypto.subtle.importKey('spki', keyData, {
696
+ name: 'RSA-OAEP',
697
+ hash: 'SHA-256'
698
+ }, false, [
699
+ 'encrypt'
700
+ ]);
701
+ };
702
+ // encodedEnvelopeBytes intentionally omitted; alg/ct/ek/iv/tag/kid is sufficient
703
+ /**
704
+ * Encrypts data using HYBRID-RSA-AES-256 encryption scheme with Web Crypto API.
705
+ * 1. Generate random AES-256 key
706
+ * 2. Encrypt AES key with RSA public key
707
+ * 3. Encrypt data with AES-256-GCM
708
+ */ const encryptDelegatedKeyShare = async (data, publicKeyPem, keyId)=>{
709
+ try {
710
+ // Step 1: Generate a random AES-256 key and 16-byte IV
711
+ const aesKey = await crypto.subtle.generateKey({
712
+ name: 'AES-GCM',
713
+ length: 256
714
+ }, true, [
715
+ 'encrypt'
716
+ ]);
717
+ const iv = crypto.getRandomValues(new Uint8Array(16)); // 128-bit IV for GCM
718
+ // Step 2: Encrypt the data with AES-256-GCM
719
+ const plaintext = new TextEncoder().encode(data);
720
+ const encryptedData = await crypto.subtle.encrypt({
721
+ name: 'AES-GCM',
722
+ iv: iv
723
+ }, aesKey, plaintext);
724
+ // Extract the auth tag from the encrypted data (last 16 bytes)
725
+ const encryptedDataArray = new Uint8Array(encryptedData);
726
+ const authTag = encryptedDataArray.slice(-16);
727
+ const ciphertext = encryptedDataArray.slice(0, -16);
728
+ // Step 3: Encrypt the AES key with RSA public key
729
+ const rsaPublicKey = await importRSAPublicKey(publicKeyPem);
730
+ // Export the AES key to encrypt it
731
+ const aesKeyData = await crypto.subtle.exportKey('raw', aesKey);
732
+ const encryptedAesKey = await crypto.subtle.encrypt({
733
+ name: 'RSA-OAEP'
734
+ }, rsaPublicKey, aesKeyData);
735
+ return _extends({
736
+ alg: ALG_LABEL_RSA,
737
+ iv: arrayBufferToBase64Url(iv.buffer),
738
+ ct: arrayBufferToBase64Url(ciphertext.buffer),
739
+ tag: arrayBufferToBase64Url(authTag.buffer),
740
+ ek: arrayBufferToBase64Url(encryptedAesKey)
741
+ }, keyId ? {
742
+ kid: keyId
743
+ } : {});
744
+ } catch (error) {
745
+ throw new Error(`Encryption failed: ${error instanceof Error ? error.message : String(error)}`);
746
+ }
747
+ };
748
+
655
749
  const localStorageWriteTest = {
656
750
  tested: false,
657
751
  writable: false
@@ -721,7 +815,61 @@ const localStorageWriteTest = {
721
815
  }
722
816
  });
723
817
 
818
+ const createDelegationWithGoogleDriveDistribution = ({ existingShares, delegatedShare })=>({
819
+ dynamicBackendShares: existingShares,
820
+ googleDriveShares: existingShares,
821
+ delegatedShare
822
+ });
823
+ const createDelegationOnlyDistribution = ({ existingShares, delegatedShare })=>({
824
+ dynamicBackendShares: existingShares,
825
+ googleDriveShares: [],
826
+ delegatedShare
827
+ });
828
+ const createGoogleDriveOnlyDistribution = ({ allShares })=>({
829
+ dynamicBackendShares: allShares.slice(0, -1),
830
+ googleDriveShares: allShares.slice(-1)
831
+ });
832
+ const createDynamicOnlyDistribution = ({ allShares })=>({
833
+ dynamicBackendShares: allShares,
834
+ googleDriveShares: []
835
+ });
836
+ const hasGoogleDriveBackup = (backupInfo)=>{
837
+ var _backupInfo_backups_BackupLocation_GOOGLE_DRIVE, _backupInfo_backups;
838
+ var _backupInfo_backups_BackupLocation_GOOGLE_DRIVE_length;
839
+ 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;
840
+ };
841
+ const hasDelegatedBackup = (backupInfo)=>{
842
+ var _backupInfo_backups_BackupLocation_DELEGATED, _backupInfo_backups;
843
+ var _backupInfo_backups_BackupLocation_DELEGATED_length;
844
+ 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;
845
+ };
846
+ /**
847
+ * Distribution for adding Google Drive backup to an existing delegation.
848
+ * Client's shares go to both Dynamic and Google Drive.
849
+ * delegatedShare is undefined - we don't re-publish, but preserve the location.
850
+ */ const createAddGoogleDriveToExistingDelegationDistribution = ({ clientShares })=>({
851
+ dynamicBackendShares: clientShares,
852
+ googleDriveShares: clientShares
853
+ });
854
+
724
855
  class DynamicWalletClient {
856
+ async initializeForwardMPCClient() {
857
+ try {
858
+ await this.apiClient.forwardMPCClient.connect();
859
+ this.logger.info('Connected to ForwardMPC enclave websocket. Instance: ' + this.instanceId);
860
+ try {
861
+ await this.apiClient.forwardMPCClient.handshake();
862
+ this.logger.debug('Handshaked with ForwardMPC enclave websocket. Instance: ' + this.instanceId);
863
+ } catch (error) {
864
+ this.logger.error('Error handshaking with ForwardMPC enclave websocket. Instance: ' + this.instanceId, error);
865
+ }
866
+ } catch (error) {
867
+ this.logger.error('Error connecting to ForwardMPC enclave websocket. Instance: ' + this.instanceId, {
868
+ error,
869
+ environmentId: this.environmentId
870
+ });
871
+ }
872
+ }
725
873
  getAuthMode() {
726
874
  return this.authMode;
727
875
  }
@@ -793,30 +941,37 @@ class DynamicWalletClient {
793
941
  throw error;
794
942
  }
795
943
  }
796
- async initialize() {
944
+ async initialize(traceContext) {
797
945
  if (this.initializePromise) {
798
946
  return await this.initializePromise;
799
947
  }
800
948
  this.logger.debug('[DynamicWaasWalletClient] Initializing Dynamic Waas Wallet SDK');
801
- this.initializePromise = this._initialize();
949
+ this.initializePromise = this._initialize(traceContext);
802
950
  const result = await this.initializePromise;
803
951
  this.logger.debug('[DynamicWaasWalletClient] Dynamic Waas Wallet SDK initialized');
804
952
  return result;
805
953
  }
806
954
  /**
807
955
  * Client initialization logic
808
- */ async _initialize() {
956
+ */ async _initialize(traceContext) {
809
957
  try {
810
958
  const initializePromises = [
811
- this.restoreWallets()
959
+ this.restoreWallets(),
960
+ this.createRooms({
961
+ roomType: RoomTypeEnum.Threshold,
962
+ thresholdSignatureScheme: ThresholdSignatureScheme.TWO_OF_TWO,
963
+ roomCount: ROOM_CACHE_COUNT
964
+ })
812
965
  ];
813
966
  await Promise.all(initializePromises);
814
967
  return {
815
- error: null
968
+ error: null,
969
+ traceContext
816
970
  };
817
971
  } catch (error) {
818
972
  return {
819
- error
973
+ error,
974
+ traceContext
820
975
  };
821
976
  }
822
977
  }
@@ -895,7 +1050,7 @@ class DynamicWalletClient {
895
1050
  clientKeygenResults
896
1051
  };
897
1052
  }
898
- async keyGen({ chainName, thresholdSignatureScheme, onError, onCeremonyComplete }) {
1053
+ async keyGen({ chainName, thresholdSignatureScheme, onError, onCeremonyComplete, traceContext }) {
899
1054
  const dynamicRequestId = v4();
900
1055
  try {
901
1056
  const clientKeygenInitResults = await this.clientInitializeKeyGen({
@@ -903,11 +1058,12 @@ class DynamicWalletClient {
903
1058
  thresholdSignatureScheme
904
1059
  });
905
1060
  const clientKeygenIds = clientKeygenInitResults.map((result)=>result.keygenId);
906
- this.logger.debug('[DynamicWaasWalletClient] Initialized client key generation', {
1061
+ this.instrument('[DynamicWaasWalletClient] Initialized client key generation', _extends({
907
1062
  clientKeygenIds: clientKeygenIds.join(', '),
908
1063
  chainName,
909
- thresholdSignatureScheme
910
- });
1064
+ thresholdSignatureScheme,
1065
+ key: 'keyGen'
1066
+ }, this.getTraceContext(traceContext)));
911
1067
  const { roomId, serverKeygenIds } = await this.serverInitializeKeyGen({
912
1068
  chainName,
913
1069
  clientKeygenIds,
@@ -915,12 +1071,14 @@ class DynamicWalletClient {
915
1071
  thresholdSignatureScheme,
916
1072
  onCeremonyComplete
917
1073
  });
918
- this.logger.debug('[DynamicWaasWalletClient] Server key generation initialized', {
1074
+ this.instrument('[DynamicWaasWalletClient] Server key generation initialized', _extends({
919
1075
  roomId,
920
1076
  clientKeygenIds: clientKeygenIds.join(', '),
921
1077
  serverKeygenIds: serverKeygenIds.join(', '),
922
- chainName
923
- });
1078
+ chainName,
1079
+ key: 'keyGen',
1080
+ operation: 'serverKeyGen'
1081
+ }, this.getTraceContext(traceContext)));
924
1082
  const { rawPublicKey, clientKeygenResults: clientKeyShares } = await this.clientKeyGen({
925
1083
  chainName,
926
1084
  roomId,
@@ -928,15 +1086,17 @@ class DynamicWalletClient {
928
1086
  clientKeygenInitResults,
929
1087
  thresholdSignatureScheme
930
1088
  });
931
- this.logger.debug('[DynamicWaasWalletClient] Client key generation completed', {
1089
+ this.instrument('[DynamicWaasWalletClient] Client key generation completed', _extends({
932
1090
  roomId,
933
1091
  serverKeygenIds: serverKeygenIds.join(', '),
934
1092
  clientKeygenIds: clientKeygenIds.join(', '),
935
1093
  chainName,
936
1094
  thresholdSignatureScheme,
937
1095
  rawPublicKey,
938
- clientKeySharesCount: clientKeyShares.length
939
- });
1096
+ clientKeySharesCount: clientKeyShares.length,
1097
+ key: 'keyGen',
1098
+ operation: 'clientKeyGen'
1099
+ }, this.getTraceContext(traceContext)));
940
1100
  return {
941
1101
  rawPublicKey,
942
1102
  clientKeyShares
@@ -945,16 +1105,16 @@ class DynamicWalletClient {
945
1105
  logError({
946
1106
  message: 'Error in keyGen',
947
1107
  error: error,
948
- context: {
1108
+ context: _extends({
949
1109
  chainName,
950
1110
  thresholdSignatureScheme,
951
1111
  dynamicRequestId
952
- }
1112
+ }, this.getTraceContext(traceContext))
953
1113
  });
954
1114
  throw error;
955
1115
  }
956
1116
  }
957
- async importRawPrivateKey({ chainName, privateKey, thresholdSignatureScheme, onError, onCeremonyComplete }) {
1117
+ async importRawPrivateKey({ chainName, privateKey, thresholdSignatureScheme, onError, onCeremonyComplete, traceContext }) {
958
1118
  const dynamicRequestId = v4();
959
1119
  try {
960
1120
  const mpcSigner = getMPCSigner({
@@ -966,11 +1126,13 @@ class DynamicWalletClient {
966
1126
  thresholdSignatureScheme
967
1127
  });
968
1128
  const clientKeygenIds = clientKeygenInitResults.map((result)=>result.keygenId);
969
- this.logger.debug('[DynamicWaasWalletClient] Client key generation initialized', {
1129
+ this.instrument('[DynamicWaasWalletClient] Client key generation initialized', _extends({
970
1130
  clientKeygenIds: clientKeygenIds.join(', '),
971
1131
  chainName,
972
- thresholdSignatureScheme
973
- });
1132
+ thresholdSignatureScheme,
1133
+ key: 'importRawPrivateKey',
1134
+ operation: 'clientKeyGen'
1135
+ }, this.getTraceContext(traceContext)));
974
1136
  const { roomId, serverKeygenIds } = await this.apiClient.importPrivateKey({
975
1137
  chainName,
976
1138
  clientKeygenIds,
@@ -979,12 +1141,14 @@ class DynamicWalletClient {
979
1141
  onError,
980
1142
  onCeremonyComplete
981
1143
  });
982
- this.logger.debug('[DynamicWaasWalletClient] Server key generation initialized', {
1144
+ this.instrument('[DynamicWaasWalletClient] Server key generation initialized', _extends({
983
1145
  roomId,
984
1146
  clientKeygenIds: clientKeygenIds.join(', '),
985
1147
  serverKeygenIds: serverKeygenIds.join(', '),
986
- chainName
987
- });
1148
+ chainName,
1149
+ key: 'importRawPrivateKey',
1150
+ operation: 'serverImportPrivateKey'
1151
+ }, this.getTraceContext(traceContext)));
988
1152
  const { threshold } = getTSSConfig(thresholdSignatureScheme);
989
1153
  const clientKeygenResults = await Promise.all(clientKeygenInitResults.map(async (currentInit, index)=>{
990
1154
  const otherClientKeygenIds = clientKeygenInitResults.filter((init)=>init.keygenId !== currentInit.keygenId).map((init)=>init.keygenId);
@@ -1009,14 +1173,16 @@ class DynamicWalletClient {
1009
1173
  keyShare: clientKeygenResult,
1010
1174
  derivationPath: undefined
1011
1175
  });
1012
- this.logger.debug('[DynamicWaasWalletClient] Completed import of raw private key', {
1176
+ this.instrument('[DynamicWaasWalletClient] Completed import of raw private key', _extends({
1013
1177
  rawPublicKey,
1014
1178
  chainName,
1015
1179
  thresholdSignatureScheme,
1016
1180
  roomId,
1017
1181
  serverKeygenIds: serverKeygenIds.join(', '),
1018
- clientKeygenIds: clientKeygenIds.join(', ')
1019
- });
1182
+ clientKeygenIds: clientKeygenIds.join(', '),
1183
+ key: 'importRawPrivateKey',
1184
+ operation: 'importRawPrivateKey'
1185
+ }, this.getTraceContext(traceContext)));
1020
1186
  return {
1021
1187
  rawPublicKey,
1022
1188
  clientKeyShares: clientKeygenResults
@@ -1025,16 +1191,16 @@ class DynamicWalletClient {
1025
1191
  logError({
1026
1192
  message: 'Error in importRawPrivateKey',
1027
1193
  error: error,
1028
- context: {
1194
+ context: _extends({
1029
1195
  chainName,
1030
1196
  thresholdSignatureScheme,
1031
1197
  dynamicRequestId
1032
- }
1198
+ }, this.getTraceContext(traceContext))
1033
1199
  });
1034
1200
  throw error;
1035
1201
  }
1036
1202
  }
1037
- async serverSign({ walletId, message, isFormatted, mfaToken, context, onError, dynamicRequestId }) {
1203
+ async serverSign({ walletId, message, isFormatted, mfaToken, roomId, context, onError, dynamicRequestId, traceContext }) {
1038
1204
  // Create the room and sign the message
1039
1205
  if (typeof message !== 'string') {
1040
1206
  message = `0x${Buffer.from(message).toString('hex')}`;
@@ -1045,44 +1211,101 @@ class DynamicWalletClient {
1045
1211
  isFormatted,
1046
1212
  dynamicRequestId,
1047
1213
  mfaToken,
1214
+ roomId,
1048
1215
  context: context ? JSON.parse(JSON.stringify(context, (_key, value)=>typeof value === 'bigint' ? value.toString() : value)) : undefined,
1049
- onError
1216
+ onError,
1217
+ forwardMPCClientEnabled: this.forwardMPCEnabled,
1218
+ traceContext
1050
1219
  });
1051
1220
  return data;
1052
1221
  }
1053
- async clientSign({ chainName, message, roomId, keyShare, derivationPath, isFormatted, dynamicRequestId }) {
1222
+ async forwardMPCClientSign({ chainName, message, roomId, keyShare, derivationPath, formattedMessage, dynamicRequestId, isFormatted, traceContext }) {
1223
+ try {
1224
+ if (!this.apiClient.forwardMPCClient.connected) {
1225
+ await this.initializeForwardMPCClient();
1226
+ }
1227
+ const messageForForwardMPC = serializeMessageForForwardMPC({
1228
+ message,
1229
+ isFormatted,
1230
+ chainName
1231
+ });
1232
+ this.logger.info('Forward MPC enabled, signing message with forward MPC', this.getTraceContext(traceContext));
1233
+ const signature = await this.apiClient.forwardMPCClient.signMessage({
1234
+ keyshare: keyShare,
1235
+ message: chainName === 'SVM' ? formattedMessage : messageForForwardMPC,
1236
+ relayDomain: this.baseMPCRelayApiUrl || '',
1237
+ signingAlgo: chainName === 'EVM' ? 'ECDSA' : 'ED25519',
1238
+ hashAlgo: chainName === 'EVM' && !isFormatted ? 'keccak256' : undefined,
1239
+ derivationPath: derivationPath,
1240
+ roomUuid: roomId
1241
+ });
1242
+ const signatureBytes = signature.data.signature;
1243
+ if (!(signatureBytes instanceof Uint8Array)) {
1244
+ throw new TypeError(`Invalid signature format: expected Uint8Array, got ${typeof signatureBytes}`);
1245
+ }
1246
+ // Convert to EcdsaSignature
1247
+ if (chainName === 'EVM') {
1248
+ const ecdsaSignature = EcdsaSignature.fromBuffer(signatureBytes);
1249
+ return ecdsaSignature;
1250
+ } else {
1251
+ return signatureBytes;
1252
+ }
1253
+ } catch (error) {
1254
+ this.logger.error('Error signing message with forward MPC client', {
1255
+ error,
1256
+ environmentId: this.environmentId,
1257
+ userId: this.userId,
1258
+ dynamicRequestId
1259
+ });
1260
+ throw error;
1261
+ }
1262
+ }
1263
+ async clientSign({ chainName, message, roomId, keyShare, derivationPath, isFormatted, dynamicRequestId, traceContext }) {
1054
1264
  try {
1055
1265
  const mpcSigner = getMPCSigner({
1056
1266
  chainName,
1057
1267
  baseRelayUrl: this.baseMPCRelayApiUrl
1058
1268
  });
1059
1269
  const formattedMessage = isFormatted ? new MessageHash(message) : formatMessage(chainName, message);
1060
- this.logger.debug('[DynamicWaasWalletClient] Starting client sign', {
1270
+ this.logger.debug('[DynamicWaasWalletClient] Starting client sign', _extends({
1061
1271
  chainName,
1062
1272
  message,
1063
1273
  roomId,
1064
1274
  derivationPath,
1065
1275
  isFormatted
1066
- });
1276
+ }, this.getTraceContext(traceContext)));
1277
+ if (this.forwardMPCEnabled) {
1278
+ return this.forwardMPCClientSign({
1279
+ chainName,
1280
+ message,
1281
+ roomId,
1282
+ keyShare,
1283
+ derivationPath,
1284
+ formattedMessage,
1285
+ dynamicRequestId,
1286
+ isFormatted,
1287
+ traceContext
1288
+ });
1289
+ }
1067
1290
  const signature = await mpcSigner.sign(roomId, keyShare, formattedMessage, derivationPath);
1068
1291
  return signature;
1069
1292
  } catch (error) {
1070
1293
  logError({
1071
1294
  message: 'Error in clientSign',
1072
1295
  error: error,
1073
- context: {
1296
+ context: _extends({
1074
1297
  chainName,
1075
1298
  roomId,
1076
1299
  derivationPath,
1077
1300
  isFormatted,
1078
1301
  dynamicRequestId
1079
- }
1302
+ }, this.getTraceContext(traceContext))
1080
1303
  });
1081
1304
  throw error;
1082
1305
  }
1083
1306
  }
1084
1307
  //todo: need to modify with imported flag
1085
- async sign({ accountAddress, message, chainName, password = undefined, isFormatted = false, signedSessionId, mfaToken, context, onError }) {
1308
+ async sign({ accountAddress, message, chainName, password = undefined, isFormatted = false, signedSessionId, mfaToken, context, onError, traceContext }) {
1086
1309
  const dynamicRequestId = v4();
1087
1310
  try {
1088
1311
  await this.verifyPassword({
@@ -1097,23 +1320,41 @@ class DynamicWalletClient {
1097
1320
  walletOperation: WalletOperation.SIGN_MESSAGE,
1098
1321
  signedSessionId
1099
1322
  });
1100
- // Perform the server sign
1101
- const data = await this.serverSign({
1323
+ const room = await this.getRoom();
1324
+ let roomId = room == null ? void 0 : room.roomId;
1325
+ if (roomId) {
1326
+ this.instrument('[DynamicWaasWalletClient] Using cached room', _extends({
1327
+ key: 'sign',
1328
+ operation: 'cachedRoomFound',
1329
+ accountAddress,
1330
+ chainName,
1331
+ walletId: wallet.walletId,
1332
+ roomId
1333
+ }, this.getTraceContext(traceContext)));
1334
+ }
1335
+ // Perform the server sign - always call, but only await if roomId is not provided
1336
+ const serverSignPromise = this.serverSign({
1102
1337
  walletId: wallet.walletId,
1103
1338
  message,
1104
1339
  isFormatted,
1105
1340
  mfaToken,
1341
+ roomId,
1106
1342
  context,
1107
1343
  onError,
1108
- dynamicRequestId
1344
+ dynamicRequestId,
1345
+ traceContext
1109
1346
  });
1110
- this.logger.debug('[DynamicWaasWalletClient] Server sign completed', {
1347
+ // Only await if roomId is not provided
1348
+ roomId = roomId != null ? roomId : (await serverSignPromise).roomId;
1349
+ this.instrument('[DynamicWaasWalletClient] Server sign completed', _extends({
1111
1350
  message,
1112
1351
  accountAddress,
1113
1352
  walletId: wallet.walletId,
1114
- roomId: data.roomId,
1115
- dynamicRequestId
1116
- });
1353
+ roomId,
1354
+ dynamicRequestId,
1355
+ key: 'sign',
1356
+ operation: 'serverSign'
1357
+ }, this.getTraceContext(traceContext)));
1117
1358
  const derivationPath = wallet.derivationPath && wallet.derivationPath != '' ? new Uint32Array(Object.values(JSON.parse(wallet.derivationPath))) : undefined;
1118
1359
  // Perform the client sign and return the signature
1119
1360
  const clientKeyShares = await this.getClientKeySharesFromLocalStorage({
@@ -1122,35 +1363,39 @@ class DynamicWalletClient {
1122
1363
  const signature = await this.clientSign({
1123
1364
  chainName,
1124
1365
  message,
1125
- roomId: data.roomId,
1366
+ roomId,
1126
1367
  keyShare: clientKeyShares[0],
1127
1368
  derivationPath,
1128
1369
  isFormatted,
1129
- dynamicRequestId
1370
+ dynamicRequestId,
1371
+ traceContext
1130
1372
  });
1131
- this.logger.debug('[DynamicWaasWalletClient] Client sign completed', {
1373
+ this.instrument('[DynamicWaasWalletClient] Client sign completed', _extends({
1374
+ accountAddress,
1132
1375
  chainName,
1133
1376
  message,
1134
- roomId: data.roomId,
1377
+ roomId,
1135
1378
  derivationPath,
1136
- isFormatted
1137
- });
1379
+ isFormatted,
1380
+ key: 'sign',
1381
+ operation: 'clientSign'
1382
+ }, this.getTraceContext(traceContext)));
1138
1383
  return signature;
1139
1384
  } catch (error) {
1140
1385
  logError({
1141
1386
  message: 'Error in sign',
1142
1387
  error: error,
1143
- context: {
1388
+ context: _extends({
1144
1389
  accountAddress,
1145
1390
  chainName,
1146
1391
  isFormatted: isFormatted ? 'true' : 'false',
1147
1392
  dynamicRequestId
1148
- }
1393
+ }, this.getTraceContext(traceContext))
1149
1394
  });
1150
1395
  throw error;
1151
1396
  }
1152
1397
  }
1153
- async refreshWalletAccountShares({ accountAddress, chainName, password = undefined, signedSessionId, mfaToken }) {
1398
+ async refreshWalletAccountShares({ accountAddress, chainName, password = undefined, signedSessionId, mfaToken, traceContext }) {
1154
1399
  const dynamicRequestId = v4();
1155
1400
  try {
1156
1401
  await this.verifyPassword({
@@ -1194,11 +1439,11 @@ class DynamicWalletClient {
1194
1439
  logError({
1195
1440
  message: 'Error in refreshWalletAccountShares',
1196
1441
  error: error,
1197
- context: {
1442
+ context: _extends({
1198
1443
  accountAddress,
1199
1444
  chainName,
1200
1445
  dynamicRequestId
1201
- }
1446
+ }, this.getTraceContext(traceContext))
1202
1447
  });
1203
1448
  throw error;
1204
1449
  }
@@ -1254,7 +1499,7 @@ class DynamicWalletClient {
1254
1499
  existingClientKeyShares
1255
1500
  };
1256
1501
  }
1257
- async reshare({ chainName, accountAddress, oldThresholdSignatureScheme, newThresholdSignatureScheme, password = undefined, signedSessionId, backupToGoogleDrive = false, delegateToProjectEnvironment = false, mfaToken }) {
1502
+ async reshare({ chainName, accountAddress, oldThresholdSignatureScheme, newThresholdSignatureScheme, password = undefined, signedSessionId, backupToGoogleDrive = false, delegateToProjectEnvironment = false, mfaToken, revokeDelegation = false }) {
1258
1503
  const dynamicRequestId = v4();
1259
1504
  try {
1260
1505
  await this.verifyPassword({
@@ -1293,7 +1538,8 @@ class DynamicWalletClient {
1293
1538
  newThresholdSignatureScheme,
1294
1539
  dynamicRequestId,
1295
1540
  delegateToProjectEnvironment,
1296
- mfaToken
1541
+ mfaToken,
1542
+ revokeDelegation
1297
1543
  });
1298
1544
  const { roomId, serverKeygenIds, newServerKeygenIds = [] } = data;
1299
1545
  // Get the MPC config for the threshold signature scheme
@@ -1308,35 +1554,68 @@ class DynamicWalletClient {
1308
1554
  chainName,
1309
1555
  baseRelayUrl: this.baseMPCRelayApiUrl
1310
1556
  });
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))
1557
+ const existingResharePromises = existingClientKeyShares.map((keyShare)=>mpcSigner.reshareRemainingParty(roomId, newMpcConfig.threshold, keyShare, allPartyKeygenIds));
1558
+ const newResharePromises = newClientInitKeygenResults.map((keygenResult)=>mpcSigner.reshareNewParty(roomId, oldMpcConfig.threshold, newMpcConfig.threshold, keygenResult, allPartyKeygenIds));
1559
+ // Run both share parties in parallel by group
1560
+ const [existingReshareResults, newReshareResults] = await Promise.all([
1561
+ Promise.all(existingResharePromises),
1562
+ Promise.all(newResharePromises)
1314
1563
  ]);
1564
+ const clientKeysharesToLocalStorage = delegateToProjectEnvironment ? [
1565
+ ...existingReshareResults
1566
+ ] : [
1567
+ ...existingReshareResults,
1568
+ ...newReshareResults
1569
+ ];
1315
1570
  this.walletMap[accountAddress] = _extends({}, this.walletMap[accountAddress], {
1316
1571
  thresholdSignatureScheme: newThresholdSignatureScheme
1317
1572
  });
1573
+ // store client key shares to localStorage
1318
1574
  await this.setClientKeySharesToLocalStorage({
1319
1575
  accountAddress,
1320
- clientKeyShares: reshareResults,
1576
+ clientKeyShares: clientKeysharesToLocalStorage,
1321
1577
  overwriteOrMerge: 'overwrite'
1322
1578
  });
1323
- if (delegateToProjectEnvironment) {
1324
- await this.storeEncryptedBackupByDelegatedWallet({
1325
- accountAddress,
1326
- password,
1327
- signedSessionId
1579
+ const allClientShares = [
1580
+ ...existingReshareResults,
1581
+ ...newReshareResults
1582
+ ];
1583
+ let distribution;
1584
+ if (delegateToProjectEnvironment && backupToGoogleDrive) {
1585
+ // Delegation + Google Drive: Client's existing share backs up to both Dynamic and Google Drive.
1586
+ // The new share goes to the webhook for delegation.
1587
+ distribution = createDelegationWithGoogleDriveDistribution({
1588
+ existingShares: existingReshareResults,
1589
+ delegatedShare: newReshareResults[0]
1590
+ });
1591
+ } else if (delegateToProjectEnvironment) {
1592
+ // Delegation only: Client's existing share backs up to Dynamic.
1593
+ // The new share goes to the webhook for delegation. No Google Drive backup.
1594
+ distribution = createDelegationOnlyDistribution({
1595
+ existingShares: existingReshareResults,
1596
+ delegatedShare: newReshareResults[0]
1597
+ });
1598
+ } else if (backupToGoogleDrive) {
1599
+ // Google Drive only: Split shares between Dynamic (N-1) and Google Drive (1).
1600
+ // The last share (new share) goes to Google Drive.
1601
+ distribution = createGoogleDriveOnlyDistribution({
1602
+ allShares: allClientShares
1328
1603
  });
1329
1604
  } else {
1330
- await this.storeEncryptedBackupByWallet({
1331
- accountAddress,
1332
- password,
1333
- signedSessionId,
1334
- backupToGoogleDrive
1605
+ // No delegation, no Google Drive: All shares go to Dynamic backend only.
1606
+ distribution = createDynamicOnlyDistribution({
1607
+ allShares: allClientShares
1335
1608
  });
1336
1609
  }
1610
+ await this.backupSharesWithDistribution({
1611
+ accountAddress,
1612
+ password,
1613
+ signedSessionId,
1614
+ distribution
1615
+ });
1337
1616
  } catch (error) {
1338
1617
  logError({
1339
- message: 'Error in reshare',
1618
+ message: 'Error in reshare, resetting wallet to previous state',
1340
1619
  error: error,
1341
1620
  context: {
1342
1621
  accountAddress,
@@ -1347,52 +1626,25 @@ class DynamicWalletClient {
1347
1626
  dynamicRequestId
1348
1627
  }
1349
1628
  });
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
- })
1629
+ // reset user wallet when reshare fails, this would allow the client to recover wallets from an active state
1630
+ this.walletMap[accountAddress] = _extends({}, this.walletMap[accountAddress], {
1631
+ thresholdSignatureScheme: oldThresholdSignatureScheme
1369
1632
  });
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
- }
1633
+ await this.setClientKeySharesToLocalStorage({
1634
+ accountAddress,
1635
+ clientKeyShares: [],
1636
+ overwriteOrMerge: 'overwrite'
1380
1637
  });
1381
1638
  throw error;
1382
1639
  }
1383
1640
  }
1384
- async delegateKeyShares({ accountAddress, password = undefined, signedSessionId, mfaToken }) {
1641
+ async performDelegationOperation({ accountAddress, password, signedSessionId, mfaToken, newThresholdSignatureScheme, revokeDelegation = false, operationName }) {
1385
1642
  try {
1643
+ var _this_walletMap_accountAddress;
1386
1644
  const delegateToProjectEnvironment = this.featureFlags && this.featureFlags[FEATURE_FLAGS.ENABLE_DELEGATED_KEY_SHARES_FLAG] === true;
1387
1645
  if (!delegateToProjectEnvironment) {
1388
1646
  throw new Error('Delegation is not allowed for this project environment');
1389
1647
  }
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
1648
  const wallet = await this.getWallet({
1397
1649
  accountAddress,
1398
1650
  walletOperation: WalletOperation.REACH_ALL_PARTIES,
@@ -1403,28 +1655,21 @@ class DynamicWalletClient {
1403
1655
  throw new Error('Delegation is not allowed for SUI');
1404
1656
  }
1405
1657
  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
- }
1658
+ await this.reshare({
1659
+ chainName: this.walletMap[accountAddress].chainName,
1660
+ accountAddress,
1661
+ oldThresholdSignatureScheme: currentThresholdSignatureScheme,
1662
+ newThresholdSignatureScheme,
1663
+ password,
1664
+ signedSessionId,
1665
+ backupToGoogleDrive: hasGoogleDriveBackup((_this_walletMap_accountAddress = this.walletMap[accountAddress]) == null ? void 0 : _this_walletMap_accountAddress.clientKeySharesBackupInfo),
1666
+ delegateToProjectEnvironment: true,
1667
+ mfaToken,
1668
+ revokeDelegation
1669
+ });
1425
1670
  } catch (error) {
1426
1671
  logError({
1427
- message: 'Error in delegateKeyShares',
1672
+ message: `Error in ${operationName}`,
1428
1673
  error: error,
1429
1674
  context: {
1430
1675
  accountAddress
@@ -1433,7 +1678,31 @@ class DynamicWalletClient {
1433
1678
  throw error;
1434
1679
  }
1435
1680
  }
1436
- async exportKey({ accountAddress, chainName, password = undefined, signedSessionId, mfaToken }) {
1681
+ async delegateKeyShares({ accountAddress, password = undefined, signedSessionId, mfaToken }) {
1682
+ await this.performDelegationOperation({
1683
+ accountAddress,
1684
+ password,
1685
+ signedSessionId,
1686
+ mfaToken,
1687
+ newThresholdSignatureScheme: ThresholdSignatureScheme.TWO_OF_THREE,
1688
+ operationName: 'delegateKeyShares'
1689
+ });
1690
+ const backupInfo = this.walletMap[accountAddress].clientKeySharesBackupInfo;
1691
+ const delegatedKeyShares = backupInfo.backups[BackupLocation.DELEGATED] || [];
1692
+ return delegatedKeyShares;
1693
+ }
1694
+ async revokeDelegation({ accountAddress, password = undefined, signedSessionId, mfaToken }) {
1695
+ await this.performDelegationOperation({
1696
+ accountAddress,
1697
+ password,
1698
+ signedSessionId,
1699
+ mfaToken,
1700
+ newThresholdSignatureScheme: ThresholdSignatureScheme.TWO_OF_TWO,
1701
+ revokeDelegation: true,
1702
+ operationName: 'revokeDelegation'
1703
+ });
1704
+ }
1705
+ async exportKey({ accountAddress, chainName, password = undefined, signedSessionId, mfaToken, traceContext }) {
1437
1706
  const dynamicRequestId = v4();
1438
1707
  try {
1439
1708
  const wallet = await this.getWallet({
@@ -1459,24 +1728,24 @@ class DynamicWalletClient {
1459
1728
  dynamicRequestId,
1460
1729
  mfaToken
1461
1730
  });
1462
- this.logger.debug('[DynamicWaasWalletClient] Starting export of private key', {
1731
+ this.logger.debug('[DynamicWaasWalletClient] Starting export of private key', _extends({
1463
1732
  accountAddress,
1464
1733
  chainName,
1465
1734
  walletId: wallet.walletId,
1466
1735
  exportId,
1467
1736
  roomId: data.roomId
1468
- });
1737
+ }, this.getTraceContext(traceContext)));
1469
1738
  const keyExportRaw = await mpcSigner.exportFullPrivateKey(data.roomId, clientKeyShares[0], exportId);
1470
1739
  if (!keyExportRaw) {
1471
1740
  throw new Error('Error exporting private key');
1472
1741
  }
1473
- this.logger.debug('[DynamicWaasWalletClient] Completed export of private key', {
1742
+ this.logger.debug('[DynamicWaasWalletClient] Completed export of private key', _extends({
1474
1743
  accountAddress,
1475
1744
  chainName,
1476
1745
  walletId: wallet.walletId,
1477
1746
  exportId,
1478
1747
  roomId: data.roomId
1479
- });
1748
+ }, this.getTraceContext(traceContext)));
1480
1749
  const derivationPath = wallet.derivationPath && wallet.derivationPath != '' ? new Uint32Array(Object.values(JSON.parse(wallet.derivationPath))) : undefined;
1481
1750
  let derivedPrivateKey;
1482
1751
  if (mpcSigner instanceof Ecdsa) {
@@ -1493,11 +1762,11 @@ class DynamicWalletClient {
1493
1762
  logError({
1494
1763
  message: 'Error in exportKey',
1495
1764
  error: error,
1496
- context: {
1765
+ context: _extends({
1497
1766
  accountAddress,
1498
1767
  chainName,
1499
1768
  dynamicRequestId
1500
- }
1769
+ }, this.getTraceContext(traceContext))
1501
1770
  });
1502
1771
  throw error;
1503
1772
  }
@@ -1597,43 +1866,14 @@ class DynamicWalletClient {
1597
1866
  });
1598
1867
  await ((_this_storage = this.storage) == null ? void 0 : _this_storage.setItem(accountAddress, stringifiedClientKeyShares));
1599
1868
  }
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 }) {
1869
+ async backupSharesWithDistribution({ accountAddress, password, signedSessionId, distribution, preserveDelegatedLocation = false }) {
1627
1870
  const dynamicRequestId = v4();
1628
1871
  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
- });
1872
+ var _this_walletMap_accountAddress;
1633
1873
  if (!((_this_walletMap_accountAddress = this.walletMap[accountAddress]) == null ? void 0 : _this_walletMap_accountAddress.walletId)) {
1634
1874
  const error = new Error(`WalletId not found for accountAddress ${accountAddress}`);
1635
1875
  logError({
1636
- message: 'Error in storeEncryptedBackupByWallet, wallet or walletId not found from the wallet map',
1876
+ message: 'Error in backupSharesWithDistribution, wallet or walletId not found from the wallet map',
1637
1877
  error,
1638
1878
  context: {
1639
1879
  accountAddress,
@@ -1642,55 +1882,73 @@ class DynamicWalletClient {
1642
1882
  });
1643
1883
  throw error;
1644
1884
  }
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
- {
1885
+ const locations = [];
1886
+ if (distribution.dynamicBackendShares.length > 0) {
1887
+ const encryptedDynamicShares = await Promise.all(distribution.dynamicBackendShares.map((keyShare)=>this.encryptKeyShare({
1888
+ keyShare,
1889
+ password
1890
+ })));
1891
+ const data = await this.apiClient.storeEncryptedBackupByWallet({
1892
+ walletId: this.walletMap[accountAddress].walletId,
1893
+ encryptedKeyShares: encryptedDynamicShares,
1894
+ passwordEncrypted: Boolean(password) && password !== this.environmentId,
1895
+ encryptionVersion: ENCRYPTION_VERSION_CURRENT,
1896
+ signedSessionId,
1897
+ authMode: this.authMode,
1898
+ requiresSignedSessionId: this.requiresSignedSessionId(),
1899
+ dynamicRequestId
1900
+ });
1901
+ if (data.keyShareIds.length === 0) {
1902
+ throw new Error('No key shares were backed up to Dynamic backend');
1903
+ }
1904
+ locations.push({
1681
1905
  location: BackupLocation.DYNAMIC,
1682
1906
  externalKeyShareId: data.keyShareIds[0]
1683
- }
1684
- ];
1685
- if (googleDriveKeyShares.length > 0) {
1907
+ });
1908
+ }
1909
+ if (distribution.googleDriveShares.length > 0) {
1910
+ const encryptedGoogleDriveShares = await Promise.all(distribution.googleDriveShares.map((keyShare)=>this.encryptKeyShare({
1911
+ keyShare,
1912
+ password
1913
+ })));
1686
1914
  await this.uploadKeySharesToGoogleDrive({
1687
1915
  accountAddress,
1688
- encryptedKeyShares: googleDriveKeyShares
1916
+ encryptedKeyShares: encryptedGoogleDriveShares
1689
1917
  });
1690
1918
  locations.push({
1691
1919
  location: BackupLocation.GOOGLE_DRIVE
1692
1920
  });
1693
1921
  }
1922
+ if (distribution.delegatedShare) {
1923
+ var _publicKey_key, _publicKey_key1, _publicKey_key2;
1924
+ const publicKey = await this.apiClient.getDelegatedEncryptionKey({
1925
+ environmentId: this.environmentId
1926
+ });
1927
+ if (!(publicKey == null ? void 0 : (_publicKey_key = publicKey.key) == null ? void 0 : _publicKey_key.publicKeyPemB64)) {
1928
+ throw new Error('Public key not found');
1929
+ }
1930
+ var _publicKey_key_keyId;
1931
+ 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);
1932
+ const { status } = await this.apiClient.publishDelegatedKeyShare({
1933
+ walletId: this.walletMap[accountAddress].walletId,
1934
+ encryptedKeyShare: encryptedDelegatedKeyShareEnvelope,
1935
+ signedSessionId,
1936
+ requiresSignedSessionId: this.requiresSignedSessionId(),
1937
+ dynamicRequestId
1938
+ });
1939
+ if (status !== 200) {
1940
+ throw new Error('Failed to publish delegated key share');
1941
+ }
1942
+ locations.push({
1943
+ location: BackupLocation.DELEGATED
1944
+ });
1945
+ }
1946
+ // Preserve existing delegated location without re-publishing
1947
+ if (preserveDelegatedLocation && !distribution.delegatedShare) {
1948
+ locations.push({
1949
+ location: BackupLocation.DELEGATED
1950
+ });
1951
+ }
1694
1952
  const backupData = await this.apiClient.markKeySharesAsBackedUp({
1695
1953
  walletId: this.walletMap[accountAddress].walletId,
1696
1954
  locations,
@@ -1712,10 +1970,10 @@ class DynamicWalletClient {
1712
1970
  clientKeySharesBackupInfo: updatedBackupInfo
1713
1971
  });
1714
1972
  await this.storage.setItem(this.storageKey, JSON.stringify(this.walletMap));
1715
- return data;
1973
+ return backupData;
1716
1974
  } catch (error) {
1717
1975
  logError({
1718
- message: 'Error in storeEncryptedBackupByWallet',
1976
+ message: 'Error in backupSharesWithDistribution',
1719
1977
  error: error,
1720
1978
  context: {
1721
1979
  accountAddress,
@@ -1725,109 +1983,87 @@ class DynamicWalletClient {
1725
1983
  throw error;
1726
1984
  }
1727
1985
  }
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
1986
+ /**
1987
+ * Central backup orchestrator that encrypts and stores wallet key shares.
1988
+ *
1989
+ * This method serves as the main backup coordinator, handling the distribution of encrypted
1990
+ * key shares between Dynamic's backend and Google Drive based on the wallet's threshold scheme.
1991
+ * It is used by multiple operations including reshare, refresh, and manual backup requests.
1992
+ *
1993
+ * **Backup Distribution Strategy:**
1994
+ * - **Single share wallets**: All shares stored on Dynamic's backend only
1995
+ * - **Multi-share wallets (2+)**: When backing up to Google Drive, N-1 shares on Dynamic's backend, 1 share on Google Drive
1996
+ * - **Multi-share wallets (2+)**: When not backing up to Google Drive, all shares on Dynamic's backend
1997
+ *
1998
+ * **Process Flow:**
1999
+ * 1. Encrypts all client key shares with the provided password (or environment ID if no password)
2000
+ * 2. For multi-share wallets (2+): conditionally distributes N-1 to backend, 1 to Google Drive
2001
+ * 3. For other configurations: stores all shares on Dynamic's backend
2002
+ * 4. Updates backup metadata and synchronizes wallet state
2003
+ * 5. Persists the updated wallet map to local storage
2004
+ *
2005
+ * **Delegated Key Shares:**
2006
+ * - 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
2007
+ * - and encrypt the delegated key share to publish it to the webhook
2008
+ *
2009
+ * @param params - The backup operation parameters
2010
+ * @param params.accountAddress - The account address of the wallet to backup
2011
+ * @param params.clientKeyShares - Optional specific key shares to backup (uses localStorage if not provided)
2012
+ * @param params.password - Optional password for encryption (uses environment ID if not provided)
2013
+ * @param params.signedSessionId - Optional signed session ID for authentication
2014
+ * @param params.backupToGoogleDrive - Whether to backup to Google Drive (defaults to false)
2015
+ * @returns Promise with backup metadata including share locations and IDs
2016
+ */ async storeEncryptedBackupByWallet({ accountAddress, clientKeyShares = undefined, password = undefined, signedSessionId, backupToGoogleDrive = false, delegatedKeyshare = undefined }) {
2017
+ var _this_walletMap_accountAddress, _this_walletMap_accountAddress1;
2018
+ const keySharesToBackup = clientKeyShares != null ? clientKeyShares : await this.getClientKeySharesFromLocalStorage({
2019
+ accountAddress
2020
+ });
2021
+ const shouldBackupToGoogleDrive = backupToGoogleDrive || hasGoogleDriveBackup((_this_walletMap_accountAddress = this.walletMap[accountAddress]) == null ? void 0 : _this_walletMap_accountAddress.clientKeySharesBackupInfo);
2022
+ const hasExistingDelegation = hasDelegatedBackup((_this_walletMap_accountAddress1 = this.walletMap[accountAddress]) == null ? void 0 : _this_walletMap_accountAddress1.clientKeySharesBackupInfo);
2023
+ let distribution;
2024
+ let preserveDelegatedLocation = false;
2025
+ if (delegatedKeyshare && shouldBackupToGoogleDrive) {
2026
+ // NEW delegation + Google Drive: Client's shares back up to both Dynamic and Google Drive.
2027
+ // The delegated share goes to the webhook.
2028
+ distribution = createDelegationWithGoogleDriveDistribution({
2029
+ existingShares: keySharesToBackup,
2030
+ delegatedShare: delegatedKeyshare
2031
+ });
2032
+ } else if (delegatedKeyshare) {
2033
+ // NEW delegation only: Client's shares back up to Dynamic.
2034
+ // The delegated share goes to the webhook. No Google Drive backup.
2035
+ distribution = createDelegationOnlyDistribution({
2036
+ existingShares: keySharesToBackup,
2037
+ delegatedShare: delegatedKeyshare
2038
+ });
2039
+ } else if (hasExistingDelegation && shouldBackupToGoogleDrive) {
2040
+ // ADD Google Drive to EXISTING delegation: Client's share backs up to both Dynamic and GD.
2041
+ // Don't re-publish delegated share, just preserve the location.
2042
+ distribution = createAddGoogleDriveToExistingDelegationDistribution({
2043
+ clientShares: keySharesToBackup
2044
+ });
2045
+ preserveDelegatedLocation = true;
2046
+ } else if (shouldBackupToGoogleDrive && keySharesToBackup.length >= 2) {
2047
+ // Google Drive only (no delegation): Split shares between Dynamic (N-1) and Google Drive (1).
2048
+ distribution = createGoogleDriveOnlyDistribution({
2049
+ allShares: keySharesToBackup
1817
2050
  });
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
- }
2051
+ } else {
2052
+ // No delegation, no Google Drive: All shares go to Dynamic backend only.
2053
+ distribution = createDynamicOnlyDistribution({
2054
+ allShares: keySharesToBackup
1828
2055
  });
1829
- throw error;
1830
2056
  }
2057
+ const backupData = await this.backupSharesWithDistribution({
2058
+ accountAddress,
2059
+ password,
2060
+ signedSessionId,
2061
+ distribution,
2062
+ preserveDelegatedLocation
2063
+ });
2064
+ return _extends({}, backupData, {
2065
+ keyShareIds: backupData.locationsWithKeyShares.map((ks)=>ks.keyShareId)
2066
+ });
1831
2067
  }
1832
2068
  async storeEncryptedBackupByWalletWithRetry({ accountAddress, clientKeyShares, password, signedSessionId }) {
1833
2069
  await retryPromise(()=>this.storeEncryptedBackupByWallet({
@@ -2018,14 +2254,18 @@ class DynamicWalletClient {
2018
2254
  return (_ks_externalKeyShareId = ks.externalKeyShareId) != null ? _ks_externalKeyShareId : '';
2019
2255
  });
2020
2256
  } else {
2021
- // Already 2-of-3, only call backup
2022
- const data = await this.storeEncryptedBackupByWallet({
2257
+ await this.storeEncryptedBackupByWallet({
2023
2258
  accountAddress,
2024
2259
  password,
2025
2260
  signedSessionId,
2026
2261
  backupToGoogleDrive: true
2027
2262
  });
2028
- return data.keyShareIds;
2263
+ const backupInfo = this.walletMap[accountAddress].clientKeySharesBackupInfo;
2264
+ const googleDriveShares = backupInfo.backups[BackupLocation.GOOGLE_DRIVE] || [];
2265
+ return googleDriveShares.map((ks)=>{
2266
+ var _ks_externalKeyShareId;
2267
+ return (_ks_externalKeyShareId = ks.externalKeyShareId) != null ? _ks_externalKeyShareId : '';
2268
+ });
2029
2269
  }
2030
2270
  } catch (error) {
2031
2271
  logError({
@@ -2061,7 +2301,8 @@ class DynamicWalletClient {
2061
2301
  const thresholdSignatureScheme = this.walletMap[accountAddress].thresholdSignatureScheme;
2062
2302
  const fileName = getClientKeyShareExportFileName({
2063
2303
  thresholdSignatureScheme,
2064
- accountAddress
2304
+ accountAddress,
2305
+ isGoogleDrive: true
2065
2306
  });
2066
2307
  const backupData = createBackupData({
2067
2308
  encryptedKeyShares,
@@ -2086,7 +2327,7 @@ class DynamicWalletClient {
2086
2327
  throw error;
2087
2328
  }
2088
2329
  }
2089
- async restoreBackupFromGoogleDrive({ accountAddress, password, signedSessionId }) {
2330
+ async exportClientKeysharesFromGoogleDrive({ accountAddress, password, signedSessionId }) {
2090
2331
  try {
2091
2332
  await this.getWallet({
2092
2333
  accountAddress,
@@ -2097,15 +2338,16 @@ class DynamicWalletClient {
2097
2338
  oauthAccountId
2098
2339
  });
2099
2340
  const thresholdSignatureScheme = this.walletMap[accountAddress].thresholdSignatureScheme;
2100
- const fileName = getClientKeyShareExportFileName({
2341
+ const backupFileName = getClientKeyShareExportFileName({
2101
2342
  thresholdSignatureScheme,
2102
- accountAddress
2343
+ accountAddress,
2344
+ isGoogleDrive: true
2103
2345
  });
2104
2346
  let backupData = null;
2105
2347
  try {
2106
2348
  backupData = await retryPromise(()=>downloadFileFromGoogleDrive({
2107
2349
  accessToken,
2108
- fileName
2350
+ fileName: backupFileName
2109
2351
  }));
2110
2352
  } catch (error) {
2111
2353
  logError({
@@ -2113,7 +2355,7 @@ class DynamicWalletClient {
2113
2355
  error: error,
2114
2356
  context: {
2115
2357
  accountAddress,
2116
- fileName
2358
+ fileName: backupFileName
2117
2359
  }
2118
2360
  });
2119
2361
  throw error;
@@ -2125,7 +2367,7 @@ class DynamicWalletClient {
2125
2367
  error: new Error('No backup file found'),
2126
2368
  context: {
2127
2369
  accountAddress,
2128
- fileName
2370
+ fileName: backupFileName
2129
2371
  }
2130
2372
  });
2131
2373
  throw error;
@@ -2138,7 +2380,7 @@ class DynamicWalletClient {
2138
2380
  error,
2139
2381
  context: {
2140
2382
  accountAddress,
2141
- fileName
2383
+ fileName: backupFileName
2142
2384
  }
2143
2385
  });
2144
2386
  throw error;
@@ -2148,15 +2390,20 @@ class DynamicWalletClient {
2148
2390
  keyShare,
2149
2391
  password
2150
2392
  })));
2151
- await this.setClientKeySharesToLocalStorage({
2393
+ const text = JSON.stringify(decryptedKeyShares, null, 2);
2394
+ const exportFileName = getClientKeyShareExportFileName({
2395
+ thresholdSignatureScheme,
2152
2396
  accountAddress,
2153
- clientKeyShares: decryptedKeyShares,
2154
- overwriteOrMerge: 'merge'
2397
+ isGoogleDrive: true
2398
+ });
2399
+ downloadStringAsFile({
2400
+ filename: exportFileName,
2401
+ content: text,
2402
+ mimeType: 'application/json'
2155
2403
  });
2156
- return decryptedKeyShares;
2157
2404
  } catch (error) {
2158
2405
  logError({
2159
- message: 'Error in restoreBackupFromGoogleDrive',
2406
+ message: 'Error in exportClientKeysharesFromGoogleDrive',
2160
2407
  error: error,
2161
2408
  context: {
2162
2409
  accountAddress
@@ -2183,16 +2430,11 @@ class DynamicWalletClient {
2183
2430
  keyShares: clientKeyShares,
2184
2431
  derivationPath
2185
2432
  });
2186
- const blob = new Blob([
2187
- text
2188
- ], {
2189
- type: 'text/plain'
2433
+ downloadStringAsFile({
2434
+ filename: `${CLIENT_KEYSHARE_EXPORT_FILENAME_PREFIX}-${accountAddress}.txt`,
2435
+ content: text,
2436
+ mimeType: 'text/plain'
2190
2437
  });
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
2438
  }
2197
2439
  async getClientKeyShares({ accountAddress, password, signedSessionId }) {
2198
2440
  await this.getWallet({
@@ -2492,29 +2734,115 @@ class DynamicWalletClient {
2492
2734
  }
2493
2735
  this.apiClient.syncAuthToken(authToken);
2494
2736
  }
2737
+ async createRooms({ roomType, thresholdSignatureScheme, roomCount = 5 }) {
2738
+ try {
2739
+ var _rooms_roomIds;
2740
+ if (DynamicWalletClient.roomsInitializing) {
2741
+ return;
2742
+ }
2743
+ DynamicWalletClient.roomsInitializing = true;
2744
+ const restoredRooms = await this.restoreRooms();
2745
+ if (restoredRooms && restoredRooms.length > 0) {
2746
+ return;
2747
+ }
2748
+ const rooms = await this.apiClient.createRoomsWithoutWalletId({
2749
+ roomType,
2750
+ roomCount,
2751
+ thresholdSignatureScheme
2752
+ });
2753
+ const newRooms = rooms == null ? void 0 : (_rooms_roomIds = rooms.roomIds) == null ? void 0 : _rooms_roomIds.map((roomId)=>({
2754
+ roomId,
2755
+ createdAt: Date.now(),
2756
+ expiresAt: Date.now() + 1000 * 60 * 10
2757
+ }));
2758
+ DynamicWalletClient.rooms = [
2759
+ ...newRooms,
2760
+ ...DynamicWalletClient.rooms
2761
+ ];
2762
+ DynamicWalletClient.roomsInitializing = false;
2763
+ await this.storage.setItem(`${this.storageKey}-rooms`, JSON.stringify(DynamicWalletClient.rooms));
2764
+ } catch (error) {
2765
+ this.logger.error('Error creating rooms', error);
2766
+ throw error;
2767
+ }
2768
+ }
2769
+ async restoreRooms() {
2770
+ const roomData = await this.storage.getItem(`${this.storageKey}-rooms`);
2771
+ if (!roomData) {
2772
+ return;
2773
+ }
2774
+ const parsedRoomData = JSON.parse(roomData);
2775
+ const now = Date.now();
2776
+ // Filter out expired rooms
2777
+ const rooms = parsedRoomData.filter((room)=>room.expiresAt > now);
2778
+ if (rooms.length === 0) {
2779
+ return;
2780
+ }
2781
+ DynamicWalletClient.rooms = rooms;
2782
+ return rooms;
2783
+ }
2784
+ async getRooms() {
2785
+ return DynamicWalletClient.rooms;
2786
+ }
2787
+ async getRoom() {
2788
+ const now = Date.now();
2789
+ // Filter out expired rooms
2790
+ DynamicWalletClient.rooms = DynamicWalletClient.rooms.filter((room)=>room.expiresAt > now);
2791
+ if (DynamicWalletClient.rooms.length === 0) {
2792
+ // create rooms if no valid rooms are cached,
2793
+ // dont await the creation of rooms, the room will be created on the server
2794
+ this.createRooms({
2795
+ roomType: RoomTypeEnum.Threshold,
2796
+ thresholdSignatureScheme: ThresholdSignatureScheme.TWO_OF_TWO,
2797
+ roomCount: ROOM_CACHE_COUNT
2798
+ });
2799
+ return;
2800
+ }
2801
+ // Return the first non-expired room (and remove it from the array)
2802
+ return DynamicWalletClient.rooms.shift();
2803
+ }
2804
+ /**
2805
+ * Helper method to instrument with automatic properties inclusion
2806
+ */ instrument(message, context) {
2807
+ const defaultContext = {
2808
+ environmentId: context.environmentId || this.environmentId
2809
+ };
2810
+ this.logger.debug(message, _extends({}, defaultContext, context));
2811
+ this.logger.instrument(message, _extends({}, defaultContext, context));
2812
+ }
2813
+ getTraceContext(traceContext) {
2814
+ const now = Date.now();
2815
+ return _extends({
2816
+ now,
2817
+ time: (traceContext == null ? void 0 : traceContext.startTime) ? now - traceContext.startTime : 0
2818
+ }, traceContext);
2819
+ }
2495
2820
  constructor({ environmentId, baseApiUrl, baseMPCRelayApiUrl, storageKey, debug, featureFlags, authMode = AuthMode.HEADER, authToken = undefined, // Represents the version of the client SDK used by developer
2496
- sdkVersion }){
2821
+ sdkVersion, forwardMPCClient, baseClientKeysharesRelayApiUrl }){
2497
2822
  this.userId = undefined;
2498
2823
  this.sessionId = undefined;
2499
- this.delegatedAccessEndpoint = undefined;
2500
2824
  this.initializePromise = null;
2501
2825
  this.logger = logger;
2502
2826
  this.walletMap = {} // todo: store in session storage
2503
2827
  ;
2504
2828
  this.memoryStorage = null;
2505
2829
  this.iframe = null;
2830
+ this.forwardMPCEnabled = false;
2506
2831
  this.featureFlags = {};
2507
2832
  this.environmentId = environmentId;
2508
2833
  this.storageKey = `${STORAGE_KEY}-${storageKey != null ? storageKey : environmentId}`;
2509
2834
  this.baseMPCRelayApiUrl = baseMPCRelayApiUrl;
2510
2835
  this.authMode = authMode;
2511
2836
  this.sdkVersion = sdkVersion;
2837
+ this.baseClientKeysharesRelayApiUrl = baseClientKeysharesRelayApiUrl;
2512
2838
  this.apiClient = new DynamicApiClient({
2513
2839
  environmentId,
2514
2840
  authToken,
2515
2841
  baseApiUrl,
2516
2842
  authMode,
2517
- sdkVersion
2843
+ sdkVersion,
2844
+ forwardMPCClient,
2845
+ baseClientKeysharesRelayApiUrl
2518
2846
  });
2519
2847
  this.debug = Boolean(debug);
2520
2848
  this.logger.setLogLevel(this.debug ? LogLevel.DEBUG : DEFAULT_LOG_LEVEL);
@@ -2534,8 +2862,15 @@ class DynamicWalletClient {
2534
2862
  if (authMode === AuthMode.HEADER && authToken) {
2535
2863
  this.initLoggerContext(authToken);
2536
2864
  }
2865
+ this.forwardMPCEnabled = this.featureFlags && this.featureFlags[FEATURE_FLAGS.ENABLE_FORWARD_MPC_CLIENT_FLAG] === true;
2866
+ // if forwardMPCEnabled is true and forwardMPCClient is not provided, initialize the forwardMPCClient
2867
+ if (this.forwardMPCEnabled && !forwardMPCClient) {
2868
+ this.initializeForwardMPCClient();
2869
+ }
2537
2870
  }
2538
2871
  }
2872
+ DynamicWalletClient.rooms = [];
2873
+ DynamicWalletClient.roomsInitializing = false;
2539
2874
 
2540
2875
  const ERROR_KEYGEN_FAILED = '[DynamicWaasWalletClient]: Error with keygen';
2541
2876
  const ERROR_CREATE_WALLET_ACCOUNT = '[DynamicWaasWalletClient]: Error creating wallet account';
@@ -2547,4 +2882,4 @@ const ERROR_VERIFY_TRANSACTION_SIGNATURE = '[DynamicWaasWalletClient]: Error ver
2547
2882
  const ERROR_EXPORT_PRIVATE_KEY = '[DynamicWaasWalletClient]: Error exporting private key';
2548
2883
  const ERROR_IMPORT_PRIVATE_KEY = '[DynamicWaasWalletClient]: Error importing private key';
2549
2884
 
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 };
2885
+ 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 };