@dynamic-labs-wallet/node 0.0.0-preview.114.4 → 0.0.1-paolo-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/index.cjs.js +790 -233
  2. package/index.esm.js +778 -195
  3. package/internal/core/bip340.cjs +1 -0
  4. package/internal/core/common.cjs +1 -0
  5. package/internal/core/ecdsa.cjs +1 -0
  6. package/internal/core/ecdsa.d.ts +1 -1
  7. package/internal/core/ed25519.cjs +1 -0
  8. package/internal/core/ed25519_exportable.cjs +1 -0
  9. package/internal/core/ed25519_exportable.d.ts +21 -0
  10. package/internal/core/index.cjs +1 -0
  11. package/internal/core/index.d.ts +4 -2
  12. package/internal/core/native.d.ts +65 -0
  13. package/internal/core/sr25519.cjs +1 -0
  14. package/internal/core/sr25519.d.ts +22 -0
  15. package/internal/core/types.cjs +1 -0
  16. package/internal/core/types.d.ts +20 -0
  17. package/internal/node/index.cjs +1 -0
  18. package/internal/node/index.d.ts +8 -2
  19. package/internal/node/native/libmpc_executor_linux_arm64_nodejs.node +0 -0
  20. package/internal/node/native/libmpc_executor_linux_x86_64_nodejs.node +0 -0
  21. package/internal/node/native/libmpc_executor_macos_arm64_nodejs.node +0 -0
  22. package/internal/node/native/libmpc_executor_macos_x86_64_nodejs.node +0 -0
  23. package/internal/node/native.cjs +1 -0
  24. package/internal/node/native.d.ts +1 -1
  25. package/package.json +13 -8
  26. package/src/backup/encryption.d.ts +42 -2
  27. package/src/backup/encryption.d.ts.map +1 -1
  28. package/src/backup/utils.d.ts +4 -0
  29. package/src/backup/utils.d.ts.map +1 -0
  30. package/src/client.d.ts +85 -30
  31. package/src/client.d.ts.map +1 -1
  32. package/src/core/createCore.d.ts +25 -0
  33. package/src/core/createCore.d.ts.map +1 -0
  34. package/src/core/types.d.ts +7 -0
  35. package/src/core/types.d.ts.map +1 -0
  36. package/src/delegatedClient/createDelegatedClient.d.ts +23 -0
  37. package/src/delegatedClient/createDelegatedClient.d.ts.map +1 -0
  38. package/src/delegatedClient/index.d.ts +7 -0
  39. package/src/delegatedClient/index.d.ts.map +1 -0
  40. package/src/delegatedClient/modules/decryptWebhookData.d.ts +18 -0
  41. package/src/delegatedClient/modules/decryptWebhookData.d.ts.map +1 -0
  42. package/src/delegatedClient/modules/revokeDelegation.d.ts +3 -0
  43. package/src/delegatedClient/modules/revokeDelegation.d.ts.map +1 -0
  44. package/src/delegatedClient/modules/sign.d.ts +31 -0
  45. package/src/delegatedClient/modules/sign.d.ts.map +1 -0
  46. package/src/index.d.ts +11 -5
  47. package/src/index.d.ts.map +1 -1
  48. package/src/mpc/index.d.ts +2 -2
  49. package/src/mpc/index.d.ts.map +1 -1
  50. package/src/mpc/mpc.d.ts +3 -4
  51. package/src/mpc/mpc.d.ts.map +1 -1
  52. package/src/mpc/types.d.ts +1 -2
  53. package/src/mpc/types.d.ts.map +1 -1
  54. package/src/types.d.ts +11 -2
  55. package/src/types.d.ts.map +1 -1
  56. package/src/utils.d.ts +5 -2
  57. package/src/utils.d.ts.map +1 -1
  58. package/internal/core/bip340.js +0 -1
  59. package/internal/core/common.js +0 -1
  60. package/internal/core/ecdsa.js +0 -1
  61. package/internal/core/ed25519.js +0 -1
  62. package/internal/core/index.js +0 -1
  63. package/internal/core/types.js +0 -1
  64. package/internal/node/index.js +0 -1
  65. package/internal/node/native.js +0 -1
  66. /package/internal/core/{native.js → native.cjs} +0 -0
package/index.esm.js CHANGED
@@ -1,16 +1,19 @@
1
- import { SigningAlgorithm, MPC_RELAY_PROD_API_URL, getMPCChainConfig, BackupLocation, DynamicApiClient, getClientThreshold, MPC_CONFIG, getTSSConfig, WalletOperation, getServerWalletReshareConfig } from '@dynamic-labs-wallet/core';
2
- export * from '@dynamic-labs-wallet/core';
3
- import { BIP340, Ed25519, Ecdsa, MessageHash, EcdsaKeygenResult, Ed25519KeygenResult, BIP340KeygenResult } from './internal/node';
4
- export { BIP340, BIP340InitKeygenResult, BIP340KeygenResult, Ecdsa, EcdsaInitKeygenResult, EcdsaKeygenResult, EcdsaPublicKey, EcdsaSignature, Ed25519, Ed25519InitKeygenResult, Ed25519KeygenResult, MessageHash } from './internal/node';
5
- import { Logger } from '@dynamic-labs/logger';
1
+ export * from '#internal/core';
2
+ import { SigningAlgorithm, MPC_RELAY_PROD_API_URL, getMPCChainConfig, BackupLocation, DynamicApiClient, getClientThreshold, MPC_CONFIG, getTSSConfig, serializeMessageForForwardMPC, getEnvironmentFromUrl, MPC_RELAY_URL_MAP, WalletOperation, getServerWalletReshareConfig } from '@dynamic-labs-wallet/core';
3
+ export { SOLANA_RPC_URL, ThresholdSignatureScheme, WalletOperation, getMPCChainConfig } from '@dynamic-labs-wallet/core';
4
+ import { BIP340, ExportableEd25519, Ecdsa, MessageHash, EcdsaSignature, EcdsaKeygenResult, ExportableEd25519KeygenResult, BIP340KeygenResult } from '#internal/node';
5
+ import { v4 } from 'uuid';
6
6
  import crypto from 'crypto';
7
+ import { Logger } from '@dynamic-labs/logger';
8
+ import 'node:crypto';
7
9
 
