@dynamic-labs-wallet/node 0.0.353 → 1.0.0-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.esm.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export * from '#internal/core';
2
- import { MPC_RELAY_PROD_API_URL, getMPCChainConfig, BackupLocation, isAxiosError, handleAxiosError, Logger, DynamicApiClient, getClientThreshold, MPC_CONFIG, classifyForwardMpcError, getTSSConfig, serializeMessageForForwardMPC, getEnvironmentFromUrl, MPC_RELAY_URL_MAP, WalletOperation, getServerWalletReshareConfig, getRequiredExternalKeyShareId, ThresholdSignatureScheme, verifiedCredentialNameToChainEnum, DYNAMIC_AUTH_PROD_BASE_API_URL, DYNAMIC_FORWARD_MPC_ENCLAVE_URL_MAP, DYNAMIC_FORWARD_MPC_ENCLAVE_ATTESTATION_CONFIG_MAP } from '@dynamic-labs-wallet/core';
2
+ import { MPC_RELAY_PROD_API_URL, getMPCChainConfig, BackupLocation, isAxiosError, handleAxiosError, Logger, DynamicApiClient, getClientThreshold, MPC_CONFIG, classifyForwardMpcError, getTSSConfig, serializeMessageForForwardMPC, getEnvironmentFromUrl, MPC_RELAY_URL_MAP, WalletOperation, getServerWalletReshareConfig, getRequiredExternalKeyShareId, verifiedCredentialNameToChainEnum, ThresholdSignatureScheme, DYNAMIC_AUTH_PROD_BASE_API_URL, DYNAMIC_FORWARD_MPC_ENCLAVE_URL_MAP, DYNAMIC_FORWARD_MPC_ENCLAVE_ATTESTATION_CONFIG_MAP } from '@dynamic-labs-wallet/core';
3
3
  export { SOLANA_RPC_URL, ThresholdSignatureScheme, WalletOperation, getMPCChainConfig } from '@dynamic-labs-wallet/core';
4
4
  import { SigningAlgorithm } from '@dynamic-labs-wallet/primitives';
5
5
  import { BIP340, ExportableEd25519, Ecdsa, MessageHash, EcdsaSignature, EcdsaKeygenResult, ExportableEd25519KeygenResult, BIP340KeygenResult } from '#internal/node';
@@ -7,7 +7,7 @@ import { Logger as Logger$1 } from '@dynamic-labs/logger';
7
7
  import { ForwardMPCClientV2 } from '@dynamic-labs-wallet/forward-mpc-client';
8
8
  import { v4 } from 'uuid';
9
9
  import crypto from 'crypto';
10
- import 'node:crypto';
10
+ import crypto$1 from 'node:crypto';
11
11
 
12
12
  // Removed duplicate exports - these are already exported from #internal/core
13
13
  const getMPCSignatureScheme = ({ signingAlgorithm, baseRelayUrl = MPC_RELAY_PROD_API_URL })=>{
@@ -316,6 +316,33 @@ class DynamicWalletClient {
316
316
  throw new Error('Client must be authenticated before making API calls. Call authenticateApiToken first.');
317
317
  }
318
318
  }
