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