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