319
+ /**
320
+ * Guards against silent operate-on-different-wallet bugs when callers pass
321
+ * both `accountAddress` and `walletMetadata`. Throws when either is missing
322
+ * or when they disagree.
323
+ *
324
+ * Comparison is case-insensitive only for EVM (where checksum casing is
325
+ * decoration over an underlying hex address). SVM (base58), BTC base58,
326
+ * and TON (base64url) are case-sensitive — two addresses that differ only
327
+ * in case are different addresses.
328
+ */ assertAddressMatchesMetadata(accountAddress, walletMetadata) {
329
+ var _walletMetadata_chainName;
330
+ if (!accountAddress) {
331
+ throw new Error('accountAddress is required and must not be empty.');
332
+ }
333
+ if (!walletMetadata.accountAddress) {
334
+ throw new Error('walletMetadata.accountAddress is required and must not be empty.');
335
+ }
336
+ // chainName comparison is case-insensitive — chain clients set the
337
+ // canonical uppercase form, but server-fetched walletMetadata could
338
+ // theoretically arrive with a different case.
339
+ const isEvm = ((_walletMetadata_chainName = walletMetadata.chainName) == null ? void 0 : _walletMetadata_chainName.toUpperCase()) === 'EVM';
340
+ const left = isEvm ? accountAddress.toLowerCase() : accountAddress;
341
+ const right = isEvm ? walletMetadata.accountAddress.toLowerCase() : walletMetadata.accountAddress;
342
+ if (left !== right) {
343
+ throw new Error('accountAddress mismatch: parameter does not match walletMetadata.accountAddress. ' + 'Pass them consistently — preferably read from walletMetadata.accountAddress.');
344
+ }
345
+ }
319
346
  async authenticateApiToken(authToken) {
320
347
  const tmpClient = new DynamicApiClient({
321
348
  environmentId: this.environmentId,
@@ -337,6 +364,57 @@ class DynamicWalletClient {
337
364
  });
338
365
  this.isApiClientAuthenticated = true;
339
366
  }
367
+ /**
368
+ * Fetches non-sensitive wallet identity (walletId, accountAddress, chainName,
369
+ * derivationPath, thresholdSignatureScheme) from the Dynamic API by
370
+ * `accountAddress`. SDK-scoped — works with the customer auth token from
371
+ * `authenticateApiToken`.
372
+ *
373
+ * **What this returns and what it does NOT return:**
374
+ * - Returns: wallet identity fields above.
375
+ * - Does NOT return: `externalServerKeySharesBackupInfo` (the per-share
376
+ * pointer metadata that drives recovery), `addressType`, or any
377
+ * encrypted material. The byAddress endpoint is intentionally a slim
378
+ * identity lookup; pointer metadata and addressType only come from the
379
+ * `createWalletAccount` / `importPrivateKey` return values.
380
+ *
381
+ * **Customer responsibility:**
382
+ * Persist the FULL `walletMetadata` returned by `createWalletAccount` /
383
+ * `importPrivateKey` (which includes `externalServerKeySharesBackupInfo` and
384
+ * `addressType`). `fetchWalletMetadata` recovers identity only — it cannot
385
+ * substitute for a properly-cached `walletMetadata` after a Redis wipe if
386
+ * the operation needs `backupInfo` (sign with auto-recovery, password
387
+ * checks, refresh, etc.).
388
+ *
389
+ * Does not cache, does not mutate SDK state.
390
+ */ async fetchWalletMetadata(accountAddress) {
391
+ this.ensureApiClientAuthenticated();
392
+ const { wallet } = await this.apiClient.getWaasWalletByAddress({
393
+ walletAddress: accountAddress
394
+ });
395
+ return {
396
+ walletId: wallet.walletId,
397
+ accountAddress: wallet.accountAddress,
398
+ chainName: wallet.chainName,
399
+ thresholdSignatureScheme: wallet.thresholdSignatureScheme,
400
+ derivationPath: wallet.derivationPath
401
+ };
402
+ }
403
+ /**
404
+ * Returns the cached `externalServerKeySharesBackupInfo` from
405
+ * `walletMetadata`. The Dynamic API does not currently expose a SDK-scoped
406
+ * endpoint that returns the keyShare pointer metadata by address (only the
407
+ * unbounded `getUser` does, which we avoid for server wallets). So the
408
+ * customer must persist the full `walletMetadata` (with backupInfo)
409
+ * returned by `createWalletAccount` / `importPrivateKey` and pass it back
410
+ * in on subsequent calls. Throws loudly when missing — never silently
411
+ * synthesizes empty state.
412
+ */ async resolveBackupInfo(walletMetadata) {
413
+ if (!walletMetadata.externalServerKeySharesBackupInfo) {
414
+ throw new Error('walletMetadata.externalServerKeySharesBackupInfo is required. ' + 'Persist the full walletMetadata returned by createWalletAccount/importPrivateKey ' + '(including externalServerKeySharesBackupInfo) and pass it back in on subsequent calls.');
415
+ }
416
+ return walletMetadata.externalServerKeySharesBackupInfo;
417
+ }
340
418
  async dynamicServerInitializeKeyGen({ chainName, externalServerKeygenIds, thresholdSignatureScheme, dynamicRequestId, skipLock, bitcoinConfig, onError, onCeremonyComplete }) {
341
419
  this.ensureApiClientAuthenticated();
342
420
  try {
@@ -513,7 +591,16 @@ class DynamicWalletClient {
513
591
  throw new Error('Error deriving public key in externalServerKeyGen');
514
592
  }
515
593
  }
516
- async keyGen({ chainName, thresholdSignatureScheme, skipLock, bitcoinConfig, onError, onCeremonyComplete }) {
594
+ validatePasswordForBackup({ password, backUpToDynamic }) {
595
+ if (backUpToDynamic && (!password || password === this.environmentId)) {
596
+ throw new Error('A password is required when backing up to Dynamic. ' + 'You can use the same password for all wallets or a unique password per wallet.');
597
+ }
598
+ }
599
+ async keyGen({ chainName, thresholdSignatureScheme, skipLock, bitcoinConfig, password, backUpToDynamic, onError, onCeremonyComplete }) {
600
+ this.validatePasswordForBackup({
601
+ password,
602
+ backUpToDynamic
603
+ });
517
604
  const dynamicRequestId = v4();
518
605
  try {
519
606
  const externalServerInitKeygenResults = await this.externalServerInitializeKeyGen({
@@ -548,7 +635,11 @@ class DynamicWalletClient {
548
635
  throw new Error('Error creating wallet account in keyGen');
549
636
  }
550
637
  }
551
- async importRawPrivateKey({ chainName, privateKey, thresholdSignatureScheme, bitcoinConfig, onError, onCeremonyComplete }) {
638
+ async importRawPrivateKey({ chainName, privateKey, thresholdSignatureScheme, bitcoinConfig, password, backUpToDynamic, onError, onCeremonyComplete }) {
639
+ this.validatePasswordForBackup({
640
+ password,
641
+ backUpToDynamic
642
+ });
552
643
  this.ensureApiClientAuthenticated();
553
644
  const dynamicRequestId = v4();
554
645
  const mpcSigner = getMPCSigner({
@@ -648,9 +739,13 @@ class DynamicWalletClient {
648
739
  this.logger.debug('Signing message with MPC Socket Client');
649
740
  const environment = getEnvironmentFromUrl(this.baseApiUrl);
650
741
  const defaultRelayUrl = MPC_RELAY_URL_MAP[environment];
742
+ // Ed25519 chains (SVM, TON) must send the formatted (hex) message — the
743
+ // forward-MPC server decodes it as hex before signing. Raw plaintext for
744
+ // odd-length messages produces "hex string expected, got unpadded hex of length N".
745
+ const isEd25519Chain = chainName === 'SVM' || chainName === 'TON';
651
746
  const { signature: signatureBytes } = await this.apiClient.forwardMPCClient.signMessage({
652
747
  keyshare: keyShare,
653
- message: chainName === 'SVM' ? formattedMessage : messageForForwardMPC,
748
+ message: isEd25519Chain ? formattedMessage : messageForForwardMPC,
654
749
  relayDomain: this.baseMPCRelayApiUrl || defaultRelayUrl,
655
750
  signingAlgo: chainName === 'EVM' ? 'ECDSA' : 'ED25519',
656
751
  hashAlgo: chainName === 'EVM' && !isFormatted ? 'keccak256' : undefined,
@@ -718,48 +813,59 @@ class DynamicWalletClient {
718
813
  return undefined;
719
814
  }
720
815
  }
721
- async sign({ accountAddress, externalServerKeyShares, message, chainName, password = undefined, isFormatted = false, context, onError, bitcoinConfig }) {
816
+ async sign({ accountAddress, externalServerKeyShares, message, chainName, password = undefined, isFormatted = false, context, onError, bitcoinConfig, walletMetadata, walletOperation = WalletOperation.SIGN_MESSAGE, passwordPreVerified = false }) {
722
817
  try {
723
- await this.verifyPassword({
724
- accountAddress,
725
- password
726
- });
727
- const wallet = await this.getWallet({
818
+ this.assertAddressMatchesMetadata(accountAddress, walletMetadata);
819
+ const backupInfo = walletMetadata.externalServerKeySharesBackupInfo;
820
+ // Recover internally if no shares supplied — decryption with `password`
821
+ // implicitly validates it, so we won't re-run verifyPassword below.
822
+ const resolvedShares = externalServerKeyShares && externalServerKeyShares.length > 0 ? externalServerKeyShares : await this.resolveKeyShares({
728
823
  accountAddress,
729
- walletOperation: WalletOperation.SIGN_MESSAGE,
730
- password
824
+ password,
825
+ walletOperation,
826
+ externalServerKeyShares,
827
+ errorMessage: 'External server key shares are required to sign a message',
828
+ walletMetadata,
829
+ backupInfo
731
830
  });
732
- externalServerKeyShares = externalServerKeyShares != null ? externalServerKeyShares : wallet.externalServerKeyShares;
733
- if (!externalServerKeyShares) {
734
- var _wallet_externalServerKeySharesBackupInfo;
735
- const hasExternalKeyShareId = Object.values(((_wallet_externalServerKeySharesBackupInfo = wallet.externalServerKeySharesBackupInfo) == null ? void 0 : _wallet_externalServerKeySharesBackupInfo.backups) || {}).some((locations)=>locations.some((loc)=>loc.location === BackupLocation.EXTERNAL));
736
- const errorMessage = 'External server key shares are required to sign a message';
737
- if (hasExternalKeyShareId) {
738
- this.logger.error(errorMessage);
739
- } else {
740
- this.logger.warn(errorMessage);
831
+ const sharesProvided = resolvedShares === externalServerKeyShares;
832
+ // verifyPassword only when shares came from the caller (no implicit
833
+ // validation). BTC sets `passwordPreVerified` when it pre-resolved.
834
+ // Refuse to silently sign without verifying missing backupInfo on
835
+ // this branch (e.g. caller built walletMetadata via fetchWalletMetadata
836
+ // identity-only path) would otherwise let any password through.
837
+ if (sharesProvided && !passwordPreVerified) {
838
+ if (!backupInfo) {
839
+ throw new Error('walletMetadata.externalServerKeySharesBackupInfo is required to verify the password ' + 'when signing with caller-supplied externalServerKeyShares. Persist the full walletMetadata ' + 'returned by createWalletAccount/importPrivateKey and pass it back in. ' + 'fetchWalletMetadata returns identity-only metadata and cannot be used on this path.');
741
840
  }
742
- throw new Error(errorMessage);
841
+ await this.verifyPassword({
842
+ accountAddress,
843
+ password,
844
+ walletMetadata,
845
+ backupInfo
846
+ });
743
847
  }
848
+ const walletId = walletMetadata.walletId;
849
+ const derivationPathStr = walletMetadata.derivationPath;
744
850
  // Perform the dynamic server sign
745
851
  const data = await this.dynamicServerSign({
746
- walletId: wallet.walletId,
852
+ walletId,
747
853
  message,
748
854
  isFormatted,
749
855
  context,
750
856
  bitcoinConfig,
751
857
  onError
752
858
  });
753
- const derivationPath = wallet.derivationPath && wallet.derivationPath != '' ? new Uint32Array(Object.values(JSON.parse(wallet.derivationPath))) : undefined;
859
+ const derivationPath = derivationPathStr && derivationPathStr != '' ? new Uint32Array(Object.values(JSON.parse(derivationPathStr))) : undefined;
754
860
  // Try forward MPC first if enabled, with fallback to relay-based signing on attestation failure
755
861
  const forwardMpcSignature = await this.trySignViaForwardMpc({
756
862
  chainName,
757
863
  message,
758
864
  roomId: data.roomId,
759
- keyShare: externalServerKeyShares[0],
865
+ keyShare: resolvedShares[0],
760
866
  derivationPath,
761
867
  isFormatted,
762
- walletId: wallet.walletId,
868
+ walletId,
763
869
  dynamicRequestId: data.dynamicRequestId
764
870
  });
765
871
  if (forwardMpcSignature !== undefined) {
@@ -770,7 +876,7 @@ class DynamicWalletClient {
770
876
  chainName,
771
877
  message,
772
878
  roomId: data.roomId,
773
- keyShare: externalServerKeyShares[0],
879
+ keyShare: resolvedShares[0],
774
880
  derivationPath,
775
881
  isFormatted,
776
882
  bitcoinConfig
@@ -782,41 +888,47 @@ class DynamicWalletClient {
782
888
  throw error;
783
889
  }
784
890
  }
785
- async refreshWalletAccountShares({ accountAddress, chainName, password = undefined, externalServerKeyShares, backUpToClientShareService = false }) {
891
+ async refreshWalletAccountShares({ accountAddress, chainName, password = undefined, externalServerKeyShares, backUpToDynamic = false, walletMetadata }) {
892
+ this.validatePasswordForBackup({
893
+ password,
894
+ backUpToDynamic
895
+ });
786
896
  this.ensureApiClientAuthenticated();
897
+ this.assertAddressMatchesMetadata(accountAddress, walletMetadata);
898
+ const backupInfo = await this.resolveBackupInfo(walletMetadata);
787
899
  await this.verifyPassword({
788
900
  accountAddress,
789
- password
790
- });
791
- const wallet = await this.getWallet({
792
- accountAddress,
793
- walletOperation: WalletOperation.REFRESH,
794
- password
901
+ password,
902
+ walletMetadata,
903
+ backupInfo
795
904
  });
905
+ const bitcoinConfig = chainName === 'BTC' && walletMetadata.addressType ? {
906
+ addressType: walletMetadata.addressType
907
+ } : undefined;
796
908
  const mpcSigner = getMPCSigner({
797
909
  chainName,
798
- baseRelayUrl: this.baseMPCRelayApiUrl
910
+ baseRelayUrl: this.baseMPCRelayApiUrl,
911
+ bitcoinConfig
799
912
  });
800
- // Create the room and refresh the shares
801
913
  const data = await this.apiClient.refreshWalletAccountShares({
802
- walletId: wallet.walletId
914
+ walletId: walletMetadata.walletId
803
915
  });
804
916
  const roomId = data.roomId;
805
- externalServerKeyShares = externalServerKeyShares != null ? externalServerKeyShares : wallet.externalServerKeyShares;
806
917
  if (!externalServerKeyShares) {
807
918
  throw new Error('External server key shares are required to refresh');
808
919
  }
809
920
  const refreshResults = await Promise.all(externalServerKeyShares.map((serverKeyShare)=>mpcSigner.refresh(roomId, serverKeyShare)));
810
- this.walletMap[accountAddress] = _extends({}, this.walletMap[accountAddress], {
811
- externalServerKeyShares: refreshResults,
812
- externalServerKeySharesBackupInfo: getExternalServerKeyShareBackupInfo()
813
- });
814
- await this.storeEncryptedBackupByWallet({
921
+ const { backupInfo: updatedBackupInfo } = await this.storeEncryptedBackupByWallet({
815
922
  accountAddress,
816
923
  password: password != null ? password : this.environmentId,
817
- backUpToClientShareService
924
+ backUpToDynamic,
925
+ externalServerKeyShares: refreshResults,
926
+ walletMetadata
818
927
  });
819
- return refreshResults;
928
+ return {
929
+ externalServerKeyShares: refreshResults,
930
+ backupInfo: updatedBackupInfo
931
+ };
820
932
  }
821
933
  async getExportId({ chainName, serverKeyShare, bitcoinConfig }) {
822
934
  const mpcSigner = getMPCSigner({
@@ -874,31 +986,29 @@ class DynamicWalletClient {
874
986
  existingExternalServerKeyShares
875
987
  };
876
988
  }
877
- async reshare({ chainName, accountAddress, oldThresholdSignatureScheme, newThresholdSignatureScheme, password = undefined, externalServerKeyShares, backUpToClientShareService = false }) {
989
+ async reshare({ chainName, accountAddress, oldThresholdSignatureScheme, newThresholdSignatureScheme, password = undefined, externalServerKeyShares, backUpToDynamic = false, walletMetadata }) {
990
+ this.validatePasswordForBackup({
991
+ password,
992
+ backUpToDynamic
993
+ });
878
994
  this.ensureApiClientAuthenticated();
995
+ this.assertAddressMatchesMetadata(accountAddress, walletMetadata);
996
+ const backupInfo = await this.resolveBackupInfo(walletMetadata);
879
997
  await this.verifyPassword({
880
998
  accountAddress,
881
- password
882
- });
883
- const { existingExternalServerShareCount } = getServerWalletReshareConfig({
884
- oldThresholdSignatureScheme,
885
- newThresholdSignatureScheme
886
- });
887
- const wallet = await this.getWallet({
888
- accountAddress,
889
- walletOperation: WalletOperation.RESHARE,
890
- shareCount: existingExternalServerShareCount,
891
- password
999
+ password,
1000
+ walletMetadata,
1001
+ backupInfo
892
1002
  });
893
- externalServerKeyShares = externalServerKeyShares != null ? externalServerKeyShares : wallet.externalServerKeyShares;
894
1003
  if (!externalServerKeyShares) {
895
1004
  throw new Error('External server key shares are required to reshare');
896
1005
  }
897
1006
  const { newExternalServerInitKeygenResults, newExternalServerKeygenIds, existingExternalServerKeygenIds, existingExternalServerKeyShares } = await this.reshareStrategy({
898
1007
  chainName,
899
- wallet: _extends({}, wallet, {
1008
+ wallet: {
1009
+ addressType: walletMetadata.addressType,
900
1010
  externalServerKeyShares
901
- }),
1011
+ },
902
1012
  oldThresholdSignatureScheme,
903
1013
  newThresholdSignatureScheme
904
1014
  });
@@ -906,15 +1016,13 @@ class DynamicWalletClient {
906
1016
  ...newExternalServerKeygenIds,
907
1017
  ...existingExternalServerKeygenIds
908
1018
  ];
909
- // Server to create the room and complete the server reshare logics
910
1019
  const data = await this.apiClient.reshare({
911
- walletId: wallet.walletId,
1020
+ walletId: walletMetadata.walletId,
912
1021
  clientKeygenIds: externalServerKeygenIds,
913
1022
  oldThresholdSignatureScheme,
914
1023
  newThresholdSignatureScheme
915
1024
  });
916
1025
  const { roomId, serverKeygenIds: dynamicServerKeygenIds, newServerKeygenIds: newDynamicServerKeygenIds = [] } = data;
917
- // Get the MPC config for the threshold signature scheme
918
1026
  const oldMpcConfig = MPC_CONFIG[oldThresholdSignatureScheme];
919
1027
  const newMpcConfig = MPC_CONFIG[newThresholdSignatureScheme];
920
1028
  const allPartyKeygenIds = [
@@ -922,40 +1030,60 @@ class DynamicWalletClient {
922
1030
  ...dynamicServerKeygenIds,
923
1031
  ...newDynamicServerKeygenIds
924
1032
  ];
1033
+ const bitcoinConfig = chainName === 'BTC' && walletMetadata.addressType ? {
1034
+ addressType: walletMetadata.addressType
1035
+ } : undefined;
925
1036
  const mpcSigner = getMPCSigner({
926
1037
  chainName,
927
- baseRelayUrl: this.baseMPCRelayApiUrl
1038
+ baseRelayUrl: this.baseMPCRelayApiUrl,
1039
+ bitcoinConfig
928
1040
  });
929
1041
  const reshareResults = await Promise.all([
930
1042
  ...newExternalServerInitKeygenResults.map((keygenResult)=>mpcSigner.reshareNewParty(roomId, oldMpcConfig.threshold, newMpcConfig.threshold, keygenResult, allPartyKeygenIds)),
931
1043
  ...existingExternalServerKeyShares.map((keyShare)=>mpcSigner.reshareRemainingParty(roomId, newMpcConfig.threshold, keyShare, allPartyKeygenIds))
932
1044
  ]);
933
- this.walletMap[accountAddress] = _extends({}, this.walletMap[accountAddress], {
934
- externalServerKeyShares: reshareResults,
935
- externalServerKeySharesBackupInfo: getExternalServerKeyShareBackupInfo()
936
- });
937
- await this.storeEncryptedBackupByWallet({
1045
+ const { backupInfo: updatedBackupInfo } = await this.storeEncryptedBackupByWallet({
938
1046
  accountAddress,
939
1047
  password,
940
- backUpToClientShareService
1048
+ backUpToDynamic,
1049
+ externalServerKeyShares: reshareResults,
1050
+ walletMetadata
941
1051
  });
942
- return reshareResults;
1052
+ return {
1053
+ externalServerKeyShares: reshareResults,
1054
+ backupInfo: updatedBackupInfo
1055
+ };
943
1056
  }
944
- async exportKey({ accountAddress, chainName, password = undefined, externalServerKeyShares, bitcoinConfig, elevatedAccessToken }) {
1057
+ async exportKey({ accountAddress, chainName, password = undefined, externalServerKeyShares, bitcoinConfig, elevatedAccessToken, walletMetadata }) {
945
1058
  this.ensureApiClientAuthenticated();
946
- await this.verifyPassword({
947
- accountAddress,
948
- password
949
- });
950
- const wallet = await this.getWallet({
1059
+ this.assertAddressMatchesMetadata(accountAddress, walletMetadata);
1060
+ const backupInfo = walletMetadata.externalServerKeySharesBackupInfo;
1061
+ const resolvedShares = externalServerKeyShares && externalServerKeyShares.length > 0 ? externalServerKeyShares : await this.resolveKeyShares({
951
1062
  accountAddress,
952
1063
  password,
953
- walletOperation: WalletOperation.EXPORT_PRIVATE_KEY
1064
+ walletOperation: WalletOperation.EXPORT_PRIVATE_KEY,
1065
+ externalServerKeyShares,
1066
+ errorMessage: 'External server key shares are required to export a private key',
1067
+ walletMetadata,
1068
+ backupInfo
954
1069
  });
955
- externalServerKeyShares = externalServerKeyShares != null ? externalServerKeyShares : wallet.externalServerKeyShares;
956
- if (!externalServerKeyShares) {
957
- throw new Error('External server key shares are required to export a private key');
1070
+ const sharesProvided = resolvedShares === externalServerKeyShares;
1071
+ // Refuse to export without verifying — exportKey returns the raw private
1072
+ // key, so a missing backupInfo on the caller-supplied branch would let
1073
+ // any password through. See sign() for the analogous guard.
1074
+ if (sharesProvided) {
1075
+ if (!backupInfo) {
1076
+ throw new Error('walletMetadata.externalServerKeySharesBackupInfo is required to verify the password ' + 'when exporting with caller-supplied externalServerKeyShares. Persist the full walletMetadata ' + 'returned by createWalletAccount/importPrivateKey and pass it back in. ' + 'fetchWalletMetadata returns identity-only metadata and cannot be used on this path.');
1077
+ }
1078
+ await this.verifyPassword({
1079
+ accountAddress,
1080
+ password,
1081
+ walletMetadata,
1082
+ backupInfo
1083
+ });
958
1084
  }
1085
+ const walletId = walletMetadata.walletId;
1086
+ const derivationPathStr = walletMetadata.derivationPath;
959
1087
  const mpcSigner = getMPCSigner({
960
1088
  chainName,
961
1089
  baseRelayUrl: this.baseMPCRelayApiUrl,
@@ -963,20 +1091,20 @@ class DynamicWalletClient {
963
1091
  });
964
1092
  const exportId = await this.getExportId({
965
1093
  chainName,
966
- serverKeyShare: externalServerKeyShares[0],
1094
+ serverKeyShare: resolvedShares[0],
967
1095
  bitcoinConfig
968
1096
  });
969
1097
  const data = await this.apiClient.exportKey({
970
- walletId: wallet.walletId,
1098
+ walletId,
971
1099
  exportId,
972
1100
  bitcoinConfig,
973
1101
  elevatedAccessToken
974
1102
  });
975
- const keyExportRaw = await mpcSigner.exportFullPrivateKey(data.roomId, externalServerKeyShares[0], exportId);
1103
+ const keyExportRaw = await mpcSigner.exportFullPrivateKey(data.roomId, resolvedShares[0], exportId);
976
1104
  if (!keyExportRaw) {
977
1105
  throw new Error('Error exporting private key');
978
1106
  }
979
- const derivationPath = wallet.derivationPath && wallet.derivationPath != '' ? new Uint32Array(Object.values(JSON.parse(wallet.derivationPath))) : undefined;
1107
+ const derivationPath = derivationPathStr && derivationPathStr != '' ? new Uint32Array(Object.values(JSON.parse(derivationPathStr))) : undefined;
980
1108
  let derivedPrivateKey;
981
1109
  if (mpcSigner instanceof Ecdsa) {
982
1110
  derivedPrivateKey = await mpcSigner.derivePrivateKeyFromXpriv(keyExportRaw, derivationPath);
@@ -1033,53 +1161,36 @@ class DynamicWalletClient {
1033
1161
  const serializedEncryptedKeyShare = Buffer.from(JSON.stringify(encryptedKeyShare)).toString('base64');
1034
1162
  return serializedEncryptedKeyShare;
1035
1163
  }
1036
- async ensureCeremonyCompletionBeforeBackup({ accountAddress }) {
1037
- let retries = 0;
1038
- const maxRetries = 3;
1039
- while((!this.walletMap[accountAddress] || !this.walletMap[accountAddress].walletId) && retries < maxRetries){
1040
- await new Promise((resolve)=>setTimeout(resolve, 1000)); // Wait 1 second
1041
- retries++;
1042
- }
1043
- if (!this.walletMap[accountAddress] || !this.walletMap[accountAddress].walletId) {
1044
- throw new Error('Ceremony completion timeout');
1045
- }
1046
- }
1047
- async storeEncryptedBackupByWallet({ accountAddress, externalServerKeyShares = undefined, password = undefined, backUpToClientShareService }) {
1164
+ async storeEncryptedBackupByWallet({ accountAddress, externalServerKeyShares = undefined, password = undefined, backUpToDynamic, walletMetadata }) {
1048
1165
  this.ensureApiClientAuthenticated();
1049
- //add retry logic for ceremony completion to prevent race condition
1050
- await this.ensureCeremonyCompletionBeforeBackup({
1051
- accountAddress
1052
- });
1166
+ this.assertAddressMatchesMetadata(accountAddress, walletMetadata);
1053
1167
  const dynamicRequestId = v4();
1054
1168
  try {
1055
1169
  const keySharesToBackup = externalServerKeyShares != null ? externalServerKeyShares : await this.getExternalServerKeyShares({
1056
1170
  accountAddress,
1057
- password
1171
+ password,
1172
+ walletMetadata
1058
1173
  });
1059
1174
  if (!keySharesToBackup || keySharesToBackup.length === 0) {
1060
- throw new Error(`Key shares not found for accountAddress: ${accountAddress}`);
1175
+ throw new Error('Key shares not found for the requested wallet.');
1061
1176
  }
1062
1177
  const encryptedKeyShares = await Promise.all(keySharesToBackup.map((keyShare)=>this.encryptKeyShare({
1063
1178
  keyShare,
1064
1179
  password
1065
1180
  })));
1066
- if (!this.walletMap[accountAddress] || !this.walletMap[accountAddress].walletId) {
1067
- throw new Error(`WalletId not found for accountAddress: ${accountAddress}`);
1068
- }
1181
+ const { walletId, chainName, addressType } = walletMetadata;
1069
1182
  const passwordEncryptedFlag = Boolean(password) && password !== this.environmentId;
1070
- // Derive bitcoinConfig for BTC wallets
1071
- const walletData = this.walletMap[accountAddress];
1072
- const bitcoinConfig = walletData.chainName === 'BTC' && walletData.addressType ? {
1073
- addressType: walletData.addressType
1183
+ const bitcoinConfig = chainName === 'BTC' && addressType ? {
1184
+ addressType: addressType
1074
1185
  } : undefined;
1075
1186
  // Build locations + result for all shares
1076
1187
  const locations = [];
1077
1188
  const result = [];
1078
1189
  // Upload first share to Dynamic if requested
1079
- if (backUpToClientShareService) {
1190
+ if (backUpToDynamic) {
1080
1191
  const keyShareToBackupToDynamic = keySharesToBackup[0];
1081
1192
  const data = await this.apiClient.storeEncryptedBackupByWallet({
1082
- walletId: this.walletMap[accountAddress].walletId,
1193
+ walletId,
1083
1194
  encryptedKeyShares: [
1084
1195
  encryptedKeyShares[0]
1085
1196
  ],
@@ -1089,12 +1200,17 @@ class DynamicWalletClient {
1089
1200
  dynamicRequestId
1090
1201
  });
1091
1202
  const keygenId = await this.getExportId({
1092
- chainName: walletData.chainName,
1203
+ chainName,
1093
1204
  serverKeyShare: keyShareToBackupToDynamic,
1094
1205
  bitcoinConfig
1095
1206
  });
1096
1207
  locations.push({
1097
1208
  location: BackupLocation.DYNAMIC,
1209
+ // `id` and `externalKeyShareId` are the same server-assigned identifier
1210
+ // for dynamic backups; populating both keeps the synthesized backupInfo
1211
+ // shape identical to what `getExternalServerKeyShareBackupInfo` produces
1212
+ // from the API response (used by fetchWalletMetadata / getWalletByAddress).
1213
+ id: data.keyShareIds[0],
1098
1214
  externalKeyShareId: data.keyShareIds[0],
1099
1215
  passwordEncrypted: passwordEncryptedFlag,
1100
1216
  keygenId
@@ -1104,10 +1220,10 @@ class DynamicWalletClient {
1104
1220
  backedUpToClientKeyShareService: true
1105
1221
  });
1106
1222
  }
1107
- const keySharesNotToBackupToDynamic = backUpToClientShareService ? keySharesToBackup.slice(1) : keySharesToBackup;
1223
+ const keySharesNotToBackupToDynamic = backUpToDynamic ? keySharesToBackup.slice(1) : keySharesToBackup;
1108
1224
  for (const keyShare of keySharesNotToBackupToDynamic){
1109
1225
  const keygenId = await this.getExportId({
1110
- chainName: walletData.chainName,
1226
+ chainName,
1111
1227
  serverKeyShare: keyShare,
1112
1228
  bitcoinConfig
1113
1229
  });
@@ -1121,28 +1237,53 @@ class DynamicWalletClient {
1121
1237
  backedUpToClientKeyShareService: false
1122
1238
  });
1123
1239
  }
1124
- const backupData = await this.apiClient.markKeySharesAsBackedUp({
1125
- walletId: this.walletMap[accountAddress].walletId,
1240
+ const markResponse = await this.apiClient.markKeySharesAsBackedUp({
1241
+ walletId,
1126
1242
  locations,
1127
1243
  dynamicRequestId
1128
1244
  });
1129
- const updatedBackupInfo = getExternalServerKeyShareBackupInfo({
1130
- walletProperties: {
1131
- derivationPath: this.walletMap[accountAddress].derivationPath,
1132
- keyShares: backupData.locationsWithKeyShares.map((ks)=>({
1133
- id: ks.id,
1134
- keygenId: ks.keygenId,
1135
- backupLocation: ks.location,
1136
- externalKeyShareId: ks.externalKeyShareId,
1137
- passwordEncrypted: passwordEncryptedFlag
1138
- })),
1139
- thresholdSignatureScheme: this.walletMap[accountAddress].thresholdSignatureScheme
1140
- }
1141
- });
1142
- this.walletMap[accountAddress] = _extends({}, this.walletMap[accountAddress], {
1143
- externalServerKeySharesBackupInfo: updatedBackupInfo
1144
- });
1145
- return result;
1245
+ // Build backupInfo from the server response (`locationsWithKeyShares`) so
1246
+ // the cached backupInfo always agrees with what the server actually
1247
+ // stored — even if the server normalizes ids/keygenIds. The server
1248
+ // response does not include `passwordEncrypted` per location, so merge
1249
+ // the local `passwordEncryptedFlag` (a single boolean for this batch).
1250
+ // The fallback to local `locations` is defensive; warn loudly when it
1251
+ // fires so a server contract regression doesn't silently degrade.
1252
+ if (!(markResponse == null ? void 0 : markResponse.locationsWithKeyShares)) {
1253
+ this.logger.warn('markKeySharesAsBackedUp response missing locationsWithKeyShares; falling back to local synthesis. ' + 'This may indicate a server contract regression.');
1254
+ }
1255
+ var _markResponse_locationsWithKeyShares;
1256
+ const serverLocations = (_markResponse_locationsWithKeyShares = markResponse == null ? void 0 : markResponse.locationsWithKeyShares) != null ? _markResponse_locationsWithKeyShares : locations;
1257
+ const backupInfo = {
1258
+ backups: serverLocations.reduce((acc, loc)=>{
1259
+ // Defensive: if the server returns an unrecognized location enum
1260
+ // (e.g. a future BackupLocation we don't know yet), initialize
1261
+ // the bucket rather than throwing on `acc[unknown].push(...)`.
1262
+ if (!acc[loc.location]) {
1263
+ acc[loc.location] = [];
1264
+ }
1265
+ acc[loc.location].push({
1266
+ location: loc.location,
1267
+ id: loc.id,
1268
+ keygenId: loc.keygenId,
1269
+ externalKeyShareId: loc.externalKeyShareId,
1270
+ passwordEncrypted: passwordEncryptedFlag
1271
+ });
1272
+ return acc;
1273
+ }, {
1274
+ [BackupLocation.DYNAMIC]: [],
1275
+ [BackupLocation.GOOGLE_DRIVE]: [],
1276
+ [BackupLocation.ICLOUD]: [],
1277
+ [BackupLocation.USER]: [],
1278
+ [BackupLocation.EXTERNAL]: [],
1279
+ [BackupLocation.DELEGATED]: []
1280
+ }),
1281
+ passwordEncrypted: passwordEncryptedFlag
1282
+ };
1283
+ return {
1284
+ keySharesWithBackupStatus: result,
1285
+ backupInfo
1286
+ };
1146
1287
  } catch (error) {
1147
1288
  this.logger.error('Error in storeEncryptedBackupByWallet:', {
1148
1289
  accountAddress,
@@ -1152,12 +1293,13 @@ class DynamicWalletClient {
1152
1293
  throw error;
1153
1294
  }
1154
1295
  }
1155
- async storeEncryptedBackupByWalletWithRetry({ accountAddress, externalServerKeyShares, password, backUpToClientShareService }) {
1296
+ async storeEncryptedBackupByWalletWithRetry({ accountAddress, externalServerKeyShares, password, backUpToDynamic, walletMetadata }) {
1156
1297
  return retryPromise(()=>this.storeEncryptedBackupByWallet({
1157
1298
  accountAddress,
1158
1299
  externalServerKeyShares,
1159
1300
  password,
1160
- backUpToClientShareService
1301
+ backUpToDynamic,
1302
+ walletMetadata
1161
1303
  }), {
1162
1304
  operationName: 'store encrypted backup',
1163
1305
  logContext: {
@@ -1165,15 +1307,12 @@ class DynamicWalletClient {
1165
1307
  }
1166
1308
  });
1167
1309
  }
1168
- async getExternalServerKeyShares({ accountAddress, password }) {
1169
- var _wallet_externalServerKeySharesBackupInfo_backups, _wallet_externalServerKeySharesBackupInfo;
1170
- const wallet = await this.getWallet({
1171
- accountAddress,
1172
- password,
1173
- walletOperation: WalletOperation.REACH_THRESHOLD
1174
- });
1310
+ async getExternalServerKeyShares({ accountAddress, password, walletMetadata, backupInfo }) {
1311
+ var _resolvedBackupInfo_backups;
1312
+ this.assertAddressMatchesMetadata(accountAddress, walletMetadata);
1313
+ const resolvedBackupInfo = backupInfo != null ? backupInfo : await this.resolveBackupInfo(walletMetadata);
1175
1314
  // Check if the wallet has a dynamic backup and derive password encryption status from the first dynamic share
1176
- 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) || [];
1315
+ const dynamicBackups = (resolvedBackupInfo == null ? void 0 : (_resolvedBackupInfo_backups = resolvedBackupInfo.backups) == null ? void 0 : _resolvedBackupInfo_backups.dynamic) || [];
1177
1316
  const passwordEncrypted = dynamicBackups.length > 0 ? Boolean(dynamicBackups[0].passwordEncrypted) : false;
1178
1317
  if (passwordEncrypted && !password) {
1179
1318
  throw new Error('Password is required for decryption but not provided. This backup was encrypted with a password.');
@@ -1182,21 +1321,36 @@ class DynamicWalletClient {
1182
1321
  const recoveredShares = await this.recoverEncryptedBackupByWallet({
1183
1322
  accountAddress,
1184
1323
  password,
1185
- walletOperation: WalletOperation.REACH_THRESHOLD
1324
+ walletOperation: WalletOperation.REACH_THRESHOLD,
1325
+ walletMetadata,
1326
+ backupInfo: resolvedBackupInfo
1186
1327
  });
1187
1328
  return recoveredShares;
1188
1329
  }
1189
- async updatePassword({ accountAddress, existingPassword, newPassword, backUpToClientShareService }) {
1190
- await this.getWallet({
1330
+ async updatePassword({ accountAddress, existingPassword, newPassword, backUpToDynamic, externalServerKeyShares, walletMetadata }) {
1331
+ this.validatePasswordForBackup({
1332
+ password: newPassword,
1333
+ backUpToDynamic
1334
+ });
1335
+ this.assertAddressMatchesMetadata(accountAddress, walletMetadata);
1336
+ const sharesToBackup = await this.resolveKeyShares({
1191
1337
  accountAddress,
1192
1338
  password: existingPassword,
1193
- walletOperation: WalletOperation.REACH_ALL_PARTIES
1339
+ walletOperation: WalletOperation.REACH_ALL_PARTIES,
1340
+ externalServerKeyShares,
1341
+ errorMessage: 'External server key shares are required to update password',
1342
+ walletMetadata
1194
1343
  });
1195
- await this.storeEncryptedBackupByWallet({
1344
+ const { backupInfo } = await this.storeEncryptedBackupByWallet({
1196
1345
  accountAddress,
1197
1346
  password: newPassword,
1198
- backUpToClientShareService
1347
+ backUpToDynamic,
1348
+ externalServerKeyShares: sharesToBackup,
1349
+ walletMetadata
1199
1350
  });
1351
+ return {
1352
+ backupInfo
1353
+ };
1200
1354
  }
1201
1355
  async decryptKeyShare({ keyShare, password }) {
1202
1356
  const decodedKeyShare = JSON.parse(Buffer.from(keyShare, 'base64').toString());
@@ -1241,49 +1395,45 @@ class DynamicWalletClient {
1241
1395
  };
1242
1396
  }
1243
1397
  /**
1244
- * Attempts to recover key shares from backup if they are not provided.
1245
- * The recovered shares will be stored in the wallet map for use in signing operations.
1246
- * @param accountAddress - The account address to recover shares for
1398
+ * Resolves and returns key shares for wallet operations.
1399
+ * Checks in order: provided shares, then attempts recovery from backup.
1400
+ * @param accountAddress - The account address to get shares for
1247
1401
  * @param password - The password to decrypt the shares
1248
1402
  * @param walletOperation - The wallet operation being performed
1249
1403
  * @param externalServerKeyShares - The provided key shares (if any)
1250
- * @param errorMessage - The error message to throw if recovery fails
1251
- * @throws Error if recovery is needed but fails
1252
- */ async ensureKeySharesRecovered({ accountAddress, password, walletOperation, externalServerKeyShares, errorMessage }) {
1253
- if (!externalServerKeyShares || externalServerKeyShares.length === 0) {
1254
- // attempt to recover dynamic keyshares if no key shares are provided
1255
- // the shares will be used for signing in the node SDK if successful
1256
- const recoveredShares = await this.recoverEncryptedBackupByWallet({
1257
- accountAddress,
1258
- password,
1259
- walletOperation,
1260
- storeRecoveredShares: true
1261
- });
1262
- // If recovery returned empty and no shares were provided, throw error
1263
- if (!recoveredShares || recoveredShares.length === 0) {
1264
- throw new Error(errorMessage);
1265
- }
1404
+ * @param errorMessage - The error message to throw if no shares are found
1405
+ * @param walletMetadata - Required wallet metadata identifying the target wallet
1406
+ * @returns The resolved key shares
1407
+ * @throws Error if no shares are found and recovery fails
1408
+ */ async resolveKeyShares({ accountAddress, password, walletOperation, externalServerKeyShares, errorMessage, walletMetadata, backupInfo }) {
1409
+ if (externalServerKeyShares == null ? void 0 : externalServerKeyShares.length) {
1410
+ return externalServerKeyShares;
1411
+ }
1412
+ const recoveredShares = await this.recoverEncryptedBackupByWallet({
1413
+ accountAddress,
1414
+ password,
1415
+ walletOperation,
1416
+ walletMetadata,
1417
+ backupInfo
1418
+ });
1419
+ if (!recoveredShares || recoveredShares.length === 0) {
1420
+ throw new Error(errorMessage);
1266
1421
  }
1422
+ return recoveredShares;
1267
1423
  }
1268
- async recoverEncryptedBackupByWallet({ accountAddress, password, walletOperation, shareCount = undefined, storeRecoveredShares = true }) {
1269
- var _wallet_externalServerKeySharesBackupInfo;
1424
+ async recoverEncryptedBackupByWallet({ accountAddress, password, walletOperation, shareCount = undefined, walletMetadata, backupInfo }) {
1270
1425
  this.ensureApiClientAuthenticated();
1271
- let wallet = this.walletMap[accountAddress];
1272
- if (!wallet) {
1273
- const fetchedWallet = await this.getWalletByAddress(accountAddress);
1274
- if (!fetchedWallet) {
1275
- throw new Error(`Wallet not found for address 1: ${accountAddress}`);
1276
- }
1277
- wallet = fetchedWallet;
1278
- }
1426
+ this.assertAddressMatchesMetadata(accountAddress, walletMetadata);
1427
+ const walletId = walletMetadata.walletId;
1428
+ const thresholdSignatureScheme = walletMetadata.thresholdSignatureScheme;
1429
+ const externalServerKeySharesBackupInfo = backupInfo != null ? backupInfo : await this.resolveBackupInfo(walletMetadata);
1279
1430
  // PII: intentionally omitting full wallet object (contains key share info)
1280
1431
  this.logger.debug(`recoverEncryptedBackupByWallet wallet: ${walletOperation}`, {
1281
- walletId: wallet.walletId,
1282
- chainName: wallet.chainName
1432
+ walletId
1283
1433
  });
1284
1434
  const { shares } = this.recoverStrategy({
1285
- externalServerKeySharesBackupInfo: wallet.externalServerKeySharesBackupInfo,
1286
- thresholdSignatureScheme: wallet.thresholdSignatureScheme,
1435
+ externalServerKeySharesBackupInfo,
1436
+ thresholdSignatureScheme,
1287
1437
  walletOperation,
1288
1438
  shareCount
1289
1439
  });
@@ -1293,13 +1443,13 @@ class DynamicWalletClient {
1293
1443
  return [];
1294
1444
  }
1295
1445
  const data = await this.apiClient.recoverEncryptedBackupByWallet({
1296
- walletId: wallet.walletId,
1446
+ walletId,
1297
1447
  externalKeyShareIds: dynamicKeyShareIds,
1298
1448
  requiresSignedSessionId: false
1299
1449
  });
1300
1450
  const dynamicKeyShares = data.keyShares.filter((keyShare)=>keyShare.encryptedAccountCredential !== null && keyShare.backupLocation === BackupLocation.DYNAMIC);
1301
- var _wallet_externalServerKeySharesBackupInfo_passwordEncrypted;
1302
- const isPasswordEncrypted = (_wallet_externalServerKeySharesBackupInfo_passwordEncrypted = (_wallet_externalServerKeySharesBackupInfo = wallet.externalServerKeySharesBackupInfo) == null ? void 0 : _wallet_externalServerKeySharesBackupInfo.passwordEncrypted) != null ? _wallet_externalServerKeySharesBackupInfo_passwordEncrypted : false;
1451
+ var _externalServerKeySharesBackupInfo_passwordEncrypted;
1452
+ const isPasswordEncrypted = (_externalServerKeySharesBackupInfo_passwordEncrypted = externalServerKeySharesBackupInfo == null ? void 0 : externalServerKeySharesBackupInfo.passwordEncrypted) != null ? _externalServerKeySharesBackupInfo_passwordEncrypted : false;
1303
1453
  if (isPasswordEncrypted && !password) {
1304
1454
  throw new Error('Password is required for decryption but not provided. This backup was encrypted with a password.');
1305
1455
  }
@@ -1307,103 +1457,37 @@ class DynamicWalletClient {
1307
1457
  keyShare: keyShare.encryptedAccountCredential,
1308
1458
  password: password != null ? password : this.environmentId
1309
1459
  })));
1310
- if (storeRecoveredShares) {
1311
- this.walletMap[accountAddress] = _extends({}, this.walletMap[accountAddress], {
1312
- externalServerKeyShares: mergeUniqueKeyShares(this.walletMap[accountAddress].externalServerKeyShares || [], decryptedKeyShares)
1313
- });
1314
- }
1315
1460
  return decryptedKeyShares;
1316
1461
  }
1317
- async exportExternalServerKeyShares({ accountAddress, password }) {
1462
+ async exportExternalServerKeyShares({ accountAddress, password, walletMetadata }) {
1463
+ this.assertAddressMatchesMetadata(accountAddress, walletMetadata);
1464
+ const backupInfo = await this.resolveBackupInfo(walletMetadata);
1318
1465
  await this.verifyPassword({
1319
1466
  accountAddress,
1320
- password
1467
+ password,
1468
+ walletMetadata,
1469
+ backupInfo
1321
1470
  });
1322
1471
  const externalServerKeyShares = await this.getExternalServerKeyShares({
1323
1472
  accountAddress,
1324
- password
1473
+ password,
1474
+ walletMetadata,
1475
+ backupInfo
1325
1476
  });
1326
1477
  return externalServerKeyShares;
1327
1478
  }
1328
1479
  /**
1329
- * Helper function to check if the required wallet fields are present and valid
1330
- * @param accountAddress - The account address of the wallet to check
1331
- * @param walletOperation - The wallet operation that determines required fields
1332
- * @returns boolean indicating if wallet needs to be re-fetched and restored from server
1333
- */ async checkWalletFields({ accountAddress, walletOperation = WalletOperation.REACH_THRESHOLD, shareCount }) {
1334
- var _existingWallet_externalServerKeyShares;
1335
- let keyshareCheck = false;
1336
- let walletCheck = false;
1337
- let thresholdSignatureSchemeCheck = false;
1338
- let derivationPathCheck = false;
1339
- // check if wallet exists
1340
- const existingWallet = this.walletMap[accountAddress];
1341
- if (existingWallet) {
1342
- walletCheck = true;
1343
- }
1344
- // check if threshold signature scheme exists
1345
- if (existingWallet == null ? void 0 : existingWallet.thresholdSignatureScheme) {
1346
- thresholdSignatureSchemeCheck = true;
1347
- }
1348
- // check if derivation path exists
1349
- if ((existingWallet == null ? void 0 : existingWallet.derivationPath) || (existingWallet == null ? void 0 : existingWallet.derivationPath) === '') {
1350
- derivationPathCheck = true;
1351
- }
1352
- // check if wallet already exists with sufficient keyshares
1353
- if (existingWallet) {
1354
- var _existingWallet_externalServerKeyShares1;
1355
- const { shares } = this.recoverStrategy({
1356
- externalServerKeySharesBackupInfo: existingWallet.externalServerKeySharesBackupInfo || {
1357
- backups: getExternalServerKeyShareBackupInfo()
1358
- },
1359
- thresholdSignatureScheme: existingWallet.thresholdSignatureScheme,
1360
- walletOperation,
1361
- shareCount
1362
- });
1363
- const { dynamic: requiredDynamicKeyShareIds = [] } = shares;
1364
- const { external: requiredExternalKeyShareIds = [] } = shares;
1365
- // Check if we have enough shares from any backup location
1366
- const totalRequiredShares = requiredDynamicKeyShareIds.length + requiredExternalKeyShareIds.length;
1367
- // Check if we have the required shares either loaded OR available in backup
1368
- const hasLoadedShares = totalRequiredShares <= (((_existingWallet_externalServerKeyShares1 = existingWallet.externalServerKeyShares) == null ? void 0 : _existingWallet_externalServerKeyShares1.length) || 0);
1369
- // Check if backup contains the specific required share IDs
1370
- const hasBackupShares = existingWallet.externalServerKeySharesBackupInfo && (()=>{
1371
- const backupShares = existingWallet.externalServerKeySharesBackupInfo.backups.dynamic;
1372
- const allRequiredIds = [
1373
- ...requiredDynamicKeyShareIds,
1374
- ...requiredExternalKeyShareIds
1375
- ];
1376
- return allRequiredIds.every((requiredId)=>backupShares.some((backupShare)=>backupShare.externalKeyShareId === requiredId));
1377
- })();
1378
- if (hasLoadedShares || hasBackupShares) {
1379
- keyshareCheck = true;
1380
- }
1381
- }
1382
- const result = walletCheck && thresholdSignatureSchemeCheck && keyshareCheck && derivationPathCheck;
1383
- this.logger.debug('Wallet checks:', {
1384
- walletCheck,
1385
- thresholdSignatureSchemeCheck,
1386
- keyshareCheck,
1387
- derivationPathCheck,
1388
- existingWallet: !!existingWallet,
1389
- keySharesLength: existingWallet == null ? void 0 : (_existingWallet_externalServerKeyShares = existingWallet.externalServerKeyShares) == null ? void 0 : _existingWallet_externalServerKeyShares.length,
1390
- result
1391
- });
1392
- return result;
1393
- }
1394
- /**
1395
1480
  * verifyPassword attempts to recover and decrypt a single client key share using the provided password.
1396
1481
  * If successful, the key share is encrypted with the new password. This method solely performs the recovery
1397
1482
  * and decryption without storing the restored key shares. If unsuccessful, it throws an error.
1398
- */ async verifyPassword({ accountAddress, password = undefined }) {
1399
- await this.getWallet({
1400
- accountAddress,
1401
- password,
1402
- walletOperation: WalletOperation.NO_OPERATION
1403
- });
1483
+ */ async verifyPassword({ accountAddress, password = undefined, walletMetadata, backupInfo }) {
1484
+ this.assertAddressMatchesMetadata(accountAddress, walletMetadata);
1485
+ const resolvedBackupInfo = backupInfo != null ? backupInfo : await this.resolveBackupInfo(walletMetadata);
1404
1486
  if (await this.requiresPasswordForOperation({
1405
1487
  accountAddress,
1406
- walletOperation: WalletOperation.NO_OPERATION
1488
+ walletOperation: WalletOperation.NO_OPERATION,
1489
+ walletMetadata,
1490
+ backupInfo: resolvedBackupInfo
1407
1491
  }) && !password) {
1408
1492
  throw new Error('Password is required for operation but not provided');
1409
1493
  }
@@ -1411,9 +1495,7 @@ class DynamicWalletClient {
1411
1495
  if (!password) {
1412
1496
  return;
1413
1497
  }
1414
- const { backups } = await this.getWalletExternalServerKeyShareBackupInfo({
1415
- accountAddress
1416
- });
1498
+ const { backups } = resolvedBackupInfo;
1417
1499
  const { dynamic: dynamicKeyShareIds = [] } = backups;
1418
1500
  const { external: externalKeyShareIds = [] } = backups;
1419
1501
  // Check if we have any shares available (DYNAMIC or EXTERNAL)
@@ -1421,180 +1503,105 @@ class DynamicWalletClient {
1421
1503
  throw new Error('No key shares found');
1422
1504
  }
1423
1505
  try {
1424
- // Recover and store shares to unlock the wallet (storeRecoveredShares defaults to true)
1425
1506
  await this.recoverEncryptedBackupByWallet({
1426
1507
  accountAddress,
1427
1508
  password,
1428
- walletOperation: WalletOperation.NO_OPERATION
1509
+ walletOperation: WalletOperation.NO_OPERATION,
1510
+ walletMetadata,
1511
+ backupInfo: resolvedBackupInfo
1429
1512
  });
1430
1513
  } catch (error) {
1431
1514
  this.logger.error('Error in verifying password', error);
1432
- throw new Error('Incorrect password');
1515
+ // We can't distinguish wrong-password from stale-metadata without a
1516
+ // server-side backupInfo lookup (fetchWalletMetadata returns identity
1517
+ // only). Surface both possibilities so customers know where to look.
1518
+ throw new Error('Failed to verify password. Either the password is incorrect, or the cached `walletMetadata.externalServerKeySharesBackupInfo` is stale ' + '(merge the `backupInfo` returned from updatePassword/refreshWalletAccountShares/reshare into your cached walletMetadata).');
1433
1519
  }
1434
1520
  }
1435
- async isPasswordEncrypted({ accountAddress }) {
1436
- const externalServerKeySharesBackupInfo = await this.getWalletExternalServerKeyShareBackupInfo({
1437
- accountAddress
1521
+ async isPasswordEncrypted({ accountAddress, walletMetadata, backupInfo }) {
1522
+ const externalServerKeySharesBackupInfo = backupInfo != null ? backupInfo : await this.getWalletExternalServerKeyShareBackupInfo({
1523
+ walletMetadata
1438
1524
  });
1439
1525
  return externalServerKeySharesBackupInfo == null ? void 0 : externalServerKeySharesBackupInfo.passwordEncrypted;
1440
1526
  }
1441
1527
  /**
1442
1528
  * check if the operation requires a password
1443
- */ async requiresPasswordForOperation({ accountAddress, walletOperation = WalletOperation.REACH_THRESHOLD }) {
1529
+ */ async requiresPasswordForOperation({ accountAddress, walletOperation = WalletOperation.REACH_THRESHOLD, walletMetadata, backupInfo }) {
1444
1530
  const isEncrypted = await this.isPasswordEncrypted({
1445
- accountAddress
1531
+ accountAddress,
1532
+ walletMetadata,
1533
+ backupInfo
1446
1534
  });
1447
1535
  if (!isEncrypted) {
1448
1536
  return false;
1449
1537
  }
1450
1538
  return this.requiresRestoreBackupSharesForOperation({
1451
1539
  accountAddress,
1452
- walletOperation
1540
+ walletOperation,
1541
+ walletMetadata,
1542
+ backupInfo
1453
1543
  });
1454
1544
  }
1455
1545
  /**
1456
1546
  * check if the operation requires restoring backup shares
1457
- */ async requiresRestoreBackupSharesForOperation({ accountAddress, walletOperation = WalletOperation.REACH_THRESHOLD }) {
1458
- const externalServerKeySharesBackupInfo = await this.getWalletExternalServerKeyShareBackupInfo({
1459
- accountAddress
1547
+ */ async requiresRestoreBackupSharesForOperation({ accountAddress, walletOperation = WalletOperation.REACH_THRESHOLD, walletMetadata, backupInfo }) {
1548
+ const externalServerKeySharesBackupInfo = backupInfo != null ? backupInfo : await this.getWalletExternalServerKeyShareBackupInfo({
1549
+ walletMetadata
1460
1550
  });
1461
- const externalServerKeyShares = this.walletMap[accountAddress].externalServerKeyShares || [];
1551
+ const thresholdSignatureScheme = walletMetadata.thresholdSignatureScheme;
1462
1552
  if (walletOperation === WalletOperation.REACH_ALL_PARTIES || walletOperation === WalletOperation.REFRESH || walletOperation === WalletOperation.RESHARE) {
1463
1553
  return true;
1464
1554
  }
1465
1555
  const { shares, requiredShareCount } = this.recoverStrategy({
1466
1556
  externalServerKeySharesBackupInfo,
1467
- thresholdSignatureScheme: this.walletMap[accountAddress].thresholdSignatureScheme,
1557
+ thresholdSignatureScheme,
1468
1558
  walletOperation
1469
1559
  });
1470
1560
  const dynamicKeyShareIds = shares[BackupLocation.DYNAMIC] || [];
1471
1561
  const externalKeyShareIds = shares[BackupLocation.EXTERNAL] || [];
1472
- // Check if we have enough shares from either location
1473
- const totalAvailableShares = externalServerKeyShares.length + dynamicKeyShareIds.length + externalKeyShareIds.length;
1562
+ const totalAvailableShares = dynamicKeyShareIds.length + externalKeyShareIds.length;
1474
1563
  if (totalAvailableShares >= requiredShareCount) {
1475
1564
  return false;
1476
1565
  }
1477
1566
  return true;
1478
1567
  }
1479
- async getWalletExternalServerKeyShareBackupInfo({ accountAddress }) {
1480
- var _wallet_externalServerKeySharesBackupInfo_backups_BackupLocation_DYNAMIC, _wallet_externalServerKeySharesBackupInfo_backups, _wallet_externalServerKeySharesBackupInfo, _user_verifiedCredentials;
1568
+ async getWalletExternalServerKeyShareBackupInfo({ walletMetadata }) {
1481
1569
  this.ensureApiClientAuthenticated();
1482
- let wallet = this.walletMap[accountAddress];
1483
- if (!wallet) {
1484
- const fetchedWallet = await this.getWalletByAddress(accountAddress);
1485
- if (!fetchedWallet) {
1486
- throw new Error(`Wallet not found for address 2: ${accountAddress}`);
1487
- }
1488
- wallet = fetchedWallet;
1489
- }
1490
- // Return existing backup info if it exists
1491
- 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) {
1492
- return wallet.externalServerKeySharesBackupInfo;
1493
- }
1494
- // Get backup info from server
1495
- const user = await this.apiClient.getUser(v4());
1496
- const walletData = (_user_verifiedCredentials = user.verifiedCredentials) == null ? void 0 : _user_verifiedCredentials.find((vc)=>{
1497
- var _vc_address;
1498
- return ((_vc_address = vc.address) == null ? void 0 : _vc_address.toLowerCase()) === accountAddress.toLowerCase();
1499
- });
1500
- return getExternalServerKeyShareBackupInfo({
1501
- walletProperties: walletData == null ? void 0 : walletData.walletProperties
1502
- });
1503
- }
1504
- async getWallet({ accountAddress, walletOperation = WalletOperation.NO_OPERATION, shareCount = undefined, password = undefined }) {
1505
- try {
1506
- this.ensureApiClientAuthenticated();
1507
- const existingWalletCheck = await this.checkWalletFields({
1508
- accountAddress,
1509
- walletOperation,
1510
- shareCount
1511
- });
1512
- if (existingWalletCheck) {
1513
- this.logger.debug(`Wallet ${accountAddress} already exists`);
1514
- return this.walletMap[accountAddress];
1515
- }
1516
- const wallet = await this.getWalletByAddress(accountAddress);
1517
- if (!wallet) {
1518
- throw new Error(`Wallet not found for address 3: ${accountAddress}`);
1519
- }
1520
- // PII: intentionally omitting full wallet object (contains key share info)
1521
- this.logger.debug('Restoring wallet', {
1522
- walletId: wallet.walletId,
1523
- chainName: wallet.chainName
1524
- });
1525
- // The wallet is already processed, so we can use it directly
1526
- this.walletMap[accountAddress] = wallet;
1527
- if (walletOperation !== WalletOperation.NO_OPERATION && await this.requiresRestoreBackupSharesForOperation({
1528
- accountAddress,
1529
- walletOperation
1530
- })) {
1531
- const decryptedKeyShares = await this.recoverEncryptedBackupByWallet({
1532
- accountAddress,
1533
- password: password != null ? password : this.environmentId,
1534
- walletOperation: walletOperation,
1535
- shareCount
1536
- });
1537
- // PII: intentionally omitting decryptedKeyShares (contains key material)
1538
- this.logger.debug('[DynamicWaasWalletClient] Recovered backup', {
1539
- shareCount: decryptedKeyShares == null ? void 0 : decryptedKeyShares.length
1540
- });
1541
- }
1542
- // externalServerKeyShares
1543
- const walletCount = Object.keys(this.walletMap).length;
1544
- if (walletCount === 0) {
1545
- throw new Error('No wallets found');
1546
- }
1547
- // Return the only wallet if there's just one
1548
- if (walletCount === 1) {
1549
- return Object.values(this.walletMap)[0];
1550
- }
1551
- return this.walletMap[accountAddress];
1552
- } catch (error) {
1553
- this.logger.error('Error in getWallet', error);
1554
- throw error;
1555
- }
1570
+ return this.resolveBackupInfo(walletMetadata);
1556
1571
  }
1557
1572
  /**
1558
- * Get a single wallet by address
1559
- * First tries the efficient getWaasWalletByAddress endpoint, falls back to getUser() if not available
1573
+ * Get a single wallet by address.
1574
+ * First tries the efficient `getWaasWalletByAddress` endpoint, falls back to
1575
+ * `getUser()` if not available.
1576
+ *
1577
+ * Note: the `getWaasWalletByAddress` endpoint returns wallet identity only
1578
+ * — it does not include keyShare pointer metadata, so the byAddress path
1579
+ * leaves `externalServerKeySharesBackupInfo` `undefined`. Operations that
1580
+ * need it (sign with auto-recovery, password verification, refresh,
1581
+ * reshare, updatePassword) will throw on a result obtained via this path.
1582
+ * The `getUser()` fallback populates `externalServerKeySharesBackupInfo`
1583
+ * from `walletProperties.keyShares`.
1584
+ *
1585
+ * Prefer `fetchWalletMetadata(accountAddress)` for identity-only lookups.
1560
1586
  */ async getWalletByAddress(accountAddress) {
1561
- // Return cached wallet if available
1562
- if (this.walletMap[accountAddress]) {
1563
- return this.walletMap[accountAddress];
1564
- }
1565
1587
  this.ensureApiClientAuthenticated();
1566
- // Try getting single wallet by address first
1567
1588
  try {
1568
- var _walletResponse_wallet;
1569
1589
  const walletResponse = await this.apiClient.getWaasWalletByAddress({
1570
1590
  walletAddress: accountAddress
1571
1591
  });
1572
- const walletProperties = {
1592
+ return {
1573
1593
  walletId: walletResponse.wallet.walletId,
1574
1594
  chainName: walletResponse.wallet.chainName,
1575
1595
  accountAddress: walletResponse.wallet.accountAddress,
1576
1596
  externalServerKeyShares: [],
1577
1597
  derivationPath: walletResponse.wallet.derivationPath,
1578
- thresholdSignatureScheme: walletResponse.wallet.thresholdSignatureScheme,
1579
- externalServerKeySharesBackupInfo: getExternalServerKeyShareBackupInfo({
1580
- walletProperties: {
1581
- // @ts-expect-error TODO: update response to get key shares
1582
- keyShares: ((_walletResponse_wallet = walletResponse.wallet) == null ? void 0 : _walletResponse_wallet.keyShares) || [],
1583
- thresholdSignatureScheme: walletResponse.wallet.thresholdSignatureScheme,
1584
- derivationPath: walletResponse.wallet.derivationPath
1585
- }
1586
- })
1598
+ thresholdSignatureScheme: walletResponse.wallet.thresholdSignatureScheme
1587
1599
  };
1588
- // Cache the wallet
1589
- this.walletMap[accountAddress] = walletProperties;
1590
- return walletProperties;
1591
1600
  } catch (error) {
1592
1601
  var _error_response;
1593
- // If the new endpoint doesn't exist (404 or not implemented), fall back to getUser()
1594
1602
  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') {
1595
1603
  var _user_verifiedCredentials, _wallet_walletProperties, _wallet_walletProperties1;
1596
1604
  this.logger.debug('getWaasWalletByAddress endpoint not available, falling back to getUser()');
1597
- // Fallback to getUser() approach
1598
1605
  const user = await this.apiClient.getUser(v4());
1599
1606
  const wallet = (_user_verifiedCredentials = user.verifiedCredentials) == null ? void 0 : _user_verifiedCredentials.find((vc)=>{
1600
1607
  var _vc_address;
@@ -1604,9 +1611,9 @@ class DynamicWalletClient {
1604
1611
  return null;
1605
1612
  }
1606
1613
  var _wallet_walletProperties_derivationPath;
1607
- const walletProperties = {
1614
+ return {
1608
1615
  walletId: wallet.id,
1609
- chainName: wallet.chain,
1616
+ chainName: verifiedCredentialNameToChainEnum[wallet.chain],
1610
1617
  accountAddress: wallet.address,
1611
1618
  externalServerKeyShares: [],
1612
1619
  derivationPath: (_wallet_walletProperties_derivationPath = (_wallet_walletProperties = wallet.walletProperties) == null ? void 0 : _wallet_walletProperties.derivationPath) != null ? _wallet_walletProperties_derivationPath : undefined,
@@ -1615,9 +1622,6 @@ class DynamicWalletClient {
1615
1622
  walletProperties: wallet.walletProperties || {}
1616
1623
  })
1617
1624
  };
1618
- // Cache the wallet
1619
- this.walletMap[accountAddress] = walletProperties;
1620
- return walletProperties;
1621
1625
  }
1622
1626
  throw error;
1623
1627
  }
@@ -1632,8 +1636,8 @@ class DynamicWalletClient {
1632
1636
  var _user_verifiedCredentials;
1633
1637
  const verifiedCredentials = (_user_verifiedCredentials = user.verifiedCredentials) != null ? _user_verifiedCredentials : [];
1634
1638
  const waasWallets = verifiedCredentials.filter((vc)=>vc.walletName === 'dynamicwaas' && !!vc.address && !!vc.chain);
1635
- const wallets = waasWallets.map((vc)=>{
1636
- var _this_walletMap_vc_address, _vc_walletProperties, _vc_walletProperties1;
1639
+ return waasWallets.map((vc)=>{
1640
+ var _vc_walletProperties, _vc_walletProperties1;
1637
1641
  var _vc_walletProperties_thresholdSignatureScheme;
1638
1642
  return {
1639
1643
  walletId: vc.id,
@@ -1642,49 +1646,13 @@ class DynamicWalletClient {
1642
1646
  externalServerKeySharesBackupInfo: getExternalServerKeyShareBackupInfo({
1643
1647
  walletProperties: vc.walletProperties
1644
1648
  }),
1645
- externalServerKeyShares: ((_this_walletMap_vc_address = this.walletMap[vc.address]) == null ? void 0 : _this_walletMap_vc_address.externalServerKeyShares) || [],
1649
+ externalServerKeyShares: [],
1646
1650
  derivationPath: (_vc_walletProperties = vc.walletProperties) == null ? void 0 : _vc_walletProperties.derivationPath,
1647
1651
  thresholdSignatureScheme: (_vc_walletProperties_thresholdSignatureScheme = (_vc_walletProperties1 = vc.walletProperties) == null ? void 0 : _vc_walletProperties1.thresholdSignatureScheme) != null ? _vc_walletProperties_thresholdSignatureScheme : ThresholdSignatureScheme.TWO_OF_TWO
1648
1652
  };
1649
1653
  });
1650
- this.walletMap = wallets.reduce((acc, wallet)=>{
1651
- acc[wallet.accountAddress] = wallet;
1652
- return acc;
1653
- }, {});
1654
- return wallets;
1655
- }
1656
- /**
1657
- * Helper method to initialize or update a wallet entry in the walletMap.
1658
- * This provides a consistent way to set up wallet properties across different operations.
1659
- * @param accountAddress - The account address for the wallet
1660
- * @param walletId - The wallet ID
1661
- * @param chainName - The chain name (e.g., 'BTC', 'EVM')
1662
- * @param thresholdSignatureScheme - The threshold signature scheme
1663
- * @param derivationPath - Optional derivation path for the wallet
1664
- * @param additionalProps - Optional additional properties to merge into the wallet entry
1665
- */ initializeWalletMapEntry({ accountAddress, walletId, chainName, thresholdSignatureScheme, derivationPath, additionalProps = {} }) {
1666
- this.walletMap[accountAddress] = _extends({}, this.walletMap[accountAddress], {
1667
- accountAddress,
1668
- walletId,
1669
- chainName,
1670
- thresholdSignatureScheme
1671
- }, derivationPath ? {
1672
- derivationPath
1673
- } : {}, {
1674
- externalServerKeySharesBackupInfo: getExternalServerKeyShareBackupInfo()
1675
- }, additionalProps);
1676
- // PII: intentionally omitting walletMap (contains key share data for all wallets)
1677
- this.logger.debug('walletMap initialized for wallet', {
1678
- context: {
1679
- accountAddress,
1680
- walletId,
1681
- chainName
1682
- }
1683
- });
1684
1654
  }
1685
1655
  constructor({ environmentId, baseApiUrl, baseMPCRelayApiUrl, debug, forwardMPCClient, enableMPCAccelerator = true, logger }){
1686
- this.walletMap = {} // todo: store in session storage
1687
- ;
1688
1656
  this.isApiClientAuthenticated = false;
1689
1657
  this.forwardMPCEnabled = true;
1690
1658
  if (logger) {
@@ -1909,4 +1877,49 @@ const revokeDelegation = async (client, walletId)=>{
1909
1877
  }
1910
1878
  };
1911
1879
 
1912
- export { DynamicWalletClient, base64ToBytes, bytesToBase64, createDelegatedWalletClient, createLogError, delegatedSignMessage, ensureBase64Padding, formatEvmMessage, formatMessage, getExternalServerKeyShareBackupInfo, getMPCSignatureScheme, getMPCSigner, isHexString, logError, mergeUniqueKeyShares, retryPromise, revokeDelegation, stringToBytes, stripHexPrefix };
1880
+ function decryptAesGcm(key, ivB64, ctB64, tagB64) {
1881
+ const iv = Buffer.from(ivB64, 'base64url');
1882
+ const ciphertext = Buffer.from(ctB64, 'base64url');
1883
+ const tag = Buffer.from(tagB64, 'base64url');
1884
+ const decipher = crypto$1.createDecipheriv('aes-256-gcm', key, iv);
1885
+ decipher.setAuthTag(tag);
1886
+ const updateResult = decipher.update(ciphertext);
1887
+ const finalResult = decipher.final();
1888
+ const plaintext = Buffer.concat([
1889
+ updateResult,
1890
+ finalResult
1891
+ ]);
1892
+ return plaintext;
1893
+ }
1894
+ function rsaOaepDecryptEk(privateKeyPem, ekB64) {
1895
+ return crypto$1.privateDecrypt({
1896
+ key: privateKeyPem,
1897
+ oaepHash: 'sha256',
1898
+ padding: crypto$1.constants.RSA_PKCS1_OAEP_PADDING
1899
+ }, Buffer.from(ekB64, 'base64url'));
1900
+ }
1901
+ function decryptDelegatedWebhookData({ privateKeyPem, encryptedDelegatedKeyShare, encryptedWalletApiKey }) {
1902
+ try {
1903
+ const shareKey = rsaOaepDecryptEk(privateKeyPem, encryptedDelegatedKeyShare.ek);
1904
+ const walletApiKeyKey = rsaOaepDecryptEk(privateKeyPem, encryptedWalletApiKey.ek);
1905
+ const decryptedDelegatedShare = decryptAesGcm(shareKey, encryptedDelegatedKeyShare.iv, encryptedDelegatedKeyShare.ct, encryptedDelegatedKeyShare.tag);
1906
+ const decryptedWalletApiKey = decryptAesGcm(walletApiKeyKey, encryptedWalletApiKey.iv, encryptedWalletApiKey.ct, encryptedWalletApiKey.tag);
1907
+ const decryptedWalletApiKeyString = decryptedWalletApiKey.toString('utf8');
1908
+ const decryptedDelegatedShareString = decryptedDelegatedShare.toString('utf8');
1909
+ const decryptedDelegatedShareObject = JSON.parse(decryptedDelegatedShareString);
1910
+ if (!decryptedDelegatedShareObject) {
1911
+ throw new Error('Failed to decrypt delegated share');
1912
+ }
1913
+ if (!decryptedWalletApiKeyString) {
1914
+ throw new Error('Failed to decrypt wallet API key');
1915
+ }
1916
+ return {
1917
+ decryptedDelegatedShare: decryptedDelegatedShareObject,
1918
+ decryptedWalletApiKey: decryptedWalletApiKeyString
1919
+ };
1920
+ } catch (error) {
1921
+ throw new Error('Failed to decrypt delegated webhook data: ' + error);
1922
+ }
1923
+ }
1924
+
1925
+ export { DynamicWalletClient, base64ToBytes, bytesToBase64, createDelegatedWalletClient, createLogError, decryptDelegatedWebhookData, delegatedSignMessage, ensureBase64Padding, formatEvmMessage, formatMessage, getExternalServerKeyShareBackupInfo, getMPCSignatureScheme, getMPCSigner, isHexString, logError, mergeUniqueKeyShares, retryPromise, revokeDelegation, stringToBytes, stripHexPrefix };