10
+ // Removed duplicate exports - these are already exported from #internal/core
8
11
  const getMPCSignatureScheme = ({ signingAlgorithm, baseRelayUrl = MPC_RELAY_PROD_API_URL })=>{
9
12
  switch(signingAlgorithm){
10
13
  case SigningAlgorithm.ECDSA:
11
14
  return new Ecdsa(baseRelayUrl);
12
15
  case SigningAlgorithm.ED25519:
13
- return new Ed25519(baseRelayUrl);
16
+ return new ExportableEd25519(baseRelayUrl);
14
17
  case SigningAlgorithm.BIP340:
15
18
  return new BIP340(baseRelayUrl);
16
19
  default:
@@ -65,7 +68,8 @@ const getExternalServerKeyShareBackupInfo = (params)=>{
65
68
  [BackupLocation.GOOGLE_DRIVE]: [],
66
69
  [BackupLocation.ICLOUD]: [],
67
70
  [BackupLocation.USER]: [],
68
- [BackupLocation.EXTERNAL]: []
71
+ [BackupLocation.EXTERNAL]: [],
72
+ [BackupLocation.DELEGATED]: []
69
73
  };
70
74
  if (!(params == null ? void 0 : (_params_walletProperties = params.walletProperties) == null ? void 0 : _params_walletProperties.keyShares)) {
71
75
  return {
@@ -75,7 +79,11 @@ const getExternalServerKeyShareBackupInfo = (params)=>{
75
79
  }
76
80
  params.walletProperties.keyShares.forEach((keyShare)=>{
77
81
  if (backups[keyShare.backupLocation]) {
78
- backups[keyShare.backupLocation].push(keyShare.id);
82
+ backups[keyShare.backupLocation].push({
83
+ location: keyShare.backupLocation,
84
+ keyShareId: keyShare.id,
85
+ passwordEncrypted: keyShare.passwordEncrypted
86
+ });
79
87
  }
80
88
  });
81
89
  const passwordEncrypted = Boolean((_params_walletProperties_keyShares_ = params.walletProperties.keyShares[0]) == null ? void 0 : _params_walletProperties_keyShares_.passwordEncrypted);
@@ -125,13 +133,64 @@ const getExternalServerKeyShareBackupInfo = (params)=>{
125
133
  // TypeScript needs this even though it's unreachable
126
134
  throw new Error('Unreachable code');
127
135
  }
136
+ const formatEvmMessage = (message)=>{
137
+ if (typeof message === 'string' && message.startsWith('0x')) {
138
+ const serializedTxBytes = Uint8Array.from(Buffer.from(message.slice(2), 'hex'));
139
+ return MessageHash.keccak256(serializedTxBytes);
140
+ }
141
+ return MessageHash.keccak256(message);
142
+ };
143
+ const formatSolanaMessage = (message)=>{
144
+ if (typeof message === 'string') {
145
+ if (!isHexString(message)) {
146
+ return Buffer.from(message).toString('hex');
147
+ } else {
148
+ return new Uint8Array(Buffer.from(message, 'hex'));
149
+ }
150
+ } else {
151
+ return message;
152
+ }
153
+ };
154
+ const formatMessage = (chainName, message)=>{
155
+ switch(chainName){
156
+ case 'EVM':
157
+ return formatEvmMessage(message);
158
+ case 'SVM':
159
+ return formatSolanaMessage(message);
160
+ case 'SUI':
161
+ return message;
162
+ default:
163
+ throw new Error('Unsupported chain name');
164
+ }
165
+ };
128
166
 
167
+ const ENCRYPTION_VERSION_LEGACY = 'v1';
168
+ const ENCRYPTION_VERSION_CURRENT = 'v2';
129
169
  const PBKDF2_ALGORITHM = 'PBKDF2';
130
- const PBKDF2_ITERATIONS = 100000;
131
170
  const PBKDF2_HASH_ALGORITHM = 'SHA-256';
132
171
  const AES_GCM_ALGORITHM = 'AES-GCM';
133
172
  const AES_GCM_LENGTH = 256;
134
- const getKey = async ({ password, salt })=>{
173
+ const ENCRYPTION_VERSIONS = {
174
+ [ENCRYPTION_VERSION_LEGACY]: {
175
+ version: ENCRYPTION_VERSION_LEGACY,
176
+ algorithm: AES_GCM_ALGORITHM,
177
+ keyDerivation: PBKDF2_ALGORITHM,
178
+ iterations: 100000,
179
+ hashAlgorithm: PBKDF2_HASH_ALGORITHM,
180
+ algorithmLength: AES_GCM_LENGTH
181
+ },
182
+ [ENCRYPTION_VERSION_CURRENT]: {
183
+ version: ENCRYPTION_VERSION_CURRENT,
184
+ algorithm: AES_GCM_ALGORITHM,
185
+ keyDerivation: PBKDF2_ALGORITHM,
186
+ iterations: 1000000,
187
+ hashAlgorithm: PBKDF2_HASH_ALGORITHM,
188
+ algorithmLength: AES_GCM_LENGTH
189
+ }
190
+ };
191
+ ENCRYPTION_VERSIONS[ENCRYPTION_VERSION_CURRENT].iterations;
192
+ ENCRYPTION_VERSIONS[ENCRYPTION_VERSION_LEGACY].iterations;
193
+ const getKey = async ({ password, salt, encryptionConfig })=>{
135
194
  const passwordBytes = stringToBytes(password);
136
195
  const initialKey = await crypto.subtle.importKey('raw', passwordBytes, {
137
196
  name: 'PBKDF2'
@@ -139,26 +198,34 @@ const getKey = async ({ password, salt })=>{
139
198
  'deriveKey'
140
199
  ]);
141
200
  return crypto.subtle.deriveKey({
142
- name: PBKDF2_ALGORITHM,
143
- salt,
144
- iterations: PBKDF2_ITERATIONS,
145
- hash: PBKDF2_HASH_ALGORITHM
201
+ name: encryptionConfig.keyDerivation,
202
+ salt: salt,
203
+ iterations: encryptionConfig.iterations,
204
+ hash: encryptionConfig.hashAlgorithm
146
205
  }, initialKey, {
147
- name: AES_GCM_ALGORITHM,
148
- length: AES_GCM_LENGTH
206
+ name: encryptionConfig.algorithm,
207
+ length: encryptionConfig.algorithmLength
149
208
  }, false, [
150
209
  'encrypt',
151
210
  'decrypt'
152
211
  ]);
153
212
  };
154
- const encryptData = async ({ data, password })=>{
213
+ /**
214
+ * Encrypts data using the specified encryption version.
215
+ * Always uses the latest encryption configuration for new encryptions by default.
216
+ */ const encryptData = async ({ data, password, version = ENCRYPTION_VERSION_CURRENT })=>{
217
+ const encryptionConfig = ENCRYPTION_VERSIONS[version];
218
+ if (!encryptionConfig) {
219
+ throw new Error(`Unsupported encryption version: ${version}`);
220
+ }
155
221
  try {
156
222
  // Generate a random salt and IV
157
223
  const salt = crypto.getRandomValues(new Uint8Array(16));
158
224
  const iv = crypto.getRandomValues(new Uint8Array(12)); // AES-GCM requires 12 bytes
159
225
  const key = await getKey({
160
226
  password,
161
- salt
227
+ salt,
228
+ encryptionConfig
162
229
  });
163
230
  // Convert the input string to bytes
164
231
  const dataBytes = new TextEncoder().encode(data);
@@ -171,25 +238,39 @@ const encryptData = async ({ data, password })=>{
171
238
  return {
172
239
  salt: bytesToBase64(salt),
173
240
  iv: bytesToBase64(iv),
174
- cipher: bytesToBase64(new Uint8Array(encryptedData))
241
+ cipher: bytesToBase64(new Uint8Array(encryptedData)),
242
+ version
175
243
  };
176
- } catch (error) {
244
+ } catch (e) {
177
245
  throw new Error('Error encrypting data');
178
246
  }
179
247
  };
180
- const decryptData = async ({ data, password })=>{
248
+ /**
249
+ * Decrypts data with version-based configuration.
250
+ * Uses the version field from the data to determine encryption parameters.
251
+ * Falls back to legacy version for backward compatibility if no version is specified.
252
+ */ const decryptData = async ({ data, password })=>{
253
+ const { salt, iv, cipher, version } = data;
254
+ // Ensure proper base64 padding for all values
255
+ const paddedSalt = ensureBase64Padding(salt);
256
+ const paddedIv = ensureBase64Padding(iv);
257
+ const paddedCipher = ensureBase64Padding(cipher);
258
+ const saltBytes = base64ToBytes(paddedSalt);
259
+ const ivBytes = base64ToBytes(paddedIv);
260
+ const cipherBytes = base64ToBytes(paddedCipher);
261
+ let encryptionConfig;
262
+ // Use version-based configuration if available, otherwise fallback to legacy
263
+ if (version && ENCRYPTION_VERSIONS[version]) {
264
+ encryptionConfig = ENCRYPTION_VERSIONS[version];
265
+ } else {
266
+ // Fallback to legacy version for backward compatibility
267
+ encryptionConfig = ENCRYPTION_VERSIONS[ENCRYPTION_VERSION_LEGACY];
268
+ }
181
269
  try {
182
- const { salt, iv, cipher } = data;
183
- // Ensure proper base64 padding for all values
184
- const paddedSalt = ensureBase64Padding(salt);
185
- const paddedIv = ensureBase64Padding(iv);
186
- const paddedCipher = ensureBase64Padding(cipher);
187
- const saltBytes = base64ToBytes(paddedSalt);
188
- const ivBytes = base64ToBytes(paddedIv);
189
- const cipherBytes = base64ToBytes(paddedCipher);
190
270
  const key = await getKey({
191
271
  password,
192
- salt: saltBytes
272
+ salt: saltBytes,
273
+ encryptionConfig
193
274
  });
194
275
  const decryptedData = await crypto.subtle.decrypt({
195
276
  name: AES_GCM_ALGORITHM,
@@ -197,13 +278,23 @@ const decryptData = async ({ data, password })=>{
197
278
  }, key, cipherBytes);
198
279
  return new TextDecoder().decode(decryptedData);
199
280
  } catch (error) {
200
- throw new Error('Decryption failed');
281
+ throw new Error('Decryption failed: ' + error);
201
282
  }
202
283
  };
203
284
 
204
285
  const DEFAULT_LOG_LEVEL = 'INFO';
205
286
 
206
287
  class DynamicWalletClient {
288
+ async initializeForwardMPCClient() {
289
+ try {
290
+ await this.apiClient.forwardMPCClient.ensureWsConnection();
291
+ } catch (error) {
292
+ this.logger.error('Error connecting to ForwardMPC enclave websocket. Environment: ' + this.environmentId, {
293
+ error,
294
+ environmentId: this.environmentId
295
+ });
296
+ }
297
+ }
207
298
  ensureApiClientAuthenticated() {
208
299
  if (!this.isApiClientAuthenticated) {
209
300
  throw new Error('Client must be authenticated before making API calls. Call authenticateApiToken first.');
@@ -227,13 +318,15 @@ class DynamicWalletClient {
227
318
  });
228
319
  this.isApiClientAuthenticated = true;
229
320
  }
230
- async dynamicServerInitializeKeyGen({ chainName, externalServerKeygenIds, thresholdSignatureScheme, onError, onCeremonyComplete }) {
321
+ async dynamicServerInitializeKeyGen({ chainName, externalServerKeygenIds, thresholdSignatureScheme, dynamicRequestId, skipLock, onError, onCeremonyComplete }) {
231
322
  this.ensureApiClientAuthenticated();
232
323
  try {
233
324
  const data = await this.apiClient.createWalletAccount({
234
325
  chainName,
235
326
  clientKeygenIds: externalServerKeygenIds,
236
327
  thresholdSignatureScheme,
328
+ dynamicRequestId,
329
+ skipLock,
237
330
  onError,
238
331
  onCeremonyComplete
239
332
  });
@@ -267,8 +360,8 @@ class DynamicWalletClient {
267
360
  let publicKey;
268
361
  if (mpcSigner instanceof Ecdsa) {
269
362
  publicKey = await mpcSigner.derivePubkey(keyShare, derivationPath);
270
- } else if (mpcSigner instanceof Ed25519) {
271
- publicKey = await mpcSigner.derivePubkey(keyShare, derivationPath);
363
+ } else if (mpcSigner instanceof ExportableEd25519) {
364
+ publicKey = await mpcSigner.getPubkey(keyShare);
272
365
  }
273
366
  return publicKey;
274
367
  } catch (error) {
@@ -294,7 +387,13 @@ class DynamicWalletClient {
294
387
  ...dynamicServerKeygenIds,
295
388
  ...otherExternalServerKeygenIds
296
389
  ];
297
- return mpcSigner.keygen(roomId, mpcConfig.numberOfParties, mpcConfig.threshold, currentInit, allOtherKeygenIds);
390
+ if (!(mpcSigner instanceof ExportableEd25519)) {
391
+ return mpcSigner.keygen(roomId, mpcConfig.numberOfParties, mpcConfig.threshold, currentInit, allOtherKeygenIds);
392
+ } else {
393
+ // One party joins the keygen room using acting as the sampler: (wallet-service)
394
+ // The remaining parties join the key sampling ceremony using: (browser)
395
+ return mpcSigner.receiveKey(roomId, mpcConfig.numberOfParties, mpcConfig.threshold, currentInit, allOtherKeygenIds);
396
+ }
298
397
  }));
299
398
  // only need one client keygen result to derive the public key
300
399
  const [serverKeygenResult] = serverKeygenResults;
@@ -314,7 +413,8 @@ class DynamicWalletClient {
314
413
  throw new Error('Error deriving public key in externalServerKeyGen');
315
414
  }
316
415
  }
317
- async keyGen({ chainName, thresholdSignatureScheme, onError, onCeremonyComplete }) {
416
+ async keyGen({ chainName, thresholdSignatureScheme, skipLock, onError, onCeremonyComplete }) {
417
+ const dynamicRequestId = v4();
318
418
  try {
319
419
  const externalServerInitKeygenResults = await this.externalServerInitializeKeyGen({
320
420
  chainName,
@@ -324,7 +424,9 @@ class DynamicWalletClient {
324
424
  const { roomId, serverKeygenIds: dynamicServerKeygenIds } = await this.dynamicServerInitializeKeyGen({
325
425
  chainName,
326
426
  externalServerKeygenIds,
427
+ dynamicRequestId,
327
428
  thresholdSignatureScheme,
429
+ skipLock,
328
430
  onCeremonyComplete
329
431
  });
330
432
  const { rawPublicKey, externalServerKeyGenResults } = await this.externalServerKeyGen({
@@ -345,6 +447,7 @@ class DynamicWalletClient {
345
447
  }
346
448
  async importRawPrivateKey({ chainName, privateKey, thresholdSignatureScheme, onError, onCeremonyComplete }) {
347
449
  this.ensureApiClientAuthenticated();
450
+ const dynamicRequestId = v4();
348
451
  const mpcSigner = getMPCSigner({
349
452
  chainName,
350
453
  baseRelayUrl: this.baseMPCRelayApiUrl
@@ -358,6 +461,7 @@ class DynamicWalletClient {
358
461
  chainName,
359
462
  clientKeygenIds: externalServerKeygenIds,
360
463
  thresholdSignatureScheme,
464
+ dynamicRequestId,
361
465
  onError,
362
466
  onCeremonyComplete
363
467
  });
@@ -390,7 +494,8 @@ class DynamicWalletClient {
390
494
  externalServerKeyShares: externalServerKeygenResults
391
495
  };
392
496
  }
393
- async dynamicServerSign({ walletId, message }) {
497
+ async dynamicServerSign({ walletId, message, isFormatted, context, onError }) {
498
+ const dynamicRequestId = v4();
394
499
  this.ensureApiClientAuthenticated();
395
500
  // Create the room and sign the message
396
501
  if (typeof message !== 'string') {
@@ -398,35 +503,22 @@ class DynamicWalletClient {
398
503
  }
399
504
  const data = await this.apiClient.signMessage({
400
505
  walletId,
401
- message
506
+ message,
507
+ isFormatted,
508
+ dynamicRequestId,
509
+ context: context ? JSON.parse(JSON.stringify(context, (_key, value)=>typeof value === 'bigint' ? value.toString() : value)) : undefined,
510
+ onError,
511
+ forwardMPCClientEnabled: this.forwardMPCEnabled
402
512
  });
403
513
  return data;
404
514
  }
405
- async externalServerSign({ chainName, message, roomId, keyShare, derivationPath }) {
515
+ async externalServerSign({ chainName, message, roomId, keyShare, derivationPath, isFormatted }) {
406
516
  try {
407
517
  const mpcSigner = getMPCSigner({
408
518
  chainName,
409
519
  baseRelayUrl: this.baseMPCRelayApiUrl
410
520
  });
411
- let formattedMessage;
412
- //note: Ecdsa can also be used by bitcoin, but only keccak256 is used by ethereum
413
- if (mpcSigner instanceof Ecdsa) {
414
- formattedMessage = MessageHash.keccak256(message);
415
- } else if (mpcSigner instanceof Ed25519) {
416
- if (typeof message === 'string') {
417
- if (!isHexString(message)) {
418
- formattedMessage = Buffer.from(message).toString('hex');
419
- } else {
420
- formattedMessage = Buffer.from(message, 'hex');
421
- }
422
- } else {
423
- formattedMessage = message;
424
- }
425
- } else if (mpcSigner instanceof BIP340 && typeof message === 'string') {
426
- formattedMessage = new TextEncoder().encode(message);
427
- } else {
428
- throw new Error('Unsupported signer type');
429
- }
521
+ const formattedMessage = isFormatted ? new MessageHash(message) : formatMessage(chainName, message);
430
522
  const signature = await mpcSigner.sign(roomId, keyShare, formattedMessage, derivationPath);
431
523
  return signature;
432
524
  } catch (error) {
@@ -434,35 +526,104 @@ class DynamicWalletClient {
434
526
  throw error;
435
527
  }
436
528
  }
437
- //todo: need to modify with imported flag
438
- async sign({ accountAddress, message, chainName, password = undefined }) {
439
- await this.verifyPassword({
440
- accountAddress,
441
- password,
442
- walletOperation: WalletOperation.SIGN_MESSAGE
443
- });
444
- const wallet = await this.getWallet({
445
- accountAddress,
446
- walletOperation: WalletOperation.SIGN_MESSAGE,
447
- password
448
- });
449
- // Perform the dynamic server sign
450
- const data = await this.dynamicServerSign({
451
- walletId: wallet.walletId,
452
- message
453
- });
454
- const derivationPath = wallet.derivationPath && wallet.derivationPath != '' ? new Uint32Array(Object.values(JSON.parse(wallet.derivationPath))) : undefined;
455
- // Perform the external server sign and return the signature
456
- const signature = await this.externalServerSign({
457
- chainName,
458
- message,
459
- roomId: data.roomId,
460
- keyShare: wallet.externalServerKeyShares[0],
461
- derivationPath
462
- });
463
- return signature;
529
+ async forwardMPCClientSign({ chainName, message, roomId, keyShare, derivationPath, formattedMessage, dynamicRequestId, isFormatted }) {
530
+ try {
531
+ if (!this.apiClient.forwardMPCClient.connected) {
532
+ await this.initializeForwardMPCClient();
533
+ }
534
+ const messageForForwardMPC = serializeMessageForForwardMPC({
535
+ message,
536
+ isFormatted,
537
+ chainName
538
+ });
539
+ this.logger.info('Forward MPC enabled, signing message with forward MPC');
540
+ const environment = getEnvironmentFromUrl(this.baseApiUrl);
541
+ const defaultRelayUrl = MPC_RELAY_URL_MAP[environment];
542
+ const signature = await this.apiClient.forwardMPCClient.signMessage({
543
+ keyshare: keyShare,
544
+ message: chainName === 'SVM' ? formattedMessage : messageForForwardMPC,
545
+ relayDomain: this.baseMPCRelayApiUrl || defaultRelayUrl,
546
+ signingAlgo: chainName === 'EVM' ? 'ECDSA' : 'ED25519',
547
+ hashAlgo: chainName === 'EVM' && !isFormatted ? 'keccak256' : undefined,
548
+ derivationPath: derivationPath,
549
+ roomUuid: roomId
550
+ });
551
+ const signatureBytes = signature.data.signature;
552
+ if (!(signatureBytes instanceof Uint8Array)) {
553
+ throw new TypeError(`Invalid signature format: expected Uint8Array, got ${typeof signatureBytes}`);
554
+ }
555
+ // Convert to EcdsaSignature
556
+ if (chainName === 'EVM') {
557
+ const ecdsaSignature = EcdsaSignature.fromBuffer(signatureBytes);
558
+ return ecdsaSignature;
559
+ } else {
560
+ return signatureBytes;
561
+ }
562
+ } catch (error) {
563
+ this.logger.error('Error signing message with forward MPC client', {
564
+ error,
565
+ environmentId: this.environmentId,
566
+ dynamicRequestId
567
+ });
568
+ throw error;
569
+ }
570
+ }
571
+ async sign({ accountAddress, externalServerKeyShares, message, chainName, password = undefined, isFormatted = false, context, onError }) {
572
+ try {
573
+ await this.verifyPassword({
574
+ accountAddress,
575
+ password,
576
+ walletOperation: WalletOperation.SIGN_MESSAGE
577
+ });
578
+ const wallet = await this.getWallet({
579
+ accountAddress,
580
+ walletOperation: WalletOperation.SIGN_MESSAGE,
581
+ password
582
+ });
583
+ externalServerKeyShares = externalServerKeyShares != null ? externalServerKeyShares : wallet.externalServerKeyShares;
584
+ if (!externalServerKeyShares) {
585
+ throw new Error('External server key shares are required to sign a message');
586
+ }
587
+ // Perform the dynamic server sign
588
+ const data = await this.dynamicServerSign({
589
+ walletId: wallet.walletId,
590
+ message,
591
+ isFormatted,
592
+ context,
593
+ onError
594
+ });
595
+ const derivationPath = wallet.derivationPath && wallet.derivationPath != '' ? new Uint32Array(Object.values(JSON.parse(wallet.derivationPath))) : undefined;
596
+ // Perform the external server sign and return the signature
597
+ if (this.forwardMPCEnabled) {
598
+ const formattedMessage = isFormatted ? new MessageHash(message) : formatMessage(chainName, message);
599
+ const signature = await this.forwardMPCClientSign({
600
+ chainName,
601
+ message,
602
+ roomId: data.roomId,
603
+ keyShare: externalServerKeyShares[0],
604
+ derivationPath,
605
+ formattedMessage,
606
+ dynamicRequestId: v4(),
607
+ isFormatted
608
+ });
609
+ return signature;
610
+ } else {
611
+ const signature = await this.externalServerSign({
612
+ chainName,
613
+ message,
614
+ roomId: data.roomId,
615
+ keyShare: externalServerKeyShares[0],
616
+ derivationPath,
617
+ isFormatted
618
+ });
619
+ return signature;
620
+ }
621
+ } catch (error) {
622
+ this.logger.error('Error in sign', error);
623
+ throw error;
624
+ }
464
625
  }
465
- async refreshWalletAccountShares({ accountAddress, chainName, password = undefined }) {
626
+ async refreshWalletAccountShares({ accountAddress, chainName, password = undefined, externalServerKeyShares, backUpToClientShareService = false }) {
466
627
  this.ensureApiClientAuthenticated();
467
628
  await this.verifyPassword({
468
629
  accountAddress,
@@ -483,14 +644,19 @@ class DynamicWalletClient {
483
644
  walletId: wallet.walletId
484
645
  });
485
646
  const roomId = data.roomId;
486
- const refreshResults = await Promise.all(wallet.externalServerKeyShares.map((serverKeyShare)=>mpcSigner.refresh(roomId, serverKeyShare)));
647
+ externalServerKeyShares = externalServerKeyShares != null ? externalServerKeyShares : wallet.externalServerKeyShares;
648
+ if (!externalServerKeyShares) {
649
+ throw new Error('External server key shares are required to refresh');
650
+ }
651
+ const refreshResults = await Promise.all(externalServerKeyShares.map((serverKeyShare)=>mpcSigner.refresh(roomId, serverKeyShare)));
487
652
  this.walletMap[accountAddress] = _extends({}, this.walletMap[accountAddress], {
488
653
  externalServerKeyShares: refreshResults,
489
654
  externalServerKeySharesBackupInfo: getExternalServerKeyShareBackupInfo()
490
655
  });
491
656
  await this.storeEncryptedBackupByWallet({
492
657
  accountAddress,
493
- password: password != null ? password : this.environmentId
658
+ password: password != null ? password : this.environmentId,
659
+ backUpToClientShareService
494
660
  });
495
661
  return refreshResults;
496
662
  }
@@ -543,7 +709,7 @@ class DynamicWalletClient {
543
709
  existingExternalServerKeyShares
544
710
  };
545
711
  }
546
- async reshare({ chainName, accountAddress, oldThresholdSignatureScheme, newThresholdSignatureScheme, password = undefined }) {
712
+ async reshare({ chainName, accountAddress, oldThresholdSignatureScheme, newThresholdSignatureScheme, password = undefined, externalServerKeyShares, backUpToClientShareService = false }) {
547
713
  this.ensureApiClientAuthenticated();
548
714
  await this.verifyPassword({
549
715
  accountAddress,
@@ -560,9 +726,15 @@ class DynamicWalletClient {
560
726
  shareCount: existingExternalServerShareCount,
561
727
  password
562
728
  });
729
+ externalServerKeyShares = externalServerKeyShares != null ? externalServerKeyShares : wallet.externalServerKeyShares;
730
+ if (!externalServerKeyShares) {
731
+ throw new Error('External server key shares are required to reshare');
732
+ }
563
733
  const { newExternalServerInitKeygenResults, newExternalServerKeygenIds, existingExternalServerKeygenIds, existingExternalServerKeyShares } = await this.reshareStrategy({
564
734
  chainName,
565
- wallet,
735
+ wallet: _extends({}, wallet, {
736
+ externalServerKeyShares
737
+ }),
566
738
  oldThresholdSignatureScheme,
567
739
  newThresholdSignatureScheme
568
740
  });
@@ -600,11 +772,12 @@ class DynamicWalletClient {
600
772
  });
601
773
  await this.storeEncryptedBackupByWallet({
602
774
  accountAddress,
603
- password
775
+ password,
776
+ backUpToClientShareService
604
777
  });
605
778
  return reshareResults;
606
779
  }
607
- async exportKey({ accountAddress, chainName, password = undefined }) {
780
+ async exportKey({ accountAddress, chainName, password = undefined, externalServerKeyShares }) {
608
781
  this.ensureApiClientAuthenticated();
609
782
  await this.verifyPassword({
610
783
  accountAddress,
@@ -616,19 +789,23 @@ class DynamicWalletClient {
616
789
  password,
617
790
  walletOperation: WalletOperation.EXPORT_PRIVATE_KEY
618
791
  });
792
+ externalServerKeyShares = externalServerKeyShares != null ? externalServerKeyShares : wallet.externalServerKeyShares;
793
+ if (!externalServerKeyShares) {
794
+ throw new Error('External server key shares are required to export a private key');
795
+ }
619
796
  const mpcSigner = getMPCSigner({
620
797
  chainName,
621
798
  baseRelayUrl: this.baseMPCRelayApiUrl
622
799
  });
623
800
  const exportId = await this.getExportId({
624
801
  chainName,
625
- serverKeyShare: wallet.externalServerKeyShares[0]
802
+ serverKeyShare: externalServerKeyShares[0]
626
803
  });
627
804
  const data = await this.apiClient.exportKey({
628
805
  walletId: wallet.walletId,
629
806
  exportId
630
807
  });
631
- const keyExportRaw = await mpcSigner.exportFullPrivateKey(data.roomId, wallet.externalServerKeyShares[0], exportId);
808
+ const keyExportRaw = await mpcSigner.exportFullPrivateKey(data.roomId, externalServerKeyShares[0], exportId);
632
809
  if (!keyExportRaw) {
633
810
  throw new Error('Error exporting private key');
634
811
  }
@@ -636,7 +813,7 @@ class DynamicWalletClient {
636
813
  let derivedPrivateKey;
637
814
  if (mpcSigner instanceof Ecdsa) {
638
815
  derivedPrivateKey = await mpcSigner.derivePrivateKeyFromXpriv(keyExportRaw, derivationPath);
639
- } else if (mpcSigner instanceof Ed25519) {
816
+ } else if (mpcSigner instanceof ExportableEd25519) {
640
817
  derivedPrivateKey = keyExportRaw;
641
818
  } else if (mpcSigner instanceof BIP340) {
642
819
  derivedPrivateKey = await mpcSigner.derivePrivateKeyFromXpriv(keyExportRaw, derivationPath);
@@ -655,7 +832,7 @@ class DynamicWalletClient {
655
832
  baseRelayUrl: this.baseMPCRelayApiUrl
656
833
  });
657
834
  const walletKeyShares = keyShares.map((keyShare)=>{
658
- return mpcSigner instanceof Ecdsa ? new EcdsaKeygenResult(keyShare.pubkey, keyShare.secretShare) : mpcSigner instanceof Ed25519 ? new Ed25519KeygenResult(keyShare.pubkey, keyShare.secretShare) : new BIP340KeygenResult(keyShare.pubkey, keyShare.secretShare);
835
+ return mpcSigner instanceof Ecdsa ? new EcdsaKeygenResult(keyShare.pubkey, keyShare.secretShare) : mpcSigner instanceof ExportableEd25519 ? new ExportableEd25519KeygenResult(keyShare.pubkey, keyShare.secretShare) : new BIP340KeygenResult(keyShare.pubkey, keyShare.secretShare);
659
836
  });
660
837
  const keyExportRaw = await mpcSigner.offlineExportFullPrivateKey(walletKeyShares);
661
838
  if (!keyExportRaw) {
@@ -666,7 +843,7 @@ class DynamicWalletClient {
666
843
  let derivedPrivateKey;
667
844
  if (mpcSigner instanceof Ecdsa) {
668
845
  derivedPrivateKey = await mpcSigner.derivePrivateKeyFromXpriv(keyExportRaw, walletDerivationPath);
669
- } else if (mpcSigner instanceof Ed25519) {
846
+ } else if (mpcSigner instanceof ExportableEd25519) {
670
847
  derivedPrivateKey = keyExportRaw;
671
848
  } else if (mpcSigner instanceof BIP340) {
672
849
  derivedPrivateKey = await mpcSigner.derivePrivateKeyFromXpriv(keyExportRaw, walletDerivationPath);
@@ -689,11 +866,28 @@ class DynamicWalletClient {
689
866
  const serializedEncryptedKeyShare = Buffer.from(JSON.stringify(encryptedKeyShare)).toString('base64');
690
867
  return serializedEncryptedKeyShare;
691
868
  }
692
- async storeEncryptedBackupByWallet({ accountAddress, externalServerKeyShares = undefined, password = undefined }) {
869
+ async ensureCeremonyCompletionBeforeBackup({ accountAddress }) {
870
+ let retries = 0;
871
+ const maxRetries = 3;
872
+ while((!this.walletMap[accountAddress] || !this.walletMap[accountAddress].walletId) && retries < maxRetries){
873
+ await new Promise((resolve)=>setTimeout(resolve, 1000)); // Wait 1 second
874
+ retries++;
875
+ }
876
+ if (!this.walletMap[accountAddress] || !this.walletMap[accountAddress].walletId) {
877
+ throw new Error('Ceremony completion timeout');
878
+ }
879
+ }
880
+ async storeEncryptedBackupByWallet({ accountAddress, externalServerKeyShares = undefined, password = undefined, backUpToClientShareService }) {
693
881
  this.ensureApiClientAuthenticated();
882
+ //add retry logic for ceremony completion to prevent race condition
883
+ await this.ensureCeremonyCompletionBeforeBackup({
884
+ accountAddress
885
+ });
886
+ const dynamicRequestId = v4();
694
887
  try {
695
888
  const keySharesToBackup = externalServerKeyShares != null ? externalServerKeyShares : await this.getExternalServerKeyShares({
696
- accountAddress
889
+ accountAddress,
890
+ password
697
891
  });
698
892
  if (!keySharesToBackup || keySharesToBackup.length === 0) {
699
893
  throw new Error(`Key shares not found for accountAddress: ${accountAddress}`);
@@ -705,51 +899,92 @@ class DynamicWalletClient {
705
899
  if (!this.walletMap[accountAddress] || !this.walletMap[accountAddress].walletId) {
706
900
  throw new Error(`WalletId not found for accountAddress: ${accountAddress}`);
707
901
  }
708
- const data = await this.apiClient.storeEncryptedBackupByWallet({
902
+ const passwordEncryptedFlag = Boolean(password) && password !== this.environmentId;
903
+ const locations = [];
904
+ if (backUpToClientShareService) {
905
+ const data = await this.apiClient.storeEncryptedBackupByWallet({
906
+ walletId: this.walletMap[accountAddress].walletId,
907
+ encryptedKeyShares: encryptedKeyShares,
908
+ passwordEncrypted: passwordEncryptedFlag,
909
+ encryptionVersion: ENCRYPTION_VERSION_CURRENT,
910
+ requiresSignedSessionId: false,
911
+ dynamicRequestId
912
+ });
913
+ locations.push({
914
+ location: BackupLocation.DYNAMIC,
915
+ externalKeyShareId: data.keyShareIds[0],
916
+ passwordEncrypted: passwordEncryptedFlag
917
+ });
918
+ } else {
919
+ locations.push({
920
+ location: BackupLocation.EXTERNAL
921
+ });
922
+ }
923
+ const backupData = await this.apiClient.markKeySharesAsBackedUp({
709
924
  walletId: this.walletMap[accountAddress].walletId,
710
- encryptedKeyShares,
711
- passwordEncrypted: Boolean(password) && password !== this.environmentId
925
+ locations,
926
+ dynamicRequestId
927
+ });
928
+ const updatedBackupInfo = getExternalServerKeyShareBackupInfo({
929
+ walletProperties: {
930
+ derivationPath: this.walletMap[accountAddress].derivationPath,
931
+ keyShares: backupData.locationsWithKeyShares.map((ks)=>({
932
+ id: ks.keyShareId,
933
+ backupLocation: ks.location,
934
+ externalKeyShareId: ks.externalKeyShareId,
935
+ passwordEncrypted: passwordEncryptedFlag
936
+ })),
937
+ thresholdSignatureScheme: this.walletMap[accountAddress].thresholdSignatureScheme
938
+ }
712
939
  });
713
940
  this.walletMap[accountAddress] = _extends({}, this.walletMap[accountAddress], {
714
- externalServerKeySharesBackupInfo: getExternalServerKeyShareBackupInfo({
715
- walletProperties: {
716
- derivationPath: this.walletMap[accountAddress].derivationPath,
717
- keyShares: data.keyShares,
718
- thresholdSignatureScheme: this.walletMap[accountAddress].thresholdSignatureScheme
719
- }
720
- })
941
+ externalServerKeySharesBackupInfo: updatedBackupInfo
721
942
  });
722
- return data;
943
+ return encryptedKeyShares;
723
944
  } catch (error) {
724
- this.logger.error('Error in storeEncryptedBackupByWallet:', error);
945
+ this.logger.error('Error in storeEncryptedBackupByWallet:', {
946
+ accountAddress,
947
+ error,
948
+ dynamicRequestId
949
+ });
725
950
  throw error;
726
951
  }
727
952
  }
728
- async storeEncryptedBackupByWalletWithRetry({ accountAddress, externalServerKeyShares, password }) {
953
+ async storeEncryptedBackupByWalletWithRetry({ accountAddress, externalServerKeyShares, password, backUpToClientShareService }) {
729
954
  await retryPromise(()=>this.storeEncryptedBackupByWallet({
730
955
  accountAddress,
731
956
  externalServerKeyShares,
732
- password
957
+ password,
958
+ backUpToClientShareService
733
959
  }), {
734
960
  operationName: 'store encrypted backup',
735
961
  logContext: {
736
- walletAddress: accountAddress,
737
- keyShares: externalServerKeyShares == null ? void 0 : externalServerKeyShares.map((keyShare)=>this.encryptKeyShare({
738
- keyShare,
739
- password
740
- }))
962
+ walletAddress: accountAddress
741
963
  }
742
964
  });
743
965
  }
744
966
  async getExternalServerKeyShares({ accountAddress, password }) {
967
+ var _wallet_externalServerKeySharesBackupInfo_backups, _wallet_externalServerKeySharesBackupInfo;
745
968
  const wallet = await this.getWallet({
746
969
  accountAddress,
747
970
  password,
748
971
  walletOperation: WalletOperation.REACH_THRESHOLD
749
972
  });
750
- return wallet.externalServerKeyShares;
973
+ // Check if the wallet has a dynamic backup and derive password encryption status from the first dynamic share
974
+ const dynamicBackups = (wallet == null ? void 0 : (_wallet_externalServerKeySharesBackupInfo = wallet.externalServerKeySharesBackupInfo) == null ? void 0 : (_wallet_externalServerKeySharesBackupInfo_backups = _wallet_externalServerKeySharesBackupInfo.backups) == null ? void 0 : _wallet_externalServerKeySharesBackupInfo_backups.dynamic) || [];
975
+ const passwordEncrypted = dynamicBackups.length > 0 ? Boolean(dynamicBackups[0].passwordEncrypted) : false;
976
+ if (passwordEncrypted && !password) {
977
+ throw new Error('Password is required for decryption but not provided. This backup was encrypted with a password.');
978
+ }
979
+ // recover the shares
980
+ const recoveredShares = await this.recoverEncryptedBackupByWallet({
981
+ accountAddress,
982
+ password,
983
+ walletOperation: WalletOperation.REACH_THRESHOLD
984
+ });
985
+ return recoveredShares;
751
986
  }
752
- async updatePassword({ accountAddress, existingPassword, newPassword }) {
987
+ async updatePassword({ accountAddress, existingPassword, newPassword, backUpToClientShareService }) {
753
988
  await this.getWallet({
754
989
  accountAddress,
755
990
  password: existingPassword,
@@ -757,7 +992,8 @@ class DynamicWalletClient {
757
992
  });
758
993
  await this.storeEncryptedBackupByWallet({
759
994
  accountAddress,
760
- password: newPassword
995
+ password: newPassword,
996
+ backUpToClientShareService
761
997
  });
762
998
  }
763
999
  async decryptKeyShare({ keyShare, password }) {
@@ -788,17 +1024,57 @@ class DynamicWalletClient {
788
1024
  if (shareCount !== undefined) {
789
1025
  requiredShareCount = shareCount;
790
1026
  }
791
- const dynamicShares = backups[BackupLocation.DYNAMIC].slice(0, requiredShareCount);
1027
+ const dynamicShares = (backups[BackupLocation.DYNAMIC] || []).slice(0, requiredShareCount);
1028
+ const externalShares = (backups[BackupLocation.EXTERNAL] || []).slice(0, requiredShareCount);
1029
+ // If we have DYNAMIC shares, use those; otherwise use EXTERNAL shares
1030
+ const sharesToUse = dynamicShares.length > 0 ? dynamicShares : externalShares;
1031
+ const backupLocation = dynamicShares.length > 0 ? BackupLocation.DYNAMIC : BackupLocation.EXTERNAL;
792
1032
  return {
793
1033
  shares: {
794
- [BackupLocation.DYNAMIC]: dynamicShares
1034
+ [backupLocation]: sharesToUse.map((ks)=>{
1035
+ var _ks_externalKeyShareId, _ref;
1036
+ return (_ref = (_ks_externalKeyShareId = ks.externalKeyShareId) != null ? _ks_externalKeyShareId : ks.keyShareId) != null ? _ref : '';
1037
+ })
795
1038
  },
796
1039
  requiredShareCount
797
1040
  };
798
1041
  }
1042
+ /**
1043
+ * Attempts to recover key shares from backup if they are not provided.
1044
+ * The recovered shares will be stored in the wallet map for use in signing operations.
1045
+ * @param accountAddress - The account address to recover shares for
1046
+ * @param password - The password to decrypt the shares
1047
+ * @param walletOperation - The wallet operation being performed
1048
+ * @param externalServerKeyShares - The provided key shares (if any)
1049
+ * @param errorMessage - The error message to throw if recovery fails
1050
+ * @throws Error if recovery is needed but fails
1051
+ */ async ensureKeySharesRecovered({ accountAddress, password, walletOperation, externalServerKeyShares, errorMessage }) {
1052
+ if (!externalServerKeyShares || externalServerKeyShares.length === 0) {
1053
+ // attempt to recover dynamic keyshares if no key shares are provided
1054
+ // the shares will be used for signing in the node SDK if successful
1055
+ const recoveredShares = await this.recoverEncryptedBackupByWallet({
1056
+ accountAddress,
1057
+ password,
1058
+ walletOperation,
1059
+ storeRecoveredShares: true
1060
+ });
1061
+ // If recovery returned empty and no shares were provided, throw error
1062
+ if (!recoveredShares || recoveredShares.length === 0) {
1063
+ throw new Error(errorMessage);
1064
+ }
1065
+ }
1066
+ }
799
1067
  async recoverEncryptedBackupByWallet({ accountAddress, password, walletOperation, shareCount = undefined, storeRecoveredShares = true }) {
1068
+ var _wallet_externalServerKeySharesBackupInfo;
800
1069
  this.ensureApiClientAuthenticated();
801
- const wallet = this.walletMap[accountAddress];
1070
+ let wallet = this.walletMap[accountAddress];
1071
+ if (!wallet) {
1072
+ const fetchedWallet = await this.getWalletByAddress(accountAddress);
1073
+ if (!fetchedWallet) {
1074
+ throw new Error(`Wallet not found for address 1: ${accountAddress}`);
1075
+ }
1076
+ wallet = fetchedWallet;
1077
+ }
802
1078
  this.logger.debug(`recoverEncryptedBackupByWallet wallet: ${walletOperation}`, wallet);
803
1079
  const { shares } = this.recoverStrategy({
804
1080
  externalServerKeySharesBackupInfo: wallet.externalServerKeySharesBackupInfo,
@@ -806,12 +1082,30 @@ class DynamicWalletClient {
806
1082
  walletOperation,
807
1083
  shareCount
808
1084
  });
809
- const { dynamic: dynamicKeyShareIds } = shares;
810
- const data = await this.apiClient.recoverEncryptedBackupByWallet({
811
- walletId: wallet.walletId,
812
- keyShareIds: dynamicKeyShareIds
813
- });
1085
+ // Get the key share IDs from whichever backup location has shares
1086
+ const dynamicKeyShareIds = shares[BackupLocation.DYNAMIC] || [];
1087
+ const externalKeyShareIds = shares[BackupLocation.EXTERNAL] || [];
1088
+ // Only attempt recovery if we have DYNAMIC shares (the API doesn't support EXTERNAL shares)
1089
+ let data;
1090
+ if (dynamicKeyShareIds.length > 0) {
1091
+ data = await this.apiClient.recoverEncryptedBackupByWallet({
1092
+ walletId: wallet.walletId,
1093
+ keyShareIds: dynamicKeyShareIds,
1094
+ requiresSignedSessionId: false
1095
+ });
1096
+ } else if (externalKeyShareIds.length > 0) {
1097
+ this.logger.debug('Skipping recovery - only EXTERNAL shares available (backUpToClientShareService: false)');
1098
+ return []; // Return empty array since we can't recover EXTERNAL shares via API
1099
+ } else {
1100
+ this.logger.debug('No shares available for recovery');
1101
+ return []; // Return empty array if no shares are available
1102
+ }
814
1103
  const dynamicKeyShares = data.keyShares.filter((keyShare)=>keyShare.encryptedAccountCredential !== null && keyShare.backupLocation === BackupLocation.DYNAMIC);
1104
+ var _wallet_externalServerKeySharesBackupInfo_passwordEncrypted;
1105
+ const isPasswordEncrypted = (_wallet_externalServerKeySharesBackupInfo_passwordEncrypted = (_wallet_externalServerKeySharesBackupInfo = wallet.externalServerKeySharesBackupInfo) == null ? void 0 : _wallet_externalServerKeySharesBackupInfo.passwordEncrypted) != null ? _wallet_externalServerKeySharesBackupInfo_passwordEncrypted : false;
1106
+ if (isPasswordEncrypted && !password) {
1107
+ throw new Error('Password is required for decryption but not provided. This backup was encrypted with a password.');
1108
+ }
815
1109
  const decryptedKeyShares = await Promise.all(dynamicKeyShares.map((keyShare)=>this.decryptKeyShare({
816
1110
  keyShare: keyShare.encryptedAccountCredential,
817
1111
  password: password != null ? password : this.environmentId
@@ -841,6 +1135,7 @@ class DynamicWalletClient {
841
1135
  * @param walletOperation - The wallet operation that determines required fields
842
1136
  * @returns boolean indicating if wallet needs to be re-fetched and restored from server
843
1137
  */ async checkWalletFields({ accountAddress, walletOperation = WalletOperation.REACH_THRESHOLD, shareCount }) {
1138
+ var _existingWallet_externalServerKeyShares;
844
1139
  let keyshareCheck = false;
845
1140
  let walletCheck = false;
846
1141
  let thresholdSignatureSchemeCheck = false;
@@ -860,7 +1155,7 @@ class DynamicWalletClient {
860
1155
  }
861
1156
  // check if wallet already exists with sufficient keyshares
862
1157
  if (existingWallet) {
863
- var _existingWallet_externalServerKeyShares;
1158
+ var _existingWallet_externalServerKeyShares1;
864
1159
  const { shares } = this.recoverStrategy({
865
1160
  externalServerKeySharesBackupInfo: existingWallet.externalServerKeySharesBackupInfo || {
866
1161
  backups: getExternalServerKeyShareBackupInfo()
@@ -870,11 +1165,35 @@ class DynamicWalletClient {
870
1165
  shareCount
871
1166
  });
872
1167
  const { dynamic: requiredDynamicKeyShareIds = [] } = shares;
873
- if (requiredDynamicKeyShareIds.length <= (((_existingWallet_externalServerKeyShares = existingWallet.externalServerKeyShares) == null ? void 0 : _existingWallet_externalServerKeyShares.length) || 0)) {
1168
+ const { external: requiredExternalKeyShareIds = [] } = shares;
1169
+ // Check if we have enough shares from any backup location
1170
+ const totalRequiredShares = requiredDynamicKeyShareIds.length + requiredExternalKeyShareIds.length;
1171
+ // Check if we have the required shares either loaded OR available in backup
1172
+ const hasLoadedShares = totalRequiredShares <= (((_existingWallet_externalServerKeyShares1 = existingWallet.externalServerKeyShares) == null ? void 0 : _existingWallet_externalServerKeyShares1.length) || 0);
1173
+ // Check if backup contains the specific required share IDs
1174
+ const hasBackupShares = existingWallet.externalServerKeySharesBackupInfo && (()=>{
1175
+ const backupShares = existingWallet.externalServerKeySharesBackupInfo.backups.dynamic;
1176
+ const allRequiredIds = [
1177
+ ...requiredDynamicKeyShareIds,
1178
+ ...requiredExternalKeyShareIds
1179
+ ];
1180
+ return allRequiredIds.every((requiredId)=>backupShares.some((backupShare)=>backupShare.externalKeyShareId === requiredId || backupShare.keyShareId === requiredId));
1181
+ })();
1182
+ if (hasLoadedShares || hasBackupShares) {
874
1183
  keyshareCheck = true;
875
1184
  }
876
1185
  }
877
- return walletCheck && thresholdSignatureSchemeCheck && keyshareCheck && derivationPathCheck;
1186
+ const result = walletCheck && thresholdSignatureSchemeCheck && keyshareCheck && derivationPathCheck;
1187
+ this.logger.debug('Wallet checks:', {
1188
+ walletCheck,
1189
+ thresholdSignatureSchemeCheck,
1190
+ keyshareCheck,
1191
+ derivationPathCheck,
1192
+ existingWallet: !!existingWallet,
1193
+ keySharesLength: existingWallet == null ? void 0 : (_existingWallet_externalServerKeyShares = existingWallet.externalServerKeyShares) == null ? void 0 : _existingWallet_externalServerKeyShares.length,
1194
+ result
1195
+ });
1196
+ return result;
878
1197
  }
879
1198
  /**
880
1199
  * verifyPassword attempts to recover and decrypt a single client key share using the provided password.
@@ -900,8 +1219,10 @@ class DynamicWalletClient {
900
1219
  accountAddress
901
1220
  });
902
1221
  const { dynamic: dynamicKeyShareIds = [] } = backups;
903
- if (!dynamicKeyShareIds || dynamicKeyShareIds.length === 0) {
904
- throw new Error('No dynamic key shares found');
1222
+ const { external: externalKeyShareIds = [] } = backups;
1223
+ // Check if we have any shares available (DYNAMIC or EXTERNAL)
1224
+ if (dynamicKeyShareIds.length === 0 && externalKeyShareIds.length === 0) {
1225
+ throw new Error('No key shares found');
905
1226
  }
906
1227
  try {
907
1228
  await this.recoverEncryptedBackupByWallet({
@@ -946,88 +1267,164 @@ class DynamicWalletClient {
946
1267
  if (walletOperation === WalletOperation.REACH_ALL_PARTIES || walletOperation === WalletOperation.REFRESH || walletOperation === WalletOperation.RESHARE) {
947
1268
  return true;
948
1269
  }
949
- const { requiredShareCount } = this.recoverStrategy({
1270
+ const { shares, requiredShareCount } = this.recoverStrategy({
950
1271
  externalServerKeySharesBackupInfo,
951
1272
  thresholdSignatureScheme: this.walletMap[accountAddress].thresholdSignatureScheme,
952
1273
  walletOperation
953
1274
  });
954
- if (externalServerKeyShares.length >= requiredShareCount) {
1275
+ const dynamicKeyShareIds = shares[BackupLocation.DYNAMIC] || [];
1276
+ const externalKeyShareIds = shares[BackupLocation.EXTERNAL] || [];
1277
+ // Check if we have enough shares from either location
1278
+ const totalAvailableShares = externalServerKeyShares.length + dynamicKeyShareIds.length + externalKeyShareIds.length;
1279
+ if (totalAvailableShares >= requiredShareCount) {
955
1280
  return false;
956
1281
  }
957
1282
  return true;
958
1283
  }
959
1284
  async getWalletExternalServerKeyShareBackupInfo({ accountAddress }) {
960
- var _this_walletMap_accountAddress_externalServerKeySharesBackupInfo_backups_BackupLocation_DYNAMIC, _this_walletMap_accountAddress_externalServerKeySharesBackupInfo_backups, _this_walletMap_accountAddress_externalServerKeySharesBackupInfo, _this_walletMap_accountAddress, _user_verifiedCredentials;
1285
+ var _wallet_externalServerKeySharesBackupInfo_backups_BackupLocation_DYNAMIC, _wallet_externalServerKeySharesBackupInfo_backups, _wallet_externalServerKeySharesBackupInfo, _user_verifiedCredentials;
961
1286
  this.ensureApiClientAuthenticated();
1287
+ let wallet = this.walletMap[accountAddress];
1288
+ if (!wallet) {
1289
+ const fetchedWallet = await this.getWalletByAddress(accountAddress);
1290
+ if (!fetchedWallet) {
1291
+ throw new Error(`Wallet not found for address 2: ${accountAddress}`);
1292
+ }
1293
+ wallet = fetchedWallet;
1294
+ }
962
1295
  // Return existing backup info if it exists
963
- if (((_this_walletMap_accountAddress = this.walletMap[accountAddress]) == null ? void 0 : (_this_walletMap_accountAddress_externalServerKeySharesBackupInfo = _this_walletMap_accountAddress.externalServerKeySharesBackupInfo) == null ? void 0 : (_this_walletMap_accountAddress_externalServerKeySharesBackupInfo_backups = _this_walletMap_accountAddress_externalServerKeySharesBackupInfo.backups) == null ? void 0 : (_this_walletMap_accountAddress_externalServerKeySharesBackupInfo_backups_BackupLocation_DYNAMIC = _this_walletMap_accountAddress_externalServerKeySharesBackupInfo_backups[BackupLocation.DYNAMIC]) == null ? void 0 : _this_walletMap_accountAddress_externalServerKeySharesBackupInfo_backups_BackupLocation_DYNAMIC.length) > 0) {
964
- return this.walletMap[accountAddress].externalServerKeySharesBackupInfo;
1296
+ if (((_wallet_externalServerKeySharesBackupInfo = wallet.externalServerKeySharesBackupInfo) == null ? void 0 : (_wallet_externalServerKeySharesBackupInfo_backups = _wallet_externalServerKeySharesBackupInfo.backups) == null ? void 0 : (_wallet_externalServerKeySharesBackupInfo_backups_BackupLocation_DYNAMIC = _wallet_externalServerKeySharesBackupInfo_backups[BackupLocation.DYNAMIC]) == null ? void 0 : _wallet_externalServerKeySharesBackupInfo_backups_BackupLocation_DYNAMIC.length) > 0) {
1297
+ return wallet.externalServerKeySharesBackupInfo;
965
1298
  }
966
1299
  // Get backup info from server
967
- const user = await this.apiClient.getUser();
968
- const wallet = (_user_verifiedCredentials = user.verifiedCredentials) == null ? void 0 : _user_verifiedCredentials.find((vc)=>vc.address.toLowerCase() === accountAddress.toLowerCase());
1300
+ const user = await this.apiClient.getUser(v4());
1301
+ const walletData = (_user_verifiedCredentials = user.verifiedCredentials) == null ? void 0 : _user_verifiedCredentials.find((vc)=>vc.address.toLowerCase() === accountAddress.toLowerCase());
969
1302
  return getExternalServerKeyShareBackupInfo({
970
- walletProperties: wallet == null ? void 0 : wallet.walletProperties
1303
+ walletProperties: walletData == null ? void 0 : walletData.walletProperties
971
1304
  });
972
1305
  }
973
1306
  async getWallet({ accountAddress, walletOperation = WalletOperation.NO_OPERATION, shareCount = undefined, password = undefined }) {
974
- var _user_verifiedCredentials;
975
- this.ensureApiClientAuthenticated();
976
- const existingWalletCheck = await this.checkWalletFields({
977
- accountAddress,
978
- walletOperation,
979
- shareCount
980
- });
981
- if (existingWalletCheck) {
982
- this.logger.debug(`Wallet ${accountAddress} already exists`);
983
- return this.walletMap[accountAddress];
984
- }
985
- //todo: Question - why don't we just call getWallets here? so then all are preloaded
986
- // Fetch and restore wallet from server
987
- const user = await this.apiClient.getUser();
988
- const wallet = (_user_verifiedCredentials = user.verifiedCredentials) == null ? void 0 : _user_verifiedCredentials.find((vc)=>vc.address.toLowerCase() === accountAddress.toLowerCase());
989
- this.logger.debug('Restoring wallet', wallet);
990
- const walletProperties = wallet.walletProperties;
991
- this.walletMap[accountAddress] = _extends({}, this.walletMap[accountAddress], {
992
- walletId: wallet.id,
993
- chainName: wallet.chainName,
994
- accountAddress,
995
- thresholdSignatureScheme: walletProperties.thresholdSignatureScheme,
996
- derivationPath: walletProperties.derivationPath,
997
- externalServerKeySharesBackupInfo: getExternalServerKeyShareBackupInfo({
998
- walletProperties
999
- })
1000
- });
1001
- if (walletOperation !== WalletOperation.NO_OPERATION && await this.requiresRestoreBackupSharesForOperation({
1002
- accountAddress,
1003
- walletOperation
1004
- })) {
1005
- const decryptedKeyShares = await this.recoverEncryptedBackupByWallet({
1307
+ try {
1308
+ this.ensureApiClientAuthenticated();
1309
+ const existingWalletCheck = await this.checkWalletFields({
1006
1310
  accountAddress,
1007
- password: password != null ? password : this.environmentId,
1008
- walletOperation: walletOperation,
1311
+ walletOperation,
1009
1312
  shareCount
1010
1313
  });
1011
- this.logger.debug('Recovered backup', decryptedKeyShares);
1314
+ if (existingWalletCheck) {
1315
+ this.logger.debug(`Wallet ${accountAddress} already exists`);
1316
+ return this.walletMap[accountAddress];
1317
+ }
1318
+ const wallet = await this.getWalletByAddress(accountAddress);
1319
+ if (!wallet) {
1320
+ throw new Error(`Wallet not found for address 3: ${accountAddress}`);
1321
+ }
1322
+ this.logger.debug('Restoring wallet', wallet);
1323
+ // The wallet is already processed, so we can use it directly
1324
+ this.walletMap[accountAddress] = wallet;
1325
+ if (walletOperation !== WalletOperation.NO_OPERATION && await this.requiresRestoreBackupSharesForOperation({
1326
+ accountAddress,
1327
+ walletOperation
1328
+ })) {
1329
+ const decryptedKeyShares = await this.recoverEncryptedBackupByWallet({
1330
+ accountAddress,
1331
+ password: password != null ? password : this.environmentId,
1332
+ walletOperation: walletOperation,
1333
+ shareCount
1334
+ });
1335
+ this.logger.debug('[DynamicWaasWalletClient] Recovered backup', decryptedKeyShares);
1336
+ }
1337
+ // externalServerKeyShares
1338
+ const walletCount = Object.keys(this.walletMap).length;
1339
+ if (walletCount === 0) {
1340
+ throw new Error('No wallets found');
1341
+ }
1342
+ // Return the only wallet if there's just one
1343
+ if (walletCount === 1) {
1344
+ return Object.values(this.walletMap)[0];
1345
+ }
1346
+ return this.walletMap[accountAddress];
1347
+ } catch (error) {
1348
+ this.logger.error('Error in getWallet', error);
1349
+ throw error;
1012
1350
  }
1013
- const walletCount = Object.keys(this.walletMap).length;
1014
- if (walletCount === 0) {
1015
- throw new Error('No wallets found');
1351
+ }
1352
+ /**
1353
+ * Get a single wallet by address
1354
+ * First tries the efficient getWaasWalletByAddress endpoint, falls back to getUser() if not available
1355
+ */ async getWalletByAddress(accountAddress) {
1356
+ // Return cached wallet if available
1357
+ if (this.walletMap[accountAddress]) {
1358
+ return this.walletMap[accountAddress];
1016
1359
  }
1017
- // Return the only wallet if there's just one
1018
- if (walletCount === 1) {
1019
- return Object.values(this.walletMap)[0];
1360
+ this.ensureApiClientAuthenticated();
1361
+ // Try getting single wallet by address first
1362
+ try {
1363
+ var _walletResponse_wallet;
1364
+ const walletResponse = await this.apiClient.getWaasWalletByAddress({
1365
+ walletAddress: accountAddress
1366
+ });
1367
+ const walletProperties = {
1368
+ walletId: walletResponse.wallet.walletId,
1369
+ chainName: walletResponse.wallet.chainName,
1370
+ accountAddress: walletResponse.wallet.accountAddress,
1371
+ externalServerKeyShares: [],
1372
+ derivationPath: walletResponse.wallet.derivationPath,
1373
+ thresholdSignatureScheme: walletResponse.wallet.thresholdSignatureScheme,
1374
+ externalServerKeySharesBackupInfo: getExternalServerKeyShareBackupInfo({
1375
+ walletProperties: {
1376
+ // @ts-expect-error TODO: update response to get key shares
1377
+ keyShares: ((_walletResponse_wallet = walletResponse.wallet) == null ? void 0 : _walletResponse_wallet.keyShares) || [],
1378
+ thresholdSignatureScheme: walletResponse.wallet.thresholdSignatureScheme,
1379
+ derivationPath: walletResponse.wallet.derivationPath
1380
+ }
1381
+ })
1382
+ };
1383
+ // Cache the wallet
1384
+ this.walletMap[accountAddress] = walletProperties;
1385
+ return walletProperties;
1386
+ } catch (error) {
1387
+ var _error_response;
1388
+ // If the new endpoint doesn't exist (404 or not implemented), fall back to getUser()
1389
+ if ((error == null ? void 0 : (_error_response = error.response) == null ? void 0 : _error_response.status) === 404 || (error == null ? void 0 : error.code) === 'ERR_BAD_REQUEST') {
1390
+ var _user_verifiedCredentials, _wallet_walletProperties, _wallet_walletProperties1;
1391
+ this.logger.debug('getWaasWalletByAddress endpoint not available, falling back to getUser()');
1392
+ // Fallback to getUser() approach
1393
+ const user = await this.apiClient.getUser(v4());
1394
+ const wallet = (_user_verifiedCredentials = user.verifiedCredentials) == null ? void 0 : _user_verifiedCredentials.find((vc)=>vc.walletName === 'dynamicwaas' && vc.address.toLowerCase() === accountAddress.toLowerCase());
1395
+ if (!wallet) {
1396
+ return null;
1397
+ }
1398
+ var _wallet_walletProperties_derivationPath;
1399
+ const walletProperties = {
1400
+ walletId: wallet.id,
1401
+ chainName: wallet.chain,
1402
+ accountAddress: wallet.address,
1403
+ externalServerKeyShares: [],
1404
+ derivationPath: (_wallet_walletProperties_derivationPath = (_wallet_walletProperties = wallet.walletProperties) == null ? void 0 : _wallet_walletProperties.derivationPath) != null ? _wallet_walletProperties_derivationPath : undefined,
1405
+ thresholdSignatureScheme: (_wallet_walletProperties1 = wallet.walletProperties) == null ? void 0 : _wallet_walletProperties1.thresholdSignatureScheme,
1406
+ externalServerKeySharesBackupInfo: getExternalServerKeyShareBackupInfo({
1407
+ walletProperties: wallet.walletProperties || {}
1408
+ })
1409
+ };
1410
+ // Cache the wallet
1411
+ this.walletMap[accountAddress] = walletProperties;
1412
+ return walletProperties;
1413
+ }
1414
+ throw error;
1020
1415
  }
1021
- return this.walletMap[accountAddress];
1022
1416
  }
1023
- async getWallets() {
1417
+ /**
1418
+ * Get all wallets (kept for backward compatibility but now uses lazy loading)
1419
+ * @deprecated Consider using getWalletByAddress for better performance with large wallet counts
1420
+ */ async getWallets() {
1024
1421
  var _user_verifiedCredentials;
1025
1422
  this.ensureApiClientAuthenticated();
1026
- const user = await this.apiClient.getUser();
1423
+ const user = await this.apiClient.getUser(v4());
1027
1424
  const waasWallets = (_user_verifiedCredentials = user.verifiedCredentials) == null ? void 0 : _user_verifiedCredentials.filter((vc)=>vc.walletName === 'dynamicwaas');
1028
1425
  const wallets = waasWallets.map((vc)=>{
1029
- var _this_walletMap_vc_address, _this_walletMap_vc_address1, _vc_walletProperties;
1030
- var _this_walletMap_vc_address_derivationPath;
1426
+ var _this_walletMap_vc_address, _vc_walletProperties, _vc_walletProperties1;
1427
+ var _vc_walletProperties_derivationPath;
1031
1428
  return {
1032
1429
  walletId: vc.id,
1033
1430
  chainName: vc.chain,
@@ -1036,37 +1433,223 @@ class DynamicWalletClient {
1036
1433
  walletProperties: vc.walletProperties || {}
1037
1434
  }),
1038
1435
  externalServerKeyShares: ((_this_walletMap_vc_address = this.walletMap[vc.address]) == null ? void 0 : _this_walletMap_vc_address.externalServerKeyShares) || [],
1039
- derivationPath: (_this_walletMap_vc_address_derivationPath = (_this_walletMap_vc_address1 = this.walletMap[vc.address]) == null ? void 0 : _this_walletMap_vc_address1.derivationPath) != null ? _this_walletMap_vc_address_derivationPath : undefined,
1040
- thresholdSignatureScheme: (_vc_walletProperties = vc.walletProperties) == null ? void 0 : _vc_walletProperties.thresholdSignatureScheme
1436
+ derivationPath: (_vc_walletProperties_derivationPath = (_vc_walletProperties = vc.walletProperties) == null ? void 0 : _vc_walletProperties.derivationPath) != null ? _vc_walletProperties_derivationPath : undefined,
1437
+ thresholdSignatureScheme: (_vc_walletProperties1 = vc.walletProperties) == null ? void 0 : _vc_walletProperties1.thresholdSignatureScheme
1041
1438
  };
1042
1439
  });
1043
1440
  this.walletMap = wallets.reduce((acc, wallet)=>{
1044
- var _acc_accountAddress;
1045
- const accountAddress = wallet.accountAddress;
1046
1441
  acc[wallet.accountAddress] = {
1047
1442
  walletId: wallet.walletId,
1048
1443
  chainName: wallet.chainName,
1049
1444
  accountAddress: wallet.accountAddress,
1050
1445
  externalServerKeyShares: wallet.externalServerKeyShares || [],
1051
1446
  externalServerKeySharesBackupInfo: wallet.externalServerKeySharesBackupInfo,
1052
- derivationPath: ((_acc_accountAddress = acc[accountAddress]) == null ? void 0 : _acc_accountAddress.derivationPath) || undefined,
1447
+ derivationPath: wallet.derivationPath,
1053
1448
  thresholdSignatureScheme: wallet.thresholdSignatureScheme
1054
1449
  };
1055
1450
  return acc;
1056
1451
  }, {});
1057
1452
  return wallets;
1058
1453
  }
1059
- constructor({ environmentId, baseApiUrl, baseMPCRelayApiUrl, debug }){
1454
+ constructor({ environmentId, baseApiUrl, baseMPCRelayApiUrl, debug, forwardMPCClient, enableMPCAccelerator = false }){
1060
1455
  this.logger = logger;
1061
1456
  this.walletMap = {} // todo: store in session storage
1062
1457
  ;
1063
1458
  this.isApiClientAuthenticated = false;
1459
+ this.forwardMPCEnabled = false;
1064
1460
  this.environmentId = environmentId;
1065
1461
  this.baseMPCRelayApiUrl = baseMPCRelayApiUrl;
1066
1462
  this.baseApiUrl = baseApiUrl;
1067
1463
  this.debug = Boolean(debug);
1068
1464
  this.logger.setLogLevel(this.debug ? 'DEBUG' : DEFAULT_LOG_LEVEL);
1465
+ this.forwardMPCEnabled = enableMPCAccelerator || Boolean(forwardMPCClient);
1466
+ // Initialize API client with forwardMPCClient
1467
+ this.apiClient = new DynamicApiClient({
1468
+ environmentId,
1469
+ baseApiUrl,
1470
+ forwardMPCClient
1471
+ });
1472
+ if (this.forwardMPCEnabled) {
1473
+ this.logger.info('Initializing ForwardMPC enclave websocket in node client. Environment: ' + this.environmentId);
1474
+ this.initializeForwardMPCClient();
1475
+ }
1069
1476
  }
1070
1477
  }
1071
1478
 
1072
- export { DynamicWalletClient, base64ToBytes, bytesToBase64, ensureBase64Padding, getExternalServerKeyShareBackupInfo, getMPCSignatureScheme, getMPCSigner, isHexString, mergeUniqueKeyShares, retryPromise, stringToBytes };
1479
+ const createCore = ({ environmentId, baseApiUrl, baseMPCRelayApiUrl, debug = false })=>{
1480
+ const coreLogger = logger;
1481
+ coreLogger.setLogLevel(debug ? 'DEBUG' : DEFAULT_LOG_LEVEL);
1482
+ const createApiClient = (options = {})=>{
1483
+ return new DynamicApiClient(_extends({
1484
+ environmentId,
1485
+ baseApiUrl
1486
+ }, options));
1487
+ };
1488
+ return {
1489
+ environmentId,
1490
+ createApiClient,
1491
+ logger: coreLogger,
1492
+ baseMPCRelayApiUrl,
1493
+ baseApiUrl,
1494
+ debug
1495
+ };
1496
+ };
1497
+
1498
+ // Helper function to create API client for delegated operations
1499
+ const createDelegatedApiClient = (client, options = {})=>{
1500
+ return client.createApiClient(options);
1501
+ };
1502
+ // Helper function to create API client with wallet-specific headers
1503
+ const createDelegatedApiClientWithWalletKey = (client, walletApiKey)=>{
1504
+ const apiClient = client.createApiClient();
1505
+ // Add the wallet-specific API key header to all requests from this client
1506
+ apiClient.apiClient.defaults.headers['x-dyn-wallet-api-key'] = walletApiKey;
1507
+ return apiClient;
1508
+ };
1509
+ const createDelegatedWalletClient = ({ environmentId, baseApiUrl, baseMPCRelayApiUrl, apiKey, debug = false })=>{
1510
+ const core = createCore({
1511
+ environmentId,
1512
+ baseApiUrl,
1513
+ baseMPCRelayApiUrl,
1514
+ debug
1515
+ });
1516
+ // Store the API key for use in delegated operations
1517
+ core.apiKey = apiKey;
1518
+ const walletMap = {};
1519
+ const client = {
1520
+ get environmentId () {
1521
+ return core.environmentId;
1522
+ },
1523
+ get debug () {
1524
+ return core.debug;
1525
+ },
1526
+ get apiUrl () {
1527
+ var _core_baseApiUrl;
1528
+ return (_core_baseApiUrl = core.baseApiUrl) != null ? _core_baseApiUrl : 'https://app.dynamicauth.com';
1529
+ },
1530
+ get wallets () {
1531
+ return walletMap;
1532
+ },
1533
+ createApiClient: (options = {})=>{
1534
+ return core.createApiClient(_extends({
1535
+ authToken: apiKey
1536
+ }, options));
1537
+ },
1538
+ logger: core.logger,
1539
+ apiKey,
1540
+ baseMPCRelayApiUrl: core.baseMPCRelayApiUrl
1541
+ };
1542
+ return client;
1543
+ };
1544
+
1545
+ const dynamicDelegatedSign = async (client, { walletId, message, isFormatted, walletApiKey, onError, context })=>{
1546
+ const dynamicRequestId = v4();
1547
+ // Convert message to hex if it's a Uint8Array
1548
+ if (typeof message !== 'string') {
1549
+ message = '0x' + Buffer.from(message).toString('hex');
1550
+ }
1551
+ const apiClient = createDelegatedApiClientWithWalletKey(client, walletApiKey);
1552
+ const data = await apiClient.delegatedSignMessage({
1553
+ walletId,
1554
+ message,
1555
+ isFormatted,
1556
+ dynamicRequestId,
1557
+ onError,
1558
+ context: context ? JSON.parse(JSON.stringify(context, (_key, value)=>typeof value === 'bigint' ? value.toString() : value)) : undefined
1559
+ });
1560
+ return data;
1561
+ };
1562
+ const delegatedSign = async (client, { chainName, message, roomId, keyShare, derivationPath, isFormatted })=>{
1563
+ try {
1564
+ const mpcSigner = getMPCSigner({
1565
+ chainName,
1566
+ baseRelayUrl: client.baseMPCRelayApiUrl
1567
+ });
1568
+ const formattedMessage = isFormatted ? new MessageHash(message) : formatMessage(chainName, message);
1569
+ const signature = await mpcSigner.sign(roomId, keyShare, formattedMessage, derivationPath);
1570
+ return signature;
1571
+ } catch (error) {
1572
+ client.logger.error('Error in delegatedSign', error);
1573
+ throw error;
1574
+ }
1575
+ };
1576
+ const delegatedSignMessage = async (client, { walletId, walletApiKey, keyShare, message, chainName, isFormatted = false, onError, context })=>{
1577
+ // Validate required parameters
1578
+ if (!keyShare) {
1579
+ throw new Error('Delegated key share is required to sign a message');
1580
+ }
1581
+ const wallet = await getWallet(client, {
1582
+ walletId
1583
+ });
1584
+ // Perform the dynamic server sign
1585
+ const data = await dynamicDelegatedSign(client, {
1586
+ walletId,
1587
+ walletApiKey,
1588
+ message,
1589
+ isFormatted,
1590
+ onError,
1591
+ context
1592
+ });
1593
+ const derivationPath = wallet.derivationPath && wallet.derivationPath !== '' ? new Uint32Array(Object.values(JSON.parse(wallet.derivationPath))) : undefined;
1594
+ // Perform the external server sign and return the signature
1595
+ const signature = await delegatedSign(client, {
1596
+ chainName,
1597
+ message,
1598
+ roomId: data.roomId,
1599
+ keyShare,
1600
+ derivationPath,
1601
+ isFormatted
1602
+ });
1603
+ return signature;
1604
+ };
1605
+ // Helper function to get wallet (to be implemented in wallet module)
1606
+ const getWallet = async (client, { walletId })=>{
1607
+ const wallets = client.wallets;
1608
+ // Check if wallet is already cached
1609
+ if (wallets[walletId]) {
1610
+ return wallets[walletId];
1611
+ }
1612
+ // Fetch wallet from API
1613
+ const apiClient = createDelegatedApiClient(client);
1614
+ const { wallet } = await apiClient.getWaasWalletById({
1615
+ walletId
1616
+ });
1617
+ const waasWallet = {
1618
+ walletId,
1619
+ chainName: wallet.chainName,
1620
+ accountAddress: wallet.accountAddress,
1621
+ externalServerKeyShares: [],
1622
+ derivationPath: wallet.derivationPath,
1623
+ thresholdSignatureScheme: wallet.thresholdSignatureScheme,
1624
+ externalServerKeySharesBackupInfo: {
1625
+ passwordEncrypted: false,
1626
+ backups: {
1627
+ dynamic: [],
1628
+ googleDrive: [],
1629
+ iCloud: [],
1630
+ user: [],
1631
+ external: [],
1632
+ delegated: []
1633
+ }
1634
+ }
1635
+ };
1636
+ // Cache the wallet
1637
+ wallets[walletId] = waasWallet;
1638
+ return waasWallet;
1639
+ };
1640
+
1641
+ const revokeDelegation = async (client, walletId)=>{
1642
+ try {
1643
+ // TODO: Uncomment when API method is implemented
1644
+ // const apiClient = createDelegatedApiClient(client);
1645
+ // await apiClient.revokeDelegation({
1646
+ // walletId,
1647
+ // });
1648
+ client.logger.info(`Delegation revoked for wallet: ${walletId}`);
1649
+ } catch (error) {
1650
+ client.logger.error('Error revoking delegation', error);
1651
+ throw new Error('Failed to revoke delegation');
1652
+ }
1653
+ };
1654
+
1655
+ export { DynamicWalletClient, base64ToBytes, bytesToBase64, createDelegatedWalletClient, delegatedSignMessage, ensureBase64Padding, formatEvmMessage, formatMessage, getExternalServerKeyShareBackupInfo, getMPCSignatureScheme, getMPCSigner, isHexString, mergeUniqueKeyShares, retryPromise, revokeDelegation, stringToBytes };